standard-tool 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +47 -9
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
# standard-tool
|
|
2
|
-
[](https://www.npmjs.com/package/standard-tool) [](https://github.com/finom/standard-tool/actions/workflows/ci.yml)
|
|
1
|
+
# standard-tool [](https://www.npmjs.com/package/standard-tool) [](https://github.com/finom/standard-tool/actions/workflows/ci.yml)
|
|
3
2
|
|
|
4
3
|
> A **standalone**, dependency-free convention for defining LLM tools — built on [Standard Schema](https://standardschema.dev) + [Standard JSON Schema](https://standardschema.dev/json-schema).
|
|
5
4
|
|
|
@@ -55,7 +54,7 @@ export interface StandardTool<Input, Output, FormattedOutput = Output | { error:
|
|
|
55
54
|
description: string;
|
|
56
55
|
inputSchema?: CombinedSchema<Input>;
|
|
57
56
|
outputSchema?: CombinedSchema<Output>;
|
|
58
|
-
execute(input: Input): FormattedOutput | Promise<FormattedOutput>;
|
|
57
|
+
execute(input: Input, meta?: any): FormattedOutput | Promise<FormattedOutput>;
|
|
59
58
|
}
|
|
60
59
|
|
|
61
60
|
export function standardTool<Input, Output, FormattedOutput = Output | { error: string }>(def: {
|
|
@@ -63,7 +62,7 @@ export function standardTool<Input, Output, FormattedOutput = Output | { error:
|
|
|
63
62
|
description: string;
|
|
64
63
|
inputSchema?: CombinedSchema<Input>;
|
|
65
64
|
outputSchema?: CombinedSchema<Output>;
|
|
66
|
-
execute: (input: Input) => Output | Promise<Output>;
|
|
65
|
+
execute: (input: Input, meta: any) => Output | Promise<Output>; // meta: optional per-call runtime context
|
|
67
66
|
formatOutput?: (result: Output | Error) => FormattedOutput | Promise<FormattedOutput>;
|
|
68
67
|
}): StandardTool<Input, Output, FormattedOutput> {
|
|
69
68
|
const check = async <T>(where: 'input' | 'output', s: CombinedSchema<T>, v: unknown): Promise<T> => {
|
|
@@ -80,11 +79,11 @@ export function standardTool<Input, Output, FormattedOutput = Output | { error:
|
|
|
80
79
|
description: def.description,
|
|
81
80
|
inputSchema: def.inputSchema,
|
|
82
81
|
outputSchema: def.outputSchema,
|
|
83
|
-
async execute(input) {
|
|
82
|
+
async execute(input, meta) {
|
|
84
83
|
let result: Output | Error;
|
|
85
84
|
try {
|
|
86
85
|
const validInput = def.inputSchema ? await check('input', def.inputSchema, input) : input;
|
|
87
|
-
const output = await def.execute(validInput);
|
|
86
|
+
const output = await def.execute(validInput, meta);
|
|
88
87
|
result = def.outputSchema ? await check('output', def.outputSchema, output) : output;
|
|
89
88
|
} catch (e) {
|
|
90
89
|
result = e instanceof Error ? e : new Error(String(e));
|
|
@@ -103,7 +102,7 @@ import { standardTool, type StandardTool, type FormatOutputFn } from 'standard-t
|
|
|
103
102
|
standardTool(def): StandardTool<Input, Output, FormattedOutput>;
|
|
104
103
|
```
|
|
105
104
|
|
|
106
|
-
`Input`/`Output` are your **data types** (what your `execute` accepts and returns); the optional schemas describe them. `FormattedOutput` is what the tool hands the model after formatting — `Output | { error: string }` by default.
|
|
105
|
+
`Input`/`Output` are your **data types** (what your `execute` accepts and returns); the optional schemas describe them. `FormattedOutput` is what the tool hands the model after formatting — `Output | { error: string }` by default. `execute` also takes an optional second `meta` argument — per-call runtime context forwarded verbatim to your handler, never validated and never in the JSON Schema (see [Per-call runtime context](#per-call-runtime-context-meta)).
|
|
107
106
|
|
|
108
107
|
| field | type | purpose |
|
|
109
108
|
| --- | --- | --- |
|
|
@@ -111,8 +110,8 @@ standardTool(def): StandardTool<Input, Output, FormattedOutput>;
|
|
|
111
110
|
| `description` | `string` | what the tool does |
|
|
112
111
|
| `inputSchema?` | `CombinedSchema<Input>` | optional input schema — validates **and** emits JSON Schema |
|
|
113
112
|
| `outputSchema?` | `CombinedSchema<Output>` | optional output schema — validates **and** emits JSON Schema |
|
|
114
|
-
| `execute` (yours) | `(input: Input) => Output \| Promise<Output>` | your logic — receives validated input
|
|
115
|
-
| `execute` (tool) | `(input: Input) => FormattedOutput \| Promise<FormattedOutput>` | validate in → run yours → validate out → format; errors become the output (no throw) **by default** |
|
|
113
|
+
| `execute` (yours) | `(input: Input, meta: any) => Output \| Promise<Output>` | your logic — receives validated input and the optional per-call `meta`, returns the output |
|
|
114
|
+
| `execute` (tool) | `(input: Input, meta?: any) => FormattedOutput \| Promise<FormattedOutput>` | validate in → run yours (forwarding `meta`) → validate out → format; errors become the output (no throw) **by default** |
|
|
116
115
|
| `formatOutput?` | `(result: Output \| Error) => FormattedOutput` | optional; maps the result — or an `Error` carrying `issues` — to the model output. Default `result instanceof Error ? { error: result.message } : result` |
|
|
117
116
|
|
|
118
117
|
`inputSchema`/`outputSchema` are optional; when present they must implement both Standard Schema and Standard JSON Schema (Zod 4.2+, ArkType 2.1.28+, or Valibot 1.2+ via `@valibot/to-json-schema`) — `Input`/`Output` are inferred from them (or from `execute` when a schema is omitted).
|
|
@@ -140,6 +139,45 @@ const out = await getWeather.execute({ city: 'Paris' }); // { tempC: number } |
|
|
|
140
139
|
const parameters = getWeather.inputSchema!['~standard'].jsonSchema.input({ target: 'draft-2020-12' });
|
|
141
140
|
```
|
|
142
141
|
|
|
142
|
+
## Per-call runtime context (`meta`)
|
|
143
|
+
|
|
144
|
+
Tools often need per-call data that must **not** appear in the model-facing `inputSchema` — an auth token, a resolver, a request-scoped DB handle. `execute` takes an optional **second `meta` argument**, forwarded verbatim to your handler. It's never validated and never part of the JSON Schema, so your tools can stay **static** (defined once at module scope) while you inject context at call time — instead of closing over it in a per-render factory.
|
|
145
|
+
|
|
146
|
+
`meta` is typed `any`; **annotate it on your handler** to type it at the call site:
|
|
147
|
+
|
|
148
|
+
```ts
|
|
149
|
+
const greet = standardTool({
|
|
150
|
+
name: 'greet',
|
|
151
|
+
description: 'Greet a person with per-call punctuation',
|
|
152
|
+
inputSchema: z.object({ name: z.string() }),
|
|
153
|
+
execute: ({ name }, meta: { punct: string }) => `hi ${name}${meta.punct}`, // annotate meta here
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
await greet.execute({ name: 'Ada' }, { punct: '!' }); // → 'hi Ada!'
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Tools that don't need it just call `execute(input)` — `meta` is optional.
|
|
160
|
+
|
|
161
|
+
## Throwing instead of the `{ error }` envelope
|
|
162
|
+
|
|
163
|
+
By default a validation failure or a thrown error comes back as `{ error: string }`, so a model loop can keep running. When you'd rather have `execute` **throw** — e.g. to let a caller's `try/catch` handle failures — re-throw the `Error` from `formatOutput`:
|
|
164
|
+
|
|
165
|
+
```ts
|
|
166
|
+
const getWeather = standardTool({
|
|
167
|
+
name: 'get_weather',
|
|
168
|
+
description: 'Current temperature for a city',
|
|
169
|
+
inputSchema: z.object({ city: z.string() }),
|
|
170
|
+
outputSchema: z.object({ tempC: z.number() }),
|
|
171
|
+
execute: async ({ city }) => ({ tempC: 21 }),
|
|
172
|
+
formatOutput: (result) => {
|
|
173
|
+
if (result instanceof Error) throw result; // validation/exec failures now reject
|
|
174
|
+
return result;
|
|
175
|
+
},
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
await getWeather.execute({ city: 'Paris' }); // { tempC: number } — rejects on bad input/output
|
|
179
|
+
```
|
|
180
|
+
|
|
143
181
|
## With the OpenAI API
|
|
144
182
|
|
|
145
183
|
Uses the [Responses API](https://developers.openai.com/api/docs/guides/function-calling). Because every tool is the same neutral shape, you keep them in one array: `.map` it into the request's `tools`, then dispatch each function call back to the matching tool by `name`. Adding a fourth tool is one more array entry — no special-casing, no per-tool wiring. And because `execute` returns `{ error }` instead of throwing **by default**, a malformed tool call comes back to the model to self-correct rather than crashing your loop (a custom `formatOutput` can opt back into throwing).
|
package/dist/index.d.ts
CHANGED
|
@@ -27,9 +27,10 @@ export interface StandardTool<Input, Output, FormattedOutput = DefaultFormattedO
|
|
|
27
27
|
* Validate input (when `inputSchema`) → run your logic → validate output (when `outputSchema`) →
|
|
28
28
|
* format. **By default it doesn't throw**: a validation failure or a thrown error becomes the
|
|
29
29
|
* formatted output (`{ error: string }`) — unless your `formatOutput` throws — so a model loop
|
|
30
|
-
* keeps running.
|
|
30
|
+
* keeps running. `meta` is optional per-call runtime context (auth tokens, resolvers, request-scoped
|
|
31
|
+
* data) forwarded verbatim to your handler — never validated, never in the JSON Schema.
|
|
31
32
|
*/
|
|
32
|
-
execute(input: Input): FormattedOutput | Promise<FormattedOutput>;
|
|
33
|
+
execute(input: Input, meta?: any): FormattedOutput | Promise<FormattedOutput>;
|
|
33
34
|
}
|
|
34
35
|
/**
|
|
35
36
|
* Create a standard tool. `inputSchema`/`outputSchema` are optional; when present they must implement
|
|
@@ -47,7 +48,7 @@ export declare function standardTool<Input, Output, FormattedOutput = DefaultFor
|
|
|
47
48
|
description: string;
|
|
48
49
|
inputSchema?: CombinedSchema<Input>;
|
|
49
50
|
outputSchema?: CombinedSchema<Output>;
|
|
50
|
-
execute: (input: Input) => Output | Promise<Output>;
|
|
51
|
+
execute: (input: Input, meta: any) => Output | Promise<Output>;
|
|
51
52
|
formatOutput?: FormatOutputFn<Output, FormattedOutput>;
|
|
52
53
|
}): StandardTool<Input, Output, FormattedOutput>;
|
|
53
54
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAoB,MAAM,sBAAsB,CAAC;AAE7E,mBAAmB,sBAAsB,CAAC;AAE1C,mHAAmH;AACnH,MAAM,MAAM,sBAAsB,CAAC,MAAM,IAAI,MAAM,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAExE;;;;GAIG;AACH,MAAM,MAAM,cAAc,CAAC,MAAM,EAAE,eAAe,IAAI,CACpD,MAAM,EAAE,MAAM,GAAG,KAAK,KACnB,eAAe,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;AAEhD;;;;;GAKG;AACH,MAAM,WAAW,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,eAAe,GAAG,sBAAsB,CAAC,MAAM,CAAC;IAC3F,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,iFAAiF;IACjF,WAAW,CAAC,EAAE,cAAc,CAAC,KAAK,CAAC,CAAC;IACpC,kFAAkF;IAClF,YAAY,CAAC,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACtC
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAoB,MAAM,sBAAsB,CAAC;AAE7E,mBAAmB,sBAAsB,CAAC;AAE1C,mHAAmH;AACnH,MAAM,MAAM,sBAAsB,CAAC,MAAM,IAAI,MAAM,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAExE;;;;GAIG;AACH,MAAM,MAAM,cAAc,CAAC,MAAM,EAAE,eAAe,IAAI,CACpD,MAAM,EAAE,MAAM,GAAG,KAAK,KACnB,eAAe,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;AAEhD;;;;;GAKG;AACH,MAAM,WAAW,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,eAAe,GAAG,sBAAsB,CAAC,MAAM,CAAC;IAC3F,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,iFAAiF;IACjF,WAAW,CAAC,EAAE,cAAc,CAAC,KAAK,CAAC,CAAC;IACpC,kFAAkF;IAClF,YAAY,CAAC,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACtC;;;;;;OAMG;IAEH,OAAO,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,CAAC,EAAE,GAAG,GAAG,eAAe,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;CAC/E;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,eAAe,GAAG,sBAAsB,CAAC,MAAM,CAAC,EAAE,GAAG,EAAE;IACjG,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,cAAc,CAAC,KAAK,CAAC,CAAC;IACpC,YAAY,CAAC,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IAEtC,OAAO,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,KAAK,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC/D,YAAY,CAAC,EAAE,cAAc,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;CACxD,GAAG,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,eAAe,CAAC,CAqB/C"}
|
package/dist/index.js
CHANGED
|
@@ -17,11 +17,11 @@ export function standardTool(def) {
|
|
|
17
17
|
description: def.description,
|
|
18
18
|
inputSchema: def.inputSchema,
|
|
19
19
|
outputSchema: def.outputSchema,
|
|
20
|
-
async execute(input) {
|
|
20
|
+
async execute(input, meta) {
|
|
21
21
|
let result;
|
|
22
22
|
try {
|
|
23
23
|
const validInput = def.inputSchema ? await validate('input', def.inputSchema, input) : input;
|
|
24
|
-
const output = await def.execute(validInput);
|
|
24
|
+
const output = await def.execute(validInput, meta);
|
|
25
25
|
result = def.outputSchema ? await validate('output', def.outputSchema, output) : output;
|
|
26
26
|
}
|
|
27
27
|
catch (error) {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAwCA;;;;;;;;;;GAUG;AACH,MAAM,UAAU,YAAY,CAAkE,GAQ7F;IACC,MAAM,YAAY,GAChB,GAAG,CAAC,YAAY;QAChB,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,YAAY,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,MAAM,CAA+B,CAAC,CAAC;IAC7G,OAAO;QACL,IAAI,EAAE,GAAG,CAAC,IAAI;QACd,WAAW,EAAE,GAAG,CAAC,WAAW;QAC5B,WAAW,EAAE,GAAG,CAAC,WAAW;QAC5B,YAAY,EAAE,GAAG,CAAC,YAAY;QAC9B,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,IAAI;YACvB,IAAI,MAAsB,CAAC;YAC3B,IAAI,CAAC;gBACH,MAAM,UAAU,GAAG,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,MAAM,QAAQ,CAAC,OAAO,EAAE,GAAG,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;gBAC7F,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,OAAO,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;gBACnD,MAAM,GAAG,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,MAAM,QAAQ,CAAC,QAAQ,EAAE,GAAG,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;YAC1F,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,MAAM,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;YACrE,CAAC;YACD,OAAO,YAAY,CAAC,MAAM,CAAC,CAAC;QAC9B,CAAC;KACF,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,QAAQ,CACrB,MAA0B,EAC1B,MAAS,EACT,KAAc;IAEd,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,4BAA4B;IACtF,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;QAClB,sFAAsF;QACtF,MAAM,MAAM,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,GAAG,MAAM,uBAAuB,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE;YAC/G,MAAM,EAAE,MAAM,CAAC,MAAM;SACtB,CAAC,CAAC;IACL,CAAC;IACD,OAAO,MAAM,CAAC,KAAK,CAAC;AACtB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "standard-tool",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.2",
|
|
4
4
|
"description": "Standard DRY LLM Tool Interface — name/description/execute with input & output validated via Standard Schema and emitted as JSON Schema via Standard JSON Schema. Zero dependencies.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -51,6 +51,7 @@
|
|
|
51
51
|
"devDependencies": {
|
|
52
52
|
"@biomejs/biome": "^2.4.16",
|
|
53
53
|
"@types/node": "^25.9.1",
|
|
54
|
-
"typescript": "^6.0.2"
|
|
54
|
+
"typescript": "^6.0.2",
|
|
55
|
+
"zod": "^4.4.3"
|
|
55
56
|
}
|
|
56
57
|
}
|