From 90bf10104037c8ccdb08fddf5e907a4c70cf0ee6 Mon Sep 17 00:00:00 2001 From: yiliang114 <1204183885@qq.com> Date: Wed, 24 Dec 2025 14:29:25 +0800 Subject: [PATCH] chore(vscode-ide-companion): simplify the implementation of context remaining --- packages/cli/src/acp-integration/acpAgent.ts | 2 - .../src/acp-integration/session/Session.ts | 42 --------- .../src/services/acpConnection.ts | 5 -- .../src/services/acpSessionManager.ts | 5 -- .../src/services/qwenAgentManager.ts | 10 --- .../src/services/qwenSessionUpdateHandler.ts | 21 +---- .../src/types/acpTypes.ts | 12 +-- .../src/types/chatTypes.ts | 1 - .../vscode-ide-companion/src/webview/App.tsx | 12 +-- .../components/layout/ContextIndicator.tsx | 88 +++++++++++++++++++ .../webview/components/layout/InputForm.tsx | 77 +--------------- .../webview/handlers/SessionMessageHandler.ts | 12 --- .../src/webview/hooks/useWebViewMessages.ts | 13 +-- 13 files changed, 100 insertions(+), 200 deletions(-) create mode 100644 packages/vscode-ide-companion/src/webview/components/layout/ContextIndicator.tsx diff --git a/packages/cli/src/acp-integration/acpAgent.ts b/packages/cli/src/acp-integration/acpAgent.ts index 6d92db8dc..5296c16ef 100644 --- a/packages/cli/src/acp-integration/acpAgent.ts +++ b/packages/cli/src/acp-integration/acpAgent.ts @@ -357,8 +357,6 @@ class GeminiAgent { await session.sendAvailableCommandsUpdate(); }, 0); - await session.announceCurrentModel(true); - if (conversation && conversation.messages) { await session.replayHistory(conversation.messages); } diff --git a/packages/cli/src/acp-integration/session/Session.ts b/packages/cli/src/acp-integration/session/Session.ts index 50a6ca6b7..1d90ed20b 100644 --- a/packages/cli/src/acp-integration/session/Session.ts +++ b/packages/cli/src/acp-integration/session/Session.ts @@ -33,7 +33,6 @@ import { UserPromptEvent, TodoWriteTool, ExitPlanModeTool, - tokenLimit, } from '@qwen-code/qwen-code-core'; import * as acp from '../acp.js'; @@ -53,7 +52,6 @@ import type { SetModeResponse, ApprovalModeValue, CurrentModeUpdate, - CurrentModelUpdate, } from '../schema.js'; import { isSlashCommand } from '../../ui/utils/commandUtils.js'; @@ -88,10 +86,6 @@ export class Session implements SessionContext { private readonly toolCallEmitter: ToolCallEmitter; private readonly planEmitter: PlanEmitter; private readonly messageEmitter: MessageEmitter; - private lastAnnouncedModel: { - name: string; - contextLimit?: number | null; - } | null = null; // Implement SessionContext interface readonly sessionId: string; @@ -197,8 +191,6 @@ export class Session implements SessionContext { parts = await this.#resolvePrompt(params.prompt, pendingSend.signal); } - await this.sendCurrentModelUpdate(); - let nextMessage: Content | null = { role: 'user', parts }; while (nextMessage !== null) { @@ -387,40 +379,6 @@ export class Session implements SessionContext { await this.sendUpdate(update); } - async announceCurrentModel(force: boolean = false): Promise { - await this.sendCurrentModelUpdate(force); - } - - private async sendCurrentModelUpdate(force: boolean = false): Promise { - const modelName = this.config.getModel(); - if (!modelName) { - return; - } - - const contextLimit = tokenLimit(modelName); - - if ( - !force && - this.lastAnnouncedModel && - this.lastAnnouncedModel.name === modelName && - this.lastAnnouncedModel.contextLimit === contextLimit - ) { - return; - } - - this.lastAnnouncedModel = { name: modelName, contextLimit }; - - const update: CurrentModelUpdate = { - sessionUpdate: 'current_model_update', - model: { - name: modelName, - contextLimit, - }, - }; - - await this.sendUpdate(update); - } - private async runTool( abortSignal: AbortSignal, promptId: string, diff --git a/packages/vscode-ide-companion/src/services/acpConnection.ts b/packages/vscode-ide-companion/src/services/acpConnection.ts index 360d19d7d..6ea38b826 100644 --- a/packages/vscode-ide-companion/src/services/acpConnection.ts +++ b/packages/vscode-ide-companion/src/services/acpConnection.ts @@ -289,11 +289,6 @@ export class AcpConnection { * @returns Response */ async sendPrompt(prompt: string): Promise { - // Verify connection is still active before sending request - if (!this.isConnected) { - throw new Error('ACP connection is not active'); - } - return this.sessionManager.sendPrompt( prompt, this.child, diff --git a/packages/vscode-ide-companion/src/services/acpSessionManager.ts b/packages/vscode-ide-companion/src/services/acpSessionManager.ts index d52d1cce9..e2055a3a2 100644 --- a/packages/vscode-ide-companion/src/services/acpSessionManager.ts +++ b/packages/vscode-ide-companion/src/services/acpSessionManager.ts @@ -231,11 +231,6 @@ export class AcpSessionManager { throw new Error('No active ACP session'); } - // Check if child process is still alive before sending the request - if (!child || child.killed) { - throw new Error('ACP child process is not available'); - } - return await this.sendRequest( AGENT_METHODS.session_prompt, { diff --git a/packages/vscode-ide-companion/src/services/qwenAgentManager.ts b/packages/vscode-ide-companion/src/services/qwenAgentManager.ts index 5799f3d51..6bd3e878b 100644 --- a/packages/vscode-ide-companion/src/services/qwenAgentManager.ts +++ b/packages/vscode-ide-companion/src/services/qwenAgentManager.ts @@ -227,16 +227,6 @@ export class QwenAgentManager { * @param message - Message content */ async sendMessage(message: string): Promise { - // Validate the current session before sending the message - const isValid = await this.validateCurrentSession(); - if (!isValid) { - console.warn( - '[QwenAgentManager] Current session is invalid, creating new session', - ); - const workingDir = this.currentWorkingDir; - await this.createNewSession(workingDir); - } - await this.connection.sendPrompt(message); } diff --git a/packages/vscode-ide-companion/src/services/qwenSessionUpdateHandler.ts b/packages/vscode-ide-companion/src/services/qwenSessionUpdateHandler.ts index 894443931..55d084b2d 100644 --- a/packages/vscode-ide-companion/src/services/qwenSessionUpdateHandler.ts +++ b/packages/vscode-ide-companion/src/services/qwenSessionUpdateHandler.ts @@ -10,11 +10,7 @@ * Handles session updates from ACP and dispatches them to appropriate callbacks */ -import type { - AcpSessionUpdate, - ModelInfo, - SessionUpdateMeta, -} from '../types/acpTypes.js'; +import type { AcpSessionUpdate, SessionUpdateMeta } from '../types/acpTypes.js'; import type { ApprovalModeValue } from '../types/approvalModeValueTypes.js'; import type { QwenAgentCallbacks, @@ -164,11 +160,6 @@ export class QwenSessionUpdateHandler { break; } - case 'current_model_update': { - this.emitModelInfo((update as unknown as { model?: ModelInfo }).model); - break; - } - default: console.log('[QwenAgentManager] Unhandled session update type'); break; @@ -183,18 +174,8 @@ export class QwenSessionUpdateHandler { const payload: UsageStatsPayload = { usage: meta.usage || undefined, durationMs: meta.durationMs ?? undefined, - model: meta.model ?? undefined, - tokenLimit: meta.tokenLimit ?? undefined, }; this.callbacks.onUsageUpdate(payload); } - - private emitModelInfo(model?: ModelInfo): void { - if (!model || !this.callbacks.onModelInfo) { - return; - } - - this.callbacks.onModelInfo(model); - } } diff --git a/packages/vscode-ide-companion/src/types/acpTypes.ts b/packages/vscode-ide-companion/src/types/acpTypes.ts index 1f22adf8b..aa844c455 100644 --- a/packages/vscode-ide-companion/src/types/acpTypes.ts +++ b/packages/vscode-ide-companion/src/types/acpTypes.ts @@ -59,8 +59,6 @@ export interface UsageMetadata { export interface SessionUpdateMeta { usage?: UsageMetadata | null; durationMs?: number | null; - model?: string | null; - tokenLimit?: number | null; } export interface ModelInfo { @@ -188,13 +186,6 @@ export interface CurrentModeUpdate extends BaseSessionUpdate { }; } -export interface CurrentModelUpdate extends BaseSessionUpdate { - update: { - sessionUpdate: 'current_model_update'; - model: ModelInfo; - }; -} - // Authenticate update (sent by agent during authentication process) export interface AuthenticateUpdateNotification { _meta: { @@ -209,8 +200,7 @@ export type AcpSessionUpdate = | ToolCallUpdate | ToolCallStatusUpdate | PlanUpdate - | CurrentModeUpdate - | CurrentModelUpdate; + | CurrentModeUpdate; // Permission request (simplified version, use schema.RequestPermissionRequest for validation) export interface AcpPermissionRequest { diff --git a/packages/vscode-ide-companion/src/types/chatTypes.ts b/packages/vscode-ide-companion/src/types/chatTypes.ts index 8cbe26a46..6581d4341 100644 --- a/packages/vscode-ide-companion/src/types/chatTypes.ts +++ b/packages/vscode-ide-companion/src/types/chatTypes.ts @@ -37,7 +37,6 @@ export interface UsageStatsPayload { cachedTokens?: number | null; } | null; durationMs?: number | null; - model?: string | null; tokenLimit?: number | null; } diff --git a/packages/vscode-ide-companion/src/webview/App.tsx b/packages/vscode-ide-companion/src/webview/App.tsx index 77e845f89..318dbd2bd 100644 --- a/packages/vscode-ide-companion/src/webview/App.tsx +++ b/packages/vscode-ide-companion/src/webview/App.tsx @@ -74,7 +74,7 @@ export const App: React.FC = () => { const [planEntries, setPlanEntries] = useState([]); const [isAuthenticated, setIsAuthenticated] = useState(null); const [isLoading, setIsLoading] = useState(true); // Track if we're still initializing/loading - const [modelInfo] = useState<{ + const [modelInfo, setModelInfo] = useState<{ name: string; contextLimit?: number | null; } | null>(null); @@ -175,9 +175,9 @@ export const App: React.FC = () => { } const modelName = - (usageStats?.model && typeof usageStats.model === 'string' - ? usageStats.model - : undefined) ?? modelInfo?.name; + modelInfo?.name && typeof modelInfo.name === 'string' + ? modelInfo.name + : undefined; const derivedLimit = modelName && modelName.length > 0 ? tokenLimit(modelName) : undefined; @@ -200,7 +200,6 @@ export const App: React.FC = () => { percentLeft, usedTokens: used, tokenLimit: limit, - model: modelName ?? undefined, }; }, [usageStats, modelInfo]); @@ -293,6 +292,9 @@ export const App: React.FC = () => { setEditMode, setIsAuthenticated, setUsageStats: (stats) => setUsageStats(stats ?? null), + setModelInfo: (info) => { + setModelInfo(info); + }, }); // Auto-scroll handling: keep the view pinned to bottom when new content arrives, diff --git a/packages/vscode-ide-companion/src/webview/components/layout/ContextIndicator.tsx b/packages/vscode-ide-companion/src/webview/components/layout/ContextIndicator.tsx new file mode 100644 index 000000000..b7f476c36 --- /dev/null +++ b/packages/vscode-ide-companion/src/webview/components/layout/ContextIndicator.tsx @@ -0,0 +1,88 @@ +/** + * @license + * Copyright 2025 Qwen Team + * SPDX-License-Identifier: Apache-2.0 + */ + +import type React from 'react'; +import { Tooltip } from '../Tooltip.js'; + +interface ContextUsage { + percentLeft: number; + usedTokens: number; + tokenLimit: number; +} + +interface ContextIndicatorProps { + contextUsage: ContextUsage | null; +} + +export const ContextIndicator: React.FC = ({ + contextUsage, +}) => { + if (!contextUsage) { + return null; + } + + // Calculate used percentage for the progress indicator + // contextUsage.percentLeft is the percentage remaining, so 100 - percentLeft = percent used + const percentUsed = 100 - contextUsage.percentLeft; + const percentFormatted = Math.max(0, Math.min(100, Math.round(percentUsed))); + const radius = 9; + const circumference = 2 * Math.PI * radius; + // To show the used portion, we need to offset the unused portion + // If 20% is used, we want to show 20% filled, so offset the remaining 80% + const dashOffset = ((100 - percentUsed) / 100) * circumference; + const formatNumber = (value: number) => { + if (value >= 1000) { + return `${(Math.round((value / 1000) * 10) / 10).toFixed(1)}k`; + } + return Math.round(value).toLocaleString(); + }; + + // Create tooltip content with proper formatting + const tooltipContent = ( +
+
+ {percentFormatted}% • {formatNumber(contextUsage.usedTokens)} /{' '} + {formatNumber(contextUsage.tokenLimit)} context used +
+
+ ); + + return ( + + + + ); +}; diff --git a/packages/vscode-ide-companion/src/webview/components/layout/InputForm.tsx b/packages/vscode-ide-companion/src/webview/components/layout/InputForm.tsx index 88f2ec1a3..2058b7c04 100644 --- a/packages/vscode-ide-companion/src/webview/components/layout/InputForm.tsx +++ b/packages/vscode-ide-companion/src/webview/components/layout/InputForm.tsx @@ -21,7 +21,7 @@ import { CompletionMenu } from '../layout/CompletionMenu.js'; import type { CompletionItem } from '../../../types/completionItemTypes.js'; import { getApprovalModeInfoFromString } from '../../../types/acpTypes.js'; import type { ApprovalModeValue } from '../../../types/approvalModeValueTypes.js'; -import { Tooltip } from '../Tooltip.js'; +import { ContextIndicator } from './ContextIndicator.js'; interface InputFormProps { inputText: string; @@ -41,7 +41,6 @@ interface InputFormProps { percentLeft: number; usedTokens: number; tokenLimit: number; - model?: string; } | null; onInputChange: (text: string) => void; onCompositionStart: () => void; @@ -151,78 +150,6 @@ export const InputForm: React.FC = ({ ? `${selectedLinesCount} ${selectedLinesCount === 1 ? 'line' : 'lines'} selected` : ''; - const renderContextIndicator = () => { - if (!contextUsage) { - return null; - } - - // Calculate used percentage for the progress indicator - // contextUsage.percentLeft is the percentage remaining, so 100 - percentLeft = percent used - const percentUsed = 100 - contextUsage.percentLeft; - const percentFormatted = Math.max( - 0, - Math.min(100, Math.round(percentUsed)), - ); - const radius = 9; - const circumference = 2 * Math.PI * radius; - // To show the used portion, we need to offset the unused portion - // If 20% is used, we want to show 20% filled, so offset the remaining 80% - const dashOffset = ((100 - percentUsed) / 100) * circumference; - const formatNumber = (value: number) => { - if (value >= 1000) { - return `${(Math.round((value / 1000) * 10) / 10).toFixed(1)}k`; - } - return Math.round(value).toLocaleString(); - }; - - // Create tooltip content with proper formatting - const tooltipContent = ( -
-
- {percentFormatted}% • {formatNumber(contextUsage.usedTokens)} /{' '} - {formatNumber(contextUsage.tokenLimit)} context used -
- {contextUsage.model &&
Model: {contextUsage.model}
} -
- ); - - return ( - - - - ); - }; - return (
@@ -321,7 +248,7 @@ export const InputForm: React.FC = ({
{/* Context usage indicator */} - {renderContextIndicator()} + {/* @yiliang114. closed temporarily */} {/* Thinking button */} diff --git a/packages/vscode-ide-companion/src/webview/handlers/SessionMessageHandler.ts b/packages/vscode-ide-companion/src/webview/handlers/SessionMessageHandler.ts index df0d9b6c1..d8861b95a 100644 --- a/packages/vscode-ide-companion/src/webview/handlers/SessionMessageHandler.ts +++ b/packages/vscode-ide-companion/src/webview/handlers/SessionMessageHandler.ts @@ -376,18 +376,6 @@ export class SessionMessageHandler extends BaseMessageHandler { data: { timestamp: Date.now() }, }); - // Verify agent is connected before sending message - if (!this.agentManager.isConnected) { - throw new Error('Agent is not connected. Please try again.'); - } - - // Verify there's an active session - if (!this.agentManager.currentSessionId) { - const workspaceFolder = vscode.workspace.workspaceFolders?.[0]; - const workingDir = workspaceFolder?.uri.fsPath || process.cwd(); - await this.agentManager.createNewSession(workingDir); - } - await this.agentManager.sendMessage(formattedText); // Save assistant message diff --git a/packages/vscode-ide-companion/src/webview/hooks/useWebViewMessages.ts b/packages/vscode-ide-companion/src/webview/hooks/useWebViewMessages.ts index e9911fd3e..c601ef0a8 100644 --- a/packages/vscode-ide-companion/src/webview/hooks/useWebViewMessages.ts +++ b/packages/vscode-ide-companion/src/webview/hooks/useWebViewMessages.ts @@ -251,18 +251,7 @@ export const useWebViewMessages = ({ case 'usageStats': { const stats = message.data as UsageStatsPayload | undefined; - if ( - stats && - (!stats.tokenLimit || stats.tokenLimit <= 0) && - modelInfoRef.current?.contextLimit - ) { - handlers.setUsageStats?.({ - ...stats, - tokenLimit: modelInfoRef.current.contextLimit ?? undefined, - }); - } else { - handlers.setUsageStats?.(stats); - } + handlers.setUsageStats?.(stats); break; }