Skip to content

Commit 73b591d

Browse files
committed
feat: add selectable context window (200K/1M) in model picker
Adds a "Context Window" dropdown in the Copilot Chat model configuration panel, letting users switch between 200K and 1M token context windows per model. - Combine reasoningEffort and contextSize into a single configurationSchema so both controls appear in the model picker dropdown (non-public VS Code API, same mechanism used by Copilot for thinking effort). - maxInputTokens raised from 656K to 1,000,000 to support 1M mode. - maxOutputTokens set to 128,000 — a conservative middle ground that keeps the displayed context window reasonable (200K input + 128K output = 328K, 1M input + 128K output = ~1.1M) without sacrificing DeepSeek's actual generation headroom. This does NOT affect API-level max_tokens, which is controlled by a separate VS Code setting. - Sync dropdown choice back to workspace setting (deepseek-copilot.contextSize) after each request so the value persists across sessions. - Both DeepSeek V4 Flash and Pro supported. - English and Chinese i18n for dropdown labels and descriptions. Why: the default 656K input window was too small for long sessions, while always reserving 1M adds latency and cost. Giving users control lets them pick the right trade-off between speed/cost (200K) and capacity (1M).
1 parent de85319 commit 73b591d

9 files changed

Lines changed: 120 additions & 46 deletions

File tree

package-lock.json

