ai-tool-set 0.1.0-alpha.1 → 0.1.0-alpha.3
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 +178 -87
- package/dist/index.d.mts +107 -35
- package/dist/index.mjs +119 -61
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
</div>
|
|
11
11
|
|
|
12
|
-
This library provides
|
|
12
|
+
This library provides a type-safe API to manage [`activeTools`](https://ai-sdk.dev/docs/reference/ai-sdk-core/generate-text#active-tools) for [`generateText()`](https://ai-sdk.dev/docs/reference/ai-sdk-core/generate-text) and [`streamText()`](https://ai-sdk.dev/docs/reference/ai-sdk-core/stream-text) in the AI SDK.
|
|
13
13
|
|
|
14
14
|
### Why?
|
|
15
15
|
|
|
@@ -37,25 +37,25 @@ import { tool } from 'ai';
|
|
|
37
37
|
import { z } from 'zod';
|
|
38
38
|
import { createToolSet } from 'ai-tool-set';
|
|
39
39
|
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
})
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
})
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
const toolSet = createToolSet({
|
|
40
|
+
const tools = {
|
|
41
|
+
search: tool({
|
|
42
|
+
description: 'Search for products',
|
|
43
|
+
inputSchema: z.object({ query: z.string() }),
|
|
44
|
+
execute: async ({ query }) => searchProducts(query),
|
|
45
|
+
}),
|
|
46
|
+
list_orders: tool({
|
|
47
|
+
description: 'List orders for a customer',
|
|
48
|
+
inputSchema: z.object({ customerId: z.string() }),
|
|
49
|
+
execute: async ({ customerId }) => listOrders(customerId),
|
|
50
|
+
}),
|
|
51
|
+
cancel_order: tool({
|
|
52
|
+
description: 'Cancel an order',
|
|
53
|
+
inputSchema: z.object({ orderId: z.string() }),
|
|
54
|
+
execute: async ({ orderId }) => cancelOrder(orderId),
|
|
55
|
+
}),
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const toolSet = createToolSet({ tools });
|
|
59
59
|
```
|
|
60
60
|
|
|
61
61
|
### Use with `generateText` / `streamText`
|
|
@@ -82,9 +82,7 @@ const result = await generateText({
|
|
|
82
82
|
Use `.activate()` and `.deactivate()` to statically control which tools are available:
|
|
83
83
|
|
|
84
84
|
```typescript
|
|
85
|
-
const { tools, activeTools } = toolSet
|
|
86
|
-
.deactivate(['cancel_order'])
|
|
87
|
-
.activate(['list_orders']);
|
|
85
|
+
const { tools, activeTools } = toolSet.deactivate(['cancel_order']).activate(['list_orders']);
|
|
88
86
|
|
|
89
87
|
const result = await generateText({
|
|
90
88
|
model,
|
|
@@ -99,27 +97,19 @@ const result = await generateText({
|
|
|
99
97
|
Use `.activateWhen()` and `.deactivateWhen()` to conditionally control tools based on messages and context. The predicate receives an `ActivationInput` with `messages` and `context`.
|
|
100
98
|
|
|
101
99
|
```typescript
|
|
102
|
-
const { tools,
|
|
103
|
-
.
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
p.output.orders?.some((order) => order.status !== 'fulfilled'),
|
|
110
|
-
),
|
|
100
|
+
const toolSet = createToolSet({ tools }).activateWhen('cancel_order', ({ messages }) =>
|
|
101
|
+
messages.some((m) =>
|
|
102
|
+
m.parts.some(
|
|
103
|
+
(p) =>
|
|
104
|
+
p.type === 'tool-list_orders' &&
|
|
105
|
+
p.state === 'output-available' &&
|
|
106
|
+
p.output.orders?.some((order) => order.status !== 'fulfilled'),
|
|
111
107
|
),
|
|
112
|
-
)
|
|
113
|
-
|
|
114
|
-
const result = await generateText({
|
|
115
|
-
model,
|
|
116
|
-
tools,
|
|
117
|
-
activeTools,
|
|
118
|
-
prompt: 'Cancel my order #1001',
|
|
119
|
-
});
|
|
108
|
+
),
|
|
109
|
+
);
|
|
120
110
|
```
|
|
121
111
|
|
|
122
|
-
Use `.inferTools()` to evaluate predicates with the current messages and context
|
|
112
|
+
Use `.inferTools()` to evaluate predicates with the current messages and context. It returns `{ tools, activeTools }` that can be spread directly into `generateText()`:
|
|
123
113
|
|
|
124
114
|
```typescript
|
|
125
115
|
const messages = [
|
|
@@ -129,18 +119,20 @@ const messages = [
|
|
|
129
119
|
},
|
|
130
120
|
{
|
|
131
121
|
role: 'assistant',
|
|
132
|
-
parts: [
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
122
|
+
parts: [
|
|
123
|
+
{
|
|
124
|
+
type: 'tool-list_orders',
|
|
125
|
+
state: 'output-available',
|
|
126
|
+
toolCallId: 'call-1',
|
|
127
|
+
input: { customerId: 'cust-123' },
|
|
128
|
+
output: {
|
|
129
|
+
orders: [
|
|
130
|
+
{ orderId: '1000', status: 'fulfilled' },
|
|
131
|
+
{ orderId: '1001', status: 'pending' },
|
|
132
|
+
],
|
|
133
|
+
},
|
|
142
134
|
},
|
|
143
|
-
|
|
135
|
+
],
|
|
144
136
|
},
|
|
145
137
|
];
|
|
146
138
|
|
|
@@ -153,11 +145,10 @@ const result = await generateText({ model, tools, activeTools, messages });
|
|
|
153
145
|
You can also activate multiple tools at once:
|
|
154
146
|
|
|
155
147
|
```typescript
|
|
156
|
-
const toolSet = createToolSet({
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
});
|
|
148
|
+
const toolSet = createToolSet({ tools }).activateWhen({
|
|
149
|
+
list_orders: ({ context }) => context.isAuthenticated,
|
|
150
|
+
cancel_order: ({ messages }) => hasUnfulfilledOrders(messages),
|
|
151
|
+
});
|
|
161
152
|
```
|
|
162
153
|
|
|
163
154
|
### Last-Call Wins
|
|
@@ -165,26 +156,75 @@ const toolSet = createToolSet({ search, list_orders, cancel_order })
|
|
|
165
156
|
Each method appends to an internal list. For each tool, the **last entry** determines its state. This makes ordering explicit and predictable:
|
|
166
157
|
|
|
167
158
|
```typescript
|
|
168
|
-
const toolSet = createToolSet({
|
|
169
|
-
.activate(['cancel_order'])
|
|
170
|
-
.deactivate(['cancel_order'])
|
|
171
|
-
.activateWhen(
|
|
172
|
-
|
|
159
|
+
const toolSet = createToolSet({ tools })
|
|
160
|
+
.activate(['cancel_order']) // cancel_order: activated
|
|
161
|
+
.deactivate(['cancel_order']) // cancel_order: deactivated
|
|
162
|
+
.activateWhen(
|
|
163
|
+
'cancel_order',
|
|
164
|
+
(
|
|
165
|
+
{ messages }, // cancel_order: conditional activation
|
|
166
|
+
) => hasUnfulfilledOrders(messages),
|
|
173
167
|
);
|
|
174
168
|
```
|
|
175
169
|
|
|
176
|
-
### Immutable
|
|
170
|
+
### Immutable vs Mutable
|
|
171
|
+
|
|
172
|
+
By default, `createToolSet()` returns an **immutable** tool set, that means every method returns a new instance and the original is never modified. This is ideal when the tool set is created once in the global scope and shared across requests:
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
// Global scope: created once, shared across requests
|
|
176
|
+
const toolSet = createToolSet({ tools }).deactivate(['list_order', 'cancel_order']);
|
|
177
|
+
|
|
178
|
+
export async function POST(req: Request) {
|
|
179
|
+
const { messages } = await req.json();
|
|
180
|
+
|
|
181
|
+
// Activate list_orders only for this request
|
|
182
|
+
// myToolSet !== toolSet, original toolSet is unchanged for next request
|
|
183
|
+
const myToolSet = toolSet.activate(['list_orders']);
|
|
184
|
+
|
|
185
|
+
const { tools, activeTools } = myToolSet;
|
|
186
|
+
|
|
187
|
+
const result = await generateText({
|
|
188
|
+
model,
|
|
189
|
+
tools,
|
|
190
|
+
activeTools,
|
|
191
|
+
messages,
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
Pass `mutable: true` to get a **mutable** tool set where each method mutates in-place and returns `this` for chaining. This is useful when the tool set is created per-request in a local scope:
|
|
197
|
+
|
|
198
|
+
```typescript
|
|
199
|
+
export async function POST(req: Request) {
|
|
200
|
+
const { messages } = await req.json();
|
|
201
|
+
|
|
202
|
+
// Local scope: created and mutated per request
|
|
203
|
+
const toolSet = createToolSet({ tools, mutable: true }).deactivate(['list_order', 'cancel_order']);
|
|
204
|
+
|
|
205
|
+
// Activate list_orders only for this request
|
|
206
|
+
// myToolSet === toolSet, original toolSet is mutated for this request
|
|
207
|
+
const myToolSet = toolSet.activate(['list_orders']);
|
|
177
208
|
|
|
178
|
-
|
|
209
|
+
const { tools, activeTools } = myToolSet;
|
|
210
|
+
|
|
211
|
+
const result = await generateText({
|
|
212
|
+
model,
|
|
213
|
+
tools,
|
|
214
|
+
activeTools,
|
|
215
|
+
messages,
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
Use `.clone()` to convert between immutable and mutable, preserving all activation entries:
|
|
179
221
|
|
|
180
222
|
```typescript
|
|
181
|
-
|
|
182
|
-
const
|
|
183
|
-
const withCancel = withoutCancel.activate(['cancel_order']);
|
|
223
|
+
// Convert an immutable toolset to mutable
|
|
224
|
+
const mutableToolSet = toolSet.clone({ mutable: true });
|
|
184
225
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
withCancel.activeTools; // ['search', 'list_orders', 'cancel_order']
|
|
226
|
+
// Convert a mutable toolset back to immutable
|
|
227
|
+
const immutableToolSet = mutableToolSet.clone();
|
|
188
228
|
```
|
|
189
229
|
|
|
190
230
|
### Typed UI Tool Set
|
|
@@ -196,7 +236,7 @@ import type { UIMessage } from 'ai';
|
|
|
196
236
|
import type { InferUIToolSet } from 'ai-tool-set';
|
|
197
237
|
|
|
198
238
|
const tools = { search, list_orders, cancel_order };
|
|
199
|
-
const toolSet = createToolSet(tools);
|
|
239
|
+
const toolSet = createToolSet({ tools });
|
|
200
240
|
|
|
201
241
|
// From the tools record
|
|
202
242
|
type MyToolSet = InferUIToolSet<typeof tools>;
|
|
@@ -216,12 +256,14 @@ If you already have a custom `UIMessage` type, you can pass it as `MESSAGE` gene
|
|
|
216
256
|
import { myTools } from './my-tools.js';
|
|
217
257
|
import { MyUIMessage } from './my-ui-message.js';
|
|
218
258
|
|
|
219
|
-
const toolSet = createToolSet<typeof myTools, MyUIMessage>(myTools)
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
259
|
+
const toolSet = createToolSet<typeof myTools, MyUIMessage>({ tools: myTools }).activateWhen(
|
|
260
|
+
'cancel_order',
|
|
261
|
+
({ messages }) => hasUnfulfilledOrders(messages),
|
|
262
|
+
);
|
|
263
|
+
// ~~~~~~~~
|
|
264
|
+
// Messages are now typed as MyUIMessage, so you get type safety and autocompletion in predicates!
|
|
223
265
|
|
|
224
|
-
const { tools, activeTools } = toolSet.inferTools({ messages });
|
|
266
|
+
const { tools, activeTools } = toolSet.inferTools({ messages, context: {} });
|
|
225
267
|
```
|
|
226
268
|
|
|
227
269
|
### Custom Context
|
|
@@ -234,10 +276,12 @@ import { MyUIMessage } from './my-ui-message.js';
|
|
|
234
276
|
|
|
235
277
|
type MyContext = { userId: string; isAdmin: boolean };
|
|
236
278
|
|
|
237
|
-
const toolSet = createToolSet<typeof myTools, MyUIMessage, MyContext>(myTools)
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
279
|
+
const toolSet = createToolSet<typeof myTools, MyUIMessage, MyContext>({ tools: myTools }).activateWhen(
|
|
280
|
+
'cancel_order',
|
|
281
|
+
({ context }) => context.isAdmin,
|
|
282
|
+
);
|
|
283
|
+
// ~~~~~~~~
|
|
284
|
+
// Context is now typed as MyContext, so you get type safety and autocompletion
|
|
241
285
|
|
|
242
286
|
const { tools, activeTools } = toolSet.inferTools({
|
|
243
287
|
messages,
|
|
@@ -247,14 +291,18 @@ const { tools, activeTools } = toolSet.inferTools({
|
|
|
247
291
|
|
|
248
292
|
## API
|
|
249
293
|
|
|
250
|
-
## `createToolSet(
|
|
294
|
+
## `createToolSet(options)`
|
|
251
295
|
|
|
252
|
-
- `tools`, a plain `Record<string, Tool>` of AI SDK tools
|
|
296
|
+
- `options.tools`, a plain `Record<string, Tool>` of AI SDK tools
|
|
297
|
+
- `options.mutable` (optional), set to `true` for a mutable tool set (default: `false`)
|
|
253
298
|
|
|
254
299
|
Returns a `ToolSet` instance. All tools are active by default.
|
|
255
300
|
|
|
256
301
|
```ts
|
|
257
|
-
const toolSet = createToolSet({ search, list_orders, cancel_order });
|
|
302
|
+
const toolSet = createToolSet({ tools: { search, list_orders, cancel_order } });
|
|
303
|
+
|
|
304
|
+
// Mutable mode — methods mutate in-place and return `this`
|
|
305
|
+
const toolSet = createToolSet({ tools: { search, list_orders, cancel_order }, mutable: true });
|
|
258
306
|
```
|
|
259
307
|
|
|
260
308
|
#### `.tools`
|
|
@@ -275,7 +323,7 @@ const { activeTools } = toolSet;
|
|
|
275
323
|
|
|
276
324
|
#### `.activate(names)`
|
|
277
325
|
|
|
278
|
-
Statically activate tools by name. Returns a new `
|
|
326
|
+
Statically activate tools by name. Returns a new instance (immutable) or `this` (mutable).
|
|
279
327
|
|
|
280
328
|
```ts
|
|
281
329
|
toolSet.activate(['cancel_order']);
|
|
@@ -283,7 +331,7 @@ toolSet.activate(['cancel_order']);
|
|
|
283
331
|
|
|
284
332
|
#### `.deactivate(names)`
|
|
285
333
|
|
|
286
|
-
Statically deactivate tools by name. Returns a new `
|
|
334
|
+
Statically deactivate tools by name. Returns a new instance (immutable) or `this` (mutable).
|
|
287
335
|
|
|
288
336
|
```ts
|
|
289
337
|
toolSet.deactivate(['search']);
|
|
@@ -312,14 +360,25 @@ toolSet.deactivateWhen('search', ({ messages }) => messages.length > 10);
|
|
|
312
360
|
|
|
313
361
|
#### `.inferTools(input)`
|
|
314
362
|
|
|
315
|
-
Evaluate all predicates with the provided messages and context. Returns
|
|
363
|
+
Evaluate all predicates with the provided messages and context. Returns `{ tools, activeTools }` — directly spreadable into `generateText()` or `streamText()`. This is a terminal operation: messages and context are consumed but not stored.
|
|
316
364
|
|
|
317
365
|
- `input`:
|
|
318
366
|
- `messages`, the current conversation messages
|
|
319
367
|
- `context`, arbitrary values passed to predicates
|
|
320
368
|
|
|
321
369
|
```ts
|
|
322
|
-
toolSet.inferTools({ messages, context: {} });
|
|
370
|
+
const { tools, activeTools } = toolSet.inferTools({ messages, context: {} });
|
|
371
|
+
|
|
372
|
+
const result = await generateText({ model, tools, activeTools, messages });
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
#### `.clone(options?)`
|
|
376
|
+
|
|
377
|
+
Clone the toolset, preserving all activation entries. Pass `{ mutable: true }` to get a mutable clone, or omit for an immutable clone. Defaults to immutable.
|
|
378
|
+
|
|
379
|
+
```ts
|
|
380
|
+
const mutableClone = toolSet.clone({ mutable: true });
|
|
381
|
+
const immutableClone = toolSet.clone();
|
|
323
382
|
```
|
|
324
383
|
|
|
325
384
|
## Types
|
|
@@ -361,6 +420,38 @@ type MyUIMessage = UIMessage<unknown, any, InferUIToolSet<typeof toolSet>>;
|
|
|
361
420
|
// message.parts[0].output // typed as search tool's return type
|
|
362
421
|
```
|
|
363
422
|
|
|
423
|
+
### `ActiveTools`
|
|
424
|
+
|
|
425
|
+
Extract the tool names tracked as active from an immutable `ToolSet` instance. Tracks tools from `.activate()` and `.deactivateWhen()`.
|
|
426
|
+
|
|
427
|
+
> [!NOTE]
|
|
428
|
+
> `ActiveTools` returns `never` for mutable toolsets, since TypeScript cannot track type changes on the same reference across method calls.
|
|
429
|
+
|
|
430
|
+
```ts
|
|
431
|
+
import type { ActiveTools } from 'ai-tool-set';
|
|
432
|
+
|
|
433
|
+
const toolSet = createToolSet({ tools }).deactivate(['cancel_order']);
|
|
434
|
+
|
|
435
|
+
type Active = ActiveTools<typeof toolSet>;
|
|
436
|
+
// 'search' | 'list_orders'
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
### `InactiveTools`
|
|
440
|
+
|
|
441
|
+
Extract the tool names tracked as inactive from an immutable `ToolSet` instance. Tracks tools from `.deactivate()` and `.activateWhen()`.
|
|
442
|
+
|
|
443
|
+
> [!NOTE]
|
|
444
|
+
> `InactiveTools` returns `never` for mutable toolsets, since TypeScript cannot track type changes on the same reference across method calls.
|
|
445
|
+
|
|
446
|
+
```ts
|
|
447
|
+
import type { InactiveTools } from 'ai-tool-set';
|
|
448
|
+
|
|
449
|
+
const toolSet = createToolSet({ tools }).deactivate(['cancel_order']);
|
|
450
|
+
|
|
451
|
+
type Inactive = InactiveTools<typeof toolSet>;
|
|
452
|
+
// 'cancel_order'
|
|
453
|
+
```
|
|
454
|
+
|
|
364
455
|
## License
|
|
365
456
|
|
|
366
457
|
MIT
|
package/dist/index.d.mts
CHANGED
|
@@ -8,9 +8,19 @@ type MessageType = UIMessage | ModelMessage;
|
|
|
8
8
|
/** The fully-typed UIMessage for a given tool record. */
|
|
9
9
|
type InferUIMessage<TOOLS extends ToolRecord> = UIMessage<unknown, any, InferUIToolSet<TOOLS>>;
|
|
10
10
|
/** Infer the raw tool record from a ToolRecord or ToolSet instance. */
|
|
11
|
-
type InferToolSet<T extends ToolRecord |
|
|
11
|
+
type InferToolSet<T extends ToolRecord | AnyToolSet> = T extends ImmutableToolSet<infer TOOLS, any, any> ? TOOLS : T extends MutableToolSet<infer TOOLS, any, any> ? TOOLS : T;
|
|
12
12
|
/** Infer the UI tool types from a tool record or ToolSet instance. */
|
|
13
|
-
type InferUIToolSet<T extends ToolRecord |
|
|
13
|
+
type InferUIToolSet<T extends ToolRecord | AnyToolSet> = { [K in keyof InferToolSet<T> & string]: InferUITool<InferToolSet<T>[K]> };
|
|
14
|
+
/**
|
|
15
|
+
* Extract tool names tracked as active from an ImmutableToolSet instance.
|
|
16
|
+
* Returns `never` for MutableToolSet (cannot be determined at compile time).
|
|
17
|
+
*/
|
|
18
|
+
type ActiveTools<T extends AnyToolSet> = T extends ImmutableToolSet<any, any, any, infer A, any> ? A : never;
|
|
19
|
+
/**
|
|
20
|
+
* Extract tool names tracked as inactive from an ImmutableToolSet instance.
|
|
21
|
+
* Returns `never` for MutableToolSet (cannot be determined at compile time).
|
|
22
|
+
*/
|
|
23
|
+
type InactiveTools<T extends AnyToolSet> = T extends ImmutableToolSet<any, any, any, any, infer D> ? D : never;
|
|
14
24
|
/**
|
|
15
25
|
* Input passed to activation predicates.
|
|
16
26
|
* Use `ActivationInput<MyMsg>` to get per-tool narrowing in callbacks.
|
|
@@ -25,28 +35,42 @@ type ActivationEntry = {
|
|
|
25
35
|
toolName: string;
|
|
26
36
|
resolve: (input: ActivationInput<any, any>) => boolean;
|
|
27
37
|
};
|
|
28
|
-
|
|
38
|
+
/** Resolved tools and active tool names returned by `inferTools()`. */
|
|
39
|
+
type ResolvedToolSet<TOOLS extends ToolRecord> = {
|
|
29
40
|
tools: TOOLS;
|
|
30
|
-
|
|
31
|
-
input: ActivationInput<MESSAGE, CONTEXT>;
|
|
41
|
+
activeTools: Array<keyof TOOLS & string>;
|
|
32
42
|
};
|
|
43
|
+
/** Union of both toolset classes for type utility constraints. */
|
|
44
|
+
type AnyToolSet = ImmutableToolSet<any> | MutableToolSet<any>;
|
|
45
|
+
/**
|
|
46
|
+
* Immutable state container for tool activation.
|
|
47
|
+
*
|
|
48
|
+
* All mutation methods return a new ToolSetState instance.
|
|
49
|
+
* Resolution follows "last-call wins": each method appends entries,
|
|
50
|
+
* and the last entry for each tool determines its state.
|
|
51
|
+
*/
|
|
52
|
+
declare class ToolSetState<TOOLS extends ToolRecord, MESSAGE extends MessageType = UIMessage, CONTEXT extends Record<string, unknown> = Record<string, unknown>> {
|
|
53
|
+
#private;
|
|
54
|
+
constructor(tools: TOOLS, entries: Array<ActivationEntry>);
|
|
55
|
+
/** All tools as a standard AI SDK tool record. */
|
|
56
|
+
get tools(): TOOLS;
|
|
57
|
+
/** Resolved list of active tool names based on current entries (no predicates, default input). */
|
|
58
|
+
get activeTools(): Array<keyof TOOLS & string>;
|
|
59
|
+
activate(names: Array<string>): ToolSetState<TOOLS, MESSAGE, CONTEXT>;
|
|
60
|
+
deactivate(names: Array<string>): ToolSetState<TOOLS, MESSAGE, CONTEXT>;
|
|
61
|
+
activateWhen(nameOrPredicates: string | Partial<Record<string, ActivationPredicate<MESSAGE, CONTEXT>>>, predicate?: ActivationPredicate<MESSAGE, CONTEXT>): ToolSetState<TOOLS, MESSAGE, CONTEXT>;
|
|
62
|
+
deactivateWhen(nameOrPredicates: string | Partial<Record<string, ActivationPredicate<MESSAGE, CONTEXT>>>, predicate?: ActivationPredicate<MESSAGE, CONTEXT>): ToolSetState<TOOLS, MESSAGE, CONTEXT>;
|
|
63
|
+
/** Evaluate all predicates with the provided input and return resolved tools + activeTools. */
|
|
64
|
+
inferTools(input: ActivationInput<MESSAGE, CONTEXT>): ResolvedToolSet<TOOLS>;
|
|
65
|
+
}
|
|
33
66
|
/**
|
|
34
67
|
* An immutable tool set with chainable activation methods.
|
|
35
68
|
*
|
|
36
69
|
* Resolution follows "last-call wins": each method appends an entry,
|
|
37
70
|
* and the last entry for each tool determines its state.
|
|
38
71
|
* Default (no entry) is active.
|
|
39
|
-
*
|
|
40
|
-
* @example
|
|
41
|
-
* ```typescript
|
|
42
|
-
* const toolSet = createToolSet({ search, list_orders, cancel_order })
|
|
43
|
-
* .deactivate(['cancel_order'])
|
|
44
|
-
* .activateWhen('cancel_order', ({ messages }) => hasUnfulfilledOrders(messages));
|
|
45
|
-
*
|
|
46
|
-
* const { tools, activeTools } = toolSet.inferTools({ messages, context: {} });
|
|
47
|
-
* ```
|
|
48
72
|
*/
|
|
49
|
-
declare class
|
|
73
|
+
declare class ImmutableToolSet<TOOLS extends ToolRecord, MESSAGE extends MessageType = UIMessage, CONTEXT extends Record<string, unknown> = Record<string, unknown>, ACTIVATED extends string = never, DEACTIVATED extends string = never> {
|
|
50
74
|
#private;
|
|
51
75
|
/** All tools as a standard AI SDK tool record. */
|
|
52
76
|
readonly tools: TOOLS;
|
|
@@ -54,36 +78,84 @@ declare class ToolSet<TOOLS extends ToolRecord, MESSAGE extends MessageType = UI
|
|
|
54
78
|
readonly activeTools: Array<keyof TOOLS & string>;
|
|
55
79
|
constructor(state: ToolSetState<TOOLS, MESSAGE, CONTEXT>);
|
|
56
80
|
/** Statically activate tools by name. */
|
|
57
|
-
activate<NAMES extends
|
|
81
|
+
activate<NAMES extends keyof TOOLS & string>(names: Array<NAMES>): ImmutableToolSet<TOOLS, MESSAGE, CONTEXT, ACTIVATED | NAMES, Exclude<DEACTIVATED, NAMES>>;
|
|
82
|
+
/** Statically deactivate tools by name. */
|
|
83
|
+
deactivate<NAMES extends keyof TOOLS & string>(names: Array<NAMES>): ImmutableToolSet<TOOLS, MESSAGE, CONTEXT, Exclude<ACTIVATED, NAMES>, DEACTIVATED | NAMES>;
|
|
84
|
+
/**
|
|
85
|
+
* Conditionally activate a tool — inactive by default, becomes active when predicate returns true.
|
|
86
|
+
* Tracks names in DEACTIVATED since the tool starts inactive.
|
|
87
|
+
*/
|
|
88
|
+
activateWhen<NAME extends keyof TOOLS & string>(name: NAME, predicate: ActivationPredicate<MESSAGE, CONTEXT>): ImmutableToolSet<TOOLS, MESSAGE, CONTEXT, Exclude<ACTIVATED, NAME>, DEACTIVATED | NAME>;
|
|
89
|
+
activateWhen<NAMES extends keyof TOOLS & string>(predicates: Partial<Record<NAMES, ActivationPredicate<MESSAGE, CONTEXT>>>): ImmutableToolSet<TOOLS, MESSAGE, CONTEXT, Exclude<ACTIVATED, NAMES>, DEACTIVATED | NAMES>;
|
|
90
|
+
/**
|
|
91
|
+
* Conditionally deactivate a tool — active by default, becomes inactive when predicate returns true.
|
|
92
|
+
* Tracks names in ACTIVATED since the tool starts active.
|
|
93
|
+
*/
|
|
94
|
+
deactivateWhen<NAME extends keyof TOOLS & string>(name: NAME, predicate: ActivationPredicate<MESSAGE, CONTEXT>): ImmutableToolSet<TOOLS, MESSAGE, CONTEXT, ACTIVATED | NAME, Exclude<DEACTIVATED, NAME>>;
|
|
95
|
+
deactivateWhen<NAMES extends keyof TOOLS & string>(predicates: Partial<Record<NAMES, ActivationPredicate<MESSAGE, CONTEXT>>>): ImmutableToolSet<TOOLS, MESSAGE, CONTEXT, ACTIVATED | NAMES, Exclude<DEACTIVATED, NAMES>>;
|
|
96
|
+
/** Evaluate all predicates with the provided input. Returns resolved `{ tools, activeTools }`. */
|
|
97
|
+
inferTools(input: ActivationInput<MESSAGE, CONTEXT>): ResolvedToolSet<TOOLS>;
|
|
98
|
+
/** Clone this toolset, optionally switching between immutable and mutable. */
|
|
99
|
+
clone(options: {
|
|
100
|
+
mutable: true;
|
|
101
|
+
}): MutableToolSet<TOOLS, MESSAGE, CONTEXT>;
|
|
102
|
+
clone(options?: {
|
|
103
|
+
mutable?: false;
|
|
104
|
+
}): ImmutableToolSet<TOOLS, MESSAGE, CONTEXT, ACTIVATED, DEACTIVATED>;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* A mutable tool set with chainable activation methods.
|
|
108
|
+
*
|
|
109
|
+
* Same resolution semantics as ImmutableToolSet, but methods mutate
|
|
110
|
+
* in-place and return `this` instead of creating new instances.
|
|
111
|
+
*/
|
|
112
|
+
declare class MutableToolSet<TOOLS extends ToolRecord, MESSAGE extends MessageType = UIMessage, CONTEXT extends Record<string, unknown> = Record<string, unknown>> {
|
|
113
|
+
#private;
|
|
114
|
+
/** All tools as a standard AI SDK tool record. */
|
|
115
|
+
readonly tools: TOOLS;
|
|
116
|
+
/** Resolved list of active tool names based on current state. */
|
|
117
|
+
activeTools: Array<keyof TOOLS & string>;
|
|
118
|
+
constructor(state: ToolSetState<TOOLS, MESSAGE, CONTEXT>);
|
|
119
|
+
/** Statically activate tools by name. */
|
|
120
|
+
activate(names: Array<keyof TOOLS & string>): this;
|
|
58
121
|
/** Statically deactivate tools by name. */
|
|
59
|
-
deactivate<
|
|
122
|
+
deactivate(names: Array<keyof TOOLS & string>): this;
|
|
60
123
|
/**
|
|
61
|
-
* Conditionally activate a tool — becomes active when predicate returns true.
|
|
62
|
-
*
|
|
63
|
-
* - `.activateWhen('tool_name', (input) => ...)`
|
|
64
|
-
* - `.activateWhen({ tool_name: (input) => ... })`
|
|
124
|
+
* Conditionally activate a tool — inactive by default, becomes active when predicate returns true.
|
|
65
125
|
*/
|
|
66
|
-
activateWhen
|
|
67
|
-
activateWhen(predicates: Partial<Record<keyof TOOLS & string, ActivationPredicate<MESSAGE, CONTEXT>>>):
|
|
126
|
+
activateWhen(name: keyof TOOLS & string, predicate: ActivationPredicate<MESSAGE, CONTEXT>): this;
|
|
127
|
+
activateWhen(predicates: Partial<Record<keyof TOOLS & string, ActivationPredicate<MESSAGE, CONTEXT>>>): this;
|
|
68
128
|
/**
|
|
69
|
-
* Conditionally deactivate a tool — becomes inactive when predicate returns true.
|
|
70
|
-
* Internally wraps with negation so resolve returns true (active) when pred is false.
|
|
71
|
-
*
|
|
72
|
-
* - `.deactivateWhen('tool_name', (input) => ...)`
|
|
73
|
-
* - `.deactivateWhen({ tool_name: (input) => ... })`
|
|
129
|
+
* Conditionally deactivate a tool — active by default, becomes inactive when predicate returns true.
|
|
74
130
|
*/
|
|
75
|
-
deactivateWhen
|
|
76
|
-
deactivateWhen(predicates: Partial<Record<keyof TOOLS & string, ActivationPredicate<MESSAGE, CONTEXT>>>):
|
|
77
|
-
/**
|
|
78
|
-
inferTools(input: ActivationInput<MESSAGE, CONTEXT>):
|
|
131
|
+
deactivateWhen(name: keyof TOOLS & string, predicate: ActivationPredicate<MESSAGE, CONTEXT>): this;
|
|
132
|
+
deactivateWhen(predicates: Partial<Record<keyof TOOLS & string, ActivationPredicate<MESSAGE, CONTEXT>>>): this;
|
|
133
|
+
/** Evaluate all predicates with the provided input. Returns resolved `{ tools, activeTools }`. */
|
|
134
|
+
inferTools(input: ActivationInput<MESSAGE, CONTEXT>): ResolvedToolSet<TOOLS>;
|
|
135
|
+
/** Clone this toolset, optionally switching between immutable and mutable. */
|
|
136
|
+
clone(options: {
|
|
137
|
+
mutable: true;
|
|
138
|
+
}): MutableToolSet<TOOLS, MESSAGE, CONTEXT>;
|
|
139
|
+
clone(options?: {
|
|
140
|
+
mutable?: false;
|
|
141
|
+
}): ImmutableToolSet<TOOLS, MESSAGE, CONTEXT, keyof TOOLS & string>;
|
|
79
142
|
}
|
|
143
|
+
type CreateToolSetOptions<TOOLS extends ToolRecord> = {
|
|
144
|
+
tools: TOOLS;
|
|
145
|
+
mutable?: boolean;
|
|
146
|
+
};
|
|
80
147
|
/**
|
|
81
|
-
* Create a chainable
|
|
148
|
+
* Create a chainable tool set.
|
|
82
149
|
*
|
|
83
150
|
* @typeParam TOOLS — inferred from the argument
|
|
84
151
|
* @typeParam MESSAGE — defaults to the fully-typed UIMessage derived from TOOLS
|
|
85
152
|
* @typeParam CONTEXT — defaults to Record<string, unknown>
|
|
86
153
|
*/
|
|
87
|
-
declare
|
|
154
|
+
declare function createToolSet<const TOOLS extends ToolRecord, MESSAGE extends MessageType = InferUIMessage<TOOLS>, CONTEXT extends Record<string, unknown> = Record<string, unknown>>(options: CreateToolSetOptions<TOOLS> & {
|
|
155
|
+
mutable: true;
|
|
156
|
+
}): MutableToolSet<TOOLS, MESSAGE, CONTEXT>;
|
|
157
|
+
declare function createToolSet<const TOOLS extends ToolRecord, MESSAGE extends MessageType = InferUIMessage<TOOLS>, CONTEXT extends Record<string, unknown> = Record<string, unknown>>(options: CreateToolSetOptions<TOOLS> & {
|
|
158
|
+
mutable?: false;
|
|
159
|
+
}): ImmutableToolSet<TOOLS, MESSAGE, CONTEXT, keyof TOOLS & string>;
|
|
88
160
|
//#endregion
|
|
89
|
-
export { type ActivationInput, type InferToolSet, type InferUIToolSet, createToolSet };
|
|
161
|
+
export { type ActivationInput, type ActiveTools, type InactiveTools, type InferToolSet, type InferUIToolSet, createToolSet };
|
package/dist/index.mjs
CHANGED
|
@@ -10,99 +10,157 @@ const toEntries = (nameOrPredicates, predicate) => {
|
|
|
10
10
|
}));
|
|
11
11
|
};
|
|
12
12
|
/**
|
|
13
|
-
*
|
|
13
|
+
* Immutable state container for tool activation.
|
|
14
14
|
*
|
|
15
|
-
*
|
|
15
|
+
* All mutation methods return a new ToolSetState instance.
|
|
16
|
+
* Resolution follows "last-call wins": each method appends entries,
|
|
16
17
|
* and the last entry for each tool determines its state.
|
|
17
|
-
* Default (no entry) is active.
|
|
18
|
-
*
|
|
19
|
-
* @example
|
|
20
|
-
* ```typescript
|
|
21
|
-
* const toolSet = createToolSet({ search, list_orders, cancel_order })
|
|
22
|
-
* .deactivate(['cancel_order'])
|
|
23
|
-
* .activateWhen('cancel_order', ({ messages }) => hasUnfulfilledOrders(messages));
|
|
24
|
-
*
|
|
25
|
-
* const { tools, activeTools } = toolSet.inferTools({ messages, context: {} });
|
|
26
|
-
* ```
|
|
27
18
|
*/
|
|
28
|
-
var
|
|
29
|
-
#
|
|
19
|
+
var ToolSetState = class ToolSetState {
|
|
20
|
+
#tools;
|
|
21
|
+
#entries;
|
|
22
|
+
constructor(tools, entries) {
|
|
23
|
+
this.#tools = tools;
|
|
24
|
+
this.#entries = entries;
|
|
25
|
+
}
|
|
30
26
|
/** All tools as a standard AI SDK tool record. */
|
|
31
|
-
tools
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
this.
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
return lastEntry.resolve(state.input);
|
|
41
|
-
});
|
|
27
|
+
get tools() {
|
|
28
|
+
return this.#tools;
|
|
29
|
+
}
|
|
30
|
+
/** Resolved list of active tool names based on current entries (no predicates, default input). */
|
|
31
|
+
get activeTools() {
|
|
32
|
+
return this.inferTools({
|
|
33
|
+
messages: [],
|
|
34
|
+
context: {}
|
|
35
|
+
}).activeTools;
|
|
42
36
|
}
|
|
43
|
-
/** Statically activate tools by name. */
|
|
44
37
|
activate(names) {
|
|
45
38
|
const newEntries = names.map((name) => ({
|
|
46
39
|
toolName: name,
|
|
47
40
|
resolve: () => true
|
|
48
41
|
}));
|
|
49
|
-
return new
|
|
50
|
-
...this.#state,
|
|
51
|
-
entries: [...this.#state.entries, ...newEntries]
|
|
52
|
-
});
|
|
42
|
+
return new ToolSetState(this.#tools, [...this.#entries, ...newEntries]);
|
|
53
43
|
}
|
|
54
|
-
/** Statically deactivate tools by name. */
|
|
55
44
|
deactivate(names) {
|
|
56
45
|
const newEntries = names.map((name) => ({
|
|
57
46
|
toolName: name,
|
|
58
47
|
resolve: () => false
|
|
59
48
|
}));
|
|
60
|
-
return new
|
|
61
|
-
...this.#state,
|
|
62
|
-
entries: [...this.#state.entries, ...newEntries]
|
|
63
|
-
});
|
|
49
|
+
return new ToolSetState(this.#tools, [...this.#entries, ...newEntries]);
|
|
64
50
|
}
|
|
65
51
|
activateWhen(nameOrPredicates, predicate) {
|
|
66
|
-
|
|
67
|
-
return new ToolSet({
|
|
68
|
-
...this.#state,
|
|
69
|
-
entries: [...this.#state.entries, ...newEntries]
|
|
70
|
-
});
|
|
52
|
+
return new ToolSetState(this.#tools, [...this.#entries, ...toEntries(nameOrPredicates, predicate)]);
|
|
71
53
|
}
|
|
72
54
|
deactivateWhen(nameOrPredicates, predicate) {
|
|
73
55
|
const newEntries = toEntries(nameOrPredicates, predicate).map((e) => ({
|
|
74
56
|
...e,
|
|
75
57
|
resolve: (input) => !e.resolve(input)
|
|
76
58
|
}));
|
|
77
|
-
return new
|
|
78
|
-
...this.#state,
|
|
79
|
-
entries: [...this.#state.entries, ...newEntries]
|
|
80
|
-
});
|
|
59
|
+
return new ToolSetState(this.#tools, [...this.#entries, ...newEntries]);
|
|
81
60
|
}
|
|
82
|
-
/**
|
|
61
|
+
/** Evaluate all predicates with the provided input and return resolved tools + activeTools. */
|
|
83
62
|
inferTools(input) {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
63
|
+
const activeTools = Object.keys(this.#tools).filter((name) => {
|
|
64
|
+
const lastEntry = this.#entries.findLast((e) => e.toolName === name);
|
|
65
|
+
if (!lastEntry) return true;
|
|
66
|
+
return lastEntry.resolve(input);
|
|
87
67
|
});
|
|
68
|
+
return {
|
|
69
|
+
tools: this.#tools,
|
|
70
|
+
activeTools
|
|
71
|
+
};
|
|
88
72
|
}
|
|
89
73
|
};
|
|
90
74
|
/**
|
|
91
|
-
*
|
|
75
|
+
* An immutable tool set with chainable activation methods.
|
|
92
76
|
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
77
|
+
* Resolution follows "last-call wins": each method appends an entry,
|
|
78
|
+
* and the last entry for each tool determines its state.
|
|
79
|
+
* Default (no entry) is active.
|
|
96
80
|
*/
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
81
|
+
var ImmutableToolSet = class ImmutableToolSet {
|
|
82
|
+
#state;
|
|
83
|
+
/** All tools as a standard AI SDK tool record. */
|
|
84
|
+
tools;
|
|
85
|
+
/** Resolved list of active tool names based on current state. */
|
|
86
|
+
activeTools;
|
|
87
|
+
constructor(state) {
|
|
88
|
+
this.#state = state;
|
|
89
|
+
this.tools = state.tools;
|
|
90
|
+
this.activeTools = state.activeTools;
|
|
91
|
+
}
|
|
92
|
+
/** Statically activate tools by name. */
|
|
93
|
+
activate(names) {
|
|
94
|
+
return new ImmutableToolSet(this.#state.activate(names));
|
|
95
|
+
}
|
|
96
|
+
/** Statically deactivate tools by name. */
|
|
97
|
+
deactivate(names) {
|
|
98
|
+
return new ImmutableToolSet(this.#state.deactivate(names));
|
|
99
|
+
}
|
|
100
|
+
activateWhen(nameOrPredicates, predicate) {
|
|
101
|
+
return new ImmutableToolSet(this.#state.activateWhen(nameOrPredicates, predicate));
|
|
102
|
+
}
|
|
103
|
+
deactivateWhen(nameOrPredicates, predicate) {
|
|
104
|
+
return new ImmutableToolSet(this.#state.deactivateWhen(nameOrPredicates, predicate));
|
|
105
|
+
}
|
|
106
|
+
/** Evaluate all predicates with the provided input. Returns resolved `{ tools, activeTools }`. */
|
|
107
|
+
inferTools(input) {
|
|
108
|
+
return this.#state.inferTools(input);
|
|
109
|
+
}
|
|
110
|
+
clone(options) {
|
|
111
|
+
return options?.mutable ? new MutableToolSet(this.#state) : new ImmutableToolSet(this.#state);
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
/**
|
|
115
|
+
* A mutable tool set with chainable activation methods.
|
|
116
|
+
*
|
|
117
|
+
* Same resolution semantics as ImmutableToolSet, but methods mutate
|
|
118
|
+
* in-place and return `this` instead of creating new instances.
|
|
119
|
+
*/
|
|
120
|
+
var MutableToolSet = class MutableToolSet {
|
|
121
|
+
#state;
|
|
122
|
+
/** All tools as a standard AI SDK tool record. */
|
|
123
|
+
tools;
|
|
124
|
+
/** Resolved list of active tool names based on current state. */
|
|
125
|
+
activeTools;
|
|
126
|
+
constructor(state) {
|
|
127
|
+
this.#state = state;
|
|
128
|
+
this.tools = state.tools;
|
|
129
|
+
this.activeTools = state.activeTools;
|
|
130
|
+
}
|
|
131
|
+
#apply(state) {
|
|
132
|
+
this.#state = state;
|
|
133
|
+
this.activeTools = state.activeTools;
|
|
134
|
+
}
|
|
135
|
+
/** Statically activate tools by name. */
|
|
136
|
+
activate(names) {
|
|
137
|
+
this.#apply(this.#state.activate(names));
|
|
138
|
+
return this;
|
|
139
|
+
}
|
|
140
|
+
/** Statically deactivate tools by name. */
|
|
141
|
+
deactivate(names) {
|
|
142
|
+
this.#apply(this.#state.deactivate(names));
|
|
143
|
+
return this;
|
|
144
|
+
}
|
|
145
|
+
activateWhen(nameOrPredicates, predicate) {
|
|
146
|
+
this.#apply(this.#state.activateWhen(nameOrPredicates, predicate));
|
|
147
|
+
return this;
|
|
148
|
+
}
|
|
149
|
+
deactivateWhen(nameOrPredicates, predicate) {
|
|
150
|
+
this.#apply(this.#state.deactivateWhen(nameOrPredicates, predicate));
|
|
151
|
+
return this;
|
|
152
|
+
}
|
|
153
|
+
/** Evaluate all predicates with the provided input. Returns resolved `{ tools, activeTools }`. */
|
|
154
|
+
inferTools(input) {
|
|
155
|
+
return this.#state.inferTools(input);
|
|
156
|
+
}
|
|
157
|
+
clone(options) {
|
|
158
|
+
return options?.mutable ? new MutableToolSet(this.#state) : new ImmutableToolSet(this.#state);
|
|
159
|
+
}
|
|
106
160
|
};
|
|
161
|
+
function createToolSet(options) {
|
|
162
|
+
const state = new ToolSetState(options.tools, []);
|
|
163
|
+
return options.mutable ? new MutableToolSet(state) : new ImmutableToolSet(state);
|
|
164
|
+
}
|
|
107
165
|
//#endregion
|
|
108
166
|
export { createToolSet };
|