Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
172 commits
Select commit Hold shift + click to select a range
423353c
KIL-715 Allow renaming eval score names for spec evals
sfierro Jun 16, 2026
840f53d
Dedupe shared custom_setter invocations in EditDialog
sfierro Jun 16, 2026
9005313
Pivot to non-destructive display names for eval scores
sfierro Jun 19, 2026
b251648
Label the edit field 'Score Name' instead of 'Score Display Name'
sfierro Jun 19, 2026
4f6a4ab
docs: fix CONTRIBUTING links after the move to .github
pratikgx Oct 3, 2026
4f9adac
Save the judge trace on eval runs
sfierro Oct 16, 2026
ede01eb
Show the judge trace on LLM judge eval results
sfierro Oct 16, 2026
19033f9
KIL-851 Default judge prompt judges the trace, not a stale task descr…
sfierro Oct 16, 2026
3b47d34
Read text parts of list content in the trace formatter
sfierro Oct 16, 2026
7b23d5f
fix: send strict: false on user tool definitions
scosman Oct 19, 2026
da749f8
chore(deps): bump mcp from 1.10.1 to 1.30.0
dependabot[bot] Oct 19, 2026
18a1fec
fix: keep the eval page's right column at the standard side-column width
sfierro Oct 19, 2026
4881e03
Load the judge trace on demand and show it when testing a judge
sfierro Oct 19, 2026
cc5719a
Add agent policy annotation for the judge trace endpoint
sfierro Oct 19, 2026
4e1c388
Merge pull request #1938 from Kiln-AI/sfierro/KIL-850
sfierro Oct 19, 2026
37f2ecd
Merge pull request #1946 from Kiln-AI/KIL-848/eval-formatting-tags
sfierro Oct 19, 2026
86abf59
Merge origin/main into sfierro/KIL-715-eval-score-name-edit
sfierro Oct 19, 2026
c4c109a
Show score display names on the task Compare page; polish score edit …
sfierro Oct 19, 2026
758216b
Merge origin/main into sfierro/KIL-715-eval-score-name-edit
sfierro Oct 19, 2026
52e1e18
fix: stop make dev auto-reload from firing on .venv and .worktrees ch…
scosman Oct 19, 2026
c53e95d
docs: note the dev server's reload excludes in the startup table
scosman Oct 19, 2026
f96d66f
Address deep CR findings for eval score display names
sfierro Oct 19, 2026
96c025b
refactor: build the reload excludes inline in dev_server.py
scosman Oct 19, 2026
c42bbe5
Rename eval scores from the Compare page instead of the spec Edit dialog
sfierro Oct 19, 2026
afe516f
docs: score JSON keys come from the score name, not its display name
sfierro Oct 19, 2026
d77e8ca
Word the default judge prompt so it reads right in both turn modes
sfierro Oct 19, 2026
635346d
Judge runs saved without a trace from their input and output
sfierro Oct 19, 2026
3658b01
Address CodeRabbit review on score display names
sfierro Oct 19, 2026
5711318
Merge pull request #1944 from Kiln-AI/claude/tender-hopper-li0w93
scosman Oct 19, 2026
88eb0f1
Eval drives: turn on automatic prompt caching
leonardmq Oct 20, 2026
efff7ec
Merge pull request #1949 from Kiln-AI/leonard/eval-drive-prompt-caching
leonardmq Oct 20, 2026
284aa3c
Bump FastAPI to 0.143.0 and Starlette to 1.7.0
chiang-daniel Oct 20, 2026
b4a60a9
chore(deps): bump aiohttp from 3.14.3 to 3.14.4
dependabot[bot] Oct 20, 2026
a32c122
Declare a starlette floor on kiln-server
chiang-daniel Oct 20, 2026
b0329e8
chore: add Minimax M3.1 Flash and two DeepSeek V4.1 Flash provider ba…
claude Oct 20, 2026
c5a30d4
chore: drop free-tier Minimax M3.1 Flash from model list
claude Oct 20, 2026
213fc24
docs: tell the model skills never to add free-tier slugs
claude Oct 20, 2026
725b340
Accept no-arg tool schemas, check run bindings, and send unparseable …
sfierro Oct 19, 2026
59fba0d
fix: reject a 'context' tool argument before the run signature check
sfierro Oct 19, 2026
95a1914
Merge pull request #1937 from Kiln-AI/sfierro/tool-call-error-followups
sfierro Oct 20, 2026
e8e5949
Reject whitespace-only score names in the rename dialog
sfierro Oct 20, 2026
46ca267
Show the rename pencil only on hover of the score label
sfierro Oct 20, 2026
4b8f254
Merge pull request #1948 from Kiln-AI/sfierro/KIL-715-eval-score-name…
sfierro Oct 20, 2026
1ab2ace
Merge branch 'main' into sfierro/KIL-851
sfierro Oct 20, 2026
867adf6
Merge pull request #1945 from Kiln-AI/dependabot/uv/mcp-1.30.0
chiang-daniel Oct 20, 2026
789fc4a
Run the MCP integration tests against a local echo server
chiang-daniel Oct 20, 2026
968dd4e
Harden the echo server fixture
chiang-daniel Oct 20, 2026
0faf59e
perf: attach the loaded parent when listing a model's children
sfierro Oct 20, 2026
130f711
perf: score all run configs in one request and keep slow reads off th…
sfierro Oct 20, 2026
91c63d9
feat: add an eval tag store counting tags across a task's evals
sfierro Oct 20, 2026
84873e4
feat: tag controls gain an eval suggestion type
sfierro Oct 20, 2026
a3388d0
fix: suggest eval tags, not task run tags, when tagging evals
sfierro Oct 20, 2026
ccd12b1
Cover the stdio tool call and the env vars a local server needs
chiang-daniel Oct 20, 2026
2b14b7d
fix: give cached children the cached readonly parent, never the caller's
sfierro Oct 20, 2026
5c96fb4
fix: retry torn reads in worker threads and move batch scores under /…
sfierro Oct 20, 2026
b0d622f
fix: correct the eval tag set error message in TagDropdown
sfierro Oct 20, 2026
e986fe1
fix: keep eval tag suggestions current
sfierro Oct 20, 2026
0774bda
docs: say to read large project data in a worker thread from async ha…
sfierro Oct 20, 2026
0e0c258
test: don't rely on the model cache being enabled in parent tests
sfierro Oct 20, 2026
210e58a
Make the echo server fixture survive its own failure modes
chiang-daniel Oct 20, 2026
2949ab5
fix: stop the compare page re-requesting all eval scores in a loop
sfierro Oct 20, 2026
96cafab
Merge pull request #1950 from Kiln-AI/dependabot/uv/aiohttp-3.14.4
chiang-daniel Oct 21, 2026
e4246e3
Merge pull request #1952 from Kiln-AI/model-sweep/adds-2026-10-20
tawnymanticore Oct 21, 2026
da4a25f
test: cover default judge types, skipped and incomplete evals, and wa…
sfierro Oct 21, 2026
c492ab9
fix: store eval tag counts only for the task they were loaded for
sfierro Oct 21, 2026
aed361a
Rename the judge's system_prompt variable and tighten the default jud…
sfierro Oct 21, 2026
1ebd2a2
Cloudflare: thinking levels for DeepSeek V4.1 Flash 1017, GLM 5.3 and…
leonardmq Oct 22, 2026
2d11f93
Test the Cloudflare thinking levels for DeepSeek V4.1 Flash 1017, GLM…
leonardmq Oct 22, 2026
2006877
Cloudflare: keep reasoning_capable off for DeepSeek V4.1 Flash 1017, …
leonardmq Oct 22, 2026
1c8fb78
Run the thinking-level paid test on Cloudflare
leonardmq Oct 22, 2026
17b4ccc
Speed up eval job requests on large tasks
leonardmq Oct 22, 2026
8bcabf3
chore: add a checker for Cloudflare thinking levels against the catalog
claude Oct 22, 2026
fca3faf
docs: require catalog-exact thinking levels on Cloudflare entries
claude Oct 22, 2026
ab420f4
Merge pull request #1960 from Kiln-AI/leonard/cloudflare-thinking-levels
leonardmq Oct 22, 2026
16fc51b
Review fixes: one parse per template check, explicit trace-index seed
leonardmq Oct 22, 2026
d90a3da
Merge remote-tracking branch 'origin/main' into skills/cloudflare-thi…
claude Oct 22, 2026
f6ed377
docs: add thinking levels to the net-new provider checklist
claude Oct 22, 2026
f1aa448
Merge pull request #1956 from Kiln-AI/sfierro/slow-load-compare-run-c…
sfierro Oct 22, 2026
2560216
chore: set Cloudflare thinking levels from the catalog's supported ef…
claude Oct 22, 2026
0853812
Merge remote-tracking branch 'origin/main' into skills/cloudflare-thi…
claude Oct 22, 2026
5e711dd
chore: check OpenRouter thinking levels against its effort lists
claude Oct 22, 2026
d0b5960
docs: note that Fireworks and Together accept unsupported efforts sil…
claude Oct 22, 2026
1d53db4
Default judge intro says what the trace holds and how to grade
sfierro Oct 22, 2026
e9f6549
fix: address review findings on the thinking-level checker and skills
claude Oct 22, 2026
0ec1867
docs: limit the sweep's thinking-level fixes to errors and respect ju…
claude Oct 22, 2026
60836bc
Judge intro names the score criteria when the prompt has them
sfierro Oct 22, 2026
fcde494
Merge pull request #1939 from Kiln-AI/sfierro/KIL-851
sfierro Oct 22, 2026
07f4f1a
Merge remote-tracking branch 'origin/main' into leonard/eval-job-perf
leonardmq Oct 22, 2026
02b1719
Lock the trace-index seed; tighten comments
leonardmq Oct 22, 2026
66eedc3
Fireworks: thinking levels for Muse Spark Open and GLM 5.3 Flash
leonardmq Oct 22, 2026
6f2ee37
Test the Fireworks thinking levels for Muse Spark Open and GLM 5.3 Flash
leonardmq Oct 22, 2026
da1c761
Merge pull request #1962 from Kiln-AI/skills/cloudflare-thinking-levels
tawnymanticore Oct 22, 2026
923b9c9
Merge pull request #1947 from Kiln-AI/claude/awesome-goodall-n891wn
scosman Oct 22, 2026
e15bf3c
Remove unused copilot types, a prompt helper and the workflow URL param
chiang-daniel Oct 22, 2026
7ae608f
Keep annotated re-drive runs and delete early-ended conversations
chiang-daniel Oct 22, 2026
9726a85
Drop history wording from the re-drive cleanup docstrings
chiang-daniel Oct 22, 2026
173e6af
Release the git sync write lock when a waiting save is cancelled
chiang-daniel Oct 22, 2026
b09a23f
Wait for the release callback instead of sleeping in the lock test
chiang-daniel Oct 22, 2026
e1522e6
Assert the cancelled waiter took the lock in the lock release test
chiang-daniel Oct 22, 2026
309293e
Merge pull request #1951 from Kiln-AI/dchiang/KIL-853/fastapi-starlet…
chiang-daniel Oct 23, 2026
c66200f
Merge pull request #1967 from Kiln-AI/dchiang/KIL-829/git-sync-cancel…
chiang-daniel Oct 23, 2026
c619025
Reject spec names whose eval tags are taken, and prefill a free name
chiang-daniel Oct 23, 2026
eabe16e
Test that the free-name route maps a name conflict to a 409
chiang-daniel Oct 23, 2026
d97531c
Keep the spec name resolver's docstring to its contract
chiang-daniel Oct 23, 2026
b3512f6
Merge origin/main into leonard/fireworks-thinking-levels
leonardmq Oct 23, 2026
7c07a4d
chore: add Minimax M3.1 Flash and Mercury 3 to model list
claude Oct 23, 2026
8f39e07
chore: add Fireworks provider to Minimax M3.1 Flash
claude Oct 23, 2026
85544f6
chore: drop banner comments from the new model entries
claude Oct 23, 2026
125646e
Merge branch 'model-sweep/adds-2026-10-23' into model-sweep/discuss-m…
claude Oct 23, 2026
ae9b667
Seed one trace index of a task at a time, process-wide
leonardmq Oct 23, 2026
a585075
Share the eval trace index across runners on one task
leonardmq Oct 23, 2026
6c5bd00
Hold the trace-index seed lock in the shared task state
leonardmq Oct 23, 2026
c773ca2
Fireworks Muse Spark Open: whitelist reasoning_effort so LiteLLM does…
leonardmq Oct 23, 2026
66579a7
Keep a key's lock exclusive when its holder fails
leonardmq Oct 23, 2026
2a63c44
Catch asyncio.TimeoutError in the lock tests
leonardmq Oct 23, 2026
add8464
Fireworks Muse Spark Open: drop the Extra High thinking level
leonardmq Oct 23, 2026
68011a0
Fireworks GLM 5.3 Flash: drop the Medium thinking level
leonardmq Oct 23, 2026
928fdba
chore(deps): bump undici from 7.30.0 to 7.30.1 in /app/web_ui
dependabot[bot] Oct 24, 2026
f716012
Allow world tools in run configs created through the API
leonardmq Oct 25, 2026
389f72e
Let an eval job drive items without judging them
leonardmq Oct 25, 2026
aa5488f
Address review: synthetic-user spend, world-version reuse, comment refs
leonardmq Oct 25, 2026
00811ae
Test world tools for a task without a project; describe the runtime t…
leonardmq Oct 25, 2026
6a7a49e
Seed the trace index only when a run has task_run_eval jobs
leonardmq Oct 25, 2026
beb989f
Replace a shared trace entry whose file is gone when seeding
leonardmq Oct 25, 2026
0125c67
Keep a trace a job filed while a seed checked the dead entry
leonardmq Oct 25, 2026
7f24bcb
Seed the trace index with the newest trace of each key
leonardmq Oct 25, 2026
0445315
Share eval traces through a registry the eval job worker owns
leonardmq Oct 25, 2026
ea6c2d3
ci: pin Playwright job to ubuntu-24.04
leonardmq Oct 25, 2026
a53a0f4
test: wait for the tray bus name to be released instead of racing the…
leonardmq Oct 25, 2026
98a865d
Run the Playwright CI job on ubuntu-24.04
leonardmq Oct 25, 2026
e193de3
Pin together<3 in v0.19 backwards-compat test script
leonardmq Oct 25, 2026
173968f
chore: mark DeepSeek V4 Flash 0731 on Together AI deprecated in model…
claude Oct 26, 2026
a5bcccc
chore: add Kimi K3.1 Pro to model list (openrouter)
claude Oct 26, 2026
194722a
chore: drop redundant heading comment on Kimi K3.1 Pro entry
claude Oct 26, 2026
e1a8a61
Allow world tools in code tool allowlists created through the API
leonardmq Oct 26, 2026
cd3d285
Narrow world tools in code tool allowlists to the allowlist check
leonardmq Oct 26, 2026
60446ac
Leave run config validation untouched; only skip world tools in allow…
leonardmq Oct 26, 2026
94739c9
Refuse a world tool whose name collides with another allowlisted tool
leonardmq Oct 26, 2026
afabac5
Record why a world tool is unavailable in the tool call log
leonardmq Oct 26, 2026
fb84695
Merge pull request #1976 from Kiln-AI/leonard/ci-pin-playwright-runner
leonardmq Oct 26, 2026
1a3d99a
Merge pull request #1977 from Kiln-AI/leonard/pin-together-v019-compa…
leonardmq Oct 26, 2026
1520394
Merge remote-tracking branch 'origin/main' into leonard/eval-job-driv…
leonardmq Oct 26, 2026
0add9ee
Merge pull request #1980 from Kiln-AI/leonard/code-tool-world-tools
leonardmq Oct 26, 2026
dd72481
Merge pull request #1955 from Kiln-AI/dchiang/KIL-mcp-integration-tests
chiang-daniel Oct 26, 2026
c776a06
Merge pull request #1965 from Kiln-AI/dchiang/KIL-813/remove-eval-bui…
chiang-daniel Oct 26, 2026
9217850
Merge pull request #1966 from Kiln-AI/dchiang/KIL-828/redrive-cleanup…
chiang-daniel Oct 26, 2026
4f32aaf
Merge pull request #1974 from Kiln-AI/leonard/run-config-world-tools
leonardmq Oct 26, 2026
8c47c94
chore: add GPT-6.1 Terra and GPT-6.1 Terra Pro to model list
claude Oct 27, 2026
8051b42
chore: mark Arcee Trinity Large (Thinking) on OpenRouter deprecated i…
claude Oct 27, 2026
6dedc94
Merge pull request #1963 from Kiln-AI/leonard/fireworks-thinking-levels
leonardmq Oct 27, 2026
68adca7
Merge pull request #1975 from Kiln-AI/leonard/eval-job-drive-only
leonardmq Oct 27, 2026
36e13a4
Merge origin/main into leonard/eval-job-perf
leonardmq Oct 27, 2026
0153263
Merge pull request #1972 from Kiln-AI/dependabot/npm_and_yarn/app/web…
chiang-daniel Oct 27, 2026
a28afc3
Merge pull request #1893 from pratikgx/fix-contributing-links
scosman Oct 27, 2026
d2037dd
Merge pull request #1961 from Kiln-AI/leonard/eval-job-perf
leonardmq Oct 27, 2026
11af30b
chore: add Ternary Bonsai 2 9B and Ultramarine 1 Flash to model list
claude Oct 28, 2026
d921372
chore: drop MIME section comments on the two new model entries
claude Oct 28, 2026
3f8f4a5
Merge pull request #1969 from Kiln-AI/model-sweep/adds-2026-10-23
tawnymanticore Oct 28, 2026
bab1ed3
Merge pull request #1983 from Kiln-AI/model-sweep/deprecations-2026-1…
tawnymanticore Oct 28, 2026
757ddf0
Merge remote-tracking branch 'origin/main' into model-sweep/discuss-m…
claude Oct 28, 2026
2161763
Merge pull request #1968 from Kiln-AI/dchiang/KIL-782/reject-duplicat…
chiang-daniel Oct 28, 2026
f5b13a7
Merge pull request #1970 from Kiln-AI/model-sweep/discuss-minimax-m3-…
tawnymanticore Oct 28, 2026
883fcbd
Merge origin/main into model-sweep/discuss-deepseek-v4-flash-together…
claude Oct 28, 2026
da292e3
Merge origin/main into model-sweep/adds-2026-10-26
claude Oct 28, 2026
645514a
chore: drop MIME group labels from the GPT-6.1 Terra entries
claude Oct 28, 2026
a9d303a
Merge origin/main into model-sweep/adds-2026-10-28
claude Oct 28, 2026
a52358f
Merge pull request #1982 from Kiln-AI/model-sweep/discuss-gpt-6-1-ter…
tawnymanticore Oct 28, 2026
d428ee9
Merge pull request #1979 from Kiln-AI/model-sweep/adds-2026-10-26
tawnymanticore Oct 28, 2026
7c8e7a6
Merge pull request #1978 from Kiln-AI/model-sweep/discuss-deepseek-v4…
tawnymanticore Oct 28, 2026
40ddbd8
Merge pull request #1985 from Kiln-AI/model-sweep/adds-2026-10-28
tawnymanticore Oct 28, 2026
d2a8382
chore: mark o4 Mini and Qwen 3 Next 80B Thinking on OpenRouter deprec…
claude Oct 29, 2026
336cdb5
Merge pull request #1986 from Kiln-AI/model-sweep/deprecations-2026-1…
tawnymanticore Oct 30, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 19 additions & 11 deletions .agents/scripts/provider_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,12 +155,16 @@ def get_api_key(env_var: str) -> str | None:
# ---------------------------------------------------------------------------


