@blueking/ai-ui-sdk 0.0.4 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -17,6 +17,9 @@ npm install @blueking/ai-ui-sdk
17
17
  - **思考过程可视化**:展示 AI 的思考过程和推理步骤
18
18
  - **内容总结**:快速对对话内容进行摘要总结
19
19
  - **统一样式**:提供一致的 UI 样式和图标
20
+ - **可拖拽和缩放的聊天窗口**:支持自定义位置和大小的聊天界面
21
+ - **快捷指令功能**:支持预设的快捷操作和自定义指令
22
+ - **多语言支持**:内置国际化支持
20
23
 
21
24
  ## 使用指南
22
25
 
@@ -28,35 +31,33 @@ import { useStyle, useChat, useClickProxy } from '@blueking/ai-ui-sdk';
28
31
  // 引入样式和图标
29
32
  useStyle();
30
33
 
31
- // 设置全局点击代理事件(用于处理折叠/展开、复制等操作)
34
+ // 设置全局点击代理事件
32
35
  useClickProxy();
33
36
 
34
37
  // 初始化聊天
35
38
  const {
36
39
  currentSession,
37
40
  sessionContents,
38
- chat,
41
+ sendChat,
39
42
  stopChat,
40
- setCurrentSession
43
+ setCurrentSession,
44
+ currentSessionLoading,
45
+ reGenerateChat,
46
+ reSendChat,
47
+ deleteChat,
41
48
  } = useChat({
42
- handleStart: (sessionCode, sessionContent) => {
43
- console.log('聊天开始', sessionCode);
44
- },
45
- handleText: (sessionCode, sessionContent) => {
46
- console.log('收到文本消息', sessionContent.content);
47
- },
48
- handleReferenceDoc: (sessionCode, sessionContent) => {
49
- console.log('收到引用资料');
49
+ handleStart: () => {
50
+ console.log('聊天开始');
50
51
  },
51
- handleThink: (sessionCode, sessionContent) => {
52
- console.log('AI 思考过程');
52
+ handleText: () => {
53
+ console.log('收到文本消息');
53
54
  },
54
- handleEnd: (sessionCode, sessionContent) => {
55
+ handleEnd: () => {
55
56
  console.log('聊天结束');
56
57
  },
57
- handleError: (sessionCode, sessionContent, code) => {
58
- console.error('聊天出错', sessionContent.content, code);
59
- }
58
+ requestOptions: {
59
+ url: 'your-api-endpoint',
60
+ },
60
61
  });
61
62
  ```
62
63
 
@@ -64,265 +65,197 @@ const {
64
65
 
65
66
  ```typescript
66
67
  // 设置当前会话
67
- setCurrentSession({
68
- sessionCode: 'session_123',
69
- sessionName: '新的对话',
70
- model: 'your-ai-model-id'
68
+ const session: ISession = {
69
+ sessionCode: '1',
70
+ sessionName: 'test',
71
+ model: '',
72
+ };
73
+ setCurrentSession(session);
74
+
75
+ // 发送聊天消息
76
+ sendChat({
77
+ message: '你的问题',
78
+ cite: '选中的文本', // 可选,引用的文本内容
71
79
  });
72
80
 
73
- // 发送聊天请求
74
- chat({
75
- sessionCode: 'session_123',
76
- url: 'https://your-ai-api-endpoint.com/chat',
77
- data: {
78
- // 可添加额外参数
79
- prompts: [...],
80
- temperature: 0.7
81
+ // 使用快捷指令发送消息
82
+ sendChat({
83
+ message: '快捷指令标签',
84
+ cite: '选中的文本',
85
+ shortcut: {
86
+ label: '快捷指令名称',
87
+ key: 'shortcut-key',
88
+ prompt: '预设的提示模板',
89
+ icon: '图标', // 可选
81
90
  },
82
- headers: {
83
- 'Authorization': 'Bearer your-api-token'
84
- }
85
91
  });
86
92
 
87
93
  // 停止正在进行的聊天
88
- stopChat('session_123');
94
+ stopChat(session.sessionCode);
89
95
  ```
90
96
 
91
- ### 使用内容总结功能
92
-
93
- ```typescript
94
- import { useSummary } from '@blueking/ai-ui-sdk';
95
-
96
- const { summary } = useSummary({
97
- handleStart: () => {
98
- console.log('开始生成摘要');
99
- },
100
- handleEnd: (text) => {
101
- console.log('摘要生成完成:', text);
102
- },
103
- handleError: (message, code) => {
104
- console.error('摘要生成失败:', message, code);
105
- }
106
- });
97
+ ### 完整的聊天界面示例
107
98
 
108
- // 生成摘要
109
- summary({
110
- content: '需要总结的长文本内容...',
111
- url: 'https://your-ai-api-endpoint.com/summary',
112
- headers: {
113
- 'Authorization': 'Bearer your-api-token'
114
- },
115
- model: 'your-ai-model-id' // 可选
116
- });
99
+ ```vue
100
+ <template>
101
+ <div class="ai-blueking-wrapper">
102
+ <vue-draggable-resizable
103
+ v-if="isShow"
104
+ :active="isShow"
105
+ :draggable="true"
106
+ :h="height"
107
+ :min-height="minHeight"
108
+ :min-width="minWidth"
109
+ :parent="true"
110
+ :prevent-deactivation="true"
111
+ :resizable="true"
112
+ :w="width"
113
+ :x="left"
114
+ :y="top"
115
+ class-name="ai-blueking-container-wrapper"
116
+ drag-handle=".drag-handle"
117
+ @dragging="handleDragging"
118
+ @resizing="handleResizing"
119
+ >
120
+ <div class="ai-blueking-container">
121
+ <!-- 聊天界面内容 -->
122
+ <div class="content-wrapper">
123
+ <div class="message-wrapper">
124
+ <render-message
125
+ v-for="(message, index) in sessionContents"
126
+ :key="message.id"
127
+ :message="message"
128
+ :index="index"
129
+ @delete="handleDelete"
130
+ @regenerate="handleRegenerate"
131
+ @resend="handleResend"
132
+ />
133
+ </div>
134
+
135
+ <div class="chat-input-container">
136
+ <ChatInputBox
137
+ v-model="inputMessage"
138
+ :loading="currentSessionLoading"
139
+ :shortcuts="shortcuts"
140
+ @send="handleSendMessage"
141
+ @shortcut-click="handleShortcutClick"
142
+ @stop="handleStop"
143
+ />
144
+ </div>
145
+ </div>
146
+ </div>
147
+ </vue-draggable-resizable>
148
+ </div>
149
+ </template>
117
150
  ```
118
151
 
119
- ## 组件说明
120
-
121
- ### 会话内容展示
122
-
123
- SDK 可以将 AI 回复渲染为包含以下元素的富文本内容:
124
-
125
- 1. **引用资料**:显示 AI 回答中引用的文档
126
- ```html
127
- <section class="knowledge-head click-close">
128
- 找到 X 篇资料参考
129
- <i class="ai-ui-sdk-icon icon-angle-up"></i>
130
- </section>
131
- <ul class="knowledge-body">
132
- <!-- 引用资料列表 -->
133
- </ul>
134
- ```
135
-
136
- 2. **思考过程**:展示 AI 的思考过程
137
- ```html
138
- <section class="think-head click-close">
139
- <i class="ai-ui-sdk-icon icon-sikao"></i>思考中...
140
- <i class="ai-ui-sdk-icon icon-angle-up"></i>
141
- </section>
142
- <section class="think-body">
143
- <!-- 思考内容 -->
144
- </section>
145
- ```
146
-
147
- ## 开发者指南
148
-
149
- ### 架构概览
150
-
151
- SDK 主要由以下模块组成:
152
+ ### 快捷指令配置
152
153
 
153
- - **核心 hooks**:
154
- - `useChat`: 管理聊天功能
155
- - `useSummary`: 处理内容总结
156
- - `useReferenceDoc`: 处理引用资料展示
157
- - `useThink`: 处理思考过程展示
158
- - `useClickProxy`: 处理点击事件代理
159
- - `useStyle`: 加载样式和图标
154
+ ```typescript
155
+ // 默认快捷指令示例
156
+ const DEFAULT_SHORTCUTS: ShortCut[] = [
157
+ {
158
+ label: '解释代码',
159
+ key: 'explain-code',
160
+ prompt: '请解释以下代码的功能:{ SELECTED_TEXT }',
161
+ icon: 'code-icon',
162
+ },
163
+ // 更多快捷指令...
164
+ ];
160
165
 
161
- - **工具类**:
162
- - `ChatHelper`: 处理与 AI 服务的通信
163
- - `type-transform`: 处理类型转换
166
+ // 在组件中使用
167
+ const props = defineProps<{
168
+ shortcuts?: ShortCut[];
169
+ }>();
170
+ ```
164
171
 
165
- ### 自定义聊天流程
172
+ ## 类型定义
166
173
 
167
- 可以通过覆盖 `useChat` 的回调函数来自定义聊天流程的各个环节:
174
+ ### 会话类型
168
175
 
169
176
  ```typescript
170
- const {
171
- chat,
172
- // 其他方法...
173
- } = useChat({
174
- // 开始时的处理
175
- handleStart: (sessionCode, sessionContent) => {
176
- // 自定义实现
177
- },
178
-
179
- // 接收文本时的处理
180
- handleText: (sessionCode, sessionContent) => {
181
- // 自定义实现
182
- },
183
-
184
- // 接收引用资料时的处理
185
- handleReferenceDoc: (sessionCode, sessionContent) => {
186
- // 自定义实现
187
- },
188
-
189
- // 接收思考过程时的处理
190
- handleThink: (sessionCode, sessionContent) => {
191
- // 自定义实现
192
- },
193
-
194
- // 结束时的处理
195
- handleEnd: (sessionCode, sessionContent) => {
196
- // 自定义实现
197
- },
198
-
199
- // 出错时的处理
200
- handleError: (sessionCode, sessionContent, code) => {
201
- // 自定义实现
202
- }
203
- });
177
+ interface ISession {
178
+ sessionCode: string;
179
+ sessionName: string;
180
+ model: string;
181
+ roleInfo?: {
182
+ collectionId: number;
183
+ collectionName: string;
184
+ content: ISessionPrompt[];
185
+ variables: any[];
186
+ };
187
+ }
204
188
  ```
205
189
 
206
- ### 会话内容状态管理
207
-
208
- SDK 使用 Vue 3 的响应式系统管理会话内容状态:
190
+ ### 聊天内容类型
209
191
 
210
192
  ```typescript
211
- const {
212
- currentSession, // 当前会话
213
- sessionContents, // 当前会话内容
214
- sessionContentsMap, // 所有会话内容的映射
215
- sessionLoadingMap, // 会话加载状态的映射
216
- prompts, // 计算得到的所有提示
217
- // 其他方法...
218
- } = useChat();
193
+ interface ISessionContent {
194
+ id?: number;
195
+ role: SessionContentRole;
196
+ content: string;
197
+ status?: SessionContentStatus;
198
+ sessionCode: string;
199
+ cite?: string;
200
+ time?: string;
201
+ }
219
202
  ```
220
203
 
221
- ### 后端流式响应格式
222
-
223
- SDK 期望后端返回的流式响应格式如下:
204
+ ### 快捷指令类型
224
205
 
206
+ ```typescript
207
+ interface ShortCut {
208
+ label: string;
209
+ key: string;
210
+ prompt: string;
211
+ icon?: string;
212
+ }
225
213
  ```
226
- data: {"event":"text","content":"文本内容","cover":false}
227
-
228
- data: {"event":"reference_doc","documents":[{"metadata":{"file_path":"文件路径","path":"路径","preview_path":"预览路径"}}]}
229
214
 
230
- data: {"event":"think","content":"思考内容","cover":false,"elapsed_time":1000}
215
+ ## 高级功能
231
216
 
232
- data: {"event":"done"}
217
+ ### 消息重发和重新生成
233
218
 
234
- data: {"event":"error","message":"错误信息","code":"错误码"}
219
+ ```typescript
220
+ // 重新生成 AI 回复
221
+ const handleRegenerate = (index: number) => {
222
+ reGenerateChat(index);
223
+ };
224
+
225
+ // 重新发送用户消息
226
+ const handleResend = (index: number, value: { message: string; cite: string }) => {
227
+ reSendChat(index, value);
228
+ };
235
229
  ```
236
230
 
237
- ## 示例应用
238
-
239
- 以下是一个简单的 AI 聊天应用示例:
240
-
241
- ```vue
242
- <template>
243
- <div class="chat-container">
244
- <div class="chat-messages" ref="messagesRef">
245
- <div v-for="message in sessionContents" :key="message.id"
246
- :class="['message', message.role]">
247
- <div v-if="message.role === 'user'" class="user-message">
248
- {{ message.content }}
249
- </div>
250
- <div v-else-if="message.role === 'ai'" class="ai-message"
251
- v-html="message.content"></div>
252
- </div>
253
- </div>
254
-
255
- <div class="chat-input">
256
- <input v-model="inputMessage" @keyup.enter="sendMessage"
257
- placeholder="输入您的问题..."/>
258
- <button @click="sendMessage">发送</button>
259
- </div>
260
- </div>
261
- </template>
231
+ ### 消息删除
262
232
 
263
- <script setup>
264
- import { ref, onMounted } from 'vue';
265
- import { useStyle, useChat, useClickProxy, SessionContentRole } from '@blueking/ai-ui-sdk';
233
+ ```typescript
234
+ const handleDelete = (index: number) => {
235
+ deleteChat(index);
236
+ };
237
+ ```
266
238
 
267
- useStyle();
268
- useClickProxy();
239
+ ## 样式定制
269
240
 
270
- const inputMessage = ref('');
271
- const messagesRef = ref(null);
241
+ SDK 提供了基础的样式和主题支持,你可以通过覆盖 CSS 变量来自定义界面外观:
272
242
 
273
- const {
274
- sessionContents,
275
- chat,
276
- setCurrentSession,
277
- plusSessionContent
278
- } = useChat();
243
+ ```scss
244
+ .ai-blueking-container {
245
+ // 自定义样式
246
+ }
279
247
 
280
- // 设置当前会话
281
- onMounted(() => {
282
- setCurrentSession({
283
- sessionCode: 'demo_session',
284
- sessionName: '演示对话',
285
- model: 'your-model-id'
286
- });
287
- });
248
+ .message-wrapper {
249
+ // 消息列表样式
250
+ }
288
251
 
289
- // 发送消息
290
- const sendMessage = () => {
291
- if (!inputMessage.value.trim()) return;
292
-
293
- // 添加用户消息
294
- plusSessionContent('demo_session', {
295
- sessionCode: 'demo_session',
296
- role: SessionContentRole.User,
297
- content: inputMessage.value
298
- });
299
-
300
- // 发送聊天请求
301
- chat({
302
- sessionCode: 'demo_session',
303
- url: 'https://your-api-endpoint/chat',
304
- data: {
305
- model: 'your-model-id',
306
- message: inputMessage.value
307
- }
308
- });
309
-
310
- // 清空输入框
311
- inputMessage.value = '';
252
+ .chat-input-container {
253
+ // 输入框样式
312
254
  }
313
- </script>
314
255
  ```
315
256
 
316
- ## API 参考
317
-
318
- 请参考源代码中的类型定义获取完整 API 参考。
319
-
320
257
  ## 注意事项
321
258
 
322
- - SDK 依赖 Vue 3 和 bkui-vue,请确保项目中已安装这些依赖
323
- - 确保 AI 服务支持流式响应格式
324
- - 处理复杂的 markdown 或代码展示时,建议使用专门的渲染库
325
-
326
- ## 许可证
259
+ - SDK 依赖 Vue 3,请确保项目中已安装
260
+ - 建议使用 TypeScript 以获得更好的类型提示
327
261
 
328
- ISC
@@ -1,4 +1,4 @@
1
- import type { ISessionContent, ISession, ISessionPrompt, ChatCallbacks } from '../types/type.ts';
1
+ import type { ISessionContent, ISession, ISessionPrompt, ChatCallbacks, BasicChatContent, ShortcutChatContent } from '../types/type.ts';
2
2
  import { SessionContentRole, SessionContentStatus } from '../types/enum';
3
3
  type SessionContentsMap = {
4
4
  [key: string]: ISessionContent[];
@@ -49,10 +49,7 @@ export declare const useChat: <T extends ISession = ISession>({ handleStart, han
49
49
  url: string;
50
50
  headers?: Record<string, string>;
51
51
  }) => void;
52
- sendChat: (content: {
53
- message: string;
54
- cite?: string;
55
- }, callback?: () => void) => void;
52
+ sendChat: (content: BasicChatContent | ShortcutChatContent, callback?: () => void) => void;
56
53
  stopChat: (sessionCode: string) => void;
57
54
  plusSessionContent: (sessionCode: string, sessionContent: ISessionContent) => void;
58
55
  updateSessionContent: (sessionContent: ISessionContent) => void;
@@ -70,5 +67,6 @@ export declare const useChat: <T extends ISession = ISession>({ handleStart, han
70
67
  message: string;
71
68
  cite?: string;
72
69
  }, callback?: () => void) => void;
70
+ deleteChat: (index: number) => void;
73
71
  };
74
72
  export {};
package/dist/main.js CHANGED
@@ -243,6 +243,6 @@ url(${m}) format("embedded-opentype");
243
243
  &~ .knowledge-head {
244
244
  margin-bottom: 8px;
245
245
  }
246
- }`;(A=document.createElement("style")).textContent=e,document.head.appendChild(A)},t=()=>{A&&(A.remove(),A=null)};(0,a.onBeforeMount)(()=>{e()}),(0,a.onBeforeUnmount)(()=>{t()})};function f(A,e,t,n,i,o,l){try{var s=A[o](l),a=s.value}catch(A){t(A);return}s.done?e(a):Promise.resolve(a).then(n,i)}function Q(A){return function(){var e=this,t=arguments;return new Promise(function(n,i){var o=A.apply(e,t);function l(A){f(o,n,i,l,s,"next",A)}function s(A){f(o,n,i,l,s,"throw",A)}l(void 0)})}}function w(A,e,t){return e in A?Object.defineProperty(A,e,{value:t,enumerable:!0,configurable:!0,writable:!0}):A[e]=t,A}class b{stream({sessionCode:A,url:e,headers:t,data:n}){var i=this;return Q(function*(){var o,l;yield null===(o=i.handleStart)||void 0===o?void 0:o.call(i,A);let s=new AbortController;i.controllerMap[A]=s,fetch(e,{method:"post",signal:s.signal,headers:function(A){for(var e=1;e<arguments.length;e++){var t=null!=arguments[e]?arguments[e]:{},n=Object.keys(t);"function"==typeof Object.getOwnPropertySymbols&&(n=n.concat(Object.getOwnPropertySymbols(t).filter(function(A){return Object.getOwnPropertyDescriptor(t,A).enumerable}))),n.forEach(function(e){w(A,e,t[e])})}return A}({"Content-Type":"application/json"},t),mode:"cors",credentials:"include",body:JSON.stringify(n)}).then((l=Q(function*(e){let t=e.body.pipeThrough(new window.TextDecoderStream).getReader(),n="";for(;;)try{let{value:o,done:l}=yield t.read();if(!e.ok){i.handleError(A,o||e.statusText,e.status);break}if(l){i.handleEnd(A);break}(n+o.toString()).split("\n").forEach(t=>{let o=t.replace("data:","").trim();if(d(o)){var l,s;let{event:t,content:a,cover:r,documents:d,result:c,code:u,elapsed_time:B,message:g}=JSON.parse(o);if(!1===c||200!==e.status){i.handleError(A,g||"模型调用失败",u);return}switch(t){case"text":i.handleText(A,a,r);break;case"reference_doc":null===(l=i.handleReferenceDoc)||void 0===l||l.call(i,A,d,r);break;case"think":null===(s=i.handleThink)||void 0===s||s.call(i,A,a,r,B);break;case"done":i.handleEnd(A,r?a:"");break;case"error":i.handleError(A,g||"模型调用失败",u)}n=""}else o&&(n=o)})}catch(e){(null==e?void 0:e.code)!==20&&i.handleError(A,`模型调用失败:${e.message}`,e.code);break}}),function(A){return l.apply(this,arguments)}))})()}stop(A){var e,t;return null===(t=this.controllerMap[A])||void 0===t||null===(e=t.abort)||void 0===e||e.call(t),this.handleEnd(A)}constructor({handleStart:A,handleText:e,handleReferenceDoc:t,handleThink:n,handleEnd:i,handleError:o}){w(this,"handleStart",void 0),w(this,"handleText",void 0),w(this,"handleReferenceDoc",void 0),w(this,"handleThink",void 0),w(this,"handleEnd",void 0),w(this,"handleError",void 0),w(this,"controllerMap",void 0),this.handleStart=A,this.handleText=e,this.handleReferenceDoc=t,this.handleThink=n,this.handleEnd=i,this.handleError=o,this.controllerMap={}}}let k=({handleStart:A,handleText:i,handleReferenceDoc:o,handleThink:l,handleEnd:r,handleError:d,requestOptions:c}={})=>{let u="内容正在生成中...",h=(0,a.ref)(),v=(0,a.ref)({}),f=(0,a.ref)([]),Q={},w=(0,a.computed)(()=>{let A=[];for(let n=f.value.length-1;n>=0;n--){let i=f.value[n],o=n+1,l=f.value[o];for(;l&&![t.Ai,t.TokenExpired,t.ImageNotSupported,t.Pause,t.Guide].includes(l.role);)o+=1,l=f.value[o];if(i.role===t.System)break;i.status===e.Fail||(null==l?void 0:l.status)===e.Fail&&[t.User,t.UserImage].includes(i.role)||[t.Time,t.System].includes(i.role)||A.unshift(i)}return A}),k=(0,a.computed)(()=>{var A;let e=null===(A=h.value)||void 0===A?void 0:A.sessionCode;return!!e&&v.value[e]}),C=(0,a.computed)(()=>{var A,e,n;let i=[],o=[],l=[],a=f.value.findLastIndex(A=>A.role===t.System&&["已启用角色","已启用模型"].some(e=>A.content.includes(e))),r=0;null===(n=h.value)||void 0===n||null===(e=n.roleInfo)||void 0===e||null===(A=e.content)||void 0===A||A.forEach(A=>{let e=f.value[a+1+r],n=f.value[a+2+r];if((null==e?void 0:e.content)===A.content&&e.role!==t.System&&(i.push(s(e)),r+=1,(null==e?void 0:e.role)===t.Pause)){for(;n&&n.role===t.System;)r+=1,n=f.value[a+1+r];for(;n&&[t.User,t.UserImage].includes(n.role);)i.push(s(n)),r+=1,n=f.value[a+1+r]}}),o.push(...w.value.map(s));let d=A=>{let e=!0;for(let n=A;n<i.length;n++){var t;i[n].content!==(null===(t=o[n-A])||void 0===t?void 0:t.content)&&(e=!1)}return e};for(let A=0;A<i.length;A++){let e=i[A];if(d(A))break;l.push(e)}return l.push(...o),l.forEach(A=>{A.content=m(A.content),A.content=g(A.content)}),l}),y=new b({handleStart:R,handleText:function(A,t,n){let o=I(A);if(o.content===u)o.content=t;else{if(o.status!==e.Loading)return;o.content=n?t:o.content+t}return null==i?void 0:i(A,o)},handleReferenceDoc:function(A,e,t){let n=I(A),i=B(e);return n.content=t?i:n.content+i,null==o?void 0:o(A,n)},handleThink:function(A,e,t,n){let i=I(A);return i.content=p(i.content,e,t,n),null==l?void 0:l(A,i)},handleEnd:function(A,t){let n=I(A);if(n.status===e.Loading)return v.value[A]=!1,t&&(n.content=t),n.status=e.Success,null==r?void 0:r(A,n);(n.content===u||E(n.content))&&D(A,"聊天内容已中断")},handleError:D});function I(A){var e,t;return(null===(e=h.value)||void 0===e?void 0:e.sessionCode)===A?f.value.at(-1):null===(t=Q[A])||void 0===t?void 0:t.at(-1)}function G(A){var e;return(null===(e=h.value)||void 0===e?void 0:e.sessionCode)===A?f.value:Q[A]}function x(A,e){G(A).push(e)}function Y(A,e,n){var i;let o=(null===(i=h.value)||void 0===i?void 0:i.sessionCode)===A?f.value:Q[A],l=[];return e.forEach(A=>{let e=o.findIndex(e=>e.id===A);if(e>-1){let i=o[e-1],s=o[e+1],a=o[e+2];if(o.splice(e,1),l.push(A),[t.Hidden].includes(null==i?void 0:i.role)){let A=o.findIndex(A=>A.id===i.id);o.splice(A,1),l.push(i.id)}if((null==s?void 0:s.role)===t.Ai||n&&[t.Ai,t.Guide,t.TokenExpired,t.ImageNotSupported,t.Pause].includes(null==s?void 0:s.role)){let A=o.findIndex(A=>A.id===s.id);o.splice(A,1),l.push(s.id)}if([t.Guide].includes(null==a?void 0:a.role)&&n){let A=o.findIndex(A=>A.id===a.id);o.splice(A,1),l.push(a.id)}}}),l}function R(n){v.value[n]=!0;let i={sessionCode:n,role:t.Ai,status:e.Loading,content:u};return x(n,i),null==A?void 0:A(n,i)}function D(A,i,o){let l=I(A);return l.status=e.Fail,l.content=i,v.value[A]=!1,o===n.TokenExpired&&(l.content="抱歉,您的剩余 Token 不足,无法返回回答内容,请先清空当前会话(上下文仍会作为历史记录保留))",l.role=t.TokenExpired),o===n.ImageNotSupported&&(l.content="抱歉,当前模型不支持图片内容解析",l.role=t.ImageNotSupported),null==d?void 0:d(A,l,o)}function F(A,n){var i,o,l;if(!(null===(i=h.value)||void 0===i?void 0:i.sessionCode)||!(null==c?void 0:c.url)||k.value)return;let{message:s,cite:a}=A;f.value.push({sessionCode:null===(o=h.value)||void 0===o?void 0:o.sessionCode,content:s,role:t.User,status:e.Success,cite:a});let r=a?`${s}: "${a}"`:s;y.stream({sessionCode:null===(l=h.value)||void 0===l?void 0:l.sessionCode,url:c.url,headers:null==c?void 0:c.headers,data:{inputs:{chat_history:C.value.slice(0,C.value.length-1),input:r}}}),null==n||n()}return{currentSession:h,sessionContents:f,sessionContentsMap:Q,sessionLoadingMap:v,prompts:C,currentSessionLoading:k,chat:function({sessionCode:A,data:e,url:t,headers:n}){y.stream({sessionCode:A,url:t,data:e,headers:n||(null==c?void 0:c.headers)})},sendChat:F,stopChat:function(A){y.stop(A)},plusSessionContent:x,updateSessionContent:function(A){var e;let t=((null===(e=h.value)||void 0===e?void 0:e.sessionCode)===A.sessionCode?f.value:Q[A.sessionCode]).find(e=>+(e.id||0)==+(A.id||0));t&&Object.assign(t,A)},getSessionContentById:function(A,e){return G(e).find(e=>e.id===A)},getLastSessionContentBySessionCode:I,getSessionContentsBySessionCode:G,setCurrentSession:function(A){h.value=A,(null==A?void 0:A.sessionCode)&&(Q[A.sessionCode]||(Q[A.sessionCode]=[]),f.value=Q[A.sessionCode])},setSessionContents:function(A){h.value&&(Q[h.value.sessionCode]=A,f.value=A)},deleteSessionContent:function(A,e){return Y(A,[e],!0)},deleteSessionContents:function(A,e){return Y(A,e,!1)},handleStartChat:R,handleErrorChat:D,reGenerateChat:function(A){if(f.value[A].role!==t.Ai)return;let e=f.value[A-1].content,n=f.value[A-1].cite;f.value.splice(A-1,2),F({message:e,cite:n})},reSendChat:function(A,{message:e,cite:n},i){f.value[A].role===t.User&&(f.value.splice(A,f.value.length-A),F({message:e,cite:n},i))}}},C=({handleStart:e,handleEnd:t,handleError:n}={})=>{let i="",o=new b({handleStart:()=>(i="",null==e?void 0:e()),handleText:(A,e,t)=>{t||"正在思考..."===i?i=e:i+=e},handleEnd:()=>("无话可说"===i&&(i=""),null==t?void 0:t(i)),handleError:(A,e,t)=>null==n?void 0:n(e,t)});return{summary:({content:e,url:t,headers:n,model:i})=>{let l=m(g(e)),s=[{role:A.User,content:`你是一个总结大师,请帮助我对下面这段话进行总结。要求总结精炼,不超过 15 个字!且末尾没有标点符号!请注意:如果你无法总结,请回复“无话可说”!
246
+ }`;(A=document.createElement("style")).textContent=e,document.head.appendChild(A)},t=()=>{A&&(A.remove(),A=null)};(0,a.onBeforeMount)(()=>{e()}),(0,a.onBeforeUnmount)(()=>{t()})};function f(A,e,t,n,i,o,l){try{var s=A[o](l),a=s.value}catch(A){t(A);return}s.done?e(a):Promise.resolve(a).then(n,i)}function Q(A){return function(){var e=this,t=arguments;return new Promise(function(n,i){var o=A.apply(e,t);function l(A){f(o,n,i,l,s,"next",A)}function s(A){f(o,n,i,l,s,"throw",A)}l(void 0)})}}function w(A,e,t){return e in A?Object.defineProperty(A,e,{value:t,enumerable:!0,configurable:!0,writable:!0}):A[e]=t,A}class b{stream({sessionCode:A,url:e,headers:t,data:n}){var i=this;return Q(function*(){var o,l;yield null===(o=i.handleStart)||void 0===o?void 0:o.call(i,A);let s=new AbortController;i.controllerMap[A]=s,fetch(e,{method:"post",signal:s.signal,headers:function(A){for(var e=1;e<arguments.length;e++){var t=null!=arguments[e]?arguments[e]:{},n=Object.keys(t);"function"==typeof Object.getOwnPropertySymbols&&(n=n.concat(Object.getOwnPropertySymbols(t).filter(function(A){return Object.getOwnPropertyDescriptor(t,A).enumerable}))),n.forEach(function(e){w(A,e,t[e])})}return A}({"Content-Type":"application/json"},t),mode:"cors",credentials:"include",body:JSON.stringify(n)}).then((l=Q(function*(e){let t=e.body.pipeThrough(new window.TextDecoderStream).getReader(),n="";for(;;)try{let{value:o,done:l}=yield t.read();if(!e.ok){i.handleError(A,o||e.statusText,e.status);break}if(l){i.handleEnd(A);break}(n+o.toString()).split("\n").forEach(t=>{let o=t.replace("data:","").trim();if(d(o)){var l,s;let{event:t,content:a,cover:r,documents:d,result:c,code:u,elapsed_time:B,message:g}=JSON.parse(o);if(!1===c||200!==e.status){i.handleError(A,g||"模型调用失败",u);return}switch(t){case"text":i.handleText(A,a,r);break;case"reference_doc":null===(l=i.handleReferenceDoc)||void 0===l||l.call(i,A,d,r);break;case"think":null===(s=i.handleThink)||void 0===s||s.call(i,A,a,r,B);break;case"done":i.handleEnd(A,r?a:"");break;case"error":i.handleError(A,g||"模型调用失败",u)}n=""}else o&&(n=o)})}catch(e){(null==e?void 0:e.code)!==20&&i.handleError(A,`模型调用失败:${e.message}`,e.code);break}}),function(A){return l.apply(this,arguments)}))})()}stop(A){var e,t;return null===(t=this.controllerMap[A])||void 0===t||null===(e=t.abort)||void 0===e||e.call(t),this.handleEnd(A)}constructor({handleStart:A,handleText:e,handleReferenceDoc:t,handleThink:n,handleEnd:i,handleError:o}){w(this,"handleStart",void 0),w(this,"handleText",void 0),w(this,"handleReferenceDoc",void 0),w(this,"handleThink",void 0),w(this,"handleEnd",void 0),w(this,"handleError",void 0),w(this,"controllerMap",void 0),this.handleStart=A,this.handleText=e,this.handleReferenceDoc=t,this.handleThink=n,this.handleEnd=i,this.handleError=o,this.controllerMap={}}}let k=({handleStart:A,handleText:i,handleReferenceDoc:o,handleThink:l,handleEnd:r,handleError:d,requestOptions:c}={})=>{let u="内容正在生成中...",h=(0,a.ref)(),v=(0,a.ref)({}),f=(0,a.ref)([]),Q={},w=(0,a.computed)(()=>{let A=[];for(let n=f.value.length-1;n>=0;n--){let i=f.value[n],o=n+1,l=f.value[o];for(;l&&![t.Ai,t.TokenExpired,t.ImageNotSupported,t.Pause,t.Guide].includes(l.role);)o+=1,l=f.value[o];if(i.role===t.System)break;i.status===e.Fail||(null==l?void 0:l.status)===e.Fail&&[t.User,t.UserImage].includes(i.role)||[t.Time,t.System].includes(i.role)||A.unshift(i)}return A}),k=(0,a.computed)(()=>{var A;let e=null===(A=h.value)||void 0===A?void 0:A.sessionCode;return!!e&&v.value[e]}),C=(0,a.computed)(()=>{var A,e,n;let i=[],o=[],l=[],a=f.value.findLastIndex(A=>A.role===t.System&&["已启用角色","已启用模型"].some(e=>A.content.includes(e))),r=0;null===(n=h.value)||void 0===n||null===(e=n.roleInfo)||void 0===e||null===(A=e.content)||void 0===A||A.forEach(A=>{let e=f.value[a+1+r],n=f.value[a+2+r];if((null==e?void 0:e.content)===A.content&&e.role!==t.System&&(i.push(s(e)),r+=1,(null==e?void 0:e.role)===t.Pause)){for(;n&&n.role===t.System;)r+=1,n=f.value[a+1+r];for(;n&&[t.User,t.UserImage].includes(n.role);)i.push(s(n)),r+=1,n=f.value[a+1+r]}}),o.push(...w.value.map(s));let d=A=>{let e=!0;for(let n=A;n<i.length;n++){var t;i[n].content!==(null===(t=o[n-A])||void 0===t?void 0:t.content)&&(e=!1)}return e};for(let A=0;A<i.length;A++){let e=i[A];if(d(A))break;l.push(e)}return l.push(...o),l.forEach(A=>{A.content=m(A.content),A.content=g(A.content)}),l}),y=new b({handleStart:R,handleText:function(A,t,n){let o=I(A);if(o.content===u)o.content=t;else{if(o.status!==e.Loading)return;o.content=n?t:o.content+t}return null==i?void 0:i(A,o)},handleReferenceDoc:function(A,e,t){let n=I(A),i=B(e);return n.content=t?i:n.content+i,null==o?void 0:o(A,n)},handleThink:function(A,e,t,n){let i=I(A);return i.content=p(i.content,e,t,n),null==l?void 0:l(A,i)},handleEnd:function(A,t){let n=I(A);if(n.status===e.Loading)return v.value[A]=!1,t&&(n.content=t),n.status=e.Success,null==r?void 0:r(A,n);(n.content===u||E(n.content))&&D(A,"聊天内容已中断")},handleError:D});function I(A){var e,t;return(null===(e=h.value)||void 0===e?void 0:e.sessionCode)===A?f.value.at(-1):null===(t=Q[A])||void 0===t?void 0:t.at(-1)}function G(A){var e;return(null===(e=h.value)||void 0===e?void 0:e.sessionCode)===A?f.value:Q[A]}function x(A,e){G(A).push(e)}function Y(A,e,n){var i;let o=(null===(i=h.value)||void 0===i?void 0:i.sessionCode)===A?f.value:Q[A],l=[];return e.forEach(A=>{let e=o.findIndex(e=>e.id===A);if(e>-1){let i=o[e-1],s=o[e+1],a=o[e+2];if(o.splice(e,1),l.push(A),[t.Hidden].includes(null==i?void 0:i.role)){let A=o.findIndex(A=>A.id===i.id);o.splice(A,1),l.push(i.id)}if((null==s?void 0:s.role)===t.Ai||n&&[t.Ai,t.Guide,t.TokenExpired,t.ImageNotSupported,t.Pause].includes(null==s?void 0:s.role)){let A=o.findIndex(A=>A.id===s.id);o.splice(A,1),l.push(s.id)}if([t.Guide].includes(null==a?void 0:a.role)&&n){let A=o.findIndex(A=>A.id===a.id);o.splice(A,1),l.push(a.id)}}}),l}function R(n){v.value[n]=!0;let i={sessionCode:n,role:t.Ai,status:e.Loading,content:u};return x(n,i),null==A?void 0:A(n,i)}function D(A,i,o){let l=I(A);return l.status=e.Fail,l.content=i,v.value[A]=!1,o===n.TokenExpired&&(l.content="抱歉,您的剩余 Token 不足,无法返回回答内容,请先清空当前会话(上下文仍会作为历史记录保留))",l.role=t.TokenExpired),o===n.ImageNotSupported&&(l.content="抱歉,当前模型不支持图片内容解析",l.role=t.ImageNotSupported),null==d?void 0:d(A,l,o)}function F(A,n){var i,o,l;if(!(null===(i=h.value)||void 0===i?void 0:i.sessionCode)||!(null==c?void 0:c.url)||k.value)return;let{message:s,cite:a,shortcut:r}=A,d="";d=r?r.prompt.replace("{ SELECTED_TEXT }",a):a?`${s}: "${a}"`:s,f.value.push({sessionCode:null===(o=h.value)||void 0===o?void 0:o.sessionCode,content:s,role:t.User,status:e.Success,cite:a}),y.stream({sessionCode:null===(l=h.value)||void 0===l?void 0:l.sessionCode,url:c.url,headers:null==c?void 0:c.headers,data:{inputs:{chat_history:C.value.slice(0,C.value.length-1),input:d}}}),null==n||n()}return{currentSession:h,sessionContents:f,sessionContentsMap:Q,sessionLoadingMap:v,prompts:C,currentSessionLoading:k,chat:function({sessionCode:A,data:e,url:t,headers:n}){y.stream({sessionCode:A,url:t,data:e,headers:n||(null==c?void 0:c.headers)})},sendChat:F,stopChat:function(A){y.stop(A)},plusSessionContent:x,updateSessionContent:function(A){var e;let t=((null===(e=h.value)||void 0===e?void 0:e.sessionCode)===A.sessionCode?f.value:Q[A.sessionCode]).find(e=>+(e.id||0)==+(A.id||0));t&&Object.assign(t,A)},getSessionContentById:function(A,e){return G(e).find(e=>e.id===A)},getLastSessionContentBySessionCode:I,getSessionContentsBySessionCode:G,setCurrentSession:function(A){h.value=A,(null==A?void 0:A.sessionCode)&&(Q[A.sessionCode]||(Q[A.sessionCode]=[]),f.value=Q[A.sessionCode])},setSessionContents:function(A){h.value&&(Q[h.value.sessionCode]=A,f.value=A)},deleteSessionContent:function(A,e){return Y(A,[e],!0)},deleteSessionContents:function(A,e){return Y(A,e,!1)},handleStartChat:R,handleErrorChat:D,reGenerateChat:function(A){if(f.value[A].role!==t.Ai)return;let e=f.value[A-1].content,n=f.value[A-1].cite;f.value.splice(A-1,2),F({message:e,cite:n})},reSendChat:function(A,{message:e,cite:n},i){f.value[A].role===t.User&&(f.value.splice(A,f.value.length-A),F({message:e,cite:n},i))},deleteChat:function(A){if(f.value[A].role===t.User){f.value.splice(A,2);return}f.value.splice(A-1,2)}}},C=({handleStart:e,handleEnd:t,handleError:n}={})=>{let i="",o=new b({handleStart:()=>(i="",null==e?void 0:e()),handleText:(A,e,t)=>{t||"正在思考..."===i?i=e:i+=e},handleEnd:()=>("无话可说"===i&&(i=""),null==t?void 0:t(i)),handleError:(A,e,t)=>null==n?void 0:n(e,t)});return{summary:({content:e,url:t,headers:n,model:i})=>{let l=m(g(e)),s=[{role:A.User,content:`你是一个总结大师,请帮助我对下面这段话进行总结。要求总结精炼,不超过 15 个字!且末尾没有标点符号!请注意:如果你无法总结,请回复“无话可说”!
247
247
  文字如下:
248
248
  ${l}`}];o.stream({sessionCode:"summary",url:t,data:{prompts:s,model:i},headers:n})}}},y=()=>{i(86),i(84),h(),v()},I=l.default;if("undefined"!=typeof window){let{currentScript:A}=window.document,e=A&&A.src.match(/(.+\/)[^/]+\.js(\?.*)?$/);e&&(i.p=e[1])}})(),o})());
@@ -58,3 +58,19 @@ export interface SummaryCallbacks {
58
58
  handleEnd?: (text: string) => void;
59
59
  handleError?: (message: string, code?: string) => void;
60
60
  }
61
+ export type BasicChatContent = {
62
+ message: string;
63
+ cite?: string;
64
+ shortcut?: never;
65
+ };
66
+ export type ShortcutChatContent = {
67
+ message: string;
68
+ cite: string;
69
+ shortcut: ShortCut;
70
+ };
71
+ export interface ShortCut {
72
+ label: string;
73
+ key: string;
74
+ prompt: string;
75
+ icon?: string;
76
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blueking/ai-ui-sdk",
3
- "version": "0.0.4",
3
+ "version": "0.0.5",
4
4
  "description": "蓝鲸AI UI SDK",
5
5
  "main": "dist/main.js",
6
6
  "types": "dist/main.d.ts",