@blueking/ai-ui-sdk 0.0.1-beta.1 → 0.0.1-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,32 @@
1
+ import type { Document } from '../types/type';
2
+ type HandleStart = (sessionCode: string) => any;
3
+ type HandleText = (sessionCode: string, message: string, cover?: boolean) => void;
4
+ type HandleReferenceDoc = (sessionCode: string, documents: Document[], cover?: boolean) => void;
5
+ type HandleThink = (sessionCode: string, content: string, cover?: boolean, elapsed_time?: number) => void;
6
+ type HandleEnd = (sessionCode: string, message?: string) => void;
7
+ type HandleError = (sessionCode: string, message: string, code: string) => any;
8
+ export declare class ChatHelper {
9
+ handleStart: HandleStart;
10
+ handleText: HandleText;
11
+ handleReferenceDoc?: HandleReferenceDoc;
12
+ handleThink?: HandleThink;
13
+ handleEnd: HandleEnd;
14
+ handleError: HandleError;
15
+ controllerMap: Record<string, AbortController>;
16
+ constructor({ handleStart, handleText, handleReferenceDoc, handleThink, handleEnd, handleError, }: {
17
+ handleStart: HandleStart;
18
+ handleText: HandleText;
19
+ handleReferenceDoc?: HandleReferenceDoc;
20
+ handleThink?: HandleThink;
21
+ handleEnd: HandleEnd;
22
+ handleError: HandleError;
23
+ });
24
+ stream({ sessionCode, url, headers, param, }: {
25
+ sessionCode: string;
26
+ url: string;
27
+ headers?: Record<string, string>;
28
+ param?: Record<string, any>;
29
+ }): Promise<void>;
30
+ stop(sessionCode: string): void;
31
+ }
32
+ export {};
@@ -0,0 +1,12 @@
1
+ /**
2
+ * 判断是否是 JSON 字符串
3
+ * @param str 字符串
4
+ * @returns 是否是 JSON 字符串
5
+ */
6
+ export declare const isJSON: (str: string) => boolean;
7
+ /**
8
+ * 响应时间格式化
9
+ * @param val 待格式化时间,xxms
10
+ * @returns 格式化后的时间,xx小时xx分钟xx秒
11
+ */
12
+ export declare function durationFormatter(val: number): string;
@@ -0,0 +1,14 @@
1
+ import type { Document } from '../types/type';
2
+ /**
3
+ * 获取引用资料的 HTML 内容
4
+ * @param documents
5
+ * @returns
6
+ */
7
+ export declare const getHtmlContentFromDocuments: (documents: Document[]) => string;
8
+ /**
9
+ * 移除引用资料的 HTML 内容
10
+ * @param content
11
+ * @returns
12
+ */
13
+ export declare const removeReferenceDoc: (content: string) => string;
14
+ export declare const useReferenceDoc: () => void;
@@ -0,0 +1,21 @@
1
+ /**
2
+ * 获取思考的 HTML 内容
3
+ * @param chatContent 聊天内容
4
+ * @param content 思考内容
5
+ * @param elapsedTime 思考时间
6
+ * @returns 完整的 chatContent
7
+ */
8
+ export declare const getHtmlContentFromThink: (chatContent: string, content: string, cover?: boolean, elapsedTime?: number) => string;
9
+ /**
10
+ * 移除思考的 HTML 内容
11
+ * @param content
12
+ * @returns
13
+ */
14
+ export declare const removeThink: (content: string) => string;
15
+ /**
16
+ * 判断是否是思考中
17
+ * @param content
18
+ * @returns
19
+ */
20
+ export declare const isThinking: (content: string) => boolean | null;
21
+ export declare const useThink: () => void;
package/dist/main.d.ts ADDED
@@ -0,0 +1,59 @@
1
+ import type { ISessionContent } from './types/type.ts';
2
+ import { SessionContentRole, SessionContentStatus } from './types/enum';
3
+ type SessionContentsMap = {
4
+ [key: string]: ISessionContent[];
5
+ };
6
+ type SessionLoadingMap = {
7
+ [key: string]: boolean;
8
+ };
9
+ export type { ISessionContent, };
10
+ export { SessionContentRole, SessionContentStatus, };
11
+ export interface AICallbacks {
12
+ handleStart?: (sessionCode: string, sessionContent: ISessionContent) => void;
13
+ handleText?: (sessionCode: string, sessionContent: ISessionContent) => void;
14
+ handleReferenceDoc?: (sessionCode: string, sessionContent: ISessionContent) => void;
15
+ handleThink?: (sessionCode: string, sessionContent: ISessionContent) => void;
16
+ handleEnd?: (sessionCode: string, sessionContent: ISessionContent) => void;
17
+ handleError?: (sessionCode: string, code: string | undefined, sessionContent: ISessionContent) => void;
18
+ }
19
+ export declare const useAI: ({ handleStart, handleText, handleReferenceDoc, handleThink, handleEnd, handleError, }: AICallbacks) => {
20
+ sessionContents: import("vue").Ref<{
21
+ id?: number | undefined;
22
+ createdAt?: string | undefined;
23
+ createdBy?: string | undefined;
24
+ updatedAt?: string | undefined;
25
+ updatedBy?: string | undefined;
26
+ spaceId?: string | undefined;
27
+ liked?: boolean | undefined;
28
+ role: SessionContentRole;
29
+ content: string;
30
+ rate?: number | undefined;
31
+ status?: SessionContentStatus | undefined;
32
+ sessionCode?: string | undefined;
33
+ }[], ISessionContent[] | {
34
+ id?: number | undefined;
35
+ createdAt?: string | undefined;
36
+ createdBy?: string | undefined;
37
+ updatedAt?: string | undefined;
38
+ updatedBy?: string | undefined;
39
+ spaceId?: string | undefined;
40
+ liked?: boolean | undefined;
41
+ role: SessionContentRole;
42
+ content: string;
43
+ rate?: number | undefined;
44
+ status?: SessionContentStatus | undefined;
45
+ sessionCode?: string | undefined;
46
+ }[]>;
47
+ sessionContentsMap: SessionContentsMap;
48
+ sessionLoadingMap: import("vue").Ref<SessionLoadingMap, SessionLoadingMap>;
49
+ chat: ({ sessionCode, param, url, headers, }: {
50
+ sessionCode: string;
51
+ param?: Record<string, any>;
52
+ url: string;
53
+ headers?: Record<string, string>;
54
+ }) => void;
55
+ getSessionContentBySessionCode: (sessionCode: string) => ISessionContent;
56
+ getSessionContentsBySessionCode: (sessionCode: string) => ISessionContent[];
57
+ changeSessionCode: (sessionCode: string) => void;
58
+ setSessionContents: (data: ISessionContent[]) => void;
59
+ };
package/dist/main.js ADDED
@@ -0,0 +1,195 @@
1
+ !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("vue")):"function"==typeof define&&define.amd?define(["vue"],t):"object"==typeof exports?exports["ai-ui-sdk"]=t(require("vue")):e["ai-ui-sdk"]=t(e.vue)}("undefined"!=typeof self?self:this,e=>(()=>{"use strict";var t,n,o,i,r={380:t=>{t.exports=e}},a={};function l(e){var t=a[e];if(void 0!==t)return t.exports;var n=a[e]={exports:{}};return r[e](n,n.exports,l),n.exports}l.d=(e,t)=>{for(var n in t)l.o(t,n)&&!l.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},l.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),l.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},l.p="/";var d={};l.r(d),l.d(d,{SessionContentRole:()=>o,SessionContentStatus:()=>n,default:()=>w,useAI:()=>y});var s={};l.r(s),l.d(s,{In:()=>o,c_:()=>n,OD:()=>y});var c=l(380);!function(e){e.System="system",e.Assistant="assistant",e.User="user",e.Guide="guide",e.Pause="pause",e.UserImage="user-image",e.Hidden="hidden",e.HiddenUser="hidden-user",e.HiddenAssistant="hidden-assistant",e.HiddenSystem="hidden-system",e.HiddenGuide="hidden-guide",e.TemplateUser="template-user",e.TemplateAssistant="template-assistant",e.TemplateSystem="template-system",e.TemplateGuide="template-guide",e.TemplateHidden="template-hidden"}(t||(t={})),function(e){e.Fail="fail",e.Loading="loading",e.Success="success"}(n||(n={})),function(e){e.Ai="ai",e.User="user",e.Time="time",e.System="system",e.Role="role",e.Hidden="hidden",e.Guide="guide",e.Pause="pause",e.TokenExpired="token-expired",e.UserImage="user-image",e.HiddenUser="hidden-user",e.HiddenAi="hidden-ai",e.HiddenRole="hidden-role",e.HiddenGuide="hidden-guide",e.TemplateUser="template-user",e.TemplateAi="template-ai",e.TemplateRole="template-role",e.TemplateGuide="template-guide",e.TemplateHidden="template-hidden",e.ImageNotSupported="image-not-supported"}(o||(o={})),function(e){e.TokenExpired="80003",e.ImageNotSupported="82003",e.UnAuthorizedPreviewFile="80401",e.TagRepeat="1513405",e[e.Aborted=20]="Aborted",e[e.UnLogin=401]="UnLogin",e.IamNoPermission="IAM_NO_PERMISSION"}(i||(i={}));let u=e=>{let t=`<section class="knowledge-head click-close">
2
+ <svg
3
+ class="bkaidev-wenzhang"
4
+ >
5
+ <use href="#bkaidev-wenzhang"></use>
6
+ </svg>
7
+ 找到 ${e.length} 篇资料参考
8
+ <i class="bkaidev-icon bkaidev-angle-up"></i>
9
+ </section>
10
+ <ul class="knowledge-body">`;return e.forEach(e=>{let{file_path:n,preview_path:o}=e.metadata,i=n.split("/").pop();t+=`<li
11
+ class="knowledge-item"
12
+ title="${i} (${o})"
13
+ >
14
+ <i class="bkaidev-icon bkaidev-zhishiku"></i>
15
+ <a href="${o}" target="_blank" class="knowledge-link g-flex-truncate">
16
+ ${i}
17
+ </a>
18
+ <span class="knowledge-path g-flex-truncate">${n}</span>
19
+ </li>`}),t+="</ul>"},p=()=>{let e=null,t=()=>{let t=`.knowledge-tips {
20
+ position: relative;
21
+ padding: 6px 8px;
22
+ margin-bottom: 14px;
23
+ line-height: 22px;
24
+ background: #f5f7fa;
25
+ border-radius: 4px;
26
+
27
+ &.closed {
28
+ .bkaidev-angle-up {
29
+ transform: rotate(180deg);
30
+ }
31
+
32
+ .knowledge-summary {
33
+ margin-bottom: 0;
34
+ }
35
+
36
+ .knowledge-link {
37
+ display: none;
38
+ }
39
+ }
40
+
41
+ .bkaidev-angle-up {
42
+ position: absolute;
43
+ top: 4px;
44
+ right: 0px;
45
+ font-size: 22px;
46
+ color: #979ba5;
47
+ cursor: pointer;
48
+ }
49
+
50
+ .bkaidev-help-document {
51
+ margin-right: 4px;
52
+ font-size: 19px;
53
+ color: #3a84ff;
54
+ }
55
+
56
+ .knowledge-summary {
57
+ display: block;
58
+ margin-bottom: 4px;
59
+ color: #313238;
60
+ }
61
+
62
+ .knowledge-link {
63
+ display: block;
64
+ color: #3a84ff;
65
+ text-decoration: none;
66
+ cursor: pointer;
67
+
68
+ .bkaidev-cc-jump-link {
69
+ font-size: 14px;
70
+ }
71
+
72
+ &:last-child {
73
+ margin-bottom: 2px;
74
+ }
75
+ }
76
+ }`,n=`.knowledge-head {
77
+ display: flex;
78
+ align-items: center;
79
+ padding: 0 8px 0 6px;
80
+ margin-bottom: 8px;
81
+ line-height: 28px;
82
+ background: #F0F1F5;
83
+ border-radius: 4px;
84
+ font-size: 12px;
85
+ color: #313238;
86
+ cursor: pointer;
87
+ width: fit-content;
88
+
89
+ .bkaidev-wenzhang {
90
+ width: 14px;
91
+ height: 14px;
92
+ margin-right: 6px;
93
+ }
94
+
95
+ .bkaidev-angle-up {
96
+ font-size: 22px;
97
+ color: #4D4F56;
98
+ margin-left: 4px;
99
+ }
100
+
101
+ &.closed {
102
+ margin-bottom: 16px;
103
+
104
+ .bkaidev-angle-up {
105
+ transform: rotate(180deg);
106
+ }
107
+
108
+ &~ .knowledge-body {
109
+ display: none;
110
+ }
111
+ }
112
+
113
+ &:hover {
114
+ background: #EAEBF0;
115
+ }
116
+ }
117
+ .knowledge-body {
118
+ background: #F5F7FA;
119
+ padding: 16px !important;
120
+ .knowledge-item {
121
+ display: flex;
122
+ align-items: center;
123
+ line-height: 28px;
124
+ a {
125
+ margin-right: 20px;
126
+ }
127
+ .bkaidev-zhishiku {
128
+ color: #D66F6B;
129
+ font-size: 14px;
130
+ margin-right: 6px;
131
+ }
132
+ .knowledge-path {
133
+ margin-left: auto;
134
+ color: #979BA5;
135
+ text-align: right;
136
+ }
137
+ &:hover {
138
+ background: #EAEBF0;
139
+ }
140
+ }
141
+ }`;(e=document.createElement("style")).textContent=t+n,document.head.appendChild(e)},n=()=>{e&&(e.remove(),e=null)};(0,c.onBeforeMount)(()=>{t()}),(0,c.onBeforeUnmount)(()=>{n()})},h=e=>{try{return JSON.parse(e),!0}catch(e){return!1}},g=(e,t,n,o)=>{let i="内容正在生成中..."===e||n?"":e;if(i.includes('<section class="think-head click-close">')){let e=i.match(/<section class="think-body">([\s\S]*?)<\/section>/);if(e){let n=e[1],o=n.replace(/\n$/g,`${t}
142
+ `);i=i.replace(n,o)}}else i+=`<section class="think-head click-close">
143
+ <i class="bkaidev-icon bkaidev-sikao"></i>思考中...<i class="bkaidev-icon bkaidev-angle-up"></i>
144
+ </section>
145
+ <section class="think-body">
146
+ ${t}
147
+ </section>`;return o&&(i=i.replace("思考中...",`已完成思考 (耗时:${function(e){let t=Math.floor(e/36e5),n=Math.floor(e%36e5/6e4),o=Math.floor(e%6e4/1e3),i=e%1e3,r=[];return t>0&&r.push(`${t}h`),n>0&&r.push(`${n}min`),o>0&&r.push(`${o}s`),i>0&&r.push(`${i.toFixed(2)}ms`),r.join(" ")}(o)})`)),i},f=e=>{let t="正在思考..."===e,n=e.match(/<section class="think-body">([\s\S]*?)<\/section>$/),o=n&&""===n[1].trim();return t||o},m=()=>{let e=null,t=()=>{let t=`.think-head {
148
+ display: flex;
149
+ align-items: center;
150
+ padding: 0 8px 0 6px;
151
+ margin-bottom: 8px;
152
+ line-height: 28px;
153
+ background: #F0F1F5;
154
+ border-radius: 4px;
155
+ font-size: 12px;
156
+ color: #313238;
157
+ cursor: pointer;
158
+ width: fit-content;
159
+ .bkaidev-sikao {
160
+ font-size: 14px;
161
+ color: #979ba5;
162
+ margin-right: 4px;
163
+ }
164
+
165
+ .bkaidev-angle-up {
166
+ font-size: 22px;
167
+ color: #4D4F56;
168
+ margin-left: 4px;
169
+ }
170
+
171
+ &.closed {
172
+ margin-bottom: 16px;
173
+
174
+ .bkaidev-angle-up {
175
+ transform: rotate(180deg);
176
+ }
177
+
178
+ &~ .think-body {
179
+ display: none;
180
+ }
181
+ }
182
+
183
+ &:hover {
184
+ background: #EAEBF0;
185
+ }
186
+ }
187
+ .think-body {
188
+ background: #F5F7FA;
189
+ color: #979BA5;
190
+ padding: 16px;
191
+ margin-bottom: 16px;
192
+ &~ .knowledge-head {
193
+ margin-bottom: 8px;
194
+ }
195
+ }`;(e=document.createElement("style")).textContent=t,document.head.appendChild(e)},n=()=>{e&&(e.remove(),e=null)};(0,c.onBeforeMount)(()=>{t()}),(0,c.onBeforeUnmount)(()=>{n()})};function b(e,t,n,o,i,r,a){try{var l=e[r](a),d=l.value}catch(e){n(e);return}l.done?t(d):Promise.resolve(d).then(o,i)}function k(e){return function(){var t=this,n=arguments;return new Promise(function(o,i){var r=e.apply(t,n);function a(e){b(r,o,i,a,l,"next",e)}function l(e){b(r,o,i,a,l,"throw",e)}a(void 0)})}}function v(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}class x{stream({sessionCode:e,url:t,headers:n,param:o}){var i=this;return k(function*(){var r,a;yield null===(r=i.handleStart)||void 0===r?void 0:r.call(i,e);let l=new AbortController;i.controllerMap[e]=l,fetch(t,{method:"post",signal:l.signal,headers:function(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{},o=Object.keys(n);"function"==typeof Object.getOwnPropertySymbols&&(o=o.concat(Object.getOwnPropertySymbols(n).filter(function(e){return Object.getOwnPropertyDescriptor(n,e).enumerable}))),o.forEach(function(t){v(e,t,n[t])})}return e}({"Content-Type":"application/json"},n),mode:"cors",credentials:"include",body:JSON.stringify(o)}).then((a=k(function*(t){let n=t.body.pipeThrough(new window.TextDecoderStream).getReader(),o="";for(;;)try{let{value:r,done:a}=yield n.read();if(!t.ok){i.handleError(e,r||t.statusText,t.status);break}if(a){i.handleEnd(e);break}(o+r.toString()).split("\n").forEach(n=>{let r=n.replace("data:","").trim();if(h(r)){var a,l;let{event:n,content:d,cover:s,documents:c,result:u,code:p,elapsed_time:h,message:g}=JSON.parse(r);if(!1===u||200!==t.status){i.handleError(e,g||"模型调用失败",p);return}switch(n){case"text":i.handleText(e,d,s);break;case"reference_doc":null===(a=i.handleReferenceDoc)||void 0===a||a.call(i,e,c,s);break;case"think":null===(l=i.handleThink)||void 0===l||l.call(i,e,d,s,h);break;case"done":i.handleEnd(e,s?d:"");break;case"error":i.handleError(e,g||"模型调用失败",p)}o=""}else r&&(o=r)})}catch(t){(null==t?void 0:t.code)!==20&&i.handleError(e,`模型调用失败:${t.message}`,t.code);break}}),function(e){return a.apply(this,arguments)}))})()}stop(e){var t,n;return null===(n=this.controllerMap[e])||void 0===n||null===(t=n.abort)||void 0===t||t.call(n),this.handleEnd(e)}constructor({handleStart:e,handleText:t,handleReferenceDoc:n,handleThink:o,handleEnd:i,handleError:r}){v(this,"handleStart",void 0),v(this,"handleText",void 0),v(this,"handleReferenceDoc",void 0),v(this,"handleThink",void 0),v(this,"handleEnd",void 0),v(this,"handleError",void 0),v(this,"controllerMap",void 0),this.handleStart=e,this.handleText=t,this.handleReferenceDoc=n,this.handleThink=o,this.handleEnd=i,this.handleError=r,this.controllerMap={}}}let y=({handleStart:e,handleText:t,handleReferenceDoc:r,handleThink:a,handleEnd:l,handleError:d})=>{p(),m();let s="",h=(0,c.ref)({}),b=(0,c.ref)([]),k={},v=new x({handleStart:function(t){h.value[t]=!0;let i={sessionCode:t,role:o.Ai,status:n.Loading,content:"内容正在生成中..."};return!function(e,t){w(e).push(t)}(t,i),null==e?void 0:e(t,i)},handleText:function(e,o,i){let r=y(e);if("内容正在生成中..."===r.content)r.content=o;else{if(r.status!==n.Loading)return;r.content=i?o:r.content+o}return null==t?void 0:t(e,r)},handleReferenceDoc:function(e,t,n){let o=y(e),i=u(t);return o.content=n?i:o.content+i,null==r?void 0:r(e,o)},handleThink:function(e,t,n,o){let i=y(e);return i.content=g(i.content,t,n,o),null==a?void 0:a(e,i)},handleEnd:function(e,t){let o=y(e);if(o.status===n.Loading)return h.value[e]=!1,t&&(o.content=t),o.status=n.Success,null==l?void 0:l(e,o);("内容正在生成中..."===o.content||f(o.content))&&S(e,"聊天内容已中断")},handleError:S});function y(e){var t;return s===e?b.value.at(-1):null===(t=k[e])||void 0===t?void 0:t.at(-1)}function w(e){return s===e?b.value:k[e]}function S(e,t,r){let a=y(e);return a.status=n.Fail,a.content=t,h.value[e]=!1,r===i.TokenExpired&&(a.content="抱歉,您的剩余 Token 不足,无法返回回答内容,请先清空当前会话(上下文仍会作为历史记录保留))",a.role=o.TokenExpired),r===i.ImageNotSupported&&(a.content="抱歉,当前模型不支持图片内容解析",a.role=o.ImageNotSupported),null==d?void 0:d(e,r,a)}return{sessionContents:b,sessionContentsMap:k,sessionLoadingMap:h,chat:function({sessionCode:e,param:t,url:n,headers:o}){v.stream({sessionCode:e,url:n,param:t,headers:o})},getSessionContentBySessionCode:y,getSessionContentsBySessionCode:w,changeSessionCode:function(e){s=e,k[e]||(k[e]=[]),b.value=k[e]},setSessionContents:function(e){s&&(k[s]=e,b.value=e)}}},w=s.default;if("undefined"!=typeof window){let{currentScript:e}=window.document,t=e&&e.src.match(/(.+\/)[^/]+\.js(\?.*)?$/);t&&(l.p=t[1])}return d})());
@@ -0,0 +1,54 @@
1
+ export declare enum SessionPromptRole {
2
+ System = "system",
3
+ Assistant = "assistant",
4
+ User = "user",
5
+ Guide = "guide",
6
+ Pause = "pause",
7
+ UserImage = "user-image",
8
+ Hidden = "hidden",
9
+ HiddenUser = "hidden-user",
10
+ HiddenAssistant = "hidden-assistant",
11
+ HiddenSystem = "hidden-system",
12
+ HiddenGuide = "hidden-guide",
13
+ TemplateUser = "template-user",
14
+ TemplateAssistant = "template-assistant",
15
+ TemplateSystem = "template-system",
16
+ TemplateGuide = "template-guide",
17
+ TemplateHidden = "template-hidden"
18
+ }
19
+ export declare enum SessionContentStatus {
20
+ Fail = "fail",
21
+ Loading = "loading",
22
+ Success = "success"
23
+ }
24
+ export declare enum SessionContentRole {
25
+ Ai = "ai",
26
+ User = "user",
27
+ Time = "time",
28
+ System = "system",
29
+ Role = "role",
30
+ Hidden = "hidden",
31
+ Guide = "guide",
32
+ Pause = "pause",
33
+ TokenExpired = "token-expired",
34
+ UserImage = "user-image",
35
+ HiddenUser = "hidden-user",
36
+ HiddenAi = "hidden-ai",
37
+ HiddenRole = "hidden-role",
38
+ HiddenGuide = "hidden-guide",
39
+ TemplateUser = "template-user",
40
+ TemplateAi = "template-ai",
41
+ TemplateRole = "template-role",
42
+ TemplateGuide = "template-guide",
43
+ TemplateHidden = "template-hidden",
44
+ ImageNotSupported = "image-not-supported"
45
+ }
46
+ export declare enum HttpErrorCode {
47
+ TokenExpired = "80003",
48
+ ImageNotSupported = "82003",
49
+ UnAuthorizedPreviewFile = "80401",
50
+ TagRepeat = "1513405",
51
+ Aborted = 20,
52
+ UnLogin = 401,
53
+ IamNoPermission = "IAM_NO_PERMISSION"
54
+ }
@@ -0,0 +1,26 @@
1
+ import { SessionContentRole, SessionPromptRole, SessionContentStatus } from './enum.ts';
2
+ export type Document = {
3
+ metadata: {
4
+ file_path: string;
5
+ path: string;
6
+ preview_path: string;
7
+ };
8
+ };
9
+ export interface ISessionContent {
10
+ id?: number;
11
+ createdAt?: string;
12
+ createdBy?: string;
13
+ updatedAt?: string;
14
+ updatedBy?: string;
15
+ spaceId?: string;
16
+ liked?: boolean;
17
+ role: SessionContentRole;
18
+ content: string;
19
+ rate?: number;
20
+ status?: SessionContentStatus;
21
+ sessionCode?: string;
22
+ }
23
+ export interface ISessionPrompt {
24
+ content: string;
25
+ role: SessionPromptRole;
26
+ }
package/package.json CHANGED
@@ -1,16 +1,23 @@
1
1
  {
2
2
  "name": "@blueking/ai-ui-sdk",
3
- "version": "0.0.1-beta.1",
3
+ "version": "0.0.1-beta.2",
4
4
  "description": "蓝鲸AI UI SDK",
5
- "main": "main.js",
5
+ "main": "dist/main.js",
6
+ "types": "dist/main.d.ts",
6
7
  "scripts": {
7
- "build": "bk-cli-service-webpack build"
8
+ "build": "bk-cli-service-webpack build && tsc --emitDeclarationOnly"
8
9
  },
10
+ "files": [
11
+ "dist"
12
+ ],
9
13
  "keywords": [],
10
14
  "author": "",
11
15
  "license": "ISC",
12
16
  "dependencies": {
13
17
  "@blueking/cli-service-webpack": "0.0.7-beta.1",
14
18
  "vue": "^3.5.13"
19
+ },
20
+ "devDependencies": {
21
+ "typescript": "^5.5.4"
15
22
  }
16
23
  }
