Skip to content
4 changes: 4 additions & 0 deletions app/api/[provider]/[...path]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,11 @@ async function handle(
req: NextRequest,
{ params }: { params: { provider: string; path: string[] } },
) {
// Handle OPTIONS request for CORS preflight
// params.provider = MODEL_PROVIDER;

const apiPath = `/api/${params.provider}`;

console.log(`[${params.provider} Route] params `, params);
switch (apiPath) {
case ApiPath.Azure:
Expand Down
56 changes: 29 additions & 27 deletions app/api/alibaba.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,16 @@
import { getServerSideConfig } from "@/app/config/server";
import {
ALIBABA_BASE_URL,
ApiPath,
ModelProvider,
ServiceProvider,
} from "@/app/constant";
import { ALIBABA_BASE_URL, ApiPath, ModelProvider } from "@/app/constant";
import { prettyObject } from "@/app/utils/format";
import { NextRequest, NextResponse } from "next/server";
import { auth } from "@/app/api/auth";
import { isModelNotavailableInServer } from "@/app/utils/model";

const serverConfig = getServerSideConfig();

export async function handle(
req: NextRequest,
{ params }: { params: { path: string[] } },
) {
console.log("[Alibaba Route] params ", params);
// console.log("[Alibaba Route] params ", params);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Remove commented console.log statements instead of leaving them commented out.

Commented-out debug logs should be removed entirely to keep the codebase clean, rather than left as commented code.

-  // console.log("[Alibaba Route] params ", params);
+
-    // console.error("[Alibaba] ", e);
+
-  // console.log("[Alibaba] fetchUrl", fetchUrl);
+
-  // console.log("[Proxy] Alibaba options: ", fetchOptions);
+
-        // console.log("[Alibaba] custom models", current_model);
+
-        // console.log("[Alibaba] request body json", jsonBody);
+
-      // console.log("[Alibaba] request body", fetchOptions.body);
+
-      // console.error(`[Alibaba] filter`, e);
+

Also applies to: 30-30, 65-65, 81-81, 103-103, 131-131, 138-138, 159-159

🤖 Prompt for AI Agents
In app/api/alibaba.ts at lines 13, 30, 65, 81, 103, 131, 138, and 159, remove
all commented-out console.log statements entirely instead of leaving them
commented out. This will clean up the codebase by eliminating unnecessary
commented debug logs.


if (req.method === "OPTIONS") {
return NextResponse.json({ body: "OK" }, { status: 200 });
Expand Down Expand Up @@ -83,28 +77,36 @@ async function request(req: NextRequest) {
if (serverConfig.customModels && req.body) {
try {
const clonedBody = await req.text();
fetchOptions.body = clonedBody;
let jsonBody: any = {};

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
}

const jsonBody = JSON.parse(clonedBody) as { model?: string };
console.log("[Alibaba] request body", fetchOptions.body);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix performance issue with delete operator and improve error handling.

The delete operator can impact performance. Also, the JSON parsing logic could be made more robust.

Apply this diff to address the performance issue and improve error handling:

   try {
     const clonedBody = await req.text();
-    let jsonBody: any = {};
-
-    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
-    }
+    
+    try {
+      const jsonBody = JSON.parse(clonedBody);
+      const { model, ...bodyWithoutModel } = jsonBody; // Destructure to remove model
+      fetchOptions.body = JSON.stringify(bodyWithoutModel);
+    } catch (parseError) {
+      console.warn("[Alibaba] Failed to parse request body as JSON:", parseError);
+      fetchOptions.body = clonedBody; // fallback if not JSON
+    }

     console.log("[Alibaba] request body", fetchOptions.body);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let jsonBody: any = {};
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
}
const jsonBody = JSON.parse(clonedBody) as { model?: string };
console.log("[Alibaba] request body", fetchOptions.body);
// Read the raw request body
const clonedBody = await req.text();
try {
// Parse JSON and remove `model` via destructuring
const jsonBody = JSON.parse(clonedBody);
const { model, ...bodyWithoutModel } = jsonBody;
fetchOptions.body = JSON.stringify(bodyWithoutModel);
} catch (parseError) {
// Improved error handling
console.warn("[Alibaba] Failed to parse request body as JSON:", parseError);
fetchOptions.body = clonedBody; // fallback if not JSON
}
console.log("[Alibaba] request body", fetchOptions.body);
🧰 Tools
🪛 Biome (1.9.4)

[error] 84-84: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.

(lint/performance/noDelete)

🤖 Prompt for AI Agents
In app/api/alibaba.ts around lines 80 to 90, avoid using the delete operator on
the parsed JSON object to improve performance by instead creating a new object
that excludes the 'model' key. Additionally, enhance error handling by catching
and logging the JSON parsing error before falling back to using the original
clonedBody as the fetch body.


// not undefined and is false
if (
isModelNotavailableInServer(
serverConfig.customModels,
jsonBody?.model as string,
ServiceProvider.Alibaba as string,
)
) {
return NextResponse.json(
{
error: true,
message: `you are not allowed to use ${jsonBody?.model} model`,
},
{
status: 403,
},
);
}
// if (
// isModelNotavailableInServer(
// serverConfig.customModels,
// jsonBody?.model as string,
// ServiceProvider.Alibaba as string,
// )
// ) {
// return NextResponse.json(
// {
// error: true,
// message: `you are not allowed to use ${jsonBody?.model} model`,
// },
// {
// status: 403,
// },
// );
// }
Comment on lines +141 to +157
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Remove commented model availability check code.

This large block of commented code should either be removed entirely if no longer needed, or uncommented and fixed if the functionality is still required.

If this functionality is no longer needed, remove the commented code:

-      // not undefined and is false
-      // if (
-      //   isModelNotavailableInServer(
-      //     serverConfig.customModels,
-      //     jsonBody?.model as string,
-      //     ServiceProvider.Alibaba as string,
-      //   )
-      // ) {
-      //   return NextResponse.json(
-      //     {
-      //       error: true,
-      //       message: `you are not allowed to use ${jsonBody?.model} model`,
-      //     },
-      //     {
-      //       status: 403,
-      //     },
-      //   );
-      // }

If this functionality should be retained, please uncomment and ensure the imports are available.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In app/api/alibaba.ts between lines 93 and 109, there is a large block of
commented-out code checking model availability. Determine if this model
availability check is still required; if not, remove the entire commented block
to clean up the code. If it is needed, uncomment the code and verify that all
necessary imports, such as isModelNotavailableInServer and ServiceProvider, are
correctly included and the logic works as intended.

} catch (e) {
console.error(`[Alibaba] filter`, e);
}
Expand Down
59 changes: 19 additions & 40 deletions app/client/platforms/alibaba.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import {
LLMModel,
SpeechOptions,
MultimodalContent,
MultimodalContentForAlibaba,
} from "../api";
import { getClientConfig } from "@/app/config/client";
import {
Expand Down Expand Up @@ -156,56 +155,33 @@ export class QwenApi implements LLMApi {
);

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
.getState()
.getAsTools(
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(
chatPath,
requestPayload,
headers,
tools as any,
funcs,
controller,
// parseSSE
// Updated SSE parse callback for new JSON structure
(text: string, runTools: ChatMessageTool[]) => {
// console.log("parseSSE", text, runTools);
// Parse the JSON response
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: "" };

const tool_calls = choices[0]?.message?.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;
}
}
console.log("[Alibaba] SSE response", json);

const reasoning = choices[0]?.message?.reasoning_content;
const content = choices[0]?.message?.content;
// Extract content from the new structure
const output = json.output;
const content = output?.text ?? "";
const reasoning = output?.reasoning_content ?? ""; // If exists in your new structure

// Skip if both content and reasoning_content are empty or null
// If both are empty, return default
if (
(!reasoning || reasoning.length === 0) &&
(!content || content.length === 0)
Expand All @@ -216,39 +192,42 @@ export class QwenApi implements LLMApi {
};
}

// If reasoning_content exists, treat as "thinking"
if (reasoning && reasoning.length > 0) {
return {
isThinking: true,
content: reasoning,
};
} else if (content && content.length > 0) {
}
// Otherwise, return the main content
else if (content && content.length > 0) {
return {
isThinking: false,
content: Array.isArray(content)
? content.map((item) => item.text).join(",")
: content,
content: content,
};
}

// Fallback
return {
isThinking: false,
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,
toolCallMessage: 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?.length,
0,
toolCallMessage,
...toolCallResult,
);
},
options,
options, // Các tuỳ chọn khác cho hàm streamWithThink
);
} else {
const res = await fetch(chatPath, chatPayload);
Expand Down
3 changes: 2 additions & 1 deletion app/client/platforms/deepseek.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,8 @@ export class DeepSeekApi implements LLMApi {
controller,
// parseSSE
(text: string, runTools: ChatMessageTool[]) => {
// console.log("parseSSE", text, runTools);
console.log("parseSSE", text, runTools);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Make debug logging conditional or remove it.

The enabled debug log will add noise to production logs and potentially expose sensitive data. Consider making it conditional based on a debug flag or environment variable.

-            console.log("parseSSE", text, runTools);
+            if (process.env.NODE_ENV === 'development' || process.env.DEBUG_DEEPSEEK) {
+              console.log("parseSSE", text, runTools);
+            }

Or if debugging is no longer needed:

-            console.log("parseSSE", text, runTools);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
console.log("parseSSE", text, runTools);
if (process.env.NODE_ENV === 'development' || process.env.DEBUG_DEEPSEEK) {
console.log("parseSSE", text, runTools);
}
🤖 Prompt for AI Agents
In app/client/platforms/deepseek.ts at line 154, the console.log statement for
debugging is always enabled, which can clutter production logs and expose
sensitive data. Modify the code to make this debug logging conditional by
checking a debug flag or environment variable before logging, or remove the
console.log entirely if it is no longer needed.


const json = JSON.parse(text);
const choices = json.choices as Array<{
delta: {
Expand Down
68 changes: 11 additions & 57 deletions app/client/platforms/siliconflow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,81 +153,35 @@ export class SiliconflowApi implements LLMApi {
tools as any,
funcs,
controller,
// parseSSE
// parseSSE mới cho SiliconFlow response
(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 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
if (
(!reasoning || reasoning.length === 0) &&
(!content || content.length === 0)
) {
return {
isThinking: false,
content: "",
};
}
// Lấy nội dung trả lời từ output.text
const content = json?.output?.text ?? "";

if (reasoning && reasoning.length > 0) {
return {
isThinking: true,
content: reasoning,
};
} else if (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 {
isThinking: false,
content: content,
content: "",
};
}

// Trả về trạng thái không suy nghĩ và nội dung trả lời
return {
isThinking: false,
content: "",
content: 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,
toolCallMessage: any,
toolCallResult: any[],
) => {
// @ts-ignore
requestPayload?.messages?.splice(
// @ts-ignore
requestPayload?.messages?.length,
0,
toolCallMessage,
...toolCallResult,
);
// 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
},
Comment on lines +156 to 185
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

🧩 Verification correct

Address code commenting language consistency and verify functional completeness.

The code has several issues that need attention:

  1. Language consistency: The Vietnamese comments should be in English to maintain codebase consistency
  2. Simplified logic: The removal of tool call handling may break existing functionality
  3. No-op callback: The empty processToolMessage callback should be documented

Apply this diff to fix the language consistency:

-          // parseSSE mới cho SiliconFlow response
+          // New parseSSE for SiliconFlow response
           (text: string, runTools: ChatMessageTool[]) => {
-            // Parse chuỗi JSON trả về thành đối tượng
+            // Parse JSON response string to object
             const json = JSON.parse(text);

-            // Lấy nội dung trả lời từ output.text
+            // Extract response content from output.text
             const content = json?.output?.text ?? "";

-            // 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 no response content, return non-thinking state with empty content
             if (!content || content.length === 0) {
               return {
                 isThinking: false,
                 content: "",
               };
             }

-            // Trả về trạng thái không suy nghĩ và nội dung trả lời
+            // Return non-thinking state with response content
             return {
               isThinking: false,
               content: content,
             };
           },
-          // processToolMessage: SiliconFlow không có tool_call nên giữ nguyên hoặc để rỗng
+          // processToolMessage: SiliconFlow does not support tool calls
           (
             requestPayload: RequestPayload,
             toolCallMessage: any,
             toolCallResult: any[],
           ) => {
-            // 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
+            // No tool call processing needed for SiliconFlow
           },

Please verify that removing tool call functionality doesn't break existing features:


🏁 Script executed:

#!/bin/bash
# Description: Check if SiliconFlow was previously using tool calls
# Expected: Find any references to tool calls in SiliconFlow context

echo "Searching for tool call usage in SiliconFlow context..."
rg -A 3 -B 3 "tool.*call.*siliconflow|siliconflow.*tool.*call" --ignore-case

echo "Searching for SiliconFlow model configurations..."
rg -A 5 "siliconflow.*tool|tool.*siliconflow" --ignore-case

echo "Checking for any SiliconFlow tool call tests..."
fd -e test.ts -e spec.ts --exec rg -l "siliconflow.*tool" {} \;

Length of output: 1488


Address code commenting language consistency and verify functional completeness.

The code has several issues that need attention:

  1. Language consistency: The Vietnamese comments should be in English to maintain codebase consistency
  2. Simplified logic: The removal of tool call handling may break existing functionality
  3. No-op callback: The empty processToolMessage callback should be documented

Apply this diff to fix the language consistency:

-          // parseSSE mới cho SiliconFlow response
+          // New parseSSE for SiliconFlow response
           (text: string, runTools: ChatMessageTool[]) => {
-            // Parse chuỗi JSON trả về thành đối tượng
+            // Parse JSON response string to object
             const json = JSON.parse(text);

-            // Lấy nội dung trả lời từ output.text
+            // Extract response content from output.text
             const content = json?.output?.text ?? "";

-            // 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 no response content, return non-thinking state with empty content
             if (!content || content.length === 0) {
               return {
                 isThinking: false,
                 content: "",
               };
             }

-            // Trả về trạng thái không suy nghĩ và nội dung trả lời
+            // Return non-thinking state with response content
             return {
               isThinking: false,
               content: content,
             };
           },
-          // processToolMessage: SiliconFlow không có tool_call nên giữ nguyên hoặc để rỗng
+          // processToolMessage: SiliconFlow does not support tool calls
           (
             requestPayload: RequestPayload,
             toolCallMessage: any,
             toolCallResult: any[],
           ) => {
-            // 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
+            // No tool call processing needed for SiliconFlow
           },

Please verify that removing tool call functionality doesn't break existing features:

#!/bin/bash
# Description: Check if SiliconFlow was previously using tool calls
# Expected: Find any references to tool calls in SiliconFlow context

echo "Searching for tool call usage in SiliconFlow context..."
rg -A 3 -B 3 "tool.*call.*siliconflow|siliconflow.*tool.*call" --ignore-case

echo "Searching for SiliconFlow model configurations..."
rg -A 5 "siliconflow.*tool|tool.*siliconflow" --ignore-case

echo "Checking for any SiliconFlow tool call tests..."
fd -e test.ts -e spec.ts --exec rg -l "siliconflow.*tool" {} \;
🤖 Prompt for AI Agents
In app/client/platforms/siliconflow.ts between lines 156 and 185, replace all
Vietnamese comments with English to maintain language consistency. Review the
removal of tool call handling in the processToolMessage callback to ensure it
does not break existing functionality by verifying if SiliconFlow previously
used tool calls. If no tool call usage is found, keep the callback as a no-op
but add a clear English comment explaining that it is intentionally left empty
for compatibility or future use.

options,
);
Expand Down
2 changes: 2 additions & 0 deletions app/config/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ declare global {
// alibaba only
ALIBABA_URL?: string;
ALIBABA_API_KEY?: string;
ALIBABA_APP_ID?: string; // alibaba app id, used for some models

// tencent only
TENCENT_URL?: string;
Expand Down Expand Up @@ -210,6 +211,7 @@ export const getServerSideConfig = () => {
isAlibaba,
alibabaUrl: process.env.ALIBABA_URL,
alibabaApiKey: getApiKey(process.env.ALIBABA_API_KEY),
alibabaAppId: process.env.ALIBABA_APP_ID,

isTencent,
tencentUrl: process.env.TENCENT_URL,
Expand Down
Loading