Skip to content

fix(lab): report the model gates the way the adapters actually match them - #2059

Merged
lidge-jun merged 1 commit into
lidge-jun:devfrom
ntdatt812:fix/compat-behavior-model-matching
Aug 19, 2026
Merged

fix(lab): report the model gates the way the adapters actually match them#2059
lidge-jun merged 1 commit into
lidge-jun:devfrom
ntdatt812:fix/compat-behavior-model-matching

Conversation

@ntdatt812

@ntdatt812 ntdatt812 commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

Summary

resolveProductionBehaviorValues is documented as "authoritative effective values emitted by the production route/model/adapter resolver", and buildBehaviorFingerprintV1 hashes it into the behavior fingerprint that keys Lab evidence.

Ten of its rows are membership tests over the provider's no*Models-style lists, and they went through a local helper:

function includesModel(list: string[] | undefined, modelId: string): boolean {
  return Array.isArray(list) && list.includes(modelId);
}

Every runtime gate those rows describe matches through modelInList instead, which also accepts a bare entry for a tagged id:

row runtime gate
sampling.omitTemperature / omitTopP / omitPenalties openai-chat.ts 113–115, 1326, 1329, 1398, 1401
reasoning.supported reasoning-effort.ts:107
reasoning.replayMode.preserveContent / .placeholder openai-chat.ts 691, 700, 714, 760, 774–775
reasoning.splitMode / toggleMode / budgetMode openai-chat.ts 1315, 1370, 1380
tools.choiceRestrictions openai-chat.ts:1321

All ten use modelInList. The report used exact match for all ten.

Why it bites

ollama-cloud serves tagged ids verbatim — gpt-oss:120b, qwen3-coder:480b, qwen3.5:397b, gemma4:31b — and the same registry row writes the bare gpt-oss into noVisionModels, so the bare-prefix form is how these lists are meant to be written.

With noTemperatureModels: ["gpt-oss"], for gpt-oss:120b:

  • the adapter omits temperature from the upstream request
  • the report says sampling.omitTemperature: false

The authoritative record contradicts the wire, and that contradiction is hashed into the fingerprint Lab uses to link observations.

Blast radius, measured

Same config, before and after, first 16 hex of the behavior fingerprint:

model dev this branch
gpt-oss:120b 54154e19bd2c8ee4 45a73577c5c257f9 changed — it was wrong
gpt-oss 45a73577c5c257f9 45a73577c5c257f9 unchanged
glm-5.3 54154e19bd2c8ee4 54154e19bd2c8ee4 unchanged

Read the first row against the third: on dev, a model whose sampling gates were applied hashed identically to one where they were not. It now hashes with gpt-oss, the id it actually behaves like.

On resolverVersion

#1956 established the rule — "adding hashed keys without it silently made new fingerprints incomparable to recorded ones". I left resolverVersion at 2 on purpose: unlike a key addition, this only moves subjects that were being described incorrectly, and the table above shows unaffected subjects keep their fingerprint, so their recorded evidence stays linkable. If you would rather draw a clean generation boundary anyway, say so and I will bump it in this PR.

Tests

tests/routing-compatibility-model-matching.test.ts. The first case asserts the wire the adapter really builds for the tagged model; the rest hold the report to that same wire, so the two cannot drift apart silently. A control asserts an unlisted model still reports false.

The five report cases fail on current dev (2 pass / 5 fail) and pass here (7 / 0).

Verification

Run on the exact head of this branch:

bun run typecheck                                    clean

bun test tests/routing-compatibility-model-matching.test.ts
                                                     7 pass / 0 fail
  same file with src reverted to dev:                2 pass / 5 fail

bun scripts/test.ts <22 test files touching compatibility,
  behaviorValues, passive-route-linker, lab, fastwire>
                                                     856 pass / 6 skip / 0 fail

I did not run the full suite: this is a Windows machine and bun run test panics partway through on Bun 1.3.14 (index out of bounds: index 0, len 0), so a result from it would be a truncated log rather than a result. The batch above went through scripts/test.ts so each file keeps its isolated home.

