1import type * as Context from "effect/Context";
2import * as Effect from "effect/Effect";
3import type { RuntimeContext } from "../RuntimeContext.ts";
4import type { Parameter } from "./Parameter.ts";
5
6export type ToolParameters<Refs> = {
7 [toolName in Extract<Refs, Parameter>["~alchemy/Name"]]: Extract<
8 Refs,
9 Parameter
10 >["schema"]["Type"];
11};
12
13export interface Tool<
14 Name extends string = string,
15 Refs extends any[] = any[],
16> {
17 "~alchemy/Kind": "Tool";
18 "~alchemy/Name": Name;
19 refs: Refs;
20 template: TemplateStringsArray;
21 params: {
22 [p in keyof ToolParameters<Refs[number]>]: ToolParameters<Refs[number]>[p];
23 };
24 impl: (props: this["params"]) => Effect.Effect<any, any, any>;
25 new (): Tool<Name, Refs>;
26 <Err = never, Req = never>(
27 impl: Effect.Effect<
28 (props: this["params"]) => Effect.Effect<any>,
29 Err,
30 Req
31 >,
32 ): ToolImpl<this, Err, Req>;
33 <Err = never, Req = never>(
34 impl: (props: this["params"]) => Effect.Effect<any, Err, Req>,
35 ): ToolImpl<this, Err, Req>;
36}
37
38export interface ToolImpl<
39 T extends Tool<any, any> = any,
40 Err = any,
41 Req = any,
42> {
43 "~alchemy/Kind": "ToolImpl";
44 tool: T;
45 impl: (props: T["params"]) => Effect.Effect<any, Err, Req>;
46 new (): {};
47}
48
49export const Tool: {
50 <Name extends string>(
51 name: Name,
52 ): {
53 <Refs extends any[]>(
54 template: TemplateStringsArray,
55 ...refs: Refs
56 ): Tool<Name, Refs>;
57 };
58 <Self>(): {
59 <Name extends string>(
60 name: Name,
61 ): {
62 <Refs extends any[]>(
63 template: TemplateStringsArray,
64 ...refs: Refs
65 ): Tool<Name, Refs> &
66 Context.Service<
67 Self,
68 | ((
69 input: ToolParameters<Refs[number]>,
70 ) => Effect.Effect<any, never, RuntimeContext>)
71 | Effect.Effect<
72 (
73 input: ToolParameters<Refs[number]>,
74 ) => Effect.Effect<any, never, RuntimeContext>,
75 never,
76 RuntimeContext
77 >
78 >;
79 };
80 };
81} = ((name?: string) =>
82 name
83 ? (template: TemplateStringsArray, ...refs: any[]) =>
84 makeTool(name, template, refs)
85 : (name: string) =>
86 (template: TemplateStringsArray, ...refs: any[]) =>
87 makeTool(name, template, refs)) as any;
88
89const makeTool = (name: string, template: TemplateStringsArray, refs: any[]) =>
90 Object.assign(
91 function (impl: (props: any) => Effect.Effect<any, any, any>) {},
92 {
93 "~alchemy/Kind": "Tool",
94 "~alchemy/Name": name,
95 refs,
96 template,
97 },
98 ) as any;