feat(chat): 增加深度思考功能支持

refactor
钟良源 3 weeks ago
parent 84bd2d1617
commit 03828cfbb6

@ -5,6 +5,7 @@ let streamingMessageContent = ''; // 用于存储当前流式消息的内容
let currentReader = null; // 用于存储当前流的reader以便可以取消 let currentReader = null; // 用于存储当前流的reader以便可以取消
let isRequestInProgress = false; // 标记是否有请求正在进行 let isRequestInProgress = false; // 标记是否有请求正在进行
let currentSessionHistory = []; // 存储当前会话历史 let currentSessionHistory = []; // 存储当前会话历史
let currentExplanationElement = null; // 存储当前的思考内容元素
const vscode = acquireVsCodeApi(); const vscode = acquireVsCodeApi();
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
@ -180,6 +181,55 @@ document.addEventListener('DOMContentLoaded', () => {
chatBox.appendChild(streamingMessageElement); chatBox.appendChild(streamingMessageElement);
streamingMessageContent = ''; streamingMessageContent = '';
currentExplanationElement = null; // 重置思考内容元素
// 滚动到底部
chatBox.scrollTop = chatBox.scrollHeight;
}
// 显示思考内容
function showExplanation(explanationText) {
if (!streamingMessageElement) return;
// 创建可折叠的思考内容框
const explanationContainer = document.createElement('details');
explanationContainer.className = 'explanation-container';
explanationContainer.open = true; // 默认展开
const summary = document.createElement('summary');
summary.className = 'explanation-summary';
summary.textContent = '💭 思考过程';
const explanationContent = document.createElement('div');
explanationContent.className = 'explanation-content';
// 使用 marked 渲染 Markdown 内容
if (marked && marked.parse) {
explanationContent.innerHTML = marked.parse(explanationText);
} else {
explanationContent.textContent = explanationText;
}
explanationContainer.appendChild(summary);
explanationContainer.appendChild(explanationContent);
// 将思考内容插入到流式消息内容之前
const contentDiv = streamingMessageElement.querySelector('.streaming-content');
if (contentDiv) {
streamingMessageElement.insertBefore(explanationContainer, contentDiv);
} else {
streamingMessageElement.insertBefore(explanationContainer, streamingMessageElement.firstChild);
}
// 高亮思考内容中的代码块
const codeBlocks = explanationContent.querySelectorAll('pre code');
codeBlocks.forEach(block => {
if (hljs) {
hljs.highlightElement(block);
}
});
currentExplanationElement = explanationContainer;
// 滚动到底部 // 滚动到底部
chatBox.scrollTop = chatBox.scrollHeight; chatBox.scrollTop = chatBox.scrollHeight;
@ -454,6 +504,9 @@ document.addEventListener('DOMContentLoaded', () => {
} else if (message.command === 'startStream') { } else if (message.command === 'startStream') {
// 开始流式传输 // 开始流式传输
startStreaming(); startStreaming();
} else if (message.command === 'explanation') {
// 显示思考内容
showExplanation(message.data);
} else if (message.command === 'streamData') { } else if (message.command === 'streamData') {
// 处理流式数据 // 处理流式数据
handleStreamData(message.data); handleStreamData(message.data);

@ -547,3 +547,57 @@ code {
color: var(--vscode-foreground); color: var(--vscode-foreground);
font-weight: 500; font-weight: 500;
} }
/* 思考内容框样式 */
.explanation-container {
margin-bottom: 12px;
border: 1px solid var(--vscode-editorWidget-border);
border-radius: 4px;
background-color: rgba(0, 122, 204, 0.05);
overflow: hidden;
}
.explanation-summary {
padding: 8px 12px;
cursor: pointer;
font-weight: 600;
font-size: 13px;
color: var(--vscode-foreground);
background-color: rgba(0, 122, 204, 0.1);
border-bottom: 1px solid var(--vscode-editorWidget-border);
user-select: none;
list-style: none;
display: flex;
align-items: center;
transition: background-color 0.2s;
}
.explanation-summary:hover {
background-color: rgba(0, 122, 204, 0.15);
}
.explanation-summary::-webkit-details-marker {
display: none;
}
.explanation-summary::before {
content: '▶';
display: inline-block;
margin-right: 6px;
transition: transform 0.2s;
font-size: 10px;
}
.explanation-container[open] .explanation-summary::before {
transform: rotate(90deg);
}
.explanation-content {
padding: 10px 12px;
font-size: 13px;
line-height: 1.6;
color: var(--vscode-foreground);
white-space: pre-wrap;
word-wrap: break-word;
background-color: var(--vscode-editor-background);
}

@ -64,7 +64,7 @@ export class MessageHandler {
this.handleRestoreState(message); this.handleRestoreState(message);
break; break;
case 'log' : case 'log' :
console.log( ...message.data); console.log(...message.data);
break; break;
} }
} }
@ -99,14 +99,46 @@ export class MessageHandler {
if (done) break; if (done) break;
const chunk = decoder.decode(value, {stream: true}); const chunk = decoder.decode(value, {stream: true});
console.log("chunk:",chunk) console.log("chunk:", chunk)
// 检查是否包含 event: explanation
if (chunk.includes('event: explanation')) {
let explanationChunk = chunk;
// 直接提取 data: 后面的所有内容
const explanationIndex = chunk.indexOf('data: ');
explanationChunk = chunk.substring(explanationIndex, explanationChunk.length - 1);
// 处理截取后的内容
if (explanationChunk.startsWith('data:')) {
// 移除data:前缀
explanationChunk = explanationChunk.replace(/^data:/, '');
// 逐个匹配换行符,当出现两个或更多连续换行符时删除多余的
explanationChunk = explanationChunk.replace(/^(\n{2,})/, (match) => {
// 删除所有连续的换行符
return '';
});
}
if (explanationChunk) {
let explanationText = explanationChunk;
console.log("explanationText:", explanationText);
// 发送 explanation 到前端
this.provider._postMessage({
command: 'explanation',
data: explanationText
});
}
continue; // 跳过这个 chunk 的其他处理
}
// 检查是否包含event: end事件 // 检查是否包含event: end事件
let processedChunk = chunk; let processedChunk = chunk;
if (chunk.includes('event: end')) { if (chunk.includes('event: end')) {
// 只处理event: end之前的内容 // 只处理event: end之前的内容
const endIndex = chunk.indexOf('event: end'); const endIndex = chunk.indexOf('event: end');
processedChunk = chunk.substring(0, endIndex); processedChunk = chunk.substring(0, endIndex);
// 处理截取后的内容 // 处理截取后的内容
if (processedChunk.startsWith('data:')) { if (processedChunk.startsWith('data:')) {
// 移除data:前缀 // 移除data:前缀
@ -117,21 +149,21 @@ export class MessageHandler {
return ''; return '';
}); });
} }
if (processedChunk) { if (processedChunk) {
aiMessage += processedChunk; aiMessage += processedChunk;
// 发送流式数据到前端 // 发送流式数据到前端
this.provider._postMessage({ this.provider._postMessage({
command: 'streamData', command: 'streamData',
data: processedChunk data: processedChunk
}); });
} }
// 停止继续处理 // 停止继续处理
break; break;
} }
if (chunk.startsWith('data:')) { if (chunk.startsWith('data:')) {
// 移除data:前缀 // 移除data:前缀
processedChunk = chunk.replace(/^data:/, ''); processedChunk = chunk.replace(/^data:/, '');
@ -180,14 +212,14 @@ export class MessageHandler {
// 解析AI响应中的多个代码块并生成独立的codeDiff信息 // 解析AI响应中的多个代码块并生成独立的codeDiff信息
const codeBlocks = aiMessage.match(/```[\s\S]*?```/g) || []; const codeBlocks = aiMessage.match(/```[\s\S]*?```/g) || [];
// console.log("codeBlocks:",codeBlocks) // console.log("codeBlocks:",codeBlocks)
// 存储所有生成的codeDiff // 存储所有生成的codeDiff
const allCodeDiffs = []; const allCodeDiffs = [];
for (const block of codeBlocks) { for (const block of codeBlocks) {
// 生成codeDiff // 生成codeDiff
const codeDiff = generateCodeDiff(fileContent, block); const codeDiff = generateCodeDiff(fileContent, block);
if (codeDiff) { if (codeDiff) {
// @ts-ignore // @ts-ignore
allCodeDiffs.push(codeDiff); allCodeDiffs.push(codeDiff);
@ -210,10 +242,10 @@ export class MessageHandler {
for (let i = 0; i < allCodeDiffs.length; i++) { for (let i = 0; i < allCodeDiffs.length; i++) {
const codeDiff = allCodeDiffs[i]; const codeDiff = allCodeDiffs[i];
const filePath = message.fileContentPath; const filePath = message.fileContentPath;
// 创建唯一的工作区变更键 // 创建唯一的工作区变更键
const changeKey = `${filePath}`; const changeKey = `${filePath}`;
workspaceChanges[changeKey] = { workspaceChanges[changeKey] = {
original: fileContent, original: fileContent,
@ -231,13 +263,17 @@ export class MessageHandler {
} }
} }
} catch (error: any) { } catch (error: any) {
console.log("error:",error) console.log("error:", error)
// 检查是否是由于取消请求导致的中断 // 检查是否是由于取消请求导致的中断
if (error.name === 'AbortError' || error.message.includes('cancel') || error.message.includes('中断')) { if (error.name === 'AbortError' || error.message.includes('cancel') || error.message.includes('中断')) {
this.provider._postMessage({command: 'requestCancelled'}); this.provider._postMessage({command: 'requestCancelled'});
} else if (error.message.includes('terminated') || error.message.includes('other side closed') || error.name === 'TypeError') { } else if (error.message.includes('terminated') || error.message.includes('other side closed') || error.name === 'TypeError') {
// 处理网络连接中断错误,但仍显示已接收的内容 // 处理网络连接中断错误,但仍显示已接收的内容
this.provider._postMessage({command: 'addMessage', role: 'ai', content: '网络连接中断,但以下为已接收的内容:'}); this.provider._postMessage({
command: 'addMessage',
role: 'ai',
content: '网络连接中断,但以下为已接收的内容:'
});
// 如果有已接收的内容,也显示出来 // 如果有已接收的内容,也显示出来
// 通知前端流传输完成 // 通知前端流传输完成
this.provider._postMessage({ this.provider._postMessage({

@ -11,7 +11,7 @@ function getApiConfig(context: vscode.ExtensionContext, enableDeepThinking: bool
// 根据是否启用深度思考选择不同的端点 // 根据是否启用深度思考选择不同的端点
const endpoint = enableDeepThinking const endpoint = enableDeepThinking
? '/comp/api/v1/chat/completions-stream-cot' ? '/comp/api/v1/chat/completions-stream-cot-v2'
: '/comp/api/v1/chat/completions-stream'; : '/comp/api/v1/chat/completions-stream';
// 如果用户配置了完整URL优先使用 // 如果用户配置了完整URL优先使用
@ -107,9 +107,10 @@ export async function callQwenAPI(
} }
}; };
// 如果启用深度思考,添加 enable_cot 参数 // 如果启用深度思考,添加 enable_cot 和 enable_explanation 参数
if (enableDeepThinking) { if (enableDeepThinking) {
requestBody.enable_cot = true; requestBody.enable_cot = true;
requestBody.enable_explanation = true;
} }
const params = { const params = {

Loading…
Cancel
Save