mirror of
https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web.git
synced 2025-09-27 21:56:38 +08:00
thay doi alibaba module mac dinh - chebichat
This commit is contained in:
parent
673f907ea4
commit
861d854e16
@ -20,7 +20,11 @@ async function handle(
|
|||||||
req: NextRequest,
|
req: NextRequest,
|
||||||
{ params }: { params: { provider: string; path: string[] } },
|
{ params }: { params: { provider: string; path: string[] } },
|
||||||
) {
|
) {
|
||||||
|
// Handle OPTIONS request for CORS preflight
|
||||||
|
// params.provider = MODEL_PROVIDER;
|
||||||
|
|
||||||
const apiPath = `/api/${params.provider}`;
|
const apiPath = `/api/${params.provider}`;
|
||||||
|
|
||||||
console.log(`[${params.provider} Route] params `, params);
|
console.log(`[${params.provider} Route] params `, params);
|
||||||
switch (apiPath) {
|
switch (apiPath) {
|
||||||
case ApiPath.Azure:
|
case ApiPath.Azure:
|
||||||
|
@ -1,14 +1,8 @@
|
|||||||
import { getServerSideConfig } from "@/app/config/server";
|
import { getServerSideConfig } from "@/app/config/server";
|
||||||
import {
|
import { ALIBABA_BASE_URL, ApiPath, ModelProvider } from "@/app/constant";
|
||||||
ALIBABA_BASE_URL,
|
|
||||||
ApiPath,
|
|
||||||
ModelProvider,
|
|
||||||
ServiceProvider,
|
|
||||||
} from "@/app/constant";
|
|
||||||
import { prettyObject } from "@/app/utils/format";
|
import { prettyObject } from "@/app/utils/format";
|
||||||
import { NextRequest, NextResponse } from "next/server";
|
import { NextRequest, NextResponse } from "next/server";
|
||||||
import { auth } from "@/app/api/auth";
|
import { auth } from "@/app/api/auth";
|
||||||
import { isModelNotavailableInServer } from "@/app/utils/model";
|
|
||||||
|
|
||||||
const serverConfig = getServerSideConfig();
|
const serverConfig = getServerSideConfig();
|
||||||
|
|
||||||
@ -83,28 +77,36 @@ async function request(req: NextRequest) {
|
|||||||
if (serverConfig.customModels && req.body) {
|
if (serverConfig.customModels && req.body) {
|
||||||
try {
|
try {
|
||||||
const clonedBody = await req.text();
|
const clonedBody = await req.text();
|
||||||
fetchOptions.body = clonedBody;
|
let jsonBody: any = {};
|
||||||
|
|
||||||
const jsonBody = JSON.parse(clonedBody) as { model?: string };
|
try {
|
||||||
|
jsonBody = JSON.parse(clonedBody);
|
||||||
|
delete jsonBody.model; // Remove the model key
|
||||||
|
fetchOptions.body = JSON.stringify(jsonBody);
|
||||||
|
} catch (e) {
|
||||||
|
fetchOptions.body = clonedBody; // fallback if not JSON
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("[Alibaba] request body", fetchOptions.body);
|
||||||
|
|
||||||
// not undefined and is false
|
// not undefined and is false
|
||||||
if (
|
// if (
|
||||||
isModelNotavailableInServer(
|
// isModelNotavailableInServer(
|
||||||
serverConfig.customModels,
|
// serverConfig.customModels,
|
||||||
jsonBody?.model as string,
|
// jsonBody?.model as string,
|
||||||
ServiceProvider.Alibaba as string,
|
// ServiceProvider.Alibaba as string,
|
||||||
)
|
// )
|
||||||
) {
|
// ) {
|
||||||
return NextResponse.json(
|
// return NextResponse.json(
|
||||||
{
|
// {
|
||||||
error: true,
|
// error: true,
|
||||||
message: `you are not allowed to use ${jsonBody?.model} model`,
|
// message: `you are not allowed to use ${jsonBody?.model} model`,
|
||||||
},
|
// },
|
||||||
{
|
// {
|
||||||
status: 403,
|
// status: 403,
|
||||||
},
|
// },
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(`[Alibaba] filter`, e);
|
console.error(`[Alibaba] filter`, e);
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,6 @@ import {
|
|||||||
LLMModel,
|
LLMModel,
|
||||||
SpeechOptions,
|
SpeechOptions,
|
||||||
MultimodalContent,
|
MultimodalContent,
|
||||||
MultimodalContentForAlibaba,
|
|
||||||
} from "../api";
|
} from "../api";
|
||||||
import { getClientConfig } from "@/app/config/client";
|
import { getClientConfig } from "@/app/config/client";
|
||||||
import {
|
import {
|
||||||
@ -156,11 +155,13 @@ export class QwenApi implements LLMApi {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (shouldStream) {
|
if (shouldStream) {
|
||||||
|
// Lấy danh sách các công cụ (tools) và hàm (funcs) từ plugin hiện tại của phiên chat
|
||||||
const [tools, funcs] = usePluginStore
|
const [tools, funcs] = usePluginStore
|
||||||
.getState()
|
.getState()
|
||||||
.getAsTools(
|
.getAsTools(
|
||||||
useChatStore.getState().currentSession().mask?.plugin || [],
|
useChatStore.getState().currentSession().mask?.plugin || [],
|
||||||
);
|
);
|
||||||
|
// Gọi hàm streamWithThink để xử lý chat dạng stream (dòng sự kiện server-sent events)
|
||||||
return streamWithThink(
|
return streamWithThink(
|
||||||
chatPath,
|
chatPath,
|
||||||
requestPayload,
|
requestPayload,
|
||||||
@ -168,44 +169,19 @@ export class QwenApi implements LLMApi {
|
|||||||
tools as any,
|
tools as any,
|
||||||
funcs,
|
funcs,
|
||||||
controller,
|
controller,
|
||||||
// parseSSE
|
// Updated SSE parse callback for new JSON structure
|
||||||
(text: string, runTools: ChatMessageTool[]) => {
|
(text: string, runTools: ChatMessageTool[]) => {
|
||||||
// console.log("parseSSE", text, runTools);
|
// Parse the JSON response
|
||||||
const json = JSON.parse(text);
|
const json = JSON.parse(text);
|
||||||
const choices = json.output.choices as Array<{
|
|
||||||
message: {
|
|
||||||
content: string | null | MultimodalContentForAlibaba[];
|
|
||||||
tool_calls: ChatMessageTool[];
|
|
||||||
reasoning_content: string | null;
|
|
||||||
};
|
|
||||||
}>;
|
|
||||||
|
|
||||||
if (!choices?.length) return { isThinking: false, content: "" };
|
// console.log("[Alibaba] SSE response", json);
|
||||||
|
|
||||||
const tool_calls = choices[0]?.message?.tool_calls;
|
// Extract content from the new structure
|
||||||
if (tool_calls?.length > 0) {
|
const output = json.output;
|
||||||
const index = tool_calls[0]?.index;
|
const content = output?.text ?? "";
|
||||||
const id = tool_calls[0]?.id;
|
const reasoning = output?.reasoning_content ?? ""; // If exists in your new structure
|
||||||
const args = tool_calls[0]?.function?.arguments;
|
|
||||||
if (id) {
|
|
||||||
runTools.push({
|
|
||||||
id,
|
|
||||||
type: tool_calls[0]?.type,
|
|
||||||
function: {
|
|
||||||
name: tool_calls[0]?.function?.name as string,
|
|
||||||
arguments: args,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// @ts-ignore
|
|
||||||
runTools[index]["function"]["arguments"] += args;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const reasoning = choices[0]?.message?.reasoning_content;
|
// If both are empty, return default
|
||||||
const content = choices[0]?.message?.content;
|
|
||||||
|
|
||||||
// Skip if both content and reasoning_content are empty or null
|
|
||||||
if (
|
if (
|
||||||
(!reasoning || reasoning.length === 0) &&
|
(!reasoning || reasoning.length === 0) &&
|
||||||
(!content || content.length === 0)
|
(!content || content.length === 0)
|
||||||
@ -216,31 +192,34 @@ export class QwenApi implements LLMApi {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If reasoning_content exists, treat as "thinking"
|
||||||
if (reasoning && reasoning.length > 0) {
|
if (reasoning && reasoning.length > 0) {
|
||||||
return {
|
return {
|
||||||
isThinking: true,
|
isThinking: true,
|
||||||
content: reasoning,
|
content: reasoning,
|
||||||
};
|
};
|
||||||
} else if (content && content.length > 0) {
|
}
|
||||||
|
// Otherwise, return the main content
|
||||||
|
else if (content && content.length > 0) {
|
||||||
return {
|
return {
|
||||||
isThinking: false,
|
isThinking: false,
|
||||||
content: Array.isArray(content)
|
content: content,
|
||||||
? content.map((item) => item.text).join(",")
|
|
||||||
: content,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fallback
|
||||||
return {
|
return {
|
||||||
isThinking: false,
|
isThinking: false,
|
||||||
content: "",
|
content: "",
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
// processToolMessage, include tool_calls message and tool call results
|
// Hàm xử lý message liên quan đến tool_call và kết quả trả về từ tool_call
|
||||||
(
|
(
|
||||||
requestPayload: RequestPayload,
|
requestPayload: RequestPayload,
|
||||||
toolCallMessage: any,
|
toolCallMessage: any,
|
||||||
toolCallResult: any[],
|
toolCallResult: any[],
|
||||||
) => {
|
) => {
|
||||||
|
// Thêm message gọi tool và kết quả trả về vào cuối mảng messages trong payload gửi lên API
|
||||||
requestPayload?.input?.messages?.splice(
|
requestPayload?.input?.messages?.splice(
|
||||||
requestPayload?.input?.messages?.length,
|
requestPayload?.input?.messages?.length,
|
||||||
0,
|
0,
|
||||||
@ -248,7 +227,7 @@ export class QwenApi implements LLMApi {
|
|||||||
...toolCallResult,
|
...toolCallResult,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
options,
|
options, // Các tuỳ chọn khác cho hàm streamWithThink
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
const res = await fetch(chatPath, chatPayload);
|
const res = await fetch(chatPath, chatPayload);
|
||||||
|
@ -151,7 +151,8 @@ export class DeepSeekApi implements LLMApi {
|
|||||||
controller,
|
controller,
|
||||||
// parseSSE
|
// parseSSE
|
||||||
(text: string, runTools: ChatMessageTool[]) => {
|
(text: string, runTools: ChatMessageTool[]) => {
|
||||||
// console.log("parseSSE", text, runTools);
|
console.log("parseSSE", text, runTools);
|
||||||
|
|
||||||
const json = JSON.parse(text);
|
const json = JSON.parse(text);
|
||||||
const choices = json.choices as Array<{
|
const choices = json.choices as Array<{
|
||||||
delta: {
|
delta: {
|
||||||
|
@ -153,81 +153,35 @@ export class SiliconflowApi implements LLMApi {
|
|||||||
tools as any,
|
tools as any,
|
||||||
funcs,
|
funcs,
|
||||||
controller,
|
controller,
|
||||||
// parseSSE
|
// parseSSE mới cho SiliconFlow response
|
||||||
(text: string, runTools: ChatMessageTool[]) => {
|
(text: string, runTools: ChatMessageTool[]) => {
|
||||||
// console.log("parseSSE", text, runTools);
|
// Parse chuỗi JSON trả về thành đối tượng
|
||||||
const json = JSON.parse(text);
|
const json = JSON.parse(text);
|
||||||
const choices = json.choices as Array<{
|
|
||||||
delta: {
|
|
||||||
content: string | null;
|
|
||||||
tool_calls: ChatMessageTool[];
|
|
||||||
reasoning_content: string | null;
|
|
||||||
};
|
|
||||||
}>;
|
|
||||||
const tool_calls = choices[0]?.delta?.tool_calls;
|
|
||||||
if (tool_calls?.length > 0) {
|
|
||||||
const index = tool_calls[0]?.index;
|
|
||||||
const id = tool_calls[0]?.id;
|
|
||||||
const args = tool_calls[0]?.function?.arguments;
|
|
||||||
if (id) {
|
|
||||||
runTools.push({
|
|
||||||
id,
|
|
||||||
type: tool_calls[0]?.type,
|
|
||||||
function: {
|
|
||||||
name: tool_calls[0]?.function?.name as string,
|
|
||||||
arguments: args,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// @ts-ignore
|
|
||||||
runTools[index]["function"]["arguments"] += args;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const reasoning = choices[0]?.delta?.reasoning_content;
|
|
||||||
const content = choices[0]?.delta?.content;
|
|
||||||
|
|
||||||
// Skip if both content and reasoning_content are empty or null
|
// Lấy nội dung trả lời từ output.text
|
||||||
if (
|
const content = json?.output?.text ?? "";
|
||||||
(!reasoning || reasoning.length === 0) &&
|
|
||||||
(!content || content.length === 0)
|
// Nếu không có nội dung trả lời, trả về trạng thái không suy nghĩ và nội dung rỗng
|
||||||
) {
|
if (!content || content.length === 0) {
|
||||||
return {
|
return {
|
||||||
isThinking: false,
|
isThinking: false,
|
||||||
content: "",
|
content: "",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (reasoning && reasoning.length > 0) {
|
// Trả về trạng thái không suy nghĩ và nội dung trả lời
|
||||||
return {
|
|
||||||
isThinking: true,
|
|
||||||
content: reasoning,
|
|
||||||
};
|
|
||||||
} else if (content && content.length > 0) {
|
|
||||||
return {
|
return {
|
||||||
isThinking: false,
|
isThinking: false,
|
||||||
content: content,
|
content: content,
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
isThinking: false,
|
|
||||||
content: "",
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
// processToolMessage, include tool_calls message and tool call results
|
// processToolMessage: SiliconFlow không có tool_call nên giữ nguyên hoặc để rỗng
|
||||||
(
|
(
|
||||||
requestPayload: RequestPayload,
|
requestPayload: RequestPayload,
|
||||||
toolCallMessage: any,
|
toolCallMessage: any,
|
||||||
toolCallResult: any[],
|
toolCallResult: any[],
|
||||||
) => {
|
) => {
|
||||||
// @ts-ignore
|
// Không cần xử lý tool_call, có thể để trống hoặc giữ nguyên nếu muốn tương thích
|
||||||
requestPayload?.messages?.splice(
|
|
||||||
// @ts-ignore
|
|
||||||
requestPayload?.messages?.length,
|
|
||||||
0,
|
|
||||||
toolCallMessage,
|
|
||||||
...toolCallResult,
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
options,
|
options,
|
||||||
);
|
);
|
||||||
|
@ -58,6 +58,7 @@ declare global {
|
|||||||
// alibaba only
|
// alibaba only
|
||||||
ALIBABA_URL?: string;
|
ALIBABA_URL?: string;
|
||||||
ALIBABA_API_KEY?: string;
|
ALIBABA_API_KEY?: string;
|
||||||
|
ALIBABA_APP_ID?: string; // alibaba app id, used for some models
|
||||||
|
|
||||||
// tencent only
|
// tencent only
|
||||||
TENCENT_URL?: string;
|
TENCENT_URL?: string;
|
||||||
@ -210,6 +211,7 @@ export const getServerSideConfig = () => {
|
|||||||
isAlibaba,
|
isAlibaba,
|
||||||
alibabaUrl: process.env.ALIBABA_URL,
|
alibabaUrl: process.env.ALIBABA_URL,
|
||||||
alibabaApiKey: getApiKey(process.env.ALIBABA_API_KEY),
|
alibabaApiKey: getApiKey(process.env.ALIBABA_API_KEY),
|
||||||
|
alibabaAppId: process.env.ALIBABA_APP_ID,
|
||||||
|
|
||||||
isTencent,
|
isTencent,
|
||||||
tencentUrl: process.env.TENCENT_URL,
|
tencentUrl: process.env.TENCENT_URL,
|
||||||
|
@ -21,7 +21,7 @@ export const BAIDU_OATUH_URL = `${BAIDU_BASE_URL}/oauth/2.0/token`;
|
|||||||
|
|
||||||
export const BYTEDANCE_BASE_URL = "https://ark.cn-beijing.volces.com";
|
export const BYTEDANCE_BASE_URL = "https://ark.cn-beijing.volces.com";
|
||||||
|
|
||||||
export const ALIBABA_BASE_URL = "https://dashscope.aliyuncs.com/api/";
|
export const ALIBABA_BASE_URL = "https://dashscope-intl.aliyuncs.com";
|
||||||
|
|
||||||
export const TENCENT_BASE_URL = "https://hunyuan.tencentcloudapi.com";
|
export const TENCENT_BASE_URL = "https://hunyuan.tencentcloudapi.com";
|
||||||
|
|
||||||
@ -39,6 +39,9 @@ export const SILICONFLOW_BASE_URL = "https://api.siliconflow.cn";
|
|||||||
export const CACHE_URL_PREFIX = "/api/cache";
|
export const CACHE_URL_PREFIX = "/api/cache";
|
||||||
export const UPLOAD_URL = `${CACHE_URL_PREFIX}/upload`;
|
export const UPLOAD_URL = `${CACHE_URL_PREFIX}/upload`;
|
||||||
|
|
||||||
|
export const ALIBABA_APP_ID = "95072bcf71bf4469a25c45c31e76f37a"; // default alibaba app id, used for some models
|
||||||
|
export const MODEL_PROVIDER = "alibaba";
|
||||||
|
|
||||||
export enum Path {
|
export enum Path {
|
||||||
Home = "/",
|
Home = "/",
|
||||||
Chat = "/chat",
|
Chat = "/chat",
|
||||||
@ -222,10 +225,13 @@ export const ByteDance = {
|
|||||||
export const Alibaba = {
|
export const Alibaba = {
|
||||||
ExampleEndpoint: ALIBABA_BASE_URL,
|
ExampleEndpoint: ALIBABA_BASE_URL,
|
||||||
ChatPath: (modelName: string) => {
|
ChatPath: (modelName: string) => {
|
||||||
|
const URL = `api/v1/apps/${ALIBABA_APP_ID}/completion`;
|
||||||
|
|
||||||
if (modelName.includes("vl") || modelName.includes("omni")) {
|
if (modelName.includes("vl") || modelName.includes("omni")) {
|
||||||
return "v1/services/aigc/multimodal-generation/generation";
|
return "v1/services/aigc/multimodal-generation/generation";
|
||||||
}
|
}
|
||||||
return `v1/services/aigc/text-generation/generation`;
|
// return `v1/services/aigc/text-generation/generation`;
|
||||||
|
return URL;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -681,20 +687,21 @@ const siliconflowModels = [
|
|||||||
|
|
||||||
let seq = 1000; // 内置的模型序号生成器从1000开始
|
let seq = 1000; // 内置的模型序号生成器从1000开始
|
||||||
export const DEFAULT_MODELS = [
|
export const DEFAULT_MODELS = [
|
||||||
...openaiModels.map((name) => ({
|
...alibabaModes.map((name) => ({
|
||||||
name,
|
name,
|
||||||
available: true,
|
available: true, // 默认可用
|
||||||
sorted: seq++, // Global sequence sort(index)
|
sorted: seq++,
|
||||||
provider: {
|
provider: {
|
||||||
id: "openai",
|
id: "alibaba",
|
||||||
providerName: "OpenAI",
|
providerName: "Alibaba",
|
||||||
providerType: "openai",
|
providerType: "alibaba",
|
||||||
sorted: 1, // 这里是固定的,确保顺序与之前内置的版本一致
|
sorted: 1,
|
||||||
},
|
},
|
||||||
})),
|
})),
|
||||||
|
|
||||||
...openaiModels.map((name) => ({
|
...openaiModels.map((name) => ({
|
||||||
name,
|
name,
|
||||||
available: true,
|
available: false,
|
||||||
sorted: seq++,
|
sorted: seq++,
|
||||||
provider: {
|
provider: {
|
||||||
id: "azure",
|
id: "azure",
|
||||||
@ -705,7 +712,7 @@ export const DEFAULT_MODELS = [
|
|||||||
})),
|
})),
|
||||||
...googleModels.map((name) => ({
|
...googleModels.map((name) => ({
|
||||||
name,
|
name,
|
||||||
available: true,
|
available: false,
|
||||||
sorted: seq++,
|
sorted: seq++,
|
||||||
provider: {
|
provider: {
|
||||||
id: "google",
|
id: "google",
|
||||||
@ -716,7 +723,7 @@ export const DEFAULT_MODELS = [
|
|||||||
})),
|
})),
|
||||||
...anthropicModels.map((name) => ({
|
...anthropicModels.map((name) => ({
|
||||||
name,
|
name,
|
||||||
available: true,
|
available: false,
|
||||||
sorted: seq++,
|
sorted: seq++,
|
||||||
provider: {
|
provider: {
|
||||||
id: "anthropic",
|
id: "anthropic",
|
||||||
@ -727,7 +734,7 @@ export const DEFAULT_MODELS = [
|
|||||||
})),
|
})),
|
||||||
...baiduModels.map((name) => ({
|
...baiduModels.map((name) => ({
|
||||||
name,
|
name,
|
||||||
available: true,
|
available: false,
|
||||||
sorted: seq++,
|
sorted: seq++,
|
||||||
provider: {
|
provider: {
|
||||||
id: "baidu",
|
id: "baidu",
|
||||||
@ -738,7 +745,7 @@ export const DEFAULT_MODELS = [
|
|||||||
})),
|
})),
|
||||||
...bytedanceModels.map((name) => ({
|
...bytedanceModels.map((name) => ({
|
||||||
name,
|
name,
|
||||||
available: true,
|
available: false,
|
||||||
sorted: seq++,
|
sorted: seq++,
|
||||||
provider: {
|
provider: {
|
||||||
id: "bytedance",
|
id: "bytedance",
|
||||||
@ -747,20 +754,22 @@ export const DEFAULT_MODELS = [
|
|||||||
sorted: 6,
|
sorted: 6,
|
||||||
},
|
},
|
||||||
})),
|
})),
|
||||||
...alibabaModes.map((name) => ({
|
|
||||||
|
...openaiModels.map((name) => ({
|
||||||
name,
|
name,
|
||||||
available: true,
|
available: false,
|
||||||
sorted: seq++,
|
sorted: seq++, // Global sequence sort(index)
|
||||||
provider: {
|
provider: {
|
||||||
id: "alibaba",
|
id: "openai",
|
||||||
providerName: "Alibaba",
|
providerName: "OpenAI",
|
||||||
providerType: "alibaba",
|
providerType: "openai",
|
||||||
sorted: 7,
|
sorted: 7, // 这里是固定的,确保顺序与之前内置的版本一致
|
||||||
},
|
},
|
||||||
})),
|
})),
|
||||||
|
|
||||||
...tencentModels.map((name) => ({
|
...tencentModels.map((name) => ({
|
||||||
name,
|
name,
|
||||||
available: true,
|
available: false,
|
||||||
sorted: seq++,
|
sorted: seq++,
|
||||||
provider: {
|
provider: {
|
||||||
id: "tencent",
|
id: "tencent",
|
||||||
@ -771,7 +780,7 @@ export const DEFAULT_MODELS = [
|
|||||||
})),
|
})),
|
||||||
...moonshotModes.map((name) => ({
|
...moonshotModes.map((name) => ({
|
||||||
name,
|
name,
|
||||||
available: true,
|
available: false,
|
||||||
sorted: seq++,
|
sorted: seq++,
|
||||||
provider: {
|
provider: {
|
||||||
id: "moonshot",
|
id: "moonshot",
|
||||||
@ -782,7 +791,7 @@ export const DEFAULT_MODELS = [
|
|||||||
})),
|
})),
|
||||||
...iflytekModels.map((name) => ({
|
...iflytekModels.map((name) => ({
|
||||||
name,
|
name,
|
||||||
available: true,
|
available: false,
|
||||||
sorted: seq++,
|
sorted: seq++,
|
||||||
provider: {
|
provider: {
|
||||||
id: "iflytek",
|
id: "iflytek",
|
||||||
@ -793,7 +802,7 @@ export const DEFAULT_MODELS = [
|
|||||||
})),
|
})),
|
||||||
...xAIModes.map((name) => ({
|
...xAIModes.map((name) => ({
|
||||||
name,
|
name,
|
||||||
available: true,
|
available: false,
|
||||||
sorted: seq++,
|
sorted: seq++,
|
||||||
provider: {
|
provider: {
|
||||||
id: "xai",
|
id: "xai",
|
||||||
@ -804,7 +813,7 @@ export const DEFAULT_MODELS = [
|
|||||||
})),
|
})),
|
||||||
...chatglmModels.map((name) => ({
|
...chatglmModels.map((name) => ({
|
||||||
name,
|
name,
|
||||||
available: true,
|
available: false,
|
||||||
sorted: seq++,
|
sorted: seq++,
|
||||||
provider: {
|
provider: {
|
||||||
id: "chatglm",
|
id: "chatglm",
|
||||||
@ -815,7 +824,7 @@ export const DEFAULT_MODELS = [
|
|||||||
})),
|
})),
|
||||||
...deepseekModels.map((name) => ({
|
...deepseekModels.map((name) => ({
|
||||||
name,
|
name,
|
||||||
available: true,
|
available: false,
|
||||||
sorted: seq++,
|
sorted: seq++,
|
||||||
provider: {
|
provider: {
|
||||||
id: "deepseek",
|
id: "deepseek",
|
||||||
@ -826,7 +835,7 @@ export const DEFAULT_MODELS = [
|
|||||||
})),
|
})),
|
||||||
...siliconflowModels.map((name) => ({
|
...siliconflowModels.map((name) => ({
|
||||||
name,
|
name,
|
||||||
available: true,
|
available: false,
|
||||||
sorted: seq++,
|
sorted: seq++,
|
||||||
provider: {
|
provider: {
|
||||||
id: "siliconflow",
|
id: "siliconflow",
|
||||||
|
@ -64,8 +64,8 @@ export const DEFAULT_CONFIG = {
|
|||||||
models: DEFAULT_MODELS as any as LLMModel[],
|
models: DEFAULT_MODELS as any as LLMModel[],
|
||||||
|
|
||||||
modelConfig: {
|
modelConfig: {
|
||||||
model: "gpt-4o-mini" as ModelType,
|
model: "qwen-turbo" as ModelType,
|
||||||
providerName: "OpenAI" as ServiceProvider,
|
providerName: "Alibaba" as ServiceProvider,
|
||||||
temperature: 0.5,
|
temperature: 0.5,
|
||||||
top_p: 1,
|
top_p: 1,
|
||||||
max_tokens: 4000,
|
max_tokens: 4000,
|
||||||
|
@ -65,32 +65,40 @@ export const useMaskStore = createPersistStore(
|
|||||||
|
|
||||||
return masks[id];
|
return masks[id];
|
||||||
},
|
},
|
||||||
|
// Hàm cập nhật một mask dựa trên id và một hàm updater
|
||||||
updateMask(id: string, updater: (mask: Mask) => void) {
|
updateMask(id: string, updater: (mask: Mask) => void) {
|
||||||
const masks = get().masks;
|
const masks = get().masks; // Lấy danh sách các mask hiện tại
|
||||||
const mask = masks[id];
|
const mask = masks[id]; // Lấy mask theo id
|
||||||
if (!mask) return;
|
if (!mask) return; // Nếu không tìm thấy thì thoát
|
||||||
const updateMask = { ...mask };
|
const updateMask = { ...mask }; // Tạo bản sao mask để cập nhật
|
||||||
updater(updateMask);
|
updater(updateMask); // Gọi hàm updater để chỉnh sửa mask
|
||||||
masks[id] = updateMask;
|
masks[id] = updateMask; // Gán lại mask đã cập nhật vào danh sách
|
||||||
set(() => ({ masks }));
|
set(() => ({ masks })); // Cập nhật lại state
|
||||||
get().markUpdate();
|
get().markUpdate(); // Đánh dấu đã cập nhật
|
||||||
},
|
},
|
||||||
|
// Hàm xóa một mask theo id
|
||||||
delete(id: string) {
|
delete(id: string) {
|
||||||
const masks = get().masks;
|
const masks = get().masks; // Lấy danh sách các mask hiện tại
|
||||||
delete masks[id];
|
delete masks[id]; // Xóa mask theo id
|
||||||
set(() => ({ masks }));
|
set(() => ({ masks })); // Cập nhật lại state
|
||||||
get().markUpdate();
|
get().markUpdate(); // Đánh dấu đã cập nhật
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Hàm lấy một mask theo id (nếu không truyền id sẽ lấy id mặc định)
|
||||||
get(id?: string) {
|
get(id?: string) {
|
||||||
return get().masks[id ?? 1145141919810];
|
return get().masks[id ?? 1145141919810];
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Hàm lấy tất cả các mask (bao gồm cả mask người dùng và mask mặc định)
|
||||||
getAll() {
|
getAll() {
|
||||||
|
// Lấy danh sách mask của người dùng, sắp xếp theo thời gian tạo mới nhất
|
||||||
const userMasks = Object.values(get().masks).sort(
|
const userMasks = Object.values(get().masks).sort(
|
||||||
(a, b) => b.createdAt - a.createdAt,
|
(a, b) => b.createdAt - a.createdAt,
|
||||||
);
|
);
|
||||||
const config = useAppConfig.getState();
|
const config = useAppConfig.getState(); // Lấy config hiện tại
|
||||||
if (config.hideBuiltinMasks) return userMasks;
|
if (config.hideBuiltinMasks) return userMasks; // Nếu ẩn mask mặc định thì chỉ trả về mask người dùng
|
||||||
|
|
||||||
|
// Tạo danh sách mask mặc định (BUILTIN_MASKS) với cấu hình model hiện tại
|
||||||
const buildinMasks = BUILTIN_MASKS.map(
|
const buildinMasks = BUILTIN_MASKS.map(
|
||||||
(m) =>
|
(m) =>
|
||||||
({
|
({
|
||||||
@ -101,6 +109,7 @@ export const useMaskStore = createPersistStore(
|
|||||||
},
|
},
|
||||||
}) as Mask,
|
}) as Mask,
|
||||||
);
|
);
|
||||||
|
// Trả về danh sách mask người dùng + mask mặc định
|
||||||
return userMasks.concat(buildinMasks);
|
return userMasks.concat(buildinMasks);
|
||||||
},
|
},
|
||||||
search(text: string) {
|
search(text: string) {
|
||||||
|
@ -236,36 +236,38 @@ export const usePluginStore = createPersistStore(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
fetch("./plugins.json")
|
// fetch("./plugins.json")
|
||||||
.then((res) => res.json())
|
// .then((res) => res.json())
|
||||||
.then((res) => {
|
// .then((res) => {
|
||||||
Promise.all(
|
// Promise.all(
|
||||||
res.map((item: any) =>
|
// res.map((item: any) =>
|
||||||
// skip get schema
|
// // skip get schema
|
||||||
state.get(item.id)
|
// state.get(item.id)
|
||||||
? item
|
// ? item
|
||||||
: fetch(item.schema)
|
// : fetch(item.schema)
|
||||||
.then((res) => res.text())
|
// .then((res) => res.text())
|
||||||
.then((content) => ({
|
// .then((content) => ({
|
||||||
...item,
|
// ...item,
|
||||||
content,
|
// content,
|
||||||
}))
|
// }))
|
||||||
.catch((e) => item),
|
// .catch((e) => item),
|
||||||
),
|
// ),
|
||||||
).then((builtinPlugins: any) => {
|
// ).then((builtinPlugins: any) => {
|
||||||
builtinPlugins
|
|
||||||
.filter((item: any) => item?.content)
|
// // builtinPlugins
|
||||||
.forEach((item: any) => {
|
// // .filter((item: any) => item?.content)
|
||||||
const plugin = state.create(item);
|
// // .forEach((item: any) => {
|
||||||
state.updatePlugin(plugin.id, (plugin) => {
|
// // const plugin = state.create(item);
|
||||||
const tool = FunctionToolService.add(plugin, true);
|
// // state.updatePlugin(plugin.id, (plugin) => {
|
||||||
plugin.title = tool.api.definition.info.title;
|
// // const tool = FunctionToolService.add(plugin, true);
|
||||||
plugin.version = tool.api.definition.info.version;
|
// // plugin.title = tool.api.definition.info.title;
|
||||||
plugin.builtin = true;
|
// // plugin.version = tool.api.definition.info.version;
|
||||||
});
|
// // plugin.builtin = true;
|
||||||
});
|
// // });
|
||||||
});
|
// // });
|
||||||
});
|
// });
|
||||||
|
|
||||||
|
// });
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -198,6 +198,7 @@ export function stream(
|
|||||||
function animateResponseText() {
|
function animateResponseText() {
|
||||||
if (finished || controller.signal.aborted) {
|
if (finished || controller.signal.aborted) {
|
||||||
responseText += remainText;
|
responseText += remainText;
|
||||||
|
|
||||||
console.log("[Response Animation] finished");
|
console.log("[Response Animation] finished");
|
||||||
if (responseText?.length === 0) {
|
if (responseText?.length === 0) {
|
||||||
options.onError?.(new Error("empty response from server"));
|
options.onError?.(new Error("empty response from server"));
|
||||||
@ -211,6 +212,12 @@ export function stream(
|
|||||||
responseText += fetchText;
|
responseText += fetchText;
|
||||||
remainText = remainText.slice(fetchCount);
|
remainText = remainText.slice(fetchCount);
|
||||||
options.onUpdate?.(responseText, fetchText);
|
options.onUpdate?.(responseText, fetchText);
|
||||||
|
|
||||||
|
console.log("[Response Animation] update", {
|
||||||
|
responseText,
|
||||||
|
fetchText,
|
||||||
|
remainText,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
requestAnimationFrame(animateResponseText);
|
requestAnimationFrame(animateResponseText);
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import { create } from "zustand";
|
import { create } from "zustand"; // Thư viện quản lý state cho React
|
||||||
import { combine, persist, createJSONStorage } from "zustand/middleware";
|
import { combine, persist, createJSONStorage } from "zustand/middleware"; // Các middleware hỗ trợ zustand
|
||||||
import { Updater } from "../typing";
|
import { Updater } from "../typing"; // Kiểu Updater tự định nghĩa
|
||||||
import { deepClone } from "./clone";
|
import { deepClone } from "./clone"; // Hàm deepClone để sao chép sâu object
|
||||||
import { indexedDBStorage } from "@/app/utils/indexedDB-storage";
|
import { indexedDBStorage } from "@/app/utils/indexedDB-storage"; // Lưu trữ dữ liệu bằng IndexedDB
|
||||||
|
|
||||||
|
// Lấy kiểu tham số thứ hai của một hàm
|
||||||
type SecondParam<T> = T extends (
|
type SecondParam<T> = T extends (
|
||||||
_f: infer _F,
|
_f: infer _F,
|
||||||
_s: infer S,
|
_s: infer S,
|
||||||
@ -12,52 +13,63 @@ type SecondParam<T> = T extends (
|
|||||||
? S
|
? S
|
||||||
: never;
|
: never;
|
||||||
|
|
||||||
|
// Định nghĩa các thuộc tính và phương thức bổ sung cho store
|
||||||
type MakeUpdater<T> = {
|
type MakeUpdater<T> = {
|
||||||
lastUpdateTime: number;
|
lastUpdateTime: number; // Thời gian cập nhật cuối cùng
|
||||||
_hasHydrated: boolean;
|
_hasHydrated: boolean; // Đánh dấu đã hydrate (khôi phục dữ liệu từ storage)
|
||||||
|
|
||||||
markUpdate: () => void;
|
markUpdate: () => void; // Đánh dấu cập nhật (cập nhật lastUpdateTime)
|
||||||
update: Updater<T>;
|
update: Updater<T>; // Hàm cập nhật state bằng một updater
|
||||||
setHasHydrated: (state: boolean) => void;
|
setHasHydrated: (state: boolean) => void; // Đặt trạng thái hydrate
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Kiểu hàm set state cho store
|
||||||
type SetStoreState<T> = (
|
type SetStoreState<T> = (
|
||||||
partial: T | Partial<T> | ((state: T) => T | Partial<T>),
|
partial: T | Partial<T> | ((state: T) => T | Partial<T>),
|
||||||
replace?: boolean | undefined,
|
replace?: boolean | undefined,
|
||||||
) => void;
|
) => void;
|
||||||
|
|
||||||
|
// Hàm tạo store có persist (lưu trữ lâu dài) với các phương thức bổ sung
|
||||||
export function createPersistStore<T extends object, M>(
|
export function createPersistStore<T extends object, M>(
|
||||||
state: T,
|
state: T, // State mặc định ban đầu
|
||||||
methods: (
|
methods: (
|
||||||
set: SetStoreState<T & MakeUpdater<T>>,
|
set: SetStoreState<T & MakeUpdater<T>>,
|
||||||
get: () => T & MakeUpdater<T>,
|
get: () => T & MakeUpdater<T>,
|
||||||
) => M,
|
) => M, // Các phương thức thao tác với store
|
||||||
persistOptions: SecondParam<typeof persist<T & M & MakeUpdater<T>>>,
|
persistOptions: SecondParam<typeof persist<T & M & MakeUpdater<T>>>, // Tùy chọn lưu trữ
|
||||||
) {
|
) {
|
||||||
|
// Thiết lập storage sử dụng IndexedDB
|
||||||
persistOptions.storage = createJSONStorage(() => indexedDBStorage);
|
persistOptions.storage = createJSONStorage(() => indexedDBStorage);
|
||||||
|
|
||||||
|
// Lưu lại hàm onRehydrateStorage cũ (nếu có)
|
||||||
const oldOonRehydrateStorage = persistOptions?.onRehydrateStorage;
|
const oldOonRehydrateStorage = persistOptions?.onRehydrateStorage;
|
||||||
|
|
||||||
|
// Gán lại hàm onRehydrateStorage để đánh dấu đã hydrate khi khôi phục dữ liệu
|
||||||
persistOptions.onRehydrateStorage = (state) => {
|
persistOptions.onRehydrateStorage = (state) => {
|
||||||
oldOonRehydrateStorage?.(state);
|
oldOonRehydrateStorage?.(state);
|
||||||
return () => state.setHasHydrated(true);
|
return () => state.setHasHydrated(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Tạo store với zustand, kết hợp các middleware và phương thức bổ sung
|
||||||
return create(
|
return create(
|
||||||
persist(
|
persist(
|
||||||
combine(
|
combine(
|
||||||
{
|
{
|
||||||
...state,
|
...state,
|
||||||
lastUpdateTime: 0,
|
lastUpdateTime: 0, // Khởi tạo thời gian cập nhật cuối là 0
|
||||||
_hasHydrated: false,
|
_hasHydrated: false, // Chưa hydrate
|
||||||
},
|
},
|
||||||
(set, get) => {
|
(set, get) => {
|
||||||
return {
|
return {
|
||||||
...methods(set, get as any),
|
...methods(set, get as any), // Thêm các phương thức custom
|
||||||
|
|
||||||
|
// Đánh dấu cập nhật (cập nhật lastUpdateTime)
|
||||||
markUpdate() {
|
markUpdate() {
|
||||||
set({ lastUpdateTime: Date.now() } as Partial<
|
set({ lastUpdateTime: Date.now() } as Partial<
|
||||||
T & M & MakeUpdater<T>
|
T & M & MakeUpdater<T>
|
||||||
>);
|
>);
|
||||||
},
|
},
|
||||||
|
// Hàm cập nhật state bằng một updater, đồng thời cập nhật lastUpdateTime
|
||||||
update(updater) {
|
update(updater) {
|
||||||
const state = deepClone(get());
|
const state = deepClone(get());
|
||||||
updater(state);
|
updater(state);
|
||||||
@ -66,6 +78,7 @@ export function createPersistStore<T extends object, M>(
|
|||||||
lastUpdateTime: Date.now(),
|
lastUpdateTime: Date.now(),
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
// Đặt trạng thái hydrate
|
||||||
setHasHydrated: (state: boolean) => {
|
setHasHydrated: (state: boolean) => {
|
||||||
set({ _hasHydrated: state } as Partial<T & M & MakeUpdater<T>>);
|
set({ _hasHydrated: state } as Partial<T & M & MakeUpdater<T>>);
|
||||||
},
|
},
|
||||||
|
Loading…
Reference in New Issue
Block a user