@lantos1618/better-ui 0.2.3 → 0.4.0
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 +271 -354
- package/dist/ThemeProvider-BYeqWMsn.d.mts +187 -0
- package/dist/ThemeProvider-BaVZaDBO.d.ts +187 -0
- package/dist/auth/index.d.mts +56 -0
- package/dist/auth/index.d.ts +56 -0
- package/dist/auth/index.js +104 -0
- package/dist/auth/index.mjs +67 -0
- package/dist/chunk-Y6FXYEAI.mjs +10 -0
- package/dist/components/index.d.mts +258 -0
- package/dist/components/index.d.ts +258 -0
- package/dist/components/index.js +1977 -0
- package/dist/components/index.mjs +1922 -0
- package/dist/index.d.mts +75 -0
- package/dist/index.d.ts +75 -0
- package/dist/index.js +587 -0
- package/dist/index.mjs +557 -0
- package/dist/persistence/index.d.mts +11 -0
- package/dist/persistence/index.d.ts +11 -0
- package/dist/persistence/index.js +66 -0
- package/dist/persistence/index.mjs +41 -0
- package/dist/react/index.d.mts +91 -0
- package/dist/react/index.d.ts +91 -0
- package/dist/react/index.js +284 -0
- package/dist/react/index.mjs +257 -0
- package/dist/tool-Ca2x-VNK.d.mts +361 -0
- package/dist/tool-Ca2x-VNK.d.ts +361 -0
- package/dist/types-CAOfGUPH.d.mts +31 -0
- package/dist/types-CAOfGUPH.d.ts +31 -0
- package/package.json +85 -30
- package/src/theme.css +101 -0
- package/lib/aui/README.md +0 -136
- package/lib/aui/__tests__/aui-complete.test.ts +0 -251
- package/lib/aui/__tests__/aui-comprehensive.test.ts +0 -376
- package/lib/aui/__tests__/aui-concise.test.ts +0 -278
- package/lib/aui/__tests__/aui-integration.test.ts +0 -309
- package/lib/aui/__tests__/aui-simple.test.ts +0 -116
- package/lib/aui/__tests__/aui.test.ts +0 -269
- package/lib/aui/__tests__/concise-api.test.ts +0 -165
- package/lib/aui/__tests__/core.test.ts +0 -265
- package/lib/aui/__tests__/simple-api.test.ts +0 -200
- package/lib/aui/ai-assistant.ts +0 -408
- package/lib/aui/ai-control.ts +0 -353
- package/lib/aui/client/use-aui.ts +0 -55
- package/lib/aui/client-control.ts +0 -551
- package/lib/aui/client-executor.ts +0 -417
- package/lib/aui/components/ToolRenderer.tsx +0 -22
- package/lib/aui/core.ts +0 -137
- package/lib/aui/demo.tsx +0 -89
- package/lib/aui/examples/ai-complete-demo.tsx +0 -359
- package/lib/aui/examples/ai-control-demo.tsx +0 -356
- package/lib/aui/examples/ai-control-tools.ts +0 -308
- package/lib/aui/examples/concise-api.tsx +0 -153
- package/lib/aui/examples/index.tsx +0 -163
- package/lib/aui/examples/quick-demo.tsx +0 -91
- package/lib/aui/examples/simple-demo.tsx +0 -71
- package/lib/aui/examples/simple-tools.tsx +0 -160
- package/lib/aui/examples/user-api.tsx +0 -208
- package/lib/aui/examples/user-requested.tsx +0 -174
- package/lib/aui/examples/weather-search-tools.tsx +0 -119
- package/lib/aui/examples.tsx +0 -367
- package/lib/aui/hooks/useAUITool.ts +0 -142
- package/lib/aui/hooks/useAUIToolEnhanced.ts +0 -343
- package/lib/aui/hooks/useAUITools.ts +0 -195
- package/lib/aui/index.ts +0 -156
- package/lib/aui/provider.tsx +0 -45
- package/lib/aui/server-control.ts +0 -386
- package/lib/aui/server-executor.ts +0 -165
- package/lib/aui/server.ts +0 -167
- package/lib/aui/tool-registry.ts +0 -380
- package/lib/aui/tools/advanced-examples.tsx +0 -86
- package/lib/aui/tools/ai-complete.ts +0 -375
- package/lib/aui/tools/api-tools.tsx +0 -230
- package/lib/aui/tools/data-tools.tsx +0 -232
- package/lib/aui/tools/dom-tools.tsx +0 -202
- package/lib/aui/tools/examples.ts +0 -43
- package/lib/aui/tools/file-tools.tsx +0 -202
- package/lib/aui/tools/form-tools.tsx +0 -233
- package/lib/aui/tools/index.ts +0 -8
- package/lib/aui/tools/navigation-tools.tsx +0 -172
- package/lib/aui/tools/notification-tools.ts +0 -213
- package/lib/aui/tools/state-tools.tsx +0 -209
- package/lib/aui/types.ts +0 -47
- package/lib/aui/vercel-ai.ts +0 -100
package/dist/index.js
ADDED
|
@@ -0,0 +1,587 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
Tool: () => Tool,
|
|
24
|
+
ToolBuilder: () => ToolBuilder,
|
|
25
|
+
createAnthropicProvider: () => createAnthropicProvider,
|
|
26
|
+
createGoogleProvider: () => createGoogleProvider,
|
|
27
|
+
createOpenAIProvider: () => createOpenAIProvider,
|
|
28
|
+
createOpenRouterProvider: () => createOpenRouterProvider,
|
|
29
|
+
createProvider: () => createProvider,
|
|
30
|
+
tool: () => tool
|
|
31
|
+
});
|
|
32
|
+
module.exports = __toCommonJS(index_exports);
|
|
33
|
+
|
|
34
|
+
// src/tool.tsx
|
|
35
|
+
var import_react = require("react");
|
|
36
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
37
|
+
var Tool = class {
|
|
38
|
+
constructor(config) {
|
|
39
|
+
this.name = config.name;
|
|
40
|
+
this.description = config.description;
|
|
41
|
+
this.inputSchema = config.input;
|
|
42
|
+
this.outputSchema = config.output;
|
|
43
|
+
this.tags = config.tags || [];
|
|
44
|
+
this.cacheConfig = config.cache;
|
|
45
|
+
this.clientFetchConfig = config.clientFetch;
|
|
46
|
+
this.confirm = config.confirm ?? false;
|
|
47
|
+
this.hints = config.hints ?? {};
|
|
48
|
+
this.groupKey = config.groupKey;
|
|
49
|
+
this.autoRespond = config.autoRespond ?? false;
|
|
50
|
+
this._initView();
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Define server-side implementation
|
|
54
|
+
* Runs on server (API routes, server components, etc.)
|
|
55
|
+
*/
|
|
56
|
+
server(handler) {
|
|
57
|
+
this._server = handler;
|
|
58
|
+
return this;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Define client-side implementation
|
|
62
|
+
* Runs in browser. If not specified, auto-fetches to /api/tools/{name}
|
|
63
|
+
*/
|
|
64
|
+
client(handler) {
|
|
65
|
+
this._client = handler;
|
|
66
|
+
return this;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Define view component for rendering results
|
|
70
|
+
* Our differentiator from TanStack AI
|
|
71
|
+
*/
|
|
72
|
+
view(component) {
|
|
73
|
+
this._view = component;
|
|
74
|
+
this._initView();
|
|
75
|
+
return this;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Define streaming implementation
|
|
79
|
+
* The handler receives a `stream` callback to push partial updates.
|
|
80
|
+
*/
|
|
81
|
+
stream(handler) {
|
|
82
|
+
this._stream = handler;
|
|
83
|
+
return this;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Execute the tool
|
|
87
|
+
* Automatically uses server or client handler based on environment
|
|
88
|
+
*
|
|
89
|
+
* SECURITY: Server handlers only run on server. Client automatically
|
|
90
|
+
* fetches from /api/tools/execute if no client handler is defined.
|
|
91
|
+
*/
|
|
92
|
+
async run(input, ctx) {
|
|
93
|
+
const validated = this.inputSchema.parse(input);
|
|
94
|
+
const isServer = ctx?.isServer ?? typeof window === "undefined";
|
|
95
|
+
const context = {
|
|
96
|
+
cache: ctx?.cache || /* @__PURE__ */ new Map(),
|
|
97
|
+
fetch: ctx?.fetch || globalThis.fetch?.bind(globalThis),
|
|
98
|
+
isServer,
|
|
99
|
+
// Only include server-sensitive fields when actually on server
|
|
100
|
+
...isServer ? {
|
|
101
|
+
env: ctx?.env,
|
|
102
|
+
headers: ctx?.headers,
|
|
103
|
+
cookies: ctx?.cookies,
|
|
104
|
+
user: ctx?.user,
|
|
105
|
+
session: ctx?.session
|
|
106
|
+
} : {
|
|
107
|
+
// Client-only fields
|
|
108
|
+
optimistic: ctx?.optimistic
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
if (this.cacheConfig && context.cache) {
|
|
112
|
+
const cacheKey = this.cacheConfig.key ? this.cacheConfig.key(validated) : `${this.name}:${JSON.stringify(validated)}`;
|
|
113
|
+
const cached = context.cache.get(cacheKey);
|
|
114
|
+
if (cached && cached.expiry > Date.now()) {
|
|
115
|
+
return cached.data;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
let result;
|
|
119
|
+
if (context.isServer) {
|
|
120
|
+
if (!this._server) {
|
|
121
|
+
throw new Error(`Tool "${this.name}" has no server implementation`);
|
|
122
|
+
}
|
|
123
|
+
result = await this._server(validated, context);
|
|
124
|
+
} else {
|
|
125
|
+
if (this._client) {
|
|
126
|
+
result = await this._client(validated, context);
|
|
127
|
+
} else if (this._server) {
|
|
128
|
+
result = await this._defaultClientFetch(validated, context);
|
|
129
|
+
} else {
|
|
130
|
+
throw new Error(`Tool "${this.name}" has no implementation`);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
if (this.outputSchema) {
|
|
134
|
+
try {
|
|
135
|
+
result = this.outputSchema.parse(result);
|
|
136
|
+
} catch (error) {
|
|
137
|
+
console.error(`Output validation failed for tool "${this.name}":`, error);
|
|
138
|
+
if (error instanceof Error && "errors" in error) {
|
|
139
|
+
console.error("Validation errors:", error.errors);
|
|
140
|
+
}
|
|
141
|
+
throw error;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
if (this.cacheConfig && context.cache) {
|
|
145
|
+
const cacheKey = this.cacheConfig.key ? this.cacheConfig.key(validated) : `${this.name}:${JSON.stringify(validated)}`;
|
|
146
|
+
context.cache.set(cacheKey, {
|
|
147
|
+
data: result,
|
|
148
|
+
expiry: Date.now() + this.cacheConfig.ttl
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
return result;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Make the tool callable directly: await weather({ city: 'London' })
|
|
155
|
+
*/
|
|
156
|
+
async call(input, ctx) {
|
|
157
|
+
return this.run(input, ctx);
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Execute with streaming - returns async generator of partial results.
|
|
161
|
+
* Falls back to run() if no stream handler is defined.
|
|
162
|
+
*/
|
|
163
|
+
async *runStream(input, ctx) {
|
|
164
|
+
const validated = this.inputSchema.parse(input);
|
|
165
|
+
if (!this._stream) {
|
|
166
|
+
const result = await this.run(input, ctx);
|
|
167
|
+
yield { partial: result, done: true };
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
const isServer = ctx?.isServer ?? typeof window === "undefined";
|
|
171
|
+
const context = {
|
|
172
|
+
cache: ctx?.cache || /* @__PURE__ */ new Map(),
|
|
173
|
+
fetch: ctx?.fetch || globalThis.fetch?.bind(globalThis),
|
|
174
|
+
isServer,
|
|
175
|
+
...isServer ? {
|
|
176
|
+
env: ctx?.env,
|
|
177
|
+
headers: ctx?.headers,
|
|
178
|
+
cookies: ctx?.cookies,
|
|
179
|
+
user: ctx?.user,
|
|
180
|
+
session: ctx?.session
|
|
181
|
+
} : {
|
|
182
|
+
optimistic: ctx?.optimistic
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
const queue = [];
|
|
186
|
+
let waiter = null;
|
|
187
|
+
const notify = () => {
|
|
188
|
+
waiter?.();
|
|
189
|
+
waiter = null;
|
|
190
|
+
};
|
|
191
|
+
const wait = () => new Promise((r) => {
|
|
192
|
+
waiter = r;
|
|
193
|
+
});
|
|
194
|
+
const streamFn = (partial) => {
|
|
195
|
+
queue.push({ partial });
|
|
196
|
+
notify();
|
|
197
|
+
};
|
|
198
|
+
let error = null;
|
|
199
|
+
let isDone = false;
|
|
200
|
+
this._stream(validated, { ...context, stream: streamFn }).then((result) => {
|
|
201
|
+
queue.push({ done: true, result });
|
|
202
|
+
isDone = true;
|
|
203
|
+
notify();
|
|
204
|
+
}).catch((err) => {
|
|
205
|
+
error = err instanceof Error ? err : new Error(String(err));
|
|
206
|
+
isDone = true;
|
|
207
|
+
notify();
|
|
208
|
+
});
|
|
209
|
+
while (true) {
|
|
210
|
+
while (queue.length > 0) {
|
|
211
|
+
const item = queue.shift();
|
|
212
|
+
if ("done" in item) {
|
|
213
|
+
let finalResult = item.result;
|
|
214
|
+
if (this.outputSchema) finalResult = this.outputSchema.parse(finalResult);
|
|
215
|
+
yield { partial: finalResult, done: true };
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
yield { partial: item.partial, done: false };
|
|
219
|
+
}
|
|
220
|
+
if (error) throw error;
|
|
221
|
+
if (isDone && queue.length === 0) return;
|
|
222
|
+
await wait();
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Default client fetch when no .client() is defined
|
|
227
|
+
*
|
|
228
|
+
* SECURITY: This ensures server handlers never run on the client.
|
|
229
|
+
* The server-side /api/tools/execute endpoint handles execution safely.
|
|
230
|
+
*/
|
|
231
|
+
async _defaultClientFetch(input, ctx) {
|
|
232
|
+
const endpoint = this.clientFetchConfig?.endpoint || "/api/tools/execute";
|
|
233
|
+
const response = await ctx.fetch(endpoint, {
|
|
234
|
+
method: "POST",
|
|
235
|
+
headers: { "Content-Type": "application/json" },
|
|
236
|
+
body: JSON.stringify({ tool: this.name, input })
|
|
237
|
+
});
|
|
238
|
+
if (!response.ok) {
|
|
239
|
+
const error = await response.json().catch(() => ({}));
|
|
240
|
+
throw new Error(error.message || error.error || `Tool execution failed: ${response.statusText}`);
|
|
241
|
+
}
|
|
242
|
+
const data = await response.json();
|
|
243
|
+
return data.result ?? data.data ?? data;
|
|
244
|
+
}
|
|
245
|
+
_initView() {
|
|
246
|
+
const viewFn = this._view;
|
|
247
|
+
const name = this.name;
|
|
248
|
+
const ViewComponent = (props) => {
|
|
249
|
+
if (!viewFn) {
|
|
250
|
+
if (props.loading) return null;
|
|
251
|
+
if (props.error) return null;
|
|
252
|
+
if (!props.data) return null;
|
|
253
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("pre", { children: JSON.stringify(props.data, null, 2) });
|
|
254
|
+
}
|
|
255
|
+
if (!props.data && !props.loading && !props.error) {
|
|
256
|
+
return null;
|
|
257
|
+
}
|
|
258
|
+
return viewFn(props.data, {
|
|
259
|
+
loading: props.loading,
|
|
260
|
+
streaming: props.streaming,
|
|
261
|
+
error: props.error,
|
|
262
|
+
onAction: props.onAction
|
|
263
|
+
});
|
|
264
|
+
};
|
|
265
|
+
ViewComponent.displayName = `${name}View`;
|
|
266
|
+
this.View = (0, import_react.memo)(ViewComponent, (prevProps, nextProps) => {
|
|
267
|
+
if (prevProps.data !== nextProps.data) return false;
|
|
268
|
+
if (prevProps.loading !== nextProps.loading) return false;
|
|
269
|
+
if (prevProps.streaming !== nextProps.streaming) return false;
|
|
270
|
+
if (prevProps.error !== nextProps.error) return false;
|
|
271
|
+
return true;
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Check if tool has a view
|
|
276
|
+
*/
|
|
277
|
+
get hasView() {
|
|
278
|
+
return !!this._view;
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Check if tool has server implementation
|
|
282
|
+
*/
|
|
283
|
+
get hasServer() {
|
|
284
|
+
return !!this._server;
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Check if tool has custom client implementation
|
|
288
|
+
*/
|
|
289
|
+
get hasClient() {
|
|
290
|
+
return !!this._client;
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Check if tool has a streaming implementation
|
|
294
|
+
*/
|
|
295
|
+
get hasStream() {
|
|
296
|
+
return !!this._stream;
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Check if tool requires human confirmation before executing (HITL)
|
|
300
|
+
* Returns true if `confirm` is truthy (boolean true or a function) OR `hints.destructive: true`
|
|
301
|
+
*/
|
|
302
|
+
get requiresConfirmation() {
|
|
303
|
+
return !!this.confirm || this.hints.destructive === true;
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Determine if a specific input should trigger user confirmation.
|
|
307
|
+
* - If `confirm` is a function, calls it with input
|
|
308
|
+
* - If `confirm` is boolean, returns it
|
|
309
|
+
* - If `hints.destructive`, returns true
|
|
310
|
+
*/
|
|
311
|
+
shouldConfirm(input) {
|
|
312
|
+
if (typeof this.confirm === "function") {
|
|
313
|
+
if (input == null) return false;
|
|
314
|
+
return this.confirm(input);
|
|
315
|
+
}
|
|
316
|
+
if (typeof this.confirm === "boolean") {
|
|
317
|
+
return this.confirm;
|
|
318
|
+
}
|
|
319
|
+
return this.hints.destructive === true;
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Get the entity group key for a given input.
|
|
323
|
+
* Returns `"toolName:groupKey(input)"` if groupKey is defined, otherwise undefined.
|
|
324
|
+
*/
|
|
325
|
+
getGroupKey(input) {
|
|
326
|
+
if (!this.groupKey || input == null) return void 0;
|
|
327
|
+
return `${this.name}:${this.groupKey(input)}`;
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Convert to plain object (for serialization)
|
|
331
|
+
*
|
|
332
|
+
* SECURITY: This intentionally excludes handlers and schemas to prevent
|
|
333
|
+
* accidental exposure of server logic or validation details.
|
|
334
|
+
*/
|
|
335
|
+
toJSON() {
|
|
336
|
+
return {
|
|
337
|
+
name: this.name,
|
|
338
|
+
description: this.description,
|
|
339
|
+
tags: this.tags,
|
|
340
|
+
hasServer: this.hasServer,
|
|
341
|
+
hasClient: this.hasClient,
|
|
342
|
+
hasView: this.hasView,
|
|
343
|
+
hasStream: this.hasStream,
|
|
344
|
+
hasCache: !!this.cacheConfig,
|
|
345
|
+
confirm: !!this.confirm,
|
|
346
|
+
hints: this.hints,
|
|
347
|
+
requiresConfirmation: this.requiresConfirmation
|
|
348
|
+
// Intentionally NOT included: handlers, schemas, clientFetchConfig
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
/**
|
|
352
|
+
* Convert to AI SDK format (Vercel AI SDK v5 compatible)
|
|
353
|
+
*
|
|
354
|
+
* If `confirm` is true, the execute function is omitted so the AI SDK
|
|
355
|
+
* leaves the tool call at `state: 'input-available'`, enabling HITL
|
|
356
|
+
* confirmation on the client before execution.
|
|
357
|
+
*/
|
|
358
|
+
toAITool() {
|
|
359
|
+
if (this.requiresConfirmation) {
|
|
360
|
+
return {
|
|
361
|
+
description: this.description || this.name,
|
|
362
|
+
inputSchema: this.inputSchema
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
return {
|
|
366
|
+
description: this.description || this.name,
|
|
367
|
+
inputSchema: this.inputSchema,
|
|
368
|
+
execute: async (input) => {
|
|
369
|
+
return this.run(input, { isServer: true });
|
|
370
|
+
}
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
};
|
|
374
|
+
function tool(nameOrConfig) {
|
|
375
|
+
if (typeof nameOrConfig === "string") {
|
|
376
|
+
return new ToolBuilder(nameOrConfig);
|
|
377
|
+
}
|
|
378
|
+
return new Tool(nameOrConfig);
|
|
379
|
+
}
|
|
380
|
+
var ToolBuilder = class {
|
|
381
|
+
constructor(name) {
|
|
382
|
+
this._tags = [];
|
|
383
|
+
this._name = name;
|
|
384
|
+
}
|
|
385
|
+
description(desc) {
|
|
386
|
+
this._description = desc;
|
|
387
|
+
return this;
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
390
|
+
* Define input schema - enables type inference for handlers
|
|
391
|
+
*
|
|
392
|
+
* NOTE: Uses type assertion internally. This is safe because:
|
|
393
|
+
* 1. The schema is stored and used correctly at runtime
|
|
394
|
+
* 2. The return type correctly reflects the new generic parameter
|
|
395
|
+
* 3. TypeScript doesn't support "this type mutation" in fluent builders
|
|
396
|
+
*/
|
|
397
|
+
input(schema) {
|
|
398
|
+
this._input = schema;
|
|
399
|
+
return this;
|
|
400
|
+
}
|
|
401
|
+
/**
|
|
402
|
+
* Define output schema - enables type inference for results
|
|
403
|
+
*/
|
|
404
|
+
output(schema) {
|
|
405
|
+
this._output = schema;
|
|
406
|
+
return this;
|
|
407
|
+
}
|
|
408
|
+
tags(...tags) {
|
|
409
|
+
this._tags.push(...tags);
|
|
410
|
+
return this;
|
|
411
|
+
}
|
|
412
|
+
cache(config) {
|
|
413
|
+
this._cache = config;
|
|
414
|
+
return this;
|
|
415
|
+
}
|
|
416
|
+
/** Configure auto-fetch endpoint for client-side execution */
|
|
417
|
+
clientFetch(config) {
|
|
418
|
+
this._clientFetch = config;
|
|
419
|
+
return this;
|
|
420
|
+
}
|
|
421
|
+
/** Require human confirmation before executing (HITL) */
|
|
422
|
+
requireConfirm(value = true) {
|
|
423
|
+
this._confirm = value;
|
|
424
|
+
return this;
|
|
425
|
+
}
|
|
426
|
+
/** Set a groupKey function for collapsing related tool calls */
|
|
427
|
+
groupBy(fn) {
|
|
428
|
+
this._groupKey = fn;
|
|
429
|
+
return this;
|
|
430
|
+
}
|
|
431
|
+
/** Auto-send updated state to AI after user interacts with this tool's UI */
|
|
432
|
+
autoRespondAfterAction(value = true) {
|
|
433
|
+
this._autoRespond = value;
|
|
434
|
+
return this;
|
|
435
|
+
}
|
|
436
|
+
/** Set behavioral hints for the tool */
|
|
437
|
+
hints(hints) {
|
|
438
|
+
this._hints = hints;
|
|
439
|
+
return this;
|
|
440
|
+
}
|
|
441
|
+
server(handler) {
|
|
442
|
+
this._serverHandler = handler;
|
|
443
|
+
return this;
|
|
444
|
+
}
|
|
445
|
+
client(handler) {
|
|
446
|
+
this._clientHandler = handler;
|
|
447
|
+
return this;
|
|
448
|
+
}
|
|
449
|
+
stream(handler) {
|
|
450
|
+
this._streamHandler = handler;
|
|
451
|
+
return this;
|
|
452
|
+
}
|
|
453
|
+
view(component) {
|
|
454
|
+
this._viewComponent = component;
|
|
455
|
+
return this;
|
|
456
|
+
}
|
|
457
|
+
/**
|
|
458
|
+
* Build the final Tool instance
|
|
459
|
+
*/
|
|
460
|
+
build() {
|
|
461
|
+
if (!this._input) {
|
|
462
|
+
throw new Error(`Tool "${this._name}" requires an input schema`);
|
|
463
|
+
}
|
|
464
|
+
const t = new Tool({
|
|
465
|
+
name: this._name,
|
|
466
|
+
description: this._description,
|
|
467
|
+
input: this._input,
|
|
468
|
+
output: this._output,
|
|
469
|
+
tags: this._tags,
|
|
470
|
+
cache: this._cache,
|
|
471
|
+
clientFetch: this._clientFetch,
|
|
472
|
+
confirm: this._confirm,
|
|
473
|
+
hints: this._hints,
|
|
474
|
+
groupKey: this._groupKey,
|
|
475
|
+
autoRespond: this._autoRespond
|
|
476
|
+
});
|
|
477
|
+
if (this._serverHandler) t.server(this._serverHandler);
|
|
478
|
+
if (this._clientHandler) t.client(this._clientHandler);
|
|
479
|
+
if (this._streamHandler) t.stream(this._streamHandler);
|
|
480
|
+
if (this._viewComponent) t.view(this._viewComponent);
|
|
481
|
+
return t;
|
|
482
|
+
}
|
|
483
|
+
/**
|
|
484
|
+
* Auto-build when accessing Tool methods
|
|
485
|
+
*/
|
|
486
|
+
async run(input, ctx) {
|
|
487
|
+
return this.build().run(input, ctx);
|
|
488
|
+
}
|
|
489
|
+
async *runStream(input, ctx) {
|
|
490
|
+
yield* this.build().runStream(input, ctx);
|
|
491
|
+
}
|
|
492
|
+
get View() {
|
|
493
|
+
return this.build().View;
|
|
494
|
+
}
|
|
495
|
+
toJSON() {
|
|
496
|
+
return this.build().toJSON();
|
|
497
|
+
}
|
|
498
|
+
toAITool() {
|
|
499
|
+
return this.build().toAITool();
|
|
500
|
+
}
|
|
501
|
+
};
|
|
502
|
+
|
|
503
|
+
// src/providers/openai.ts
|
|
504
|
+
function createOpenAIProvider(config) {
|
|
505
|
+
return {
|
|
506
|
+
type: "openai",
|
|
507
|
+
modelId: config.model,
|
|
508
|
+
model: () => {
|
|
509
|
+
const id = "@ai-sdk/openai";
|
|
510
|
+
const { openai } = require(id);
|
|
511
|
+
return openai(config.model);
|
|
512
|
+
}
|
|
513
|
+
};
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
// src/providers/anthropic.ts
|
|
517
|
+
function createAnthropicProvider(config) {
|
|
518
|
+
return {
|
|
519
|
+
type: "anthropic",
|
|
520
|
+
modelId: config.model,
|
|
521
|
+
model: () => {
|
|
522
|
+
const id = "@ai-sdk/anthropic";
|
|
523
|
+
const { anthropic } = require(id);
|
|
524
|
+
return anthropic(config.model);
|
|
525
|
+
}
|
|
526
|
+
};
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
// src/providers/google.ts
|
|
530
|
+
function createGoogleProvider(config) {
|
|
531
|
+
return {
|
|
532
|
+
type: "google",
|
|
533
|
+
modelId: config.model,
|
|
534
|
+
model: () => {
|
|
535
|
+
const id = "@ai-sdk/google";
|
|
536
|
+
const { google } = require(id);
|
|
537
|
+
return google(config.model);
|
|
538
|
+
}
|
|
539
|
+
};
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
// src/providers/openrouter.ts
|
|
543
|
+
function createOpenRouterProvider(config) {
|
|
544
|
+
return {
|
|
545
|
+
type: "openrouter",
|
|
546
|
+
modelId: config.model,
|
|
547
|
+
model: () => {
|
|
548
|
+
const id = "@ai-sdk/openai";
|
|
549
|
+
const { createOpenAI } = require(id);
|
|
550
|
+
const openrouter = createOpenAI({
|
|
551
|
+
baseURL: config.baseURL || "https://openrouter.ai/api/v1",
|
|
552
|
+
apiKey: config.apiKey,
|
|
553
|
+
...config.options
|
|
554
|
+
});
|
|
555
|
+
return openrouter(config.model);
|
|
556
|
+
}
|
|
557
|
+
};
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
// src/providers/index.ts
|
|
561
|
+
function createProvider(config) {
|
|
562
|
+
switch (config.provider) {
|
|
563
|
+
case "openai":
|
|
564
|
+
return createOpenAIProvider(config);
|
|
565
|
+
case "anthropic":
|
|
566
|
+
return createAnthropicProvider(config);
|
|
567
|
+
case "google":
|
|
568
|
+
return createGoogleProvider(config);
|
|
569
|
+
case "openrouter":
|
|
570
|
+
return createOpenRouterProvider(config);
|
|
571
|
+
default: {
|
|
572
|
+
const exhaustiveCheck = config.provider;
|
|
573
|
+
throw new Error(`Unknown provider: ${exhaustiveCheck}`);
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
578
|
+
0 && (module.exports = {
|
|
579
|
+
Tool,
|
|
580
|
+
ToolBuilder,
|
|
581
|
+
createAnthropicProvider,
|
|
582
|
+
createGoogleProvider,
|
|
583
|
+
createOpenAIProvider,
|
|
584
|
+
createOpenRouterProvider,
|
|
585
|
+
createProvider,
|
|
586
|
+
tool
|
|
587
|
+
});
|