mirror of
https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web.git
synced 2025-10-20 08:53:44 +08:00
Compare commits
12 Commits
de8f288623
...
dependabot
Author | SHA1 | Date | |
---|---|---|---|
|
f9d3c93633 | ||
|
673f907ea4 | ||
|
fb3af2a08f | ||
|
eb193ac0ff | ||
|
c30ddfbb07 | ||
|
a2f0149786 | ||
|
03d36f96ed | ||
|
705dffc664 | ||
|
02f7e6de98 | ||
|
843dc52efa | ||
|
c261ebc82c | ||
|
f7c747c65f |
@@ -22,12 +22,12 @@ English / [简体中文](./README_CN.md)
|
|||||||
[![MacOS][MacOS-image]][download-url]
|
[![MacOS][MacOS-image]][download-url]
|
||||||
[![Linux][Linux-image]][download-url]
|
[![Linux][Linux-image]][download-url]
|
||||||
|
|
||||||
[NextChatAI](https://nextchat.club?utm_source=readme) / [iOS APP](https://apps.apple.com/us/app/nextchat-ai/id6743085599) / [Web App Demo](https://app.nextchat.dev) / [Desktop App](https://github.com/Yidadaa/ChatGPT-Next-Web/releases) / [Enterprise Edition](#enterprise-edition)
|
[NextChatAI](https://nextchat.club?utm_source=readme) / [iOS APP](https://apps.apple.com/us/app/nextchat-ai/id6743085599) / [Web App Demo](https://app.nextchat.club) / [Desktop App](https://github.com/Yidadaa/ChatGPT-Next-Web/releases) / [Enterprise Edition](#enterprise-edition)
|
||||||
|
|
||||||
|
|
||||||
[saas-url]: https://nextchat.club?utm_source=readme
|
[saas-url]: https://nextchat.club?utm_source=readme
|
||||||
[saas-image]: https://img.shields.io/badge/NextChat-Saas-green?logo=microsoftedge
|
[saas-image]: https://img.shields.io/badge/NextChat-Saas-green?logo=microsoftedge
|
||||||
[web-url]: https://app.nextchat.dev/
|
[web-url]: https://app.nextchat.club/
|
||||||
[download-url]: https://github.com/Yidadaa/ChatGPT-Next-Web/releases
|
[download-url]: https://github.com/Yidadaa/ChatGPT-Next-Web/releases
|
||||||
[Web-image]: https://img.shields.io/badge/Web-PWA-orange?logo=microsoftedge
|
[Web-image]: https://img.shields.io/badge/Web-PWA-orange?logo=microsoftedge
|
||||||
[Windows-image]: https://img.shields.io/badge/-Windows-blue?logo=windows
|
[Windows-image]: https://img.shields.io/badge/-Windows-blue?logo=windows
|
||||||
|
@@ -90,14 +90,6 @@ export async function requestOpenai(req: NextRequest) {
|
|||||||
|
|
||||||
const fetchUrl = cloudflareAIGatewayUrl(`${baseUrl}/${path}`);
|
const fetchUrl = cloudflareAIGatewayUrl(`${baseUrl}/${path}`);
|
||||||
console.log("fetchUrl", fetchUrl);
|
console.log("fetchUrl", fetchUrl);
|
||||||
|
|
||||||
let payload = await req.text();
|
|
||||||
if (baseUrl.includes("openrouter.ai")) {
|
|
||||||
const body = JSON.parse(payload);
|
|
||||||
body["include_reasoning"] = true;
|
|
||||||
payload = JSON.stringify(body);
|
|
||||||
}
|
|
||||||
|
|
||||||
const fetchOptions: RequestInit = {
|
const fetchOptions: RequestInit = {
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
@@ -108,7 +100,7 @@ export async function requestOpenai(req: NextRequest) {
|
|||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
method: req.method,
|
method: req.method,
|
||||||
body: payload,
|
body: req.body,
|
||||||
// to fix #2485: https://stackoverflow.com/questions/55920957/cloudflare-worker-typeerror-one-time-use-body
|
// to fix #2485: https://stackoverflow.com/questions/55920957/cloudflare-worker-typeerror-one-time-use-body
|
||||||
redirect: "manual",
|
redirect: "manual",
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
@@ -119,7 +111,10 @@ export async function requestOpenai(req: NextRequest) {
|
|||||||
// #1815 try to refuse gpt4 request
|
// #1815 try to refuse gpt4 request
|
||||||
if (serverConfig.customModels && req.body) {
|
if (serverConfig.customModels && req.body) {
|
||||||
try {
|
try {
|
||||||
const jsonBody = JSON.parse(payload) as { model?: string };
|
const clonedBody = await req.text();
|
||||||
|
fetchOptions.body = clonedBody;
|
||||||
|
|
||||||
|
const jsonBody = JSON.parse(clonedBody) as { model?: string };
|
||||||
|
|
||||||
// not undefined and is false
|
// not undefined and is false
|
||||||
if (
|
if (
|
||||||
|
@@ -2,10 +2,10 @@
|
|||||||
// azure and openai, using same models. so using same LLMApi.
|
// azure and openai, using same models. so using same LLMApi.
|
||||||
import {
|
import {
|
||||||
ApiPath,
|
ApiPath,
|
||||||
Azure,
|
|
||||||
DEFAULT_MODELS,
|
|
||||||
OPENAI_BASE_URL,
|
OPENAI_BASE_URL,
|
||||||
|
DEFAULT_MODELS,
|
||||||
OpenaiPath,
|
OpenaiPath,
|
||||||
|
Azure,
|
||||||
REQUEST_TIMEOUT_MS,
|
REQUEST_TIMEOUT_MS,
|
||||||
ServiceProvider,
|
ServiceProvider,
|
||||||
} from "@/app/constant";
|
} from "@/app/constant";
|
||||||
@@ -18,13 +18,13 @@ import {
|
|||||||
} from "@/app/store";
|
} from "@/app/store";
|
||||||
import { collectModelsWithDefaultModel } from "@/app/utils/model";
|
import { collectModelsWithDefaultModel } from "@/app/utils/model";
|
||||||
import {
|
import {
|
||||||
base64Image2Blob,
|
|
||||||
preProcessImageContent,
|
preProcessImageContent,
|
||||||
streamWithThink,
|
|
||||||
uploadImage,
|
uploadImage,
|
||||||
|
base64Image2Blob,
|
||||||
|
streamWithThink,
|
||||||
} from "@/app/utils/chat";
|
} from "@/app/utils/chat";
|
||||||
import { cloudflareAIGatewayUrl } from "@/app/utils/cloudflare";
|
import { cloudflareAIGatewayUrl } from "@/app/utils/cloudflare";
|
||||||
import { DalleQuality, DalleStyle, ModelSize } from "@/app/typing";
|
import { ModelSize, DalleQuality, DalleStyle } from "@/app/typing";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ChatOptions,
|
ChatOptions,
|
||||||
@@ -39,9 +39,9 @@ import Locale from "../../locales";
|
|||||||
import { getClientConfig } from "@/app/config/client";
|
import { getClientConfig } from "@/app/config/client";
|
||||||
import {
|
import {
|
||||||
getMessageTextContent,
|
getMessageTextContent,
|
||||||
getTimeoutMSByModel,
|
|
||||||
isDalle3 as _isDalle3,
|
|
||||||
isVisionModel,
|
isVisionModel,
|
||||||
|
isDalle3 as _isDalle3,
|
||||||
|
getTimeoutMSByModel,
|
||||||
} from "@/app/utils";
|
} from "@/app/utils";
|
||||||
import { fetch } from "@/app/utils/stream";
|
import { fetch } from "@/app/utils/stream";
|
||||||
|
|
||||||
@@ -56,7 +56,7 @@ export interface OpenAIListModelResponse {
|
|||||||
|
|
||||||
export interface RequestPayload {
|
export interface RequestPayload {
|
||||||
messages: {
|
messages: {
|
||||||
role: "system" | "user" | "assistant";
|
role: "developer" | "system" | "user" | "assistant";
|
||||||
content: string | MultimodalContent[];
|
content: string | MultimodalContent[];
|
||||||
}[];
|
}[];
|
||||||
stream?: boolean;
|
stream?: boolean;
|
||||||
@@ -238,8 +238,16 @@ export class ChatGPTApi implements LLMApi {
|
|||||||
// Please do not ask me why not send max_tokens, no reason, this param is just shit, I dont want to explain anymore.
|
// 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) {
|
if (isO1OrO3) {
|
||||||
|
// by default the o1/o3 models will not attempt to produce output that includes markdown formatting
|
||||||
|
// manually add "Formatting re-enabled" developer message to encourage markdown inclusion in model responses
|
||||||
|
// (https://learn.microsoft.com/en-us/azure/ai-services/openai/how-to/reasoning?tabs=python-secure#markdown-output)
|
||||||
|
requestPayload["messages"].unshift({
|
||||||
|
role: "developer",
|
||||||
|
content: "Formatting re-enabled",
|
||||||
|
});
|
||||||
|
|
||||||
|
// o1/o3 uses max_completion_tokens to control the number of tokens (https://platform.openai.com/docs/guides/reasoning#controlling-costs)
|
||||||
requestPayload["max_completion_tokens"] = modelConfig.max_tokens;
|
requestPayload["max_completion_tokens"] = modelConfig.max_tokens;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -295,13 +303,6 @@ export class ChatGPTApi implements LLMApi {
|
|||||||
useChatStore.getState().currentSession().mask?.plugin || [],
|
useChatStore.getState().currentSession().mask?.plugin || [],
|
||||||
);
|
);
|
||||||
// console.log("getAsTools", tools, funcs);
|
// console.log("getAsTools", tools, funcs);
|
||||||
|
|
||||||
// Add "include_reasoning" for OpenRouter: https://openrouter.ai/announcements/reasoning-tokens-for-thinking-models
|
|
||||||
if (chatPath.includes("openrouter.ai")) {
|
|
||||||
// @ts-ignore
|
|
||||||
requestPayload["include_reasoning"] = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
streamWithThink(
|
streamWithThink(
|
||||||
chatPath,
|
chatPath,
|
||||||
requestPayload,
|
requestPayload,
|
||||||
@@ -318,7 +319,6 @@ export class ChatGPTApi implements LLMApi {
|
|||||||
content: string;
|
content: string;
|
||||||
tool_calls: ChatMessageTool[];
|
tool_calls: ChatMessageTool[];
|
||||||
reasoning_content: string | null;
|
reasoning_content: string | null;
|
||||||
reasoning: string | null;
|
|
||||||
};
|
};
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
@@ -344,9 +344,7 @@ export class ChatGPTApi implements LLMApi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const reasoning =
|
const reasoning = choices[0]?.delta?.reasoning_content;
|
||||||
choices[0]?.delta?.reasoning_content ||
|
|
||||||
choices[0]?.delta?.reasoning;
|
|
||||||
const content = choices[0]?.delta?.content;
|
const content = choices[0]?.delta?.content;
|
||||||
|
|
||||||
// Skip if both content and reasoning_content are empty or null
|
// Skip if both content and reasoning_content are empty or null
|
||||||
@@ -422,7 +420,6 @@ export class ChatGPTApi implements LLMApi {
|
|||||||
options.onError?.(e as Error);
|
options.onError?.(e as Error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async usage() {
|
async usage() {
|
||||||
const formatDate = (d: Date) =>
|
const formatDate = (d: Date) =>
|
||||||
`${d.getFullYear()}-${(d.getMonth() + 1).toString().padStart(2, "0")}-${d
|
`${d.getFullYear()}-${(d.getMonth() + 1).toString().padStart(2, "0")}-${d
|
||||||
@@ -526,5 +523,4 @@ export class ChatGPTApi implements LLMApi {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export { OpenaiPath };
|
export { OpenaiPath };
|
||||||
|
@@ -523,20 +523,15 @@ const openaiModels = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
const googleModels = [
|
const googleModels = [
|
||||||
"gemini-1.0-pro", // Deprecated on 2/15/2025
|
|
||||||
"gemini-1.5-pro-latest",
|
"gemini-1.5-pro-latest",
|
||||||
"gemini-1.5-pro",
|
"gemini-1.5-pro",
|
||||||
"gemini-1.5-pro-002",
|
"gemini-1.5-pro-002",
|
||||||
"gemini-1.5-pro-exp-0827",
|
|
||||||
"gemini-1.5-flash-latest",
|
"gemini-1.5-flash-latest",
|
||||||
"gemini-1.5-flash-8b-latest",
|
"gemini-1.5-flash-8b-latest",
|
||||||
"gemini-1.5-flash",
|
"gemini-1.5-flash",
|
||||||
"gemini-1.5-flash-8b",
|
"gemini-1.5-flash-8b",
|
||||||
"gemini-1.5-flash-002",
|
"gemini-1.5-flash-002",
|
||||||
"gemini-1.5-flash-exp-0827",
|
|
||||||
"learnlm-1.5-pro-experimental",
|
"learnlm-1.5-pro-experimental",
|
||||||
"gemini-exp-1114",
|
|
||||||
"gemini-exp-1121",
|
|
||||||
"gemini-exp-1206",
|
"gemini-exp-1206",
|
||||||
"gemini-2.0-flash",
|
"gemini-2.0-flash",
|
||||||
"gemini-2.0-flash-exp",
|
"gemini-2.0-flash-exp",
|
||||||
@@ -546,6 +541,7 @@ const googleModels = [
|
|||||||
"gemini-2.0-flash-thinking-exp-01-21",
|
"gemini-2.0-flash-thinking-exp-01-21",
|
||||||
"gemini-2.0-pro-exp",
|
"gemini-2.0-pro-exp",
|
||||||
"gemini-2.0-pro-exp-02-05",
|
"gemini-2.0-pro-exp-02-05",
|
||||||
|
"gemini-2.5-pro-preview-06-05",
|
||||||
];
|
];
|
||||||
|
|
||||||
const anthropicModels = [
|
const anthropicModels = [
|
||||||
@@ -632,6 +628,18 @@ const xAIModes = [
|
|||||||
"grok-2-vision-1212",
|
"grok-2-vision-1212",
|
||||||
"grok-2-vision",
|
"grok-2-vision",
|
||||||
"grok-2-vision-latest",
|
"grok-2-vision-latest",
|
||||||
|
"grok-3-mini-fast-beta",
|
||||||
|
"grok-3-mini-fast",
|
||||||
|
"grok-3-mini-fast-latest",
|
||||||
|
"grok-3-mini-beta",
|
||||||
|
"grok-3-mini",
|
||||||
|
"grok-3-mini-latest",
|
||||||
|
"grok-3-fast-beta",
|
||||||
|
"grok-3-fast",
|
||||||
|
"grok-3-fast-latest",
|
||||||
|
"grok-3-beta",
|
||||||
|
"grok-3",
|
||||||
|
"grok-3-latest",
|
||||||
];
|
];
|
||||||
|
|
||||||
const chatglmModels = [
|
const chatglmModels = [
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import {
|
import {
|
||||||
CACHE_URL_PREFIX,
|
CACHE_URL_PREFIX,
|
||||||
REQUEST_TIMEOUT_MS,
|
|
||||||
UPLOAD_URL,
|
UPLOAD_URL,
|
||||||
|
REQUEST_TIMEOUT_MS,
|
||||||
} from "@/app/constant";
|
} from "@/app/constant";
|
||||||
import { MultimodalContent, RequestMessage } from "@/app/client/api";
|
import { MultimodalContent, RequestMessage } from "@/app/client/api";
|
||||||
import Locale from "@/app/locales";
|
import Locale from "@/app/locales";
|
||||||
@@ -111,7 +111,6 @@ export async function preProcessImageContentForAlibabaDashScope(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const imageCaches: Record<string, string> = {};
|
const imageCaches: Record<string, string> = {};
|
||||||
|
|
||||||
export function cacheImageToBase64Image(imageUrl: string) {
|
export function cacheImageToBase64Image(imageUrl: string) {
|
||||||
if (imageUrl.includes(CACHE_URL_PREFIX)) {
|
if (imageUrl.includes(CACHE_URL_PREFIX)) {
|
||||||
if (!imageCaches[imageUrl]) {
|
if (!imageCaches[imageUrl]) {
|
||||||
@@ -386,7 +385,6 @@ export function stream(
|
|||||||
openWhenHidden: true,
|
openWhenHidden: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
console.debug("[ChatAPI] start");
|
console.debug("[ChatAPI] start");
|
||||||
chatApi(chatPath, headers, requestPayload, tools); // call fetchEventSource
|
chatApi(chatPath, headers, requestPayload, tools); // call fetchEventSource
|
||||||
}
|
}
|
||||||
@@ -629,9 +627,16 @@ export function streamWithThink(
|
|||||||
if (remainText.length > 0) {
|
if (remainText.length > 0) {
|
||||||
remainText += "\n";
|
remainText += "\n";
|
||||||
}
|
}
|
||||||
remainText += "> ";
|
remainText += "> " + chunk.content;
|
||||||
|
} else {
|
||||||
|
// Handle newlines in thinking content
|
||||||
|
if (chunk.content.includes("\n\n")) {
|
||||||
|
const lines = chunk.content.split("\n\n");
|
||||||
|
remainText += lines.join("\n\n> ");
|
||||||
|
} else {
|
||||||
|
remainText += chunk.content;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
remainText += chunk.content.replaceAll("\n", "\n> ");
|
|
||||||
} else {
|
} else {
|
||||||
// If in normal mode
|
// If in normal mode
|
||||||
if (isInThinkingMode || isThinkingChanged) {
|
if (isInThinkingMode || isThinkingChanged) {
|
||||||
@@ -657,7 +662,6 @@ export function streamWithThink(
|
|||||||
openWhenHidden: true,
|
openWhenHidden: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
console.debug("[ChatAPI] start");
|
console.debug("[ChatAPI] start");
|
||||||
chatApi(chatPath, headers, requestPayload, tools); // call fetchEventSource
|
chatApi(chatPath, headers, requestPayload, tools); // call fetchEventSource
|
||||||
}
|
}
|
||||||
|
@@ -83,7 +83,7 @@
|
|||||||
"jest": "^29.7.0",
|
"jest": "^29.7.0",
|
||||||
"jest-environment-jsdom": "^29.7.0",
|
"jest-environment-jsdom": "^29.7.0",
|
||||||
"lint-staged": "^13.2.2",
|
"lint-staged": "^13.2.2",
|
||||||
"prettier": "^3.0.2",
|
"prettier": "^3.6.2",
|
||||||
"ts-node": "^10.9.2",
|
"ts-node": "^10.9.2",
|
||||||
"tsx": "^4.16.0",
|
"tsx": "^4.16.0",
|
||||||
"typescript": "5.2.2",
|
"typescript": "5.2.2",
|
||||||
|
@@ -7076,10 +7076,10 @@ prettier-linter-helpers@^1.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
fast-diff "^1.1.2"
|
fast-diff "^1.1.2"
|
||||||
|
|
||||||
prettier@^3.0.2:
|
prettier@^3.6.2:
|
||||||
version "3.0.2"
|
version "3.6.2"
|
||||||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.0.2.tgz#78fcecd6d870551aa5547437cdae39d4701dca5b"
|
resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.6.2.tgz#ccda02a1003ebbb2bfda6f83a074978f608b9393"
|
||||||
integrity sha512-o2YR9qtniXvwEZlOKbveKfDQVyqxbEIWn48Z8m3ZJjBjcCmUy3xZGIv+7AkaeuaTr6yPXJjwv07ZWlsWbEy1rQ==
|
integrity sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==
|
||||||
|
|
||||||
pretty-format@^27.0.2:
|
pretty-format@^27.0.2:
|
||||||
version "27.5.1"
|
version "27.5.1"
|
||||||
|
Reference in New Issue
Block a user