Unverified 提交 f4c7d614 authored 作者: Will Chen's avatar Will Chen 提交者: GitHub

Escape dyad tags inside thinking blocks (#229)

上级 5e86f4b5
...@@ -215,6 +215,7 @@ export function registerChatStreamHandlers() { ...@@ -215,6 +215,7 @@ export function registerChatStreamHandlers() {
abortController, abortController,
updatedChat, updatedChat,
); );
fullResponse = cleanThinkingByEscapingDyadTags(fullResponse);
} else { } else {
// Normal AI processing for non-test prompts // Normal AI processing for non-test prompts
const settings = readSettings(); const settings = readSettings();
...@@ -348,7 +349,10 @@ This conversation includes one or more image attachments. When the user uploads ...@@ -348,7 +349,10 @@ This conversation includes one or more image attachments. When the user uploads
...codebasePrefix, ...codebasePrefix,
...limitedMessageHistory.map((msg) => ({ ...limitedMessageHistory.map((msg) => ({
role: msg.role as "user" | "assistant" | "system", role: msg.role as "user" | "assistant" | "system",
content: msg.content, // Why remove thinking tags?
// Thinking tags are generally not critical for the context
// and eats up extra tokens.
content: removeThinkingTags(msg.content),
})), })),
]; ];
...@@ -411,6 +415,7 @@ This conversation includes one or more image attachments. When the user uploads ...@@ -411,6 +415,7 @@ This conversation includes one or more image attachments. When the user uploads
try { try {
for await (const textPart of textStream) { for await (const textPart of textStream) {
fullResponse += textPart; fullResponse += textPart;
fullResponse = cleanThinkingByEscapingDyadTags(fullResponse);
if ( if (
fullResponse.includes("$$SUPABASE_CLIENT_CODE$$") && fullResponse.includes("$$SUPABASE_CLIENT_CODE$$") &&
updatedChat.app?.supabaseProjectId updatedChat.app?.supabaseProjectId
...@@ -707,3 +712,27 @@ async function prepareMessageWithAttachments( ...@@ -707,3 +712,27 @@ async function prepareMessageWithAttachments(
content: contentParts, content: contentParts,
}; };
} }
function cleanThinkingByEscapingDyadTags(text: string): string {
// Extract content inside <think> </think> tags
const thinkRegex = /<think>([\s\S]*?)<\/think>/g;
return text.replace(thinkRegex, (match, content) => {
// We are replacing the opening tag with a look-alike character
// to avoid issues where thinking content includes dyad tags
// and are mishandled by:
// 1. FE markdown parser
// 2. Main process response processor
const processedContent = content
.replace(/<dyad/g, "<dyad")
.replace(/<\/dyad/g, "</dyad");
// Return the modified think tag with processed content
return `<think>${processedContent}</think>`;
});
}
function removeThinkingTags(text: string): string {
const thinkRegex = /<think>([\s\S]*?)<\/think>/g;
return text.replace(thinkRegex, "").trim();
}
...@@ -32,9 +32,28 @@ function createStreamChunk( ...@@ -32,9 +32,28 @@ function createStreamChunk(
return `data: ${JSON.stringify(chunk)}\n\n${isLast ? "data: [DONE]\n\n" : ""}`; return `data: ${JSON.stringify(chunk)}\n\n${isLast ? "data: [DONE]\n\n" : ""}`;
} }
const CANNED_MESSAGE = `
<think>
\`<dyad-write>\`:
I'll think about the problem and write a bug report.
<dyad-write>
<dyad-write path="file1.txt">
Fake dyad write
</dyad-write>
</think>
<dyad-write path="file1.txt">
A file (2)
</dyad-write>
More
EOM`;
// Handle POST requests to /v1/chat/completions // Handle POST requests to /v1/chat/completions
app.post("/v1/chat/completions", (req, res) => { app.post("/v1/chat/completions", (req, res) => {
const { stream = false, messages = [] } = req.body; const { stream = false, messages = [] } = req.body;
console.log("* Received messages", messages);
// Check if the last message contains "[429]" to simulate rate limiting // Check if the last message contains "[429]" to simulate rate limiting
const lastMessage = messages[messages.length - 1]; const lastMessage = messages[messages.length - 1];
...@@ -61,7 +80,7 @@ app.post("/v1/chat/completions", (req, res) => { ...@@ -61,7 +80,7 @@ app.post("/v1/chat/completions", (req, res) => {
index: 0, index: 0,
message: { message: {
role: "assistant", role: "assistant",
content: "hello world", content: CANNED_MESSAGE,
}, },
finish_reason: "stop", finish_reason: "stop",
}, },
...@@ -75,7 +94,7 @@ app.post("/v1/chat/completions", (req, res) => { ...@@ -75,7 +94,7 @@ app.post("/v1/chat/completions", (req, res) => {
res.setHeader("Connection", "keep-alive"); res.setHeader("Connection", "keep-alive");
// Split the "hello world" message into characters to simulate streaming // Split the "hello world" message into characters to simulate streaming
const message = "hello world"; const message = CANNED_MESSAGE;
const messageChars = message.split(""); const messageChars = message.split("");
// Stream each character with a delay // Stream each character with a delay
...@@ -94,7 +113,7 @@ app.post("/v1/chat/completions", (req, res) => { ...@@ -94,7 +113,7 @@ app.post("/v1/chat/completions", (req, res) => {
clearInterval(interval); clearInterval(interval);
res.end(); res.end();
} }
}, 100); }, 10);
}); });
// Start the server // Start the server
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论