package/bk.config.js DELETED
@@ -1,21 +0,0 @@
1
-
2
- module.exports = {
3
- host: process.env.BK_APP_HOST,
4
- port: process.env.BK_APP_PORT,
5
- publicPath: process.env.BK_STATIC_URL,
6
- cache: false,
7
- open: false,
8
- replaceStatic: false,
9
- target: 'lib',
10
- splitChunk: false,
11
- resource: {
12
- main: {
13
- entry: './src/main.ts',
14
- },
15
- },
16
- configureWebpack: {
17
- externals: {
18
- 'vue': 'vue',
19
- }
20
- }
21
- };
@@ -1,156 +0,0 @@
1
- import {
2
- isJSON,
3
- } from './util.ts';
4
-
5
- import type {
6
- Document,
7
- } from '../types/type.ts';
8
-
9
- type HandleStart = (sessionCode: string) => any;
10
- type HandleText = (sessionCode: string, message: string, cover?: boolean) => void;
11
- type HandleReferenceDoc = (sessionCode: string, documents: Document[], cover?: boolean) => void;
12
- type HandleThink = (sessionCode: string, content: string, cover?: boolean, elapsed_time?: number) => void;
13
- type HandleEnd = (sessionCode: string, message?: string) => void;
14
- type HandleError = (sessionCode: string, message: string, code: string) => any;
15
-
16
- export class ChatHelper {
17
- handleStart: HandleStart;
18
- handleText: HandleText;
19
- handleReferenceDoc?: HandleReferenceDoc;
20
- handleThink?: HandleThink;
21
- handleEnd: HandleEnd;
22
- handleError: HandleError;
23
- controllerMap: Record<string, AbortController>;
24
-
25
- constructor({
26
- handleStart,
27
- handleText,
28
- handleReferenceDoc,
29
- handleThink,
30
- handleEnd,
31
- handleError,
32
- }: {
33
- handleStart: HandleStart;
34
- handleText: HandleText;
35
- handleReferenceDoc?: HandleReferenceDoc;
36
- handleThink?: HandleThink;
37
- handleEnd: HandleEnd;
38
- handleError: HandleError;
39
- }) {
40
- this.handleStart = handleStart;
41
- this.handleText = handleText;
42
- this.handleReferenceDoc = handleReferenceDoc;
43
- this.handleThink = handleThink;
44
- this.handleEnd = handleEnd;
45
- this.handleError = handleError;
46
- this.controllerMap = {};
47
- }
48
-
49
- async stream({
50
- sessionCode,
51
- url,
52
- headers,
53
- param,
54
- }: {
55
- sessionCode: string;
56
- url: string;
57
- headers?: Record<string, string>;
58
- param?: Record<string, any>;
59
- }) {
60
- // 开始
61
- await this.handleStart?.(sessionCode);
62
- // 记录 controller
63
- const controller = new AbortController();
64
- this.controllerMap[sessionCode] = controller;
65
- // 发送请求
66
- fetch(url, {
67
- method: 'post',
68
- signal: controller.signal,
69
- headers: {
70
- 'Content-Type': 'application/json',
71
- ...headers,
72
- },
73
- mode: 'cors',
74
- credentials: 'include',
75
- body: JSON.stringify(param),
76
- })
77
- .then(async (response: any) => {
78
- const reader = response.body
79
- .pipeThrough(new window.TextDecoderStream())
80
- .getReader();
81
-
82
- // 临时存储数据
83
- let temp = '';
84
-
85
- while (true) {
86
- try {
87
- const { value, done } = await reader.read();
88
-
89
- // 接口异常处理
90
- if (!response.ok) {
91
- this.handleError(sessionCode, value || response.statusText, response.status);
92
- break;
93
- }
94
- // 接口完成
95
- if (done) {
96
- this.handleEnd(sessionCode);
97
- break;
98
- }
99
-
100
- const values = (temp + value.toString()).split('\n');
101
- values.forEach((value) => {
102
- const item = value.replace('data:', '').trim();
103
- if (isJSON(item)) {
104
- const {
105
- event,
106
- content,
107
- cover,
108
- documents,
109
- result,
110
- code,
111
- elapsed_time,
112
- message,
113
- } = JSON.parse(item);
114
- // 业务错误处理
115
- if (result === false || response.status !== 200) {
116
- this.handleError(sessionCode, message || '模型调用失败', code);
117
- return;
118
- }
119
-
120
- switch (event) {
121
- case 'text':
122
- this.handleText(sessionCode, content, cover);
123
- break;
124
- case 'reference_doc':
125
- this.handleReferenceDoc?.(sessionCode, documents, cover);
126
- break;
127
- case 'think':
128
- this.handleThink?.(sessionCode, content, cover, elapsed_time);
129
- break;
130
- case 'done':
131
- this.handleEnd(sessionCode, cover ? content : '');
132
- break;
133
- case 'error':
134
- this.handleError(sessionCode, message || '模型调用失败', code);
135
- break;
136
- }
137
- temp = '';
138
- } else if (item) {
139
- temp = item;
140
- }
141
- });
142
- } catch (error: any) {
143
- if (error?.code !== 20) {
144
- this.handleError(sessionCode, `模型调用失败:${error.message}`, error.code);
145
- }
146
- break;
147
- }
148
- }
149
- });
150
- }
151
-
152
- stop(sessionCode: string) {
153
- this.controllerMap[sessionCode]?.abort?.();
154
- return this.handleEnd(sessionCode);
155
- }
156
- }
@@ -1,41 +0,0 @@
1
- /**
2
- * 判断是否是 JSON 字符串
3
- * @param str 字符串
4
- * @returns 是否是 JSON 字符串
5
- */
6
- export const isJSON = (str: string) => {
7
- try {
8
- JSON.parse(str);
9
- return true;
10
- } catch (e) {
11
- return false;
12
- }
13
- };
14
-
15
- /**
16
- * 响应时间格式化
17
- * @param val 待格式化时间,xxms
18
- * @returns 格式化后的时间,xx小时xx分钟xx秒
19
- */
20
- export function durationFormatter(val: number) {
21
- const hours = Math.floor(val / 3600000);
22
- const minutes = Math.floor((val % 3600000) / 60000);
23
- const seconds = Math.floor((val % 60000) / 1000);
24
- const milliseconds = val % 1000;
25
-
26
- const parts: string[] = [];
27
- if (hours > 0) {
28
- parts.push(`${hours}h`);
29
- }
30
- if (minutes > 0) {
31
- parts.push(`${minutes}min`);
32
- }
33
- if (seconds > 0) {
34
- parts.push(`${seconds}s`);
35
- }
36
- if (milliseconds > 0) {
37
- parts.push(`${milliseconds.toFixed(2)}ms`);
38
- }
39
-
40
- return parts.join(' ');
41
- }
@@ -1,205 +0,0 @@
1
- import {
2
- onBeforeMount,
3
- onBeforeUnmount,
4
- } from 'vue';
5
-
6
- import type {
7
- Document,
8
- } from '../types/type.ts';
9
-
10
- /**
11
- * 获取引用资料的 HTML 内容
12
- * @param documents
13
- * @returns
14
- */
15
- export const getHtmlContentFromDocuments = (documents: Document[]) => {
16
- let htmlContent = `<section class="knowledge-head click-close">
17
- <svg
18
- class="bkaidev-wenzhang"
19
- >
20
- <use href="#bkaidev-wenzhang"></use>
21
- </svg>
22
- 找到 ${documents.length} 篇资料参考
23
- <i class="bkaidev-icon bkaidev-angle-up"></i>
24
- </section>
25
- <ul class="knowledge-body">`;
26
- documents.forEach((document) => {
27
- const { file_path: filePath, preview_path: previewPath } = document.metadata;
28
- const title = filePath.split('/').pop();
29
- htmlContent += `<li
30
- class="knowledge-item"
31
- title="${title} (${previewPath})"
32
- >
33
- <i class="bkaidev-icon bkaidev-zhishiku"></i>
34
- <a href="${previewPath}" target="_blank" class="knowledge-link g-flex-truncate">
35
- ${title}
36
- </a>
37
- <span class="knowledge-path g-flex-truncate">${filePath}</span>
38
- </li>`;
39
- });
40
- htmlContent += '</ul>';
41
- return htmlContent;
42
- };
43
-
44
- /**
45
- * 移除引用资料的 HTML 内容
46
- * @param content
47
- * @returns
48
- */
49
- export const removeReferenceDoc = (content: string) => {
50
- return content
51
- .replace(/<section class="knowledge-head click-close">[\s\S]*?<\/section>/, '')
52
- .replace(/<ul class="knowledge-body">[\s\S]*?<\/ul>/, '')
53
- .replace(/<section class="knowledge-tips">[\s\S]*?<\/section>/, '');
54
- };
55
-
56
- export const useReferenceDoc = () => {
57
- let styleEle: HTMLStyleElement | null = null;
58
-
59
- const addReferenceDocStyle = () => {
60
- // 旧版知识样式
61
- const oldStyle = `.knowledge-tips {
62
- position: relative;
63
- padding: 6px 8px;
64
- margin-bottom: 14px;
65
- line-height: 22px;
66
- background: #f5f7fa;
67
- border-radius: 4px;
68
-
69
- &.closed {
70
- .bkaidev-angle-up {
71
- transform: rotate(180deg);
72
- }
73
-
74
- .knowledge-summary {
75
- margin-bottom: 0;
76
- }
77
-
78
- .knowledge-link {
79
- display: none;
80
- }
81
- }
82
-
83
- .bkaidev-angle-up {
84
- position: absolute;
85
- top: 4px;
86
- right: 0px;
87
- font-size: 22px;
88
- color: #979ba5;
89
- cursor: pointer;
90
- }
91
-
92
- .bkaidev-help-document {
93
- margin-right: 4px;
94
- font-size: 19px;
95
- color: #3a84ff;
96
- }
97
-
98
- .knowledge-summary {
99
- display: block;
100
- margin-bottom: 4px;
101
- color: #313238;
102
- }
103
-
104
- .knowledge-link {
105
- display: block;
106
- color: #3a84ff;
107
- text-decoration: none;
108
- cursor: pointer;
109
-
110
- .bkaidev-cc-jump-link {
111
- font-size: 14px;
112
- }
113
-
114
- &:last-child {
115
- margin-bottom: 2px;
116
- }
117
- }
118
- }`;
119
- // 新版知识样式
120
- const newStyle = `.knowledge-head {
121
- display: flex;
122
- align-items: center;
123
- padding: 0 8px 0 6px;
124
- margin-bottom: 8px;
125
- line-height: 28px;
126
- background: #F0F1F5;
127
- border-radius: 4px;
128
- font-size: 12px;
129
- color: #313238;
130
- cursor: pointer;
131
- width: fit-content;
132
-
133
- .bkaidev-wenzhang {
134
- width: 14px;
135
- height: 14px;
136
- margin-right: 6px;
137
- }
138
-
139
- .bkaidev-angle-up {
140
- font-size: 22px;
141
- color: #4D4F56;
142
- margin-left: 4px;
143
- }
144
-
145
- &.closed {
146
- margin-bottom: 16px;
147
-
148
- .bkaidev-angle-up {
149
- transform: rotate(180deg);
150
- }
151
-
152
- &~ .knowledge-body {
153
- display: none;
154
- }
155
- }
156
-
157
- &:hover {
158
- background: #EAEBF0;
159
- }
160
- }
161
- .knowledge-body {
162
- background: #F5F7FA;
163
- padding: 16px !important;
164
- .knowledge-item {
165
- display: flex;
166
- align-items: center;
167
- line-height: 28px;
168
- a {
169
- margin-right: 20px;
170
- }
171
- .bkaidev-zhishiku {
172
- color: #D66F6B;
173
- font-size: 14px;
174
- margin-right: 6px;
175
- }
176
- .knowledge-path {
177
- margin-left: auto;
178
- color: #979BA5;
179
- text-align: right;
180
- }
181
- &:hover {
182
- background: #EAEBF0;
183
- }
184
- }
185
- }`;
186
- styleEle = document.createElement('style');
187
- styleEle.textContent = oldStyle + newStyle;
188
- document.head.appendChild(styleEle);
189
- };
190
-
191
- const removeReferenceDocStyle = () => {
192
- if (styleEle) {
193
- styleEle.remove();
194
- styleEle = null;
195
- }
196
- };
197
-
198
- onBeforeMount(() => {
199
- addReferenceDocStyle();
200
- });
201
-
202
- onBeforeUnmount(() => {
203
- removeReferenceDocStyle();
204
- });
205
- };
@@ -1,156 +0,0 @@
1
- import {
2
- onBeforeMount,
3
- onBeforeUnmount,
4
- } from 'vue';
5
-
6
- import {
7
- durationFormatter,
8
- } from '../common/util.ts';
9
-
10
- /**
11
- * 获取思考的 HTML 内容
12
- * @param chatContent 聊天内容
13
- * @param content 思考内容
14
- * @param elapsedTime 思考时间
15
- * @returns 完整的 chatContent
16
- */
17
- export const getHtmlContentFromThink = (chatContent: string, content: string, cover?: boolean, elapsedTime?: number) => {
18
- let htmlContent = (chatContent === '内容正在生成中...' || cover) ? '' : chatContent;
19
- // 思考开始
20
- if (!htmlContent.includes('<section class="think-head click-close">')) {
21
- htmlContent += `<section class="think-head click-close">
22
- <i class="bkaidev-icon bkaidev-sikao"></i>思考中...<i class="bkaidev-icon bkaidev-angle-up"></i>
23
- </section>
24
- <section class="think-body">
25
- ${content}
26
- </section>`;
27
- } else {
28
- // 补充思考内容
29
- const thinkBodyMatch = htmlContent.match(/<section class="think-body">([\s\S]*?)<\/section>/);
30
- if (thinkBodyMatch) {
31
- // 原有思考内容
32
- const thinkContent = thinkBodyMatch[1];
33
- // 在最后的换行符前面添加新的思考内容
34
- const newThinkContent = thinkContent.replace(/\n$/g, `${content}\n`);
35
- htmlContent = htmlContent.replace(
36
- thinkContent,
37
- newThinkContent,
38
- );
39
- }
40
- }
41
- // 思考结束
42
- if (elapsedTime) {
43
- htmlContent = htmlContent.replace('思考中...', `已完成思考 (耗时:${durationFormatter(elapsedTime)})`);
44
- }
45
- return htmlContent;
46
- };
47
-
48
- /**
49
- * 移除思考的 HTML 内容
50
- * @param content
51
- * @returns
52
- */
53
- export const removeThink = (content: string) => {
54
- // 移除思考头部
55
- const afterRemoveThinkHead = content.replace(/<section class="think-head click-close">[\s\S]*<\/section>/, '');
56
- // 移除思考内容
57
- let afterRemoveThinkBody = afterRemoveThinkHead.replace(/<section class="think-body">[\s\S]*<\/section>/, '');
58
- // 如果内容只有思考,那就给ai传递思考内容
59
- if (afterRemoveThinkBody.trim() === '') {
60
- const thinkBodyMatch = content.match(/<section class="think-body">([\s\S]*?)<\/section>/);
61
- if (thinkBodyMatch) {
62
- [, afterRemoveThinkBody] = thinkBodyMatch;
63
- }
64
- }
65
- return afterRemoveThinkBody;
66
- };
67
-
68
- /**
69
- * 判断是否是思考中
70
- * @param content
71
- * @returns
72
- */
73
- export const isThinking = (content: string) => {
74
- // 是否开始思考
75
- const isStartThinking = content === '正在思考...';
76
- // 判断是否空思考
77
- const thinkBodyMatch = content.match(/<section class="think-body">([\s\S]*?)<\/section>$/);
78
- const isEmptyThinking = thinkBodyMatch && thinkBodyMatch[1].trim() === '';
79
-
80
- return isStartThinking || isEmptyThinking;
81
- };
82
-
83
- // 思考
84
- export const useThink = () => {
85
- let styleEle: HTMLStyleElement | null = null;
86
-
87
- const addThinkStyle = () => {
88
- const style = `.think-head {
89
- display: flex;
90
- align-items: center;
91
- padding: 0 8px 0 6px;
92
- margin-bottom: 8px;
93
- line-height: 28px;
94
- background: #F0F1F5;
95
- border-radius: 4px;
96
- font-size: 12px;
97
- color: #313238;
98
- cursor: pointer;
99
- width: fit-content;
100
- .bkaidev-sikao {
101
- font-size: 14px;
102
- color: #979ba5;
103
- margin-right: 4px;
104
- }
105
-
106
- .bkaidev-angle-up {
107
- font-size: 22px;
108
- color: #4D4F56;
109
- margin-left: 4px;
110
- }
111
-
112
- &.closed {
113
- margin-bottom: 16px;
114
-
115
- .bkaidev-angle-up {
116
- transform: rotate(180deg);
117
- }
118
-
119
- &~ .think-body {
120
- display: none;
121
- }
122
- }
123
-
124
- &:hover {
125
- background: #EAEBF0;
126
- }
127
- }
128
- .think-body {
129
- background: #F5F7FA;
130
- color: #979BA5;
131
- padding: 16px;
132
- margin-bottom: 16px;
133
- &~ .knowledge-head {
134
- margin-bottom: 8px;
135
- }
136
- }`;
137
- styleEle = document.createElement('style');
138
- styleEle.textContent = style;
139
- document.head.appendChild(styleEle);
140
- };
141
-
142
- const removeThinkStyle = () => {
143
- if (styleEle) {
144
- styleEle.remove();
145
- styleEle = null;
146
- }
147
- };
148
-
149
- onBeforeMount(() => {
150
- addThinkStyle();
151
- });
152
-
153
- onBeforeUnmount(() => {
154
- removeThinkStyle();
155
- });
156
- };
package/src/main.ts DELETED
@@ -1,227 +0,0 @@
1
- import {
2
- ref,
3
- } from 'vue';
4
- import type {
5
- Document,
6
- ISessionContent,
7
- } from './types/type.ts';
8
- import {
9
- HttpErrorCode,
10
- SessionContentRole,
11
- SessionContentStatus,
12
- } from './types/enum.ts';
13
- import {
14
- useReferenceDoc,
15
- getHtmlContentFromDocuments
16
- } from './hooks/use-reference-doc.ts';
17
- import {
18
- useThink,
19
- getHtmlContentFromThink,
20
- isThinking
21
- } from './hooks/use-think.ts';
22
- import {
23
- ChatHelper,
24
- } from './common/chart-helper.ts';
25
-
26
- type SessionContentsMap = {
27
- [key: string]: ISessionContent[]
28
- };
29
-
30
- type SessionLoadingMap = {
31
- [key: string]: boolean
32
- };
33
-
34
- interface AICallbacks {
35
- handleStart?: (sessionCode: string, sessionContent: ISessionContent) => void;
36
- handleText?: (sessionCode: string, sessionContent: ISessionContent) => void;
37
- handleReferenceDoc?: (sessionCode: string, sessionContent: ISessionContent) => void;
38
- handleThink?: (sessionCode: string, sessionContent: ISessionContent) => void;
39
- handleEnd?: (sessionCode: string, sessionContent: ISessionContent) => void;
40
- handleError?: (sessionCode: string, code: string | undefined, sessionContent: ISessionContent) => void;
41
- }
42
-
43
- export const useAI = ({
44
- handleStart,
45
- handleText,
46
- handleReferenceDoc,
47
- handleThink,
48
- handleEnd,
49
- handleError,
50
- }: AICallbacks) => {
51
- // 引用资料
52
- useReferenceDoc();
53
- // 思考
54
- useThink();
55
- // 聊天上下文
56
- let currentSessionCode = '';
57
- const sessionLoadingMap = ref<SessionLoadingMap>({});
58
- const sessionContents = ref<ISessionContent[]>([]);
59
- const sessionContentsMap: SessionContentsMap = {};
60
-
61
- const chatHelper = new ChatHelper({
62
- handleStart: handleStartChat,
63
- handleText: handleTextChat,
64
- handleReferenceDoc: handleReferenceDocChat,
65
- handleThink: handleThinkChat,
66
- handleEnd: handleEndChat,
67
- handleError: handleErrorChat,
68
- });
69
-
70
- // 设置 sessionCode
71
- function changeSessionCode(sessionCode: string) {
72
- currentSessionCode = sessionCode;
73
- if (!sessionContentsMap[sessionCode]) {
74
- sessionContentsMap[sessionCode] = [];
75
- }
76
- sessionContents.value = sessionContentsMap[sessionCode];
77
- }
78
-
79
- // 设置 sessionContents
80
- function setSessionContents(data: ISessionContent[]) {
81
- if (!currentSessionCode) return;
82
- sessionContentsMap[currentSessionCode] = data;
83
- sessionContents.value = data;
84
- }
85
-
86
- // 获取执行中的 SessionContent
87
- function getSessionContentBySessionCode(sessionCode: string) {
88
- if (currentSessionCode === sessionCode) {
89
- return sessionContents.value.at(-1) as ISessionContent;
90
- }
91
- return sessionContentsMap[sessionCode]?.at(-1) as ISessionContent;
92
- }
93
-
94
- // 获取 SessionContents
95
- function getSessionContentsBySessionCode(sessionCode: string) {
96
- if (currentSessionCode === sessionCode) {
97
- return sessionContents.value;
98
- }
99
- return sessionContentsMap[sessionCode];
100
- }
101
-
102
- // 新增 sessionContent
103
- function plusSessionContent(sessionCode: string, sessionContent: ISessionContent) {
104
- const sessionContents = getSessionContentsBySessionCode(sessionCode);
105
- sessionContents.push(sessionContent);
106
- }
107
-
108
- // 聊天开始
109
- function handleStartChat(sessionCode: string) {
110
- sessionLoadingMap.value[sessionCode] = true;
111
- const sessionContent: ISessionContent = {
112
- sessionCode,
113
- role: SessionContentRole.Ai,
114
- status: SessionContentStatus.Loading,
115
- content: '内容正在生成中...',
116
- };
117
- // 画布新增
118
- plusSessionContent(sessionCode, sessionContent);
119
- // 调用cb
120
- return handleStart?.(sessionCode, sessionContent);
121
- }
122
-
123
- // 引用资料
124
- function handleReferenceDocChat(sessionCode: string, documents: Document[], cover?: boolean) {
125
- const sessionContent = getSessionContentBySessionCode(sessionCode);
126
- const content = getHtmlContentFromDocuments(documents);
127
- sessionContent.content = cover ? content : sessionContent.content + content;
128
- return handleReferenceDoc?.(sessionCode, sessionContent);
129
- }
130
-
131
- // 思考
132
- function handleThinkChat(sessionCode: string, content: string, cover?: boolean, elapsedTime?: number) {
133
- const sessionContent = getSessionContentBySessionCode(sessionCode);
134
- // 获取思考的html
135
- sessionContent.content = getHtmlContentFromThink(sessionContent.content, content, cover, elapsedTime);
136
- // 调用cb
137
- return handleThink?.(sessionCode, sessionContent);
138
- }
139
-
140
- // 文本
141
- function handleTextChat(sessionCode: string, message: string, cover?: boolean) {
142
- const sessionContent = getSessionContentBySessionCode(sessionCode);
143
- if (sessionContent.content === '内容正在生成中...') {
144
- sessionContent.content = message;
145
- } else if (sessionContent.status === SessionContentStatus.Loading) {
146
- sessionContent.content = cover
147
- ? message
148
- : sessionContent.content + message;
149
- } else {
150
- return;
151
- }
152
- // 调用cb
153
- return handleText?.(sessionCode, sessionContent);
154
- }
155
-
156
- // 聊天结束
157
- function handleEndChat(sessionCode: string, message?: string) {
158
- const sessionContent = getSessionContentBySessionCode(sessionCode);
159
- // 防止 error 的情况下异步触发 end 更新了数据
160
- if (sessionContent.status === SessionContentStatus.Loading) {
161
- sessionLoadingMap.value[sessionCode] = false;
162
- // end 的时候,如果 message 有值,则更新 content
163
- if (message) {
164
- sessionContent.content = message;
165
- }
166
- // 更新状态
167
- sessionContent.status = SessionContentStatus.Success;
168
- // 调用cb
169
- return handleEnd?.(sessionCode, sessionContent);
170
- }
171
- // 内容正在生成中或者正在思考中
172
- if (sessionContent.content === '内容正在生成中...' || isThinking(sessionContent.content)) {
173
- handleErrorChat(sessionCode, '聊天内容已中断');
174
- }
175
- }
176
-
177
- // 聊天异常
178
- function handleErrorChat(sessionCode: string, message: string, code?: string) {
179
- const sessionContent = getSessionContentBySessionCode(sessionCode);
180
- sessionContent.status = SessionContentStatus.Fail;
181
- sessionContent.content = message;
182
- sessionLoadingMap.value[sessionCode] = false;
183
- // token 超了,提示 token 不足
184
- if (code === HttpErrorCode.TokenExpired) {
185
- sessionContent.content = '抱歉,您的剩余 Token 不足,无法返回回答内容,请先清空当前会话(上下文仍会作为历史记录保留))';
186
- sessionContent.role = SessionContentRole.TokenExpired;
187
- }
188
- if (code === HttpErrorCode.ImageNotSupported) {
189
- sessionContent.content = '抱歉,当前模型不支持图片内容解析';
190
- sessionContent.role = SessionContentRole.ImageNotSupported;
191
- }
192
- // 调用cb
193
- return handleError?.(sessionCode, code, sessionContent);
194
- }
195
-
196
- // 聊天
197
- function chat({
198
- sessionCode,
199
- param,
200
- url,
201
- headers,
202
- }: {
203
- sessionCode: string;
204
- param?: Record<string, any>;
205
- url: string;
206
- headers?: Record<string, string>;
207
- }) {
208
- // 发送请求
209
- chatHelper.stream({
210
- sessionCode,
211
- url,
212
- param,
213
- headers,
214
- });
215
- }
216
-
217
- return {
218
- sessionContents,
219
- sessionContentsMap,
220
- sessionLoadingMap,
221
- chat,
222
- getSessionContentBySessionCode,
223
- getSessionContentsBySessionCode,
224
- changeSessionCode,
225
- setSessionContents,
226
- };
227
- };
package/src/types/enum.ts DELETED
@@ -1,58 +0,0 @@
1
- // 传给 ai 的类型
2
- export enum SessionPromptRole {
3
- System = 'system',
4
- Assistant = 'assistant',
5
- User = 'user',
6
- Guide = 'guide',
7
- Pause = 'pause',
8
- UserImage = 'user-image',
9
- Hidden = 'hidden',
10
- HiddenUser = 'hidden-user',
11
- HiddenAssistant = 'hidden-assistant',
12
- HiddenSystem = 'hidden-system',
13
- HiddenGuide = 'hidden-guide',
14
- TemplateUser = 'template-user',
15
- TemplateAssistant = 'template-assistant',
16
- TemplateSystem = 'template-system',
17
- TemplateGuide = 'template-guide',
18
- TemplateHidden = 'template-hidden',
19
- }
20
-
21
- export enum SessionContentStatus {
22
- Fail = 'fail',
23
- Loading = 'loading',
24
- Success = 'success',
25
- }
26
-
27
- export enum SessionContentRole {
28
- Ai = 'ai',
29
- User = 'user',
30
- Time = 'time',
31
- System = 'system',
32
- Role = 'role',
33
- Hidden = 'hidden',
34
- Guide = 'guide',
35
- Pause = 'pause',
36
- TokenExpired = 'token-expired',
37
- UserImage = 'user-image',
38
- HiddenUser = 'hidden-user',
39
- HiddenAi = 'hidden-ai',
40
- HiddenRole = 'hidden-role',
41
- HiddenGuide = 'hidden-guide',
42
- TemplateUser = 'template-user',
43
- TemplateAi = 'template-ai',
44
- TemplateRole = 'template-role',
45
- TemplateGuide = 'template-guide',
46
- TemplateHidden = 'template-hidden',
47
- ImageNotSupported = 'image-not-supported'
48
- }
49
-
50
- export enum HttpErrorCode {
51
- TokenExpired = '80003',
52
- ImageNotSupported = '82003',
53
- UnAuthorizedPreviewFile = '80401',
54
- TagRepeat = '1513405',
55
- Aborted = 20,
56
- UnLogin = 401,
57
- IamNoPermission = 'IAM_NO_PERMISSION'
58
- }
package/src/types/type.ts DELETED
@@ -1,33 +0,0 @@
1
- import {
2
- SessionContentRole,
3
- SessionPromptRole,
4
- SessionContentStatus,
5
- } from './enum.ts';
6
-
7
- export type Document = {
8
- metadata: {
9
- file_path: string;
10
- path: string;
11
- preview_path: string;
12
- };
13
- };
14
-
15
- export interface ISessionContent {
16
- id?: number;
17
- createdAt?: string;
18
- createdBy?: string;
19
- updatedAt?: string;
20
- updatedBy?: string;
21
- spaceId?: string;
22
- liked?: boolean;
23
- role: SessionContentRole;
24
- content: string;
25
- rate?: number;
26
- status?: SessionContentStatus;
27
- sessionCode?: string;
28
- }
29
-
30
- export interface ISessionPrompt {
31
- content: string;
32
- role: SessionPromptRole;
33
- }