adaptive-memory-multi-model-router 2.7.0 → 2.9.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/index.d.ts +4 -0
- package/dist/index.js +24 -1
- package/dist/observability/index.d.ts +4 -0
- package/dist/observability/index.js +32 -0
- package/dist/observability/index.js.map +1 -0
- package/dist/observability/metrics.d.ts +35 -0
- package/dist/observability/metrics.js +181 -0
- package/dist/observability/metrics.js.map +1 -0
- package/dist/observability/middleware.d.ts +16 -0
- package/dist/observability/middleware.js +129 -0
- package/dist/observability/middleware.js.map +1 -0
- package/dist/observability/tracer.d.ts +57 -0
- package/dist/observability/tracer.js +192 -0
- package/dist/observability/tracer.js.map +1 -0
- package/dist/observability/types.d.ts +52 -0
- package/dist/observability/types.js +4 -0
- package/dist/observability/types.js.map +1 -0
- package/dist/routing/providerRetry.d.ts +110 -0
- package/dist/routing/providerRetry.js +460 -0
- package/dist/routing/providerRetry.js.map +1 -0
- package/package.json +1 -1
- package/src/index.ts +34 -0
- package/src/observability/index.ts +10 -0
- package/src/observability/metrics.ts +215 -0
- package/src/observability/middleware.ts +164 -0
- package/src/observability/tracer.ts +229 -0
- package/src/observability/types.ts +45 -0
- package/src/routing/providerRetry.ts +578 -0
- package/test/test_observability.ts +390 -0
- package/test/test_providerRetry.ts +348 -0
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getTracer = getTracer;
|
|
4
|
+
exports.createTracer = createTracer;
|
|
5
|
+
const events_1 = require("events");
|
|
6
|
+
function generateId() {
|
|
7
|
+
return Math.random().toString(36).substring(2, 15) +
|
|
8
|
+
Math.random().toString(36).substring(2, 15);
|
|
9
|
+
}
|
|
10
|
+
class Tracer extends events_1.EventEmitter {
|
|
11
|
+
traces = new Map();
|
|
12
|
+
routeTraces = [];
|
|
13
|
+
langfuseClient;
|
|
14
|
+
langfuseConfig;
|
|
15
|
+
constructor() {
|
|
16
|
+
super();
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Initialize Langfuse if configured
|
|
20
|
+
*/
|
|
21
|
+
initLangfuse(config) {
|
|
22
|
+
if (!config.publicKey || !config.secretKey) {
|
|
23
|
+
console.warn('[Tracer] Langfuse keys not provided, running without Langfuse');
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
this.langfuseConfig = config;
|
|
27
|
+
// Simple Langfuse REST client
|
|
28
|
+
this.langfuseClient = {
|
|
29
|
+
trace: async (data) => {
|
|
30
|
+
try {
|
|
31
|
+
const response = await fetch(`${config.baseUrl || 'https://cloud.langfuse.com'}/api/public/ingestion`, {
|
|
32
|
+
method: 'POST',
|
|
33
|
+
headers: {
|
|
34
|
+
'Content-Type': 'application/json',
|
|
35
|
+
'Authorization': `Bearer ${config.publicKey}`,
|
|
36
|
+
},
|
|
37
|
+
body: JSON.stringify(data),
|
|
38
|
+
});
|
|
39
|
+
if (!response.ok) {
|
|
40
|
+
console.warn('[Tracer] Langfuse ingestion failed:', response.status);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
catch (err) {
|
|
44
|
+
console.warn('[Tracer] Langfuse error:', err);
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
flush: async () => {
|
|
48
|
+
// Langfuse uses async ingestion, noop here
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
console.log('[Tracer] Langfuse initialized');
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Create a new trace span
|
|
55
|
+
*/
|
|
56
|
+
startSpan(operationName, parentSpanId) {
|
|
57
|
+
const traceId = parentSpanId
|
|
58
|
+
? this.traces.get(parentSpanId)?.traceId || this.generateTraceId()
|
|
59
|
+
: this.generateTraceId();
|
|
60
|
+
const span = {
|
|
61
|
+
traceId,
|
|
62
|
+
spanId: this.generateTraceId(),
|
|
63
|
+
parentSpanId,
|
|
64
|
+
operationName,
|
|
65
|
+
startTime: Date.now(),
|
|
66
|
+
attributes: {},
|
|
67
|
+
status: 'started',
|
|
68
|
+
};
|
|
69
|
+
this.traces.set(span.spanId, span);
|
|
70
|
+
this.emit('span_started', span);
|
|
71
|
+
this.emit('observability_event', { type: 'span_started', span });
|
|
72
|
+
return span;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* End a span
|
|
76
|
+
*/
|
|
77
|
+
endSpan(spanId, attributes) {
|
|
78
|
+
const span = this.traces.get(spanId);
|
|
79
|
+
if (!span) {
|
|
80
|
+
console.warn(`[Tracer] span ${spanId} not found`);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
span.endTime = Date.now();
|
|
84
|
+
span.duration = span.endTime - span.startTime;
|
|
85
|
+
span.status = 'completed';
|
|
86
|
+
if (attributes) {
|
|
87
|
+
span.attributes = { ...span.attributes, ...attributes };
|
|
88
|
+
}
|
|
89
|
+
this.emit('span_completed', span);
|
|
90
|
+
this.emit('observability_event', { type: 'span_completed', span });
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Record an error on a span
|
|
94
|
+
*/
|
|
95
|
+
errorSpan(spanId, error, attributes) {
|
|
96
|
+
const span = this.traces.get(spanId);
|
|
97
|
+
if (!span) {
|
|
98
|
+
console.warn(`[Tracer] span ${spanId} not found`);
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
span.endTime = Date.now();
|
|
102
|
+
span.duration = span.endTime - span.startTime;
|
|
103
|
+
span.status = 'error';
|
|
104
|
+
span.error = error;
|
|
105
|
+
if (attributes) {
|
|
106
|
+
span.attributes = { ...span.attributes, ...attributes };
|
|
107
|
+
}
|
|
108
|
+
this.emit('error', spanId, error);
|
|
109
|
+
this.emit('observability_event', { type: 'error', spanId, error });
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Record a route decision
|
|
113
|
+
*/
|
|
114
|
+
recordRoute(trace) {
|
|
115
|
+
this.routeTraces.push(trace);
|
|
116
|
+
// Emit event
|
|
117
|
+
this.emit('route_complete', trace);
|
|
118
|
+
this.emit('observability_event', { type: 'route_complete', trace });
|
|
119
|
+
// Export to Langfuse async
|
|
120
|
+
if (this.langfuseClient) {
|
|
121
|
+
this.langfuseClient.trace({
|
|
122
|
+
name: 'route_decision',
|
|
123
|
+
traceId: trace.traceId,
|
|
124
|
+
timestamp: new Date(trace.timestamp).toISOString(),
|
|
125
|
+
input: { query: trace.query },
|
|
126
|
+
output: {
|
|
127
|
+
model: trace.model,
|
|
128
|
+
provider: trace.provider,
|
|
129
|
+
tokens: trace.queryTokens + trace.responseTokens,
|
|
130
|
+
latencyMs: trace.latencyMs,
|
|
131
|
+
cost: trace.cost,
|
|
132
|
+
},
|
|
133
|
+
metadata: {
|
|
134
|
+
queryTokens: trace.queryTokens,
|
|
135
|
+
responseTokens: trace.responseTokens,
|
|
136
|
+
cacheHit: trace.cacheHit,
|
|
137
|
+
complexity: trace.complexity,
|
|
138
|
+
tier: trace.tier,
|
|
139
|
+
},
|
|
140
|
+
}).catch(() => { }); // Fire and forget
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Generate a unique trace ID
|
|
145
|
+
*/
|
|
146
|
+
generateTraceId() {
|
|
147
|
+
return generateId();
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Get all route traces
|
|
151
|
+
*/
|
|
152
|
+
getTraces(limit) {
|
|
153
|
+
if (limit) {
|
|
154
|
+
return this.routeTraces.slice(-limit);
|
|
155
|
+
}
|
|
156
|
+
return [...this.routeTraces];
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Get all spans for a trace
|
|
160
|
+
*/
|
|
161
|
+
getSpans(traceId) {
|
|
162
|
+
return Array.from(this.traces.values()).filter(s => s.traceId === traceId);
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Export to Langfuse (async, non-blocking)
|
|
166
|
+
*/
|
|
167
|
+
async flushToLangfuse() {
|
|
168
|
+
if (this.langfuseClient) {
|
|
169
|
+
await this.langfuseClient.flush();
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Clear all traces (for testing)
|
|
174
|
+
*/
|
|
175
|
+
reset() {
|
|
176
|
+
this.traces.clear();
|
|
177
|
+
this.routeTraces = [];
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
// Singleton instance
|
|
181
|
+
let tracerInstance = null;
|
|
182
|
+
function getTracer() {
|
|
183
|
+
if (!tracerInstance) {
|
|
184
|
+
tracerInstance = new Tracer();
|
|
185
|
+
}
|
|
186
|
+
return tracerInstance;
|
|
187
|
+
}
|
|
188
|
+
function createTracer() {
|
|
189
|
+
tracerInstance = new Tracer();
|
|
190
|
+
return tracerInstance;
|
|
191
|
+
}
|
|
192
|
+
//# sourceMappingURL=tracer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tracer.js","sourceRoot":"","sources":["../../src/observability/tracer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
export interface Span {
|
|
2
|
+
traceId: string;
|
|
3
|
+
spanId: string;
|
|
4
|
+
parentSpanId?: string;
|
|
5
|
+
operationName: string;
|
|
6
|
+
startTime: number;
|
|
7
|
+
endTime?: number;
|
|
8
|
+
duration?: number;
|
|
9
|
+
attributes: Record<string, string | number | boolean>;
|
|
10
|
+
status: 'started' | 'completed' | 'error';
|
|
11
|
+
error?: string;
|
|
12
|
+
}
|
|
13
|
+
export interface Metric {
|
|
14
|
+
name: string;
|
|
15
|
+
value: number;
|
|
16
|
+
timestamp: number;
|
|
17
|
+
labels: Record<string, string>;
|
|
18
|
+
type: 'counter' | 'gauge' | 'histogram';
|
|
19
|
+
}
|
|
20
|
+
export interface RouteTrace {
|
|
21
|
+
traceId: string;
|
|
22
|
+
timestamp: number;
|
|
23
|
+
query: string;
|
|
24
|
+
queryTokens: number;
|
|
25
|
+
model: string;
|
|
26
|
+
provider: string;
|
|
27
|
+
responseTokens: number;
|
|
28
|
+
latencyMs: number;
|
|
29
|
+
cost: number;
|
|
30
|
+
cacheHit: boolean;
|
|
31
|
+
complexity: number;
|
|
32
|
+
tier: 'budget' | 'standard' | 'premium';
|
|
33
|
+
}
|
|
34
|
+
export type ObservabilityEvent = {
|
|
35
|
+
type: 'route_complete';
|
|
36
|
+
trace: RouteTrace;
|
|
37
|
+
} | {
|
|
38
|
+
type: 'span_started';
|
|
39
|
+
span: Span;
|
|
40
|
+
} | {
|
|
41
|
+
type: 'span_completed';
|
|
42
|
+
span: Span;
|
|
43
|
+
} | {
|
|
44
|
+
type: 'budget_warning';
|
|
45
|
+
apiKey: string;
|
|
46
|
+
usedCents: number;
|
|
47
|
+
thresholdCents: number;
|
|
48
|
+
} | {
|
|
49
|
+
type: 'error';
|
|
50
|
+
spanId: string;
|
|
51
|
+
error: string;
|
|
52
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/observability/types.ts"],"names":[],"mappings":";AAAA,qCAAqC"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A3M Router - Per-Provider Retry Logic
|
|
3
|
+
*
|
|
4
|
+
* Implements exponential backoff with jitter for transient errors,
|
|
5
|
+
* rate limit (429) handling with Retry-After header support,
|
|
6
|
+
* and context window validation before sending requests.
|
|
7
|
+
*/
|
|
8
|
+
export interface RetryConfig {
|
|
9
|
+
maxRetries: number;
|
|
10
|
+
initialDelayMs: number;
|
|
11
|
+
maxDelayMs: number;
|
|
12
|
+
backoffMultiplier: number;
|
|
13
|
+
retryableErrors?: string[];
|
|
14
|
+
}
|
|
15
|
+
export interface ProviderRetryConfig {
|
|
16
|
+
[providerName: string]: {
|
|
17
|
+
timeout: number;
|
|
18
|
+
retry: RetryConfig;
|
|
19
|
+
rateLimitRetries?: number;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
export interface RetryStats {
|
|
23
|
+
totalRequests: number;
|
|
24
|
+
successfulRequests: number;
|
|
25
|
+
failedRequests: number;
|
|
26
|
+
totalRetries: number;
|
|
27
|
+
rateLimitRetries: number;
|
|
28
|
+
averageLatencyMs: number;
|
|
29
|
+
}
|
|
30
|
+
export interface ContextWindowValidation {
|
|
31
|
+
valid: boolean;
|
|
32
|
+
reason?: string;
|
|
33
|
+
suggestedProvider?: string;
|
|
34
|
+
}
|
|
35
|
+
declare const DEFAULT_RETRY_CONFIG: RetryConfig;
|
|
36
|
+
export declare const DEFAULT_PROVIDER_CONFIG: ProviderRetryConfig;
|
|
37
|
+
declare const PROVIDER_CONTEXT_LIMITS: Record<string, number>;
|
|
38
|
+
export declare class ProviderRetryHandler {
|
|
39
|
+
private configs;
|
|
40
|
+
private stats;
|
|
41
|
+
private customProviders;
|
|
42
|
+
constructor(customConfigs?: ProviderRetryConfig);
|
|
43
|
+
private initStats;
|
|
44
|
+
/**
|
|
45
|
+
* Configure or update a provider's retry settings
|
|
46
|
+
*/
|
|
47
|
+
configureProvider(provider: string, config: Partial<{
|
|
48
|
+
timeout: number;
|
|
49
|
+
retry: Partial<RetryConfig>;
|
|
50
|
+
rateLimitRetries: number;
|
|
51
|
+
}>): void;
|
|
52
|
+
/**
|
|
53
|
+
* Get current config for a provider
|
|
54
|
+
*/
|
|
55
|
+
getConfig(provider: string): {
|
|
56
|
+
timeout: number;
|
|
57
|
+
retry: RetryConfig;
|
|
58
|
+
rateLimitRetries: number;
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* Execute a function with retry logic
|
|
62
|
+
*/
|
|
63
|
+
executeWithRetry<T>(provider: string, fn: () => Promise<T>, options?: {
|
|
64
|
+
timeout?: number;
|
|
65
|
+
onRetry?: (attempt: number, error: any, delayMs: number) => void;
|
|
66
|
+
}): Promise<T>;
|
|
67
|
+
/**
|
|
68
|
+
* Execute with custom timeout wrapper
|
|
69
|
+
*/
|
|
70
|
+
private executeWithTimeout;
|
|
71
|
+
/**
|
|
72
|
+
* Check if an error should trigger a retry
|
|
73
|
+
*/
|
|
74
|
+
isRetryableError(error: any): boolean;
|
|
75
|
+
/**
|
|
76
|
+
* Check if error is a rate limit (429)
|
|
77
|
+
*/
|
|
78
|
+
isRateLimitError(error: any): boolean;
|
|
79
|
+
/**
|
|
80
|
+
* Calculate backoff delay with exponential increase and jitter
|
|
81
|
+
*/
|
|
82
|
+
calculateBackoffDelay(attempt: number, config: RetryConfig, error?: any): number;
|
|
83
|
+
/**
|
|
84
|
+
* Validate context window size before sending request
|
|
85
|
+
*/
|
|
86
|
+
validateContextWindow(provider: string, prompt: string, expectedTokens?: number): ContextWindowValidation;
|
|
87
|
+
/**
|
|
88
|
+
* Get retry statistics for a provider
|
|
89
|
+
*/
|
|
90
|
+
getStats(provider: string): RetryStats;
|
|
91
|
+
/**
|
|
92
|
+
* Get all provider stats
|
|
93
|
+
*/
|
|
94
|
+
getAllStats(): Record<string, RetryStats>;
|
|
95
|
+
/**
|
|
96
|
+
* Reset stats for a provider
|
|
97
|
+
*/
|
|
98
|
+
resetStats(provider?: string): void;
|
|
99
|
+
private createTimeoutError;
|
|
100
|
+
private sleep;
|
|
101
|
+
private recordSuccess;
|
|
102
|
+
private recordFailure;
|
|
103
|
+
private recordRetry;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Create a retry handler with optional custom configs
|
|
107
|
+
*/
|
|
108
|
+
export declare function createRetryHandler(customConfigs?: ProviderRetryConfig): ProviderRetryHandler;
|
|
109
|
+
export declare function getDefaultRetryHandler(): ProviderRetryHandler;
|
|
110
|
+
export { DEFAULT_RETRY_CONFIG, PROVIDER_CONTEXT_LIMITS, };
|