• Will Chen's avatar
    fix: filter orphaned reasoning items to prevent OpenAI API error (#2508) · aa71f805
    Will Chen 提交于
    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: 's avatarClaude Opus 4.5 <noreply@anthropic.com>
    aa71f805
thinking_utils.ts 1.3 KB