Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add 3 type of reasoning_content support (+deepseek-r1@OpenAI @Alibaba @ByteDance), parse <think></think> from SSE #6204

Merged

Conversation

bestsanmao
Copy link
Contributor

@bestsanmao bestsanmao commented Feb 11, 2025

支持阿里和字节跳动的deepseek-r1的思考过程

+deepseek-r1@Alibaba
+deepseek-r1@ByteDance
+deepseek-r1@OpenAI
模式的添加
并非指定阿里或字节的地址到DEEPSEEK_BASE_URL
这样不耽误同时使用deepseek官方的模型

增加支持兼容流式 <think></think> 标签的思考内容

鉴于各个provider都有共享的model(比如r1)重写超时逻辑

solved #6183 #6200 #6212

Summary by CodeRabbit

Summary by CodeRabbit

  • New Features
    • Enhanced chat interactions with improved streaming responses that clearly differentiate between processing (thinking) states and final outputs.
    • Smoother integration of interactive tool functions for more responsive and accurate conversations.
    • Refined message flow and error handling deliver a more intuitive and reliable chat experience.
    • Dynamic timeout handling based on model configuration for improved flexibility in request management.
    • New mechanism for handling tool messages within chat requests, enhancing functionality.

Copy link

vercel bot commented Feb 11, 2025

@bestsanmao is attempting to deploy a commit to the NextChat Team on Vercel.

A member of the Team first needs to authorize it.

Copy link
Contributor

coderabbitai bot commented Feb 11, 2025

Walkthrough

This update refines the chat functionality across multiple platform modules by enhancing the chat logic in their respective APIs. Key modifications include the addition of imports such as ChatMessageTool and usePluginStore, and adjustments in message processing based on the sender's role. The previous streaming mechanisms have been replaced with a unified call to streamWithThink, while error handling and content validation have been streamlined. These changes affect methods in the QwenApi, DoubaoApi, and ChatGPTApi classes, ensuring structured handling of streamed responses.

Changes

File(s) Change Summary
app/client/.../alibaba.ts and app/client/.../bytedance.ts - Added new imports: ChatMessageTool and usePluginStore
- Updated chat methods to process messages differently for the "assistant" role using getMessageTextContentWithoutThinking
- Replaced manual streaming logic with streamWithThink
- Improved response validation and error handling with structured responses
app/client/.../openai.ts - Renamed method from stream to streamWithThink
- Enhanced response handling to differentiate between reasoning and regular content
app/utils/.../chat.ts - Added logic to handle <think> and </think> tags in messages within streamWithThink
- Introduced variable to track thinking state
app/client/.../baidu.ts, app/client/.../deepseek.ts, app/client/.../glm.ts, app/client/.../google.ts, app/client/.../siliconflow.ts, app/client/.../tencent.ts, app/client/.../xai.ts - Removed REQUEST_TIMEOUT_MS imports
- Added getTimeoutMSByModel imports
- Updated timeout handling in chat methods to use getTimeoutMSByModel(options.config.model)

Possibly related PRs

  • Fix formatting of reasoning model on SiliconFlow #6186: The changes in this PR both involve modifications to the handling of reasoning and content within chat methods, focusing on evaluation and processing.
  • fix several bugs #6172: Related to the enhancements in the chat method of the QwenApi class, which align with modifications in the DoubaoApi class regarding message processing logic and integration of streamWithThink.

Poem

I’m a code-hopping rabbit, light on my feet,
Streaming new messages that are crisp and neat.
With plugins in tow and logic so bright,
My whiskers twitch as I hop through the night.
A joyful dance in code, pure delight!
🐇✨


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 476d946 and cf140d4.

