fix: recover specs when a model drops a closing bracket - #35
Merged
Conversation
Smaller models reliably under-close deeply nested response schemas and then stop with finish_reason "stop" — they believe they finished, so all three generation attempts reproduced the same truncation and the endpoint was documented as nothing at all. Measured on deepseek-chat, this was the cause of *every* failing accuracy fixture: 4 of 14 endpoints produced no spec, while every reply that did parse scored perfectly. The eval gate had been reporting this as an accuracy problem because it only prints a mean. The reply is now re-read with the missing brackets restored. Crucially the bracket is not simply appended: in the captured failures the model under-closed just before a trailing top-level key, so closing at the end parses but nests `security` inside `responses` — valid JSON, wrong document. Each candidate reading is validated against OperationSchema, which is the only reliable arbiter of where the bracket belonged. Also: - Retry guidance names the likely cause instead of echoing the parser position, which models ignored. - The gate prints why each imperfect fixture lost points, so a hard zero is distinguishable from a slightly-wrong spec. deepseek-chat on the eval suite: 0.714-0.929 (straddling the 0.85 gate, 4-5 generation failures) → 0.951-0.964 with zero failures, over three consecutive gate runs. Claude-Session: https://claude.ai/code/session_01GrfumUQ4vFUAgsFYwzDyK8
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The accuracy gate wasn't flaky — it was faithfully reporting a real bug that had been invisible for weeks.
What was actually happening
The gate's means were exact multiples of 1/14 and the worst fixture always scored 0.00 flat. With a weighted scorer (F1, partial credit per dimension) a slightly-wrong spec can't score zero, so every zero had to be the
catchbranch. It was:Every reply that parsed scored a perfect 1.00. The zeros weren't inaccuracy — they were exceptions. Capturing the raw HTTP response on the real
buildOperationpath:deepseek-chatunder-closes deeply nested response schemas and then stops, believing it finished — so all three attempts reproduced the identical mistake and the endpoint was documented as nothing at all.This is not just a CI problem: every EasyDocs user on DeepSeek was losing ~30% of their endpoints. It's the cheapest supported provider and it's featured in
BENCHMARK.md.The fix, and why it isn't a one-liner
The obvious repair — append the missing
}— parses, but produces the wrong document:The dropped bracket belonged before
,"security", not at the end. Closing at the end buriessecurityinsideresponses, which then fails schema validation anyway.So
jsonCandidatesyields each plausible reading (exact → closed at the end → closed at each member boundary) and the caller takes the first that satisfiesOperationSchema. The schema is the only reliable arbiter of where the bracket belonged. A well-formed reply still takes the fast path and yields exactly one candidate.Two supporting changes:
Expected ',' or '}' ... at position 557, which the model demonstrably ignored — all three attempts repeated the same error.Measured effect
deepseek-chaton the eval suite, before:After, three consecutive full gate runs:
Zero generation failures, and the suite runs in half the time because it no longer burns three retries per failure.
post-orders.jsonnow surfaces as a genuine partial miss (responses=0.00,responseSchema=0.00) rather than a hard zero — a real, separate accuracy issue that is now visible instead of masked.Verification
build,lint,typecheckgreen; 266 tests pass (was 256).builder-json.test.ts, built on the verbatim malformed output captured from deepseek-chat. They assert the repair putssecurityat the top level and not insideresponses— i.e. that it picked the correct insertion point, not merely a parseable one.Not included
JSON mode (
response_format: json_object) would prevent the malformation at the source rather than repairing it, but it's provider-specific and worth evaluating separately.https://claude.ai/code/session_01GrfumUQ4vFUAgsFYwzDyK8