Skip to content

Commit f65aca6

Browse files
authored
fix(qa): issue unique mock tool call ids (#96338)
1 parent a2725b6 commit f65aca6

2 files changed

Lines changed: 28 additions & 3 deletions

File tree

extensions/qa-lab/src/providers/mock-openai/server.test.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1014,6 +1014,27 @@ describe("qa mock openai server", () => {
10141014
expect(firstPayload.output?.[0]?.call_id).not.toBe(secondPayload.output?.[0]?.call_id);
10151015
});
10161016

1017+
it("uses unique ids for repeated identical tool calls", async () => {
1018+
const server = await startMockServer();
1019+
const body = {
1020+
stream: false,
1021+
model: "gpt-5.5",
1022+
input: [makeUserInput("Read QA_KICKOFF_TASK.md, then answer with exactly QA-READ-OK.")],
1023+
};
1024+
1025+
const first = await expectResponsesJson<{ output?: Array<{ call_id?: string }> }>(server, body);
1026+
const second = await expectResponsesJson<{ output?: Array<{ call_id?: string }> }>(
1027+
server,
1028+
body,
1029+
);
1030+
1031+
const firstCallId = first.output?.[0]?.call_id;
1032+
const secondCallId = second.output?.[0]?.call_id;
1033+
expect(firstCallId).toMatch(/^call_mock_read_/);
1034+
expect(secondCallId).toMatch(/^call_mock_read_/);
1035+
expect(firstCallId).not.toBe(secondCallId);
1036+
});
1037+
10171038
it("continues repo-contract followthrough when a retry user item follows tool output", async () => {
10181039
const server = await startQaMockOpenAiServer({
10191040
host: "127.0.0.1",

extensions/qa-lab/src/providers/mock-openai/server.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import { writeJson } from "../shared/http-json.js";
1010

1111
type ResponsesInputItem = Record<string, unknown>;
1212

13+
let mockFunctionCallSequence = 0;
14+
1315
type StreamEvent =
1416
| { type: "response.output_item.added"; item: Record<string, unknown> }
1517
| {
@@ -773,8 +775,10 @@ function buildMockFunctionCall(name: string, args: Record<string, unknown>) {
773775
.update(serialized)
774776
.digest("hex")
775777
.slice(0, 10);
776-
const callId = `call_mock_${name}_${callSuffix}`;
777-
const itemId = `fc_mock_${name}_${callSuffix}`;
778+
const sequence = ++mockFunctionCallSequence;
779+
const uniqueSuffix = `${callSuffix}_${sequence}`;
780+
const callId = `call_mock_${name}_${uniqueSuffix}`;
781+
const itemId = `fc_mock_${name}_${uniqueSuffix}`;
778782
const item = {
779783
type: "function_call",
780784
id: itemId,
@@ -786,7 +790,7 @@ function buildMockFunctionCall(name: string, args: Record<string, unknown>) {
786790
callId,
787791
item,
788792
itemId,
789-
responseId: `resp_mock_${name}_${callSuffix}`,
793+
responseId: `resp_mock_${name}_${uniqueSuffix}`,
790794
serialized,
791795
};
792796
}

0 commit comments

Comments
 (0)