aiChat.ts 12 KB

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