codeep 2.17.0 → 2.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/index.js +7 -1
- package/dist/config/providers.d.ts +9 -0
- package/dist/config/providers.js +69 -17
- package/dist/renderer/App.js +8 -2
- package/dist/renderer/commands/helpers.d.ts +2 -0
- package/dist/renderer/commands/helpers.js +16 -2
- package/dist/renderer/components/Status.d.ts +6 -0
- package/dist/renderer/main.js +2 -0
- package/dist/utils/tokenTracker.d.ts +6 -0
- package/dist/utils/tokenTracker.js +34 -7
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/dist/api/index.js
CHANGED
|
@@ -343,7 +343,13 @@ async function chatOpenAI(message, history, model, apiKey, onChunk, abortSignal)
|
|
|
343
343
|
let baseUrl = resolveBaseUrl(providerId, 'openai');
|
|
344
344
|
const authHeader = getProviderAuthHeader(providerId, 'openai');
|
|
345
345
|
const useCompletionTokens = usesMaxCompletionTokens(providerId);
|
|
346
|
-
|
|
346
|
+
// Two independent reasons to leave temperature out, and both must be checked
|
|
347
|
+
// here: the provider-level flag (endpoints that only accept the default) and
|
|
348
|
+
// the per-model one (generations that removed the sampling params outright,
|
|
349
|
+
// e.g. gemini-3.7-flash). Testing only the provider flag let a rejecting
|
|
350
|
+
// model through, because every model this rule covers is served over the
|
|
351
|
+
// OpenAI-compatible path, not the Anthropic one.
|
|
352
|
+
const omitTemperature = requiresDefaultTemperature(providerId) || modelRejectsSamplingParams(model);
|
|
347
353
|
if (!baseUrl) {
|
|
348
354
|
throw new Error(`Provider ${providerId} does not support OpenAI protocol`);
|
|
349
355
|
}
|
|
@@ -34,6 +34,10 @@ export interface ProviderConfig {
|
|
|
34
34
|
subscribeUrl?: string;
|
|
35
35
|
noApiKey?: boolean;
|
|
36
36
|
dynamicModels?: boolean;
|
|
37
|
+
/** Billed as a flat subscription (or free) — token counts are real, per-token
|
|
38
|
+
* cost is not. Cost surfaces must say "included in plan" instead of pricing
|
|
39
|
+
* these tokens at the provider's pay-per-use rates. */
|
|
40
|
+
flatFee?: boolean;
|
|
37
41
|
groupLabel?: string;
|
|
38
42
|
hint?: string;
|
|
39
43
|
mcpEndpoints?: {
|
|
@@ -60,6 +64,11 @@ export declare function getProviderModels(providerId: string): {
|
|
|
60
64
|
}[];
|
|
61
65
|
export declare function isNoApiKeyProvider(providerId: string): boolean;
|
|
62
66
|
export declare function isDynamicModelsProvider(providerId: string): boolean;
|
|
67
|
+
/**
|
|
68
|
+
* Returns true if the provider bills a flat subscription (or is free), so any
|
|
69
|
+
* per-token dollar figure we compute for it is invented — see `flatFee`.
|
|
70
|
+
*/
|
|
71
|
+
export declare function isFlatFeeProvider(providerId: string): boolean;
|
|
63
72
|
export declare function getProviderBaseUrl(providerId: string, protocol: 'openai' | 'anthropic'): string | null;
|
|
64
73
|
export declare function getProviderAuthHeader(providerId: string, protocol: 'openai' | 'anthropic'): 'Bearer' | 'x-api-key';
|
|
65
74
|
export declare function getProviderMcpEndpoints(providerId: string): ProviderConfig['mcpEndpoints'] | null;
|
package/dist/config/providers.js
CHANGED
|
@@ -18,15 +18,19 @@ export const PROVIDERS = {
|
|
|
18
18
|
},
|
|
19
19
|
},
|
|
20
20
|
models: [
|
|
21
|
-
|
|
21
|
+
// GLM-5.3 is Coding-Plan only — the standalone pay-per-use API does not
|
|
22
|
+
// accept it yet, so it must NOT be copied into `z.ai-api`.
|
|
23
|
+
{ id: 'glm-5.3', name: 'GLM-5.3', description: 'Latest flagship for project-scale engineering (1M context)' },
|
|
24
|
+
{ id: 'glm-5.2', name: 'GLM-5.2', description: 'Previous flagship for project-scale engineering (1M context)' },
|
|
22
25
|
{ id: 'glm-5-turbo', name: 'GLM-5 Turbo', description: 'Fast GLM-5 variant, available to all users' },
|
|
23
26
|
],
|
|
24
|
-
defaultModel: 'glm-5.
|
|
27
|
+
defaultModel: 'glm-5.3',
|
|
25
28
|
defaultProtocol: 'openai',
|
|
26
29
|
maxOutputTokens: 131_072,
|
|
27
30
|
envKey: 'ZAI_API_KEY',
|
|
28
31
|
subscribeUrl: 'https://z.ai/subscribe?ic=NXYNXZOV14',
|
|
29
32
|
groupLabel: 'Z.AI — Subscription (GLM Coding Plan)',
|
|
33
|
+
flatFee: true,
|
|
30
34
|
hint: 'Uses your Z.AI subscription — no per-token charges.',
|
|
31
35
|
mcpEndpoints: {
|
|
32
36
|
webSearch: 'https://api.z.ai/api/mcp/web_search_prime/mcp',
|
|
@@ -81,6 +85,7 @@ export const PROVIDERS = {
|
|
|
81
85
|
envKey: 'ZAI_CN_API_KEY',
|
|
82
86
|
subscribeUrl: 'https://open.bigmodel.cn/glm-coding',
|
|
83
87
|
groupLabel: 'Z.AI China — Subscription (GLM Coding Plan)',
|
|
88
|
+
flatFee: true,
|
|
84
89
|
hint: 'Uses your ZhipuAI China subscription.',
|
|
85
90
|
mcpEndpoints: {
|
|
86
91
|
webSearch: 'https://open.bigmodel.cn/api/mcp/web_search_prime/mcp',
|
|
@@ -133,6 +138,7 @@ export const PROVIDERS = {
|
|
|
133
138
|
envKey: 'MINIMAX_API_KEY',
|
|
134
139
|
subscribeUrl: 'https://platform.minimax.io/subscribe/coding-plan?code=2lWvoWUhrp&source=link',
|
|
135
140
|
groupLabel: 'MiniMax — Subscription',
|
|
141
|
+
flatFee: true,
|
|
136
142
|
hint: 'Uses your MiniMax subscription — no per-token charges.',
|
|
137
143
|
},
|
|
138
144
|
'minimax-api': {
|
|
@@ -178,6 +184,7 @@ export const PROVIDERS = {
|
|
|
178
184
|
envKey: 'MINIMAX_CN_API_KEY',
|
|
179
185
|
subscribeUrl: 'https://platform.minimaxi.com',
|
|
180
186
|
groupLabel: 'MiniMax China — Subscription',
|
|
187
|
+
flatFee: true,
|
|
181
188
|
hint: 'Uses your MiniMax China subscription.',
|
|
182
189
|
},
|
|
183
190
|
'deepseek': {
|
|
@@ -230,6 +237,7 @@ export const PROVIDERS = {
|
|
|
230
237
|
envKey: 'KIMI_CODE_API_KEY',
|
|
231
238
|
subscribeUrl: 'https://www.kimi.com/code',
|
|
232
239
|
groupLabel: 'Kimi — Subscription (Kimi Code)',
|
|
240
|
+
flatFee: true,
|
|
233
241
|
hint: 'Uses your Kimi Code subscription — no per-token charges. Key from kimi.com/code/console.',
|
|
234
242
|
},
|
|
235
243
|
'kimi-api': {
|
|
@@ -283,10 +291,15 @@ export const PROVIDERS = {
|
|
|
283
291
|
openai: { baseUrl: 'https://api.x.ai/v1', authHeader: 'Bearer', supportsNativeTools: true },
|
|
284
292
|
},
|
|
285
293
|
models: [
|
|
286
|
-
{ id: 'grok-4.
|
|
294
|
+
{ id: 'grok-4.6', name: 'Grok 4.6', description: 'Flagship reasoning model — xAI recommends it for code, 500K context' },
|
|
295
|
+
{ id: 'grok-4.5', name: 'Grok 4.5', description: 'Previous flagship reasoning model, 500K context' },
|
|
287
296
|
{ id: 'grok-build-0.1', name: 'Grok Build 0.1', description: 'Agentic coding model — fast, 256K context' },
|
|
288
|
-
{ id: 'grok-4.3', name: 'Grok 4.3', description: '
|
|
297
|
+
{ id: 'grok-4.3', name: 'Grok 4.3', description: 'Older flagship, 1M context' },
|
|
289
298
|
],
|
|
299
|
+
// Stays on the agentic coder, not the new flagship. grok-4.6 is the better
|
|
300
|
+
// model and xAI recommends it for code, but it bills 2x input and 3x output
|
|
301
|
+
// against grok-build-0.1 — moving every unpinned user onto it silently is
|
|
302
|
+
// not ours to decide. It is one `/model` away for anyone who wants it.
|
|
290
303
|
defaultModel: 'grok-build-0.1',
|
|
291
304
|
defaultProtocol: 'openai',
|
|
292
305
|
useMaxCompletionTokens: true, // reasoning models reject max_tokens
|
|
@@ -317,6 +330,7 @@ export const PROVIDERS = {
|
|
|
317
330
|
envKey: 'BAILIAN_CODING_PLAN_API_KEY',
|
|
318
331
|
subscribeUrl: 'https://www.alibabacloud.com/help/en/model-studio/qwen-code-coding-plan',
|
|
319
332
|
groupLabel: 'Qwen — Subscription (Coding Plan)',
|
|
333
|
+
flatFee: true,
|
|
320
334
|
hint: 'Uses your Qwen Coding Plan — no per-token charges. sk-sp-… key from Model Studio. Interactive coding use only.',
|
|
321
335
|
},
|
|
322
336
|
'qwen-api': {
|
|
@@ -359,6 +373,7 @@ export const PROVIDERS = {
|
|
|
359
373
|
envKey: 'BAILIAN_TOKEN_PLAN_API_KEY',
|
|
360
374
|
subscribeUrl: 'https://modelstudio.console.alibabacloud.com/',
|
|
361
375
|
groupLabel: 'Qwen — Subscription (Token Plan)',
|
|
376
|
+
flatFee: true,
|
|
362
377
|
hint: 'Uses monthly Token Plan credits. Requires a separate sk-sp-… Token Plan key.',
|
|
363
378
|
},
|
|
364
379
|
'qwen-cn': {
|
|
@@ -379,6 +394,7 @@ export const PROVIDERS = {
|
|
|
379
394
|
envKey: 'BAILIAN_CODING_PLAN_CN_API_KEY',
|
|
380
395
|
subscribeUrl: 'https://bailian.console.aliyun.com/',
|
|
381
396
|
groupLabel: 'Qwen China — Subscription (Coding Plan)',
|
|
397
|
+
flatFee: true,
|
|
382
398
|
hint: 'Uses your Qwen Coding Plan (China). sk-sp-… key from Bailian.',
|
|
383
399
|
},
|
|
384
400
|
'qwen-cn-api': {
|
|
@@ -418,6 +434,7 @@ export const PROVIDERS = {
|
|
|
418
434
|
envKey: 'MODELSCOPE_API_KEY',
|
|
419
435
|
subscribeUrl: 'https://modelscope.cn/my/myaccesstoken',
|
|
420
436
|
groupLabel: 'ModelScope — Free (Qwen)',
|
|
437
|
+
flatFee: true,
|
|
421
438
|
hint: 'Fetches the live free catalog for your ModelScope token; availability and limits vary by account.',
|
|
422
439
|
},
|
|
423
440
|
'openai': {
|
|
@@ -478,7 +495,8 @@ export const PROVIDERS = {
|
|
|
478
495
|
},
|
|
479
496
|
models: [
|
|
480
497
|
{ id: 'gemini-3.1-pro-preview', name: 'Gemini 3.1 Pro', description: 'Most capable Gemini model' },
|
|
481
|
-
{ id: 'gemini-3.
|
|
498
|
+
{ id: 'gemini-3.7-flash', name: 'Gemini 3.7 Flash', description: 'Latest production Flash model — adjustable thinking, 64K output' },
|
|
499
|
+
{ id: 'gemini-3.6-flash', name: 'Gemini 3.6 Flash', description: 'Previous production Flash model' },
|
|
482
500
|
{ id: 'gemini-3.5-flash', name: 'Gemini 3.5 Flash', description: 'Stable frontier Flash model for coding and long agentic tasks' },
|
|
483
501
|
{ id: 'gemini-3.5-flash-lite', name: 'Gemini 3.5 Flash-Lite', description: 'Latest low-latency, low-cost workhorse' },
|
|
484
502
|
],
|
|
@@ -510,11 +528,11 @@ export const PROVIDERS = {
|
|
|
510
528
|
{ id: 'anthropic/claude-sonnet-5', name: 'Claude Sonnet 5', description: 'Anthropic — balanced' },
|
|
511
529
|
{ id: 'openai/gpt-5.6-sol', name: 'GPT-5.6 Sol', description: 'OpenAI — flagship' },
|
|
512
530
|
{ id: 'openai/gpt-5.6-luna', name: 'GPT-5.6 Luna', description: 'OpenAI — fast/efficient' },
|
|
513
|
-
{ id: 'google/gemini-3.
|
|
531
|
+
{ id: 'google/gemini-3.7-flash', name: 'Gemini 3.7 Flash', description: 'Google — latest production Flash' },
|
|
514
532
|
{ id: 'deepseek/deepseek-v4-pro', name: 'DeepSeek V4 Pro', description: 'DeepSeek — flagship agentic model' },
|
|
515
533
|
{ id: 'moonshotai/kimi-k3', name: 'Kimi K3', description: 'Moonshot — long-horizon coding' },
|
|
516
534
|
{ id: 'qwen/qwen3.8-max', name: 'Qwen 3.8 Max', description: 'Alibaba — latest flagship' },
|
|
517
|
-
{ id: 'x-ai/grok-4.
|
|
535
|
+
{ id: 'x-ai/grok-4.6', name: 'Grok 4.6', description: 'xAI — flagship reasoning' },
|
|
518
536
|
],
|
|
519
537
|
defaultModel: 'openrouter/auto',
|
|
520
538
|
defaultProtocol: 'openai',
|
|
@@ -695,6 +713,13 @@ export function isNoApiKeyProvider(providerId) {
|
|
|
695
713
|
export function isDynamicModelsProvider(providerId) {
|
|
696
714
|
return PROVIDERS[providerId]?.dynamicModels === true;
|
|
697
715
|
}
|
|
716
|
+
/**
|
|
717
|
+
* Returns true if the provider bills a flat subscription (or is free), so any
|
|
718
|
+
* per-token dollar figure we compute for it is invented — see `flatFee`.
|
|
719
|
+
*/
|
|
720
|
+
export function isFlatFeeProvider(providerId) {
|
|
721
|
+
return PROVIDERS[providerId]?.flatFee === true;
|
|
722
|
+
}
|
|
698
723
|
export function getProviderBaseUrl(providerId, protocol) {
|
|
699
724
|
const provider = PROVIDERS[providerId];
|
|
700
725
|
if (!provider)
|
|
@@ -745,14 +770,23 @@ export function providerNoStreamWithTools(providerId) {
|
|
|
745
770
|
* MODEL-level check, not a provider-level one (requiresDefaultTemperature
|
|
746
771
|
* can't express it). Omitting the field is always safe — the API treats
|
|
747
772
|
* omission as default. Kimi K2.x code/thinking models fix temperature
|
|
748
|
-
* internally and 400 on any custom value, so they're here too.
|
|
773
|
+
* internally and 400 on any custom value, so they're here too. Google removed
|
|
774
|
+
* the deprecated sampling parameters outright in the Gemini 3.7 generation.
|
|
749
775
|
*/
|
|
750
776
|
const SAMPLING_PARAMS_REJECTED = [
|
|
751
777
|
'claude-fable-5', 'claude-opus-5', 'claude-opus-4-8', 'claude-opus-4-7', 'claude-sonnet-5',
|
|
752
778
|
'kimi-k3', 'kimi-k2.7-code', 'kimi-for-coding', 'k3',
|
|
779
|
+
'gemini-3.7-flash',
|
|
753
780
|
];
|
|
754
781
|
export function modelRejectsSamplingParams(model) {
|
|
755
|
-
|
|
782
|
+
// Canonicalize both sides. OpenRouter routes these as `google/gemini-3.7-flash`
|
|
783
|
+
// and `anthropic/claude-opus-4.8`, which a raw comparison misses — so the model
|
|
784
|
+
// gets a temperature it rejects, on the one path where the id is namespaced.
|
|
785
|
+
const id = canonicalModelId(model);
|
|
786
|
+
return SAMPLING_PARAMS_REJECTED.some(entry => {
|
|
787
|
+
const canonical = canonicalModelId(entry);
|
|
788
|
+
return id === canonical || id.startsWith(`${canonical}-`);
|
|
789
|
+
});
|
|
756
790
|
}
|
|
757
791
|
/**
|
|
758
792
|
* Returns the effective max output tokens for a provider, capped by the provider's limit.
|
|
@@ -807,8 +841,9 @@ export function modelSupportsReasoningEffort(providerId, model) {
|
|
|
807
841
|
case 'z.ai-api':
|
|
808
842
|
case 'z.ai-cn':
|
|
809
843
|
case 'z.ai-cn-api':
|
|
810
|
-
// GLM-5.2 exposes graded High/Max effort;
|
|
811
|
-
|
|
844
|
+
// GLM-5.2 exposes graded High/Max effort; GLM-5.3 adds a distinct Low.
|
|
845
|
+
// Turbo is a plain toggle.
|
|
846
|
+
return idMatches(id, 'glm-5-2') || idMatches(id, 'glm-5-3');
|
|
812
847
|
case 'kimi':
|
|
813
848
|
return idMatches(id, 'k3');
|
|
814
849
|
case 'kimi-api':
|
|
@@ -854,14 +889,25 @@ export function reasoningParamsFor(providerId, model, tier) {
|
|
|
854
889
|
// none/low/medium/high/xhigh — no "max"; map our Max → xhigh (the ceiling).
|
|
855
890
|
return { reasoning_effort: tier === 'max' ? 'xhigh' : tier };
|
|
856
891
|
case 'google':
|
|
857
|
-
// Gemini
|
|
858
|
-
|
|
892
|
+
// Gemini's OpenAI-compat layer maps reasoning_effort onto thinking_level
|
|
893
|
+
// and documents low | medium | high. (Medium 400'd on Gemini 3 Preview,
|
|
894
|
+
// which is why this used to collapse it — that was a preview-era bug and
|
|
895
|
+
// is fixed.) 'max' has no Gemini equivalent, so it tops out at high.
|
|
896
|
+
// 'minimal' is deliberately not emitted: 3.7 Flash rejects it outright.
|
|
897
|
+
return { reasoning_effort: tier === 'max' ? 'high' : tier };
|
|
859
898
|
case 'deepseek':
|
|
899
|
+
// Graded thinking depth: high (default) or max. Lower tiers collapse to high.
|
|
900
|
+
return { reasoning_effort: tier === 'max' ? 'max' : 'high' };
|
|
860
901
|
case 'z.ai':
|
|
861
902
|
case 'z.ai-api':
|
|
862
903
|
case 'z.ai-cn':
|
|
863
904
|
case 'z.ai-cn-api':
|
|
864
|
-
//
|
|
905
|
+
// GLM-5.3 accepts low/high/max; GLM-5.2 grades only high|max, so lower
|
|
906
|
+
// tiers collapse to high there. Either way we always send an effort and
|
|
907
|
+
// never a disabled thinking block — GLM-5.3 rejects "disabled" outright.
|
|
908
|
+
if (idMatches(canonicalModelId(model), 'glm-5-3')) {
|
|
909
|
+
return { reasoning_effort: tier === 'low' ? 'low' : tier === 'max' ? 'max' : 'high' };
|
|
910
|
+
}
|
|
865
911
|
return { reasoning_effort: tier === 'max' ? 'max' : 'high' };
|
|
866
912
|
case 'kimi':
|
|
867
913
|
case 'kimi-api':
|
|
@@ -896,14 +942,20 @@ export function availableReasoningTiers(providerId, model) {
|
|
|
896
942
|
case 'openai':
|
|
897
943
|
return ['auto', 'low', 'medium', 'high', 'max'];
|
|
898
944
|
case 'google':
|
|
899
|
-
//
|
|
900
|
-
|
|
945
|
+
// low | medium | high, per Gemini's OpenAI-compat mapping table. Medium
|
|
946
|
+
// is 3.7 Flash's own default and the tier Google recommends for agentic
|
|
947
|
+
// coding, so collapsing it hid the setting most users want.
|
|
948
|
+
return ['auto', 'low', 'medium', 'high'];
|
|
901
949
|
case 'deepseek':
|
|
950
|
+
return ['auto', 'high', 'max'];
|
|
902
951
|
case 'z.ai':
|
|
903
952
|
case 'z.ai-api':
|
|
904
953
|
case 'z.ai-cn':
|
|
905
954
|
case 'z.ai-cn-api':
|
|
906
|
-
|
|
955
|
+
// GLM-5.3 distinguishes a Low tier; GLM-5.2 grades only high|max.
|
|
956
|
+
return idMatches(canonicalModelId(model), 'glm-5-3')
|
|
957
|
+
? ['auto', 'low', 'high', 'max']
|
|
958
|
+
: ['auto', 'high', 'max'];
|
|
907
959
|
case 'kimi':
|
|
908
960
|
case 'kimi-api':
|
|
909
961
|
case 'kimi-cn':
|
package/dist/renderer/App.js
CHANGED
|
@@ -2636,8 +2636,14 @@ export class App {
|
|
|
2636
2636
|
`runtime ${elapsed}`,
|
|
2637
2637
|
`tokens ${formatTokenCount(totalTokens)}`,
|
|
2638
2638
|
];
|
|
2639
|
-
|
|
2640
|
-
|
|
2639
|
+
// Only pay-per-use tokens carry a real price; flat-fee providers get the
|
|
2640
|
+
// short "in plan" wording so the segment can't crowd the right-edge hint.
|
|
2641
|
+
const billable = typeof stats.billableCost === 'number' ? stats.billableCost : (stats.estimatedCost ?? 0);
|
|
2642
|
+
if (billable > 0) {
|
|
2643
|
+
leftParts.push(`cost $${billable < 0.01 ? billable.toFixed(4) : billable.toFixed(2)}${stats.hasFlatFeeUsage ? ' + in plan' : ''}`);
|
|
2644
|
+
}
|
|
2645
|
+
else if (stats.hasFlatFeeUsage) {
|
|
2646
|
+
leftParts.push('cost in plan');
|
|
2641
2647
|
}
|
|
2642
2648
|
// Thinking-effort tier, same chip the compact fallback shows beside the
|
|
2643
2649
|
// model. Only present when set + supported — see getStatus.
|
|
@@ -77,6 +77,8 @@ export interface StatsTotals {
|
|
|
77
77
|
totalTokens: number;
|
|
78
78
|
totalPromptTokens: number;
|
|
79
79
|
totalCompletionTokens: number;
|
|
80
|
+
/** Raw sum across every entry. The report's total is derived from the
|
|
81
|
+
* breakdown instead, so flat-fee rows don't contribute dollars. */
|
|
80
82
|
estimatedCost: number;
|
|
81
83
|
}
|
|
82
84
|
export interface StatsCache {
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* alongside `ctx.app.*` calls. Pulling them here gives them direct unit
|
|
8
8
|
* coverage.
|
|
9
9
|
*/
|
|
10
|
+
import { isFlatFeeProvider } from '../../config/providers.js';
|
|
10
11
|
/** Snippet window: chars of context before / after the match. */
|
|
11
12
|
export const SEARCH_SNIPPET_BEFORE = 30;
|
|
12
13
|
export const SEARCH_SNIPPET_AFTER = 50;
|
|
@@ -121,6 +122,10 @@ export function formatMemoryList(notes) {
|
|
|
121
122
|
export function formatModelCost(provider, estimatedCost) {
|
|
122
123
|
if (provider === 'ollama')
|
|
123
124
|
return 'free';
|
|
125
|
+
// Flat-fee providers meter real tokens but charge nothing per token — the
|
|
126
|
+
// dollar figure our pricing table computes for them is invented.
|
|
127
|
+
if (isFlatFeeProvider(provider))
|
|
128
|
+
return 'included in plan';
|
|
124
129
|
return estimatedCost > 0 ? `~$${estimatedCost.toFixed(4)}` : '(no pricing data)';
|
|
125
130
|
}
|
|
126
131
|
/**
|
|
@@ -143,11 +148,20 @@ export function formatStatsReport(args) {
|
|
|
143
148
|
lines.push(`- **${b.model}** (${b.provider}): ${fmt(b.promptTokens)} in / ${fmt(b.completionTokens)} out — ${costStr}`);
|
|
144
149
|
}
|
|
145
150
|
lines.push('');
|
|
151
|
+
// The total prices only pay-per-use rows; flat-fee rows are called out
|
|
152
|
+
// rather than folded in (or quietly dropped) — see formatModelCost.
|
|
153
|
+
const planRows = breakdown.filter(b => isFlatFeeProvider(b.provider));
|
|
154
|
+
const billable = breakdown
|
|
155
|
+
.filter(b => !isFlatFeeProvider(b.provider))
|
|
156
|
+
.reduce((s, b) => s + b.estimatedCost, 0);
|
|
146
157
|
if (currentProvider === 'ollama') {
|
|
147
158
|
lines.push(`**Total: free · ${fmt(totals.totalTokens)} tokens**`);
|
|
148
159
|
}
|
|
149
|
-
else if (
|
|
150
|
-
lines.push(`**Total:
|
|
160
|
+
else if (planRows.length === breakdown.length) {
|
|
161
|
+
lines.push(`**Total: included in plan · ${fmt(totals.totalTokens)} tokens**`);
|
|
162
|
+
}
|
|
163
|
+
else if (billable > 0) {
|
|
164
|
+
lines.push(`**Total: ~$${billable.toFixed(4)}${planRows.length > 0 ? ' + usage included in plan' : ''}**`);
|
|
151
165
|
}
|
|
152
166
|
}
|
|
153
167
|
if (cache.cacheReadTokens > 0 || cache.cacheCreationTokens > 0) {
|
|
@@ -21,6 +21,12 @@ export interface StatusInfo {
|
|
|
21
21
|
completionTokens: number;
|
|
22
22
|
requestCount: number;
|
|
23
23
|
estimatedCost?: number;
|
|
24
|
+
/** `estimatedCost` minus flat-fee entries — the only spend we may show in
|
|
25
|
+
* dollars (see providers.flatFee). */
|
|
26
|
+
billableCost?: number;
|
|
27
|
+
/** At least one entry came from a flat-fee provider, so the footer says
|
|
28
|
+
* "in plan" instead of pricing those tokens. */
|
|
29
|
+
hasFlatFeeUsage?: boolean;
|
|
24
30
|
};
|
|
25
31
|
}
|
|
26
32
|
/**
|
package/dist/renderer/main.js
CHANGED
|
@@ -111,6 +111,8 @@ function getStatus() {
|
|
|
111
111
|
completionTokens: stats.totalCompletionTokens,
|
|
112
112
|
requestCount: stats.requestCount,
|
|
113
113
|
estimatedCost: stats.estimatedCost,
|
|
114
|
+
billableCost: stats.billableCost,
|
|
115
|
+
hasFlatFeeUsage: stats.hasFlatFeeUsage,
|
|
114
116
|
},
|
|
115
117
|
};
|
|
116
118
|
}
|
|
@@ -19,6 +19,12 @@ export interface SessionTokenStats {
|
|
|
19
19
|
totalTokens: number;
|
|
20
20
|
requestCount: number;
|
|
21
21
|
estimatedCost: number;
|
|
22
|
+
/** `estimatedCost` minus every flat-fee entry — the only figure we may show
|
|
23
|
+
* as a dollar total, since flat-fee tokens carry no per-token charge. */
|
|
24
|
+
billableCost: number;
|
|
25
|
+
/** True when at least one entry came from a flat-fee provider, so callers can
|
|
26
|
+
* say "included in plan" instead of silently dropping that usage. */
|
|
27
|
+
hasFlatFeeUsage: boolean;
|
|
22
28
|
/** Anthropic prompt caching: total tokens written to cache this session. */
|
|
23
29
|
totalCacheCreationTokens: number;
|
|
24
30
|
/** Anthropic prompt caching: total tokens read from cache this session. */
|
|
@@ -2,12 +2,14 @@
|
|
|
2
2
|
* Token and cost tracking for API usage
|
|
3
3
|
*/
|
|
4
4
|
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
5
|
+
import { isFlatFeeProvider } from '../config/providers.js';
|
|
5
6
|
import { formatResourceImpactReport } from './resourceImpact.js';
|
|
6
7
|
// Context window sizes per model (in tokens). Primarily mirrors providers.ts;
|
|
7
8
|
// retired aliases remain only where restored historical sessions still need a
|
|
8
9
|
// meaningful context/cost display.
|
|
9
10
|
const MODEL_CONTEXT_WINDOWS = {
|
|
10
11
|
// Z.AI / ZhipuAI
|
|
12
|
+
'glm-5.3': 1_000_000,
|
|
11
13
|
'glm-5.2': 1_000_000,
|
|
12
14
|
'glm-5.1': 200_000,
|
|
13
15
|
'glm-5-turbo': 202_752,
|
|
@@ -29,6 +31,7 @@ const MODEL_CONTEXT_WINDOWS = {
|
|
|
29
31
|
'deepseek-v4-flash': 1_000_000,
|
|
30
32
|
// Google
|
|
31
33
|
'gemini-3.1-pro-preview': 1_048_576,
|
|
34
|
+
'gemini-3.7-flash': 1_048_576,
|
|
32
35
|
'gemini-3.6-flash': 1_048_576,
|
|
33
36
|
'gemini-3.5-flash': 1_048_576,
|
|
34
37
|
'gemini-3.5-flash-lite': 1_048_576,
|
|
@@ -45,6 +48,7 @@ const MODEL_CONTEXT_WINDOWS = {
|
|
|
45
48
|
'k3': 1_000_000,
|
|
46
49
|
'k3-256k': 262_144,
|
|
47
50
|
// Grok (xAI)
|
|
51
|
+
'grok-4.6': 500_000,
|
|
48
52
|
'grok-4.5': 500_000,
|
|
49
53
|
'grok-build-0.1': 256_000,
|
|
50
54
|
'grok-4.3': 1_000_000,
|
|
@@ -72,6 +76,9 @@ export function getModelContextWindow(model) {
|
|
|
72
76
|
const MODEL_PRICING = {
|
|
73
77
|
// Z.AI / ZhipuAI
|
|
74
78
|
// Coding Plan is flat-fee; these official rates apply to pay-per-use.
|
|
79
|
+
// `glm-5.3` is GLM Coding Plan only — Z.AI publishes no per-token rate for it
|
|
80
|
+
// (the standalone model API is still "coming soon"), so it stays unpriced
|
|
81
|
+
// rather than borrowing GLM-5.2's.
|
|
75
82
|
'glm-5.2': { inputPer1M: 1.40, outputPer1M: 4.40 },
|
|
76
83
|
'glm-5.1': { inputPer1M: 1.40, outputPer1M: 4.40 },
|
|
77
84
|
'glm-5-turbo': { inputPer1M: 1.20, outputPer1M: 4.00 },
|
|
@@ -92,8 +99,11 @@ const MODEL_PRICING = {
|
|
|
92
99
|
'deepseek-v4-pro': { inputPer1M: 0.435, outputPer1M: 0.87 },
|
|
93
100
|
'deepseek-v4-flash': { inputPer1M: 0.14, outputPer1M: 0.28 },
|
|
94
101
|
// Google
|
|
102
|
+
// Gemini 3.6/3.7 Flash carry PROMOTIONAL rates that run through 2026-12-31 and
|
|
103
|
+
// step up to 1.50/7.50 on 2027-01-01 — revisit both rows on that date.
|
|
95
104
|
'gemini-3.1-pro-preview': { inputPer1M: 2.00, outputPer1M: 12.00 },
|
|
96
|
-
'gemini-3.
|
|
105
|
+
'gemini-3.7-flash': { inputPer1M: 0.75, outputPer1M: 3.75 },
|
|
106
|
+
'gemini-3.6-flash': { inputPer1M: 0.75, outputPer1M: 3.75 },
|
|
97
107
|
'gemini-3.5-flash': { inputPer1M: 1.50, outputPer1M: 9.00 },
|
|
98
108
|
'gemini-3.5-flash-lite': { inputPer1M: 0.30, outputPer1M: 2.50 },
|
|
99
109
|
'gemini-3-flash-preview': { inputPer1M: 0.50, outputPer1M: 3.00 },
|
|
@@ -110,7 +120,9 @@ const MODEL_PRICING = {
|
|
|
110
120
|
'kimi-for-coding-highspeed': { inputPer1M: 0.95, outputPer1M: 4.00 },
|
|
111
121
|
'k3': { inputPer1M: 3.00, outputPer1M: 15.00 },
|
|
112
122
|
'k3-256k': { inputPer1M: 3.00, outputPer1M: 15.00 },
|
|
113
|
-
// Grok (xAI)
|
|
123
|
+
// Grok (xAI) — base-tier rates. xAI doubles Grok 4.5/4.6 at prompts ≥200K;
|
|
124
|
+
// this table stores one flat rate per model, so the base tier is what we show.
|
|
125
|
+
'grok-4.6': { inputPer1M: 2.00, outputPer1M: 6.00 },
|
|
114
126
|
'grok-4.5': { inputPer1M: 2.00, outputPer1M: 6.00 },
|
|
115
127
|
'grok-build-0.1': { inputPer1M: 1.00, outputPer1M: 2.00 },
|
|
116
128
|
'grok-4.3': { inputPer1M: 1.25, outputPer1M: 2.50 },
|
|
@@ -293,13 +305,20 @@ export function getSessionStats() {
|
|
|
293
305
|
totalCacheCreationTokens += record.cacheCreationTokens ?? 0;
|
|
294
306
|
totalCacheReadTokens += record.cacheReadTokens ?? 0;
|
|
295
307
|
}
|
|
296
|
-
const
|
|
308
|
+
const breakdown = getCostBreakdown();
|
|
309
|
+
const estimatedCost = breakdown.reduce((s, b) => s + b.estimatedCost, 0);
|
|
310
|
+
const billableCost = breakdown
|
|
311
|
+
.filter(b => !isFlatFeeProvider(b.provider))
|
|
312
|
+
.reduce((s, b) => s + b.estimatedCost, 0);
|
|
313
|
+
const hasFlatFeeUsage = breakdown.some(b => isFlatFeeProvider(b.provider));
|
|
297
314
|
return {
|
|
298
315
|
totalPromptTokens,
|
|
299
316
|
totalCompletionTokens,
|
|
300
317
|
totalTokens,
|
|
301
318
|
requestCount: currentRecords().length,
|
|
302
319
|
estimatedCost,
|
|
320
|
+
billableCost,
|
|
321
|
+
hasFlatFeeUsage,
|
|
303
322
|
totalCacheCreationTokens,
|
|
304
323
|
totalCacheReadTokens,
|
|
305
324
|
};
|
|
@@ -350,18 +369,26 @@ export function formatCostReport() {
|
|
|
350
369
|
return '_No API requests in this session yet._';
|
|
351
370
|
}
|
|
352
371
|
const breakdown = getCostBreakdown();
|
|
372
|
+
// Flat-fee providers (subscriptions / free tiers) bill nothing per token, so
|
|
373
|
+
// their notional cost is never shown and never folded into the total. A
|
|
374
|
+
// session can mix them with pay-per-use models, so decide per entry.
|
|
375
|
+
const planEntries = breakdown.filter(b => isFlatFeeProvider(b.provider));
|
|
376
|
+
const costLine = planEntries.length === breakdown.length
|
|
377
|
+
? '**Estimated cost:** included in plan'
|
|
378
|
+
: `**Estimated cost:** $${stats.billableCost.toFixed(4)}${planEntries.length > 0 ? ' + usage included in plan' : ''}`;
|
|
353
379
|
const lines = [
|
|
354
380
|
'## Session Cost',
|
|
355
381
|
'',
|
|
356
382
|
`**Requests:** ${stats.requestCount} · **Input:** ${formatTokenCount(stats.totalPromptTokens)} · **Output:** ${formatTokenCount(stats.totalCompletionTokens)} · **Total:** ${formatTokenCount(stats.totalTokens)}`,
|
|
357
|
-
|
|
383
|
+
costLine,
|
|
358
384
|
'',
|
|
359
385
|
];
|
|
360
|
-
if (breakdown.length > 1 || (breakdown.length === 1 && breakdown[0].estimatedCost > 0)) {
|
|
386
|
+
if (breakdown.length > 1 || (breakdown.length === 1 && (breakdown[0].estimatedCost > 0 || planEntries.length > 0))) {
|
|
361
387
|
lines.push('| Provider / Model | Input | Output | Cost |');
|
|
362
388
|
lines.push('|---|---:|---:|---:|');
|
|
363
389
|
for (const b of breakdown) {
|
|
364
|
-
|
|
390
|
+
const cost = isFlatFeeProvider(b.provider) ? 'included in plan' : `$${b.estimatedCost.toFixed(4)}`;
|
|
391
|
+
lines.push(`| \`${b.provider}\` / \`${b.model}\` | ${formatTokenCount(b.promptTokens)} | ${formatTokenCount(b.completionTokens)} | ${cost} |`);
|
|
365
392
|
}
|
|
366
393
|
}
|
|
367
394
|
// Prompt caching summary — only shown if at least one cached call landed.
|
|
@@ -379,7 +406,7 @@ export function formatCostReport() {
|
|
|
379
406
|
lines.push('', ...formatResourceImpactReport(stats.totalTokens));
|
|
380
407
|
// Models with no pricing entry don't contribute to cost — flag so users
|
|
381
408
|
// aren't surprised the total looks low.
|
|
382
|
-
const untracked = breakdown.filter(b => b.estimatedCost === 0 && (b.promptTokens + b.completionTokens) > 0);
|
|
409
|
+
const untracked = breakdown.filter(b => b.estimatedCost === 0 && (b.promptTokens + b.completionTokens) > 0 && !isFlatFeeProvider(b.provider));
|
|
383
410
|
if (untracked.length > 0) {
|
|
384
411
|
lines.push('', `_Note: ${untracked.length} model${untracked.length === 1 ? '' : 's'} (${untracked.map(u => `\`${u.model}\``).join(', ')}) have no pricing entry — token counts are tracked but not priced._`);
|
|
385
412
|
}
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const VERSION = "2.
|
|
1
|
+
export declare const VERSION = "2.18.0";
|
package/dist/version.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
// AUTO-GENERATED by scripts/gen-version.js — do not edit by hand.
|
|
2
2
|
// Baked from package.json at build time so the bun-compiled binary reports
|
|
3
3
|
// the right version (it has no package.json on disk to read at runtime).
|
|
4
|
-
export const VERSION = '2.
|
|
4
|
+
export const VERSION = '2.18.0';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codeep",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.18.0",
|
|
4
4
|
"description": "AI-powered coding assistant built for the terminal. Multiple LLM providers, project-aware context, and a seamless development workflow.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|