@apo-ai/sdk 0.6.0 → 0.7.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.
@@ -38,6 +38,7 @@ function createApoTracer(options) {
38
38
  task_id: taskId ?? "ai-sdk",
39
39
  parent_call_id: observationType === "TOOL" ? currentGenerationSpanId ?? parentSpanId ?? trace.rootSpanId : parentSpanId ?? trace.rootSpanId,
40
40
  step_name: stepName,
41
+ tool_name: observationType === "TOOL" ? String(initialAttrs["ai.toolCall.name"] ?? "unknown") : void 0,
41
42
  model: String(initialAttrs["ai.model.id"] ?? "ai-sdk"),
42
43
  observation_type: observationType,
43
44
  ...input !== void 0 ? { input } : {},
@@ -229,4 +230,4 @@ function extractTextFromAttrs(attrs) {
229
230
  export {
230
231
  createApoTracer
231
232
  };
232
- //# sourceMappingURL=chunk-H7ITLBWU.js.map
233
+ //# sourceMappingURL=chunk-QHRWPVPZ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/agent-task/integrations/ai-sdk.ts"],"sourcesContent":["/**\n * Vercel AI SDK tracing integration for apo.\n *\n * Pass the result of {@link createApoTracer} to `generateText`'s\n * `experimental_telemetry` option. The AI SDK already emits OpenTelemetry\n * spans natively (e.g. `ai.toolCall`, `ai.generateText`); this tracer\n * intercepts them and translates them into apo trace spans, where they\n * become the TOOL/GENERATION observations that `t.calledTool`,\n * `t.noFailedActions`, and `t.messageIncludes` read.\n *\n * The adapter author writes zero manual span code.\n *\n * @example\n * ```ts\n * import { generateText } from \"ai\";\n * import { createApoTracer } from \"@apo-ai/sdk/agent-task\";\n *\n * async sendUserTurn(turn, { trace, parentSpanId }) {\n * const result = await generateText({\n * model, system, messages, tools,\n * experimental_telemetry: createApoTracer({ trace, parentSpanId }),\n * });\n * return { response: result.text };\n * }\n * ```\n *\n * @module\n */\n\nimport type { AgentTaskTraceContext } from \"../tracing.ts\";\nimport { extractTokenUsage } from \"./token-usage.ts\";\n\n// ── OpenTelemetry-compatible types ───────────────────────────────────────\n// We implement the subset of the OTel `Tracer` and `Span` interfaces that the\n// Vercel AI SDK touches (verified from `ai@6` source: `recordSpan` uses\n// `startActiveSpan`; the AI SDK also calls `startSpan` on the default tracer\n// for some operations). We re-declare the structural shape here so the\n// `experimental_telemetry: { tracer }` option type-checks against the AI\n// SDK's `TelemetrySettings` without taking a runtime dep on `@opentelemetry/api`.\n\ntype OTelAttributeValue =\n | string\n | number\n | boolean\n | null\n | undefined\n | Array<string | number | boolean | null | undefined>;\ntype OTelAttributes = Record<string, OTelAttributeValue>;\n\ninterface OTelSpanContext {\n traceId: string;\n spanId: string;\n traceFlags: number;\n}\n\ninterface OTelSpanStatus {\n code: number; // 0 = UNSET, 1 = OK, 2 = ERROR\n message?: string;\n}\n\ninterface OTelException {\n name: string;\n message: string;\n stack?: string;\n}\n\n/**\n * Minimal `Span` implementation. The AI SDK calls:\n * - `setAttributes` / `setAttribute` (tool result, response text)\n * - `setStatus` / `recordException` (errors)\n * - `end()` (finalises the span)\n *\n * Other methods (`addEvent`, `addLink`, `updateName`) are no-ops but must\n * exist on the object for structural type compatibility with the OTel `Span`\n * interface the AI SDK expects.\n */\ninterface ApoSpan {\n spanContext(): OTelSpanContext;\n setAttribute(key: string, value: OTelAttributeValue): this;\n setAttributes(attrs: OTelAttributes): this;\n addEvent(name: string, attributesOrStartTime?: unknown, startTime?: unknown): this;\n addLink(link: unknown): this;\n addLinks(links: unknown[]): this;\n setStatus(status: OTelSpanStatus): this;\n updateName(name: string): this;\n end(endTime?: unknown): void;\n isRecording(): boolean;\n recordException(exception: OTelException | string, time?: unknown): this;\n}\n\n/**\n * Minimal `Tracer` implementation. The AI SDK's `recordSpan` uses\n * `startActiveSpan(name, { attributes }, fn)`. We also implement\n * `startSpan(name, opts)` for structural compatibility — it returns a span\n * without invoking a callback, and the caller is responsible for `.end()`.\n */\ninterface ApoTracer {\n startSpan(name: string, options?: { attributes?: OTelAttributes }): ApoSpan;\n startActiveSpan<T>(\n name: string,\n options: { attributes?: OTelAttributes },\n fn: (span: ApoSpan) => Promise<T> | T,\n ): Promise<T>;\n startActiveSpan<T>(\n name: string,\n options: { attributes?: OTelAttributes },\n context: unknown,\n fn: (span: ApoSpan) => Promise<T> | T,\n ): Promise<T>;\n startActiveSpan<T>(\n name: string,\n fn: (span: ApoSpan) => Promise<T> | T,\n ): Promise<T>;\n}\n\n// ── helpers ──────────────────────────────────────────────────────────────\n\nfunction safeParse(raw: unknown): unknown {\n if (typeof raw !== \"string\") return raw;\n try {\n return JSON.parse(raw);\n } catch {\n return raw;\n }\n}\n\nfunction monotonicNowMs(): number {\n if (typeof performance !== \"undefined\" && typeof performance.now === \"function\") {\n return performance.now();\n }\n return Date.now();\n}\n\nfunction round3(n: number): number {\n return Math.round(n * 1000) / 1000;\n}\n\nconst SPAN_ID_PREFIX = \"ai-sdk-\";\nlet spanCounter = 0;\n\nfunction nextSpanId(): string {\n spanCounter += 1;\n return `${SPAN_ID_PREFIX}${spanCounter}`;\n}\n\n// ── createApoTracer ──────────────────────────────────────────────────────\n\nexport interface CreateApoTracerOptions {\n /** The tee'd trace context from `sendUserTurn`'s second argument. */\n trace: AgentTaskTraceContext;\n /** The parent span id for this turn (from `sendUserTurn`'s context). */\n parentSpanId?: string;\n /** Optional task id, surfaced in span metadata. */\n taskId?: string;\n /** Optional turn number, surfaced in span metadata. */\n turnNumber?: number;\n}\n\n/**\n * Build a `Tracer` compatible with the Vercel AI SDK's\n * `experimental_telemetry.tracer` option. The returned object satisfies the\n * shape `{ isEnabled: true, tracer }` so it can be passed directly to\n * `experimental_telemetry`.\n *\n * The AI SDK emits spans named `ai.generateText`, `ai.generateText.doGenerate`,\n * and `ai.toolCall`. We translate the load-bearing ones:\n *\n * - `ai.toolCall` → a TOOL span (so `t.calledTool(name)` works)\n * - `ai.generateText` → a GENERATION span (so `t.messageIncludes(text)` works)\n * and error status → `t.noFailedActions()` catches it\n *\n * Other spans (`ai.generateText.doGenerate`, etc.) are ignored — only\n * TOOL/AGENT/GENERATION observation types are visible to `t.*` assertions.\n */\nexport function createApoTracer(\n options: CreateApoTracerOptions,\n): { isEnabled: true; tracer: ApoTracer } {\n const { trace, parentSpanId, taskId, turnNumber } = options;\n\n // The generation span id for the current turn. Once `ai.generateText`\n // creates it, subsequent `ai.toolCall` spans should nest under it (the\n // agent calls tools *during* generation), not under the turn span.\n // Without this, every tool renders as a sibling of `agent.generate`\n // instead of a child, flattening the trace hierarchy.\n let currentGenerationSpanId: string | undefined;\n\n /**\n * Build a live span that accumulates attributes and, on `.end()`,\n * calls `trace.createSpan` + `trace.endSpan` with the right observation type.\n */\n function makeLiveSpan(\n name: string,\n observationType: \"TOOL\" | \"GENERATION\",\n initialAttrs: OTelAttributes,\n ): ApoSpan {\n const stepName =\n observationType === \"TOOL\"\n ? String(initialAttrs[\"ai.toolCall.name\"] ?? \"unknown\")\n : \"agent.generate\";\n\n const input =\n observationType === \"TOOL\"\n ? (safeParse(initialAttrs[\"ai.toolCall.args\"]) as Record<string, unknown>)\n : undefined;\n\n const spanId = trace.createSpan({\n task_id: taskId ?? \"ai-sdk\",\n parent_call_id:\n observationType === \"TOOL\"\n ? (currentGenerationSpanId ?? parentSpanId ?? trace.rootSpanId)\n : (parentSpanId ?? trace.rootSpanId),\n step_name: stepName,\n tool_name:\n observationType === \"TOOL\"\n ? String(initialAttrs[\"ai.toolCall.name\"] ?? \"unknown\")\n : undefined,\n model: String(initialAttrs[\"ai.model.id\"] ?? \"ai-sdk\"),\n observation_type: observationType,\n ...(input !== undefined ? { input } : {}),\n metadata: {\n ...(turnNumber !== undefined ? { turnNumber } : {}),\n ...(taskId ? { taskId } : {}),\n },\n });\n\n // Record the generation span id so tool spans created after it nest\n // under it (tools are called during generation).\n if (observationType === \"GENERATION\") {\n currentGenerationSpanId = spanId;\n }\n\n const startedAt = monotonicNowMs();\n let pendingAttrs: OTelAttributes = { ...initialAttrs };\n let status: OTelSpanStatus = { code: 0 };\n let errored = false;\n let ended = false;\n\n const span: ApoSpan = {\n spanContext(): OTelSpanContext {\n return { traceId: trace.runId, spanId, traceFlags: 0 };\n },\n setAttribute(key: string, value: OTelAttributeValue): ApoSpan {\n pendingAttrs[key] = value;\n return span;\n },\n setAttributes(attrs: OTelAttributes): ApoSpan {\n Object.assign(pendingAttrs, attrs);\n return span;\n },\n addEvent(_name: string, _attributesOrStartTime?: unknown, _startTime?: unknown): ApoSpan {\n return span; // no-op — events aren't load-bearing for Flow assertions\n },\n addLink(_link: unknown): ApoSpan {\n return span;\n },\n addLinks(_links: unknown[]): ApoSpan {\n return span;\n },\n setStatus(s: OTelSpanStatus): ApoSpan {\n status = s;\n if (s.code === 2) errored = true;\n return span;\n },\n updateName(_name: string): ApoSpan {\n return span; // name was set at creation time\n },\n end(_endTime?: unknown): void {\n if (ended) return;\n ended = true;\n const latency = round3(monotonicNowMs() - startedAt);\n\n if (observationType === \"TOOL\") {\n const resultRaw = pendingAttrs[\"ai.toolCall.result\"];\n const output = resultRaw !== undefined ? safeParse(resultRaw) : undefined;\n trace.endSpan(spanId, {\n ...(output !== undefined\n ? {\n output:\n output && typeof output === \"object\"\n ? (output as Record<string, unknown>)\n : { value: output },\n }\n : {}),\n latency_ms: latency,\n ...(errored ? { level: \"ERROR\" as const } : {}),\n });\n } else {\n const text = extractTextFromAttrs(pendingAttrs);\n const tokens = extractTokenUsage(pendingAttrs);\n trace.endSpan(spanId, {\n output: text !== undefined ? { text } : {},\n latency_ms: latency,\n ...tokens,\n ...(errored\n ? { level: \"ERROR\" as const, status_message: status.message }\n : {}),\n });\n }\n },\n isRecording(): boolean {\n return !ended;\n },\n recordException(exception: OTelException | string, _time?: unknown): ApoSpan {\n errored = true;\n const msg = typeof exception === \"string\" ? exception : exception.message;\n status = { code: 2, message: msg };\n return span;\n },\n };\n\n return span;\n }\n\n function makeNoopSpan(): ApoSpan {\n const noop: ApoSpan = {\n spanContext: () => ({ traceId: \"\", spanId: nextSpanId(), traceFlags: 0 }),\n setAttribute() {\n return noop;\n },\n setAttributes() {\n return noop;\n },\n addEvent() {\n return noop;\n },\n addLink() {\n return noop;\n },\n addLinks() {\n return noop;\n },\n setStatus() {\n return noop;\n },\n updateName() {\n return noop;\n },\n end() {},\n isRecording: () => false,\n recordException() {\n return noop;\n },\n };\n return noop;\n }\n\n const tracer: ApoTracer = {\n startSpan(name: string, opts?: { attributes?: OTelAttributes }): ApoSpan {\n const attrs = opts?.attributes ?? {};\n const isToolCall = name === \"ai.toolCall\";\n const isGeneration = name === \"ai.generateText\" || name === \"ai.streamText\";\n if (!isToolCall && !isGeneration) return makeNoopSpan();\n return makeLiveSpan(name, isToolCall ? \"TOOL\" : \"GENERATION\", attrs);\n },\n\n startActiveSpan<T>(\n name: string,\n optionsOrFn:\n | { attributes?: OTelAttributes }\n | ((span: ApoSpan) => Promise<T> | T),\n contextOrFn?: ((span: ApoSpan) => Promise<T> | T) | unknown,\n maybeFn?: (span: ApoSpan) => Promise<T> | T,\n ): Promise<T> {\n // Three overload shapes:\n // (name, fn) → optionsOrFn is the fn\n // (name, options, fn) → contextOrFn is the fn\n // (name, options, context, fn) → maybeFn is the fn (context ignored)\n let opts: { attributes?: OTelAttributes };\n let fn: (span: ApoSpan) => Promise<T> | T;\n\n if (typeof optionsOrFn === \"function\") {\n opts = {};\n fn = optionsOrFn;\n } else if (typeof contextOrFn === \"function\") {\n opts = optionsOrFn;\n fn = contextOrFn as (span: ApoSpan) => Promise<T> | T;\n } else {\n opts = optionsOrFn;\n fn = maybeFn!;\n }\n const attrs = opts.attributes ?? {};\n const isToolCall = name === \"ai.toolCall\";\n const isGeneration = name === \"ai.generateText\" || name === \"ai.streamText\";\n\n if (!isToolCall && !isGeneration) {\n const noopSpan = makeNoopSpan();\n return Promise.resolve(fn(noopSpan)).then((result) => {\n noopSpan.end();\n return result;\n });\n }\n\n const span = makeLiveSpan(\n name,\n isToolCall ? \"TOOL\" : \"GENERATION\",\n attrs,\n );\n\n return Promise.resolve(fn(span))\n .then((result) => {\n span.end();\n return result;\n })\n .catch((error) => {\n span.setStatus({\n code: 2,\n message: error instanceof Error ? error.message : String(error),\n });\n span.recordException({\n name: error instanceof Error ? error.name : \"Error\",\n message: error instanceof Error ? error.message : String(error),\n ...(error instanceof Error && error.stack ? { stack: error.stack } : {}),\n });\n span.end();\n throw error;\n });\n },\n };\n\n return { isEnabled: true, tracer };\n}\n\n// ── internals ────────────────────────────────────────────────────────────\n\n/**\n * The AI SDK sets the model's response text under various attribute keys\n * depending on the operation. Try the known ones.\n */\nfunction extractTextFromAttrs(attrs: OTelAttributes): string | undefined {\n const candidates = [\n \"ai.response.text\",\n \"ai.response.output\",\n \"ai.generateText.result\",\n ];\n for (const key of candidates) {\n const val = attrs[key];\n if (typeof val === \"string\" && val.length > 0) {\n const parsed = safeParse(val);\n if (typeof parsed === \"string\") return parsed;\n return val;\n }\n }\n return undefined;\n}\n"],"mappings":"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","names":[]}
@@ -22,6 +22,8 @@ interface CreateSpanParams {
22
22
  step_index?: number;
23
23
  version?: string;
24
24
  model?: string;
25
+ /** Tool name for TOOL observations; emitted as `gen_ai.tool.name`. */
26
+ tool_name?: string;
25
27
  input?: Record<string, unknown>;
26
28
  messages?: Record<string, unknown>[];
27
29
  output?: Record<string, unknown>;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@apo-ai/sdk",
3
- "version": "0.6.0",
4
- "description": "TypeScript SDK for testing and observing AI agents with Apo",
3
+ "version": "0.7.1",
4
+ "description": "TypeScript SDK for executable agent capability specifications and harness engineering with Apo",
5
5
  "keywords": [
6
6
  "ai-agents",
7
7
  "agent-testing",
@@ -75,17 +75,19 @@
75
75
  "publint": "^0.3.22",
76
76
  "tsup": "^8.5.1",
77
77
  "typescript": "^5.0.0",
78
- "vitest": "^4.1.9",
79
- "zod": "^3.22.0"
78
+ "vitest": "^4.1.9"
80
79
  },
81
80
  "dependencies": {
81
+ "@ai-sdk/openai-compatible": "^3.0.48",
82
82
  "@opentelemetry/api": "^1.9.1",
83
83
  "@opentelemetry/context-async-hooks": "^2.9.0",
84
84
  "@opentelemetry/core": "^2.9.0",
85
85
  "@opentelemetry/exporter-trace-otlp-http": "^0.220.0",
86
86
  "@opentelemetry/resources": "^2.9.0",
87
87
  "@opentelemetry/sdk-trace-base": "^2.9.0",
88
- "@opentelemetry/semantic-conventions": "^1.43.0"
88
+ "@opentelemetry/semantic-conventions": "^1.43.0",
89
+ "ai": "^7.0.101",
90
+ "zod": "^3.22.0"
89
91
  },
90
92
  "scripts": {
91
93
  "typecheck": "tsc --noEmit && tsc --noEmit -p tsconfig.type-tests.json",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/agent-task/integrations/ai-sdk.ts"],"sourcesContent":["/**\n * Vercel AI SDK tracing integration for apo.\n *\n * Pass the result of {@link createApoTracer} to `generateText`'s\n * `experimental_telemetry` option. The AI SDK already emits OpenTelemetry\n * spans natively (e.g. `ai.toolCall`, `ai.generateText`); this tracer\n * intercepts them and translates them into apo trace spans, where they\n * become the TOOL/GENERATION observations that `t.calledTool`,\n * `t.noFailedActions`, and `t.messageIncludes` read.\n *\n * The adapter author writes zero manual span code.\n *\n * @example\n * ```ts\n * import { generateText } from \"ai\";\n * import { createApoTracer } from \"@apo-ai/sdk/agent-task\";\n *\n * async sendUserTurn(turn, { trace, parentSpanId }) {\n * const result = await generateText({\n * model, system, messages, tools,\n * experimental_telemetry: createApoTracer({ trace, parentSpanId }),\n * });\n * return { response: result.text };\n * }\n * ```\n *\n * @module\n */\n\nimport type { AgentTaskTraceContext } from \"../tracing.ts\";\nimport { extractTokenUsage } from \"./token-usage.ts\";\n\n// ── OpenTelemetry-compatible types ───────────────────────────────────────\n// We implement the subset of the OTel `Tracer` and `Span` interfaces that the\n// Vercel AI SDK touches (verified from `ai@6` source: `recordSpan` uses\n// `startActiveSpan`; the AI SDK also calls `startSpan` on the default tracer\n// for some operations). We re-declare the structural shape here so the\n// `experimental_telemetry: { tracer }` option type-checks against the AI\n// SDK's `TelemetrySettings` without taking a runtime dep on `@opentelemetry/api`.\n\ntype OTelAttributeValue =\n | string\n | number\n | boolean\n | null\n | undefined\n | Array<string | number | boolean | null | undefined>;\ntype OTelAttributes = Record<string, OTelAttributeValue>;\n\ninterface OTelSpanContext {\n traceId: string;\n spanId: string;\n traceFlags: number;\n}\n\ninterface OTelSpanStatus {\n code: number; // 0 = UNSET, 1 = OK, 2 = ERROR\n message?: string;\n}\n\ninterface OTelException {\n name: string;\n message: string;\n stack?: string;\n}\n\n/**\n * Minimal `Span` implementation. The AI SDK calls:\n * - `setAttributes` / `setAttribute` (tool result, response text)\n * - `setStatus` / `recordException` (errors)\n * - `end()` (finalises the span)\n *\n * Other methods (`addEvent`, `addLink`, `updateName`) are no-ops but must\n * exist on the object for structural type compatibility with the OTel `Span`\n * interface the AI SDK expects.\n */\ninterface ApoSpan {\n spanContext(): OTelSpanContext;\n setAttribute(key: string, value: OTelAttributeValue): this;\n setAttributes(attrs: OTelAttributes): this;\n addEvent(name: string, attributesOrStartTime?: unknown, startTime?: unknown): this;\n addLink(link: unknown): this;\n addLinks(links: unknown[]): this;\n setStatus(status: OTelSpanStatus): this;\n updateName(name: string): this;\n end(endTime?: unknown): void;\n isRecording(): boolean;\n recordException(exception: OTelException | string, time?: unknown): this;\n}\n\n/**\n * Minimal `Tracer` implementation. The AI SDK's `recordSpan` uses\n * `startActiveSpan(name, { attributes }, fn)`. We also implement\n * `startSpan(name, opts)` for structural compatibility — it returns a span\n * without invoking a callback, and the caller is responsible for `.end()`.\n */\ninterface ApoTracer {\n startSpan(name: string, options?: { attributes?: OTelAttributes }): ApoSpan;\n startActiveSpan<T>(\n name: string,\n options: { attributes?: OTelAttributes },\n fn: (span: ApoSpan) => Promise<T> | T,\n ): Promise<T>;\n startActiveSpan<T>(\n name: string,\n options: { attributes?: OTelAttributes },\n context: unknown,\n fn: (span: ApoSpan) => Promise<T> | T,\n ): Promise<T>;\n startActiveSpan<T>(\n name: string,\n fn: (span: ApoSpan) => Promise<T> | T,\n ): Promise<T>;\n}\n\n// ── helpers ──────────────────────────────────────────────────────────────\n\nfunction safeParse(raw: unknown): unknown {\n if (typeof raw !== \"string\") return raw;\n try {\n return JSON.parse(raw);\n } catch {\n return raw;\n }\n}\n\nfunction monotonicNowMs(): number {\n if (typeof performance !== \"undefined\" && typeof performance.now === \"function\") {\n return performance.now();\n }\n return Date.now();\n}\n\nfunction round3(n: number): number {\n return Math.round(n * 1000) / 1000;\n}\n\nconst SPAN_ID_PREFIX = \"ai-sdk-\";\nlet spanCounter = 0;\n\nfunction nextSpanId(): string {\n spanCounter += 1;\n return `${SPAN_ID_PREFIX}${spanCounter}`;\n}\n\n// ── createApoTracer ──────────────────────────────────────────────────────\n\nexport interface CreateApoTracerOptions {\n /** The tee'd trace context from `sendUserTurn`'s second argument. */\n trace: AgentTaskTraceContext;\n /** The parent span id for this turn (from `sendUserTurn`'s context). */\n parentSpanId?: string;\n /** Optional task id, surfaced in span metadata. */\n taskId?: string;\n /** Optional turn number, surfaced in span metadata. */\n turnNumber?: number;\n}\n\n/**\n * Build a `Tracer` compatible with the Vercel AI SDK's\n * `experimental_telemetry.tracer` option. The returned object satisfies the\n * shape `{ isEnabled: true, tracer }` so it can be passed directly to\n * `experimental_telemetry`.\n *\n * The AI SDK emits spans named `ai.generateText`, `ai.generateText.doGenerate`,\n * and `ai.toolCall`. We translate the load-bearing ones:\n *\n * - `ai.toolCall` → a TOOL span (so `t.calledTool(name)` works)\n * - `ai.generateText` → a GENERATION span (so `t.messageIncludes(text)` works)\n * and error status → `t.noFailedActions()` catches it\n *\n * Other spans (`ai.generateText.doGenerate`, etc.) are ignored — only\n * TOOL/AGENT/GENERATION observation types are visible to `t.*` assertions.\n */\nexport function createApoTracer(\n options: CreateApoTracerOptions,\n): { isEnabled: true; tracer: ApoTracer } {\n const { trace, parentSpanId, taskId, turnNumber } = options;\n\n // The generation span id for the current turn. Once `ai.generateText`\n // creates it, subsequent `ai.toolCall` spans should nest under it (the\n // agent calls tools *during* generation), not under the turn span.\n // Without this, every tool renders as a sibling of `agent.generate`\n // instead of a child, flattening the trace hierarchy.\n let currentGenerationSpanId: string | undefined;\n\n /**\n * Build a live span that accumulates attributes and, on `.end()`,\n * calls `trace.createSpan` + `trace.endSpan` with the right observation type.\n */\n function makeLiveSpan(\n name: string,\n observationType: \"TOOL\" | \"GENERATION\",\n initialAttrs: OTelAttributes,\n ): ApoSpan {\n const stepName =\n observationType === \"TOOL\"\n ? String(initialAttrs[\"ai.toolCall.name\"] ?? \"unknown\")\n : \"agent.generate\";\n\n const input =\n observationType === \"TOOL\"\n ? (safeParse(initialAttrs[\"ai.toolCall.args\"]) as Record<string, unknown>)\n : undefined;\n\n const spanId = trace.createSpan({\n task_id: taskId ?? \"ai-sdk\",\n parent_call_id:\n observationType === \"TOOL\"\n ? (currentGenerationSpanId ?? parentSpanId ?? trace.rootSpanId)\n : (parentSpanId ?? trace.rootSpanId),\n step_name: stepName,\n model: String(initialAttrs[\"ai.model.id\"] ?? \"ai-sdk\"),\n observation_type: observationType,\n ...(input !== undefined ? { input } : {}),\n metadata: {\n ...(turnNumber !== undefined ? { turnNumber } : {}),\n ...(taskId ? { taskId } : {}),\n },\n });\n\n // Record the generation span id so tool spans created after it nest\n // under it (tools are called during generation).\n if (observationType === \"GENERATION\") {\n currentGenerationSpanId = spanId;\n }\n\n const startedAt = monotonicNowMs();\n let pendingAttrs: OTelAttributes = { ...initialAttrs };\n let status: OTelSpanStatus = { code: 0 };\n let errored = false;\n let ended = false;\n\n const span: ApoSpan = {\n spanContext(): OTelSpanContext {\n return { traceId: trace.runId, spanId, traceFlags: 0 };\n },\n setAttribute(key: string, value: OTelAttributeValue): ApoSpan {\n pendingAttrs[key] = value;\n return span;\n },\n setAttributes(attrs: OTelAttributes): ApoSpan {\n Object.assign(pendingAttrs, attrs);\n return span;\n },\n addEvent(_name: string, _attributesOrStartTime?: unknown, _startTime?: unknown): ApoSpan {\n return span; // no-op — events aren't load-bearing for Flow assertions\n },\n addLink(_link: unknown): ApoSpan {\n return span;\n },\n addLinks(_links: unknown[]): ApoSpan {\n return span;\n },\n setStatus(s: OTelSpanStatus): ApoSpan {\n status = s;\n if (s.code === 2) errored = true;\n return span;\n },\n updateName(_name: string): ApoSpan {\n return span; // name was set at creation time\n },\n end(_endTime?: unknown): void {\n if (ended) return;\n ended = true;\n const latency = round3(monotonicNowMs() - startedAt);\n\n if (observationType === \"TOOL\") {\n const resultRaw = pendingAttrs[\"ai.toolCall.result\"];\n const output = resultRaw !== undefined ? safeParse(resultRaw) : undefined;\n trace.endSpan(spanId, {\n ...(output !== undefined\n ? {\n output:\n output && typeof output === \"object\"\n ? (output as Record<string, unknown>)\n : { value: output },\n }\n : {}),\n latency_ms: latency,\n ...(errored ? { level: \"ERROR\" as const } : {}),\n });\n } else {\n const text = extractTextFromAttrs(pendingAttrs);\n const tokens = extractTokenUsage(pendingAttrs);\n trace.endSpan(spanId, {\n output: text !== undefined ? { text } : {},\n latency_ms: latency,\n ...tokens,\n ...(errored\n ? { level: \"ERROR\" as const, status_message: status.message }\n : {}),\n });\n }\n },\n isRecording(): boolean {\n return !ended;\n },\n recordException(exception: OTelException | string, _time?: unknown): ApoSpan {\n errored = true;\n const msg = typeof exception === \"string\" ? exception : exception.message;\n status = { code: 2, message: msg };\n return span;\n },\n };\n\n return span;\n }\n\n function makeNoopSpan(): ApoSpan {\n const noop: ApoSpan = {\n spanContext: () => ({ traceId: \"\", spanId: nextSpanId(), traceFlags: 0 }),\n setAttribute() {\n return noop;\n },\n setAttributes() {\n return noop;\n },\n addEvent() {\n return noop;\n },\n addLink() {\n return noop;\n },\n addLinks() {\n return noop;\n },\n setStatus() {\n return noop;\n },\n updateName() {\n return noop;\n },\n end() {},\n isRecording: () => false,\n recordException() {\n return noop;\n },\n };\n return noop;\n }\n\n const tracer: ApoTracer = {\n startSpan(name: string, opts?: { attributes?: OTelAttributes }): ApoSpan {\n const attrs = opts?.attributes ?? {};\n const isToolCall = name === \"ai.toolCall\";\n const isGeneration = name === \"ai.generateText\" || name === \"ai.streamText\";\n if (!isToolCall && !isGeneration) return makeNoopSpan();\n return makeLiveSpan(name, isToolCall ? \"TOOL\" : \"GENERATION\", attrs);\n },\n\n startActiveSpan<T>(\n name: string,\n optionsOrFn:\n | { attributes?: OTelAttributes }\n | ((span: ApoSpan) => Promise<T> | T),\n contextOrFn?: ((span: ApoSpan) => Promise<T> | T) | unknown,\n maybeFn?: (span: ApoSpan) => Promise<T> | T,\n ): Promise<T> {\n // Three overload shapes:\n // (name, fn) → optionsOrFn is the fn\n // (name, options, fn) → contextOrFn is the fn\n // (name, options, context, fn) → maybeFn is the fn (context ignored)\n let opts: { attributes?: OTelAttributes };\n let fn: (span: ApoSpan) => Promise<T> | T;\n\n if (typeof optionsOrFn === \"function\") {\n opts = {};\n fn = optionsOrFn;\n } else if (typeof contextOrFn === \"function\") {\n opts = optionsOrFn;\n fn = contextOrFn as (span: ApoSpan) => Promise<T> | T;\n } else {\n opts = optionsOrFn;\n fn = maybeFn!;\n }\n const attrs = opts.attributes ?? {};\n const isToolCall = name === \"ai.toolCall\";\n const isGeneration = name === \"ai.generateText\" || name === \"ai.streamText\";\n\n if (!isToolCall && !isGeneration) {\n const noopSpan = makeNoopSpan();\n return Promise.resolve(fn(noopSpan)).then((result) => {\n noopSpan.end();\n return result;\n });\n }\n\n const span = makeLiveSpan(\n name,\n isToolCall ? \"TOOL\" : \"GENERATION\",\n attrs,\n );\n\n return Promise.resolve(fn(span))\n .then((result) => {\n span.end();\n return result;\n })\n .catch((error) => {\n span.setStatus({\n code: 2,\n message: error instanceof Error ? error.message : String(error),\n });\n span.recordException({\n name: error instanceof Error ? error.name : \"Error\",\n message: error instanceof Error ? error.message : String(error),\n ...(error instanceof Error && error.stack ? { stack: error.stack } : {}),\n });\n span.end();\n throw error;\n });\n },\n };\n\n return { isEnabled: true, tracer };\n}\n\n// ── internals ────────────────────────────────────────────────────────────\n\n/**\n * The AI SDK sets the model's response text under various attribute keys\n * depending on the operation. Try the known ones.\n */\nfunction extractTextFromAttrs(attrs: OTelAttributes): string | undefined {\n const candidates = [\n \"ai.response.text\",\n \"ai.response.output\",\n \"ai.generateText.result\",\n ];\n for (const key of candidates) {\n const val = attrs[key];\n if (typeof val === \"string\" && val.length > 0) {\n const parsed = safeParse(val);\n if (typeof parsed === \"string\") return parsed;\n return val;\n }\n }\n return undefined;\n}\n"],"mappings":"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","names":[]}