Compare commits
4 Commits
c49aaf753c
...
f933d84cd1
| Author | SHA1 | Date | |
|---|---|---|---|
| f933d84cd1 | |||
| b794d1ceb0 | |||
| 259310a29d | |||
| 715eac5949 |
@ -1,9 +1,15 @@
|
|||||||
/**
|
/**
|
||||||
* 配置管理
|
* 配置管理
|
||||||
* 后端地址已预配置,用户无需手动设置
|
* 支持 dev(本地开发)和 test(测试服务器)两种环境
|
||||||
*/
|
*/
|
||||||
import * as vscode from "vscode";
|
import * as vscode from "vscode";
|
||||||
|
|
||||||
|
/** 环境类型 */
|
||||||
|
type Environment = "dev" | "test" | "prod";
|
||||||
|
|
||||||
|
/** 当前环境 - 修改这里切换环境 */
|
||||||
|
const CURRENT_ENV: Environment = "dev";
|
||||||
|
|
||||||
/** 配置项接口 */
|
/** 配置项接口 */
|
||||||
export interface IccoderConfig {
|
export interface IccoderConfig {
|
||||||
/** 后端服务地址 */
|
/** 后端服务地址 */
|
||||||
@ -14,19 +20,40 @@ export interface IccoderConfig {
|
|||||||
userId: string;
|
userId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 默认配置(预配置,不暴露给用户) */
|
/** 环境配置 */
|
||||||
const DEFAULT_CONFIG: IccoderConfig = {
|
const ENV_CONFIG: Record<Environment, IccoderConfig> = {
|
||||||
backendUrl: "http://192.168.1.108:2233",
|
/** 本地开发环境 */
|
||||||
timeout: 60000,
|
dev: {
|
||||||
userId: "default-user",
|
backendUrl: "http://localhost:2233",
|
||||||
|
timeout: 60000,
|
||||||
|
userId: "default-user",
|
||||||
|
},
|
||||||
|
/** 测试服务器环境 */
|
||||||
|
test: {
|
||||||
|
backendUrl: "http://192.168.1.108:2233",
|
||||||
|
timeout: 60000,
|
||||||
|
userId: "default-user",
|
||||||
|
},
|
||||||
|
/** 生产环境 */
|
||||||
|
prod: {
|
||||||
|
backendUrl: "https://api.iccoder.com", // TODO: 替换为实际生产地址
|
||||||
|
timeout: 60000,
|
||||||
|
userId: "default-user",
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前环境
|
||||||
|
*/
|
||||||
|
export function getCurrentEnv(): Environment {
|
||||||
|
return CURRENT_ENV;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取配置项
|
* 获取配置项
|
||||||
* 直接返回预配置的值,用户无需手动配置
|
|
||||||
*/
|
*/
|
||||||
export function getConfig(): IccoderConfig {
|
export function getConfig(): IccoderConfig {
|
||||||
return { ...DEFAULT_CONFIG };
|
return { ...ENV_CONFIG[CURRENT_ENV] };
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -34,7 +61,6 @@ export function getConfig(): IccoderConfig {
|
|||||||
*/
|
*/
|
||||||
export function getApiUrl(path: string): string {
|
export function getApiUrl(path: string): string {
|
||||||
const { backendUrl } = getConfig();
|
const { backendUrl } = getConfig();
|
||||||
// 确保 URL 格式正确
|
|
||||||
const baseUrl = backendUrl.endsWith("/")
|
const baseUrl = backendUrl.endsWith("/")
|
||||||
? backendUrl.slice(0, -1)
|
? backendUrl.slice(0, -1)
|
||||||
: backendUrl;
|
: backendUrl;
|
||||||
|
|||||||
@ -12,7 +12,9 @@ import {
|
|||||||
handlePlanAction,
|
handlePlanAction,
|
||||||
setPendingPlanExecution,
|
setPendingPlanExecution,
|
||||||
getCurrentTaskId,
|
getCurrentTaskId,
|
||||||
|
setLastTaskId,
|
||||||
} from "../utils/messageHandler";
|
} from "../utils/messageHandler";
|
||||||
|
import { compactDialog } from "../services/apiClient";
|
||||||
import { VCDViewerPanel } from "./VCDViewerPanel";
|
import { VCDViewerPanel } from "./VCDViewerPanel";
|
||||||
import { ChatHistoryManager } from "../utils/chatHistoryManager";
|
import { ChatHistoryManager } from "../utils/chatHistoryManager";
|
||||||
import { MessageType } from "../types/chatHistory";
|
import { MessageType } from "../types/chatHistory";
|
||||||
@ -195,6 +197,39 @@ export async function showICHelperPanel(
|
|||||||
case "abortDialog":
|
case "abortDialog":
|
||||||
void abortCurrentDialog();
|
void abortCurrentDialog();
|
||||||
break;
|
break;
|
||||||
|
// 新增:压缩会话
|
||||||
|
case "compressConversation":
|
||||||
|
{
|
||||||
|
const taskId = getCurrentTaskId();
|
||||||
|
if (taskId) {
|
||||||
|
compactDialog(taskId)
|
||||||
|
.then((result) => {
|
||||||
|
if (result.success) {
|
||||||
|
panel.webview.postMessage({
|
||||||
|
command: "receiveMessage",
|
||||||
|
text: "✅ 会话压缩完成",
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
panel.webview.postMessage({
|
||||||
|
command: "receiveMessage",
|
||||||
|
text: `❌ 压缩失败: ${result.error || "未知错误"}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
panel.webview.postMessage({
|
||||||
|
command: "receiveMessage",
|
||||||
|
text: `❌ 压缩失败: ${err.message || "网络错误"}`,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
panel.webview.postMessage({
|
||||||
|
command: "receiveMessage",
|
||||||
|
text: "❌ 没有活跃的会话",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
// 处理计划操作(只做模式切换,响应已通过 submitAnswer 发送)
|
// 处理计划操作(只做模式切换,响应已通过 submitAnswer 发送)
|
||||||
case "planAction":
|
case "planAction":
|
||||||
if (message.action === "confirm") {
|
if (message.action === "confirm") {
|
||||||
@ -528,6 +563,9 @@ async function selectConversation(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 设置 lastTaskId,用于压缩等操作
|
||||||
|
setLastTaskId(taskId);
|
||||||
|
|
||||||
// 更新面板的任务映射,确保后续对话保存到正确的任务中
|
// 更新面板的任务映射,确保后续对话保存到正确的任务中
|
||||||
const panelId = (panel as any).__uniqueId;
|
const panelId = (panel as any).__uniqueId;
|
||||||
historyManager.setPanelTask(panelId, taskId, workspacePath);
|
historyManager.setPanelTask(panelId, taskId, workspacePath);
|
||||||
|
|||||||
@ -155,6 +155,26 @@ export async function stopDialog(taskId: string): Promise<StopDialogResponse> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 压缩对话响应 */
|
||||||
|
export interface CompactDialogResponse {
|
||||||
|
success: boolean;
|
||||||
|
taskId: string;
|
||||||
|
message?: string;
|
||||||
|
error?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 手动压缩对话历史
|
||||||
|
* POST /api/dialog/compact
|
||||||
|
*/
|
||||||
|
export async function compactDialog(taskId: string): Promise<CompactDialogResponse> {
|
||||||
|
console.log(`[API] 压缩对话: taskId=${taskId}`);
|
||||||
|
return request<CompactDialogResponse>('/api/dialog/compact', {
|
||||||
|
method: 'POST',
|
||||||
|
body: { taskId }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建成功的工具结果
|
* 创建成功的工具结果
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -73,6 +73,8 @@ export interface DialogCallbacks {
|
|||||||
onError?: (message: string) => void;
|
onError?: (message: string) => void;
|
||||||
/** 通知消息 */
|
/** 通知消息 */
|
||||||
onNotification?: (message: string) => void;
|
onNotification?: (message: string) => void;
|
||||||
|
/** 上下文使用量更新 */
|
||||||
|
onContextUsage?: (data: { currentTokens: number; maxTokens: number; percentage: number }) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -553,6 +555,12 @@ export class DialogSession {
|
|||||||
onComplete: (data) => {
|
onComplete: (data) => {
|
||||||
this.isActive = false;
|
this.isActive = false;
|
||||||
this.finalizeTextSegment();
|
this.finalizeTextSegment();
|
||||||
|
|
||||||
|
// 追踪 AI 消息(用于后端重启后恢复)
|
||||||
|
if (this.accumulatedText) {
|
||||||
|
historyManager.trackAiMessage(this.accumulatedText);
|
||||||
|
}
|
||||||
|
|
||||||
// 发送所有段落
|
// 发送所有段落
|
||||||
callbacks.onComplete?.(this.segments);
|
callbacks.onComplete?.(this.segments);
|
||||||
},
|
},
|
||||||
@ -639,6 +647,11 @@ export class DialogSession {
|
|||||||
await historyManager.saveCompactedData(data.compactedData);
|
await historyManager.saveCompactedData(data.compactedData);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onContextUsage: (data) => {
|
||||||
|
console.log('[DialogSession] onContextUsage:', data.currentTokens, '/', data.maxTokens);
|
||||||
|
callbacks.onContextUsage?.(data);
|
||||||
|
},
|
||||||
|
|
||||||
onOpen: () => {
|
onOpen: () => {
|
||||||
console.log('[DialogSession] SSE 连接已建立');
|
console.log('[DialogSession] SSE 连接已建立');
|
||||||
},
|
},
|
||||||
|
|||||||
@ -27,7 +27,8 @@ import type {
|
|||||||
AgentStartEvent,
|
AgentStartEvent,
|
||||||
AgentProgressEvent,
|
AgentProgressEvent,
|
||||||
AgentCompleteEvent,
|
AgentCompleteEvent,
|
||||||
AgentErrorEvent
|
AgentErrorEvent,
|
||||||
|
ContextUsageEvent
|
||||||
} from '../types/api';
|
} from '../types/api';
|
||||||
import type { MemoryCompactedEvent } from '../types/memory';
|
import type { MemoryCompactedEvent } from '../types/memory';
|
||||||
|
|
||||||
@ -71,6 +72,8 @@ export interface SSECallbacks {
|
|||||||
onAgentError?: (data: AgentErrorEvent) => void;
|
onAgentError?: (data: AgentErrorEvent) => void;
|
||||||
/** 记忆压缩完成 */
|
/** 记忆压缩完成 */
|
||||||
onMemoryCompacted?: (data: MemoryCompactedEvent) => void;
|
onMemoryCompacted?: (data: MemoryCompactedEvent) => void;
|
||||||
|
/** 上下文使用量更新 */
|
||||||
|
onContextUsage?: (data: ContextUsageEvent) => void;
|
||||||
/** 连接打开 */
|
/** 连接打开 */
|
||||||
onOpen?: () => void;
|
onOpen?: () => void;
|
||||||
/** 连接关闭 */
|
/** 连接关闭 */
|
||||||
@ -325,6 +328,9 @@ function dispatchEvent(
|
|||||||
case 'memory_compacted':
|
case 'memory_compacted':
|
||||||
callbacks.onMemoryCompacted?.(data as MemoryCompactedEvent);
|
callbacks.onMemoryCompacted?.(data as MemoryCompactedEvent);
|
||||||
break;
|
break;
|
||||||
|
case 'context_usage':
|
||||||
|
callbacks.onContextUsage?.(data as ContextUsageEvent);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
console.log(`[SSE] 未知事件类型: ${eventType}`, data);
|
console.log(`[SSE] 未知事件类型: ${eventType}`, data);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -67,13 +67,13 @@ export class UserInteractionManager {
|
|||||||
reject
|
reject
|
||||||
});
|
});
|
||||||
|
|
||||||
// 设置超时(5分钟)
|
// 设置超时(2小时)
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (this.pendingQuestions.has(askId)) {
|
if (this.pendingQuestions.has(askId)) {
|
||||||
this.pendingQuestions.delete(askId);
|
this.pendingQuestions.delete(askId);
|
||||||
reject(new Error('用户回答超时'));
|
reject(new Error('用户回答超时'));
|
||||||
}
|
}
|
||||||
}, 300000);
|
}, 7200000);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -54,6 +54,7 @@ export type SSEEventType =
|
|||||||
| 'agent_complete' // 子智能体完成
|
| 'agent_complete' // 子智能体完成
|
||||||
| 'agent_error' // 子智能体错误
|
| 'agent_error' // 子智能体错误
|
||||||
| 'memory_compacted' // 记忆压缩完成
|
| 'memory_compacted' // 记忆压缩完成
|
||||||
|
| 'context_usage' // 上下文使用量
|
||||||
| 'complete' // 对话完成
|
| 'complete' // 对话完成
|
||||||
| 'error' // 错误
|
| 'error' // 错误
|
||||||
| 'warning' // 警告
|
| 'warning' // 警告
|
||||||
@ -181,6 +182,13 @@ export interface AgentErrorEvent {
|
|||||||
timestamp: number;
|
timestamp: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** context_usage 事件数据 */
|
||||||
|
export interface ContextUsageEvent {
|
||||||
|
currentTokens: number;
|
||||||
|
maxTokens: number;
|
||||||
|
percentage: number;
|
||||||
|
}
|
||||||
|
|
||||||
// ============== 工具调用协议 (MCP 格式) ==============
|
// ============== 工具调用协议 (MCP 格式) ==============
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -27,6 +27,9 @@ let useBackendService = true;
|
|||||||
/** 当前对话会话 */
|
/** 当前对话会话 */
|
||||||
let currentSession: DialogSession | null = null;
|
let currentSession: DialogSession | null = null;
|
||||||
|
|
||||||
|
/** 最后一个活跃的 taskId(用于压缩等操作) */
|
||||||
|
let lastTaskId: string | null = null;
|
||||||
|
|
||||||
/** 待执行的计划(Plan 模式确认后自动执行) */
|
/** 待执行的计划(Plan 模式确认后自动执行) */
|
||||||
let pendingPlanExecution: {
|
let pendingPlanExecution: {
|
||||||
panel: vscode.WebviewPanel;
|
panel: vscode.WebviewPanel;
|
||||||
@ -128,6 +131,8 @@ async function handleUserMessageWithBackend(
|
|||||||
// 创建或复用会话
|
// 创建或复用会话
|
||||||
if (!currentSession || !currentSession.active) {
|
if (!currentSession || !currentSession.active) {
|
||||||
currentSession = dialogManager.createSession(extensionPath, reuseTaskId);
|
currentSession = dialogManager.createSession(extensionPath, reuseTaskId);
|
||||||
|
// 保存 taskId 用于后续操作(如压缩)
|
||||||
|
lastTaskId = currentSession.getTaskId();
|
||||||
if (reuseTaskId) {
|
if (reuseTaskId) {
|
||||||
console.log('[MessageHandler] 复用 taskId 创建会话:', reuseTaskId);
|
console.log('[MessageHandler] 复用 taskId 创建会话:', reuseTaskId);
|
||||||
}
|
}
|
||||||
@ -253,6 +258,16 @@ async function handleUserMessageWithBackend(
|
|||||||
onNotification: (message) => {
|
onNotification: (message) => {
|
||||||
vscode.window.showInformationMessage(message);
|
vscode.window.showInformationMessage(message);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onContextUsage: (data) => {
|
||||||
|
// 发送上下文使用量到 WebView
|
||||||
|
panel.webview.postMessage({
|
||||||
|
command: 'contextUsage',
|
||||||
|
currentTokens: data.currentTokens,
|
||||||
|
maxTokens: data.maxTokens,
|
||||||
|
percentage: data.percentage
|
||||||
|
});
|
||||||
|
},
|
||||||
}, mode);
|
}, mode);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -310,7 +325,15 @@ export async function abortCurrentDialog(): Promise<void> {
|
|||||||
* 获取当前会话的 taskId
|
* 获取当前会话的 taskId
|
||||||
*/
|
*/
|
||||||
export function getCurrentTaskId(): string | null {
|
export function getCurrentTaskId(): string | null {
|
||||||
return currentSession?.getTaskId() || null;
|
return currentSession?.getTaskId() || lastTaskId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置最后的 taskId(加载历史会话时调用)
|
||||||
|
*/
|
||||||
|
export function setLastTaskId(taskId: string): void {
|
||||||
|
lastTaskId = taskId;
|
||||||
|
console.log('[MessageHandler] 设置 lastTaskId:', taskId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -571,6 +571,13 @@ export function getWebviewContent(iconUri?: string): string {
|
|||||||
currentSegmentedMessage = null;
|
currentSegmentedMessage = null;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'contextUsage':
|
||||||
|
// 更新上下文使用量显示
|
||||||
|
if (typeof updateContextDisplay === 'function') {
|
||||||
|
updateContextDisplay(message.currentTokens, message.maxTokens);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case 'hideLoading':
|
case 'hideLoading':
|
||||||
// 隐藏加载指示器
|
// 隐藏加载指示器
|
||||||
hideLoadingIndicator();
|
hideLoadingIndicator();
|
||||||
|
|||||||
Reference in New Issue
Block a user