| 268 | return Layer.scopedContext(this.toContext(build)) |
| 269 | }, |
| 270 | commit(this: Toolkit<Record<string, Tool.Any>>) { |
| 271 | return Effect.gen(this, function*() { |
| 272 | const tools = this.tools |
| 273 | const context = yield* Effect.context<never>() |
| 274 | const schemasCache = new WeakMap<any, { |
| 275 | readonly context: Context.Context<never> |
| 276 | readonly handler: (params: any) => Effect.Effect<any, any> |
| 277 | readonly decodeParameters: (u: unknown) => Effect.Effect<Tool.Parameters<any>, ParseError> |
| 278 | readonly validateResult: (u: unknown) => Effect.Effect<unknown, ParseError> |
| 279 | readonly encodeResult: (u: unknown) => Effect.Effect<unknown, ParseError> |
| 280 | }>() |
| 281 | const getSchemas = (tool: Tool.Any) => { |
| 282 | let schemas = schemasCache.get(tool) |
| 283 | if (Predicate.isUndefined(schemas)) { |
| 284 | const handler = context.unsafeMap.get(tool.id)! as Tool.Handler<any> |
| 285 | const decodeParameters = Schema.decodeUnknown(tool.parametersSchema as Schema.Schema.Any) as any |
| 286 | const resultSchema = Schema.Union(tool.successSchema, tool.failureSchema) |
| 287 | const validateResult = Schema.validate(resultSchema) as any |
| 288 | const encodeResult = Schema.encodeUnknown(resultSchema) as any |
| 289 | schemas = { |
| 290 | context: handler.context, |
| 291 | handler: handler.handler, |
| 292 | decodeParameters, |
| 293 | validateResult, |
| 294 | encodeResult |
| 295 | } |
| 296 | schemasCache.set(tool, schemas) |
| 297 | } |
| 298 | return schemas |
| 299 | } |
| 300 | const handle = Effect.fn("Toolkit.handle", { captureStackTrace: false })( |
| 301 | function*(name: string, params: unknown) { |
| 302 | yield* Effect.annotateCurrentSpan({ tool: name, parameters: params }) |
| 303 | const tool = tools[name] |
| 304 | if (Predicate.isUndefined(tool)) { |
| 305 | const toolNames = Object.keys(tools).join(",") |
| 306 | return yield* new AiError.MalformedOutput({ |
| 307 | module: "Toolkit", |
| 308 | method: `${name}.handle`, |
| 309 | description: `Failed to find tool with name '${name}' in toolkit - available tools: ${toolNames}` |
| 310 | }) |
| 311 | } |
| 312 | const schemas = getSchemas(tool) |
| 313 | const decodedParams = yield* Effect.mapError( |
| 314 | schemas.decodeParameters(params), |
| 315 | (cause) => |
| 316 | new AiError.MalformedOutput({ |
| 317 | module: "Toolkit", |
| 318 | method: `${name}.handle`, |
| 319 | description: `Failed to decode tool call parameters for tool '${name}' from:\n'${ |
| 320 | JSON.stringify(params, undefined, 2) |
| 321 | }'`, |
| 322 | cause |
| 323 | }) |
| 324 | ) |
| 325 | const { isFailure, result } = yield* schemas.handler(decodedParams).pipe( |
| 326 | Effect.map((result) => ({ result, isFailure: false })), |
| 327 | Effect.catchAll((error) => |