toad-eye 2.4.5 → 2.6.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.
Files changed (50) hide show
  1. package/README.md +34 -1
  2. package/dist/agent.js +6 -2
  3. package/dist/cli.js +8 -1
  4. package/dist/context/error.d.ts +15 -0
  5. package/dist/context/error.js +17 -0
  6. package/dist/context/guard.d.ts +8 -0
  7. package/dist/context/guard.js +41 -0
  8. package/dist/context/index.d.ts +1 -0
  9. package/dist/context/index.js +4 -0
  10. package/dist/core/metrics.d.ts +4 -1
  11. package/dist/core/metrics.js +74 -2
  12. package/dist/core/pricing.d.ts +2 -0
  13. package/dist/core/pricing.js +61 -16
  14. package/dist/core/spans.d.ts +2 -0
  15. package/dist/core/spans.js +48 -9
  16. package/dist/core/tracer.js +7 -1
  17. package/dist/instrumentations/anthropic.js +8 -2
  18. package/dist/instrumentations/create.js +67 -8
  19. package/dist/instrumentations/gemini.js +7 -0
  20. package/dist/instrumentations/openai.js +5 -0
  21. package/dist/instrumentations/types.d.ts +8 -0
  22. package/dist/mcp/context.d.ts +12 -0
  23. package/dist/mcp/context.js +22 -0
  24. package/dist/mcp/index.d.ts +3 -0
  25. package/dist/mcp/index.js +1 -0
  26. package/dist/mcp/metrics.d.ts +11 -0
  27. package/dist/mcp/metrics.js +58 -0
  28. package/dist/mcp/middleware.d.ts +48 -0
  29. package/dist/mcp/middleware.js +253 -0
  30. package/dist/mcp/spans.d.ts +28 -0
  31. package/dist/mcp/spans.js +67 -0
  32. package/dist/mcp/types.d.ts +34 -0
  33. package/dist/mcp/types.js +1 -0
  34. package/dist/types/attributes.d.ts +1 -0
  35. package/dist/types/attributes.js +1 -0
  36. package/dist/types/config.d.ts +9 -0
  37. package/dist/types/metrics.d.ts +8 -0
  38. package/dist/types/metrics.js +10 -0
  39. package/dist/types/spans.d.ts +4 -0
  40. package/package.json +11 -2
  41. package/templates/docker-compose.yml +4 -4
  42. package/templates/grafana/dashboards/agent-workflow.json +154 -31
  43. package/templates/grafana/dashboards/cost-breakdown.json +168 -40
  44. package/templates/grafana/dashboards/error-drilldown.json +155 -37
  45. package/templates/grafana/dashboards/finops-attribution.json +192 -43
  46. package/templates/grafana/dashboards/latency-analysis.json +103 -24
  47. package/templates/grafana/dashboards/llm-overview.json +268 -66
  48. package/templates/grafana/dashboards/mcp-server.json +519 -0
  49. package/templates/grafana/dashboards/model-comparison.json +119 -26
  50. package/templates/grafana/dashboards/provider-health.json +137 -31
