@miller-tech/uap 1.210.1 → 1.210.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.tsbuildinfo +1 -1
- package/dist/benchmarks/paired/runner.d.ts.map +1 -1
- package/dist/benchmarks/paired/runner.js +57 -40
- package/dist/benchmarks/paired/runner.js.map +1 -1
- package/dist/cli/bench.d.ts.map +1 -1
- package/dist/cli/bench.js +10 -1
- package/dist/cli/bench.js.map +1 -1
- package/dist/cli/systemd-services.d.ts.map +1 -1
- package/dist/cli/systemd-services.js +57 -1
- package/dist/cli/systemd-services.js.map +1 -1
- package/dist/delivery/vision-judge.d.ts.map +1 -1
- package/dist/delivery/vision-judge.js +25 -2
- package/dist/delivery/vision-judge.js.map +1 -1
- package/dist/utils/llama-discovery.d.ts +10 -0
- package/dist/utils/llama-discovery.d.ts.map +1 -0
- package/dist/utils/llama-discovery.js +72 -0
- package/dist/utils/llama-discovery.js.map +1 -0
- package/dist/utils/model-slots.d.ts.map +1 -1
- package/dist/utils/model-slots.js +8 -0
- package/dist/utils/model-slots.js.map +1 -1
- package/package.json +3 -1
- package/scripts/lib/llama-upstream.sh +226 -0
- package/scripts/run-anthropic-proxy-continuity.sh +136 -0
- package/src/policies/enforcers/__pycache__/_common.cpython-312.pyc +0 -0
- package/templates/hooks/__pycache__/deliver_autoroute.cpython-312.pyc +0 -0
- package/tools/agents/scripts/__pycache__/toolcall_path_normalizer.cpython-312.pyc +0 -0
- package/tools/agents/tests/test_confidence_escalation.py +9 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@miller-tech/uap",
|
|
3
|
-
"version": "1.210.
|
|
3
|
+
"version": "1.210.3",
|
|
4
4
|
"description": "Autonomous AI agent memory system with CLAUDE.md protocol enforcement",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -119,6 +119,8 @@
|
|
|
119
119
|
"src/policies/schemas/policies",
|
|
120
120
|
"src/policies/enforcers",
|
|
121
121
|
"tools/agents",
|
|
122
|
+
"scripts/lib",
|
|
123
|
+
"scripts/run-anthropic-proxy-continuity.sh",
|
|
122
124
|
"scripts/setup",
|
|
123
125
|
"scripts/version-bump.sh",
|
|
124
126
|
"scripts/validate-build.sh",
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Resolve the live llama.cpp upstream for the Anthropic proxy.
|
|
3
|
+
#
|
|
4
|
+
# WHY: the upstream is not always a fixed port. Unsloth Studio restarts its
|
|
5
|
+
# bundled llama-server on a NEW random port every launch (observed :50047 ->
|
|
6
|
+
# :34407 -> :59879), while LLAMA_CPP_BASE is pinned in the proxy env file. The
|
|
7
|
+
# moment Studio restarts, every local request fails with
|
|
8
|
+
# "Upstream connect failed after 3 attempts: ConnectError" -> HTTP 529
|
|
9
|
+
# and stays broken until someone hand-edits the env file. That env file is also
|
|
10
|
+
# self-protect'd, so the agent cannot repair it — the stack sits dead.
|
|
11
|
+
#
|
|
12
|
+
# TRUST MODEL. A discovered endpoint receives every prompt and its replies drive
|
|
13
|
+
# tool execution, so discovery is deliberately narrow:
|
|
14
|
+
# - The pin wins whenever it answers. A deliberate operator pin (remote host,
|
|
15
|
+
# second server) is never silently overridden by a local process.
|
|
16
|
+
# - Only loopback and wildcard binds are accepted. A llama-server bound to a
|
|
17
|
+
# specific non-loopback address is REJECTED rather than assumed to also be
|
|
18
|
+
# on 127.0.0.1 — that address may belong to a different user's socket.
|
|
19
|
+
# - A candidate must prove it is a chat-capable llama-server (/props shape +
|
|
20
|
+
# completion capability), not merely something returning 200. That rejects
|
|
21
|
+
# a decoy and, just as importantly, the embedding llama-server that also
|
|
22
|
+
# runs on this host.
|
|
23
|
+
# Residual risk, accepted: a process running as THIS user can still name itself
|
|
24
|
+
# llama-server and pass the shape checks. Such a process can already read the
|
|
25
|
+
# source tree and the agent's files.
|
|
26
|
+
#
|
|
27
|
+
# Sourced by scripts/run-anthropic-proxy-continuity.sh. Also runnable directly:
|
|
28
|
+
# scripts/lib/llama-upstream.sh resolve http://127.0.0.1:8080/v1
|
|
29
|
+
#
|
|
30
|
+
# `curl` and `ss` are invoked by bare name (never absolute) so tests can stub
|
|
31
|
+
# them on PATH and exercise the real code path. The TS counterpart is
|
|
32
|
+
# src/utils/llama-discovery.ts — the two MUST agree; test/llama-upstream-parity
|
|
33
|
+
# asserts it.
|
|
34
|
+
|
|
35
|
+
# Strip the trailing /v1 (and any trailing slash) off an OpenAI-compatible base.
|
|
36
|
+
llama_upstream_root() {
|
|
37
|
+
local base="${1:-}"
|
|
38
|
+
base="${base%/}"
|
|
39
|
+
base="${base%/v1}"
|
|
40
|
+
printf '%s' "$base"
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
# 0 if the base answers llama-server's /health with 200. Liveness only.
|
|
44
|
+
# `--` terminates curl's options: a base beginning with `-` would otherwise be
|
|
45
|
+
# read as a flag (e.g. -K reads an attacker-named config file).
|
|
46
|
+
llama_upstream_alive() {
|
|
47
|
+
local base="${1:-}"
|
|
48
|
+
[ -n "$base" ] || return 1
|
|
49
|
+
local code
|
|
50
|
+
code="$(curl -s -o /dev/null -w '%{http_code}' --max-time "${UAP_LLAMA_PROBE_TIMEOUT:-2}" \
|
|
51
|
+
-- "$(llama_upstream_root "$base")/health" 2>/dev/null || true)"
|
|
52
|
+
[ "$code" = "200" ]
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
# 0 if the base looks like a chat-capable llama.cpp server. Applied to
|
|
56
|
+
# DISCOVERED candidates only — an operator pin is taken at its word.
|
|
57
|
+
llama_upstream_is_chat_server() {
|
|
58
|
+
local base="${1:-}" root props models
|
|
59
|
+
root="$(llama_upstream_root "$base")"
|
|
60
|
+
|
|
61
|
+
# /props carries llama-server's generation settings. A bare HTTP server, an
|
|
62
|
+
# error page, or a still-loading server does not.
|
|
63
|
+
props="$(curl -s --max-time "${UAP_LLAMA_PROBE_TIMEOUT:-2}" -- "${root}/props" 2>/dev/null || true)"
|
|
64
|
+
case "$props" in
|
|
65
|
+
*'"default_generation_settings"'*) ;;
|
|
66
|
+
*) return 1 ;;
|
|
67
|
+
esac
|
|
68
|
+
|
|
69
|
+
# Reject an embedding-only server (this host runs one). When the build does
|
|
70
|
+
# not report capabilities at all, accept — older llama.cpp omits the field.
|
|
71
|
+
models="$(curl -s --max-time "${UAP_LLAMA_PROBE_TIMEOUT:-2}" -- "${root}/v1/models" 2>/dev/null || true)"
|
|
72
|
+
case "$models" in
|
|
73
|
+
*'"capabilities"'*)
|
|
74
|
+
case "$models" in
|
|
75
|
+
*'"completion"'*) ;;
|
|
76
|
+
*) return 1 ;;
|
|
77
|
+
esac
|
|
78
|
+
;;
|
|
79
|
+
esac
|
|
80
|
+
return 0
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
# Print the host:port of every locally listening llama-server, one per line,
|
|
84
|
+
# already normalised to a loopback authority. Non-loopback binds are dropped.
|
|
85
|
+
#
|
|
86
|
+
# `ss -ltnp` attributes a process only to sockets the caller owns, which is the
|
|
87
|
+
# case here (the server runs as the same user). Column 4 is Local Address:Port;
|
|
88
|
+
# the port is the segment after the LAST colon so IPv6 forms survive.
|
|
89
|
+
llama_upstream_candidate_authorities() {
|
|
90
|
+
ss -ltnp 2>/dev/null \
|
|
91
|
+
| grep -i 'users:(("llama-server"' \
|
|
92
|
+
| awk '{
|
|
93
|
+
n = split($4, a, ":");
|
|
94
|
+
port = a[n];
|
|
95
|
+
if (port !~ /^[0-9]+$/ || port+0 <= 0 || port+0 >= 65536) next;
|
|
96
|
+
addr = substr($4, 1, length($4) - length(port) - 1);
|
|
97
|
+
# Wildcard binds cover loopback, so 127.0.0.1 reaches the same
|
|
98
|
+
# socket. A specific non-loopback address does NOT imply loopback.
|
|
99
|
+
# Emit "port <tab> family-rank <tab> authority" so ordering is
|
|
100
|
+
# numeric by port and deterministic (IPv4 wins a tie) — matching
|
|
101
|
+
# src/utils/llama-discovery.ts exactly. Sorting the authority
|
|
102
|
+
# strings instead would order 10001 before 8080 and mangle [::1].
|
|
103
|
+
if (addr == "0.0.0.0" || addr == "*" || addr == "127.0.0.1") print port "\t0\t127.0.0.1:" port;
|
|
104
|
+
else if (addr == "[::]" || addr == "[::1]") print port "\t1\t[::1]:" port;
|
|
105
|
+
}' \
|
|
106
|
+
| sort -n -k1,1 -k2,2 \
|
|
107
|
+
| awk '!seen[$1]++ { print $3 }'
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
# Resolve the base to use. Echoes the preferred base untouched when it is
|
|
111
|
+
# healthy, or when nothing better can be proven — never worse than the pin.
|
|
112
|
+
llama_upstream_resolve() {
|
|
113
|
+
local preferred="${1:-}"
|
|
114
|
+
|
|
115
|
+
if llama_upstream_alive "$preferred"; then
|
|
116
|
+
printf '%s' "$preferred"
|
|
117
|
+
return 0
|
|
118
|
+
fi
|
|
119
|
+
|
|
120
|
+
if [ "${UAP_LLAMA_UPSTREAM_AUTODISCOVER:-on}" != "on" ]; then
|
|
121
|
+
printf '%s' "$preferred"
|
|
122
|
+
return 0
|
|
123
|
+
fi
|
|
124
|
+
|
|
125
|
+
local authority candidate
|
|
126
|
+
for authority in $(llama_upstream_candidate_authorities); do
|
|
127
|
+
candidate="http://${authority}/v1"
|
|
128
|
+
[ "$candidate" = "$preferred" ] && continue
|
|
129
|
+
if llama_upstream_alive "$candidate" && llama_upstream_is_chat_server "$candidate"; then
|
|
130
|
+
printf '%s' "$candidate"
|
|
131
|
+
return 0
|
|
132
|
+
fi
|
|
133
|
+
done
|
|
134
|
+
|
|
135
|
+
printf '%s' "$preferred"
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
# Port of a base URL ("http://192.168.1.165:8080/v1" -> "8080"). Empty when the
|
|
139
|
+
# base carries no explicit port.
|
|
140
|
+
llama_upstream_port() {
|
|
141
|
+
local authority="${1:-}"
|
|
142
|
+
authority="${authority#*://}"
|
|
143
|
+
authority="${authority%%/*}"
|
|
144
|
+
case "$authority" in
|
|
145
|
+
\[*\]:*) printf '%s' "${authority##*]:}" ;; # [::1]:8080
|
|
146
|
+
\[*\]) : ;; # [::1], no port
|
|
147
|
+
*:*) printf '%s' "${authority##*:}" ;;
|
|
148
|
+
esac
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
# Field 22 of /proc/<pid>/stat is the process start time. PID + starttime is an
|
|
152
|
+
# identity; PID alone is not, and this repo has already been bitten by PID reuse
|
|
153
|
+
# (see the deliver lock). Empty when the pid is gone.
|
|
154
|
+
llama_upstream_pid_token() {
|
|
155
|
+
local pid="${1:-}"
|
|
156
|
+
[ -n "$pid" ] || return 0
|
|
157
|
+
awk '{ print $22 }' "/proc/${pid}/stat" 2>/dev/null || true
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
# Background guard: startup resolution alone still strands a LONG-LIVED proxy,
|
|
161
|
+
# because llama can move ports hours after the proxy came up. Watch the upstream
|
|
162
|
+
# and, once it is dead AND a DIFFERENT live server exists, stop the proxy so its
|
|
163
|
+
# supervisor restarts it through resolve.
|
|
164
|
+
#
|
|
165
|
+
# ONLY safe under a supervisor — the caller gates on that. Two consecutive
|
|
166
|
+
# failures are required before acting: a llama restart briefly shows no listener
|
|
167
|
+
# at all, and reacting to that gap would bounce the proxy for nothing. Never
|
|
168
|
+
# acts on a dead upstream alone, only on a proven live alternative, so an
|
|
169
|
+
# intentionally stopped server does not cause a restart loop.
|
|
170
|
+
llama_upstream_watch() {
|
|
171
|
+
local base="${1:-}" target_pid="${2:-}"
|
|
172
|
+
local interval="${UAP_LLAMA_UPSTREAM_WATCH_SECS:-20}"
|
|
173
|
+
local misses=0 found token now_token
|
|
174
|
+
|
|
175
|
+
case "$target_pid" in
|
|
176
|
+
''|*[!0-9]*) return 0 ;; # never signal a non-PID (kill -TERM -1 sprays)
|
|
177
|
+
esac
|
|
178
|
+
# A non-numeric interval makes `sleep` fail, which would end the loop and
|
|
179
|
+
# silently disable the guard. Fall back rather than vanish.
|
|
180
|
+
case "$interval" in
|
|
181
|
+
''|*[!0-9.]*) interval=20 ;;
|
|
182
|
+
esac
|
|
183
|
+
token="$(llama_upstream_pid_token "$target_pid")"
|
|
184
|
+
|
|
185
|
+
while sleep "$interval"; do
|
|
186
|
+
now_token="$(llama_upstream_pid_token "$target_pid")"
|
|
187
|
+
# Gone, or the PID was recycled into someone else's process.
|
|
188
|
+
[ -n "$now_token" ] || return 0
|
|
189
|
+
[ "$now_token" = "$token" ] || return 0
|
|
190
|
+
|
|
191
|
+
if llama_upstream_alive "$base"; then
|
|
192
|
+
misses=0
|
|
193
|
+
continue
|
|
194
|
+
fi
|
|
195
|
+
|
|
196
|
+
misses=$((misses + 1))
|
|
197
|
+
[ "$misses" -ge 2 ] || continue
|
|
198
|
+
|
|
199
|
+
found="$(llama_upstream_resolve "$base")"
|
|
200
|
+
# "Moved" means a DIFFERENT PORT, not a different URL string. The
|
|
201
|
+
# documented pin is a LAN or localhost form (http://192.168.1.165:8080/v1,
|
|
202
|
+
# http://localhost:8080/v1) while a discovered candidate is always
|
|
203
|
+
# 127.0.0.1 — so comparing URLs calls the SAME server "moved" and bounces
|
|
204
|
+
# a healthy proxy every time the pin's address blips. A port change is
|
|
205
|
+
# the actual failure mode this watches for.
|
|
206
|
+
if [ -n "$found" ] && [ "$(llama_upstream_port "$found")" != "$(llama_upstream_port "$base")" ]; then
|
|
207
|
+
# Re-verify identity: resolve() above can take seconds.
|
|
208
|
+
now_token="$(llama_upstream_pid_token "$target_pid")"
|
|
209
|
+
[ "$now_token" = "$token" ] || return 0
|
|
210
|
+
echo "[proxy-upstream] upstream moved ${base} -> ${found}; restarting proxy to re-resolve" >&2
|
|
211
|
+
kill -TERM "$target_pid" 2>/dev/null || true
|
|
212
|
+
return 0
|
|
213
|
+
fi
|
|
214
|
+
misses=1
|
|
215
|
+
done
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
# Direct invocation: `llama-upstream.sh resolve <base>` / `alive <base>`.
|
|
219
|
+
if [ "${BASH_SOURCE[0]}" = "${0}" ]; then
|
|
220
|
+
case "${1:-}" in
|
|
221
|
+
resolve) llama_upstream_resolve "${2:-}"; echo ;;
|
|
222
|
+
alive) llama_upstream_alive "${2:-}" ;;
|
|
223
|
+
authorities) llama_upstream_candidate_authorities ;;
|
|
224
|
+
*) echo "usage: ${0##*/} {resolve|alive|authorities} [base]" >&2; exit 2 ;;
|
|
225
|
+
esac
|
|
226
|
+
fi
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
5
|
+
|
|
6
|
+
# Fail-safe passthrough default: when ANTHROPIC_PASSTHROUGH_MODELS is unset OR
|
|
7
|
+
# EMPTY, default it to __local_only__ (no api.anthropic.com forwarding) rather
|
|
8
|
+
# than the code's empty=forward-cloud default. Set here in the ExecStart script
|
|
9
|
+
# so it wins over systemd's EnvironmentFile (which overrides Environment=, so a
|
|
10
|
+
# systemd Environment= pin cannot hold) and over the env file drifting back to
|
|
11
|
+
# empty via `uap model routing use`/setup. An operator who genuinely wants cloud
|
|
12
|
+
# passthrough sets an explicit non-empty value (a comma list of claude- ids),
|
|
13
|
+
# which is preserved. This is the durable enforcement of the local-only policy.
|
|
14
|
+
export ANTHROPIC_PASSTHROUGH_MODELS="${ANTHROPIC_PASSTHROUGH_MODELS:-__local_only__}"
|
|
15
|
+
|
|
16
|
+
export PROXY_PORT="${PROXY_PORT:-4000}"
|
|
17
|
+
export LLAMA_CPP_BASE="${LLAMA_CPP_BASE:-http://127.0.0.1:8080/v1}"
|
|
18
|
+
export PROXY_LOG_LEVEL="${PROXY_LOG_LEVEL:-INFO}"
|
|
19
|
+
|
|
20
|
+
# ---------------------------------------------------------------------------
|
|
21
|
+
# Upstream resolution. LLAMA_CPP_BASE above is a PIN, and a pin goes stale:
|
|
22
|
+
# Unsloth Studio restarts its bundled llama-server on a new random port each
|
|
23
|
+
# launch (:50047 -> :34407 -> :59879 observed), after which every local request
|
|
24
|
+
# 529s until the env file is hand-edited — and that file is self-protect'd, so
|
|
25
|
+
# the agent cannot repair it. Resolve against reality instead: the pin is kept
|
|
26
|
+
# whenever it answers /health, and only a proven-dead pin falls through to
|
|
27
|
+
# discovering the live llama-server. Set UAP_LLAMA_UPSTREAM_AUTODISCOVER=off to
|
|
28
|
+
# pin hard. Must run BEFORE the context-window probe below, which reads
|
|
29
|
+
# LLAMA_CPP_BASE.
|
|
30
|
+
# ---------------------------------------------------------------------------
|
|
31
|
+
# Sourced defensively. Under `set -e` an unreadable lib would abort the script
|
|
32
|
+
# BEFORE exec, and the unit's Restart=always/RestartSec=3 would then respawn it
|
|
33
|
+
# every three seconds with no proxy at all — strictly worse than the stale pin
|
|
34
|
+
# this resolves. scripts/lib is not in package.json `files`, so an installed
|
|
35
|
+
# deployment can legitimately lack it. Degrade to the pin instead.
|
|
36
|
+
_upstream_lib="${ROOT_DIR}/scripts/lib/llama-upstream.sh"
|
|
37
|
+
if [ -r "$_upstream_lib" ]; then
|
|
38
|
+
# shellcheck source=lib/llama-upstream.sh
|
|
39
|
+
. "$_upstream_lib"
|
|
40
|
+
else
|
|
41
|
+
echo "[proxy-startup] WARNING: ${_upstream_lib} missing; using pinned upstream without discovery" >&2
|
|
42
|
+
llama_upstream_resolve() { printf '%s' "${1:-}"; }
|
|
43
|
+
llama_upstream_root() { local b="${1:-}"; b="${b%/}"; printf '%s' "${b%/v1}"; }
|
|
44
|
+
fi
|
|
45
|
+
_resolved_base="$(llama_upstream_resolve "$LLAMA_CPP_BASE")"
|
|
46
|
+
# An empty result would export LLAMA_CPP_BASE="" and degrade every upstream URL
|
|
47
|
+
# to a bare "/chat/completions"; keep the pin instead.
|
|
48
|
+
[ -n "$_resolved_base" ] || _resolved_base="$LLAMA_CPP_BASE"
|
|
49
|
+
if [ "$_resolved_base" != "$LLAMA_CPP_BASE" ]; then
|
|
50
|
+
echo "[proxy-startup] pinned upstream ${LLAMA_CPP_BASE} is unreachable; using discovered ${_resolved_base}"
|
|
51
|
+
export LLAMA_CPP_BASE="$_resolved_base"
|
|
52
|
+
else
|
|
53
|
+
echo "[proxy-startup] upstream: ${LLAMA_CPP_BASE}"
|
|
54
|
+
fi
|
|
55
|
+
|
|
56
|
+
export PROXY_LOOP_BREAKER="${PROXY_LOOP_BREAKER:-on}"
|
|
57
|
+
export PROXY_LOOP_WINDOW="${PROXY_LOOP_WINDOW:-6}"
|
|
58
|
+
export PROXY_LOOP_REPEAT_THRESHOLD="${PROXY_LOOP_REPEAT_THRESHOLD:-8}"
|
|
59
|
+
export PROXY_FORCED_THRESHOLD="${PROXY_FORCED_THRESHOLD:-15}"
|
|
60
|
+
export PROXY_NO_PROGRESS_THRESHOLD="${PROXY_NO_PROGRESS_THRESHOLD:-4}"
|
|
61
|
+
export PROXY_CONTEXT_RELEASE_THRESHOLD="${PROXY_CONTEXT_RELEASE_THRESHOLD:-0.90}"
|
|
62
|
+
export PROXY_GUARDRAIL_RETRY="${PROXY_GUARDRAIL_RETRY:-on}"
|
|
63
|
+
export PROXY_SESSION_TTL_SECS="${PROXY_SESSION_TTL_SECS:-7200}"
|
|
64
|
+
|
|
65
|
+
# Cross-session slot save/restore (UAP PR #179). Default ON: with
|
|
66
|
+
# llama-server on --parallel 1 (a single slot), N agentic sessions
|
|
67
|
+
# multiplexing the slot each evict the prior session's KV cache, forcing
|
|
68
|
+
# 60-96s full prompt reprocesses (~17% of requests). The proxy saves the
|
|
69
|
+
# outgoing session's slot state and restores the incoming session's on a
|
|
70
|
+
# switch. PROXY_SLOT_SAVE_DIR must match llama-server's --slot-save-path
|
|
71
|
+
# (run-llama-server-continuity.sh LLAMA_SLOT_SAVE_PATH). Set
|
|
72
|
+
# PROXY_SLOT_SAVE_RESTORE=off to disable.
|
|
73
|
+
export PROXY_SLOT_SAVE_RESTORE="${PROXY_SLOT_SAVE_RESTORE:-on}"
|
|
74
|
+
export PROXY_SLOT_SAVE_DIR="${PROXY_SLOT_SAVE_DIR:-${HOME}/.cache/uap/llama-slots}"
|
|
75
|
+
export PROXY_SLOT_CACHE_MAX_FILES="${PROXY_SLOT_CACHE_MAX_FILES:-12}"
|
|
76
|
+
|
|
77
|
+
export PROXY_TOOL_CALL_GRAMMAR="${PROXY_TOOL_CALL_GRAMMAR:-on}"
|
|
78
|
+
export PROXY_TOOL_CALL_GRAMMAR_REQUIRED_ONLY="${PROXY_TOOL_CALL_GRAMMAR_REQUIRED_ONLY:-on}"
|
|
79
|
+
export PROXY_TOOL_CALL_GRAMMAR_PATH="${PROXY_TOOL_CALL_GRAMMAR_PATH:-${ROOT_DIR}/tools/agents/config/tool-call.gbnf}"
|
|
80
|
+
|
|
81
|
+
# Structured thinking grammar (opt-in). When on, non-tool reasoning turns
|
|
82
|
+
# are constrained to emit a compact <think> Q/M/K/R/V header before output.
|
|
83
|
+
export PROXY_THINKING_GRAMMAR="${PROXY_THINKING_GRAMMAR:-off}"
|
|
84
|
+
export PROXY_THINKING_GRAMMAR_PATH="${PROXY_THINKING_GRAMMAR_PATH:-${ROOT_DIR}/tools/agents/config/thinking.gbnf}"
|
|
85
|
+
|
|
86
|
+
# ---------------------------------------------------------------------------
|
|
87
|
+
# Auto-detect context window from upstream llama-server /slots endpoint.
|
|
88
|
+
# Waits up to 60s for the server to be ready. Falls back to env var or 131072.
|
|
89
|
+
# This ensures the proxy always matches the server's actual per-slot context,
|
|
90
|
+
# even after server restarts with different --ctx-size / --parallel settings.
|
|
91
|
+
# ---------------------------------------------------------------------------
|
|
92
|
+
if [ "${PROXY_CONTEXT_WINDOW:-0}" = "0" ]; then
|
|
93
|
+
# Was an inline ${LLAMA_CPP_BASE/\/v1/} substitution, which strips the FIRST
|
|
94
|
+
# "/v1" anywhere in the string; llama_upstream_root strips only a trailing
|
|
95
|
+
# one. Same job, one rule.
|
|
96
|
+
SLOTS_URL="$(llama_upstream_root "$LLAMA_CPP_BASE")/slots"
|
|
97
|
+
echo "[proxy-startup] Detecting context window from ${SLOTS_URL}..."
|
|
98
|
+
for i in $(seq 1 30); do
|
|
99
|
+
CTX=$(curl -sf --max-time 2 -- "$SLOTS_URL" 2>/dev/null \
|
|
100
|
+
| python3 -c "import sys,json; print(json.load(sys.stdin)[0]['n_ctx'])" 2>/dev/null)
|
|
101
|
+
if [ -n "$CTX" ] && [ "$CTX" -gt 0 ]; then
|
|
102
|
+
export PROXY_CONTEXT_WINDOW="$CTX"
|
|
103
|
+
echo "[proxy-startup] Auto-detected context window: ${CTX} tokens"
|
|
104
|
+
break
|
|
105
|
+
fi
|
|
106
|
+
sleep 2
|
|
107
|
+
done
|
|
108
|
+
if [ "${PROXY_CONTEXT_WINDOW:-0}" = "0" ]; then
|
|
109
|
+
export PROXY_CONTEXT_WINDOW=131072
|
|
110
|
+
echo "[proxy-startup] WARNING: Could not detect context, using default: 131072"
|
|
111
|
+
fi
|
|
112
|
+
fi
|
|
113
|
+
|
|
114
|
+
cd "$ROOT_DIR"
|
|
115
|
+
|
|
116
|
+
# Startup resolution alone still strands a LONG-LIVED proxy: llama can move
|
|
117
|
+
# ports hours after the proxy came up. Watch it in the background and stop the
|
|
118
|
+
# proxy once the upstream has demonstrably moved, so the supervisor restarts it
|
|
119
|
+
# through resolution above. $$ is the proxy's PID after the exec below, and the
|
|
120
|
+
# watcher dies with it via the unit's control group.
|
|
121
|
+
#
|
|
122
|
+
# SUPERVISED ONLY. src/cli/proxy.ts also launches this script as a detached,
|
|
123
|
+
# unsupervised `spawn(...)` when the systemd unit is not installed (fresh
|
|
124
|
+
# installs, UAP_PROXY_NO_SYSTEMD=1, containers, bench hosts). There, stopping
|
|
125
|
+
# the proxy is not a restart — it is the end of the proxy, which is strictly
|
|
126
|
+
# worse than pointing at a dead upstream, and on a bench host it would score as
|
|
127
|
+
# model failure rather than infrastructure failure. systemd sets INVOCATION_ID,
|
|
128
|
+
# so use it as the supervisor probe. UAP_LLAMA_UPSTREAM_WATCH=on forces the
|
|
129
|
+
# watcher on (for another supervisor), =off disables it everywhere.
|
|
130
|
+
_watch="${UAP_LLAMA_UPSTREAM_WATCH:-auto}"
|
|
131
|
+
if command -v llama_upstream_watch >/dev/null 2>&1 &&
|
|
132
|
+
{ [ "$_watch" = "on" ] || { [ "$_watch" = "auto" ] && [ -n "${INVOCATION_ID:-}" ]; }; }; then
|
|
133
|
+
llama_upstream_watch "$LLAMA_CPP_BASE" "$$" &
|
|
134
|
+
fi
|
|
135
|
+
|
|
136
|
+
exec python3 tools/agents/scripts/anthropic_proxy.py
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -9,11 +9,17 @@ mod_path = Path(__file__).resolve().parents[3] / "tools" / "agents" / "scripts"
|
|
|
9
9
|
spec = importlib.util.spec_from_file_location("confidence_escalation", mod_path)
|
|
10
10
|
ce = importlib.util.module_from_spec(spec)
|
|
11
11
|
sys.modules["confidence_escalation"] = ce
|
|
12
|
-
import tempfile, os as _os2
|
|
13
|
-
|
|
12
|
+
import atexit, shutil, tempfile, os as _os2
|
|
13
|
+
_sig_dir = tempfile.mkdtemp(prefix="uap-sig-")
|
|
14
|
+
_os2.environ["UAP_RECIPE_SIGNAL_DIR"] = _sig_dir
|
|
14
15
|
# Isolate the (now auto-on) real-time adaptation dir to an empty temp so no test
|
|
15
16
|
# accidentally reads a stray ~/.cache adaptation signal from the dev machine.
|
|
16
|
-
|
|
17
|
+
_adapt_dir = tempfile.mkdtemp(prefix="uap-adapt-empty-")
|
|
18
|
+
_os2.environ["UAP_ADAPTATION_SIGNAL_DIR"] = _adapt_dir
|
|
19
|
+
# These leaked one dir per suite run into RAM-backed /tmp (26k accumulated by
|
|
20
|
+
# 2026-08-16 — the enforcer suite runs on every commit). Clean up on exit.
|
|
21
|
+
atexit.register(shutil.rmtree, _sig_dir, True)
|
|
22
|
+
atexit.register(shutil.rmtree, _adapt_dir, True)
|
|
17
23
|
spec.loader.exec_module(ce)
|
|
18
24
|
|
|
19
25
|
|