@shareai-lab/kode-sdk 2.7.1 → 2.7.2
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/core/agent/breakpoint-manager.js +36 -0
- package/dist/core/agent/message-queue.js +57 -0
- package/dist/core/agent/permission-manager.js +32 -0
- package/dist/core/agent/todo-manager.js +91 -0
- package/dist/core/agent/tool-runner.js +45 -0
- package/dist/core/agent.js +2035 -0
- package/dist/core/config.js +2 -0
- package/dist/core/context-manager.js +241 -0
- package/dist/core/errors.js +49 -0
- package/dist/core/events.js +329 -0
- package/dist/core/file-pool.d.ts +2 -0
- package/dist/core/file-pool.js +125 -0
- package/dist/core/hooks.js +71 -0
- package/dist/core/permission-modes.js +61 -0
- package/dist/core/pool.js +301 -0
- package/dist/core/room.js +57 -0
- package/dist/core/scheduler.js +58 -0
- package/dist/core/skills/index.js +20 -0
- package/dist/core/skills/management-manager.js +557 -0
- package/dist/core/skills/manager.js +243 -0
- package/dist/core/skills/operation-queue.js +113 -0
- package/dist/core/skills/sandbox-file-manager.js +183 -0
- package/dist/core/skills/types.js +9 -0
- package/dist/core/skills/xml-generator.js +70 -0
- package/dist/core/template.js +35 -0
- package/dist/core/time-bridge.js +100 -0
- package/dist/core/todo.js +89 -0
- package/dist/core/types.js +3 -0
- package/dist/index.js +148 -60461
- package/dist/infra/db/postgres/postgres-store.js +1073 -0
- package/dist/infra/db/sqlite/sqlite-store.js +800 -0
- package/dist/infra/e2b/e2b-fs.js +128 -0
- package/dist/infra/e2b/e2b-sandbox.js +156 -0
- package/dist/infra/e2b/e2b-template.js +105 -0
- package/dist/infra/e2b/index.js +9 -0
- package/dist/infra/e2b/types.js +2 -0
- package/dist/infra/provider.js +67 -0
- package/dist/infra/providers/anthropic.js +308 -0
- package/dist/infra/providers/core/errors.js +353 -0
- package/dist/infra/providers/core/fork.js +418 -0
- package/dist/infra/providers/core/index.js +76 -0
- package/dist/infra/providers/core/logger.js +191 -0
- package/dist/infra/providers/core/retry.js +189 -0
- package/dist/infra/providers/core/usage.js +376 -0
- package/dist/infra/providers/gemini.js +493 -0
- package/dist/infra/providers/index.js +83 -0
- package/dist/infra/providers/openai.js +662 -0
- package/dist/infra/providers/types.js +20 -0
- package/dist/infra/providers/utils.js +400 -0
- package/dist/infra/sandbox-factory.js +30 -0
- package/dist/infra/sandbox.js +243 -0
- package/dist/infra/store/factory.js +80 -0
- package/dist/infra/store/index.js +26 -0
- package/dist/infra/store/json-store.js +606 -0
- package/dist/infra/store/types.js +2 -0
- package/dist/infra/store.js +29 -0
- package/dist/tools/bash_kill/index.js +35 -0
- package/dist/tools/bash_kill/prompt.js +14 -0
- package/dist/tools/bash_logs/index.js +40 -0
- package/dist/tools/bash_logs/prompt.js +14 -0
- package/dist/tools/bash_run/index.js +61 -0
- package/dist/tools/bash_run/prompt.js +18 -0
- package/dist/tools/builtin.js +26 -0
- package/dist/tools/define.js +214 -0
- package/dist/tools/fs_edit/index.js +62 -0
- package/dist/tools/fs_edit/prompt.js +15 -0
- package/dist/tools/fs_glob/index.js +40 -0
- package/dist/tools/fs_glob/prompt.js +15 -0
- package/dist/tools/fs_grep/index.js +66 -0
- package/dist/tools/fs_grep/prompt.js +16 -0
- package/dist/tools/fs_multi_edit/index.js +106 -0
- package/dist/tools/fs_multi_edit/prompt.js +16 -0
- package/dist/tools/fs_read/index.js +40 -0
- package/dist/tools/fs_read/prompt.js +16 -0
- package/dist/tools/fs_write/index.js +40 -0
- package/dist/tools/fs_write/prompt.js +15 -0
- package/dist/tools/index.js +61 -0
- package/dist/tools/mcp.js +185 -0
- package/dist/tools/registry.js +26 -0
- package/dist/tools/scripts.js +205 -0
- package/dist/tools/skills.js +115 -0
- package/dist/tools/task_run/index.js +58 -0
- package/dist/tools/task_run/prompt.js +25 -0
- package/dist/tools/todo_read/index.js +29 -0
- package/dist/tools/todo_read/prompt.js +18 -0
- package/dist/tools/todo_write/index.js +42 -0
- package/dist/tools/todo_write/prompt.js +23 -0
- package/dist/tools/tool.js +211 -0
- package/dist/tools/toolkit.js +98 -0
- package/dist/tools/type-inference.js +207 -0
- package/dist/utils/agent-id.js +28 -0
- package/dist/utils/logger.js +44 -0
- package/dist/utils/session-id.js +64 -0
- package/package.json +7 -38
- package/dist/index.js.map +0 -7
- package/dist/index.mjs +0 -60385
- package/dist/index.mjs.map +0 -7
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Retry Strategy Module
|
|
4
|
+
*
|
|
5
|
+
* Exponential backoff with jitter for handling transient failures.
|
|
6
|
+
* Respects provider-specific retry-after headers.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.AGGRESSIVE_RETRY_CONFIG = exports.DEFAULT_RETRY_CONFIG = void 0;
|
|
10
|
+
exports.withRetry = withRetry;
|
|
11
|
+
exports.createRetryWrapper = createRetryWrapper;
|
|
12
|
+
exports.withRetryAndTimeout = withRetryAndTimeout;
|
|
13
|
+
exports.shouldRetry = shouldRetry;
|
|
14
|
+
exports.getRetryDelay = getRetryDelay;
|
|
15
|
+
const errors_1 = require("./errors");
|
|
16
|
+
/**
|
|
17
|
+
* Default retry configuration.
|
|
18
|
+
* Suitable for most provider API calls.
|
|
19
|
+
*/
|
|
20
|
+
exports.DEFAULT_RETRY_CONFIG = {
|
|
21
|
+
maxRetries: 3,
|
|
22
|
+
baseDelayMs: 1000,
|
|
23
|
+
maxDelayMs: 60000,
|
|
24
|
+
jitterFactor: 0.2,
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Aggressive retry configuration for critical operations.
|
|
28
|
+
*/
|
|
29
|
+
exports.AGGRESSIVE_RETRY_CONFIG = {
|
|
30
|
+
maxRetries: 5,
|
|
31
|
+
baseDelayMs: 500,
|
|
32
|
+
maxDelayMs: 120000,
|
|
33
|
+
jitterFactor: 0.3,
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Execute a function with retry logic.
|
|
37
|
+
*
|
|
38
|
+
* @param fn - Async function to execute
|
|
39
|
+
* @param config - Retry configuration
|
|
40
|
+
* @param onRetry - Optional callback before each retry
|
|
41
|
+
* @returns Result of the function
|
|
42
|
+
* @throws Last error if all retries exhausted
|
|
43
|
+
*/
|
|
44
|
+
async function withRetry(fn, config = exports.DEFAULT_RETRY_CONFIG, onRetry) {
|
|
45
|
+
let lastError;
|
|
46
|
+
for (let attempt = 0; attempt <= config.maxRetries; attempt++) {
|
|
47
|
+
try {
|
|
48
|
+
return await fn();
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
// Convert to ProviderError if needed
|
|
52
|
+
const providerError = error instanceof errors_1.ProviderError
|
|
53
|
+
? error
|
|
54
|
+
: (0, errors_1.parseProviderError)(error, config.provider || 'unknown');
|
|
55
|
+
lastError = providerError;
|
|
56
|
+
// Don't retry non-retryable errors
|
|
57
|
+
if (!providerError.retryable) {
|
|
58
|
+
throw providerError;
|
|
59
|
+
}
|
|
60
|
+
// Don't retry if we've exhausted attempts
|
|
61
|
+
if (attempt === config.maxRetries) {
|
|
62
|
+
throw providerError;
|
|
63
|
+
}
|
|
64
|
+
// Calculate delay with exponential backoff
|
|
65
|
+
let delay = calculateBackoffDelay(attempt, config);
|
|
66
|
+
// Respect retry-after header if available
|
|
67
|
+
if (providerError instanceof errors_1.RateLimitError && providerError.retryAfter) {
|
|
68
|
+
delay = Math.max(delay, providerError.retryAfter * 1000);
|
|
69
|
+
}
|
|
70
|
+
else if (providerError instanceof errors_1.ServiceUnavailableError && providerError.retryAfter) {
|
|
71
|
+
delay = Math.max(delay, providerError.retryAfter * 1000);
|
|
72
|
+
}
|
|
73
|
+
// Apply jitter
|
|
74
|
+
delay = applyJitter(delay, config.jitterFactor);
|
|
75
|
+
// Invoke callback
|
|
76
|
+
onRetry?.(providerError, attempt + 1, delay);
|
|
77
|
+
// Wait before retry
|
|
78
|
+
await sleep(delay);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
// Should not reach here, but TypeScript needs this
|
|
82
|
+
throw lastError || new Error('Unexpected retry loop exit');
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Calculate exponential backoff delay.
|
|
86
|
+
*/
|
|
87
|
+
function calculateBackoffDelay(attempt, config) {
|
|
88
|
+
const delay = config.baseDelayMs * Math.pow(2, attempt);
|
|
89
|
+
return Math.min(delay, config.maxDelayMs);
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Apply jitter to a delay value.
|
|
93
|
+
*/
|
|
94
|
+
function applyJitter(delay, jitterFactor) {
|
|
95
|
+
const jitter = delay * jitterFactor * (Math.random() - 0.5) * 2;
|
|
96
|
+
return Math.max(0, Math.floor(delay + jitter));
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Sleep for a specified duration.
|
|
100
|
+
*/
|
|
101
|
+
function sleep(ms) {
|
|
102
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Create a retry wrapper for a provider.
|
|
106
|
+
*
|
|
107
|
+
* @param provider - Provider name for error context
|
|
108
|
+
* @param config - Retry configuration
|
|
109
|
+
* @returns Configured retry function
|
|
110
|
+
*/
|
|
111
|
+
function createRetryWrapper(provider, config = {}) {
|
|
112
|
+
const mergedConfig = {
|
|
113
|
+
...exports.DEFAULT_RETRY_CONFIG,
|
|
114
|
+
...config,
|
|
115
|
+
provider,
|
|
116
|
+
};
|
|
117
|
+
return (fn, onRetry) => withRetry(fn, mergedConfig, onRetry);
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Retry with timeout.
|
|
121
|
+
* Aborts if total time exceeds timeout even if retries remain.
|
|
122
|
+
*/
|
|
123
|
+
async function withRetryAndTimeout(fn, timeoutMs, config = exports.DEFAULT_RETRY_CONFIG, onRetry) {
|
|
124
|
+
const startTime = Date.now();
|
|
125
|
+
let lastError;
|
|
126
|
+
for (let attempt = 0; attempt <= config.maxRetries; attempt++) {
|
|
127
|
+
// Check if we've exceeded timeout
|
|
128
|
+
if (Date.now() - startTime > timeoutMs) {
|
|
129
|
+
throw lastError || new Error(`Operation timed out after ${timeoutMs}ms`);
|
|
130
|
+
}
|
|
131
|
+
try {
|
|
132
|
+
// Create a timeout promise
|
|
133
|
+
const remainingTime = timeoutMs - (Date.now() - startTime);
|
|
134
|
+
return await Promise.race([
|
|
135
|
+
fn(),
|
|
136
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error('Timeout')), remainingTime)),
|
|
137
|
+
]);
|
|
138
|
+
}
|
|
139
|
+
catch (error) {
|
|
140
|
+
const providerError = error instanceof errors_1.ProviderError
|
|
141
|
+
? error
|
|
142
|
+
: (0, errors_1.parseProviderError)(error, config.provider || 'unknown');
|
|
143
|
+
lastError = providerError;
|
|
144
|
+
if (!providerError.retryable || attempt === config.maxRetries) {
|
|
145
|
+
throw providerError;
|
|
146
|
+
}
|
|
147
|
+
let delay = calculateBackoffDelay(attempt, config);
|
|
148
|
+
if (providerError instanceof errors_1.RateLimitError && providerError.retryAfter) {
|
|
149
|
+
delay = Math.max(delay, providerError.retryAfter * 1000);
|
|
150
|
+
}
|
|
151
|
+
delay = applyJitter(delay, config.jitterFactor);
|
|
152
|
+
// Don't wait longer than remaining timeout
|
|
153
|
+
const remainingTime = timeoutMs - (Date.now() - startTime);
|
|
154
|
+
if (delay > remainingTime) {
|
|
155
|
+
throw lastError;
|
|
156
|
+
}
|
|
157
|
+
onRetry?.(providerError, attempt + 1, delay);
|
|
158
|
+
await sleep(delay);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
throw lastError;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Check if an operation should be retried.
|
|
165
|
+
* Useful for manual retry logic.
|
|
166
|
+
*/
|
|
167
|
+
function shouldRetry(error, attempt, maxRetries) {
|
|
168
|
+
if (attempt >= maxRetries) {
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
if (error instanceof errors_1.ProviderError) {
|
|
172
|
+
return error.retryable;
|
|
173
|
+
}
|
|
174
|
+
// For unknown errors, be conservative
|
|
175
|
+
return false;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Get recommended delay for next retry.
|
|
179
|
+
*/
|
|
180
|
+
function getRetryDelay(error, attempt, config = exports.DEFAULT_RETRY_CONFIG) {
|
|
181
|
+
let delay = calculateBackoffDelay(attempt, config);
|
|
182
|
+
if (error instanceof errors_1.RateLimitError && error.retryAfter) {
|
|
183
|
+
delay = Math.max(delay, error.retryAfter * 1000);
|
|
184
|
+
}
|
|
185
|
+
else if (error instanceof errors_1.ServiceUnavailableError && error.retryAfter) {
|
|
186
|
+
delay = Math.max(delay, error.retryAfter * 1000);
|
|
187
|
+
}
|
|
188
|
+
return applyJitter(delay, config.jitterFactor);
|
|
189
|
+
}
|
|
@@ -0,0 +1,376 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Usage Statistics Module
|
|
4
|
+
*
|
|
5
|
+
* Unified usage tracking, cache metrics, and cost calculation
|
|
6
|
+
* across all supported model providers.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.PROVIDER_PRICING = void 0;
|
|
10
|
+
exports.createEmptyUsage = createEmptyUsage;
|
|
11
|
+
exports.calculateCost = calculateCost;
|
|
12
|
+
exports.normalizeAnthropicUsage = normalizeAnthropicUsage;
|
|
13
|
+
exports.normalizeOpenAIUsage = normalizeOpenAIUsage;
|
|
14
|
+
exports.normalizeGeminiUsage = normalizeGeminiUsage;
|
|
15
|
+
exports.normalizeDeepSeekUsage = normalizeDeepSeekUsage;
|
|
16
|
+
exports.aggregateUsage = aggregateUsage;
|
|
17
|
+
exports.formatUsageString = formatUsageString;
|
|
18
|
+
/**
|
|
19
|
+
* Provider pricing table (per 1M tokens).
|
|
20
|
+
*/
|
|
21
|
+
exports.PROVIDER_PRICING = {
|
|
22
|
+
anthropic: {
|
|
23
|
+
'claude-opus-4-5': {
|
|
24
|
+
input: 5.0,
|
|
25
|
+
output: 25.0,
|
|
26
|
+
cacheWrite: 6.25, // 5m TTL: 1.25x input
|
|
27
|
+
cacheRead: 0.5, // 10% of input
|
|
28
|
+
},
|
|
29
|
+
'claude-opus-4-5-1h': {
|
|
30
|
+
input: 5.0,
|
|
31
|
+
output: 25.0,
|
|
32
|
+
cacheWrite: 10.0, // 1h TTL: 2x input
|
|
33
|
+
cacheRead: 0.5,
|
|
34
|
+
},
|
|
35
|
+
'claude-sonnet-4-5': {
|
|
36
|
+
input: 3.0,
|
|
37
|
+
output: 15.0,
|
|
38
|
+
cacheWrite: 3.75,
|
|
39
|
+
cacheRead: 0.3,
|
|
40
|
+
},
|
|
41
|
+
'claude-haiku-4-5': {
|
|
42
|
+
input: 1.0,
|
|
43
|
+
output: 5.0,
|
|
44
|
+
cacheWrite: 1.25,
|
|
45
|
+
cacheRead: 0.1,
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
openai: {
|
|
49
|
+
'gpt-5.2': {
|
|
50
|
+
input: 5.0,
|
|
51
|
+
output: 15.0,
|
|
52
|
+
cacheRead: 1.25, // 75% discount
|
|
53
|
+
},
|
|
54
|
+
'gpt-4.1': {
|
|
55
|
+
input: 2.0,
|
|
56
|
+
output: 8.0,
|
|
57
|
+
cacheRead: 0.5,
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
gemini: {
|
|
61
|
+
'gemini-3-pro': {
|
|
62
|
+
input: 2.5,
|
|
63
|
+
output: 10.0,
|
|
64
|
+
cacheRead: 0.625, // 75% discount
|
|
65
|
+
},
|
|
66
|
+
'gemini-3-flash': {
|
|
67
|
+
input: 0.075,
|
|
68
|
+
output: 0.3,
|
|
69
|
+
cacheRead: 0.01875,
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
deepseek: {
|
|
73
|
+
'deepseek-reasoner': {
|
|
74
|
+
input: 0.28,
|
|
75
|
+
output: 1.10,
|
|
76
|
+
cacheRead: 0.028, // 90% discount
|
|
77
|
+
},
|
|
78
|
+
'deepseek-chat': {
|
|
79
|
+
input: 0.14,
|
|
80
|
+
output: 0.28,
|
|
81
|
+
cacheRead: 0.014,
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
qwen: {
|
|
85
|
+
'qwen3-max': {
|
|
86
|
+
input: 0.80,
|
|
87
|
+
output: 2.00,
|
|
88
|
+
},
|
|
89
|
+
'qwen3-plus': {
|
|
90
|
+
input: 0.50,
|
|
91
|
+
output: 1.50,
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
/**
|
|
96
|
+
* Create empty usage statistics.
|
|
97
|
+
*/
|
|
98
|
+
function createEmptyUsage() {
|
|
99
|
+
return {
|
|
100
|
+
inputTokens: 0,
|
|
101
|
+
outputTokens: 0,
|
|
102
|
+
totalTokens: 0,
|
|
103
|
+
cache: {
|
|
104
|
+
cacheCreationTokens: 0,
|
|
105
|
+
cacheReadTokens: 0,
|
|
106
|
+
provider: {},
|
|
107
|
+
},
|
|
108
|
+
cost: {
|
|
109
|
+
inputCost: 0,
|
|
110
|
+
outputCost: 0,
|
|
111
|
+
cacheWriteCost: 0,
|
|
112
|
+
totalCost: 0,
|
|
113
|
+
cacheSavings: 0,
|
|
114
|
+
currency: 'USD',
|
|
115
|
+
},
|
|
116
|
+
request: {
|
|
117
|
+
startTime: 0,
|
|
118
|
+
endTime: 0,
|
|
119
|
+
latencyMs: 0,
|
|
120
|
+
modelUsed: '',
|
|
121
|
+
},
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Calculate cost based on usage and pricing.
|
|
126
|
+
*/
|
|
127
|
+
function calculateCost(usage, pricing, cacheTtl) {
|
|
128
|
+
const perMillionFactor = 1000000;
|
|
129
|
+
// Calculate raw input cost (before cache)
|
|
130
|
+
const rawInputCost = (usage.inputTokens / perMillionFactor) * pricing.input;
|
|
131
|
+
// Calculate cache costs
|
|
132
|
+
const cacheReadCost = pricing.cacheRead
|
|
133
|
+
? ((usage.cacheReadTokens || 0) / perMillionFactor) * pricing.cacheRead
|
|
134
|
+
: 0;
|
|
135
|
+
let cacheWriteCost = 0;
|
|
136
|
+
if (usage.cacheCreationTokens && pricing.cacheWrite) {
|
|
137
|
+
const multiplier = cacheTtl === '1h' ? 2.0 : 1.25;
|
|
138
|
+
cacheWriteCost = ((usage.cacheCreationTokens) / perMillionFactor) * pricing.input * multiplier;
|
|
139
|
+
}
|
|
140
|
+
// Actual input cost = raw - cached tokens + cache read cost
|
|
141
|
+
const cachedInputTokens = usage.cacheReadTokens || 0;
|
|
142
|
+
const nonCachedInputTokens = Math.max(0, usage.inputTokens - cachedInputTokens);
|
|
143
|
+
const inputCost = (nonCachedInputTokens / perMillionFactor) * pricing.input + cacheReadCost;
|
|
144
|
+
// Output cost
|
|
145
|
+
const outputCost = (usage.outputTokens / perMillionFactor) * pricing.output;
|
|
146
|
+
// Reasoning cost (if separate pricing)
|
|
147
|
+
const reasoningCost = pricing.reasoning && usage.reasoningTokens
|
|
148
|
+
? (usage.reasoningTokens / perMillionFactor) * pricing.reasoning
|
|
149
|
+
: 0;
|
|
150
|
+
// Total cost
|
|
151
|
+
const totalCost = inputCost + outputCost + cacheWriteCost + reasoningCost;
|
|
152
|
+
// Cache savings = what we would have paid - what we actually paid
|
|
153
|
+
const cacheSavings = cachedInputTokens > 0
|
|
154
|
+
? (cachedInputTokens / perMillionFactor) * pricing.input - cacheReadCost
|
|
155
|
+
: 0;
|
|
156
|
+
return {
|
|
157
|
+
inputCost: Math.round(inputCost * 100000) / 100000, // 5 decimal precision
|
|
158
|
+
outputCost: Math.round((outputCost + reasoningCost) * 100000) / 100000,
|
|
159
|
+
cacheWriteCost: Math.round(cacheWriteCost * 100000) / 100000,
|
|
160
|
+
totalCost: Math.round(totalCost * 100000) / 100000,
|
|
161
|
+
cacheSavings: Math.round(cacheSavings * 100000) / 100000,
|
|
162
|
+
currency: 'USD',
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Normalize Anthropic usage to unified format.
|
|
167
|
+
*/
|
|
168
|
+
function normalizeAnthropicUsage(raw, model, startTime, requestId, cacheTtl) {
|
|
169
|
+
const inputTokens = raw.input_tokens || 0;
|
|
170
|
+
const outputTokens = raw.output_tokens || 0;
|
|
171
|
+
const cacheCreationTokens = raw.cache_creation_input_tokens || 0;
|
|
172
|
+
const cacheReadTokens = raw.cache_read_input_tokens || 0;
|
|
173
|
+
// Determine model key for pricing
|
|
174
|
+
const modelKey = model.includes('opus') ? 'claude-opus-4-5'
|
|
175
|
+
: model.includes('sonnet') ? 'claude-sonnet-4-5'
|
|
176
|
+
: 'claude-haiku-4-5';
|
|
177
|
+
const pricing = cacheTtl === '1h'
|
|
178
|
+
? exports.PROVIDER_PRICING.anthropic[`${modelKey}-1h`] || exports.PROVIDER_PRICING.anthropic[modelKey]
|
|
179
|
+
: exports.PROVIDER_PRICING.anthropic[modelKey];
|
|
180
|
+
const cost = pricing
|
|
181
|
+
? calculateCost({ inputTokens, outputTokens, cacheCreationTokens, cacheReadTokens }, pricing, cacheTtl)
|
|
182
|
+
: createEmptyUsage().cost;
|
|
183
|
+
const endTime = Date.now();
|
|
184
|
+
return {
|
|
185
|
+
inputTokens,
|
|
186
|
+
outputTokens,
|
|
187
|
+
totalTokens: inputTokens + outputTokens + cacheCreationTokens + cacheReadTokens,
|
|
188
|
+
cache: {
|
|
189
|
+
cacheCreationTokens,
|
|
190
|
+
cacheReadTokens,
|
|
191
|
+
cacheSavingsEstimate: cost.cacheSavings,
|
|
192
|
+
provider: {
|
|
193
|
+
anthropic: {
|
|
194
|
+
breakpointsUsed: 0, // Inferred from request
|
|
195
|
+
ttlUsed: cacheTtl || '5m',
|
|
196
|
+
},
|
|
197
|
+
},
|
|
198
|
+
},
|
|
199
|
+
cost,
|
|
200
|
+
request: {
|
|
201
|
+
startTime,
|
|
202
|
+
endTime,
|
|
203
|
+
latencyMs: endTime - startTime,
|
|
204
|
+
requestId,
|
|
205
|
+
modelUsed: model,
|
|
206
|
+
},
|
|
207
|
+
raw,
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Normalize OpenAI usage to unified format.
|
|
212
|
+
*/
|
|
213
|
+
function normalizeOpenAIUsage(raw, model, api, startTime, requestId) {
|
|
214
|
+
const inputTokens = raw.prompt_tokens || 0;
|
|
215
|
+
const outputTokens = raw.completion_tokens || 0;
|
|
216
|
+
const cacheReadTokens = raw.prompt_tokens_details?.cached_tokens || 0;
|
|
217
|
+
const reasoningTokens = raw.completion_tokens_details?.reasoning_tokens || 0;
|
|
218
|
+
const modelKey = model.includes('gpt-5') ? 'gpt-5.2' : 'gpt-4.1';
|
|
219
|
+
const pricing = exports.PROVIDER_PRICING.openai[modelKey];
|
|
220
|
+
const cost = pricing
|
|
221
|
+
? calculateCost({ inputTokens, outputTokens, cacheReadTokens }, pricing)
|
|
222
|
+
: createEmptyUsage().cost;
|
|
223
|
+
const endTime = Date.now();
|
|
224
|
+
return {
|
|
225
|
+
inputTokens,
|
|
226
|
+
outputTokens,
|
|
227
|
+
totalTokens: raw.total_tokens || (inputTokens + outputTokens),
|
|
228
|
+
reasoningTokens: reasoningTokens || undefined,
|
|
229
|
+
cache: {
|
|
230
|
+
cacheCreationTokens: 0,
|
|
231
|
+
cacheReadTokens,
|
|
232
|
+
cacheSavingsEstimate: cost.cacheSavings,
|
|
233
|
+
provider: {
|
|
234
|
+
openai: {
|
|
235
|
+
automaticCacheHit: cacheReadTokens > 0,
|
|
236
|
+
},
|
|
237
|
+
},
|
|
238
|
+
},
|
|
239
|
+
cost,
|
|
240
|
+
request: {
|
|
241
|
+
startTime,
|
|
242
|
+
endTime,
|
|
243
|
+
latencyMs: endTime - startTime,
|
|
244
|
+
requestId,
|
|
245
|
+
modelUsed: model,
|
|
246
|
+
},
|
|
247
|
+
raw,
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Normalize Gemini usage to unified format.
|
|
252
|
+
*/
|
|
253
|
+
function normalizeGeminiUsage(raw, model, startTime, cachedContentName) {
|
|
254
|
+
const inputTokens = raw.promptTokenCount || 0;
|
|
255
|
+
const outputTokens = raw.candidatesTokenCount || 0;
|
|
256
|
+
const cacheReadTokens = raw.cachedContentTokenCount || 0;
|
|
257
|
+
const reasoningTokens = raw.thoughtsTokenCount || 0;
|
|
258
|
+
const modelKey = model.includes('pro') ? 'gemini-3-pro' : 'gemini-3-flash';
|
|
259
|
+
const pricing = exports.PROVIDER_PRICING.gemini[modelKey];
|
|
260
|
+
const cost = pricing
|
|
261
|
+
? calculateCost({ inputTokens, outputTokens, cacheReadTokens }, pricing)
|
|
262
|
+
: createEmptyUsage().cost;
|
|
263
|
+
const endTime = Date.now();
|
|
264
|
+
return {
|
|
265
|
+
inputTokens,
|
|
266
|
+
outputTokens,
|
|
267
|
+
totalTokens: raw.totalTokenCount || (inputTokens + outputTokens),
|
|
268
|
+
reasoningTokens: reasoningTokens || undefined,
|
|
269
|
+
cache: {
|
|
270
|
+
cacheCreationTokens: 0,
|
|
271
|
+
cacheReadTokens,
|
|
272
|
+
cacheSavingsEstimate: cost.cacheSavings,
|
|
273
|
+
provider: {
|
|
274
|
+
gemini: {
|
|
275
|
+
cachedContentName,
|
|
276
|
+
implicitCacheHit: cacheReadTokens > 0 && !cachedContentName,
|
|
277
|
+
},
|
|
278
|
+
},
|
|
279
|
+
},
|
|
280
|
+
cost,
|
|
281
|
+
request: {
|
|
282
|
+
startTime,
|
|
283
|
+
endTime,
|
|
284
|
+
latencyMs: endTime - startTime,
|
|
285
|
+
modelUsed: model,
|
|
286
|
+
},
|
|
287
|
+
raw,
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Normalize DeepSeek usage to unified format.
|
|
292
|
+
*/
|
|
293
|
+
function normalizeDeepSeekUsage(raw, model, startTime, requestId) {
|
|
294
|
+
const inputTokens = raw.prompt_tokens || 0;
|
|
295
|
+
const outputTokens = raw.completion_tokens || 0;
|
|
296
|
+
const cacheReadTokens = raw.prompt_cache_hit_tokens || 0;
|
|
297
|
+
const modelKey = model.includes('reasoner') ? 'deepseek-reasoner' : 'deepseek-chat';
|
|
298
|
+
const pricing = exports.PROVIDER_PRICING.deepseek[modelKey];
|
|
299
|
+
const cost = pricing
|
|
300
|
+
? calculateCost({ inputTokens, outputTokens, cacheReadTokens }, pricing)
|
|
301
|
+
: createEmptyUsage().cost;
|
|
302
|
+
const endTime = Date.now();
|
|
303
|
+
return {
|
|
304
|
+
inputTokens,
|
|
305
|
+
outputTokens,
|
|
306
|
+
totalTokens: raw.total_tokens || (inputTokens + outputTokens),
|
|
307
|
+
cache: {
|
|
308
|
+
cacheCreationTokens: 0,
|
|
309
|
+
cacheReadTokens,
|
|
310
|
+
cacheSavingsEstimate: cost.cacheSavings,
|
|
311
|
+
provider: {
|
|
312
|
+
deepseek: {
|
|
313
|
+
prefixCacheHit: cacheReadTokens > 0,
|
|
314
|
+
},
|
|
315
|
+
},
|
|
316
|
+
},
|
|
317
|
+
cost,
|
|
318
|
+
request: {
|
|
319
|
+
startTime,
|
|
320
|
+
endTime,
|
|
321
|
+
latencyMs: endTime - startTime,
|
|
322
|
+
requestId,
|
|
323
|
+
modelUsed: model,
|
|
324
|
+
},
|
|
325
|
+
raw,
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* Aggregate multiple usage statistics.
|
|
330
|
+
*/
|
|
331
|
+
function aggregateUsage(usages) {
|
|
332
|
+
const aggregated = createEmptyUsage();
|
|
333
|
+
for (const usage of usages) {
|
|
334
|
+
aggregated.inputTokens += usage.inputTokens;
|
|
335
|
+
aggregated.outputTokens += usage.outputTokens;
|
|
336
|
+
aggregated.totalTokens += usage.totalTokens;
|
|
337
|
+
aggregated.reasoningTokens = (aggregated.reasoningTokens || 0) + (usage.reasoningTokens || 0);
|
|
338
|
+
aggregated.cache.cacheCreationTokens += usage.cache.cacheCreationTokens;
|
|
339
|
+
aggregated.cache.cacheReadTokens += usage.cache.cacheReadTokens;
|
|
340
|
+
aggregated.cache.cacheSavingsEstimate = (aggregated.cache.cacheSavingsEstimate || 0) +
|
|
341
|
+
(usage.cache.cacheSavingsEstimate || 0);
|
|
342
|
+
aggregated.cost.inputCost += usage.cost.inputCost;
|
|
343
|
+
aggregated.cost.outputCost += usage.cost.outputCost;
|
|
344
|
+
aggregated.cost.cacheWriteCost += usage.cost.cacheWriteCost;
|
|
345
|
+
aggregated.cost.totalCost += usage.cost.totalCost;
|
|
346
|
+
aggregated.cost.cacheSavings += usage.cost.cacheSavings;
|
|
347
|
+
}
|
|
348
|
+
// Average latency
|
|
349
|
+
if (usages.length > 0) {
|
|
350
|
+
aggregated.request.latencyMs = usages.reduce((sum, u) => sum + u.request.latencyMs, 0) / usages.length;
|
|
351
|
+
}
|
|
352
|
+
return aggregated;
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* Format usage as human-readable string.
|
|
356
|
+
*/
|
|
357
|
+
function formatUsageString(usage) {
|
|
358
|
+
const parts = [];
|
|
359
|
+
parts.push(`Tokens: ${usage.inputTokens} in / ${usage.outputTokens} out`);
|
|
360
|
+
if (usage.reasoningTokens) {
|
|
361
|
+
parts.push(`(${usage.reasoningTokens} reasoning)`);
|
|
362
|
+
}
|
|
363
|
+
if (usage.cache.cacheReadTokens > 0) {
|
|
364
|
+
parts.push(`Cache hit: ${usage.cache.cacheReadTokens} tokens`);
|
|
365
|
+
}
|
|
366
|
+
if (usage.cost.totalCost > 0) {
|
|
367
|
+
parts.push(`Cost: $${usage.cost.totalCost.toFixed(5)}`);
|
|
368
|
+
}
|
|
369
|
+
if (usage.cost.cacheSavings > 0) {
|
|
370
|
+
parts.push(`(saved: $${usage.cost.cacheSavings.toFixed(5)})`);
|
|
371
|
+
}
|
|
372
|
+
if (usage.request.latencyMs > 0) {
|
|
373
|
+
parts.push(`Latency: ${usage.request.latencyMs}ms`);
|
|
374
|
+
}
|
|
375
|
+
return parts.join(' | ');
|
|
376
|
+
}
|