@@ -0,0 +1,67 @@
1
+ /**
2
+ * OTel span creation for MCP operations.
3
+ *
4
+ * Maps MCP methods to OTel GenAI semantic conventions:
5
+ * - tools/call → execute_tool {tool_name}
6
+ * - resources/read → retrieval {uri}
7
+ * - prompts/get → prompt {name}
8
+ * - sampling/createMessage → chat {model}
9
+ */
10
+ import { trace, SpanStatusCode, SpanKind, } from "@opentelemetry/api";
11
+ const TRACER_NAME = "toad-eye-mcp";
12
+ const tracer = trace.getTracer(TRACER_NAME);
13
+ export function startToolSpan(toolName, options) {
14
+ return tracer.startSpan(`execute_tool ${toolName}`, {
15
+ kind: SpanKind.INTERNAL,
16
+ attributes: {
17
+ "gen_ai.operation.name": "execute_tool",
18
+ "gen_ai.tool.name": toolName,
19
+ "mcp.server.name": options.serverName,
20
+ "mcp.server.version": options.serverVersion,
21
+ },
22
+ }, options.parentContext);
23
+ }
24
+ export function startResourceSpan(uri, options) {
25
+ return tracer.startSpan(`retrieval ${uri}`, {
26
+ kind: SpanKind.INTERNAL,
27
+ attributes: {
28
+ "gen_ai.operation.name": "retrieval",
29
+ "gen_ai.data_source.id": uri,
30
+ "mcp.server.name": options.serverName,
31
+ "mcp.server.version": options.serverVersion,
32
+ },
33
+ }, options.parentContext);
34
+ }
35
+ export function startPromptSpan(promptName, options) {
36
+ return tracer.startSpan(`prompt ${promptName}`, {
37
+ kind: SpanKind.INTERNAL,
38
+ attributes: {
39
+ "gen_ai.operation.name": "prompt",
40
+ "gen_ai.prompt.name": promptName,
41
+ "mcp.server.name": options.serverName,
42
+ "mcp.server.version": options.serverVersion,
43
+ },
44
+ }, options.parentContext);
45
+ }
46
+ export function startSamplingSpan(model, options) {
47
+ return tracer.startSpan(`chat ${model}`, {
48
+ kind: SpanKind.CLIENT,
49
+ attributes: {
50
+ "gen_ai.operation.name": "chat",
51
+ "gen_ai.request.model": model,
52
+ "mcp.server.name": options.serverName,
53
+ "mcp.server.version": options.serverVersion,
54
+ },
55
+ }, options.parentContext);
56
+ }
57
+ export function endSpanSuccess(span) {
58
+ span.setStatus({ code: SpanStatusCode.OK });
59
+ span.end();
60
+ }
61
+ export function endSpanError(span, error) {
62
+ const message = error instanceof Error ? error.message : String(error);
63
+ const errorType = error instanceof Error ? error.constructor.name : "UnknownError";
64
+ span.setStatus({ code: SpanStatusCode.ERROR, message });
65
+ span.setAttribute("error.type", errorType);
66
+ span.end();
67
+ }
@@ -0,0 +1,34 @@
1
+ import type { Span } from "@opentelemetry/api";
2
+ /** Configuration for toadEyeMiddleware(). */
3
+ export interface ToadMcpOptions {
4
+ /** Service name for OTel resource. Defaults to MCP server name. */
5
+ readonly serviceName?: string | undefined;
6
+ /** OTLP endpoint. Uses existing OTel config if initObservability() was called. */
7
+ readonly endpoint?: string | undefined;
8
+ /** Record tool call arguments in spans. Default: false (privacy). */
9
+ readonly recordInputs?: boolean | undefined;
10
+ /** Record tool call results in spans. Default: false (privacy). */
11
+ readonly recordOutputs?: boolean | undefined;
12
+ /** Keys to redact from recorded arguments. */
13
+ readonly redactKeys?: readonly string[] | undefined;
14
+ /** Max payload size in bytes before truncation. Default: 4096. */
15
+ readonly maxPayloadSize?: number | undefined;
16
+ /** Extract W3C traceparent from _meta field. Default: true. */
17
+ readonly propagateContext?: boolean | undefined;
18
+ /** Hook called on each tool call span. Add custom attributes here. */
19
+ readonly onToolCall?: ((span: Span, toolName: string, args: unknown) => void) | undefined;
20
+ /** Hook called on each resource read span. */
21
+ readonly onResourceRead?: ((span: Span, uri: string) => void) | undefined;
22
+ }
23
+ /** OTel attributes set on MCP spans. */
24
+ export interface McpSpanAttributes {
25
+ readonly "gen_ai.operation.name": string;
26
+ readonly "gen_ai.tool.name"?: string;
27
+ readonly "gen_ai.tool.call.id"?: string;
28
+ readonly "gen_ai.data_source.id"?: string;
29
+ readonly "gen_ai.prompt.name"?: string;
30
+ readonly "mcp.server.name": string;
31
+ readonly "mcp.server.version": string;
32
+ readonly "mcp.transport"?: "stdio" | "sse" | "streamable-http";
33
+ readonly "mcp.session.id"?: string;
34
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -47,6 +47,7 @@ export declare const GEN_AI_ATTRS: {
47
47
  readonly COMPLETION: "gen_ai.toad_eye.completion";
48
48
  readonly COST: "gen_ai.toad_eye.cost";
49
49
  readonly STATUS: "gen_ai.toad_eye.status";
50
+ readonly CONTEXT_UTILIZATION: "gen_ai.toad_eye.context_utilization";
50
51
  readonly SESSION_ID: "session.id";
51
52
  readonly TEAM: "toad_eye.team";
52
53
  readonly USER_ID: "toad_eye.user_id";
@@ -53,6 +53,7 @@ export const GEN_AI_ATTRS = {
53
53
  COMPLETION: "gen_ai.toad_eye.completion",
54
54
  COST: "gen_ai.toad_eye.cost",
55
55
  STATUS: "gen_ai.toad_eye.status",
56
+ CONTEXT_UTILIZATION: "gen_ai.toad_eye.context_utilization",
56
57
  SESSION_ID: "session.id",
57
58
  // FinOps attribution
58
59
  TEAM: "toad_eye.team",
@@ -38,6 +38,8 @@ export interface ToadEyeConfig {
38
38
  readonly redactDefaults?: boolean | undefined;
39
39
  /** Log what was masked to console (for debugging redaction config). No data is sent externally. */
40
40
  readonly auditMasking?: boolean | undefined;
41
+ /** Record content for only this fraction of calls (0.0–1.0). E.g., 0.01 = 1%. Enables debugging without full storage/privacy cost. */
42
+ readonly contentSamplingRate?: number | undefined;
41
43
  /** Configure trace sampling. Tail sampling happens in OTel Collector, not SDK. */
42
44
  readonly sampling?: SamplingConfig | undefined;
43
45
  readonly instrument?: readonly InstrumentTarget[] | undefined;
@@ -47,6 +49,13 @@ export interface ToadEyeConfig {
47
49
  readonly onBudgetExceeded?: BudgetExceededMode | undefined;
48
50
  /** Callback for 'downgrade' mode — receives original provider/model, returns replacement. */
49
51
  readonly downgradeCallback?: DowngradeCallback | undefined;
52
+ /** Warn when context window utilization exceeds thresholds (post-call). */
53
+ readonly contextGuard?: {
54
+ /** Warn when utilization exceeds this ratio (0.0–1.0). Default: none. */
55
+ readonly warnAt?: number | undefined;
56
+ /** Alert (span event + metric + warning) when utilization exceeds this ratio. Default: none. */
57
+ readonly alertAt?: number | undefined;
58
+ } | undefined;
50
59
  /** Static session ID — all spans will carry this value as `session.id`. */
51
60
  readonly sessionId?: string | undefined;
52
61
  /** Dynamic session ID extractor — called per traceLLMCall to resolve session ID. */
@@ -14,6 +14,7 @@ export declare const GEN_AI_METRICS: {
14
14
  readonly ERRORS: "gen_ai.client.errors";
15
15
  readonly AGENT_STEPS_PER_QUERY: "gen_ai.agent.steps_per_query";
16
16
  readonly AGENT_TOOL_USAGE: "gen_ai.agent.tool_usage";
17
+ readonly AGENT_TOOL_DURATION: "gen_ai.agent.tool_duration";
17
18
  readonly GUARD_EVALUATIONS: "gen_ai.toad_eye.guard.evaluations";
18
19
  readonly GUARD_WOULD_BLOCK: "gen_ai.toad_eye.guard.would_block";
19
20
  readonly SEMANTIC_DRIFT: "gen_ai.toad_eye.semantic_drift";
@@ -23,6 +24,13 @@ export declare const GEN_AI_METRICS: {
23
24
  readonly BUDGET_DOWNGRADED: "gen_ai.toad_eye.budget.downgraded";
24
25
  readonly RESPONSE_EMPTY: "gen_ai.toad_eye.response.empty";
25
26
  readonly RESPONSE_LATENCY_PER_TOKEN: "gen_ai.toad_eye.response.latency_per_token";
27
+ readonly CONTEXT_UTILIZATION: "gen_ai.toad_eye.context_utilization";
28
+ readonly CONTEXT_BLOCKED: "gen_ai.toad_eye.context.blocked";
29
+ readonly MCP_TOOL_DURATION: "gen_ai.mcp.tool.duration";
30
+ readonly MCP_TOOL_CALLS: "gen_ai.mcp.tool.calls";
31
+ readonly MCP_TOOL_ERRORS: "gen_ai.mcp.tool.errors";
32
+ readonly MCP_RESOURCE_READS: "gen_ai.mcp.resource.reads";
33
+ readonly MCP_SESSION_ACTIVE: "gen_ai.mcp.session.active";
26
34
  };
27
35
  /** @deprecated Use GEN_AI_METRICS instead. Kept for backward compatibility. */
28
36
  export declare const LLM_METRICS: {
@@ -15,6 +15,7 @@ export const GEN_AI_METRICS = {
15
15
  // Agent metrics
16
16
  AGENT_STEPS_PER_QUERY: "gen_ai.agent.steps_per_query",
17
17
  AGENT_TOOL_USAGE: "gen_ai.agent.tool_usage",
18
+ AGENT_TOOL_DURATION: "gen_ai.agent.tool_duration",
18
19
  // Guard metrics
19
20
  GUARD_EVALUATIONS: "gen_ai.toad_eye.guard.evaluations",
20
21
  GUARD_WOULD_BLOCK: "gen_ai.toad_eye.guard.would_block",
@@ -29,6 +30,15 @@ export const GEN_AI_METRICS = {
29
30
  // Response quality proxy metrics
30
31
  RESPONSE_EMPTY: "gen_ai.toad_eye.response.empty",
31
32
  RESPONSE_LATENCY_PER_TOKEN: "gen_ai.toad_eye.response.latency_per_token",
33
+ // Context utilization
34
+ CONTEXT_UTILIZATION: "gen_ai.toad_eye.context_utilization",
35
+ CONTEXT_BLOCKED: "gen_ai.toad_eye.context.blocked",
36
+ // MCP metrics
37
+ MCP_TOOL_DURATION: "gen_ai.mcp.tool.duration",
38
+ MCP_TOOL_CALLS: "gen_ai.mcp.tool.calls",
39
+ MCP_TOOL_ERRORS: "gen_ai.mcp.tool.errors",
40
+ MCP_RESOURCE_READS: "gen_ai.mcp.resource.reads",
41
+ MCP_SESSION_ACTIVE: "gen_ai.mcp.session.active",
32
42
  };
33
43
  /** @deprecated Use GEN_AI_METRICS instead. Kept for backward compatibility. */
34
44
  export const LLM_METRICS = {
@@ -11,6 +11,10 @@ export interface AgentStepInput {
11
11
  readonly toolName?: string | undefined;
12
12
  /** Tool type per OTel GenAI spec: function, extension, retrieval, builtin */
13
13
  readonly toolType?: "function" | "extension" | "retrieval" | "builtin" | undefined;
14
+ /** Duration of tool execution in milliseconds (for act steps). */
15
+ readonly toolDurationMs?: number | undefined;
16
+ /** Whether the tool execution succeeded or failed (for act steps). */
17
+ readonly toolStatus?: "success" | "error" | undefined;
14
18
  /** Target agent name for handoff steps */
15
19
  readonly toAgent?: string | undefined;
16
20
  /** Reason for handoff */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toad-eye",
3
- "version": "2.4.5",
3
+ "version": "2.6.0",
4
4
  "description": "Auto-instrumentation, cost tracking, budget guards, and observability for LLM services",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -30,6 +30,11 @@
30
30
  "types": "./dist/vercel.d.ts",
31
31
  "import": "./dist/vercel.js",
32
32
  "default": "./dist/vercel.js"
33
+ },
34
+ "./mcp": {
35
+ "types": "./dist/mcp/index.d.ts",
36
+ "import": "./dist/mcp/index.js",
37
+ "default": "./dist/mcp/index.js"
33
38
  }
34
39
  },
35
40
  "bin": {
@@ -87,7 +92,8 @@
87
92
  "@anthropic-ai/sdk": ">=0.30.0",
88
93
  "@google/generative-ai": ">=0.20.0",
89
94
  "nodemailer": ">=8.0.0",
90
- "openai": ">=4.0.0"
95
+ "openai": ">=4.0.0",
96
+ "@modelcontextprotocol/sdk": ">=1.0.0"
91
97
  },
92
98
  "peerDependenciesMeta": {
93
99
  "openai": {
@@ -101,6 +107,9 @@
101
107
  },
102
108
  "nodemailer": {
103
109
  "optional": true
110
+ },
111
+ "@modelcontextprotocol/sdk": {
112
+ "optional": true
104
113
  }
105
114
  },
106
115
  "devDependencies": {
@@ -1,6 +1,6 @@
1
1
  services:
2
2
  collector:
3
- image: otel/opentelemetry-collector-contrib:latest
3
+ image: otel/opentelemetry-collector-contrib:0.102.0
4
4
  volumes:
5
5
  - ./otel-collector.yml:/etc/otelcol-contrib/config.yaml
6
6
  ports:
@@ -10,14 +10,14 @@ services:
10
10
  - prometheus
11
11
 
12
12
  prometheus:
13
- image: prom/prometheus:latest
13
+ image: prom/prometheus:v2.53.0
14
14
  volumes:
15
15
  - ./prometheus.yml:/etc/prometheus/prometheus.yml
16
16
  ports:
17
17
  - "9090:9090"
18
18
 
19
19
  jaeger:
20
- image: jaegertracing/all-in-one:latest
20
+ image: jaegertracing/all-in-one:1.58
21
21
  environment:
22
22
  - COLLECTOR_OTLP_ENABLED=true
23
23
  ports:
@@ -25,7 +25,7 @@ services:
25
25
  - "4317:4317"
26
26
 
27
27
  grafana:
28
- image: grafana/grafana:latest
28
+ image: grafana/grafana:11.1.0
29
29
  volumes:
30
30
  - ./grafana/datasources.yml:/etc/grafana/provisioning/datasources/datasources.yml
31
31
  - ./grafana/dashboards:/etc/grafana/provisioning/dashboards
@@ -1,5 +1,7 @@
1
1
  {
2
- "annotations": { "list": [] },
2
+ "annotations": {
3
+ "list": []
4
+ },
3
5
  "editable": true,
4
6
  "fiscalYearStartMonth": 0,
5
7
  "graphTooltip": 1,
@@ -9,8 +11,16 @@
9
11
  {
10
12
  "title": "Avg Steps per Query",
11
13
  "type": "stat",
12
- "gridPos": { "h": 4, "w": 6, "x": 0, "y": 0 },
13
- "datasource": { "type": "prometheus", "uid": "prometheus" },
14
+ "gridPos": {
15
+ "h": 4,
16
+ "w": 6,
17
+ "x": 0,
18
+ "y": 0
19
+ },
20
+ "datasource": {
21
+ "type": "prometheus",
22
+ "uid": "prometheus"
23
+ },
14
24
  "targets": [
15
25
  {
16
26
  "expr": "sum(gen_ai_agent_steps_per_query_sum) / sum(gen_ai_agent_steps_per_query_count) or vector(0)",
@@ -23,9 +33,18 @@
23
33
  "decimals": 1,
24
34
  "thresholds": {
25
35
  "steps": [
26
- { "color": "green", "value": null },
27
- { "color": "yellow", "value": 10 },
28
- { "color": "red", "value": 20 }
36
+ {
37
+ "color": "green",
38
+ "value": null
39
+ },
40
+ {
41
+ "color": "yellow",
42
+ "value": 10
43
+ },
44
+ {
45
+ "color": "red",
46
+ "value": 20
47
+ }
29
48
  ]
30
49
  }
31
50
  },
@@ -35,8 +54,16 @@
35
54
  {
36
55
  "title": "Total Agent Queries",
37
56
  "type": "stat",
38
- "gridPos": { "h": 4, "w": 6, "x": 6, "y": 0 },
39
- "datasource": { "type": "prometheus", "uid": "prometheus" },
57
+ "gridPos": {
58
+ "h": 4,
59
+ "w": 6,
60
+ "x": 6,
61
+ "y": 0
62
+ },
63
+ "datasource": {
64
+ "type": "prometheus",
65
+ "uid": "prometheus"
66
+ },
40
67
  "targets": [
41
68
  {
42
69
  "expr": "sum(gen_ai_agent_steps_per_query_count)",
@@ -44,15 +71,25 @@
44
71
  }
45
72
  ],
46
73
  "fieldConfig": {
47
- "defaults": { "unit": "short" },
74
+ "defaults": {
75
+ "unit": "short"
76
+ },
48
77
  "overrides": []
49
78
  }
50
79
  },
51
80
  {
52
81
  "title": "Total Tool Invocations",
53
82
  "type": "stat",
54
- "gridPos": { "h": 4, "w": 6, "x": 12, "y": 0 },
55
- "datasource": { "type": "prometheus", "uid": "prometheus" },
83
+ "gridPos": {
84
+ "h": 4,
85
+ "w": 6,
86
+ "x": 12,
87
+ "y": 0
88
+ },
89
+ "datasource": {
90
+ "type": "prometheus",
91
+ "uid": "prometheus"
92
+ },
56
93
  "targets": [
57
94
  {
58
95
  "expr": "sum(gen_ai_agent_tool_usage_total)",
@@ -60,15 +97,25 @@
60
97
  }
61
98
  ],
62
99
  "fieldConfig": {
63
- "defaults": { "unit": "short" },
100
+ "defaults": {
101
+ "unit": "short"
102
+ },
64
103
  "overrides": []
65
104
  }
66
105
  },
67
106
  {
68
107
  "title": "Unique Tools Used",
69
108
  "type": "stat",
70
- "gridPos": { "h": 4, "w": 6, "x": 18, "y": 0 },
71
- "datasource": { "type": "prometheus", "uid": "prometheus" },
109
+ "gridPos": {
110
+ "h": 4,
111
+ "w": 6,
112
+ "x": 18,
113
+ "y": 0
114
+ },
115
+ "datasource": {
116
+ "type": "prometheus",
117
+ "uid": "prometheus"
118
+ },
72
119
  "targets": [
73
120
  {
74
121
  "expr": "count(count by (gen_ai_agent_tool_name) (gen_ai_agent_tool_usage_total))",
@@ -76,7 +123,9 @@
76
123
  }
77
124
  ],
78
125
  "fieldConfig": {
79
- "defaults": { "unit": "short" },
126
+ "defaults": {
127
+ "unit": "short"
128
+ },
80
129
  "overrides": []
81
130
  }
82
131
  },
@@ -84,8 +133,16 @@
84
133
  "title": "Agent Step Type Breakdown",
85
134
  "description": "Distribution of think / act / observe / answer steps",
86
135
  "type": "piechart",
87
- "gridPos": { "h": 8, "w": 12, "x": 0, "y": 4 },
88
- "datasource": { "type": "prometheus", "uid": "prometheus" },
136
+ "gridPos": {
137
+ "h": 8,
138
+ "w": 12,
139
+ "x": 0,
140
+ "y": 4
141
+ },
142
+ "datasource": {
143
+ "type": "prometheus",
144
+ "uid": "prometheus"
145
+ },
89
146
  "targets": [
90
147
  {
91
148
  "expr": "sum by (gen_ai_agent_step_type) (gen_ai_agent_steps_per_query_sum)",
@@ -106,8 +163,16 @@
106
163
  "title": "Tool Usage Frequency",
107
164
  "description": "Invocations per tool name",
108
165
  "type": "barchart",
109
- "gridPos": { "h": 8, "w": 12, "x": 12, "y": 4 },
110
- "datasource": { "type": "prometheus", "uid": "prometheus" },
166
+ "gridPos": {
167
+ "h": 8,
168
+ "w": 12,
169
+ "x": 12,
170
+ "y": 4
171
+ },
172
+ "datasource": {
173
+ "type": "prometheus",
174
+ "uid": "prometheus"
175
+ },
111
176
  "targets": [
112
177
  {
113
178
  "expr": "sort_desc(sum by (gen_ai_agent_tool_name) (gen_ai_agent_tool_usage_total))",
@@ -119,8 +184,16 @@
119
184
  {
120
185
  "title": "Steps per Query Over Time",
121
186
  "type": "timeseries",
122
- "gridPos": { "h": 8, "w": 24, "x": 0, "y": 12 },
123
- "datasource": { "type": "prometheus", "uid": "prometheus" },
187
+ "gridPos": {
188
+ "h": 8,
189
+ "w": 24,
190
+ "x": 0,
191
+ "y": 12
192
+ },
193
+ "datasource": {
194
+ "type": "prometheus",
195
+ "uid": "prometheus"
196
+ },
124
197
  "targets": [
125
198
  {
126
199
  "expr": "sum(rate(gen_ai_agent_steps_per_query_sum[5m])) / sum(rate(gen_ai_agent_steps_per_query_count[5m]))",
@@ -136,7 +209,9 @@
136
209
  "fieldConfig": {
137
210
  "defaults": {
138
211
  "unit": "short",
139
- "custom": { "fillOpacity": 10 }
212
+ "custom": {
213
+ "fillOpacity": 10
214
+ }
140
215
  },
141
216
  "overrides": []
142
217
  }
@@ -144,8 +219,16 @@
144
219
  {
145
220
  "title": "Tool Invocations Over Time",
146
221
  "type": "timeseries",
147
- "gridPos": { "h": 8, "w": 12, "x": 0, "y": 20 },
148
- "datasource": { "type": "prometheus", "uid": "prometheus" },
222
+ "gridPos": {
223
+ "h": 8,
224
+ "w": 12,
225
+ "x": 0,
226
+ "y": 20
227
+ },
228
+ "datasource": {
229
+ "type": "prometheus",
230
+ "uid": "prometheus"
231
+ },
149
232
  "targets": [
150
233
  {
151
234
  "expr": "sum by (gen_ai_agent_tool_name) (rate(gen_ai_agent_tool_usage_total[5m]))",
@@ -156,7 +239,12 @@
156
239
  "fieldConfig": {
157
240
  "defaults": {
158
241
  "unit": "reqps",
159
- "custom": { "fillOpacity": 20, "stacking": { "mode": "normal" } }
242
+ "custom": {
243
+ "fillOpacity": 20,
244
+ "stacking": {
245
+ "mode": "normal"
246
+ }
247
+ }
160
248
  },
161
249
  "overrides": []
162
250
  }
@@ -165,8 +253,16 @@
165
253
  "title": "Steps Distribution Histogram",
166
254
  "description": "How many steps do agent queries typically take?",
167
255
  "type": "histogram",
168
- "gridPos": { "h": 8, "w": 12, "x": 12, "y": 20 },
169
- "datasource": { "type": "prometheus", "uid": "prometheus" },
256
+ "gridPos": {
257
+ "h": 8,
258
+ "w": 12,
259
+ "x": 12,
260
+ "y": 20
261
+ },
262
+ "datasource": {
263
+ "type": "prometheus",
264
+ "uid": "prometheus"
265
+ },
170
266
  "targets": [
171
267
  {
172
268
  "expr": "sum by (le) (gen_ai_agent_steps_per_query_bucket)",
@@ -176,7 +272,9 @@
176
272
  ],
177
273
  "fieldConfig": {
178
274
  "defaults": {
179
- "custom": { "fillOpacity": 80 }
275
+ "custom": {
276
+ "fillOpacity": 80
277
+ }
180
278
  },
181
279
  "overrides": []
182
280
  }
@@ -186,19 +284,44 @@
186
284
  "tags": ["toad-eye", "agent"],
187
285
  "templating": {
188
286
  "list": [
287
+ {
288
+ "name": "service",
289
+ "type": "query",
290
+ "datasource": {
291
+ "type": "prometheus",
292
+ "uid": "prometheus"
293
+ },
294
+ "query": "label_values(gen_ai_client_requests_total, job)",
295
+ "includeAll": true,
296
+ "allValue": ".*",
297
+ "current": {
298
+ "text": "All",
299
+ "value": "$__all"
300
+ },
301
+ "multi": true
302
+ },
189
303
  {
190
304
  "name": "tool",
191
305
  "type": "query",
192
- "datasource": { "type": "prometheus", "uid": "prometheus" },
306
+ "datasource": {
307
+ "type": "prometheus",
308
+ "uid": "prometheus"
309
+ },
193
310
  "query": "label_values(gen_ai_agent_tool_usage_total, gen_ai_agent_tool_name)",
194
311
  "includeAll": true,
195
312
  "multi": true,
196
- "current": { "text": "All", "value": "$__all" },
313
+ "current": {
314
+ "text": "All",
315
+ "value": "$__all"
316
+ },
197
317
  "regex": ""
198
318
  }
199
319
  ]
200
320
  },
201
- "time": { "from": "now-6h", "to": "now" },
321
+ "time": {
322
+ "from": "now-6h",
323
+ "to": "now"
324
+ },
202
325
  "timepicker": {},
203
326
  "timezone": "utc",
204
327
  "title": "toad-eye / Agent Workflow",