index.ts 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. import {
  2. ChatRequest,
  3. ChatResponse,
  4. ErrorResponse,
  5. LmConfigListParams,
  6. LmConfigAddReq,
  7. LmConfigEditReq,
  8. LmConfigStatusReq,
  9. LmConfigDeleteParams,
  10. LmConfigGetParams
  11. } from '/@/api/assist/type'
  12. import { get, post, del, put } from '/@/utils/request'
  13. import getOrigin from '/@/utils/origin'
  14. import { getToken } from "/@/utils/auth";
  15. export default {
  16. model: {
  17. // 大语言模型配置列表
  18. getList: (params?: LmConfigListParams) => get('/system/lmconfig/list', params),
  19. // 获取大语言模型配置信息
  20. detail: (params: LmConfigGetParams) => get('/system/lmconfig/get', params),
  21. // 添加大语言模型配置
  22. add: (data: LmConfigAddReq) => post('/system/lmconfig/add', data),
  23. // 修改大语言模型配置
  24. edit: (data: LmConfigEditReq) => put('/system/lmconfig/edit', data),
  25. // 删除大语言模型配置
  26. del: (params: LmConfigDeleteParams) => del('/system/lmconfig/delete', params),
  27. // 设置大语言模型配置状态
  28. setStatus: (data: LmConfigStatusReq) => put('/system/lmconfig/status', data),
  29. },
  30. // SSE聊天方法
  31. chat: (
  32. data:
  33. {
  34. chatRequest: ChatRequest,
  35. // eslint-disable-next-line no-unused-vars
  36. onReceive: (resp: ChatResponse) => void,
  37. // eslint-disable-next-line no-unused-vars
  38. onComplete?: (error: Error|undefined)=> void
  39. },
  40. ) => {
  41. const {chatRequest, onReceive,onComplete} = data
  42. //FIXME 需要抹掉
  43. chatRequest.modelMcpId = 1 as unknown as number[]
  44. chatRequest["UserId"] = 10
  45. // 构建SSE URL
  46. const baseURL = getOrigin();
  47. const url = `${baseURL}/ai/chat`;
  48. // 使用fetch API实现SSE POST请求(EventSource不支持POST和自定义headers)
  49. const controller = new AbortController();
  50. // 使用fetch API实现SSE POST请求
  51. const startSSE = async () => {
  52. try {
  53. const headers: Record<string, string> = {
  54. 'Content-Type': 'application/json',
  55. 'Accept': 'text/event-stream',
  56. 'Cache-Control': 'no-cache',
  57. };
  58. // 添加认证token
  59. const token = getToken();
  60. if (token) {
  61. headers['Authorization'] = `Bearer ${token}`;
  62. }
  63. const response = await fetch(url, {
  64. method: 'POST',
  65. headers,
  66. body: JSON.stringify(chatRequest),
  67. signal: controller.signal,
  68. }).catch((e:Error)=> e);
  69. if (response instanceof Error) {
  70. throw new Error('无法连接到服务器');
  71. }
  72. if (!response.ok) {
  73. throw new Error(`HTTP error! status: ${response.status}`);
  74. }
  75. const reader = response.body?.getReader();
  76. const decoder = new TextDecoder();
  77. if (!reader) {
  78. throw new Error('Response body is not readable');
  79. }
  80. // 读取SSE流
  81. let currentEvent = '';
  82. let buffer = '';
  83. while (true) {
  84. const { done, value } = await reader.read();
  85. if (done) {
  86. break;
  87. }
  88. buffer += decoder.decode(value, { stream: true });
  89. // 按双换行符分割事件块
  90. const eventBlocks = buffer.split('\n\n');
  91. // 保留最后一个可能不完整的事件块
  92. buffer = eventBlocks.pop() || '';
  93. for (const eventBlock of eventBlocks) {
  94. if (!eventBlock.trim()) continue;
  95. const lines = eventBlock.split('\n');
  96. let eventType = '';
  97. let dataLines: string[] = [];
  98. // 解析事件块中的每一行
  99. for (const line of lines) {
  100. const trimmedLine = line.trim();
  101. if (trimmedLine.startsWith('event:')) {
  102. eventType = trimmedLine.substring(6).trim();
  103. } else if (trimmedLine.startsWith('data:')) {
  104. // 提取data后的内容,保留原始格式(包括可能的空格)
  105. const dataContent = line.substring(line.indexOf('data:') + 5);
  106. dataLines.push(dataContent);
  107. }
  108. }
  109. // 如果有事件类型,更新当前事件
  110. if (eventType) {
  111. currentEvent = eventType;
  112. }
  113. // 如果有数据行,处理数据
  114. if (dataLines.length > 0) {
  115. // 将多行data合并,用换行符连接
  116. const data = dataLines.join('\n');
  117. // 根据事件类型处理数据
  118. switch (currentEvent) {
  119. case 'message': {
  120. const messageResponse: ChatResponse = {
  121. type: 'message',
  122. message: data
  123. };
  124. onReceive(messageResponse);
  125. break;
  126. }
  127. case 'toolcall': {
  128. const tools = JSON.parse(data);
  129. const toolcallResponse: ChatResponse = {
  130. type: 'toolcall',
  131. request: {
  132. name: tools["name"],
  133. data: tools["arguments"]
  134. }
  135. };
  136. onReceive(toolcallResponse);
  137. break;
  138. }
  139. case 'toolres': {
  140. const tools = JSON.parse(data);
  141. const toolresResponse: ChatResponse = {
  142. type: 'toolres',
  143. response: {
  144. name: tools["name"],
  145. data: tools["response"]
  146. },
  147. };
  148. onReceive(toolresResponse);
  149. break;
  150. }
  151. case 'error': {
  152. const errorResponse: ErrorResponse = {
  153. type: 'error',
  154. error: data
  155. }
  156. onReceive(errorResponse)
  157. break
  158. }
  159. case 'meta': {
  160. break
  161. }
  162. default: {
  163. // 如果没有明确的事件类型,默认作为消息处理
  164. const defaultResponse: ChatResponse = {
  165. type: 'message',
  166. message: data
  167. };
  168. onReceive(defaultResponse);
  169. break;
  170. }
  171. }
  172. }
  173. }
  174. // 处理buffer中剩余的可能完整的单行事件
  175. const remainingLines = buffer.split('\n');
  176. const completeLines = remainingLines.slice(0, -1);
  177. buffer = remainingLines[remainingLines.length - 1] || '';
  178. for (const line of completeLines) {
  179. const trimmedLine = line.trim();
  180. if (trimmedLine === '') {
  181. continue;
  182. }
  183. if (trimmedLine.startsWith('event:')) {
  184. currentEvent = trimmedLine.substring(6).trim();
  185. continue;
  186. }
  187. if (trimmedLine.startsWith('data:')) {
  188. const data = line.substring(line.indexOf('data:') + 5);
  189. // 根据事件类型处理数据
  190. switch (currentEvent) {
  191. case 'message': {
  192. const messageResponse: ChatResponse = {
  193. type: 'message',
  194. message: data
  195. };
  196. onReceive(messageResponse);
  197. break;
  198. }
  199. case 'toolcall': {
  200. try {
  201. const tools = JSON.parse(data);
  202. const toolcallResponse: ChatResponse = {
  203. type: 'toolcall',
  204. request: {
  205. name: tools["name"],
  206. data: tools["arguments"]
  207. }
  208. };
  209. onReceive(toolcallResponse);
  210. } catch (error) {
  211. console.error('解析toolcall数据失败:', error, '原始数据:', data);
  212. }
  213. break;
  214. }
  215. case 'toolres': {
  216. try {
  217. const tools = JSON.parse(data);
  218. const toolresResponse: ChatResponse = {
  219. type: 'toolres',
  220. response: {
  221. name: tools["name"],
  222. data: tools["response"]
  223. },
  224. };
  225. onReceive(toolresResponse);
  226. } catch (error) {
  227. console.error('解析toolres数据失败:', error, '原始数据:', data);
  228. }
  229. break;
  230. }
  231. default: {
  232. // 如果没有明确的事件类型,默认作为消息处理
  233. const defaultResponse: ChatResponse = {
  234. type: 'message',
  235. message: data
  236. };
  237. onReceive(defaultResponse);
  238. break;
  239. }
  240. }
  241. }
  242. }
  243. }
  244. onComplete?.(undefined)
  245. } catch (error: any) {
  246. if (error.name !== 'AbortError') {
  247. console.error('SSE连接错误:', error);
  248. }
  249. onComplete?.(error)
  250. }
  251. };
  252. // 启动SSE连接
  253. startSSE();
  254. // 返回关闭连接的函数
  255. return () => {
  256. controller.abort();
  257. };
  258. },
  259. }