This commit is contained in:
GH Action - Upstream Sync 2024-07-13 00:12:32 +00:00
commit ca8e84702b
14 changed files with 153 additions and 97 deletions

View File

@ -11,6 +11,7 @@ import { prettyObject } from "@/app/utils/format";
import { NextRequest, NextResponse } from "next/server"; import { NextRequest, NextResponse } from "next/server";
import { auth } from "../../auth"; import { auth } from "../../auth";
import { isModelAvailableInServer } from "@/app/utils/model"; import { isModelAvailableInServer } from "@/app/utils/model";
import { cloudflareAIGatewayUrl } from "@/app/utils/cloudflare";
const ALLOWD_PATH = new Set([Anthropic.ChatPath, Anthropic.ChatPath1]); const ALLOWD_PATH = new Set([Anthropic.ChatPath, Anthropic.ChatPath1]);
@ -114,7 +115,8 @@ async function request(req: NextRequest) {
10 * 60 * 1000, 10 * 60 * 1000,
); );
const fetchUrl = `${baseUrl}${path}`; // try rebuild url, when using cloudflare ai gateway in server
const fetchUrl = cloudflareAIGatewayUrl(`${baseUrl}${path}`);
const fetchOptions: RequestInit = { const fetchOptions: RequestInit = {
headers: { headers: {
@ -164,17 +166,17 @@ async function request(req: NextRequest) {
console.error(`[Anthropic] filter`, e); console.error(`[Anthropic] filter`, e);
} }
} }
console.log("[Anthropic request]", fetchOptions.headers, req.method); // console.log("[Anthropic request]", fetchOptions.headers, req.method);
try { try {
const res = await fetch(fetchUrl, fetchOptions); const res = await fetch(fetchUrl, fetchOptions);
console.log( // console.log(
"[Anthropic response]", // "[Anthropic response]",
res.status, // res.status,
" ", // " ",
res.headers, // res.headers,
res.url, // res.url,
); // );
// to prevent browser prompt for credentials // to prevent browser prompt for credentials
const newHeaders = new Headers(res.headers); const newHeaders = new Headers(res.headers);
newHeaders.delete("www-authenticate"); newHeaders.delete("www-authenticate");

View File

@ -7,6 +7,7 @@ import {
ServiceProvider, ServiceProvider,
} from "../constant"; } from "../constant";
import { isModelAvailableInServer } from "../utils/model"; import { isModelAvailableInServer } from "../utils/model";
import { cloudflareAIGatewayUrl } from "../utils/cloudflare";
const serverConfig = getServerSideConfig(); const serverConfig = getServerSideConfig();
@ -37,7 +38,7 @@ export async function requestOpenai(req: NextRequest) {
); );
let baseUrl = let baseUrl =
serverConfig.azureUrl || serverConfig.baseUrl || OPENAI_BASE_URL; (isAzure ? serverConfig.azureUrl : serverConfig.baseUrl) || OPENAI_BASE_URL;
if (!baseUrl.startsWith("http")) { if (!baseUrl.startsWith("http")) {
baseUrl = `https://${baseUrl}`; baseUrl = `https://${baseUrl}`;
@ -95,7 +96,8 @@ export async function requestOpenai(req: NextRequest) {
} }
} }
const fetchUrl = `${baseUrl}/${path}`; const fetchUrl = cloudflareAIGatewayUrl(`${baseUrl}/${path}`);
console.log("fetchUrl", fetchUrl);
const fetchOptions: RequestInit = { const fetchOptions: RequestInit = {
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",

View File

@ -1,5 +1,5 @@
import { ACCESS_CODE_PREFIX, Anthropic, ApiPath } from "@/app/constant"; import { ACCESS_CODE_PREFIX, Anthropic, ApiPath } from "@/app/constant";
import { ChatOptions, getHeaders, LLMApi, MultimodalContent, } from "../api"; import { ChatOptions, getHeaders, LLMApi, MultimodalContent } from "../api";
import { useAccessStore, useAppConfig, useChatStore } from "@/app/store"; import { useAccessStore, useAppConfig, useChatStore } from "@/app/store";
import { getClientConfig } from "@/app/config/client"; import { getClientConfig } from "@/app/config/client";
import { DEFAULT_API_HOST } from "@/app/constant"; import { DEFAULT_API_HOST } from "@/app/constant";
@ -12,6 +12,7 @@ import {
import Locale from "../../locales"; import Locale from "../../locales";
import { prettyObject } from "@/app/utils/format"; import { prettyObject } from "@/app/utils/format";
import { getMessageTextContent, isVisionModel } from "@/app/utils"; import { getMessageTextContent, isVisionModel } from "@/app/utils";
import { cloudflareAIGatewayUrl } from "@/app/utils/cloudflare";
export type MultiBlockContent = { export type MultiBlockContent = {
type: "image" | "text"; type: "image" | "text";
@ -190,7 +191,7 @@ export class ClaudeApi implements LLMApi {
body: JSON.stringify(requestBody), body: JSON.stringify(requestBody),
signal: controller.signal, signal: controller.signal,
headers: { headers: {
...getHeaders(), // get common headers ...getHeaders(), // get common headers
"anthropic-version": accessStore.anthropicApiVersion, "anthropic-version": accessStore.anthropicApiVersion,
// do not send `anthropicApiKey` in browser!!! // do not send `anthropicApiKey` in browser!!!
// Authorization: getAuthKey(accessStore.anthropicApiKey), // Authorization: getAuthKey(accessStore.anthropicApiKey),
@ -375,7 +376,8 @@ export class ClaudeApi implements LLMApi {
baseUrl = trimEnd(baseUrl, "/"); baseUrl = trimEnd(baseUrl, "/");
return `${baseUrl}/${path}`; // try rebuild url, when using cloudflare ai gateway in client
return cloudflareAIGatewayUrl(`${baseUrl}/${path}`);
} }
} }

View File

@ -122,16 +122,13 @@ export class GeminiProApi implements LLMApi {
const controller = new AbortController(); const controller = new AbortController();
options.onController?.(controller); options.onController?.(controller);
try { try {
// let baseUrl = accessStore.googleUrl; if (!baseUrl && isApp) {
baseUrl = DEFAULT_API_HOST + "/api/proxy/google/";
if (!baseUrl) {
baseUrl = isApp
? DEFAULT_API_HOST +
"/api/proxy/google/" +
Google.ChatPath(modelConfig.model)
: this.path(Google.ChatPath(modelConfig.model));
} }
baseUrl = `${baseUrl}/${Google.ChatPath(modelConfig.model)}`.replaceAll(
"//",
"/",
);
if (isApp) { if (isApp) {
baseUrl += `?key=${accessStore.googleApiKey}`; baseUrl += `?key=${accessStore.googleApiKey}`;
} }

View File

@ -11,6 +11,7 @@ import {
} from "@/app/constant"; } from "@/app/constant";
import { useAccessStore, useAppConfig, useChatStore } from "@/app/store"; import { useAccessStore, useAppConfig, useChatStore } from "@/app/store";
import { collectModelsWithDefaultModel } from "@/app/utils/model"; import { collectModelsWithDefaultModel } from "@/app/utils/model";
import { cloudflareAIGatewayUrl } from "@/app/utils/cloudflare";
import { import {
ChatOptions, ChatOptions,
@ -94,7 +95,8 @@ export class ChatGPTApi implements LLMApi {
console.log("[Proxy Endpoint] ", baseUrl, path); console.log("[Proxy Endpoint] ", baseUrl, path);
return [baseUrl, path].join("/"); // try rebuild url, when using cloudflare ai gateway in client
return cloudflareAIGatewayUrl([baseUrl, path].join("/"));
} }
extractMessage(res: any) { extractMessage(res: any) {

View File

@ -1195,8 +1195,7 @@ export function Settings() {
<ListItem <ListItem
title={Locale.Settings.Access.Baidu.Endpoint.Title} title={Locale.Settings.Access.Baidu.Endpoint.Title}
subTitle={ subTitle={
Locale.Settings.Access.Anthropic.Endpoint.SubTitle + Locale.Settings.Access.Baidu.Endpoint.SubTitle
Baidu.ExampleEndpoint
} }
> >
<input <input

View File

@ -35,6 +35,7 @@ export enum ApiPath {
Azure = "/api/azure", Azure = "/api/azure",
OpenAI = "/api/openai", OpenAI = "/api/openai",
Anthropic = "/api/anthropic", Anthropic = "/api/anthropic",
Google = "/api/google",
Baidu = "/api/baidu", Baidu = "/api/baidu",
ByteDance = "/api/bytedance", ByteDance = "/api/bytedance",
Alibaba = "/api/alibaba", Alibaba = "/api/alibaba",

View File

@ -349,18 +349,18 @@ const cn = {
}, },
Baidu: { Baidu: {
ApiKey: { ApiKey: {
Title: "接口密钥", Title: "API Key",
SubTitle: "使用自定义 Baidu API Key", SubTitle: "使用自定义 Baidu API Key",
Placeholder: "Baidu API Key", Placeholder: "Baidu API Key",
}, },
SecretKey: { SecretKey: {
Title: "接口密钥", Title: "Secret Key",
SubTitle: "使用自定义 Baidu Secret Key", SubTitle: "使用自定义 Baidu Secret Key",
Placeholder: "Baidu Secret Key", Placeholder: "Baidu Secret Key",
}, },
Endpoint: { Endpoint: {
Title: "接口地址", Title: "接口地址",
SubTitle: "样例:", SubTitle: "不支持自定义前往.env配置",
}, },
}, },
ByteDance: { ByteDance: {

View File

@ -326,7 +326,7 @@ const en: LocaleType = {
Endpoint: { Endpoint: {
Title: "Endpoint Address", Title: "Endpoint Address",
SubTitle: "Example:", SubTitle: "Example: ",
}, },
ApiVerion: { ApiVerion: {
@ -347,7 +347,7 @@ const en: LocaleType = {
}, },
Endpoint: { Endpoint: {
Title: "Endpoint Address", Title: "Endpoint Address",
SubTitle: "Example:", SubTitle: "not supported, configure in .env",
}, },
}, },
ByteDance: { ByteDance: {
@ -358,7 +358,7 @@ const en: LocaleType = {
}, },
Endpoint: { Endpoint: {
Title: "Endpoint Address", Title: "Endpoint Address",
SubTitle: "Example:", SubTitle: "Example: ",
}, },
}, },
Alibaba: { Alibaba: {
@ -369,7 +369,7 @@ const en: LocaleType = {
}, },
Endpoint: { Endpoint: {
Title: "Endpoint Address", Title: "Endpoint Address",
SubTitle: "Example:", SubTitle: "Example: ",
}, },
}, },
CustomModel: { CustomModel: {
@ -385,7 +385,7 @@ const en: LocaleType = {
Endpoint: { Endpoint: {
Title: "Endpoint Address", Title: "Endpoint Address",
SubTitle: "Example:", SubTitle: "Example: ",
}, },
ApiVersion: { ApiVersion: {

View File

@ -4,11 +4,11 @@ import { SubmitKey } from "../store/config";
const isApp = !!getClientConfig()?.isApp; const isApp = !!getClientConfig()?.isApp;
const tw = { const tw = {
WIP: "功能仍在開發中……", WIP: "功能仍在開發中……",
Error: { Error: {
Unauthorized: isApp Unauthorized: isApp
? "檢測到無效 API Key請前往[設定](/#/settings)頁檢查 API Key 是否設定正確。" ? "偵測到無效的 API Key請前往[設定](/#/settings)頁面檢查 API Key 是否設定正確。"
: "存取密碼不正確或未填寫,請前往[登入](/#/auth)頁輸入正確的存取密碼,或者在[設定](/#/settings)頁填入你自己的 OpenAI API Key。", : "存取密碼不正確或未填寫,請前往[登入](/#/auth)頁輸入正確的存取密碼,或者在[設定](/#/settings)頁填入你自己的 OpenAI API Key。",
}, },
Auth: { Auth: {
@ -159,7 +159,7 @@ const tw = {
}, },
InputTemplate: { InputTemplate: {
Title: "使用者輸入預處理", Title: "使用者輸入預處理",
SubTitle: "使用者最新的一訊息會填充到此範本", SubTitle: "使用者最新的一訊息會填充到此範本",
}, },
Update: { Update: {
@ -194,19 +194,19 @@ const tw = {
}, },
SyncType: { SyncType: {
Title: "同步類型", Title: "同步類型",
SubTitle: "選擇喜愛的同步伺服器", SubTitle: "選擇偏好的同步伺服器",
}, },
Proxy: { Proxy: {
Title: "啟用代理", Title: "啟用代理伺服器",
SubTitle: "在瀏覽器中同步時,必須啟用代理以避免跨域限制", SubTitle: "在瀏覽器中同步時,啟用代理伺服器以避免跨域限制",
}, },
ProxyUrl: { ProxyUrl: {
Title: "代理地址", Title: "代理伺服器位置",
SubTitle: "僅適用於本專案自帶的跨域代理", SubTitle: "僅適用於本專案內建的跨域代理",
}, },
WebDav: { WebDav: {
Endpoint: "WebDAV 地址", Endpoint: "WebDAV 位置",
UserName: "使用者名稱", UserName: "使用者名稱",
Password: "密碼", Password: "密碼",
}, },
@ -218,9 +218,9 @@ const tw = {
}, },
}, },
LocalState: "本資料", LocalState: "本資料",
Overview: (overview: any) => { Overview: (overview: any) => {
return `${overview.chat} 次對話,${overview.message} 訊息,${overview.prompt} 條提示詞,${overview.mask} 個角色範本`; return `${overview.chat} 次對話,${overview.message} 訊息,${overview.prompt} 條提示詞,${overview.mask} 個角色範本`;
}, },
ImportFailed: "匯入失敗", ImportFailed: "匯入失敗",
}, },
@ -239,13 +239,13 @@ const tw = {
Title: "停用提示詞自動補齊", Title: "停用提示詞自動補齊",
SubTitle: "在輸入框開頭輸入 / 即可觸發自動補齊", SubTitle: "在輸入框開頭輸入 / 即可觸發自動補齊",
}, },
List: "自定義提示詞列表", List: "自提示詞列表",
ListCount: (builtin: number, custom: number) => ListCount: (builtin: number, custom: number) =>
`內建 ${builtin} 條,使用者定義 ${custom}`, `內建 ${builtin} 條,使用者自訂 ${custom}`,
Edit: "編輯", Edit: "編輯",
Modal: { Modal: {
Title: "提示詞列表", Title: "提示詞列表",
Add: "新增一", Add: "新增一",
Search: "搜尋提示詞", Search: "搜尋提示詞",
}, },
EditModal: { EditModal: {
@ -278,40 +278,40 @@ const tw = {
Placeholder: "請輸入存取密碼", Placeholder: "請輸入存取密碼",
}, },
CustomEndpoint: { CustomEndpoint: {
Title: "自定義介面 (Endpoint)", Title: "自訂 API 端點 (Endpoint)",
SubTitle: "是否使用自定義 Azure 或 OpenAI 服務", SubTitle: "是否使用自 Azure 或 OpenAI 服務",
}, },
Provider: { Provider: {
Title: "模型服務商", Title: "模型供應商",
SubTitle: "切換不同的服務商", SubTitle: "切換不同的服務供應商",
}, },
OpenAI: { OpenAI: {
ApiKey: { ApiKey: {
Title: "API Key", Title: "API Key",
SubTitle: "使用自定義 OpenAI Key 繞過密碼存取限制", SubTitle: "使用自 OpenAI Key 繞過密碼存取限制",
Placeholder: "OpenAI API Key", Placeholder: "OpenAI API Key",
}, },
Endpoint: { Endpoint: {
Title: "介面(Endpoint) 地址", Title: "API 端點 (Endpoint) 位址",
SubTitle: "除預設址外,必須包含 http(s)://", SubTitle: "除預設址外,必須包含 http(s)://",
}, },
}, },
Azure: { Azure: {
ApiKey: { ApiKey: {
Title: "介面金鑰", Title: "API 金鑰",
SubTitle: "使用自定義 Azure Key 繞過密碼存取限制", SubTitle: "使用自 Azure Key 繞過密碼存取限制",
Placeholder: "Azure API Key", Placeholder: "Azure API Key",
}, },
Endpoint: { Endpoint: {
Title: "介面(Endpoint) 地址", Title: "API 端點 (Endpoint) 位址",
SubTitle: "例:", SubTitle: "例:",
}, },
ApiVerion: { ApiVerion: {
Title: "介面版本 (azure api version)", Title: "API 版本 (azure api version)",
SubTitle: "選擇指定的部分版本", SubTitle: "指定一個特定的 API 版本",
}, },
}, },
Anthropic: { Anthropic: {
@ -322,13 +322,13 @@ const tw = {
}, },
Endpoint: { Endpoint: {
Title: "終端地址", Title: "端點位址",
SubTitle: "範例:", SubTitle: "範例:",
}, },
ApiVerion: { ApiVerion: {
Title: "API 版本 (claude api version)", Title: "API 版本 (claude api version)",
SubTitle: "選擇一個特定的 API 版本輸入", SubTitle: "指定一個特定的 API 版本",
}, },
}, },
Google: { Google: {
@ -339,7 +339,7 @@ const tw = {
}, },
Endpoint: { Endpoint: {
Title: "終端地址", Title: "端點位址",
SubTitle: "範例:", SubTitle: "範例:",
}, },
@ -349,8 +349,8 @@ const tw = {
}, },
}, },
CustomModel: { CustomModel: {
Title: "自定義模型名", Title: "自訂模型名稱",
SubTitle: "增加自定義模型可選項,使用英文逗號隔開", SubTitle: "增加自訂模型可選擇項目,使用英文逗號隔開",
}, },
}, },
@ -400,7 +400,7 @@ const tw = {
Context: { Context: {
Toast: (x: any) => `已設定 ${x} 條前置上下文`, Toast: (x: any) => `已設定 ${x} 條前置上下文`,
Edit: "前置上下文和歷史記憶", Edit: "前置上下文和歷史記憶",
Add: "新增一", Add: "新增一",
Clear: "上下文已清除", Clear: "上下文已清除",
Revert: "恢復上下文", Revert: "恢復上下文",
}, },
@ -425,16 +425,16 @@ const tw = {
EditModal: { EditModal: {
Title: (readonly: boolean) => Title: (readonly: boolean) =>
`編輯預設角色範本 ${readonly ? "(唯讀)" : ""}`, `編輯預設角色範本 ${readonly ? "(唯讀)" : ""}`,
Download: "下載預設", Download: "下載預設",
Clone: "複製預設", Clone: "以此預設值建立副本",
}, },
Config: { Config: {
Avatar: "角色頭像", Avatar: "角色頭像",
Name: "角色名稱", Name: "角色名稱",
Sync: { Sync: {
Title: "使用全域設定", Title: "使用全域設定",
SubTitle: "目前對話是否使用全域模型設定", SubTitle: "目前對話是否使用全域模型設定",
Confirm: "目前對話的自定義設定將會被自動覆蓋,確認啟用全域性設定?", Confirm: "目前對話的自訂設定將會被自動覆蓋,確認啟用全域設定?",
}, },
HideContext: { HideContext: {
Title: "隱藏預設對話", Title: "隱藏預設對話",
@ -450,15 +450,15 @@ const tw = {
NewChat: { NewChat: {
Return: "返回", Return: "返回",
Skip: "跳過", Skip: "跳過",
NotShow: "不再呈現", NotShow: "不再顯示",
ConfirmNoShow: "確認停用?停用後可以隨時在設定中重新啟用。", ConfirmNoShow: "確認停用?停用後可以隨時在設定中重新啟用。",
Title: "挑選一個角色範本", Title: "挑選一個角色範本",
SubTitle: "現在開始,與角色範本背後的靈魂思維碰撞", SubTitle: "現在開始,與角色範本背後的靈魂思維碰撞",
More: "搜尋更多", More: "搜尋更多",
}, },
URLCommand: { URLCommand: {
Code: "測到連結中已經包含存取密碼,是否自動填入?", Code: "測到連結中已經包含存取密碼,是否自動填入?",
Settings: "測到連結中包含了預設設定,是否自動填入?", Settings: "測到連結中包含了預設設定,是否自動填入?",
}, },
UI: { UI: {
Confirm: "確認", Confirm: "確認",

View File

@ -12,15 +12,33 @@ import { DEFAULT_CONFIG } from "./config";
let fetchState = 0; // 0 not fetch, 1 fetching, 2 done let fetchState = 0; // 0 not fetch, 1 fetching, 2 done
const DEFAULT_OPENAI_URL = const isApp = getClientConfig()?.buildMode === "export";
getClientConfig()?.buildMode === "export"
? DEFAULT_API_HOST + "/api/proxy/openai"
: ApiPath.OpenAI;
const DEFAULT_AZURE_URL = const DEFAULT_OPENAI_URL = isApp
getClientConfig()?.buildMode === "export" ? DEFAULT_API_HOST + "/api/proxy/openai"
? DEFAULT_API_HOST + "/api/proxy/azure/{resource_name}" : ApiPath.OpenAI;
: ApiPath.Azure;
const DEFAULT_GOOGLE_URL = isApp
? DEFAULT_API_HOST + "/api/proxy/google"
: ApiPath.Google;
const DEFAULT_ANTHROPIC_URL = isApp
? DEFAULT_API_HOST + "/api/proxy/anthropic"
: ApiPath.Anthropic;
const DEFAULT_BAIDU_URL = isApp
? DEFAULT_API_HOST + "/api/proxy/baidu"
: ApiPath.Baidu;
const DEFAULT_BYTEDANCE_URL = isApp
? DEFAULT_API_HOST + "/api/proxy/bytedance"
: ApiPath.ByteDance;
const DEFAULT_ALIBABA_URL = isApp
? DEFAULT_API_HOST + "/api/proxy/alibaba"
: ApiPath.Alibaba;
console.log("DEFAULT_ANTHROPIC_URL", DEFAULT_ANTHROPIC_URL);
const DEFAULT_ACCESS_STATE = { const DEFAULT_ACCESS_STATE = {
accessCode: "", accessCode: "",
@ -33,31 +51,31 @@ const DEFAULT_ACCESS_STATE = {
openaiApiKey: "", openaiApiKey: "",
// azure // azure
azureUrl: DEFAULT_AZURE_URL, azureUrl: "",
azureApiKey: "", azureApiKey: "",
azureApiVersion: "2023-08-01-preview", azureApiVersion: "2023-08-01-preview",
// google ai studio // google ai studio
googleUrl: "", googleUrl: DEFAULT_GOOGLE_URL,
googleApiKey: "", googleApiKey: "",
googleApiVersion: "v1", googleApiVersion: "v1",
// anthropic // anthropic
anthropicUrl: DEFAULT_ANTHROPIC_URL,
anthropicApiKey: "", anthropicApiKey: "",
anthropicApiVersion: "2023-06-01", anthropicApiVersion: "2023-06-01",
anthropicUrl: "",
// baidu // baidu
baiduUrl: "", baiduUrl: DEFAULT_BAIDU_URL,
baiduApiKey: "", baiduApiKey: "",
baiduSecretKey: "", baiduSecretKey: "",
// bytedance // bytedance
bytedanceUrl: DEFAULT_BYTEDANCE_URL,
bytedanceApiKey: "", bytedanceApiKey: "",
bytedanceUrl: "",
// alibaba // alibaba
alibabaUrl: "", alibabaUrl: DEFAULT_ALIBABA_URL,
alibabaApiKey: "", alibabaApiKey: "",
// server config // server config

26
app/utils/cloudflare.ts Normal file
View File

@ -0,0 +1,26 @@
export function cloudflareAIGatewayUrl(fetchUrl: string) {
// rebuild fetchUrl, if using cloudflare ai gateway
// document: https://developers.cloudflare.com/ai-gateway/providers/openai/
const paths = fetchUrl.split("/");
if ("gateway.ai.cloudflare.com" == paths[2]) {
// is cloudflare.com ai gateway
// https://gateway.ai.cloudflare.com/v1/{account_id}/{gateway_id}/azure-openai/{resource_name}/{deployment_name}/chat/completions?api-version=2023-05-15'
if ("azure-openai" == paths[6]) {
// is azure gateway
return paths.slice(0, 8).concat(paths.slice(-3)).join("/"); // rebuild ai gateway azure_url
}
// https://gateway.ai.cloudflare.com/v1/{account_id}/{gateway_id}/openai/chat/completions
if ("openai" == paths[6]) {
// is openai gateway
return paths.slice(0, 7).concat(paths.slice(-2)).join("/"); // rebuild ai gateway openai_url
}
// https://gateway.ai.cloudflare.com/v1/{account_id}/{gateway_id}/anthropic/v1/messages \
if ("anthropic" == paths[6]) {
// is anthropic gateway
return paths.slice(0, 7).concat(paths.slice(-2)).join("/"); // rebuild ai gateway anthropic_url
}
// TODO: Amazon Bedrock, Groq, HuggingFace...
}
return fetchUrl;
}

View File

@ -1,9 +1,9 @@
import { DEFAULT_MODELS } from "../constant"; import { DEFAULT_MODELS } from "../constant";
import { LLMModel } from "../client/api"; import { LLMModel } from "../client/api";
const customProvider = (modelName: string) => ({ const customProvider = (providerName: string) => ({
id: modelName, id: providerName.toLowerCase(),
providerName: "Custom", providerName: providerName,
providerType: "custom", providerType: "custom",
}); });
@ -71,10 +71,17 @@ export function collectModelTable(
} }
// 2. if model not exists, create new model with available value // 2. if model not exists, create new model with available value
if (count === 0) { if (count === 0) {
const provider = customProvider(name); let [customModelName, customProviderName] = name.split("@");
modelTable[`${name}@${provider?.id}`] = { const provider = customProvider(
name, customProviderName || customModelName,
displayName: displayName || name, );
// swap name and displayName for bytedance
if (displayName && provider.providerName == "ByteDance") {
[customModelName, displayName] = [displayName, customModelName];
}
modelTable[`${customModelName}@${provider?.id}`] = {
name: customModelName,
displayName: displayName || customModelName,
available, available,
provider, // Use optional chaining provider, // Use optional chaining
}; };

Binary file not shown.