codeep 3.4.1 → 3.5.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/acp/commands.js +9 -4
- package/dist/acp/server.d.ts +13 -0
- package/dist/acp/server.js +46 -4
- package/dist/acp/serverHandlers.js +10 -10
- package/dist/api/index.js +6 -3
- package/dist/config/index.js +12 -4
- package/dist/config/providers.d.ts +48 -4
- package/dist/config/providers.js +325 -88
- package/dist/renderer/commands.js +17 -8
- package/dist/utils/agent.d.ts +16 -0
- package/dist/utils/agent.js +24 -3
- package/dist/utils/agentChat.js +22 -10
- package/dist/utils/personalities.js +8 -2
- package/dist/utils/taskPlanner.js +12 -4
- package/dist/utils/tokenTracker.d.ts +13 -5
- package/dist/utils/tokenTracker.js +163 -34
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
|
@@ -9,7 +9,7 @@ import { config, getCurrentProvider, getModelsForCurrentProvider, PROTOCOLS, LAN
|
|
|
9
9
|
import { setTelegramToken, clearTelegramToken, hasTelegramToken } from '../utils/telegramCredentials.js';
|
|
10
10
|
import { getProjectContext } from '../utils/project.js';
|
|
11
11
|
import { getCurrentVersion } from '../utils/update.js';
|
|
12
|
-
import { getProviderList, getProvider, modelSupportsReasoningEffort, reasoningParamsFor, availableReasoningTiers, resolveReasoningTier, REASONING_TIERS } from '../config/providers.js';
|
|
12
|
+
import { getProviderList, getProvider, modelSupportsReasoningEffort, reasoningParamsFor, availableReasoningTiers, resolveReasoningTier, agentTurnReasoningNote, replacementModelFor, REASONING_TIERS } from '../config/providers.js';
|
|
13
13
|
import { setProjectContext } from '../api/index.js';
|
|
14
14
|
import { runSkill, runCommandChain } from './agentExecution.js';
|
|
15
15
|
import { loadProjectIntelligence, saveProjectIntelligence, INTELLIGENCE_NOT_SAVED } from '../utils/projectIntelligence.js';
|
|
@@ -301,14 +301,15 @@ export async function handleCommand(command, args, ctx) {
|
|
|
301
301
|
ctx.app.notify('Thinking effort: auto — each model uses its own default.');
|
|
302
302
|
}
|
|
303
303
|
else if (!supported) {
|
|
304
|
-
ctx.app.notify(`Thinking effort set to "${sub}", but ${model} has no graded thinking control — it will be ignored until you switch to a model that does (e.g. Opus 5, GPT-5.x or GPT-6, Gemini 3, DeepSeek V4
|
|
304
|
+
ctx.app.notify(`Thinking effort set to "${sub}", but ${model} has no graded thinking control — it will be ignored until you switch to a model that does (e.g. Opus 5.5, GPT-5.x or GPT-6, Gemini 3, DeepSeek V4, GLM-5.x, Kimi K3).`);
|
|
305
305
|
}
|
|
306
306
|
else {
|
|
307
307
|
// Tell the user what THIS model will actually run (the tier may
|
|
308
308
|
// collapse onto a level the model distinguishes, e.g. medium→high on Kimi K3).
|
|
309
309
|
const resolved = resolveReasoningTier(providerId, model, sub);
|
|
310
310
|
const note = resolved === sub ? '' : ` (${model} runs this as "${resolved}")`;
|
|
311
|
-
|
|
311
|
+
const agentNote = agentTurnReasoningNote(providerId, model);
|
|
312
|
+
ctx.app.notify(`Thinking effort: ${sub}${note} — sending ${JSON.stringify(reasoningParamsFor(providerId, model, sub))}.${agentNote ? ` ${agentNote}` : ''}`);
|
|
312
313
|
}
|
|
313
314
|
break;
|
|
314
315
|
}
|
|
@@ -333,8 +334,11 @@ export async function handleCommand(command, args, ctx) {
|
|
|
333
334
|
}
|
|
334
335
|
if (supported)
|
|
335
336
|
tLines.push(`**Available** ${available.join(' · ')}`);
|
|
337
|
+
const agentNote = agentTurnReasoningNote(providerId, model);
|
|
338
|
+
if (agentNote)
|
|
339
|
+
tLines.push(`**Agent turns** reasoning off — ${agentNote}`);
|
|
336
340
|
tLines.push('');
|
|
337
|
-
tLines.push('Sets how hard the model reasons. Each model offers only the levels it distinguishes (DeepSeek
|
|
341
|
+
tLines.push('Sets how hard the model reasons. Each model offers only the levels it distinguishes (DeepSeek & Kimi K3 → low · high · max; Gemini → low · medium · high; Opus/Sonnet & GPT-5.x/6 → the full set). The setting is global and clamps to the active model, so it never sends a value the API rejects. `/effort` is an alias.');
|
|
338
342
|
ctx.app.addMessage({ role: 'system', content: tLines.join('\n') });
|
|
339
343
|
break;
|
|
340
344
|
}
|
|
@@ -1737,14 +1741,19 @@ Format: use headers per category, only include categories where you found issues
|
|
|
1737
1741
|
const replacedCount = ctx.app.getMessages().length;
|
|
1738
1742
|
ctx.app.setMessages(cp.messages);
|
|
1739
1743
|
saveSession(ctx.sessionId, cp.messages, ctx.projectPath);
|
|
1740
|
-
// Switch provider/model back to checkpoint state if different.
|
|
1744
|
+
// Switch provider/model back to checkpoint state if different. A
|
|
1745
|
+
// checkpoint predates any later retirement, so its model goes through the
|
|
1746
|
+
// same map as a stored config (`gpt-6-astra` comes back as `gpt-6-sol`),
|
|
1747
|
+
// looked up on the provider actually active after the switch.
|
|
1741
1748
|
if (cp.provider && cp.provider !== getCurrentProvider().id)
|
|
1742
1749
|
setProvider(cp.provider);
|
|
1743
|
-
|
|
1744
|
-
|
|
1750
|
+
const cpModel = cp.model && (replacementModelFor(config.get('provider'), cp.model) ?? cp.model);
|
|
1751
|
+
if (cpModel && cpModel !== config.get('model'))
|
|
1752
|
+
config.set('model', cpModel);
|
|
1753
|
+
const movedNote = cpModel !== cp.model ? ` (the checkpoint's \`${cp.model}\` is no longer offered)` : '';
|
|
1745
1754
|
ctx.app.addMessage({
|
|
1746
1755
|
role: 'system',
|
|
1747
|
-
content: `# Rewound to ${cp.name ? `**${cp.name}**` : `\`${cp.id}\``}\n\nRestored ${cp.messages.length} message${cp.messages.length === 1 ? '' : 's'} (was ${replacedCount}). Provider: \`${cp.provider}\` · Model: \`${
|
|
1756
|
+
content: `# Rewound to ${cp.name ? `**${cp.name}**` : `\`${cp.id}\``}\n\nRestored ${cp.messages.length} message${cp.messages.length === 1 ? '' : 's'} (was ${replacedCount}). Provider: \`${cp.provider}\` · Model: \`${cpModel}\`${movedNote}\n\n${buildRewindGitHint(cp)}`,
|
|
1748
1757
|
});
|
|
1749
1758
|
break;
|
|
1750
1759
|
}
|
package/dist/utils/agent.d.ts
CHANGED
|
@@ -14,6 +14,22 @@ import { type TrustBearingWrite } from './toolExecution';
|
|
|
14
14
|
import { undoLastAction, undoAllActions, getCurrentSession, getRecentSessions, formatSession, ActionSession } from './history';
|
|
15
15
|
import { VerifyResult } from './verify';
|
|
16
16
|
import { TaskPlan, SubTask } from './taskPlanner';
|
|
17
|
+
/**
|
|
18
|
+
* The text an assistant turn is kept as in the history sent back next time.
|
|
19
|
+
*
|
|
20
|
+
* The loop stores each turn as plain text, and a turn that only called tools
|
|
21
|
+
* has none: Claude often skips the narration, and Opus 5.5 and Fable 5.1 move
|
|
22
|
+
* it into thinking blocks, which the stream parser does not keep. Stored as
|
|
23
|
+
* '', that turn is an empty non-final message on the next request, which
|
|
24
|
+
* Anthropic's Messages API refuses with a 400 ("all messages must have
|
|
25
|
+
* non-empty content except for the optional final assistant message").
|
|
26
|
+
* agentChat turns that 400 into the text-tool fallback, which sends the same
|
|
27
|
+
* history and fails the same way, so the run died on its second iteration.
|
|
28
|
+
* Naming the tools keeps the turn truthful and non-empty.
|
|
29
|
+
*/
|
|
30
|
+
export declare function assistantHistoryText(content: string, toolCalls: ReadonlyArray<{
|
|
31
|
+
tool: string;
|
|
32
|
+
}>): string;
|
|
17
33
|
export type PermissionOutcome = 'allow_once' | 'allow_always' | 'reject_once' | 'reject_always';
|
|
18
34
|
export type PermissionDecision = 'allow-once' | 'allow-always' | 'deny-once' | 'deny-always';
|
|
19
35
|
/**
|
package/dist/utils/agent.js
CHANGED
|
@@ -56,6 +56,27 @@ function truncateToolResult(output, toolName) {
|
|
|
56
56
|
const truncated = output.length - TOOL_RESULT_MAX_CHARS;
|
|
57
57
|
return `${kept}\n[... ${truncated} chars truncated — use search_code or read specific sections if you need more]`;
|
|
58
58
|
}
|
|
59
|
+
// ─── Assistant turns in the flattened history ─────────────────────────────────
|
|
60
|
+
/**
|
|
61
|
+
* The text an assistant turn is kept as in the history sent back next time.
|
|
62
|
+
*
|
|
63
|
+
* The loop stores each turn as plain text, and a turn that only called tools
|
|
64
|
+
* has none: Claude often skips the narration, and Opus 5.5 and Fable 5.1 move
|
|
65
|
+
* it into thinking blocks, which the stream parser does not keep. Stored as
|
|
66
|
+
* '', that turn is an empty non-final message on the next request, which
|
|
67
|
+
* Anthropic's Messages API refuses with a 400 ("all messages must have
|
|
68
|
+
* non-empty content except for the optional final assistant message").
|
|
69
|
+
* agentChat turns that 400 into the text-tool fallback, which sends the same
|
|
70
|
+
* history and fails the same way, so the run died on its second iteration.
|
|
71
|
+
* Naming the tools keeps the turn truthful and non-empty.
|
|
72
|
+
*/
|
|
73
|
+
export function assistantHistoryText(content, toolCalls) {
|
|
74
|
+
if (content.trim())
|
|
75
|
+
return content;
|
|
76
|
+
if (toolCalls.length > 0)
|
|
77
|
+
return `Using ${[...new Set(toolCalls.map(t => t.tool))].join(', ')}.`;
|
|
78
|
+
return '(no reply)';
|
|
79
|
+
}
|
|
59
80
|
// ─── Context window compression ───────────────────────────────────────────────
|
|
60
81
|
const CONTEXT_COMPRESS_THRESHOLD = 200_000; // ~50K tokens, safe for all providers
|
|
61
82
|
const RECENT_MESSAGES_TO_KEEP = 6; // Always preserve the last N messages verbatim
|
|
@@ -1120,7 +1141,7 @@ export async function runAgent(prompt, projectContext, options = {}) {
|
|
|
1120
1141
|
if (hasIncompleteWork) {
|
|
1121
1142
|
debug('Model wants to continue, prompting for next action');
|
|
1122
1143
|
incompleteWorkRetries++;
|
|
1123
|
-
messages.push({ role: 'assistant', content });
|
|
1144
|
+
messages.push({ role: 'assistant', content: assistantHistoryText(content, toolCalls) });
|
|
1124
1145
|
messages.push({
|
|
1125
1146
|
role: 'user',
|
|
1126
1147
|
content: 'Continue. Execute the tool calls now.'
|
|
@@ -1136,8 +1157,8 @@ export async function runAgent(prompt, projectContext, options = {}) {
|
|
|
1136
1157
|
debug(`Agent finished at iteration ${iteration}`);
|
|
1137
1158
|
break;
|
|
1138
1159
|
}
|
|
1139
|
-
// Add assistant response to history
|
|
1140
|
-
messages.push({ role: 'assistant', content });
|
|
1160
|
+
// Add assistant response to history — never empty (see assistantHistoryText).
|
|
1161
|
+
messages.push({ role: 'assistant', content: assistantHistoryText(content, toolCalls) });
|
|
1141
1162
|
// Execute tool calls
|
|
1142
1163
|
const toolResults = [];
|
|
1143
1164
|
for (const toolCall of toolCalls) {
|
package/dist/utils/agentChat.js
CHANGED
|
@@ -20,7 +20,7 @@ import { config, getApiKey, resolveBaseUrl } from '../config/index.js';
|
|
|
20
20
|
import { loadProjectIntelligence, generateContextFromIntelligence } from './projectIntelligence.js';
|
|
21
21
|
import { formatCommandIndex } from './commandIndex.js';
|
|
22
22
|
import { syncProgress, generateProjectId } from './codeepCloud.js';
|
|
23
|
-
import { getProviderAuthHeader, supportsNativeTools, getEffectiveMaxTokens, usesMaxCompletionTokens, requiresDefaultTemperature, modelRejectsSamplingParams, isNoApiKeyProvider, reasoningParamsFor, providerNoStreamWithTools } from '../config/providers.js';
|
|
23
|
+
import { getProviderAuthHeader, supportsNativeTools, getEffectiveMaxTokens, usesMaxCompletionTokens, requiresDefaultTemperature, modelRejectsSamplingParams, isNoApiKeyProvider, reasoningParamsFor, providerNoStreamWithTools, minResponseTokensFor } from '../config/providers.js';
|
|
24
24
|
import { recordTokenUsage, extractOpenAIUsage, extractAnthropicUsage } from './tokenTracker.js';
|
|
25
25
|
import { parseOpenAIToolCalls, parseAnthropicToolCalls, parseToolCalls } from './toolParsing.js';
|
|
26
26
|
import { formatToolDefinitions, getOpenAITools, getAnthropicTools } from './tools.js';
|
|
@@ -484,10 +484,17 @@ additionalTools, runtime) {
|
|
|
484
484
|
// Provider-level guard (OpenAI GPT-5+) OR model-level guard — Anthropic's
|
|
485
485
|
// Fable 5 / Opus 4.7+ reject temperature with a 400; omission is safe.
|
|
486
486
|
const tempParam = (requiresDefaultTemperature(providerId) || modelRejectsSamplingParams(model)) ? {} : { temperature: config.get('temperature') };
|
|
487
|
-
|
|
488
|
-
|
|
487
|
+
const tier = config.get('reasoningEffort');
|
|
488
|
+
// Room for the answer after the thinking (Opus 5.5 thinks on every turn).
|
|
489
|
+
const responseBudget = Math.max(config.get('maxTokens'), 16384, minResponseTokensFor(model, tier));
|
|
489
490
|
if (protocol === 'openai') {
|
|
490
|
-
const
|
|
491
|
+
const openAITools = getOpenAITools(additionalTools, allowedTools);
|
|
492
|
+
// Thinking-effort tier → provider-shaped param ({} for 'auto'/unsupported).
|
|
493
|
+
// This request carries tools, which GPT-6 Sol/Luna on Chat Completions
|
|
494
|
+
// accept only at reasoning_effort "none"; told so, reasoningParamsFor
|
|
495
|
+
// sends that for them whatever the tier.
|
|
496
|
+
const openAIReasoning = reasoningParamsFor(providerId, model, tier, { tools: openAITools.length > 0 });
|
|
497
|
+
const maxTok = getEffectiveMaxTokens(providerId, responseBudget);
|
|
491
498
|
const tokParam = usesMaxCompletionTokens(providerId) ? { max_completion_tokens: maxTok } : { max_tokens: maxTok };
|
|
492
499
|
endpoint = `${baseUrl}/chat/completions`;
|
|
493
500
|
// OpenRouter-specific extras: request `usage` block in the response
|
|
@@ -537,8 +544,8 @@ additionalTools, runtime) {
|
|
|
537
544
|
}
|
|
538
545
|
body = {
|
|
539
546
|
model, messages: [{ role: 'system', content: systemPrompt }, ...messages],
|
|
540
|
-
tools:
|
|
541
|
-
...tempParam, ...tokParam, ...
|
|
547
|
+
tools: openAITools, tool_choice: 'auto', stream: useStreaming,
|
|
548
|
+
...tempParam, ...tokParam, ...openAIReasoning,
|
|
542
549
|
// Ask ALL OpenAI-compatible providers to emit a usage block in the
|
|
543
550
|
// stream — without this most (DeepSeek/Kimi/Grok/Qwen/GLM/…) send no
|
|
544
551
|
// usage on streamed responses and the whole turn records zero tokens.
|
|
@@ -568,7 +575,8 @@ additionalTools, runtime) {
|
|
|
568
575
|
system: [{ type: 'text', text: systemPrompt, cache_control: { type: 'ephemeral' } }],
|
|
569
576
|
messages,
|
|
570
577
|
tools: cachedTools, stream: useStreaming,
|
|
571
|
-
...tempParam, ...
|
|
578
|
+
...tempParam, ...reasoningParamsFor(providerId, model, tier),
|
|
579
|
+
max_tokens: getEffectiveMaxTokens(providerId, responseBudget),
|
|
572
580
|
};
|
|
573
581
|
}
|
|
574
582
|
const response = await fetch(endpoint, {
|
|
@@ -701,10 +709,14 @@ export async function agentChatFallback(messages, systemPrompt, onChunk, abortSi
|
|
|
701
709
|
// Provider-level guard (OpenAI GPT-5+) OR model-level guard — Anthropic's
|
|
702
710
|
// Fable 5 / Opus 4.7+ reject temperature with a 400; omission is safe.
|
|
703
711
|
const tempParam = (requiresDefaultTemperature(providerId) || modelRejectsSamplingParams(model)) ? {} : { temperature: config.get('temperature') };
|
|
712
|
+
const tier = config.get('reasoningEffort');
|
|
704
713
|
// Thinking-effort tier → provider-shaped param ({} for 'auto'/unsupported).
|
|
705
|
-
|
|
714
|
+
// No `tools` array goes out on this path — the tools are text in the
|
|
715
|
+
// prompt — so GPT-6 Sol/Luna keep the user's tier here.
|
|
716
|
+
const reasoningParam = reasoningParamsFor(providerId, model, tier);
|
|
717
|
+
const responseBudget = Math.max(config.get('maxTokens'), 16384, minResponseTokensFor(model, tier));
|
|
706
718
|
if (protocol === 'openai') {
|
|
707
|
-
const maxTok = getEffectiveMaxTokens(providerId,
|
|
719
|
+
const maxTok = getEffectiveMaxTokens(providerId, responseBudget);
|
|
708
720
|
const tokParam = usesMaxCompletionTokens(providerId) ? { max_completion_tokens: maxTok } : { max_tokens: maxTok };
|
|
709
721
|
endpoint = `${baseUrl}/chat/completions`;
|
|
710
722
|
body = {
|
|
@@ -727,7 +739,7 @@ export async function agentChatFallback(messages, systemPrompt, onChunk, abortSi
|
|
|
727
739
|
...messages,
|
|
728
740
|
],
|
|
729
741
|
stream: Boolean(onChunk), ...tempParam, ...reasoningParam,
|
|
730
|
-
max_tokens: getEffectiveMaxTokens(providerId,
|
|
742
|
+
max_tokens: getEffectiveMaxTokens(providerId, responseBudget),
|
|
731
743
|
};
|
|
732
744
|
}
|
|
733
745
|
const response = await fetch(endpoint, {
|
|
@@ -39,7 +39,7 @@ import { basename, join } from 'path';
|
|
|
39
39
|
import { homedir } from 'os';
|
|
40
40
|
import { leadsOutsideProject } from './projectPaths.js';
|
|
41
41
|
import { config } from '../config/index.js';
|
|
42
|
-
import { getProvider } from '../config/providers.js';
|
|
42
|
+
import { getProvider, replacementModelFor } from '../config/providers.js';
|
|
43
43
|
const CAPABILITIES = new Set([
|
|
44
44
|
'files', 'terminal', 'tests', 'git', 'web', 'mcp',
|
|
45
45
|
]);
|
|
@@ -126,7 +126,13 @@ function exactModelPreference(preference) {
|
|
|
126
126
|
return null;
|
|
127
127
|
const providerId = value.slice(0, slash).trim();
|
|
128
128
|
const model = value.slice(slash + 1).trim();
|
|
129
|
-
|
|
129
|
+
if (!providerId || !model)
|
|
130
|
+
return null;
|
|
131
|
+
// A bot written before a vendor retired its model (or before Codeep stopped
|
|
132
|
+
// offering one, like `openai/gpt-6-astra`) names an id the picker no longer
|
|
133
|
+
// has, and the exact check below would make the whole bot unavailable. Map it
|
|
134
|
+
// the way the startup migration and applyProfile map a stored id.
|
|
135
|
+
return { providerId, model: replacementModelFor(providerId, model) ?? model };
|
|
130
136
|
}
|
|
131
137
|
/** Whether a structured bot's model field satisfies the portable v1 contract. */
|
|
132
138
|
export function isPersonalityModelPreferenceValid(personality) {
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Task Planning - breaks down complex tasks into subtasks
|
|
3
3
|
*/
|
|
4
4
|
import { config, getApiKey, resolveBaseUrl } from '../config/index.js';
|
|
5
|
-
import { getProviderAuthHeader, isNoApiKeyProvider, requiresDefaultTemperature } from '../config/providers.js';
|
|
5
|
+
import { getProviderAuthHeader, isNoApiKeyProvider, requiresDefaultTemperature, modelRejectsSamplingParams, minResponseTokensFor } from '../config/providers.js';
|
|
6
6
|
/**
|
|
7
7
|
* Ask AI to break down a complex task into subtasks
|
|
8
8
|
*/
|
|
@@ -51,10 +51,14 @@ Break this down into subtasks. Each task = one file or one logical unit. Respond
|
|
|
51
51
|
const messages = [
|
|
52
52
|
{ role: 'user', content: systemPrompt }
|
|
53
53
|
];
|
|
54
|
+
// 2048 is plenty for a JSON plan, but not for a model that thinks on every
|
|
55
|
+
// request inside the same limit (Opus 5.5 — see minResponseTokensFor). The
|
|
56
|
+
// planner sends no effort, so the model runs at its own default: 'auto'.
|
|
57
|
+
const maxTokens = Math.max(2048, minResponseTokensFor(model, 'auto'));
|
|
54
58
|
const requestBody = protocol === 'anthropic'
|
|
55
59
|
? {
|
|
56
60
|
model,
|
|
57
|
-
max_tokens:
|
|
61
|
+
max_tokens: maxTokens,
|
|
58
62
|
messages,
|
|
59
63
|
system: 'You are a task planning assistant. Respond with JSON only.',
|
|
60
64
|
}
|
|
@@ -64,8 +68,12 @@ Break this down into subtasks. Each task = one file or one logical unit. Respond
|
|
|
64
68
|
{ role: 'system', content: 'You are a task planning assistant. Respond with JSON only.' },
|
|
65
69
|
...messages
|
|
66
70
|
],
|
|
67
|
-
|
|
68
|
-
|
|
71
|
+
// Both guards, as in agentChat: the provider flag covers direct
|
|
72
|
+
// OpenAI, and the model list covers ids that reach this branch through
|
|
73
|
+
// OpenRouter (anthropic/claude-opus-5.5, openai/gpt-6-*), which 400 on
|
|
74
|
+
// a temperature they do not accept.
|
|
75
|
+
...(requiresDefaultTemperature(provider) || modelRejectsSamplingParams(model) ? {} : { temperature: 0.3 }),
|
|
76
|
+
max_tokens: maxTokens,
|
|
69
77
|
};
|
|
70
78
|
const headers = {
|
|
71
79
|
'Content-Type': 'application/json',
|
|
@@ -5,9 +5,10 @@ export interface TokenUsage {
|
|
|
5
5
|
promptTokens: number;
|
|
6
6
|
completionTokens: number;
|
|
7
7
|
totalTokens: number;
|
|
8
|
-
/**
|
|
9
|
-
*
|
|
10
|
-
*
|
|
8
|
+
/** Prompt caching: tokens written to the cache on this call — Anthropic's
|
|
9
|
+
* cache_creation_input_tokens, or OpenAI-protocol
|
|
10
|
+
* prompt_tokens_details.cache_write_tokens (GPT-5.6+, Kimi K3). Billed at
|
|
11
|
+
* cacheWriteRateFor(). Undefined when the provider reports none. */
|
|
11
12
|
cacheCreationTokens?: number;
|
|
12
13
|
/** Anthropic prompt caching: tokens read from cache on this call
|
|
13
14
|
* (billed at ~0.1× input rate — the big savings live here). */
|
|
@@ -85,8 +86,8 @@ export interface ProviderCostBreakdown {
|
|
|
85
86
|
model: string;
|
|
86
87
|
promptTokens: number;
|
|
87
88
|
completionTokens: number;
|
|
88
|
-
/**
|
|
89
|
-
* 0 for providers that don't report
|
|
89
|
+
/** Prompt caching: tokens written to cache (billed at cacheWriteRateFor()).
|
|
90
|
+
* 0 for providers that don't report writes. */
|
|
90
91
|
cacheCreationTokens: number;
|
|
91
92
|
/** Anthropic prompt caching: tokens read from cache (billed ~0.1× input).
|
|
92
93
|
* 0 for providers that don't report caching. */
|
|
@@ -102,6 +103,11 @@ export interface ProviderCostBreakdown {
|
|
|
102
103
|
* cost and a "saved" figure that disagreed with each other.
|
|
103
104
|
*/
|
|
104
105
|
export declare function cacheReadRateFor(model: string, provider: string | undefined): number;
|
|
106
|
+
/**
|
|
107
|
+
* What writing a prompt token into the cache costs, as a multiple of the
|
|
108
|
+
* model's input rate. Same order as reads: model, then provider, then default.
|
|
109
|
+
*/
|
|
110
|
+
export declare function cacheWriteRateFor(model: string, provider: string | undefined): number;
|
|
105
111
|
/**
|
|
106
112
|
* The rate note for a report. One rate reads as "0.02×"; a session mixing
|
|
107
113
|
* providers reads as a range, because any single number there would be wrong
|
|
@@ -138,6 +144,8 @@ export interface CacheStats {
|
|
|
138
144
|
/** The read rate of each metered record that read from cache, so a report
|
|
139
145
|
* can state the rate that actually applied instead of assuming 0.1×. */
|
|
140
146
|
cacheReadRates: number[];
|
|
147
|
+
/** Likewise for writes, which are not 1.25× everywhere (Kimi: 1.0×). */
|
|
148
|
+
cacheWriteRates: number[];
|
|
141
149
|
}
|
|
142
150
|
export declare function getCacheStats(): CacheStats;
|
|
143
151
|
/**
|