perf: 优化AI设置

This commit is contained in:
kuaifan 2025-02-21 11:37:39 +08:00
parent db6500369f
commit f78c4a1fb0
2 changed files with 278 additions and 391 deletions

View File

@ -68,39 +68,39 @@
</li> </li>
</ul> </ul>
<Tabs v-else v-model="aibotTabAction" class="ai-tabs"> <Tabs v-else v-model="aibotTabAction" class="ai-tabs">
<TabPane label="ChatGPT" name="opanai"> <TabPane label="ChatGPT" name="openai">
<div class="aibot-warp"> <div class="aibot-warp">
<SystemAibot type="ChatGPT" v-if="aibotTabAction == 'opanai'" /> <SystemAibot :type="aibotTabAction" v-if="aibotTabAction == 'openai'" />
</div> </div>
</TabPane> </TabPane>
<TabPane label="Claude" name="claude"> <TabPane label="Claude" name="claude">
<div class="aibot-warp"> <div class="aibot-warp">
<SystemAibot type="Claude" v-if="aibotTabAction == 'claude'" /> <SystemAibot :type="aibotTabAction" v-if="aibotTabAction == 'claude'" />
</div> </div>
</TabPane> </TabPane>
<TabPane label="DeepSeek" name="deepseek"> <TabPane label="DeepSeek" name="deepseek">
<div class="aibot-warp"> <div class="aibot-warp">
<SystemAibot type="DeepSeek" v-if="aibotTabAction == 'deepseek'" /> <SystemAibot :type="aibotTabAction" v-if="aibotTabAction == 'deepseek'" />
</div> </div>
</TabPane> </TabPane>
<TabPane label="Gemini" name="gemini"> <TabPane label="Gemini" name="gemini">
<div class="aibot-warp"> <div class="aibot-warp">
<SystemAibot type="Gemini" v-if="aibotTabAction == 'gemini'" /> <SystemAibot :type="aibotTabAction" v-if="aibotTabAction == 'gemini'" />
</div> </div>
</TabPane> </TabPane>
<TabPane :label="$L('智谱清言')" name="zhipu"> <TabPane :label="$L('智谱清言')" name="zhipu">
<div class="aibot-warp"> <div class="aibot-warp">
<SystemAibot type="Zhipu" v-if="aibotTabAction == 'zhipu'" /> <SystemAibot :type="aibotTabAction" v-if="aibotTabAction == 'zhipu'" />
</div> </div>
</TabPane> </TabPane>
<TabPane :label="$L('文心一言')" name="wenxin"> <TabPane :label="$L('文心一言')" name="wenxin">
<div class="aibot-warp"> <div class="aibot-warp">
<SystemAibot type="Wenxin" v-if="aibotTabAction == 'wenxin'" /> <SystemAibot :type="aibotTabAction" v-if="aibotTabAction == 'wenxin'" />
</div> </div>
</TabPane> </TabPane>
<TabPane :label="$L('通义千问')" name="qianwen"> <TabPane :label="$L('通义千问')" name="qianwen">
<div class="aibot-warp"> <div class="aibot-warp">
<SystemAibot type="Qianwen" v-if="aibotTabAction == 'qianwen'" /> <SystemAibot :type="aibotTabAction" v-if="aibotTabAction == 'qianwen'" />
</div> </div>
</TabPane> </TabPane>
</Tabs> </Tabs>
@ -298,7 +298,7 @@ export default {
desc: this.$L('我是达摩院自主研发的超大规模语言模型,能够回答问题、创作文字,还能表达观点、撰写代码。') desc: this.$L('我是达摩院自主研发的超大规模语言模型,能够回答问题、创作文字,还能表达观点、撰写代码。')
}, },
], ],
aibotTabAction: "opanai", aibotTabAction: "openai",
aibotShow: false, aibotShow: false,
aibotType: 1, aibotType: 1,
aibotDialogSearchLoad: "", aibotDialogSearchLoad: "",
@ -449,7 +449,7 @@ export default {
break; break;
case 'robot': case 'robot':
this.aibotType = 1; this.aibotType = 1;
this.aibotTabAction = "opanai"; this.aibotTabAction = "openai";
this.aibotShow = true; this.aibotShow = true;
break; break;
case 'signin': case 'signin':

View File

@ -6,49 +6,47 @@
:rules="ruleData" :rules="ruleData"
v-bind="formOptions" v-bind="formOptions"
@submit.native.prevent> @submit.native.prevent>
<div class="block-setting-box" v-if="aiConfig[type]"> <template v-for="field in fields">
<h3>{{ type }}</h3> <FormItem :label="$L(field.label)" :prop="field.prop">
<div class="form-box"> <template v-if="field.type === 'password'">
<template v-for="field in aiConfig[type].fields"> <Input
<FormItem :label="$L(field.label)" :prop="field.prop"> :maxlength="255"
<template v-if="field.type === 'password'"> v-model="formData[field.prop]"
<Input type="password"
:maxlength="255" :placeholder="$L(field.placeholder)"/>
v-model="formData[field.prop]"
type="password"
:placeholder="$L(field.placeholder)"/>
</template>
<template v-else-if="field.type === 'model'">
<Select v-model="formData[field.prop]" transfer>
<Option v-for="item in modelOption(field.prop)" :value="item.value" :key="item.value">{{ item.label }}</Option>
</Select>
</template>
<template v-else-if="field.type === 'textarea'">
<Input
:maxlength="500"
type="textarea"
:autosize="{minRows:2,maxRows:6}"
v-model="formData[field.prop]"
:placeholder="$L(field.placeholder)"/>
</template>
<template v-else>
<Input
:maxlength="500"
v-model="formData[field.prop]"
:placeholder="$L(field.placeholder)"/>
</template>
<div v-if="field.link || field.tip" class="form-tip">
<template v-if="field.link">
{{$L(field.tipPrefix || '获取方式')}} <a :href="field.link" target="_blank">{{ field.link }}</a>
</template>
<template v-else-if="field.tip">
{{$L(field.tip)}}
</template>
</div>
</FormItem>
</template> </template>
</div> <template v-else-if="field.type === 'model'">
</div> <Select v-model="formData[field.prop]" transfer>
<Option v-for="item in modelOption(field.prop)" :value="item.value" :key="item.value">{{ item.label }}</Option>
</Select>
</template>
<template v-else-if="field.type === 'textarea'">
<Input
:maxlength="500"
type="textarea"
:autosize="{minRows:2,maxRows:6}"
v-model="formData[field.prop]"
:placeholder="$L(field.placeholder)"/>
</template>
<template v-else>
<Input
:maxlength="500"
v-model="formData[field.prop]"
:placeholder="$L(field.placeholder)"/>
</template>
<div v-if="field.link || field.tip" class="form-tip">
<template v-if="field.link">
{{$L(field.tipPrefix || '获取方式')}} <a :href="field.link" target="_blank">{{ field.link }}</a>
</template>
<template v-else-if="field.tip">
{{$L(field.tip)}}
</template>
</div>
<div v-if="field.functions" class="form-tip" style="margin-top:-5px">
<a href="javascript:void(0)" @click="functionClick(field.prop)">{{ $L(field.functions) }}</a>
</div>
</FormItem>
</template>
</Form> </Form>
<div class="setting-footer"> <div class="setting-footer">
<Button :loading="loadIng > 0" type="primary" @click="submitForm">{{ $L('提交') }}</Button> <Button :loading="loadIng > 0" type="primary" @click="submitForm">{{ $L('提交') }}</Button>
@ -73,344 +71,159 @@ export default {
formData: {}, formData: {},
ruleData: {}, ruleData: {},
aiConfig: { aiConfig: {
ChatGPT: { fields: [
fields: [ {
{ label: "API Key",
label: 'API Key', prop: "key",
prop: 'openai_key', type: "password"
type: 'password', },
placeholder: 'OpenAI API Key', {
tipPrefix: '访问OpenAI网站查看', label: "模型列表",
link: 'https://platform.openai.com/account/api-keys' prop: "models",
}, type: "textarea",
{ placeholder: "一行一个模型名称",
label: '模型列表', functions: "使用默认模型列表"
prop: 'openai_models', },
type: 'textarea', {
placeholder: '一行一个模型名称', label: "默认模型",
tipPrefix: '查看说明', prop: "model",
link: 'https://platform.openai.com/docs/models' type: "model",
}, placeholder: "请选择默认模型",
{ tip: "可选数据来自模型列表"
label: '默认模型', },
prop: 'openai_model', {
type: 'model', label: "Base URL",
placeholder: '请选择默认模型', prop: "base_url",
tip: '可选数据来自模型列表', placeholder: "Enter base URL...",
}, tip: "API请求的基础URL路径如果没有请留空"
{ },
label: 'Base URL', {
prop: 'openai_base_url', label: "使用代理",
placeholder: 'Enter base URL...', prop: "agency",
tip: 'API请求的基础URL路径如果没有请留空' placeholder: '支持 http 或 socks 代理',
}, tip: "例如http://proxy.com 或 socks5://proxy.com"
{ },
label: '使用代理', {
prop: 'openai_agency', label: "Temperature",
placeholder: '支持 http 或 socks 代理', prop: "temperature",
tip: '例如http://proxy.com 或 socks5://proxy.com' placeholder: "模型温度,低则保守,高则多样",
}, tip: "例如0.7范围0-1默认0.7"
{ },
label: 'Temperature', {
prop: 'openai_temperature', label: "默认提示词",
placeholder: '模型温度,低则保守,高则多样', prop: "system",
tip: '例如0.7范围0-1默认0.7' type: "textarea",
}, placeholder: "请输入默认提示词",
{ tip: "例如你是一个人开发的AI助手"
label: '默认提示词', }
prop: 'openai_system', ],
type: 'textarea', aiList: {
placeholder: '请输入默认提示词', openai: {
tip: '例如你是一个人开发的AI助手' extraFields: [
} {
] prop: "key",
}, placeholder: "OpenAI API Key",
Claude: { link: "https://platform.openai.com/account/api-keys"
fields: [ },
{ {
label: 'API Key', prop: "models",
prop: 'claude_key', link: "https://platform.openai.com/docs/models",
type: 'password', }
placeholder: 'Claude API Key', ]
link: 'https://docs.anthropic.com/en/api/getting-started' },
}, claude: {
{ extraFields: [
label: '模型列表', {
prop: 'claude_models', prop: "key",
type: 'textarea', placeholder: "Claude API Key",
placeholder: '一行一个模型名称', link: "https://docs.anthropic.com/en/api/getting-started"
tipPrefix: '查看说明', },
link: 'https://docs.anthropic.com/en/docs/about-claude/models' {
}, prop: "models",
{ link: "https://docs.anthropic.com/en/docs/about-claude/models"
label: '默认模型', }
prop: 'claude_model', ]
type: 'model', },
placeholder: '请选择默认模型', deepseek: {
tip: '可选数据来自模型列表', extraFields: [
}, {
{ prop: "key",
label: '使用代理', placeholder: "DeepSeek API Key",
prop: 'claude_agency', link: "https://platform.deepseek.com/api_keys"
placeholder: '支持 http 或 socks 代理', },
tip: '例如http://proxy.com 或 socks5://proxy.com' {
}, prop: "models",
{ link: "https://api-docs.deepseek.com/zh-cn/quick_start/pricing"
label: 'Temperature', }
prop: 'claude_temperature', ]
placeholder: '模型温度,低则保守,高则多样', },
tip: '例如0.7范围0-1默认0.7' gemini: {
}, extraFields: [
{ {
label: '默认提示词', prop: "key",
prop: 'claude_system', placeholder: "Gemini API Key",
type: 'textarea', link: "https://makersuite.google.com/app/apikey"
placeholder: '请输入默认提示词', },
tip: '例如你是一个人开发的AI助手' {
} prop: "models",
] link: "https://ai.google.dev/models/gemini"
}, },
DeepSeek: { {
fields: [ prop: "agency",
{ placeholder: "仅支持 http 代理",
label: 'API Key', tip: "例如http://proxy.com"
prop: 'deepseek_key', }
type: 'password', ]
placeholder: 'DeepSeek API Key', },
tipPrefix: '访问DeepSeek网站查看', zhipu: {
link: 'https://platform.deepseek.com/api_keys' extraFields: [
}, {
{ prop: "key",
label: '模型列表', placeholder: "Zhipu API Key",
prop: 'deepseek_models', link: "https://bigmodel.cn/usercenter/apikeys"
type: 'textarea', },
placeholder: '一行一个模型名称', {
tipPrefix: '查看说明', prop: "models",
link: 'https://api-docs.deepseek.com/zh-cn/quick_start/pricing' link: "https://open.bigmodel.cn/dev/api"
}, }
{ ]
label: '默认模型', },
prop: 'deepseek_model', qianwen: {
type: 'model', extraFields: [
placeholder: '请选择默认模型', {
tip: '可选数据来自模型列表', prop: "key",
}, placeholder: "Qianwen API Key",
{ link: "https://help.aliyun.com/zh/model-studio/developer-reference/get-api-key"
label: 'Base URL', },
prop: 'deepseek_base_url', {
type: 'input', prop: "models",
placeholder: 'Enter base URL...', link: "https://help.aliyun.com/zh/model-studio/getting-started/models"
tip: 'API请求的基础URL路径如果没有请留空' }
}, ]
{ },
label: '使用代理', wenxin: {
prop: 'deepseek_agency', extraFields: [
placeholder: '支持 http 或 socks 代理', {
tip: '例如http://proxy.com 或 socks5://proxy.com' prop: "key",
}, placeholder: "Wenxin API Key",
{ link: "https://console.bce.baidu.com/qianfan/ais/console/applicationConsole/application/v1"
label: 'Temperature', },
prop: 'deepseek_temperature', {
placeholder: '模型温度,低则保守,高则多样', prop: "secret",
tip: '例如0.7范围0-1默认0.7' placeholder: "Wenxin Secret Key",
}, link: "https://console.bce.baidu.com/qianfan/ais/console/applicationConsole/application/v1",
{ type: "password",
label: '默认提示词', label: "Secret Key",
prop: 'deepseek_system', after: "key"
type: 'textarea', },
placeholder: '请输入默认提示词', {
tip: '例如你是一个人开发的AI助手' prop: "models",
} link: "https://cloud.baidu.com/doc/WENXINWORKSHOP/s/Blfmc9dlf"
] }
}, ]
Gemini: { }
fields: [
{
label: 'API Key',
prop: 'gemini_key',
type: 'password',
placeholder: 'Gemini API Key',
link: 'https://makersuite.google.com/app/apikey'
},
{
label: '模型列表',
prop: 'gemini_models',
type: 'textarea',
placeholder: '一行一个模型名称',
tipPrefix: '查看说明',
link: 'https://ai.google.dev/models/gemini'
},
{
label: '默认模型',
prop: 'gemini_model',
type: 'model',
placeholder: '请选择默认模型',
tip: '可选数据来自模型列表',
},
{
label: '使用代理',
prop: 'gemini_agency',
placeholder: '仅支持 http 代理',
tip: '例如http://proxy.com 或 https://proxy.com'
},
{
label: 'Temperature',
prop: 'gemini_temperature',
placeholder: '模型温度,低则保守,高则多样',
tip: '例如0.7范围0-1默认0.7'
},
{
label: '默认提示词',
prop: 'gemini_system',
type: 'textarea',
placeholder: '请输入默认提示词',
tip: '例如你是一个人开发的AI助手'
}
]
},
Zhipu: {
fields: [
{
label: 'API Key',
prop: 'zhipu_key',
type: 'password',
placeholder: 'Zhipu API Key',
link: 'https://bigmodel.cn/usercenter/apikeys'
},
{
label: '模型列表',
prop: 'zhipu_models',
type: 'textarea',
placeholder: '一行一个模型名称',
tipPrefix: '查看说明',
link: 'https://open.bigmodel.cn/dev/api'
},
{
label: '默认模型',
prop: 'zhipu_model',
type: 'model',
placeholder: '请选择默认模型',
tip: '可选数据来自模型列表',
},
{
label: '使用代理',
prop: 'zhipu_agency',
placeholder: '支持 http 或 socks 代理',
tip: '例如http://proxy.com 或 socks5://proxy.com'
},
{
label: 'Temperature',
prop: 'zhipu_temperature',
placeholder: '模型温度,低则保守,高则多样',
tip: '例如0.7范围0-1默认0.7'
},
{
label: '默认提示词',
prop: 'zhipu_system',
type: 'textarea',
placeholder: '请输入默认提示词',
tip: '例如你是一个人开发的AI助手'
}
]
},
Qianwen: {
fields: [
{
label: 'API Key',
prop: 'qianwen_key',
type: 'password',
placeholder: 'Qianwen API Key',
link: 'https://help.aliyun.com/zh/model-studio/developer-reference/get-api-key'
},
{
label: '模型列表',
prop: 'qianwen_models',
type: 'textarea',
placeholder: '一行一个模型名称',
tipPrefix: '查看说明',
link: 'https://help.aliyun.com/zh/model-studio/getting-started/models'
},
{
label: '默认模型',
prop: 'qianwen_model',
type: 'model',
placeholder: '请选择默认模型',
tip: '可选数据来自模型列表',
},
{
label: '使用代理',
prop: 'qianwen_agency',
placeholder: '支持 http 或 socks 代理',
tip: '例如http://proxy.com 或 socks5://proxy.com'
},
{
label: 'Temperature',
prop: 'qianwen_temperature',
placeholder: '模型温度,低则保守,高则多样',
tip: '例如0.7范围0-1默认0.7'
},
{
label: '默认提示词',
prop: 'qianwen_system',
type: 'textarea',
placeholder: '请输入默认提示词',
tip: '例如你是一个人开发的AI助手'
}
]
},
Wenxin: {
fields: [
{
label: 'API Key',
prop: 'wenxin_key',
type: 'password',
placeholder: 'Wenxin API Key',
link: 'https://console.bce.baidu.com/qianfan/ais/console/applicationConsole/application/v1'
},
{
label: 'Secret Key',
prop: 'wenxin_secret',
type: 'password',
placeholder: 'Wenxin Secret Key',
link: 'https://console.bce.baidu.com/qianfan/ais/console/applicationConsole/application/v1'
},
{
label: '模型列表',
prop: 'wenxin_models',
type: 'textarea',
placeholder: '一行一个模型名称',
tipPrefix: '查看说明',
link: 'https://cloud.baidu.com/doc/WENXINWORKSHOP/s/Blfmc9dlf'
},
{
label: '默认模型',
prop: 'wenxin_model',
type: 'model',
placeholder: '请选择默认模型',
tip: '可选数据来自模型列表',
},
{
label: '使用代理',
prop: 'wenxin_agency',
placeholder: '支持 http 或 socks 代理',
tip: '例如http://proxy.com 或 socks5://proxy.com'
},
{
label: 'Temperature',
prop: 'wenxin_temperature',
placeholder: '模型温度,低则保守,高则多样',
tip: '例如0.7范围0-1默认0.7'
},
{
label: '默认提示词',
prop: 'wenxin_system',
type: 'textarea',
placeholder: '请输入默认提示词',
tip: '例如你是一个人开发的AI助手'
}
]
} }
} },
} }
}, },
mounted() { mounted() {
@ -418,6 +231,62 @@ export default {
}, },
computed: { computed: {
...mapState(['formOptions']), ...mapState(['formOptions']),
fields({aiConfig, type}) {
// typeaiList
if (!aiConfig.aiList?.[type]) {
return [];
}
// fieldstypeextraFields
const baseFields = JSON.parse(JSON.stringify(aiConfig.fields));
const { extraFields = [] } = aiConfig.aiList[type];
// fieldtype
const prefixedFields = baseFields.map(field => ({
...field,
prop: `${type}_${field.prop}`
}));
// extraFields
extraFields.forEach(extraField => {
const newField = {
...extraField,
prop: `${type}_${extraField.prop}`
};
// propfield
const existingIndex = prefixedFields.findIndex(f =>
f.prop === newField.prop
);
if (existingIndex !== -1) {
// field
prefixedFields[existingIndex] = {
...prefixedFields[existingIndex],
...newField
};
} else {
//
if (extraField.after) {
// after
const afterIndex = prefixedFields.findIndex(f =>
f.prop === `${type}_${extraField.after}`
);
if (afterIndex !== -1) {
prefixedFields.splice(afterIndex + 1, 0, newField);
} else {
prefixedFields.push(newField);
}
} else {
// after
prefixedFields.push(newField);
}
}
});
return prefixedFields;
},
}, },
methods: { methods: {
modelOption(prop) { modelOption(prop) {
@ -430,6 +299,7 @@ export default {
} }
return [] return []
}, },
submitForm() { submitForm() {
this.$refs.formData.validate((valid) => { this.$refs.formData.validate((valid) => {
if (valid) { if (valid) {
@ -437,16 +307,18 @@ export default {
} }
}) })
}, },
resetForm() { resetForm() {
this.formData = $A.cloneJSON(this.formDatum_bak); this.formData = $A.cloneJSON(this.formDatum_bak);
}, },
systemSetting(save) { systemSetting(save) {
const props = this.aiConfig[this.type].fields.map(item => item.prop); const props = this.fields.map(item => item.prop);
const data = Object.fromEntries(Object.entries(this.formData).filter(([key]) => props.includes(key))); const data = Object.fromEntries(Object.entries(this.formData).filter(([key]) => props.includes(key)));
this.loadIng++; this.loadIng++;
this.$store.dispatch("call", { this.$store.dispatch("call", {
url: 'system/setting/aibot?type=' + (save ? 'save' : 'all'), url: 'system/setting/aibot?type=' + (save ? 'save' : 'all'),
data, data: save ? data : {},
}).then(({data}) => { }).then(({data}) => {
if (save) { if (save) {
$A.messageSuccess('修改成功'); $A.messageSuccess('修改成功');
@ -460,6 +332,21 @@ export default {
}).finally(_ => { }).finally(_ => {
this.loadIng--; this.loadIng--;
}); });
},
functionClick(prop) {
if (prop === `${this.type}_models`) {
this.$store.dispatch("call", {
url: 'system/setting/aibot_defmodels?type=' + this.type,
spinner: 600,
}).then(({data}) => {
this.formData[prop] = data.models.join('\n');
}).catch(({msg}) => {
$A.modalError(msg || '获取失败');
});
} else {
$A.messageError('未知操作');
}
} }
} }
} }