


























@@ -1059,7 +1059,7 @@ describe("openai transport stream", () => {
10591059expect(params.input?.some((item) => item.role === "system" || item.role === "developer")).toBe(
10601060false,
10611061);
1062-expect(params.prompt_cache_key).toBe("session-123");
1062+expect(params).not.toHaveProperty("prompt_cache_key");
10631063expect(params.store).toBe(false);
10641064expect(params).not.toHaveProperty("metadata");
10651065expect(params).not.toHaveProperty("max_output_tokens");
@@ -1097,7 +1097,7 @@ describe("openai transport stream", () => {
10971097payload,
10981098);
109910991100-expect(sanitized.prompt_cache_key).toBe("session-123");
1100+expect(sanitized).not.toHaveProperty("prompt_cache_key");
11011101expect(sanitized).not.toHaveProperty("metadata");
11021102expect(sanitized).not.toHaveProperty("max_output_tokens");
11031103expect(sanitized).not.toHaveProperty("prompt_cache_retention");
@@ -1178,6 +1178,197 @@ describe("openai transport stream", () => {
11781178expect(sanitized).toEqual(payload);
11791179});
118011801181+it("omits prior Responses replay item ids for native Codex responses", () => {
1182+const params = buildOpenAIResponsesParams(
1183+{
1184+id: "gpt-5.4",
1185+name: "GPT-5.4",
1186+api: "openai-codex-responses",
1187+provider: "openai-codex",
1188+baseUrl: "https://chatgpt.com/backend-api",
1189+reasoning: true,
1190+input: ["text"],
1191+cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
1192+contextWindow: 200000,
1193+maxTokens: 8192,
1194+} satisfies Model<"openai-codex-responses">,
1195+{
1196+systemPrompt: "system",
1197+messages: [
1198+{
1199+role: "assistant",
1200+api: "openai-codex-responses",
1201+provider: "openai-codex",
1202+model: "gpt-5.4",
1203+usage: {
1204+input: 0,
1205+output: 0,
1206+cacheRead: 0,
1207+cacheWrite: 0,
1208+totalTokens: 0,
1209+cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
1210+},
1211+stopReason: "toolUse",
1212+timestamp: 1,
1213+content: [
1214+{
1215+type: "thinking",
1216+thinking: "Need a tool.",
1217+thinkingSignature: JSON.stringify({
1218+type: "reasoning",
1219+id: "rs_prior",
1220+encrypted_content: "ciphertext",
1221+}),
1222+},
1223+{
1224+type: "text",
1225+text: "Checking the price.",
1226+textSignature: JSON.stringify({
1227+v: 1,
1228+id: "msg_prior",
1229+phase: "commentary",
1230+}),
1231+},
1232+{
1233+type: "toolCall",
1234+id: "call_abc|fc_prior",
1235+name: "price_lookup",
1236+arguments: { symbol: "SOL" },
1237+},
1238+],
1239+},
1240+{
1241+role: "toolResult",
1242+toolCallId: "call_abc|fc_prior",
1243+toolName: "price_lookup",
1244+content: [{ type: "text", text: "$83.95" }],
1245+isError: false,
1246+timestamp: 2,
1247+},
1248+{ role: "user", content: "what is the capital of the philippines", timestamp: 3 },
1249+],
1250+tools: [],
1251+} as never,
1252+{ sessionId: "session-123" },
1253+) as {
1254+input?: Array<{
1255+type?: string;
1256+role?: string;
1257+id?: string;
1258+call_id?: string;
1259+phase?: string;
1260+}>;
1261+};
1262+1263+expect(params.input?.some((item) => item.type === "reasoning")).toBe(false);
1264+const assistantMessage = params.input?.find(
1265+(item) => item.type === "message" && item.role === "assistant",
1266+);
1267+expect(assistantMessage).toMatchObject({
1268+type: "message",
1269+role: "assistant",
1270+phase: "commentary",
1271+});
1272+expect(assistantMessage?.id).toBeUndefined();
1273+const functionCall = params.input?.find((item) => item.type === "function_call");
1274+expect(functionCall).toMatchObject({
1275+type: "function_call",
1276+call_id: "call_abc",
1277+});
1278+expect(functionCall?.id).toBeUndefined();
1279+});
1280+1281+it("preserves prior Responses replay item ids for custom Codex-compatible responses", () => {
1282+const params = buildOpenAIResponsesParams(
1283+{
1284+id: "gpt-5.4",
1285+name: "GPT-5.4",
1286+api: "openai-codex-responses",
1287+provider: "openai-codex",
1288+baseUrl: "https://proxy.example.com/v1",
1289+reasoning: true,
1290+input: ["text"],
1291+cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
1292+contextWindow: 200000,
1293+maxTokens: 8192,
1294+} satisfies Model<"openai-codex-responses">,
1295+{
1296+systemPrompt: "system",
1297+messages: [
1298+{
1299+role: "assistant",
1300+api: "openai-codex-responses",
1301+provider: "openai-codex",
1302+model: "gpt-5.4",
1303+usage: {
1304+input: 0,
1305+output: 0,
1306+cacheRead: 0,
1307+cacheWrite: 0,
1308+totalTokens: 0,
1309+cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
1310+},
1311+stopReason: "toolUse",
1312+timestamp: 1,
1313+content: [
1314+{
1315+type: "thinking",
1316+thinking: "Need a tool.",
1317+thinkingSignature: JSON.stringify({
1318+type: "reasoning",
1319+id: "rs_prior",
1320+encrypted_content: "ciphertext",
1321+}),
1322+},
1323+{
1324+type: "text",
1325+text: "Checking the price.",
1326+textSignature: JSON.stringify({
1327+v: 1,
1328+id: "msg_prior",
1329+phase: "commentary",
1330+}),
1331+},
1332+{
1333+type: "toolCall",
1334+id: "call_abc|fc_prior",
1335+name: "price_lookup",
1336+arguments: { symbol: "SOL" },
1337+},
1338+],
1339+},
1340+],
1341+tools: [],
1342+} as never,
1343+{ sessionId: "session-123" },
1344+) as {
1345+input?: Array<{
1346+type?: string;
1347+role?: string;
1348+id?: string;
1349+call_id?: string;
1350+phase?: string;
1351+}>;
1352+};
1353+1354+expect(params.input?.some((item) => item.type === "reasoning")).toBe(true);
1355+const assistantMessage = params.input?.find(
1356+(item) => item.type === "message" && item.role === "assistant",
1357+);
1358+expect(assistantMessage).toMatchObject({
1359+type: "message",
1360+role: "assistant",
1361+id: "msg_prior",
1362+phase: "commentary",
1363+});
1364+const functionCall = params.input?.find((item) => item.type === "function_call");
1365+expect(functionCall).toMatchObject({
1366+type: "function_call",
1367+id: "fc_prior",
1368+call_id: "call_abc",
1369+});
1370+});
1371+11811372it("adds minimal user input for Codex responses when only the system prompt is present", () => {
11821373const params = buildOpenAIResponsesParams(
11831374{
@@ -1492,7 +1683,7 @@ describe("openai transport stream", () => {
14921683baseUrl: "https://proxy.example.com/v1",
14931684},
14941685},
1495-])("replays assistant phase metadata for $label responses payloads", ({ model }) => {
1686+])("replays assistant phase metadata for $label responses payloads", ({ label, model }) => {
14961687const params = buildOpenAIResponsesParams(
14971688{
14981689 ...model,
@@ -1548,9 +1739,13 @@ describe("openai transport stream", () => {
15481739const assistantItem = params.input?.find((item) => item.role === "assistant");
15491740expect(assistantItem).toMatchObject({
15501741role: "assistant",
1551-id: "msg_commentary",
15521742phase: "commentary",
15531743});
1744+if (label === "openai-codex") {
1745+expect(assistantItem?.id).toBeUndefined();
1746+} else {
1747+expect(assistantItem?.id).toBe("msg_commentary");
1748+}
15541749});
1555175015561751it("strips the internal cache boundary from OpenAI system prompts", () => {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。