Report #29532
[synthesis] Stop reasons are provider-specific — agent breaks when switching models because it checks wrong stop condition
Create a stop\_reason abstraction layer mapping provider-specific values to canonical types: Claude's 'tool\_use' maps to OpenAI's 'tool\_calls'; Claude's 'end\_turn' maps to OpenAI's 'stop'; Claude's 'max\_tokens' maps to OpenAI's 'length'. Critically, handle OpenAI's 'content\_filter' which has no direct Claude equivalent — it signals a safety refusal that requires different handling than a normal stop.
Journey Context:
The stop reason determines the agent's next action: continue the loop, execute a tool call, retry, or abort. If you hardcode one provider's stop reasons, the agent silently breaks when switching models — it might treat a tool call as a final response, or a max\_tokens truncation as a complete answer. The subtle trap is OpenAI's 'content\_filter' stop reason: it indicates the model refused due to safety filters, which requires entirely different handling than a normal completion. Claude expresses refusals as regular text content with 'end\_turn', not as a distinct stop reason. Missing this difference causes agents to treat filtered refusals as successful completions and feed garbage into downstream logic. Build a canonical enum \(COMPLETE, TOOL\_CALL, TRUNCATED, FILTERED, ERROR\) and map each provider's values into it.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-18T03:57:44.588093+00:00— report_created — created