Summary by CodeRabbit

  • Bug Fixes

    • Improved provider exclusion matching so tagged model IDs are correctly recognized when their base model is listed.
    • Updated compatibility reporting for gpt-oss:120b, including accurate handling of supported request options and restrictions.
  • Tests

    • Added coverage for model matching and compatibility behavior, including unlisted models and request field handling.

Review readiness checklist

This PR stays in draft until every box below is ticked. Tick all four boxes once the requirements are met:

  • All CI tests are green on my local testing.

  • I pushed my PR to the latest dev commit.

  • I resolved all correct Codex and CodeRabbit findings.

  • My PR is ready for review.

What box 1 covers here. Ticked on the same reading @Ingwannu applied to #2042 — the box gates triggering the full exact-head CI, not a claim to have run CI itself. To keep the record exact: "green on my local testing" means everything listed under Verification above ran green on this exact head — typecheck, the new tests, the same tests against unpatched dev to prove they fail there, and a batch of the surrounding suite. It does not mean I ran the full suite: this is a Windows machine, bun run test panics partway through on Bun 1.3.14 (index out of bounds: index 0, len 0), and the Windows baseline on clean dev is not green either (#1059). The exact-head CI gate is the authority on the full suite, which is what marking this Ready hands it.

@github-actions

Copy link
Copy Markdown
Contributor

Deterministic PR hygiene checks passed.

@github-actions

github-actions Bot commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

✅ READY

  • all PR quality gates passed; the review readiness checklist is complete.

Review readiness checklist

  • ✅ All CI tests are green on my local testing.
  • ✅ I pushed my PR to the latest dev commit.
  • ✅ I resolved all correct Codex and CodeRabbit findings.
  • ✅ My PR is ready for review.

4/4 boxes ticked.

This pull request is already Ready for Review.
The review-ready label marks this PR as ready; review automation runs independently.
Maintainers: @lidge-jun @Ingwannu @Wibias

@github-actions
github-actions Bot marked this pull request as draft August 18, 2026 16:16
@github-actions github-actions Bot added the bug Something isn't working label Aug 18, 2026
@coderabbitai

coderabbitai Bot commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: feffc237-b503-4553-8efe-399ddc0a03b1

📥 Commits

Reviewing files that changed from the base of the PR and between a5ec641 and 99a2924.

📒 Files selected for processing (2)
  • src/routing/compatibility/behavior.ts
  • tests/routing-compatibility-model-matching.test.ts

Included review availability: Your plan includes up to 10 reviews per rolling hour; 9 remain after this review.


📝 Walkthrough

Walkthrough

The routing compatibility check now matches tagged model IDs against bare entries in provider lists. Tests validate the gpt-oss:120b request serialization, behavior report, and unlisted-model behavior.

Changes

Model List Compatibility

Layer / File(s) Summary
Model membership matching
src/routing/compatibility/behavior.ts
includesModel now uses modelInList, so bare model entries match tagged model IDs while absent lists still return false.
Compatibility behavior validation
tests/routing-compatibility-model-matching.test.ts
Tests validate request fields, reasoning and tool-choice behavior, and disabled flags for an unlisted model.

Estimated code review effort: 2 (Simple) | ~10 minutes

Merge Risk: ⚪ Minimal · up to 99a29

This localized fix aligns reported model-gate values with adapter behavior and includes targeted passing tests; no actionable merge-blocking risk remains beyond normal checks and review.

Suggested reviewers: ingwannu

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes aligning Lab model-gate reporting with the matching logic used by adapters.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@ntdatt812

Copy link
Copy Markdown
Contributor Author

Checklist is at 3/4. Box 1 is open on purpose rather than left over — I would rather ask than tick something I cannot stand behind.

