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

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

@ -5,6 +5,7 @@ let streamingMessageContent = ''; // 用于存储当前流式消息的内容
let currentReader = null; // 用于存储当前流的reader以便可以取消
let isRequestInProgress = false; // 标记是否有请求正在进行
let currentSessionHistory = []; // 存储当前会话历史
let currentExplanationElement = null; // 存储当前的思考内容元素
const vscode = acquireVsCodeApi();
document.addEventListener('DOMContentLoaded', () => {
@ -180,6 +181,55 @@ document.addEventListener('DOMContentLoaded', () => {
chatBox.appendChild(streamingMessageElement);
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;
@ -454,6 +504,9 @@ document.addEventListener('DOMContentLoaded', () => {
} else if (message.command === 'startStream') {
// 开始流式传输
startStreaming();
} else if (message.command === 'explanation') {
// 显示思考内容
showExplanation(message.data);
} else if (message.command === 'streamData') {
// 处理流式数据
handleStreamData(message.data);

@ -547,3 +547,57 @@ code {
color: var(--vscode-foreground);
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);
break;
case 'log' :
console.log( ...message.data);
console.log(...message.data);
break;
}
}
@ -99,7 +99,39 @@ export class MessageHandler {
if (done) break;
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事件
let processedChunk = chunk;
if (chunk.includes('event: end')) {
@ -231,13 +263,17 @@ export class MessageHandler {
}
}
} catch (error: any) {
console.log("error:",error)
console.log("error:", error)
// 检查是否是由于取消请求导致的中断
if (error.name === 'AbortError' || error.message.includes('cancel') || error.message.includes('中断')) {
this.provider._postMessage({command: 'requestCancelled'});
} 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({

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

Loading…
Cancel
Save