aiChat.ts 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455
  1. import axios, { cancelToken } from '@/services/config'
  2. import { v4 as uuidv4 } from 'uuid'
  3. import { fetchEventSource } from '@microsoft/fetch-event-source'
  4. import { ref } from 'vue'
  5. const model = {}
  6. let organizeId = ''
  7. const userId2 = ref('')
  8. const userName = ref('')
  9. interface ChatParams {
  10. id: string;
  11. message: string;
  12. userId: string;
  13. model: string;
  14. file_ids: string[];
  15. sound_url: string;
  16. temperature: number;
  17. top_p: number;
  18. max_completion_tokens: number;
  19. stream: boolean;
  20. uid: string;
  21. session_name: string;
  22. tts_language: string;
  23. }
  24. const DEFAULT_PARAMS: Omit<ChatParams, 'message' | 'uid' | 'stream'> = {
  25. id: 'a7741704-ba56-40b7-a6b8-62a423ef9376',
  26. userId: '6c56ec0e-2c74-11ef-bee5-005056b86db5',
  27. model: 'open-doubao',
  28. file_ids: [],
  29. sound_url: '',
  30. temperature: 0.2,
  31. top_p: 1,
  32. max_completion_tokens: 4096,
  33. session_name: 'pptSession_name',
  34. tts_language: 'zh-CN'
  35. }
  36. export const chat_no_stream = (msg: string, agentId: string, userId: string, language: string, session_name?: string): { promise: Promise<string>; abort: () => void } => {
  37. const source = cancelToken.source()
  38. const promise = (async () => {
  39. const agentData = await getAgentModel(agentId)
  40. const params: ChatParams = {
  41. ...DEFAULT_PARAMS,
  42. id: agentId,
  43. message: `Language: ${language === 'en'
  44. ? 'English'
  45. : language === 'hk'
  46. ? 'Traditional Chinese'
  47. : 'Chinese'
  48. } ${msg} ${language === 'hk' ? '請用繁體中文回复' : language === 'en' ? 'Please reply in English' : '請用中文回复'}`,
  49. uid: uuidv4(),
  50. stream: false,
  51. model: agentData?.modelType || 'open-doubao',
  52. userId: userId,
  53. tts_language: getTtsLanguage(language),
  54. session_name: session_name || uuidv4()
  55. }
  56. try {
  57. const res = await axios.post('https://appapi.cocorobo.cn/api/agentchats/ai_agent_chat', params, {
  58. cancelToken: source.token
  59. })
  60. let content = res?.message || ''
  61. console.log(content)
  62. // 清理可能的 markdown 格式
  63. if (content.includes('```json')) {
  64. // 提取 ```json 和 ``` 之间的内容
  65. const jsonMatch = content.match(/```json\s*([\s\S]*?)\s*```/)
  66. if (jsonMatch) {
  67. content = jsonMatch[1].trim()
  68. }
  69. }
  70. else if (content.includes('```')) {
  71. // 提取 ``` 和 ``` 之间的内容
  72. const codeMatch = content.match(/```\s*([\s\S]*?)\s*```/)
  73. if (codeMatch) {
  74. content = codeMatch[1].trim()
  75. }
  76. }
  77. return content
  78. }
  79. catch (error) {
  80. console.log(error)
  81. return ''
  82. }
  83. })()
  84. return {
  85. promise,
  86. abort: () => source.cancel('Request canceled by user')
  87. }
  88. }
  89. export const chat_stream = async (
  90. msg: string,
  91. agentId: string,
  92. userId: string,
  93. language: string,
  94. onMessage: (event: { type: 'message' | 'close' | 'error' | 'messageEnd'; data: string }) => void,
  95. session_name?: string,
  96. file_ids?: Array<string>
  97. ): Promise<{ abort: () => void }> => {
  98. const agentData = await getAgentModel(agentId)
  99. const params: ChatParams = {
  100. ...DEFAULT_PARAMS,
  101. id: agentId,
  102. file_ids: file_ids || [],
  103. message: `Language: ${language === 'en'
  104. ? 'English'
  105. : language === 'hk'
  106. ? 'Traditional Chinese'
  107. : 'Chinese'
  108. } ${msg} ${language === 'hk' ? '請用繁體中文回复' : language === 'en' ? 'Please reply in English' : '請用中文回复'}`,
  109. uid: uuidv4(),
  110. stream: true,
  111. model: agentData?.modelType || 'open-doubao',
  112. userId: userId,
  113. tts_language: getTtsLanguage(language),
  114. session_name: session_name || uuidv4()
  115. }
  116. const ctrl = new AbortController()
  117. let content = ''
  118. // 开始请求
  119. fetchEventSource('https://appapi.cocorobo.cn/api/agentchats/ai_agent_chat', {
  120. method: 'POST',
  121. body: JSON.stringify(params),
  122. signal: ctrl.signal,
  123. headers: {
  124. 'Content-Type': 'application/json',
  125. },
  126. onmessage(event) {
  127. const data = JSON.parse(event.data)
  128. if (data.content) {
  129. if (data.content != '[DONE]') {
  130. content += data.content
  131. onMessage({
  132. type: 'message',
  133. data: content
  134. })
  135. }
  136. else {
  137. onMessage({
  138. type: 'messageEnd',
  139. data: content
  140. })
  141. }
  142. }
  143. },
  144. onclose() {
  145. onMessage({
  146. type: 'close',
  147. data: 'SSE Connection closed'
  148. })
  149. },
  150. onerror(err) {
  151. onMessage({
  152. type: 'error',
  153. data: err.message || 'Unknown error'
  154. })
  155. // 返回 undefined 以阻止自动重连,如需重连则删除此行
  156. throw err
  157. },
  158. }).catch(err => {
  159. onMessage({
  160. type: 'error',
  161. data: err || 'Unknown error'
  162. })
  163. console.log('err', err)
  164. })
  165. // 返回 abort 方法
  166. return {
  167. abort: () => ctrl.abort()
  168. }
  169. }
  170. export const getAgentModel = async (agentId: string) => {
  171. if (model[agentId]) {
  172. return model[agentId]
  173. }
  174. const res = await axios.get(`https://appapi.cocorobo.cn/api/agents/agent/${agentId}`)
  175. model[agentId] = res
  176. return model[agentId]
  177. }
  178. export const getTtsLanguage = (langCode: string) => {
  179. switch (langCode) {
  180. case 'en':return 'en-US'
  181. case 'hk':return 'yue-CN'
  182. default :return 'zh-CN'
  183. }
  184. }
  185. // AI 模型常量
  186. const AI_MODEL_CONSTANTS = {
  187. // DEFAULT_MODEL: 'gpt-4o-2024-11-20'
  188. DEFAULT_MODEL: 'qwen-flash'
  189. }
  190. export const chat_no_stream2 = async (prompt: any[] = [], response_format = {
  191. 'type': 'text'
  192. }, model = AI_MODEL_CONSTANTS.DEFAULT_MODEL) => {
  193. return await new Promise((resolve) => {
  194. const uid = uuidv4()
  195. const data = JSON.stringify({
  196. model: model,
  197. temperature: 0,
  198. max_tokens: 4096,
  199. top_p: 1,
  200. frequency_penalty: 0,
  201. presence_penalty: 0,
  202. messages: prompt,
  203. uid: uid,
  204. mind_map_question: '',
  205. stream: false,
  206. response_format: response_format
  207. })
  208. const config = {
  209. method: 'post',
  210. url: 'https://appapi.cocorobo.cn/api/common/chat',
  211. headers: {
  212. 'Content-Type': 'application/json'
  213. },
  214. data: data,
  215. }
  216. axios(config)
  217. .then((response) => {
  218. let content = response?.FunctionResponse?.choices[0]?.message?.content || ''
  219. // 清理可能的 markdown 格式
  220. if (content.includes('```json')) {
  221. // 提取 ```json 和 ``` 之间的内容
  222. const jsonMatch = content.match(/```json\s*([\s\S]*?)\s*```/)
  223. if (jsonMatch) {
  224. content = jsonMatch[1].trim()
  225. }
  226. }
  227. else if (content.includes('```')) {
  228. // 提取 ``` 和 ``` 之间的内容
  229. const codeMatch = content.match(/```\s*([\s\S]*?)\s*```/)
  230. if (codeMatch) {
  231. content = codeMatch[1].trim()
  232. }
  233. }
  234. resolve(content)
  235. })
  236. .catch( (error) => {
  237. console.log(error)
  238. // this.$message.error('服务器繁忙');
  239. resolve(false)
  240. })
  241. })
  242. }
  243. export const agentlistloading = ref(false)
  244. export const getAgentChatList = async (id: string, userId: string): Promise<any[]> => {
  245. if (!id) {
  246. return []
  247. }
  248. agentlistloading.value = true
  249. if (!organizeId || userId2.value !== userId) {
  250. userId2.value = userId
  251. const res = await axios.get('https://pbl.cocorobo.cn/api/pbl/selectUser', {
  252. params: { userid: userId }
  253. })
  254. userName.value = res[0][0].name
  255. organizeId = res[0][0].organizeId
  256. }
  257. try {
  258. const response = await axios.post('https://gpt4.cocorobo.cn/get_agent_chat', {
  259. userid: userId,
  260. groupid: id,
  261. }, {
  262. headers: {
  263. 'Content-Type': 'application/json',
  264. 'hwMac': organizeId,
  265. },
  266. })
  267. const chat_list = JSON.parse(response?.FunctionResponse || '[]')
  268. const messages = []
  269. chat_list.forEach((item: any, index: number) => {
  270. const json: any = {
  271. role: 'user' as const,
  272. userName: item.username,
  273. content: decodeURIComponent(item.problem),
  274. uid: id,
  275. AI: 'AI',
  276. aiContent: decodeURIComponent(item.answer),
  277. reasoning: item.reasoning_content,
  278. oldContent: decodeURIComponent(new DOMParser().parseFromString(
  279. item.answer,
  280. 'text/html'
  281. ).documentElement.textContent),
  282. isShowSynchronization: false,
  283. filename: item.filename,
  284. index: index,
  285. createtime: item.createtime,
  286. is_mind_map: item.problem.includes('思维导图') ||
  287. item.problem.includes('思維導圖') ||
  288. item.problem.includes('mindMap'),
  289. graph: item.problem === '知识图谱', // 使用默认值,因为无法访问this.lang
  290. }
  291. try {
  292. json.jsonData = item.jsonData !== 'undefined' && item.jsonData !== null ? JSON.parse(decodeURIComponent(item.jsonData)) : null
  293. // 从 jsonData 中读取 syncTranscriptionText 值
  294. if (json.jsonData && json.jsonData.syncTranscriptionText !== undefined) {
  295. json.syncTranscriptionText = json.jsonData.syncTranscriptionText
  296. }
  297. else {
  298. // 如果没有 jsonData 或 syncTranscriptionText,使用默认值
  299. json.syncTranscriptionText = false
  300. }
  301. // 从 jsonData 中读取 contentType 值
  302. if (json.jsonData && json.jsonData.contentType !== undefined) {
  303. json.contentType = json.jsonData.contentType
  304. }
  305. else {
  306. // 如果没有 jsonData 或 contentType,使用默认值
  307. json.contentType = 'text'
  308. }
  309. // 新增:从 jsonData 中恢复用户音频播放所需字段
  310. if (json.jsonData && json.jsonData.audio) {
  311. json.audio = json.jsonData.audio
  312. }
  313. if (json.jsonData && json.jsonData.durationSec) {
  314. json.durationSec = json.jsonData.durationSec
  315. }
  316. }
  317. catch (error) {
  318. console.error('Error parsing jsonData:', error)
  319. json.jsonData = null
  320. json.syncTranscriptionText = false
  321. json.contentType = 'text'
  322. agentlistloading.value = false
  323. }
  324. messages.push(json)
  325. })
  326. agentlistloading.value = false
  327. return messages
  328. }
  329. catch (error) {
  330. console.error('Error fetching agent chat list:', error)
  331. return []
  332. }
  333. }
  334. export interface InsertChatParams {
  335. answer: string;
  336. problem: string;
  337. type: string;
  338. alltext: string;
  339. assistant_id: string;
  340. userId: string;
  341. userName: string;
  342. fileId?: string;
  343. latestMessage?: any;
  344. agentHeadUrl?: string;
  345. agentAssistantName?: string;
  346. jsonData?: any;
  347. }
  348. export interface InsertChatResult {
  349. success: boolean;
  350. questions?: string[];
  351. }
  352. export const insertChat = async (params: InsertChatParams): Promise<InsertChatResult> => {
  353. const {
  354. answer,
  355. problem,
  356. type,
  357. alltext,
  358. assistant_id,
  359. fileId,
  360. latestMessage,
  361. agentHeadUrl,
  362. agentAssistantName,
  363. jsonData
  364. } = params
  365. const jsonData2: any = {
  366. headUrl: agentHeadUrl || '',
  367. assistantName: agentAssistantName || '',
  368. ...jsonData
  369. }
  370. try {
  371. const response = await axios.post('https://gpt4.cocorobo.cn/insert_chat', {
  372. userId: userId2.value,
  373. userName: userName.value,
  374. groupId: assistant_id,
  375. answer: encodeURIComponent(answer),
  376. problem: encodeURIComponent(problem),
  377. file_id: type === 'chat' ? '' : fileId,
  378. alltext,
  379. type,
  380. jsonData: encodeURIComponent(JSON.stringify(jsonData))
  381. }, {
  382. headers: {
  383. 'Content-Type': 'application/json',
  384. hwMac: organizeId,
  385. },
  386. })
  387. // 处理返回的问题结果
  388. if (response?.FunctionResponse?.questions_result) {
  389. const data = response?.FunctionResponse.questions_result
  390. if (data.includes('\n')) {
  391. const arr = data.split('\n')
  392. let questions: string[] = []
  393. if (arr.length > 3) {
  394. questions = arr.slice(0, 3)
  395. }
  396. else {
  397. questions = [...arr]
  398. }
  399. return {
  400. success: true,
  401. questions
  402. }
  403. }
  404. }
  405. return {
  406. success: true
  407. }
  408. }
  409. catch (error) {
  410. console.error('Error inserting chat:', error)
  411. return {
  412. success: false
  413. }
  414. }
  415. }