Lines changed: 0 additions & 12 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,20 @@
127127
"minimum": 0,
128128
"description": "%deepseek-copilot.config.maxTokens.description%"
129129
},
130+
"deepseek-copilot.contextSize": {
131+
"type": "number",
132+
"default": 1000000,
133+
"enum": [200000, 1000000],
134+
"enumItemLabels": [
135+
"%deepseek-copilot.config.contextSize.200k.label%",
136+
"%deepseek-copilot.config.contextSize.1m.label%"
137+
],
138+
"markdownEnumDescriptions": [
139+
"%deepseek-copilot.config.contextSize.200k.description%",
140+
"%deepseek-copilot.config.contextSize.1m.description%"
141+
],
142+
"markdownDescription": "%deepseek-copilot.config.contextSize.description%"
143+
},
130144
"deepseek-copilot.experimental.stabilizeToolList": {
131145
"type": "boolean",
132146
"default": false,

package.nls.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@
1717
"deepseek-copilot.config.title": "DeepSeek Copilot",
1818
"deepseek-copilot.config.baseUrl.description": "DeepSeek API base URL. Defaults to official DeepSeek API endpoint.",
1919
"deepseek-copilot.config.maxTokens.description": "Maximum number of output tokens per request. Set to 0 to use the API default (no limit). Useful for controlling costs.",
20+
"deepseek-copilot.config.contextSize.description": "Input context window size reported to VS Code. Larger context allows longer conversations without compaction but may increase cost.",
21+
"deepseek-copilot.config.contextSize.200k.label": "200K",
22+
"deepseek-copilot.config.contextSize.200k.description": "200K tokens — safe default for most sessions.",
23+
"deepseek-copilot.config.contextSize.1m.label": "1M",
24+
"deepseek-copilot.config.contextSize.1m.description": "1M tokens — longer sessions without compaction.",
2025
"deepseek-copilot.config.experimental.stabilizeToolList.description": "**Experimental**: improve DeepSeek context-cache hit rate by pre-activating available tools.\n- When the enabled tools list changes across turns, this may improve DeepSeek context-cache hit rate.\n- Requests will include more function definitions, so input tokens may increase. Cache-hit input tokens are billed at a lower price, but still count toward usage.\n- This may add internal preflight tool calls to the current Copilot chat history. If you switch to another model in the same conversation, that model provider may reject or mishandle the replayed history. Start a new chat if model switching behaves unexpectedly.\n\nUse [Configure Tools](command:workbench.action.chat.configureTools) to **view and manage** your tool list:\n\n- 64 or fewer enabled tools: usually no need to enable this unless the tool list still changes across turns.\n- More than 128 enabled tools: not recommended. DeepSeek supports at most 128 functions in one `tools` request. Consider disabling tools you rarely use.",
2126
"deepseek-copilot.config.debugMode.description": "Controls what diagnostic information DeepSeek Copilot writes. Token usage is always reported to Copilot regardless of this setting.\n\n- **Minimal** — Token usage only. No diagnostic logs or request dumps.\n- **Metadata** — Privacy-safe diagnostic metadata (request hashes, prefix overlap, tool schema changes). Does not contain prompt text — safe to share in public issue reports. View with [`DeepSeek: Show Logs`](command:deepseek-copilot.showLogs).\n- **Verbose** — Complete request payloads written to disk for local debugging. **Warning: contains sensitive prompt content.** View with [`DeepSeek: Open Request Dumps Folder`](command:deepseek-copilot.openRequestDumpsFolder).",
2227
"deepseek-copilot.config.debugMode.minimal.label": "Minimal",

src/config.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,15 @@ export function getMaxTokens(): number | undefined {
3636
return value > 0 ? value : undefined;
3737
}
3838

39+
/**
40+
* Get the configured context window size in tokens.
41+
* Returns the value set by the user (200K or 1M), defaulting to 1M.
42+
*/
43+
export function getContextSize(): number {
44+
const config = vscode.workspace.getConfiguration(CONFIG_SECTION);
45+
return config.get<number>('contextSize', 1000000);
46+
}
47+
3948
/**
4049
* Diagnostic mode. `verbose` also enables metadata logs.
4150
*

src/consts.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,8 @@ export const MODELS: ModelDefinition[] = [
5454
family: 'deepseek',
5555
version: 'v4',
5656
detail: 'Fast, general-purpose model',
57-
maxInputTokens: 655360,
58-
maxOutputTokens: 393216,
57+
maxInputTokens: 1000000,
58+
maxOutputTokens: 128000,
5959
capabilities: {
6060
toolCalling: DEEPSEEK_TOOLS_LIMIT,
6161
imageInput: true,
@@ -74,8 +74,8 @@ export const MODELS: ModelDefinition[] = [
7474
family: 'deepseek',
7575
version: 'v4',
7676
detail: 'Most capable reasoning model',
77-
maxInputTokens: 655360,
78-
maxOutputTokens: 393216,
77+
maxInputTokens: 1000000,
78+
maxOutputTokens: 128000,
7979
capabilities: {
8080
toolCalling: DEEPSEEK_TOOLS_LIMIT,
8181
imageInput: true,

src/i18n.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,13 @@ const zh: Translations = {
4242
'thinking.max': '深度',
4343
'thinking.max.desc': '深度推理,适合复杂任务',
4444

45+
// Context Size — model picker dropdown
46+
'contextSize.title': '上下文窗口',
47+
'contextSize.200k': '200K',
48+
'contextSize.200k.desc': '200K token 上下文(更快)',
49+
'contextSize.1m': '1M',
50+
'contextSize.1m.desc': '1M token 上下文(更大容量)',
51+
4552
// Vision
4653
'vision.proxyUsing': '视觉代理:{0}',
4754
'vision.notFound': '未找到视觉模型 "{0}"',
@@ -232,6 +239,13 @@ const en: Translations = {
232239
'thinking.max': 'Max',
233240
'thinking.max.desc': 'Maximum reasoning depth for complex agent tasks',
234241

242+
// Context Size — model picker dropdown
243+
'contextSize.title': 'Context Window',
244+
'contextSize.200k': '200K',
245+
'contextSize.200k.desc': '200K token context (faster)',
246+
'contextSize.1m': '1M',
247+
'contextSize.1m.desc': '1M token context (larger capacity)',
248+
235249
// Vision
236250
// NOTE: vision.unableToDescribe has been moved to consts.ts as
237251
// IMAGE_DESCRIPTION_UNAVAILABLE — it is prompt content, not UI text.

src/provider/index.ts

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import vscode from 'vscode';
22
import { AuthManager } from '../auth';
3-
import { getStabilizeToolListEnabled } from '../config';
4-
import { MODELS } from '../consts';
3+
import { getContextSize, getStabilizeToolListEnabled } from '../config';
4+
import { CONFIG_SECTION, MODELS } from '../consts';
55
import { t } from '../i18n';
66
import { logger } from '../logger';
77
import { createCacheDiagnosticsRecorder, dumpProviderInput } from './debug';
@@ -133,10 +133,11 @@ export class DeepSeekChatProvider implements vscode.LanguageModelChatProvider {
133133

134134
const hasKey = await this.authManager.hasApiKey();
135135
const pricingCurrency = this.balanceCurrencyResolver.getDisplayCurrency();
136+
const contextSize = getContextSize();
136137
if (hasKey) {
137138
this.balanceCurrencyResolver.refreshInBackground();
138139
}
139-
return MODELS.map((model) => toChatInfo(model, hasKey, pricingCurrency));
140+
return MODELS.map((model) => toChatInfo(model, hasKey, pricingCurrency, contextSize));
140141
}
141142

142143
async provideLanguageModelChatResponse(
@@ -184,6 +185,17 @@ export class DeepSeekChatProvider implements vscode.LanguageModelChatProvider {
184185
getVisionDescriber: () => this.vision.get(),
185186
});
186187

188+
// Sync context size back to VS Code setting when user changes it via the
189+
// model-picker dropdown. The updated maxInputTokens takes effect on the
190+
// next request after Copilot Chat re-queries model information.
191+
const currentContextSize = getContextSize();
192+
if (prepared.configuredContextSize !== currentContextSize) {
193+
await vscode.workspace
194+
.getConfiguration(CONFIG_SECTION)
195+
.update('contextSize', prepared.configuredContextSize, vscode.ConfigurationTarget.Global);
196+
this.onDidChangeLanguageModelChatInformationEmitter.fire();
197+
}
198+
187199
return streamChatCompletion({
188200
prepared,
189201
progress,

src/provider/models.ts

Lines changed: 50 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -15,24 +15,27 @@ import { toModelCostInfo, type ModelCostInformation } from './pricing/costs';
1515

1616
export type ThinkingEffort = 'none' | 'high' | 'max';
1717

18+
export type ContextSize = 200000 | 1000000;
19+
1820
export type ModelConfigurationOptions = vscode.ProvideLanguageModelChatResponseOptions & {
1921
readonly modelConfiguration?: Record<string, unknown>;
2022
readonly configuration?: Record<string, unknown>;
2123
};
2224

23-
type ThinkingEffortConfigurationSchema = ReturnType<typeof buildThinkingEffortSchema>;
25+
type ModelConfigurationSchema = ReturnType<typeof buildModelConfigurationSchema>;
2426

2527
export type ModelPickerChatInformation = vscode.LanguageModelChatInformation &
2628
ModelCostInformation & {
2729
readonly isUserSelectable: boolean;
2830
readonly statusIcon?: vscode.ThemeIcon;
29-
readonly configurationSchema?: ThinkingEffortConfigurationSchema;
31+
readonly configurationSchema?: ModelConfigurationSchema;
3032
};
3133

3234
export function toChatInfo(
3335
m: ModelDefinition,
3436
hasApiKey: boolean,
3537
pricingCurrency?: PricingCurrency,
38+
contextSize?: number,
3639
): ModelPickerChatInformation {
3740
const modelDetail = resolveModelText(m, 'detail') ?? m.detail;
3841
const modelTooltip = resolveModelText(m, 'tooltip');
@@ -44,15 +47,15 @@ export function toChatInfo(
4447
detail: hasApiKey ? modelDetail : t('auth.apiKeyRequiredDetail'),
4548
tooltip: hasApiKey ? modelTooltip : t('auth.apiKeyRequiredDetail'),
4649
statusIcon: hasApiKey ? undefined : new vscode.ThemeIcon('warning'),
47-
maxInputTokens: m.maxInputTokens,
50+
maxInputTokens: contextSize ?? m.maxInputTokens,
4851
maxOutputTokens: m.maxOutputTokens,
4952
isUserSelectable: true,
5053
capabilities: {
5154
toolCalling: m.capabilities.toolCalling,
5255
imageInput: m.capabilities.imageInput,
5356
},
5457
...toModelCostInfo(m, pricingCurrency),
55-
...(m.capabilities.thinking ? { configurationSchema: buildThinkingEffortSchema() } : {}),
58+
...(hasApiKey ? { configurationSchema: buildModelConfigurationSchema(m) } : {}),
5659
};
5760
}
5861

@@ -71,24 +74,49 @@ export function getConfiguredThinkingEffort(options: ModelConfigurationOptions):
7174
return configuredEffort === 'max' ? 'max' : 'high';
7275
}
7376

74-
function buildThinkingEffortSchema() {
75-
return {
76-
properties: {
77-
reasoningEffort: {
78-
type: 'string',
79-
title: t('status.thinking'),
80-
enum: ['none', 'high', 'max'],
81-
enumItemLabels: [t('thinking.none'), t('thinking.high'), t('thinking.max')],
82-
enumDescriptions: [
83-
t('thinking.none.desc'),
84-
t('thinking.high.desc'),
85-
t('thinking.max.desc'),
86-
],
87-
default: 'high',
88-
group: 'navigation',
89-
},
90-
},
91-
} as const;
77+
/**
78+
* Read the context size selected by the user via the model-picker dropdown.
79+
* Falls back to the VS Code setting when the dropdown hasn't been used yet.
80+
*/
81+
export function getConfiguredContextSize(options: ModelConfigurationOptions): ContextSize {
82+
const configured =
83+
options.modelConfiguration?.contextSize ?? options.configuration?.contextSize;
84+
if (configured === 200000) {
85+
return 200000;
86+
}
87+
return 1000000;
88+
}
89+
90+
function buildModelConfigurationSchema(m: ModelDefinition) {
91+
const properties: Record<string, unknown> = {};
92+
93+
if (m.capabilities.thinking) {
94+
properties.reasoningEffort = {
95+
type: 'string',
96+
title: t('status.thinking'),
97+
enum: ['none', 'high', 'max'],
98+
enumItemLabels: [t('thinking.none'), t('thinking.high'), t('thinking.max')],
99+
enumDescriptions: [
100+
t('thinking.none.desc'),
101+
t('thinking.high.desc'),
102+
t('thinking.max.desc'),
103+
],
104+
default: 'high',
105+
group: 'navigation',
106+
};
107+
}
108+
109+
properties.contextSize = {
110+
type: 'number',
111+
title: t('contextSize.title'),
112+
enum: [200000, 1000000],
113+
enumItemLabels: [t('contextSize.200k'), t('contextSize.1m')],
114+
enumDescriptions: [t('contextSize.200k.desc'), t('contextSize.1m.desc')],
115+
default: 1000000,
116+
group: 'tokens',
117+
};
118+
119+
return { properties } as const;
92120
}
93121

94122
function resolveModelText(m: ModelDefinition, field: 'detail' | 'tooltip'): string | undefined {

src/provider/request.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,13 @@ import { t } from '../i18n';
88
import type { DeepSeekRequest } from '../types';
99
import { convertMessages, countMessageChars } from './convert';
1010
import {
11-
dumpDeepSeekRequest,
12-
type CacheDiagnosticsRecorder,
13-
type CacheDiagnosticsRun,
11+
dumpDeepSeekRequest,
12+
type CacheDiagnosticsRecorder,
13+
type CacheDiagnosticsRun,
1414
} from './debug';
15-
import { getConfiguredThinkingEffort, type ModelConfigurationOptions } from './models';
16-
import { classifyDeepSeekRequest, shouldForceThinkingNone, type RequestKind } from './routing';
15+
import { getConfiguredContextSize, getConfiguredThinkingEffort, type ContextSize, type ModelConfigurationOptions } from './models';
1716
import type { ReplayMarkerMetadata } from './replay';
17+
import { classifyDeepSeekRequest, shouldForceThinkingNone, type RequestKind } from './routing';
1818
import type { ConversationSegment } from './segment';
1919
import { collectTrailingToolResultIds, prepareRequestTools } from './tools/request';
2020
import { resolveImageMessages, type VisionDescriber } from './vision';
@@ -31,6 +31,8 @@ export interface PreparedChatRequest {
3131
replayMarkerMetadata: ReplayMarkerMetadata;
3232
visionMarkerTextChars?: number;
3333
initialResponseNotice?: string;
34+
/** The context size selected via the model-picker dropdown (if any). */
35+
configuredContextSize: ContextSize;
3436
}
3537

3638
export interface PrepareChatRequestOptions {
@@ -88,6 +90,7 @@ export async function prepareChatRequest({
8890
const configuredThinkingEffort = getConfiguredThinkingEffort(
8991
options as ModelConfigurationOptions,
9092
);
93+
const configuredContextSize = getConfiguredContextSize(options as ModelConfigurationOptions);
9194
// Only force helper requests into disabled thinking on the official API.
9295
// Custom endpoints keep their configured effort to preserve pre-#137 request shape.
9396
const forceNoneThinking =
@@ -147,5 +150,6 @@ export async function prepareChatRequest({
147150
replayMarkerMetadata: visionResolution.replayMarkerMetadata,
148151
visionMarkerTextChars: visionResolution.stats.markerVisionTextChars || undefined,
149152
initialResponseNotice: visionResolution.initialResponseNotice,
153+
configuredContextSize,
150154
};
151155
}

0 commit comments

Comments
 (0)