toad-eye 2.2.0 β†’ 2.4.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 (47) hide show
  1. package/README.md +166 -152
  2. package/dist/agent.d.ts +31 -28
  3. package/dist/agent.js +116 -35
  4. package/dist/alerts/channels.js +24 -6
  5. package/dist/alerts/index.js +46 -2
  6. package/dist/alerts/manager.d.ts +1 -0
  7. package/dist/alerts/manager.js +36 -21
  8. package/dist/budget/tracker.d.ts +16 -6
  9. package/dist/budget/tracker.js +38 -17
  10. package/dist/budget/types.d.ts +2 -0
  11. package/dist/cli.d.ts +8 -1
  12. package/dist/cli.js +106 -54
  13. package/dist/core/metrics.d.ts +4 -0
  14. package/dist/core/metrics.js +20 -1
  15. package/dist/core/pricing.d.ts +2 -0
  16. package/dist/core/pricing.js +4 -0
  17. package/dist/core/spans.d.ts +5 -3
  18. package/dist/core/spans.js +80 -21
  19. package/dist/core/tracer.d.ts +6 -3
  20. package/dist/core/tracer.js +49 -7
  21. package/dist/drift/baseline.js +7 -1
  22. package/dist/drift/monitor.js +6 -6
  23. package/dist/index.d.ts +7 -2
  24. package/dist/index.js +6 -1
  25. package/dist/instrumentations/anthropic.d.ts +1 -1
  26. package/dist/instrumentations/anthropic.js +2 -3
  27. package/dist/instrumentations/create.js +80 -19
  28. package/dist/instrumentations/gemini.d.ts +1 -1
  29. package/dist/instrumentations/gemini.js +6 -7
  30. package/dist/instrumentations/openai.d.ts +1 -1
  31. package/dist/instrumentations/openai.js +2 -3
  32. package/dist/instrumentations/registry.d.ts +3 -1
  33. package/dist/instrumentations/registry.js +28 -5
  34. package/dist/instrumentations/types.d.ts +4 -2
  35. package/dist/types/attributes.d.ts +20 -0
  36. package/dist/types/attributes.js +23 -1
  37. package/dist/types/config.d.ts +22 -2
  38. package/dist/types/index.d.ts +3 -3
  39. package/dist/types/metrics.d.ts +2 -0
  40. package/dist/types/metrics.js +3 -0
  41. package/dist/types/providers.d.ts +6 -1
  42. package/dist/types/spans.d.ts +21 -2
  43. package/dist/vercel.d.ts +53 -0
  44. package/dist/vercel.js +106 -0
  45. package/package.json +29 -3
  46. package/templates/grafana/dashboards/agent-workflow.json +207 -0
  47. package/templates/otel-collector.yml +25 -1
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # toad-eye
1
+ # toad-eye πŸΈπŸ‘οΈβ€πŸ—¨οΈπŸ€–
2
2
 
