Compare commits

...

6 Commits

Author SHA1 Message Date
LaskyJ
e4264d3301 Update test.yml 2025-04-17 23:55:44 -05:00
Davidlasky
3aa2c68ce6 add gemini-2.5-flash-preview-04-17 and replace gemini-2.5-pro-exp-03-25 with preview one 2025-04-17 23:47:45 -05:00
Davidlasky
d2e484f274 change to npm 2025-04-17 20:59:49 -05:00
Davidlasky
5d4e393302 add o4-mini-high and remove all unused models 2025-04-17 20:03:31 -05:00
Davidlasky
55b6f236db switch from yarn to npm 2025-04-17 20:01:50 -05:00
Davidlasky
ef7674bcf4 only used desired model providers 2025-04-17 19:56:57 -05:00
8 changed files with 77 additions and 8230 deletions

View File

@@ -21,19 +21,19 @@ jobs:
- name: Set up Node.js
uses: actions/setup-node@v3
with:
node-version: 18
cache: "yarn"
node-version: 23
cache: 'npm'
- name: Cache node_modules
uses: actions/cache@v4
with:
path: node_modules
key: ${{ runner.os }}-node_modules-${{ hashFiles('**/yarn.lock') }}
key: ${{ runner.os }}-node_modules-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node_modules-
- name: Install dependencies
run: yarn install
run: npm install
- name: Run Jest tests
run: yarn test:ci
run: npm run test:ci

View File

@@ -1,4 +1,4 @@
FROM node:18-alpine AS base
FROM node:23-alpine AS base
FROM base AS deps
@@ -6,24 +6,20 @@ RUN apk add --no-cache libc6-compat
WORKDIR /app
COPY package.json yarn.lock ./
COPY package.json package-lock.json ./
RUN yarn config set registry 'https://registry.npmmirror.com/'
RUN yarn install
RUN npm config set registry 'https://registry.npmmirror.com/'
RUN npm install
FROM base AS builder
RUN apk update && apk add --no-cache git
ENV OPENAI_API_KEY=""
ENV GOOGLE_API_KEY=""
ENV CODE=""
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN yarn build
RUN npm run build
FROM base AS runner
WORKDIR /app
@@ -33,6 +29,8 @@ RUN apk add proxychains-ng
ENV PROXY_URL=""
ENV OPENAI_API_KEY=""
ENV GOOGLE_API_KEY=""
ENV ANTHROPIC_API_KEY=""
ENV VISION_MODELS=""
ENV CODE=""
ENV ENABLE_MCP=""

View File

