byok: default statefulResponses to true for custom Responses endpoints

The Open Responses spec requires servers to honor previous_response_id or fail with previous_response_not_found, which the existing full-history retry already handles. Chain by default and let stateless servers that silently ignore the ID (e.g. DeepSeek) opt out with statefulResponses: false.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
vritant24andCopilot committed 2026-10-06 09:23:51 -07:00
1 parent d87184e0ce
commit f3de02c677
4 files changed
+18 -17

No files matched your search

+2 -2
View File
@@ -2158,8 +2158,8 @@
},
"statefulResponses": {
"type": "boolean",
"default": false,
"markdownDescription": "Whether the Responses API server retains prior responses. When `true`, requests chain via `previous_response_id` and send only new history; leave `false` for stateless servers (for example DeepSeek), which require the full history on every request. Ignored when `zeroDataRetentionEnabled` is `true`."
"default": true,
"markdownDescription": "Whether the Responses API server retains prior responses. When `true`, requests chain via `previous_response_id` and send only new history. Set to `false` for stateless servers that ignore `previous_response_id` (for example DeepSeek), so the full history is sent on every request. Ignored when `zeroDataRetentionEnabled` is `true`."
},
"supportsReasoningEffort": {
"type": "array",
@@ -208,8 +208,8 @@ export class CustomEndpointBYOKModelProvider extends AbstractOpenAICompatibleLMP
* conflicting credentials.
* 4. Omits the Responses API `store` property when Zero Data Retention was not
* explicitly configured, allowing custom implementations to use their own default.
* 5. Sends full Responses history instead of chaining via `previous_response_id`
* unless the user declares the server stateful with `statefulResponses: true`.
* 5. Chains Responses requests via `previous_response_id` per the Open Responses spec,
* unless the user marks the server stateless with `statefulResponses: false`.
*/
export class CustomEndpointOAIEndpoint extends OpenAIEndpoint {
/**
@@ -261,11 +261,11 @@ export class CustomEndpointOAIEndpoint extends OpenAIEndpoint {
}
/**
* Many OpenAI-compatible Responses implementations (e.g. DeepSeek) are stateless and
* ignore `previous_response_id`, so only chain when the user opts in via `statefulResponses`.
* Spec-compliant Responses servers honor `previous_response_id`, but some (e.g. DeepSeek)
* silently ignore it, so users can opt out with `statefulResponses: false`.
*/
protected override get supportsStatefulResponses(): boolean {
return this.modelMetadata.statefulResponses === true;
return this.modelMetadata.statefulResponses !== false;
}
override createRequestBody(options: ICreateEndpointBodyOptions): IEndpointBody {
@@ -236,7 +236,7 @@ describe('CustomEndpointBYOKModelProvider', () => {
};
}
it('omits store and previous_response_id after cloning a Custom Endpoint Responses endpoint when zeroDataRetentionEnabled is omitted', async () => {
it('omits store but keeps previous_response_id after cloning a Custom Endpoint Responses endpoint when both flags are omitted', async () => {
const endpoint = (await createConfiguredResponsesEndpoint()).cloneWithTokenOverride(64000);
const body = createResponsesBody(endpoint);
@@ -247,12 +247,12 @@ describe('CustomEndpointBYOKModelProvider', () => {
}).toEqual({
storePresent: false,
store: undefined,
previousResponseId: undefined,
previousResponseId: customResponsesMarker,
});
});
it('issue #331636: sends the full tool-call history to stateless Custom Endpoint Responses servers', async () => {
const endpoint = await createConfiguredResponsesEndpoint();
it('issue #331636: sends the full tool-call history when statefulResponses is false', async () => {
const endpoint = await createConfiguredResponsesEndpoint(undefined, false);
const body = endpoint.createRequestBody({
debugName: 'test',
messages: [
@@ -280,8 +280,8 @@ describe('CustomEndpointBYOKModelProvider', () => {
});
});
it('enables store but not previous_response_id when zeroDataRetentionEnabled is false without statefulResponses', async () => {
const endpoint = await createConfiguredResponsesEndpoint(false);
it('enables store but not previous_response_id when zeroDataRetentionEnabled and statefulResponses are false', async () => {
const endpoint = await createConfiguredResponsesEndpoint(false, false);
const body = createResponsesBody(endpoint);
expect({
@@ -295,8 +295,8 @@ describe('CustomEndpointBYOKModelProvider', () => {
});
});
it('chains via previous_response_id when statefulResponses is true', async () => {
const endpoint = await createConfiguredResponsesEndpoint(false, true);
it('enables store and previous_response_id when zeroDataRetentionEnabled is false and statefulResponses is omitted', async () => {
const endpoint = await createConfiguredResponsesEndpoint(false);
const body = createResponsesBody(endpoint);
expect({
@@ -156,8 +156,9 @@ export type IChatModelInformation = IModelAPIResponse & {
modelOptions?: Readonly<IChatModelRequestOptions>;
zeroDataRetentionEnabled?: boolean;
/**
* BYOK Custom Endpoint opt-in declaring that the Responses API server retains prior
* responses, so requests may chain via `previous_response_id` and send only post-marker history.
* BYOK Custom Endpoint setting declaring whether the Responses API server retains prior
* responses, so requests may chain via `previous_response_id` and send only post-marker
* history. Unset is treated as `true`; set `false` for stateless servers.
*/
statefulResponses?: boolean;
/**