Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions packages/opencode/src/provider/transform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -895,8 +895,13 @@ export namespace ProviderTransform {
}
*/

// Convert integer enums to string enums for Google/Gemini
if (model.providerID === "google" || model.api.id.includes("gemini")) {
// Convert integer enums to string enums for Google/Gemini.
// Skip for Copilot models — they use OpenAI-compatible format and the
// Copilot API handles Gemini schema requirements internally.
if (
(model.providerID === "google" || model.api.id.includes("gemini")) &&
!model.providerID.includes("github-copilot")
) {
Comment on lines +898 to +904
Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New behavior (skipping sanitizeGemini for github-copilot* providers) isn’t covered by unit tests. Please add a ProviderTransform.schema test case where providerID is "github-copilot" (or "github-copilot-enterprise") and api.id includes "gemini", asserting that Gemini-specific sanitization (e.g., enum-to-string conversion or stripping properties/required) does NOT occur.

Copilot uses AI. Check for mistakes.
const sanitizeGemini = (obj: any): any => {
if (obj === null || typeof obj !== "object") {
return obj
Expand Down
57 changes: 57 additions & 0 deletions packages/opencode/test/provider/transform.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -618,6 +618,63 @@ describe("ProviderTransform.schema - gemini non-object properties removal", () =

expect(result.properties.data.properties).toBeDefined()
})

test("does not apply to github-copilot provider even when api.id includes gemini", () => {
const copilotGeminiModel = {
providerID: "github-copilot",
api: {
id: "gemini-3-flash-preview",
},
} as any

const schema = {
type: "object",
properties: {
status: {
type: "integer",
enum: [1, 2, 3],
},
data: {
type: "string",
properties: { invalid: { type: "string" } },
required: ["invalid"],
},
},
} as any

const result = ProviderTransform.schema(copilotGeminiModel, schema) as any

// enum values should NOT be converted to strings
expect(result.properties.status.type).toBe("integer")
expect(result.properties.status.enum).toEqual([1, 2, 3])
// properties/required should NOT be stripped from non-object types
expect(result.properties.data.properties).toBeDefined()
expect(result.properties.data.required).toBeDefined()
})

test("does not apply to github-copilot-enterprise provider", () => {
const copilotEnterpriseModel = {
providerID: "github-copilot-enterprise",
api: {
id: "gemini-3-flash-preview",
},
} as any

const schema = {
type: "object",
properties: {
count: {
type: "integer",
enum: [10, 20],
},
},
} as any

const result = ProviderTransform.schema(copilotEnterpriseModel, schema) as any

expect(result.properties.count.type).toBe("integer")
expect(result.properties.count.enum).toEqual([10, 20])
})
})

describe("ProviderTransform.message - DeepSeek reasoning content", () => {
Expand Down
Loading