fix: improve truncation-aware parse failure logging#754
Conversation
Greptile SummaryThis PR surfaces actionable remediation guidance when a parse or recipe failure follows a model response that was cut off at the output-token or context-window limit. Previously all parse failures received the same generic validation message regardless of why the response was truncated.
|
| Filename | Overview |
|---|---|
| packages/data-designer-engine/src/data_designer/engine/models/clients/adapters/anthropic_translation.py | Populates canonical choices[0].finish_reason from Anthropic stop_reason; existing message, usage, and raw fields unchanged. |
| packages/data-designer-engine/src/data_designer/engine/models/errors.py | Adds GenerationTruncationReason enum and optional truncation_reason on GenerationValidationFailureError; handle_llm_exceptions selects reason-specific solution text correctly. |
| packages/data-designer-engine/src/data_designer/engine/models/facade.py | Adds canonical-first / raw-fallback truncation classification, deterministic accumulation across retries, and threads truncation_reason into both raise paths of generate/agenerate. |
| packages/data-designer-engine/tests/engine/models/test_facade.py | Comprehensive coverage: single-attempt classification, raw fallback, canonical preference, accumulation across correction steps, context-window precedence, and success-after-truncation path. |
| packages/data-designer-engine/tests/engine/models/test_model_errors.py | New tests verify truncation_reason storage, reason-specific solution formatting, and that the public error surface remains unchanged. |
| packages/data-designer-engine/tests/engine/models/clients/test_anthropic_translation.py | Parameterised test confirms all four Anthropic stop reasons map 1:1 to canonical choices[0].finish_reason. |
| packages/data-designer-engine/tests/engine/dataset_builders/test_async_scheduler.py | Characterisation test verifies that the scheduler's existing non-retryable warning path surfaces the formatted max-tokens guidance without any scheduler-specific production changes. |
| plans/411/truncation-aware-parse-failure-guidance.md | Design plan document tracking the implementation; the explicitly excluded list for test_dataset_builder.py conflicts with the PR description (addressed in a previous review thread). |
Sequence Diagram
sequenceDiagram
participant Caller
participant ModelFacade
participant ModelClient
participant Parser
participant Errors as handle_llm_exceptions
Caller->>ModelFacade: generate(prompt, parser)
ModelFacade->>ModelClient: completion(request)
ModelClient-->>ModelFacade: ChatCompletionResponse with finish_reason
ModelFacade->>Parser: parser(response_text)
Parser-->>ModelFacade: ParserException
Note over ModelFacade: Classify finish_reason via canonical choices first, raw fallback if None
Note over ModelFacade: Merge into accumulated truncation_reason, MODEL_CONTEXT_WINDOW_EXCEEDED wins
alt more retries available
ModelFacade->>ModelClient: completion(correction)
ModelClient-->>ModelFacade: ChatCompletionResponse
ModelFacade->>Parser: parser(response_text)
Parser-->>ModelFacade: ParserException again
Note over ModelFacade: Merge new reason into accumulated truncation_reason
end
ModelFacade->>Errors: raise GenerationValidationFailureError(truncation_reason)
Errors-->>Caller: ModelGenerationValidationFailureError with reason-specific solution text
Reviews (3): Last reviewed commit: "fix: address #411 review follow-ups" | Re-trigger Greptile
|
Thanks for the automated review. Greptile, DCO, semantic title, and the agentic CI gate are now passing. The only failing check is the linked-issue gate because #411 does not yet have the maintainer-added |
Stale PR reminderThis PR has had failing checks for 7 days without activity. Failing checks: check Please push an update or leave a comment if you're still working on this. To prevent auto-close, add the |
|
Issue #411 has been triaged. The linked issue check is being re-evaluated. |
|
Thanks for the contribution, this is a nice quality-of-life fix. I reviewed the truncation metadata flow, Anthropic
Focused tests and smoke checks passed locally. Once those small items are addressed, this looks good to merge from my side. |
|
One broader simplification suggestion, especially now that the legacy sync builder has been removed from The current flow is roughly:
The async scheduler already logs the formatted public exception, so I think the same behavior can be implemented with fewer layers:
For the narrow scope of #411, an internal There is also one Anthropic nuance worth accounting for in the design: Anthropic can terminate with either
If we want this PR to support both, a small canonical truncation-reason enum on the internal error would be cleaner than a boolean. If we intentionally keep this PR limited to #411's output-token case, the dedicated subtype plus a follow-up for context-window truncation would keep this change small and explicit. Overall, I think the behavior is worth keeping; this is mainly an opportunity to reduce coupling and make the Anthropic boundary clearer before merge. |
nabinchha
left a comment
There was a problem hiding this comment.
One broader simplification suggestion, especially now that the legacy sync builder has been removed from main: could we avoid threading truncated_by_max_tokens through both exception classes and then reading it with getattr() in the builder?
The current flow is roughly:
finish_reason → boolean → GenerationValidationFailureError → boolean → ModelGenerationValidationFailureError → builder formatting
The async scheduler already logs the formatted public exception, so I think the same behavior can be implemented with fewer layers:
- Normalize the provider termination reason in the client adapter.
- Accumulate the truncation state locally across correction attempts/restarts.
- Raise a dedicated internal validation-error subtype (or carry a typed reason only on the internal validation error).
- Format the actionable message in
handle_llm_exceptions(). - Let the scheduler log the existing
ModelGenerationValidationFailureErrornormally.
For the narrow scope of #411, an internal MaxTokensGenerationFailureError(GenerationValidationFailureError) subtype would be enough. That would avoid adding truncated_by_max_tokens to the public model error, avoid the builder getattr(), and allow the obsolete sync-builder hunk and test to be dropped during the rebase.
There is also one Anthropic nuance worth accounting for in the design: Anthropic can terminate with either max_tokens or model_context_window_exceeded, and both can leave structured output truncated. They need different guidance:
max_tokens: increaseinference_parameters.max_tokens.model_context_window_exceeded: reduce the prompt/context/schema or use a model with a larger context window; increasingmax_tokenswill not help.
If we want this PR to support both, a small canonical truncation-reason enum on the internal error would be cleaner than a boolean. If we intentionally keep this PR limited to #411's output-token case, the dedicated subtype plus a follow-up for context-window truncation would keep this change small and explicit.
Overall, I think the behavior is worth keeping; this is mainly an opportunity to reduce coupling and make the Anthropic boundary clearer before merge.
|
@stepwise-ai-dev, are you planning to continue working on this PR? We think the behavior is worth keeping, but If you are not planning to continue, we will likely close this PR and implement a smaller version in a separate maintainer PR. Please let us know. Thanks again for the contribution. |
|
Yes, I'm planning to continue. Thanks for checking in, and for the clear feedback on the current error flow. I've reviewed the changes on I've prepared a smaller plan around the current async path: normalize provider termination reasons, accumulate truncation across correction and restart attempts, keep the reason internal, and let the scheduler log the existing public error. It also distinguishes I'll add the plan to the PR for review before resuming implementation. |
Signed-off-by: stepwise-ai-dev <stepwise-ai-dev@users.noreply.github.com>
8411985 to
7e4a8a4
Compare
andreatnvidia
left a comment
There was a problem hiding this comment.
Thanks for revising this against current main. The plan addresses the earlier feedback from @nabinchha and me, and the direction looks good. Approved to proceed with implementation.
Two non-blocking follow-ups:
- Put direct
parse_anthropic_response()coverage intest_anthropic_translation.py. - Update the PR description to reflect the new plan and remove the obsolete sync DatasetBuilder claims.
Signed-off-by: schultzjack <schultzjack@users.noreply.github.com>
Signed-off-by: schultzjack <schultzjack@users.noreply.github.com>
Signed-off-by: schultzjack <schultzjack@users.noreply.github.com>
Signed-off-by: schultzjack <schultzjack@users.noreply.github.com>
Signed-off-by: schultzjack <schultzjack@users.noreply.github.com>
|
The branch is now updated to current
The PR description now matches the implemented scope and removes the obsolete sync DatasetBuilder claims. All required checks on this head are passing. Local validation also completed with 288 focused tests, @nabinchha, could you re-review the updated implementation when convenient? |
|
Thanks for the careful rework, @stepwise-ai-dev — this is much cleaner and lines up well with the approved direction. SummaryThis revision normalizes Anthropic stop reasons, keeps truncation metadata internal, and surfaces reason-specific guidance through the existing public error boundary. The implementation matches the updated PR description and addresses the earlier feedback around helper reuse, raw-response fallback rationale, retry accumulation, and async scheduler propagation. FindingsWarnings — Worth addressing
What Looks Good
VerdictNeeds changes — adjust mixed truncation guidance across conversation restarts so it does not categorically rule out increasing This review was generated by an AI assistant. |
📋 Summary
Surfaces actionable guidance when a structured-output parse or recipe failure follows a model response terminated by an output-token or context-window limit. The implementation follows the current model-error and async scheduler flow while preserving generic validation behavior.
🔗 Related Issue
Fixes #411
🔄 Changes
stop_reasoninto the canonical completion-choicefinish_reason.🔍 Attention Areas
🧪 Testing
make testpasses — 3,975 passed, 1 skippedmake check-enginepasses.make check-all-fixpasses with no tracked changes.✅ Checklist