Skip to content

Commit cf00736

Browse files
committed
fix(core): drop non-persisted reasoning items for responses
1 parent e9affe8 commit cf00736

2 files changed

Lines changed: 77 additions & 1 deletion

File tree

packages/core/src/agent.test.ts

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -319,7 +319,7 @@ vi.mock('@mariozechner/pi-ai', () => ({
319319
}),
320320
}));
321321

322-
import { generateViaAgent } from './agent.js';
322+
import { generateViaAgent, sanitizeOpenAIResponsesPayloadForStoreFalse } from './agent.js';
323323
import { applyComment } from './index.js';
324324

325325
const MODEL: ModelRef = { provider: 'anthropic', modelId: 'claude-sonnet-4-6' };
@@ -509,6 +509,61 @@ describe('generateViaAgent()', () => {
509509
expect(model?.compat?.supportsDeveloperRole).toBe(false);
510510
});
511511

512+
it('omits non-persisted reasoning items from OpenAI Responses store=false payloads', async () => {
513+
const payload = {
514+
model: 'gpt-5.5',
515+
store: false,
516+
input: [
517+
{ type: 'reasoning', id: 'rs_missing', encrypted_content: 'opaque' },
518+
{ type: 'message', id: 'msg_1', role: 'assistant' },
519+
{ type: 'function_call', id: 'fc_1', call_id: 'call_1' },
520+
{ type: 'function_call_output', call_id: 'call_1', output: 'ok' },
521+
{ role: 'user', content: [{ type: 'input_text', text: 'continue' }] },
522+
],
523+
};
524+
525+
const sanitized = sanitizeOpenAIResponsesPayloadForStoreFalse(payload) as typeof payload;
526+
527+
expect(sanitized.input.map((entry) => entry.type ?? entry.role)).toEqual([
528+
'message',
529+
'function_call',
530+
'function_call_output',
531+
'user',
532+
]);
533+
expect(payload.input[0]?.type).toBe('reasoning');
534+
});
535+
536+
it('passes the OpenAI Responses store=false sanitizer to agent runs', async () => {
537+
scriptedAgent = { assistantText: RESPONSE_WITH_ARTIFACT };
538+
await generateViaAgent({
539+
prompt: 'design a dashboard',
540+
history: [],
541+
model: { provider: 'codex-coproxy', modelId: 'gpt-5.5' },
542+
apiKey: 'sk-test',
543+
baseUrl: 'http://127.0.0.1:8538/v1',
544+
wire: 'openai-responses',
545+
});
546+
547+
const onPayload = agentCalls[0]?.options.onPayload;
548+
expect(onPayload).toBeDefined();
549+
const sanitized = onPayload?.(
550+
{
551+
store: false,
552+
input: [
553+
{ type: 'reasoning', id: 'rs_not_persisted' },
554+
{ role: 'user', content: [{ type: 'input_text', text: 'next' }] },
555+
],
556+
},
557+
agentCalls[0]?.options.initialState?.model ??
558+
(() => {
559+
throw new Error('expected agent model');
560+
})(),
561+
) as { input: Array<{ type?: string; role?: string }> };
562+
expect(sanitized.input).toEqual([
563+
{ role: 'user', content: [{ type: 'input_text', text: 'next' }] },
564+
]);
565+
});
566+
512567
it('uses conservative OpenAI-chat compat for DeepInfra agent models', async () => {
513568
scriptedAgent = { assistantText: RESPONSE_WITH_ARTIFACT };
514569
await generateViaAgent({

packages/core/src/agent.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,24 @@ interface PiModel {
173173
headers?: Record<string, string>;
174174
}
175175

176+
function isRecord(value: unknown): value is Record<string, unknown> {
177+
return typeof value === 'object' && value !== null;
178+
}
179+
180+
function isResponsesReasoningItem(value: unknown): boolean {
181+
return isRecord(value) && value['type'] === 'reasoning';
182+
}
183+
184+
export function sanitizeOpenAIResponsesPayloadForStoreFalse(payload: unknown): unknown {
185+
if (!isRecord(payload) || payload['store'] !== false || !Array.isArray(payload['input'])) {
186+
return payload;
187+
}
188+
return {
189+
...payload,
190+
input: payload['input'].filter((entry) => !isResponsesReasoningItem(entry)),
191+
};
192+
}
193+
176194
function apiForWire(wire: WireApi | undefined): string {
177195
if (wire === 'anthropic') return 'anthropic-messages';
178196
if (wire === 'openai-responses') return 'openai-responses';
@@ -1243,6 +1261,8 @@ export async function generateViaAgent(
12431261
messages: AgentMessage[],
12441262
retryThinkingLevel = thinkingLevel,
12451263
): Agent => {
1264+
const onPayload =
1265+
piModel.api === 'openai-responses' ? sanitizeOpenAIResponsesPayloadForStoreFalse : undefined;
12461266
const retryAgent = new Agent({
12471267
initialState: {
12481268
systemPrompt: augmentedSystemPrompt,
@@ -1274,6 +1294,7 @@ export async function generateViaAgent(
12741294
}
12751295
}
12761296
: () => initialApiKey || 'open-codesign-keyless',
1297+
...(onPayload !== undefined ? { onPayload } : {}),
12771298
});
12781299
if (deps.onEvent) {
12791300
retryAgent.subscribe((event) => deps.onEvent?.(event));

0 commit comments

Comments
 (0)