3
3
  ![TypeScript](https://img.shields.io/badge/TypeScript-3178C6?logo=typescript&logoColor=white)
4
4
  ![OpenTelemetry](https://img.shields.io/badge/OpenTelemetry-000000?logo=opentelemetry&logoColor=white)
@@ -10,9 +10,9 @@
10
10
  ![CI](https://github.com/vola-trebla/toad-eye/actions/workflows/ci.yml/badge.svg)
11
11
  ![License](https://img.shields.io/badge/License-ISC-blue)
12
12
 
13
- OpenTelemetry-based observability toolkit for LLM systems.
13
+ **LLM observability in 3 commands. Auto-instrument, trace, alert, save money.**
14
14
 
15
- Auto-instrument any LLM SDK (including streaming), get traces, metrics, cost tracking, budget guards, agent observability, guardrail monitoring, and 6 Grafana dashboards out of the box. Self-hosted or cloud mode.
15
+ You don't know what your LLMs cost, how slow they are, or why they fail. toad-eye gives you complete visibility with zero manual instrumentation.
16
16
 
17
17
  ![toad-eye demo](https://raw.githubusercontent.com/vola-trebla/toad-eye/main/demo/toad-eye-demo-gifski.gif)
18
18
 
@@ -25,48 +25,57 @@ npx toad-eye up # start Grafana + Prometheus + Jaeger
25
25
  npx toad-eye demo # send mock LLM traffic, see data immediately
26
26
  ```
27
27
 
28
- Open [localhost:3100](http://localhost:3100) (Grafana, admin/admin) to see your dashboards.
29
-
30
- ### Cloud mode
31
-
32
- No Docker needed. Send telemetry to toad-eye cloud with one line:
33
-
34
28
  ```typescript
29
+ import { initObservability } from "toad-eye";
30
+
35
31
  initObservability({
36
32
  serviceName: "my-app",
37
- apiKey: "toad_xxxxxxxx",
38
- instrument: ["openai"],
33
+ instrument: ["openai", "anthropic"],
39
34
  });
35
+
36
+ // That's it. Every SDK call is auto-traced β€” including streaming.
40
37
  ```
41
38
 
42
- Self-hosted mode remains the default. Cloud mode activates automatically when `apiKey` is set.
39
+ Open [localhost:3100](http://localhost:3100) (Grafana, admin/admin) to see your dashboards.
43
40
 
44
- ## Auto-instrumentation
41
+ ## What you get
42
+
43
+ | Feature | Description |
44
+ | ------------------------ | ------------------------------------------------------------------------------------------ |
45
+ | **Auto-instrumentation** | OpenAI, Anthropic, Gemini, Vercel AI SDK β€” regular + streaming |
46
+ | **8 Grafana dashboards** | Overview, Cost, Latency, Errors, Model Comparison, FinOps, Provider Health, Agent Workflow |
47
+ | **Cost tracking** | Per-request USD cost, daily totals, projected monthly spend |
48
+ | **Budget guards** | Daily/per-user/per-model spend limits β€” warn, block, or auto-downgrade |
49
+ | **Multi-agent tracing** | ReAct steps, handoffs, loop detection, maxSteps guard |
50
+ | **Shadow guardrails** | Record what _would_ be blocked without blocking β€” tune thresholds on live traffic |
51
+ | **Quality proxy** | Empty response rate, latency per token β€” zero-dependency quality metrics |
52
+ | **Semantic drift** | Detect silent quality degradation via embedding comparison |
53
+ | **Privacy controls** | Built-in PII redaction (email, SSN, CC, phone), hashing, content masking |
54
+ | **Tail sampling** | Keep 100% errors + slow traces, sample healthy traffic via OTel Collector |
55
+ | **Alerting** | Cost spikes, latency anomalies, error rate β€” via Telegram, Slack, email, webhook |
56
+ | **FinOps attribution** | Break down costs by team, user, feature, environment |
57
+ | **TTFT metric** | Time To First Token for streaming β€” separate from total duration |
58
+ | **Trace export** | Convert production Jaeger traces into regression test cases |
59
+
60
+ ## Auto-instrumentation πŸ€–
45
61
 
46
62
  Zero-code instrumentation for popular LLM SDKs. No wrappers needed.
47
63
 
48
64
  ```typescript
49
- import { initObservability } from "toad-eye";
50
-
51
65
  initObservability({
52
66
  serviceName: "my-app",
53
67
  instrument: ["openai", "anthropic"],
54
68
  });
55
-
56
- // That's it. Every SDK call is auto-traced.
57
- const result = await openai.chat.completions.create({
58
- model: "gpt-4o",
59
- messages: [{ role: "user", content: "Hello" }],
60
- });
61
69
  ```
62
70
 
63
- **Supported SDKs:** OpenAI (`openai`), Anthropic (`@anthropic-ai/sdk`), Google GenAI (`@google/generative-ai`)
71
+ **Supported SDKs:** OpenAI (`openai`), Anthropic (`@anthropic-ai/sdk`), Google GenAI (`@google/generative-ai`), Vercel AI SDK (`ai`)
64
72
 
65
73
  Both regular and **streaming** calls are fully instrumented β€” spans, metrics, and cost tracking work transparently for `stream: true`.
66
74
 
67
- ### Manual instrumentation
75
+ Vercel AI SDK uses a SpanProcessor (not monkey-patching) β€” add `instrument: ['ai']` and `experimental_telemetry: withToadEye()` to your calls.
68
76
 
69
- For custom providers or fine-grained control:
77
+ <details>
78
+ <summary>Manual instrumentation (custom providers)</summary>
70
79
 
71
80
  ```typescript
72
81
  import { initObservability, traceLLMCall } from "toad-eye";
@@ -79,9 +88,33 @@ const result = await traceLLMCall(
79
88
  );
80
89
  ```
81
90
 
91
+ </details>
92
+
93
+ ## Budget guards
94
+
95
+ Prevent cost overruns at runtime. Three modes: warn, block, or auto-downgrade to a cheaper model.
96
+
97
+ ```typescript
98
+ initObservability({
99
+ serviceName: "my-app",
100
+ budgets: {
101
+ daily: 50, // $50/day max
102
+ perUser: 5, // $5/day per user
103
+ perModel: { "gpt-4o": 30 }, // $30/day on GPT-4o
104
+ },
105
+ onBudgetExceeded: "block", // throws ToadBudgetExceededError
106
+ });
107
+ ```
108
+
109
+ | Mode | Behavior |
110
+ | ----------- | --------------------------------------------------- |
111
+ | `warn` | Log warning, continue normally |
112
+ | `block` | Throw `ToadBudgetExceededError` before LLM call |
113
+ | `downgrade` | Call `downgradeCallback` to switch to cheaper model |
114
+
82
115
  ## Agent observability
83
116
 
84
- Structured tracing for ReAct agents (think / act / observe / answer) as nested OpenTelemetry spans.
117
+ Structured tracing for ReAct agents with multi-agent support, handoffs, and loop detection.
85
118
 
86
119
  ```typescript
87
120
  import { traceAgentQuery } from "toad-eye";
@@ -111,17 +144,37 @@ const result = await traceAgentQuery(
111
144
  );
112
145
  ```
113
146
 
114
- Metrics: steps per query (histogram), tool usage frequency (counter per tool name).
147
+ ## FinOps attribution
148
+
149
+ Track costs by team, user, feature, or any business dimension:
150
+
151
+ ```typescript
152
+ initObservability({
153
+ serviceName: "my-app",
154
+ attributes: { team: "checkout", environment: "production" },
155
+ });
156
+
157
+ await traceLLMCall(
158
+ {
159
+ provider: "openai",
160
+ model: "gpt-4o",
161
+ prompt: "Summarize order",
162
+ attributes: { userId: "user-123", feature: "order-summary" },
163
+ },
164
+ () => callLLM(),
165
+ );
166
+ ```
167
+
168
+ <details>
169
+ <summary>More features</summary>
115
170
 
116
- ## Shadow guardrails
171
+ ### Shadow guardrails
117
172
 
118
- Validate LLM responses without blocking them. Record what _would_ have been blocked, then decide when to enforce.
173
+ Record what _would_ have been blocked without blocking. Tune thresholds on live traffic.
119
174
 
120
175
  ```typescript
121
176
  import { recordGuardResult } from "toad-eye";
122
177
 
123
- // toad-guard validates, toad-eye records
124
- const result = guard.validate(response);
125
178
  recordGuardResult({
126
179
  mode: "shadow",
127
180
  passed: false,
@@ -130,49 +183,20 @@ recordGuardResult({
130
183
  });
131
184
  ```
132
185
 
133
- Metrics: `guard.evaluations` (total checks), `guard.would_block` (would-have-blocked count) per rule name.
186
+ ### Semantic drift monitoring
134
187
 
135
- ## Semantic drift monitoring
136
-
137
- Detect silent LLM quality degradation by comparing current responses to a saved baseline via embeddings.
188
+ Detect silent LLM quality degradation by comparing responses to a saved baseline via embeddings.
138
189
 
139
190
  ```typescript
140
- import { createDriftMonitor, saveBaseline } from "toad-eye";
191
+ import { createDriftMonitor } from "toad-eye";
141
192
 
142
193
  const monitor = createDriftMonitor({
143
194
  embedding: { provider: "openai", apiKey: process.env.OPENAI_API_KEY! },
144
195
  baselinePath: "./baseline.json",
145
- sampleRate: 10, // check every 10th response
196
+ sampleRate: 10,
146
197
  });
147
198
 
148
- // In your LLM call handler:
149
- const drift = await monitor.check(response, "openai", "gpt-4o");
150
- // drift = 0 β†’ identical to baseline
151
- // drift > 0.3 β†’ significant deviation
152
- ```
153
-
154
- ## Trace-to-dataset export
155
-
156
- Convert production Jaeger traces into test cases. Production failure becomes a regression test.
157
-
158
- ```bash
159
- npx toad-eye export-trace <trace_id> --output ./evals/
160
- ```
161
-
162
- Generates YAML in toad-eval format with auto-generated assertions (max length, refusal detection, JSON validation).
163
-
164
- ## Features
165
-
166
- ### Built-in cost tracking
167
-
168
- Automatic cost calculation for major models (GPT-4o, Claude, Gemini) based on token usage. Custom pricing for enterprise contracts:
169
-
170
- ```typescript
171
- import { setCustomPricing } from "toad-eye";
172
-
173
- setCustomPricing({
174
- "my-fine-tuned-model": { inputPer1M: 5, outputPer1M: 15 },
175
- });
199
+ monitor.checkInBackground(response, "openai", "gpt-4o");
176
200
  ```
177
201
 
178
202
  ### Privacy controls
@@ -180,28 +204,36 @@ setCustomPricing({
180
204
  ```typescript
181
205
  initObservability({
182
206
  serviceName: "my-app",
207
+ redactDefaults: true, // built-in PII patterns (email, SSN, CC, phone)
183
208
  recordContent: false, // disable prompt/completion recording
184
209
  hashContent: true, // SHA-256 hash instead of plain text
185
- redactPatterns: [/\b\S+@\S+\.\S+\b/g], // regex redaction
210
+ salt: "my-secret", // prevent rainbow table attacks
211
+ auditMasking: true, // log what was masked (debug only)
212
+ redactPatterns: [/custom-pattern/g], // additional regex redaction
186
213
  });
187
214
  ```
188
215
 
189
216
  ### Session tracking
190
217
 
191
- Group traces by conversation session:
192
-
193
218
  ```typescript
194
219
  initObservability({
195
220
  serviceName: "my-app",
196
221
  sessionId: "static-session-id",
197
- // or dynamic:
198
- sessionExtractor: () => getCurrentSessionId(),
222
+ sessionExtractor: () => getCurrentSessionId(), // or dynamic
199
223
  });
200
224
  ```
201
225
 
202
- ### Alerting
226
+ ### Built-in cost tracking
227
+
228
+ ```typescript
229
+ import { setCustomPricing } from "toad-eye";
203
230
 
204
- Cost spikes, latency anomalies, error rate monitoring via YAML config:
231
+ setCustomPricing({
232
+ "my-fine-tuned-model": { inputPer1M: 5, outputPer1M: 15 },
233
+ });
234
+ ```
235
+
236
+ ### Alerting
205
237
 
206
238
  ```yaml
207
239
  alerts:
@@ -210,78 +242,52 @@ alerts:
210
242
  condition: sum_1h > 10
211
243
  channels: [slack]
212
244
 
213
- - name: latency_anomaly
214
- metric: gen_ai.client.operation.duration
215
- condition: p95_pct_5m_7d > 50
216
- channels: [slack]
217
-
218
245
  - name: error_rate
219
246
  metric: gen_ai.client.errors
220
247
  condition: ratio_15m > 0.05
221
- channels: [slack]
248
+ channels: [telegram]
222
249
  ```
223
250
 
224
- Delivery channels: Telegram, Slack webhook, generic HTTP webhook, email (SMTP).
251
+ Channels: Telegram, Slack webhook, HTTP webhook, email (SMTP).
225
252
 
226
- ### Budget guards
253
+ ### Trace-to-dataset export
227
254
 
228
- Prevent cost overruns at runtime. Three modes: warn, block, or auto-downgrade to a cheaper model.
255
+ Convert production Jaeger traces into test cases:
229
256
 
230
- ```typescript
231
- initObservability({
232
- serviceName: "my-app",
233
- budgets: {
234
- daily: 50, // $50/day max
235
- perUser: 5, // $5/day per user
236
- perModel: { "gpt-4o": 30 }, // $30/day on GPT-4o
237
- },
238
- onBudgetExceeded: "block", // throws ToadBudgetExceededError
239
- });
257
+ ```bash
258
+ npx toad-eye export-trace <trace_id> --output ./evals/
240
259
  ```
241
260
 
242
- | Mode | Behavior |
243
- | ----------- | --------------------------------------------------- |
244
- | `warn` | Log warning, continue normally |
245
- | `block` | Throw `ToadBudgetExceededError` before LLM call |
246
- | `downgrade` | Call `downgradeCallback` to switch to cheaper model |
247
-
248
- ## FinOps attribution
261
+ ### Cloud mode (coming soon)
249
262
 
250
- Track costs by team, user, feature, or any business dimension:
263
+ No Docker needed. Send telemetry to toad-eye cloud with one line:
251
264
 
252
265
  ```typescript
253
- // Global attributes β€” applied to all spans/metrics
254
266
  initObservability({
255
267
  serviceName: "my-app",
256
- attributes: { team: "checkout", environment: "production" },
268
+ apiKey: "toad_xxxxxxxx",
269
+ instrument: ["openai"],
257
270
  });
258
-
259
- // Per-request attributes β€” override or extend global
260
- await traceLLMCall(
261
- {
262
- provider: "openai",
263
- model: "gpt-4o",
264
- prompt: "Summarize order",
265
- attributes: { userId: "user-123", feature: "order-summary" },
266
- },
267
- () => callLLM(),
268
- );
269
271
  ```
270
272
 
271
- ## Grafana dashboards
273
+ Self-hosted mode remains the default. Cloud mode activates automatically when `apiKey` is set.
274
+
275
+ </details>
272
276
 
273
- 6 pre-built dashboards auto-provisioned on `npx toad-eye init`:
277
+ ## Grafana dashboards
274
278
 
275
- | Dashboard | What it shows |
276
- | ---------------------- | --------------------------------------------------------- |
277
- | **Overview** | Request rate, error rate, latency p50/p95, cost, totals |
278
- | **Cost Breakdown** | Spend by provider/model, daily trend, projected monthly |
279
- | **Latency Analysis** | p50/p95/p99 by model, distribution histogram |
280
- | **Error Drill-down** | Error rate by provider/model, error vs success |
281
- | **Model Comparison** | Latency vs cost vs error rate vs throughput per model |
282
- | **FinOps Attribution** | Cost by team/user/feature, projected spend, what-if table |
279
+ 8 pre-built dashboards auto-provisioned on `npx toad-eye init`:
283
280
 
284
- All dashboards have template variables for filtering (`$provider`, `$model`, `$team`, `$feature`).
281
+ | Dashboard | What it shows |
282
+ | ---------------------- | ---------------------------------------------------------------- |
283
+ | **Overview** | Request rate, error rate, latency p50/p95, cost, totals |
284
+ | **Cost Breakdown** | Spend by provider/model, daily trend, projected monthly |
285
+ | **Latency Analysis** | p50/p95/p99 by model, distribution histogram, TTFT |
286
+ | **Error Drill-down** | Error rate by provider/model, error vs success |
287
+ | **Model Comparison** | Latency vs cost vs error rate vs throughput per model |
288
+ | **FinOps Attribution** | Cost by team/user/feature, projected spend, what-if table |
289
+ | **Provider Health** | Provider status (healthy/degraded/down), uptime, error breakdown |
290
+ | **Agent Workflow** | Steps per query, tool usage frequency, step type breakdown |
285
291
 
286
292
  ## CLI
287
293
 
@@ -300,40 +306,48 @@ All dashboards have template variables for filtering (`$provider`, `$model`, `$t
300
306
 
301
307
  ## What it tracks
302
308
 
309
+ <details>
310
+ <summary>Metrics & span attributes</summary>
311
+
303
312
  ### Metrics (OTel GenAI semconv)
304
313
 
305
- | Metric | Type | Description |
306
- | ----------------------------------- | --------- | ----------------------------------- |
307
- | `gen_ai.client.operation.duration` | Histogram | Request latency (ms) |
308
- | `gen_ai.client.request.cost` | Histogram | Cost per request (USD) |
309
- | `gen_ai.client.token.usage` | Counter | Total tokens consumed |
310
- | `gen_ai.client.requests` | Counter | Total requests |
311
- | `gen_ai.client.errors` | Counter | Total failed requests |
312
- | `gen_ai.agent.steps_per_query` | Histogram | Agent steps per query |
313
- | `gen_ai.agent.tool_usage` | Counter | Agent tool invocations by tool name |
314
- | `gen_ai.toad_eye.guard.evaluations` | Counter | Guard evaluations per rule |
315
- | `gen_ai.toad_eye.guard.would_block` | Counter | Would-have-blocked per rule |
316
- | `gen_ai.toad_eye.semantic_drift` | Histogram | Semantic drift from baseline (0..1) |
317
- | `gen_ai.toad_eye.budget.exceeded` | Counter | Budget limit exceeded events |
318
- | `gen_ai.toad_eye.budget.blocked` | Counter | LLM calls blocked by budget |
319
- | `gen_ai.toad_eye.budget.downgraded` | Counter | LLM calls downgraded by budget |
320
-
321
- All metrics labeled with `gen_ai.provider.name` and `gen_ai.request.model`.
314
+ | Metric | Type | Description |
315
+ | -------------------------------------------- | --------- | --------------------------- |
316
+ | `gen_ai.client.operation.duration` | Histogram | Request latency (ms) |
317
+ | `gen_ai.client.time_to_first_token` | Histogram | TTFT for streaming (ms) |
318
+ | `gen_ai.client.request.cost` | Histogram | Cost per request (USD) |
319
+ | `gen_ai.client.token.usage` | Counter | Total tokens consumed |
320
+ | `gen_ai.client.requests` | Counter | Total requests |
321
+ | `gen_ai.client.errors` | Counter | Total failed requests |
322
+ | `gen_ai.agent.steps_per_query` | Histogram | Agent steps per query |
323
+ | `gen_ai.agent.tool_usage` | Counter | Agent tool invocations |
324
+ | `gen_ai.toad_eye.guard.evaluations` | Counter | Guard evaluations per rule |
325
+ | `gen_ai.toad_eye.guard.would_block` | Counter | Would-have-blocked per rule |
326
+ | `gen_ai.toad_eye.semantic_drift` | Histogram | Drift from baseline (0..1) |
327
+ | `gen_ai.toad_eye.budget.exceeded` | Counter | Budget exceeded events |
328
+ | `gen_ai.toad_eye.budget.blocked` | Counter | LLM calls blocked by budget |
329
+ | `gen_ai.toad_eye.budget.downgraded` | Counter | LLM calls downgraded |
330
+ | `gen_ai.toad_eye.response.empty` | Counter | Empty/whitespace responses |
331
+ | `gen_ai.toad_eye.response.latency_per_token` | Histogram | Generation speed (ms/token) |
322
332
 
323
333
  ### Span attributes
324
334
 
325
- | Attribute | Description |
326
- | ---------------------------- | ------------------------------------ |
327
- | `gen_ai.provider.name` | `anthropic`, `gemini`, `openai` |
328
- | `gen_ai.request.model` | Model identifier |
329
- | `gen_ai.usage.input_tokens` | Tokens in the prompt |
330
- | `gen_ai.usage.output_tokens` | Tokens in the completion |
331
- | `gen_ai.request.temperature` | Temperature parameter |
332
- | `gen_ai.toad_eye.cost` | Cost in USD |
333
- | `gen_ai.agent.step.type` | Agent step: think/act/observe/answer |
334
- | `gen_ai.agent.tool.name` | Tool name for agent act steps |
335
- | `gen_ai.toad_eye.guard.mode` | Guard mode: shadow/enforce |
336
- | `session.id` | Session identifier (if configured) |
335
+ | Attribute | Description |
336
+ | ---------------------------- | -------------------------------------------- |
337
+ | `gen_ai.provider.name` | `anthropic`, `gemini`, `openai` |
338
+ | `gen_ai.request.model` | Model identifier |
339
+ | `gen_ai.usage.input_tokens` | Tokens in the prompt |
340
+ | `gen_ai.usage.output_tokens` | Tokens in the completion |
341
+ | `gen_ai.request.temperature` | Temperature parameter |
342
+ | `gen_ai.toad_eye.cost` | Cost in USD |
343
+ | `gen_ai.agent.step.type` | Agent step: think/act/observe/answer/handoff |
344
+ | `gen_ai.agent.tool.name` | Tool name for agent act steps |
345
+ | `gen_ai.agent.handoff.to` | Target agent for handoff steps |
346
+ | `gen_ai.agent.loop_count` | Agent loop iterations (observe→think) |
347
+ | `gen_ai.toad_eye.guard.mode` | Guard mode: shadow/enforce |
348
+ | `session.id` | Session identifier (if configured) |
349
+
350
+ </details>
337
351
 
338
352
  ## Services
339
353
 
@@ -349,4 +363,4 @@ All metrics labeled with `gen_ai.provider.name` and `gen_ai.request.model`.
349
363
  - TypeScript, OpenTelemetry SDK 2.x, OTel GenAI semantic conventions
350
364
  - Hono (demo server + cloud ingestion server)
351
365
  - Docker Compose (Prometheus, Jaeger, Grafana, OTel Collector)
352
- - Vitest (154 tests)
366
+ - Vitest (143+ tests)
package/dist/agent.d.ts CHANGED
@@ -1,41 +1,44 @@
1
- import type { AgentStepInput } from "./types/index.js";
1
+ import type { AgentStepInput, AgentQueryOptions, AgentQueryInput } from "./types/index.js";
2
2
  /**
3
- * Record a single agent step as a child span (ReAct pattern: think β†’ act β†’ observe β†’ answer).
4
- *
5
- * Creates a short-lived span under the current active context. Call this inside
6
- * {@link traceAgentQuery} (preferred) or any other active span (e.g. traceLLMCall).
7
- *
8
- * - Respects `recordContent` config β€” content is omitted when recording is disabled.
9
- * - For `act` steps with a `toolName`, automatically increments the `agent.tool_usage` metric.
3
+ * Record a single agent step as a child span (ReAct pattern).
4
+ * Supports: think, act, observe, answer, handoff.
10
5
  */
11
6
  declare function traceAgentStep(input: AgentStepInput): void;
12
7
  /**
13
- * Wrap an entire agent query in a parent span, producing structured ReAct traces.
8
+ * Wrap an entire agent query in a parent span with ReAct tracing.
14
9
  *
15
- * The callback receives a `step` function to record individual steps.
16
- * When the callback completes, the wrapper automatically records the
17
- * `agent.steps_per_query` histogram metric with the total step count.
10
+ * Accepts either a string query (backward-compatible) or an object with
11
+ * agent metadata per OTel GenAI spec (agentName, agentId).
18
12
  *
19
- * Span hierarchy in Jaeger:
20
- * ```
21
- * [parent] agent.query "Is anything dangerous near Earth?"
22
- * β”œβ”€β”€ agent.step.think "I need to check asteroids"
23
- * β”œβ”€β”€ agent.step.act tool=near-earth-asteroids
24
- * β”œβ”€β”€ agent.step.observe "7 asteroids found"
25
- * └── agent.step.answer "7 asteroids passing safely..."
26
- * ```
13
+ * Supports multi-agent via nesting β€” child traceAgentQuery calls
14
+ * automatically become child spans in Jaeger.
15
+ *
16
+ * Loop detection: counts observe→think transitions. Records loop count
17
+ * as span attribute. Emits warning when maxSteps exceeded.
27
18
  *
28
19
  * @example
29
20
  * ```ts
30
- * const result = await traceAgentQuery("Is anything dangerous near Earth?", async (step) => {
31
- * step({ type: "think", stepNumber: 1, content: "I need to check asteroids" });
32
- * const data = await callTool("near-earth-asteroids");
33
- * step({ type: "act", stepNumber: 2, toolName: "near-earth-asteroids" });
34
- * step({ type: "observe", stepNumber: 3, content: `${data.length} asteroids found` });
35
- * step({ type: "answer", stepNumber: 4, content: "7 asteroids passing safely..." });
36
- * return { answer: "7 asteroids passing safely..." };
21
+ * // Simple form (backward-compatible)
22
+ * await traceAgentQuery('Is anything dangerous?', async (step) => { ... });
23
+ *
24
+ * // Object form with agent metadata (OTel GenAI spec)
25
+ * await traceAgentQuery(
26
+ * { query: 'Is anything dangerous?', agentName: 'space-monitor', agentId: 'agent-001' },
27
+ * async (step) => { ... }
28
+ * );
29
+ *
30
+ * // Multi-agent: orchestrator delegates to specialist
31
+ * await traceAgentQuery({ query: 'Analyze data', agentName: 'orchestrator' }, async (step) => {
32
+ * step({ type: 'think', stepNumber: 1, content: 'Need domain expert' });
33
+ * step({ type: 'handoff', stepNumber: 2, toAgent: 'specialist', handoffReason: 'domain expertise' });
34
+ *
35
+ * const result = await traceAgentQuery({ query: 'Analyze', agentName: 'specialist' }, async (step) => {
36
+ * step({ type: 'act', stepNumber: 1, toolName: 'analyze', toolType: 'function' });
37
+ * step({ type: 'answer', stepNumber: 2, content: 'analysis complete' });
38
+ * return { answer: 'done' };
39
+ * });
37
40
  * });
38
41
  * ```
39
42
  */
40
- export declare function traceAgentQuery<T>(query: string, fn: (step: (input: AgentStepInput) => void) => Promise<T>): Promise<T>;
43
+ export declare function traceAgentQuery<T>(queryOrInput: string | AgentQueryInput, fn: (step: (input: AgentStepInput) => void) => Promise<T>, options?: AgentQueryOptions): Promise<T>;
41
44
  export { traceAgentStep };