From 561afb401a4df9c8e677011b87e08f65b31cdfc3 Mon Sep 17 00:00:00 2001 From: Steven Ao Date: Tue, 11 Aug 2026 14:04:36 -0700 Subject: [PATCH] fix(opencode): detect Copilot PDF input support (#41522) --- .../src/plugin/github-copilot/models.ts | 5 +- .../test/plugin/github-copilot-models.test.ts | 68 +++++++++++++++++++ 2 files changed, 72 insertions(+), 1 deletion(-) diff --git a/packages/opencode/src/plugin/github-copilot/models.ts b/packages/opencode/src/plugin/github-copilot/models.ts index f3f32a0b0b..870e3f87a0 100644 --- a/packages/opencode/src/plugin/github-copilot/models.ts +++ b/packages/opencode/src/plugin/github-copilot/models.ts @@ -88,6 +88,9 @@ function build(key: string, remote: SelectableItem, url: string, prev?: Model): const image = (remote.capabilities.supports.vision ?? false) || (remote.capabilities.limits.vision?.supported_media_types ?? []).some((item) => item.startsWith("image/")) + const pdf = + (remote.capabilities.supports.vision ?? false) && + (remote.capabilities.limits.vision?.supported_media_types?.includes("application/pdf") ?? false) const isMsgApi = remote.supported_endpoints?.includes("/v1/messages") const endpoint: CopilotEndpoint | undefined = isMsgApi @@ -127,7 +130,7 @@ function build(key: string, remote: SelectableItem, url: string, prev?: Model): audio: false, image, video: false, - pdf: false, + pdf, }, output: { text: true, diff --git a/packages/opencode/test/plugin/github-copilot-models.test.ts b/packages/opencode/test/plugin/github-copilot-models.test.ts index e6d89fe01c..d968402e3b 100644 --- a/packages/opencode/test/plugin/github-copilot-models.test.ts +++ b/packages/opencode/test/plugin/github-copilot-models.test.ts @@ -187,6 +187,74 @@ test("converts Copilot AIC token prices to USD per million tokens", async () => expect(models["ignored-non-chat-record"]).toBeUndefined() }) +test("detects PDF input support when vision and media type are advertised", async () => { + globalThis.fetch = mock(() => + Promise.resolve( + new Response( + JSON.stringify({ + data: [ + { + model_picker_enabled: true, + id: "pdf-model", + name: "PDF Model", + version: "pdf-model-2026-06-01", + capabilities: { + family: "pdf-model", + limits: { + max_context_window_tokens: 128000, + max_output_tokens: 16384, + max_prompt_tokens: 128000, + vision: { + max_prompt_image_size: 10000000, + max_prompt_images: 10, + supported_media_types: ["application/pdf"], + }, + }, + supports: { + streaming: true, + vision: true, + tool_calls: true, + }, + }, + }, + { + model_picker_enabled: true, + id: "vision-only-model", + name: "Vision Only Model", + version: "vision-only-model-2026-06-01", + capabilities: { + family: "vision-only-model", + limits: { + max_context_window_tokens: 128000, + max_output_tokens: 16384, + max_prompt_tokens: 128000, + vision: { + max_prompt_image_size: 10000000, + max_prompt_images: 10, + supported_media_types: ["image/png"], + }, + }, + supports: { + streaming: true, + vision: true, + tool_calls: true, + }, + }, + }, + ], + }), + { status: 200 }, + ), + ), + ) as unknown as typeof fetch + + const models = (await CopilotModels.get("https://api.githubcopilot.com")).models + const model = models["pdf-model"] + + expect(model.capabilities.input.pdf).toBe(true) + expect(models["vision-only-model"].capabilities.input.pdf).toBe(false) +}) + test("uses zero cost when Copilot reports a zero billing batch size", async () => { globalThis.fetch = mock(() => Promise.resolve(