-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathTokenStreamOptimizer.ts
More file actions
93 lines (73 loc) · 2.52 KB
/
TokenStreamOptimizer.ts
File metadata and controls
93 lines (73 loc) · 2.52 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
import { EventEmitter } from 'events';
interface TokenBatch { tokens: string[], timestamp: number, count: number }
interface RateLimitConfig { tokensPerSecond: number, burst: number }
class TokenStreamOptimizer extends EventEmitter {
private tokenQueue: TokenBatch[] = [];
private circuitOpen = false;
private lastTokenTime = 0;
private allowedTokens: number;
private readonly config: RateLimitConfig;
private tokenCount = 0;
private estimatedCost = 0;
constructor(config: RateLimitConfig = { tokensPerSecond: 100, burst: 10 }) {
super();
this.config = config;
this.allowedTokens = config.burst;
}
private tokenize(text: string): string[] {
return text.match(/\b\w+\b|[^\w\s]/g) || [];
}
async streamTokens(
text: string,
chunkSize: number = 5,
delayMs: number = 50
): Promise<void> {
if (this.circuitOpen) throw new Error('Circuit breaker open');
const tokens = this.tokenize(text);
let processed = 0;
for (let i = 0; i < tokens.length; i += chunkSize) {
const batch = tokens.slice(i, i + chunkSize);
const available = this.checkRateLimit(batch.length);
if (!available) {
await new Promise(resolve => setTimeout(resolve, delayMs));
this.refillTokens();
}
this.emit('tokens', batch);
this.tokenCount += batch.length;
processed++;
if (processed % 20 === 0) this.emit('progress', { processed, total: tokens.length });
}
this.emit('complete', { tokenCount: this.tokenCount, estimated_cost_usd: this.estimateCost() });
}
private checkRateLimit(tokensNeeded: number): boolean {
const now = Date.now();
const timeSinceLastReset = (now - this.lastTokenTime) / 1000;
if (timeSinceLastReset > 1) {
this.refillTokens();
this.lastTokenTime = now;
}
if (this.allowedTokens >= tokensNeeded) {
this.allowedTokens -= tokensNeeded;
return true;
}
return false;
}
private refillTokens(): void {
this.allowedTokens = Math.min(
this.allowedTokens + (this.config.tokensPerSecond / 1000),
this.config.burst
);
}
private estimateCost(): number {
const inputCost = (this.tokenCount / 1000) * 0.0001;
return Math.round(inputCost * 10000) / 10000;
}
setCircuitBreaker(open: boolean): void {
this.circuitOpen = open;
if (open) this.emit('circuit-open');
}
getMetrics() {
return { tokenCount: this.tokenCount, estimatedCost: this.estimateCost(), rateLimitRemaining: this.allowedTokens };
}
}
export default TokenStreamOptimizer;