@ray-js/t-agent 0.0.5-beta-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/LICENSE.md +21 -0
- package/README-zh_CN.md +1350 -0
- package/README.md +1345 -0
- package/dist/chat/ChatAgent.d.ts +157 -0
- package/dist/chat/ChatAgent.js +276 -0
- package/dist/chat/ChatBubbleTile.d.ts +32 -0
- package/dist/chat/ChatBubbleTile.js +104 -0
- package/dist/chat/ChatMessage.d.ts +32 -0
- package/dist/chat/ChatMessage.js +173 -0
- package/dist/chat/ChatSession.d.ts +27 -0
- package/dist/chat/ChatSession.js +89 -0
- package/dist/chat/ChatTile.d.ts +26 -0
- package/dist/chat/ChatTile.js +75 -0
- package/dist/chat/Emitter.d.ts +28 -0
- package/dist/chat/Emitter.js +61 -0
- package/dist/chat/Logger.d.ts +16 -0
- package/dist/chat/Logger.js +90 -0
- package/dist/chat/StreamResponse.d.ts +22 -0
- package/dist/chat/StreamResponse.js +47 -0
- package/dist/chat/createChatAgent.d.ts +6 -0
- package/dist/chat/createChatAgent.js +21 -0
- package/dist/chat/index.d.ts +12 -0
- package/dist/chat/index.js +12 -0
- package/dist/chat/types.d.ts +177 -0
- package/dist/chat/types.js +29 -0
- package/dist/chat/utils.d.ts +32 -0
- package/dist/chat/utils.js +207 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/plugins/debug.d.ts +10 -0
- package/dist/plugins/debug.js +33 -0
- package/dist/plugins/ui.d.ts +28 -0
- package/dist/plugins/ui.js +44 -0
- package/package.json +30 -0
package/README.md
ADDED
|
@@ -0,0 +1,1345 @@
|
|
|
1
|
+
English | [简体中文](./README-zh_CN.md)
|
|
2
|
+
|
|
3
|
+
# AI Intelligent Agent SDK
|
|
4
|
+
|
|
5
|
+
## Installation (ray mini-program)
|
|
6
|
+
|
|
7
|
+
```shell
|
|
8
|
+
yarn add @ray-js/t-agent @ray-js/t-agent-plugin-assistant @ray-js/t-agent-ui-ray
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
> Ensure that the versions of `@ray-js/t-agent`, `@ray-js/t-agent-plugin-assistant`, and `@ray-js/t-agent-ui-ray` are consistent.
|
|
12
|
+
|
|
13
|
+
## Mini-Program Kit Requirements
|
|
14
|
+
|
|
15
|
+
```json
|
|
16
|
+
{
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"BaseKit": "3.12.0",
|
|
19
|
+
"BizKit": "4.10.0",
|
|
20
|
+
"DeviceKit": "4.6.1",
|
|
21
|
+
"HomeKit": "3.4.0",
|
|
22
|
+
"MiniKit": "3.12.1"
|
|
23
|
+
},
|
|
24
|
+
"baseversion": "2.21.10"
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## package.json Dependency Requirements
|
|
29
|
+
|
|
30
|
+
```json
|
|
31
|
+
{
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"@ray-js/ray": ">=1.6.8"
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Development
|
|
39
|
+
|
|
40
|
+
```shell
|
|
41
|
+
# Develop the SDK
|
|
42
|
+
yarn run dev
|
|
43
|
+
|
|
44
|
+
# Develop the template mini-program
|
|
45
|
+
yarn run miniapp
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Usage Example
|
|
49
|
+
|
|
50
|
+
Implementing a chat page using ray UI
|
|
51
|
+
|
|
52
|
+