@@ -200,7 +200,8 @@ export class ChatGPTApi implements LLMApi {
const isDalle3 = _isDalle3(options.config.model);
const isO1 = options.config.model.startsWith("o1");
const isO3 = options.config.model.startsWith("o3");
const isO1OrO3 = isO1 || isO3;
const isO4 = options.config.model.startsWith("o4");
const isO1OrO3orO4 = isO1 || isO3 || isO4;
if (isDalle3) {
const prompt = getMessageTextContent(
options.messages.slice(-1)?.pop() as any,
@@ -222,7 +223,7 @@ export class ChatGPTApi implements LLMApi {
const content = visionModel
? await preProcessImageContent(v.content)
: getMessageTextContent(v);
if (!(isO1OrO3 && v.role === "system"))
if (!(isO1OrO3orO4 && v.role === "system"))
messages.push({ role: v.role, content });
}
@@ -231,28 +232,28 @@ export class ChatGPTApi implements LLMApi {
messages,
stream: options.config.stream,
model: modelConfig.model,
temperature: !isO1OrO3 ? modelConfig.temperature : 1,
presence_penalty: !isO1OrO3 ? modelConfig.presence_penalty : 0,
frequency_penalty: !isO1OrO3 ? modelConfig.frequency_penalty : 0,
top_p: !isO1OrO3 ? modelConfig.top_p : 1,
temperature: !isO1OrO3orO4 ? modelConfig.temperature : 1,
presence_penalty: !isO1OrO3orO4 ? modelConfig.presence_penalty : 0,
frequency_penalty: !isO1OrO3orO4 ? modelConfig.frequency_penalty : 0,
top_p: !isO1OrO3orO4 ? modelConfig.top_p : 1,
// max_tokens: Math.max(modelConfig.max_tokens, 1024),
// Please do not ask me why not send max_tokens, no reason, this param is just shit, I dont want to explain anymore.
};
// O1 使用 max_completion_tokens 控制token数 (https://platform.openai.com/docs/guides/reasoning#controlling-costs)
if (isO1OrO3) {
requestPayload["max_completion_tokens"] = modelConfig.max_tokens;
if (isO1OrO3orO4) {
requestPayload["max_completion_tokens"] = 20000;
}
if (isO3) {
if (isO4) {
requestPayload["reasoning_effort"] = "high";
// make o3-mini defaults to high reasoning effort
// make o4-mini defaults to high reasoning effort
}
// add max_tokens to vision model
if (visionModel) {
if (isO1) {
requestPayload["max_completion_tokens"] = modelConfig.max_tokens;
if (isO1OrO3orO4) {
requestPayload["max_completion_tokens"] = 20000;
} else {
requestPayload["max_tokens"] = Math.max(modelConfig.max_tokens, 4000);
}

View File

@@ -416,12 +416,13 @@ export const KnowledgeCutOffDate: Record<string, string> = {
default: "2023-10",
// After improvements,
// it's now easier to add "KnowledgeCutOffDate" instead of stupid hardcoding it, as was done previously.
"gemini-2.5-pro-exp-03-25": "2025-01",
"gemini-2.0-flash": "2024-08",
"gemini-2.5-pro-preview-03-25": "2025-01",
"gemini-2.5-flash-preview-04-17": "2025-01",
"claude-3-7-sonnet-latest": "2024-10",
"claude-3-5-haiku-latest": "2024-10",
"gpt-4.1": "2024-06",
"gpt-4.1-mini": "2024-06",
"o4-mini": "2024-06",
"deepseek-chat": "2024-07",
"deepseek-coder": "2024-07",
};
@@ -442,141 +443,48 @@ export const DEFAULT_TTS_VOICES = [
export const VISION_MODEL_REGEXES = [
/vision/,
/gpt-4\.1/,
/gpt-4/,
/claude-3/,
/gemini-1\.5/,
/gemini-exp/,
/gemini-2\.0/,
/gemini-2\.5-pro/,
/learnlm/,
/qwen-vl/,
/qwen2-vl/,
/gpt-4-turbo(?!.*preview)/, // Matches "gpt-4-turbo" but not "gpt-4-turbo-preview"
/gemini/,
/^dall-e-3$/, // Matches exactly "dall-e-3"
/glm-4v/,
/vl/i,
/o1/,
/o3/,
/o4/,
];
export const EXCLUDE_VISION_MODEL_REGEXES = [/claude-3-5-haiku-20241022/];
const openaiModels = ["dall-e-3", "o1", "o3-mini", "gpt-4.1", "gpt-4.1-mini"];
const openaiModels = ["dall-e-3", "o1", "o4-mini", "gpt-4.1", "gpt-4.1-mini"];
const googleModels = [
"gemini-2.0-flash",
"gemini-2.0-flash-lite",
"gemini-2.5-pro-exp-03-25",
"gemini-2.5-flash-preview-04-17",
"gemini-2.5-pro-preview-03-25",
];
const anthropicModels = [
"claude-3-opus-latest",
"claude-3-5-haiku-latest",
"claude-3-5-sonnet-latest",
"claude-3-7-sonnet-latest",
];
const baiduModels = [
"ernie-4.0-turbo-8k",
"ernie-4.0-8k",
"ernie-4.0-8k-preview",
"ernie-4.0-8k-preview-0518",
"ernie-4.0-8k-latest",
"ernie-3.5-8k",
"ernie-3.5-8k-0205",
"ernie-speed-128k",
"ernie-speed-8k",
"ernie-lite-8k",
"ernie-tiny-8k",
];
const baiduModels = ["ernie-4.0-turbo-8k"];
const bytedanceModels = [
"Doubao-lite-4k",
"Doubao-lite-32k",
"Doubao-lite-128k",
"Doubao-pro-4k",
"Doubao-pro-32k",
"Doubao-pro-128k",
];
const bytedanceModels = ["Doubao-pro-128k"];
const alibabaModes = [
"qwen-turbo",
"qwen-plus",
"qwen-max",
"qwen-max-0428",
"qwen-max-0403",
"qwen-max-0107",
"qwen-max-longcontext",
"qwen-omni-turbo",
"qwen-vl-plus",
"qwen-vl-max",
];
const alibabaModes = ["qwen-vl-max"];
const tencentModels = [
"hunyuan-pro",
"hunyuan-standard",
"hunyuan-lite",
"hunyuan-role",
"hunyuan-functioncall",
"hunyuan-code",
"hunyuan-vision",
];
const tencentModels = ["hunyuan-code"];
const moonshotModes = ["moonshot-v1-8k", "moonshot-v1-32k", "moonshot-v1-128k"];
const moonshotModes = ["moonshot-v1-128k"];
const iflytekModels = [
"general",
"generalv3",
"pro-128k",
"generalv3.5",
"4.0Ultra",
];
const iflytekModels = ["4.0Ultra"];
const deepseekModels = ["deepseek-chat", "deepseek-coder", "deepseek-reasoner"];
const xAIModes = [
"grok-beta",
"grok-2",
"grok-2-1212",
"grok-2-latest",
"grok-vision-beta",
"grok-2-vision-1212",
"grok-2-vision",
"grok-2-vision-latest",
];
const xAIModes = ["grok-2-vision-latest"];
const chatglmModels = [
"glm-4-plus",
"glm-4-0520",
"glm-4",
"glm-4-air",
"glm-4-airx",
"glm-4-long",
"glm-4-flashx",
"glm-4-flash",
"glm-4v-plus",
"glm-4v",
"glm-4v-flash", // free
"cogview-3-plus",
"cogview-3",
"cogview-3-flash", // free
// 目前无法适配轮询任务
// "cogvideox",
// "cogvideox-flash", // free
];
const chatglmModels = ["glm-4-plus"];
const siliconflowModels = [
"Qwen/Qwen2.5-7B-Instruct",
"Qwen/Qwen2.5-72B-Instruct",
"deepseek-ai/DeepSeek-R1",
"deepseek-ai/DeepSeek-R1-Distill-Llama-70B",
"deepseek-ai/DeepSeek-R1-Distill-Llama-8B",
"deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B",
"deepseek-ai/DeepSeek-R1-Distill-Qwen-14B",
"deepseek-ai/DeepSeek-R1-Distill-Qwen-32B",
"deepseek-ai/DeepSeek-R1-Distill-Qwen-7B",
"deepseek-ai/DeepSeek-V3",
"meta-llama/Llama-3.3-70B-Instruct",
"THUDM/glm-4-9b-chat",
"Pro/deepseek-ai/DeepSeek-R1",
"Pro/deepseek-ai/DeepSeek-V3",
];
@@ -596,7 +504,7 @@ export const DEFAULT_MODELS = [
})),
...openaiModels.map((name) => ({
name,
available: true,
available: false,
sorted: seq++,
provider: {
id: "azure",
@@ -629,7 +537,7 @@ export const DEFAULT_MODELS = [
})),
...baiduModels.map((name) => ({
name,
available: true,
available: false,
sorted: seq++,
provider: {
id: "baidu",
@@ -640,7 +548,7 @@ export const DEFAULT_MODELS = [
})),
...bytedanceModels.map((name) => ({
name,
available: true,
available: false,
sorted: seq++,
provider: {
id: "bytedance",
@@ -651,7 +559,7 @@ export const DEFAULT_MODELS = [
})),
...alibabaModes.map((name) => ({
name,
available: true,
available: false,
sorted: seq++,
provider: {
id: "alibaba",
@@ -662,7 +570,7 @@ export const DEFAULT_MODELS = [
})),
...tencentModels.map((name) => ({
name,
available: true,
available: false,
sorted: seq++,
provider: {
id: "tencent",
@@ -673,7 +581,7 @@ export const DEFAULT_MODELS = [
})),
...moonshotModes.map((name) => ({
name,
available: true,
available: false,
sorted: seq++,
provider: {
id: "moonshot",
@@ -684,7 +592,7 @@ export const DEFAULT_MODELS = [
})),
...iflytekModels.map((name) => ({
name,
available: true,
available: false,
sorted: seq++,
provider: {
id: "iflytek",
@@ -695,7 +603,7 @@ export const DEFAULT_MODELS = [
})),
...xAIModes.map((name) => ({
name,
available: true,
available: false,
sorted: seq++,
provider: {
id: "xai",
@@ -706,7 +614,7 @@ export const DEFAULT_MODELS = [
})),
...chatglmModels.map((name) => ({
name,
available: true,
available: false,
sorted: seq++,
provider: {
id: "chatglm",
@@ -717,7 +625,7 @@ export const DEFAULT_MODELS = [
})),
...deepseekModels.map((name) => ({
name,
available: true,
available: false,
sorted: seq++,
provider: {
id: "deepseek",
@@ -728,7 +636,7 @@ export const DEFAULT_MODELS = [
})),
...siliconflowModels.map((name) => ({
name,
available: true,
available: false,
sorted: seq++,
provider: {
id: "siliconflow",

View File

@@ -303,6 +303,7 @@ export function getTimeoutMSByModel(model: string) {
model.startsWith("dalle") ||
model.startsWith("o1") ||
model.startsWith("o3") ||
model.startsWith("o4") ||
model.includes("deepseek-r") ||
model.includes("-thinking") ||
model.includes("pro")

View File

@@ -1,16 +1,34 @@
import { useMemo } from "react";
import { useAccessStore, useAppConfig } from "../store";
import { collectModelsWithDefaultModel } from "./model";
import { ServiceProvider } from "../constant";
// Define the allowed providers
const allowedProviders = [
ServiceProvider.OpenAI,
ServiceProvider.Google,
ServiceProvider.Anthropic,
];
export function useAllModels() {
const accessStore = useAccessStore();
const configStore = useAppConfig();
const models = useMemo(() => {
return collectModelsWithDefaultModel(
// First, collect all models including custom ones
const allModels = collectModelsWithDefaultModel(
configStore.models,
[configStore.customModels, accessStore.customModels].join(","),
accessStore.defaultModel,
);
// Then, filter the collected models based on the allowed providers
return allModels.filter(
(model) =>
model.provider &&
allowedProviders.includes(
model.provider.providerName as ServiceProvider,
),
);
}, [
accessStore.customModels,
accessStore.defaultModel,

View File

@@ -17,15 +17,10 @@ describe("isVisionModel", () => {
const visionModels = [
"gpt-4.1",
"claude-3-opus",
"gemini-1.5-pro",
"gemini-2.0",
"gemini-2.5-pro",
"gemini-exp-vision",
"learnlm-vision",
"qwen-vl-max",
"qwen2-vl-max",
"gpt-4-turbo",
"o4-mini",
"dall-e-3",
"o1",
];
visionModels.forEach((model) => {
@@ -38,12 +33,7 @@ describe("isVisionModel", () => {
});
test("should not identify non-vision models", () => {
const nonVisionModels = [
"gpt-3.5-turbo",
"gpt-4-turbo-preview",
"claude-2",
"regular-model",
];
const nonVisionModels = ["gpt-3.5-turbo", "claude-2", "regular-model"];
nonVisionModels.forEach((model) => {
expect(isVisionModel(model)).toBe(false);

8069
yarn.lock

File diff suppressed because it is too large Load Diff