@hsafa/ui-sdk 0.6.0 → 0.6.2
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/DOCUMENTATION.md +17 -0
- package/dist/index.cjs +277 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +65 -111
- package/dist/index.d.ts +65 -111
- package/dist/index.js +277 -8
- package/dist/index.js.map +1 -1
- package/docs/CUSTOM_UI_EXAMPLES.md +4 -5
- package/docs/DYNAMIC_PAGE_SCHEMAS.md +3 -0
- package/docs/HEADLESS_QUICK_REFERENCE.md +0 -13
- package/docs/HEADLESS_USAGE.md +10 -26
- package/docs/MIGRATION_TO_HEADLESS.md +3 -40
- package/docs/PROFESSIONAL_SDK_GUIDE.md +441 -0
- package/docs/README.md +29 -7
- package/docs/api/README.md +3 -0
- package/docs/api/functions/useHsafaAction.md +3 -0
- package/docs/api/functions/useHsafaComponent.md +3 -0
- package/docs/handbook/00-Overview.md +3 -6
- package/docs/handbook/02-Architecture.md +1 -8
- package/docs/handbook/03-Components-and-Hooks.md +4 -4
- package/docs/handbook/10-Examples-and-Recipes.md +0 -19
- package/docs/handbook/11-API-Reference-Map.md +0 -7
- package/docs/handbook/README.md +0 -1
- package/package.json +17 -16
|
@@ -16,7 +16,7 @@ Create custom UIs for specific tool calls using the `HsafaUI` prop. Your compone
|
|
|
16
16
|
### Usage Example
|
|
17
17
|
|
|
18
18
|
```tsx
|
|
19
|
-
import { HsafaChat, CustomToolUIRenderProps } from '@hsafa/sdk';
|
|
19
|
+
import { HsafaChat, CustomToolUIRenderProps } from '@hsafa/ui-sdk';
|
|
20
20
|
|
|
21
21
|
// Custom UI component for a tool
|
|
22
22
|
function ChoiceToolUI({ toolName, toolCallId, input, output, status, addToolResult, ...restInputProps }: CustomToolUIRenderProps & any) {
|
|
@@ -123,7 +123,7 @@ Add a persistent component above the chat input (e.g., quick actions, status bar
|
|
|
123
123
|
### Usage Example
|
|
124
124
|
|
|
125
125
|
```tsx
|
|
126
|
-
import { HsafaChat } from '@hsafa/sdk';
|
|
126
|
+
import { HsafaChat } from '@hsafa/ui-sdk';
|
|
127
127
|
|
|
128
128
|
function QuickActions() {
|
|
129
129
|
const handleQuickAction = (action: string) => {
|
|
@@ -264,7 +264,7 @@ function App() {
|
|
|
264
264
|
Combining all customizations:
|
|
265
265
|
|
|
266
266
|
```tsx
|
|
267
|
-
import { HsafaChat } from '@hsafa/sdk';
|
|
267
|
+
import { HsafaChat } from '@hsafa/ui-sdk';
|
|
268
268
|
import { ChoiceToolUI } from './components/ChoiceToolUI';
|
|
269
269
|
import { QuickActions } from './components/QuickActions';
|
|
270
270
|
|
|
@@ -303,7 +303,6 @@ All types are exported from the SDK:
|
|
|
303
303
|
```tsx
|
|
304
304
|
import type {
|
|
305
305
|
CustomToolUIRenderProps,
|
|
306
|
-
CustomEditModalRenderProps,
|
|
307
306
|
Attachment
|
|
308
|
-
} from '@hsafa/sdk';
|
|
307
|
+
} from '@hsafa/ui-sdk';
|
|
309
308
|
```
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
# Dynamic Page Schemas Guide
|
|
2
2
|
|
|
3
|
+
> Note: the Dynamic Page subsystem referenced in this document is not present in the current `sdk/src` in this repository.
|
|
4
|
+
> This file is kept for historical/reference purposes only.
|
|
5
|
+
|
|
3
6
|
This guide explains how to use optional Zod schemas and examples with Dynamic Page component types.
|
|
4
7
|
|
|
5
8
|
## Overview
|
|
@@ -37,22 +37,10 @@ const agent = useHsafaAgent({
|
|
|
37
37
|
baseUrl?: string,
|
|
38
38
|
tools?: Record<string, Function | { tool: Function, executeEachToken?: boolean }>,
|
|
39
39
|
uiComponents?: Record<string, React.ComponentType>,
|
|
40
|
-
dynamicPageTypes?: Array<DynamicPageTypeConfig>,
|
|
41
40
|
onFinish?: (message: any) => void,
|
|
42
41
|
onError?: (error: Error) => void,
|
|
43
42
|
onMessagesChange?: (messages: any[]) => void,
|
|
44
43
|
initialMessages?: any[],
|
|
45
|
-
colors?: {
|
|
46
|
-
primaryColor?: string,
|
|
47
|
-
backgroundColor?: string,
|
|
48
|
-
borderColor?: string,
|
|
49
|
-
textColor?: string,
|
|
50
|
-
accentColor?: string,
|
|
51
|
-
mutedTextColor?: string,
|
|
52
|
-
inputBackground?: string,
|
|
53
|
-
cardBackground?: string,
|
|
54
|
-
hoverBackground?: string,
|
|
55
|
-
},
|
|
56
44
|
});
|
|
57
45
|
```
|
|
58
46
|
|
|
@@ -74,7 +62,6 @@ const agent = useHsafaAgent({
|
|
|
74
62
|
| `chatId` | `string` | Current chat ID |
|
|
75
63
|
| `tools` | `object` | All available tools |
|
|
76
64
|
| `uiComponents` | `object` | All UI components |
|
|
77
|
-
| `dynamicPage` | `object \| null` | Dynamic page operations |
|
|
78
65
|
| `formHostRef` | `React.MutableRefObject` | Form host elements ref |
|
|
79
66
|
| `formStateRef` | `React.MutableRefObject` | Form state ref |
|
|
80
67
|
| `cleanupForms()` | `() => void` | Cleanup all forms |
|
package/docs/HEADLESS_USAGE.md
CHANGED
|
@@ -19,7 +19,7 @@ The Hsafa SDK provides headless hooks that let you build completely custom chat
|
|
|
19
19
|
Here's the simplest example of using the headless API:
|
|
20
20
|
|
|
21
21
|
```tsx
|
|
22
|
-
import { useHsafaAgent } from '@hsafa/sdk';
|
|
22
|
+
import { useHsafaAgent } from '@hsafa/ui-sdk';
|
|
23
23
|
|
|
24
24
|
function MyCustomChat() {
|
|
25
25
|
const agent = useHsafaAgent({
|
|
@@ -67,7 +67,7 @@ function MyCustomChat() {
|
|
|
67
67
|
The main hook that provides all agent functionality.
|
|
68
68
|
|
|
69
69
|
```tsx
|
|
70
|
-
import { useHsafaAgent } from '@hsafa/sdk';
|
|
70
|
+
import { useHsafaAgent } from '@hsafa/ui-sdk';
|
|
71
71
|
|
|
72
72
|
const agent = useHsafaAgent({
|
|
73
73
|
agentId: 'my-agent-id',
|
|
@@ -85,26 +85,12 @@ const agent = useHsafaAgent({
|
|
|
85
85
|
uiComponents: {
|
|
86
86
|
MyCustomComponent: ({ data }) => <div>{data.message}</div>,
|
|
87
87
|
},
|
|
88
|
-
|
|
89
|
-
// Optional: Enable dynamic pages
|
|
90
|
-
dynamicPageTypes: [
|
|
91
|
-
{
|
|
92
|
-
type: 'product-catalog',
|
|
93
|
-
schema: { /* ... */ },
|
|
94
|
-
},
|
|
95
|
-
],
|
|
96
|
-
|
|
88
|
+
|
|
97
89
|
// Optional: Callbacks
|
|
98
90
|
onFinish: (message) => console.log('Message finished:', message),
|
|
99
91
|
onError: (error) => console.error('Error:', error),
|
|
100
92
|
onMessagesChange: (messages) => console.log('Messages updated:', messages),
|
|
101
93
|
|
|
102
|
-
// Optional: Theme colors for built-in forms
|
|
103
|
-
colors: {
|
|
104
|
-
primaryColor: '#3b82f6',
|
|
105
|
-
backgroundColor: '#ffffff',
|
|
106
|
-
textColor: '#000000',
|
|
107
|
-
},
|
|
108
94
|
});
|
|
109
95
|
|
|
110
96
|
// Access the API
|
|
@@ -128,7 +114,7 @@ agent.uiComponents // All UI components
|
|
|
128
114
|
Handle file uploads for messages.
|
|
129
115
|
|
|
130
116
|
```tsx
|
|
131
|
-
import { useFileUpload } from '@hsafa/sdk';
|
|
117
|
+
import { useFileUpload } from '@hsafa/ui-sdk';
|
|
132
118
|
|
|
133
119
|
function FileUploadExample() {
|
|
134
120
|
const agent = useHsafaAgent({ agentId: 'my-agent', baseUrl: 'http://localhost:3000' });
|
|
@@ -184,7 +170,7 @@ function FileUploadExample() {
|
|
|
184
170
|
Persist and manage chat history.
|
|
185
171
|
|
|
186
172
|
```tsx
|
|
187
|
-
import { useHsafaAgent, useChatStorage } from '@hsafa/sdk';
|
|
173
|
+
import { useHsafaAgent, useChatStorage } from '@hsafa/ui-sdk';
|
|
188
174
|
|
|
189
175
|
function ChatWithHistory() {
|
|
190
176
|
const agent = useHsafaAgent({ agentId: 'my-agent', baseUrl: 'http://localhost:3000' });
|
|
@@ -235,7 +221,7 @@ function ChatWithHistory() {
|
|
|
235
221
|
Edit messages and regenerate responses.
|
|
236
222
|
|
|
237
223
|
```tsx
|
|
238
|
-
import { useHsafaAgent, useMessageEditor } from '@hsafa/sdk';
|
|
224
|
+
import { useHsafaAgent, useMessageEditor } from '@hsafa/ui-sdk';
|
|
239
225
|
|
|
240
226
|
function EditableChat() {
|
|
241
227
|
const agent = useHsafaAgent({ agentId: 'my-agent', baseUrl: 'http://localhost:3000' });
|
|
@@ -280,7 +266,7 @@ function EditableChat() {
|
|
|
280
266
|
Auto-scroll to bottom during streaming.
|
|
281
267
|
|
|
282
268
|
```tsx
|
|
283
|
-
import { useHsafaAgent, useAutoScroll } from '@hsafa/sdk';
|
|
269
|
+
import { useHsafaAgent, useAutoScroll } from '@hsafa/ui-sdk';
|
|
284
270
|
|
|
285
271
|
function AutoScrollChat() {
|
|
286
272
|
const agent = useHsafaAgent({ agentId: 'my-agent', baseUrl: 'http://localhost:3000' });
|
|
@@ -301,7 +287,7 @@ function AutoScrollChat() {
|
|
|
301
287
|
### Minimal Chat Interface
|
|
302
288
|
|
|
303
289
|
```tsx
|
|
304
|
-
import { useHsafaAgent } from '@hsafa/sdk';
|
|
290
|
+
import { useHsafaAgent } from '@hsafa/ui-sdk';
|
|
305
291
|
|
|
306
292
|
function MinimalChat() {
|
|
307
293
|
const agent = useHsafaAgent({
|
|
@@ -406,7 +392,7 @@ import {
|
|
|
406
392
|
useChatStorage,
|
|
407
393
|
useMessageEditor,
|
|
408
394
|
useAutoScroll
|
|
409
|
-
} from '@hsafa/sdk';
|
|
395
|
+
} from '@hsafa/ui-sdk';
|
|
410
396
|
|
|
411
397
|
function FullFeaturedChat() {
|
|
412
398
|
const [showHistory, setShowHistory] = useState(false);
|
|
@@ -629,7 +615,7 @@ function FullFeaturedChat() {
|
|
|
629
615
|
### Custom Tool Example
|
|
630
616
|
|
|
631
617
|
```tsx
|
|
632
|
-
import { useHsafaAgent } from '@hsafa/sdk';
|
|
618
|
+
import { useHsafaAgent } from '@hsafa/ui-sdk';
|
|
633
619
|
|
|
634
620
|
function ChatWithCustomTools() {
|
|
635
621
|
const agent = useHsafaAgent({
|
|
@@ -677,6 +663,4 @@ See individual hook files for complete TypeScript interfaces:
|
|
|
677
663
|
|
|
678
664
|
## Next Steps
|
|
679
665
|
|
|
680
|
-
- Check out the [Dynamic Pages documentation](./DYNAMIC_PAGE_SCHEMAS.md) for building dynamic UIs
|
|
681
|
-
- See the [Tool Development Guide](./TOOL_DEVELOPMENT.md) for creating custom tools
|
|
682
666
|
- Browse the `/examples` folder for more use cases
|
|
@@ -58,13 +58,6 @@ function App() {
|
|
|
58
58
|
uiComponents: {
|
|
59
59
|
CustomCard: ({ data }) => <div>{data.text}</div>
|
|
60
60
|
},
|
|
61
|
-
|
|
62
|
-
// Theme colors for built-in forms
|
|
63
|
-
colors: {
|
|
64
|
-
primaryColor: '#0ea5e9',
|
|
65
|
-
backgroundColor: '#0B0B0F',
|
|
66
|
-
textColor: '#EDEEF0',
|
|
67
|
-
}
|
|
68
61
|
});
|
|
69
62
|
|
|
70
63
|
const scrollRef = useAutoScroll<HTMLDivElement>(agent.isLoading);
|
|
@@ -258,16 +251,9 @@ const agent = useHsafaAgent({
|
|
|
258
251
|
|
|
259
252
|
**After:**
|
|
260
253
|
```tsx
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
backgroundColor: '#0B0B0F',
|
|
265
|
-
textColor: '#EDEEF0',
|
|
266
|
-
// ... other colors
|
|
267
|
-
}
|
|
268
|
-
});
|
|
269
|
-
|
|
270
|
-
// Then apply these colors in your own styling
|
|
254
|
+
// In headless mode, you control styling directly.
|
|
255
|
+
// If you need theme colors, keep them in your app state/theme system and apply them
|
|
256
|
+
// when rendering your custom UI.
|
|
271
257
|
```
|
|
272
258
|
|
|
273
259
|
### 8. Callbacks
|
|
@@ -288,29 +274,6 @@ const agent = useHsafaAgent({
|
|
|
288
274
|
});
|
|
289
275
|
```
|
|
290
276
|
|
|
291
|
-
### 9. Dynamic Pages
|
|
292
|
-
|
|
293
|
-
**Before:**
|
|
294
|
-
```tsx
|
|
295
|
-
<HsafaChat
|
|
296
|
-
dynamicPageTypes={[
|
|
297
|
-
{ type: 'product-catalog', schema: { /* ... */ } }
|
|
298
|
-
]}
|
|
299
|
-
/>
|
|
300
|
-
```
|
|
301
|
-
|
|
302
|
-
**After:**
|
|
303
|
-
```tsx
|
|
304
|
-
const agent = useHsafaAgent({
|
|
305
|
-
dynamicPageTypes: [
|
|
306
|
-
{ type: 'product-catalog', schema: { /* ... */ } }
|
|
307
|
-
]
|
|
308
|
-
});
|
|
309
|
-
|
|
310
|
-
// Access dynamic page operations:
|
|
311
|
-
agent.dynamicPage?.getOperations()
|
|
312
|
-
```
|
|
313
|
-
|
|
314
277
|
## Common Patterns
|
|
315
278
|
|
|
316
279
|
### Pattern 1: Minimal Chat
|
|
@@ -0,0 +1,441 @@
|
|
|
1
|
+
# HSAFA UI SDK — Professional Guide
|
|
2
|
+
|
|
3
|
+
This guide is an **authoritative, implementation-aligned** documentation for `@hsafa/ui-sdk` as it exists in this repository.
|
|
4
|
+
|
|
5
|
+
It is written for:
|
|
6
|
+
- Teams embedding **Hsafa Chat** into existing products
|
|
7
|
+
- Teams building a **fully custom UI** around the agent runtime
|
|
8
|
+
- Teams implementing **tool-driven interfaces** (dashboards, builders, maps)
|
|
9
|
+
|
|
10
|
+
## Scope and sources
|
|
11
|
+
|
|
12
|
+
- **Package**: `@hsafa/ui-sdk` (see `sdk/package.json`)
|
|
13
|
+
- **Public entry point**: `sdk/src/index.ts`
|
|
14
|
+
- **Type signature reference**: `sdk/dist/index.d.ts` (the built build output)
|
|
15
|
+
- **Real integration examples**: `use-cases/rafed-hack` and `use-cases/ksu-agent`
|
|
16
|
+
|
|
17
|
+
> Important: some legacy or experimental docs in `sdk/docs/` and `sdk/docs/api/` may reference APIs not present in the current source. This guide always prefers the current source/export surface.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
# 1) Mental model
|
|
22
|
+
|
|
23
|
+
## 1.1 Two integration modes
|
|
24
|
+
|
|
25
|
+
- **UI mode (drop-in chat)**
|
|
26
|
+
- Use `HsafaProvider` + `HsafaChat`
|
|
27
|
+
- You get chat UI, streaming, file upload, history UI, message editing, tool rendering
|
|
28
|
+
|
|
29
|
+
- **Headless mode (custom UI)**
|
|
30
|
+
- Use `useHsafaAgent` (+ optionally `useChatStorage`, `useMessageEditor`, `useFileUpload`, `useAutoScroll`)
|
|
31
|
+
- You build the UI yourself (layout, styling, message rendering)
|
|
32
|
+
|
|
33
|
+
Both modes support:
|
|
34
|
+
- **Server-side tools** (tools executed on your server)
|
|
35
|
+
- **Client-side tools** (tools executed in the browser)
|
|
36
|
+
- **Tool-driven UI rendering** (`HsafaUI` / `uiComponents`)
|
|
37
|
+
|
|
38
|
+
## 1.2 Data flow overview
|
|
39
|
+
|
|
40
|
+
1. **User types** a prompt (optionally attaches files).
|
|
41
|
+
2. Client sends `POST {baseUrl}/api/run/:agentId` using the Vercel AI SDK v5 transport.
|
|
42
|
+
3. Server streams NDJSON events.
|
|
43
|
+
4. Client renders:
|
|
44
|
+
- assistant text (Markdown)
|
|
45
|
+
- reasoning (optional)
|
|
46
|
+
- tool calls and tool results
|
|
47
|
+
- custom UI for selected tools (via `HsafaUI`)
|
|
48
|
+
5. If the assistant requests a **client tool**:
|
|
49
|
+
- the SDK executes it in the browser and sends `addToolResult(...)` back into the chat stream.
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
# 2) Installation & setup
|
|
54
|
+
|
|
55
|
+
## 2.1 Install
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
pnpm add @hsafa/ui-sdk
|
|
59
|
+
# or npm i @hsafa/ui-sdk
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Peer dependencies:
|
|
63
|
+
- `react >= 18`
|
|
64
|
+
- `react-dom >= 18`
|
|
65
|
+
- `@tabler/icons-react` (some UI pieces rely on icons)
|
|
66
|
+
|
|
67
|
+
## 2.2 Styles
|
|
68
|
+
|
|
69
|
+
The package exports CSS:
|
|
70
|
+
- `@hsafa/ui-sdk/index.css`
|
|
71
|
+
|
|
72
|
+
If your host app does not already include the SDK styles, import them once near your app root:
|
|
73
|
+
|
|
74
|
+
```ts
|
|
75
|
+
import '@hsafa/ui-sdk/index.css';
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
> Some components also bring their own styles from dependencies (e.g. `@ant-design/x-markdown` theme CSS) internally.
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
# 3) Quickstart (pre-built chat UI)
|
|
83
|
+
|
|
84
|
+
## 3.1 Minimal setup
|
|
85
|
+
|
|
86
|
+
```tsx
|
|
87
|
+
import { HsafaProvider, HsafaChat } from '@hsafa/ui-sdk';
|
|
88
|
+
|
|
89
|
+
export default function App() {
|
|
90
|
+
return (
|
|
91
|
+
<HsafaProvider baseUrl="http://localhost:3900">
|
|
92
|
+
<HsafaChat agentId="YOUR_AGENT_ID" />
|
|
93
|
+
</HsafaProvider>
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## 3.2 `baseUrl` rules
|
|
99
|
+
|
|
100
|
+
- `baseUrl` can be:
|
|
101
|
+
- `""` (same origin)
|
|
102
|
+
- `"https://your-server.example"`
|
|
103
|
+
- `HsafaChat` and `useHsafaAgent` accept a `baseUrl` prop/config.
|
|
104
|
+
- If omitted, they fall back to the provider’s `baseUrl`.
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
# 4) Provider: `HsafaProvider` and `useHsafa`
|
|
109
|
+
|
|
110
|
+
Source: `sdk/src/providers/HsafaProvider.tsx`
|
|
111
|
+
|
|
112
|
+
## 4.1 Responsibilities
|
|
113
|
+
|
|
114
|
+
- Stores SDK defaults: `baseUrl`, `dir`, `theme`
|
|
115
|
+
- Tracks cross-chat state:
|
|
116
|
+
- `isAnyStreaming` (useful for global UI effects)
|
|
117
|
+
- `isAnyChatOpen` (useful for layout adjustments)
|
|
118
|
+
- Exposes `currentChatId` (set by `HsafaChat` / `useHsafaAgent`)
|
|
119
|
+
|
|
120
|
+
## 4.2 Practical pattern: layout that reacts to chat open/streaming
|
|
121
|
+
|
|
122
|
+
The SDK ships `ContentContainer` which reads provider state and:
|
|
123
|
+
- animates a border during streaming
|
|
124
|
+
- applies margin when chat is open (so your app content is not covered)
|
|
125
|
+
|
|
126
|
+
Source: `sdk/src/components/ContentContainer.tsx`
|
|
127
|
+
|
|
128
|
+
Common usage (as seen in `use-cases/rafed-hack`):
|
|
129
|
+
|
|
130
|
+
```tsx
|
|
131
|
+
<HsafaProvider baseUrl={AGENT_BASE_URL}>
|
|
132
|
+
<ContentContainer>
|
|
133
|
+
<YourAppRoutes />
|
|
134
|
+
</ContentContainer>
|
|
135
|
+
|
|
136
|
+
<HsafaChat agentId={AGENT_ID} alwaysOpen />
|
|
137
|
+
</HsafaProvider>
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
# 5) `HsafaChat` (UI component)
|
|
143
|
+
|
|
144
|
+
Source: `sdk/src/components/HsafaChat.tsx`
|
|
145
|
+
|
|
146
|
+
## 5.1 What `HsafaChat` provides
|
|
147
|
+
|
|
148
|
+
- Streaming chat UI (based on `useHsafaAgent`)
|
|
149
|
+
- Attachments (via `useFileUpload`)
|
|
150
|
+
- Chat history (via `useChatStorage`)
|
|
151
|
+
- Message editing (internally + `useMessageEditor`)
|
|
152
|
+
- Tool visualization + custom tool UIs (`HsafaUI`)
|
|
153
|
+
- RTL + Arabic labels
|
|
154
|
+
- Optional full-page mode (`fullPageChat`)
|
|
155
|
+
|
|
156
|
+
## 5.2 Key props (most used in real apps)
|
|
157
|
+
|
|
158
|
+
From `sdk/src/types/chat.ts`:
|
|
159
|
+
- `agentId: string` (**required**)
|
|
160
|
+
- `baseUrl?: string` (optional override)
|
|
161
|
+
- `HsafaTools?: Record<string, HsafaTool>` (client-side tools)
|
|
162
|
+
- `HsafaUI?: Record<string, React.ComponentType<any>>` (tool rendering)
|
|
163
|
+
- `onMessagesChange?: (messages, chatId?) => void`
|
|
164
|
+
- `onFinish?: (payload) => void`
|
|
165
|
+
- `templateParams?: Record<string, unknown>`
|
|
166
|
+
- UI configuration:
|
|
167
|
+
- `theme`, `dir`, `lang/language`
|
|
168
|
+
- colors: `primaryColor`, `backgroundColor`, ...
|
|
169
|
+
- `title`, `placeholder`, `emptyStateMessage`
|
|
170
|
+
- `presetPrompts`
|
|
171
|
+
- `fullPageChat`
|
|
172
|
+
|
|
173
|
+
## 5.3 Example: Arabic RTL “full page” chat (from `use-cases/ksu-agent`)
|
|
174
|
+
|
|
175
|
+
- Uses `dir="rtl"` + `language="ar"`
|
|
176
|
+
- Uses `fullPageChat`
|
|
177
|
+
- Provides `presetPrompts` in Arabic
|
|
178
|
+
- Provides `HsafaUI` to render specialized tool results
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
# 6) Headless mode: `useHsafaAgent`
|
|
183
|
+
|
|
184
|
+
Source: `sdk/src/hooks/useHsafaAgent.ts`
|
|
185
|
+
|
|
186
|
+
## 6.1 When to use headless
|
|
187
|
+
|
|
188
|
+
Choose headless mode when you need:
|
|
189
|
+
- full control over layout and component library
|
|
190
|
+
- custom message presentation (timeline, cards, split panes)
|
|
191
|
+
- deep integration with app state / routing
|
|
192
|
+
|
|
193
|
+
## 6.2 What it returns
|
|
194
|
+
|
|
195
|
+
`useHsafaAgent(config)` returns an API with:
|
|
196
|
+
- **state**: `messages`, `input`, `status`, `isLoading`, `error`, `chatId`
|
|
197
|
+
- **actions**: `setInput`, `sendMessage`, `stop`, `newChat`, `setMessages`, `setChatId`
|
|
198
|
+
- **tool/UI wiring**: `tools`, `uiComponents`, `onUISuccess`, `onUIError`, `cleanupForms`
|
|
199
|
+
|
|
200
|
+
## 6.3 The transport: `/api/run/:agentId` and `templateParams`
|
|
201
|
+
|
|
202
|
+
The SDK uses `DefaultChatTransport` (from the `ai` package).
|
|
203
|
+
|
|
204
|
+
`createHsafaTransport(baseUrl, agentId, chatId, templateParams)`:
|
|
205
|
+
- posts to `${baseUrl}/api/run/${agentId}`
|
|
206
|
+
- merges `{ ...templateParams, ...body, chatId }` into the request
|
|
207
|
+
|
|
208
|
+
Practical uses for `templateParams`:
|
|
209
|
+
- pass tenant context
|
|
210
|
+
- pass feature flags
|
|
211
|
+
- pass a document id or workspace id
|
|
212
|
+
|
|
213
|
+
---
|
|
214
|
+
|
|
215
|
+
# 7) Tools: client-side tool execution
|
|
216
|
+
|
|
217
|
+
## 7.1 Tool types
|
|
218
|
+
|
|
219
|
+
`HsafaTool` (from `sdk/src/types/chat.ts`):
|
|
220
|
+
- `async (input) => output`
|
|
221
|
+
- or `{ tool: async (input) => output, executeEachToken?: boolean }`
|
|
222
|
+
|
|
223
|
+
## 7.2 `executeEachToken` (streaming-safe tools)
|
|
224
|
+
|
|
225
|
+
If `executeEachToken: true`, the tool may run multiple times as the model streams partial tool inputs.
|
|
226
|
+
|
|
227
|
+
This is used to power:
|
|
228
|
+
- progressive UI feedback (“building component…”, “tool input streaming…”) as seen in the dashboard builder
|
|
229
|
+
- streaming-friendly “patch/merge” tools
|
|
230
|
+
|
|
231
|
+
**Use-case example** (dashboard builder):
|
|
232
|
+
- `create_component` uses `executeEachToken: true`
|
|
233
|
+
- intermediate calls update `componentsUnderLoading` to show placeholders while the model is still deciding
|
|
234
|
+
|
|
235
|
+
## 7.3 Designing tools (recommended)
|
|
236
|
+
|
|
237
|
+
- Make tools **idempotent** or **merge-based** when streaming.
|
|
238
|
+
- Validate input strictly and return structured errors.
|
|
239
|
+
- Return **human-readable** `message` + structured `data`.
|
|
240
|
+
- Keep side-effects explicit (avoid hidden mutations).
|
|
241
|
+
|
|
242
|
+
---
|
|
243
|
+
|
|
244
|
+
# 8) Tool UIs: rendering tool calls with `HsafaUI`
|
|
245
|
+
|
|
246
|
+
## 8.1 How it works
|
|
247
|
+
|
|
248
|
+
- The assistant emits tool parts such as `tool-<name>` or `tool-call`.
|
|
249
|
+
- `AssistantMassage` checks `HsafaUI[toolName]`.
|
|
250
|
+
- If present, it renders your UI component.
|
|
251
|
+
|
|
252
|
+
Source: `sdk/src/components/hsafa-chat/AssistantMassage.tsx`
|
|
253
|
+
|
|
254
|
+
## 8.2 Props your tool UI receives
|
|
255
|
+
|
|
256
|
+
Your component receives (at minimum):
|
|
257
|
+
- `toolName: string`
|
|
258
|
+
- `toolCallId: string`
|
|
259
|
+
- `input: any`
|
|
260
|
+
- `output: any`
|
|
261
|
+
- `status?: string` (`input-streaming`, `input-available`, `output-available`, ...)
|
|
262
|
+
- `addToolResult?: (payload) => void`
|
|
263
|
+
|
|
264
|
+
## 8.3 Error handling and reporting
|
|
265
|
+
|
|
266
|
+
Tool UIs render inside `UIErrorBoundary`:
|
|
267
|
+
- errors are caught and shown to the user
|
|
268
|
+
- `onUIError(toolCallId, toolName, error)` is invoked
|
|
269
|
+
- on successful mount, `onUISuccess(toolCallId, toolName)` is invoked
|
|
270
|
+
|
|
271
|
+
This is important if you want to:
|
|
272
|
+
- notify the agent that the UI rendered (continuation)
|
|
273
|
+
- notify the agent that the UI failed (fallback to text)
|
|
274
|
+
|
|
275
|
+
---
|
|
276
|
+
|
|
277
|
+
# 9) Attachments: `useFileUpload` and `/api/uploads`
|
|
278
|
+
|
|
279
|
+
## 9.1 Endpoint
|
|
280
|
+
|
|
281
|
+
The SDK expects:
|
|
282
|
+
- `POST {baseUrl}/api/uploads` returning JSON `{ id, name, url, mimeType, size }`
|
|
283
|
+
|
|
284
|
+
## 9.2 File size
|
|
285
|
+
|
|
286
|
+
The SDK enforces a max file size (25MB) in the client.
|
|
287
|
+
|
|
288
|
+
---
|
|
289
|
+
|
|
290
|
+
# 10) Persistence: `useChatStorage`
|
|
291
|
+
|
|
292
|
+
Source: `sdk/src/hooks/useChatStorage.ts` + `sdk/src/utils/chat-storage.ts`
|
|
293
|
+
|
|
294
|
+
- Persists chat index + chat messages in `localStorage`
|
|
295
|
+
- Namespaced by `agentId` using keys with prefix `hsafaChat_${agentId}`
|
|
296
|
+
|
|
297
|
+
This is how `HsafaChat` provides chat history without server-side storage.
|
|
298
|
+
|
|
299
|
+
---
|
|
300
|
+
|
|
301
|
+
# 11) Editing: `useMessageEditor`
|
|
302
|
+
|
|
303
|
+
Source: `sdk/src/hooks/useMessageEditor.ts`
|
|
304
|
+
|
|
305
|
+
Implements:
|
|
306
|
+
- editing a prior user message
|
|
307
|
+
- truncating subsequent messages
|
|
308
|
+
- resending the edited message to regenerate assistant responses
|
|
309
|
+
|
|
310
|
+
Important constraints:
|
|
311
|
+
- editing is blocked while `isLoading`
|
|
312
|
+
- attachment upload during edit requires `baseUrl` (provider or explicit)
|
|
313
|
+
|
|
314
|
+
---
|
|
315
|
+
|
|
316
|
+
# 12) Markdown rendering: `XMarkdownRenderer`
|
|
317
|
+
|
|
318
|
+
Source: `sdk/src/components/XMarkdownRenderer.tsx`
|
|
319
|
+
|
|
320
|
+
- Uses `@ant-design/x-markdown`
|
|
321
|
+
- Supports streaming animations
|
|
322
|
+
- RTL fixes are applied so Arabic works well
|
|
323
|
+
|
|
324
|
+
Practical usage:
|
|
325
|
+
- You can use it inside custom message UIs, not only inside the built-in chat.
|
|
326
|
+
|
|
327
|
+
---
|
|
328
|
+
|
|
329
|
+
# 13) Web-controller tools (built-in)
|
|
330
|
+
|
|
331
|
+
Exported from `sdk/src/components/web-controler/*` and included as built-in tools:
|
|
332
|
+
- `getDomComponents`
|
|
333
|
+
- `controlCursor`
|
|
334
|
+
- `fillActiveInput`
|
|
335
|
+
- `requestInput` (special: renders an inline form)
|
|
336
|
+
|
|
337
|
+
These enable “agent controls the UI” demos and automation-like flows.
|
|
338
|
+
|
|
339
|
+
---
|
|
340
|
+
|
|
341
|
+
# 14) Patterns from `use-cases/` (real-world techniques)
|
|
342
|
+
|
|
343
|
+
This section summarizes *how you used the SDK* and what to copy.
|
|
344
|
+
|
|
345
|
+
## 14.1 Pattern: split-screen builder + always-open chat
|
|
346
|
+
|
|
347
|
+
Used in `rafed-hack` pages:
|
|
348
|
+
- left/main pane renders your artifact (presentation, infographic, map, dashboard)
|
|
349
|
+
- the chat is always open and acts as the control plane
|
|
350
|
+
|
|
351
|
+
Benefits:
|
|
352
|
+
- user sees changes immediately
|
|
353
|
+
- tool calls become an “execution log”
|
|
354
|
+
|
|
355
|
+
## 14.2 Pattern: custom `HsafaUI` for “tool call cards”
|
|
356
|
+
|
|
357
|
+
You create a mapping like:
|
|
358
|
+
- `set_presentation_slides` → shows a status card
|
|
359
|
+
- `imageGenerator` → shows a loading card until `imageUrl` appears
|
|
360
|
+
- `set_map_config` / `update_map_config` / `read_map_config` → shows map config state
|
|
361
|
+
|
|
362
|
+
Benefits:
|
|
363
|
+
- tool calls feel like product UI, not debug logs
|
|
364
|
+
- streaming tool status is translated into user-friendly states
|
|
365
|
+
|
|
366
|
+
## 14.3 Pattern: robust input parsing
|
|
367
|
+
|
|
368
|
+
In tools (`deckglMapTools.ts`, dashboard tools), you apply:
|
|
369
|
+
- `safeParseJSON` to accept either JSON string or object
|
|
370
|
+
- `asRecord`/guards to avoid runtime crashes
|
|
371
|
+
|
|
372
|
+
This is essential because LLM tool inputs are often:
|
|
373
|
+
- strings
|
|
374
|
+
- partially streamed
|
|
375
|
+
- missing optional fields
|
|
376
|
+
|
|
377
|
+
## 14.4 Pattern: tool validation + warnings
|
|
378
|
+
|
|
379
|
+
In `deckglMapTools.ts` you:
|
|
380
|
+
- validate layer queries (optional `validate` flag)
|
|
381
|
+
- produce warnings for unsupported style fields
|
|
382
|
+
|
|
383
|
+
This is a professional technique:
|
|
384
|
+
- it helps the model self-correct
|
|
385
|
+
- it helps the user trust results
|
|
386
|
+
|
|
387
|
+
## 14.5 Pattern: streaming-friendly UX (`executeEachToken`)
|
|
388
|
+
|
|
389
|
+
In `DashboardBuilderTools.tsx`:
|
|
390
|
+
- `create_component.executeEachToken = true`
|
|
391
|
+
- intermediate `toolCallNumber` updates `componentsUnderLoading`
|
|
392
|
+
|
|
393
|
+
This produces “AI is building…” UI without waiting for the final tool output.
|
|
394
|
+
|
|
395
|
+
---
|
|
396
|
+
|
|
397
|
+
# 15) Troubleshooting
|
|
398
|
+
|
|
399
|
+
## 15.1 Chat does not stream
|
|
400
|
+
|
|
401
|
+
- Confirm your server endpoint is `POST /api/run/:agentId` and streams NDJSON.
|
|
402
|
+
- Confirm CORS if `baseUrl` is cross-origin.
|
|
403
|
+
|
|
404
|
+
## 15.2 Uploads fail
|
|
405
|
+
|
|
406
|
+
- Confirm endpoint `POST /api/uploads` exists.
|
|
407
|
+
- Confirm returned JSON includes `url`.
|
|
408
|
+
|
|
409
|
+
## 15.3 My `HsafaUI` component never renders
|
|
410
|
+
|
|
411
|
+
- Ensure your tool name matches exactly (snake_case vs camelCase matters).
|
|
412
|
+
- Check `AssistantMassage.tsx` tool-name normalization rules.
|
|
413
|
+
|
|
414
|
+
## 15.4 “Dynamic Pages” docs mismatch
|
|
415
|
+
|
|
416
|
+
Some docs reference a dynamic-page subsystem that is not present in the current `sdk/src`. Treat those docs as historical or planned work.
|
|
417
|
+
|
|
418
|
+
---
|
|
419
|
+
|
|
420
|
+
# 16) Recommended doc map
|
|
421
|
+
|
|
422
|
+
- Start here:
|
|
423
|
+
- `sdk/docs/handbook/01-Quickstart.md`
|
|
424
|
+
- `sdk/docs/handbook/02-Architecture.md`
|
|
425
|
+
- For headless:
|
|
426
|
+
- `sdk/docs/HEADLESS_QUICK_REFERENCE.md`
|
|
427
|
+
- `sdk/docs/HEADLESS_USAGE.md`
|
|
428
|
+
- For custom tool UIs:
|
|
429
|
+
- `sdk/docs/CUSTOM_UI_EXAMPLES.md`
|
|
430
|
+
- For Markdown:
|
|
431
|
+
- `sdk/docs/XMARKDOWN_USAGE.md`
|
|
432
|
+
|
|
433
|
+
---
|
|
434
|
+
|
|
435
|
+
# 17) Appendix: current public API surface
|
|
436
|
+
|
|
437
|
+
Always verify the current exported surface here:
|
|
438
|
+
- `sdk/src/index.ts`
|
|
439
|
+
- `sdk/dist/index.d.ts`
|
|
440
|
+
|
|
441
|
+
This avoids drift between code and generated docs.
|