@blueking/chat-helper 0.0.1-beta.3 → 0.0.1-beta.30

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.
Files changed (43) hide show
  1. package/README.md +962 -63
  2. package/dist/agent/type.d.ts +27 -2
  3. package/dist/agent/type.ts.js +1 -1
  4. package/dist/agent/use-agent.d.ts +506 -436
  5. package/dist/agent/use-agent.ts.js +224 -25
  6. package/dist/event/ag-ui.d.ts +27 -7
  7. package/dist/event/ag-ui.ts.js +180 -150
  8. package/dist/event/type.d.ts +36 -108
  9. package/dist/event/type.ts.js +9 -11
  10. package/dist/http/fetch/fetch.d.ts +40 -36
  11. package/dist/http/fetch/fetch.ts.js +59 -32
  12. package/dist/http/fetch/index.d.ts +1 -0
  13. package/dist/http/fetch/index.ts.js +17 -1
  14. package/dist/http/index.d.ts +24 -16
  15. package/dist/http/index.ts.js +60 -3
  16. package/dist/http/module/agent.d.ts +2 -2
  17. package/dist/http/module/index.d.ts +22 -16
  18. package/dist/http/module/index.ts.js +2 -1
  19. package/dist/http/module/message.d.ts +22 -7
  20. package/dist/http/module/message.ts.js +49 -7
  21. package/dist/http/module/session.d.ts +14 -11
  22. package/dist/http/module/session.ts.js +66 -4
  23. package/dist/http/transform/agent.ts.js +11 -8
  24. package/dist/http/transform/message.d.ts +6 -6
  25. package/dist/http/transform/message.ts.js +542 -118
  26. package/dist/http/transform/session.ts.js +9 -1
  27. package/dist/index.d.ts +2926 -2631
  28. package/dist/index.ts.js +26 -5
  29. package/dist/mediator/index.d.ts +2 -0
  30. package/dist/mediator/index.ts.js +26 -0
  31. package/dist/mediator/type.d.ts +50 -0
  32. package/dist/mediator/type.ts.js +28 -0
  33. package/dist/mediator/use-mediator.d.ts +7 -0
  34. package/dist/mediator/use-mediator.ts.js +47 -0
  35. package/dist/message/type.d.ts +239 -142
  36. package/dist/message/type.ts.js +15 -15
  37. package/dist/message/use-message.d.ts +817 -754
  38. package/dist/message/use-message.ts.js +226 -28
  39. package/dist/session/type.d.ts +10 -0
  40. package/dist/session/use-session.d.ts +1881 -1729
  41. package/dist/session/use-session.ts.js +198 -33
  42. package/dist/type.d.ts +4 -4
  43. package/package.json +2 -1
package/README.md CHANGED
@@ -2,6 +2,17 @@
2
2
 
3
3
  一个用于 Vue3 的聊天助手工具包,提供完整的 AI 对话功能,支持流式响应、会话管理、消息管理等功能。
4
4
 
5
+ ## 特性
6
+
7
+ - 🚀 **Vue3 Composition API**:基于 Vue3 的响应式系统,提供流畅的开发体验
8
+ - 💬 **完整的会话管理**:支持多会话创建、切换、更新和删除
9
+ - 📨 **消息管理**:支持消息列表、添加、修改、删除和批量操作
10
+ - 🤖 **AI Agent 集成**:轻松接入 AI 代理,获取代理信息和进行对话
11
+ - 🌊 **流式响应**:支持 SSE(Server-Sent Events)流式响应,实时展示 AI 回复
12
+ - 🎯 **中介者模式**:模块间通过中介者协调通信,降低耦合度
13
+ - 🔧 **高度可定制**:支持自定义拦截器、自定义 Protocol、自定义请求配置
14
+ - 📝 **TypeScript 支持**:完整的类型定义,提供良好的类型提示
15
+
5
16
  ## 安装
6
17
 
7
18
  ```bash
@@ -27,6 +38,34 @@ const chatHelper = useChatHelper({
27
38
  const { agent, session, message, http } = chatHelper;
28
39
  ```
29
40
 
41
+ ## 架构设计
42
+
43
+ `@blueking/chat-helper` 采用**中介者模式(Mediator Pattern)**进行架构设计,将复杂的模块间通信集中到中介者对象中,降低了各模块之间的耦合度。
44
+
45
+ ### 核心模块
46
+
47
+ - **Agent 模块**:管理 AI 代理相关功能,包括获取代理信息、开始聊天、停止聊天等
48
+ - **Session 模块**:管理聊天会话,包括会话列表、创建、切换、更新和删除
49
+ - **Message 模块**:管理聊天消息,包括消息列表、添加、修改和删除
50
+ - **HTTP 模块**:底层 HTTP 请求封装,提供各种 API 调用方法和 SSE 流式响应支持
51
+ - **Mediator 模块**:中介者,协调各模块之间的通信,避免模块间直接依赖
52
+
53
+ ### 数据流向
54
+
55
+ ```
56
+ 用户操作 → Agent/Session/Message 模块 → Mediator → HTTP 模块 → 后端 API
57
+ ↑ ↓
58
+ ←────────── 响应数据/流式事件 ←─────────────
59
+ ```
60
+
61
+ ### Protocol 系统
62
+
63
+ `@blueking/chat-helper` 提供了可扩展的 Protocol 系统来处理 SSE 流式响应:
64
+
65
+ - **ISSEProtocol 接口**:定义了流式响应的处理接口
66
+ - **AGUIProtocol 实现**:默认的 AGUI 协议实现,支持丰富的事件类型
67
+ - **自定义 Protocol**:用户可以实现自己的 Protocol 来处理特定的流式响应格式
68
+
30
69
  ## useChatHelper 返回数据说明
31
70
 
32
71
  `useChatHelper` 返回一个包含四个模块的对象:
@@ -44,10 +83,17 @@ agent.isInfoLoading; // Ref<boolean> - agent 信息加载状态
44
83
 
45
84
  // 方法
46
85
  agent.getAgentInfo(); // () => Promise<void> - 获取 agent 信息
47
- agent.chat(); // () => void - 开始聊天(流式响应)
86
+ agent.chat(userInput, sessionCode, url?, config?); // (userInput: string, sessionCode: string, url?: string, config?: IRequestConfig) => Promise<void> - 开始聊天(流式响应)
48
87
  agent.stopChat(); // () => Promise<void> - 停止聊天
