diff --git a/api/core/types/function.go b/api/core/types/function.go index f126d659..62237133 100644 --- a/api/core/types/function.go +++ b/api/core/types/function.go @@ -83,7 +83,7 @@ var InnerFunctions = []Function{ Properties: map[string]Property{ "prompt": { Type: "string", - Description: "提示词,如果该参数中有中文的话,则需要翻译成英文。", + Description: "提示词,请自动将该参数翻译成英文。", }, }, Required: []string{}, diff --git a/api/handler/chatimpl/openai_handler.go b/api/handler/chatimpl/openai_handler.go index dfe0c23d..f59ffde6 100644 --- a/api/handler/chatimpl/openai_handler.go +++ b/api/handler/chatimpl/openai_handler.go @@ -132,11 +132,13 @@ func (h *ChatHandler) sendOpenAiMessage( utils.ReplyMessage(ws, ErrImg) } else { f := h.App.Functions[functionName] + // translate prompt if functionName == types.FuncImage { - params["user_id"] = userVo.Id - params["role_id"] = role.Id - params["chat_id"] = session.ChatId - params["session_id"] = session.SessionId + const translatePromptTemplate = "Translate the following painting prompt words into English keyword phrases. Without any explanation, directly output the keyword phrases separated by commas. The content to be translated is: [%s]" + r, err := utils.OpenAIRequest(fmt.Sprintf(translatePromptTemplate, params["prompt"]), apiKey, h.App.Config.ProxyURL, chatConfig.OpenAI.ApiURL) + if err == nil { + params["prompt"] = r + } } data, err := f.Invoke(params) if err != nil { diff --git a/api/handler/prompt_handler.go b/api/handler/prompt_handler.go index 1a0eb36c..c4e021d1 100644 --- a/api/handler/prompt_handler.go +++ b/api/handler/prompt_handler.go @@ -4,11 +4,10 @@ import ( "chatplus/core" "chatplus/core/types" "chatplus/store/model" + "chatplus/utils" "chatplus/utils/resp" "fmt" - "github.com/imroc/req/v3" - "github.com/gin-gonic/gin" "gorm.io/gorm" ) @@ -27,27 +26,6 @@ func NewPromptHandler(app *core.AppServer, db *gorm.DB) *PromptHandler { return h } -type apiRes struct { - Model string `json:"model"` - Choices []struct { - Index int `json:"index"` - Message struct { - Role string `json:"role"` - Content string `json:"content"` - } `json:"message"` - FinishReason string `json:"finish_reason"` - } `json:"choices"` -} - -type apiErrRes struct { - Error struct { - Code interface{} `json:"code"` - Message string `json:"message"` - Param interface{} `json:"param"` - Type string `json:"type"` - } `json:"error"` -} - // Rewrite translate and rewrite prompt with ChatGPT func (h *PromptHandler) Rewrite(c *gin.Context) { var data struct { @@ -93,28 +71,5 @@ func (h *PromptHandler) request(prompt string, promptTemplate string) (string, e return "", fmt.Errorf("error with fetch OpenAI API KEY:%v", res.Error) } - messages := make([]interface{}, 1) - messages[0] = types.Message{ - Role: "user", - Content: fmt.Sprintf(promptTemplate, prompt), - } - - var response apiRes - var errRes apiErrRes - r, err := req.C().SetProxyURL(h.App.Config.ProxyURL).R().SetHeader("Content-Type", "application/json"). - SetHeader("Authorization", "Bearer "+apiKey.Value). - SetBody(types.ApiRequest{ - Model: "gpt-3.5-turbo", - Temperature: 0.9, - MaxTokens: 1024, - Stream: false, - Messages: messages, - }). - SetErrorResult(&errRes). - SetSuccessResult(&response).Post(h.App.ChatConfig.OpenAI.ApiURL) - if err != nil || r.IsErrorState() { - return "", fmt.Errorf("error with http request: %v%v%s", err, r.Err, errRes.Error.Message) - } - - return response.Choices[0].Message.Content, nil + return utils.OpenAIRequest(fmt.Sprintf(promptTemplate, prompt), apiKey.Value, h.App.Config.ProxyURL, h.App.ChatConfig.OpenAI.ApiURL) } diff --git a/api/service/fun/func_img.go b/api/service/fun/func_img.go index d7476aec..22eb2e75 100644 --- a/api/service/fun/func_img.go +++ b/api/service/fun/func_img.go @@ -105,7 +105,7 @@ func (f FuncImage) Invoke(params map[string]interface{}) (string, error) { return "", fmt.Errorf("下载图片失败: %s", err.Error()) } - logger.Info(imgURL) + //logger.Info(imgURL) return fmt.Sprintf("\n\n![](%s)\n", imgURL), nil } diff --git a/api/store/model/function.go b/api/store/model/function.go new file mode 100644 index 00000000..b737d18c --- /dev/null +++ b/api/store/model/function.go @@ -0,0 +1,11 @@ +package model + +type Function struct { + Id uint `gorm:"primarykey;column:id"` + Name string + Description string + Parameters string + Required string + Action string + Enabled bool +} diff --git a/api/store/vo/function.go b/api/store/vo/function.go new file mode 100644 index 00000000..09f1f7bc --- /dev/null +++ b/api/store/vo/function.go @@ -0,0 +1,22 @@ +package vo + +type Parameters struct { + Type string `json:"type"` + Required []string `json:"required"` + Properties map[string]Property `json:"properties"` +} + +type Property struct { + Type string `json:"type"` + Description string `json:"description"` +} + +type Function struct { + Id uint `json:"id"` + Name string `json:"name"` + Description string `json:"description"` + Parameters Parameters `json:"parameters"` + Required []string `json:"required"` + Action string `json:"action"` + Enabled bool `json:"enabled"` +} diff --git a/api/utils/net.go b/api/utils/net.go index a45ecb44..972d7c74 100644 --- a/api/utils/net.go +++ b/api/utils/net.go @@ -4,6 +4,8 @@ import ( "chatplus/core/types" logger2 "chatplus/logger" "encoding/json" + "fmt" + "github.com/imroc/req/v3" "io" "net/http" "net/url" @@ -61,3 +63,51 @@ func DownloadImage(imageURL string, proxy string) ([]byte, error) { return imageBytes, nil } + +type apiRes struct { + Model string `json:"model"` + Choices []struct { + Index int `json:"index"` + Message struct { + Role string `json:"role"` + Content string `json:"content"` + } `json:"message"` + FinishReason string `json:"finish_reason"` + } `json:"choices"` +} + +type apiErrRes struct { + Error struct { + Code interface{} `json:"code"` + Message string `json:"message"` + Param interface{} `json:"param"` + Type string `json:"type"` + } `json:"error"` +} + +func OpenAIRequest(prompt string, apiKey string, proxy string, apiURL string) (string, error) { + messages := make([]interface{}, 1) + messages[0] = types.Message{ + Role: "user", + Content: prompt, + } + + var response apiRes + var errRes apiErrRes + r, err := req.C().SetProxyURL(proxy).R().SetHeader("Content-Type", "application/json"). + SetHeader("Authorization", "Bearer "+apiKey). + SetBody(types.ApiRequest{ + Model: "gpt-3.5-turbo", + Temperature: 0.9, + MaxTokens: 1024, + Stream: false, + Messages: messages, + }). + SetErrorResult(&errRes). + SetSuccessResult(&response).Post(apiURL) + if err != nil || r.IsErrorState() { + return "", fmt.Errorf("error with http request: %v%v%s", err, r.Err, errRes.Error.Message) + } + + return response.Choices[0].Message.Content, nil +} diff --git a/database/update-v3.2.3.sql b/database/update-v3.2.3.sql index d19bbc60..fe77482f 100644 --- a/database/update-v3.2.3.sql +++ b/database/update-v3.2.3.sql @@ -1,3 +1,19 @@ ALTER TABLE `chatgpt_mj_jobs` ADD `channel_id` CHAR(40) NULL DEFAULT NULL COMMENT '频道ID' AFTER `message_id`; ALTER TABLE `chatgpt_mj_jobs` DROP INDEX `task_id`; ALTER TABLE `chatgpt_products` ADD `img_calls` INT(11) NOT NULL DEFAULT '0' COMMENT '绘图次数' AFTER `calls`; + +CREATE TABLE `chatgpt_functions` ( + `id` int NOT NULL, + `name` varchar(30) NOT NULL COMMENT '函数名称', + `description` varchar(255) DEFAULT NULL COMMENT '函数描述', + `parameters` text COMMENT '函数参数(JSON)', + `required` varchar(255) NOT NULL COMMENT '必填参数(JSON)', + `action` varchar(255) DEFAULT NULL COMMENT '函数处理 API' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='函数插件表'; + +ALTER TABLE `chatgpt_functions` ADD PRIMARY KEY (`id`); +ALTER TABLE `chatgpt_functions` MODIFY `id` int NOT NULL AUTO_INCREMENT; + +ALTER TABLE `chatgpt_functions` ADD UNIQUE(`name`); + +ALTER TABLE `chatgpt_functions` ADD `enabled` TINYINT(1) NOT NULL DEFAULT '0' COMMENT '是否启用' AFTER `action`; \ No newline at end of file diff --git a/web/src/components/admin/AdminSidebar.vue b/web/src/components/admin/AdminSidebar.vue index cbd2c5e5..0dc5e898 100644 --- a/web/src/components/admin/AdminSidebar.vue +++ b/web/src/components/admin/AdminSidebar.vue @@ -111,6 +111,11 @@ const items = [ index: '/admin/reward', title: '众筹管理', }, + { + icon: 'control', + index: '/admin/functions', + title: '函数管理', + }, { icon: 'config', index: '/admin/system', @@ -121,29 +126,29 @@ const items = [ index: '/admin/loginLog', title: '用户登录日志', }, - { - icon: 'menu', - index: '1', - title: '常用模板页面', - subs: [ - { - index: '/admin/demo/form', - title: '表单页面', - }, - { - index: '/admin/demo/table', - title: '常用表格', - }, - { - index: '/admin/demo/import', - title: '导入Excel', - }, - { - index: '/admin/demo/editor', - title: '富文本编辑器', - }, - ], - }, + // { + // icon: 'menu', + // index: '1', + // title: '常用模板页面', + // subs: [ + // { + // index: '/admin/demo/form', + // title: '表单页面', + // }, + // { + // index: '/admin/demo/table', + // title: '常用表格', + // }, + // { + // index: '/admin/demo/import', + // title: '导入Excel', + // }, + // { + // index: '/admin/demo/editor', + // title: '富文本编辑器', + // }, + // ], + // }, ]; const route = useRoute(); diff --git a/web/src/router.js b/web/src/router.js index 200b0ec1..ca10f62e 100644 --- a/web/src/router.js +++ b/web/src/router.js @@ -151,28 +151,10 @@ const routes = [ component: () => import('@/views/admin/LoginLog.vue'), }, { - path: '/admin/demo/form', - name: 'admin-form', - meta: {title: '表单页面'}, - component: () => import('@/views/admin/demo/Form.vue'), - }, - { - path: '/admin/demo/table', - name: 'admin-table', - meta: {title: '数据列表'}, - component: () => import('@/views/admin/demo/Table.vue'), - }, - { - path: '/admin/demo/import', - name: 'admin-import', - meta: {title: '导入数据'}, - component: () => import('@/views/admin/demo/Import.vue'), - }, - { - path: '/admin/demo/editor', - name: 'admin-editor', - meta: {title: '富文本编辑器'}, - component: () => import('@/views/admin/demo/Editor.vue'), + path: '/admin/functions', + name: 'admin-functions', + meta: {title: '函数管理'}, + component: () => import('@/views/admin/Functions.vue'), }, ] }, @@ -185,7 +167,7 @@ const routes = [ { name: 'mobile', path: '/mobile', - meta: {title: 'ChatGPT-智能助手V3'}, + meta: {title: process.env.VUE_APP_TITLE}, component: () => import('@/views/mobile/Home.vue'), redirect: '/mobile/chat/list', children: [ diff --git a/web/src/views/admin/Functions.vue b/web/src/views/admin/Functions.vue new file mode 100644 index 00000000..5e6cad3a --- /dev/null +++ b/web/src/views/admin/Functions.vue @@ -0,0 +1,304 @@ + + + + + \ No newline at end of file