diff --git a/app/controller/app-center/aiChat.ts b/app/controller/app-center/aiChat.ts index 9527c80..350af76 100644 --- a/app/controller/app-center/aiChat.ts +++ b/app/controller/app-center/aiChat.ts @@ -23,6 +23,14 @@ export default class AiChatController extends Controller { const model = foundationModel?.model ?? E_FOUNDATION_MODEL.GPT_35_TURBO; const token = foundationModel.token; ctx.body = await ctx.service.appCenter.aiChat.getAnswerFromAi(messages, { model, token }); + } + + public async uploadFile() { + const { ctx } = this; + const fileStream = await ctx.getFileStream(); + const foundationModelObject = JSON.parse(fileStream.fields.foundationModel); + const { model, token } = foundationModelObject.foundationModel; + ctx.body = await ctx.service.appCenter.aiChat.getFileContentFromAi(fileStream, { model, token }); } } diff --git a/app/router/appCenter/base.ts b/app/router/appCenter/base.ts index c406442..2b7c273 100644 --- a/app/router/appCenter/base.ts +++ b/app/router/appCenter/base.ts @@ -1,14 +1,14 @@ /** -* Copyright (c) 2023 - present TinyEngine Authors. -* Copyright (c) 2023 - present Huawei Cloud Computing Technologies Co., Ltd. -* -* Use of this source code is governed by an MIT-style license. -* -* THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, -* BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR -* A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS. -* -*/ + * Copyright (c) 2023 - present TinyEngine Authors. + * Copyright (c) 2023 - present Huawei Cloud Computing Technologies Co., Ltd. + * + * Use of this source code is governed by an MIT-style license. + * + * THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, + * BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR + * A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS. + * + */ import { Application } from 'egg'; export default (app: Application) => { @@ -22,7 +22,6 @@ export default (app: Application) => { const subRouter = router.namespace(ROUTER_PREFIX); // 应用管理 - subRouter.get('/apps/detail/:id', controller.appCenter.apps.detail); subRouter.post('/apps/update/:id', controller.appCenter.apps.update); @@ -114,4 +113,5 @@ export default (app: Application) => { // AI大模型聊天接口 subRouter.post('/ai/chat', controller.appCenter.aiChat.aiChat); + subRouter.post('/ai/files', controller.appCenter.aiChat.uploadFile); }; diff --git a/app/service/app-center/aiChat.ts b/app/service/app-center/aiChat.ts index 85d3edf..8ed5207 100644 --- a/app/service/app-center/aiChat.ts +++ b/app/service/app-center/aiChat.ts @@ -10,15 +10,26 @@ * */ import { Service } from 'egg'; -import Transformer from '@opentiny/tiny-engine-transform'; import { E_FOUNDATION_MODEL } from '../../lib/enum'; +import * as fs from 'fs'; +import * as path from 'path'; + +const to = require('await-to-js').default; +const OpenAI = require('openai'); + export type AiMessage = { role: string; // 角色 name?: string; // 名称 content: string; // 聊天内容 + partial?: boolean; }; +interface ConfigModel { + model: string; + token: string; +} + export default class AiChat extends Service { /** * 获取ai的答复 @@ -31,11 +42,35 @@ export default class AiChat extends Service { */ async getAnswerFromAi(messages: Array, chatConfig: any) { - const answer = await this.requestAnswerFromAi(messages, chatConfig); - const answerContent = answer.choices[0]?.message.content; - // 从ai回复中提取页面的代码 - const codes = this.extractCode(answerContent); - const schema = codes ? Transformer.translate(codes) : null; + let res = await this.requestAnswerFromAi(messages, chatConfig); + let answerContent = ''; + let isFinish = res.choices[0].finish_reason; + + if (isFinish !== 'length') { + answerContent = res.choices[0]?.message.content; + } + + // 若内容过长被截断,继续回复 + while (isFinish === 'length') { + const prefix = res.choices[0].message.content; + answerContent += prefix; + messages.push({ + role: 'assistant', + content: prefix, + partial: true + }); + + res = await this.requestAnswerFromAi(messages, chatConfig); + answerContent += res.choices[0].message.content; + isFinish = res.choices[0].finish_reason; + } + + const code = this.extractCode(answerContent); + const schema = this.extractSchemaCode(code); + const answer = { + role: res.choices[0].message.role, + content: answerContent + }; const replyWithoutCode = this.removeCode(answerContent); return this.ctx.helper.getResponseData({ originalResponse: answer, @@ -123,6 +158,20 @@ export default class AiChat extends Service { return content.substring(0, start) + '<代码在画布中展示>' + content.substring(end); } + private extractSchemaCode(content) { + const startMarker = /```json/; + const endMarker = /```/; + + const start = content.search(startMarker); + const end = content.slice(start + 7).search(endMarker) + start + 7; + + if (start >= 0 && end >= 0) { + return JSON.parse(content.substring(start + 7, end).trim()); + } + + return null; + } + private getStartAndEnd(str: string) { const start = str.search(/```|