49
88
  ```
50
89
 
90
+ **参数说明**:
91
+
92
+ - `userInput`: 用户输入的消息内容,必需参数
93
+ - `sessionCode`: 会话代码,必需参数,标识当前聊天会话
94
+ - `url`: 可选参数,自定义聊天接口地址,默认为 `'chat_completion/'`
95
+ - `config`: 可选参数,额外的请求配置(如自定义 headers、data 等)
96
+
51
97
  **使用示例**:
52
98
 
53
99
  ```vue
@@ -55,32 +101,66 @@ agent.stopChat(); // () => Promise<void> - 停止聊天
55
101
  <div>
56
102
  <div v-if="agent.isInfoLoading">加载中...</div>
57
103
  <div v-else-if="agent.info">
58
- <h2>{{ agent.info.name }}</h2>
59
- <p>{{ agent.info.description }}</p>
104
+ <h2>{{ agent.info.agentName }}</h2>
105
+ <p>{{ agent.info.conversationSettings?.openingRemark }}</p>
60
106
  </div>
107
+ <input
108
+ v-model="userInput"
109
+ placeholder="请输入消息..."
110
+ />
61
111
  <button @click="startChat">开始对话</button>
62
112
  <button @click="agent.stopChat">停止对话</button>
63
113
  </div>
64
114
  </template>
65
115
 
66
116
  <script setup lang="ts">
67
- import { onMounted } from 'vue';
117
+ import { onMounted, ref } from 'vue';
68
118
  import { useChatHelper } from '@blueking/chat-helper';
69
119
 
