mirror of
				https://github.com/songquanpeng/one-api.git
				synced 2025-11-04 07:43:41 +08:00 
			
		
		
		
	feat: initial i18n support
This commit is contained in:
		@@ -34,5 +34,123 @@
 | 
			
		||||
      "request_failed": "Request failed",
 | 
			
		||||
      "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": "请求失败",
 | 
			
		||||
      "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 { useTranslation } from 'react-i18next';
 | 
			
		||||
import {
 | 
			
		||||
  Button,
 | 
			
		||||
  Dropdown,
 | 
			
		||||
@@ -31,13 +32,17 @@ function renderTimestamp(timestamp) {
 | 
			
		||||
 | 
			
		||||
let type2label = undefined;
 | 
			
		||||
 | 
			
		||||
function renderType(type) {
 | 
			
		||||
function renderType(type, t) {
 | 
			
		||||
  if (!type2label) {
 | 
			
		||||
    type2label = new Map();
 | 
			
		||||
    for (let i = 0; i < CHANNEL_OPTIONS.length; 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 (
 | 
			
		||||
    <Label basic color={type2label[type]?.color}>
 | 
			
		||||
@@ -46,7 +51,7 @@ function renderType(type) {
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function renderBalance(type, balance) {
 | 
			
		||||
function renderBalance(type, balance, t) {
 | 
			
		||||
  switch (type) {
 | 
			
		||||
    case 1: // OpenAI
 | 
			
		||||
      return <span>${balance.toFixed(2)}</span>;
 | 
			
		||||
@@ -67,7 +72,7 @@ function renderBalance(type, balance) {
 | 
			
		||||
    case 44: // SiliconFlow
 | 
			
		||||
      return <span>¥{balance.toFixed(2)}</span>;
 | 
			
		||||
    default:
 | 
			
		||||
      return <span>不支持</span>;
 | 
			
		||||
      return <span>{t('channel.table.balance_not_supported')}</span>;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -78,6 +83,7 @@ function isShowDetail() {
 | 
			
		||||
const promptID = 'detail';
 | 
			
		||||
 | 
			
		||||
const ChannelsTable = () => {
 | 
			
		||||
  const { t } = useTranslation();
 | 
			
		||||
  const [channels, setChannels] = useState([]);
 | 
			
		||||
  const [loading, setLoading] = useState(true);
 | 
			
		||||
  const [activePage, setActivePage] = useState(1);
 | 
			
		||||
@@ -207,12 +213,12 @@ const ChannelsTable = () => {
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const renderStatus = (status) => {
 | 
			
		||||
  const renderStatus = (status, t) => {
 | 
			
		||||
    switch (status) {
 | 
			
		||||
      case 1:
 | 
			
		||||
        return (
 | 
			
		||||
          <Label basic color='green'>
 | 
			
		||||
            已启用
 | 
			
		||||
            {t('channel.table.status_enabled')}
 | 
			
		||||
          </Label>
 | 
			
		||||
        );
 | 
			
		||||
      case 2:
 | 
			
		||||
@@ -220,10 +226,10 @@ const ChannelsTable = () => {
 | 
			
		||||
          <Popup
 | 
			
		||||
            trigger={
 | 
			
		||||
              <Label basic color='red'>
 | 
			
		||||
                已禁用
 | 
			
		||||
                {t('channel.table.status_disabled')}
 | 
			
		||||
              </Label>
 | 
			
		||||
            }
 | 
			
		||||
            content='本渠道被手动禁用'
 | 
			
		||||
            content={t('channel.table.status_disabled_tip')}
 | 
			
		||||
            basic
 | 
			
		||||
          />
 | 
			
		||||
        );
 | 
			
		||||
@@ -232,29 +238,29 @@ const ChannelsTable = () => {
 | 
			
		||||
          <Popup
 | 
			
		||||
            trigger={
 | 
			
		||||
              <Label basic color='yellow'>
 | 
			
		||||
                已禁用
 | 
			
		||||
                {t('channel.table.status_auto_disabled')}
 | 
			
		||||
              </Label>
 | 
			
		||||
            }
 | 
			
		||||
            content='本渠道被程序自动禁用'
 | 
			
		||||
            content={t('channel.table.status_auto_disabled_tip')}
 | 
			
		||||
            basic
 | 
			
		||||
          />
 | 
			
		||||
        );
 | 
			
		||||
      default:
 | 
			
		||||
        return (
 | 
			
		||||
          <Label basic color='grey'>
 | 
			
		||||
            未知状态
 | 
			
		||||
            {t('channel.table.status_unknown')}
 | 
			
		||||
          </Label>
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const renderResponseTime = (responseTime) => {
 | 
			
		||||
  const renderResponseTime = (responseTime, t) => {
 | 
			
		||||
    let time = responseTime / 1000;
 | 
			
		||||
    time = time.toFixed(2) + ' 秒';
 | 
			
		||||
    time = time.toFixed(2) + 's';
 | 
			
		||||
    if (responseTime === 0) {
 | 
			
		||||
      return (
 | 
			
		||||
        <Label basic color='grey'>
 | 
			
		||||
          未测试
 | 
			
		||||
          {t('channel.table.not_tested')}
 | 
			
		||||
        </Label>
 | 
			
		||||
      );
 | 
			
		||||
    } else if (responseTime <= 1000) {
 | 
			
		||||
@@ -320,9 +326,12 @@ const ChannelsTable = () => {
 | 
			
		||||
      newChannels[realIdx].test_time = Date.now() / 1000;
 | 
			
		||||
      setChannels(newChannels);
 | 
			
		||||
      showInfo(
 | 
			
		||||
        `渠道 ${name} 测试成功,模型 ${model},耗时 ${time.toFixed(
 | 
			
		||||
          2
 | 
			
		||||
        )} 秒,模型输出:${message}`
 | 
			
		||||
        t('channel.messages.test_success', {
 | 
			
		||||
          name: name,
 | 
			
		||||
          model: model,
 | 
			
		||||
          time: time.toFixed(2),
 | 
			
		||||
          message: message,
 | 
			
		||||
        })
 | 
			
		||||
      );
 | 
			
		||||
    } else {
 | 
			
		||||
      showError(message);
 | 
			
		||||
@@ -338,7 +347,7 @@ const ChannelsTable = () => {
 | 
			
		||||
    const res = await API.get(`/api/channel/test?scope=${scope}`);
 | 
			
		||||
    const { success, message } = res.data;
 | 
			
		||||
    if (success) {
 | 
			
		||||
      showInfo('已成功开始测试渠道,请刷新页面查看结果。');
 | 
			
		||||
      showInfo(t('channel.messages.test_all_started'));
 | 
			
		||||
    } else {
 | 
			
		||||
      showError(message);
 | 
			
		||||
    }
 | 
			
		||||
@@ -348,7 +357,9 @@ const ChannelsTable = () => {
 | 
			
		||||
    const res = await API.delete(`/api/channel/disabled`);
 | 
			
		||||
    const { success, message, data } = res.data;
 | 
			
		||||
    if (success) {
 | 
			
		||||
      showSuccess(`已删除所有禁用渠道,共计 ${data} 个`);
 | 
			
		||||
      showSuccess(
 | 
			
		||||
        t('channel.messages.delete_disabled_success', { count: data })
 | 
			
		||||
      );
 | 
			
		||||
      await refresh();
 | 
			
		||||
    } else {
 | 
			
		||||
      showError(message);
 | 
			
		||||
@@ -364,7 +375,7 @@ const ChannelsTable = () => {
 | 
			
		||||
      newChannels[realIdx].balance = balance;
 | 
			
		||||
      newChannels[realIdx].balance_updated_time = Date.now() / 1000;
 | 
			
		||||
      setChannels(newChannels);
 | 
			
		||||
      showInfo(`渠道 ${name} 余额更新成功!`);
 | 
			
		||||
      showInfo(t('channel.messages.balance_update_success', { name: name }));
 | 
			
		||||
    } else {
 | 
			
		||||
      showError(message);
 | 
			
		||||
    }
 | 
			
		||||
@@ -375,7 +386,7 @@ const ChannelsTable = () => {
 | 
			
		||||
    const res = await API.get(`/api/channel/update_balance`);
 | 
			
		||||
    const { success, message } = res.data;
 | 
			
		||||
    if (success) {
 | 
			
		||||
      showInfo('已更新完毕所有已启用渠道余额!');
 | 
			
		||||
      showInfo(t('channel.messages.all_balance_updated'));
 | 
			
		||||
    } else {
 | 
			
		||||
      showError(message);
 | 
			
		||||
    }
 | 
			
		||||
@@ -413,7 +424,7 @@ const ChannelsTable = () => {
 | 
			
		||||
          icon='search'
 | 
			
		||||
          fluid
 | 
			
		||||
          iconPosition='left'
 | 
			
		||||
          placeholder='搜索渠道的 ID,名称和密钥 ...'
 | 
			
		||||
          placeholder={t('channel.search')}
 | 
			
		||||
          value={searchKeyword}
 | 
			
		||||
          loading={searching}
 | 
			
		||||
          onChange={handleKeywordChange}
 | 
			
		||||
@@ -426,13 +437,11 @@ const ChannelsTable = () => {
 | 
			
		||||
            setPromptShown(promptID);
 | 
			
		||||
          }}
 | 
			
		||||
        >
 | 
			
		||||
          OpenAI 渠道已经不再支持通过 key 获取余额,因此余额显示为
 | 
			
		||||
          0。对于支持的渠道类型,请点击余额进行刷新。
 | 
			
		||||
          {t('channel.balance_notice')}
 | 
			
		||||
          <br />
 | 
			
		||||
          渠道测试仅支持 chat 模型,优先使用
 | 
			
		||||
          gpt-3.5-turbo,如果该模型不可用则使用你所配置的模型列表中的第一个模型。
 | 
			
		||||
          {t('channel.test_notice')}
 | 
			
		||||
          <br />
 | 
			
		||||
          点击下方详情按钮可以显示余额以及设置额外的测试模型。
 | 
			
		||||
          {t('channel.detail_notice')}
 | 
			
		||||
        </Message>
 | 
			
		||||
      )}
 | 
			
		||||
      <Table basic={'very'} compact size='small'>
 | 
			
		||||
@@ -444,7 +453,7 @@ const ChannelsTable = () => {
 | 
			
		||||
                sortChannel('id');
 | 
			
		||||
              }}
 | 
			
		||||
            >
 | 
			
		||||
              ID
 | 
			
		||||
              {t('channel.table.id')}
 | 
			
		||||
            </Table.HeaderCell>
 | 
			
		||||
            <Table.HeaderCell
 | 
			
		||||
              style={{ cursor: 'pointer' }}
 | 
			
		||||
@@ -452,7 +461,7 @@ const ChannelsTable = () => {
 | 
			
		||||
                sortChannel('name');
 | 
			
		||||
              }}
 | 
			
		||||
            >
 | 
			
		||||
              名称
 | 
			
		||||
              {t('channel.table.name')}
 | 
			
		||||
            </Table.HeaderCell>
 | 
			
		||||
            <Table.HeaderCell
 | 
			
		||||
              style={{ cursor: 'pointer' }}
 | 
			
		||||
@@ -460,7 +469,7 @@ const ChannelsTable = () => {
 | 
			
		||||
                sortChannel('group');
 | 
			
		||||
              }}
 | 
			
		||||
            >
 | 
			
		||||
              分组
 | 
			
		||||
              {t('channel.table.group')}
 | 
			
		||||
            </Table.HeaderCell>
 | 
			
		||||
            <Table.HeaderCell
 | 
			
		||||
              style={{ cursor: 'pointer' }}
 | 
			
		||||
@@ -468,7 +477,7 @@ const ChannelsTable = () => {
 | 
			
		||||
                sortChannel('type');
 | 
			
		||||
              }}
 | 
			
		||||
            >
 | 
			
		||||
              类型
 | 
			
		||||
              {t('channel.table.type')}
 | 
			
		||||
            </Table.HeaderCell>
 | 
			
		||||
            <Table.HeaderCell
 | 
			
		||||
              style={{ cursor: 'pointer' }}
 | 
			
		||||
@@ -476,7 +485,7 @@ const ChannelsTable = () => {
 | 
			
		||||
                sortChannel('status');
 | 
			
		||||
              }}
 | 
			
		||||
            >
 | 
			
		||||
              状态
 | 
			
		||||
              {t('channel.table.status')}
 | 
			
		||||
            </Table.HeaderCell>
 | 
			
		||||
            <Table.HeaderCell
 | 
			
		||||
              style={{ cursor: 'pointer' }}
 | 
			
		||||
@@ -484,7 +493,7 @@ const ChannelsTable = () => {
 | 
			
		||||
                sortChannel('response_time');
 | 
			
		||||
              }}
 | 
			
		||||
            >
 | 
			
		||||
              响应时间
 | 
			
		||||
              {t('channel.table.response_time')}
 | 
			
		||||
            </Table.HeaderCell>
 | 
			
		||||
            <Table.HeaderCell
 | 
			
		||||
              style={{ cursor: 'pointer' }}
 | 
			
		||||
@@ -493,7 +502,7 @@ const ChannelsTable = () => {
 | 
			
		||||
              }}
 | 
			
		||||
              hidden={!showDetail}
 | 
			
		||||
            >
 | 
			
		||||
              余额
 | 
			
		||||
              {t('channel.table.balance')}
 | 
			
		||||
            </Table.HeaderCell>
 | 
			
		||||
            <Table.HeaderCell
 | 
			
		||||
              style={{ cursor: 'pointer' }}
 | 
			
		||||
@@ -501,10 +510,12 @@ const ChannelsTable = () => {
 | 
			
		||||
                sortChannel('priority');
 | 
			
		||||
              }}
 | 
			
		||||
            >
 | 
			
		||||
              优先级
 | 
			
		||||
              {t('channel.table.priority')}
 | 
			
		||||
            </Table.HeaderCell>
 | 
			
		||||
            <Table.HeaderCell hidden={!showDetail}>测试模型</Table.HeaderCell>
 | 
			
		||||
            <Table.HeaderCell>操作</Table.HeaderCell>
 | 
			
		||||
            <Table.HeaderCell hidden={!showDetail}>
 | 
			
		||||
              {t('channel.table.test_model')}
 | 
			
		||||
            </Table.HeaderCell>
 | 
			
		||||
            <Table.HeaderCell>{t('channel.table.actions')}</Table.HeaderCell>
 | 
			
		||||
          </Table.Row>
 | 
			
		||||
        </Table.Header>
 | 
			
		||||
 | 
			
		||||
@@ -519,19 +530,21 @@ const ChannelsTable = () => {
 | 
			
		||||
              return (
 | 
			
		||||
                <Table.Row key={channel.id}>
 | 
			
		||||
                  <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>{renderType(channel.type)}</Table.Cell>
 | 
			
		||||
                  <Table.Cell>{renderStatus(channel.status)}</Table.Cell>
 | 
			
		||||
                  <Table.Cell>{renderType(channel.type, t)}</Table.Cell>
 | 
			
		||||
                  <Table.Cell>{renderStatus(channel.status, t)}</Table.Cell>
 | 
			
		||||
                  <Table.Cell>
 | 
			
		||||
                    <Popup
 | 
			
		||||
                      content={
 | 
			
		||||
                        channel.test_time
 | 
			
		||||
                          ? renderTimestamp(channel.test_time)
 | 
			
		||||
                          : '未测试'
 | 
			
		||||
                          : t('channel.table.not_tested')
 | 
			
		||||
                      }
 | 
			
		||||
                      key={channel.id}
 | 
			
		||||
                      trigger={renderResponseTime(channel.response_time)}
 | 
			
		||||
                      trigger={renderResponseTime(channel.response_time, t)}
 | 
			
		||||
                      basic
 | 
			
		||||
                    />
 | 
			
		||||
                  </Table.Cell>
 | 
			
		||||
@@ -544,10 +557,10 @@ const ChannelsTable = () => {
 | 
			
		||||
                          }}
 | 
			
		||||
                          style={{ cursor: 'pointer' }}
 | 
			
		||||
                        >
 | 
			
		||||
                          {renderBalance(channel.type, channel.balance)}
 | 
			
		||||
                          {renderBalance(channel.type, channel.balance, t)}
 | 
			
		||||
                        </span>
 | 
			
		||||
                      }
 | 
			
		||||
                      content='点击更新'
 | 
			
		||||
                      content={t('channel.table.click_to_update')}
 | 
			
		||||
                      basic
 | 
			
		||||
                    />
 | 
			
		||||
                  </Table.Cell>
 | 
			
		||||
@@ -569,13 +582,13 @@ const ChannelsTable = () => {
 | 
			
		||||
                          <input style={{ maxWidth: '60px' }} />
 | 
			
		||||
                        </Input>
 | 
			
		||||
                      }
 | 
			
		||||
                      content='渠道选择优先级,越高越优先'
 | 
			
		||||
                      content={t('channel.table.priority_tip')}
 | 
			
		||||
                      basic
 | 
			
		||||
                    />
 | 
			
		||||
                  </Table.Cell>
 | 
			
		||||
                  <Table.Cell hidden={!showDetail}>
 | 
			
		||||
                    <Dropdown
 | 
			
		||||
                      placeholder='请选择测试模型'
 | 
			
		||||
                      placeholder={t('channel.table.select_test_model')}
 | 
			
		||||
                      selection
 | 
			
		||||
                      options={channel.model_options}
 | 
			
		||||
                      defaultValue={channel.test_model}
 | 
			
		||||
@@ -598,22 +611,12 @@ const ChannelsTable = () => {
 | 
			
		||||
                          );
 | 
			
		||||
                        }}
 | 
			
		||||
                      >
 | 
			
		||||
                        测试
 | 
			
		||||
                        {t('channel.buttons.test')}
 | 
			
		||||
                      </Button>
 | 
			
		||||
                      {/*<Button*/}
 | 
			
		||||
                      {/*  size={'small'}*/}
 | 
			
		||||
                      {/*  positive*/}
 | 
			
		||||
                      {/*  loading={updatingBalance}*/}
 | 
			
		||||
                      {/*  onClick={() => {*/}
 | 
			
		||||
                      {/*    updateChannelBalance(channel.id, channel.name, idx);*/}
 | 
			
		||||
                      {/*  }}*/}
 | 
			
		||||
                      {/*>*/}
 | 
			
		||||
                      {/*  更新余额*/}
 | 
			
		||||
                      {/*</Button>*/}
 | 
			
		||||
                      <Popup
 | 
			
		||||
                        trigger={
 | 
			
		||||
                          <Button size='small' negative>
 | 
			
		||||
                            删除
 | 
			
		||||
                            {t('channel.buttons.delete')}
 | 
			
		||||
                          </Button>
 | 
			
		||||
                        }
 | 
			
		||||
                        on='click'
 | 
			
		||||
@@ -626,7 +629,7 @@ const ChannelsTable = () => {
 | 
			
		||||
                            manageChannel(channel.id, 'delete', idx);
 | 
			
		||||
                          }}
 | 
			
		||||
                        >
 | 
			
		||||
                          删除渠道 {channel.name}
 | 
			
		||||
                          {t('channel.buttons.confirm_delete')} {channel.name}
 | 
			
		||||
                        </Button>
 | 
			
		||||
                      </Popup>
 | 
			
		||||
                      <Button
 | 
			
		||||
@@ -639,14 +642,16 @@ const ChannelsTable = () => {
 | 
			
		||||
                          );
 | 
			
		||||
                        }}
 | 
			
		||||
                      >
 | 
			
		||||
                        {channel.status === 1 ? '禁用' : '启用'}
 | 
			
		||||
                        {channel.status === 1
 | 
			
		||||
                          ? t('channel.buttons.disable')
 | 
			
		||||
                          : t('channel.buttons.enable')}
 | 
			
		||||
                      </Button>
 | 
			
		||||
                      <Button
 | 
			
		||||
                        size={'small'}
 | 
			
		||||
                        as={Link}
 | 
			
		||||
                        to={'/channel/edit/' + channel.id}
 | 
			
		||||
                      >
 | 
			
		||||
                        编辑
 | 
			
		||||
                        {t('channel.buttons.edit')}
 | 
			
		||||
                      </Button>
 | 
			
		||||
                    </div>
 | 
			
		||||
                  </Table.Cell>
 | 
			
		||||
@@ -664,7 +669,7 @@ const ChannelsTable = () => {
 | 
			
		||||
                to='/channel/add'
 | 
			
		||||
                loading={loading}
 | 
			
		||||
              >
 | 
			
		||||
                添加新的渠道
 | 
			
		||||
                {t('channel.buttons.add')}
 | 
			
		||||
              </Button>
 | 
			
		||||
              <Button
 | 
			
		||||
                size='small'
 | 
			
		||||
@@ -673,7 +678,7 @@ const ChannelsTable = () => {
 | 
			
		||||
                  testChannels('all');
 | 
			
		||||
                }}
 | 
			
		||||
              >
 | 
			
		||||
                测试所有渠道
 | 
			
		||||
                {t('channel.buttons.test_all')}
 | 
			
		||||
              </Button>
 | 
			
		||||
              <Button
 | 
			
		||||
                size='small'
 | 
			
		||||
@@ -682,14 +687,12 @@ const ChannelsTable = () => {
 | 
			
		||||
                  testChannels('disabled');
 | 
			
		||||
                }}
 | 
			
		||||
              >
 | 
			
		||||
                测试禁用渠道
 | 
			
		||||
                {t('channel.buttons.test_disabled')}
 | 
			
		||||
              </Button>
 | 
			
		||||
              {/*<Button size='small' onClick={updateAllChannelsBalance}*/}
 | 
			
		||||
              {/*        loading={loading || updatingBalance}>更新已启用渠道余额</Button>*/}
 | 
			
		||||
              <Popup
 | 
			
		||||
                trigger={
 | 
			
		||||
                  <Button size='small' loading={loading}>
 | 
			
		||||
                    删除禁用渠道
 | 
			
		||||
                    {t('channel.buttons.delete_disabled')}
 | 
			
		||||
                  </Button>
 | 
			
		||||
                }
 | 
			
		||||
                on='click'
 | 
			
		||||
@@ -702,7 +705,7 @@ const ChannelsTable = () => {
 | 
			
		||||
                  negative
 | 
			
		||||
                  onClick={deleteAllDisabledChannels}
 | 
			
		||||
                >
 | 
			
		||||
                  确认删除
 | 
			
		||||
                  {t('channel.buttons.confirm_delete_disabled')}
 | 
			
		||||
                </Button>
 | 
			
		||||
              </Popup>
 | 
			
		||||
              <Pagination
 | 
			
		||||
@@ -717,10 +720,12 @@ const ChannelsTable = () => {
 | 
			
		||||
                }
 | 
			
		||||
              />
 | 
			
		||||
              <Button size='small' onClick={refresh} loading={loading}>
 | 
			
		||||
                刷新
 | 
			
		||||
                {t('channel.buttons.refresh')}
 | 
			
		||||
              </Button>
 | 
			
		||||
              <Button size='small' onClick={toggleShowDetail}>
 | 
			
		||||
                {showDetail ? '隐藏详情' : '详情'}
 | 
			
		||||
                {showDetail
 | 
			
		||||
                  ? t('channel.buttons.hide_detail')
 | 
			
		||||
                  : t('channel.buttons.show_detail')}
 | 
			
		||||
              </Button>
 | 
			
		||||
            </Table.HeaderCell>
 | 
			
		||||
          </Table.Row>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,5 @@
 | 
			
		||||
import React, { useEffect, useState } from 'react';
 | 
			
		||||
import { useTranslation } from 'react-i18next';
 | 
			
		||||
import {
 | 
			
		||||
  Button,
 | 
			
		||||
  Form,
 | 
			
		||||
@@ -26,23 +27,23 @@ const MODEL_MAPPING_EXAMPLE = {
 | 
			
		||||
  'gpt-4-32k-0314': 'gpt-4-32k',
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
function type2secretPrompt(type) {
 | 
			
		||||
  // inputs.type === 15 ? '按照如下格式输入:APIKey|SecretKey' : (inputs.type === 18 ? '按照如下格式输入:APPID|APISecret|APIKey' : '请输入渠道对应的鉴权密钥')
 | 
			
		||||
function type2secretPrompt(type, t) {
 | 
			
		||||
  switch (type) {
 | 
			
		||||
    case 15:
 | 
			
		||||
      return '按照如下格式输入:APIKey|SecretKey';
 | 
			
		||||
      return t('channel.edit.key_prompts.zhipu');
 | 
			
		||||
    case 18:
 | 
			
		||||
      return '按照如下格式输入:APPID|APISecret|APIKey';
 | 
			
		||||
      return t('channel.edit.key_prompts.spark');
 | 
			
		||||
    case 22:
 | 
			
		||||
      return '按照如下格式输入:APIKey-AppId,例如:fastgpt-0sp2gtvfdgyi4k30jwlgwf1i-64f335d84283f05518e9e041';
 | 
			
		||||
      return t('channel.edit.key_prompts.fastgpt');
 | 
			
		||||
    case 23:
 | 
			
		||||
      return '按照如下格式输入:AppId|SecretId|SecretKey';
 | 
			
		||||
      return t('channel.edit.key_prompts.tencent');
 | 
			
		||||
    default:
 | 
			
		||||
      return '请输入渠道对应的鉴权密钥';
 | 
			
		||||
      return t('channel.edit.key_prompts.default');
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const EditChannel = () => {
 | 
			
		||||
  const { t } = useTranslation();
 | 
			
		||||
  const params = useParams();
 | 
			
		||||
  const navigate = useNavigate();
 | 
			
		||||
  const channelId = params.id;
 | 
			
		||||
@@ -194,15 +195,15 @@ const EditChannel = () => {
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    if (!isEdit && (inputs.name === '' || inputs.key === '')) {
 | 
			
		||||
      showInfo('请填写渠道名称和渠道密钥!');
 | 
			
		||||
      showInfo(t('channel.edit.messages.name_required'));
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    if (inputs.type !== 43 && inputs.models.length === 0) {
 | 
			
		||||
      showInfo('请至少选择一个模型!');
 | 
			
		||||
      showInfo(t('channel.edit.messages.models_required'));
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    if (inputs.model_mapping !== '' && !verifyJSON(inputs.model_mapping)) {
 | 
			
		||||
      showInfo('模型映射必须是合法的 JSON 格式!');
 | 
			
		||||
      showInfo(t('channel.edit.messages.model_mapping_invalid'));
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    let localInputs = { ...inputs };
 | 
			
		||||
@@ -230,9 +231,9 @@ const EditChannel = () => {
 | 
			
		||||
    const { success, message } = res.data;
 | 
			
		||||
    if (success) {
 | 
			
		||||
      if (isEdit) {
 | 
			
		||||
        showSuccess('渠道更新成功!');
 | 
			
		||||
        showSuccess(t('channel.edit.messages.update_success'));
 | 
			
		||||
      } else {
 | 
			
		||||
        showSuccess('渠道创建成功!');
 | 
			
		||||
        showSuccess(t('channel.edit.messages.create_success'));
 | 
			
		||||
        setInputs(originInputs);
 | 
			
		||||
      }
 | 
			
		||||
    } else {
 | 
			
		||||
@@ -263,12 +264,14 @@ const EditChannel = () => {
 | 
			
		||||
      <Card fluid className='chart-card'>
 | 
			
		||||
        <Card.Content>
 | 
			
		||||
          <Card.Header className='header'>
 | 
			
		||||
            {isEdit ? '更新渠道信息' : '创建新的渠道'}
 | 
			
		||||
            {isEdit
 | 
			
		||||
              ? t('channel.edit.title_edit')
 | 
			
		||||
              : t('channel.edit.title_create')}
 | 
			
		||||
          </Card.Header>
 | 
			
		||||
          <Form loading={loading} autoComplete='new-password'>
 | 
			
		||||
            <Form.Field>
 | 
			
		||||
              <Form.Select
 | 
			
		||||
                label='类型'
 | 
			
		||||
                label={t('channel.edit.type')}
 | 
			
		||||
                name='type'
 | 
			
		||||
                required
 | 
			
		||||
                search
 | 
			
		||||
@@ -277,6 +280,35 @@ const EditChannel = () => {
 | 
			
		||||
                onChange={handleInputChange}
 | 
			
		||||
              />
 | 
			
		||||
            </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 && (
 | 
			
		||||
              <>
 | 
			
		||||
                <Message>
 | 
			
		||||
@@ -295,9 +327,7 @@ const EditChannel = () => {
 | 
			
		||||
                  <Form.Input
 | 
			
		||||
                    label='AZURE_OPENAI_ENDPOINT'
 | 
			
		||||
                    name='base_url'
 | 
			
		||||
                    placeholder={
 | 
			
		||||
                      '请输入 AZURE_OPENAI_ENDPOINT,例如:https://docs-test-001.openai.azure.com'
 | 
			
		||||
                    }
 | 
			
		||||
                    placeholder='请输入 AZURE_OPENAI_ENDPOINT,例如:https://docs-test-001.openai.azure.com'
 | 
			
		||||
                    onChange={handleInputChange}
 | 
			
		||||
                    value={inputs.base_url}
 | 
			
		||||
                    autoComplete='new-password'
 | 
			
		||||
@@ -307,9 +337,7 @@ const EditChannel = () => {
 | 
			
		||||
                  <Form.Input
 | 
			
		||||
                    label='默认 API 版本'
 | 
			
		||||
                    name='other'
 | 
			
		||||
                    placeholder={
 | 
			
		||||
                      '请输入默认 API 版本,例如:2024-03-01-preview,该配置可以被实际的请求查询参数所覆盖'
 | 
			
		||||
                    }
 | 
			
		||||
                    placeholder='请输入默认 API 版本,例如:2024-03-01-preview,该配置可以被实际的请求查询参数所覆盖'
 | 
			
		||||
                    onChange={handleInputChange}
 | 
			
		||||
                    value={inputs.other}
 | 
			
		||||
                    autoComplete='new-password'
 | 
			
		||||
@@ -317,55 +345,27 @@ const EditChannel = () => {
 | 
			
		||||
                </Form.Field>
 | 
			
		||||
              </>
 | 
			
		||||
            )}
 | 
			
		||||
 | 
			
		||||
            {/* Custom base URL field */}
 | 
			
		||||
            {inputs.type === 8 && (
 | 
			
		||||
              <Form.Field>
 | 
			
		||||
                <Form.Input
 | 
			
		||||
                  label='Base URL'
 | 
			
		||||
                  label={t('channel.edit.base_url')}
 | 
			
		||||
                  name='base_url'
 | 
			
		||||
                  placeholder={
 | 
			
		||||
                    '请输入自定义渠道的 Base URL,例如:https://openai.justsong.cn'
 | 
			
		||||
                  }
 | 
			
		||||
                  placeholder={t('channel.edit.base_url_placeholder')}
 | 
			
		||||
                  onChange={handleInputChange}
 | 
			
		||||
                  value={inputs.base_url}
 | 
			
		||||
                  autoComplete='new-password'
 | 
			
		||||
                />
 | 
			
		||||
              </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 && (
 | 
			
		||||
              <Form.Field>
 | 
			
		||||
                <Form.Input
 | 
			
		||||
                  label='模型版本'
 | 
			
		||||
                  label={t('channel.edit.spark_version')}
 | 
			
		||||
                  name='other'
 | 
			
		||||
                  placeholder={
 | 
			
		||||
                    '请输入星火大模型版本,注意是接口地址中的版本号,例如:v2.1'
 | 
			
		||||
                  }
 | 
			
		||||
                  placeholder={t('channel.edit.spark_version_placeholder')}
 | 
			
		||||
                  onChange={handleInputChange}
 | 
			
		||||
                  value={inputs.other}
 | 
			
		||||
                  autoComplete='new-password'
 | 
			
		||||
@@ -375,9 +375,9 @@ const EditChannel = () => {
 | 
			
		||||
            {inputs.type === 21 && (
 | 
			
		||||
              <Form.Field>
 | 
			
		||||
                <Form.Input
 | 
			
		||||
                  label='知识库 ID'
 | 
			
		||||
                  label={t('channel.edit.knowledge_id')}
 | 
			
		||||
                  name='other'
 | 
			
		||||
                  placeholder={'请输入知识库 ID,例如:123456'}
 | 
			
		||||
                  placeholder={t('channel.edit.knowledge_id_placeholder')}
 | 
			
		||||
                  onChange={handleInputChange}
 | 
			
		||||
                  value={inputs.other}
 | 
			
		||||
                  autoComplete='new-password'
 | 
			
		||||
@@ -387,11 +387,9 @@ const EditChannel = () => {
 | 
			
		||||
            {inputs.type === 17 && (
 | 
			
		||||
              <Form.Field>
 | 
			
		||||
                <Form.Input
 | 
			
		||||
                  label='插件参数'
 | 
			
		||||
                  label={t('channel.edit.plugin_param')}
 | 
			
		||||
                  name='other'
 | 
			
		||||
                  placeholder={
 | 
			
		||||
                    '请输入插件参数,即 X-DashScope-Plugin 请求头的取值'
 | 
			
		||||
                  }
 | 
			
		||||
                  placeholder={t('channel.edit.plugin_param_placeholder')}
 | 
			
		||||
                  onChange={handleInputChange}
 | 
			
		||||
                  value={inputs.other}
 | 
			
		||||
                  autoComplete='new-password'
 | 
			
		||||
@@ -399,28 +397,25 @@ const EditChannel = () => {
 | 
			
		||||
              </Form.Field>
 | 
			
		||||
            )}
 | 
			
		||||
            {inputs.type === 34 && (
 | 
			
		||||
              <Message>
 | 
			
		||||
                对于 Coze 而言,模型名称即 Bot ID,你可以添加一个前缀
 | 
			
		||||
                `bot-`,例如:`bot-123456`。
 | 
			
		||||
              </Message>
 | 
			
		||||
              <Message>{t('channel.edit.coze_notice')}</Message>
 | 
			
		||||
            )}
 | 
			
		||||
            {inputs.type === 40 && (
 | 
			
		||||
              <Message>
 | 
			
		||||
                对于豆包而言,需要手动去{' '}
 | 
			
		||||
                {t('channel.edit.douban_notice')}
 | 
			
		||||
                <a
 | 
			
		||||
                  target='_blank'
 | 
			
		||||
                  href='https://console.volcengine.com/ark/region:ark+cn-beijing/endpoint'
 | 
			
		||||
                >
 | 
			
		||||
                  模型推理页面
 | 
			
		||||
                </a>{' '}
 | 
			
		||||
                创建推理接入点,以接入点名称作为模型名称,例如:`ep-20240608051426-tkxvl`。
 | 
			
		||||
                  {t('channel.edit.douban_notice_link')}
 | 
			
		||||
                </a>
 | 
			
		||||
                {t('channel.edit.douban_notice_2')}
 | 
			
		||||
              </Message>
 | 
			
		||||
            )}
 | 
			
		||||
            {inputs.type !== 43 && (
 | 
			
		||||
              <Form.Field>
 | 
			
		||||
                <Form.Dropdown
 | 
			
		||||
                  label='模型'
 | 
			
		||||
                  placeholder={'请选择该渠道所支持的模型'}
 | 
			
		||||
                  label={t('channel.edit.models')}
 | 
			
		||||
                  placeholder={t('channel.edit.models_placeholder')}
 | 
			
		||||
                  name='models'
 | 
			
		||||
                  required
 | 
			
		||||
                  fluid
 | 
			
		||||
@@ -448,7 +443,7 @@ const EditChannel = () => {
 | 
			
		||||
                    });
 | 
			
		||||
                  }}
 | 
			
		||||
                >
 | 
			
		||||
                  填入相关模型
 | 
			
		||||
                  {t('channel.edit.buttons.fill_models')}
 | 
			
		||||
                </Button>
 | 
			
		||||
                <Button
 | 
			
		||||
                  type={'button'}
 | 
			
		||||
@@ -459,7 +454,7 @@ const EditChannel = () => {
 | 
			
		||||
                    });
 | 
			
		||||
                  }}
 | 
			
		||||
                >
 | 
			
		||||
                  填入所有模型
 | 
			
		||||
                  {t('channel.edit.buttons.fill_all')}
 | 
			
		||||
                </Button>
 | 
			
		||||
                <Button
 | 
			
		||||
                  type={'button'}
 | 
			
		||||
@@ -467,15 +462,15 @@ const EditChannel = () => {
 | 
			
		||||
                    handleInputChange(null, { name: 'models', value: [] });
 | 
			
		||||
                  }}
 | 
			
		||||
                >
 | 
			
		||||
                  清除所有模型
 | 
			
		||||
                  {t('channel.edit.buttons.clear')}
 | 
			
		||||
                </Button>
 | 
			
		||||
                <Input
 | 
			
		||||
                  action={
 | 
			
		||||
                    <Button type={'button'} onClick={addCustomModel}>
 | 
			
		||||
                      填入
 | 
			
		||||
                      {t('channel.edit.buttons.add_custom')}
 | 
			
		||||
                    </Button>
 | 
			
		||||
                  }
 | 
			
		||||
                  placeholder='输入自定义模型名称'
 | 
			
		||||
                  placeholder={t('channel.edit.buttons.custom_placeholder')}
 | 
			
		||||
                  value={customModel}
 | 
			
		||||
                  onChange={(e, { value }) => {
 | 
			
		||||
                    setCustomModel(value);
 | 
			
		||||
@@ -493,12 +488,10 @@ const EditChannel = () => {
 | 
			
		||||
              <>
 | 
			
		||||
                <Form.Field>
 | 
			
		||||
                  <Form.TextArea
 | 
			
		||||
                    label='模型重定向'
 | 
			
		||||
                    placeholder={`此项可选,用于修改请求体中的模型名称,为一个 JSON 字符串,键为请求中模型名称,值为要替换的模型名称,例如:\n${JSON.stringify(
 | 
			
		||||
                      MODEL_MAPPING_EXAMPLE,
 | 
			
		||||
                      null,
 | 
			
		||||
                      2
 | 
			
		||||
                    )}`}
 | 
			
		||||
                    label={t('channel.edit.model_mapping')}
 | 
			
		||||
                    placeholder={`${t(
 | 
			
		||||
                      'channel.edit.model_mapping_placeholder'
 | 
			
		||||
                    )}\n${JSON.stringify(MODEL_MAPPING_EXAMPLE, null, 2)}`}
 | 
			
		||||
                    name='model_mapping'
 | 
			
		||||
                    onChange={handleInputChange}
 | 
			
		||||
                    value={inputs.model_mapping}
 | 
			
		||||
@@ -511,8 +504,8 @@ const EditChannel = () => {
 | 
			
		||||
                </Form.Field>
 | 
			
		||||
                <Form.Field>
 | 
			
		||||
                  <Form.TextArea
 | 
			
		||||
                    label='系统提示词'
 | 
			
		||||
                    placeholder={`此项可选,用于强制设置给定的系统提示词,请配合自定义模型 & 模型重定向使用,首先创建一个唯一的自定义模型名称并在上面填入,之后将该自定义模型重定向映射到该渠道一个原生支持的模型`}
 | 
			
		||||
                    label={t('channel.edit.system_prompt')}
 | 
			
		||||
                    placeholder={t('channel.edit.system_prompt_placeholder')}
 | 
			
		||||
                    name='system_prompt'
 | 
			
		||||
                    onChange={handleInputChange}
 | 
			
		||||
                    value={inputs.system_prompt}
 | 
			
		||||
@@ -531,7 +524,7 @@ const EditChannel = () => {
 | 
			
		||||
                  label='Region'
 | 
			
		||||
                  name='region'
 | 
			
		||||
                  required
 | 
			
		||||
                  placeholder={'region,e.g. us-west-2'}
 | 
			
		||||
                  placeholder={t('channel.edit.aws_region_placeholder')}
 | 
			
		||||
                  onChange={handleConfigChange}
 | 
			
		||||
                  value={config.region}
 | 
			
		||||
                  autoComplete=''
 | 
			
		||||
@@ -540,7 +533,7 @@ const EditChannel = () => {
 | 
			
		||||
                  label='AK'
 | 
			
		||||
                  name='ak'
 | 
			
		||||
                  required
 | 
			
		||||
                  placeholder={'AWS IAM Access Key'}
 | 
			
		||||
                  placeholder={t('channel.edit.aws_ak_placeholder')}
 | 
			
		||||
                  onChange={handleConfigChange}
 | 
			
		||||
                  value={config.ak}
 | 
			
		||||
                  autoComplete=''
 | 
			
		||||
@@ -549,7 +542,7 @@ const EditChannel = () => {
 | 
			
		||||
                  label='SK'
 | 
			
		||||
                  name='sk'
 | 
			
		||||
                  required
 | 
			
		||||
                  placeholder={'AWS IAM Secret Key'}
 | 
			
		||||
                  placeholder={t('channel.edit.aws_sk_placeholder')}
 | 
			
		||||
                  onChange={handleConfigChange}
 | 
			
		||||
                  value={config.sk}
 | 
			
		||||
                  autoComplete=''
 | 
			
		||||
@@ -562,27 +555,25 @@ const EditChannel = () => {
 | 
			
		||||
                  label='Region'
 | 
			
		||||
                  name='region'
 | 
			
		||||
                  required
 | 
			
		||||
                  placeholder={'Vertex AI Region.g. us-east5'}
 | 
			
		||||
                  placeholder={t('channel.edit.vertex_region_placeholder')}
 | 
			
		||||
                  onChange={handleConfigChange}
 | 
			
		||||
                  value={config.region}
 | 
			
		||||
                  autoComplete=''
 | 
			
		||||
                />
 | 
			
		||||
                <Form.Input
 | 
			
		||||
                  label='Vertex AI Project ID'
 | 
			
		||||
                  label={t('channel.edit.vertex_project_id')}
 | 
			
		||||
                  name='vertex_ai_project_id'
 | 
			
		||||
                  required
 | 
			
		||||
                  placeholder={'Vertex AI Project ID'}
 | 
			
		||||
                  placeholder={t('channel.edit.vertex_project_id_placeholder')}
 | 
			
		||||
                  onChange={handleConfigChange}
 | 
			
		||||
                  value={config.vertex_ai_project_id}
 | 
			
		||||
                  autoComplete=''
 | 
			
		||||
                />
 | 
			
		||||
                <Form.Input
 | 
			
		||||
                  label='Google Cloud Application Default Credentials JSON'
 | 
			
		||||
                  label={t('channel.edit.vertex_credentials')}
 | 
			
		||||
                  name='vertex_ai_adc'
 | 
			
		||||
                  required
 | 
			
		||||
                  placeholder={
 | 
			
		||||
                    'Google Cloud Application Default Credentials JSON'
 | 
			
		||||
                  }
 | 
			
		||||
                  placeholder={t('channel.edit.vertex_credentials_placeholder')}
 | 
			
		||||
                  onChange={handleConfigChange}
 | 
			
		||||
                  value={config.vertex_ai_adc}
 | 
			
		||||
                  autoComplete=''
 | 
			
		||||
@@ -591,10 +582,10 @@ const EditChannel = () => {
 | 
			
		||||
            )}
 | 
			
		||||
            {inputs.type === 34 && (
 | 
			
		||||
              <Form.Input
 | 
			
		||||
                label='User ID'
 | 
			
		||||
                label={t('channel.edit.user_id')}
 | 
			
		||||
                name='user_id'
 | 
			
		||||
                required
 | 
			
		||||
                placeholder={'生成该密钥的用户 ID'}
 | 
			
		||||
                placeholder={t('channel.edit.user_id_placeholder')}
 | 
			
		||||
                onChange={handleConfigChange}
 | 
			
		||||
                value={config.user_id}
 | 
			
		||||
                autoComplete=''
 | 
			
		||||
@@ -605,10 +596,10 @@ const EditChannel = () => {
 | 
			
		||||
              (batch ? (
 | 
			
		||||
                <Form.Field>
 | 
			
		||||
                  <Form.TextArea
 | 
			
		||||
                    label='密钥'
 | 
			
		||||
                    label={t('channel.edit.key')}
 | 
			
		||||
                    name='key'
 | 
			
		||||
                    required
 | 
			
		||||
                    placeholder={'请输入密钥,一行一个'}
 | 
			
		||||
                    placeholder={t('channel.edit.batch_placeholder')}
 | 
			
		||||
                    onChange={handleInputChange}
 | 
			
		||||
                    value={inputs.key}
 | 
			
		||||
                    style={{
 | 
			
		||||
@@ -621,35 +612,20 @@ const EditChannel = () => {
 | 
			
		||||
              ) : (
 | 
			
		||||
                <Form.Field>
 | 
			
		||||
                  <Form.Input
 | 
			
		||||
                    label='密钥'
 | 
			
		||||
                    label={t('channel.edit.key')}
 | 
			
		||||
                    name='key'
 | 
			
		||||
                    required
 | 
			
		||||
                    placeholder={type2secretPrompt(inputs.type)}
 | 
			
		||||
                    placeholder={type2secretPrompt(inputs.type, t)}
 | 
			
		||||
                    onChange={handleInputChange}
 | 
			
		||||
                    value={inputs.key}
 | 
			
		||||
                    autoComplete='new-password'
 | 
			
		||||
                  />
 | 
			
		||||
                </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 && (
 | 
			
		||||
              <Form.Checkbox
 | 
			
		||||
                checked={batch}
 | 
			
		||||
                label='批量创建'
 | 
			
		||||
                label={t('channel.edit.batch')}
 | 
			
		||||
                name='batch'
 | 
			
		||||
                onChange={() => setBatch(!batch)}
 | 
			
		||||
              />
 | 
			
		||||
@@ -660,11 +636,9 @@ const EditChannel = () => {
 | 
			
		||||
              inputs.type !== 22 && (
 | 
			
		||||
                <Form.Field>
 | 
			
		||||
                  <Form.Input
 | 
			
		||||
                    label='代理'
 | 
			
		||||
                    label={t('channel.edit.base_url')}
 | 
			
		||||
                    name='base_url'
 | 
			
		||||
                    placeholder={
 | 
			
		||||
                      '此项可选,用于通过代理站来进行 API 调用,请输入代理站地址,格式为:https://domain.com'
 | 
			
		||||
                    }
 | 
			
		||||
                    placeholder={t('channel.edit.base_url_placeholder')}
 | 
			
		||||
                    onChange={handleInputChange}
 | 
			
		||||
                    value={inputs.base_url}
 | 
			
		||||
                    autoComplete='new-password'
 | 
			
		||||
@@ -685,13 +659,15 @@ const EditChannel = () => {
 | 
			
		||||
                />
 | 
			
		||||
              </Form.Field>
 | 
			
		||||
            )}
 | 
			
		||||
            <Button onClick={handleCancel}>取消</Button>
 | 
			
		||||
            <Button onClick={handleCancel}>
 | 
			
		||||
              {t('channel.edit.buttons.cancel')}
 | 
			
		||||
            </Button>
 | 
			
		||||
            <Button
 | 
			
		||||
              type={isEdit ? 'button' : 'submit'}
 | 
			
		||||
              positive
 | 
			
		||||
              onClick={submit}
 | 
			
		||||
            >
 | 
			
		||||
              提交
 | 
			
		||||
              {t('channel.edit.buttons.submit')}
 | 
			
		||||
            </Button>
 | 
			
		||||
          </Form>
 | 
			
		||||
        </Card.Content>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,16 +1,21 @@
 | 
			
		||||
import React from 'react';
 | 
			
		||||
import { Card } from 'semantic-ui-react';
 | 
			
		||||
import ChannelsTable from '../../components/ChannelsTable';
 | 
			
		||||
import { useTranslation } from 'react-i18next';
 | 
			
		||||
 | 
			
		||||
const Channel = () => (
 | 
			
		||||
  <div className='dashboard-container'>
 | 
			
		||||
    <Card fluid className='chart-card'>
 | 
			
		||||
      <Card.Content>
 | 
			
		||||
        <Card.Header className='header'>管理渠道</Card.Header>
 | 
			
		||||
        <ChannelsTable />
 | 
			
		||||
      </Card.Content>
 | 
			
		||||
    </Card>
 | 
			
		||||
  </div>
 | 
			
		||||
);
 | 
			
		||||
const Channel = () => {
 | 
			
		||||
  const { t } = useTranslation();
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <div className='dashboard-container'>
 | 
			
		||||
      <Card fluid className='chart-card'>
 | 
			
		||||
        <Card.Content>
 | 
			
		||||
          <Card.Header className='header'>{t('channel.title')}</Card.Header>
 | 
			
		||||
          <ChannelsTable />
 | 
			
		||||
        </Card.Content>
 | 
			
		||||
      </Card>
 | 
			
		||||
    </div>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default Channel;
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user