Repository navigation
Expand file tree
/
Copy pathstart.sh
More file actions
executable file
·169 lines (159 loc) · 7.23 KB
/
Copy pathstart.sh
File metadata and controls
executable file
·169 lines (159 loc) · 7.23 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
#!/usr/bin/env sh
# Start ATC: (re)builds the distribution when its sources changed, loads .env, runs it.
#
# ./start.sh # interactive, in the current directory
# ./start.sh -C ~/proj -m gpt # any atc flag is passed through
# ./start.sh -p "run: 1 + 1" # one non-interactive turn
# ./start.sh -Xmx4g # -Xmx/-Xms go to the JVM, everything else to atc
#
# Environment (see .env.example): ANTHROPIC_API_KEY, OPENAI_API_KEY, ATC_MODEL,
# ATC_CONFIG, ATC_CWD, ATC_JAVA_OPTS, ATC_DEBUG, ATC_ENV_FILE, ATC_SKIP_BUILD.
set -eu
ROOT=$(cd "$(dirname "$0")" && pwd)
ENV_FILE=${ATC_ENV_FILE:-$ROOT/.env}
# Load literal KEY=value entries without replacing non-empty exported values; with an
# argument, only that key.
load_env() {
[ -f "$ENV_FILE" ] || return 0
cr=$(printf '\r')
while IFS= read -r line || [ -n "$line" ]; do
line=${line%"$cr"}
case "$line" in ''|'#'*) continue ;; esac
line=${line#export }
case "$line" in *=*) ;; *) continue ;; esac
key=${line%%=*}
value=${line#*=}
case "$key" in ''|[0-9]*|*[!A-Za-z0-9_]*) continue ;; esac
[ -z "${1:-}" ] || [ "$key" = "$1" ] || continue
# strip surrounding quotes
case "$value" in
\"*\") value=${value#\"}; value=${value%\"} ;;
\'*\') value=${value#\'}; value=${value%\'} ;;
esac
[ -z "$value" ] && continue
eval "current=\${$key:-}"
if [ -z "$current" ]; then
export "$key=$value"
fi
done < "$ENV_FILE"
}
DIST="$ROOT/out/dist.dest"
JAR="$DIST/atc.jar"
LIBJAR="$DIST/atc-lib.jar"
# Touched after each build. Mill leaves the jars alone when the content they are built from
# did not change, so a source only touched or checked out again would stay newer than them
# and make every start rebuild.
STAMP="$DIST/build.stamp"
# The build runs before the rest of .env is loaded, so a Mill server it starts does not keep
# the API keys in its environment.
load_env ATC_SKIP_BUILD
needs_build=0
if [ "${ATC_SKIP_BUILD:-0}" != "1" ]; then
if [ ! -f "$JAR" ] || [ ! -f "$LIBJAR" ] || [ ! -f "$STAMP" ]; then
needs_build=1
# What the distribution is built from; the tests are not part of it.
elif [ -n "$(find "$ROOT/build.mill" "$ROOT/app/src" "$ROOT/app/resources" "$ROOT/lib/src" "$ROOT/windows" \
-type f -newer "$STAMP" | head -1)" ]; then
needs_build=1
fi
fi
if [ "$needs_build" = 1 ]; then
echo "[start.sh] building distribution (./mill dist)..." >&2
(cd "$ROOT" && ./mill dist >/dev/null)
touch "$STAMP"
fi
load_env
# `-Xmx<size>` / `-Xms<size>` are the JVM's flags, not ATC's: take them out for java. The
# value of an ATC option that takes one (-p 'text', -C dir, ...) is forwarded untouched even
# when it starts with -Xm: the list of those options mirrors `FlagsWithValues` in
# app/src/atc/Cli.scala (`atc` and the PowerShell launchers carry the same list).
JVM_OPTS=
expect_value=
for arg in "$@"; do
shift
if [ -n "$expect_value" ]; then
expect_value=
set -- "$@" "$arg"
continue
fi
case "$arg" in
-c|--config|-C|--cwd|-m|--model|-p|--prompt|--mode)
expect_value=1
set -- "$@" "$arg" ;;
-Xmx*|-Xms*)
size=${arg#-Xm?}
case "${size%[kKmMgG]}" in
''|*[!0-9]*)
echo "[start.sh] Invalid JVM heap size '$arg': use a number with an optional k, m or g suffix, e.g. -Xmx4g or -Xms512m" >&2
exit 1 ;;
esac
JVM_OPTS="$JVM_OPTS $arg" ;;
*) set -- "$@" "$arg" ;;
esac
done
[ -n "${ATC_MODEL:-}" ] && set -- -m "$ATC_MODEL" "$@"
[ -n "${ATC_CONFIG:-}" ] && set -- -c "$ATC_CONFIG" "$@"
[ -n "${ATC_CWD:-}" ] && set -- -C "$ATC_CWD" "$@"
# JVM defaults as in the `atc` wrapper (doc/development.md "JVM settings"); the same list
# is repeated in atc, start.ps1, windows/atc.ps1 and the dist script in build.mill.
DEFAULT_JVM_OPTS="-Xms256m -Xmx2g -Xss4m -XX:-UsePerfData"
# Startup cache as in the `atc` wrapper: an AOT cache (Java 25+) or a CDS archive (Java
# 19-24) under out/dist.dest/startup/, rebuilt by one echo-model run when the jars (every
# ./mill dist) or the JDK change. ATC_STARTUP_CACHE=0 disables it. The version line need not
# come first: JAVA_TOOL_OPTIONS and similar variables add a "Picked up ..." line before it.
java_version=$(java -version 2>&1)
java_major=$(printf '%s\n' "$java_version" | grep -m1 ' version "' | sed -n 's/^[^"]*"\([0-9]*\).*/\1/p')
case "$java_major" in ''|*[!0-9]*) java_major=0 ;; esac
# Scala's LazyVals still use sun.misc.Unsafe; Java 23+ warns about it on every run (JEP 471).
VERSIONED_JVM_OPTS=
[ "$java_major" -lt 23 ] || VERSIONED_JVM_OPTS="--sun-misc-unsafe-memory-access=allow"
# JLine loads a native library; Java 24+ warns about that on every run (JEP 472).
[ "$java_major" -lt 24 ] || VERSIONED_JVM_OPTS="$VERSIONED_JVM_OPTS --enable-native-access=ALL-UNNAMED"
STARTUP_OPTS=
if [ "${ATC_STARTUP_CACHE:-1}" != 0 ]; then
cache_dir="$DIST/startup"
cache_key="$cache_dir/key.txt"
# The JDK and the JVM options besides the defaults the cache was built with (the JVM refuses
# a cache built with different module options and prints errors for another GC or heap size).
key_text="$java_version
$VERSIONED_JVM_OPTS
${ATC_JAVA_OPTS:-}$JVM_OPTS"
if [ "$java_major" -ge 25 ]; then
cache="$cache_dir/atc.aot"; create="-XX:AOTCacheOutput=$cache"; use="-XX:AOTCache=$cache"
elif [ "$java_major" -ge 19 ]; then
cache="$cache_dir/atc.jsa"; create="-XX:ArchiveClassesAtExit=$cache"; use="-XX:SharedArchiveFile=$cache"
else
cache=
fi
if [ -n "$cache" ]; then
if [ ! -f "$cache_key" ] || [ "$(cat "$cache_key")" != "$key_text" ] || [ -n "$(find "$JAR" "$LIBJAR" -newer "$cache_key")" ]; then
# The cache is an optimisation: ATC runs without it when its directory cannot be
# created or written, and a key that cannot be written does not stop the start.
if { mkdir -p "$cache_dir" && [ -w "$cache_dir" ]; } 2>/dev/null; then
echo "[start.sh] preparing the JVM startup cache (a few seconds)..." >&2
rm -f "$cache"
tmp=$(mktemp -d "${TMPDIR:-/tmp}/atc-startup-cache.XXXXXX")
trap 'rm -rf "$tmp"; exit 130' INT TERM HUP
printf '%s\n' '{"providers":{"echo":{"api":"echo","models":{"echo":{}}}},"model":"echo"}' > "$tmp/train.json"
# shellcheck disable=SC2086
java $DEFAULT_JVM_OPTS $VERSIONED_JVM_OPTS "$create" ${ATC_JAVA_OPTS:-} $JVM_OPTS \
-Dfile.encoding=UTF-8 -Datc.lib.classpath="$LIBJAR" -jar "$JAR" \
-c "$tmp/train.json" -C "$tmp" -p 'run: 1 + 1' >/dev/null 2>&1 || true
rm -rf "$tmp"
trap - INT TERM HUP
printf '%s\n' "$key_text" > "$cache_key" || true
for jar in "$JAR" "$LIBJAR"; do
[ -z "$(find "$jar" -newer "$cache_key" 2>/dev/null)" ] || touch -r "$jar" "$cache_key"
done
[ -s "$cache" ] || { rm -f "$cache"; echo "[start.sh] could not build the startup cache; running without it." >&2; }
else
cache=
fi
fi
[ -s "$cache" ] && STARTUP_OPTS=$use
fi
fi
# Later flags win: the defaults, then ATC_JAVA_OPTS, then -Xmx/-Xms from the command line.
# The cache flag holds a path, which may contain spaces, so it stays one argument.
# shellcheck disable=SC2086
exec java $DEFAULT_JVM_OPTS $VERSIONED_JVM_OPTS ${STARTUP_OPTS:+"$STARTUP_OPTS"} ${ATC_JAVA_OPTS:-} $JVM_OPTS -Dfile.encoding=UTF-8 -Datc.lib.classpath="$LIBJAR" -jar "$JAR" "$@"