Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f8aa65362b | ||
|
|
0ce0391bb1 | ||
|
|
4611ea3578 | ||
|
|
63701fbebb | ||
|
|
e8a047eb14 | ||
|
|
0279210595 | ||
|
|
25f09e9b52 | ||
|
|
82f2825d3f | ||
|
|
0fec080b4d | ||
|
|
7424c6aefd |
@@ -125,8 +125,16 @@ steps:
|
||||
echo "POST pull -> HTTP $$code"
|
||||
[ "$$code" -ge 200 ] && [ "$$code" -lt 300 ] || { cat /tmp/pr-resp.json; exit 1; }
|
||||
NUM=$$(jq -r '.number' /tmp/pr-resp.json)
|
||||
# Merge without delete_branch_after_merge: run 10254 got HTTP 500 from that
|
||||
# call although the merge itself had landed. Verify the PR state instead of
|
||||
# trusting the status code, then drop the branch as a separate, best-effort step.
|
||||
code=$$(curl -s -o /tmp/merge-resp.json -w '%{http_code}' -X POST \
|
||||
-H "Authorization: token $$TOKEN" -H "Content-Type: application/json" \
|
||||
--data '{"Do":"merge","delete_branch_after_merge":true}' "$$API/pulls/$$NUM/merge")
|
||||
--data '{"Do":"merge"}' "$$API/pulls/$$NUM/merge")
|
||||
echo "POST merge #$$NUM -> HTTP $$code"
|
||||
[ "$$code" -ge 200 ] && [ "$$code" -lt 300 ] || { cat /tmp/merge-resp.json; echo "PR #$$NUM left open"; exit 1; }
|
||||
MERGED=$$(curl -s -H "Authorization: token $$TOKEN" "$$API/pulls/$$NUM" | jq -r '.merged')
|
||||
echo "PR #$$NUM merged: $$MERGED"
|
||||
[ "$$MERGED" = "true" ] || { cat /tmp/merge-resp.json; echo "PR #$$NUM left open"; exit 1; }
|
||||
code=$$(curl -s -o /tmp/del-resp.json -w '%{http_code}' -X DELETE -H "Authorization: token $$TOKEN" \
|
||||
"$$API/branches/$$(printf '%s' "$$BRANCH" | sed 's|/|%2F|g')")
|
||||
echo "DELETE branch $$BRANCH -> HTTP $$code (best effort)"
|
||||
|
||||
@@ -27,6 +27,8 @@ data:
|
||||
PAPERLESS_ALLOWED_HOSTS: "*"
|
||||
PAPERLESS_TIME_ZONE: "Europe/Berlin"
|
||||
PAPERLESS_OCR_LANGUAGE: "deu+eng"
|
||||
# Default is cpu_count / TASK_WORKERS, 20 on k8s-1; OCR memory scales with it.
|
||||
PAPERLESS_THREADS_PER_WORKER: "5"
|
||||
PAPERLESS_CONSUMER_RECURSIVE: "true"
|
||||
PAPERLESS_CONSUMER_DELETE_DUPLICATES: "true"
|
||||
PAPERLESS_CONSUMER_ENABLE_BARCODES: "true"
|
||||
@@ -273,7 +275,7 @@ spec:
|
||||
cpu: 200m
|
||||
memory: 512Mi
|
||||
limits:
|
||||
memory: 2Gi
|
||||
memory: 4Gi
|
||||
securityContext:
|
||||
runAsNonRoot: false
|
||||
allowPrivilegeEscalation: true
|
||||
|
||||
@@ -25,7 +25,7 @@ spec:
|
||||
type: RuntimeDefault
|
||||
containers:
|
||||
- name: redis
|
||||
image: redis:8.10.1-alpine # {"$imagepolicy": "flux-system:redis-paperless"}
|
||||
image: redis:8.10.2-alpine # {"$imagepolicy": "flux-system:redis-paperless"}
|
||||
args:
|
||||
- redis-server
|
||||
- --save
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
#!/bin/sh
|
||||
# Turn the TypeSafe picker's probability distribution into one model id.
|
||||
#
|
||||
# The judgment (how well each catalog model fits the task) comes from the
|
||||
# model; the policy lives here, in code: among the candidates whose probability
|
||||
# is within MARGIN of the best one, take the most recently released. Several
|
||||
# same-family models (Sonnet 4.5 / 4.6 / 5) split the probability for a
|
||||
# mid-weight task, and the newest of a comparable set is the one to run.
|
||||
#
|
||||
# Usage: choose-model.sh <catalog-table-file> < <systemone-response.json>
|
||||
# The table is fetch-models.sh --table output: id \t name \t released=YYYY-MM-DD ...
|
||||
# Prints the chosen id, or nothing when the answer is NONE / unusable.
|
||||
# Env: CHOOSE_MODEL_MARGIN (default 0.7): candidate if p >= MARGIN * p_best.
|
||||
|
||||
set -eu
|
||||
TABLE_FILE="${1:?usage: choose-model.sh <catalog-table-file> < response.json}"
|
||||
MARGIN="${CHOOSE_MODEL_MARGIN:-0.7}"
|
||||
|
||||
jq -r --rawfile table "$TABLE_FILE" --argjson margin "$MARGIN" '
|
||||
($table | split("\n") | map(select(length > 0) | split("\t")
|
||||
| {key: .[0], value: ((.[2:] | map(select(startswith("released="))) | .[0] // "released=") | ltrimstr("released="))})
|
||||
| from_entries) as $released
|
||||
| (.answers.model.probabilities // {}) | to_entries
|
||||
| map(select(.key != "NONE")) | sort_by(-.value)
|
||||
| if length == 0 then empty
|
||||
else (.[0].value) as $best
|
||||
| map(select(.value >= $margin * $best))
|
||||
| sort_by([($released[.key] // ""), .value])
|
||||
| .[-1].key
|
||||
end' 2>&1
|
||||
@@ -33,6 +33,42 @@ none() { log "$1"; exit 0; } # emit nothing -> caller uses CLI defaul
|
||||
TABLE="$(sh "$HERE/fetch-models.sh" --table || true)"
|
||||
[ -n "$TABLE" ] || none "no catalog available — caller will use the CLI default model"
|
||||
|
||||
# First choice: one TypeSafe (System One) Choice over the catalog. The task and
|
||||
# the catalog rows are state, every model id is an option with its catalog row
|
||||
# as the criterion, and the answer is typed — no prompt, no JSON parsing, and a
|
||||
# request that takes a fraction of a second instead of a claude run. The id is
|
||||
# still validated against the pristine catalog. Falls through to the claude
|
||||
# picker when the key is absent or the request fails.
|
||||
if [ -n "${TYPESAFE_API_KEY:-}" ]; then
|
||||
REQ="$(printf '%s\n' "$TABLE" | jq -R -s --arg task "$GUIDANCE" '
|
||||
split("\n") | map(select(length > 0) | split("\t") | {id: .[0], name: .[1], info: (.[2:] | join(", "))})
|
||||
| {model: "jev-latest",
|
||||
state: {task: $task, models: .},
|
||||
questions: {model: {
|
||||
type: "choice",
|
||||
instructions: "Which model in `models` should run `task`? Match the model to the task: not the largest or newest flagship for trivial or mechanical work, not a minimal model for hard, multi-step or security-sensitive work; prefer a newer model when otherwise comparable. Consider every listed model, including unfamiliar ones. Pick NONE only if no listed model fits.",
|
||||
criteria: ((map({key: .id, value: "\(.name); \(.info)"}) | from_entries) + {NONE: "No listed model fits the task"})}}}')"
|
||||
if [ -n "${PICK_MODEL_TYPESAFE_DRYRUN:-}" ]; then printf '%s\n' "$REQ"; exit 0; fi
|
||||
RESP="$(printf '%s' "$REQ" | curl -sS --max-time 30 -X POST https://api.typesafe.ai/v1/systemone \
|
||||
-H "Authorization: Bearer $TYPESAFE_API_KEY" -H 'Content-Type: application/json' --data-binary @- || true)"
|
||||
# Policy in code (choose-model.sh): among candidates within a margin of the
|
||||
# best probability, the most recently released wins. Same-family models split
|
||||
# the probability for mid-weight tasks (live catalog: Sonnet 4.5 chosen at
|
||||
# 0.23 over Sonnet 4.6).
|
||||
TABLE_FILE="$(mktemp)"
|
||||
printf '%s\n' "$TABLE" > "$TABLE_FILE"
|
||||
CHOICE="$(printf '%s' "$RESP" | sh "$HERE/choose-model.sh" "$TABLE_FILE" | head -1)"
|
||||
rm -f "$TABLE_FILE"
|
||||
TOP="$(printf '%s' "$RESP" | jq -r '(.answers.model.probabilities // {}) | to_entries | sort_by(-.value) | .[0:4] | map("\(.key)=\(.value)") | join(" ")' 2>&1 | head -1)"
|
||||
VALID="$(printf '%s\n' "$TABLE" | sh "$HERE/valid-model.sh" "$CHOICE")"
|
||||
if [ -n "$VALID" ]; then
|
||||
log "typesafe chose '$VALID' (top: ${TOP:-?}) for: $GUIDANCE"
|
||||
printf '%s\n' "$VALID"
|
||||
exit 0
|
||||
fi
|
||||
log "typesafe picker returned '${CHOICE:-<empty>}' (rejected, NONE, or request failed) — falling back to the claude picker"
|
||||
fi
|
||||
|
||||
PROMPT="$(cat <<EOF
|
||||
You are a model-selection agent for an automated CI job. From the models
|
||||
currently available (listed below with their capabilities), choose the single
|
||||
|
||||
@@ -84,9 +84,40 @@ else
|
||||
printf 'FAIL recorded fixture: opus=[%s] haiku=[%s]\n' "$opusline" "$haikuline"; fails=$((fails + 1))
|
||||
fi
|
||||
|
||||
|
||||
# TypeSafe path of pick-model.sh: the request offers exactly the catalog ids plus
|
||||
# NONE as options, carries the task as state, and never sends anything else.
|
||||
printf '%s' '{"data":[{"type":"model","id":"claude-opus-5","display_name":"Claude Opus 5","created_at":"2026-07-24T00:00:00Z","max_input_tokens":1000000,"max_tokens":128000},{"type":"model","id":"claude-haiku-4-5-20251001","display_name":"Claude Haiku 4.5","created_at":"2025-10-15T00:00:00Z","max_input_tokens":200000,"max_tokens":64000}],"has_more":false}' > "$FX"
|
||||
req="$(FETCH_MODELS_BODY_FILE="$FX" TYPESAFE_API_KEY=dry PICK_MODEL_TYPESAFE_DRYRUN=1 sh "$HERE/pick-model.sh" "a task; with \"quotes\" and $ signs" 2>/dev/null || true)"
|
||||
keys="$(printf '%s' "$req" | jq -r '.questions.model.criteria | keys | join(",")' 2>/dev/null || true)"
|
||||
task="$(printf '%s' "$req" | jq -r '.state.task' 2>/dev/null || true)"
|
||||
if [ "$keys" = "NONE,claude-haiku-4-5-20251001,claude-opus-5" ] && [ "$task" = 'a task; with "quotes" and $ signs' ]; then
|
||||
printf 'ok typesafe request offers catalog ids + NONE and carries the task verbatim\n'
|
||||
else
|
||||
printf 'FAIL typesafe request: keys [%s] task [%s]\n' "$keys" "$task"; fails=$((fails + 1))
|
||||
fi
|
||||
|
||||
# choose-model.sh: newest among comparable candidates, best otherwise, NONE -> nothing.
|
||||
TB="$(mktemp)"
|
||||
printf '%s\n' 'claude-sonnet-4-5 Claude Sonnet 4.5 released=2025-09-29 context=200000' \
|
||||
'claude-sonnet-4-6 Claude Sonnet 4.6 released=2026-02-17 context=200000' \
|
||||
'claude-opus-5 Claude Opus 5 released=2026-07-24 context=1000000' \
|
||||
'claude-haiku-4-5 Claude Haiku 4.5 released=2025-10-15 context=200000' > "$TB"
|
||||
choose() { printf '%s' "$1" | sh "$HERE/choose-model.sh" "$TB" || true; }
|
||||
got="$(choose '{"answers":{"model":{"probabilities":{"claude-sonnet-4-5":0.30,"claude-sonnet-4-6":0.25,"claude-opus-5":0.10,"NONE":0.05}}}}')"
|
||||
if [ "$got" = "claude-sonnet-4-6" ]; then printf 'ok newer sibling within margin wins\n'; else printf 'FAIL comparable: got [%s]\n' "$got"; fails=$((fails + 1)); fi
|
||||
got="$(choose '{"answers":{"model":{"probabilities":{"claude-opus-5":0.90,"claude-haiku-4-5":0.05,"claude-sonnet-4-6":0.04,"NONE":0.01}}}}')"
|
||||
if [ "$got" = "claude-opus-5" ]; then printf 'ok clear best is kept\n'; else printf 'FAIL clear best: got [%s]\n' "$got"; fails=$((fails + 1)); fi
|
||||
got="$(choose '{"answers":{"model":{"probabilities":{"claude-haiku-4-5":0.60,"claude-sonnet-4-5":0.35,"NONE":0.05}}}}')"
|
||||
if [ "$got" = "claude-haiku-4-5" ]; then printf 'ok older best outside margin is kept\n'; else printf 'FAIL margin: got [%s]\n' "$got"; fails=$((fails + 1)); fi
|
||||
got="$(choose '{"answers":{"model":{"probabilities":{"NONE":1.0}}}}')"
|
||||
if [ -z "$got" ]; then printf 'ok NONE yields nothing\n'; else printf 'FAIL NONE: got [%s]\n' "$got"; fails=$((fails + 1)); fi
|
||||
rm -f "$TB"
|
||||
|
||||
if [ "$fails" -eq 0 ]; then
|
||||
echo "All model-selection tests passed."
|
||||
else
|
||||
echo "$fails test(s) FAILED."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
Reference in New Issue
Block a user