Skip to content

Commit e366430

Browse files
committed
support gpt 5.5
1 parent 5ebab3e commit e366430

4 files changed

Lines changed: 139 additions & 3 deletions

File tree

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@ New endpoints for monitoring your Copilot usage and quotas.
215215
| Model | Status | Notes |
216216
| ----- | ------ | ----- |
217217
| `gpt-5.4` | Supported | Uses the standard GPT-5 chat/completions path. |
218+
| `gpt-5.5` | Supported | Uses the standard GPT-5 chat/completions path and resolves upstream snapshots such as `gpt-5.5-2026-04-23`. |
218219
| `gpt-5.3-codex` | Supported | Uses the Responses API bridge internally. |
219220
| `gpt-5.4-mini` | Supported | Uses the Responses API bridge internally. |
220221
| `claude-opus-4.7` | Supported | Snapshot aliases such as `claude-opus-4-7-20260417` resolve to this model, with `low`, `medium`, `high`, `xhigh`, and `max` effort support. |
@@ -230,6 +231,7 @@ Reasoning effort is model-specific.
230231
| Model | Supported values | Default |
231232
| ----- | ---------------- | ------- |
232233
| `gpt-5.4` | `low`, `medium`, `high`, `xhigh` | `medium` |
234+
| `gpt-5.5` | `none`, `low`, `medium`, `high`, `xhigh` | `medium` |
233235
| `gpt-5.3-codex` | `low`, `medium`, `high`, `xhigh` | `medium` |
234236
| `gpt-5.4-mini` | `none`, `low`, `medium` | `medium` |
235237
| `claude-opus-4.7` | `low`, `medium`, `high`, `xhigh`, `max` | `medium` |

