fix: filter orphaned reasoning items to prevent OpenAI API error (#2508)
fixes https://github.com/dyad-sh/dyad/issues/2500
## Summary
- Adds `filterOrphanedReasoningParts()` function to filter out OpenAI
reasoning items that aren't followed by output items
- Fixes error: "Item of type 'reasoning' was provided without its
required following item"
- This error occurs with OpenAI reasoning models (o1, o3, o4-mini) when
conversation history contains orphaned reasoning items
## Test plan
- Added 4 unit tests covering:
- Reasoning with following output is preserved
- Orphaned reasoning (alone in message) is filtered out
- Reasoning followed by tool-call is preserved
- Trailing reasoning after text output is filtered
- All 672 tests pass
🤖 Generated with [Claude Code](https://claude.com/claude-code)
<!-- devin-review-badge-begin -->
---
<a href="https://app.devin.ai/review/dyad-sh/dyad/pull/2508"
target="_blank">
<picture>
<source media="(prefers-color-scheme: dark)"
srcset="https://static.devin.ai/assets/gh-open-in-devin-review-dark.svg?v=1">
<img
src="https://static.devin.ai/assets/gh-open-in-devin-review-light.svg?v=1"
alt="Open with Devin">
</picture>
</a>
<!-- devin-review-badge-end -->
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> **Medium Risk**
> Touches message-history shaping sent to OpenAI (including multi-step
local-agent flows); incorrect filtering could drop reasoning parts or
change message arrays, potentially impacting model behavior, but changes
are scoped and covered by new unit tests.
>
> **Overview**
> Prevents OpenAI Responses API errors by **cleaning conversation
history** before reuse: message parts now have stale
`providerOptions/providerMetadata.{openai,azure}.itemId` removed and
**orphaned `reasoning` parts (not followed by any non-reasoning
output)** filtered out via `cleanMessageForOpenAI()`.
>
> This cleaning is applied both when parsing persisted `aiMessagesJson`
(`parseAiMessagesJson`) and during local-agent multi-step
`prepareStepMessages`, with expanded unit tests covering preservation vs
filtering cases; `thinking_utils` also disables OpenAI `store` for
`local-agent` mode and `AGENTS.md` documents the failure mode and fix.
>
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
5b0c70c2817345598e2eb88dcefbb0f859d6bb88. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Filters orphaned reasoning parts in conversation history to prevent
OpenAI Responses API errors when a reasoning item isn’t followed by
output. Applies filtering when parsing stored messages and during
multi-step prepare step.
- **Bug Fixes**
- Replaced stripItemIds with cleanMessageForOpenAI in
parseAiMessagesJson and prepareStepMessages to strip provider itemId and
filter orphaned reasoning parts.
- Preserves reasoning when followed by text or tool-call, preventing
“reasoning without required following item” errors on o1/o3/o4-mini;
sets store: false to avoid expired item references.
- Expanded tests for parsing and multi-step flows; documented behavior
and fix in AGENTS.md.
<sup>Written for commit 5b0c70c2817345598e2eb88dcefbb0f859d6bb88.
Summary will update on new commits.</sup>
<!-- End of auto-generated description by cubic. -->
Co-authored-by:
Claude Opus 4.5 <noreply@anthropic.com>
正在显示
请
注册
或者
登录
后发表评论