def fetch_openrouter_models() -> list[dict]:
"""Returns OpenRouter's raw model records. No auth required."""
return fetch_json("https://openrouter.ai/api/v1/models")["data"]


def fetch_openrouter() -> tuple[set[str], dict[str, str]]:
"""Returns (model_ids, expiring_dict) from OpenRouter. No auth required."""
data = fetch_json("https://openrouter.ai/api/v1/models")
ids: set[str] = set()
expiring: dict[str, str] = {}
for m in data["data"]:
for m in fetch_openrouter_models():
ids.add(m["id"])
if m.get("expiration_date"):
expiring[m["id"]] = m["expiration_date"]
Expand Down Expand Up @@ -262,7 +266,7 @@ def fetch_vertex_with_aliases(project_id: str) -> set[str]:
return with_aliases


def _cloudflare_property(model: dict, property_id: str) -> Any:
def cloudflare_property(model: dict, property_id: str) -> Any:
for prop in model.get("properties") or []:
if prop.get("property_id") == property_id:
return prop.get("value")
Expand Down Expand Up @@ -331,7 +335,7 @@ def cloudflare_availability(
if task.get("name") != CLOUDFLARE_TEXT_GENERATION_TASK:
continue
name = model["name"]
planned = _cloudflare_property(model, "planned_deprecation_date")
planned = cloudflare_property(model, "planned_deprecation_date")
if planned:
deprecation_dates[name] = str(planned)
try:
Expand All @@ -343,19 +347,23 @@ def cloudflare_availability(
return available, deprecation_dates


def fetch_cloudflare(
api_key: str | None, account_id: str | None
) -> tuple[set[str], dict[str, str]]:
"""Returns (model_ids, deprecation_dates) for Cloudflare Workers AI.
def fetch_cloudflare_models(api_key: str | None, account_id: str | None) -> list[dict]:
"""Returns Cloudflare's raw model records, with their properties.

Uses the authenticated models-search API when both credentials are set,
otherwise the public catalog JSON. Never use LiteLLM's Cloudflare catalog:
it is months stale.
"""
if api_key and account_id:
models = _fetch_cloudflare_search(api_key, account_id)
else:
models = _fetch_cloudflare_public()
return _fetch_cloudflare_search(api_key, account_id)
return _fetch_cloudflare_public()


def fetch_cloudflare(
api_key: str | None, account_id: str | None
) -> tuple[set[str], dict[str, str]]:
"""Returns (model_ids, deprecation_dates) for Cloudflare Workers AI."""
models = fetch_cloudflare_models(api_key, account_id)
return cloudflare_availability(models, date.today())


Expand Down
44 changes: 38 additions & 6 deletions .agents/skills/claude-maintain-models/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ Run this check on **every invocation** of the skill, regardless of whether you'r

3. **If a lagging provider now supports a recently-added model that isn't yet in its `KilnModel` entry**, flag it to the user and propose either bundling the provider addition into the current change or opening a separate PR. Do not silently add it.

4. **Run the Cloudflare thinking-level checker** (see [Cloudflare thinking levels](#cloudflare-thinking-levels)). It checks every Cloudflare entry, not only the recent ones, because Cloudflare edits the effort lists of models Kiln already ships. Treat each ❌ finding like a backfill: fix the entry in the current change or propose a separate PR.

---

## Phase 2 – Gather Context
Expand Down Expand Up @@ -455,6 +457,7 @@ Replace the `` `Insert AI summary of PR using .agents/skills/open-pr/SKILL.md` `
- [ ] Flags inherited from predecessor and adjusted for quirks
- [ ] `reasoning_capable` defaulted to `False` for adaptive-reasoning models (only `True` for always-emits-reasoning models — see [Reasoning Capable Default](#reasoning-capable-default))
- [ ] Thinking levels configured if model supports reasoning effort (see [Thinking Levels Reference](#thinking-levels-reference))
- [ ] Cloudflare and OpenRouter: `check_thinking_levels.py` reports no ❌ finding (see [Cloudflare thinking levels](#cloudflare-thinking-levels) and [OpenRouter (general)](#openrouter-general))
- [ ] Every `suggested_for_evals` entry with thinking levels defaults to `"medium"` (or `"low"`), never `"none"` (see [Judge Models Must Reason by Default](#judge-models-must-reason-by-default))
- [ ] Preserve existing comments from predecessor (e.g. reasoning notes, MIME type groupings)
- [ ] Zero-sum applied if model is suggested for evals/data gen
Expand Down Expand Up @@ -567,6 +570,15 @@ model.
parametrized cases
- `.agents/scripts/provider_utils.py` — add the provider's model-catalog
endpoint so agent tooling can enumerate its models
- Thinking levels — find out how the provider takes reasoning effort, and
where it publishes the values each model accepts. Add a quirks section
that names that source and says thinking levels are required wherever
it lists values. If the provider silently remaps unsupported values,
add the provider to `check_thinking_levels.py`, because a
paid test cannot see a remap.
- `model_adapters/test_thinking_level_paid.py` — add the provider to the
provider list of `test_thinking_level_reasoning_content`, so each of its
thinking levels gets a paid test case

**Known gap:** the generated Copilot API client
(`app/desktop/studio_server/api_client/`) mirrors the remote Copilot service's
Expand Down Expand Up @@ -608,10 +620,16 @@ plumbing PR must not skip them.

### OpenRouter (general)
- Slugs: `vendor/model-name`
- Never add a `:free` slug. Free tiers have low rate limits and are temporary. If OpenRouter serves a model only as `:free`, wait for a paid slug.
- Reasoning models: may need `require_openrouter_reasoning=True`
- Some models: `openrouter_skip_required_parameters=True`
- Logprobs: `logprobs_openrouter_options=True` if supported
- Always `multimodal_requires_pdf_as_image=True` (OpenRouter's PDF routing breaks LiteLLM)
- Thinking levels: offer only values in the model's `reasoning.supported_efforts` (see [Thinking Levels Reference](#thinking-levels-reference)). OpenRouter maps an unlisted effort to a listed one, so an extra level silently runs a different effort. When the model has no `reasoning.supported_efforts`, OpenRouter turns the effort into a thinking-token budget for Claude, so `CLAUDE_OPENROUTER_THINKING_LEVELS` (with Minimal) fits only those Claude models. `check_thinking_levels.py` checks every OpenRouter entry; a missing level list on OpenRouter is a note, because Kiln leaves levels off some models on purpose.

### Fireworks AI and Together AI: thinking levels

Neither publishes the efforts each model accepts, and both can accept an unsupported effort without an error. Fireworks takes `xhigh`, `max` and `minimal` on GLM 5.3 with HTTP 200. Together took a made-up effort on Nemotron 3.5, but Muse Spark Open's chat template rejects one. A paid test cannot see this. Add thinking levels on these providers only when the vendor documents the levels for that model (the model card or its chat template), and name that source in the PR. OpenAI and Anthropic return a 400 that names the supported levels, so their paid per-level test catches a wrong level.

### Featherless AI

Expand Down Expand Up @@ -651,10 +669,25 @@ Runs through LiteLLM's generic `openai/` route against Cloudflare's OpenAI-compa
| `function_calling: "true"` | leave `supports_function_calling` at its default; set `False` if the tool tests fail |
| `vision: "true"` | `supports_vision`, `multimodal_capable`, MIME types TXT/MD/JPG/PNG; start broad, narrow on 400s. Don't set `supports_doc_extraction`: the document extractor doesn't send the AI Gateway header, so extraction would bypass the user's gateway |
| `reasoning: "true"` | keep `reasoning_capable=False` (see [Reasoning Capable Default](#reasoning-capable-default)); LiteLLM already reads `reasoning_content` |
| `reasoning_effort.supported_efforts` | source for `available_thinking_levels` if you add them; `mandatory: true` means reasoning cannot be turned off. Cloudflare edits these, so re-read on every run |
| `reasoning_effort.supported_efforts` present | **required:** set `available_thinking_levels` to exactly these values, and `default_thinking_level` to `default_effort`. See [Cloudflare thinking levels](#cloudflare-thinking-levels) |
| `reasoning_effort` without `supported_efforts` | no thinking levels; the catalog does not say which values work |
| `require_workers_paid: "true"` | no Kiln field. Free-plan users get 403 / code 5035. Mention it in the PR |
| `planned_deprecation_date` present | do **not** add the model |

- <a id="cloudflare-thinking-levels"></a>**Thinking levels come from the catalog, and only from the catalog.** Kiln sends the level as `reasoning_effort`. For each model, `reasoning_effort` in the catalog lists `supported_efforts`, a `default_effort`, and `normalizes_to`: the values that Cloudflare silently changes to another effort. A remapped value returns HTTP 200, so a live call or a paid test cannot find it. Example: GLM 5.3 maps `medium` to `max`, so a "Medium" option runs the most expensive effort.
- Offer only values in `supported_efforts`, and each of them unless a completed paid test shows that the level does not work. The list is not always right: on DeepSeek V4 Pro 0813, DeepSeek V4 Flash 0731 and GLM 5.2, `none` still returns reasoning, so those entries have no Off level. A timeout, a rate limit or a credential error proves nothing about the level: keep the level and report it as untested. Do not copy the levels of the same model on another provider.
- Labels: `none` → `"Off"`, `low` → `"Low"`, `medium` → `"Medium"`, `high` → `"High"`, `xhigh` → `"Extra High"`, `max` → `"Max"`. List them from least to most effort.
- `default_thinking_level` is `default_effort`, unless the entry is suggested for evals (see [Judge Models Must Reason by Default](#judge-models-must-reason-by-default)).
- `mandatory: true` means `none` is not in the list; do not add an Off level.
- Reuse a constant when the values match exactly; else name a new one `{MODEL}_CLOUDFLARE_THINKING_LEVELS`.
- Cloudflare edits these lists. After every change to a Cloudflare entry, and on every run of this skill, run the checker below. It must report no ❌ finding:

```bash
uv run python .agents/skills/claude-maintain-models/scripts/check_thinking_levels.py
```

It compares every Cloudflare and OpenRouter entry in this checkout with the provider's catalog, and exits 1 on a level or default outside `supported_efforts`. For Cloudflare it also exits 1 on a missing level list, or levels on a model whose catalog lists none. `--provider cloudflare` or `--provider openrouter` checks one provider. A `note:` line is information only.
- Then run the paid `test_thinking_level_reasoning_content` for each new level. If `-k "<model_enum>"` collects no `cloudflare/...` case, `cloudflare` is missing from the provider list in `libs/core/kiln_ai/adapters/model_adapters/test_thinking_level_paid.py`; add it.
- **Structured output:** these models take the OpenAI `response_format` envelope, so start from `json_schema` and fall back only on test failures. Cloudflare's JSON mode doesn't support streaming.
- **Don't send `max_tokens`.** Live tests showed no truncation on the OpenAI-format models without it.
- **Workers Paid:** many of the newer models need the Workers Paid plan. Paid tests need an account on it.
Expand All @@ -672,7 +705,7 @@ Runs through LiteLLM's generic `openai/` route against Cloudflare's OpenAI-compa

## Thinking Levels Reference

No API provides the available thinking levels programmatically — they must be manually sourced. Use this lookup chain in priority order:
Two providers publish the thinking levels per model, and their list is the only source for their entries: Cloudflare (see [Cloudflare thinking levels](#cloudflare-thinking-levels)) and OpenRouter (`reasoning.supported_efforts` in its models API, see [OpenRouter (general)](#openrouter-general)). For every other provider the levels must be sourced by hand. Use this lookup chain in priority order.

### Lookup Chain

Expand All @@ -688,11 +721,11 @@ No API provides the available thinking levels programmatically — they must be

3. **Inherit from predecessor** — if the same family/tier model has a `_THINKING_LEVELS` dict, the new model very likely uses the same or a superset.

4. **OpenRouter `supported_parameters`** — check if `reasoning` is present:
4. **OpenRouter `reasoning`** — most models list their efforts:
```bash
curl -s https://openrouter.ai/api/v1/models | jq '.data[] | select(.id == "SLUG") | .supported_parameters'
curl -s https://openrouter.ai/api/v1/models | jq '.data[] | select(.id == "SLUG") | {reasoning, supported_parameters}'
```
If `reasoning` is absent, the model does not support effort levels — skip thinking levels entirely.
`reasoning.supported_efforts` lists the efforts (highest first) and `reasoning.default_effort` the default. If `reasoning` is absent and `supported_parameters` has no `reasoning`, the model does not support effort levels — skip thinking levels entirely. OpenRouter documents that it maps an unsupported effort to the nearest supported one for some models, so an extra level may silently run a different effort.

5. **Smoke test** — as a last resort, send a request with an invalid effort level and check the error message, which often enumerates the valid values.

Expand Down Expand Up @@ -732,7 +765,6 @@ Reuse when levels match exactly. Create a new constant only if levels differ. Th
### Sources That Do NOT Work

These were investigated and confirmed to lack thinking level data:
- **OpenRouter API** — only boolean `reasoning` in `supported_parameters`
- **LiteLLM catalog** — only `supports_reasoning: true/false`
- **Google Gemini models API** — only `thinking: true/false`
- **OpenAI `/v1/models` endpoint** — minimal object with no capability fields
Expand Down
Loading
Loading