adaptive-memory-multi-model-router 2.8.0 → 2.9.1
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/README.md +73 -42
- package/dist/index.d.ts +2 -0
- package/dist/index.js +14 -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/package.json +1 -1
- package/src/index.ts +16 -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/test/test_observability.ts +390 -0
package/README.md
CHANGED
|
@@ -177,57 +177,88 @@ A3M Router combines multi-signal routing, semantic caching, and load balancing t
|
|
|
177
177
|
|
|
178
178
|
### Routing Signals
|
|
179
179
|
|
|
180
|
+
A3M Router uses **multi-signal heuristic scoring** — 12 keyword signals across 5 dimensions — to classify query complexity and route to the cheapest capable model. No ML model weights. No GPU required. <1ms latency.
|
|
181
|
+
|
|
180
182
|
```
|
|
181
183
|
User Query
|
|
182
184
|
↓
|
|
183
|
-
|
|
184
|
-
│
|
|
185
|
-
|
|
186
|
-
│
|
|
187
|
-
│
|
|
188
|
-
│
|
|
189
|
-
│
|
|
190
|
-
│
|
|
191
|
-
│
|
|
192
|
-
│
|
|
193
|
-
│
|
|
194
|
-
│
|
|
195
|
-
│
|
|
196
|
-
│
|
|
197
|
-
│
|
|
198
|
-
│
|
|
199
|
-
│
|
|
200
|
-
│
|
|
201
|
-
│
|
|
202
|
-
│
|
|
203
|
-
|
|
204
|
-
│
|
|
205
|
-
│
|
|
206
|
-
|
|
207
|
-
│
|
|
208
|
-
│
|
|
209
|
-
│
|
|
210
|
-
|
|
185
|
+
┌──────────────────────────────────────────────────────────────┐
|
|
186
|
+
│ 12-Keyword Signal Extraction │
|
|
187
|
+
├──────────────────────────────────────────────────────────────┤
|
|
188
|
+
│ │
|
|
189
|
+
│ Signal 1: Domain Detection (+0.35 max) │
|
|
190
|
+
│ ┌────────────────────────────────────────────────────────┐ │
|
|
191
|
+
│ │ legal/contract/liability/clause → +0.35 │ │
|
|
192
|
+
│ │ medical/clinical/patient/diagnosis → +0.35 │ │
|
|
193
|
+
│ │ finance/investment/risk/portfolio → +0.30 │ │
|
|
194
|
+
│ │ security/vulnerability/exploit → +0.35 │ │
|
|
195
|
+
│ │ architecture/system design → +0.25 │ │
|
|
196
|
+
│ │ ML/model/training/gradient → +0.25 │ │
|
|
197
|
+
│ └────────────────────────────────────────────────────────┘ │
|
|
198
|
+
│ ↓ │
|
|
199
|
+
│ Signal 2: Task Indicators (+0.25 max) │
|
|
200
|
+
│ ┌────────────────────────────────────────────────────────┐ │
|
|
201
|
+
│ │ code/function/algorithm/debug → +0.25 │ │
|
|
202
|
+
│ │ math/calculate/equation/formula → +0.20 │ │
|
|
203
|
+
│ │ creative/story/poem → +0.10 │ │
|
|
204
|
+
│ │ translate/multilingual/language → +0.15 │ │
|
|
205
|
+
│ └────────────────────────────────────────────────────────┘ │
|
|
206
|
+
│ ↓ │
|
|
207
|
+
│ Signal 3: Query Structure (+0.20 max) │
|
|
208
|
+
│ ┌────────────────────────────────────────────────────────┐ │
|
|
209
|
+
│ │ Length > 200 chars → +0.05 │ │
|
|
210
|
+
│ │ Multiple clauses (and/or/but) → +0.10 │ │
|
|
211
|
+
│ │ Qualifiers (explain, analyze) → +0.05 │ │
|
|
212
|
+
│ └────────────────────────────────────────────────────────┘ │
|
|
213
|
+
│ ↓ │
|
|
214
|
+
│ Signal 4: Action Verb Intensity (+0.20 max) │
|
|
215
|
+
│ ┌────────────────────────────────────────────────────────┐ │
|
|
216
|
+
│ │ Expert: design/architect/optimize → +0.20 │ │
|
|
217
|
+
│ │ Mid: analyze/review/evaluate → +0.10 │ │
|
|
218
|
+
│ │ Simple: what/who/when/where → -0.10 │ │
|
|
219
|
+
│ └────────────────────────────────────────────────────────┘ │
|
|
220
|
+
│ ↓ │
|
|
221
|
+
│ Signal 5: Multi-Step Detection (+0.15 max) │
|
|
222
|
+
│ ┌────────────────────────────────────────────────────────┐ │
|
|
223
|
+
│ │ "first...then...finally" → +0.15 │ │
|
|
224
|
+
│ │ "step 1, step 2, step 3" → +0.15 │ │
|
|
225
|
+
│ └────────────────────────────────────────────────────────┘ │
|
|
226
|
+
│ │
|
|
227
|
+
├──────────────────────────────────────────────────────────────┤
|
|
228
|
+
│ Complexity Score → Tier Assignment │
|
|
229
|
+
│ │
|
|
230
|
+
│ 0.00 ────────── 0.19 ─────────── 0.44 ──────────── 1.00 │
|
|
231
|
+
│ ├─── free ─────|── cheap ───────|── mid ─────────| premium │
|
|
232
|
+
│ └── taste-1 ───┘ └── llama3.3 ──┘ └── gpt-4o-mini ┘ └──gpt4o│
|
|
233
|
+
│ $0 $0.20/M $0.60/M $2.50/M │
|
|
234
|
+
│ │
|
|
235
|
+
│ Route: Pick cheapest available model in tier │
|
|
236
|
+
│ Fallback: +2 fallback models if primary fails │
|
|
237
|
+
│ Quality: Adaptive scores from historical success rates │
|
|
238
|
+
└──────────────────────────────────────────────────────────────┘
|
|
211
239
|
↓
|
|
212
|
-
|
|
240
|
+
Result: { model, tier, cost, complexity, reasoning[], fallbackModels[] }
|
|
213
241
|
```
|
|
214
242
|
|
|
215
243
|
### Complexity Examples
|
|
216
244
|
|
|
217
|
-
| Query |
|
|
218
|
-
|
|
219
|
-
| "What is 2+2?" |
|
|
220
|
-
| "Write a Python sort
|
|
221
|
-
| "Analyze
|
|
222
|
-
| "Review
|
|
223
|
-
| "Design
|
|
245
|
+
| Query | Signals Detected | Score | Tier | Route To |
|
|
246
|
+
|-------|------------------|:-----:|:----:|----------|
|
|
247
|
+
| "What is 2+2?" | Simple structure | 0.10 | free | taste-1 ($0) |
|
|
248
|
+
| "Write a Python sort" | code+0.25, simple-0.10 | 0.33 | cheap | llama-3.3-70b ($0.20/M) |
|
|
249
|
+
| "Analyze AI implications" | analyze+0.10 | 0.41 | cheap | llama-3.3-70b ($0.20/M) |
|
|
250
|
+
| "Review contract liability" | legal+0.35, review+0.10, long+0.05 | 0.87 | premium | claude-3.5-sonnet ($1.50/M) |
|
|
251
|
+
| "Design oncology trial" | medical+0.35, design+0.20, steps+0.15 | 1.00 | premium | gpt-4o ($2.50/M) |
|
|
252
|
+
|
|
253
|
+
### Cost Savings by Query Type
|
|
254
|
+
|
|
224
255
|
| Query Type | % Traffic | GPT-4o Only | A3M Routes To | A3M Cost | Savings |
|
|
225
|
-
|
|
226
|
-
| Simple Q&A | 47% | $4.94 |
|
|
227
|
-
| Code gen | 15% | $4.88 |
|
|
228
|
-
| Summarization | 18% | $7.20 |
|
|
229
|
-
| Reasoning | 12% | $8.70 |
|
|
230
|
-
| Expert | 8% | $8.40 |
|
|
256
|
+
|------------|:---------:|:-----------:|:-------------:|:--------:|:-------:|
|
|
257
|
+
| Simple Q&A | 47% | $4.94 | taste-1 (free) | $0.00 | **100%** |
|
|
258
|
+
| Code gen | 15% | $4.88 | deepseek ($0.14/M) | $0.17 | **97%** |
|
|
259
|
+
| Summarization | 18% | $7.20 | gpt-4o-mini ($0.15/M) | $0.43 | **94%** |
|
|
260
|
+
| Reasoning | 12% | $8.70 | claude-haiku ($0.80/M) | $3.36 | **61%** |
|
|
261
|
+
| Expert | 8% | $8.40 | gpt-4o ($2.50/M) | $8.40 | **0%** |
|
|
231
262
|
| **Total** | **100%** | **$34.11** | — | **$12.36** | **64%** |
|
|
232
263
|
|
|
233
264
|
| Monthly Queries | GPT-4o Only | A3M Router | You Save | Annualized |
|
package/dist/index.d.ts
CHANGED
|
@@ -14,6 +14,8 @@ export { SemanticCache } from './cache/semanticCache';
|
|
|
14
14
|
export { GuardrailEngine } from './security/guardrails';
|
|
15
15
|
export { CostAnalytics } from './analytics/costAnalytics';
|
|
16
16
|
export { createProxyServer } from './server/proxyServer';
|
|
17
|
+
export { Tracer, getTracer, createTracer, MetricsCollector, getMetrics, createMetricsCollector, observabilityMiddleware, observabilityPlugin, budgetAlertMiddleware, } from './observability';
|
|
18
|
+
export type { Span, Metric, RouteTrace, ObservabilityEvent } from './observability';
|
|
17
19
|
import { getAvailableProviders, healthCheck } from './providers/providerConfig';
|
|
18
20
|
import { MemoryTree } from './memory/memoryTree';
|
|
19
21
|
export interface A3MRouterOptions {
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// A3M Router - Main Entry Point
|
|
3
3
|
// Version: 2.0.0
|
|
4
4
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
-
exports.createProxyServer = exports.CostAnalytics = exports.GuardrailEngine = exports.SemanticCache = exports.MODEL_COSTS = exports.estimateTokens = exports.countTokens = exports.MemoryTree = exports.createBudgetEnforcer = exports.BudgetExceededError = exports.BudgetEnforcer = exports.CostTracker = exports.saveConfig = exports.loadConfig = exports.findFastestAvailableProvider = exports.findCheapestAvailableProvider = exports.checkAllProviders = exports.healthCheck = exports.updateProvider = exports.deregisterProvider = exports.registerProvider = exports.getAvailableProviders = exports.DEFAULT_PROVIDERS = exports.PROVIDER_CONTEXT_LIMITS = exports.DEFAULT_PROVIDER_CONFIG = exports.DEFAULT_RETRY_CONFIG = exports.getDefaultRetryHandler = exports.createRetryHandler = exports.ProviderRetryHandler = exports.getProviderHealth = exports.updateModelProfile = exports.MODEL_PROFILES = exports.extractQueryFeatures = exports.recommendForTask = exports.routeBatch = exports.routeQuery = void 0;
|
|
5
|
+
exports.budgetAlertMiddleware = exports.observabilityPlugin = exports.observabilityMiddleware = exports.createMetricsCollector = exports.getMetrics = exports.MetricsCollector = exports.createTracer = exports.getTracer = exports.Tracer = exports.createProxyServer = exports.CostAnalytics = exports.GuardrailEngine = exports.SemanticCache = exports.MODEL_COSTS = exports.estimateTokens = exports.countTokens = exports.MemoryTree = exports.createBudgetEnforcer = exports.BudgetExceededError = exports.BudgetEnforcer = exports.CostTracker = exports.saveConfig = exports.loadConfig = exports.findFastestAvailableProvider = exports.findCheapestAvailableProvider = exports.checkAllProviders = exports.healthCheck = exports.updateProvider = exports.deregisterProvider = exports.registerProvider = exports.getAvailableProviders = exports.DEFAULT_PROVIDERS = exports.PROVIDER_CONTEXT_LIMITS = exports.DEFAULT_PROVIDER_CONFIG = exports.DEFAULT_RETRY_CONFIG = exports.getDefaultRetryHandler = exports.createRetryHandler = exports.ProviderRetryHandler = exports.getProviderHealth = exports.updateModelProfile = exports.MODEL_PROFILES = exports.extractQueryFeatures = exports.recommendForTask = exports.routeBatch = exports.routeQuery = void 0;
|
|
6
6
|
exports.createA3MRouter = createA3MRouter;
|
|
7
7
|
// ============================================================
|
|
8
8
|
// ROUTING ENGINE
|
|
@@ -74,6 +74,19 @@ Object.defineProperty(exports, "CostAnalytics", { enumerable: true, get: functio
|
|
|
74
74
|
var proxyServer_1 = require("./server/proxyServer");
|
|
75
75
|
Object.defineProperty(exports, "createProxyServer", { enumerable: true, get: function () { return proxyServer_1.createProxyServer; } });
|
|
76
76
|
// ============================================================
|
|
77
|
+
// OBSERVABILITY
|
|
78
|
+
// ============================================================
|
|
79
|
+
var observability_1 = require("./observability");
|
|
80
|
+
Object.defineProperty(exports, "Tracer", { enumerable: true, get: function () { return observability_1.Tracer; } });
|
|
81
|
+
Object.defineProperty(exports, "getTracer", { enumerable: true, get: function () { return observability_1.getTracer; } });
|
|
82
|
+
Object.defineProperty(exports, "createTracer", { enumerable: true, get: function () { return observability_1.createTracer; } });
|
|
83
|
+
Object.defineProperty(exports, "MetricsCollector", { enumerable: true, get: function () { return observability_1.MetricsCollector; } });
|
|
84
|
+
Object.defineProperty(exports, "getMetrics", { enumerable: true, get: function () { return observability_1.getMetrics; } });
|
|
85
|
+
Object.defineProperty(exports, "createMetricsCollector", { enumerable: true, get: function () { return observability_1.createMetricsCollector; } });
|
|
86
|
+
Object.defineProperty(exports, "observabilityMiddleware", { enumerable: true, get: function () { return observability_1.observabilityMiddleware; } });
|
|
87
|
+
Object.defineProperty(exports, "observabilityPlugin", { enumerable: true, get: function () { return observability_1.observabilityPlugin; } });
|
|
88
|
+
Object.defineProperty(exports, "budgetAlertMiddleware", { enumerable: true, get: function () { return observability_1.budgetAlertMiddleware; } });
|
|
89
|
+
// ============================================================
|
|
77
90
|
// CONVENIENCE: Create a router instance
|
|
78
91
|
// ============================================================
|
|
79
92
|
const advancedRouter_2 = require("./routing/advancedRouter");
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Observability module exports
|
|
3
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
|
+
if (k2 === undefined) k2 = k;
|
|
5
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
6
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
7
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
8
|
+
}
|
|
9
|
+
Object.defineProperty(o, k2, desc);
|
|
10
|
+
}) : (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
o[k2] = m[k];
|
|
13
|
+
}));
|
|
14
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
15
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
16
|
+
};
|
|
17
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
exports.budgetAlertMiddleware = exports.observabilityPlugin = exports.observabilityMiddleware = exports.createMetricsCollector = exports.getMetrics = exports.MetricsCollector = exports.createTracer = exports.getTracer = exports.Tracer = void 0;
|
|
19
|
+
__exportStar(require("./types"), exports);
|
|
20
|
+
var tracer_1 = require("./tracer");
|
|
21
|
+
Object.defineProperty(exports, "Tracer", { enumerable: true, get: function () { return tracer_1.Tracer; } });
|
|
22
|
+
Object.defineProperty(exports, "getTracer", { enumerable: true, get: function () { return tracer_1.getTracer; } });
|
|
23
|
+
Object.defineProperty(exports, "createTracer", { enumerable: true, get: function () { return tracer_1.createTracer; } });
|
|
24
|
+
var metrics_1 = require("./metrics");
|
|
25
|
+
Object.defineProperty(exports, "MetricsCollector", { enumerable: true, get: function () { return metrics_1.MetricsCollector; } });
|
|
26
|
+
Object.defineProperty(exports, "getMetrics", { enumerable: true, get: function () { return metrics_1.getMetrics; } });
|
|
27
|
+
Object.defineProperty(exports, "createMetricsCollector", { enumerable: true, get: function () { return metrics_1.createMetricsCollector; } });
|
|
28
|
+
var middleware_1 = require("./middleware");
|
|
29
|
+
Object.defineProperty(exports, "observabilityMiddleware", { enumerable: true, get: function () { return middleware_1.observabilityMiddleware; } });
|
|
30
|
+
Object.defineProperty(exports, "observabilityPlugin", { enumerable: true, get: function () { return middleware_1.observabilityPlugin; } });
|
|
31
|
+
Object.defineProperty(exports, "budgetAlertMiddleware", { enumerable: true, get: function () { return middleware_1.budgetAlertMiddleware; } });
|
|
32
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/observability/index.ts"],"names":[],"mappings":";AAAA,+BAA+B;;;;;;;;;;;;;;;;;AAE/B,0CAAwB;AACxB,mCAA2D;AAAlD,gGAAA,MAAM,OAAA;AAAE,mGAAA,SAAS,OAAA;AAAE,sGAAA,YAAY,OAAA;AACxC,qCAAiF;AAAxE,2GAAA,gBAAgB,OAAA;AAAE,qGAAA,UAAU,OAAA;AAAE,iHAAA,sBAAsB,OAAA;AAC7D,2CAIsB;AAHpB,qHAAA,uBAAuB,OAAA;AACvB,iHAAA,mBAAmB,OAAA;AACnB,mHAAA,qBAAqB,OAAA"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Metric } from './types';
|
|
2
|
+
declare class MetricsCollector {
|
|
3
|
+
private counters;
|
|
4
|
+
private gauges;
|
|
5
|
+
private histograms;
|
|
6
|
+
private createdAt;
|
|
7
|
+
/**
|
|
8
|
+
* Increment counter
|
|
9
|
+
*/
|
|
10
|
+
incrementCounter(name: string, labels?: Record<string, string>, value?: number): void;
|
|
11
|
+
/**
|
|
12
|
+
* Set gauge
|
|
13
|
+
*/
|
|
14
|
+
setGauge(name: string, value: number, labels?: Record<string, string>): void;
|
|
15
|
+
/**
|
|
16
|
+
* Record histogram value
|
|
17
|
+
*/
|
|
18
|
+
recordHistogram(name: string, value: number, labels?: Record<string, string>): void;
|
|
19
|
+
/**
|
|
20
|
+
* Get metrics in Prometheus format
|
|
21
|
+
*/
|
|
22
|
+
getPrometheusMetrics(): string;
|
|
23
|
+
/**
|
|
24
|
+
* Get all metrics as Metric objects
|
|
25
|
+
*/
|
|
26
|
+
getMetrics(): Metric[];
|
|
27
|
+
private extractLabels;
|
|
28
|
+
/**
|
|
29
|
+
* Clear metrics
|
|
30
|
+
*/
|
|
31
|
+
reset(): void;
|
|
32
|
+
}
|
|
33
|
+
export declare function getMetrics(): MetricsCollector;
|
|
34
|
+
export declare function createMetricsCollector(): MetricsCollector;
|
|
35
|
+
export {};
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getMetrics = getMetrics;
|
|
4
|
+
exports.createMetricsCollector = createMetricsCollector;
|
|
5
|
+
function formatLabels(labels) {
|
|
6
|
+
const parts = [];
|
|
7
|
+
for (const [key, value] of Object.entries(labels)) {
|
|
8
|
+
parts.push(`${key}="${value}"`);
|
|
9
|
+
}
|
|
10
|
+
return parts.join(',');
|
|
11
|
+
}
|
|
12
|
+
function metricKey(name, labels) {
|
|
13
|
+
if (!labels)
|
|
14
|
+
return name;
|
|
15
|
+
return `${name}{${formatLabels(labels)}}`;
|
|
16
|
+
}
|
|
17
|
+
class MetricsCollector {
|
|
18
|
+
counters = new Map();
|
|
19
|
+
gauges = new Map();
|
|
20
|
+
histograms = new Map();
|
|
21
|
+
createdAt = Date.now();
|
|
22
|
+
/**
|
|
23
|
+
* Increment counter
|
|
24
|
+
*/
|
|
25
|
+
incrementCounter(name, labels, value = 1) {
|
|
26
|
+
const key = metricKey(name, labels);
|
|
27
|
+
this.counters.set(key, (this.counters.get(key) || 0) + value);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Set gauge
|
|
31
|
+
*/
|
|
32
|
+
setGauge(name, value, labels) {
|
|
33
|
+
const key = metricKey(name, labels);
|
|
34
|
+
this.gauges.set(key, value);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Record histogram value
|
|
38
|
+
*/
|
|
39
|
+
recordHistogram(name, value, labels) {
|
|
40
|
+
const key = metricKey(name, labels);
|
|
41
|
+
if (!this.histograms.has(name)) {
|
|
42
|
+
this.histograms.set(name, new Map());
|
|
43
|
+
}
|
|
44
|
+
const buckets = this.histograms.get(name);
|
|
45
|
+
const bucket = buckets.get(key) || { count: 0, sum: 0, min: Infinity, max: -Infinity };
|
|
46
|
+
bucket.count++;
|
|
47
|
+
bucket.sum += value;
|
|
48
|
+
bucket.min = Math.min(bucket.min, value);
|
|
49
|
+
bucket.max = Math.max(bucket.max, value);
|
|
50
|
+
buckets.set(key, bucket);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Get metrics in Prometheus format
|
|
54
|
+
*/
|
|
55
|
+
getPrometheusMetrics() {
|
|
56
|
+
const lines = [];
|
|
57
|
+
const timestamp = Date.now();
|
|
58
|
+
lines.push('# HELP a3m_router_info A3M Router info');
|
|
59
|
+
lines.push('# TYPE a3m_router_info gauge');
|
|
60
|
+
lines.push('a3m_router_info{router_version="2.0.0"} 1');
|
|
61
|
+
// Counters
|
|
62
|
+
lines.push('');
|
|
63
|
+
lines.push('# HELP a3m_router_requests_total Total number of router requests');
|
|
64
|
+
lines.push('# TYPE a3m_router_requests_total counter');
|
|
65
|
+
for (const [key, value] of this.counters) {
|
|
66
|
+
if (key.startsWith('a3m_router_') || key.startsWith('a3m_')) {
|
|
67
|
+
lines.push(`${key} ${value} ${timestamp}`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
// Cache counters
|
|
71
|
+
for (const [key, value] of this.counters) {
|
|
72
|
+
if (key.startsWith('cache_')) {
|
|
73
|
+
lines.push(`${key} ${value} ${timestamp}`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
// Gauges
|
|
77
|
+
lines.push('');
|
|
78
|
+
lines.push('# HELP a3m_active_providers Number of active providers');
|
|
79
|
+
lines.push('# TYPE a3m_active_providers gauge');
|
|
80
|
+
for (const [key, value] of this.gauges) {
|
|
81
|
+
lines.push(`${key} ${value} ${timestamp}`);
|
|
82
|
+
}
|
|
83
|
+
// Histograms
|
|
84
|
+
lines.push('');
|
|
85
|
+
lines.push('# HELP a3m_request_latency_seconds Request latency in seconds');
|
|
86
|
+
lines.push('# TYPE a3m_request_latency_seconds histogram');
|
|
87
|
+
for (const [name, buckets] of this.histograms) {
|
|
88
|
+
if (name.includes('latency')) {
|
|
89
|
+
for (const [bucketKey, bucket] of buckets) {
|
|
90
|
+
const labelStr = bucketKey.includes('{') ? bucketKey.slice(name.length) : '';
|
|
91
|
+
lines.push(`${name}_sum${labelStr} ${bucket.sum} ${timestamp}`);
|
|
92
|
+
lines.push(`${name}_count${labelStr} ${bucket.count} ${timestamp}`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
lines.push('');
|
|
97
|
+
lines.push('# HELP a3m_request_cost_cents Request cost in cents');
|
|
98
|
+
lines.push('# TYPE a3m_request_cost_cents histogram');
|
|
99
|
+
for (const [name, buckets] of this.histograms) {
|
|
100
|
+
if (name.includes('cost')) {
|
|
101
|
+
for (const [bucketKey, bucket] of buckets) {
|
|
102
|
+
const labelStr = bucketKey.includes('{') ? bucketKey.slice(name.length) : '';
|
|
103
|
+
lines.push(`${name}_sum${labelStr} ${bucket.sum} ${timestamp}`);
|
|
104
|
+
lines.push(`${name}_count${labelStr} ${bucket.count} ${timestamp}`);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return lines.join('\n');
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Get all metrics as Metric objects
|
|
112
|
+
*/
|
|
113
|
+
getMetrics() {
|
|
114
|
+
const metrics = [];
|
|
115
|
+
const timestamp = Date.now();
|
|
116
|
+
for (const [key, value] of this.counters) {
|
|
117
|
+
metrics.push({
|
|
118
|
+
name: key,
|
|
119
|
+
value,
|
|
120
|
+
timestamp,
|
|
121
|
+
labels: this.extractLabels(key),
|
|
122
|
+
type: 'counter',
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
for (const [key, value] of this.gauges) {
|
|
126
|
+
metrics.push({
|
|
127
|
+
name: key,
|
|
128
|
+
value,
|
|
129
|
+
timestamp,
|
|
130
|
+
labels: this.extractLabels(key),
|
|
131
|
+
type: 'gauge',
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
for (const [name, buckets] of this.histograms) {
|
|
135
|
+
for (const [bucketKey, bucket] of buckets) {
|
|
136
|
+
metrics.push({
|
|
137
|
+
name: bucketKey,
|
|
138
|
+
value: bucket.count,
|
|
139
|
+
timestamp,
|
|
140
|
+
labels: this.extractLabels(bucketKey),
|
|
141
|
+
type: 'histogram',
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return metrics;
|
|
146
|
+
}
|
|
147
|
+
extractLabels(metricKey) {
|
|
148
|
+
const labels = {};
|
|
149
|
+
const match = metricKey.match(/\{(.+)\}/);
|
|
150
|
+
if (match) {
|
|
151
|
+
const pairs = match[1].split(',');
|
|
152
|
+
for (const pair of pairs) {
|
|
153
|
+
const [key, value] = pair.split('=');
|
|
154
|
+
labels[key] = value.replace(/"/g, '');
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return labels;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Clear metrics
|
|
161
|
+
*/
|
|
162
|
+
reset() {
|
|
163
|
+
this.counters.clear();
|
|
164
|
+
this.gauges.clear();
|
|
165
|
+
this.histograms.clear();
|
|
166
|
+
this.createdAt = Date.now();
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
// Singleton instance
|
|
170
|
+
let metricsInstance = null;
|
|
171
|
+
function getMetrics() {
|
|
172
|
+
if (!metricsInstance) {
|
|
173
|
+
metricsInstance = new MetricsCollector();
|
|
174
|
+
}
|
|
175
|
+
return metricsInstance;
|
|
176
|
+
}
|
|
177
|
+
function createMetricsCollector() {
|
|
178
|
+
metricsInstance = new MetricsCollector();
|
|
179
|
+
return metricsInstance;
|
|
180
|
+
}
|
|
181
|
+
//# sourceMappingURL=metrics.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metrics.js","sourceRoot":"","sources":["../../src/observability/metrics.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Request, Response, NextFunction } from 'express';
|
|
2
|
+
/**
|
|
3
|
+
* Express middleware for observability
|
|
4
|
+
* - Adds trace ID to all requests
|
|
5
|
+
* - Records request/response metrics
|
|
6
|
+
* - Attaches span context
|
|
7
|
+
*/
|
|
8
|
+
export declare function observabilityMiddleware(req: Request, res: Response, next: NextFunction): void;
|
|
9
|
+
/**
|
|
10
|
+
* Fastify plugin for observability middleware
|
|
11
|
+
*/
|
|
12
|
+
export declare function observabilityPlugin(instance: any, options: any, next: (err?: Error) => void): void;
|
|
13
|
+
/**
|
|
14
|
+
* Middleware for budget warning alerts
|
|
15
|
+
*/
|
|
16
|
+
export declare function budgetAlertMiddleware(req: Request, res: Response, next: NextFunction): void;
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.observabilityMiddleware = observabilityMiddleware;
|
|
4
|
+
exports.observabilityPlugin = observabilityPlugin;
|
|
5
|
+
exports.budgetAlertMiddleware = budgetAlertMiddleware;
|
|
6
|
+
const tracer_1 = require("./tracer");
|
|
7
|
+
const metrics_1 = require("./metrics");
|
|
8
|
+
/**
|
|
9
|
+
* Express middleware for observability
|
|
10
|
+
* - Adds trace ID to all requests
|
|
11
|
+
* - Records request/response metrics
|
|
12
|
+
* - Attaches span context
|
|
13
|
+
*/
|
|
14
|
+
function observabilityMiddleware(req, res, next) {
|
|
15
|
+
const tracer = (0, tracer_1.getTracer)();
|
|
16
|
+
const metrics = (0, metrics_1.getMetrics)();
|
|
17
|
+
// Generate or extract trace ID
|
|
18
|
+
const traceId = req.headers['x-trace-id'] || tracer.generateTraceId();
|
|
19
|
+
// Attach to request object
|
|
20
|
+
req.traceId = traceId;
|
|
21
|
+
req.spanId = null;
|
|
22
|
+
// Add trace ID to response headers
|
|
23
|
+
res.setHeader('X-Trace-ID', traceId);
|
|
24
|
+
// Start span for this request
|
|
25
|
+
const span = tracer.startSpan(`${req.method} ${req.path}`);
|
|
26
|
+
req.spanId = span.spanId;
|
|
27
|
+
req.span = span;
|
|
28
|
+
// Record start time for latency calculation
|
|
29
|
+
const startTime = Date.now();
|
|
30
|
+
// Hook into response finish
|
|
31
|
+
res.on('finish', () => {
|
|
32
|
+
const duration = Date.now() - startTime;
|
|
33
|
+
// End the span
|
|
34
|
+
tracer.endSpan(span.spanId, {
|
|
35
|
+
method: req.method,
|
|
36
|
+
path: req.path,
|
|
37
|
+
statusCode: res.statusCode,
|
|
38
|
+
durationMs: duration,
|
|
39
|
+
});
|
|
40
|
+
// Record metrics
|
|
41
|
+
const model = req.model || 'unknown';
|
|
42
|
+
const provider = req.provider || 'unknown';
|
|
43
|
+
const cacheHit = req.cacheHit || false;
|
|
44
|
+
const tier = req.tier || 'standard';
|
|
45
|
+
// Request counter
|
|
46
|
+
metrics.incrementCounter('a3m_router_requests_total', {
|
|
47
|
+
model,
|
|
48
|
+
provider,
|
|
49
|
+
tier,
|
|
50
|
+
cache_hit: String(cacheHit),
|
|
51
|
+
});
|
|
52
|
+
// Latency histogram (convert to seconds)
|
|
53
|
+
metrics.recordHistogram('a3m_request_latency_seconds', duration / 1000, {
|
|
54
|
+
model,
|
|
55
|
+
provider,
|
|
56
|
+
});
|
|
57
|
+
// Error counter
|
|
58
|
+
if (res.statusCode >= 400) {
|
|
59
|
+
metrics.incrementCounter('a3m_router_errors_total', {
|
|
60
|
+
provider,
|
|
61
|
+
error_type: res.statusCode >= 500 ? 'server_error' : 'client_error',
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
next();
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Fastify plugin for observability middleware
|
|
69
|
+
*/
|
|
70
|
+
function observabilityPlugin(instance, options, next) {
|
|
71
|
+
instance.addHook('onRequest', async (request, reply) => {
|
|
72
|
+
const tracer = (0, tracer_1.getTracer)();
|
|
73
|
+
const metrics = (0, metrics_1.getMetrics)();
|
|
74
|
+
const traceId = request.headers['x-trace-id'] || tracer.generateTraceId();
|
|
75
|
+
request.traceId = traceId;
|
|
76
|
+
reply.header('X-Trace-ID', traceId);
|
|
77
|
+
const span = tracer.startSpan(`${request.method} ${request.url}`);
|
|
78
|
+
request.spanId = span.spanId;
|
|
79
|
+
request.span = span;
|
|
80
|
+
request.startTime = Date.now();
|
|
81
|
+
});
|
|
82
|
+
instance.addHook('onResponse', async (request, reply) => {
|
|
83
|
+
const tracer = (0, tracer_1.getTracer)();
|
|
84
|
+
const metrics = (0, metrics_1.getMetrics)();
|
|
85
|
+
const duration = Date.now() - request.startTime;
|
|
86
|
+
if (request.spanId) {
|
|
87
|
+
tracer.endSpan(request.spanId, {
|
|
88
|
+
method: request.method,
|
|
89
|
+
path: request.url,
|
|
90
|
+
statusCode: reply.statusCode,
|
|
91
|
+
durationMs: duration,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
const model = request.model || 'unknown';
|
|
95
|
+
const provider = request.provider || 'unknown';
|
|
96
|
+
const cacheHit = request.cacheHit || false;
|
|
97
|
+
const tier = request.tier || 'standard';
|
|
98
|
+
metrics.incrementCounter('a3m_router_requests_total', {
|
|
99
|
+
model,
|
|
100
|
+
provider,
|
|
101
|
+
tier,
|
|
102
|
+
cache_hit: String(cacheHit),
|
|
103
|
+
});
|
|
104
|
+
metrics.recordHistogram('a3m_request_latency_seconds', duration / 1000, {
|
|
105
|
+
model,
|
|
106
|
+
provider,
|
|
107
|
+
});
|
|
108
|
+
if (reply.statusCode >= 400) {
|
|
109
|
+
metrics.incrementCounter('a3m_router_errors_total', {
|
|
110
|
+
provider,
|
|
111
|
+
error_type: reply.statusCode >= 500 ? 'server_error' : 'client_error',
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
next();
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Middleware for budget warning alerts
|
|
119
|
+
*/
|
|
120
|
+
function budgetAlertMiddleware(req, res, next) {
|
|
121
|
+
const tracer = (0, tracer_1.getTracer)();
|
|
122
|
+
// Hook into cost tracking to emit budget warnings
|
|
123
|
+
tracer.on('route_complete', (trace) => {
|
|
124
|
+
// This would be connected to budget enforcement in practice
|
|
125
|
+
// For now just a placeholder for the event hook
|
|
126
|
+
});
|
|
127
|
+
next();
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=middleware.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"middleware.js","sourceRoot":"","sources":["../../src/observability/middleware.ts"],"names":[],"mappings":"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"}
|