Compare commits

...
Author SHA1 Message Date
AidenandHona 8e1ebd462b fix(core): reuse tool schema compilation
Co-authored-by: Hona <10430890+Hona@users.noreply.github.com>
2026-08-21 04:28:17 +00:00
2 changed files with 57 additions and 6 deletions
+28 -6
View File
@@ -3,6 +3,10 @@ import { Tool } from "@opencode-ai/schema/tool"
import type { StandardJSONSchemaV1, StandardSchemaV1 } from "@standard-schema/spec"
import { Effect, JsonSchema, Schema } from "effect"
const inputDecoders = new WeakMap<object, (value: unknown) => Effect.Effect<unknown, Schema.SchemaError>>()
const outputEncoders = new WeakMap<object, (value: unknown) => Effect.Effect<unknown, Schema.SchemaError>>()
const jsonSchemas = new WeakMap<object, JsonSchema.JsonSchema>()
export const definition = (tool: Tool.Info<any, any>): ToolDefinition => ({
name: effectiveName(tool),
description: tool.description,
@@ -45,22 +49,30 @@ export const execute = (tool: Tool.Info<any, any>, input: unknown, context: Tool
})
const decodeInput = (schema: Tool.ValueSchema<any>, value: unknown) => {
if (Schema.isSchema(schema))
return Schema.decodeUnknownEffect(schema)(value).pipe(
if (Schema.isSchema(schema)) {
const cached = inputDecoders.get(schema)
const decode = cached ?? Schema.decodeUnknownEffect(schema)
if (!cached) inputDecoders.set(schema, decode)
return decode(value).pipe(
Effect.mapError((error) => new Tool.Error({ message: `Invalid tool input: ${error.message}` })),
)
}
if (isStandardSchema(schema)) return validateStandard(schema, value, "Invalid tool input")
return Effect.succeed(value)
}
const encodeOutput = (schema: Tool.ValueSchema<any>, value: unknown) => {
if (Schema.isSchema(schema))
return Schema.encodeEffect(schema)(value).pipe(
if (Schema.isSchema(schema)) {
const cached = outputEncoders.get(schema)
const encode = cached ?? Schema.encodeEffect(schema)
if (!cached) outputEncoders.set(schema, encode)
return encode(value).pipe(
Effect.mapError(
(error) =>
new Tool.Error({ message: `Tool returned an invalid value for its output schema: ${error.message}` }),
),
)
}
if (isStandardSchema(schema))
return validateStandard(schema, value, "Tool returned an invalid value for its output schema")
return Schema.decodeUnknownEffect(Schema.Json)(value).pipe(
@@ -103,13 +115,23 @@ const inputJsonSchema = (schema: Tool.ValueSchema<any>): JsonSchema.JsonSchema =
if (schema === undefined || schema === null) return {}
if (isStandardSchema(schema))
return schema["~standard"].jsonSchema.input({ target: "draft-2020-12" }) as JsonSchema.JsonSchema
return Schema.isSchema(schema) ? toJsonSchema(schema) : (schema as JsonSchema.JsonSchema)
if (!Schema.isSchema(schema)) return schema as JsonSchema.JsonSchema
const cached = jsonSchemas.get(schema)
if (cached) return structuredClone(cached)
const compiled = toJsonSchema(schema)
jsonSchemas.set(schema, compiled)
return structuredClone(compiled)
}
const outputJsonSchema = (schema: Tool.ValueSchema<any>): JsonSchema.JsonSchema => {
if (isStandardSchema(schema))
return schema["~standard"].jsonSchema.output({ target: "draft-2020-12" }) as JsonSchema.JsonSchema
return Schema.isSchema(schema) ? toJsonSchema(schema) : (schema as JsonSchema.JsonSchema)
if (!Schema.isSchema(schema)) return schema as JsonSchema.JsonSchema
const cached = jsonSchemas.get(schema)
if (cached) return structuredClone(cached)
const compiled = toJsonSchema(schema)
jsonSchemas.set(schema, compiled)
return structuredClone(compiled)
}
const toJsonSchema = (schema: Schema.Top): JsonSchema.JsonSchema => {
+29
View File
@@ -52,6 +52,10 @@ test("Effect tool schemas use exact optional keys and flatten compatible constra
required: ["code"],
additionalProperties: false,
})
const first = definition(tool).inputSchema as { properties: object }
const second = definition(tool).inputSchema as { properties: object }
expect(first).not.toBe(second)
expect(first.properties).not.toBe(second.properties)
})
test("Effect tool schemas inline named child schemas", () => {
@@ -167,6 +171,31 @@ test("portable schema failures become tool failures", async () => {
expect(error.toString()).toContain("Invalid tool input: expected a string")
})
test("portable schema definitions reflect current converter state", () => {
let type = "string"
const input = {
"~standard": {
version: 1,
vendor: "test",
validate: (value: unknown) => ({ value }),
jsonSchema: {
input: () => ({ type }),
output: () => ({ type }),
},
},
}
const tool: Info = {
name: "dynamic-portable",
description: "Dynamic portable schema",
input,
execute: () => Effect.succeed({ content: "unused" }),
}
expect(definition(tool).inputSchema).toEqual({ type: "string" })
type = "number"
expect(definition(tool).inputSchema).toEqual({ type: "number" })
})
test("canonical results carry metadata with typed output", async () => {
const input = Schema.Struct({ value: Schema.String })
const output = Schema.Struct({ value: Schema.String, internal: Schema.Boolean })