mirror of
https://github.com/songquanpeng/one-api.git
synced 2025-09-18 01:26:37 +08:00
feat: initial i18n support
This commit is contained in:
parent
bdf312e5dc
commit
d9f2df2baf
@ -34,5 +34,123 @@
|
|||||||
"request_failed": "Request failed",
|
"request_failed": "Request failed",
|
||||||
"no_link": "Admin has not set up the top-up link!"
|
"no_link": "Admin has not set up the top-up link!"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"channel": {
|
||||||
|
"title": "Channel Management",
|
||||||
|
"search": "Search channels by ID, name and key...",
|
||||||
|
"balance_notice": "OpenAI channels no longer support getting balance via key, so balance shows as 0. For supported channel types, click balance to refresh.",
|
||||||
|
"test_notice": "Channel testing only supports chat models, preferring gpt-3.5-turbo. If unavailable, uses the first model in your configured list.",
|
||||||
|
"detail_notice": "Click the detail button below to show balance and set additional test models.",
|
||||||
|
"table": {
|
||||||
|
"id": "ID",
|
||||||
|
"name": "Name",
|
||||||
|
"group": "Group",
|
||||||
|
"type": "Type",
|
||||||
|
"status": "Status",
|
||||||
|
"response_time": "Response Time",
|
||||||
|
"balance": "Balance",
|
||||||
|
"priority": "Priority",
|
||||||
|
"test_model": "Test Model",
|
||||||
|
"actions": "Actions",
|
||||||
|
"no_name": "None",
|
||||||
|
"status_enabled": "Enabled",
|
||||||
|
"status_disabled": "Disabled",
|
||||||
|
"status_auto_disabled": "Disabled",
|
||||||
|
"status_disabled_tip": "This channel is manually disabled",
|
||||||
|
"status_auto_disabled_tip": "This channel is automatically disabled",
|
||||||
|
"status_unknown": "Unknown Status",
|
||||||
|
"not_tested": "Not Tested",
|
||||||
|
"priority_tip": "Channel selection priority, higher is preferred",
|
||||||
|
"select_test_model": "Please select test model",
|
||||||
|
"click_to_update": "Click to update"
|
||||||
|
},
|
||||||
|
"buttons": {
|
||||||
|
"test": "Test",
|
||||||
|
"delete": "Delete",
|
||||||
|
"confirm_delete": "Delete Channel",
|
||||||
|
"enable": "Enable",
|
||||||
|
"disable": "Disable",
|
||||||
|
"edit": "Edit",
|
||||||
|
"add": "Add New Channel",
|
||||||
|
"test_all": "Test All Channels",
|
||||||
|
"test_disabled": "Test Disabled Channels",
|
||||||
|
"delete_disabled": "Delete Disabled Channels",
|
||||||
|
"confirm_delete_disabled": "Confirm Delete",
|
||||||
|
"refresh": "Refresh",
|
||||||
|
"show_detail": "Details",
|
||||||
|
"hide_detail": "Hide Details"
|
||||||
|
},
|
||||||
|
"messages": {
|
||||||
|
"test_success": "Channel ${name} test successful, model ${model}, time ${time}s, output: ${message}",
|
||||||
|
"test_all_started": "Channel testing started successfully, please refresh page to see results.",
|
||||||
|
"delete_disabled_success": "Deleted all disabled channels, total: ${count}",
|
||||||
|
"balance_update_success": "Channel ${name} balance updated successfully!",
|
||||||
|
"all_balance_updated": "All enabled channel balances have been updated!"
|
||||||
|
},
|
||||||
|
"edit": {
|
||||||
|
"title_edit": "Update Channel Information",
|
||||||
|
"title_create": "Create New Channel",
|
||||||
|
"type": "Type",
|
||||||
|
"name": "Name",
|
||||||
|
"name_placeholder": "Please enter name",
|
||||||
|
"group": "Group",
|
||||||
|
"group_placeholder": "Please select groups that can use this channel",
|
||||||
|
"group_addition": "Please edit group multipliers in system settings to add new group:",
|
||||||
|
"models": "Models",
|
||||||
|
"models_placeholder": "Please select models supported by this channel",
|
||||||
|
"model_mapping": "Model Mapping",
|
||||||
|
"model_mapping_placeholder": "Optional, used to modify model names in request body. A JSON string where keys are request model names and values are target model names",
|
||||||
|
"system_prompt": "System Prompt",
|
||||||
|
"system_prompt_placeholder": "Optional, used to force set system prompt. Use with custom model & model mapping. First create a unique custom model name above, then map it to a natively supported model",
|
||||||
|
"base_url": "Proxy",
|
||||||
|
"base_url_placeholder": "Optional, used for API calls through proxy. Enter proxy address in format: https://domain.com",
|
||||||
|
"key": "Key",
|
||||||
|
"key_placeholder": "Please enter key",
|
||||||
|
"batch": "Batch Create",
|
||||||
|
"batch_placeholder": "Please enter keys, one per line",
|
||||||
|
"buttons": {
|
||||||
|
"cancel": "Cancel",
|
||||||
|
"submit": "Submit",
|
||||||
|
"fill_models": "Fill Related Models",
|
||||||
|
"fill_all": "Fill All Models",
|
||||||
|
"clear": "Clear All Models",
|
||||||
|
"add_custom": "Add",
|
||||||
|
"custom_placeholder": "Enter custom model name"
|
||||||
|
},
|
||||||
|
"messages": {
|
||||||
|
"name_required": "Please enter channel name and key!",
|
||||||
|
"models_required": "Please select at least one model!",
|
||||||
|
"model_mapping_invalid": "Model mapping must be valid JSON format!",
|
||||||
|
"update_success": "Channel updated successfully!",
|
||||||
|
"create_success": "Channel created successfully!"
|
||||||
|
},
|
||||||
|
"spark_version": "Model Version",
|
||||||
|
"spark_version_placeholder": "Please enter Spark model version from API URL, e.g.: v2.1",
|
||||||
|
"knowledge_id": "Knowledge Base ID",
|
||||||
|
"knowledge_id_placeholder": "Please enter knowledge base ID, e.g.: 123456",
|
||||||
|
"plugin_param": "Plugin Parameter",
|
||||||
|
"plugin_param_placeholder": "Please enter plugin parameter (X-DashScope-Plugin header value)",
|
||||||
|
"coze_notice": "For Coze, model name is the Bot ID. You can add prefix `bot-`, e.g.: `bot-123456`.",
|
||||||
|
"douban_notice": "For Douban, you need to go to",
|
||||||
|
"douban_notice_link": "Model Inference Page",
|
||||||
|
"douban_notice_2": "to create an inference endpoint, and use the endpoint name as model name, e.g.: `ep-20240608051426-tkxvl`.",
|
||||||
|
"aws_region_placeholder": "region, e.g.: us-west-2",
|
||||||
|
"aws_ak_placeholder": "AWS IAM Access Key",
|
||||||
|
"aws_sk_placeholder": "AWS IAM Secret Key",
|
||||||
|
"vertex_region_placeholder": "Vertex AI Region, e.g.: us-east5",
|
||||||
|
"vertex_project_id": "Vertex AI Project ID",
|
||||||
|
"vertex_project_id_placeholder": "Vertex AI Project ID",
|
||||||
|
"vertex_credentials": "Google Cloud Application Default Credentials JSON",
|
||||||
|
"vertex_credentials_placeholder": "Google Cloud Application Default Credentials JSON",
|
||||||
|
"user_id": "User ID",
|
||||||
|
"user_id_placeholder": "User ID who generated this key",
|
||||||
|
"key_prompts": {
|
||||||
|
"default": "Please enter the authentication key for this channel",
|
||||||
|
"zhipu": "Enter in format: APIKey|SecretKey",
|
||||||
|
"spark": "Enter in format: APPID|APISecret|APIKey",
|
||||||
|
"fastgpt": "Enter in format: APIKey-AppId, e.g.: fastgpt-0sp2gtvfdgyi4k30jwlgwf1i-64f335d84283f05518e9e041",
|
||||||
|
"tencent": "Enter in format: AppId|SecretId|SecretKey"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,5 +34,123 @@
|
|||||||
"request_failed": "请求失败",
|
"request_failed": "请求失败",
|
||||||
"no_link": "超级管理员未设置充值链接!"
|
"no_link": "超级管理员未设置充值链接!"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"channel": {
|
||||||
|
"title": "管理渠道",
|
||||||
|
"search": "搜索渠道的 ID,名称和密钥 ...",
|
||||||
|
"balance_notice": "OpenAI 渠道已经不再支持通过 key 获取余额,因此余额显示为 0。对于支持的渠道类型,请点击余额进行刷新。",
|
||||||
|
"test_notice": "渠道测试仅支持 chat 模型,优先使用 gpt-3.5-turbo,如果该模型不可用则使用你所配置的模型列表中的第一个模型。",
|
||||||
|
"detail_notice": "点击下方详情按钮可以显示余额以及设置额外的测试模型。",
|
||||||
|
"table": {
|
||||||
|
"id": "ID",
|
||||||
|
"name": "名称",
|
||||||
|
"group": "分组",
|
||||||
|
"type": "类型",
|
||||||
|
"status": "状态",
|
||||||
|
"response_time": "响应时间",
|
||||||
|
"balance": "余额",
|
||||||
|
"priority": "优先级",
|
||||||
|
"test_model": "测试模型",
|
||||||
|
"actions": "操作",
|
||||||
|
"no_name": "无",
|
||||||
|
"status_enabled": "已启用",
|
||||||
|
"status_disabled": "已禁用",
|
||||||
|
"status_auto_disabled": "已禁用",
|
||||||
|
"status_disabled_tip": "本渠道被手动禁用",
|
||||||
|
"status_auto_disabled_tip": "本渠道被程序自动禁用",
|
||||||
|
"status_unknown": "未知状态",
|
||||||
|
"not_tested": "未测试",
|
||||||
|
"priority_tip": "渠道选择优先级,越高越优先",
|
||||||
|
"select_test_model": "请选择测试模型",
|
||||||
|
"click_to_update": "点击更新"
|
||||||
|
},
|
||||||
|
"buttons": {
|
||||||
|
"test": "测试",
|
||||||
|
"delete": "删除",
|
||||||
|
"confirm_delete": "删除渠道",
|
||||||
|
"enable": "启用",
|
||||||
|
"disable": "禁用",
|
||||||
|
"edit": "编辑",
|
||||||
|
"add": "添加新的渠道",
|
||||||
|
"test_all": "测试所有渠道",
|
||||||
|
"test_disabled": "测试禁用渠道",
|
||||||
|
"delete_disabled": "删除禁用渠道",
|
||||||
|
"confirm_delete_disabled": "确认删除",
|
||||||
|
"refresh": "刷新",
|
||||||
|
"show_detail": "详情",
|
||||||
|
"hide_detail": "隐藏详情"
|
||||||
|
},
|
||||||
|
"messages": {
|
||||||
|
"test_success": "渠道 ${name} 测试成功,模型 ${model},耗时 ${time} 秒,模型输出:${message}",
|
||||||
|
"test_all_started": "已成功开始测试渠道,请刷新页面查看结果。",
|
||||||
|
"delete_disabled_success": "已删除所有禁用渠道,共计 ${count} 个",
|
||||||
|
"balance_update_success": "渠道 ${name} 余额更新成功!",
|
||||||
|
"all_balance_updated": "已更新完毕所有已启用渠道余额!"
|
||||||
|
},
|
||||||
|
"edit": {
|
||||||
|
"title_edit": "更新渠道信息",
|
||||||
|
"title_create": "创建新的渠道",
|
||||||
|
"type": "类型",
|
||||||
|
"name": "名称",
|
||||||
|
"name_placeholder": "请输入名称",
|
||||||
|
"group": "分组",
|
||||||
|
"group_placeholder": "请选择可以使用该渠道的分组",
|
||||||
|
"group_addition": "请在系统设置页面编辑分组倍率以添加新的分组:",
|
||||||
|
"models": "模型",
|
||||||
|
"models_placeholder": "请选择该渠道所支持的模型",
|
||||||
|
"model_mapping": "模型重定向",
|
||||||
|
"model_mapping_placeholder": "此项可选,用于修改请求体中的模型名称,为一个 JSON 字符串,键为请求中模型名称,值为要替换的模型名称",
|
||||||
|
"system_prompt": "系统提示词",
|
||||||
|
"system_prompt_placeholder": "此项可选,用于强制设置给定的系统提示词,请配合自定义模型 & 模型重定向使用,首先创建一个唯一的自定义模型名称并在上面填入,之后将该自定义模型重定向映射到该渠道一个原生支持的模型",
|
||||||
|
"base_url": "代理",
|
||||||
|
"base_url_placeholder": "此项可选,用于通过代理站来进行 API 调用,请输入代理站地址,格式为:https://domain.com",
|
||||||
|
"key": "密钥",
|
||||||
|
"key_placeholder": "请输入密钥",
|
||||||
|
"batch": "批量创建",
|
||||||
|
"batch_placeholder": "请输入密钥,一行一个",
|
||||||
|
"buttons": {
|
||||||
|
"cancel": "取消",
|
||||||
|
"submit": "提交",
|
||||||
|
"fill_models": "填入相关模型",
|
||||||
|
"fill_all": "填入所有模型",
|
||||||
|
"clear": "清除所有模型",
|
||||||
|
"add_custom": "填入",
|
||||||
|
"custom_placeholder": "输入自定义模型名称"
|
||||||
|
},
|
||||||
|
"messages": {
|
||||||
|
"name_required": "请填写渠道名称和渠道密钥!",
|
||||||
|
"models_required": "请至少选择一个模型!",
|
||||||
|
"model_mapping_invalid": "模型映射必须是合法的 JSON 格式!",
|
||||||
|
"update_success": "渠道更新成功!",
|
||||||
|
"create_success": "渠道创建成功!"
|
||||||
|
},
|
||||||
|
"spark_version": "模型版本",
|
||||||
|
"spark_version_placeholder": "请输入星火大模型版本,注意是接口地址中的版本号,例如:v2.1",
|
||||||
|
"knowledge_id": "知识库 ID",
|
||||||
|
"knowledge_id_placeholder": "请输入知识库 ID,例如:123456",
|
||||||
|
"plugin_param": "插件参数",
|
||||||
|
"plugin_param_placeholder": "请输入插件参数,即 X-DashScope-Plugin 请求头的取值",
|
||||||
|
"coze_notice": "对于 Coze 而言,模型名称即 Bot ID,你可以添加一个前缀 `bot-`,例如:`bot-123456`。",
|
||||||
|
"douban_notice": "对于豆包而言,需要手动去",
|
||||||
|
"douban_notice_link": "模型推理页面",
|
||||||
|
"douban_notice_2": "创建推理接入点,以接入点名称作为模型名称,例如:`ep-20240608051426-tkxvl`。",
|
||||||
|
"aws_region_placeholder": "region,例如:us-west-2",
|
||||||
|
"aws_ak_placeholder": "AWS IAM Access Key",
|
||||||
|
"aws_sk_placeholder": "AWS IAM Secret Key",
|
||||||
|
"vertex_region_placeholder": "Vertex AI Region,例如:us-east5",
|
||||||
|
"vertex_project_id": "Vertex AI Project ID",
|
||||||
|
"vertex_project_id_placeholder": "Vertex AI Project ID",
|
||||||
|
"vertex_credentials": "Google Cloud Application Default Credentials JSON",
|
||||||
|
"vertex_credentials_placeholder": "Google Cloud Application Default Credentials JSON",
|
||||||
|
"user_id": "User ID",
|
||||||
|
"user_id_placeholder": "生成该密钥的用户 ID",
|
||||||
|
"key_prompts": {
|
||||||
|
"default": "请输入渠道对应的鉴权密钥",
|
||||||
|
"zhipu": "按照如下格式输入:APIKey|SecretKey",
|
||||||
|
"spark": "按照如下格式输入:APPID|APISecret|APIKey",
|
||||||
|
"fastgpt": "按照如下格式输入:APIKey-AppId,例如:fastgpt-0sp2gtvfdgyi4k30jwlgwf1i-64f335d84283f05518e9e041",
|
||||||
|
"tencent": "按照如下格式输入:AppId|SecretId|SecretKey"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
Dropdown,
|
Dropdown,
|
||||||
@ -31,13 +32,17 @@ function renderTimestamp(timestamp) {
|
|||||||
|
|
||||||
let type2label = undefined;
|
let type2label = undefined;
|
||||||
|
|
||||||
function renderType(type) {
|
function renderType(type, t) {
|
||||||
if (!type2label) {
|
if (!type2label) {
|
||||||
type2label = new Map();
|
type2label = new Map();
|
||||||
for (let i = 0; i < CHANNEL_OPTIONS.length; i++) {
|
for (let i = 0; i < CHANNEL_OPTIONS.length; i++) {
|
||||||
type2label[CHANNEL_OPTIONS[i].value] = CHANNEL_OPTIONS[i];
|
type2label[CHANNEL_OPTIONS[i].value] = CHANNEL_OPTIONS[i];
|
||||||
}
|
}
|
||||||
type2label[0] = { value: 0, text: '未知类型', color: 'grey' };
|
type2label[0] = {
|
||||||
|
value: 0,
|
||||||
|
text: t('channel.table.status_unknown'),
|
||||||
|
color: 'grey',
|
||||||
|
};
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<Label basic color={type2label[type]?.color}>
|
<Label basic color={type2label[type]?.color}>
|
||||||
@ -46,7 +51,7 @@ function renderType(type) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderBalance(type, balance) {
|
function renderBalance(type, balance, t) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 1: // OpenAI
|
case 1: // OpenAI
|
||||||
return <span>${balance.toFixed(2)}</span>;
|
return <span>${balance.toFixed(2)}</span>;
|
||||||
@ -67,7 +72,7 @@ function renderBalance(type, balance) {
|
|||||||
case 44: // SiliconFlow
|
case 44: // SiliconFlow
|
||||||
return <span>¥{balance.toFixed(2)}</span>;
|
return <span>¥{balance.toFixed(2)}</span>;
|
||||||
default:
|
default:
|
||||||
return <span>不支持</span>;
|
return <span>{t('channel.table.balance_not_supported')}</span>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,6 +83,7 @@ function isShowDetail() {
|
|||||||
const promptID = 'detail';
|
const promptID = 'detail';
|
||||||
|
|
||||||
const ChannelsTable = () => {
|
const ChannelsTable = () => {
|
||||||
|
const { t } = useTranslation();
|
||||||
const [channels, setChannels] = useState([]);
|
const [channels, setChannels] = useState([]);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [activePage, setActivePage] = useState(1);
|
const [activePage, setActivePage] = useState(1);
|
||||||
@ -207,12 +213,12 @@ const ChannelsTable = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderStatus = (status) => {
|
const renderStatus = (status, t) => {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case 1:
|
case 1:
|
||||||
return (
|
return (
|
||||||
<Label basic color='green'>
|
<Label basic color='green'>
|
||||||
已启用
|
{t('channel.table.status_enabled')}
|
||||||
</Label>
|
</Label>
|
||||||
);
|
);
|
||||||
case 2:
|
case 2:
|
||||||
@ -220,10 +226,10 @@ const ChannelsTable = () => {
|
|||||||
<Popup
|
<Popup
|
||||||
trigger={
|
trigger={
|
||||||
<Label basic color='red'>
|
<Label basic color='red'>
|
||||||
已禁用
|
{t('channel.table.status_disabled')}
|
||||||
</Label>
|
</Label>
|
||||||
}
|
}
|
||||||
content='本渠道被手动禁用'
|
content={t('channel.table.status_disabled_tip')}
|
||||||
basic
|
basic
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@ -232,29 +238,29 @@ const ChannelsTable = () => {
|
|||||||
<Popup
|
<Popup
|
||||||
trigger={
|
trigger={
|
||||||
<Label basic color='yellow'>
|
<Label basic color='yellow'>
|
||||||
已禁用
|
{t('channel.table.status_auto_disabled')}
|
||||||
</Label>
|
</Label>
|
||||||
}
|
}
|
||||||
content='本渠道被程序自动禁用'
|
content={t('channel.table.status_auto_disabled_tip')}
|
||||||
basic
|
basic
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
default:
|
default:
|
||||||
return (
|
return (
|
||||||
<Label basic color='grey'>
|
<Label basic color='grey'>
|
||||||
未知状态
|
{t('channel.table.status_unknown')}
|
||||||
</Label>
|
</Label>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderResponseTime = (responseTime) => {
|
const renderResponseTime = (responseTime, t) => {
|
||||||
let time = responseTime / 1000;
|
let time = responseTime / 1000;
|
||||||
time = time.toFixed(2) + ' 秒';
|
time = time.toFixed(2) + 's';
|
||||||
if (responseTime === 0) {
|
if (responseTime === 0) {
|
||||||
return (
|
return (
|
||||||
<Label basic color='grey'>
|
<Label basic color='grey'>
|
||||||
未测试
|
{t('channel.table.not_tested')}
|
||||||
</Label>
|
</Label>
|
||||||
);
|
);
|
||||||
} else if (responseTime <= 1000) {
|
} else if (responseTime <= 1000) {
|
||||||
@ -320,9 +326,12 @@ const ChannelsTable = () => {
|
|||||||
newChannels[realIdx].test_time = Date.now() / 1000;
|
newChannels[realIdx].test_time = Date.now() / 1000;
|
||||||
setChannels(newChannels);
|
setChannels(newChannels);
|
||||||
showInfo(
|
showInfo(
|
||||||
`渠道 ${name} 测试成功,模型 ${model},耗时 ${time.toFixed(
|
t('channel.messages.test_success', {
|
||||||
2
|
name: name,
|
||||||
)} 秒,模型输出:${message}`
|
model: model,
|
||||||
|
time: time.toFixed(2),
|
||||||
|
message: message,
|
||||||
|
})
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
showError(message);
|
showError(message);
|
||||||
@ -338,7 +347,7 @@ const ChannelsTable = () => {
|
|||||||
const res = await API.get(`/api/channel/test?scope=${scope}`);
|
const res = await API.get(`/api/channel/test?scope=${scope}`);
|
||||||
const { success, message } = res.data;
|
const { success, message } = res.data;
|
||||||
if (success) {
|
if (success) {
|
||||||
showInfo('已成功开始测试渠道,请刷新页面查看结果。');
|
showInfo(t('channel.messages.test_all_started'));
|
||||||
} else {
|
} else {
|
||||||
showError(message);
|
showError(message);
|
||||||
}
|
}
|
||||||
@ -348,7 +357,9 @@ const ChannelsTable = () => {
|
|||||||
const res = await API.delete(`/api/channel/disabled`);
|
const res = await API.delete(`/api/channel/disabled`);
|
||||||
const { success, message, data } = res.data;
|
const { success, message, data } = res.data;
|
||||||
if (success) {
|
if (success) {
|
||||||
showSuccess(`已删除所有禁用渠道,共计 ${data} 个`);
|
showSuccess(
|
||||||
|
t('channel.messages.delete_disabled_success', { count: data })
|
||||||
|
);
|
||||||
await refresh();
|
await refresh();
|
||||||
} else {
|
} else {
|
||||||
showError(message);
|
showError(message);
|
||||||
@ -364,7 +375,7 @@ const ChannelsTable = () => {
|
|||||||
newChannels[realIdx].balance = balance;
|
newChannels[realIdx].balance = balance;
|
||||||
newChannels[realIdx].balance_updated_time = Date.now() / 1000;
|
newChannels[realIdx].balance_updated_time = Date.now() / 1000;
|
||||||
setChannels(newChannels);
|
setChannels(newChannels);
|
||||||
showInfo(`渠道 ${name} 余额更新成功!`);
|
showInfo(t('channel.messages.balance_update_success', { name: name }));
|
||||||
} else {
|
} else {
|
||||||
showError(message);
|
showError(message);
|
||||||
}
|
}
|
||||||
@ -375,7 +386,7 @@ const ChannelsTable = () => {
|
|||||||
const res = await API.get(`/api/channel/update_balance`);
|
const res = await API.get(`/api/channel/update_balance`);
|
||||||
const { success, message } = res.data;
|
const { success, message } = res.data;
|
||||||
if (success) {
|
if (success) {
|
||||||
showInfo('已更新完毕所有已启用渠道余额!');
|
showInfo(t('channel.messages.all_balance_updated'));
|
||||||
} else {
|
} else {
|
||||||
showError(message);
|
showError(message);
|
||||||
}
|
}
|
||||||
@ -413,7 +424,7 @@ const ChannelsTable = () => {
|
|||||||
icon='search'
|
icon='search'
|
||||||
fluid
|
fluid
|
||||||
iconPosition='left'
|
iconPosition='left'
|
||||||
placeholder='搜索渠道的 ID,名称和密钥 ...'
|
placeholder={t('channel.search')}
|
||||||
value={searchKeyword}
|
value={searchKeyword}
|
||||||
loading={searching}
|
loading={searching}
|
||||||
onChange={handleKeywordChange}
|
onChange={handleKeywordChange}
|
||||||
@ -426,13 +437,11 @@ const ChannelsTable = () => {
|
|||||||
setPromptShown(promptID);
|
setPromptShown(promptID);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
OpenAI 渠道已经不再支持通过 key 获取余额,因此余额显示为
|
{t('channel.balance_notice')}
|
||||||
0。对于支持的渠道类型,请点击余额进行刷新。
|
|
||||||
<br />
|
<br />
|
||||||
渠道测试仅支持 chat 模型,优先使用
|
{t('channel.test_notice')}
|
||||||
gpt-3.5-turbo,如果该模型不可用则使用你所配置的模型列表中的第一个模型。
|
|
||||||
<br />
|
<br />
|
||||||
点击下方详情按钮可以显示余额以及设置额外的测试模型。
|
{t('channel.detail_notice')}
|
||||||
</Message>
|
</Message>
|
||||||
)}
|
)}
|
||||||
<Table basic={'very'} compact size='small'>
|
<Table basic={'very'} compact size='small'>
|
||||||
@ -444,7 +453,7 @@ const ChannelsTable = () => {
|
|||||||
sortChannel('id');
|
sortChannel('id');
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
ID
|
{t('channel.table.id')}
|
||||||
</Table.HeaderCell>
|
</Table.HeaderCell>
|
||||||
<Table.HeaderCell
|
<Table.HeaderCell
|
||||||
style={{ cursor: 'pointer' }}
|
style={{ cursor: 'pointer' }}
|
||||||
@ -452,7 +461,7 @@ const ChannelsTable = () => {
|
|||||||
sortChannel('name');
|
sortChannel('name');
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
名称
|
{t('channel.table.name')}
|
||||||
</Table.HeaderCell>
|
</Table.HeaderCell>
|
||||||
<Table.HeaderCell
|
<Table.HeaderCell
|
||||||
style={{ cursor: 'pointer' }}
|
style={{ cursor: 'pointer' }}
|
||||||
@ -460,7 +469,7 @@ const ChannelsTable = () => {
|
|||||||
sortChannel('group');
|
sortChannel('group');
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
分组
|
{t('channel.table.group')}
|
||||||
</Table.HeaderCell>
|
</Table.HeaderCell>
|
||||||
<Table.HeaderCell
|
<Table.HeaderCell
|
||||||
style={{ cursor: 'pointer' }}
|
style={{ cursor: 'pointer' }}
|
||||||
@ -468,7 +477,7 @@ const ChannelsTable = () => {
|
|||||||
sortChannel('type');
|
sortChannel('type');
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
类型
|
{t('channel.table.type')}
|
||||||
</Table.HeaderCell>
|
</Table.HeaderCell>
|
||||||
<Table.HeaderCell
|
<Table.HeaderCell
|
||||||
style={{ cursor: 'pointer' }}
|
style={{ cursor: 'pointer' }}
|
||||||
@ -476,7 +485,7 @@ const ChannelsTable = () => {
|
|||||||
sortChannel('status');
|
sortChannel('status');
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
状态
|
{t('channel.table.status')}
|
||||||
</Table.HeaderCell>
|
</Table.HeaderCell>
|
||||||
<Table.HeaderCell
|
<Table.HeaderCell
|
||||||
style={{ cursor: 'pointer' }}
|
style={{ cursor: 'pointer' }}
|
||||||
@ -484,7 +493,7 @@ const ChannelsTable = () => {
|
|||||||
sortChannel('response_time');
|
sortChannel('response_time');
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
响应时间
|
{t('channel.table.response_time')}
|
||||||
</Table.HeaderCell>
|
</Table.HeaderCell>
|
||||||
<Table.HeaderCell
|
<Table.HeaderCell
|
||||||
style={{ cursor: 'pointer' }}
|
style={{ cursor: 'pointer' }}
|
||||||
@ -493,7 +502,7 @@ const ChannelsTable = () => {
|
|||||||
}}
|
}}
|
||||||
hidden={!showDetail}
|
hidden={!showDetail}
|
||||||
>
|
>
|
||||||
余额
|
{t('channel.table.balance')}
|
||||||
</Table.HeaderCell>
|
</Table.HeaderCell>
|
||||||
<Table.HeaderCell
|
<Table.HeaderCell
|
||||||
style={{ cursor: 'pointer' }}
|
style={{ cursor: 'pointer' }}
|
||||||
@ -501,10 +510,12 @@ const ChannelsTable = () => {
|
|||||||
sortChannel('priority');
|
sortChannel('priority');
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
优先级
|
{t('channel.table.priority')}
|
||||||
</Table.HeaderCell>
|
</Table.HeaderCell>
|
||||||
<Table.HeaderCell hidden={!showDetail}>测试模型</Table.HeaderCell>
|
<Table.HeaderCell hidden={!showDetail}>
|
||||||
<Table.HeaderCell>操作</Table.HeaderCell>
|
{t('channel.table.test_model')}
|
||||||
|
</Table.HeaderCell>
|
||||||
|
<Table.HeaderCell>{t('channel.table.actions')}</Table.HeaderCell>
|
||||||
</Table.Row>
|
</Table.Row>
|
||||||
</Table.Header>
|
</Table.Header>
|
||||||
|
|
||||||
@ -519,19 +530,21 @@ const ChannelsTable = () => {
|
|||||||
return (
|
return (
|
||||||
<Table.Row key={channel.id}>
|
<Table.Row key={channel.id}>
|
||||||
<Table.Cell>{channel.id}</Table.Cell>
|
<Table.Cell>{channel.id}</Table.Cell>
|
||||||
<Table.Cell>{channel.name ? channel.name : '无'}</Table.Cell>
|
<Table.Cell>
|
||||||
|
{channel.name ? channel.name : t('channel.table.no_name')}
|
||||||
|
</Table.Cell>
|
||||||
<Table.Cell>{renderGroup(channel.group)}</Table.Cell>
|
<Table.Cell>{renderGroup(channel.group)}</Table.Cell>
|
||||||
<Table.Cell>{renderType(channel.type)}</Table.Cell>
|
<Table.Cell>{renderType(channel.type, t)}</Table.Cell>
|
||||||
<Table.Cell>{renderStatus(channel.status)}</Table.Cell>
|
<Table.Cell>{renderStatus(channel.status, t)}</Table.Cell>
|
||||||
<Table.Cell>
|
<Table.Cell>
|
||||||
<Popup
|
<Popup
|
||||||
content={
|
content={
|
||||||
channel.test_time
|
channel.test_time
|
||||||
? renderTimestamp(channel.test_time)
|
? renderTimestamp(channel.test_time)
|
||||||
: '未测试'
|
: t('channel.table.not_tested')
|
||||||
}
|
}
|
||||||
key={channel.id}
|
key={channel.id}
|
||||||
trigger={renderResponseTime(channel.response_time)}
|
trigger={renderResponseTime(channel.response_time, t)}
|
||||||
basic
|
basic
|
||||||
/>
|
/>
|
||||||
</Table.Cell>
|
</Table.Cell>
|
||||||
@ -544,10 +557,10 @@ const ChannelsTable = () => {
|
|||||||
}}
|
}}
|
||||||
style={{ cursor: 'pointer' }}
|
style={{ cursor: 'pointer' }}
|
||||||
>
|
>
|
||||||
{renderBalance(channel.type, channel.balance)}
|
{renderBalance(channel.type, channel.balance, t)}
|
||||||
</span>
|
</span>
|
||||||
}
|
}
|
||||||
content='点击更新'
|
content={t('channel.table.click_to_update')}
|
||||||
basic
|
basic
|
||||||
/>
|
/>
|
||||||
</Table.Cell>
|
</Table.Cell>
|
||||||
@ -569,13 +582,13 @@ const ChannelsTable = () => {
|
|||||||
<input style={{ maxWidth: '60px' }} />
|
<input style={{ maxWidth: '60px' }} />
|
||||||
</Input>
|
</Input>
|
||||||
}
|
}
|
||||||
content='渠道选择优先级,越高越优先'
|
content={t('channel.table.priority_tip')}
|
||||||
basic
|
basic
|
||||||
/>
|
/>
|
||||||
</Table.Cell>
|
</Table.Cell>
|
||||||
<Table.Cell hidden={!showDetail}>
|
<Table.Cell hidden={!showDetail}>
|
||||||
<Dropdown
|
<Dropdown
|
||||||
placeholder='请选择测试模型'
|
placeholder={t('channel.table.select_test_model')}
|
||||||
selection
|
selection
|
||||||
options={channel.model_options}
|
options={channel.model_options}
|
||||||
defaultValue={channel.test_model}
|
defaultValue={channel.test_model}
|
||||||
@ -598,22 +611,12 @@ const ChannelsTable = () => {
|
|||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
测试
|
{t('channel.buttons.test')}
|
||||||
</Button>
|
</Button>
|
||||||
{/*<Button*/}
|
|
||||||
{/* size={'small'}*/}
|
|
||||||
{/* positive*/}
|
|
||||||
{/* loading={updatingBalance}*/}
|
|
||||||
{/* onClick={() => {*/}
|
|
||||||
{/* updateChannelBalance(channel.id, channel.name, idx);*/}
|
|
||||||
{/* }}*/}
|
|
||||||
{/*>*/}
|
|
||||||
{/* 更新余额*/}
|
|
||||||
{/*</Button>*/}
|
|
||||||
<Popup
|
<Popup
|
||||||
trigger={
|
trigger={
|
||||||
<Button size='small' negative>
|
<Button size='small' negative>
|
||||||
删除
|
{t('channel.buttons.delete')}
|
||||||
</Button>
|
</Button>
|
||||||
}
|
}
|
||||||
on='click'
|
on='click'
|
||||||
@ -626,7 +629,7 @@ const ChannelsTable = () => {
|
|||||||
manageChannel(channel.id, 'delete', idx);
|
manageChannel(channel.id, 'delete', idx);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
删除渠道 {channel.name}
|
{t('channel.buttons.confirm_delete')} {channel.name}
|
||||||
</Button>
|
</Button>
|
||||||
</Popup>
|
</Popup>
|
||||||
<Button
|
<Button
|
||||||
@ -639,14 +642,16 @@ const ChannelsTable = () => {
|
|||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{channel.status === 1 ? '禁用' : '启用'}
|
{channel.status === 1
|
||||||
|
? t('channel.buttons.disable')
|
||||||
|
: t('channel.buttons.enable')}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
size={'small'}
|
size={'small'}
|
||||||
as={Link}
|
as={Link}
|
||||||
to={'/channel/edit/' + channel.id}
|
to={'/channel/edit/' + channel.id}
|
||||||
>
|
>
|
||||||
编辑
|
{t('channel.buttons.edit')}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</Table.Cell>
|
</Table.Cell>
|
||||||
@ -664,7 +669,7 @@ const ChannelsTable = () => {
|
|||||||
to='/channel/add'
|
to='/channel/add'
|
||||||
loading={loading}
|
loading={loading}
|
||||||
>
|
>
|
||||||
添加新的渠道
|
{t('channel.buttons.add')}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
size='small'
|
size='small'
|
||||||
@ -673,7 +678,7 @@ const ChannelsTable = () => {
|
|||||||
testChannels('all');
|
testChannels('all');
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
测试所有渠道
|
{t('channel.buttons.test_all')}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
size='small'
|
size='small'
|
||||||
@ -682,14 +687,12 @@ const ChannelsTable = () => {
|
|||||||
testChannels('disabled');
|
testChannels('disabled');
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
测试禁用渠道
|
{t('channel.buttons.test_disabled')}
|
||||||
</Button>
|
</Button>
|
||||||
{/*<Button size='small' onClick={updateAllChannelsBalance}*/}
|
|
||||||
{/* loading={loading || updatingBalance}>更新已启用渠道余额</Button>*/}
|
|
||||||
<Popup
|
<Popup
|
||||||
trigger={
|
trigger={
|
||||||
<Button size='small' loading={loading}>
|
<Button size='small' loading={loading}>
|
||||||
删除禁用渠道
|
{t('channel.buttons.delete_disabled')}
|
||||||
</Button>
|
</Button>
|
||||||
}
|
}
|
||||||
on='click'
|
on='click'
|
||||||
@ -702,7 +705,7 @@ const ChannelsTable = () => {
|
|||||||
negative
|
negative
|
||||||
onClick={deleteAllDisabledChannels}
|
onClick={deleteAllDisabledChannels}
|
||||||
>
|
>
|
||||||
确认删除
|
{t('channel.buttons.confirm_delete_disabled')}
|
||||||
</Button>
|
</Button>
|
||||||
</Popup>
|
</Popup>
|
||||||
<Pagination
|
<Pagination
|
||||||
@ -717,10 +720,12 @@ const ChannelsTable = () => {
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<Button size='small' onClick={refresh} loading={loading}>
|
<Button size='small' onClick={refresh} loading={loading}>
|
||||||
刷新
|
{t('channel.buttons.refresh')}
|
||||||
</Button>
|
</Button>
|
||||||
<Button size='small' onClick={toggleShowDetail}>
|
<Button size='small' onClick={toggleShowDetail}>
|
||||||
{showDetail ? '隐藏详情' : '详情'}
|
{showDetail
|
||||||
|
? t('channel.buttons.hide_detail')
|
||||||
|
: t('channel.buttons.show_detail')}
|
||||||
</Button>
|
</Button>
|
||||||
</Table.HeaderCell>
|
</Table.HeaderCell>
|
||||||
</Table.Row>
|
</Table.Row>
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
Form,
|
Form,
|
||||||
@ -26,23 +27,23 @@ const MODEL_MAPPING_EXAMPLE = {
|
|||||||
'gpt-4-32k-0314': 'gpt-4-32k',
|
'gpt-4-32k-0314': 'gpt-4-32k',
|
||||||
};
|
};
|
||||||
|
|
||||||
function type2secretPrompt(type) {
|
function type2secretPrompt(type, t) {
|
||||||
// inputs.type === 15 ? '按照如下格式输入:APIKey|SecretKey' : (inputs.type === 18 ? '按照如下格式输入:APPID|APISecret|APIKey' : '请输入渠道对应的鉴权密钥')
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 15:
|
case 15:
|
||||||
return '按照如下格式输入:APIKey|SecretKey';
|
return t('channel.edit.key_prompts.zhipu');
|
||||||
case 18:
|
case 18:
|
||||||
return '按照如下格式输入:APPID|APISecret|APIKey';
|
return t('channel.edit.key_prompts.spark');
|
||||||
case 22:
|
case 22:
|
||||||
return '按照如下格式输入:APIKey-AppId,例如:fastgpt-0sp2gtvfdgyi4k30jwlgwf1i-64f335d84283f05518e9e041';
|
return t('channel.edit.key_prompts.fastgpt');
|
||||||
case 23:
|
case 23:
|
||||||
return '按照如下格式输入:AppId|SecretId|SecretKey';
|
return t('channel.edit.key_prompts.tencent');
|
||||||
default:
|
default:
|
||||||
return '请输入渠道对应的鉴权密钥';
|
return t('channel.edit.key_prompts.default');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const EditChannel = () => {
|
const EditChannel = () => {
|
||||||
|
const { t } = useTranslation();
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const channelId = params.id;
|
const channelId = params.id;
|
||||||
@ -194,15 +195,15 @@ const EditChannel = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!isEdit && (inputs.name === '' || inputs.key === '')) {
|
if (!isEdit && (inputs.name === '' || inputs.key === '')) {
|
||||||
showInfo('请填写渠道名称和渠道密钥!');
|
showInfo(t('channel.edit.messages.name_required'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (inputs.type !== 43 && inputs.models.length === 0) {
|
if (inputs.type !== 43 && inputs.models.length === 0) {
|
||||||
showInfo('请至少选择一个模型!');
|
showInfo(t('channel.edit.messages.models_required'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (inputs.model_mapping !== '' && !verifyJSON(inputs.model_mapping)) {
|
if (inputs.model_mapping !== '' && !verifyJSON(inputs.model_mapping)) {
|
||||||
showInfo('模型映射必须是合法的 JSON 格式!');
|
showInfo(t('channel.edit.messages.model_mapping_invalid'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let localInputs = { ...inputs };
|
let localInputs = { ...inputs };
|
||||||
@ -230,9 +231,9 @@ const EditChannel = () => {
|
|||||||
const { success, message } = res.data;
|
const { success, message } = res.data;
|
||||||
if (success) {
|
if (success) {
|
||||||
if (isEdit) {
|
if (isEdit) {
|
||||||
showSuccess('渠道更新成功!');
|
showSuccess(t('channel.edit.messages.update_success'));
|
||||||
} else {
|
} else {
|
||||||
showSuccess('渠道创建成功!');
|
showSuccess(t('channel.edit.messages.create_success'));
|
||||||
setInputs(originInputs);
|
setInputs(originInputs);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -263,12 +264,14 @@ const EditChannel = () => {
|
|||||||
<Card fluid className='chart-card'>
|
<Card fluid className='chart-card'>
|
||||||
<Card.Content>
|
<Card.Content>
|
||||||
<Card.Header className='header'>
|
<Card.Header className='header'>
|
||||||
{isEdit ? '更新渠道信息' : '创建新的渠道'}
|
{isEdit
|
||||||
|
? t('channel.edit.title_edit')
|
||||||
|
: t('channel.edit.title_create')}
|
||||||
</Card.Header>
|
</Card.Header>
|
||||||
<Form loading={loading} autoComplete='new-password'>
|
<Form loading={loading} autoComplete='new-password'>
|
||||||
<Form.Field>
|
<Form.Field>
|
||||||
<Form.Select
|
<Form.Select
|
||||||
label='类型'
|
label={t('channel.edit.type')}
|
||||||
name='type'
|
name='type'
|
||||||
required
|
required
|
||||||
search
|
search
|
||||||
@ -277,6 +280,35 @@ const EditChannel = () => {
|
|||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
/>
|
/>
|
||||||
</Form.Field>
|
</Form.Field>
|
||||||
|
<Form.Field>
|
||||||
|
<Form.Input
|
||||||
|
label={t('channel.edit.name')}
|
||||||
|
name='name'
|
||||||
|
placeholder={t('channel.edit.name_placeholder')}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
value={inputs.name}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</Form.Field>
|
||||||
|
<Form.Field>
|
||||||
|
<Form.Dropdown
|
||||||
|
label={t('channel.edit.group')}
|
||||||
|
placeholder={t('channel.edit.group_placeholder')}
|
||||||
|
name='groups'
|
||||||
|
required
|
||||||
|
fluid
|
||||||
|
multiple
|
||||||
|
selection
|
||||||
|
allowAdditions
|
||||||
|
additionLabel={t('channel.edit.group_addition')}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
value={inputs.groups}
|
||||||
|
autoComplete='new-password'
|
||||||
|
options={groupOptions}
|
||||||
|
/>
|
||||||
|
</Form.Field>
|
||||||
|
|
||||||
|
{/* Azure OpenAI specific fields */}
|
||||||
{inputs.type === 3 && (
|
{inputs.type === 3 && (
|
||||||
<>
|
<>
|
||||||
<Message>
|
<Message>
|
||||||
@ -295,9 +327,7 @@ const EditChannel = () => {
|
|||||||
<Form.Input
|
<Form.Input
|
||||||
label='AZURE_OPENAI_ENDPOINT'
|
label='AZURE_OPENAI_ENDPOINT'
|
||||||
name='base_url'
|
name='base_url'
|
||||||
placeholder={
|
placeholder='请输入 AZURE_OPENAI_ENDPOINT,例如:https://docs-test-001.openai.azure.com'
|
||||||
'请输入 AZURE_OPENAI_ENDPOINT,例如:https://docs-test-001.openai.azure.com'
|
|
||||||
}
|
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
value={inputs.base_url}
|
value={inputs.base_url}
|
||||||
autoComplete='new-password'
|
autoComplete='new-password'
|
||||||
@ -307,9 +337,7 @@ const EditChannel = () => {
|
|||||||
<Form.Input
|
<Form.Input
|
||||||
label='默认 API 版本'
|
label='默认 API 版本'
|
||||||
name='other'
|
name='other'
|
||||||
placeholder={
|
placeholder='请输入默认 API 版本,例如:2024-03-01-preview,该配置可以被实际的请求查询参数所覆盖'
|
||||||
'请输入默认 API 版本,例如:2024-03-01-preview,该配置可以被实际的请求查询参数所覆盖'
|
|
||||||
}
|
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
value={inputs.other}
|
value={inputs.other}
|
||||||
autoComplete='new-password'
|
autoComplete='new-password'
|
||||||
@ -317,55 +345,27 @@ const EditChannel = () => {
|
|||||||
</Form.Field>
|
</Form.Field>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* Custom base URL field */}
|
||||||
{inputs.type === 8 && (
|
{inputs.type === 8 && (
|
||||||
<Form.Field>
|
<Form.Field>
|
||||||
<Form.Input
|
<Form.Input
|
||||||
label='Base URL'
|
label={t('channel.edit.base_url')}
|
||||||
name='base_url'
|
name='base_url'
|
||||||
placeholder={
|
placeholder={t('channel.edit.base_url_placeholder')}
|
||||||
'请输入自定义渠道的 Base URL,例如:https://openai.justsong.cn'
|
|
||||||
}
|
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
value={inputs.base_url}
|
value={inputs.base_url}
|
||||||
autoComplete='new-password'
|
autoComplete='new-password'
|
||||||
/>
|
/>
|
||||||
</Form.Field>
|
</Form.Field>
|
||||||
)}
|
)}
|
||||||
<Form.Field>
|
|
||||||
<Form.Input
|
|
||||||
label='名称'
|
|
||||||
name='name'
|
|
||||||
placeholder={'请输入名称'}
|
|
||||||
onChange={handleInputChange}
|
|
||||||
value={inputs.name}
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
</Form.Field>
|
|
||||||
<Form.Field>
|
|
||||||
<Form.Dropdown
|
|
||||||
label='分组'
|
|
||||||
placeholder={'请选择可以使用该渠道的分组'}
|
|
||||||
name='groups'
|
|
||||||
required
|
|
||||||
fluid
|
|
||||||
multiple
|
|
||||||
selection
|
|
||||||
allowAdditions
|
|
||||||
additionLabel={'请在系统设置页面编辑分组倍率以添加新的分组:'}
|
|
||||||
onChange={handleInputChange}
|
|
||||||
value={inputs.groups}
|
|
||||||
autoComplete='new-password'
|
|
||||||
options={groupOptions}
|
|
||||||
/>
|
|
||||||
</Form.Field>
|
|
||||||
{inputs.type === 18 && (
|
{inputs.type === 18 && (
|
||||||
<Form.Field>
|
<Form.Field>
|
||||||
<Form.Input
|
<Form.Input
|
||||||
label='模型版本'
|
label={t('channel.edit.spark_version')}
|
||||||
name='other'
|
name='other'
|
||||||
placeholder={
|
placeholder={t('channel.edit.spark_version_placeholder')}
|
||||||
'请输入星火大模型版本,注意是接口地址中的版本号,例如:v2.1'
|
|
||||||
}
|
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
value={inputs.other}
|
value={inputs.other}
|
||||||
autoComplete='new-password'
|
autoComplete='new-password'
|
||||||
@ -375,9 +375,9 @@ const EditChannel = () => {
|
|||||||
{inputs.type === 21 && (
|
{inputs.type === 21 && (
|
||||||
<Form.Field>
|
<Form.Field>
|
||||||
<Form.Input
|
<Form.Input
|
||||||
label='知识库 ID'
|
label={t('channel.edit.knowledge_id')}
|
||||||
name='other'
|
name='other'
|
||||||
placeholder={'请输入知识库 ID,例如:123456'}
|
placeholder={t('channel.edit.knowledge_id_placeholder')}
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
value={inputs.other}
|
value={inputs.other}
|
||||||
autoComplete='new-password'
|
autoComplete='new-password'
|
||||||
@ -387,11 +387,9 @@ const EditChannel = () => {
|
|||||||
{inputs.type === 17 && (
|
{inputs.type === 17 && (
|
||||||
<Form.Field>
|
<Form.Field>
|
||||||
<Form.Input
|
<Form.Input
|
||||||
label='插件参数'
|
label={t('channel.edit.plugin_param')}
|
||||||
name='other'
|
name='other'
|
||||||
placeholder={
|
placeholder={t('channel.edit.plugin_param_placeholder')}
|
||||||
'请输入插件参数,即 X-DashScope-Plugin 请求头的取值'
|
|
||||||
}
|
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
value={inputs.other}
|
value={inputs.other}
|
||||||
autoComplete='new-password'
|
autoComplete='new-password'
|
||||||
@ -399,28 +397,25 @@ const EditChannel = () => {
|
|||||||
</Form.Field>
|
</Form.Field>
|
||||||
)}
|
)}
|
||||||
{inputs.type === 34 && (
|
{inputs.type === 34 && (
|
||||||
<Message>
|
<Message>{t('channel.edit.coze_notice')}</Message>
|
||||||
对于 Coze 而言,模型名称即 Bot ID,你可以添加一个前缀
|
|
||||||
`bot-`,例如:`bot-123456`。
|
|
||||||
</Message>
|
|
||||||
)}
|
)}
|
||||||
{inputs.type === 40 && (
|
{inputs.type === 40 && (
|
||||||
<Message>
|
<Message>
|
||||||
对于豆包而言,需要手动去{' '}
|
{t('channel.edit.douban_notice')}
|
||||||
<a
|
<a
|
||||||
target='_blank'
|
target='_blank'
|
||||||
href='https://console.volcengine.com/ark/region:ark+cn-beijing/endpoint'
|
href='https://console.volcengine.com/ark/region:ark+cn-beijing/endpoint'
|
||||||
>
|
>
|
||||||
模型推理页面
|
{t('channel.edit.douban_notice_link')}
|
||||||
</a>{' '}
|
</a>
|
||||||
创建推理接入点,以接入点名称作为模型名称,例如:`ep-20240608051426-tkxvl`。
|
{t('channel.edit.douban_notice_2')}
|
||||||
</Message>
|
</Message>
|
||||||
)}
|
)}
|
||||||
{inputs.type !== 43 && (
|
{inputs.type !== 43 && (
|
||||||
<Form.Field>
|
<Form.Field>
|
||||||
<Form.Dropdown
|
<Form.Dropdown
|
||||||
label='模型'
|
label={t('channel.edit.models')}
|
||||||
placeholder={'请选择该渠道所支持的模型'}
|
placeholder={t('channel.edit.models_placeholder')}
|
||||||
name='models'
|
name='models'
|
||||||
required
|
required
|
||||||
fluid
|
fluid
|
||||||
@ -448,7 +443,7 @@ const EditChannel = () => {
|
|||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
填入相关模型
|
{t('channel.edit.buttons.fill_models')}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
type={'button'}
|
type={'button'}
|
||||||
@ -459,7 +454,7 @@ const EditChannel = () => {
|
|||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
填入所有模型
|
{t('channel.edit.buttons.fill_all')}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
type={'button'}
|
type={'button'}
|
||||||
@ -467,15 +462,15 @@ const EditChannel = () => {
|
|||||||
handleInputChange(null, { name: 'models', value: [] });
|
handleInputChange(null, { name: 'models', value: [] });
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
清除所有模型
|
{t('channel.edit.buttons.clear')}
|
||||||
</Button>
|
</Button>
|
||||||
<Input
|
<Input
|
||||||
action={
|
action={
|
||||||
<Button type={'button'} onClick={addCustomModel}>
|
<Button type={'button'} onClick={addCustomModel}>
|
||||||
填入
|
{t('channel.edit.buttons.add_custom')}
|
||||||
</Button>
|
</Button>
|
||||||
}
|
}
|
||||||
placeholder='输入自定义模型名称'
|
placeholder={t('channel.edit.buttons.custom_placeholder')}
|
||||||
value={customModel}
|
value={customModel}
|
||||||
onChange={(e, { value }) => {
|
onChange={(e, { value }) => {
|
||||||
setCustomModel(value);
|
setCustomModel(value);
|
||||||
@ -493,12 +488,10 @@ const EditChannel = () => {
|
|||||||
<>
|
<>
|
||||||
<Form.Field>
|
<Form.Field>
|
||||||
<Form.TextArea
|
<Form.TextArea
|
||||||
label='模型重定向'
|
label={t('channel.edit.model_mapping')}
|
||||||
placeholder={`此项可选,用于修改请求体中的模型名称,为一个 JSON 字符串,键为请求中模型名称,值为要替换的模型名称,例如:\n${JSON.stringify(
|
placeholder={`${t(
|
||||||
MODEL_MAPPING_EXAMPLE,
|
'channel.edit.model_mapping_placeholder'
|
||||||
null,
|
)}\n${JSON.stringify(MODEL_MAPPING_EXAMPLE, null, 2)}`}
|
||||||
2
|
|
||||||
)}`}
|
|
||||||
name='model_mapping'
|
name='model_mapping'
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
value={inputs.model_mapping}
|
value={inputs.model_mapping}
|
||||||
@ -511,8 +504,8 @@ const EditChannel = () => {
|
|||||||
</Form.Field>
|
</Form.Field>
|
||||||
<Form.Field>
|
<Form.Field>
|
||||||
<Form.TextArea
|
<Form.TextArea
|
||||||
label='系统提示词'
|
label={t('channel.edit.system_prompt')}
|
||||||
placeholder={`此项可选,用于强制设置给定的系统提示词,请配合自定义模型 & 模型重定向使用,首先创建一个唯一的自定义模型名称并在上面填入,之后将该自定义模型重定向映射到该渠道一个原生支持的模型`}
|
placeholder={t('channel.edit.system_prompt_placeholder')}
|
||||||
name='system_prompt'
|
name='system_prompt'
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
value={inputs.system_prompt}
|
value={inputs.system_prompt}
|
||||||
@ -531,7 +524,7 @@ const EditChannel = () => {
|
|||||||
label='Region'
|
label='Region'
|
||||||
name='region'
|
name='region'
|
||||||
required
|
required
|
||||||
placeholder={'region,e.g. us-west-2'}
|
placeholder={t('channel.edit.aws_region_placeholder')}
|
||||||
onChange={handleConfigChange}
|
onChange={handleConfigChange}
|
||||||
value={config.region}
|
value={config.region}
|
||||||
autoComplete=''
|
autoComplete=''
|
||||||
@ -540,7 +533,7 @@ const EditChannel = () => {
|
|||||||
label='AK'
|
label='AK'
|
||||||
name='ak'
|
name='ak'
|
||||||
required
|
required
|
||||||
placeholder={'AWS IAM Access Key'}
|
placeholder={t('channel.edit.aws_ak_placeholder')}
|
||||||
onChange={handleConfigChange}
|
onChange={handleConfigChange}
|
||||||
value={config.ak}
|
value={config.ak}
|
||||||
autoComplete=''
|
autoComplete=''
|
||||||
@ -549,7 +542,7 @@ const EditChannel = () => {
|
|||||||
label='SK'
|
label='SK'
|
||||||
name='sk'
|
name='sk'
|
||||||
required
|
required
|
||||||
placeholder={'AWS IAM Secret Key'}
|
placeholder={t('channel.edit.aws_sk_placeholder')}
|
||||||
onChange={handleConfigChange}
|
onChange={handleConfigChange}
|
||||||
value={config.sk}
|
value={config.sk}
|
||||||
autoComplete=''
|
autoComplete=''
|
||||||
@ -562,27 +555,25 @@ const EditChannel = () => {
|
|||||||
label='Region'
|
label='Region'
|
||||||
name='region'
|
name='region'
|
||||||
required
|
required
|
||||||
placeholder={'Vertex AI Region.g. us-east5'}
|
placeholder={t('channel.edit.vertex_region_placeholder')}
|
||||||
onChange={handleConfigChange}
|
onChange={handleConfigChange}
|
||||||
value={config.region}
|
value={config.region}
|
||||||
autoComplete=''
|
autoComplete=''
|
||||||
/>
|
/>
|
||||||
<Form.Input
|
<Form.Input
|
||||||
label='Vertex AI Project ID'
|
label={t('channel.edit.vertex_project_id')}
|
||||||
name='vertex_ai_project_id'
|
name='vertex_ai_project_id'
|
||||||
required
|
required
|
||||||
placeholder={'Vertex AI Project ID'}
|
placeholder={t('channel.edit.vertex_project_id_placeholder')}
|
||||||
onChange={handleConfigChange}
|
onChange={handleConfigChange}
|
||||||
value={config.vertex_ai_project_id}
|
value={config.vertex_ai_project_id}
|
||||||
autoComplete=''
|
autoComplete=''
|
||||||
/>
|
/>
|
||||||
<Form.Input
|
<Form.Input
|
||||||
label='Google Cloud Application Default Credentials JSON'
|
label={t('channel.edit.vertex_credentials')}
|
||||||
name='vertex_ai_adc'
|
name='vertex_ai_adc'
|
||||||
required
|
required
|
||||||
placeholder={
|
placeholder={t('channel.edit.vertex_credentials_placeholder')}
|
||||||
'Google Cloud Application Default Credentials JSON'
|
|
||||||
}
|
|
||||||
onChange={handleConfigChange}
|
onChange={handleConfigChange}
|
||||||
value={config.vertex_ai_adc}
|
value={config.vertex_ai_adc}
|
||||||
autoComplete=''
|
autoComplete=''
|
||||||
@ -591,10 +582,10 @@ const EditChannel = () => {
|
|||||||
)}
|
)}
|
||||||
{inputs.type === 34 && (
|
{inputs.type === 34 && (
|
||||||
<Form.Input
|
<Form.Input
|
||||||
label='User ID'
|
label={t('channel.edit.user_id')}
|
||||||
name='user_id'
|
name='user_id'
|
||||||
required
|
required
|
||||||
placeholder={'生成该密钥的用户 ID'}
|
placeholder={t('channel.edit.user_id_placeholder')}
|
||||||
onChange={handleConfigChange}
|
onChange={handleConfigChange}
|
||||||
value={config.user_id}
|
value={config.user_id}
|
||||||
autoComplete=''
|
autoComplete=''
|
||||||
@ -605,10 +596,10 @@ const EditChannel = () => {
|
|||||||
(batch ? (
|
(batch ? (
|
||||||
<Form.Field>
|
<Form.Field>
|
||||||
<Form.TextArea
|
<Form.TextArea
|
||||||
label='密钥'
|
label={t('channel.edit.key')}
|
||||||
name='key'
|
name='key'
|
||||||
required
|
required
|
||||||
placeholder={'请输入密钥,一行一个'}
|
placeholder={t('channel.edit.batch_placeholder')}
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
value={inputs.key}
|
value={inputs.key}
|
||||||
style={{
|
style={{
|
||||||
@ -621,35 +612,20 @@ const EditChannel = () => {
|
|||||||
) : (
|
) : (
|
||||||
<Form.Field>
|
<Form.Field>
|
||||||
<Form.Input
|
<Form.Input
|
||||||
label='密钥'
|
label={t('channel.edit.key')}
|
||||||
name='key'
|
name='key'
|
||||||
required
|
required
|
||||||
placeholder={type2secretPrompt(inputs.type)}
|
placeholder={type2secretPrompt(inputs.type, t)}
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
value={inputs.key}
|
value={inputs.key}
|
||||||
autoComplete='new-password'
|
autoComplete='new-password'
|
||||||
/>
|
/>
|
||||||
</Form.Field>
|
</Form.Field>
|
||||||
))}
|
))}
|
||||||
{inputs.type === 37 && (
|
|
||||||
<Form.Field>
|
|
||||||
<Form.Input
|
|
||||||
label='Account ID'
|
|
||||||
name='user_id'
|
|
||||||
required
|
|
||||||
placeholder={
|
|
||||||
'请输入 Account ID,例如:d8d7c61dbc334c32d3ced580e4bf42b4'
|
|
||||||
}
|
|
||||||
onChange={handleConfigChange}
|
|
||||||
value={config.user_id}
|
|
||||||
autoComplete=''
|
|
||||||
/>
|
|
||||||
</Form.Field>
|
|
||||||
)}
|
|
||||||
{inputs.type !== 33 && !isEdit && (
|
{inputs.type !== 33 && !isEdit && (
|
||||||
<Form.Checkbox
|
<Form.Checkbox
|
||||||
checked={batch}
|
checked={batch}
|
||||||
label='批量创建'
|
label={t('channel.edit.batch')}
|
||||||
name='batch'
|
name='batch'
|
||||||
onChange={() => setBatch(!batch)}
|
onChange={() => setBatch(!batch)}
|
||||||
/>
|
/>
|
||||||
@ -660,11 +636,9 @@ const EditChannel = () => {
|
|||||||
inputs.type !== 22 && (
|
inputs.type !== 22 && (
|
||||||
<Form.Field>
|
<Form.Field>
|
||||||
<Form.Input
|
<Form.Input
|
||||||
label='代理'
|
label={t('channel.edit.base_url')}
|
||||||
name='base_url'
|
name='base_url'
|
||||||
placeholder={
|
placeholder={t('channel.edit.base_url_placeholder')}
|
||||||
'此项可选,用于通过代理站来进行 API 调用,请输入代理站地址,格式为:https://domain.com'
|
|
||||||
}
|
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
value={inputs.base_url}
|
value={inputs.base_url}
|
||||||
autoComplete='new-password'
|
autoComplete='new-password'
|
||||||
@ -685,13 +659,15 @@ const EditChannel = () => {
|
|||||||
/>
|
/>
|
||||||
</Form.Field>
|
</Form.Field>
|
||||||
)}
|
)}
|
||||||
<Button onClick={handleCancel}>取消</Button>
|
<Button onClick={handleCancel}>
|
||||||
|
{t('channel.edit.buttons.cancel')}
|
||||||
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
type={isEdit ? 'button' : 'submit'}
|
type={isEdit ? 'button' : 'submit'}
|
||||||
positive
|
positive
|
||||||
onClick={submit}
|
onClick={submit}
|
||||||
>
|
>
|
||||||
提交
|
{t('channel.edit.buttons.submit')}
|
||||||
</Button>
|
</Button>
|
||||||
</Form>
|
</Form>
|
||||||
</Card.Content>
|
</Card.Content>
|
||||||
|
@ -1,16 +1,21 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Card } from 'semantic-ui-react';
|
import { Card } from 'semantic-ui-react';
|
||||||
import ChannelsTable from '../../components/ChannelsTable';
|
import ChannelsTable from '../../components/ChannelsTable';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
const Channel = () => (
|
const Channel = () => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
return (
|
||||||
<div className='dashboard-container'>
|
<div className='dashboard-container'>
|
||||||
<Card fluid className='chart-card'>
|
<Card fluid className='chart-card'>
|
||||||
<Card.Content>
|
<Card.Content>
|
||||||
<Card.Header className='header'>管理渠道</Card.Header>
|
<Card.Header className='header'>{t('channel.title')}</Card.Header>
|
||||||
<ChannelsTable />
|
<ChannelsTable />
|
||||||
</Card.Content>
|
</Card.Content>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export default Channel;
|
export default Channel;
|
||||||
|
Loading…
Reference in New Issue
Block a user