70
- const { agent } = useChatHelper({
120
+ const { agent, session } = useChatHelper({
71
121
  /* ... */
72
122
  });
73
123
 
124
+ const userInput = ref('');
125
+
74
126
  onMounted(() => {
75
127
  agent.getAgentInfo();
76
128
  });
77
129
 
78
130
  const startChat = () => {
79
- agent.chat();
131
+ if (session.current?.sessionCode && userInput.value.trim()) {
132
+ agent.chat(userInput.value, session.current.sessionCode);
133
+ userInput.value = ''; // 清空输入框
134
+ }
80
135
  };
81
136
  </script>
82
137
  ```
83
138
 
139
+ **高级用法 - 自定义请求配置**:
140
+
141
+ ```typescript
142
+ // 使用自定义 URL
143
+ agent.chat('你好', sessionCode, 'custom_chat_endpoint/');
144
+
145
+ // 使用自定义请求配置
146
+ agent.chat('你好', sessionCode, undefined, {
147
+ headers: {
148
+ 'X-Custom-Header': 'value',
149
+ },
150
+ data: {
151
+ temperature: 0.7,
152
+ max_tokens: 1000,
153
+ },
154
+ });
155
+
156
+ // 同时使用自定义 URL 和配置
157
+ agent.chat('你好', sessionCode, 'custom_chat_endpoint/', {
158
+ headers: {
159
+ 'X-Custom-Header': 'value',
160
+ },
161
+ });
162
+ ```
163
+
84
164
  ### 2. session - 会话模块
85
165
 
86
166
  管理聊天会话的创建、切换、更新和删除。
@@ -118,7 +198,7 @@ session.deleteSession(sessionCode); // (sessionCode: string) => Promise<void> -
118
198
  @click="session.chooseSession(item.sessionCode)"
119
199
  :class="{ active: session.current?.sessionCode === item.sessionCode }"
120
200
  >
121
- {{ item.title }}
201
+ {{ item.sessionName }}
122
202
  </li>
123
203
  </ul>
124
204
  <button @click="createNewSession">新建会话</button>
@@ -139,7 +219,8 @@ session.deleteSession(sessionCode); // (sessionCode: string) => Promise<void> -
139
219
 
140
220
  const createNewSession = () => {
141
221
  session.createSession({
142
- title: '新会话',
222
+ sessionName: '新会话',
223
+ sessionCode: `session_${Date.now()}`,
143
224
  // ... 其他会话属性
144
225
  });
145
226
  };
@@ -157,17 +238,28 @@ const { message } = chatHelper;
157
238
  message.list; // Ref<IMessage[]> - 消息列表
158
239
  message.isListLoading; // Ref<boolean> - 列表加载状态
159
240
  message.isDeleteLoading; // Ref<boolean> - 删除加载状态
241
+ message.isBatchDeleteLoading; // Ref<boolean> - 批量删除加载状态
160
242
 
161
243
  // 方法
162
244
  message.getMessages(sessionCode); // (sessionCode: string) => Promise<void> - 获取消息列表
163
- message.plusMessage(message); // (message: IMessage) => void - 添加消息
245
+ message.plusMessage(message); // (message: IMessage) => Promise<void> - 添加消息
164
246
  message.modifyMessage(message); // (message: IMessage) => void - 修改消息
165
- message.deleteMessage(id); // (id: number) => Promise<void> - 删除消息
166
- message.batchDeleteMessages(ids); // (ids: number[]) => Promise<void> - 批量删除消息
247
+ message.deleteMessage(id); // (id: string) => Promise<void> - 删除消息
248
+ message.batchDeleteMessages(ids); // (ids: string[]) => Promise<void> - 批量删除消息
167
249
  message.getCurrentLoadingMessage(); // () => IMessage | undefined - 获取当前加载中的消息
168
- message.getMessageById(id); // (id: number) => IMessage | undefined - 根据 ID 获取消息
250
+ message.getMessageByMessageId(id); // (id: string) => IMessage | undefined - 根据消息 ID 获取消息
169
251
  ```
170
252
 
253
+ **方法说明**:
254
+
255
+ - `getMessages`: 获取指定会话的所有消息列表
256
+ - `plusMessage`: 主动添加一条新消息到列表(会先调用后端接口创建消息,然后添加到列表最前面)
257
+ - `modifyMessage`: 修改已存在的消息(本地更新,不调用接口)
258
+ - `deleteMessage`: 删除单条消息(调用接口删除并从列表中移除)
259
+ - `batchDeleteMessages`: 批量删除多条消息(调用接口批量删除并从列表中移除)
260
+ - `getCurrentLoadingMessage`: 获取当前正在加载/流式传输中的 AI 响应消息(状态为 Pending 或 Streaming)
261
+ - `getMessageByMessageId`: 根据消息 ID 从列表中查找消息
262
+
171
263
  **使用示例**:
172
264
 
173
265
  ```vue
@@ -175,31 +267,45 @@ message.getMessageById(id); // (id: number) => IMessage | undefined - 根据 ID
175
267
  <div class="message-list">
176
268
  <div
177
269
  v-for="msg in message.list"
178
- :key="msg.id"
270
+ :key="msg.messageId"
179
271
  class="message-item"
180
272
  >
181
273
  <div class="role">{{ msg.role }}</div>
182
274
  <div class="content">
183
- <div
184
- v-for="(content, index) in msg.content"
185
- :key="index"
186
- >
187
- <template v-if="content.type === 'text'">
188
- {{ content.data }}
189
- </template>
275
+ <!-- 根据消息类型显示不同内容 -->
276
+ <div v-if="msg.role === 'user'">
277
+ {{ typeof msg.content === 'string' ? msg.content : JSON.stringify(msg.content) }}
278
+ </div>
279
+ <div v-else-if="msg.role === 'assistant'">
280
+ {{ msg.content || '' }}
190
281
  </div>
191
282
  </div>
192
- <button @click="message.deleteMessage(msg.id)">删除</button>
283
+ <button @click="message.deleteMessage(msg.messageId)">删除</button>
193
284
  </div>
285
+ <button
286
+ @click="batchDelete"
287
+ :disabled="message.isBatchDeleteLoading"
288
+ >
289
+ 批量删除选中消息
290
+ </button>
194
291
  </div>
195
292
  </template>
196
293
 
197
294
  <script setup lang="ts">
295
+ import { ref } from 'vue';
198
296
  import { useChatHelper } from '@blueking/chat-helper';
199
297
 
200
298
  const { message } = useChatHelper({
201
299
  /* ... */
202
300
  });
301
+
302
+ const selectedIds = ref<string[]>([]);
303
+
304
+ const batchDelete = () => {
305
+ if (selectedIds.value.length > 0) {
306
+ message.batchDeleteMessages(selectedIds.value);
307
+ }
308
+ };
203
309
  </script>
204
310
  ```
205
311
 
@@ -283,7 +389,7 @@ useChatHelper({
283
389
  // 响应拦截器
284
390
  response: response => {
285
391
  // 对响应数据做些什么
286
- console.log('Response:', response);
392
+ console.log('IResponse:', response);
287
393
 
288
394
  // 可以在这里做统一的错误处理
289
395
  if (response.data.code !== 0) {
@@ -681,6 +787,223 @@ class SafeProtocol extends AGUIProtocol {
681
787
 
682
788
  </details>
683
789
 
790
+ ## 实战场景
791
+
792
+ ### 场景 1:带输入框的聊天界面
793
+
794
+ 使用独立的 `ref` 管理用户输入:
795
+
796
+ ```vue
797
+ <template>
798
+ <div class="chat-interface">
799
+ <div class="messages">
800
+ <div
801
+ v-for="msg in message.list"
802
+ :key="msg.id"
803
+ >
804
+ <!-- 消息渲染 -->
805
+ </div>
806
+ </div>
807
+ <div class="input-box">
808
+ <textarea
809
+ v-model="userInput"
810
+ @keydown.enter.prevent="handleSend"
811
+ placeholder="输入消息,按 Enter 发送..."
812
+ :disabled="isStreaming"
813
+ />
814
+ <button
815
+ @click="handleSend"
816
+ :disabled="!canSend"
817
+ >
818
+ {{ isStreaming ? '发送中...' : '发送' }}
819
+ </button>
820
+ </div>
821
+ </div>
822
+ </template>
823
+
824
+ <script setup lang="ts">
825
+ import { computed, ref } from 'vue';
826
+ import { useChatHelper } from '@blueking/chat-helper';
827
+
828
+ const { agent, message, session } = useChatHelper({
829
+ /* ... */
830
+ });
831
+ const isStreaming = ref(false);
832
+ const userInput = ref('');
833
+
834
+ const canSend = computed(
835
+ () => !isStreaming.value && userInput.value.trim().length > 0 && session.current?.sessionCode,
836
+ );
837
+
838
+ const handleSend = () => {
839
+ if (canSend.value) {
840
+ isStreaming.value = true;
841
+ const input = userInput.value;
842
+ userInput.value = ''; // 立即清空输入框
843
+ agent.chat(input, session.current!.sessionCode).finally(() => {
844
+ isStreaming.value = false;
845
+ });
846
+ }
847
+ };
848
+ </script>
849
+ ```
850
+
851
+ ### 场景 2:消息批量管理
852
+
853
+ 实现消息的选择和批量删除功能:
854
+
855
+ ```vue
856
+ <template>
857
+ <div class="message-manager">
858
+ <div class="toolbar">
859
+ <button @click="selectAll">全选</button>
860
+ <button @click="clearSelection">取消选择</button>
861
+ <button
862
+ @click="batchDelete"
863
+ :disabled="selectedIds.length === 0 || message.isBatchDeleteLoading"
864
+ >
865
+ {{ message.isBatchDeleteLoading ? '删除中...' : `删除选中 (${selectedIds.length})` }}
866
+ </button>
867
+ </div>
868
+
869
+ <div class="messages">
870
+ <div
871
+ v-for="msg in message.list"
872
+ :key="msg.messageId"
873
+ @click="toggleSelection(msg.messageId)"
874
+ :class="{ selected: selectedIds.includes(msg.messageId) }"
875
+ >
876
+ <input
877
+ type="checkbox"
878
+ :checked="selectedIds.includes(msg.messageId)"
879
+ @click.stop
880
+ />
881
+ <!-- 消息内容 -->
882
+ </div>
883
+ </div>
884
+ </div>
885
+ </template>
886
+
887
+ <script setup lang="ts">
888
+ import { ref } from 'vue';
889
+ import { useChatHelper } from '@blueking/chat-helper';
890
+
891
+ const { message } = useChatHelper({
892
+ /* ... */
893
+ });
894
+ const selectedIds = ref<string[]>([]);
895
+
896
+ const toggleSelection = (id: string) => {
897
+ const index = selectedIds.value.indexOf(id);
898
+ if (index > -1) {
899
+ selectedIds.value.splice(index, 1);
900
+ } else {
901
+ selectedIds.value.push(id);
902
+ }
903
+ };
904
+
905
+ const selectAll = () => {
906
+ selectedIds.value = message.list.value.filter(msg => msg.messageId).map(msg => msg.messageId!);
907
+ };
908
+
909
+ const clearSelection = () => {
910
+ selectedIds.value = [];
911
+ };
912
+
913
+ const batchDelete = async () => {
914
+ if (selectedIds.value.length > 0) {
915
+ await message.batchDeleteMessages(selectedIds.value);
916
+ selectedIds.value = [];
917
+ }
918
+ };
919
+ </script>
920
+ ```
921
+
922
+ ### 场景 3:自定义聊天参数
923
+
924
+ 根据不同场景使用不同的聊天配置:
925
+
926
+ ```vue
927
+ <template>
928
+ <div class="custom-chat">
929
+ <div class="settings">
930
+ <label>
931
+ 温度:
932
+ <input
933
+ v-model.number="temperature"
934
+ type="range"
935
+ min="0"
936
+ max="1"
937
+ step="0.1"
938
+ />
939
+ {{ temperature }}
940
+ </label>
941
+ <label>
942
+ 最大tokens:
943
+ <input
944
+ v-model.number="maxTokens"
945
+ type="number"
946
+ />
947
+ </label>
948
+ <label>
949
+ 模式:
950
+ <select v-model="mode">
951
+ <option value="default">默认</option>
952
+ <option value="creative">创意</option>
953
+ <option value="precise">精确</option>
954
+ </select>
955
+ </label>
956
+ </div>
957
+
958
+ <textarea
959
+ v-model="userInput"
960
+ placeholder="输入消息..."
961
+ />
962
+ <button @click="sendWithConfig">发送</button>
963
+ </div>
964
+ </template>
965
+
966
+ <script setup lang="ts">
967
+ import { ref } from 'vue';
968
+ import { useChatHelper } from '@blueking/chat-helper';
969
+
970
+ const { agent, session } = useChatHelper({
971
+ /* ... */
972
+ });
973
+
974
+ const userInput = ref('');
975
+ const temperature = ref(0.7);
976
+ const maxTokens = ref(1000);
977
+ const mode = ref('default');
978
+
979
+ const sendWithConfig = () => {
980
+ if (!session.current?.sessionCode || !userInput.value.trim()) return;
981
+
982
+ // 根据模式设置不同的端点
983
+ const endpoint =
984
+ mode.value === 'creative'
985
+ ? 'chat_completion_creative/'
986
+ : mode.value === 'precise'
987
+ ? 'chat_completion_precise/'
988
+ : undefined;
989
+
990
+ // 自定义请求参数
991
+ agent.chat(userInput.value, session.current.sessionCode, endpoint, {
992
+ data: {
993
+ temperature: temperature.value,
994
+ max_tokens: maxTokens.value,
995
+ mode: mode.value,
996
+ },
997
+ headers: {
998
+ 'X-Chat-Mode': mode.value,
999
+ },
1000
+ });
1001
+
1002
+ userInput.value = ''; // 清空输入框
1003
+ };
1004
+ </script>
1005
+ ```
1006
+
684
1007
  ## 完整示例
685
1008
 
686
1009
  以下是一个完整的聊天应用示例:
@@ -698,7 +1021,7 @@ class SafeProtocol extends AGUIProtocol {
698
1021
  @click="session.chooseSession(item.sessionCode)"
699
1022
  :class="{ active: session.current?.sessionCode === item.sessionCode }"
700
1023
  >
701
- {{ item.title }}
1024
+ {{ item.sessionName }}
702
1025
  <button @click.stop="session.deleteSession(item.sessionCode)">删除</button>
703
1026
  </li>
704
1027
  </ul>
@@ -713,49 +1036,31 @@ class SafeProtocol extends AGUIProtocol {
713
1036
  >
714
1037
  <div
715
1038
  v-for="msg in message.list"
716
- :key="msg.id"
1039
+ :key="msg.messageId"
717
1040
  :class="['message-item', msg.role]"
718
1041
  >
719
1042
  <div class="message-content">
720
- <template
721
- v-for="(content, index) in msg.content"
722
- :key="index"
723
- >
724
- <!-- 文本消息 -->
725
- <div
726
- v-if="content.type === 'text'"
727
- class="text-content"
728
- >
729
- {{ content.data }}
730
- </div>
731
-
732
- <!-- 思考过程 -->
733
- <div
734
- v-else-if="content.type === 'thinking'"
735
- class="thinking-content"
736
- >
737
- <strong>{{ content.data.title }}</strong>
738
- <p>{{ content.data.text }}</p>
739
- </div>
740
-
741
- <!-- 工具调用 -->
742
- <div
743
- v-else-if="content.type === 'tool_call'"
744
- class="tool-call-content"
745
- >
746
- <strong>调用工具: {{ content.data.toolCallName }}</strong>
747
- <pre>{{ content.data.args }}</pre>
748
- </div>
1043
+ <template v-if="msg.role === 'user'">
1044
+ {{ typeof msg.content === 'string' ? msg.content : JSON.stringify(msg.content) }}
1045
+ </template>
1046
+ <template v-else-if="msg.role === 'assistant'">
1047
+ {{ msg.content || '' }}
749
1048
  </template>
750
1049
  </div>
751
- <button @click="message.deleteMessage(msg.id)">删除</button>
1050
+ <button @click="message.deleteMessage(msg.messageId)">删除</button>
752
1051
  </div>
753
1052
  </div>
754
1053
 
755
1054
  <!-- 输入区域 -->
756
1055
  <div class="input-area">
1056
+ <input
1057
+ v-model="userInput"
1058
+ @keyup.enter="sendMessage"
1059
+ placeholder="输入消息..."
1060
+ :disabled="isStreaming"
1061
+ />
757
1062
  <button
758
- @click="agent.chat"
1063
+ @click="sendMessage"
759
1064
  :disabled="isStreaming"
760
1065
  >
761
1066
  {{ isStreaming ? '生成中...' : '发送消息' }}
@@ -776,8 +1081,9 @@ class SafeProtocol extends AGUIProtocol {
776
1081
  import { useChatHelper, AGUIProtocol } from '@blueking/chat-helper';
777
1082
 
778
1083
  const isStreaming = ref(false);
1084
+ const userInput = ref('');
779
1085
 
780
- const { agent, session } = useChatHelper({
1086
+ const { agent, session, message } = useChatHelper({
781
1087
  requestData: {
782
1088
  urlPrefix: 'https://your-api-domain.com/api/',
783
1089
  headers: () => ({
@@ -827,10 +1133,17 @@ class SafeProtocol extends AGUIProtocol {
827
1133
 
828
1134
  const createNewSession = () => {
829
1135
  session.createSession({
830
- title: `会话 ${new Date().toLocaleString()}`,
1136
+ sessionName: `会话 ${new Date().toLocaleString()}`,
831
1137
  sessionCode: `session_${Date.now()}`,
832
1138
  });
833
1139
  };
1140
+
1141
+ const sendMessage = () => {
1142
+ if (session.current?.sessionCode && userInput.value.trim()) {
1143
+ agent.chat(userInput.value, session.current.sessionCode);
1144
+ userInput.value = ''; // 清空输入框
1145
+ }
1146
+ };
834
1147
  </script>
835
1148
 
836
1149
  <style scoped>
@@ -902,7 +1215,7 @@ class SafeProtocol extends AGUIProtocol {
902
1215
  <div class="messages">
903
1216
  <div
904
1217
  v-for="msg in message.list"
905
- :key="msg.id"
1218
+ :key="msg.messageId"
906
1219
  class="message"
907
1220
  >
908
1221
  <template
@@ -941,6 +1254,12 @@ class SafeProtocol extends AGUIProtocol {
941
1254
 
942
1255
  <!-- 输入区域 -->
943
1256
  <div class="input-area">
1257
+ <input
1258
+ v-model="userInput"
1259
+ @keyup.enter="sendMessage"
1260
+ placeholder="输入消息..."
1261
+ :disabled="isStreaming"
1262
+ />
944
1263
  <button
945
1264
  @click="sendMessage"
946
1265
  :disabled="isStreaming"
@@ -969,6 +1288,7 @@ class SafeProtocol extends AGUIProtocol {
969
1288
 
970
1289
  const isStreaming = ref(false);
971
1290
  const status = ref<{ type: string; message: string } | null>(null);
1291
+ const userInput = ref('');
972
1292
 
973
1293
  // 自定义 Protocol,添加各种钩子
974
1294
  class CustomChatProtocol extends AGUIProtocol {
@@ -1005,7 +1325,7 @@ class SafeProtocol extends AGUIProtocol {
1005
1325
  }
1006
1326
  }
1007
1327
 
1008
- const { agent } = useChatHelper({
1328
+ const { agent, session, message } = useChatHelper({
1009
1329
  requestData: {
1010
1330
  urlPrefix: 'https://your-api-domain.com/api/',
1011
1331
  },
@@ -1045,7 +1365,10 @@ class SafeProtocol extends AGUIProtocol {
1045
1365
  });
1046
1366
 
1047
1367
  const sendMessage = () => {
1048
- agent.chat();
1368
+ if (session.current?.sessionCode && userInput.value.trim()) {
1369
+ agent.chat(userInput.value, session.current.sessionCode);
1370
+ userInput.value = ''; // 清空输入框
1371
+ }
1049
1372
  };
1050
1373
 
1051
1374
  const stopStreaming = () => {
@@ -1099,19 +1422,595 @@ class SafeProtocol extends AGUIProtocol {
1099
1422
 
1100
1423
  ```typescript
1101
1424
  import type {
1425
+ // 主要配置和模块类型
1102
1426
  IUseChatHelperOptions,
1103
1427
  IAgentModule,
1104
1428
  ISessionModule,
1105
1429
  IMessageModule,
1106
1430
  IHttpModule,
1431
+
1432
+ // 核心数据类型
1107
1433
  IMessage,
1108
1434
  ISession,
1109
- MessageRole,
1110
- MessageStatus,
1111
- MessageContentType,
1435
+ IAgentInfo,
1436
+
1437
+ // 消息相关枚举
1438
+ MessageRole, // 消息角色:user, assistant, system, tool, activity, reasoning 等
1439
+ MessageStatus, // 消息状态:pending, streaming, complete, error, stop
1440
+ MessageType, // 消息类型:text, binary, function
1441
+
1442
+ // 协议相关
1443
+ ISSEProtocol,
1444
+ AGUIProtocol,
1445
+
1446
+ // 事件类型(用于自定义 Protocol)
1447
+ IEvent,
1448
+ IRunStartedEvent,
1449
+ IRunFinishedEvent,
1450
+ IRunErrorEvent,
1451
+ ITextMessageChunkEvent,
1452
+ IThinkingStartEvent,
1453
+ IThinkingEndEvent,
1454
+ IToolCallStartEvent,
1455
+ // ... 更多事件类型
1112
1456
  } from '@blueking/chat-helper';
1113
1457
  ```
1114
1458
 
1459
+ ### MessageRole 枚举
1460
+
1461
+ 消息支持多种角色类型:
1462
+
1463
+ ```typescript
1464
+ enum MessageRole {
1465
+ User = 'user', // 用户消息
1466
+ Assistant = 'assistant', // AI 助手消息
1467
+ System = 'system', // 系统消息
1468
+ Tool = 'tool', // 工具调用结果
1469
+ Activity = 'activity', // 活动消息
1470
+ Reasoning = 'reasoning', // 推理过程
1471
+ Developer = 'developer', // 开发者消息
1472
+ Guide = 'guide', // 引导消息
1473
+ Info = 'info', // 信息消息
1474
+ Pause = 'pause', // 暂停消息
1475
+ Placeholder = 'placeholder', // 占位消息
1476
+ Hidden = 'hidden', // 隐藏消息
1477
+ HiddenUser = 'hidden-user', // 隐藏的用户消息
1478
+ HiddenAssistant = 'hidden-assistant', // 隐藏的助手消息
1479
+ HiddenSystem = 'hidden-system', // 隐藏的系统消息
1480
+ HiddenGuide = 'hidden-guide', // 隐藏的引导消息
1481
+ TemplateUser = 'template-user', // 用户消息模板
1482
+ TemplateAssistant = 'template-assistant', // 助手消息模板
1483
+ TemplateSystem = 'template-system', // 系统消息模板
1484
+ TemplateGuide = 'template-guide', // 引导消息模板
1485
+ TemplateHidden = 'template-hidden', // 隐藏消息模板
1486
+ }
1487
+ ```
1488
+
1489
+ ### MessageStatus 枚举
1490
+
1491
+ 消息状态定义:
1492
+
1493
+ ```typescript
1494
+ enum MessageStatus {
1495
+ Pending = 'pending', // 等待中
1496
+ Streaming = 'streaming', // 流式传输中
1497
+ Complete = 'complete', // 已完成
1498
+ Error = 'error', // 错误
1499
+ Stop = 'stop', // 已停止
1500
+ }
1501
+ ```
1502
+
1503
+ ### IMessage 类型
1504
+
1505
+ 消息是联合类型,根据 `role` 字段不同有不同的结构:
1506
+
1507
+ ```typescript
1508
+ // 用户消息
1509
+ interface IUserMessage {
1510
+ messageId?: string;
1511
+ role: MessageRole.User;
1512
+ status: MessageStatus;
1513
+ content: string | IInputContent[]; // 支持文本或多媒体内容
1514
+ }
1515
+
1516
+ // AI 助手消息
1517
+ interface IAssistantMessage {
1518
+ messageId?: string;
1519
+ role: MessageRole.Assistant;
1520
+ status: MessageStatus;
1521
+ content?: string;
1522
+ toolCalls?: IToolCall[]; // 工具调用信息
1523
+ }
1524
+
1525
+ // 工具消息
1526
+ interface IToolMessage {
1527
+ messageId?: string;
1528
+ role: MessageRole.Tool;
1529
+ status: MessageStatus;
1530
+ content: string;
1531
+ toolCallId: string;
1532
+ duration?: number;
1533
+ error?: string;
1534
+ }
1535
+
1536
+ // ... 更多消息类型
1537
+ ```
1538
+
1539
+ ## API 参考
1540
+
1541
+ ### useChatHelper(options)
1542
+
1543
+ 创建 Chat Helper 实例。
1544
+
1545
+ **参数:**
1546
+
1547
+ - `options.requestData` (必需)
1548
+
1549
+ - `urlPrefix: string` - API 基础路径
1550
+ - `data?: Record<string, unknown> | (() => Record<string, unknown>)` - 额外的请求数据
1551
+ - `headers?: Record<string, string> | (() => Record<string, string>)` - 额外的请求头
1552
+
1553
+ - `options.interceptors` (可选)
1554
+
1555
+ - `request?: (config: IRequestConfig) => IRequestConfig` - 请求拦截器
1556
+ - `response?: (response: IResponse) => IResponse` - 响应拦截器
1557
+
1558
+ - `options.protocol` (可选) - 自定义 SSE Protocol 实例,默认使用 `AGUIProtocol`
1559
+
1560
+ **返回值:**
1561
+
1562
+ ```typescript
1563
+ {
1564
+ agent: IAgentModule, // Agent 模块
1565
+ session: ISessionModule, // Session 模块
1566
+ message: IMessageModule, // Message 模块
1567
+ http: IHttpModule // HTTP 模块
1568
+ }
1569
+ ```
1570
+
1571
+ ### Agent 模块 API
1572
+
1573
+ #### agent.info
1574
+
1575
+ - **类型**:`Ref<IAgentInfo | null>`
1576
+ - **说明**:当前 Agent 的信息,包括名称、会话设置、预定义问题等
1577
+
1578
+ #### agent.isInfoLoading
1579
+
1580
+ - **类型**:`Ref<boolean>`
1581
+ - **说明**:Agent 信息是否正在加载
1582
+
1583
+ #### agent.getAgentInfo()
1584
+
1585
+ - **返回值**:`Promise<void>`
1586
+ - **说明**:获取 Agent 信息,结果保存在 `agent.info` 中
1587
+
1588
+ #### agent.chat(userInput, sessionCode, url?, config?)
1589
+
1590
+ - **参数**:
1591
+ - `userInput: string` - 用户输入的消息内容
1592
+ - `sessionCode: string` - 会话代码
1593
+ - `url?: string` - 自定义聊天接口地址,默认为 `'chat_completion/'`
1594
+ - `config?: IRequestConfig` - 额外的请求配置
1595
+ - **返回值**:`Promise<void>`
1596
+ - **说明**:开始与 AI 进行聊天,支持流式响应。会先创建一条用户消息,然后发起流式请求
1597
+
1598
+ #### agent.stopChat()
1599
+
1600
+ - **返回值**:`Promise<void>`
1601
+ - **说明**:停止当前的聊天流式响应
1602
+
1603
+ ### Session 模块 API
1604
+
1605
+ #### session.list
1606
+
1607
+ - **类型**:`Ref<ISession[]>`
1608
+ - **说明**:会话列表
1609
+
1610
+ #### session.current
1611
+
1612
+ - **类型**:`Ref<ISession | null>`
1613
+ - **说明**:当前选中的会话
1614
+
1615
+ #### session.isListLoading / isCurrentLoading / isCreateLoading / isUpdateLoading / isDeleteLoading
1616
+
1617
+ - **类型**:`Ref<boolean>`
1618
+ - **说明**:各操作的加载状态
1619
+
1620
+ #### session.getSessions()
1621
+
1622
+ - **返回值**:`Promise<void>`
1623
+ - **说明**:获取会话列表,结果保存在 `session.list` 中
1624
+
1625
+ #### session.chooseSession(sessionCode)
1626
+
1627
+ - **参数**:`sessionCode: string` - 会话代码
1628
+ - **返回值**:`Promise<void>`
1629
+ - **说明**:选择会话,会停止当前聊天、设置 `session.current`,并自动加载该会话的消息列表
1630
+
1631
+ #### session.getSession(sessionCode)
1632
+
1633
+ - **参数**:`sessionCode: string` - 会话代码
1634
+ - **返回值**:`Promise<void>`
1635
+ - **说明**:获取单个会话信息,结果保存在 `session.current` 中
1636
+
1637
+ #### session.createSession(session)
1638
+
1639
+ - **参数**:`session: ISession` - 会话数据
1640
+ - **返回值**:`Promise<void>`
1641
+ - **说明**:创建新会话,创建成功后会添加到列表并自动选中
1642
+
1643
+ #### session.updateSession(session)
1644
+
1645
+ - **参数**:`session: ISession` - 会话数据
1646
+ - **返回值**:`Promise<void>`
1647
+ - **说明**:更新会话信息
1648
+
1649
+ #### session.deleteSession(sessionCode)
1650
+
1651
+ - **参数**:`sessionCode: string` - 会话代码
1652
+ - **返回值**:`Promise<void>`
1653
+ - **说明**:删除会话,如果删除的是当前会话,会自动切换到第一个会话
1654
+
1655
+ ### Message 模块 API
1656
+
1657
+ #### message.list
1658
+
1659
+ - **类型**:`Ref<IMessage[]>`
1660
+ - **说明**:消息列表
1661
+
1662
+ #### message.isListLoading / isDeleteLoading / isBatchDeleteLoading
1663
+
1664
+ - **类型**:`Ref<boolean>`
1665
+ - **说明**:各操作的加载状态
1666
+
1667
+ #### message.getMessages(sessionCode)
1668
+
1669
+ - **参数**:`sessionCode: string` - 会话代码
1670
+ - **返回值**:`Promise<void>`
1671
+ - **说明**:获取指定会话的消息列表
1672
+
1673
+ #### message.plusMessage(message)
1674
+
1675
+ - **参数**:`message: IMessage` - 消息数据
1676
+ - **返回值**:`Promise<void>`
1677
+ - **说明**:添加新消息,会先调用接口创建,然后添加到列表最前面
1678
+
1679
+ #### message.modifyMessage(message)
1680
+
1681
+ - **参数**:`message: IMessage` - 消息数据
1682
+ - **返回值**:`void`
1683
+ - **说明**:修改消息(仅本地更新,不调用接口)
1684
+
1685
+ #### message.deleteMessage(id)
1686
+
1687
+ - **参数**:`id: string` - 消息 ID
1688
+ - **返回值**:`Promise<void>`
1689
+ - **说明**:删除单条消息
1690
+
1691
+ #### message.batchDeleteMessages(ids)
1692
+
1693
+ - **参数**:`ids: string[]` - 消息 ID 数组
1694
+ - **返回值**:`Promise<void>`
1695
+ - **说明**:批量删除多条消息
1696
+
1697
+ #### message.getCurrentLoadingMessage()
1698
+
1699
+ - **返回值**:`IMessage | undefined`
1700
+ - **说明**:获取当前正在加载或流式传输中的 AI 消息(状态为 Pending 或 Streaming)
1701
+
1702
+ #### message.getMessageByMessageId(id)
1703
+
1704
+ - **参数**:`id: string` - 消息 ID
1705
+ - **返回值**:`IMessage | undefined`
1706
+ - **说明**:根据消息 ID 从列表中查找消息
1707
+
1708
+ ## 最佳实践
1709
+
1710
+ ### 1. 错误处理
1711
+
1712
+ 建议在使用 API 时添加适当的错误处理:
1713
+
1714
+ ```typescript
1715
+ const { session } = useChatHelper({
1716
+ /* ... */
1717
+ });
1718
+
1719
+ const loadSessions = async () => {
1720
+ try {
1721
+ await session.getSessions();
1722
+ } catch (error) {
1723
+ console.error('加载会话列表失败:', error);
1724
+ // 显示错误提示给用户
1725
+ }
1726
+ };
1727
+ ```
1728
+
1729
+ ### 2. 加载状态管理
1730
+
1731
+ 利用响应式的加载状态提供更好的用户体验:
1732
+
1733
+ ```vue
1734
+ <template>
1735
+ <div>
1736
+ <div v-if="session.isListLoading">
1737
+ <Spinner />
1738
+ 加载中...
1739
+ </div>
1740
+ <ul v-else>
1741
+ <li
1742
+ v-for="item in session.list"
1743
+ :key="item.sessionCode"
1744
+ >
1745
+ {{ item.sessionName }}
1746
+ </li>
1747
+ </ul>
1748
+ </div>
1749
+ </template>
1750
+ ```
1751
+
1752
+ ### 3. 会话切换
1753
+
1754
+ 切换会话时,`chooseSession` 会自动停止当前聊天并加载新会话的消息:
1755
+
1756
+ ```typescript
1757
+ // ✅ 推荐:使用 chooseSession
1758
+ await session.chooseSession(sessionCode);
1759
+
1760
+ // ❌ 不推荐:手动操作
1761
+ agent.stopChat();
1762
+ session.current.value = session.list.value.find(s => s.sessionCode === sessionCode);
1763
+ message.getMessages(sessionCode);
1764
+ ```
1765
+
1766
+ ### 4. 消息 ID 使用
1767
+
1768
+ 代码中使用 `messageId` 字段作为消息的唯一标识:
1769
+
1770
+ ```typescript
1771
+ // ✅ 正确
1772
+ message.deleteMessage(msg.messageId);
1773
+
1774
+ // ❌ 错误
1775
+ message.deleteMessage(msg.id);
1776
+ ```
1777
+
1778
+ ### 5. 动态请求配置
1779
+
1780
+ 对于需要动态获取的配置(如 token),使用函数形式:
1781
+
1782
+ ```typescript
1783
+ useChatHelper({
1784
+ requestData: {
1785
+ urlPrefix: 'https://api.example.com/',
1786
+ // ✅ 使用函数动态获取
1787
+ headers: () => ({
1788
+ Authorization: `Bearer ${localStorage.getItem('token')}`,
1789
+ 'X-Request-ID': generateRequestId(),
1790
+ }),
1791
+ },
1792
+ });
1793
+ ```
1794
+
1795
+ ### 6. Protocol 钩子函数
1796
+
1797
+ 使用 Protocol 钩子函数时,注意不要阻塞事件处理:
1798
+
1799
+ ```typescript
1800
+ new AGUIProtocol({
1801
+ onMessage: async event => {
1802
+ // ❌ 不推荐:阻塞事件处理
1803
+ await someAsyncOperation();
1804
+ console.log(event);
1805
+ },
1806
+
1807
+ onMessage: event => {
1808
+ // ✅ 推荐:快速处理,异步操作不阻塞
1809
+ console.log(event);
1810
+ someAsyncOperation(); // 不 await
1811
+ },
1812
+ });
1813
+ ```
1814
+
1815
+ ### 7. 内存管理
1816
+
1817
+ 在组件卸载时,记得停止正在进行的聊天:
1818
+
1819
+ ```typescript
1820
+ import { onUnmounted } from 'vue';
1821
+
1822
+ const { agent } = useChatHelper({
1823
+ /* ... */
1824
+ });
1825
+
1826
+ onUnmounted(() => {
1827
+ agent.stopChat();
1828
+ });
1829
+ ```
1830
+
1831
+ ### 8. 消息状态判断
1832
+
1833
+ 使用枚举类型进行状态判断,避免硬编码字符串:
1834
+
1835
+ ```typescript
1836
+ import { MessageStatus, MessageRole } from '@blueking/chat-helper';
1837
+
1838
+ // ✅ 推荐:使用枚举
1839
+ if (msg.status === MessageStatus.Streaming && msg.role === MessageRole.Assistant) {
1840
+ // 显示流式动画
1841
+ }
1842
+
1843
+ // ❌ 不推荐:硬编码字符串
1844
+ if (msg.status === 'streaming' && msg.role === 'assistant') {
1845
+ // 显示流式动画
1846
+ }
1847
+ ```
1848
+
1849
+ ## 常见问题
1850
+
1851
+ ### Q: 如何处理流式响应中的错误?
1852
+
1853
+ A: 使用 Protocol 的 `onError` 钩子函数:
1854
+
1855
+ ```typescript
1856
+ useChatHelper({
1857
+ requestData: {
1858
+ /* ... */
1859
+ },
1860
+ protocol: new AGUIProtocol({
1861
+ onError: error => {
1862
+ console.error('流式响应错误:', error);
1863
+ // 显示错误提示
1864
+ showErrorMessage('AI 响应出错,请重试');
1865
+ },
1866
+ }),
1867
+ });
1868
+ ```
1869
+
1870
+ ### Q: 如何自定义不同端点的聊天行为?
1871
+
1872
+ A: 使用 `agent.chat` 的 `url` 和 `config` 参数:
1873
+
1874
+ ```typescript
1875
+ // 使用不同的端点
1876
+ agent.chat(userInput, sessionCode, 'custom_chat/');
1877
+
1878
+ // 添加自定义参数
1879
+ agent.chat(userInput, sessionCode, undefined, {
1880
+ data: {
1881
+ temperature: 0.8,
1882
+ model: 'gpt-4',
1883
+ },
1884
+ });
1885
+ ```
1886
+
1887
+ ### Q: 消息列表如何实现自动滚动到底部?
1888
+
1889
+ A: 使用 Vue 的 `watch` 监听消息列表变化:
1890
+
1891
+ ```typescript
1892
+ import { watch, nextTick, ref } from 'vue';
1893
+
1894
+ const messageContainer = ref<HTMLElement>();
1895
+ const { message } = useChatHelper({
1896
+ /* ... */
1897
+ });
1898
+
1899
+ watch(
1900
+ () => message.list.value.length,
1901
+ async () => {
1902
+ await nextTick();
1903
+ messageContainer.value?.scrollTo({
1904
+ top: messageContainer.value.scrollHeight,
1905
+ behavior: 'smooth',
1906
+ });
1907
+ },
1908
+ );
1909
+ ```
1910
+
1911
+ ### Q: 如何判断 AI 是否正在回复?
1912
+
1913
+ A: 使用 `message.getCurrentLoadingMessage()` 方法:
1914
+
1915
+ ```typescript
1916
+ const { message } = useChatHelper({
1917
+ /* ... */
1918
+ });
1919
+
1920
+ const isAIResponding = computed(() => {
1921
+ return message.getCurrentLoadingMessage() !== undefined;
1922
+ });
1923
+ ```
1924
+
1925
+ ### Q: 如何实现多租户或多应用场景?
1926
+
1927
+ A: 使用动态的 `data` 配置:
1928
+
1929
+ ```typescript
1930
+ const currentAppId = ref('app-1');
1931
+
1932
+ useChatHelper({
1933
+ requestData: {
1934
+ urlPrefix: 'https://api.example.com/',
1935
+ data: () => ({
1936
+ app_id: currentAppId.value,
1937
+ tenant_id: getTenantId(),
1938
+ }),
1939
+ },
1940
+ });
1941
+ ```
1942
+
1943
+ ### Q: 如何自定义流式事件的处理逻辑?
1944
+
1945
+ A: 继承 `AGUIProtocol` 并重写对应的事件处理方法:
1946
+
1947
+ ```typescript
1948
+ class MyProtocol extends AGUIProtocol {
1949
+ handleTextMessageChunkEvent(event: ITextMessageChunkEvent) {
1950
+ // 自定义逻辑:例如敏感词过滤
1951
+ const filteredText = filterSensitiveWords(event.delta);
1952
+
1953
+ // 调用父类方法继续处理
1954
+ super.handleTextMessageChunkEvent({
1955
+ ...event,
1956
+ delta: filteredText,
1957
+ });
1958
+ }
1959
+ }
1960
+
1961
+ useChatHelper({
1962
+ requestData: {
1963
+ /* ... */
1964
+ },
1965
+ protocol: new MyProtocol(),
1966
+ });
1967
+ ```
1968
+
1969
+ ### Q: 如何实现消息的本地缓存?
1970
+
1971
+ A: 使用 `watch` 监听消息变化并保存到本地存储:
1972
+
1973
+ ```typescript
1974
+ import { watch } from 'vue';
1975
+
1976
+ const { message, session } = useChatHelper({
1977
+ /* ... */
1978
+ });
1979
+
1980
+ // 监听消息变化并缓存
1981
+ watch(
1982
+ () => message.list.value,
1983
+ newList => {
1984
+ if (session.current?.sessionCode) {
1985
+ localStorage.setItem(`messages_${session.current.sessionCode}`, JSON.stringify(newList));
1986
+ }
1987
+ },
1988
+ { deep: true },
1989
+ );
1990
+
1991
+ // 加载缓存
1992
+ const loadCachedMessages = (sessionCode: string) => {
1993
+ const cached = localStorage.getItem(`messages_${sessionCode}`);
1994
+ if (cached) {
1995
+ message.list.value = JSON.parse(cached);
1996
+ }
1997
+ };
1998
+ ```
1999
+
2000
+ ## 贡献指南
2001
+
2002
+ 欢迎提交 Issue 和 Pull Request!
2003
+
2004
+ 在提交 PR 之前,请确保:
2005
+
2006
+ 1. 代码通过 ESLint 检查
2007
+ 2. 添加了必要的类型定义
2008
+ 3. 更新了相关文档
2009
+
2010
+ ## 更新日志
2011
+
2012
+ 查看 [CHANGELOG.md](./CHANGELOG.md) 了解版本更新历史。
2013
+
1115
2014
  ## License
1116
2015
 
1117
2016
  MIT License