@adonis-agora/telescope 0.26.1 → 0.27.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/CHANGELOG.md +23 -0
- package/dist/providers/telescope_provider.d.ts +10 -0
- package/dist/providers/telescope_provider.d.ts.map +1 -1
- package/dist/providers/telescope_provider.js +37 -1
- package/dist/providers/telescope_provider.js.map +1 -1
- package/dist/providers/telescope_ui_provider.d.ts.map +1 -1
- package/dist/providers/telescope_ui_provider.js +29 -0
- package/dist/providers/telescope_ui_provider.js.map +1 -1
- package/dist/src/ai/factory.d.ts.map +1 -1
- package/dist/src/ai/factory.js +26 -3
- package/dist/src/ai/factory.js.map +1 -1
- package/dist/src/ai/index.d.ts +1 -1
- package/dist/src/ai/index.js +1 -1
- package/dist/src/ai_metrics/agent_events.d.ts +106 -0
- package/dist/src/ai_metrics/agent_events.d.ts.map +1 -0
- package/dist/src/ai_metrics/agent_events.js +170 -0
- package/dist/src/ai_metrics/agent_events.js.map +1 -0
- package/dist/src/ai_metrics/agent_metrics.d.ts +103 -0
- package/dist/src/ai_metrics/agent_metrics.d.ts.map +1 -0
- package/dist/src/ai_metrics/agent_metrics.js +255 -0
- package/dist/src/ai_metrics/agent_metrics.js.map +1 -0
- package/dist/src/ai_metrics/ai_usage.d.ts +123 -0
- package/dist/src/ai_metrics/ai_usage.d.ts.map +1 -0
- package/dist/src/ai_metrics/ai_usage.js +207 -0
- package/dist/src/ai_metrics/ai_usage.js.map +1 -0
- package/dist/src/ai_metrics/index.d.ts +11 -0
- package/dist/src/ai_metrics/index.d.ts.map +1 -0
- package/dist/src/ai_metrics/index.js +11 -0
- package/dist/src/ai_metrics/index.js.map +1 -0
- package/dist/src/ai_metrics/otel_metrics_exporter.d.ts +60 -0
- package/dist/src/ai_metrics/otel_metrics_exporter.d.ts.map +1 -0
- package/dist/src/ai_metrics/otel_metrics_exporter.js +66 -0
- package/dist/src/ai_metrics/otel_metrics_exporter.js.map +1 -0
- package/dist/src/ai_metrics/subscribe.d.ts +17 -0
- package/dist/src/ai_metrics/subscribe.d.ts.map +1 -0
- package/dist/src/ai_metrics/subscribe.js +38 -0
- package/dist/src/ai_metrics/subscribe.js.map +1 -0
- package/dist/src/alerts/index.d.ts +1 -1
- package/dist/src/alerts/index.js +1 -1
- package/dist/src/client_errors/ingestor.d.ts.map +1 -1
- package/dist/src/client_errors/ingestor.js +3 -2
- package/dist/src/client_errors/ingestor.js.map +1 -1
- package/dist/src/define_config.d.ts +72 -0
- package/dist/src/define_config.d.ts.map +1 -1
- package/dist/src/define_config.js +24 -4
- package/dist/src/define_config.js.map +1 -1
- package/dist/src/diagnostics_watcher.d.ts.map +1 -1
- package/dist/src/diagnostics_watcher.js +12 -4
- package/dist/src/diagnostics_watcher.js.map +1 -1
- package/dist/src/index.d.ts +2 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +2 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/mcp/index.d.ts +1 -1
- package/dist/src/mcp/index.js +1 -1
- package/dist/src/metrics/metrics_service.d.ts +22 -0
- package/dist/src/metrics/metrics_service.d.ts.map +1 -1
- package/dist/src/metrics/metrics_service.js +49 -0
- package/dist/src/metrics/metrics_service.js.map +1 -1
- package/dist/src/profiling/index.d.ts +1 -1
- package/dist/src/profiling/index.js +1 -1
- package/dist/src/registry.d.ts +9 -0
- package/dist/src/registry.d.ts.map +1 -1
- package/dist/src/registry.js +6 -0
- package/dist/src/registry.js.map +1 -1
- package/dist/src/report_failure.d.ts +31 -0
- package/dist/src/report_failure.d.ts.map +1 -0
- package/dist/src/report_failure.js +70 -0
- package/dist/src/report_failure.js.map +1 -0
- package/dist/src/stores/lucid.d.ts +13 -2
- package/dist/src/stores/lucid.d.ts.map +1 -1
- package/dist/src/stores/lucid.js +85 -12
- package/dist/src/stores/lucid.js.map +1 -1
- package/dist/src/telescope_middleware.d.ts.map +1 -1
- package/dist/src/telescope_middleware.js +3 -2
- package/dist/src/telescope_middleware.js.map +1 -1
- package/dist/src/ui/api.d.ts +6 -0
- package/dist/src/ui/api.d.ts.map +1 -1
- package/dist/src/ui/api.js +23 -0
- package/dist/src/ui/api.js.map +1 -1
- package/dist/src/ui/index.d.ts +1 -1
- package/dist/src/ui/index.js +1 -1
- package/dist/src/watchers/index.d.ts +1 -1
- package/dist/src/watchers/index.js +1 -1
- package/dist/src/watchers/record.d.ts.map +1 -1
- package/dist/src/watchers/record.js +4 -2
- package/dist/src/watchers/record.js.map +1 -1
- package/grafana/agora-ai-dashboard.json +813 -0
- package/package.json +14 -2
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure readers for the `agora:agent:*` events `@adonis-agora/agent` publishes through
|
|
3
|
+
* `@adonis-agora/diagnostics` (`usage.recorded` and the run timings from agent 0.93.0 on).
|
|
4
|
+
* Telescope cannot import the agent lib, so the payload shapes are read structurally here: every
|
|
5
|
+
* field is optional and type-checked, and an event from an older agent lib simply yields fewer
|
|
6
|
+
* fields, never a throw.
|
|
7
|
+
*
|
|
8
|
+
* Shared by the Prometheus registry, the OTLP metrics exporter and the dashboard's AI screen so the
|
|
9
|
+
* three can never disagree on what "a priced call" or "a run's model time" means.
|
|
10
|
+
*/
|
|
11
|
+
/** The channel prefix `@adonis-agora/diagnostics` publishes on (`agora:<lib>:<event>`). */
|
|
12
|
+
export const AGENT_CHANNEL_PREFIX = 'agora:agent:';
|
|
13
|
+
/** The agent events the metrics read. */
|
|
14
|
+
export const AGENT_METRIC_EVENTS = [
|
|
15
|
+
'usage.recorded',
|
|
16
|
+
'run.finished',
|
|
17
|
+
'run.failed',
|
|
18
|
+
'run.cancelled',
|
|
19
|
+
];
|
|
20
|
+
/** The `actorRef` the agent ledger uses for spend the platform pays for, not a user. */
|
|
21
|
+
export const PLATFORM_ACTOR_REF = '@platform';
|
|
22
|
+
function asRecord(value) {
|
|
23
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value)
|
|
24
|
+
? value
|
|
25
|
+
: null;
|
|
26
|
+
}
|
|
27
|
+
function num(value) {
|
|
28
|
+
return typeof value === 'number' && Number.isFinite(value) ? value : undefined;
|
|
29
|
+
}
|
|
30
|
+
function nonNegative(value) {
|
|
31
|
+
const n = num(value);
|
|
32
|
+
return n !== undefined && n > 0 ? n : 0;
|
|
33
|
+
}
|
|
34
|
+
function str(value) {
|
|
35
|
+
return typeof value === 'string' && value !== '' ? value : undefined;
|
|
36
|
+
}
|
|
37
|
+
/** Read a `usage.recorded` payload. `null` when it is not one. */
|
|
38
|
+
export function readUsage(payload) {
|
|
39
|
+
const p = asRecord(payload);
|
|
40
|
+
if (p === null)
|
|
41
|
+
return null;
|
|
42
|
+
const actorRef = str(p.actorRef) ?? 'unknown';
|
|
43
|
+
const costUsd = num(p.costUsd);
|
|
44
|
+
const declared = p.costSource === 'provider' || p.costSource === 'estimate' ? p.costSource : null;
|
|
45
|
+
// A cost with no declared source was priced by someone; the ledger's default is the provider.
|
|
46
|
+
const costSource = costUsd === undefined ? 'none' : (declared ?? 'provider');
|
|
47
|
+
const requests = num(p.requests);
|
|
48
|
+
const purpose = str(p.purpose);
|
|
49
|
+
const runId = str(p.runId);
|
|
50
|
+
const threadId = str(p.threadId);
|
|
51
|
+
return {
|
|
52
|
+
kind: 'usage',
|
|
53
|
+
actorRef,
|
|
54
|
+
feature: str(p.feature) ?? 'unknown',
|
|
55
|
+
model: str(p.modelId) ?? 'unknown',
|
|
56
|
+
provider: str(p.provider) ?? 'unknown',
|
|
57
|
+
costSource,
|
|
58
|
+
platform: actorRef === PLATFORM_ACTOR_REF,
|
|
59
|
+
...(costUsd !== undefined ? { costUsd } : {}),
|
|
60
|
+
inputTokens: nonNegative(p.inputTokens),
|
|
61
|
+
outputTokens: nonNegative(p.outputTokens),
|
|
62
|
+
cacheReadTokens: nonNegative(p.cacheReadTokens),
|
|
63
|
+
cacheWriteTokens: nonNegative(p.cacheWriteTokens),
|
|
64
|
+
audioSeconds: nonNegative(p.audioSeconds),
|
|
65
|
+
characters: nonNegative(p.characters),
|
|
66
|
+
requests: requests !== undefined && requests >= 0 ? requests : 1,
|
|
67
|
+
...(purpose !== undefined ? { purpose } : {}),
|
|
68
|
+
...(runId !== undefined ? { runId } : {}),
|
|
69
|
+
...(threadId !== undefined ? { threadId } : {}),
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
function readStepTimings(value) {
|
|
73
|
+
if (!Array.isArray(value))
|
|
74
|
+
return [];
|
|
75
|
+
const steps = [];
|
|
76
|
+
for (const raw of value) {
|
|
77
|
+
const s = asRecord(raw);
|
|
78
|
+
if (s === null)
|
|
79
|
+
continue;
|
|
80
|
+
const tools = [];
|
|
81
|
+
if (Array.isArray(s.tools)) {
|
|
82
|
+
for (const rawTool of s.tools) {
|
|
83
|
+
const t = asRecord(rawTool);
|
|
84
|
+
const ms = num(t?.ms);
|
|
85
|
+
if (t === null || ms === undefined)
|
|
86
|
+
continue;
|
|
87
|
+
tools.push({ toolName: str(t.toolName) ?? 'unknown', ms });
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
const modelMs = num(s.modelMs);
|
|
91
|
+
steps.push({
|
|
92
|
+
step: num(s.step) ?? steps.length,
|
|
93
|
+
...(modelMs !== undefined ? { modelMs } : {}),
|
|
94
|
+
toolsMs: num(s.toolsMs) ?? 0,
|
|
95
|
+
tools,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
return steps;
|
|
99
|
+
}
|
|
100
|
+
const RUN_STATUS = {
|
|
101
|
+
'run.finished': 'completed',
|
|
102
|
+
'run.failed': 'failed',
|
|
103
|
+
'run.cancelled': 'cancelled',
|
|
104
|
+
};
|
|
105
|
+
/**
|
|
106
|
+
* Read a terminal run payload. The run context (`agentName`, `actorId`) rides the payload's top
|
|
107
|
+
* level; a nested `context` object is read as a fallback so a payload shaped either way parses.
|
|
108
|
+
*/
|
|
109
|
+
export function readRun(event, payload) {
|
|
110
|
+
const status = RUN_STATUS[event];
|
|
111
|
+
const p = asRecord(payload);
|
|
112
|
+
if (status === undefined || p === null)
|
|
113
|
+
return null;
|
|
114
|
+
const context = asRecord(p.context) ?? {};
|
|
115
|
+
const pick = (key) => p[key] ?? context[key];
|
|
116
|
+
const out = {
|
|
117
|
+
kind: 'run',
|
|
118
|
+
status,
|
|
119
|
+
agent: str(pick('agentName')) ?? 'default',
|
|
120
|
+
stepTimings: readStepTimings(p.stepTimings),
|
|
121
|
+
};
|
|
122
|
+
const strings = ['runId', 'threadId', 'actorId', 'modelId', 'code'];
|
|
123
|
+
for (const key of strings) {
|
|
124
|
+
const value = str(pick(key));
|
|
125
|
+
if (value !== undefined)
|
|
126
|
+
out[key] = value;
|
|
127
|
+
}
|
|
128
|
+
const numbers = [
|
|
129
|
+
'durationMs',
|
|
130
|
+
'ttftMs',
|
|
131
|
+
'queueMs',
|
|
132
|
+
'modelMs',
|
|
133
|
+
'toolsMs',
|
|
134
|
+
'steps',
|
|
135
|
+
'modelCalls',
|
|
136
|
+
'toolCalls',
|
|
137
|
+
'inputTokens',
|
|
138
|
+
'outputTokens',
|
|
139
|
+
'costUsd',
|
|
140
|
+
];
|
|
141
|
+
for (const key of numbers) {
|
|
142
|
+
const value = num(pick(key));
|
|
143
|
+
if (value !== undefined)
|
|
144
|
+
out[key] = value;
|
|
145
|
+
}
|
|
146
|
+
return out;
|
|
147
|
+
}
|
|
148
|
+
/** Read any agent event the metrics care about; `null` for every other event. */
|
|
149
|
+
export function readAgentEvent(event, payload) {
|
|
150
|
+
if (event === 'usage.recorded')
|
|
151
|
+
return readUsage(payload);
|
|
152
|
+
return readRun(event, payload);
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* The agent event a stored Telescope entry carries: the generic diagnostics watcher records every
|
|
156
|
+
* `agora:agent:<event>` publish as `type: 'diagnostic'` with `content: { lib: 'agent', event,
|
|
157
|
+
* payload }`. `null` for any other entry.
|
|
158
|
+
*/
|
|
159
|
+
export function agentEventOfEntry(entry) {
|
|
160
|
+
if (entry.type !== 'diagnostic')
|
|
161
|
+
return null;
|
|
162
|
+
const content = asRecord(entry.content);
|
|
163
|
+
if (content === null || content.lib !== 'agent')
|
|
164
|
+
return null;
|
|
165
|
+
const event = str(content.event);
|
|
166
|
+
if (event === undefined)
|
|
167
|
+
return null;
|
|
168
|
+
return { event, payload: content.payload };
|
|
169
|
+
}
|
|
170
|
+
//# sourceMappingURL=agent_events.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent_events.js","sourceRoot":"","sources":["../../../src/ai_metrics/agent_events.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import type { AgentObservation } from './agent_events.js';
|
|
2
|
+
/**
|
|
3
|
+
* The AI-usage and agent-run metrics, as ONE table both exporters read: the dependency-free
|
|
4
|
+
* Prometheus registry below and the OTLP exporter in `otel_metrics_exporter.ts`. Names are
|
|
5
|
+
* stable API — dashboards are built on them.
|
|
6
|
+
*
|
|
7
|
+
* Units live in the NAME (`_usd`, `_ms`, `_seconds`), and the OTLP instruments are created with an
|
|
8
|
+
* EMPTY unit on purpose: Prometheus' OTLP translation appends a unit suffix (`ms` → `_milliseconds`),
|
|
9
|
+
* which would make the same metric read `agora_agent_run_duration_ms_milliseconds` through a
|
|
10
|
+
* Collector and `agora_agent_run_duration_ms` from the scrape. With no unit both paths expose the
|
|
11
|
+
* same series, so one Grafana dashboard serves both.
|
|
12
|
+
*
|
|
13
|
+
* Cardinality: attributes are features, models, providers, agent and tool names only. `actorRef`,
|
|
14
|
+
* `runId` and `threadId` NEVER become attributes — they grow with users and runs.
|
|
15
|
+
*/
|
|
16
|
+
export declare const AGENT_METRICS: {
|
|
17
|
+
readonly costUsd: {
|
|
18
|
+
readonly name: 'agora_ai_cost_usd';
|
|
19
|
+
readonly type: 'counter';
|
|
20
|
+
readonly help: 'AI spend in US dollars, from usage.recorded rows that carried a cost.';
|
|
21
|
+
};
|
|
22
|
+
readonly tokens: {
|
|
23
|
+
readonly name: 'agora_ai_tokens';
|
|
24
|
+
readonly type: 'counter';
|
|
25
|
+
readonly help: 'AI tokens, by direction (in, out, cache_read, cache_write).';
|
|
26
|
+
};
|
|
27
|
+
readonly audioSeconds: {
|
|
28
|
+
readonly name: 'agora_ai_audio_seconds';
|
|
29
|
+
readonly type: 'counter';
|
|
30
|
+
readonly help: 'Seconds of audio processed by AI calls.';
|
|
31
|
+
};
|
|
32
|
+
readonly requests: {
|
|
33
|
+
readonly name: 'agora_ai_requests';
|
|
34
|
+
readonly type: 'counter';
|
|
35
|
+
readonly help: 'AI calls recorded in the usage ledger; cost_source="none" are the unpriced ones.';
|
|
36
|
+
};
|
|
37
|
+
readonly runDurationMs: {
|
|
38
|
+
readonly name: 'agora_agent_run_duration_ms';
|
|
39
|
+
readonly type: 'histogram';
|
|
40
|
+
readonly help: 'Agent run wall-clock duration in milliseconds, turn start to end.';
|
|
41
|
+
};
|
|
42
|
+
readonly runQueueMs: {
|
|
43
|
+
readonly name: 'agora_agent_run_queue_ms';
|
|
44
|
+
readonly type: 'histogram';
|
|
45
|
+
readonly help: 'Agent run time between enqueue and its body starting, in milliseconds.';
|
|
46
|
+
};
|
|
47
|
+
readonly runModelMs: {
|
|
48
|
+
readonly name: 'agora_agent_run_model_ms';
|
|
49
|
+
readonly type: 'histogram';
|
|
50
|
+
readonly help: 'Agent run time spent in model calls, in milliseconds.';
|
|
51
|
+
};
|
|
52
|
+
readonly runToolsMs: {
|
|
53
|
+
readonly name: 'agora_agent_run_tools_ms';
|
|
54
|
+
readonly type: 'histogram';
|
|
55
|
+
readonly help: 'Agent run time spent running tools (parallel calls once), in milliseconds.';
|
|
56
|
+
};
|
|
57
|
+
readonly toolDurationMs: {
|
|
58
|
+
readonly name: 'agora_agent_tool_duration_ms';
|
|
59
|
+
readonly type: 'histogram';
|
|
60
|
+
readonly help: 'One tool call duration, in milliseconds.';
|
|
61
|
+
};
|
|
62
|
+
};
|
|
63
|
+
export type AgentMetricKey = keyof typeof AGENT_METRICS;
|
|
64
|
+
/** Histogram upper bounds (ms), shared by the scrape and the OTLP instruments. */
|
|
65
|
+
export declare const AGENT_METRIC_BUCKETS_MS: readonly number[];
|
|
66
|
+
/** Attribute values are strings, except `platform`, which OTLP carries as a boolean. */
|
|
67
|
+
export type MetricAttributes = Record<string, string | boolean>;
|
|
68
|
+
/** One value an observation contributes to one metric. */
|
|
69
|
+
export interface MetricPoint {
|
|
70
|
+
metric: AgentMetricKey;
|
|
71
|
+
value: number;
|
|
72
|
+
attributes: MetricAttributes;
|
|
73
|
+
}
|
|
74
|
+
/** Every metric point an observation contributes. Pure. */
|
|
75
|
+
export declare function agentMetricPoints(observation: AgentObservation): MetricPoint[];
|
|
76
|
+
export interface AgentMetricsRegistryOptions {
|
|
77
|
+
/**
|
|
78
|
+
* Max distinct label sets kept per metric. New combinations past it are dropped (existing ones
|
|
79
|
+
* keep counting); every observation dropped that way is tallied in
|
|
80
|
+
* `agora_ai_metrics_dropped_series_total`. Default 2000.
|
|
81
|
+
*/
|
|
82
|
+
maxSeriesPerMetric?: number;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Dependency-free, in-process aggregation of {@link AGENT_METRICS} for the Prometheus scrape
|
|
86
|
+
* (`GET <telescope path>/metrics`). Cumulative since process start, as Prometheus expects.
|
|
87
|
+
*/
|
|
88
|
+
export declare class AgentMetricsRegistry {
|
|
89
|
+
private readonly counters;
|
|
90
|
+
private readonly histograms;
|
|
91
|
+
private readonly dropped;
|
|
92
|
+
private readonly maxSeries;
|
|
93
|
+
constructor(options?: AgentMetricsRegistryOptions);
|
|
94
|
+
observe(observation: AgentObservation): void;
|
|
95
|
+
add(point: MetricPoint): void;
|
|
96
|
+
private drop;
|
|
97
|
+
/**
|
|
98
|
+
* Prometheus text exposition (format 0.0.4). Counters are exposed with the `_total` suffix —
|
|
99
|
+
* the same name Prometheus gives an OTLP monotonic sum — so the shipped dashboard reads either.
|
|
100
|
+
*/
|
|
101
|
+
prometheus(): string;
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=agent_metrics.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent_metrics.d.ts","sourceRoot":"","sources":["../../../src/ai_metrics/agent_metrics.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAoC,MAAM,mBAAmB,CAAC;AAE5F;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,aAAa;aACxB,OAAO;iBACL,IAAI,EAAE,mBAAmB;iBACzB,IAAI,EAAE,SAAS;iBACf,IAAI,EAAE,uEAAuE;;aAE/E,MAAM;iBACJ,IAAI,EAAE,iBAAiB;iBACvB,IAAI,EAAE,SAAS;iBACf,IAAI,EAAE,6DAA6D;;aAErE,YAAY;iBACV,IAAI,EAAE,wBAAwB;iBAC9B,IAAI,EAAE,SAAS;iBACf,IAAI,EAAE,yCAAyC;;aAEjD,QAAQ;iBACN,IAAI,EAAE,mBAAmB;iBACzB,IAAI,EAAE,SAAS;iBACf,IAAI,EAAE,kFAAkF;;aAE1F,aAAa;iBACX,IAAI,EAAE,6BAA6B;iBACnC,IAAI,EAAE,WAAW;iBACjB,IAAI,EAAE,mEAAmE;;aAE3E,UAAU;iBACR,IAAI,EAAE,0BAA0B;iBAChC,IAAI,EAAE,WAAW;iBACjB,IAAI,EAAE,wEAAwE;;aAEhF,UAAU;iBACR,IAAI,EAAE,0BAA0B;iBAChC,IAAI,EAAE,WAAW;iBACjB,IAAI,EAAE,uDAAuD;;aAE/D,UAAU;iBACR,IAAI,EAAE,0BAA0B;iBAChC,IAAI,EAAE,WAAW;iBACjB,IAAI,EAAE,4EAA4E;;aAEpF,cAAc;iBACZ,IAAI,EAAE,8BAA8B;iBACpC,IAAI,EAAE,WAAW;iBACjB,IAAI,EAAE,0CAA0C;;CAE1C,CAAC;AAEX,MAAM,MAAM,cAAc,GAAG,MAAM,OAAO,aAAa,CAAC;AAExD,kFAAkF;AAClF,eAAO,MAAM,uBAAuB,EAAE,SAAS,MAAM,EAEpD,CAAC;AAEF,wFAAwF;AACxF,MAAM,MAAM,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC;AAEhE,0DAA0D;AAC1D,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,cAAc,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,gBAAgB,CAAC;CAC9B;AA4DD,2DAA2D;AAC3D,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,gBAAgB,GAAG,WAAW,EAAE,CAE9E;AAcD,MAAM,WAAW,2BAA2B;IAC1C;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED;;;GAGG;AACH,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAyD;IAClF,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA2D;IACtF,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA6B;IACrD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IAEnC,YAAY,OAAO,GAAE,2BAAgC,EAEpD;IAED,OAAO,CAAC,WAAW,EAAE,gBAAgB,GAAG,IAAI,CAE3C;IAED,GAAG,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI,CAsC5B;IAED,OAAO,CAAC,IAAI;IAIZ;;;OAGG;IACH,UAAU,IAAI,MAAM,CAyCnB;CACF"}
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The AI-usage and agent-run metrics, as ONE table both exporters read: the dependency-free
|
|
3
|
+
* Prometheus registry below and the OTLP exporter in `otel_metrics_exporter.ts`. Names are
|
|
4
|
+
* stable API — dashboards are built on them.
|
|
5
|
+
*
|
|
6
|
+
* Units live in the NAME (`_usd`, `_ms`, `_seconds`), and the OTLP instruments are created with an
|
|
7
|
+
* EMPTY unit on purpose: Prometheus' OTLP translation appends a unit suffix (`ms` → `_milliseconds`),
|
|
8
|
+
* which would make the same metric read `agora_agent_run_duration_ms_milliseconds` through a
|
|
9
|
+
* Collector and `agora_agent_run_duration_ms` from the scrape. With no unit both paths expose the
|
|
10
|
+
* same series, so one Grafana dashboard serves both.
|
|
11
|
+
*
|
|
12
|
+
* Cardinality: attributes are features, models, providers, agent and tool names only. `actorRef`,
|
|
13
|
+
* `runId` and `threadId` NEVER become attributes — they grow with users and runs.
|
|
14
|
+
*/
|
|
15
|
+
export const AGENT_METRICS = {
|
|
16
|
+
costUsd: {
|
|
17
|
+
name: 'agora_ai_cost_usd',
|
|
18
|
+
type: 'counter',
|
|
19
|
+
help: 'AI spend in US dollars, from usage.recorded rows that carried a cost.',
|
|
20
|
+
},
|
|
21
|
+
tokens: {
|
|
22
|
+
name: 'agora_ai_tokens',
|
|
23
|
+
type: 'counter',
|
|
24
|
+
help: 'AI tokens, by direction (in, out, cache_read, cache_write).',
|
|
25
|
+
},
|
|
26
|
+
audioSeconds: {
|
|
27
|
+
name: 'agora_ai_audio_seconds',
|
|
28
|
+
type: 'counter',
|
|
29
|
+
help: 'Seconds of audio processed by AI calls.',
|
|
30
|
+
},
|
|
31
|
+
requests: {
|
|
32
|
+
name: 'agora_ai_requests',
|
|
33
|
+
type: 'counter',
|
|
34
|
+
help: 'AI calls recorded in the usage ledger; cost_source="none" are the unpriced ones.',
|
|
35
|
+
},
|
|
36
|
+
runDurationMs: {
|
|
37
|
+
name: 'agora_agent_run_duration_ms',
|
|
38
|
+
type: 'histogram',
|
|
39
|
+
help: 'Agent run wall-clock duration in milliseconds, turn start to end.',
|
|
40
|
+
},
|
|
41
|
+
runQueueMs: {
|
|
42
|
+
name: 'agora_agent_run_queue_ms',
|
|
43
|
+
type: 'histogram',
|
|
44
|
+
help: 'Agent run time between enqueue and its body starting, in milliseconds.',
|
|
45
|
+
},
|
|
46
|
+
runModelMs: {
|
|
47
|
+
name: 'agora_agent_run_model_ms',
|
|
48
|
+
type: 'histogram',
|
|
49
|
+
help: 'Agent run time spent in model calls, in milliseconds.',
|
|
50
|
+
},
|
|
51
|
+
runToolsMs: {
|
|
52
|
+
name: 'agora_agent_run_tools_ms',
|
|
53
|
+
type: 'histogram',
|
|
54
|
+
help: 'Agent run time spent running tools (parallel calls once), in milliseconds.',
|
|
55
|
+
},
|
|
56
|
+
toolDurationMs: {
|
|
57
|
+
name: 'agora_agent_tool_duration_ms',
|
|
58
|
+
type: 'histogram',
|
|
59
|
+
help: 'One tool call duration, in milliseconds.',
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
/** Histogram upper bounds (ms), shared by the scrape and the OTLP instruments. */
|
|
63
|
+
export const AGENT_METRIC_BUCKETS_MS = [
|
|
64
|
+
10, 25, 50, 100, 250, 500, 1000, 2500, 5000, 10000, 20000, 30000, 60000, 120000, 300000, 600000,
|
|
65
|
+
];
|
|
66
|
+
function usagePoints(u) {
|
|
67
|
+
const common = {
|
|
68
|
+
feature: u.feature,
|
|
69
|
+
model: u.model,
|
|
70
|
+
provider: u.provider,
|
|
71
|
+
cost_source: u.costSource,
|
|
72
|
+
platform: u.platform,
|
|
73
|
+
};
|
|
74
|
+
const points = [{ metric: 'requests', value: u.requests, attributes: common }];
|
|
75
|
+
if (u.costUsd !== undefined)
|
|
76
|
+
points.push({ metric: 'costUsd', value: u.costUsd, attributes: common });
|
|
77
|
+
if (u.audioSeconds > 0) {
|
|
78
|
+
points.push({ metric: 'audioSeconds', value: u.audioSeconds, attributes: common });
|
|
79
|
+
}
|
|
80
|
+
const tokens = [
|
|
81
|
+
['in', u.inputTokens],
|
|
82
|
+
['out', u.outputTokens],
|
|
83
|
+
['cache_read', u.cacheReadTokens],
|
|
84
|
+
['cache_write', u.cacheWriteTokens],
|
|
85
|
+
];
|
|
86
|
+
for (const [direction, value] of tokens) {
|
|
87
|
+
if (value > 0) {
|
|
88
|
+
points.push({
|
|
89
|
+
metric: 'tokens',
|
|
90
|
+
value,
|
|
91
|
+
attributes: { direction, feature: u.feature, model: u.model },
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return points;
|
|
96
|
+
}
|
|
97
|
+
function runPoints(r) {
|
|
98
|
+
const attributes = { agent: r.agent, status: r.status };
|
|
99
|
+
const points = [];
|
|
100
|
+
const timed = [
|
|
101
|
+
['runDurationMs', r.durationMs],
|
|
102
|
+
['runQueueMs', r.queueMs],
|
|
103
|
+
['runModelMs', r.modelMs],
|
|
104
|
+
['runToolsMs', r.toolsMs],
|
|
105
|
+
];
|
|
106
|
+
for (const [metric, value] of timed) {
|
|
107
|
+
if (value !== undefined && value >= 0)
|
|
108
|
+
points.push({ metric, value, attributes });
|
|
109
|
+
}
|
|
110
|
+
for (const step of r.stepTimings) {
|
|
111
|
+
for (const tool of step.tools) {
|
|
112
|
+
if (tool.ms >= 0) {
|
|
113
|
+
points.push({
|
|
114
|
+
metric: 'toolDurationMs',
|
|
115
|
+
value: tool.ms,
|
|
116
|
+
attributes: { tool: tool.toolName },
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return points;
|
|
122
|
+
}
|
|
123
|
+
/** Every metric point an observation contributes. Pure. */
|
|
124
|
+
export function agentMetricPoints(observation) {
|
|
125
|
+
return observation.kind === 'usage' ? usagePoints(observation) : runPoints(observation);
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Dependency-free, in-process aggregation of {@link AGENT_METRICS} for the Prometheus scrape
|
|
129
|
+
* (`GET <telescope path>/metrics`). Cumulative since process start, as Prometheus expects.
|
|
130
|
+
*/
|
|
131
|
+
export class AgentMetricsRegistry {
|
|
132
|
+
counters = new Map();
|
|
133
|
+
histograms = new Map();
|
|
134
|
+
dropped = new Map();
|
|
135
|
+
maxSeries;
|
|
136
|
+
constructor(options = {}) {
|
|
137
|
+
this.maxSeries = options.maxSeriesPerMetric ?? 2000;
|
|
138
|
+
}
|
|
139
|
+
observe(observation) {
|
|
140
|
+
for (const point of agentMetricPoints(observation))
|
|
141
|
+
this.add(point);
|
|
142
|
+
}
|
|
143
|
+
add(point) {
|
|
144
|
+
const def = AGENT_METRICS[point.metric];
|
|
145
|
+
const key = labelKey(point.attributes);
|
|
146
|
+
if (def.type === 'counter') {
|
|
147
|
+
const series = getOrCreate(this.counters, point.metric);
|
|
148
|
+
let cell = series.get(key);
|
|
149
|
+
if (cell === undefined) {
|
|
150
|
+
if (series.size >= this.maxSeries) {
|
|
151
|
+
// The exposed name, so the drop joins with the series it describes.
|
|
152
|
+
this.drop(`${def.name}_total`);
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
cell = { labels: point.attributes, value: 0 };
|
|
156
|
+
series.set(key, cell);
|
|
157
|
+
}
|
|
158
|
+
cell.value += point.value;
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
const series = getOrCreate(this.histograms, point.metric);
|
|
162
|
+
let cell = series.get(key);
|
|
163
|
+
if (cell === undefined) {
|
|
164
|
+
if (series.size >= this.maxSeries) {
|
|
165
|
+
this.drop(def.name);
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
cell = {
|
|
169
|
+
labels: point.attributes,
|
|
170
|
+
sum: 0,
|
|
171
|
+
count: 0,
|
|
172
|
+
buckets: new Array(AGENT_METRIC_BUCKETS_MS.length).fill(0),
|
|
173
|
+
};
|
|
174
|
+
series.set(key, cell);
|
|
175
|
+
}
|
|
176
|
+
cell.sum += point.value;
|
|
177
|
+
cell.count += 1;
|
|
178
|
+
AGENT_METRIC_BUCKETS_MS.forEach((bound, i) => {
|
|
179
|
+
if (point.value <= bound)
|
|
180
|
+
cell.buckets[i] = (cell.buckets[i] ?? 0) + 1;
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
drop(metric) {
|
|
184
|
+
this.dropped.set(metric, (this.dropped.get(metric) ?? 0) + 1);
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Prometheus text exposition (format 0.0.4). Counters are exposed with the `_total` suffix —
|
|
188
|
+
* the same name Prometheus gives an OTLP monotonic sum — so the shipped dashboard reads either.
|
|
189
|
+
*/
|
|
190
|
+
prometheus() {
|
|
191
|
+
const lines = [];
|
|
192
|
+
for (const key of Object.keys(AGENT_METRICS)) {
|
|
193
|
+
const def = AGENT_METRICS[key];
|
|
194
|
+
if (def.type === 'counter') {
|
|
195
|
+
const series = this.counters.get(key);
|
|
196
|
+
if (series === undefined || series.size === 0)
|
|
197
|
+
continue;
|
|
198
|
+
const name = `${def.name}_total`;
|
|
199
|
+
lines.push(`# HELP ${name} ${def.help}`, `# TYPE ${name} counter`);
|
|
200
|
+
for (const cell of series.values()) {
|
|
201
|
+
lines.push(`${name}${formatLabels(cell.labels)} ${formatNumber(cell.value)}`);
|
|
202
|
+
}
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
const series = this.histograms.get(key);
|
|
206
|
+
if (series === undefined || series.size === 0)
|
|
207
|
+
continue;
|
|
208
|
+
lines.push(`# HELP ${def.name} ${def.help}`, `# TYPE ${def.name} histogram`);
|
|
209
|
+
for (const cell of series.values()) {
|
|
210
|
+
AGENT_METRIC_BUCKETS_MS.forEach((bound, i) => {
|
|
211
|
+
lines.push(`${def.name}_bucket${formatLabels({ ...cell.labels, le: String(bound) })} ${cell.buckets[i] ?? 0}`);
|
|
212
|
+
});
|
|
213
|
+
lines.push(`${def.name}_bucket${formatLabels({ ...cell.labels, le: '+Inf' })} ${cell.count}`);
|
|
214
|
+
lines.push(`${def.name}_sum${formatLabels(cell.labels)} ${formatNumber(cell.sum)}`);
|
|
215
|
+
lines.push(`${def.name}_count${formatLabels(cell.labels)} ${cell.count}`);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
if (this.dropped.size > 0) {
|
|
219
|
+
const name = 'agora_ai_metrics_dropped_series_total';
|
|
220
|
+
lines.push(`# HELP ${name} Observations dropped because their label set was new and the metric was at its series cap.`, `# TYPE ${name} counter`);
|
|
221
|
+
for (const [metric, count] of this.dropped) {
|
|
222
|
+
lines.push(`${name}${formatLabels({ metric })} ${count}`);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return lines.length === 0 ? '' : `${lines.join('\n')}\n`;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
function getOrCreate(map, key) {
|
|
229
|
+
let inner = map.get(key);
|
|
230
|
+
if (inner === undefined) {
|
|
231
|
+
inner = new Map();
|
|
232
|
+
map.set(key, inner);
|
|
233
|
+
}
|
|
234
|
+
return inner;
|
|
235
|
+
}
|
|
236
|
+
function labelKey(labels) {
|
|
237
|
+
return Object.keys(labels)
|
|
238
|
+
.sort()
|
|
239
|
+
.map((k) => `${k}=${String(labels[k])}`)
|
|
240
|
+
.join('\u0000');
|
|
241
|
+
}
|
|
242
|
+
function escapeLabel(value) {
|
|
243
|
+
return value.replace(/\\/g, '\\\\').replace(/\n/g, '\\n').replace(/"/g, '\\"');
|
|
244
|
+
}
|
|
245
|
+
function formatLabels(labels) {
|
|
246
|
+
const keys = Object.keys(labels);
|
|
247
|
+
if (keys.length === 0)
|
|
248
|
+
return '';
|
|
249
|
+
return `{${keys.map((k) => `${k}="${escapeLabel(String(labels[k]))}"`).join(',')}}`;
|
|
250
|
+
}
|
|
251
|
+
function formatNumber(value) {
|
|
252
|
+
// Avoid float noise like 0.30000000000000004 in the exposition.
|
|
253
|
+
return Number.isInteger(value) ? String(value) : String(Number(value.toPrecision(12)));
|
|
254
|
+
}
|
|
255
|
+
//# sourceMappingURL=agent_metrics.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent_metrics.js","sourceRoot":"","sources":["../../../src/ai_metrics/agent_metrics.ts"],"names":[],"mappings":"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"}
|