Преглед изворни кода

合并上游、支持已有渠道获取模型

bubu пре 1 година
родитељ
комит
6aa1f2fcbe

+ 85 - 85
common/model-ratio.go

@@ -5,7 +5,7 @@ import (
 	"strings"
 )
 
-//from songquanpeng/one-api
+// from songquanpeng/one-api
 const (
 	USD2RMB = 7.3 // 暂定 1 USD = 7.3 RMB
 	USD     = 500 // $0.002 = 1 -> $1 = 500
@@ -40,86 +40,86 @@ var DefaultModelRatio = map[string]float64{
 	"gpt-4-turbo-2024-04-09":    5,    // $0.01 / 1K tokens
 	"gpt-3.5-turbo":             0.25, // $0.0015 / 1K tokens
 	//"gpt-3.5-turbo-0301":           0.75, //deprecated
-	"gpt-3.5-turbo-0613":           0.75,
-	"gpt-3.5-turbo-16k":            1.5, // $0.003 / 1K tokens
-	"gpt-3.5-turbo-16k-0613":       1.5,
-	"gpt-3.5-turbo-instruct":       0.75, // $0.0015 / 1K tokens
-	"gpt-3.5-turbo-1106":           0.5,  // $0.001 / 1K tokens
-	"gpt-3.5-turbo-0125":           0.25,
-	"babbage-002":                  0.2, // $0.0004 / 1K tokens
-	"davinci-002":                  1,   // $0.002 / 1K tokens
-	"text-ada-001":                 0.2,
-	"text-babbage-001":             0.25,
-	"text-curie-001":               1,
-	"text-davinci-002":             10,
-	"text-davinci-003":             10,
-	"text-davinci-edit-001":        10,
-	"code-davinci-edit-001":        10,
-	"whisper-1":                    15,  // $0.006 / minute -> $0.006 / 150 words -> $0.006 / 200 tokens -> $0.03 / 1k tokens
-	"tts-1":                        7.5, // 1k characters -> $0.015
-	"tts-1-1106":                   7.5, // 1k characters -> $0.015
-	"tts-1-hd":                     15,  // 1k characters -> $0.03
-	"tts-1-hd-1106":                15,  // 1k characters -> $0.03
-	"davinci":                      10,
-	"curie":                        10,
-	"babbage":                      10,
-	"ada":                          10,
-	"text-embedding-3-small":       0.01,
-	"text-embedding-3-large":       0.065,
-	"text-embedding-ada-002":       0.05,
-	"text-search-ada-doc-001":      10,
-	"text-moderation-stable":       0.1,
-	"text-moderation-latest":       0.1,
-	"claude-instant-1":             0.4,    // $0.8 / 1M tokens
-	"claude-2.0":                   4,      // $8 / 1M tokens
-	"claude-2.1":                   4,      // $8 / 1M tokens
-	"claude-3-haiku-20240307":      0.125,  // $0.25 / 1M tokens
-	"claude-3-sonnet-20240229":     1.5,    // $3 / 1M tokens
-	"claude-3-opus-20240229":       7.5,    // $15 / 1M tokens
-	"ERNIE-Bot":                    0.8572, // ¥0.012 / 1k tokens //renamed to ERNIE-3.5-8K
-	"ERNIE-Bot-turbo":              0.5715, // ¥0.008 / 1k tokens //renamed to ERNIE-Lite-8K
-	"ERNIE-Bot-4":                  8.572,  // ¥0.12 / 1k tokens //renamed to ERNIE-4.0-8K
-	"ERNIE-4.0-8K":                 8.572,  // ¥0.12 / 1k tokens
-	"ERNIE-3.5-8K":                 0.8572, // ¥0.012 / 1k tokens
-	"ERNIE-Speed-8K":               0.2858, // ¥0.004 / 1k tokens
-	"ERNIE-Speed-128K":             0.2858, // ¥0.004 / 1k tokens
-	"ERNIE-Lite-8K":                0.2143, // ¥0.003 / 1k tokens
-	"ERNIE-Tiny-8K":                0.0715, // ¥0.001 / 1k tokens
-	"ERNIE-Character-8K":           0.2858, // ¥0.004 / 1k tokens
-	"ERNIE-Functions-8K":           0.2858, // ¥0.004 / 1k tokens
-	"Embedding-V1":                 0.1429, // ¥0.002 / 1k tokens
-	"PaLM-2":                       1,
-	"gemini-pro":                   1, // $0.00025 / 1k characters -> $0.001 / 1k tokens
-	"gemini-pro-vision":            1, // $0.00025 / 1k characters -> $0.001 / 1k tokens
-	"gemini-1.0-pro-vision-001":    1,
-	"gemini-1.0-pro-001":           1,
-	"gemini-1.5-pro-latest":        1,
-	"gemini-1.5-flash-latest":      1,
-	"gemini-1.0-pro-latest":        1,
-	"gemini-1.0-pro-vision-latest": 1,
-	"gemini-ultra":                 1,
-	"chatglm_turbo":                0.3572, // ¥0.005 / 1k tokens
-	"chatglm_pro":                  0.7143, // ¥0.01 / 1k tokens
-	"chatglm_std":                  0.3572, // ¥0.005 / 1k tokens
-	"chatglm_lite":                 0.1429, // ¥0.002 / 1k tokens
-	"glm-4":                        7.143,  // ¥0.1 / 1k tokens
-	"glm-4v":                       7.143,  // ¥0.1 / 1k tokens
-	"glm-3-turbo":                  0.3572,
-	"qwen-turbo":                   0.8572, // ¥0.012 / 1k tokens
-	"qwen-plus":                    10,     // ¥0.14 / 1k tokens
-	"text-embedding-v1":            0.05,   // ¥0.0007 / 1k tokens
-	"SparkDesk-v1.1":               1.2858, // ¥0.018 / 1k tokens
-	"SparkDesk-v2.1":               1.2858, // ¥0.018 / 1k tokens
-	"SparkDesk-v3.1":               1.2858, // ¥0.018 / 1k tokens
-	"SparkDesk-v3.5":               1.2858, // ¥0.018 / 1k tokens
-	"360GPT_S2_V9":                 0.8572, // ¥0.012 / 1k tokens
-	"360gpt-turbo":                 0.0858, // ¥0.0012 / 1k tokens
+	"gpt-3.5-turbo-0613":             0.75,
+	"gpt-3.5-turbo-16k":              1.5, // $0.003 / 1K tokens
+	"gpt-3.5-turbo-16k-0613":         1.5,
+	"gpt-3.5-turbo-instruct":         0.75, // $0.0015 / 1K tokens
+	"gpt-3.5-turbo-1106":             0.5,  // $0.001 / 1K tokens
+	"gpt-3.5-turbo-0125":             0.25,
+	"babbage-002":                    0.2, // $0.0004 / 1K tokens
+	"davinci-002":                    1,   // $0.002 / 1K tokens
+	"text-ada-001":                   0.2,
+	"text-babbage-001":               0.25,
+	"text-curie-001":                 1,
+	"text-davinci-002":               10,
+	"text-davinci-003":               10,
+	"text-davinci-edit-001":          10,
+	"code-davinci-edit-001":          10,
+	"whisper-1":                      15,  // $0.006 / minute -> $0.006 / 150 words -> $0.006 / 200 tokens -> $0.03 / 1k tokens
+	"tts-1":                          7.5, // 1k characters -> $0.015
+	"tts-1-1106":                     7.5, // 1k characters -> $0.015
+	"tts-1-hd":                       15,  // 1k characters -> $0.03
+	"tts-1-hd-1106":                  15,  // 1k characters -> $0.03
+	"davinci":                        10,
+	"curie":                          10,
+	"babbage":                        10,
+	"ada":                            10,
+	"text-embedding-3-small":         0.01,
+	"text-embedding-3-large":         0.065,
+	"text-embedding-ada-002":         0.05,
+	"text-search-ada-doc-001":        10,
+	"text-moderation-stable":         0.1,
+	"text-moderation-latest":         0.1,
+	"claude-instant-1":               0.4,    // $0.8 / 1M tokens
+	"claude-2.0":                     4,      // $8 / 1M tokens
+	"claude-2.1":                     4,      // $8 / 1M tokens
+	"claude-3-haiku-20240307":        0.125,  // $0.25 / 1M tokens
+	"claude-3-sonnet-20240229":       1.5,    // $3 / 1M tokens
+	"claude-3-opus-20240229":         7.5,    // $15 / 1M tokens
+	"ERNIE-Bot":                      0.8572, // ¥0.012 / 1k tokens //renamed to ERNIE-3.5-8K
+	"ERNIE-Bot-turbo":                0.5715, // ¥0.008 / 1k tokens //renamed to ERNIE-Lite-8K
+	"ERNIE-Bot-4":                    8.572,  // ¥0.12 / 1k tokens //renamed to ERNIE-4.0-8K
+	"ERNIE-4.0-8K":                   8.572,  // ¥0.12 / 1k tokens
+	"ERNIE-3.5-8K":                   0.8572, // ¥0.012 / 1k tokens
+	"ERNIE-Speed-8K":                 0.2858, // ¥0.004 / 1k tokens
+	"ERNIE-Speed-128K":               0.2858, // ¥0.004 / 1k tokens
+	"ERNIE-Lite-8K":                  0.2143, // ¥0.003 / 1k tokens
+	"ERNIE-Tiny-8K":                  0.0715, // ¥0.001 / 1k tokens
+	"ERNIE-Character-8K":             0.2858, // ¥0.004 / 1k tokens
+	"ERNIE-Functions-8K":             0.2858, // ¥0.004 / 1k tokens
+	"Embedding-V1":                   0.1429, // ¥0.002 / 1k tokens
+	"PaLM-2":                         1,
+	"gemini-pro":                     1, // $0.00025 / 1k characters -> $0.001 / 1k tokens
+	"gemini-pro-vision":              1, // $0.00025 / 1k characters -> $0.001 / 1k tokens
+	"gemini-1.0-pro-vision-001":      1,
+	"gemini-1.0-pro-001":             1,
+	"gemini-1.5-pro-latest":          1,
+	"gemini-1.5-flash-latest":        1,
+	"gemini-1.0-pro-latest":          1,
+	"gemini-1.0-pro-vision-latest":   1,
+	"gemini-ultra":                   1,
+	"chatglm_turbo":                  0.3572, // ¥0.005 / 1k tokens
+	"chatglm_pro":                    0.7143, // ¥0.01 / 1k tokens
+	"chatglm_std":                    0.3572, // ¥0.005 / 1k tokens
+	"chatglm_lite":                   0.1429, // ¥0.002 / 1k tokens
+	"glm-4":                          7.143,  // ¥0.1 / 1k tokens
+	"glm-4v":                         7.143,  // ¥0.1 / 1k tokens
+	"glm-3-turbo":                    0.3572,
+	"qwen-turbo":                     0.8572, // ¥0.012 / 1k tokens
+	"qwen-plus":                      10,     // ¥0.14 / 1k tokens
+	"text-embedding-v1":              0.05,   // ¥0.0007 / 1k tokens
+	"SparkDesk-v1.1":                 1.2858, // ¥0.018 / 1k tokens
+	"SparkDesk-v2.1":                 1.2858, // ¥0.018 / 1k tokens
+	"SparkDesk-v3.1":                 1.2858, // ¥0.018 / 1k tokens
+	"SparkDesk-v3.5":                 1.2858, // ¥0.018 / 1k tokens
+	"360GPT_S2_V9":                   0.8572, // ¥0.012 / 1k tokens
+	"360gpt-turbo":                   0.0858, // ¥0.0012 / 1k tokens
 	"360gpt-turbo-responsibility-8k": 0.8572, // ¥0.012 / 1k tokens
-	"360gpt-pro":                   0.8572, // ¥0.012 / 1k tokens
-	"embedding-bert-512-v1":        0.0715, // ¥0.001 / 1k tokens
-	"embedding_s1_v1":              0.0715, // ¥0.001 / 1k tokens
-	"semantic_similarity_s1_v1":    0.0715, // ¥0.001 / 1k tokens
-	"hunyuan":                      7.143,  // ¥0.1 / 1k tokens  // https://cloud.tencent.com/document/product/1729/97731#e0e6be58-60c8-469f-bdeb-6c264ce3b4d0
+	"360gpt-pro":                     0.8572, // ¥0.012 / 1k tokens
+	"embedding-bert-512-v1":          0.0715, // ¥0.001 / 1k tokens
+	"embedding_s1_v1":                0.0715, // ¥0.001 / 1k tokens
+	"semantic_similarity_s1_v1":      0.0715, // ¥0.001 / 1k tokens
+	"hunyuan":                        7.143,  // ¥0.1 / 1k tokens  // https://cloud.tencent.com/document/product/1729/97731#e0e6be58-60c8-469f-bdeb-6c264ce3b4d0
 	// https://platform.lingyiwanwu.com/docs#-计费单元
 	// 已经按照 7.2 来换算美元价格
 	"yi-34b-chat-0205":      0.18,
@@ -143,10 +143,10 @@ var DefaultModelRatio = map[string]float64{
 	"deepseek-chat":         0.07,
 	"deepseek-coder":        0.07,
 	// Perplexity online 模型对搜索额外收费,有需要应自行调整,此处不计入搜索费用
-	"llama-3-sonar-small-32k-chat":    0.2 / 1000 * USD,
-	"llama-3-sonar-small-32k-online":  0.2 / 1000 * USD,
-	"llama-3-sonar-large-32k-chat":    1 / 1000 * USD,
-	"llama-3-sonar-large-32k-online":  1 / 1000 * USD,
+	"llama-3-sonar-small-32k-chat":   0.2 / 1000 * USD,
+	"llama-3-sonar-small-32k-online": 0.2 / 1000 * USD,
+	"llama-3-sonar-large-32k-chat":   1 / 1000 * USD,
+	"llama-3-sonar-large-32k-online": 1 / 1000 * USD,
 }
 
 var DefaultModelPrice = map[string]float64{
@@ -289,7 +289,7 @@ func GetCompletionRatio(name string) float64 {
 		}
 		return 4.0 / 3.0
 	}
-	if strings.HasPrefix(name, "gpt-4") && name != "gpt-4-all" && name != "gpt-4-gizmo-*" {
+	if strings.HasPrefix(name, "gpt-4") && !strings.HasSuffix(name, "-all") && !strings.HasSuffix(name, "-gizmo-*") {
 		if strings.HasPrefix(name, "gpt-4-turbo") || strings.HasSuffix(name, "preview") || strings.HasPrefix(name, "gpt-4o") {
 			return 3
 		}

+ 4 - 4
relay/channel/openai/adaptor.go

@@ -41,10 +41,10 @@ func (a *Adaptor) GetRequestURL(info *relaycommon.RelayInfo) (string, error) {
 		return relaycommon.GetFullRequestURL(info.BaseUrl, requestURL, info.ChannelType), nil
 	case common.ChannelTypeMiniMax:
 		return minimax.GetRequestURL(info)
-	//case common.ChannelTypeCustom:
-	//	url := info.BaseUrl
-	//	url = strings.Replace(url, "{model}", info.UpstreamModelName, -1)
-	//	return url, nil
+	case common.ChannelTypeCustom:
+		url := info.BaseUrl
+		url = strings.Replace(url, "{model}", info.UpstreamModelName, -1)
+		return url, nil
 	default:
 		return relaycommon.GetFullRequestURL(info.BaseUrl, info.RequestURLPath, info.ChannelType), nil
 	}

+ 6 - 6
web/src/components/ChannelsTable.js

@@ -310,12 +310,12 @@ const ChannelsTable = () => {
 
   const setChannelFormat = (channels) => {
     for (let i = 0; i < channels.length; i++) {
-      if (channels[i].type === 8) {
-        showWarning(
-          '检测到您使用了“自定义渠道”类型,请更换为“OpenAI”渠道类型!',
-        );
-        showWarning('下个版本将不再支持“自定义渠道”类型!');
-      }
+      // if (channels[i].type === 8) {
+      //   showWarning(
+      //     '检测到您使用了“自定义渠道”类型,请更换为“OpenAI”渠道类型!',
+      //   );
+      //   showWarning('下个版本将不再支持“自定义渠道”类型!');
+      // }
       channels[i].key = '' + channels[i].id;
       let test_models = [];
       channels[i].models.split(',').forEach((item, index) => {

+ 5 - 2
web/src/components/LogsTable.js

@@ -302,6 +302,9 @@ const LogsTable = () => {
         let content = '渠道:' + record.channel;
         if (record.other !== '') {
           let other = JSON.parse(record.other);
+          if (other === null) {
+            return <></>
+          }
           if (other.admin_info !== undefined) {
             if (
               other.admin_info.use_channel !== null &&
@@ -322,7 +325,8 @@ const LogsTable = () => {
       title: '详情',
       dataIndex: 'content',
       render: (text, record, index) => {
-        if (record.other === '') {
+        let other = JSON.parse(record.other);
+        if (other == null) {
           return (
             <Paragraph
               ellipsis={{
@@ -338,7 +342,6 @@ const LogsTable = () => {
             </Paragraph>
           );
         }
-        let other = JSON.parse(record.other);
         let content = renderModelPrice(
           record.prompt_tokens,
           record.completion_tokens,

+ 3 - 3
web/src/components/ModelPricing.js

@@ -146,11 +146,11 @@ const ModelPricing = () => {
       render: (text, record, index) => {
         let content = text;
         if (record.quota_type === 0) {
-          let inputRatioPrice = record.model_ratio * 2.0 * record.group_ratio;
+          // 这里的 *2 是因为 1倍率=0.002刀,请勿删除
+          let inputRatioPrice = record.model_ratio * 2 * record.group_ratio;
           let completionRatioPrice =
             record.model_ratio *
-            record.completion_ratio *
-            2.0 *
+            record.completion_ratio * 2 *
             record.group_ratio;
           content = (
             <>

+ 2 - 1
web/src/helpers/render.js

@@ -149,8 +149,9 @@ export function renderModelPrice(
     if (completionRatio === undefined) {
       completionRatio = 0;
     }
+    // 这里的 *2 是因为 1倍率=0.002刀,请勿删除
     let inputRatioPrice = modelRatio * 2.0 * groupRatio;
-    let completionRatioPrice = modelRatio * completionRatio * 2.0 * groupRatio;
+    let completionRatioPrice = modelRatio * 2.0 * completionRatio * groupRatio;
     let price =
       (inputTokens / 1000000) * inputRatioPrice +
       (completionTokens / 1000000) * completionRatioPrice;

+ 12 - 58
web/src/pages/Channel/EditChannel.js

@@ -37,8 +37,6 @@ const STATUS_CODE_MAPPING_EXAMPLE = {
   400: '500',
 };
 
-const fetchButtonTips = "1. 新建渠道时,请求通过当前浏览器发出;2. 编辑已有渠道,请求通过后端服务器发出"
-
 function type2secretPrompt(type) {
   // inputs.type === 15 ? '按照如下格式输入:APIKey|SecretKey' : (inputs.type === 18 ? '按照如下格式输入:APPID|APISecret|APIKey' : '请输入渠道对应的鉴权密钥')
   switch (type) {
@@ -90,55 +88,6 @@ const EditChannel = (props) => {
   const [basicModels, setBasicModels] = useState([]);
   const [fullModels, setFullModels] = useState([]);
   const [customModel, setCustomModel] = useState('');
-
-  const fetchUpstreamModelList = async (name) => {
-    if (inputs["type"] !== 1) {
-      showError("仅支持 OpenAI 接口格式")
-      return;
-    }
-    const models = inputs["models"] || []
-    let err = false;
-    if (isEdit) {
-      const res = await API.get("/api/channel/fetch_models/" + channelId)
-      if (res.data && res.data?.success) {
-        models.push(...res.data.data)
-      } else {
-        err = true
-      }
-    } else {
-      if (!inputs?.["key"]) {
-        showError("请填写密钥")
-        return;
-      }
-      try {
-        const host = new URL((inputs["base_url"] || "https://api.openai.com"))
-
-        const url = `https://${host.hostname}/v1/models`;
-        const key = inputs["key"];
-        const res = await axios.get(url, {
-          headers: {
-            'Authorization': `Bearer ${key}`
-          }
-        })
-        if (res.data && res.data?.success) {
-          models.push(...es.data.data.map((model) => model.id))
-        } else {
-          err = true
-        }
-      }
-      catch (error) {
-        err = true
-      }
-    }
-    if (!err) {
-      handleInputChange(name, Array.from(new Set(models)));
-      showSuccess("获取模型列表成功");
-    } else {
-      showError('获取模型列表失败');
-    }
-  }
-
-
   const handleInputChange = (name, value) => {
     setInputs((inputs) => ({ ...inputs, [name]: value }));
     if (name === 'type') {
@@ -284,7 +233,7 @@ const EditChannel = (props) => {
     fetchModels().then();
     fetchGroups().then();
     if (isEdit) {
-      loadChannel().then(() => { });
+      loadChannel().then(() => {});
     } else {
       setInputs(originInputs);
       let localModels = getChannelModels(inputs.type);
@@ -354,17 +303,18 @@ const EditChannel = (props) => {
   const addCustomModels = () => {
     if (customModel.trim() === '') return;
     // 使用逗号分隔字符串,然后去除每个模型名称前后的空格
-    const modelArray = customModel.split(',').map(model => model.trim());
+    const modelArray = customModel.split(',').map((model) => model.trim());
 
     let localModels = [...inputs.models];
     let localModelOptions = [...modelOptions];
     let hasError = false;
 
-    modelArray.forEach(model => {
+    modelArray.forEach((model) => {
       // 检查模型是否已存在,且模型名称非空
       if (model && !localModels.includes(model)) {
         localModels.push(model); // 添加到模型列表
-        localModelOptions.push({ // 添加到下拉选项
+        localModelOptions.push({
+          // 添加到下拉选项
           key: model,
           text: model,
           value: model,
@@ -486,11 +436,15 @@ const EditChannel = (props) => {
           {inputs.type === 8 && (
             <>
               <div style={{ marginTop: 10 }}>
-                <Typography.Text strong>Base URL:</Typography.Text>
+                <Typography.Text strong>
+                  完整的 Base URL,支持变量{'{model}'}:
+                </Typography.Text>
               </div>
               <Input
                 name='base_url'
-                placeholder={'请输入自定义渠道的 Base URL'}
+                placeholder={
+                  '请输入完整的URL,例如:https://api.openai.com/v1/chat/completions'
+                }
                 onChange={(value) => {
                   handleInputChange('base_url', value);
                 }}
@@ -723,7 +677,7 @@ const EditChannel = (props) => {
                 onChange={() => {
                   setAutoBan(!autoBan);
                 }}
-              // onChange={handleInputChange}
+                // onChange={handleInputChange}
               />
               <Typography.Text strong>
                 是否自动禁用(仅当自动禁用开启时有效),关闭后不会自动禁用该渠道: