From f67e9c6b050f4dc0c85f3afcb62be0e20581ad07 Mon Sep 17 00:00:00 2001 From: Etienne <45695613+etiennejouan@users.noreply.github.com> Date: Wed, 22 Jul 2026 18:31:22 +0200 Subject: [PATCH] fix(ai-chat): disable Responses API storage for Azure models to stop "Item with id ... not found" stream failures (#23182) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Problem AI chat and workflow-agent turns on Azure-routed reasoning models (e.g. `azure-foundry-us/gpt-5.5`) intermittently die mid-answer with: > Failed to get response — `400 Item with id 'rs_...' not found` ## Root cause The OpenAI Responses API is stateful: each output item gets a server-side id (`rs_...` reasoning, `fc_...` function call). With `store: true` (the API default), the Vercel AI SDK replays prior assistant reasoning as `item_reference` entries that the provider must resolve from its own storage. For reasoning models the chain-of-thought is never returned in plaintext — it lives only as a stored referenced item, or as `encrypted_content` requested via `include: ['reasoning.encrypted_content']` (which the SDK only auto-adds when `store: false`). PR #20888 (June 22) switched to the safe `store: false` mode, but only for `AI_SDK_OPENAI`. `AI_SDK_AZURE` fell through to `default` and got no options, so every Azure request ran in stored-reference mode. Azure's item storage resolves those references unreliably (a server-side race — the July 21 failure could not find the exact `rs_...` id Azure itself had streamed seconds earlier in the same turn), and the failure is fatal to the stream. Retries replay the same persisted references, so they can fail again. ## Fix Add an `AI_SDK_AZURE` case in `getCallLevelProviderOptions` that passes `azure: { store: false }`. Both AI chat and workflow agents go through this helper, so both paths are covered. The provider-options key for `@ai-sdk/azure` is `azure` (its responses model is registered as `azure.responses`); I verified the `@ai-sdk/openai@3.0.54` copy nested inside `@ai-sdk/azure` has the same guards as the root `3.0.71`. With `store: false` the SDK: - auto-requests `include: ['reasoning.encrypted_content']` for reasoning models (the `gpt-5.5` deployment name passes reasoning detection), - replays reasoning as self-contained encrypted items instead of server-side references, - drops any stale unencrypted reasoning parts instead of referencing them. ### Transition safety Existing threads whose persisted reasoning has `reasoningEncryptedContent: null` simply have those parts skipped on replay. I checked prod logs for the related pairing-validation error ("was provided without its required...") and found zero occurrences since OpenAI-direct made this same switch a month ago, so the transition is safe. ## Testing - Added two Azure cases to `provider-options.util.spec.ts` — all 9 pass. - oxlint (type-aware) and oxfmt clean on the changed files. ## Post-deploy validation Inverse of the evidence: new Azure reasoning parts should persist with non-null `reasoningEncryptedContent`, and the Loki query below should go quiet. fixes : https://discord.com/channels/1130383047699738754/1526873169124659230 --- .../__tests__/provider-options.util.spec.ts | 31 +++++++++++++++++++ .../ai/ai-chat/utils/provider-options.util.ts | 6 ++++ 2 files changed, 37 insertions(+) diff --git a/packages/twenty-server/src/engine/metadata-modules/ai/ai-chat/utils/__tests__/provider-options.util.spec.ts b/packages/twenty-server/src/engine/metadata-modules/ai/ai-chat/utils/__tests__/provider-options.util.spec.ts index 629e39ae38..59d0a8dda8 100644 --- a/packages/twenty-server/src/engine/metadata-modules/ai/ai-chat/utils/__tests__/provider-options.util.spec.ts +++ b/packages/twenty-server/src/engine/metadata-modules/ai/ai-chat/utils/__tests__/provider-options.util.spec.ts @@ -5,6 +5,7 @@ import { } from 'src/engine/metadata-modules/ai/ai-chat/utils/provider-options.util'; import { AI_SDK_ANTHROPIC, + AI_SDK_AZURE, AI_SDK_BEDROCK, AI_SDK_OPENAI, } from 'src/engine/metadata-modules/ai/ai-models/constants/ai-sdk-package.const'; @@ -65,6 +66,36 @@ describe('provider-options.util', () => { }); }); + it('returns store false for Azure models', () => { + expect(getCallLevelProviderOptions({ sdkPackage: AI_SDK_AZURE })).toEqual( + { + azure: { + store: false, + }, + }, + ); + }); + + it('merges existing provider options with Azure store false', () => { + expect( + getCallLevelProviderOptions({ + sdkPackage: AI_SDK_AZURE, + providerOptions: { + xai: { + searchParameters: { mode: 'auto' }, + }, + }, + }), + ).toEqual({ + xai: { + searchParameters: { mode: 'auto' }, + }, + azure: { + store: false, + }, + }); + }); + it('omits promptCacheKey for non-OpenAI providers', () => { expect( getCallLevelProviderOptions({ diff --git a/packages/twenty-server/src/engine/metadata-modules/ai/ai-chat/utils/provider-options.util.ts b/packages/twenty-server/src/engine/metadata-modules/ai/ai-chat/utils/provider-options.util.ts index 399ae2d9f2..51dd0d4d76 100644 --- a/packages/twenty-server/src/engine/metadata-modules/ai/ai-chat/utils/provider-options.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/ai/ai-chat/utils/provider-options.util.ts @@ -4,6 +4,7 @@ import { type AiSdkPackage } from 'twenty-shared/ai'; import { AI_SDK_ANTHROPIC, + AI_SDK_AZURE, AI_SDK_BEDROCK, AI_SDK_OPENAI, } from 'src/engine/metadata-modules/ai/ai-models/constants/ai-sdk-package.const'; @@ -38,6 +39,11 @@ export const getCallLevelProviderOptions = ({ ...(providerOptions ?? {}), openai: { store: false, ...(promptCacheKey ? { promptCacheKey } : {}) }, }; + case AI_SDK_AZURE: + return { + ...(providerOptions ?? {}), + azure: { store: false }, + }; default: return providerOptions; }