@hsafa/ui-sdk 0.1.3 → 0.1.5
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/dist/index.cjs +18 -72
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +438 -43
- package/dist/index.d.ts +438 -43
- package/dist/index.js +18 -72
- package/dist/index.js.map +1 -1
- package/docs/README.md +327 -0
- package/examples/nested-chat-example.tsx +61 -0
- package/package.json +5 -1
package/docs/README.md
ADDED
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
# HSAFA UI SDK — Advanced Guide
|
|
2
|
+
|
|
3
|
+
Modern React SDK for integrating AI agents built with HSAFA AI Agent Studio into any web app. This guide covers architecture, setup, customization, streaming, actions, UI component injection, theming, i18n/RTL, persistence, and best practices.
|
|
4
|
+
|
|
5
|
+
- **Package**: `@hsafa/ui-sdk`
|
|
6
|
+
- **Entry point**: `sdk/src/index.ts`
|
|
7
|
+
- **Core building blocks**: `HsafaProvider`, `HsafaChat`, `useHsafaAction`, `useHsafaComponent`, `useHsafa`
|
|
8
|
+
- **Generated API reference**: `sdk/docs/api/` (TypeDoc)
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## 1) Install
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
pnpm add @hsafa/ui-sdk
|
|
16
|
+
# or
|
|
17
|
+
npm i @hsafa/ui-sdk
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Peer dependencies:
|
|
21
|
+
- react >= 18
|
|
22
|
+
- react-dom >= 18
|
|
23
|
+
- @tabler/icons-react (icons used by some components)
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## 2) Quick start
|
|
28
|
+
|
|
29
|
+
Wrap your app with `HsafaProvider` and render `HsafaChat` anywhere. The `baseUrl` should point to your server that exposes the agent API endpoints.
|
|
30
|
+
|
|
31
|
+
```tsx
|
|
32
|
+
import React from 'react';
|
|
33
|
+
import { HsafaProvider, HsafaChat } from '@hsafa/ui-sdk';
|
|
34
|
+
|
|
35
|
+
export default function App() {
|
|
36
|
+
return (
|
|
37
|
+
<HsafaProvider baseUrl={process.env.NEXT_PUBLIC_API_BASE || ''}>
|
|
38
|
+
<main style={{ height: '100vh' }}>
|
|
39
|
+
<HsafaChat agentId="my-agent-id" theme="dark" />
|
|
40
|
+
</main>
|
|
41
|
+
</HsafaProvider>
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
What this does under the hood:
|
|
47
|
+
- Streams NDJSON from `POST {baseUrl}/api/run/:agentId` with `Accept: application/x-ndjson`.
|
|
48
|
+
- Uploads files to `POST {baseUrl}/api/uploads` and attaches them to prompts.
|
|
49
|
+
- Persists chat history and UI state in `localStorage` by `agentId`.
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## 3) Architecture overview
|
|
54
|
+
|
|
55
|
+
- **Provider**: `HsafaProvider` stores SDK config and dynamic registries:
|
|
56
|
+
- `actions: Map<string, HsafaActionHandler>`
|
|
57
|
+
- `components: Map<string, React.ComponentType>`
|
|
58
|
+
- **Chat UI**: `HsafaChat` handles input, history, streaming, rendering of assistant responses, and attachments.
|
|
59
|
+
- **Streaming**: `useAgentStreaming()` parses NDJSON event stream into a structured timeline:
|
|
60
|
+
- `first-agent-*`, `main-agent-*` events, tool calls/results, reasoning, and final response items.
|
|
61
|
+
- **Action execution**: agent’s response items can include `type: 'action'`. Use `useHsafaAction()` to register handlers.
|
|
62
|
+
- **UI injection**: agent’s response items can include `type: 'ui'` to render custom components registered via `useHsafaComponent()`.
|
|
63
|
+
|
|
64
|
+
Server contract (by default used by `HsafaChat`):
|
|
65
|
+
- POST `{baseUrl}/api/run/:agentId` — streaming NDJSON
|
|
66
|
+
- POST `{baseUrl}/api/uploads` — file uploads
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## 4) Provider API
|
|
71
|
+
|
|
72
|
+
`HsafaProvider` (see `sdk/src/providers/HsafaProvider.tsx`):
|
|
73
|
+
- Props:
|
|
74
|
+
- `baseUrl?: string` — base URL for API calls (e.g. `""` for same-origin or `"https://api.example.com"`).
|
|
75
|
+
- Context (`useHsafa()`):
|
|
76
|
+
- `baseUrl?: string`
|
|
77
|
+
- `actions: Map<string, HsafaActionHandler>`
|
|
78
|
+
- `components: Map<string, React.ComponentType<any>>`
|
|
79
|
+
- `registerAction(name, handler) => unregister()`
|
|
80
|
+
- `unregisterAction(name, handler?)`
|
|
81
|
+
- `registerComponent(name, component) => unregister()`
|
|
82
|
+
- `unregisterComponent(name, component?)`
|
|
83
|
+
|
|
84
|
+
Example using `useHsafa()` directly:
|
|
85
|
+
```tsx
|
|
86
|
+
import { useHsafa } from '@hsafa/ui-sdk';
|
|
87
|
+
|
|
88
|
+
function Debug() {
|
|
89
|
+
const { actions, components, baseUrl } = useHsafa();
|
|
90
|
+
// inspect registries or baseUrl
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## 5) Chat component (`HsafaChat`)
|
|
98
|
+
|
|
99
|
+
Source: `sdk/src/components/HsafaChat.tsx`
|
|
100
|
+
|
|
101
|
+
Minimal usage:
|
|
102
|
+
```tsx
|
|
103
|
+
<HsafaChat agentId="my-agent" />
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Important props (selected):
|
|
107
|
+
- `agentId: string` — required
|
|
108
|
+
- `theme?: 'light' | 'dark'` — default `dark`
|
|
109
|
+
- `language?: 'en' | 'ar'` — defaults by `dir` if not provided
|
|
110
|
+
- `dir?: 'ltr' | 'rtl'` — controls layout RTL/LTR
|
|
111
|
+
- Colors (override theme defaults): `primaryColor`, `backgroundColor`, `borderColor`, `textColor`, `accentColor`
|
|
112
|
+
- Layout: `width` (default 420), `height` (default `100vh`), `floatingButtonPosition`, `alwaysOpen`, `defaultOpen`, `expandable`, `maximized`
|
|
113
|
+
- Borders/animation: `enableBorderAnimation`, `enableContentPadding`, `enableContentBorder`, `borderRadius`
|
|
114
|
+
- UX copy: `placeholder`, `title`
|
|
115
|
+
- Advanced UI: `defaultReasoningOpen`, `hideReasoningContent`
|
|
116
|
+
- Styling hooks: `className`, `chatContainerClassName`
|
|
117
|
+
- Children: content beside the fixed chat panel (panel opens over the right/left edge)
|
|
118
|
+
|
|
119
|
+
Behavior highlights:
|
|
120
|
+
- Persists chat sessions under `localStorage` prefix `hsafaChat_${agentId}`.
|
|
121
|
+
- Supports file/image attachments with size checks and server upload.
|
|
122
|
+
- Streams partial updates; auto-scrolls intelligently; preserves scroll when toggling reasoning.
|
|
123
|
+
- Renders assistant "items" including markdown, mermaid diagrams, actions, and custom UI components.
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
## 6) Registering actions (agent -> app)
|
|
128
|
+
|
|
129
|
+
Use `useHsafaAction(name, handler)` to make functions callable by the agent. The handler receives `(params, meta)` where `meta` includes the `trigger` type:
|
|
130
|
+
- `'partial'` — called during streaming (when explicitly enabled by the agent/SDK logic)
|
|
131
|
+
- `'params_complete'` — parameters stabilized mid-stream (debounced and deduped)
|
|
132
|
+
- `'final'` — after finalization
|
|
133
|
+
|
|
134
|
+
```tsx
|
|
135
|
+
import { useHsafaAction } from '@hsafa/ui-sdk';
|
|
136
|
+
|
|
137
|
+
export function CartActions() {
|
|
138
|
+
useHsafaAction('addToCart', async (params, meta) => {
|
|
139
|
+
// params: arbitrary JSON inferred by the agent
|
|
140
|
+
// meta: { name, trigger, index, assistantMessageId?, chatId? }
|
|
141
|
+
await fetch('/api/cart', { method: 'POST', body: JSON.stringify(params) });
|
|
142
|
+
return { success: true };
|
|
143
|
+
});
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Advanced execution behavior is implemented in `useActions()` / `useStreaming()` (parameter stabilization, debouncing, final guarantees). For most apps, just register via `useHsafaAction()`.
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## 7) Registering UI components (agent-rendered UI)
|
|
153
|
+
|
|
154
|
+
Use `useHsafaComponent(name, Component)` to expose UI that the agent can render with an item of shape `{ type: 'ui', component: name, props: {...} }`.
|
|
155
|
+
|
|
156
|
+
```tsx
|
|
157
|
+
import { useHsafaComponent } from '@hsafa/ui-sdk';
|
|
158
|
+
|
|
159
|
+
function ProductCard({ name, price }: { name: string; price: number }) {
|
|
160
|
+
return <div>{name}: ${price}</div>;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export function UIRegistry() {
|
|
164
|
+
useHsafaComponent('ProductCard', ProductCard);
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
If an agent returns an unregistered component name, `HsafaChat` will render a helpful placeholder with the props payload so you can register it.
|
|
170
|
+
|
|
171
|
+
Relevant renderer: `sdk/src/components/AssistantMessageItems.tsx`.
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## 8) What does the assistant response look like?
|
|
176
|
+
|
|
177
|
+
`HsafaChat` expects a streaming sequence from the server that ultimately yields a `response` with `items`:
|
|
178
|
+
- Strings are rendered as Markdown (`MarkdownRendererWithMermaid`).
|
|
179
|
+
- Objects with `type: 'ui'` render registered components via the provider registry.
|
|
180
|
+
- Objects with `type: 'action'` show execution status and trigger corresponding action handlers.
|
|
181
|
+
- Tool calls/results and reasoning are also visualized.
|
|
182
|
+
|
|
183
|
+
Example of a single final item payload (simplified):
|
|
184
|
+
```json
|
|
185
|
+
{
|
|
186
|
+
"type": "ui",
|
|
187
|
+
"component": "ProductCard",
|
|
188
|
+
"props": { "name": "Laptop", "price": 1299 }
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
---
|
|
193
|
+
|
|
194
|
+
## 9) Theming & styling
|
|
195
|
+
|
|
196
|
+
Theme presets (`light`/`dark`) with overridable colors. See `sdk/src/utils/chat-theme.ts`.
|
|
197
|
+
|
|
198
|
+
- Preset colors: `primaryColor`, `backgroundColor`, `borderColor`, `textColor`, `accentColor`, plus `mutedTextColor`, `inputBackground`, `cardBackground`, `hoverBackground` (derived).
|
|
199
|
+
- Override via `HsafaChat` props:
|
|
200
|
+
|
|
201
|
+
```tsx
|
|
202
|
+
<HsafaChat
|
|
203
|
+
agentId="my-agent"
|
|
204
|
+
theme="light"
|
|
205
|
+
primaryColor="#3b82f6"
|
|
206
|
+
backgroundColor="#ffffff"
|
|
207
|
+
borderColor="#e5e7eb"
|
|
208
|
+
textColor="#111827"
|
|
209
|
+
accentColor="#F9FAFB"
|
|
210
|
+
/>
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
Markdown with Mermaid is supported via `MarkdownRenderer` / `MarkdownRendererWithMermaid` and `MermaidDiagram`.
|
|
214
|
+
|
|
215
|
+
---
|
|
216
|
+
|
|
217
|
+
## 10) i18n and RTL
|
|
218
|
+
|
|
219
|
+
- Supported languages: `'en' | 'ar'` (see `sdk/src/i18n/translations.ts`).
|
|
220
|
+
- You can pass `language` and/or `dir` to `HsafaChat`:
|
|
221
|
+
|
|
222
|
+
```tsx
|
|
223
|
+
<HsafaChat agentId="my-agent" language="ar" dir="rtl" />
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
Common UI strings for input/header/history are localized. You can still override `placeholder` and `title` directly.
|
|
227
|
+
|
|
228
|
+
---
|
|
229
|
+
|
|
230
|
+
## 11) Persistence & storage
|
|
231
|
+
|
|
232
|
+
`HsafaChat` uses `useChatStorage(agentId)` to persist:
|
|
233
|
+
- Chats index and message history under `localStorage` key prefix `hsafaChat_${agentId}`.
|
|
234
|
+
- Current chat ID and chat visibility (`.currentChatId`, `.showChat`).
|
|
235
|
+
|
|
236
|
+
Key API (see `sdk/src/hooks/useChatStorage.ts`):
|
|
237
|
+
- `createNewChat(firstMessage?)` — returns new chat id
|
|
238
|
+
- `persistChatData(messages)` — saves messages + updates meta
|
|
239
|
+
- `loadChatsIndex()`, `loadChat(id)`, `deleteChat(id, cb)`
|
|
240
|
+
|
|
241
|
+
---
|
|
242
|
+
|
|
243
|
+
## 12) Attachments & uploads
|
|
244
|
+
|
|
245
|
+
`HsafaChat` integrates `useFileUpload(baseUrl)` which:
|
|
246
|
+
- Validates size (default 25MB) and uploads to `{baseUrl}/api/uploads`.
|
|
247
|
+
- Adds uploaded assets to the user message content as `image` or `file` parts.
|
|
248
|
+
|
|
249
|
+
Server is expected to return:
|
|
250
|
+
```json
|
|
251
|
+
{ "id": "file-id", "name": "foo.png", "url": "https://...", "mimeType": "image/png", "size": 12345 }
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
---
|
|
255
|
+
|
|
256
|
+
## 13) Streaming contract (server)
|
|
257
|
+
|
|
258
|
+
The server should stream NDJSON lines with event `type` keys consumed by `useAgentStreaming()` (see `sdk/src/hooks/useAgentStreaming.ts`). Common events include:
|
|
259
|
+
- `first-agent-start|partial|end`
|
|
260
|
+
- `main-agent-start|skipped|reasoning-start|reasoning-delta|reasoning-end`
|
|
261
|
+
- `main-agent-tool-call-start|tool-call|tool-result|tool-error`
|
|
262
|
+
- `main-agent-response-partial` (with `value.items`)
|
|
263
|
+
- `text-delta|text-end`
|
|
264
|
+
- `final` (with `value.items`)
|
|
265
|
+
- `error`
|
|
266
|
+
|
|
267
|
+
You can adopt any LLM/tooling backend as long as you conform to the above event stream and endpoints.
|
|
268
|
+
|
|
269
|
+
---
|
|
270
|
+
|
|
271
|
+
## 14) Full example
|
|
272
|
+
|
|
273
|
+
See ready-to-run examples in `sdk/examples/`:
|
|
274
|
+
- `getting-started.tsx`
|
|
275
|
+
- `ecommerce-agent.tsx`
|
|
276
|
+
- `nested-chat-example.tsx`
|
|
277
|
+
|
|
278
|
+
A minimal page:
|
|
279
|
+
```tsx
|
|
280
|
+
import { HsafaProvider, HsafaChat, useHsafaAction, useHsafaComponent } from '@hsafa/ui-sdk';
|
|
281
|
+
|
|
282
|
+
function Registry() {
|
|
283
|
+
useHsafaAction('notify', async (params) => {
|
|
284
|
+
console.log('Notify', params);
|
|
285
|
+
});
|
|
286
|
+
useHsafaComponent('ProductCard', (p: any) => <div>{p.name}: ${p.price}</div>);
|
|
287
|
+
return null;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
export default function Page() {
|
|
291
|
+
return (
|
|
292
|
+
<HsafaProvider baseUrl="">
|
|
293
|
+
<Registry />
|
|
294
|
+
<HsafaChat agentId="my-agent" theme="dark" />
|
|
295
|
+
</HsafaProvider>
|
|
296
|
+
);
|
|
297
|
+
}
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
---
|
|
301
|
+
|
|
302
|
+
## 15) Troubleshooting
|
|
303
|
+
|
|
304
|
+
- Ensure your server responds to `POST /api/run/:agentId` with `Content-Type: application/x-ndjson` and streams events, not a single JSON.
|
|
305
|
+
- If attachments fail, verify `POST /api/uploads` exists and returns the required JSON fields.
|
|
306
|
+
- Unregistered UI component? Check the `component` name in items and make sure you called `useHsafaComponent(name, Comp)` under the same `HsafaProvider`.
|
|
307
|
+
- Actions not firing? Confirm the action name matches and that the agent actually emits `type: 'action'` items. Check browser console warnings from `useActions()`/`useStreaming()`.
|
|
308
|
+
|
|
309
|
+
---
|
|
310
|
+
|
|
311
|
+
## 16) API reference
|
|
312
|
+
|
|
313
|
+
The full API is generated from TypeScript types. Start here:
|
|
314
|
+
- `sdk/docs/api/README.md`
|
|
315
|
+
- `sdk/docs/api/functions/HsafaChat.md`
|
|
316
|
+
- `sdk/docs/api/functions/HsafaProvider.md`
|
|
317
|
+
- `sdk/docs/api/functions/useHsafa.md`
|
|
318
|
+
- `sdk/docs/api/functions/useHsafaAction.md`
|
|
319
|
+
- `sdk/docs/api/functions/useHsafaComponent.md`
|
|
320
|
+
|
|
321
|
+
---
|
|
322
|
+
|
|
323
|
+
## 17) License & links
|
|
324
|
+
|
|
325
|
+
- License: MIT
|
|
326
|
+
- Repo: https://github.com/husamabusafa/hsafa/tree/main/sdk
|
|
327
|
+
- Issues: https://github.com/husamabusafa/hsafa/issues
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { HsafaProvider, HsafaChat } from '@hsafa/ui-sdk';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Example demonstrating nested HsafaChat components.
|
|
6
|
+
*
|
|
7
|
+
* Scenario:
|
|
8
|
+
* - Outer HsafaChat wraps the entire website/app (for general questions)
|
|
9
|
+
* - Inner HsafaChat wraps a specific component (for component-specific help)
|
|
10
|
+
*
|
|
11
|
+
* Result:
|
|
12
|
+
* - Only the innermost (most specific) chat panel will be displayed
|
|
13
|
+
* - The outer chat panel is automatically hidden when inner chat exists
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
function App() {
|
|
17
|
+
return (
|
|
18
|
+
<HsafaProvider baseUrl="http://localhost:8000" apiKey="your-api-key">
|
|
19
|
+
{/* Outer HsafaChat - wraps the whole website */}
|
|
20
|
+
<HsafaChat
|
|
21
|
+
agentId="general-agent"
|
|
22
|
+
title="General Help"
|
|
23
|
+
placeholder="Ask about anything..."
|
|
24
|
+
>
|
|
25
|
+
<div style={{ padding: '40px' }}>
|
|
26
|
+
<h1>My Website</h1>
|
|
27
|
+
<p>This is the main content of your website.</p>
|
|
28
|
+
|
|
29
|
+
{/* Some component that needs specific help */}
|
|
30
|
+
<SpecificComponent />
|
|
31
|
+
</div>
|
|
32
|
+
</HsafaChat>
|
|
33
|
+
</HsafaProvider>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function SpecificComponent() {
|
|
38
|
+
return (
|
|
39
|
+
<div style={{
|
|
40
|
+
border: '2px solid #333',
|
|
41
|
+
padding: '20px',
|
|
42
|
+
marginTop: '20px',
|
|
43
|
+
borderRadius: '8px'
|
|
44
|
+
}}>
|
|
45
|
+
{/* Inner HsafaChat - wraps only this specific component */}
|
|
46
|
+
<HsafaChat
|
|
47
|
+
agentId="product-agent"
|
|
48
|
+
title="Product Help"
|
|
49
|
+
placeholder="Ask about this product..."
|
|
50
|
+
>
|
|
51
|
+
<div>
|
|
52
|
+
<h2>Product Details</h2>
|
|
53
|
+
<p>This is a specific product component with its own chat assistant.</p>
|
|
54
|
+
<p><strong>Only this chat panel will be shown, not the outer one!</strong></p>
|
|
55
|
+
</div>
|
|
56
|
+
</HsafaChat>
|
|
57
|
+
</div>
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export default App;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hsafa/ui-sdk",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "React SDK for integrating AI agents built with HSAFA AI Agent Studio",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -105,5 +105,9 @@
|
|
|
105
105
|
"homepage": "https://github.com/husamabusafa/hsafa/tree/main/sdk#readme",
|
|
106
106
|
"bugs": {
|
|
107
107
|
"url": "https://github.com/husamabusafa/hsafa/issues"
|
|
108
|
+
},
|
|
109
|
+
"dependencies": {
|
|
110
|
+
"lucide-react": "^0.509.0",
|
|
111
|
+
"mermaid": "^11.4.0"
|
|
108
112
|
}
|
|
109
113
|
}
|