@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.
@@ -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;