mirror of
https://github.com/yangjian102621/geekai.git
synced 2025-09-17 16:56:38 +08:00
feat: support dall-e3 api mirrors, add name field for ApiKey
This commit is contained in:
parent
a06a81a415
commit
bcc622a24d
@ -115,11 +115,10 @@ type ChatConfig struct {
|
||||
Baidu ModelAPIConfig `json:"baidu"`
|
||||
XunFei ModelAPIConfig `json:"xun_fei"`
|
||||
|
||||
EnableContext bool `json:"enable_context"` // 是否开启聊天上下文
|
||||
EnableHistory bool `json:"enable_history"` // 是否允许保存聊天记录
|
||||
ContextDeep int `json:"context_deep"` // 上下文深度
|
||||
DallApiURL string `json:"dall_api_url"` // dall-e3 绘图 API 地址
|
||||
DallImgNum int `json:"dall_img_num"` // dall-e3 出图数量
|
||||
EnableContext bool `json:"enable_context"` // 是否开启聊天上下文
|
||||
EnableHistory bool `json:"enable_history"` // 是否允许保存聊天记录
|
||||
ContextDeep int `json:"context_deep"` // 上下文深度
|
||||
DallImgNum int `json:"dall_img_num"` // dall-e3 出图数量
|
||||
}
|
||||
|
||||
type Platform string
|
||||
@ -143,7 +142,6 @@ type InviteReward struct {
|
||||
type ModelAPIConfig struct {
|
||||
Temperature float32 `json:"temperature"`
|
||||
MaxTokens int `json:"max_tokens"`
|
||||
ApiKey string `json:"api_key"`
|
||||
}
|
||||
|
||||
type SystemConfig struct {
|
||||
|
@ -27,6 +27,7 @@ func (h *ApiKeyHandler) Save(c *gin.Context) {
|
||||
var data struct {
|
||||
Id uint `json:"id"`
|
||||
Platform string `json:"platform"`
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
Value string `json:"value"`
|
||||
ApiURL string `json:"api_url"`
|
||||
@ -48,6 +49,7 @@ func (h *ApiKeyHandler) Save(c *gin.Context) {
|
||||
apiKey.ApiURL = data.ApiURL
|
||||
apiKey.Enabled = data.Enabled
|
||||
apiKey.UseProxy = data.UseProxy
|
||||
apiKey.Name = data.Name
|
||||
res := h.db.Save(&apiKey)
|
||||
if res.Error != nil {
|
||||
resp.ERROR(c, "更新数据库失败!")
|
||||
|
@ -448,8 +448,9 @@ func (h *ChatHandler) doRequest(ctx context.Context, req types.ApiRequest, platf
|
||||
|
||||
request = request.WithContext(ctx)
|
||||
request.Header.Set("Content-Type", "application/json")
|
||||
proxyURL := h.App.Config.ProxyURL
|
||||
if proxyURL != "" && platform == types.OpenAI { // 使用代理
|
||||
var proxyURL string
|
||||
if h.App.Config.ProxyURL != "" && apiKey.UseProxy { // 使用代理
|
||||
proxyURL = h.App.Config.ProxyURL
|
||||
proxy, _ := url.Parse(proxyURL)
|
||||
client = &http.Client{
|
||||
Transport: &http.Transport{
|
||||
|
@ -231,15 +231,10 @@ func (h *FunctionHandler) Dall3(c *gin.Context) {
|
||||
|
||||
// translate prompt
|
||||
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]"
|
||||
pt, err := utils.OpenAIRequest(fmt.Sprintf(translatePromptTemplate, params["prompt"]), apiKey, h.App.Config.ProxyURL)
|
||||
pt, err := utils.OpenAIRequest(h.db, fmt.Sprintf(translatePromptTemplate, params["prompt"]), h.App.Config.ProxyURL)
|
||||
if err == nil {
|
||||
prompt = pt
|
||||
}
|
||||
|
||||
apiURL := chatConfig.DallApiURL
|
||||
if utils.IsEmptyValue(apiURL) {
|
||||
apiURL = "https://api.openai.com/v1/images/generations"
|
||||
}
|
||||
imgNum := chatConfig.DallImgNum
|
||||
if imgNum <= 0 {
|
||||
imgNum = 1
|
||||
@ -247,11 +242,12 @@ func (h *FunctionHandler) Dall3(c *gin.Context) {
|
||||
var res imgRes
|
||||
var errRes ErrRes
|
||||
var request *req.Request
|
||||
if strings.Contains(apiURL, "api.openai.com") {
|
||||
if apiKey.UseProxy && h.proxyURL != "" {
|
||||
request = req.C().SetProxyURL(h.proxyURL).R()
|
||||
} else {
|
||||
request = req.C().R()
|
||||
}
|
||||
logger.Debugf("Sending %s request, ApiURL:%s, ApiKey:%s, PROXY: %s", apiKey.Platform, apiKey.ApiURL, apiKey.Value, h.proxyURL)
|
||||
r, err := request.SetHeader("Content-Type", "application/json").
|
||||
SetHeader("Authorization", "Bearer "+apiKey.Value).
|
||||
SetBody(imgReq{
|
||||
@ -261,7 +257,7 @@ func (h *FunctionHandler) Dall3(c *gin.Context) {
|
||||
Size: "1024x1024",
|
||||
}).
|
||||
SetErrorResult(&errRes).
|
||||
SetSuccessResult(&res).Post(apiURL)
|
||||
SetSuccessResult(&res).Post(apiKey.ApiURL)
|
||||
if r.IsErrorState() {
|
||||
resp.ERROR(c, "请求 OpenAI API 失败: "+errRes.Error.Message)
|
||||
return
|
||||
|
@ -3,11 +3,8 @@ package handler
|
||||
import (
|
||||
"chatplus/core"
|
||||
"chatplus/core/types"
|
||||
"chatplus/store/model"
|
||||
"chatplus/utils"
|
||||
"chatplus/utils/resp"
|
||||
"fmt"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
@ -36,7 +33,7 @@ func (h *PromptHandler) Rewrite(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
content, err := h.request(data.Prompt, rewritePromptTemplate)
|
||||
content, err := utils.OpenAIRequest(h.db, data.Prompt, rewritePromptTemplate)
|
||||
if err != nil {
|
||||
resp.ERROR(c, err.Error())
|
||||
return
|
||||
@ -54,7 +51,7 @@ func (h *PromptHandler) Translate(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
content, err := h.request(data.Prompt, translatePromptTemplate)
|
||||
content, err := utils.OpenAIRequest(h.db, data.Prompt, translatePromptTemplate)
|
||||
if err != nil {
|
||||
resp.ERROR(c, err.Error())
|
||||
return
|
||||
@ -62,14 +59,3 @@ func (h *PromptHandler) Translate(c *gin.Context) {
|
||||
|
||||
resp.SUCCESS(c, content)
|
||||
}
|
||||
|
||||
func (h *PromptHandler) request(prompt string, promptTemplate string) (string, error) {
|
||||
// 获取 OpenAI 的 API KEY
|
||||
var apiKey model.ApiKey
|
||||
res := h.db.Where("platform = ?", types.OpenAI).Where("type = ?", "chat").Where("enabled = ?", true).First(&apiKey)
|
||||
if res.Error != nil {
|
||||
return "", fmt.Errorf("error with fetch OpenAI API KEY:%v", res.Error)
|
||||
}
|
||||
|
||||
return utils.OpenAIRequest(fmt.Sprintf(promptTemplate, prompt), apiKey, h.App.Config.ProxyURL)
|
||||
}
|
||||
|
@ -303,7 +303,7 @@ func (h *UserHandler) ProfileUpdate(c *gin.Context) {
|
||||
}
|
||||
h.db.First(&user, user.Id)
|
||||
user.Avatar = data.Avatar
|
||||
user.ChatConfig = utils.JsonEncode(data.ChatConfig)
|
||||
user.Nickname = data.Nickname
|
||||
res := h.db.Updates(&user)
|
||||
if res.Error != nil {
|
||||
resp.ERROR(c, "更新用户信息失败")
|
||||
|
@ -4,6 +4,7 @@ package model
|
||||
type ApiKey struct {
|
||||
BaseModel
|
||||
Platform string
|
||||
Name string
|
||||
Type string // 用途 chat => 聊天,img => 绘图
|
||||
Value string // API Key 的值
|
||||
ApiURL string // 当前 KEY 的 API 地址
|
||||
|
@ -4,6 +4,7 @@ package vo
|
||||
type ApiKey struct {
|
||||
BaseVo
|
||||
Platform string `json:"platform"`
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
Value string `json:"value"` // API Key 的值
|
||||
ApiURL string `json:"api_url"`
|
||||
|
@ -7,6 +7,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/imroc/req/v3"
|
||||
"gorm.io/gorm"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
@ -86,7 +87,13 @@ type apiErrRes struct {
|
||||
} `json:"error"`
|
||||
}
|
||||
|
||||
func OpenAIRequest(prompt string, apiKey model.ApiKey, proxy string) (string, error) {
|
||||
func OpenAIRequest(db *gorm.DB, prompt string, proxy string) (string, error) {
|
||||
var apiKey model.ApiKey
|
||||
res := db.Where("platform = ?", types.OpenAI).Where("type = ?", "chat").Where("enabled = ?", true).First(&apiKey)
|
||||
if res.Error != nil {
|
||||
return "", fmt.Errorf("error with fetch OpenAI API KEY:%v", res.Error)
|
||||
}
|
||||
|
||||
messages := make([]interface{}, 1)
|
||||
messages[0] = types.Message{
|
||||
Role: "user",
|
||||
|
@ -4,3 +4,4 @@ ALTER TABLE `chatgpt_api_keys` ADD `api_url` VARCHAR(255) NULL COMMENT 'API 地
|
||||
ALTER TABLE `chatgpt_api_keys` DROP INDEX `value`;
|
||||
ALTER TABLE `chatgpt_mj_jobs` ADD UNIQUE(`task_id`);
|
||||
ALTER TABLE `chatgpt_api_keys` ADD `use_proxy` TINYINT(1) NULL COMMENT '是否使用代理访问' AFTER `enabled`;
|
||||
ALTER TABLE `chatgpt_api_keys` ADD `name` VARCHAR(30) NULL COMMENT '名称' AFTER `platform`;
|
@ -15,7 +15,7 @@
|
||||
</el-upload>
|
||||
</el-row>
|
||||
<el-form-item label="昵称">
|
||||
{{ user['nickname'] }}
|
||||
<el-input v-model="user['nickname']"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="手机号">
|
||||
<span>{{ user.mobile }}</span>
|
||||
@ -44,16 +44,6 @@
|
||||
<el-tag type="danger">{{ dateFormat(user['expired_time']) }}</el-tag>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="OpenAI API KEY">
|
||||
<el-input v-model="user.chat_config['api_keys']['OpenAI']"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="Azure API KEY">
|
||||
<el-input v-model="user['chat_config']['api_keys']['Azure']"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="ChatGLM API KEY">
|
||||
<el-input v-model="user['chat_config']['api_keys']['ChatGLM']"/>
|
||||
</el-form-item>
|
||||
|
||||
<el-row class="opt-line">
|
||||
<el-button color="#47fff1" :dark="false" round @click="save">保存</el-button>
|
||||
</el-row>
|
||||
@ -78,7 +68,6 @@ const user = ref({
|
||||
mobile: '',
|
||||
calls: 0,
|
||||
tokens: 0,
|
||||
chat_config: {api_keys: {OpenAI: "", Azure: "", ChatGLM: ""}}
|
||||
})
|
||||
const vipImg = ref("/images/vip.png")
|
||||
|
||||
@ -87,7 +76,6 @@ onMounted(() => {
|
||||
// 获取最新用户信息
|
||||
httpGet('/api/user/profile').then(res => {
|
||||
user.value = res.data
|
||||
user.value.chat_config.api_keys = res.data.chat_config.api_keys ?? {OpenAI: "", Azure: "", ChatGLM: ""}
|
||||
}).catch(e => {
|
||||
ElMessage.error("获取用户信息失败:" + e.message)
|
||||
});
|
||||
|
@ -8,7 +8,16 @@
|
||||
<el-row>
|
||||
<el-table :data="items" :row-key="row => row.id" table-layout="auto">
|
||||
<el-table-column prop="platform" label="所属平台"/>
|
||||
<el-table-column prop="value" label="KEY"/>
|
||||
<el-table-column prop="name" label="名称"/>
|
||||
<el-table-column prop="value" label="KEY">
|
||||
<template #default="scope">
|
||||
<el-tooltip class="box-item"
|
||||
effect="dark"
|
||||
:content="scope.row.api_url"
|
||||
placement="top">{{ scope.row.value }}
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="type" label="用途">
|
||||
<template #default="scope">
|
||||
<el-tag v-if="scope.row.type === 'chat'">聊天</el-tag>
|
||||
@ -67,6 +76,9 @@
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="名称:" prop="name">
|
||||
<el-input v-model="item.name" autocomplete="off"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="用途:" prop="type">
|
||||
<el-select v-model="item.type" placeholder="请选择用途" @change="changePlatform">
|
||||
<el-option v-for="item in types" :value="item.value" :label="item.name" :key="item.value">{{
|
||||
@ -125,6 +137,7 @@ const item = ref({})
|
||||
const showDialog = ref(false)
|
||||
const rules = reactive({
|
||||
platform: [{required: true, message: '请选择平台', trigger: 'change',}],
|
||||
name: [{required: true, message: '请输入名称', trigger: 'change',}],
|
||||
type: [{required: true, message: '请选择用途', trigger: 'change',}],
|
||||
value: [{required: true, message: '请输入 API KEY 值', trigger: 'change',}]
|
||||
})
|
||||
@ -135,8 +148,8 @@ const platforms = ref([
|
||||
{
|
||||
name: "【OpenAI】ChatGPT",
|
||||
value: "OpenAI",
|
||||
api_url: "https://api.fast-tunnel.one/v1/chat/completions",
|
||||
img_url: "https://api.openai.com/v1/images/generations"
|
||||
api_url: "https://gpt.bemore.lol/v1/chat/completions",
|
||||
img_url: "https://gpt.bemore.lol/v1/images/generations"
|
||||
},
|
||||
{
|
||||
name: "【讯飞】星火大模型",
|
||||
|
@ -204,9 +204,6 @@
|
||||
</el-form-item>
|
||||
|
||||
<el-divider content-position="center">OpenAI</el-divider>
|
||||
<el-form-item label="API 地址" prop="open_ai.api_url">
|
||||
<el-input v-model="chat['open_ai']['api_url']" placeholder="支持变量,{model} => 模型名称"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="模型创意度">
|
||||
<el-slider v-model="chat['open_ai']['temperature']" :max="2" :step="0.1"/>
|
||||
<div class="tip">值越大 AI 回答越发散,值越小回答越保守,建议保持默认值</div>
|
||||
@ -216,9 +213,6 @@
|
||||
</el-form-item>
|
||||
|
||||
<el-divider content-position="center">Azure</el-divider>
|
||||
<el-form-item label="API 地址" prop="azure.api_url">
|
||||
<el-input v-model="chat['azure']['api_url']" placeholder="支持变量,{model} => 模型名称"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="模型创意度">
|
||||
<el-slider v-model="chat['azure']['temperature']" :max="2" :step="0.1"/>
|
||||
<div class="tip">值越大 AI 回答越发散,值越小回答越保守,建议保持默认值</div>
|
||||
@ -228,9 +222,6 @@
|
||||
</el-form-item>
|
||||
|
||||
<el-divider content-position="center">ChatGLM</el-divider>
|
||||
<el-form-item label="API 地址" prop="chat_gml.api_url">
|
||||
<el-input v-model="chat['chat_gml']['api_url']" placeholder="支持变量,{model} => 模型名称"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="模型创意度">
|
||||
<el-slider v-model="chat['chat_gml']['temperature']" :max="1" :step="0.01"/>
|
||||
<div class="tip">值越大 AI 回答越发散,值越小回答越保守,建议保持默认值</div>
|
||||
@ -240,9 +231,6 @@
|
||||
</el-form-item>
|
||||
|
||||
<el-divider content-position="center">文心一言</el-divider>
|
||||
<el-form-item label="API 地址" prop="baidu.api_url">
|
||||
<el-input v-model="chat['baidu']['api_url']" placeholder="支持变量,{model} => 模型名称"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="模型创意度">
|
||||
<el-slider v-model="chat['baidu']['temperature']" :max="1" :step="0.01"/>
|
||||
<div class="tip">值越大 AI 回答越发散,值越小回答越保守,建议保持默认值</div>
|
||||
@ -252,9 +240,6 @@
|
||||
</el-form-item>
|
||||
|
||||
<el-divider content-position="center">讯飞星火</el-divider>
|
||||
<el-form-item label="API 地址" prop="xun_fei.api_url">
|
||||
<el-input v-model="chat['xun_fei']['api_url']" placeholder="支持变量,{model} => 模型名称"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="模型创意度">
|
||||
<el-slider v-model="chat['xun_fei']['temperature']" :max="1" :step="0.1"/>
|
||||
<div class="tip">值越大 AI 回答越发散,值越小回答越保守,建议保持默认值</div>
|
||||
@ -264,10 +249,7 @@
|
||||
</el-form-item>
|
||||
|
||||
<el-divider content-position="center">AI绘图</el-divider>
|
||||
<el-form-item label="DALL-E3 API地址">
|
||||
<el-input v-model="chat['dall_api_url']" placeholder="OpenAI官方API需要配合代理使用"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="默认出图数量">
|
||||
<el-form-item label="DALL-E3出图数量">
|
||||
<el-input v-model.number="chat['dall_img_num']" placeholder="调用 DALL E3 API 传入的出图数量"/>
|
||||
</el-form-item>
|
||||
<el-form-item style="text-align: right">
|
||||
@ -287,11 +269,11 @@ import {InfoFilled, UploadFilled} from "@element-plus/icons-vue";
|
||||
|
||||
const system = ref({models: []})
|
||||
const chat = ref({
|
||||
open_ai: {api_url: "", temperature: 1, max_tokens: 1024},
|
||||
azure: {api_url: "", temperature: 1, max_tokens: 1024},
|
||||
chat_gml: {api_url: "", temperature: 0.95, max_tokens: 1024},
|
||||
baidu: {api_url: "", temperature: 0.95, max_tokens: 1024},
|
||||
xun_fei: {api_url: "", temperature: 0.5, max_tokens: 1024},
|
||||
open_ai: {temperature: 1, max_tokens: 1024},
|
||||
azure: {temperature: 1, max_tokens: 1024},
|
||||
chat_gml: {temperature: 0.95, max_tokens: 1024},
|
||||
baidu: {temperature: 0.95, max_tokens: 1024},
|
||||
xun_fei: {temperature: 0.5, max_tokens: 1024},
|
||||
context_deep: 0,
|
||||
enable_context: true,
|
||||
enable_history: true,
|
||||
|
Loading…
Reference in New Issue
Block a user