📒 Files selected for processing (1)
  • app/client/platforms/siliconflow.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/client/platforms/siliconflow.ts

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@bestsanmao bestsanmao changed the title support alibaba and bytedance's reasoning_content (+deepseek-r1@Alibaba and +deepseek-r1@ByteDance) support r1 on alibaba and bytedance's reasoning_content (+deepseek-r1@Alibaba and +deepseek-r1@ByteDance) Feb 11, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (3)
app/client/platforms/bytedance.ts (1)

179-208: Selective usage of reasoning vs. content.
Current logic prioritizes reasoning_content whenever present, ignoring content if both exist. If partial merging is desired, handle both. Otherwise, this looks fine for an either/or approach.

app/client/platforms/alibaba.ts (2)

175-192: Tool calls merging logic.
Appending or updating arguments based on ID or index is sound. Just verify that partial arguments are re-merged safely if subsequent calls modify them further.


193-223: Reasoning vs. content usage.
Same approach as bytedance.ts, returning reasoning if present. Carefully verify user expectations about partial or combined content.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a029b43 and 98a11e5.

📒 Files selected for processing (2)
  • app/client/platforms/alibaba.ts (5 hunks)
  • app/client/platforms/bytedance.ts (4 hunks)
🔇 Additional comments (11)
app/client/platforms/bytedance.ts (5)

8-14: Imports look consistent.
These newly introduced store imports appear valid and are referenced within the file. No immediate concerns.


25-25: New utility import recognized.
The streamWithThink import is used to manage streaming responses. Implementation references look correct.


28-28: Import usage check.
getMessageTextContentWithoutThinking is properly used to handle message content for the assistant role.


92-95: Role-based content handling.
Switching to getMessageTextContentWithoutThinking for assistant messages, and awaiting preProcessImageContent for other roles, is logically sound. However, ensure both functions handle all edge cases (e.g., empty content).


137-142: Plugin store usage verification.
Retrieving tools and functions via usePluginStore for streaming is a good approach. Confirm that getAsTools returns objects compatible with the subsequent logic.

app/client/platforms/alibaba.ts (6)

8-14: Imports approved.
The additional store imports are consistent with the new streaming logic and plugin features.


15-15: Stream utility import.
streamWithThink is referenced correctly and matches the new streaming implementation.


25-28: Added message content utilities.
getMessageTextContent and getMessageTextContentWithoutThinking are now imported, which aligns with the refined role-based message handling below.


98-101: Role-based content extraction.
Using getMessageTextContentWithoutThinking for the assistant role prevents the “thinking” portion from appearing in final responses. This is a neat approach. Ensure it meets UX requirements.


131-134: SSE header management.
Conditionally adding "X-DashScope-SSE": "enable" is a clean way to toggle streaming. Just verify servers handle/expect this header properly.


151-156: Integration with plugin store.
usePluginStore.getAsTools(...) usage is consistent with the approach in bytedance.ts. No immediate issues spotted.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
app/client/platforms/alibaba.ts (1)

177-194: 🛠️ Refactor suggestion

Maintain consistency with ByteDance implementation.

Apply the same type safety improvements as suggested for the ByteDance implementation.

