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
56 changes: 28 additions & 28 deletions extensions/copilot/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion extensions/copilot/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6865,7 +6865,7 @@
"@anthropic-ai/claude-agent-sdk": "0.2.112",
"@anthropic-ai/sdk": "^0.82.0",
"@github/blackbird-external-ingest-utils": "^0.3.0",
"@github/copilot": "1.0.39",
"@github/copilot": "1.0.48",
"@google/genai": "^1.22.0",
"@humanwhocodes/gitignore-to-minimatch": "1.0.2",
"@microsoft/tiktokenizer": "^1.0.10",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ export interface CopilotCLIModelInfo {
readonly id: string;
readonly name: string;
readonly multiplier?: number;
readonly priceCategory?: string;
readonly inputCost?: number;
readonly outputCost?: number;
readonly cacheCost?: number;
readonly maxInputTokens?: number;
readonly maxOutputTokens?: number;
readonly maxContextWindowTokens: number;
Expand Down Expand Up @@ -152,18 +156,26 @@ export class CopilotCLIModels extends Disposable implements ICopilotCLIModels {
const [{ getAvailableModels }, authInfo] = await Promise.all([this.copilotCLISDK.getPackage(), this.copilotCLISDK.getAuthInfo()]);
try {
const models = await getAvailableModels(authInfo);
return models.map(model => ({
id: model.id,
name: model.name,
multiplier: model.billing?.multiplier,
maxInputTokens: model.capabilities.limits.max_prompt_tokens,
maxOutputTokens: model.capabilities.limits.max_output_tokens,
maxContextWindowTokens: model.capabilities.limits.max_context_window_tokens,
supportsVision: model.capabilities.supports.vision,
supportsReasoningEffort: model.capabilities.supports.reasoningEffort,
defaultReasoningEffort: model.defaultReasoningEffort,
supportedReasoningEfforts: model.supportedReasoningEfforts,
} satisfies CopilotCLIModelInfo));
return models.map(model => {
const tokenPrices = model.billing?.token_prices;
const normalizedPricing = normalizeTokenPricing(tokenPrices);
return {
id: model.id,
name: model.name,
multiplier: model.billing?.multiplier,
priceCategory: model.model_picker_price_category,
inputCost: normalizedPricing?.inputPrice,
outputCost: normalizedPricing?.outputPrice,
cacheCost: normalizedPricing?.cachePrice,
Comment thread
lramos15 marked this conversation as resolved.
maxInputTokens: model.capabilities.limits.max_prompt_tokens,
maxOutputTokens: model.capabilities.limits.max_output_tokens,
maxContextWindowTokens: model.capabilities.limits.max_context_window_tokens,
supportsVision: model.capabilities.supports.vision,
supportsReasoningEffort: model.capabilities.supports.reasoningEffort,
defaultReasoningEffort: model.defaultReasoningEffort,
supportedReasoningEfforts: model.supportedReasoningEfforts,
} satisfies CopilotCLIModelInfo;
});
} catch (ex) {
this.logService.error(`[CopilotCLISession] Failed to fetch models`, ex);
return [];
Expand Down Expand Up @@ -205,6 +217,10 @@ export class CopilotCLIModels extends Disposable implements ICopilotCLIModels {
maxInputTokens: model.maxInputTokens ?? model.maxContextWindowTokens,
maxOutputTokens: model.maxOutputTokens ?? 0,
pricing: multiplier,
priceCategory: model.priceCategory,
inputCost: model.inputCost,
outputCost: model.outputCost,
cacheCost: model.cacheCost,
multiplierNumeric: model.multiplier,
isUserSelectable: true,
configurationSchema: isReasoningEffortEnabled ? buildConfigurationSchema(model) : undefined,
Expand Down Expand Up @@ -601,3 +617,24 @@ export function isEnabledForCopilotCLI(customization: { sessionTypes?: readonly
return sessionTypes === undefined || sessionTypes.includes('copilotcli') || false;
}

const AIC_DIVISOR = 1_000_000_000;
const TOKENS_PER_MILLION = 1_000_000;

/**
* Converts raw billing token prices (nano-AICs with a batch_size) into
* normalized AICs per million tokens, matching the normalization in
* chatEndpoint.ts for non-CLI models.
*/
function normalizeTokenPricing(tokenPrices: { input_price?: number; output_price?: number; cache_price?: number; batch_size?: number } | undefined): { inputPrice: number; outputPrice: number; cachePrice: number | undefined } | undefined {
if (!tokenPrices || tokenPrices.input_price === undefined || tokenPrices.output_price === undefined) {
return undefined;
}
const batchSize = tokenPrices.batch_size ?? TOKENS_PER_MILLION;
const scale = TOKENS_PER_MILLION / batchSize;
return {
inputPrice: (tokenPrices.input_price / AIC_DIVISOR) * scale,
outputPrice: (tokenPrices.output_price / AIC_DIVISOR) * scale,
cachePrice: tokenPrices.cache_price !== undefined ? (tokenPrices.cache_price / AIC_DIVISOR) * scale : undefined,
};
}

Original file line number Diff line number Diff line change
Expand Up @@ -55,19 +55,28 @@ describe('CopilotCLI SDK Upgrade', function () {
path.join('prebuilds', 'linux-x64', 'computer.node'),
path.join('prebuilds', 'win32-arm64', 'computer.node'),
path.join('prebuilds', 'win32-x64', 'computer.node'),
// win32 native module (formerly win_error_mode)
path.join('prebuilds', 'win32-arm64', 'win32.node'),
path.join('prebuilds', 'win32-x64', 'win32.node'),
// Second copy of computer.node / win32.node re-shipped by the @github/copilot/sdk subpackage
// icu-native and runtime native modules
path.join('prebuilds', 'darwin-arm64', 'icu-native.node'),
path.join('prebuilds', 'darwin-arm64', 'runtime.node'),
path.join('prebuilds', 'darwin-x64', 'icu-native.node'),
path.join('prebuilds', 'darwin-x64', 'runtime.node'),
path.join('prebuilds', 'linux-arm64', 'runtime.node'),
path.join('prebuilds', 'linux-x64', 'icu-native.node'),
path.join('prebuilds', 'linux-x64', 'runtime.node'),
path.join('prebuilds', 'win32-arm64', 'icu-native.node'),
path.join('prebuilds', 'win32-arm64', 'runtime.node'),
path.join('prebuilds', 'win32-arm64', 'win32-native.node'),
path.join('prebuilds', 'win32-x64', 'icu-native.node'),
path.join('prebuilds', 'win32-x64', 'runtime.node'),
path.join('prebuilds', 'win32-x64', 'win32-native.node'),
// Second copy of computer.node re-shipped by the @github/copilot/sdk subpackage
// (previously hidden by a broad sdk/prebuilds/** exclusion that masked the node-pty files we used to shim in at test setup).
path.join('sdk', 'prebuilds', 'darwin-arm64', 'computer.node'),
path.join('sdk', 'prebuilds', 'darwin-x64', 'computer.node'),
path.join('sdk', 'prebuilds', 'linux-arm64', 'computer.node'),
path.join('sdk', 'prebuilds', 'linux-x64', 'computer.node'),
path.join('sdk', 'prebuilds', 'win32-arm64', 'computer.node'),
path.join('sdk', 'prebuilds', 'win32-x64', 'computer.node'),
path.join('sdk', 'prebuilds', 'win32-arm64', 'win32.node'),
path.join('sdk', 'prebuilds', 'win32-x64', 'win32.node'),
path.join('ripgrep', 'bin', 'darwin-arm64', 'rg'),
path.join('ripgrep', 'bin', 'darwin-x64', 'rg'),
path.join('ripgrep', 'bin', 'linux-x64', 'rg'),
Expand Down Expand Up @@ -96,10 +105,46 @@ describe('CopilotCLI SDK Upgrade', function () {
'tree-sitter-rust.wasm',
'tree-sitter-typescript.wasm',
'tree-sitter-scala.wasm',
// foundry-local-sdk native bindings
path.join('foundry-local-sdk', 'node_modules', 'foundry-local-sdk', 'prebuilds', 'darwin-arm64', 'foundry_local_napi.node'),
path.join('foundry-local-sdk', 'node_modules', 'foundry-local-sdk', 'prebuilds', 'win32-x64', 'foundry_local_napi.node'),
path.join('foundry-local-sdk', 'node_modules', 'foundry-local-sdk', 'prebuilds', 'linux-x64', 'foundry_local_napi.node'),
path.join('foundry-local-sdk', 'node_modules', 'foundry-local-sdk', 'prebuilds', 'win32-arm64', 'foundry_local_napi.node'),
// pvrecorder native bindings
path.join('pvrecorder', 'node_modules', '@picovoice', 'pvrecorder-node', 'lib', 'linux', 'x86_64', 'pv_recorder.node'),
path.join('pvrecorder', 'node_modules', '@picovoice', 'pvrecorder-node', 'lib', 'mac', 'x86_64', 'pv_recorder.node'),
path.join('pvrecorder', 'node_modules', '@picovoice', 'pvrecorder-node', 'lib', 'windows', 'amd64', 'pv_recorder.node'),
path.join('pvrecorder', 'node_modules', '@picovoice', 'pvrecorder-node', 'lib', 'mac', 'arm64', 'pv_recorder.node'),
path.join('pvrecorder', 'node_modules', '@picovoice', 'pvrecorder-node', 'lib', 'windows', 'arm64', 'pv_recorder.node'),
// mxc-bin binaries (sandbox)
path.join('mxc-bin', 'arm64', 'winhttp-proxy-shim.exe'),
path.join('mxc-bin', 'arm64', 'wslcsdk.dll'),
path.join('mxc-bin', 'arm64', 'wxc-exec.exe'),
path.join('mxc-bin', 'arm64', 'wxc-test-proxy.exe'),
path.join('mxc-bin', 'arm64', 'lxc-exec'),
path.join('mxc-bin', 'arm64', 'wxc-windows-sandbox-guest.exe'),
path.join('mxc-bin', 'arm64', 'wxc-windows-sandbox-daemon.exe'),
path.join('mxc-bin', 'arm64', '_manifest', 'spdx_2.2', 'bsi.cose'),
path.join('mxc-bin', 'arm64', '_manifest', 'spdx_2.2', 'manifest.cat'),
path.join('mxc-bin', 'arm64', '_manifest', 'spdx_2.2', 'manifest.spdx.cose'),
path.join('mxc-bin', 'x64', 'winhttp-proxy-shim.exe'),
path.join('mxc-bin', 'x64', 'wslcsdk.dll'),
path.join('mxc-bin', 'x64', 'wxc-exec.exe'),
path.join('mxc-bin', 'x64', 'wxc-test-proxy.exe'),
path.join('mxc-bin', 'x64', 'lxc-exec'),
path.join('mxc-bin', 'x64', 'wxc-windows-sandbox-guest.exe'),
path.join('mxc-bin', 'x64', 'wxc-windows-sandbox-daemon.exe'),
path.join('mxc-bin', 'x64', '_manifest', 'spdx_2.2', 'bsi.cose'),
path.join('mxc-bin', 'x64', '_manifest', 'spdx_2.2', 'manifest.cat'),
path.join('mxc-bin', 'x64', '_manifest', 'spdx_2.2', 'manifest.spdx.cose'),
].map(p => path.join(copilotSDKPath, p)));

// Exclude ripgrep files that we copy over in src/extension/chatSessions/copilotcli/node/ripgrepShim.ts (until we get better API/solution from SDK)
const ripgrepFilesWeCopy = path.join(copilotSDKPath, 'sdk', 'ripgrep', 'bin');
// Exclude node-pty files under sdk/prebuilds/ — these are platform-specific postinstall artifacts
// that vary per OS (pty.node/spawn-helper on macOS/Linux, conpty files on Windows).
const sdkPrebuildsPath = path.join(copilotSDKPath, 'sdk', 'prebuilds');
const nodePtyBinaryNames = new Set(['pty.node', 'spawn-helper', 'conpty.node', 'conpty.pdb', 'conpty_console_list.node', 'conpty_console_list.pdb', 'OpenConsole.exe', 'conpty.dll']);

const errors: string[] = [];
// Look for new binaries
Expand All @@ -111,6 +156,9 @@ describe('CopilotCLI SDK Upgrade', function () {
if (binaryName.startsWith('keytar') || binaryName.startsWith('clipboard')) {
continue;
}
if (binary.startsWith(sdkPrebuildsPath) && nodePtyBinaryNames.has(binaryName)) {
continue;
}
if (!knownBinaries.has(binary)) {
errors.push(`Unexpected native binary found in Copilot CLI SDK: ${path.relative(copilotSDKPath, binary)}`);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -252,11 +252,19 @@ function resolveConfigProperty(
*/
function getPriceCategoryLabel(priceCategory: string | undefined): string | undefined {
switch (priceCategory) {
case 'low': return localize('chat.priceCategory.low', "Low cost");
case 'medium': return localize('chat.priceCategory.medium', "Medium cost");
case 'high': return localize('chat.priceCategory.high', "High cost");
case 'very_high': return localize('chat.priceCategory.veryHigh', "Very high cost");
default: return undefined;
case undefined:
case '':
return undefined;
case 'low':
return localize('chat.priceCategory.low', "Low cost");
case 'medium':
return localize('chat.priceCategory.medium', "Medium cost");
case 'high':
return localize('chat.priceCategory.high', "High cost");
case 'very_high':
return localize('chat.priceCategory.veryHigh', "Very high cost");
default:
return localize('chat.priceCategory.unknown', "{0} cost", priceCategory.charAt(0).toUpperCase() + priceCategory.slice(1));
Comment thread
lramos15 marked this conversation as resolved.
}
}

Expand Down
Loading