summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorMuhammad Mugni Hadi <[email protected]>2026-02-03 22:24:52 +0700
committerGitHub <[email protected]>2026-02-03 09:24:52 -0600
commit3741516fe3e34be2655b0b6d3008ff7189eb8d90 (patch)
tree6ac8f79818543896c00afc21d493ee641ec99cda
parent76381f33d5b25f01867034767ab30650cb10fd32 (diff)
downloadopencode-3741516fe3e34be2655b0b6d3008ff7189eb8d90.tar.gz
opencode-3741516fe3e34be2655b0b6d3008ff7189eb8d90.zip
fix: handle nested array items for Gemini schema validation (#11952)
-rw-r--r--packages/opencode/src/provider/transform.ts11
-rw-r--r--packages/opencode/test/provider/transform.test.ts113
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",