-              const index = tool_calls[0]?.index;
-              const id = tool_calls[0]?.id;
-              const args = tool_calls[0]?.function?.arguments;
+              const toolCall = tool_calls[0];
+              if (!toolCall) return;
+              const { index, id, function: func } = toolCall;
+              const args = func?.arguments;
               if (id) {
🧹 Nitpick comments (1)
app/client/platforms/bytedance.ts (1)

136-142: Consider adding type safety for plugin tools.

The as any type assertion for tools could mask type errors.

-          tools as any,
+          tools as ChatMessageTool[],
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 98a11e5 and b0758cc.

📒 Files selected for processing (2)
  • app/client/platforms/alibaba.ts (5 hunks)
  • app/client/platforms/bytedance.ts (6 hunks)
🔇 Additional comments (6)
app/client/platforms/bytedance.ts (3)

8-14: LGTM! Interface and imports are well-structured.

The renamed interface and new imports provide better type safety and plugin support.

Also applies to: 25-25, 40-52


92-95: LGTM! Improved message content processing.

The conditional processing correctly handles assistant messages by removing thinking content.


214-225: 🛠️ Refactor suggestion

Remove @ts-ignore by adding proper type safety.

The type issues should be resolved rather than ignored.

-              // @ts-ignore
-              runTools[index]["function"]["arguments"] += args;
+              if (index !== undefined && index < runTools.length) {
+                const tool = runTools[index];
+                if (tool?.function) {
+                  tool.function.arguments = (tool.function.arguments || '') + (args || '');
+                }
+              }

Likely an incorrect or invalid review comment.

app/client/platforms/alibaba.ts (3)

8-15: LGTM! Well-structured imports and streaming setup.

The SSE header is correctly configured based on the streaming flag.

Also applies to: 131-134


98-101: LGTM! Clear role-based content processing.

The conditional processing correctly differentiates between assistant and other roles.


200-226: LGTM! Consistent error handling.

The implementation correctly handles empty content and reasoning states.

Comment on lines +164 to 181
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;
}
}
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

Add safety checks for tool calls processing.

The code directly accesses array indices and properties without proper validation.

-              const index = tool_calls[0]?.index;
-              const id = tool_calls[0]?.id;
-              const args = tool_calls[0]?.function?.arguments;
+              const toolCall = tool_calls[0];
+              if (!toolCall) return;
+              const { index, id, function: func } = toolCall;
+              const args = func?.arguments;
               if (id) {
📝 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
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;
}
}
if (tool_calls?.length > 0) {
const toolCall = tool_calls[0];
if (!toolCall) return;
const { index, id, function: func } = toolCall;
const args = func?.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;
}
}

@bestsanmao bestsanmao changed the title support r1 on alibaba and bytedance's reasoning_content (+deepseek-r1@Alibaba and +deepseek-r1@ByteDance) add 3 type of reasoning_content support (+deepseek-r1@OpenAI @Alibaba @ByteDance), parse <think></think> from SSE Feb 11, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (4)
app/utils/chat.ts (2)

584-599: Consider extracting tag processing logic into a separate function.

The tag processing logic is complex and could benefit from being extracted into a dedicated function for better maintainability and testability.

Here's a suggested refactor:

+ function processThinkingTags(chunk: { isThinking: boolean, content: string }, lastIsThinkingTagged: boolean) {
+   if (!chunk.isThinking) {
+     if (chunk.content.startsWith("<think>")) {
+       chunk.isThinking = true;
+       chunk.content = chunk.content.slice(7).trim();
+       lastIsThinkingTagged = true;
+     } else if (chunk.content.endsWith("</think>")) {
+       chunk.isThinking = false;
+       chunk.content = chunk.content.slice(0, -8).trim();
+       lastIsThinkingTagged = false;
+     } else if (lastIsThinkingTagged) {
+       chunk.isThinking = true;
+     }
+   }
+   return { chunk, lastIsThinkingTagged };
+ }

- // deal with <think> and </think> tags start
- if (!chunk.isThinking) {
-   if (chunk.content.startsWith("<think>")) {
-     chunk.isThinking = true;
-     chunk.content = chunk.content.slice(7).trim();
-     lastIsThinkingTagged = true;
-   } else if (chunk.content.endsWith("</think>")) {
-     chunk.isThinking = false;
-     chunk.content = chunk.content.slice(0, -8).trim();
-     lastIsThinkingTagged = false;
-   } else if (lastIsThinkingTagged) {
-     chunk.isThinking = true;
-   }
- }
- // deal with <think> and </think> tags start
+ const { chunk: processedChunk, lastIsThinkingTagged: newLastIsThinkingTagged } = 
+   processThinkingTags(chunk, lastIsThinkingTagged);
+ chunk = processedChunk;
+ lastIsThinkingTagged = newLastIsThinkingTagged;

