@ddse/acm-aicoder 0.5.0 → 0.5.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/src/capability-map.d.ts +4 -0
- package/dist/src/capability-map.d.ts.map +1 -0
- package/dist/src/capability-map.js +289 -0
- package/dist/src/capability-map.js.map +1 -0
- package/dist/src/registries.d.ts +3 -0
- package/dist/src/registries.d.ts.map +1 -1
- package/dist/src/registries.js +11 -0
- package/dist/src/registries.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +21 -7
- package/.aicoder/index.json +0 -304
- package/AICODER_IMPLEMENTATION_PLAN_PHASE2.md +0 -284
- package/bin/interactive.tsx +0 -232
- package/docs/AICODER.png +0 -0
- package/docs/INTERACTIVE_CLI_GUIDE.md +0 -201
- package/docs/TUI_MOCKUP.md +0 -180
- package/src/config/providers.ts +0 -174
- package/src/config/session.ts +0 -143
- package/src/context/bm25.ts +0 -173
- package/src/context/code-search.ts +0 -188
- package/src/context/context-pack.ts +0 -133
- package/src/context/dependency-mapper.ts +0 -72
- package/src/context/index.ts +0 -8
- package/src/context/symbol-extractor.ts +0 -149
- package/src/context/test-mapper.ts +0 -77
- package/src/context/types.ts +0 -69
- package/src/context/workspace-indexer.ts +0 -249
- package/src/index.ts +0 -5
- package/src/registries.ts +0 -118
- package/src/runtime/budget-manager.ts +0 -118
- package/src/runtime/interactive-runtime.ts +0 -423
- package/src/tasks-v2/analysis-tasks.ts +0 -311
- package/src/tasks-v2/developer-tasks.ts +0 -437
- package/src/tasks-v2/index.ts +0 -3
- package/src/tools-v2/edit-tools.ts +0 -153
- package/src/tools-v2/index.ts +0 -6
- package/src/tools-v2/read-tools.ts +0 -286
- package/src/tools-v2/search-tools.ts +0 -175
- package/src/tools-v2/test-tools.ts +0 -147
- package/src/tools-v2/workspace-context.ts +0 -428
- package/src/ui/App.tsx +0 -392
- package/src/ui/components/ChatPane.tsx +0 -84
- package/src/ui/components/EventsPane.tsx +0 -81
- package/src/ui/components/GoalsTasksPane.tsx +0 -149
- package/src/ui/store.ts +0 -362
- package/tests/integration.test.ts +0 -537
- package/tsconfig.json +0 -22
|
@@ -1,249 +0,0 @@
|
|
|
1
|
-
// Workspace Indexer - Fast file scanning and caching
|
|
2
|
-
import * as fs from 'fs/promises';
|
|
3
|
-
import * as path from 'path';
|
|
4
|
-
import { createHash } from 'crypto';
|
|
5
|
-
import type { FileMetadata, WorkspaceIndex } from './types.js';
|
|
6
|
-
|
|
7
|
-
const DEFAULT_IGNORES = [
|
|
8
|
-
'node_modules',
|
|
9
|
-
'.git',
|
|
10
|
-
'dist',
|
|
11
|
-
'build',
|
|
12
|
-
'.next',
|
|
13
|
-
'.cache',
|
|
14
|
-
'coverage',
|
|
15
|
-
'.aicoder',
|
|
16
|
-
'*.lock',
|
|
17
|
-
'package-lock.json',
|
|
18
|
-
'pnpm-lock.yaml',
|
|
19
|
-
'yarn.lock',
|
|
20
|
-
];
|
|
21
|
-
|
|
22
|
-
const BINARY_EXTENSIONS = new Set([
|
|
23
|
-
'.png', '.jpg', '.jpeg', '.gif', '.svg', '.ico',
|
|
24
|
-
'.pdf', '.zip', '.tar', '.gz', '.bz2',
|
|
25
|
-
'.exe', '.dll', '.so', '.dylib',
|
|
26
|
-
'.woff', '.woff2', '.ttf', '.eot',
|
|
27
|
-
'.mp4', '.mp3', '.wav', '.avi',
|
|
28
|
-
]);
|
|
29
|
-
|
|
30
|
-
const LANGUAGE_MAP: Record<string, string> = {
|
|
31
|
-
'.ts': 'typescript',
|
|
32
|
-
'.tsx': 'typescript',
|
|
33
|
-
'.js': 'javascript',
|
|
34
|
-
'.jsx': 'javascript',
|
|
35
|
-
'.mjs': 'javascript',
|
|
36
|
-
'.cjs': 'javascript',
|
|
37
|
-
'.py': 'python',
|
|
38
|
-
'.go': 'go',
|
|
39
|
-
'.rs': 'rust',
|
|
40
|
-
'.java': 'java',
|
|
41
|
-
'.c': 'c',
|
|
42
|
-
'.cpp': 'cpp',
|
|
43
|
-
'.h': 'c',
|
|
44
|
-
'.hpp': 'cpp',
|
|
45
|
-
'.md': 'markdown',
|
|
46
|
-
'.json': 'json',
|
|
47
|
-
'.yaml': 'yaml',
|
|
48
|
-
'.yml': 'yaml',
|
|
49
|
-
'.toml': 'toml',
|
|
50
|
-
'.xml': 'xml',
|
|
51
|
-
'.html': 'html',
|
|
52
|
-
'.css': 'css',
|
|
53
|
-
'.scss': 'scss',
|
|
54
|
-
'.sql': 'sql',
|
|
55
|
-
'.sh': 'shell',
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
export class WorkspaceIndexer {
|
|
59
|
-
private rootPath: string;
|
|
60
|
-
private ignorePatterns: Set<string>;
|
|
61
|
-
private cacheDir: string;
|
|
62
|
-
|
|
63
|
-
constructor(rootPath: string, additionalIgnores: string[] = []) {
|
|
64
|
-
this.rootPath = path.resolve(rootPath);
|
|
65
|
-
this.ignorePatterns = new Set([...DEFAULT_IGNORES, ...additionalIgnores]);
|
|
66
|
-
this.cacheDir = path.join(this.rootPath, '.aicoder');
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Build or refresh the workspace index
|
|
71
|
-
*/
|
|
72
|
-
async buildIndex(options: { useCache?: boolean; maxFileSize?: number } = {}): Promise<WorkspaceIndex> {
|
|
73
|
-
const useCache = options.useCache ?? true;
|
|
74
|
-
const maxFileSize = options.maxFileSize ?? 1_000_000; // 1MB default
|
|
75
|
-
|
|
76
|
-
// Try to load from cache
|
|
77
|
-
if (useCache) {
|
|
78
|
-
const cached = await this.loadCache();
|
|
79
|
-
if (cached) {
|
|
80
|
-
return cached;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// Scan workspace
|
|
85
|
-
const files: FileMetadata[] = [];
|
|
86
|
-
await this.scanDirectory(this.rootPath, files, maxFileSize);
|
|
87
|
-
|
|
88
|
-
const index: WorkspaceIndex = {
|
|
89
|
-
files,
|
|
90
|
-
totalFiles: files.length,
|
|
91
|
-
totalSize: files.reduce((sum, f) => sum + f.size, 0),
|
|
92
|
-
indexedAt: new Date().toISOString(),
|
|
93
|
-
rootPath: this.rootPath,
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
// Save to cache
|
|
97
|
-
if (useCache) {
|
|
98
|
-
await this.saveCache(index);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
return index;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Recursively scan directory for files
|
|
106
|
-
*/
|
|
107
|
-
private async scanDirectory(
|
|
108
|
-
dirPath: string,
|
|
109
|
-
files: FileMetadata[],
|
|
110
|
-
maxFileSize: number
|
|
111
|
-
): Promise<void> {
|
|
112
|
-
try {
|
|
113
|
-
const entries = await fs.readdir(dirPath, { withFileTypes: true });
|
|
114
|
-
|
|
115
|
-
for (const entry of entries) {
|
|
116
|
-
const fullPath = path.join(dirPath, entry.name);
|
|
117
|
-
const relativePath = path.relative(this.rootPath, fullPath);
|
|
118
|
-
|
|
119
|
-
// Skip ignored patterns
|
|
120
|
-
if (this.shouldIgnore(relativePath, entry.name)) {
|
|
121
|
-
continue;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
if (entry.isDirectory()) {
|
|
125
|
-
await this.scanDirectory(fullPath, files, maxFileSize);
|
|
126
|
-
} else if (entry.isFile()) {
|
|
127
|
-
try {
|
|
128
|
-
const stats = await fs.stat(fullPath);
|
|
129
|
-
|
|
130
|
-
// Skip files that are too large
|
|
131
|
-
if (stats.size > maxFileSize) {
|
|
132
|
-
continue;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
const ext = path.extname(entry.name);
|
|
136
|
-
const isBinary = BINARY_EXTENSIONS.has(ext);
|
|
137
|
-
|
|
138
|
-
// Compute hash for non-binary files
|
|
139
|
-
let hash = '';
|
|
140
|
-
if (!isBinary && stats.size < 100_000) { // Hash only small text files
|
|
141
|
-
try {
|
|
142
|
-
const content = await fs.readFile(fullPath, 'utf-8');
|
|
143
|
-
hash = createHash('sha256').update(content).digest('hex').substring(0, 16);
|
|
144
|
-
} catch {
|
|
145
|
-
// Skip files that can't be read
|
|
146
|
-
continue;
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
files.push({
|
|
151
|
-
path: relativePath,
|
|
152
|
-
size: stats.size,
|
|
153
|
-
mtime: stats.mtimeMs,
|
|
154
|
-
hash,
|
|
155
|
-
language: LANGUAGE_MAP[ext] || 'unknown',
|
|
156
|
-
isBinary,
|
|
157
|
-
});
|
|
158
|
-
} catch (error) {
|
|
159
|
-
// Skip files we can't stat
|
|
160
|
-
continue;
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
} catch (error) {
|
|
165
|
-
// Skip directories we can't read
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
/**
|
|
170
|
-
* Check if path should be ignored
|
|
171
|
-
*/
|
|
172
|
-
private shouldIgnore(relativePath: string, name: string): boolean {
|
|
173
|
-
// Check exact matches
|
|
174
|
-
if (this.ignorePatterns.has(name)) {
|
|
175
|
-
return true;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
// Check if any part of the path matches ignore patterns
|
|
179
|
-
const parts = relativePath.split(path.sep);
|
|
180
|
-
for (const part of parts) {
|
|
181
|
-
if (this.ignorePatterns.has(part)) {
|
|
182
|
-
return true;
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
// Check wildcard patterns
|
|
187
|
-
for (const pattern of this.ignorePatterns) {
|
|
188
|
-
if (pattern.includes('*')) {
|
|
189
|
-
const regex = new RegExp('^' + pattern.replace(/\*/g, '.*') + '$');
|
|
190
|
-
if (regex.test(name)) {
|
|
191
|
-
return true;
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
return false;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
/**
|
|
200
|
-
* Load index from cache
|
|
201
|
-
*/
|
|
202
|
-
private async loadCache(): Promise<WorkspaceIndex | null> {
|
|
203
|
-
try {
|
|
204
|
-
const cachePath = path.join(this.cacheDir, 'index.json');
|
|
205
|
-
const content = await fs.readFile(cachePath, 'utf-8');
|
|
206
|
-
const index: WorkspaceIndex = JSON.parse(content);
|
|
207
|
-
|
|
208
|
-
// Validate cache is recent (within 1 hour)
|
|
209
|
-
const cacheAge = Date.now() - new Date(index.indexedAt).getTime();
|
|
210
|
-
if (cacheAge > 3600_000) {
|
|
211
|
-
return null;
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
return index;
|
|
215
|
-
} catch {
|
|
216
|
-
return null;
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
/**
|
|
221
|
-
* Save index to cache
|
|
222
|
-
*/
|
|
223
|
-
private async saveCache(index: WorkspaceIndex): Promise<void> {
|
|
224
|
-
try {
|
|
225
|
-
await fs.mkdir(this.cacheDir, { recursive: true });
|
|
226
|
-
const cachePath = path.join(this.cacheDir, 'index.json');
|
|
227
|
-
await fs.writeFile(cachePath, JSON.stringify(index, null, 2), 'utf-8');
|
|
228
|
-
} catch {
|
|
229
|
-
// Cache save is best-effort
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
/**
|
|
234
|
-
* Get files by language
|
|
235
|
-
*/
|
|
236
|
-
static filterByLanguage(index: WorkspaceIndex, languages: string[]): FileMetadata[] {
|
|
237
|
-
const langSet = new Set(languages);
|
|
238
|
-
return index.files.filter(f => langSet.has(f.language));
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
/**
|
|
242
|
-
* Get recently modified files
|
|
243
|
-
*/
|
|
244
|
-
static getRecentFiles(index: WorkspaceIndex, count: number = 10): FileMetadata[] {
|
|
245
|
-
return [...index.files]
|
|
246
|
-
.sort((a, b) => b.mtime - a.mtime)
|
|
247
|
-
.slice(0, count);
|
|
248
|
-
}
|
|
249
|
-
}
|
package/src/index.ts
DELETED
package/src/registries.ts
DELETED
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
// Registries and Policy for AI Coder
|
|
2
|
-
import {
|
|
3
|
-
CapabilityRegistry,
|
|
4
|
-
ToolRegistry,
|
|
5
|
-
PolicyEngine,
|
|
6
|
-
type Capability,
|
|
7
|
-
type Tool,
|
|
8
|
-
type Task,
|
|
9
|
-
type PolicyDecision,
|
|
10
|
-
} from '@ddse/acm-sdk';
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Simple Capability Registry
|
|
14
|
-
*/
|
|
15
|
-
export class SimpleCapabilityRegistry extends CapabilityRegistry {
|
|
16
|
-
private tasks = new Map<string, Task>();
|
|
17
|
-
private capabilities = new Map<string, Capability>();
|
|
18
|
-
|
|
19
|
-
register(capability: Capability, task: Task): void {
|
|
20
|
-
this.capabilities.set(capability.name, capability);
|
|
21
|
-
this.tasks.set(capability.name, task);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
list(): Capability[] {
|
|
25
|
-
return Array.from(this.capabilities.values());
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
has(name: string): boolean {
|
|
29
|
-
return this.capabilities.has(name);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
resolve(name: string): Task | undefined {
|
|
33
|
-
return this.tasks.get(name);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
inputSchema(name: string): unknown | undefined {
|
|
37
|
-
return this.capabilities.get(name)?.inputSchema;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
outputSchema(name: string): unknown | undefined {
|
|
41
|
-
return this.capabilities.get(name)?.outputSchema;
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Simple Tool Registry
|
|
47
|
-
*/
|
|
48
|
-
export class SimpleToolRegistry extends ToolRegistry {
|
|
49
|
-
private tools = new Map<string, Tool>();
|
|
50
|
-
|
|
51
|
-
register(tool: Tool): void {
|
|
52
|
-
this.tools.set(tool.name(), tool);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
get(name: string): Tool | undefined {
|
|
56
|
-
return this.tools.get(name);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
list(): string[] {
|
|
60
|
-
return Array.from(this.tools.keys());
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Simple Policy Engine with safety rules for code modifications
|
|
66
|
-
*/
|
|
67
|
-
export class SimplePolicyEngine implements PolicyEngine {
|
|
68
|
-
private allowedPaths: string[] = [];
|
|
69
|
-
private blockedActions: string[] = [];
|
|
70
|
-
|
|
71
|
-
setAllowedPaths(paths: string[]): void {
|
|
72
|
-
this.allowedPaths = paths;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
setBlockedActions(actions: string[]): void {
|
|
76
|
-
this.blockedActions = actions;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
async evaluate(
|
|
80
|
-
action: 'plan.admit' | 'task.pre' | 'task.post',
|
|
81
|
-
payload: any
|
|
82
|
-
): Promise<PolicyDecision> {
|
|
83
|
-
// Check if action is blocked
|
|
84
|
-
if (action === 'task.pre' && this.blockedActions.includes(payload?.action)) {
|
|
85
|
-
return {
|
|
86
|
-
allow: false,
|
|
87
|
-
reason: `Action ${payload?.action} is blocked by policy`,
|
|
88
|
-
};
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// For code editing operations, check path is allowed
|
|
92
|
-
if (action === 'task.pre' && (payload?.action === 'fix_bug' || payload?.action === 'implement_feature')) {
|
|
93
|
-
const targetPath = payload?.path || payload?.targetPath;
|
|
94
|
-
|
|
95
|
-
if (this.allowedPaths.length > 0 && targetPath && !this.allowedPaths.includes('*')) {
|
|
96
|
-
const isAllowed = this.allowedPaths.some(allowed =>
|
|
97
|
-
targetPath.includes(allowed)
|
|
98
|
-
);
|
|
99
|
-
|
|
100
|
-
if (!isAllowed) {
|
|
101
|
-
return {
|
|
102
|
-
allow: false,
|
|
103
|
-
reason: `Path ${targetPath} is not in allowed paths`,
|
|
104
|
-
};
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// Default: allow
|
|
110
|
-
return {
|
|
111
|
-
allow: true,
|
|
112
|
-
limits: {
|
|
113
|
-
timeoutMs: 30000,
|
|
114
|
-
retries: 3,
|
|
115
|
-
},
|
|
116
|
-
};
|
|
117
|
-
}
|
|
118
|
-
}
|
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
// Token budget manager
|
|
2
|
-
// Tracks cumulative token usage and ensures calls stay within provider limits
|
|
3
|
-
|
|
4
|
-
import {
|
|
5
|
-
ProviderMetadata,
|
|
6
|
-
getProviderMetadata,
|
|
7
|
-
estimateTokenCount,
|
|
8
|
-
} from '../config/providers.js';
|
|
9
|
-
|
|
10
|
-
export interface BudgetEstimate {
|
|
11
|
-
inputTokens: number;
|
|
12
|
-
outputTokens: number;
|
|
13
|
-
totalTokens: number;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export interface BudgetStatus {
|
|
17
|
-
totalInputTokens: number;
|
|
18
|
-
totalOutputTokens: number;
|
|
19
|
-
totalTokens: number;
|
|
20
|
-
maxTokens?: number;
|
|
21
|
-
remainingTokens?: number;
|
|
22
|
-
callCount: number;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export class BudgetManager {
|
|
26
|
-
private metadata: ProviderMetadata;
|
|
27
|
-
private maxTokens?: number;
|
|
28
|
-
private totalInputTokens = 0;
|
|
29
|
-
private totalOutputTokens = 0;
|
|
30
|
-
private callCount = 0;
|
|
31
|
-
|
|
32
|
-
constructor(model: string, overrideMaxTokens?: number) {
|
|
33
|
-
this.metadata = getProviderMetadata(model);
|
|
34
|
-
this.maxTokens = overrideMaxTokens ?? this.metadata.maxContextTokens;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Estimate whether an upcoming inference fits within the token allowance
|
|
39
|
-
* @param inputText Planned prompt text
|
|
40
|
-
* @param estimatedOutputTokens Rough guess for completion tokens
|
|
41
|
-
*/
|
|
42
|
-
checkBudget(inputText: string, estimatedOutputTokens: number = 1000): BudgetEstimate {
|
|
43
|
-
const inputTokens = estimateTokenCount(inputText);
|
|
44
|
-
const totalTokens = inputTokens + estimatedOutputTokens;
|
|
45
|
-
|
|
46
|
-
if (this.maxTokens !== undefined) {
|
|
47
|
-
const projected = this.totalTokensConsumed() + totalTokens;
|
|
48
|
-
if (projected > this.maxTokens) {
|
|
49
|
-
throw new TokenBudgetExceededError(
|
|
50
|
-
`Token allowance exceeded: projected ${projected} tokens > limit ${this.maxTokens}`,
|
|
51
|
-
{
|
|
52
|
-
inputTokens,
|
|
53
|
-
outputTokens: estimatedOutputTokens,
|
|
54
|
-
totalTokens,
|
|
55
|
-
},
|
|
56
|
-
this.getStatus()
|
|
57
|
-
);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
return {
|
|
62
|
-
inputTokens,
|
|
63
|
-
outputTokens: estimatedOutputTokens,
|
|
64
|
-
totalTokens,
|
|
65
|
-
};
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Record actual token usage after a call completes
|
|
70
|
-
*/
|
|
71
|
-
recordUsage(actualInputTokens: number, actualOutputTokens: number): void {
|
|
72
|
-
this.totalInputTokens += actualInputTokens;
|
|
73
|
-
this.totalOutputTokens += actualOutputTokens;
|
|
74
|
-
this.callCount += 1;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
getStatus(): BudgetStatus {
|
|
78
|
-
const totalTokens = this.totalTokensConsumed();
|
|
79
|
-
const status: BudgetStatus = {
|
|
80
|
-
totalInputTokens: this.totalInputTokens,
|
|
81
|
-
totalOutputTokens: this.totalOutputTokens,
|
|
82
|
-
totalTokens,
|
|
83
|
-
maxTokens: this.maxTokens,
|
|
84
|
-
callCount: this.callCount,
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
if (this.maxTokens !== undefined) {
|
|
88
|
-
status.remainingTokens = Math.max(0, this.maxTokens - totalTokens);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
return status;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
getMetadata(): ProviderMetadata {
|
|
95
|
-
return this.metadata;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
reset(): void {
|
|
99
|
-
this.totalInputTokens = 0;
|
|
100
|
-
this.totalOutputTokens = 0;
|
|
101
|
-
this.callCount = 0;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
private totalTokensConsumed(): number {
|
|
105
|
-
return this.totalInputTokens + this.totalOutputTokens;
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
export class TokenBudgetExceededError extends Error {
|
|
110
|
-
constructor(
|
|
111
|
-
message: string,
|
|
112
|
-
public estimate: BudgetEstimate,
|
|
113
|
-
public status: BudgetStatus
|
|
114
|
-
) {
|
|
115
|
-
super(message);
|
|
116
|
-
this.name = 'TokenBudgetExceededError';
|
|
117
|
-
}
|
|
118
|
-
}
|