mirror of
https://github.com/anomalyco/opencode.git
synced 2026-07-24 03:09:48 -04:00
8c94e9005f
Co-authored-by: Brendan Allan <14191578+Brendonovich@users.noreply.github.com> Co-authored-by: Kit Langton <kit.langton@gmail.com> Co-authored-by: opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com> Co-authored-by: Affan Ali <93028901+affanali2k3@users.noreply.github.com> Co-authored-by: affanali2k3 <affanalikhanxx@gmail.com> Co-authored-by: Frank <frank@anoma.ly> Co-authored-by: opencode-agent[bot] <219766164+opencode-agent[bot]@users.noreply.github.com> Co-authored-by: 𝓛𝓲𝓽𝓽𝓵𝓮 𝓕𝓻𝓪𝓷𝓴 <little-frank@opencord.local> Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com> Co-authored-by: Jay V <air@live.ca> Co-authored-by: Dax Raad <d@ironbay.co> Co-authored-by: Aarav Sareen <96787824+arvsrn@users.noreply.github.com> Co-authored-by: OpeOginni <107570612+OpeOginni@users.noreply.github.com> Co-authored-by: Luke Parker <10430890+Hona@users.noreply.github.com> Co-authored-by: Ben Guthrie <benjee.012@gmail.com> Co-authored-by: Dax <mail@thdxr.com> Co-authored-by: Filip <34747899+neriousy@users.noreply.github.com> Co-authored-by: Max Anderson <max.a.anderson95@gmail.com> Co-authored-by: Brendan Allan <git@brendonovich.dev> Co-authored-by: Jack <jack@anoma.ly> Co-authored-by: Shoubhit Dash <shoubhit2005@gmail.com> Co-authored-by: Dustin Deus <deusdustin@gmail.com> Co-authored-by: starptech <starptech@starptechs-MBP.fritz.box> Co-authored-by: Aiden Cline <aidenpcline@gmail.com> Co-authored-by: usrnk1 <7547651+usrnk1@users.noreply.github.com> Co-authored-by: Jay <53023+jayair@users.noreply.github.com> Co-authored-by: runvip <164729189+runvip@users.noreply.github.com> Co-authored-by: opencode <opencode@sst.dev> Co-authored-by: Julian Coy <julian@ex-machina.co> Co-authored-by: Vladimir Glafirov <vglafirov@gitlab.com>
118 lines
3.8 KiB
TypeScript
118 lines
3.8 KiB
TypeScript
import { describe, expect, test } from "bun:test"
|
|
import { Effect } from "effect"
|
|
import { LLM } from "../src"
|
|
import { OpenAIChat } from "../src/protocols"
|
|
import { ToolSchemaProjection } from "../src/protocols/utils/tool-schema"
|
|
import { Auth, LLMClient } from "../src/route"
|
|
import { it } from "./lib/effect"
|
|
|
|
describe("tool schema projections", () => {
|
|
test("moonshot strips $ref siblings and converts tuple arrays to a schema object", () => {
|
|
expect(
|
|
ToolSchemaProjection.moonshot({
|
|
type: "object",
|
|
properties: {
|
|
linked: { $ref: "#/$defs/Linked", description: "drop me" },
|
|
tuple: { type: "array", items: [{ type: "string" }, { type: "number" }] },
|
|
prefixTuple: { type: "array", prefixItems: [{ type: "boolean" }, { type: "string" }] },
|
|
},
|
|
}),
|
|
).toEqual({
|
|
type: "object",
|
|
properties: {
|
|
linked: { $ref: "#/$defs/Linked" },
|
|
tuple: { type: "array", items: { anyOf: [{ type: "string" }, { type: "number" }] } },
|
|
prefixTuple: { type: "array", items: { anyOf: [{ type: "boolean" }, { type: "string" }] } },
|
|
},
|
|
})
|
|
})
|
|
|
|
test("gemini handles numeric enums, dangling required fields, untyped arrays, and scalar object keys", () => {
|
|
expect(
|
|
ToolSchemaProjection.gemini({
|
|
type: "object",
|
|
required: ["status", "missing"],
|
|
properties: {
|
|
status: { type: "integer", enum: [1, 2] },
|
|
tags: { type: "array" },
|
|
name: { type: "string", properties: { ignored: { type: "string" } }, required: ["ignored"] },
|
|
},
|
|
}),
|
|
).toEqual({
|
|
type: "object",
|
|
required: ["status"],
|
|
properties: {
|
|
status: { type: "string", enum: ["1", "2"] },
|
|
tags: { type: "array", items: { type: "string" } },
|
|
name: { type: "string" },
|
|
},
|
|
})
|
|
})
|
|
|
|
test("openai keeps one flat object top-level schema", () => {
|
|
expect(
|
|
ToolSchemaProjection.openAI({
|
|
anyOf: [
|
|
{
|
|
type: "object",
|
|
properties: {
|
|
path: { type: "string" },
|
|
maybe: { anyOf: [{ type: "string" }, { type: "null" }] },
|
|
},
|
|
},
|
|
{ type: "object", properties: { resource: { type: "string" } } },
|
|
],
|
|
}),
|
|
).toEqual({
|
|
type: "object",
|
|
properties: {
|
|
path: { type: "string" },
|
|
maybe: { type: "string" },
|
|
resource: { type: "string" },
|
|
},
|
|
additionalProperties: false,
|
|
})
|
|
})
|
|
|
|
it.effect("applies model compatibility before protocol projection", () =>
|
|
Effect.gen(function* () {
|
|
const model = OpenAIChat.route
|
|
.with({ endpoint: { baseURL: "https://api.openai.test/v1/" }, auth: Auth.bearer("test") })
|
|
.model({ id: "kimi-k2", compatibility: { toolSchema: "moonshot" } })
|
|
const prepared = yield* LLMClient.prepare<OpenAIChat.OpenAIChatBody>(
|
|
LLM.request({
|
|
model,
|
|
prompt: "Use the tool.",
|
|
tools: [
|
|
{
|
|
name: "lookup",
|
|
description: "Lookup data.",
|
|
inputSchema: {
|
|
type: "object",
|
|
anyOf: [
|
|
{
|
|
type: "object",
|
|
properties: {
|
|
tuple: { type: "array", items: [{ type: "string" }, { type: "number" }] },
|
|
linked: { $ref: "#/$defs/Linked", description: "drop me" },
|
|
},
|
|
},
|
|
],
|
|
},
|
|
},
|
|
],
|
|
}),
|
|
)
|
|
|
|
expect(prepared.body.tools?.[0]?.function.parameters).toEqual({
|
|
type: "object",
|
|
properties: {
|
|
tuple: { type: "array", items: { anyOf: [{ type: "string" }, { type: "number" }] } },
|
|
linked: { $ref: "#/$defs/Linked" },
|
|
},
|
|
additionalProperties: false,
|
|
})
|
|
}),
|
|
)
|
|
})
|