600-631: Consider adding unit tests for thinking mode state transitions.

The thinking mode state management logic is complex with multiple state transitions. Adding comprehensive unit tests would help ensure reliability.

Would you like me to help generate unit tests for the thinking mode state transitions? The tests would cover:

  • Transition from normal to thinking mode
  • Transition from thinking to normal mode
  • Handling of newlines in thinking content
  • Edge cases with empty content
app/client/platforms/openai.ts (2)

297-303: Consider adding resilience to SSE parsing.
Within the streamWithThink invocation, JSON.parse(text) could throw if upstream JSON is malformed or incomplete. Adding a try/catch mechanism, or validating the text before parsing, may help avoid runtime errors. Also, the cast tools as any could be replaced with more precise TypeScript types for stronger safety.

Proposed snippet for safer parsing (inside the callback):

   // parseSSE
   (text: string, runTools: ChatMessageTool[]) => {
-    const json = JSON.parse(text);
+    let json;
+    try {
+      json = JSON.parse(text);
+    } catch (err) {
+      console.error("Invalid JSON from SSE:", text, err);
+      return { isThinking: false, content: "" };
+    }

    const choices = json.choices as Array<{
      delta: {
        content: string;
        tool_calls: ChatMessageTool[];
        reasoning_content: string | null;
      };
    }>;

352-367: Reduce duplicated code in conditional returns.
The code separately handles reasoning and content, returning nearly identical data structures. You can streamline it to make the logic more concise and maintainable.

Example refactor:

            if ((reasoning?.trim().length ?? 0) > 0) {
              return {
                isThinking: true,
                content: reasoning,
              };
            }
-           else if (content && content.trim().length > 0) {
+           if ((content?.trim().length ?? 0) > 0) {
              return {
                isThinking: false,
                content: content,
              };
            }
            return {
              isThinking: false,
              content: "",
            };
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b0758cc and 9714258.

📒 Files selected for processing (2)
  • app/client/platforms/openai.ts (4 hunks)
  • app/utils/chat.ts (2 hunks)
🔇 Additional comments (5)
app/utils/chat.ts (2)

403-403: LGTM: Clear variable naming.

The lastIsThinkingTagged boolean flag clearly indicates its purpose of tracking the state between <think> and </think> tags.


632-635: LGTM: Improved error handling.

Good improvement in error handling by logging parse errors without throwing them, preventing stream interruption due to malformed messages.

app/client/platforms/openai.ts (3)

25-25: New import usage looks good.
The addition of streamWithThink is aligned with the rest of the changes and appears correctly imported from @/app/utils/chat.


312-316: Validate new reasoning_content property.
The new field reasoning_content is typed as string | null. Verify the backend consistently returns a valid string or null to avoid undefined behavior if the server response deviates from this format. The short-circuit logic for if (!choices?.length) is a solid safeguard.


339-350: Skipping empty SSE chunks is a good optimization.
By discarding events that have neither reasoning_content nor content, you avoid sending extraneous updates to the user. This is a clean approach to filtering out irrelevant or empty SSE chunks.

@wyxogo
Copy link
Contributor

wyxogo commented Feb 11, 2025

Could you support reasoning_content of Deepseek/deepseek-r1@OpenRouter

@bestsanmao
Copy link
Contributor Author

Could you support reasoning_content of Deepseek/deepseek-r1@OpenRouter

我试了一下openrouter的api
好像它并不返回 reasoning_content 也不会返回 标签

@bestsanmao
Copy link
Contributor Author

some useful url: https://www.reddit.com/r/OpenWebUI/comments/1ifm00i/i_am_using_deepseek_r1_via_open_router_how_do_i/ https://openwebui.com/f/roneru/openrouter_reasoning_tokens_pipe

我测试了一下
openrouter支持一个请求参数 include_reasoning
但是我测试发现 只有非流式 才有reasoning结果

@Issues-translate-bot
Copy link

Bot detected the issue body's language is not English, translate it automatically.


some useful url: https://www.reddit.com/r/OpenWebUI/comments/1ifm00i/i_am_using_deepseek_r1_via_open_router_how_do_i/ https://openwebui.com/f/roneru/openrouter_reasoning_tokens_pipe

I've tested it
openrouter supports a request parameter including_reasoning
But I found that only non-streaming methods can have reasonable results

@wyxogo
Copy link
Contributor

wyxogo commented Feb 12, 2025

Maybe non-streaming could be accepted. I also found the OpenRouter official chat room supports stream reasoning response.
Some related discussions:
https://www.reddit.com/r/LocalLLaMA/comments/1idsaah/when_querying_r1_through_openrouter_do_you_guys/
huggingface/chat-ui#1664
continuedev/continue#3946

@CaesarC
Copy link

CaesarC commented Feb 12, 2025

image

这个修改会导致输出的markdown格式少了空格

@Issues-translate-bot
Copy link

Bot detected the issue body's language is not English, translate it automatically.


image

This modification will cause the output markdown format to be missing spaces

@bestsanmao
Copy link
Contributor Author

image

这个修改会导致输出的markdown格式少了空格

确实会吞空格
马上查一下原因修复一下

@Issues-translate-bot
Copy link

Bot detected the issue body's language is not English, translate it automatically.


image

This modification will cause the output markdown format to be missing spaces

Will indeed swallow spaces
Check the reason now and fix it

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🔭 Outside diff range comments (2)
app/client/platforms/deepseek.ts (1)

144-162: 🛠️ Refactor suggestion

Add safety checks for tool calls processing.

The code directly accesses array indices and properties without proper validation.

-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;
+const tool_calls = choices[0]?.delta?.tool_calls;
+if (tool_calls?.length > 0) {
+  const toolCall = tool_calls[0];
+  if (!toolCall) return;
+  const { index, id, function: func } = toolCall;
+  const args = func?.arguments;
app/client/platforms/glm.ts (1)

257-265: 🛠️ Refactor suggestion

Remove type assertions and improve type safety.

The code uses @ts-ignore and unsafe array operations.

-            // @ts-ignore
-            requestPayload?.messages?.splice(
-              // @ts-ignore
-              requestPayload?.messages?.length,
-              0,
-              toolCallMessage,
-              ...toolCallResult,
-            );
+            if ('messages' in requestPayload && Array.isArray(requestPayload.messages)) {
+              requestPayload.messages.splice(
+                requestPayload.messages.length,
+                0,
+                toolCallMessage,
+                ...toolCallResult,
+              );
+            }
♻️ Duplicate comments (1)
app/client/platforms/bytedance.ts (1)

162-179: 🛠️ Refactor suggestion

Add safety checks for tool calls processing.

The code directly accesses array indices and properties without proper validation.

-const index = tool_calls[0]?.index;
-const id = tool_calls[0]?.id;
-const args = tool_calls[0]?.function?.arguments;
+const toolCall = tool_calls[0];
+if (!toolCall) return;
+const { index, id, function: func } = toolCall;
+const args = func?.arguments;
🧹 Nitpick comments (4)
app/client/platforms/alibaba.ts (1)

94-97: Improve content extraction logic.

The content extraction logic could be simplified using a ternary operator.

-      content:
-        v.role === "assistant"
-          ? getMessageTextContentWithoutThinking(v)
-          : getMessageTextContent(v),
+      content: v.role === "assistant" ? getMessageTextContentWithoutThinking(v) : getMessageTextContent(v),
app/client/platforms/baidu.ts (1)

241-249: Improve error handling in stream processing.

The error handling in the stream processing could be more informative.

             try {
               const json = JSON.parse(text);
               const delta = json?.result;
               if (delta) {
                 remainText += delta;
               }
             } catch (e) {
-              console.error("[Request] parse error", text, msg);
+              console.error("[Request] Failed to parse stream message:", {
+                error: e,
+                text,
+                message: msg,
+              });
             }
app/utils.ts (1)

299-311: Consider improving model name matching logic.

While the function works correctly, there are a few improvements that could make it more maintainable:

Consider applying these changes:

 export function getTimeoutMSByModel(model: string) {
   model = model.toLowerCase();
+  const thinkingModels = [
+    'dall-e',
+    'dalle',
+    'o1',
+    'o3',
+    'deepseek-r',
+    '-thinking'
+  ];
-  if (
-    model.startsWith("dall-e") ||
-    model.startsWith("dalle") ||
-    model.startsWith("o1") ||
-    model.startsWith("o3") ||
-    model.includes("deepseek-r") ||
-    model.includes("-thinking")
-  )
+  if (thinkingModels.some(prefix => 
+    prefix.endsWith('-') ? model.startsWith(prefix) : model.includes(prefix)
+  ))
     return REQUEST_TIMEOUT_MS_FOR_THINKING;
   return REQUEST_TIMEOUT_MS;
 }

Benefits:

  1. Easier to maintain the list of models that require longer timeouts
  2. Cleaner logic for prefix vs substring matching
  3. More efficient string operations
app/client/platforms/openai.ts (1)

297-368: Enhance error handling in streaming response parsing.

The streaming response parsing logic has been improved to handle reasoning content, but there are a few areas that could be enhanced:

  1. The error handling for malformed JSON responses could be more robust
  2. The type definitions for the response structure could be more explicit

Consider applying these changes:

+interface StreamChoice {
+  delta: {
+    content?: string;
+    tool_calls?: ChatMessageTool[];
+    reasoning_content?: string | null;
+  };
+}
+
+interface StreamResponse {
+  isThinking: boolean;
+  content: string;
+}

-const json = JSON.parse(text);
+let json;
+try {
+  json = JSON.parse(text);
+} catch (e) {
+  console.error('Failed to parse SSE message:', e);
+  return { isThinking: false, content: '' };
+}

-const choices = json.choices as Array<{
-  delta: {
-    content: string;
-    tool_calls: ChatMessageTool[];
-    reasoning_content: string | null;
-  };
-}>;
+const choices = json.choices as StreamChoice[];
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9714258 and 476d946.

📒 Files selected for processing (11)
  • app/client/platforms/alibaba.ts (4 hunks)
  • app/client/platforms/baidu.ts (3 hunks)
  • app/client/platforms/bytedance.ts (6 hunks)
  • app/client/platforms/deepseek.ts (4 hunks)
  • app/client/platforms/glm.ts (3 hunks)
  • app/client/platforms/google.ts (3 hunks)
  • app/client/platforms/openai.ts (6 hunks)
  • app/client/platforms/siliconflow.ts (3 hunks)
  • app/client/platforms/tencent.ts (3 hunks)
  • app/client/platforms/xai.ts (3 hunks)
  • app/utils.ts (2 hunks)
🔇 Additional comments (10)
app/client/platforms/xai.ts (1)

105-108: LGTM! Improved timeout handling.

The change to use getTimeoutMSByModel for dynamic timeout configuration is a good improvement that aligns with the standardized approach across the codebase.

app/client/platforms/siliconflow.ts (1)

119-123: LGTM! Improved timeout handling and streaming support.

The changes to use getTimeoutMSByModel and streamWithThink are well-implemented and align with the standardized approach across the codebase.

app/client/platforms/bytedance.ts (2)

38-50: LGTM! Improved type safety with ByteDance-specific interface.

The renaming of RequestPayload to RequestPayloadForByteDance improves type safety by making the interface specific to ByteDance's requirements.


159-159: LGTM! Added safety check for choices array.

The addition of the length check for the choices array prevents potential runtime errors.

app/client/platforms/tencent.ts (1)

140-143: LGTM: Dynamic timeout based on model.

The change to use getTimeoutMSByModel instead of a static timeout is a good improvement as it allows for model-specific timeout values.

app/client/platforms/baidu.ts (1)

151-154: LGTM: Model-specific timeout implementation.

The implementation of model-specific timeout is consistent with other platform implementations.

app/client/platforms/glm.ts (1)

191-194: LGTM: Consistent timeout handling.

The timeout implementation is consistent with other platform implementations.

app/client/platforms/google.ts (1)

25-25: LGTM! Dynamic timeout handling is a good improvement.

The change from static timeout constants to dynamic timeout calculation based on the model type is a good improvement. This allows for more flexible timeout handling, especially for models that require longer processing times.

Also applies to: 204-205

app/client/platforms/openai.ts (2)

24-24: LGTM! Import changes align with new functionality.

The imports have been updated to support the new reasoning content feature and dynamic timeout handling.

Also applies to: 44-44


397-399: LGTM! Dynamic timeout handling is consistent.

The change to use getTimeoutMSByModel for timeout calculation is consistent with the changes in other files.

};
}>;

if (!choices?.length) return { isThinking: false, content: "" };
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

Verify array access safety.

The code accesses array elements without checking if the array exists or has elements.

-            if (!choices?.length) return { isThinking: false, content: "" };
-
-            const tool_calls = choices[0]?.message?.tool_calls;
+            if (!choices?.length || !choices[0]?.message) {
+              return { isThinking: false, content: "" };
+            }
+            const tool_calls = choices[0].message.tool_calls;

Also applies to: 173-190

Comment on lines +230 to +235
requestPayload?.input?.messages?.splice(
requestPayload?.input?.messages?.length,
0,
toolCallMessage,
...toolCallResult,
);
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

Improve array manipulation safety.

The splice operation could fail if the messages array is undefined.

-            requestPayload?.input?.messages?.splice(
-              requestPayload?.input?.messages?.length,
-              0,
-              toolCallMessage,
-              ...toolCallResult,
-            );
+            if (requestPayload?.input?.messages) {
+              requestPayload.input.messages.splice(
+                requestPayload.input.messages.length,
+                0,
+                toolCallMessage,
+                ...toolCallResult,
+              );
+            }
📝 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
requestPayload?.input?.messages?.splice(
requestPayload?.input?.messages?.length,
0,
toolCallMessage,
...toolCallResult,
);
if (requestPayload?.input?.messages) {
requestPayload.input.messages.splice(
requestPayload.input.messages.length,
0,
toolCallMessage,
...toolCallResult,
);
}

Comment on lines +188 to +190
// @ts-ignore
runTools[index]["function"]["arguments"] += args;
}
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 type assertion and improve type safety.

The code uses @ts-ignore to bypass type checking, which could lead to runtime errors.

-                // @ts-ignore
-                runTools[index]["function"]["arguments"] += args;
+                if (index !== undefined && index < runTools.length && runTools[index]?.function) {
+                  runTools[index].function.arguments += args;
+                }
📝 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
// @ts-ignore
runTools[index]["function"]["arguments"] += args;
}
if (index !== undefined && index < runTools.length && runTools[index]?.function) {
runTools[index].function.arguments += args;
}

@bestsanmao
Copy link
Contributor Author

Bot detected the issue body's language is not English, translate it automatically.

image

This modification will cause the output markdown format to be missing spaces

您现在再pull一下试试

@Leizhenpeng Leizhenpeng merged commit 12863f5 into ChatGPTNextWeb:main Feb 13, 2025
1 check failed
@Leizhenpeng
Copy link
Member

感谢~

@Issues-translate-bot
Copy link

Bot detected the issue body's language is not English, translate it automatically.


Thanks ~

@bestsanmao bestsanmao deleted the ali_bytedance_reasoning_content branch February 13, 2025 06:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants