Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
**Pick DeepSeek V4 from the Copilot Chat model picker — and keep everything else Copilot already gives you.**

<p align="center">
<img src="resources/screenshots/01-picker.png" alt="DeepSeek V4 Pro and Flash in the Copilot Chat model picker, with the per-model Thinking Effort dropdown (None / High / Max)" width="800">
<img src="resources/screenshots/01-picker.png" alt="DeepSeek V4 Pro and Flash in the Copilot Chat model picker, with a per-model Thinking Effort dropdown" width="800">
</p>

Love DeepSeek's price-performance but don't want to give up GitHub Copilot's agent mode, tool calling, and polished UI? This extension drops **DeepSeek V4 Pro & Flash** straight into the Copilot Chat model selector — with **vision**, **thinking mode**, and your own API key.
Expand All @@ -45,7 +45,7 @@ This proxy is a compatibility bridge; if DeepSeek native vision becomes availabl
</p>

### Thinking Mode with Reasoning Effort Control
Full support for DeepSeek V4's `reasoning_content`. Use Copilot Chat's native model picker menu to choose `none` (off), `high` (balanced, default), or `max` (deep reasoning for hard agent tasks).
Full support for DeepSeek V4's `reasoning_content`. DeepSeek V4 Flash offers `none` (off), `low` (light reasoning), `high` (balanced, default), and `max` (deep reasoning for hard agent tasks). DeepSeek V4 Pro currently offers `none`, `high`, and `max`, matching the effort levels implemented by the official API.

### Inherits Every Copilot Capability
Because this plugs into Copilot's native provider API, you get the full stack for free:
Expand Down Expand Up @@ -88,10 +88,10 @@ Install from the registry used by your editor:

## Models

| Model | Best For |
|---|---|
| **DeepSeek V4 Flash** | Fast everyday coding, quick edits, cheap iteration |
| **DeepSeek V4 Pro** | Complex refactors, agent tasks, deep reasoning |
| Model | Thinking Effort | Best For |
|---|---|---|
| **DeepSeek V4 Flash** | `none` / `low` / `high` / `max` | Fast everyday coding, quick edits, cheap iteration |
| **DeepSeek V4 Pro** | `none` / `high` / `max` | Complex refactors, agent tasks, deep reasoning |

Both support optional thinking mode, tool calling, and 1M token context.

Expand Down
12 changes: 6 additions & 6 deletions README.zh-cn.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
**在 Copilot Chat 模型选择器中直接使用 DeepSeek V4——无需离开你熟悉的 Copilot 工作流。**

<p align="center">
<img src="resources/screenshots/01-picker.png" alt="DeepSeek V4 Pro 和 Flash 出现在 Copilot Chat 模型选择器中,带有可按模型独立设置的思考深度下拉菜单(停用 / 标准 / 深度)" width="800">
<img src="resources/screenshots/01-picker.png" alt="DeepSeek V4 Pro 和 Flash 出现在 Copilot Chat 模型选择器中,带有可按模型独立设置的思考深度下拉菜单" width="800">
</p>

喜欢 DeepSeek 的性价比,但不想放弃 GitHub Copilot 的 Agent 模式、工具调用和成熟的交互体验?本扩展将 **DeepSeek V4 Pro 和 Flash** 直接接入 Copilot Chat 模型选择器,支持**视觉识别**、**思考模式**,使用你自己的 API Key。
Expand All @@ -45,7 +45,7 @@ DeepSeek V4 是纯文本模型。将截图拖入聊天,本扩展会自动将
</p>

### 思考模式与推理深度控制
完整支持 DeepSeek V4 的 `reasoning_content`。通过 Copilot Chat 模型选择器的菜单选择 `停用`、`标准`(均衡,默认)或 `深度`(适用于复杂 Agent 任务)。
完整支持 DeepSeek V4 的 `reasoning_content`。DeepSeek V4 Flash 可选择 `停用`、`轻量`、`标准`(均衡,默认)或 `深度`(适用于复杂 Agent 任务);DeepSeek V4 Pro 目前提供 `停用`、`标准` 和 `深度`,与官方 API 已实现的推理档位保持一致

### 继承全部 Copilot 能力
由于本扩展接入的是 Copilot 的原生 provider API,你免费获得完整能力栈:
Expand Down Expand Up @@ -88,10 +88,10 @@ API Key 存储在 VS Code 的 `SecretStorage` 中(macOS 钥匙串 / Windows

## 模型

| 模型 | 适用场景 |
|---|---|
| **DeepSeek V4 Flash** | 日常快速编码、小改动、低成本迭代 |
| **DeepSeek V4 Pro** | 复杂重构、Agent 任务、深度推理 |
| 模型 | 思考深度 | 适用场景 |
|---|---|---|
| **DeepSeek V4 Flash** | `停用` / `轻量` / `标准` / `深度` | 日常快速编码、小改动、低成本迭代 |
| **DeepSeek V4 Pro** | `停用` / `标准` / `深度` | 复杂重构、Agent 任务、深度推理 |

两者均支持可选的思考模式、工具调用和 1M Token 上下文。

Expand Down
12 changes: 10 additions & 2 deletions src/consts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,11 @@ export const MODELS: ModelDefinition[] = [
capabilities: {
toolCalling: DEEPSEEK_TOOLS_LIMIT,
imageInput: true,
thinking: true,
thinking: {
supportedEfforts: ['low', 'high', 'max'],
defaultEffort: 'high',
canDisable: true,
},
},
requiresThinkingParam: true,
pricing: {
Expand All @@ -79,7 +83,11 @@ export const MODELS: ModelDefinition[] = [
capabilities: {
toolCalling: DEEPSEEK_TOOLS_LIMIT,
imageInput: true,
thinking: true,
thinking: {
supportedEfforts: ['high', 'max'],
defaultEffort: 'high',
canDisable: true,
},
},
requiresThinkingParam: true,
pricing: {
Expand Down
4 changes: 4 additions & 0 deletions src/i18n.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ const zh: Translations = {
'status.thinking': '思考模式',
'thinking.none': '停用',
'thinking.none.desc': '停用思考,响应更快',
'thinking.low': '轻量',
'thinking.low.desc': '轻量推理,适合快速编辑和简单任务',
'thinking.high': '标准',
'thinking.high.desc': '推荐日常使用',
'thinking.max': '深度',
Expand Down Expand Up @@ -227,6 +229,8 @@ const en: Translations = {
'status.thinking': 'Thinking Effort',
'thinking.none': 'None',
'thinking.none.desc': 'Disable thinking for faster responses',
'thinking.low': 'Low',
'thinking.low.desc': 'Light reasoning for quick edits and simple tasks',
'thinking.high': 'High',
'thinking.high.desc': 'Recommended for most tasks',
'thinking.max': 'Max',
Expand Down
53 changes: 36 additions & 17 deletions src/provider/models.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import vscode from 'vscode';
import { t } from '../i18n';
import type { ModelDefinition, PricingCurrency } from '../types';
import type {
ModelDefinition,
PricingCurrency,
ReasoningEffort,
ThinkingCapability,
} from '../types';
import { toModelCostInfo, type ModelCostInformation } from './pricing/costs';

/**
Expand All @@ -14,7 +19,7 @@ import { toModelCostInfo, type ModelCostInformation } from './pricing/costs';
* metadata and per-model configuration controls.
*/

export type ThinkingEffort = 'none' | 'high' | 'max';
export type ThinkingEffort = 'none' | ReasoningEffort;

export type ModelConfigurationOptions = vscode.ProvideLanguageModelChatResponseOptions & {
readonly modelConfiguration?: Record<string, unknown>;
Expand All @@ -38,6 +43,7 @@ export function toChatInfo(
): ModelPickerChatInformation {
const modelDetail = resolveModelText(m, 'detail') ?? m.detail;
const modelTooltip = resolveModelText(m, 'tooltip');
const thinkingCapability = m.capabilities.thinking;
return {
id: m.id,
name: m.name,
Expand All @@ -55,45 +61,58 @@ export function toChatInfo(
imageInput: m.capabilities.imageInput,
},
...toModelCostInfo(m, pricingCurrency),
...(m.capabilities.thinking ? { configurationSchema: buildThinkingEffortSchema() } : {}),
...(thinkingCapability
? { configurationSchema: buildThinkingEffortSchema(thinkingCapability) }
: {}),
};
}

export function getConfiguredThinkingEffort(options: ModelConfigurationOptions): ThinkingEffort {
export function getConfiguredThinkingEffort(
options: ModelConfigurationOptions,
thinkingCapability: ThinkingCapability,
): ThinkingEffort {
const configuredEffort =
options.modelConfiguration?.reasoningEffort ?? options.configuration?.reasoningEffort;

if (configuredEffort === 'none') {
if (configuredEffort === 'none' && thinkingCapability.canDisable) {
return 'none';
}

if (configuredEffort === 'high') {
return 'high';
if (isSupportedReasoningEffort(configuredEffort, thinkingCapability)) {
return configuredEffort;
}

return configuredEffort === 'max' ? 'max' : 'high';
return thinkingCapability.defaultEffort;
}

function buildThinkingEffortSchema() {
function buildThinkingEffortSchema(thinkingCapability: ThinkingCapability) {
const efforts: ThinkingEffort[] = [
...(thinkingCapability.canDisable ? (['none'] as const) : []),
...thinkingCapability.supportedEfforts,
];

return {
properties: {
reasoningEffort: {
type: 'string',
title: t('status.thinking'),
enum: ['none', 'high', 'max'],
enumItemLabels: [t('thinking.none'), t('thinking.high'), t('thinking.max')],
enumDescriptions: [
t('thinking.none.desc'),
t('thinking.high.desc'),
t('thinking.max.desc'),
],
default: 'high',
enum: efforts,
enumItemLabels: efforts.map((effort) => t(`thinking.${effort}`)),
enumDescriptions: efforts.map((effort) => t(`thinking.${effort}.desc`)),
default: thinkingCapability.defaultEffort,
group: 'navigation',
},
},
} as const;
}

function isSupportedReasoningEffort(
value: unknown,
thinkingCapability: ThinkingCapability,
): value is ReasoningEffort {
return thinkingCapability.supportedEfforts.some((effort) => effort === value);
}

function resolveModelText(m: ModelDefinition, field: 'detail' | 'tooltip'): string | undefined {
const suffix = m.id.startsWith('deepseek-v4-') ? m.id.slice('deepseek-v4-'.length) : m.id;
const key = `model.${suffix}.${field}`;
Expand Down
9 changes: 5 additions & 4 deletions src/provider/request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@ export async function prepareChatRequest({
const baseUrl = getBaseUrl();
const client = new DeepSeekClient(baseUrl, apiKey);
const modelDef = MODELS.find((m) => m.id === modelInfo.id);
const isThinkingModel = modelDef?.capabilities.thinking ?? false;
const thinkingCapability = modelDef?.capabilities.thinking;
const isThinkingModel = Boolean(thinkingCapability);
const maxTokens = getMaxTokens();

const visionResolution = await resolveImageMessages(messages, token, getVisionDescriber);
Expand All @@ -85,9 +86,9 @@ export async function prepareChatRequest({
request: baseRequest,
inputMessages: messages,
});
const configuredThinkingEffort = getConfiguredThinkingEffort(
options as ModelConfigurationOptions,
);
const configuredThinkingEffort = thinkingCapability
? getConfiguredThinkingEffort(options as ModelConfigurationOptions, thinkingCapability)
: 'none';
// Only force helper requests into disabled thinking on the official API.
// Custom endpoints keep their configured effort to preserve pre-#137 request shape.
const forceNoneThinking =
Expand Down
14 changes: 12 additions & 2 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

// ---- API request/response types ----

/** API-level reasoning efforts. Disabling thinking is modeled separately. */
export type ReasoningEffort = 'low' | 'high' | 'max';

export interface DeepSeekMessage {
role: 'system' | 'user' | 'assistant' | 'tool';
content: string;
Expand Down Expand Up @@ -48,7 +51,7 @@ export interface DeepSeekRequest {
tools?: DeepSeekTool[];
tool_choice?: 'none' | 'auto' | 'required';
thinking?: { type: 'enabled' | 'disabled' };
reasoning_effort?: 'high' | 'max';
reasoning_effort?: ReasoningEffort;
stream_options?: {
include_usage: boolean;
};
Expand Down Expand Up @@ -103,6 +106,13 @@ export interface ModelPricing {
output: number;
}

export interface ThinkingCapability {
/** Effort values this model implements and may receive in API requests. */
supportedEfforts: readonly ReasoningEffort[];
defaultEffort: ReasoningEffort;
canDisable: boolean;
}

export interface ModelDefinition {
id: string;
name: string;
Expand All @@ -114,7 +124,7 @@ export interface ModelDefinition {
capabilities: {
toolCalling: boolean | number;
imageInput: boolean;
thinking: boolean;
thinking: ThinkingCapability | false;
};
requiresThinkingParam: boolean;
pricing?: Readonly<Record<PricingCurrency, ModelPricing>>;
Expand Down
Loading