fix(ai-chat): disable Responses API storage for Azure models to stop "Item with id ... not found" stream failures (#23182)

## 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
This commit is contained in:
Etienne
2026-07-22 18:31:22 +02:00
committed by GitHub
parent c54ad3c0b9
commit f67e9c6b05
2 changed files with 37 additions and 0 deletions
@@ -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({
@@ -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;
}