@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/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
+ ![ChatPage1](https://static1.tuyacn.com/static/txp-ray-TAgent/ChatPage1.png)
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
+ ![flow.png](https://static1.tuyacn.com/static/txp-ray-TAgent/flow-en.png)
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