|
|
53
|
+
|
|
54
|
+
```tsx
|
|
55
|
+
// ChatPage.tsx
|
|
56
|
+
import React from 'react';
|
|
57
|
+
import { View } from '@ray-js/components';
|
|
58
|
+
import { createChatAgent, withDebug, withUI } from '@ray-js/t-agent';
|
|
59
|
+
import { ChatContainer, MessageInput, MessageList } from '@ray-js/t-agent-ui-ray';
|
|
60
|
+
import { withAssistant, withBuildIn } from '@ray-js/t-agent-plugin-assistant';
|
|
61
|
+
|
|
62
|
+
const createAgent = () => {
|
|
63
|
+
// The order of applying plugins matters
|
|
64
|
+
const agent = createChatAgent(
|
|
65
|
+
withUI(), // The withUI plugin provides default UI behavior, essential
|
|
66
|
+
withAssistant({
|
|
67
|
+
// Connects to the mini-program AI agent platform, essential in mini-program
|
|
68
|
+
channel: '', // Enter your agent ID
|
|
69
|
+
multiModal: false, // Whether to enable multi-modal capabilities
|
|
70
|
+
}),
|
|
71
|
+
withDebug(), // Enables logging to the console
|
|
72
|
+
withBuildIn() // Provides built-in features
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
// Lifecycle hooks for custom behavior registration
|
|
76
|
+
const { onChatStart, createMessage, onChatResume, onError, onInputBlocksPush, session } = agent;
|
|
77
|
+
|
|
78
|
+
// Initialize chat with a message
|
|
79
|
+
onChatStart(async result => {
|
|
80
|
+
const hello = createMessage({
|
|
81
|
+
role: 'assistant',
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
hello.bubble.setText('Hello, world!');
|
|
85
|
+
result.messages.push(hello);
|
|
86
|
+
// Persist message for display upon next entry
|
|
87
|
+
await hello.persist('createText');
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
// Message upon chat resume
|
|
91
|
+
onChatResume(async result => {
|
|
92
|
+
const welcomeBack = createMessage({
|
|
93
|
+
role: 'assistant',
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
welcomeBack.bubble.setText('Welcome back');
|
|
97
|
+
result.messages.push(welcomeBack);
|
|
98
|
+
await welcomeBack.persist('createText');
|
|
99
|
+
});
|
|
100
|
+
return agent;
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
export default function ChatPage() {
|
|
104
|
+
return (
|
|
105
|
+
<View style={{ height: '100vh' }}>
|
|
106
|
+
<ChatContainer createAgent={createAgent}>
|
|
107
|
+
<MessageList />
|
|
108
|
+
<MessageInput />
|
|
109
|
+
</ChatContainer>
|
|
110
|
+
</View>
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
# t-agent
|
|
116
|
+
|
|
117
|
+
The t-agent package, crafted in TypeScript, is a dialogue agent SDK for building feature-rich dialogue interactions, supporting plugins to enhance functionality. As a pure SDK, it doesn’t contain UI components, allowing flexibility to work with any framework.
|
|
118
|
+
|
|
119
|
+
## Basic Concepts
|
|
120
|
+
|
|
121
|
+

|
|
122
|
+
|
|
123
|
+
### ChatAgent Dialogue Agent
|
|
124
|
+
|
|
125
|
+
The core class handles the dialogue lifecycle, message creation, persistence, and supports plugins and hooks to extend functionality.
|
|
126
|
+
|
|
127
|
+
Create a ChatAgent instance using `createChatAgent`:
|
|
128
|
+
|
|
129
|
+
```tsx
|
|
130
|
+
import { createChatAgent } from '@ray-js/t-agent';
|
|
131
|
+
const createAgent = () => {
|
|
132
|
+
/* Apply plugins in the createChatAgent function. Plugin order matters. */
|
|
133
|
+
const agent = createChatAgent();
|
|
134
|
+
|
|
135
|
+
return agent;
|
|
136
|
+
};
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Core Attributes:
|
|
140
|
+
|
|
141
|
+
- `agent.session` ChatSession container for session data
|
|
142
|
+
- `agent.plugins` Contains applied plugins' methods and hooks
|
|
143
|
+
|
|
144
|
+
Core Methods:
|
|
145
|
+
|
|
146
|
+
- `agent.start()` Activates the agent
|
|
147
|
+
- `agent.dispose()` Deactivates the agent
|
|
148
|
+
- `agent.pushInputBlocks(blocks, signal)` Pushes user messages to ChatAgent
|
|
149
|
+
- `agent.createMessage(data)` Creates a message linked to the agent
|
|
150
|
+
- `agent.emitTileEvent(tileId: string, payload: any)` Emits tile events
|
|
151
|
+
- `agent.removeMessage(messageId: string)` Removes messages
|
|
152
|
+
- `agent.flushStreamToShow(message: ChatMessage, response: StreamResponse, composer: ComposeHandler)` Handles stream updates
|
|
153
|
+
|
|
154
|
+
### Hooks Mechanism
|
|
155
|
+
|
|
156
|
+
ChatAgent implements a framework and data structure. Behavioral details are executed using a hook-driven, event-model approach by registering specific callbacks.
|
|
157
|
+
|
|
158
|
+
```tsx
|
|
159
|
+
import { createChatAgent } from '@ray-js/t-agent';
|
|
160
|
+
const createAgent = () => {
|
|
161
|
+
const agent = createChatAgent();
|
|
162
|
+
|
|
163
|
+
const { onChatStart } = agent;
|
|
164
|
+
|
|
165
|
+
// onChatStart Hook triggers on dialogue start
|
|
166
|
+
onChatStart(result => {
|
|
167
|
+
console.log('Chat start', result);
|
|
168
|
+
});
|
|
169
|
+
return agent;
|
|
170
|
+
};
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
ChatAgent's Central Hooks and Parameters:
|
|
174
|
+
|
|
175
|
+
- `agent.onAgentStart` Agent initialization
|
|
176
|
+
- `agent.onChatStart` Triggered at dialogue initiation
|
|
177
|
+
- `result.messages` Initializes message lists
|
|
178
|
+
- `agent.onChatResume` Triggered on dialogue continuation
|
|
179
|
+
- `result.messages` Restored message lists
|
|
180
|
+
- `agent.onMessageListInit` On message list initialization
|
|
181
|
+
- `result.messages` Rendering message list
|
|
182
|
+
- `agent.onInputBlocksPush` On message block input
|
|
183
|
+
- `blocks` Input message blocks
|
|
184
|
+
- `signal` Interrupt signal
|
|
185
|
+
- `agent.onMessageChange` On message updates
|
|
186
|
+
- `type` Update types: `show`, `update`, `remove`
|
|
187
|
+
- `message` Updated message instance
|
|
188
|
+
- `agent.onMessagePersist` At message persistence
|
|
189
|
+
- `payload` Persistence parameters
|
|
190
|
+
- `message` The message to persist
|
|
191
|
+
- `agent.onTileEvent` After message persistence
|
|
192
|
+
- `tile` Event-triggering tile
|
|
193
|
+
- `payload` Event data
|
|
194
|
+
- `agent.onAgentDispose` On agent disposal
|
|
195
|
+
- `agent.onUserAbort` On user interruption
|
|
196
|
+
- `reason` Interruption reason
|
|
197
|
+
- `agent.onError` On error occurrence
|
|
198
|
+
- `error` Error details
|
|
199
|
+
|
|
200
|
+
These hooks accept a callback parameter, invoked upon their criteria. Callbacks can be synchronous or asynchronous. To modify results, adjust the result parameter or message object directly.
|
|
201
|
+
|
|
202
|
+
### ChatSession Session Container
|
|
203
|
+
|
|
204
|
+
ChatSession holds message lists, session-related data along with intelligent interactions, generated alongside ChatAgent.
|
|
205
|
+
|
|
206
|
+
Main Properties:
|
|
207
|
+
|
|
208
|
+
- `session.messages` List of messages
|
|
209
|
+
- `session.sessionId` Unique session ID
|
|
210
|
+
- `session.isNewChat` To distinguish fresh or ongoing chats
|
|
211
|
+
|
|
212
|
+
Principal Methods:
|
|
213
|
+
|
|
214
|
+
- `session.set` Assign session data (any type)
|
|
215
|
+
- `session.get` Retrieve session data
|
|
216
|
+
- `session.getData` Fetch session data in object format
|
|
217
|
+
- `session.getLatestMessage` Access the latest message
|
|
218
|
+
|
|
219
|
+
Hook:
|
|
220
|
+
|
|
221
|
+
- `session.onChange` Register for session data updates
|
|
222
|
+
|
|
223
|
+
### ChatMessage Dialogue Message
|
|
224
|
+
|
|
225
|
+
An abstraction representing each dialogue's content, status with utility methods for message control. Composed of multiple ChatTiles for varied display elements.
|
|
226
|
+
|
|
227
|
+
Creating a Message with `createMessage`:
|
|
228
|
+
|
|
229
|
+
```tsx
|
|
230
|
+
const createAgent = () => {
|
|
231
|
+
const agent = createChatAgent();
|
|
232
|
+
|
|
233
|
+
const { createMessage, onChatStart } = agent;
|
|
234
|
+
|
|
235
|
+
// Initialize chat with an assistant's message
|
|
236
|
+
onChatStart(async result => {
|
|
237
|
+
const message = createMessage({
|
|
238
|
+
role: 'assistant',
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
// Quickly create text bubble
|
|
242
|
+
message.bubble.setText('Hello!');
|
|
243
|
+
result.messages.push(message);
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
return agent;
|
|
247
|
+
};
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
Core Properties:
|
|
251
|
+
|
|
252
|
+
- `message.id` Unique message identifier
|
|
253
|
+
- `message.role` Either assistant or user
|
|
254
|
+
- `message.tiles` List of message tiles
|
|
255
|
+
- `message.status` Message state, `ChatMessageStatus` enum: `START`, `UPDATING`, `FINISH`
|
|
256
|
+
- `message.meta` Supplementary data
|
|
257
|
+
- `message.isShow` Display status
|
|
258
|
+
- `message.bubble` Shortcut for bubble tile
|
|
259
|
+
|
|
260
|
+
Key Methods:
|
|
261
|
+
|
|
262
|
+
- `message.show` Display message
|
|
263
|
+
- `message.update` Refresh current message
|
|
264
|
+
- `message.remove` Delete current message
|
|
265
|
+
- `message.persist` Save message
|
|
266
|
+
- `message.addTile` Add a tile
|
|
267
|
+
- `message.removeTile` Delete a tile
|
|
268
|
+
- `message.setTilesLocked` Lock/unlock all tiles
|
|
269
|
+
- `message.set` Configure message attributes
|
|
270
|
+
- `message.setMetaValue` Assign meta attribute by key
|
|
271
|
+
- `message.deleteMetaValue` Remove meta attribute
|
|
272
|
+
- `message.setMeta` Comprehensive meta object assignment
|
|
273
|
+
- `message.findTileByType` Locate tile by type
|
|
274
|
+
|
|
275
|
+
#### ChatTile Dialogue Message Block
|
|
276
|
+
|
|
277
|
+
ChatTiles render specific content types fairly like text, images, and cards.
|
|
278
|
+
|
|
279
|
+
Adding a Tile with `addTile`:
|
|
280
|
+
|
|
281
|
+
```tsx
|
|
282
|
+
const message = createMessage({
|
|
283
|
+
role: 'assistant',
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
// Introduce an image tile
|
|
287
|
+
message.addTile('image', {
|
|
288
|
+
src: '/image.jpg',
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
await message.show();
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
Chief Attributes of ChatTile:
|
|
295
|
+
|
|
296
|
+
- `tile.id` Tile's identifier
|
|
297
|
+
- `tile.type` Type of tile
|
|
298
|
+
- `tile.data` Content held by tile
|
|
299
|
+
- `tile.children` Nested tiles
|
|
300
|
+
- `tile.locked` Tile lock status
|
|
301
|
+
- `tile.fallback` Alternative content for non-displayed tile
|
|
302
|
+
- `tile.message` Parent message of tile
|
|
303
|
+
|
|
304
|
+
Primary Methods of ChatTile:
|
|
305
|
+
|
|
306
|
+
- `tile.update` Equivalent to `tile.message.update`
|
|
307
|
+
- `tile.show` Synonymous with `tile.message.show`
|
|
308
|
+
- `tile.setLocked` Adjust tile's lock status
|
|
309
|
+
- `tile.addTile` Add a sub-tile
|
|
310
|
+
- `tile.setData` Update tile content
|
|
311
|
+
- `tile.setFallback` Update fallback content
|
|
312
|
+
- `tile.findByType` Locate sub-tile by type
|
|
313
|
+
|
|
314
|
+
Accessing `message.bubble` enables swift bubble tile addition:
|
|
315
|
+
|
|
316
|
+
```tsx
|
|
317
|
+
const message = createMessage({
|
|
318
|
+
role: 'assistant',
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
message.bubble.setText('Hello, world!');
|
|
322
|
+
// Equivalent to
|
|
323
|
+
message.addTile('bubble', {}).addTile('text', { text: 'Hello, world!' });
|
|
324
|
+
|
|
325
|
+
await message.show();
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
For bubble messages, additional properties and methods include:
|
|
329
|
+
|
|
330
|
+
- `message.bubble.text` Reads bubble text
|
|
331
|
+
- `message.bubble.setText` Assigns bubble text
|
|
332
|
+
- `message.bubble.isMarkdown` Determines markdown formatting
|
|
333
|
+
- `message.bubble.setIsMarkdown` Sets markdown format
|
|
334
|
+
- `message.bubble.status` Indicates bubble status
|
|
335
|
+
- `message.bubble.setStatus` Defines bubble status
|
|
336
|
+
- `message.bubble.info` Relays bubble info
|
|
337
|
+
- `message.bubble.setInfo` Configures bubble info
|
|
338
|
+
- `message.bubble.initWithInputBlocks` Initializes bubble with input blocks
|
|
339
|
+
|
|
340
|
+
### Lifecycle
|
|
341
|
+
|
|
342
|
+
ChatAgent phases trigger hooks enabling developers to integrate custom behavior efficiently. Below is the lifecycle depiction:
|
|
343
|
+
|
|
344
|
+
```mermaid
|
|
345
|
+
sequenceDiagram
|
|
346
|
+
participant AI as Backend(AI)
|
|
347
|
+
participant A as ChatAgent
|
|
348
|
+
participant UI as UI Interface
|
|
349
|
+
actor User as User
|
|
350
|
+
|
|
351
|
+
User ->> UI: Open chat interface
|
|
352
|
+
UI ->> UI: Create Agent object agent
|
|
353
|
+
UI ->> A: agent.start() Start Agent
|
|
354
|
+
rect rgba(255,255,0,0.1)
|
|
355
|
+
note over A: Hook: onAgentStart
|
|
356
|
+
A ->> AI: Check if new session
|
|
357
|
+
AI ->> A: Return
|
|
358
|
+
end
|
|
359
|
+
alt Is new session
|
|
360
|
+
A ->> AI: Start a new session
|
|
361
|
+
note over A: Hook: onChatStart
|
|
362
|
+
else Is resume session
|
|
363
|
+
A ->> AI: Read session history
|
|
364
|
+
AI ->> A: Return history messages
|
|
365
|
+
note over A: Hook: onChatResume
|
|
366
|
+
end
|
|
367
|
+
rect rgba(255,255,0,0.1)
|
|
368
|
+
note over A: Hook: onMessageListInit
|
|
369
|
+
A ->> UI: Assemble messages to display
|
|
370
|
+
UI ->> User: Display messages
|
|
371
|
+
end
|
|
372
|
+
opt Conversation
|
|
373
|
+
User ->> UI: Enter and send message
|
|
374
|
+
UI ->> A: agent.pushInputBlocks()
|
|
375
|
+
rect rgba(255,255,0,0.1)
|
|
376
|
+
note over A: Hook: onInputBlocksPush
|
|
377
|
+
A ->> A: Create Message object msg\nSet msg as user's input text\nmsg.show()
|
|
378
|
+
note over A: Hook: onMessageChange show
|
|
379
|
+
A -->> UI: Update interface
|
|
380
|
+
UI -->> User: Display new message
|
|
381
|
+
A ->> A: Create Message object respMsg\nSet respMsg as "loading"\nrespMsg.show()
|
|
382
|
+
note over A: Hook: onMessageChange show
|
|
383
|
+
A -->> UI: Update interface
|
|
384
|
+
UI -->> User: Display loading message
|
|
385
|
+
A ->> AI: Invoke AI
|
|
386
|
+
AI ->> A: Return message stream
|
|
387
|
+
loop Streaming messages
|
|
388
|
+
AI ->> A: Message packet
|
|
389
|
+
A ->> A: Update respMsg data\nrespMsg.update()
|
|
390
|
+
note over A: Hook: onMessageChange update
|
|
391
|
+
A -->> UI: Update interface
|
|
392
|
+
UI -->> User: Display typing effect/card
|
|
393
|
+
end
|
|
394
|
+
end
|
|
395
|
+
end
|
|
396
|
+
|
|
397
|
+
opt Operate message action points (e.g., single selection)
|
|
398
|
+
User ->> UI: Select option
|
|
399
|
+
UI ->> A: agent.emitTileEvent()
|
|
400
|
+
rect rgba(255,255,0,0.1)
|
|
401
|
+
note over A: Hook: onTileEvent
|
|
402
|
+
A ->> A: Set msg status\nmsg.persist()
|
|
403
|
+
note over A: Hook: onMessagePersist
|
|
404
|
+
A ->> AI: Persist message
|
|
405
|
+
AI ->> A: Return result
|
|
406
|
+
A ->> A: msg.update()
|
|
407
|
+
note over A: Hook: onMessageChange update
|
|
408
|
+
A ->> UI: Update interface
|
|
409
|
+
UI ->> User: Message greyed out, highlight selection
|
|
410
|
+
end
|
|
411
|
+
end
|
|
412
|
+
opt Delete message
|
|
413
|
+
User ->> UI: Delete message
|
|
414
|
+
UI ->> A: agent.removeMessage()
|
|
415
|
+
rect rgba(255,255,0,0.1)
|
|
416
|
+
A ->> A: msg.remove()
|
|
417
|
+
A ->> AI: Mark message deleted
|
|
418
|
+
note over A: Hook: onMessageChange remove
|
|
419
|
+
A ->> UI: Update interface
|
|
420
|
+
UI ->> User: Message disappears
|
|
421
|
+
end
|
|
422
|
+
end
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
### Plugin Mechanism
|
|
426
|
+
|
|
427
|
+
Plugins are implemented based on the above Hook mechanism and can enhance the functionality of chat agents, such as interfacing with AI platforms, providing UI interfaces, etc. Plugins can also expose methods and properties for developers to use.
|
|
428
|
+
|
|
429
|
+
```tsx
|
|
430
|
+
import { createChatAgent, withUI } from '@ray-js/t-agent';
|
|
431
|
+
const createAgent = () => {
|
|
432
|
+
const agent = createChatAgent(
|
|
433
|
+
withUI() // withUI plugin provides methods for rendering UI
|
|
434
|
+
);
|
|
435
|
+
|
|
436
|
+
return agent;
|
|
437
|
+
};
|
|
438
|
+
|
|
439
|
+
// ScrollToBottom.tsx
|
|
440
|
+
import React from 'react';
|
|
441
|
+
import { Button } from '@ray-js/components';
|
|
442
|
+
import { useChatAgent } from '@ray-js/t-agent-ui-ray';
|
|
443
|
+
const ScrollToBottom = () => {
|
|
444
|
+
const agent = useChatAgent();
|
|
445
|
+
|
|
446
|
+
const scroll = () => {
|
|
447
|
+
// Use methods exposed by the plugin
|
|
448
|
+
agent.plugins.ui.emitEvent('scrollToBottom', { animation: false });
|
|
449
|
+
};
|
|
450
|
+
|
|
451
|
+
return <Button onClick={scroll}>Scroll to bottom</Button>;
|
|
452
|
+
};
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
#### Writing a Plugin
|
|
456
|
+
|
|
457
|
+
A plugin is a higher-order function that takes options and returns a function. This function receives a ChatAgent object, within which hooks can be registered.
|
|
458
|
+
|
|
459
|
+
```tsx
|
|
460
|
+
import { ChatAgent, createHooks, Hookable } from '@ray-js/t-agent';
|
|
461
|
+
|
|
462
|
+
// Implementing MyPlugin
|
|
463
|
+
export type MyPlugin = GetChatPluginHandler<typeof withMyPlugin>;
|
|
464
|
+
|
|
465
|
+
export const withMyPlugin = (options: any) => {
|
|
466
|
+
// Create a Hookable object
|
|
467
|
+
const hooks = createHooks();
|
|
468
|
+
|
|
469
|
+
return (agent: ChatAgent) => {
|
|
470
|
+
const { onChatStart } = agent;
|
|
471
|
+
|
|
472
|
+
onChatStart(async () => {
|
|
473
|
+
console.log('Chat start');
|
|
474
|
+
// Trigger plugin hook
|
|
475
|
+
await hooks.callHook('onMyHook', 'Hello, world!');
|
|
476
|
+
});
|
|
477
|
+
|
|
478
|
+
// Expose plugin methods and hooks
|
|
479
|
+
return {
|
|
480
|
+
hooks,
|
|
481
|
+
myPlugin: {
|
|
482
|
+
// Expose a method
|
|
483
|
+
myMethod() {
|
|
484
|
+
console.log('My method');
|
|
485
|
+
},
|
|
486
|
+
// Expose a hook
|
|
487
|
+
onMyHook: fn => {
|
|
488
|
+
return hooks.hook('onMyHook', fn);
|
|
489
|
+
},
|
|
490
|
+
},
|
|
491
|
+
};
|
|
492
|
+
};
|
|
493
|
+
};
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
```tsx
|
|
497
|
+
// Using the plugin
|
|
498
|
+
import { createChatAgent } from '@ray-js/t-agent';
|
|
499
|
+
|
|
500
|
+
const createAgent = () => {
|
|
501
|
+
const agent = createChatAgent(
|
|
502
|
+
// Use the plugin
|
|
503
|
+
withMyPlugin({})
|
|
504
|
+
);
|
|
505
|
+
|
|
506
|
+
// Call methods exposed by the plugin
|
|
507
|
+
agent.plugins.myPlugin.myMethod();
|
|
508
|
+
|
|
509
|
+
// Register plugin hooks
|
|
510
|
+
agent.plugins.myPlugin.onMyHook(msg => {
|
|
511
|
+
console.log(msg);
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
return agent;
|
|
515
|
+
};
|
|
516
|
+
```
|
|
517
|
+
|
|
518
|
+
## Built-in Plugins
|
|
519
|
+
|
|
520
|
+
### withDebug
|
|
521
|
+
|
|
522
|
+
The withDebug plugin logs messages to the console for easy debugging.
|
|
523
|
+
|
|
524
|
+
```tsx
|
|
525
|
+
const agent = createChatAgent(
|
|
526
|
+
withDebug({
|
|
527
|
+
autoStart: true, // Whether to start automatically, default is true
|
|
528
|
+
})
|
|
529
|
+
);
|
|
530
|
+
|
|
531
|
+
// Start
|
|
532
|
+
agent.plugins.debug.start();
|
|
533
|
+
|
|
534
|
+
// Stop
|
|
535
|
+
agent.plugins.debug.stop();
|
|
536
|
+
```
|
|
537
|
+
|
|
538
|
+
### withUI
|
|
539
|
+
|
|
540
|
+
The withUI plugin provides default UI behaviors such as message display, message deletion, etc., and a message bus.
|
|
541
|
+
|
|
542
|
+
```tsx
|
|
543
|
+
const agent = createChatAgent(withUI());
|
|
544
|
+
|
|
545
|
+
agent.plugins.ui.emitter; // Message bus
|
|
546
|
+
|
|
547
|
+
// Scroll to bottom
|
|
548
|
+
agent.plugins.ui.emitEvent('scrollToBottom', { animation: false });
|
|
549
|
+
|
|
550
|
+
// Listen to events
|
|
551
|
+
const off = agent.plugins.ui.onEvent('scrollToBottom', payload => {
|
|
552
|
+
console.log('scroll to bottom', payload.animation);
|
|
553
|
+
});
|
|
554
|
+
|
|
555
|
+
// Unlisten to events
|
|
556
|
+
off();
|
|
557
|
+
```
|
|
558
|
+
|
|
559
|
+
The main events of the UI plugin are as follows, and you can freely register additional events you need:
|
|
560
|
+
|
|
561
|
+
- `messageListInit`: Initialize the message list
|
|
562
|
+
- `payload.messages: ChatMessageObject[]`: Message list
|
|
563
|
+
- `messageChange`: Message changes
|
|
564
|
+
- `payload.type: 'show' | 'update' | 'remove'`: Change type
|
|
565
|
+
- `payload.message: ChatMessageObject`: Message
|
|
566
|
+
- `scrollToBottom`: Scroll to bottom
|
|
567
|
+
- `payload.animation: boolean`: Whether to use animation
|
|
568
|
+
- `sendMessage`: Send message, triggering UI update
|
|
569
|
+
- `payload.blocks: InputBlock[]`: Input blocks
|
|
570
|
+
- `setInputBlocks`: Set input blocks in MessageInput
|
|
571
|
+
- `payload.blocks: InputBlock[]`: Input blocks
|
|
572
|
+
|
|
573
|
+
> Note: `ChatMessageObject` here is a message object, not a `ChatMessage` type.
|
|
574
|
+
> It contains various attributes and methods to ensure changes are made in the ChatAgent, avoiding conflicts with the UI layer.
|
|
575
|
+
|
|
576
|
+
## Included utils
|
|
577
|
+
|
|
578
|
+
### getLogger(prefix: string): Logger
|
|
579
|
+
|
|
580
|
+
Create a logger for logging messages.
|
|
581
|
+
|
|
582
|
+
```tsx
|
|
583
|
+
import { getLogger } from '@ray-js/t-agent';
|
|
584
|
+
const logger = getLogger('MyPlugin');
|
|
585
|
+
logger.debug('Hello, world!');
|
|
586
|
+
```
|
|
587
|
+
|
|
588
|
+
### Emitter
|
|
589
|
+
|
|
590
|
+
Emitter is an event bus for registering and triggering events.
|
|
591
|
+
|
|
592
|
+
```tsx
|
|
593
|
+
import { Emitter, EmitterEvent } from '@ray-js/t-agent';
|
|
594
|
+
const emitter = new Emitter();
|
|
595
|
+
|
|
596
|
+
// Register event
|
|
597
|
+
const cb = event => console.log('detail', event.detail);
|
|
598
|
+
emitter.addEventListener('event', cb);
|
|
599
|
+
|
|
600
|
+
// Trigger event
|
|
601
|
+
emitter.dispatchEvent(new EmitterEvent('event', { detail: 'Hello, world!' }));
|
|
602
|
+
|
|
603
|
+
// Remove event
|
|
604
|
+
emitter.removeEventListener('event', cb);
|
|
605
|
+
```
|
|
606
|
+
|
|
607
|
+
### StreamResponse
|
|
608
|
+
|
|
609
|
+
StreamResponse is a streaming response object for handling streaming messages.
|
|
610
|
+
|
|
611
|
+
```tsx
|
|
612
|
+
import { StreamResponse } from '@ray-js/t-agent';
|
|
613
|
+
|
|
614
|
+
const partStream = await getPartStream(); // Obtain stream data
|
|
615
|
+
const response = new StreamResponse(partStream);
|
|
616
|
+
|
|
617
|
+
const parts = response.parts();
|
|
618
|
+
for await (const part of parts) {
|
|
619
|
+
console.log('part', part);
|
|
620
|
+
}
|
|
621
|
+
```
|
|
622
|
+
|
|
623
|
+
### createHooks, Hookable
|
|
624
|
+
|
|
625
|
+
Refer to the `hookable` npm package for more details.
|
|
626
|
+
|
|
627
|
+
### isAbortError
|
|
628
|
+
|
|
629
|
+
Determine if it is an abort error.
|
|
630
|
+
|
|
631
|
+
### safeParseJSON
|
|
632
|
+
|
|
633
|
+
Safely parse JSON strings, returning `undefined` on failure.
|
|
634
|
+
|
|
635
|
+
```tsx
|
|
636
|
+
import { safeParseJSON } from '@ray-js/t-agent';
|
|
637
|
+
|
|
638
|
+
const obj = safeParseJSON<{ a: number }>('{"a": 1}');
|
|
639
|
+
|
|
640
|
+
console.log(obj.a); // 1
|
|
641
|
+
```
|
|
642
|
+
|
|
643
|
+
# t-agent-plugin-assistant
|
|
644
|
+
|
|
645
|
+
t-agent-plugin-assistant is a plugin designed for integration with applet AI agent platforms, offering capabilities to connect to these platforms.
|
|
646
|
+
|
|
647
|
+
## Installation
|
|
648
|
+
|
|
649
|
+
```shell
|
|
650
|
+
yarn add @ray-js/t-agent-plugin-assistant
|
|
651
|
+
```
|
|
652
|
+
|
|
653
|
+
## Usage
|
|
654
|
+
|
|
655
|
+
```tsx
|
|
656
|
+
import { createChatAgent, withUI } from '@ray-js/t-agent';
|
|
657
|
+
import { withAssistant, withBuildIn } from '@ray-js/t-agent-plugin-assistant';
|
|
658
|
+
|
|
659
|
+
const createAgent = () => {
|
|
660
|
+
const agent = createChatAgent(
|
|
661
|
+
withUI(), // Typically, the withUI plugin is necessary
|
|
662
|
+
withAssistant({
|
|
663
|
+
channel: 'your-channel-id', // Provide your agent ID
|
|
664
|
+
multiModal: false, // Enable multi-modal? Defaults to true
|
|
665
|
+
}),
|
|
666
|
+
withBuildIn()
|
|
667
|
+
);
|
|
668
|
+
|
|
669
|
+
return agent;
|
|
670
|
+
};
|
|
671
|
+
```
|
|
672
|
+
|
|
673
|
+
## Included Plugins
|
|
674
|
+
|
|
675
|
+
### withAssistant Plugin
|
|
676
|
+
|
|
677
|
+
This plugin facilitates communication with applet AI agent platforms.
|
|
678
|
+
|
|
679
|
+
Parameters:
|
|
680
|
+
|
|
681
|
+
- `channel`: Agent ID
|
|
682
|
+
- `multiModal`: Enable multi-modal? Defaults to true
|
|
683
|
+
- `wireInputToAssistant`: Should input blocks be passed to the agent? Defaults to true. Set to false if you plan to handle input blocks manually with the onInputBlocksPush Hook.
|
|
684
|
+
- `historySize`: Size of message history, default is 100.
|
|
685
|
+
|
|
686
|
+
Methods:
|
|
687
|
+
|
|
688
|
+
- `agent.plugins.assistant.send`: Send a message to the agent.
|
|
689
|
+
- `agent.plugins.assistant.chat`: Send a message to the agent while generating a ChatMessage object for the question and the AI's answer, updating in a streaming manner. Useful for onInputBlocksPush.
|
|
690
|
+
|
|
691
|
+
Hooks:
|
|
692
|
+
|
|
693
|
+
- `onSocketStatusChange`: Triggered when the network status changes.
|
|
694
|
+
- `onRawMessageParse`: Triggered during history message parsing, allowing for message modification in this Hook.
|
|
695
|
+
- `rawItems`: Original message data
|
|
696
|
+
- `result.messages`: Message list, typically generating one question message (`messages[0]`) and one answer message (`messages[1]`) per history record. You can modify or extend this list.
|
|
697
|
+
- `onExtensionCompose`: When parsing message extension data, this hook activates.
|
|
698
|
+
- `extension`: Extension data
|
|
699
|
+
- `responseMessage`: Response message
|
|
700
|
+
- `result.messages`: Message list
|
|
701
|
+
|
|
702
|
+
### withBuildIn Plugin
|
|
703
|
+
|
|
704
|
+
Offers built-in features like workflows, buttons, recommended actions, etc., currently under development.
|
|
705
|
+
|
|
706
|
+
### withAssistantCopyHistory Plugin
|
|
707
|
+
|
|
708
|
+
Utilizing this plugin in real machine previews automatically copies history records to the clipboard for easier debugging.
|
|
709
|
+
|
|
710
|
+
## Mock Mechanism
|
|
711
|
+
|
|
712
|
+
To facilitate development, we offer a mock mechanism allowing mock data to be used without connecting to applet AI platforms.
|
|
713
|
+
|
|
714
|
+
### Mock a ttt Interface
|
|
715
|
+
|
|
716
|
+
```tsx
|
|
717
|
+
import { mock } from '@ray-js/t-agent-plugin-assistant';
|
|
718
|
+
|
|
719
|
+
// Mock the interface for retrieving historical data
|
|
720
|
+
mock.hooks.hook('getAIAssistantGroupHistory', context => {
|
|
721
|
+
context.result = yourMockData;
|
|
722
|
+
});
|
|
723
|
+
```
|
|
724
|
+
|
|
725
|
+
### Mock AI Assistant Response
|
|
726
|
+
|
|
727
|
+
````
|
|
728
|
+
```tsx
|
|
729
|
+
import { mock } from '@ray-js/t-agent-plugin-assistant';
|
|
730
|
+
|
|
731
|
+
mock.hooks.hook('sendToAssistant', context => {
|
|
732
|
+
if (context.options.block?.includes('hello')) {
|
|
733
|
+
context.responseText = 'Hello, who are you?';
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
if (context.options.block?.includes('卡片')) {
|
|
737
|
+
context.responseText = 'This is a card example';
|
|
738
|
+
context.responseExtensions = {
|
|
739
|
+
aiCards: [
|
|
740
|
+
{
|
|
741
|
+
cardCode: 'myCard',
|
|
742
|
+
cardType: ChatCardType.CUSTOM,
|
|
743
|
+
cardData: { title: 'Card 1' },
|
|
744
|
+
},
|
|
745
|
+
{
|
|
746
|
+
cardCode: 'myCard',
|
|
747
|
+
cardType: ChatCardType.CUSTOM,
|
|
748
|
+
cardData: { title: 'Card 2' },
|
|
749
|
+
},
|
|
750
|
+
],
|
|
751
|
+
};
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
if (context.options.block?.includes('工作流')) {
|
|
755
|
+
context.responseText = 'This is a workflow';
|
|
756
|
+
context.responseExtensions = {
|
|
757
|
+
workflowAskOptions: {
|
|
758
|
+
options: [
|
|
759
|
+
{
|
|
760
|
+
name: 'Option 1',
|
|
761
|
+
value: 'Option 1',
|
|
762
|
+
},
|
|
763
|
+
{
|
|
764
|
+
name: 'Option 2',
|
|
765
|
+
value: 'Option 2',
|
|
766
|
+
},
|
|
767
|
+
{
|
|
768
|
+
name: 'Option 3',
|
|
769
|
+
value: 'Option 3',
|
|
770
|
+
},
|
|
771
|
+
],
|
|
772
|
+
},
|
|
773
|
+
};
|
|
774
|
+
}
|
|
775
|
+
});
|
|
776
|
+
````
|
|
777
|
+
|
|
778
|
+
### mock ASR Speech Recognition
|
|
779
|
+
|
|
780
|
+
```tsx
|
|
781
|
+
import { mock } from '@ray-js/t-agent-plugin-assistant';
|
|
782
|
+
|
|
783
|
+
mock.hooks.hook('asrDetection', context => {
|
|
784
|
+
context.responseText = 'Hello world!, I am a virtual assistant.';
|
|
785
|
+
});
|
|
786
|
+
```
|
|
787
|
+
|
|
788
|
+
## Additional Utils Tools (Beta Tools)
|
|
789
|
+
|
|
790
|
+
### AbortController
|
|
791
|
+
|
|
792
|
+
This is an AbortController ponyfill for mini-programs, refer to mdn
|
|
793
|
+
|
|
794
|
+
### runTTTAction
|
|
795
|
+
|
|
796
|
+
Run a TTTAction to handle user actions, currently supporting the following actions
|
|
797
|
+
|
|
798
|
+
- `openRoute` Open a route
|
|
799
|
+
- `openMiniApp` Open a mini program
|
|
800
|
+
- `openH5` Open an H5 page
|
|
801
|
+
- `sendMessage` Send a message
|
|
802
|
+
- `buildIn` Built-in action
|
|
803
|
+
|
|
804
|
+
### Asr
|
|
805
|
+
|
|
806
|
+
ASR Speech Recognition encapsulation, used for recognizing user's voice input
|
|
807
|
+
|
|
808
|
+
Usage:
|
|
809
|
+
|
|
810
|
+
```tsx
|
|
811
|
+
import { Asr, AsrDetectResultState, AsrError } from '@ray-js/t-agent-plugin-assistant';
|
|
812
|
+
|
|
813
|
+
async function startAsr() {
|
|
814
|
+
// Request user permission first
|
|
815
|
+
await Asr.authorize();
|
|
816
|
+
|
|
817
|
+
// Initial text
|
|
818
|
+
let initial = '';
|
|
819
|
+
|
|
820
|
+
// Previously recognized text
|
|
821
|
+
let last = initial;
|
|
822
|
+
|
|
823
|
+
// Generate a recognizer
|
|
824
|
+
const asr = Asr.detect(async res => {
|
|
825
|
+
if (res.state === AsrDetectResultState.STARTING || res.state === AsrDetectResultState.END) {
|
|
826
|
+
// Full text
|
|
827
|
+
const full = initial + res.text;
|
|
828
|
+
|
|
829
|
+
// Recently recognized text
|
|
830
|
+
const incoming = full.slice(last.length);
|
|
831
|
+
|
|
832
|
+
// Previously recognized text
|
|
833
|
+
last = full;
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
if (res.state === AsrDetectResultState.ERROR) {
|
|
837
|
+
onError(new AsrError(res.errorCode));
|
|
838
|
+
}
|
|
839
|
+
});
|
|
840
|
+
|
|
841
|
+
// Start recognition
|
|
842
|
+
await asr.start();
|
|
843
|
+
}
|
|
844
|
+
```
|
|
845
|
+
|
|
846
|
+
### promisify TTT
|
|
847
|
+
|
|
848
|
+
A promisify method for the built-in TTT API, used to convert the TTT API into a Promise, supporting mock concurrently
|
|
849
|
+
|
|
850
|
+
Usage:
|
|
851
|
+
|
|
852
|
+
```tsx
|
|
853
|
+
import { promisify } from '@ray-js/t-agent-plugin-assistant';
|
|
854
|
+
|
|
855
|
+
interface RouterParams {
|
|
856
|
+
/** Route link */
|
|
857
|
+
url: string;
|
|
858
|
+
complete?: () => void;
|
|
859
|
+
success?: (params: null) => void;
|
|
860
|
+
fail?: (params: {
|
|
861
|
+
errorMsg: string;
|
|
862
|
+
errorCode: string | number;
|
|
863
|
+
innerError: {
|
|
864
|
+
errorCode: string | number;
|
|
865
|
+
errorMsg: string;
|
|
866
|
+
};
|
|
867
|
+
}) => void;
|
|
868
|
+
}
|
|
869
|
+
const router = promisify<RouterParams>(ty.router);
|
|
870
|
+
|
|
871
|
+
// mock, only effective under IDE
|
|
872
|
+
mock.hooks.hook('router', context => {
|
|
873
|
+
console.log('call router', context.options);
|
|
874
|
+
});
|
|
875
|
+
|
|
876
|
+
// invoke
|
|
877
|
+
await router({ url: '/pages/index/index' });
|
|
878
|
+
```
|
|
879
|
+
|
|
880
|
+
### sendBlockToAssistant
|
|
881
|
+
|
|
882
|
+
Send a message to the Assistant
|
|
883
|
+
|
|
884
|
+
```tsx
|
|
885
|
+
import { sendBlockToAssistant, getAIAssistantRequestId } from '@ray-js/t-agent-ui-ray';
|
|
886
|
+
|
|
887
|
+
const send = async () => {
|
|
888
|
+
const requestId = await getAIAssistantRequestId();
|
|
889
|
+
const result = sendBlockToAssistant({
|
|
890
|
+
channel: 'your-channel-id',
|
|
891
|
+
sessionId: 'your-session-id',
|
|
892
|
+
requestId,
|
|
893
|
+
blocks: [{ type: 'text', text: 'hello' }],
|
|
894
|
+
});
|
|
895
|
+
|
|
896
|
+
// Get metadata after sending
|
|
897
|
+
const meta = await result.metaPromise;
|
|
898
|
+
|
|
899
|
+
// Get streamed message
|
|
900
|
+
const parts = result.parts();
|
|
901
|
+
|
|
902
|
+
for await (const part of parts) {
|
|
903
|
+
console.log('part', part);
|
|
904
|
+
}
|
|
905
|
+
};
|
|
906
|
+
```
|
|
907
|
+
|
|
908
|
+
### sendSkillToAssistant
|
|
909
|
+
|
|
910
|
+
Send skills to the Assistant
|
|
911
|
+
|
|
912
|
+
```tsx
|
|
913
|
+
import { sendSkillToAssistant } from '@ray-js/t-agent-ui-ray';
|
|
914
|
+
|
|
915
|
+
const send = async () => {
|
|
916
|
+
const result = sendSkillToAssistant({
|
|
917
|
+
channel: 'your-channel-id',
|
|
918
|
+
sessionId: 'your-session-id',
|
|
919
|
+
options: {
|
|
920
|
+
domain: 'string',
|
|
921
|
+
intent: 'string',
|
|
922
|
+
},
|
|
923
|
+
});
|
|
924
|
+
|
|
925
|
+
// Get metadata after sending
|
|
926
|
+
const meta = await result.metaPromise;
|
|
927
|
+
|
|
928
|
+
// Get streamed messages
|
|
929
|
+
const parts = result.parts();
|
|
930
|
+
|
|
931
|
+
for await (const part of parts) {
|
|
932
|
+
console.log('part', part);
|
|
933
|
+
}
|
|
934
|
+
};
|
|
935
|
+
```
|
|
936
|
+
|
|
937
|
+
### authorizeAssistantPolicy
|
|
938
|
+
|
|
939
|
+
Popup to remind users to accept the AI privacy policy, a security requirement that users must agree to access AI features in the app
|
|
940
|
+
|
|
941
|
+
```tsx
|
|
942
|
+
import { authorizeAssistantPolicy } from '@ray-js/t-agent-plugin-assistant';
|
|
943
|
+
|
|
944
|
+
// Call this function after the page load completes to pop up the agreement prompt
|
|
945
|
+
const authorize = async () => {
|
|
946
|
+
try {
|
|
947
|
+
const result = await authorizeAssistantPolicy();
|
|
948
|
+
|
|
949
|
+
if (result) {
|
|
950
|
+
// Already agreed or clicked the agree button
|
|
951
|
+
console.log('User agreed to the AI privacy policy');
|
|
952
|
+
} else {
|
|
953
|
+
ty.exitMiniProgram({});
|
|
954
|
+
console.log('User declined the AI privacy policy');
|
|
955
|
+
}
|
|
956
|
+
} catch (e) {
|
|
957
|
+
// Popup error message
|
|
958
|
+
ty.showToast({
|
|
959
|
+
title: I18n.t('get_sign_error'),
|
|
960
|
+
icon: 'error',
|
|
961
|
+
});
|
|
962
|
+
// Delay to allow the user to read clearly
|
|
963
|
+
setTimeout(() => {
|
|
964
|
+
ty.exitMiniProgram({});
|
|
965
|
+
}, 1000);
|
|
966
|
+
}
|
|
967
|
+
};
|
|
968
|
+
```
|
|
969
|
+
|
|
970
|
+
### Functions Related to Media Files
|
|
971
|
+
|
|
972
|
+
`uploadMedia`, `uploadVideo`, `uploadImage` are used to upload media files, with caching supported.
|
|
973
|
+
|
|
974
|
+
`isFullLink` is used to determine whether it is a link with http(s) protocol
|
|
975
|
+
|
|
976
|
+
`parseCloudKey` is used to parse the key of cloud storage in the URL
|
|
977
|
+
|
|
978
|
+
`isLinkExpired` is used to determine whether the link is expired
|
|
979
|
+
|
|
980
|
+
`getUrlByCloudKey` is used to get the signed download URL of cloud storage from the cache, otherwise returns `undefined`
|
|
981
|
+
|
|
982
|
+
`setUrlByCloudKey` is used to set the signed download URL of cloud storage into the cache
|
|
983
|
+
|
|
984
|
+
`resetUrlByCloudKey` is used to reset the cache of the signed download URL of cloud storage
|
|
985
|
+
|
|
986
|
+
`chooseImage`, `chooseVideo` are used to select media files
|
|
987
|
+
|
|
988
|
+
```tsx
|
|
989
|
+
import { useEffect } from 'react';
|
|
990
|
+
|
|
991
|
+
async function getPictureList() {
|
|
992
|
+
// Retrieve the user's private picture list from the cloud
|
|
993
|
+
const list = await pictureListRequest();
|
|
994
|
+
// Set into the cache this way, so it won’t need to request next time
|
|
995
|
+
for (const item of list) {
|
|
996
|
+
setUrlByCloudKey(item.path, item.displayUrl);
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
```
|
|
1000
|
+
|
|
1001
|
+
# t-agent-ui-ray
|
|
1002
|
+
|
|
1003
|
+
t-agent-ui-ray is a UI component library based on ray, containing some commonly used dialogue interface components, such as message list, message input box, etc.
|
|
1004
|
+
|
|
1005
|
+
## Installation
|
|
1006
|
+
|
|
1007
|
+
```shell
|
|
1008
|
+
yarn add @ray-js/t-agent-ui-ray
|
|
1009
|
+
```
|
|
1010
|
+
|
|
1011
|
+
## Usage
|
|
1012
|
+
|
|
1013
|
+
```tsx
|
|
1014
|
+
import { ChatContainer, MessageList, MessageInput } from '@ray-js/t-agent-ui-ray';
|
|
1015
|
+
// see the use case of t-agent for createAgent implementation
|
|
1016
|
+
import { createAgent } from './createAgent';
|
|
1017
|
+
|
|
1018
|
+
export default function ChatPage() {
|
|
1019
|
+
// createAgent must return a ChatAgent instance applied with withUI, withAssistant plugins
|
|
1020
|
+
return (
|
|
1021
|
+
<View style={{ height: '100vh' }}>
|
|
1022
|
+
<ChatContainer createAgent={createAgent}>
|
|
1023
|
+
<MessageList />
|
|
1024
|
+
<MessageInput />
|
|
1025
|
+
</ChatContainer>
|
|
1026
|
+
</View>
|
|
1027
|
+
);
|
|
1028
|
+
}
|
|
1029
|
+
```
|
|
1030
|
+
|
|
1031
|
+
## Components
|
|
1032
|
+
|
|
1033
|
+
### ChatContainer
|
|
1034
|
+
|
|
1035
|
+
Dialog container, used to wrap message lists and message input boxes, provides the context for `ChatAgent`
|
|
1036
|
+
|
|
1037
|
+
props:
|
|
1038
|
+
|
|
1039
|
+
- `className` Class name of the container
|
|
1040
|
+
- `createAgent` Function to create `ChatAgent`, which will call this function to create `ChatAgent` instance after `ChatContainer` mount
|
|
1041
|
+
- `renderOptions` Rendering options, used to decide the rendering method of each element in `MessageList`, refer to the renderOptions custom rendering part below
|
|
1042
|
+
- `renderTileAs` This function decides how to render tiles in the messages
|
|
1043
|
+
- `customBlockTypes` Custom block types, only block types registered here will be rendered by `renderCustomBlockAs`
|
|
1044
|
+
- `renderCustomBlockAs` This function decides how to render custom blocks in markdown bubble messages, by default supports `echarts`
|
|
1045
|
+
- `renderCardAs` This function decides how to render cards in messages, generally no need to customize this item
|
|
1046
|
+
- `customCardMap` Custom card mapping, no need to modify the `renderCardAs` function, just register the card type and corresponding component here
|
|
1047
|
+
- `getStaticResourceBizType` Get static resource `bizType` used to fetch static resources
|
|
1048
|
+
|
|
1049
|
+
### MessageList
|
|
1050
|
+
|
|
1051
|
+
Message list, used to display messages
|
|
1052
|
+
|
|
1053
|
+
props:
|
|
1054
|
+
|
|
1055
|
+
- `className` Class name of the list
|
|
1056
|
+
- `roleSide` Alignment of message roles, default `{ user: 'end', assistant: 'start' }`
|
|
1057
|
+
|
|
1058
|
+
### MessageInput
|
|
1059
|
+
|
|
1060
|
+
Message input box, used to input messages, upload attachments, ASR speech recognition
|
|
1061
|
+
|
|
1062
|
+
props:
|
|
1063
|
+
|
|
1064
|
+
- `className` Class name of the input box
|
|
1065
|
+
- `placeholder` Placeholder of the input box
|
|
1066
|
+
- `renderTop` Used to render content above the input box
|
|
1067
|
+
|
|
1068
|
+
### PrivateImage
|
|
1069
|
+
|
|
1070
|
+
Private image component, used to display private images, props are the same as Image, adding bizType parameter
|
|
1071
|
+
|
|
1072
|
+
### Built-in tile components
|
|
1073
|
+
|
|
1074
|
+
- bubble Bubble
|
|
1075
|
+
- buttons Button group
|
|
1076
|
+
- card Card
|
|
1077
|
+
- image Image
|
|
1078
|
+
- recommendations Recommended actions
|
|
1079
|
+
- text Text, including markdown support
|
|
1080
|
+
- time Time marker
|
|
1081
|
+
- tip Tip
|
|
1082
|
+
- video Video
|
|
1083
|
+
- workflow Workflow options
|
|
1084
|
+
|
|
1085
|
+
### Built-in card
|
|
1086
|
+
|
|
1087
|
+
- WorkflowReplyCard Workflow reply card
|
|
1088
|
+
|
|
1089
|
+
## React Hooks
|
|
1090
|
+
|
|
1091
|
+
### useChatAgent
|
|
1092
|
+
|
|
1093
|
+
Get `ChatAgent` instance in the `ChatContainer` context
|
|
1094
|
+
|
|
1095
|
+
### useAgentMessage
|
|
1096
|
+
|
|
1097
|
+
Get `messages: ChatMessageObject[]` list in the `ChatContainer` context
|
|
1098
|
+
|
|
1099
|
+
### useRenderOptions
|
|
1100
|
+
|
|
1101
|
+
Get `renderOptions` object in the `ChatContainer` context
|
|
1102
|
+
|
|
1103
|
+
### useOnEvent
|
|
1104
|
+
|
|
1105
|
+
Register UI events in the `ChatContainer` context, and automatically unregister them when the component is unloaded
|
|
1106
|
+
|
|
1107
|
+
```tsx
|
|
1108
|
+
import { useOnEvent } from '@ray-js/t-agent-ui-ray';
|
|
1109
|
+
|
|
1110
|
+
const MyComponent = () => {
|
|
1111
|
+
useOnEvent('scrollToBottom', payload => {
|
|
1112
|
+
console.log('scroll to bottom', payload.animation);
|
|
1113
|
+
});
|
|
1114
|
+
|
|
1115
|
+
return <div>My Component</div>;
|
|
1116
|
+
};
|
|
1117
|
+
```
|
|
1118
|
+
|
|
1119
|
+
### useEmitEvent
|
|
1120
|
+
|
|
1121
|
+
Trigger UI events in the `ChatContainer` context
|
|
1122
|
+
|
|
1123
|
+
```tsx
|
|
1124
|
+
import { useEmitEvent } from '@ray-js/t-agent-ui-ray';
|
|
1125
|
+
|
|
1126
|
+
const MyComponent = () => {
|
|
1127
|
+
const emitEvent = useEmitEvent();
|
|
1128
|
+
|
|
1129
|
+
const scroll = () => {
|
|
1130
|
+
emitEvent('scrollToBottom', { animation: false });
|
|
1131
|
+
};
|
|
1132
|
+
|
|
1133
|
+
return <button onClick={scroll}>Scroll to bottom</button>;
|
|
1134
|
+
};
|
|
1135
|
+
```
|
|
1136
|
+
|
|
1137
|
+
### useTileProps
|
|
1138
|
+
|
|
1139
|
+
Get the `TileProps` object in the tile component; if it's a tile, you can directly obtain it from props.
|
|
1140
|
+
|
|
1141
|
+
```tsx
|
|
1142
|
+
import { useTileProps } from '@ray-js/t-agent-ui-ray';
|
|
1143
|
+
|
|
1144
|
+
const MyTilePart = () => {
|
|
1145
|
+
const { message, agent, tile, emitEvent } = useTileProps();
|
|
1146
|
+
|
|
1147
|
+
return <div>My Tile</div>;
|
|
1148
|
+
};
|
|
1149
|
+
```
|
|
1150
|
+
|
|
1151
|
+
### useSendAction
|
|
1152
|
+
|
|
1153
|
+
Send a TTTAction, note that it can only be used inside tile components (or cards).
|
|
1154
|
+
|
|
1155
|
+
```tsx
|
|
1156
|
+
import { useSendAction } from '@ray-js/t-agent-ui-ray';
|
|
1157
|
+
|
|
1158
|
+
const MyTilePart = () => {
|
|
1159
|
+
const sendAction = useSendAction();
|
|
1160
|
+
|
|
1161
|
+
const handleClick = () => {
|
|
1162
|
+
sendAction({ type: 'sendMessage', blocks: [{ type: 'text', text: 'hello' }] });
|
|
1163
|
+
};
|
|
1164
|
+
|
|
1165
|
+
return <button onClick={handleClick}>Send Message</button>;
|
|
1166
|
+
};
|
|
1167
|
+
```
|
|
1168
|
+
|
|
1169
|
+
## renderOptions Custom Rendering
|
|
1170
|
+
|
|
1171
|
+
### Replace or Add Tile
|
|
1172
|
+
|
|
1173
|
+
If you need to replace a tile with your own implementation, or add a new tile, you can override `renderTileAs`, for example:
|
|
1174
|
+
|
|
1175
|
+
```tsx
|
|
1176
|
+
import { ImageTileData } from '@ray-js/t-agent';
|
|
1177
|
+
import { Image } from '@ray-js/ray';
|
|
1178
|
+
import { defaultRenderOptions, TileProps } from '@ray-js/t-agent-ui-ray';
|
|
1179
|
+
|
|
1180
|
+
function MyImageTile(props: TileProps<ImageTileData>) {
|
|
1181
|
+
// Implement your own ImageTile
|
|
1182
|
+
return <Image src={props.tile.data.src}></Image>;
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
const renderOptions = {
|
|
1186
|
+
...defaultRenderOptions,
|
|
1187
|
+
renderTileAs: (props: TileProps) => {
|
|
1188
|
+
if (props.tile.type === 'image') {
|
|
1189
|
+
return <MyImageTile {...props} />;
|
|
1190
|
+
}
|
|
1191
|
+
// Maintain default behavior
|
|
1192
|
+
return defaultRenderOptions.renderTileAs(props);
|
|
1193
|
+
},
|
|
1194
|
+
};
|
|
1195
|
+
```
|
|
1196
|
+
|
|
1197
|
+
### Customize Cards
|
|
1198
|
+
|
|
1199
|
+
Cards can be categorized into three types: built-in cards (buildIn), custom cards (custom), and low-code cards (lowCode). Currently, low-code cards are still under development. Custom cards can be registered using `customCardMap`.
|
|
1200
|
+
|
|
1201
|
+
The data structure of the card is as follows:
|
|
1202
|
+
|
|
1203
|
+
```tsx
|
|
1204
|
+
enum ChatCardType {
|
|
1205
|
+
CUSTOM = 'custom', // Custom card available for business use
|
|
1206
|
+
BUILD_IN = 'buildIn', // Platform built-in card
|
|
1207
|
+
LOW_CODE = 'lowCode', // Low-code card
|
|
1208
|
+
}
|
|
1209
|
+
|
|
1210
|
+
interface ChatCardObject<T = any> {
|
|
1211
|
+
cardCode: string; // Unique identifier for the card
|
|
1212
|
+
cardType: ChatCardType; // Card type
|
|
1213
|
+
cardData: T; // Data carried by the card
|
|
1214
|
+
}
|
|
1215
|
+
```
|
|
1216
|
+
|
|
1217
|
+
Register a custom card:
|
|
1218
|
+
|
|
1219
|
+
```tsx
|
|
1220
|
+
import {
|
|
1221
|
+
ChatCardObject,
|
|
1222
|
+
ChatCardType,
|
|
1223
|
+
defaultRenderOptions,
|
|
1224
|
+
useTileProps,
|
|
1225
|
+
useSendAction,
|
|
1226
|
+
} from '@ray-js/t-agent-ui-ray';
|
|
1227
|
+
import { View, Text, Button } from '@ray-js/ray';
|
|
1228
|
+
|
|
1229
|
+
function MyCard(props: { card: ChatCardObject }) {
|
|
1230
|
+
// If you need to access attributes like agent, message, tile, emitEvent, use useTileProps
|
|
1231
|
+
const { message, agent, tile, emitEvent } = useTileProps();
|
|
1232
|
+
|
|
1233
|
+
// If you need to send a TTTAction, use useSendAction
|
|
1234
|
+
const sendAction = useSendAction();
|
|
1235
|
+
|
|
1236
|
+
return (
|
|
1237
|
+
<View>
|
|
1238
|
+
<Text>My Card</Text>
|
|
1239
|
+
<Button
|
|
1240
|
+
onClick={() =>
|
|
1241
|
+
sendAction({ type: 'sendMessage', blocks: [{ type: 'text', text: 'hello' }] })
|
|
1242
|
+
}
|
|
1243
|
+
>
|
|
1244
|
+
Fill text into input
|
|
1245
|
+
</Button>
|
|
1246
|
+
</View>
|
|
1247
|
+
);
|
|
1248
|
+
}
|
|
1249
|
+
|
|
1250
|
+
const renderOptions = {
|
|
1251
|
+
...defaultRenderOptions,
|
|
1252
|
+
customCardMap: {
|
|
1253
|
+
myCard: MyCard,
|
|
1254
|
+
},
|
|
1255
|
+
};
|
|
1256
|
+
```
|
|
1257
|
+
|
|
1258
|
+
### Customize Block
|
|
1259
|
+
|
|
1260
|
+
In `TextTile`, the markdown renderer supports custom blocks. You can register and render custom blocks through `customBlockTypes` and `renderCustomBlockAs`.
|
|
1261
|
+
|
|
1262
|
+
The data structure of the block is as follows:
|
|
1263
|
+
|
|
1264
|
+
```tsx
|
|
1265
|
+
export interface MarkdownBlock {
|
|
1266
|
+
id: string;
|
|
1267
|
+
type: string;
|
|
1268
|
+
children: string; // Content in the block
|
|
1269
|
+
}
|
|
1270
|
+
```
|
|
1271
|
+
|
|
1272
|
+
Register a custom block:
|
|
1273
|
+
|
|
1274
|
+
```tsx
|
|
1275
|
+
import { defaultRenderOptions, MarkdownBlock } from '@ray-js/t-agent-ui-ray';
|
|
1276
|
+
import { View, Text } from '@ray-js/ray';
|
|
1277
|
+
|
|
1278
|
+
const renderOptions = {
|
|
1279
|
+
...defaultRenderOptions,
|
|
1280
|
+
customBlockTypes: ['my-block'],
|
|
1281
|
+
renderCustomBlockAs: (block: MarkdownBlock) => {
|
|
1282
|
+
if (block.type === 'my-block') {
|
|
1283
|
+
return (
|
|
1284
|
+
<View>
|
|
1285
|
+
<View>This is My Block</View>
|
|
1286
|
+
<View>{block.children}</View>
|
|
1287
|
+
</View>
|
|
1288
|
+
);
|
|
1289
|
+
}
|
|
1290
|
+
return defaultRenderOptions.renderCustomBlockAs(block);
|
|
1291
|
+
},
|
|
1292
|
+
};
|
|
1293
|
+
```
|
|
1294
|
+
|
|
1295
|
+
If a markdown text containing a `fence` of type `my-block` is sent by an AI, since we registered `my-block` above, this `fence` can be treated as a custom block.
|
|
1296
|
+
|
|
1297
|
+
````markdown
|
|
1298
|
+
This is my custom block!
|
|
1299
|
+
|
|
1300
|
+
```my-block
|
|
1301
|
+
Hello, world!
|
|
1302
|
+
```
|
|
1303
|
+
|
|
1304
|
+
The following fence is not registered, and will not be rendered as a custom block, but just as a regular code block.
|
|
1305
|
+
|
|
1306
|
+
```javascript
|
|
1307
|
+
console.log('Hello, world!');
|
|
1308
|
+
```
|
|
1309
|
+
````
|
|
1310
|
+
|
|
1311
|
+
The rendered result is as follows:
|
|
1312
|
+
|
|
1313
|
+
```
|
|
1314
|
+
This is my custom block!
|
|
1315
|
+
This is My Block
|
|
1316
|
+
Hello, world!
|
|
1317
|
+
The following fence is not registered, and will not be rendered as a custom block, but just as a regular code block.
|
|
1318
|
+
----------------------------------
|
|
1319
|
+
| console.log('Hello, world!'); |
|
|
1320
|
+
----------------------------------
|
|
1321
|
+
```
|
|
1322
|
+
|
|
1323
|
+
### getStaticResourceBizType
|
|
1324
|
+
|
|
1325
|
+
If your static resources need to include `bizType`, it can be obtained via `getStaticResourceBizType`.
|
|
1326
|
+
|
|
1327
|
+
```tsx
|
|
1328
|
+
import { defaultRenderOptions } from '@ray-js/t-agent-ui-ray';
|
|
1329
|
+
|
|
1330
|
+
const renderOptions = {
|
|
1331
|
+
...defaultRenderOptions,
|
|
1332
|
+
getStaticResourceBizType: (src: string, scene: string) => 'bizType',
|
|
1333
|
+
};
|
|
1334
|
+
```
|
|
1335
|
+
|
|
1336
|
+
For different scenarios, you may require different `bizType`. You can return different `bizType` based on `src` and `scene`.
|
|
1337
|
+
|
|
1338
|
+
The built-in `scene` includes the following:
|
|
1339
|
+
|
|
1340
|
+
- `image:view` Image viewing
|
|
1341
|
+
- `image:upload` Image uploading
|
|
1342
|
+
- `video:view` Video viewing
|
|
1343
|
+
- `video:upload` Video uploading
|
|
1344
|
+
- `videoThumb:view` Video thumbnail viewing
|
|
1345
|
+
- `videoThumb:upload` Video thumbnail uploading
|