@hsafa/ui-sdk 0.3.3 → 0.4.1
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 +133 -2
- package/dist/index.cjs +23 -23
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +572 -20
- package/dist/index.d.ts +572 -20
- package/dist/index.js +23 -23
- package/dist/index.js.map +1 -1
- package/docs/CUSTOM_UI_EXAMPLES.md +309 -0
- package/docs/DYNAMIC_PAGE_SCHEMAS.md +261 -0
- package/docs/HEADLESS_QUICK_REFERENCE.md +426 -0
- package/docs/HEADLESS_USAGE.md +682 -0
- package/docs/MIGRATION_TO_HEADLESS.md +408 -0
- package/docs/README.md +43 -71
- package/docs/handbook/00-Overview.md +69 -0
- package/docs/handbook/01-Quickstart.md +133 -0
- package/docs/handbook/02-Architecture.md +75 -0
- package/docs/handbook/03-Components-and-Hooks.md +81 -0
- package/docs/handbook/04-Streaming-and-Transport.md +73 -0
- package/docs/handbook/05-Tools-and-UI.md +73 -0
- package/docs/handbook/06-Storage-and-History.md +63 -0
- package/docs/handbook/07-Dynamic-Pages.md +49 -0
- package/docs/handbook/08-Server-Integration.md +84 -0
- package/docs/handbook/09-Agent-Studio-Client.md +40 -0
- package/docs/handbook/10-Examples-and-Recipes.md +154 -0
- package/docs/handbook/11-API-Reference-Map.md +48 -0
- package/docs/handbook/README.md +24 -0
- package/examples/custom-tools-example.tsx +401 -0
- package/examples/custom-ui-customizations-example.tsx +543 -0
- package/examples/dynamic-page-example.tsx +380 -0
- package/examples/headless-chat-example.tsx +537 -0
- package/examples/minimal-headless-example.tsx +142 -0
- package/package.json +3 -2
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Custom Tools & UI Components Example
|
|
3
|
+
*
|
|
4
|
+
* This example shows how to:
|
|
5
|
+
* 1. Add custom tools that the agent can use
|
|
6
|
+
* 2. Add custom UI components to render tool results
|
|
7
|
+
* 3. Handle tool execution and UI rendering
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import React from 'react';
|
|
11
|
+
import { useHsafaAgent } from '@hsafa/ui-sdk';
|
|
12
|
+
|
|
13
|
+
// Custom UI Component for displaying weather
|
|
14
|
+
function WeatherCard({ data }: { data: any }) {
|
|
15
|
+
return (
|
|
16
|
+
<div style={{
|
|
17
|
+
padding: '20px',
|
|
18
|
+
backgroundColor: '#e3f2fd',
|
|
19
|
+
borderRadius: '12px',
|
|
20
|
+
border: '2px solid #2196f3',
|
|
21
|
+
margin: '10px 0'
|
|
22
|
+
}}>
|
|
23
|
+
<h3 style={{ margin: '0 0 10px 0', color: '#1976d2' }}>
|
|
24
|
+
🌤️ Weather in {data.location}
|
|
25
|
+
</h3>
|
|
26
|
+
<div style={{ fontSize: '32px', fontWeight: 'bold', color: '#1976d2' }}>
|
|
27
|
+
{data.temperature}°C
|
|
28
|
+
</div>
|
|
29
|
+
<div style={{ marginTop: '8px', color: '#555' }}>
|
|
30
|
+
{data.conditions}
|
|
31
|
+
</div>
|
|
32
|
+
</div>
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Custom UI Component for displaying product info
|
|
37
|
+
function ProductCard({ data }: { data: any }) {
|
|
38
|
+
return (
|
|
39
|
+
<div style={{
|
|
40
|
+
padding: '20px',
|
|
41
|
+
backgroundColor: '#fff',
|
|
42
|
+
borderRadius: '12px',
|
|
43
|
+
border: '2px solid #4caf50',
|
|
44
|
+
margin: '10px 0'
|
|
45
|
+
}}>
|
|
46
|
+
<div style={{ display: 'flex', gap: '15px' }}>
|
|
47
|
+
<img
|
|
48
|
+
src={data.image}
|
|
49
|
+
alt={data.name}
|
|
50
|
+
style={{ width: '100px', height: '100px', objectFit: 'cover', borderRadius: '8px' }}
|
|
51
|
+
/>
|
|
52
|
+
<div style={{ flex: 1 }}>
|
|
53
|
+
<h3 style={{ margin: '0 0 8px 0' }}>{data.name}</h3>
|
|
54
|
+
<p style={{ margin: '0 0 8px 0', color: '#666', fontSize: '14px' }}>
|
|
55
|
+
{data.description}
|
|
56
|
+
</p>
|
|
57
|
+
<div style={{ fontSize: '24px', fontWeight: 'bold', color: '#4caf50' }}>
|
|
58
|
+
${data.price}
|
|
59
|
+
</div>
|
|
60
|
+
<div style={{ marginTop: '8px', fontSize: '13px', color: '#999' }}>
|
|
61
|
+
Stock: {data.stock} units
|
|
62
|
+
</div>
|
|
63
|
+
</div>
|
|
64
|
+
</div>
|
|
65
|
+
</div>
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function CustomToolsExample() {
|
|
70
|
+
const agent = useHsafaAgent({
|
|
71
|
+
agentId: 'custom-tools-demo',
|
|
72
|
+
baseUrl: 'http://localhost:3000',
|
|
73
|
+
|
|
74
|
+
// Define custom tools
|
|
75
|
+
tools: {
|
|
76
|
+
// Simple async tool
|
|
77
|
+
getWeather: async ({ location }: { location: string }) => {
|
|
78
|
+
console.log('Fetching weather for:', location);
|
|
79
|
+
|
|
80
|
+
// Simulate API call
|
|
81
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
location,
|
|
85
|
+
temperature: Math.floor(Math.random() * 30 + 10),
|
|
86
|
+
conditions: ['Sunny', 'Cloudy', 'Rainy', 'Partly Cloudy'][Math.floor(Math.random() * 4)]
|
|
87
|
+
};
|
|
88
|
+
},
|
|
89
|
+
|
|
90
|
+
// Tool that returns product information
|
|
91
|
+
searchProducts: async ({ query }: { query: string }) => {
|
|
92
|
+
console.log('Searching products:', query);
|
|
93
|
+
|
|
94
|
+
// Simulate API call
|
|
95
|
+
await new Promise(resolve => setTimeout(resolve, 800));
|
|
96
|
+
|
|
97
|
+
return {
|
|
98
|
+
results: [
|
|
99
|
+
{
|
|
100
|
+
id: '1',
|
|
101
|
+
name: 'Wireless Headphones',
|
|
102
|
+
description: 'Premium noise-cancelling headphones',
|
|
103
|
+
price: 199.99,
|
|
104
|
+
stock: 50,
|
|
105
|
+
image: 'https://via.placeholder.com/100'
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
id: '2',
|
|
109
|
+
name: 'Smart Watch',
|
|
110
|
+
description: 'Fitness tracker with heart rate monitor',
|
|
111
|
+
price: 299.99,
|
|
112
|
+
stock: 30,
|
|
113
|
+
image: 'https://via.placeholder.com/100'
|
|
114
|
+
}
|
|
115
|
+
]
|
|
116
|
+
};
|
|
117
|
+
},
|
|
118
|
+
|
|
119
|
+
// Tool with streaming execution (executes on each token)
|
|
120
|
+
calculateSum: {
|
|
121
|
+
tool: async ({ numbers }: { numbers: number[] }) => {
|
|
122
|
+
console.log('Calculating sum of:', numbers);
|
|
123
|
+
const sum = numbers.reduce((a, b) => a + b, 0);
|
|
124
|
+
return { sum, count: numbers.length };
|
|
125
|
+
},
|
|
126
|
+
executeEachToken: true // Execute immediately as tokens stream in
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
|
|
130
|
+
// Define custom UI components for rendering
|
|
131
|
+
uiComponents: {
|
|
132
|
+
WeatherCard,
|
|
133
|
+
ProductCard,
|
|
134
|
+
|
|
135
|
+
// Inline component example
|
|
136
|
+
SummaryCard: ({ data }) => (
|
|
137
|
+
<div style={{
|
|
138
|
+
padding: '15px',
|
|
139
|
+
backgroundColor: '#fff3e0',
|
|
140
|
+
borderRadius: '8px',
|
|
141
|
+
border: '1px solid #ff9800',
|
|
142
|
+
margin: '10px 0'
|
|
143
|
+
}}>
|
|
144
|
+
<strong>📊 Summary:</strong>
|
|
145
|
+
<div style={{ marginTop: '8px' }}>{data.text}</div>
|
|
146
|
+
</div>
|
|
147
|
+
),
|
|
148
|
+
},
|
|
149
|
+
|
|
150
|
+
// Callbacks
|
|
151
|
+
onFinish: (message) => {
|
|
152
|
+
console.log('✅ Message completed:', message);
|
|
153
|
+
},
|
|
154
|
+
|
|
155
|
+
onError: (error) => {
|
|
156
|
+
console.error('❌ Error occurred:', error);
|
|
157
|
+
},
|
|
158
|
+
|
|
159
|
+
// Theme colors
|
|
160
|
+
colors: {
|
|
161
|
+
primaryColor: '#2196f3',
|
|
162
|
+
backgroundColor: '#ffffff',
|
|
163
|
+
textColor: '#212121',
|
|
164
|
+
borderColor: '#e0e0e0',
|
|
165
|
+
},
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
return (
|
|
169
|
+
<div style={{
|
|
170
|
+
maxWidth: '800px',
|
|
171
|
+
margin: '50px auto',
|
|
172
|
+
fontFamily: 'system-ui, -apple-system, sans-serif'
|
|
173
|
+
}}>
|
|
174
|
+
<div style={{
|
|
175
|
+
marginBottom: '30px',
|
|
176
|
+
padding: '20px',
|
|
177
|
+
backgroundColor: '#f5f5f5',
|
|
178
|
+
borderRadius: '8px'
|
|
179
|
+
}}>
|
|
180
|
+
<h1 style={{ margin: '0 0 10px 0' }}>Custom Tools Demo</h1>
|
|
181
|
+
<p style={{ margin: 0, color: '#666' }}>
|
|
182
|
+
This agent has access to custom tools: weather, product search, and calculations.
|
|
183
|
+
</p>
|
|
184
|
+
<div style={{
|
|
185
|
+
marginTop: '15px',
|
|
186
|
+
padding: '10px',
|
|
187
|
+
backgroundColor: '#e3f2fd',
|
|
188
|
+
borderRadius: '4px',
|
|
189
|
+
fontSize: '14px'
|
|
190
|
+
}}>
|
|
191
|
+
<strong>Try asking:</strong>
|
|
192
|
+
<ul style={{ margin: '5px 0 0 0', paddingLeft: '20px' }}>
|
|
193
|
+
<li>"What's the weather in New York?"</li>
|
|
194
|
+
<li>"Search for wireless headphones"</li>
|
|
195
|
+
<li>"Calculate the sum of 10, 20, and 30"</li>
|
|
196
|
+
</ul>
|
|
197
|
+
</div>
|
|
198
|
+
</div>
|
|
199
|
+
|
|
200
|
+
{/* Messages */}
|
|
201
|
+
<div style={{
|
|
202
|
+
minHeight: '400px',
|
|
203
|
+
maxHeight: '500px',
|
|
204
|
+
border: '1px solid #e0e0e0',
|
|
205
|
+
borderRadius: '12px',
|
|
206
|
+
padding: '20px',
|
|
207
|
+
overflow: 'auto',
|
|
208
|
+
marginBottom: '20px',
|
|
209
|
+
backgroundColor: '#fafafa'
|
|
210
|
+
}}>
|
|
211
|
+
{agent.messages.length === 0 ? (
|
|
212
|
+
<div style={{
|
|
213
|
+
textAlign: 'center',
|
|
214
|
+
color: '#999',
|
|
215
|
+
marginTop: '100px',
|
|
216
|
+
fontSize: '16px'
|
|
217
|
+
}}>
|
|
218
|
+
💬 Start a conversation with the agent
|
|
219
|
+
</div>
|
|
220
|
+
) : (
|
|
221
|
+
agent.messages.map(msg => (
|
|
222
|
+
<div
|
|
223
|
+
key={msg.id}
|
|
224
|
+
style={{
|
|
225
|
+
marginBottom: '16px',
|
|
226
|
+
display: 'flex',
|
|
227
|
+
justifyContent: msg.role === 'user' ? 'flex-end' : 'flex-start'
|
|
228
|
+
}}
|
|
229
|
+
>
|
|
230
|
+
<div style={{
|
|
231
|
+
maxWidth: '75%',
|
|
232
|
+
padding: '12px 16px',
|
|
233
|
+
borderRadius: '12px',
|
|
234
|
+
backgroundColor: msg.role === 'user' ? '#2196f3' : '#fff',
|
|
235
|
+
color: msg.role === 'user' ? '#fff' : '#212121',
|
|
236
|
+
border: msg.role === 'assistant' ? '1px solid #e0e0e0' : 'none',
|
|
237
|
+
boxShadow: msg.role === 'assistant' ? '0 1px 2px rgba(0,0,0,0.05)' : 'none'
|
|
238
|
+
}}>
|
|
239
|
+
<div style={{
|
|
240
|
+
fontSize: '12px',
|
|
241
|
+
opacity: 0.7,
|
|
242
|
+
marginBottom: '4px',
|
|
243
|
+
fontWeight: '600'
|
|
244
|
+
}}>
|
|
245
|
+
{msg.role === 'user' ? 'You' : '🤖 Agent'}
|
|
246
|
+
</div>
|
|
247
|
+
<div style={{ lineHeight: '1.5' }}>{msg.content}</div>
|
|
248
|
+
</div>
|
|
249
|
+
</div>
|
|
250
|
+
))
|
|
251
|
+
)}
|
|
252
|
+
|
|
253
|
+
{agent.isLoading && (
|
|
254
|
+
<div style={{
|
|
255
|
+
padding: '12px',
|
|
256
|
+
color: '#666',
|
|
257
|
+
fontStyle: 'italic',
|
|
258
|
+
display: 'flex',
|
|
259
|
+
alignItems: 'center',
|
|
260
|
+
gap: '8px'
|
|
261
|
+
}}>
|
|
262
|
+
<div className="loading-dots">●●●</div>
|
|
263
|
+
Agent is processing...
|
|
264
|
+
</div>
|
|
265
|
+
)}
|
|
266
|
+
</div>
|
|
267
|
+
|
|
268
|
+
{/* Input */}
|
|
269
|
+
<div style={{
|
|
270
|
+
display: 'flex',
|
|
271
|
+
gap: '12px',
|
|
272
|
+
padding: '20px',
|
|
273
|
+
backgroundColor: '#fff',
|
|
274
|
+
border: '1px solid #e0e0e0',
|
|
275
|
+
borderRadius: '12px'
|
|
276
|
+
}}>
|
|
277
|
+
<input
|
|
278
|
+
type="text"
|
|
279
|
+
value={agent.input}
|
|
280
|
+
onChange={(e) => agent.setInput(e.target.value)}
|
|
281
|
+
onKeyPress={(e) => {
|
|
282
|
+
if (e.key === 'Enter' && !agent.isLoading && agent.input.trim()) {
|
|
283
|
+
agent.sendMessage();
|
|
284
|
+
}
|
|
285
|
+
}}
|
|
286
|
+
placeholder="Ask about weather, search products, or calculate..."
|
|
287
|
+
disabled={agent.isLoading}
|
|
288
|
+
style={{
|
|
289
|
+
flex: 1,
|
|
290
|
+
padding: '14px',
|
|
291
|
+
fontSize: '15px',
|
|
292
|
+
border: '1px solid #e0e0e0',
|
|
293
|
+
borderRadius: '8px',
|
|
294
|
+
outline: 'none',
|
|
295
|
+
backgroundColor: agent.isLoading ? '#f5f5f5' : '#fff'
|
|
296
|
+
}}
|
|
297
|
+
/>
|
|
298
|
+
|
|
299
|
+
{agent.isLoading ? (
|
|
300
|
+
<button
|
|
301
|
+
onClick={agent.stop}
|
|
302
|
+
style={{
|
|
303
|
+
padding: '14px 28px',
|
|
304
|
+
fontSize: '15px',
|
|
305
|
+
fontWeight: '600',
|
|
306
|
+
backgroundColor: '#f44336',
|
|
307
|
+
color: '#fff',
|
|
308
|
+
border: 'none',
|
|
309
|
+
borderRadius: '8px',
|
|
310
|
+
cursor: 'pointer',
|
|
311
|
+
}}
|
|
312
|
+
>
|
|
313
|
+
Stop
|
|
314
|
+
</button>
|
|
315
|
+
) : (
|
|
316
|
+
<button
|
|
317
|
+
onClick={() => agent.sendMessage()}
|
|
318
|
+
disabled={!agent.input.trim()}
|
|
319
|
+
style={{
|
|
320
|
+
padding: '14px 28px',
|
|
321
|
+
fontSize: '15px',
|
|
322
|
+
fontWeight: '600',
|
|
323
|
+
backgroundColor: agent.input.trim() ? '#2196f3' : '#e0e0e0',
|
|
324
|
+
color: agent.input.trim() ? '#fff' : '#999',
|
|
325
|
+
border: 'none',
|
|
326
|
+
borderRadius: '8px',
|
|
327
|
+
cursor: agent.input.trim() ? 'pointer' : 'not-allowed',
|
|
328
|
+
transition: 'background-color 0.2s'
|
|
329
|
+
}}
|
|
330
|
+
>
|
|
331
|
+
Send
|
|
332
|
+
</button>
|
|
333
|
+
)}
|
|
334
|
+
</div>
|
|
335
|
+
|
|
336
|
+
{/* Error Display */}
|
|
337
|
+
{agent.error && (
|
|
338
|
+
<div style={{
|
|
339
|
+
marginTop: '15px',
|
|
340
|
+
padding: '15px',
|
|
341
|
+
backgroundColor: '#ffebee',
|
|
342
|
+
color: '#c62828',
|
|
343
|
+
borderRadius: '8px',
|
|
344
|
+
fontSize: '14px',
|
|
345
|
+
border: '1px solid #ef5350'
|
|
346
|
+
}}>
|
|
347
|
+
<strong>⚠️ Error:</strong> {agent.error.message}
|
|
348
|
+
</div>
|
|
349
|
+
)}
|
|
350
|
+
|
|
351
|
+
{/* Controls */}
|
|
352
|
+
<div style={{
|
|
353
|
+
marginTop: '15px',
|
|
354
|
+
display: 'flex',
|
|
355
|
+
gap: '10px',
|
|
356
|
+
justifyContent: 'flex-end'
|
|
357
|
+
}}>
|
|
358
|
+
<button
|
|
359
|
+
onClick={agent.newChat}
|
|
360
|
+
disabled={agent.isLoading}
|
|
361
|
+
style={{
|
|
362
|
+
padding: '10px 20px',
|
|
363
|
+
fontSize: '14px',
|
|
364
|
+
backgroundColor: '#fff',
|
|
365
|
+
border: '1px solid #e0e0e0',
|
|
366
|
+
borderRadius: '6px',
|
|
367
|
+
cursor: agent.isLoading ? 'not-allowed' : 'pointer',
|
|
368
|
+
color: '#666'
|
|
369
|
+
}}
|
|
370
|
+
>
|
|
371
|
+
🔄 New Chat
|
|
372
|
+
</button>
|
|
373
|
+
</div>
|
|
374
|
+
|
|
375
|
+
{/* Debug Info */}
|
|
376
|
+
<details style={{ marginTop: '30px', fontSize: '13px', color: '#666' }}>
|
|
377
|
+
<summary style={{ cursor: 'pointer', fontWeight: '600' }}>
|
|
378
|
+
Debug Info
|
|
379
|
+
</summary>
|
|
380
|
+
<pre style={{
|
|
381
|
+
marginTop: '10px',
|
|
382
|
+
padding: '15px',
|
|
383
|
+
backgroundColor: '#f5f5f5',
|
|
384
|
+
borderRadius: '6px',
|
|
385
|
+
overflow: 'auto'
|
|
386
|
+
}}>
|
|
387
|
+
{JSON.stringify({
|
|
388
|
+
chatId: agent.chatId,
|
|
389
|
+
messageCount: agent.messages.length,
|
|
390
|
+
isLoading: agent.isLoading,
|
|
391
|
+
status: agent.status,
|
|
392
|
+
availableTools: Object.keys(agent.tools),
|
|
393
|
+
availableUIComponents: Object.keys(agent.uiComponents),
|
|
394
|
+
}, null, 2)}
|
|
395
|
+
</pre>
|
|
396
|
+
</details>
|
|
397
|
+
</div>
|
|
398
|
+
);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
export default CustomToolsExample;
|