fix(ai): retry full transcript on blocked stateful OpenAI Responses prompts
This commit is contained in:
@@ -594,7 +594,11 @@ const streamOpenAIResponsesOnce = (
|
||||
error instanceof Error &&
|
||||
/previous[ _]?response/i.test(error.message) &&
|
||||
/zero[ _-]?data[ _-]?retention/i.test(error.message);
|
||||
if (!zdrRejection && !isOpenAIResponsesStalePreviousResponseError(error)) {
|
||||
const isPromptBlocked =
|
||||
error instanceof Error &&
|
||||
((error as { code?: string }).code === "invalid_prompt" ||
|
||||
/invalid_prompt|Request blocked/i.test(error.message));
|
||||
if (!zdrRejection && !isPromptBlocked && !isOpenAIResponsesStalePreviousResponseError(error)) {
|
||||
throw error;
|
||||
}
|
||||
// Server rejected the chain baseline: reset, count the failure (or
|
||||
|
||||
@@ -230,6 +230,58 @@ describe("openai-responses stateful chaining", () => {
|
||||
expect(JSON.stringify(sentRequests[2]?.input)).toContain("First question");
|
||||
expect(JSON.stringify(sentRequests[2]?.input)).toContain("Second question");
|
||||
});
|
||||
it("retries a blocked invalid_prompt previous_response_id with the full transcript", async () => {
|
||||
const sentRequests: Array<Record<string, unknown>> = [];
|
||||
const fetchMock = vi.fn(async (_input: string | URL | Request, init?: RequestInit) => {
|
||||
const request = JSON.parse(String(init?.body)) as Record<string, unknown>;
|
||||
sentRequests.push(request);
|
||||
if (typeof request.previous_response_id === "string") {
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
error: {
|
||||
message: "Request blocked.",
|
||||
type: "invalid_request_error",
|
||||
code: "invalid_prompt",
|
||||
},
|
||||
}),
|
||||
{ status: 400, headers: { "content-type": "application/json" } },
|
||||
);
|
||||
}
|
||||
return createStatefulSse(`Answer ${sentRequests.length}`, `resp_${sentRequests.length}`);
|
||||
}) as FetchImpl;
|
||||
const providerSessionState = new Map<string, ProviderSessionState>();
|
||||
const options = {
|
||||
apiKey: "test-key",
|
||||
sessionId: "stateful-blocked-session",
|
||||
providerSessionState,
|
||||
statefulResponses: true,
|
||||
reasoning: "low" as const,
|
||||
fetch: fetchMock,
|
||||
};
|
||||
|
||||
const firstUser = { role: "user" as const, content: "First question", timestamp: 1000 };
|
||||
const firstResponse = await streamOpenAIResponses(
|
||||
model,
|
||||
{ systemPrompt, messages: [firstUser] },
|
||||
options,
|
||||
).result();
|
||||
const secondResponse = await streamOpenAIResponses(
|
||||
model,
|
||||
{
|
||||
systemPrompt,
|
||||
messages: [firstUser, firstResponse, { role: "user", content: "Second question", timestamp: 1001 }],
|
||||
},
|
||||
options,
|
||||
).result();
|
||||
|
||||
expect(secondResponse.stopReason).toBe("stop");
|
||||
expect(JSON.stringify(secondResponse.content)).toContain("Answer 3");
|
||||
expect(sentRequests).toHaveLength(3);
|
||||
expect(sentRequests[1]?.previous_response_id).toBe("resp_1");
|
||||
expect(sentRequests[2]?.previous_response_id).toBeUndefined();
|
||||
expect(JSON.stringify(sentRequests[2]?.input)).toContain("First question");
|
||||
expect(JSON.stringify(sentRequests[2]?.input)).toContain("Second question");
|
||||
});
|
||||
|
||||
it("disables chaining for the session after repeated stale failures and stops forcing store", async () => {
|
||||
const sentRequests: Array<Record<string, unknown>> = [];
|
||||
|
||||
Reference in New Issue
Block a user