diff options
| author | Muhammad Mugni Hadi <[email protected]> | 2026-02-03 22:24:52 +0700 |
|---|---|---|
| committer | GitHub <[email protected]> | 2026-02-03 09:24:52 -0600 |
| commit | 3741516fe3e34be2655b0b6d3008ff7189eb8d90 (patch) | |
| tree | 6ac8f79818543896c00afc21d493ee641ec99cda | |
| parent | 76381f33d5b25f01867034767ab30650cb10fd32 (diff) | |
| download | opencode-3741516fe3e34be2655b0b6d3008ff7189eb8d90.tar.gz opencode-3741516fe3e34be2655b0b6d3008ff7189eb8d90.zip | |
fix: handle nested array items for Gemini schema validation (#11952)
| -rw-r--r-- | packages/opencode/src/provider/transform.ts | 11 | ||||
| -rw-r--r-- | packages/opencode/test/provider/transform.test.ts | 113 |
2 files changed, 122 insertions, 2 deletions
diff --git a/packages/opencode/src/provider/transform.ts b/packages/opencode/src/provider/transform.ts index b4f1aaca4..16f2bd3f2 100644 --- a/packages/opencode/src/provider/transform.ts +++ b/packages/opencode/src/provider/transform.ts @@ -768,8 +768,15 @@ export namespace ProviderTransform { result.required = result.required.filter((field: any) => field in result.properties) } - if (result.type === "array" && result.items == null) { - result.items = {} + if (result.type === "array") { + if (result.items == null) { + result.items = {} + } + // Ensure items has at least a type if it's an empty object + // This handles nested arrays like { type: "array", items: { type: "array", items: {} } } + if (typeof result.items === "object" && !Array.isArray(result.items) && !result.items.type) { + result.items.type = "string" + } } // Remove properties/required from non-object types (Gemini rejects these) diff --git a/packages/opencode/test/provider/transform.test.ts b/packages/opencode/test/provider/transform.test.ts index 0743049fe..0e0bb440a 100644 --- a/packages/opencode/test/provider/transform.test.ts +++ b/packages/opencode/test/provider/transform.test.ts @@ -293,6 +293,119 @@ describe("ProviderTransform.schema - gemini array items", () => { }) }) +describe("ProviderTransform.schema - gemini nested array items", () => { + const geminiModel = { + providerID: "google", + api: { + id: "gemini-3-pro", + }, + } as any + + test("adds type to 2D array with empty inner items", () => { + const schema = { + type: "object", + properties: { + values: { + type: "array", + items: { + type: "array", + items: {}, // Empty items object + }, + }, + }, + } as any + + const result = ProviderTransform.schema(geminiModel, schema) as any + + // Inner items should have a default type + expect(result.properties.values.items.items.type).toBe("string") + }) + + test("adds items and type to 2D array with missing inner items", () => { + const schema = { + type: "object", + properties: { + data: { + type: "array", + items: { type: "array" }, // No items at all + }, + }, + } as any + + const result = ProviderTransform.schema(geminiModel, schema) as any + + expect(result.properties.data.items.items).toBeDefined() + expect(result.properties.data.items.items.type).toBe("string") + }) + + test("handles deeply nested arrays (3D)", () => { + const schema = { + type: "object", + properties: { + matrix: { + type: "array", + items: { + type: "array", + items: { + type: "array", + // No items + }, + }, + }, + }, + } as any + + const result = ProviderTransform.schema(geminiModel, schema) as any + + expect(result.properties.matrix.items.items.items).toBeDefined() + expect(result.properties.matrix.items.items.items.type).toBe("string") + }) + + test("preserves existing item types in nested arrays", () => { + const schema = { + type: "object", + properties: { + numbers: { + type: "array", + items: { + type: "array", + items: { type: "number" }, // Has explicit type + }, + }, + }, + } as any + + const result = ProviderTransform.schema(geminiModel, schema) as any + + // Should preserve the explicit type + expect(result.properties.numbers.items.items.type).toBe("number") + }) + + test("handles mixed nested structures with objects and arrays", () => { + const schema = { + type: "object", + properties: { + spreadsheetData: { + type: "object", + properties: { + rows: { + type: "array", + items: { + type: "array", + items: {}, // Empty items + }, + }, + }, + }, + }, + } as any + + const result = ProviderTransform.schema(geminiModel, schema) as any + + expect(result.properties.spreadsheetData.properties.rows.items.items.type).toBe("string") + }) +}) + describe("ProviderTransform.schema - gemini non-object properties removal", () => { const geminiModel = { providerID: "google", |