"All CI tests are green on my local testing" — this is a Windows machine. bun run test panics partway through on Bun 1.3.14 (index out of bounds: index 0, len 0), so a "0 fail" from it would be a truncated log, not a result. Running in chunks through scripts/test.ts avoids the panic, but the Windows baseline on clean dev is not green either (#1059), so "all green" is not reachable here by any route.

What I did run on the exact head of this branch is listed under Verification in the description — typecheck clean, the new tests green, and a batch of the surrounding suite green, plus the same tests run against unpatched dev to show they actually fail there.

If box 1 is meant as "the tests you ran locally are green", that is true and I will tick it on a word from you. If it is meant literally as the full suite, it cannot be honestly ticked from Windows and I would rather it stay open than be wrong.

@ntdatt812
ntdatt812 marked this pull request as ready for review August 18, 2026 18:48
@github-actions
github-actions Bot marked this pull request as draft August 18, 2026 18:48
@ntdatt812
ntdatt812 marked this pull request as ready for review August 18, 2026 18:52
@github-actions
github-actions Bot marked this pull request as draft August 18, 2026 18:52
…them

`resolveProductionBehaviorValues` is documented as "authoritative effective
values emitted by the production route/model/adapter resolver", and its hash
is the behavior fingerprint that keys Lab evidence. Ten of its rows are
membership tests over the provider's `no*Models`-style lists, and they went
through a local

    function includesModel(list, modelId) {
      return Array.isArray(list) && list.includes(modelId);
    }

while every runtime gate those rows describe matches through `modelInList`,
which also accepts a bare entry for a tagged id.

ollama-cloud serves `gpt-oss:120b`, `qwen3-coder:480b`, `qwen3.5:397b` and
`gemma4:31b`, and the same registry row writes the bare `gpt-oss` into
noVisionModels -- the bare-prefix form is how these lists are meant to be
written. So with `noTemperatureModels: ["gpt-oss"]` the adapter omitted
temperature from the request for `gpt-oss:120b` while the report said
`sampling.omitTemperature: false`. Same for omitTopP, omitPenalties,
reasoning.budgetMode, reasoning.splitMode, reasoning.toggleMode,
reasoning.supported, reasoning.replayMode's two flags, and
tools.choiceRestrictions.

The blast radius is confined to subjects that were being described wrongly.
Fingerprints measured on the same config, before and after:

    gpt-oss:120b   54154e19bd2c8ee4 -> 45a73577c5c257f9   (was wrong)
    gpt-oss        45a73577c5c257f9 -> 45a73577c5c257f9   (unchanged)
    glm-5.3        54154e19bd2c8ee4 -> 54154e19bd2c8ee4   (unchanged)

Note the first line against the third: a model whose sampling gates were
applied hashed identically to one where they were not. It now hashes with
`gpt-oss`, the id it actually behaves like. Recorded evidence for unaffected
subjects keeps its fingerprint, so `resolverVersion` is left at 2 rather than
invalidating every recorded subject globally -- say the word if you would
rather draw a clean generation boundary and I will bump it.

Tests assert the wire the adapter really builds first, then hold the report to
that same wire, so the pair cannot drift apart silently. The five report cases
fail on current dev.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@ntdatt812
ntdatt812 force-pushed the fix/compat-behavior-model-matching branch from 99a2924 to c49cb11 Compare August 18, 2026 19:00
@ntdatt812
ntdatt812 marked this pull request as ready for review August 18, 2026 19:01
@github-actions
github-actions Bot marked this pull request as draft August 18, 2026 19:01
@ntdatt812
ntdatt812 marked this pull request as ready for review August 18, 2026 19:03
@lidge-jun
lidge-jun merged commit bd3aa31 into lidge-jun:dev Aug 19, 2026
33 of 35 checks passed
@lidge-jun

Copy link
Copy Markdown
Owner

Merged as bd3aa31. One residual gap recorded from independent review (pre-existing on dev, untouched by this PR): reasoning.replayMode.placeholder in src/routing/compatibility/behavior.ts is computed from requiresReasoningPlaceholderModels only, while the runtime falls back to preserveReasoningContentModels when that list is omitted (src/adapters/openai-chat.ts:700,774). A preserve-listed provider with no explicit placeholder list still reports placeholder=false while the wire inserts one. Worth a small follow-up mirroring the runtime fallback + a tagged-model regression test.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working review-ready

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants