@lantos1618/better-ui 0.1.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.
Files changed (56) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +190 -0
  3. package/lib/aui/README.md +136 -0
  4. package/lib/aui/__tests__/aui-complete.test.ts +251 -0
  5. package/lib/aui/__tests__/aui-comprehensive.test.ts +376 -0
  6. package/lib/aui/__tests__/aui-concise.test.ts +278 -0
  7. package/lib/aui/__tests__/aui-integration.test.ts +309 -0
  8. package/lib/aui/__tests__/aui-simple.test.ts +116 -0
  9. package/lib/aui/__tests__/aui.test.ts +269 -0
  10. package/lib/aui/__tests__/concise-api.test.ts +165 -0
  11. package/lib/aui/__tests__/core.test.ts +265 -0
  12. package/lib/aui/__tests__/simple-api.test.ts +200 -0
  13. package/lib/aui/ai-assistant.ts +408 -0
  14. package/lib/aui/ai-control.ts +353 -0
  15. package/lib/aui/client/use-aui.ts +55 -0
  16. package/lib/aui/client-control.ts +551 -0
  17. package/lib/aui/client-executor.ts +417 -0
  18. package/lib/aui/components/ToolRenderer.tsx +22 -0
  19. package/lib/aui/core.ts +137 -0
  20. package/lib/aui/demo.tsx +89 -0
  21. package/lib/aui/examples/ai-complete-demo.tsx +359 -0
  22. package/lib/aui/examples/ai-control-demo.tsx +356 -0
  23. package/lib/aui/examples/ai-control-tools.ts +308 -0
  24. package/lib/aui/examples/concise-api.tsx +153 -0
  25. package/lib/aui/examples/index.tsx +163 -0
  26. package/lib/aui/examples/quick-demo.tsx +91 -0
  27. package/lib/aui/examples/simple-demo.tsx +71 -0
  28. package/lib/aui/examples/simple-tools.tsx +160 -0
  29. package/lib/aui/examples/user-api.tsx +208 -0
  30. package/lib/aui/examples/user-requested.tsx +174 -0
  31. package/lib/aui/examples/weather-search-tools.tsx +119 -0
  32. package/lib/aui/examples.tsx +367 -0
  33. package/lib/aui/hooks/useAUITool.ts +142 -0
  34. package/lib/aui/hooks/useAUIToolEnhanced.ts +343 -0
  35. package/lib/aui/hooks/useAUITools.ts +195 -0
  36. package/lib/aui/index.ts +156 -0
  37. package/lib/aui/provider.tsx +45 -0
  38. package/lib/aui/server-control.ts +386 -0
  39. package/lib/aui/server-executor.ts +165 -0
  40. package/lib/aui/server.ts +167 -0
  41. package/lib/aui/tool-registry.ts +380 -0
  42. package/lib/aui/tools/advanced-examples.tsx +86 -0
  43. package/lib/aui/tools/ai-complete.ts +375 -0
  44. package/lib/aui/tools/api-tools.tsx +230 -0
  45. package/lib/aui/tools/data-tools.tsx +232 -0
  46. package/lib/aui/tools/dom-tools.tsx +202 -0
  47. package/lib/aui/tools/examples.ts +43 -0
  48. package/lib/aui/tools/file-tools.tsx +202 -0
  49. package/lib/aui/tools/form-tools.tsx +233 -0
  50. package/lib/aui/tools/index.ts +8 -0
  51. package/lib/aui/tools/navigation-tools.tsx +172 -0
  52. package/lib/aui/tools/notification-tools.ts +213 -0
  53. package/lib/aui/tools/state-tools.tsx +209 -0
  54. package/lib/aui/types.ts +47 -0
  55. package/lib/aui/vercel-ai.ts +100 -0
  56. package/package.json +51 -0
@@ -0,0 +1,417 @@
1
+ import { AUIContext } from './core';
2
+ import { AIControlledTool } from './ai-control';
3
+
4
+ export interface CacheStrategy {
5
+ key: (toolName: string, input: any) => string;
6
+ ttl?: number; // Time to live in milliseconds
7
+ maxSize?: number; // Maximum number of cached items
8
+ }
9
+
10
+ export interface ClientExecutorOptions {
11
+ baseUrl?: string;
12
+ cache?: {
13
+ enabled: boolean;
14
+ strategy?: CacheStrategy;
15
+ storage?: 'memory' | 'localStorage' | 'sessionStorage';
16
+ };
17
+ retry?: {
18
+ attempts?: number;
19
+ delay?: number;
20
+ backoff?: 'linear' | 'exponential';
21
+ };
22
+ timeout?: number;
23
+ headers?: HeadersInit;
24
+ }
25
+
26
+ interface CacheEntry {
27
+ data: any;
28
+ timestamp: number;
29
+ ttl?: number;
30
+ }
31
+
32
+ export class ClientExecutor {
33
+ private cache: Map<string, CacheEntry> = new Map();
34
+ private options: Required<ClientExecutorOptions>;
35
+ private localStorage?: Storage;
36
+ private sessionStorage?: Storage;
37
+
38
+ constructor(options: ClientExecutorOptions = {}) {
39
+ this.options = {
40
+ baseUrl: options.baseUrl || '/api/aui/execute',
41
+ cache: {
42
+ enabled: options.cache?.enabled ?? true,
43
+ strategy: options.cache?.strategy || {
44
+ key: (tool, input) => `${tool}:${JSON.stringify(input)}`,
45
+ ttl: 5 * 60 * 1000, // 5 minutes default
46
+ maxSize: 100,
47
+ },
48
+ storage: options.cache?.storage || 'memory',
49
+ },
50
+ retry: {
51
+ attempts: options.retry?.attempts ?? 3,
52
+ delay: options.retry?.delay ?? 1000,
53
+ backoff: options.retry?.backoff || 'exponential',
54
+ },
55
+ timeout: options.timeout ?? 30000, // 30 seconds default
56
+ headers: options.headers || {},
57
+ };
58
+
59
+ if (typeof window !== 'undefined') {
60
+ this.localStorage = window.localStorage;
61
+ this.sessionStorage = window.sessionStorage;
62
+ }
63
+ }
64
+
65
+ async execute<TInput = any, TOutput = any>(
66
+ toolName: string,
67
+ input: TInput,
68
+ context?: Partial<AUIContext>
69
+ ): Promise<TOutput> {
70
+ // Check cache first if enabled
71
+ if (this.options.cache.enabled) {
72
+ const cached = this.getFromCache(toolName, input);
73
+ if (cached !== null) {
74
+ return cached;
75
+ }
76
+ }
77
+
78
+ // Execute with retry logic
79
+ let lastError: Error | null = null;
80
+ for (let attempt = 0; attempt < this.options.retry.attempts!; attempt++) {
81
+ try {
82
+ const result = await this.executeOnce<TInput, TOutput>(
83
+ toolName,
84
+ input,
85
+ context
86
+ );
87
+
88
+ // Store in cache if enabled
89
+ if (this.options.cache.enabled) {
90
+ this.storeInCache(toolName, input, result);
91
+ }
92
+
93
+ return result;
94
+ } catch (error) {
95
+ lastError = error as Error;
96
+
97
+ // Don't retry on certain errors
98
+ if (error instanceof Error) {
99
+ if (error.message.includes('Tool') && error.message.includes('not found')) {
100
+ throw error; // Don't retry if tool doesn't exist
101
+ }
102
+ if (error.message.includes('Rate limit')) {
103
+ throw error; // Don't retry on rate limit
104
+ }
105
+ }
106
+
107
+ // Calculate delay for next attempt
108
+ if (attempt < this.options.retry.attempts! - 1) {
109
+ const delay = this.calculateRetryDelay(attempt);
110
+ await this.sleep(delay);
111
+ }
112
+ }
113
+ }
114
+
115
+ throw lastError || new Error('Execution failed after retries');
116
+ }
117
+
118
+ private async executeOnce<TInput = any, TOutput = any>(
119
+ toolName: string,
120
+ input: TInput,
121
+ context?: Partial<AUIContext>
122
+ ): Promise<TOutput> {
123
+ const controller = new AbortController();
124
+ const timeoutId = setTimeout(
125
+ () => controller.abort(),
126
+ this.options.timeout
127
+ );
128
+
129
+ try {
130
+ const response = await fetch(this.options.baseUrl, {
131
+ method: 'POST',
132
+ headers: {
133
+ 'Content-Type': 'application/json',
134
+ ...this.options.headers,
135
+ },
136
+ body: JSON.stringify({
137
+ tool: toolName,
138
+ input,
139
+ context,
140
+ }),
141
+ signal: controller.signal,
142
+ });
143
+
144
+ clearTimeout(timeoutId);
145
+
146
+ if (!response.ok) {
147
+ const error = await response.json();
148
+ throw new Error(error.error || `HTTP ${response.status}`);
149
+ }
150
+
151
+ const result = await response.json();
152
+ return result.data || result;
153
+ } catch (error) {
154
+ clearTimeout(timeoutId);
155
+
156
+ if (error instanceof Error && error.name === 'AbortError') {
157
+ throw new Error(`Execution timeout after ${this.options.timeout}ms`);
158
+ }
159
+
160
+ throw error;
161
+ }
162
+ }
163
+
164
+ private getFromCache(toolName: string, input: any): any | null {
165
+ const key = this.options.cache.strategy!.key(toolName, input);
166
+
167
+ // Check appropriate storage
168
+ let entry: CacheEntry | null = null;
169
+
170
+ switch (this.options.cache.storage) {
171
+ case 'localStorage':
172
+ if (this.localStorage) {
173
+ const stored = this.localStorage.getItem(key);
174
+ if (stored) {
175
+ try {
176
+ entry = JSON.parse(stored);
177
+ } catch {
178
+ // Invalid cache entry
179
+ }
180
+ }
181
+ }
182
+ break;
183
+
184
+ case 'sessionStorage':
185
+ if (this.sessionStorage) {
186
+ const stored = this.sessionStorage.getItem(key);
187
+ if (stored) {
188
+ try {
189
+ entry = JSON.parse(stored);
190
+ } catch {
191
+ // Invalid cache entry
192
+ }
193
+ }
194
+ }
195
+ break;
196
+
197
+ default: // memory
198
+ entry = this.cache.get(key) || null;
199
+ }
200
+
201
+ if (!entry) return null;
202
+
203
+ // Check if entry is still valid
204
+ const now = Date.now();
205
+ const ttl = entry.ttl || this.options.cache.strategy!.ttl;
206
+
207
+ if (ttl && now - entry.timestamp > ttl) {
208
+ // Entry expired
209
+ this.removeFromCache(key);
210
+ return null;
211
+ }
212
+
213
+ return entry.data;
214
+ }
215
+
216
+ private storeInCache(toolName: string, input: any, data: any): void {
217
+ const key = this.options.cache.strategy!.key(toolName, input);
218
+ const entry: CacheEntry = {
219
+ data,
220
+ timestamp: Date.now(),
221
+ ttl: this.options.cache.strategy!.ttl,
222
+ };
223
+
224
+ switch (this.options.cache.storage) {
225
+ case 'localStorage':
226
+ if (this.localStorage) {
227
+ try {
228
+ this.localStorage.setItem(key, JSON.stringify(entry));
229
+ this.enforceMaxSize('localStorage');
230
+ } catch {
231
+ // Storage quota exceeded
232
+ }
233
+ }
234
+ break;
235
+
236
+ case 'sessionStorage':
237
+ if (this.sessionStorage) {
238
+ try {
239
+ this.sessionStorage.setItem(key, JSON.stringify(entry));
240
+ this.enforceMaxSize('sessionStorage');
241
+ } catch {
242
+ // Storage quota exceeded
243
+ }
244
+ }
245
+ break;
246
+
247
+ default: // memory
248
+ this.cache.set(key, entry);
249
+ this.enforceMaxSize('memory');
250
+ }
251
+ }
252
+
253
+ private removeFromCache(key: string): void {
254
+ switch (this.options.cache.storage) {
255
+ case 'localStorage':
256
+ if (this.localStorage) {
257
+ this.localStorage.removeItem(key);
258
+ }
259
+ break;
260
+
261
+ case 'sessionStorage':
262
+ if (this.sessionStorage) {
263
+ this.sessionStorage.removeItem(key);
264
+ }
265
+ break;
266
+
267
+ default:
268
+ this.cache.delete(key);
269
+ }
270
+ }
271
+
272
+ private enforceMaxSize(storage: 'memory' | 'localStorage' | 'sessionStorage'): void {
273
+ const maxSize = this.options.cache.strategy!.maxSize;
274
+ if (!maxSize) return;
275
+
276
+ if (storage === 'memory') {
277
+ if (this.cache.size > maxSize) {
278
+ // Remove oldest entries
279
+ const entries = Array.from(this.cache.entries());
280
+ entries.sort((a, b) => a[1].timestamp - b[1].timestamp);
281
+
282
+ const toRemove = entries.slice(0, entries.length - maxSize);
283
+ toRemove.forEach(([key]) => this.cache.delete(key));
284
+ }
285
+ } else {
286
+ const storageObj = storage === 'localStorage' ? this.localStorage : this.sessionStorage;
287
+ if (!storageObj) return;
288
+
289
+ // Get all cache keys
290
+ const cacheKeys: Array<{ key: string; timestamp: number }> = [];
291
+ for (let i = 0; i < storageObj.length; i++) {
292
+ const key = storageObj.key(i);
293
+ if (key) {
294
+ try {
295
+ const entry = JSON.parse(storageObj.getItem(key)!);
296
+ if (entry && entry.timestamp) {
297
+ cacheKeys.push({ key, timestamp: entry.timestamp });
298
+ }
299
+ } catch {
300
+ // Skip invalid entries
301
+ }
302
+ }
303
+ }
304
+
305
+ if (cacheKeys.length > maxSize) {
306
+ cacheKeys.sort((a, b) => a.timestamp - b.timestamp);
307
+ const toRemove = cacheKeys.slice(0, cacheKeys.length - maxSize);
308
+ toRemove.forEach(({ key }) => storageObj.removeItem(key));
309
+ }
310
+ }
311
+ }
312
+
313
+ private calculateRetryDelay(attempt: number): number {
314
+ const baseDelay = this.options.retry.delay!;
315
+
316
+ if (this.options.retry.backoff === 'exponential') {
317
+ return baseDelay * Math.pow(2, attempt);
318
+ }
319
+
320
+ return baseDelay * (attempt + 1);
321
+ }
322
+
323
+ private sleep(ms: number): Promise<void> {
324
+ return new Promise(resolve => setTimeout(resolve, ms));
325
+ }
326
+
327
+ clearCache(): void {
328
+ switch (this.options.cache.storage) {
329
+ case 'localStorage':
330
+ if (this.localStorage) {
331
+ this.localStorage.clear();
332
+ }
333
+ break;
334
+
335
+ case 'sessionStorage':
336
+ if (this.sessionStorage) {
337
+ this.sessionStorage.clear();
338
+ }
339
+ break;
340
+
341
+ default:
342
+ this.cache.clear();
343
+ }
344
+ }
345
+
346
+ getCacheStats(): {
347
+ size: number;
348
+ storage: string;
349
+ enabled: boolean;
350
+ } {
351
+ let size = 0;
352
+
353
+ switch (this.options.cache.storage) {
354
+ case 'localStorage':
355
+ size = this.localStorage?.length || 0;
356
+ break;
357
+
358
+ case 'sessionStorage':
359
+ size = this.sessionStorage?.length || 0;
360
+ break;
361
+
362
+ default:
363
+ size = this.cache.size;
364
+ }
365
+
366
+ return {
367
+ size,
368
+ storage: this.options.cache.storage!,
369
+ enabled: this.options.cache.enabled,
370
+ };
371
+ }
372
+ }
373
+
374
+ // Default client executor instance
375
+ export const clientExecutor = new ClientExecutor();
376
+
377
+ // Enhanced client execution with optimistic updates
378
+ export async function executeClientTool<TInput = any, TOutput = any>(
379
+ toolName: string,
380
+ input: TInput,
381
+ options?: {
382
+ optimistic?: () => TOutput;
383
+ onSuccess?: (result: TOutput) => void;
384
+ onError?: (error: Error) => void;
385
+ context?: Partial<AUIContext>;
386
+ }
387
+ ): Promise<TOutput> {
388
+ // Return optimistic result immediately if provided
389
+ if (options?.optimistic) {
390
+ const optimisticResult = options.optimistic();
391
+
392
+ // Execute in background
393
+ clientExecutor.execute<TInput, TOutput>(toolName, input, options?.context)
394
+ .then(result => {
395
+ options?.onSuccess?.(result);
396
+ })
397
+ .catch(error => {
398
+ options?.onError?.(error);
399
+ });
400
+
401
+ return optimisticResult;
402
+ }
403
+
404
+ // Normal execution
405
+ try {
406
+ const result = await clientExecutor.execute<TInput, TOutput>(
407
+ toolName,
408
+ input,
409
+ options?.context
410
+ );
411
+ options?.onSuccess?.(result);
412
+ return result;
413
+ } catch (error) {
414
+ options?.onError?.(error as Error);
415
+ throw error;
416
+ }
417
+ }
@@ -0,0 +1,22 @@
1
+ import React from 'react';
2
+ import { AUITool } from '../index';
3
+
4
+ interface ToolRendererProps {
5
+ tool: AUITool;
6
+ input?: any;
7
+ result?: any;
8
+ loading?: boolean;
9
+ error?: Error;
10
+ }
11
+
12
+ export function ToolRenderer({ tool, input, result, loading, error }: ToolRendererProps) {
13
+ if (!tool.renderer) {
14
+ return (
15
+ <div className="p-4 bg-gray-100 rounded">
16
+ <pre className="text-sm">{JSON.stringify(result, null, 2)}</pre>
17
+ </div>
18
+ );
19
+ }
20
+
21
+ return tool.renderer({ data: result, input, loading, error });
22
+ }
@@ -0,0 +1,137 @@
1
+ import { ReactElement } from 'react';
2
+ import { z } from 'zod';
3
+
4
+ export interface AUIContext {
5
+ cache: Map<string, any>;
6
+ fetch: typeof fetch;
7
+ user?: any;
8
+ session?: any;
9
+ env?: Record<string, string>;
10
+ headers?: HeadersInit;
11
+ cookies?: Record<string, string>;
12
+ isServer?: boolean;
13
+ }
14
+
15
+ export interface ToolConfig<TInput, TOutput> {
16
+ name: string;
17
+ inputSchema?: z.ZodType<TInput>;
18
+ executeHandler?: (params: { input: TInput; ctx?: AUIContext }) => Promise<TOutput> | TOutput;
19
+ clientHandler?: (params: { input: TInput; ctx: AUIContext }) => Promise<TOutput> | TOutput;
20
+ renderHandler?: (props: { data: TOutput; input?: TInput; loading?: boolean; error?: Error }) => ReactElement;
21
+ middleware?: Array<(params: { input: TInput; ctx: AUIContext; next: () => Promise<TOutput> }) => Promise<TOutput>>;
22
+ description?: string;
23
+ tags?: string[];
24
+ }
25
+
26
+ export class AUITool<TInput = any, TOutput = any> {
27
+ protected config: ToolConfig<TInput, TOutput> = { name: '' };
28
+
29
+ constructor(name: string) {
30
+ this.config.name = name;
31
+ }
32
+
33
+ input<T>(schema: z.ZodType<T>): AUITool<T, TOutput> {
34
+ this.config.inputSchema = schema as any;
35
+ return this as any;
36
+ }
37
+
38
+ execute<O>(handler: (params: { input: TInput; ctx?: AUIContext }) => O | Promise<O>): AUITool<TInput, O> {
39
+ this.config.executeHandler = handler as any;
40
+ return this as any;
41
+ }
42
+
43
+ clientExecute(handler: (params: { input: TInput; ctx: AUIContext }) => TOutput | Promise<TOutput>): this {
44
+ this.config.clientHandler = handler;
45
+ return this;
46
+ }
47
+
48
+ render(component: (props: { data: TOutput; input?: TInput; loading?: boolean; error?: Error }) => ReactElement): this {
49
+ this.config.renderHandler = component;
50
+ return this;
51
+ }
52
+
53
+ async run(input: TInput, ctx?: AUIContext): Promise<TOutput> {
54
+ const validated = this.config.inputSchema ? this.config.inputSchema.parse(input) : input;
55
+ const context = ctx || {
56
+ cache: new Map(),
57
+ fetch: globalThis.fetch?.bind(globalThis) || (() => Promise.reject(new Error('Fetch not available'))),
58
+ isServer: typeof window === 'undefined',
59
+ };
60
+
61
+ // Apply middleware if present
62
+ if (this.config.middleware?.length) {
63
+ let index = 0;
64
+ const next = async (): Promise<TOutput> => {
65
+ if (index >= this.config.middleware!.length) {
66
+ return this.executeCore(validated, context);
67
+ }
68
+ const middleware = this.config.middleware![index++];
69
+ return middleware({ input: validated, ctx: context, next });
70
+ };
71
+ return next();
72
+ }
73
+
74
+ return this.executeCore(validated, context);
75
+ }
76
+
77
+ private async executeCore(input: TInput, ctx: AUIContext): Promise<TOutput> {
78
+ // Use clientHandler if it exists and we're on the client
79
+ if (!ctx.isServer && this.config.clientHandler) {
80
+ return this.config.clientHandler({ input, ctx });
81
+ }
82
+
83
+ if (!this.config.executeHandler) {
84
+ throw new Error(`Tool ${this.config.name} has no execute handler`);
85
+ }
86
+
87
+ return this.config.executeHandler({ input, ctx });
88
+ }
89
+
90
+ middleware(fn: (params: { input: TInput; ctx: AUIContext; next: () => Promise<TOutput> }) => Promise<TOutput>): this {
91
+ if (!this.config.middleware) {
92
+ this.config.middleware = [];
93
+ }
94
+ this.config.middleware.push(fn);
95
+ return this;
96
+ }
97
+
98
+ describe(description: string): this {
99
+ this.config.description = description;
100
+ return this;
101
+ }
102
+
103
+ tag(...tags: string[]): this {
104
+ if (!this.config.tags) {
105
+ this.config.tags = [];
106
+ }
107
+ this.config.tags.push(...tags);
108
+ return this;
109
+ }
110
+
111
+ get name() { return this.config.name; }
112
+ get schema() { return this.config.inputSchema; }
113
+ get inputSchema() { return this.config.inputSchema; }
114
+ get outputSchema() { return undefined; } // Can be extended if needed
115
+ get isServerOnly() { return !this.config.clientHandler; }
116
+ get metadata() { return { description: this.config.description, tags: this.config.tags }; }
117
+ get renderer() { return this.config.renderHandler; }
118
+ get description() { return this.config.description; }
119
+ get tags() { return this.config.tags || []; }
120
+
121
+ toJSON() {
122
+ return {
123
+ name: this.config.name,
124
+ description: this.config.description,
125
+ tags: this.config.tags || [],
126
+ hasInput: !!this.config.inputSchema,
127
+ hasExecute: !!this.config.executeHandler,
128
+ hasClientExecute: !!this.config.clientHandler,
129
+ hasRender: !!this.config.renderHandler,
130
+ hasMiddleware: !!(this.config.middleware && this.config.middleware.length > 0)
131
+ };
132
+ }
133
+
134
+ getConfig(): Readonly<ToolConfig<TInput, TOutput>> {
135
+ return { ...this.config };
136
+ }
137
+ }
@@ -0,0 +1,89 @@
1
+ import aui from './index';
2
+ import { z } from 'zod';
3
+
4
+ // ============================================
5
+ // EXACT API AS REQUESTED BY USER
6
+ // ============================================
7
+
8
+ // Simple tool - just 2 methods
9
+ const simpleTool = aui
10
+ .tool('weather')
11
+ .input(z.object({ city: z.string() }))
12
+ .execute(async ({ input }) => ({ temp: 72, city: input.city }))
13
+ .render(({ data }) => <div>{data.city}: {data.temp}°</div>);
14
+
15
+ // Complex tool - adds client optimization
16
+ const complexTool = aui
17
+ .tool('search')
18
+ .input(z.object({ query: z.string() }))
19
+ .execute(async ({ input }) => {
20
+ // Mock database search
21
+ return { results: [`Result for ${input.query}`] };
22
+ })
23
+ .clientExecute(async ({ input, ctx }) => {
24
+ // Only when you need caching, offline, etc.
25
+ const cached = ctx.cache.get(input.query);
26
+ return cached || ctx.fetch('/api/tools/search', {
27
+ method: 'POST',
28
+ headers: { 'Content-Type': 'application/json' },
29
+ body: JSON.stringify(input)
30
+ }).then(r => r.json());
31
+ })
32
+ .render(({ data }) => <SearchResults results={data} />);
33
+
34
+ // Component for rendering search results
35
+ function SearchResults({ results }: { results: any }) {
36
+ return (
37
+ <div>
38
+ {results.results?.map((r: string, i: number) => (
39
+ <div key={i}>{r}</div>
40
+ ))}
41
+ </div>
42
+ );
43
+ }
44
+
45
+ // ============================================
46
+ // USAGE EXAMPLES
47
+ // ============================================
48
+
49
+ // Direct execution
50
+ async function exampleUsage() {
51
+ const ctx = aui.createContext();
52
+
53
+ // Execute weather tool
54
+ const weatherResult = await simpleTool.run({ city: 'Tokyo' }, ctx);
55
+ console.log(weatherResult); // { temp: 72, city: 'Tokyo' }
56
+
57
+ // Execute search tool
58
+ const searchResult = await complexTool.run({ query: 'AI tools' }, ctx);
59
+ console.log(searchResult); // { results: ['Result for AI tools'] }
60
+ }
61
+
62
+ // React component usage
63
+ import { useAUITool } from './hooks/useAUITool';
64
+
65
+ export function WeatherWidget() {
66
+ const { execute, data, loading } = useAUITool(simpleTool);
67
+
68
+ return (
69
+ <div>
70
+ <button onClick={() => execute({ city: 'Tokyo' })}>
71
+ Get Weather
72
+ </button>
73
+ {loading && <span>Loading...</span>}
74
+ {data && simpleTool.renderer && simpleTool.renderer({ data })}
75
+ </div>
76
+ );
77
+ }
78
+
79
+ // AI Agent can discover and use tools
80
+ export function getToolsForAI() {
81
+ return aui.getTools().map((tool: any) => ({
82
+ name: tool.name,
83
+ description: tool.description,
84
+ schema: tool.schema,
85
+ execute: (input: any) => tool.run(input)
86
+ }));
87
+ }
88
+
89
+ export { simpleTool, complexTool };