Skip to content

Commit 8ed4722

Browse files
committed
fix(openai-codex): address Luna Responses Lite review feedback
- Capture telemetry when transformLunaResponsesLiteBody throws in createMessage - Drop weak Record<string, any> alias from transformLunaResponsesLiteBody signature - Document intentional tool_choice/parallel_tool_calls and reasoning context overrides - Strengthen tests: full input array assertion, reasoning context overwrite case, retry header coverage, single-build spy, and uuidv7 session shape check
1 parent e5acba2 commit 8ed4722

2 files changed

Lines changed: 48 additions & 12 deletions

File tree

src/api/providers/__tests__/openai-codex.spec.ts

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,19 @@ describe("transformLunaResponsesLiteBody", () => {
275275
})
276276
})
277277

278+
it("overwrites a pre-existing reasoning context with all_turns", () => {
279+
const result = transformLunaResponsesLiteBody(
280+
{
281+
model: "gpt-5.6-luna",
282+
input: [{ role: "user", content: [{ type: "input_text", text: "Hello" }] }],
283+
reasoning: { effort: "high", context: "current_turn" },
284+
},
285+
"session-1",
286+
)
287+
288+
expect(result.reasoning).toEqual({ effort: "high", context: "all_turns" })
289+
})
290+
278291
it.each([
279292
["input", { input: "invalid" }, "input must be an array"],
280293
["tools", { input: [], tools: {} }, "tools must be an array when provided"],
@@ -325,12 +338,17 @@ describe("OpenAiCodexHandler Luna Responses Lite requests", () => {
325338
})
326339
expect(body).not.toHaveProperty("tools")
327340
expect(body).not.toHaveProperty("instructions")
341+
expect(body.input).toHaveLength(3)
328342
expect(body.input[0]).toMatchObject({ type: "additional_tools", role: "developer" })
329343
expect(body.input[1]).toEqual({
330344
type: "message",
331345
role: "developer",
332346
content: [{ type: "input_text", text: "Luna instructions" }],
333347
})
348+
expect(body.input[2]).toEqual({
349+
role: "user",
350+
content: [{ type: "input_text", text: "Hello" }],
351+
})
334352
expect(options.headers).toMatchObject({
335353
originator: "zoo-code",
336354
session_id: "task-luna",
@@ -389,6 +407,7 @@ describe("OpenAiCodexHandler Luna Responses Lite requests", () => {
389407

390408
it("preserves Luna session affinity while retrying with refreshed authentication", async () => {
391409
const handler = new OpenAiCodexHandler({ apiModelId: "gpt-5.6-luna" })
410+
const transformSpy = vitest.spyOn(handler as any, "buildLunaRequestBody")
392411
vitest.spyOn(openAiCodexOAuthManager, "getAccessToken").mockResolvedValue("expired-token")
393412
vitest.spyOn(openAiCodexOAuthManager, "forceRefreshAccessToken").mockResolvedValue("refreshed-token")
394413
vitest.spyOn(openAiCodexOAuthManager, "getAccountId").mockResolvedValue("acct_test")
@@ -432,16 +451,21 @@ describe("OpenAiCodexHandler Luna Responses Lite requests", () => {
432451
const retryBody = JSON.parse(retryOptions.body)
433452

434453
expect(retryBody).toEqual(firstBody)
454+
expect(transformSpy).toHaveBeenCalledTimes(1)
435455
expect(firstBody.prompt_cache_key).toBe("task-retry")
436456
expect(firstOptions.headers).toMatchObject({
437457
Authorization: "Bearer expired-token",
438458
"session-id": "task-retry",
439459
"x-session-affinity": "task-retry",
460+
version: "0.144.0",
461+
"x-openai-internal-codex-responses-lite": "true",
440462
})
441463
expect(retryOptions.headers).toMatchObject({
442464
Authorization: "Bearer refreshed-token",
443465
"session-id": "task-retry",
444466
"x-session-affinity": "task-retry",
467+
version: "0.144.0",
468+
"x-openai-internal-codex-responses-lite": "true",
445469
})
446470
})
447471

@@ -499,7 +523,7 @@ describe("OpenAiCodexHandler Luna Responses Lite requests", () => {
499523
const fetchOptions = mockFetch.mock.calls[0][1]
500524
const body = JSON.parse(fetchOptions.body)
501525
const sessionId = body.prompt_cache_key
502-
expect(sessionId).toEqual(expect.any(String))
526+
expect(sessionId).toMatch(/^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i)
503527
expect(body).toMatchObject({
504528
model: "gpt-5.6-luna",
505529
stream: false,

src/api/providers/openai-codex.ts

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,6 @@ const CODEX_API_BASE_URL = "https://chatgpt.com/backend-api/codex"
3737
const LUNA_MODEL_ID = "gpt-5.6-luna"
3838
const LUNA_CODEX_VERSION = "0.144.0"
3939

40-
type ResponsesRequestBody = Record<string, any>
41-
4240
function stripInputImageDetail(value: any): any {
4341
if (Array.isArray(value)) {
4442
return value.map(stripInputImageDetail)
@@ -55,10 +53,7 @@ function stripInputImageDetail(value: any): any {
5553
)
5654
}
5755

58-
export function transformLunaResponsesLiteBody(
59-
requestBody: ResponsesRequestBody,
60-
effectiveSessionId: string,
61-
): ResponsesRequestBody {
56+
export function transformLunaResponsesLiteBody(requestBody: any, effectiveSessionId: string): any {
6257
if (!Array.isArray(requestBody.input)) {
6358
throw new Error("Invalid gpt-5.6-luna Responses Lite request: input must be an array.")
6459
}
@@ -91,9 +86,13 @@ export function transformLunaResponsesLiteBody(
9186
: []),
9287
...transformedInput,
9388
],
89+
// Luna Responses Lite requires these exact values, so they intentionally
90+
// override any caller-supplied tool_choice or parallel_tool_calls.
9491
tool_choice: "auto",
9592
parallel_tool_calls: false,
9693
prompt_cache_key: effectiveSessionId,
94+
// Luna Responses Lite requires reasoning context "all_turns"; this intentionally
95+
// overwrites any context value already present in the incoming reasoning config.
9796
reasoning: { ...reasoning, context: "all_turns" },
9897
}
9998
}
@@ -248,10 +247,19 @@ export class OpenAiCodexHandler extends BaseProvider implements SingleCompletion
248247
// Notably: max_output_tokens and prompt_cache_retention may be rejected
249248
const effectiveSessionId = metadata?.taskId || this.sessionId
250249
const baseRequestBody = this.buildRequestBody(model, formattedInput, systemPrompt, reasoningEffort, metadata)
251-
const requestBody =
252-
model.id === LUNA_MODEL_ID
253-
? transformLunaResponsesLiteBody(baseRequestBody, effectiveSessionId)
254-
: baseRequestBody
250+
let requestBody: any
251+
try {
252+
requestBody =
253+
model.id === LUNA_MODEL_ID
254+
? this.buildLunaRequestBody(baseRequestBody, effectiveSessionId)
255+
: baseRequestBody
256+
} catch (error) {
257+
const message = error instanceof Error ? error.message : String(error)
258+
TelemetryService.instance.captureException(
259+
new ApiProviderError(message, this.providerName, model.id, "createMessage"),
260+
)
261+
throw error
262+
}
255263

256264
// Make the request with retry on auth failure
257265
for (let attempt = 0; attempt < 2; attempt++) {
@@ -281,6 +289,10 @@ export class OpenAiCodexHandler extends BaseProvider implements SingleCompletion
281289
}
282290
}
283291

292+
private buildLunaRequestBody(baseRequestBody: any, effectiveSessionId: string): any {
293+
return transformLunaResponsesLiteBody(baseRequestBody, effectiveSessionId)
294+
}
295+
284296
private buildRequestBody(
285297
model: OpenAiCodexModel,
286298
formattedInput: any,
@@ -1272,7 +1284,7 @@ export class OpenAiCodexHandler extends BaseProvider implements SingleCompletion
12721284

12731285
const requestBody =
12741286
model.id === LUNA_MODEL_ID
1275-
? transformLunaResponsesLiteBody(baseRequestBody, this.sessionId)
1287+
? this.buildLunaRequestBody(baseRequestBody, this.sessionId)
12761288
: baseRequestBody
12771289

12781290
const url = `${CODEX_API_BASE_URL}/responses`

0 commit comments

Comments
 (0)