mirror of
https://github.com/anomalyco/opencode.git
synced 2026-05-13 15:44:56 +00:00
wip
This commit is contained in:
@@ -1089,19 +1089,37 @@ export function schema(model: Provider.Model, schema: JSONSchema.BaseSchema | JS
|
||||
}
|
||||
*/
|
||||
|
||||
// Moonshot models want their tools in MFJS format: https://github.com/MoonshotAI/walle/blob/main/docs/mfjs-spec.md
|
||||
if (model.providerID === "moonshotai" || model.api.id.toLowerCase().includes("kimi")) {
|
||||
const sanitizeMoonshot = (obj: unknown): unknown => {
|
||||
if (obj === null || typeof obj !== "object") return obj
|
||||
if (Array.isArray(obj)) return obj.map(sanitizeMoonshot)
|
||||
const isRecord = (obj: unknown): obj is Record<string, unknown> =>
|
||||
typeof obj === "object" && obj !== null && !Array.isArray(obj)
|
||||
const sanitizeMoonshot = (obj: unknown): void => {
|
||||
if (Array.isArray(obj)) return obj.forEach(sanitizeMoonshot)
|
||||
if (!isRecord(obj)) return
|
||||
// Moonshot expands $ref before validation and rejects sibling keywords like description on the same node.
|
||||
if ("$ref" in obj && typeof obj.$ref === "string") return { $ref: obj.$ref }
|
||||
const result = Object.fromEntries(Object.entries(obj).map(([key, value]) => [key, sanitizeMoonshot(value)]))
|
||||
if (typeof obj.$ref === "string") {
|
||||
for (const key of Object.keys(obj)) {
|
||||
if (key !== "$ref") delete obj[key]
|
||||
}
|
||||
return
|
||||
}
|
||||
for (const key of ["title", "$comment", "format"]) {
|
||||
delete obj[key]
|
||||
}
|
||||
for (const key of ["exclusiveMinimum", "exclusiveMaximum", "minContains", "maxContains"]) {
|
||||
delete obj[key]
|
||||
}
|
||||
// MFJS does not support tuple-style arrays (`prefixItems`) or open-ended tuple controls.
|
||||
const prefixItems = Array.isArray(obj.prefixItems) ? obj.prefixItems : undefined
|
||||
delete obj.unevaluatedItems
|
||||
Object.values(obj).forEach(sanitizeMoonshot)
|
||||
// MFJS does not support tuple-style `items` arrays; it requires one schema object for all array items.
|
||||
if (Array.isArray(result.items)) result.items = result.items[0] ?? {}
|
||||
return result
|
||||
if (Array.isArray(obj.items)) obj.items = obj.items[0] ?? {}
|
||||
if (prefixItems && !isRecord(obj.items)) obj.items = prefixItems[0] ?? {}
|
||||
delete obj.prefixItems
|
||||
}
|
||||
|
||||
schema = sanitizeMoonshot(schema) as JSONSchema.BaseSchema | JSONSchema7
|
||||
sanitizeMoonshot(schema)
|
||||
}
|
||||
|
||||
// Convert integer enums to string enums for Google/Gemini
|
||||
|
||||
@@ -997,6 +997,80 @@ describe("ProviderTransform.schema - moonshot $ref siblings", () => {
|
||||
type: "number",
|
||||
})
|
||||
})
|
||||
|
||||
test("converts prefixItems tuples to a single item schema", () => {
|
||||
const result = ProviderTransform.schema(moonshotModel, {
|
||||
type: "object",
|
||||
properties: {
|
||||
renderedSize: {
|
||||
description: "Rendered size [width, height] in px",
|
||||
type: "array",
|
||||
prefixItems: [{ type: "number", title: "Width" }, { type: "number" }],
|
||||
unevaluatedItems: false,
|
||||
},
|
||||
},
|
||||
} as any) as any
|
||||
|
||||
expect(result.properties.renderedSize.prefixItems).toBeUndefined()
|
||||
expect(result.properties.renderedSize.unevaluatedItems).toBeUndefined()
|
||||
expect(result.properties.renderedSize.items).toEqual({
|
||||
type: "number",
|
||||
})
|
||||
})
|
||||
|
||||
test("removes unsupported annotation fields", () => {
|
||||
const result = ProviderTransform.schema(moonshotModel, {
|
||||
title: "Tool input",
|
||||
$comment: "Internal note",
|
||||
type: "object",
|
||||
properties: {
|
||||
count: {
|
||||
title: "Count",
|
||||
$comment: "Generated from int32",
|
||||
description: "How many items to include.",
|
||||
default: 10,
|
||||
format: "int32",
|
||||
type: "integer",
|
||||
},
|
||||
},
|
||||
} as any) as any
|
||||
|
||||
expect(result.title).toBeUndefined()
|
||||
expect(result.$comment).toBeUndefined()
|
||||
expect(result.properties.count.title).toBeUndefined()
|
||||
expect(result.properties.count.$comment).toBeUndefined()
|
||||
expect(result.properties.count.format).toBeUndefined()
|
||||
expect(result.properties.count.description).toBe("How many items to include.")
|
||||
expect(result.properties.count.default).toBe(10)
|
||||
})
|
||||
|
||||
test("removes unsupported complex validation fields", () => {
|
||||
const result = ProviderTransform.schema(moonshotModel, {
|
||||
type: "object",
|
||||
properties: {
|
||||
count: {
|
||||
type: "integer",
|
||||
minimum: 1,
|
||||
exclusiveMinimum: 0,
|
||||
exclusiveMaximum: 10,
|
||||
},
|
||||
values: {
|
||||
type: "array",
|
||||
items: { type: "string" },
|
||||
contains: { type: "string" },
|
||||
minContains: 1,
|
||||
maxContains: 3,
|
||||
},
|
||||
},
|
||||
} as any) as any
|
||||
|
||||
expect(result.properties.count.exclusiveMinimum).toBeUndefined()
|
||||
expect(result.properties.count.exclusiveMaximum).toBeUndefined()
|
||||
expect(result.properties.count.minimum).toBe(1)
|
||||
expect(result.properties.values.minContains).toBeUndefined()
|
||||
expect(result.properties.values.maxContains).toBeUndefined()
|
||||
expect(result.properties.values.contains).toEqual({ type: "string" })
|
||||
})
|
||||
})
|
||||
|
||||
describe("ProviderTransform.message - DeepSeek reasoning content", () => {
|
||||
|
||||
Reference in New Issue
Block a user