Is your feature request related to a problem?
AI summaries in EvaluationRun.score.overall.ai_summary are being truncated mid-sentence, affecting data integrity. This results in incomplete information being stored and reported.
Describe the solution you'd like
- Modify
generate_run_ai_summary to handle quotes properly in the structured output.
- Ensure that the full summary, including complete sentences, is stored regardless of quotation marks.
- Implement logging for genuinely truncated summaries to provide visibility into issues.
Original issue
Describe the bug
Eval-v2 run AI summaries (EvaluationRun.score.overall.ai_summary) are stored truncated mid-sentence. 11 of 74 rows in a local ai_platform DB are affected.
... All five repeats omit the required <- ends here
... because the model never names <- ends here
... adds Linnaeus's nationality ( <- ends here
Root cause
generate_run_ai_summary (backend/app/crud/evaluations/summary.py) requests Anthropic structured output for what is a single prose field:
output_config={"format": {"type": "json_schema", "schema": _OUTPUT_SCHEMA}}
# _OUTPUT_SCHEMA = {"summary": {"type": "string"}}
Structured outputs use constrained decoding. Inside the string value of {"summary": "..."} a bare " is a grammatically legal token — it is the string terminator. When the summary reaches a quoted phrase (omit the required "wellness message", nationality ("Swedish")) and the sampler emits the raw " instead of \ + ", the grammar reads it as end-of-string. The object closes, generation finishes normally, and json.loads succeeds on valid JSON holding an amputated sentence.
Every layer reports success: valid JSON, stop_reason == "end_turn", and a non-fatal except Exception with nothing to log.
Ruled out
max_tokens — cap is 3000; truncated rows are 354-842 chars (~90-220 output tokens). Complete summaries in the same table reach 2000+ chars.
- Column limit —
score is JSONB, containing columns are Text. No constraint in the write path (save_score, crud/evaluations/core.py).
- Display clamp — ruled out by
SELECT length(score->'overall'->>'ai_summary') in psql rather than reading a DB-client grid cell.
Evidence
|
rows |
contain a " |
| complete |
58 |
11 |
| truncated |
16 |
1 |
Quotes are near-absent from truncated summaries, because the first quote is what ended generation. Cut points land exactly where a quote would open (twice immediately after (, which in this prompt's style precedes a quoted example). Per-quote coin flip: ~11 rows got lucky with \", ~11 did not.
To Reproduce
- Run a v2 (
is_judge_run) evaluation to completion so the terminal hook calls generate_run_ai_summary.
- Query the stored summary:
SELECT id, length(score->'overall'->>'ai_summary'), right(score->'overall'->>'ai_summary', 40)
FROM evaluation_run
WHERE score->'overall'->>'ai_summary' IS NOT NULL
ORDER BY id DESC LIMIT 20;
- Rows whose summary would have contained a quoted phrase end mid-sentence, with no closing reminder line.
Expected behavior
The full summary is stored — overall read, Top 3 to check: items, and the closing go-verify line — regardless of quotation marks in the prose. A summary that is genuinely cut short should be visible in logs, not silent.
Additional context
Two related defects in the same call, both fixed by removing the schema:
next(b.text for b in response.content if b.type == "text") takes only the first text block and discards the rest.
- Any malformed JSON degrades the whole summary to
None with no operator-facing signal.
The same json_schema pattern is used by _call_prompt_drafting_llm (backend/app/services/evaluations/prompt_improvement.py), where the blast radius is larger: improved_instructions is persisted as a new config_version, so a truncated draft becomes a live system prompt. That call has two required fields, so after an early string close the grammar forces ,"rationale":... and json.loads returns both keys with the instructions amputated — zero signal.
Not covered by the fix
- The already-truncated rows stay truncated; a backfill would mean re-running the summary call against each run's stored traces.
- The quote-termination class remains open on
prompt_improvement, whose schema earns its place (two fields + a maxLength cap). Closing it there means switching to delimiter-separated output.
Is your feature request related to a problem?
AI summaries in
EvaluationRun.score.overall.ai_summaryare being truncated mid-sentence, affecting data integrity. This results in incomplete information being stored and reported.Describe the solution you'd like
generate_run_ai_summaryto handle quotes properly in the structured output.Original issue
Describe the bug
Eval-v2 run AI summaries (
EvaluationRun.score.overall.ai_summary) are stored truncated mid-sentence. 11 of 74 rows in a localai_platformDB are affected.Root cause
generate_run_ai_summary(backend/app/crud/evaluations/summary.py) requests Anthropic structured output for what is a single prose field:Structured outputs use constrained decoding. Inside the string value of
{"summary": "..."}a bare"is a grammatically legal token — it is the string terminator. When the summary reaches a quoted phrase (omit the required "wellness message",nationality ("Swedish")) and the sampler emits the raw"instead of\+", the grammar reads it as end-of-string. The object closes, generation finishes normally, andjson.loadssucceeds on valid JSON holding an amputated sentence.Every layer reports success: valid JSON,
stop_reason == "end_turn", and a non-fatalexcept Exceptionwith nothing to log.Ruled out
max_tokens— cap is 3000; truncated rows are 354-842 chars (~90-220 output tokens). Complete summaries in the same table reach 2000+ chars.scoreis JSONB, containing columns areText. No constraint in the write path (save_score,crud/evaluations/core.py).SELECT length(score->'overall'->>'ai_summary')in psql rather than reading a DB-client grid cell.Evidence
"Quotes are near-absent from truncated summaries, because the first quote is what ended generation. Cut points land exactly where a quote would open (twice immediately after
(, which in this prompt's style precedes a quoted example). Per-quote coin flip: ~11 rows got lucky with\", ~11 did not.To Reproduce
is_judge_run) evaluation to completion so the terminal hook callsgenerate_run_ai_summary.Expected behavior
The full summary is stored — overall read,
Top 3 to check:items, and the closing go-verify line — regardless of quotation marks in the prose. A summary that is genuinely cut short should be visible in logs, not silent.Additional context
Two related defects in the same call, both fixed by removing the schema:
next(b.text for b in response.content if b.type == "text")takes only the first text block and discards the rest.Nonewith no operator-facing signal.The same
json_schemapattern is used by_call_prompt_drafting_llm(backend/app/services/evaluations/prompt_improvement.py), where the blast radius is larger:improved_instructionsis persisted as a newconfig_version, so a truncated draft becomes a live system prompt. That call has two required fields, so after an early string close the grammar forces,"rationale":...andjson.loadsreturns both keys with the instructions amputated — zero signal.Not covered by the fix
prompt_improvement, whose schema earns its place (two fields + amaxLengthcap). Closing it there means switching to delimiter-separated output.