src/services/copilot/create-chat-completions.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ const getAllowedReasoningEfforts = (
3838
): Array<
3939
Exclude<ChatCompletionsPayload["reasoning_effort"], null | undefined>
4040
> => {
41+
if (modelId.startsWith("gpt-5.5")) {
42+
return ["none", "low", "medium", "high", "xhigh"]
43+
}
44+
4145
if (modelId.startsWith("gpt-5.4-mini")) {
4246
return ["none", "low", "medium"]
4347
}

tests/anthropic-request.test.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,23 @@ describe("Anthropic model resolution", () => {
122122
expect(openAIPayload.model).toBe("gpt-5")
123123
})
124124

125+
test("should resolve GPT-5.5 aliases to the GPT-5 Copilot model", () => {
126+
state.models = {
127+
object: "list",
128+
data: [createModel("gpt-5"), createModel("gpt-5-mini")],
129+
}
130+
131+
const anthropicPayload: AnthropicMessagesPayload = {
132+
model: "gpt-5.5",
133+
messages: [{ role: "user", content: "Hello!" }],
134+
max_tokens: 0,
135+
}
136+
137+
const openAIPayload = translateToOpenAI(anthropicPayload)
138+
139+
expect(openAIPayload.model).toBe("gpt-5")
140+
})
141+
125142
test("should resolve Claude snapshot aliases to the base Copilot model", () => {
126143
state.models = {
127144
object: "list",

tests/create-chat-completions.test.ts

Lines changed: 116 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ state.copilotToken = "test-token"
1313
state.vsCodeVersion = "1.0.0"
1414
state.accountType = "individual"
1515

16+
const isolatedHome = path.join(os.tmpdir(), "copilot-api-test-home")
17+
await mkdir(isolatedHome, { recursive: true })
18+
process.env.HOME = isolatedHome
19+
1620
const createMockResponse = (jsonBody: unknown, ok: boolean = true) => ({
1721
ok,
1822
status: ok ? 200 : 400,
@@ -41,11 +45,9 @@ const fetchMock = mock(
4145
afterEach(() => {
4246
fetchMock.mockClear()
4347
delete process.env.COPILOT_REASONING_EFFORT
44-
process.env.HOME = originalHome
48+
process.env.HOME = isolatedHome
4549
})
4650

47-
const originalHome = process.env.HOME
48-
4951
test("sets X-Initiator to agent if tool/assistant present", async () => {
5052
const payload: ChatCompletionsPayload = {
5153
messages: [
@@ -96,6 +98,45 @@ test("uses max_completion_tokens for GPT-5 family models", async () => {
9698
expect(body.max_tokens).toBeUndefined()
9799
})
98100

101+
test("uses max_completion_tokens for gpt-5.5 with explicit reasoning effort", async () => {
102+
const payload: ChatCompletionsPayload = {
103+
messages: [{ role: "user", content: "hi" }],
104+
model: "gpt-5.5",
105+
max_tokens: 384,
106+
reasoning_effort: "high",
107+
}
108+
109+
await createChatCompletions(payload)
110+
111+
expect(fetchMock).toHaveBeenCalled()
112+
const body = JSON.parse(
113+
(fetchMock.mock.calls[0][1] as { body: string }).body,
114+
) as Record<string, unknown>
115+
116+
expect(body.max_completion_tokens).toBe(384)
117+
expect(body.max_tokens).toBeUndefined()
118+
expect(body.reasoning_effort).toBe("high")
119+
})
120+
121+
test("passes through reasoning_effort none for gpt-5.5", async () => {
122+
const payload: ChatCompletionsPayload = {
123+
messages: [{ role: "user", content: "hi" }],
124+
model: "gpt-5.5",
125+
max_tokens: 128,
126+
reasoning_effort: "none",
127+
}
128+
129+
await createChatCompletions(payload)
130+
131+
expect(fetchMock).toHaveBeenCalled()
132+
const body = JSON.parse(
133+
(fetchMock.mock.calls[0][1] as { body: string }).body,
134+
) as Record<string, unknown>
135+
136+
expect(body.max_completion_tokens).toBe(128)
137+
expect(body.reasoning_effort).toBe("none")
138+
})
139+
99140
test("uses max_completion_tokens for gpt-5.3-codex", async () => {
100141
fetchMock.mockImplementationOnce(
101142
(_url: string, opts: { headers: Record<string, string>; body?: string }) =>
@@ -271,6 +312,42 @@ test("keeps max_tokens for non GPT-5 models", async () => {
271312
expect(body.max_completion_tokens).toBeUndefined()
272313
})
273314

315+
test("keeps max_tokens and omits reasoning_effort for gemini-3.1-pro-preview", async () => {
316+
const payload: ChatCompletionsPayload = {
317+
messages: [{ role: "user", content: "hi" }],
318+
model: "gemini-3.1-pro-preview",
319+
max_tokens: 256,
320+
}
321+
322+
await createChatCompletions(payload)
323+
324+
const body = JSON.parse(
325+
(fetchMock.mock.calls[0][1] as { body: string }).body,
326+
) as Record<string, unknown>
327+
328+
expect(body.max_tokens).toBe(256)
329+
expect(body.max_completion_tokens).toBeUndefined()
330+
expect(body.reasoning_effort).toBeUndefined()
331+
})
332+
333+
test("keeps max_tokens and omits reasoning_effort for gemini-3-flash-preview", async () => {
334+
const payload: ChatCompletionsPayload = {
335+
messages: [{ role: "user", content: "hi" }],
336+
model: "gemini-3-flash-preview",
337+
max_tokens: 256,
338+
}
339+
340+
await createChatCompletions(payload)
341+
342+
const body = JSON.parse(
343+
(fetchMock.mock.calls[0][1] as { body: string }).body,
344+
) as Record<string, unknown>
345+
346+
expect(body.max_tokens).toBe(256)
347+
expect(body.max_completion_tokens).toBeUndefined()
348+
expect(body.reasoning_effort).toBeUndefined()
349+
})
350+
274351
test("passes through explicit reasoning_effort for GPT-5 family models", async () => {
275352
const payload: ChatCompletionsPayload = {
276353
messages: [{ role: "user", content: "hi" }],
@@ -559,6 +636,42 @@ test("uses COPILOT_REASONING_EFFORT from Claude settings.json", async () => {
559636
}
560637
})
561638

639+
test("uses COPILOT_REASONING_EFFORT from Claude settings.json for gpt-5.5", async () => {
640+
const tempHome = await mkdtemp(path.join(os.tmpdir(), "claude-settings-"))
641+
const claudeDirectory = path.join(tempHome, ".claude")
642+
643+
try {
644+
await mkdir(claudeDirectory, { recursive: true })
645+
await writeFile(
646+
path.join(claudeDirectory, "settings.json"),
647+
JSON.stringify({
648+
env: {
649+
COPILOT_REASONING_EFFORT: "none",
650+
},
651+
}),
652+
)
653+
654+
process.env.HOME = tempHome
655+
656+
const payload: ChatCompletionsPayload = {
657+
messages: [{ role: "user", content: "hi" }],
658+
model: "gpt-5.5",
659+
max_tokens: 128,
660+
}
661+
662+
await createChatCompletions(payload)
663+
664+
const body = JSON.parse(
665+
(fetchMock.mock.calls[0][1] as { body: string }).body,
666+
) as Record<string, unknown>
667+
668+
expect(body.max_completion_tokens).toBe(128)
669+
expect(body.reasoning_effort).toBe("none")
670+
} finally {
671+
await rm(tempHome, { recursive: true, force: true })
672+
}
673+
})
674+
562675
test("uses COPILOT_REASONING_EFFORT from Claude settings.json for claude-opus-4.7", async () => {
563676
const tempHome = await mkdtemp(path.join(os.tmpdir(), "claude-settings-"))
564677
const claudeDirectory = path.join(tempHome, ".claude")

0 commit comments

Comments
 (0)