evlog 2.19.2 → 2.21.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 (160) hide show
  1. package/README.md +30 -1
  2. package/dist/adapters/axiom.d.mts +1 -1
  3. package/dist/adapters/axiom.mjs +2 -2
  4. package/dist/adapters/better-stack.d.mts +1 -1
  5. package/dist/adapters/better-stack.mjs +2 -2
  6. package/dist/adapters/datadog.d.mts +1 -1
  7. package/dist/adapters/datadog.mjs +2 -2
  8. package/dist/adapters/fs.d.mts +1 -1
  9. package/dist/adapters/fs.mjs +1 -1
  10. package/dist/adapters/hyperdx.d.mts +1 -1
  11. package/dist/adapters/hyperdx.mjs +1 -1
  12. package/dist/adapters/memory.d.mts +1 -1
  13. package/dist/adapters/memory.mjs +1 -1
  14. package/dist/adapters/otlp.d.mts +1 -1
  15. package/dist/adapters/otlp.mjs +2 -2
  16. package/dist/adapters/posthog.d.mts +1 -1
  17. package/dist/adapters/posthog.mjs +2 -2
  18. package/dist/adapters/sentry.d.mts +1 -1
  19. package/dist/adapters/sentry.mjs +2 -2
  20. package/dist/ai/index.d.mts +39 -16
  21. package/dist/ai/index.d.mts.map +1 -1
  22. package/dist/ai/index.mjs +81 -16
  23. package/dist/ai/index.mjs.map +1 -1
  24. package/dist/asyncStorageScope-DnF7Xk8l.mjs +81 -0
  25. package/dist/asyncStorageScope-DnF7Xk8l.mjs.map +1 -0
  26. package/dist/{audit-CL0q7GJU.mjs → audit-BXCYMjcC.mjs} +21 -6
  27. package/dist/audit-BXCYMjcC.mjs.map +1 -0
  28. package/dist/{audit-CBfv3rl1.d.mts → audit-BZrUvjfW.d.mts} +2 -1
  29. package/dist/{audit-CBfv3rl1.d.mts.map → audit-BZrUvjfW.d.mts.map} +1 -1
  30. package/dist/audit-action.d.mts +1 -1
  31. package/dist/audit.d.mts +1 -1
  32. package/dist/audit.mjs +1 -1
  33. package/dist/better-auth/index.d.mts +1 -1
  34. package/dist/browser.d.mts +1 -1
  35. package/dist/{catalog-CyemiLYG.d.mts → catalog-1cQnNxR2.d.mts} +3 -3
  36. package/dist/{catalog-CyemiLYG.d.mts.map → catalog-1cQnNxR2.d.mts.map} +1 -1
  37. package/dist/catalog.d.mts +1 -1
  38. package/dist/{define-BuDJpV2b.d.mts → define-Ct2QyrE9.d.mts} +3 -3
  39. package/dist/{define-BuDJpV2b.d.mts.map → define-Ct2QyrE9.d.mts.map} +1 -1
  40. package/dist/{drain-6lV-g7JB.mjs → drain-AD7Mn_5_.mjs} +3 -3
  41. package/dist/{drain-6lV-g7JB.mjs.map → drain-AD7Mn_5_.mjs.map} +1 -1
  42. package/dist/elysia/index.d.mts +6 -5
  43. package/dist/elysia/index.d.mts.map +1 -1
  44. package/dist/elysia/index.mjs +11 -8
  45. package/dist/elysia/index.mjs.map +1 -1
  46. package/dist/{enrich-error-stack-next.node-BecxSKUc.mjs → enrich-error-stack-next.node-KB0Oap7R.mjs} +1 -1
  47. package/dist/{enrich-error-stack-next.node-BecxSKUc.mjs.map → enrich-error-stack-next.node-KB0Oap7R.mjs.map} +1 -1
  48. package/dist/{enricher-VdBVX4oT.d.mts → enricher-BOzt2FCi.d.mts} +2 -2
  49. package/dist/{enricher-VdBVX4oT.d.mts.map → enricher-BOzt2FCi.d.mts.map} +1 -1
  50. package/dist/enrichers.d.mts +2 -2
  51. package/dist/{error-CPCM5a1D.d.mts → error-BRymuv8U.d.mts} +2 -2
  52. package/dist/{error-CPCM5a1D.d.mts.map → error-BRymuv8U.d.mts.map} +1 -1
  53. package/dist/error.d.mts +1 -1
  54. package/dist/{errors-ZdY6Ohi6.d.mts → errors-C7qA6CAi.d.mts} +2 -2
  55. package/dist/{errors-ZdY6Ohi6.d.mts.map → errors-C7qA6CAi.d.mts.map} +1 -1
  56. package/dist/eve/index.d.mts +72 -0
  57. package/dist/eve/index.d.mts.map +1 -0
  58. package/dist/eve/index.mjs +593 -0
  59. package/dist/eve/index.mjs.map +1 -0
  60. package/dist/express/index.d.mts +2 -2
  61. package/dist/express/index.mjs +3 -3
  62. package/dist/fastify/index.d.mts +2 -2
  63. package/dist/fastify/index.mjs +3 -3
  64. package/dist/fork-S9gpZmF8.mjs +72 -0
  65. package/dist/fork-S9gpZmF8.mjs.map +1 -0
  66. package/dist/hono/index.d.mts +2 -2
  67. package/dist/hono/index.mjs +1 -1
  68. package/dist/{http-DA3RWQrm.mjs → http-CuZ6Cm2M.mjs} +2 -2
  69. package/dist/{http-DA3RWQrm.mjs.map → http-CuZ6Cm2M.mjs.map} +1 -1
  70. package/dist/http.d.mts +1 -1
  71. package/dist/index.d.mts +8 -8
  72. package/dist/index.mjs +1 -1
  73. package/dist/{instrumentation-create-STGmQGQt.d.mts → instrumentation-create-gSDk0leX.d.mts} +2 -2
  74. package/dist/{instrumentation-create-STGmQGQt.d.mts.map → instrumentation-create-gSDk0leX.d.mts.map} +1 -1
  75. package/dist/{integration-2PpI-bCm.mjs → integration-CeL90DtN.mjs} +3 -2
  76. package/dist/{integration-2PpI-bCm.mjs.map → integration-CeL90DtN.mjs.map} +1 -1
  77. package/dist/{logger-DWTPZ0c1.d.mts → logger-CxWz7QJ3.d.mts} +7 -3
  78. package/dist/logger-CxWz7QJ3.d.mts.map +1 -0
  79. package/dist/logger.d.mts +2 -2
  80. package/dist/logger.mjs +2 -2
  81. package/dist/{fork-Di44utlp.mjs → middleware-Cdebyfdt.mjs} +3 -69
  82. package/dist/middleware-Cdebyfdt.mjs.map +1 -0
  83. package/dist/{middleware-CjVyGyrG.d.mts → middleware-fbtEs6bq.d.mts} +2 -2
  84. package/dist/{middleware-CjVyGyrG.d.mts.map → middleware-fbtEs6bq.d.mts.map} +1 -1
  85. package/dist/nestjs/index.d.mts +2 -2
  86. package/dist/nestjs/index.mjs +4 -3
  87. package/dist/nestjs/index.mjs.map +1 -1
  88. package/dist/next/client.d.mts +1 -1
  89. package/dist/next/index.d.mts +4 -4
  90. package/dist/next/index.d.mts.map +1 -1
  91. package/dist/next/index.mjs +4 -3
  92. package/dist/next/index.mjs.map +1 -1
  93. package/dist/next/instrumentation/create.d.mts +1 -1
  94. package/dist/next/instrumentation/create.mjs +1 -1
  95. package/dist/next/instrumentation.d.mts +1 -1
  96. package/dist/next/stream.d.mts +1 -1
  97. package/dist/nitro/errorHandler.mjs +1 -1
  98. package/dist/nitro/module.d.mts +2 -2
  99. package/dist/nitro/module.mjs +1 -1
  100. package/dist/nitro/plugin.mjs +2 -2
  101. package/dist/nitro/v3/errorHandler.mjs +1 -1
  102. package/dist/nitro/v3/index.d.mts +2 -2
  103. package/dist/nitro/v3/module.d.mts +1 -1
  104. package/dist/nitro/v3/module.mjs +1 -1
  105. package/dist/nitro/v3/plugin.mjs +2 -2
  106. package/dist/nitro/v3/useLogger.d.mts +1 -1
  107. package/dist/{nitro--E4op34w.d.mts → nitro-CcN7HDnr.d.mts} +2 -2
  108. package/dist/{nitro--E4op34w.d.mts.map → nitro-CcN7HDnr.d.mts.map} +1 -1
  109. package/dist/{nitro-ClHmVj1H.mjs → nitro-DYFCSlho.mjs} +2 -2
  110. package/dist/{nitro-ClHmVj1H.mjs.map → nitro-DYFCSlho.mjs.map} +1 -1
  111. package/dist/{nitroConfigBridge-BkVWnSV3.mjs → nitroConfigBridge-lHPsE5zG.mjs} +8 -6
  112. package/dist/nitroConfigBridge-lHPsE5zG.mjs.map +1 -0
  113. package/dist/{nodeResponse-DKrYWrxk.mjs → nodeResponse-Dsl7gZUf.mjs} +1 -1
  114. package/dist/{nodeResponse-DKrYWrxk.mjs.map → nodeResponse-Dsl7gZUf.mjs.map} +1 -1
  115. package/dist/nuxt/module.d.mts +1 -1
  116. package/dist/nuxt/module.d.mts.map +1 -1
  117. package/dist/nuxt/module.mjs +44 -12
  118. package/dist/nuxt/module.mjs.map +1 -1
  119. package/dist/orpc/index.d.mts +2 -2
  120. package/dist/orpc/index.mjs +2 -2
  121. package/dist/{package-Bgs0VSDF.mjs → package-DtGhNwVN.mjs} +2 -2
  122. package/dist/package-DtGhNwVN.mjs.map +1 -0
  123. package/dist/{parseError-BwBg-iW4.d.mts → parseError-BmoJ7XeF.d.mts} +2 -2
  124. package/dist/parseError-BmoJ7XeF.d.mts.map +1 -0
  125. package/dist/react-router/index.d.mts +2 -2
  126. package/dist/react-router/index.mjs +3 -2
  127. package/dist/react-router/index.mjs.map +1 -1
  128. package/dist/{register-disk-snippet-B_JyhodL.mjs → register-disk-snippet-B5svlT8-.mjs} +1 -1
  129. package/dist/{register-disk-snippet-B_JyhodL.mjs.map → register-disk-snippet-B5svlT8-.mjs.map} +1 -1
  130. package/dist/runtime/client/log.d.mts +1 -1
  131. package/dist/runtime/server/routes/_evlog/ingest.post.mjs +1 -1
  132. package/dist/runtime/server/useLogger.d.mts +1 -1
  133. package/dist/runtime/utils/parseError.d.mts +2 -2
  134. package/dist/{storage-D4sBVffQ.mjs → storage-BeFdoGTI.mjs} +1 -1
  135. package/dist/{storage-D4sBVffQ.mjs.map → storage-BeFdoGTI.mjs.map} +1 -1
  136. package/dist/stream.d.mts +1 -1
  137. package/dist/stream.d.mts.map +1 -1
  138. package/dist/stream.mjs +7 -1
  139. package/dist/stream.mjs.map +1 -1
  140. package/dist/sveltekit/index.d.mts +2 -2
  141. package/dist/sveltekit/index.mjs +5 -4
  142. package/dist/sveltekit/index.mjs.map +1 -1
  143. package/dist/toolkit.d.mts +5 -5
  144. package/dist/toolkit.mjs +7 -6
  145. package/dist/types.d.mts +1 -1
  146. package/dist/{useLogger-DiRWGD33.d.mts → useLogger-BupVLO9_.d.mts} +2 -2
  147. package/dist/{useLogger-DiRWGD33.d.mts.map → useLogger-BupVLO9_.d.mts.map} +1 -1
  148. package/dist/{utils-XPMPENUK.d.mts → utils-D_LY-J1k.d.mts} +2 -2
  149. package/dist/{utils-XPMPENUK.d.mts.map → utils-D_LY-J1k.d.mts.map} +1 -1
  150. package/dist/utils.d.mts +1 -1
  151. package/dist/vite/index.d.mts +1 -1
  152. package/dist/workers.d.mts +1 -1
  153. package/dist/workers.mjs +1 -1
  154. package/package.json +17 -2
  155. package/dist/audit-CL0q7GJU.mjs.map +0 -1
  156. package/dist/fork-Di44utlp.mjs.map +0 -1
  157. package/dist/logger-DWTPZ0c1.d.mts.map +0 -1
  158. package/dist/nitroConfigBridge-BkVWnSV3.mjs.map +0 -1
  159. package/dist/package-Bgs0VSDF.mjs.map +0 -1
  160. package/dist/parseError-BwBg-iW4.d.mts.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../src/ai/index.ts"],"sourcesContent":["import { gateway, wrapLanguageModel, bindTelemetryIntegration } from 'ai'\nimport type { GatewayModelId, TelemetryIntegration, OnStartEvent, OnToolCallFinishEvent, OnFinishEvent } from 'ai'\nimport type { LanguageModelV3, LanguageModelV3Middleware, LanguageModelV3StreamPart } from '@ai-sdk/provider'\nimport type { RequestLogger } from '../types'\n\n/**\n * Fine-grained control over tool call input capture.\n */\nexport interface ToolInputsOptions {\n /**\n * Max character length for the stringified input JSON.\n * Inputs exceeding this limit are truncated with a `…` suffix.\n */\n maxLength?: number\n /**\n * Custom transform applied to each captured input before storing.\n * Receives the parsed input and tool name; return value is stored.\n * Runs before `maxLength` truncation.\n */\n transform?: (input: unknown, toolName: string) => unknown\n}\n\n/**\n * Pricing entry for a single model: cost per 1 million tokens in dollars.\n */\nexport interface ModelCost {\n input: number\n output: number\n}\n\n/**\n * Options for `createAILogger` and `createAIMiddleware`.\n */\nexport interface AILoggerOptions {\n /**\n * When enabled, `toolCalls` contains `{ name, input }` objects instead of plain tool name strings.\n * Opt-in because inputs can be large and may contain sensitive data.\n *\n * - `true` — capture all inputs as-is\n * - `{ maxLength, transform }` — capture with truncation or custom transform\n * @default false\n */\n toolInputs?: boolean | ToolInputsOptions\n /**\n * Pricing map for estimating request cost.\n * Keys are model IDs (e.g. `'claude-sonnet-4.6'`, `'gpt-4o'`), values are\n * `{ input, output }` in dollars per 1M tokens.\n *\n * When provided, the wide event includes `ai.estimatedCost` (in dollars).\n *\n * @example\n * ```ts\n * const ai = createAILogger(log, {\n * cost: {\n * 'claude-sonnet-4.6': { input: 3, output: 15 },\n * 'gpt-4o': { input: 2.5, output: 10 },\n * },\n * })\n * ```\n */\n cost?: Record<string, ModelCost>\n}\n\n/**\n * Per-step token usage breakdown for multi-step agent runs.\n */\nexport interface AIStepUsage {\n model: string\n inputTokens: number\n outputTokens: number\n toolCalls?: string[]\n}\n\n/**\n * Tool execution detail captured via `TelemetryIntegration`.\n */\nexport interface AIToolExecution {\n name: string\n durationMs: number\n success: boolean\n error?: string\n}\n\n/**\n * Embedding metadata captured via `captureEmbed`.\n */\nexport interface AIEmbeddingData {\n model?: string\n tokens: number\n dimensions?: number\n count?: number\n}\n\n/**\n * Shape of the `ai` field written to the wide event, and the public\n * snapshot returned by `AILogger.getMetadata()`.\n *\n * `model` and `provider` are populated after the first model call.\n * They may be undefined when only `captureEmbed` has been called or\n * before any AI activity has happened.\n */\nexport interface AIEventData {\n calls: number\n model?: string\n models?: string[]\n provider?: string\n inputTokens: number\n outputTokens: number\n totalTokens: number\n cacheReadTokens?: number\n cacheWriteTokens?: number\n reasoningTokens?: number\n finishReason?: string\n toolCalls?: string[] | Array<{ name: string, input: unknown }>\n responseId?: string\n steps?: number\n stepsUsage?: AIStepUsage[]\n msToFirstChunk?: number\n msToFinish?: number\n tokensPerSecond?: number\n error?: string\n tools?: AIToolExecution[]\n totalDurationMs?: number\n embedding?: AIEmbeddingData\n estimatedCost?: number\n}\n\n/**\n * Public alias for the metadata snapshot returned by `AILogger.getMetadata()`.\n *\n * Mirrors the shape of the `ai` field on the wide event, so the same object\n * can be persisted, surfaced to end-users, or compared across runs.\n */\nexport type AIMetadata = AIEventData\n\n/**\n * Callback fired on every metadata update (per step, per embed, on error,\n * and on integration completion). Receives a structured snapshot.\n */\nexport type AIMetadataListener = (metadata: AIMetadata) => void\n\nexport interface AILogger {\n /**\n * Wrap a language model with evlog middleware.\n * All `generateText` and `streamText` calls using the wrapped model\n * are captured automatically into the wide event.\n *\n * Accepts a `LanguageModelV3` object or a model string (e.g. `'anthropic/claude-sonnet-4.6'`).\n * Strings are resolved via the AI SDK gateway.\n *\n * Also works with pre-wrapped models (e.g. from supermemory, guardrails):\n * `ai.wrap(withSupermemory(base, orgId))` composes correctly.\n *\n * @example\n * ```ts\n * const ai = createAILogger(log)\n * const model = ai.wrap('anthropic/claude-sonnet-4.6')\n *\n * // Also accepts a model object\n * const model = ai.wrap(anthropic('claude-sonnet-4.6'))\n * ```\n */\n wrap: (model: LanguageModelV3 | GatewayModelId) => LanguageModelV3\n\n /**\n * Manually capture token usage from an `embed()` or `embedMany()` result.\n * Embedding models use a different type than language models, so they\n * cannot be wrapped with middleware.\n *\n * @example\n * ```ts\n * const { embedding, usage } = await embed({ model: embeddingModel, value: query })\n * ai.captureEmbed({ usage })\n *\n * // With model info (v2)\n * ai.captureEmbed({ usage, model: 'text-embedding-3-small', dimensions: 1536 })\n *\n * // After embedMany\n * ai.captureEmbed({ usage, count: texts.length })\n * ```\n */\n captureEmbed: (result: {\n usage: { tokens: number }\n model?: string\n dimensions?: number\n count?: number\n }) => void\n\n /**\n * Get a snapshot of the current AI execution metadata.\n *\n * Returns the same structured object that is written to the `ai` field\n * of the wide event. Safe to call at any time — including inside the\n * AI SDK's `onFinish` callback, after `await generateText()`, or while a\n * stream is in progress.\n *\n * The returned snapshot is a fresh copy: mutating it does not affect\n * subsequent calls or the underlying state.\n *\n * @example Persist execution history after a run\n * ```ts\n * const ai = createAILogger(log, { cost: { ... } })\n *\n * await generateText({ model: ai.wrap('anthropic/claude-sonnet-4.6'), prompt })\n *\n * const metadata = ai.getMetadata()\n * await db.insert('ai_runs', metadata)\n * ```\n *\n * @example Surface usage to end-users in a streaming response\n * ```ts\n * const result = streamText({\n * model: ai.wrap('anthropic/claude-sonnet-4.6'),\n * messages,\n * onFinish: () => {\n * const { totalTokens, estimatedCost } = ai.getMetadata()\n * trackUsage(userId, { totalTokens, estimatedCost })\n * },\n * })\n * ```\n */\n getMetadata: () => AIMetadata\n\n /**\n * Get the current estimated cost in dollars.\n *\n * Returns `undefined` if no `cost` map was provided to `createAILogger`,\n * or if the model is not in the pricing map.\n *\n * Convenience for `getMetadata().estimatedCost`.\n *\n * @example\n * ```ts\n * const ai = createAILogger(log, {\n * cost: { 'claude-sonnet-4.6': { input: 3, output: 15 } },\n * })\n *\n * await generateText({ model: ai.wrap('anthropic/claude-sonnet-4.6'), prompt })\n *\n * console.log(`Cost: $${ai.getEstimatedCost()?.toFixed(4)}`)\n * ```\n */\n getEstimatedCost: () => number | undefined\n\n /**\n * Subscribe to metadata updates.\n *\n * The callback fires every time the underlying state flushes — once per\n * step (in multi-step agent runs), once per `captureEmbed` call, on\n * model errors, and once on `createEvlogIntegration`'s `onFinish`.\n *\n * Each invocation receives a fresh snapshot (same shape as `getMetadata`).\n * Returns an unsubscribe function.\n *\n * @example Stream incremental usage updates to the client\n * ```ts\n * const ai = createAILogger(log)\n *\n * ai.onUpdate((metadata) => {\n * pushToClient({ type: 'ai-progress', metadata })\n * })\n *\n * const result = streamText({ model: ai.wrap('...'), messages })\n * ```\n *\n * @example Cleanup\n * ```ts\n * const off = ai.onUpdate((metadata) => { ... })\n * // later\n * off()\n * ```\n */\n onUpdate: (callback: AIMetadataListener) => () => void\n\n /**\n * Internal accumulator state exposed for `createEvlogIntegration` to share.\n * @internal\n */\n _state: AccumulatorState\n}\n\ninterface UsageAccumulator {\n inputTokens: number\n outputTokens: number\n cacheReadTokens: number\n cacheWriteTokens: number\n reasoningTokens: number\n}\n\nfunction addUsage(\n acc: UsageAccumulator,\n usage: {\n inputTokens: { total: number | undefined, cacheRead?: number | undefined, cacheWrite?: number | undefined }\n outputTokens: { total: number | undefined, reasoning?: number | undefined }\n },\n): void {\n acc.inputTokens += usage.inputTokens.total ?? 0\n acc.outputTokens += usage.outputTokens.total ?? 0\n acc.cacheReadTokens += usage.inputTokens.cacheRead ?? 0\n acc.cacheWriteTokens += usage.inputTokens.cacheWrite ?? 0\n acc.reasoningTokens += usage.outputTokens.reasoning ?? 0\n}\n\n/**\n * When using `gateway('google/gemini-3-flash')`, the model object has\n * `provider: 'gateway'` and `modelId: 'google/gemini-3-flash'`.\n * This extracts the real provider and model name from the modelId.\n */\nfunction resolveProviderAndModel(provider: string, modelId: string): { provider: string, model: string } {\n if (provider !== 'gateway' || !modelId.includes('/')) {\n return { provider, model: modelId }\n }\n const slashIndex = modelId.indexOf('/')\n return {\n provider: modelId.slice(0, slashIndex),\n model: modelId.slice(slashIndex + 1),\n }\n}\n\n/**\n * Create the evlog AI middleware that captures AI SDK data into a wide event.\n *\n * Use this when you need explicit middleware composition with other wrappers\n * (e.g. supermemory, guardrails). For most cases, use `createAILogger` instead.\n *\n * Note: `captureEmbed` is not available with the raw middleware — use\n * `createAILogger` if you need embedding capture.\n *\n * @example Nuxt API route with supermemory\n * ```ts\n * import { createAIMiddleware } from 'evlog/ai'\n * import { wrapLanguageModel } from 'ai'\n *\n * export default defineEventHandler(async (event) => {\n * const log = useLogger(event)\n *\n * const model = wrapLanguageModel({\n * model: withSupermemory(base, orgId),\n * middleware: [createAIMiddleware(log, { toolInputs: true })],\n * })\n * })\n * ```\n */\nexport function createAIMiddleware(log: RequestLogger, options?: AILoggerOptions): LanguageModelV3Middleware {\n return buildMiddleware(log, options)\n}\n\n/**\n * Create an AI logger that captures AI SDK data into the wide event.\n *\n * Uses model middleware (`wrapLanguageModel`) to transparently intercept\n * all LLM calls. `onFinish` and `onStepFinish` remain free for user code.\n *\n * @example\n * ```ts\n * import { createAILogger } from 'evlog/ai'\n *\n * const log = useLogger(event)\n * const ai = createAILogger(log)\n * const model = ai.wrap('anthropic/claude-sonnet-4.6')\n *\n * const result = streamText({\n * model,\n * messages,\n * onFinish: ({ text }) => saveConversation(text),\n * })\n * ```\n *\n * @example Capture tool call inputs\n * ```ts\n * const ai = createAILogger(log, { toolInputs: true })\n * ```\n */\nexport function createAILogger(log: RequestLogger, options?: AILoggerOptions): AILogger {\n const state = createAccumulatorState(options)\n state._log = log\n const middleware = buildMiddlewareFromState(log, state)\n\n return {\n wrap: (model: LanguageModelV3 | GatewayModelId) => {\n const resolved = typeof model === 'string' ? gateway(model) : model\n return wrapLanguageModel({ model: resolved, middleware })\n },\n\n captureEmbed: (result: {\n usage: { tokens: number }\n model?: string\n dimensions?: number\n count?: number\n }) => {\n state.calls++\n state.usage.inputTokens += result.usage.tokens\n state.embedding = {\n tokens: (state.embedding?.tokens ?? 0) + result.usage.tokens,\n ...(result.model ? { model: result.model } : state.embedding?.model ? { model: state.embedding.model } : {}),\n ...(result.dimensions ? { dimensions: result.dimensions } : state.embedding?.dimensions ? { dimensions: state.embedding.dimensions } : {}),\n ...(result.count ? { count: (state.embedding?.count ?? 0) + result.count } : state.embedding?.count ? { count: state.embedding.count } : {}),\n }\n flushState(log, state)\n },\n\n getMetadata: () => buildMetadata(state),\n\n getEstimatedCost: () => computeEstimatedCost(state),\n\n onUpdate: (callback: AIMetadataListener) => {\n state.subscribers.add(callback)\n return () => {\n state.subscribers.delete(callback)\n }\n },\n\n _state: state,\n }\n}\n\n/**\n * Snapshot of how much of each cumulative array we've already sent to the\n * wide event. `flushState` reads it to compute the delta and updates it in\n * place; passing zero-watermarks to `buildMetadata` yields a full snapshot.\n */\ninterface Watermarks {\n toolCalls: number\n toolCallInputs: number\n stepsUsage: number\n toolExecutions: number\n models: Set<string>\n}\n\nfunction freshWatermarks(): Watermarks {\n return { toolCalls: 0, toolCallInputs: 0, stepsUsage: 0, toolExecutions: 0, models: new Set() }\n}\n\ninterface AccumulatorState {\n calls: number\n steps: number\n usage: UsageAccumulator\n models: string[]\n lastProvider: string | undefined\n allToolCalls: string[]\n allToolCallInputs: Array<{ name: string, input: unknown }>\n stepsUsage: AIStepUsage[]\n lastFinishReason: string | undefined\n lastMsToFirstChunk: number | undefined\n lastMsToFinish: number | undefined\n lastError: string | undefined\n lastResponseId: string | undefined\n toolInputs: boolean\n toolInputsOptions: ToolInputsOptions | undefined\n toolExecutions: AIToolExecution[]\n generationStartTime: number | undefined\n totalDurationMs: number | undefined\n embedding: AIEmbeddingData | undefined\n costMap: Record<string, ModelCost> | undefined\n subscribers: Set<AIMetadataListener>\n /** @internal What's already been written to the wide event. */\n _flushed: Watermarks\n /** @internal Logger reference for integration flush */\n _log?: RequestLogger\n}\n\nfunction resolveToolInputs(raw?: boolean | ToolInputsOptions): { enabled: boolean, options: ToolInputsOptions | undefined } {\n if (!raw) return { enabled: false, options: undefined }\n if (raw === true) return { enabled: true, options: undefined }\n return { enabled: true, options: raw }\n}\n\nfunction processToolInput(input: unknown, toolName: string, options: ToolInputsOptions | undefined): unknown {\n let value = input\n if (options?.transform) {\n value = options.transform(value, toolName)\n }\n if (options?.maxLength) {\n const str = typeof value === 'string' ? value : JSON.stringify(value)\n if (str.length > options.maxLength) {\n return `${str.slice(0, options.maxLength)}…`\n }\n }\n return value\n}\n\nfunction createAccumulatorState(options?: AILoggerOptions): AccumulatorState {\n const { enabled, options: captureOpts } = resolveToolInputs(options?.toolInputs)\n return {\n calls: 0,\n steps: 0,\n usage: {\n inputTokens: 0,\n outputTokens: 0,\n cacheReadTokens: 0,\n cacheWriteTokens: 0,\n reasoningTokens: 0,\n },\n models: [],\n lastProvider: undefined,\n allToolCalls: [],\n allToolCallInputs: [],\n stepsUsage: [],\n lastFinishReason: undefined,\n lastMsToFirstChunk: undefined,\n lastMsToFinish: undefined,\n lastError: undefined,\n lastResponseId: undefined,\n toolInputs: enabled,\n toolInputsOptions: captureOpts,\n toolExecutions: [],\n generationStartTime: undefined,\n totalDurationMs: undefined,\n embedding: undefined,\n costMap: options?.cost,\n subscribers: new Set(),\n _flushed: freshWatermarks(),\n }\n}\n\nfunction computeEstimatedCost(state: AccumulatorState): number | undefined {\n if (!state.costMap) return undefined\n const lastModel = state.models[state.models.length - 1]\n if (!lastModel) return undefined\n const pricing = state.costMap[lastModel]\n if (!pricing) return undefined\n const inputCost = (state.usage.inputTokens / 1_000_000) * pricing.input\n const outputCost = (state.usage.outputTokens / 1_000_000) * pricing.output\n const total = inputCost + outputCost\n return total > 0 ? Math.round(total * 1_000_000) / 1_000_000 : undefined\n}\n\n/**\n * Build the `ai.*` payload from the accumulator. Pass `since` to emit\n * only the array entries appended after that watermark (used by\n * `flushState` to avoid quadratic growth on the wide event, since evlog's\n * `mergeInto` concatenates arrays). The default — fresh watermarks —\n * yields the full cumulative snapshot used by `getMetadata()` and\n * delivered to `onUpdate` subscribers.\n */\nfunction buildMetadata(state: AccumulatorState, since: Watermarks = freshWatermarks()): AIMetadata {\n const lastModel = state.models[state.models.length - 1]\n\n const data: AIMetadata = {\n calls: state.calls,\n inputTokens: state.usage.inputTokens,\n outputTokens: state.usage.outputTokens,\n totalTokens: state.usage.inputTokens + state.usage.outputTokens,\n }\n\n if (lastModel) data.model = lastModel\n if (state.lastProvider) data.provider = state.lastProvider\n if (state.usage.cacheReadTokens > 0) data.cacheReadTokens = state.usage.cacheReadTokens\n if (state.usage.cacheWriteTokens > 0) data.cacheWriteTokens = state.usage.cacheWriteTokens\n if (state.usage.reasoningTokens > 0) data.reasoningTokens = state.usage.reasoningTokens\n if (state.lastFinishReason) data.finishReason = state.lastFinishReason\n if (state.lastResponseId) data.responseId = state.lastResponseId\n if (state.lastMsToFirstChunk !== undefined) data.msToFirstChunk = state.lastMsToFirstChunk\n if (state.lastMsToFinish !== undefined) {\n data.msToFinish = state.lastMsToFinish\n if (state.usage.outputTokens > 0 && state.lastMsToFinish > 0) {\n data.tokensPerSecond = Math.round((state.usage.outputTokens / state.lastMsToFinish) * 1000)\n }\n }\n if (state.lastError) data.error = state.lastError\n if (state.totalDurationMs !== undefined) data.totalDurationMs = state.totalDurationMs\n if (state.embedding) data.embedding = { ...state.embedding }\n const cost = computeEstimatedCost(state)\n if (cost !== undefined) data.estimatedCost = cost\n\n if (state.toolInputs) {\n if (state.allToolCallInputs.length > since.toolCallInputs) {\n data.toolCalls = state.allToolCallInputs.slice(since.toolCallInputs).map(t => ({ ...t }))\n }\n } else if (state.allToolCalls.length > since.toolCalls) {\n data.toolCalls = state.allToolCalls.slice(since.toolCalls)\n }\n\n if (state.steps > 1 && state.stepsUsage.length > since.stepsUsage) {\n data.steps = state.steps\n data.stepsUsage = state.stepsUsage\n .slice(since.stepsUsage)\n .map(s => ({ ...s, ...(s.toolCalls ? { toolCalls: [...s.toolCalls] } : {}) }))\n }\n\n if (state.toolExecutions.length > since.toolExecutions) {\n data.tools = state.toolExecutions.slice(since.toolExecutions).map(t => ({ ...t }))\n }\n\n // `models` is only emitted once a second distinct model appears, then\n // deduplicated across flushes so the merged wide event stays unique.\n const uniqueModels = new Set(state.models)\n if (uniqueModels.size > 1) {\n const newModels = [...uniqueModels].filter(m => !since.models.has(m))\n if (newModels.length > 0) data.models = newModels\n }\n\n return data\n}\n\nfunction notifySubscribers(state: AccumulatorState, metadata: AIMetadata): void {\n if (state.subscribers.size === 0) return\n for (const subscriber of state.subscribers) {\n try {\n subscriber(metadata)\n } catch {\n // Subscribers must not break the AI flow.\n }\n }\n}\n\n/**\n * Push the accumulator's latest changes onto the wide event using delta\n * semantics for arrays, then notify subscribers with the full snapshot.\n */\nfunction flushState(log: RequestLogger, state: AccumulatorState): void {\n const flushed = state._flushed\n const data = buildMetadata(state, flushed)\n\n flushed.toolCalls = state.allToolCalls.length\n flushed.toolCallInputs = state.allToolCallInputs.length\n flushed.toolExecutions = state.toolExecutions.length\n if (data.stepsUsage) flushed.stepsUsage = state.stepsUsage.length\n if (data.models) for (const m of data.models) flushed.models.add(m)\n\n log.set({ ai: data } as Record<string, unknown>)\n\n if (state.subscribers.size > 0) {\n notifySubscribers(state, buildMetadata(state))\n }\n}\n\nfunction recordModel(state: AccumulatorState, provider: string, modelId: string, responseModelId?: string): void {\n const resolved = resolveProviderAndModel(provider, responseModelId ?? modelId)\n state.models.push(resolved.model)\n state.lastProvider = resolved.provider\n}\n\nfunction safeParseJSON(input: string): unknown {\n try {\n return JSON.parse(input)\n } catch {\n return input\n }\n}\n\nfunction recordError(log: RequestLogger, state: AccumulatorState, model: { provider: string, modelId: string }, error: unknown): void {\n state.calls++\n state.steps++\n recordModel(state, model.provider, model.modelId)\n state.lastFinishReason = 'error'\n state.lastError = error instanceof Error ? error.message : String(error)\n\n const resolved = resolveProviderAndModel(model.provider, model.modelId)\n state.stepsUsage.push({\n model: resolved.model,\n inputTokens: 0,\n outputTokens: 0,\n })\n\n flushState(log, state)\n}\n\nfunction buildMiddleware(log: RequestLogger, options?: AILoggerOptions): LanguageModelV3Middleware {\n const state = createAccumulatorState(options)\n state._log = log\n return buildMiddlewareFromState(log, state)\n}\n\nfunction buildMiddlewareFromState(log: RequestLogger, state: AccumulatorState): LanguageModelV3Middleware {\n return {\n specificationVersion: 'v3',\n wrapGenerate: async ({ doGenerate, model }) => {\n try {\n const result = await doGenerate()\n\n state.calls++\n state.steps++\n addUsage(state.usage, result.usage)\n recordModel(state, model.provider, model.modelId, result.response?.modelId)\n state.lastFinishReason = result.finishReason.unified\n\n if (result.response?.id) {\n state.lastResponseId = result.response.id\n }\n\n const stepToolCalls: string[] = []\n for (const item of result.content) {\n if (item.type === 'tool-call') {\n state.allToolCalls.push(item.toolName)\n stepToolCalls.push(item.toolName)\n if (state.toolInputs) {\n const raw = typeof item.input === 'string' ? safeParseJSON(item.input) : item.input\n state.allToolCallInputs.push({\n name: item.toolName,\n input: processToolInput(raw, item.toolName, state.toolInputsOptions),\n })\n }\n }\n }\n\n const resolvedModel = resolveProviderAndModel(model.provider, result.response?.modelId ?? model.modelId)\n state.stepsUsage.push({\n model: resolvedModel.model,\n inputTokens: result.usage.inputTokens.total ?? 0,\n outputTokens: result.usage.outputTokens.total ?? 0,\n ...(stepToolCalls.length > 0 ? { toolCalls: stepToolCalls } : {}),\n })\n\n flushState(log, state)\n return result\n } catch (error) {\n recordError(log, state, model, error)\n throw error\n }\n },\n\n wrapStream: async ({ doStream, model }) => {\n const streamStart = Date.now()\n let firstChunkTime: number | undefined\n\n let streamUsage: UsageAccumulator | undefined\n let streamFinishReason: string | undefined\n let streamModelId: string | undefined\n let streamResponseId: string | undefined\n const streamToolCalls: string[] = []\n const streamToolInputBuffers = new Map<string, { name: string, chunks: string[] }>()\n let streamError: string | undefined\n\n let doStreamResult: Awaited<ReturnType<typeof doStream>>\n try {\n doStreamResult = await doStream()\n } catch (error) {\n recordError(log, state, model, error)\n throw error\n }\n\n const { stream, ...rest } = doStreamResult\n\n const transformStream = new TransformStream<\n LanguageModelV3StreamPart,\n LanguageModelV3StreamPart\n >({\n transform(chunk, controller) {\n if (!firstChunkTime && chunk.type === 'text-delta') {\n firstChunkTime = Date.now()\n }\n\n if (chunk.type === 'tool-input-start') {\n streamToolCalls.push(chunk.toolName)\n if (state.toolInputs) {\n streamToolInputBuffers.set(chunk.id, { name: chunk.toolName, chunks: [] })\n }\n }\n\n if (chunk.type === 'tool-input-delta' && state.toolInputs) {\n const buffer = streamToolInputBuffers.get(chunk.id)\n if (buffer) {\n buffer.chunks.push(chunk.delta)\n }\n }\n\n if (chunk.type === 'tool-input-end' && state.toolInputs) {\n const buffer = streamToolInputBuffers.get(chunk.id)\n if (buffer) {\n const raw = safeParseJSON(buffer.chunks.join(''))\n state.allToolCallInputs.push({\n name: buffer.name,\n input: processToolInput(raw, buffer.name, state.toolInputsOptions),\n })\n streamToolInputBuffers.delete(chunk.id)\n }\n }\n\n if (chunk.type === 'finish') {\n streamUsage = {\n inputTokens: chunk.usage.inputTokens.total ?? 0,\n outputTokens: chunk.usage.outputTokens.total ?? 0,\n cacheReadTokens: chunk.usage.inputTokens.cacheRead ?? 0,\n cacheWriteTokens: chunk.usage.inputTokens.cacheWrite ?? 0,\n reasoningTokens: chunk.usage.outputTokens.reasoning ?? 0,\n }\n streamFinishReason = chunk.finishReason.unified\n }\n\n if (chunk.type === 'response-metadata') {\n if (chunk.modelId) streamModelId = chunk.modelId\n if (chunk.id) streamResponseId = chunk.id\n }\n\n if (chunk.type === 'error') {\n streamError = chunk.error instanceof Error ? chunk.error.message : String(chunk.error)\n }\n\n controller.enqueue(chunk)\n },\n\n flush() {\n state.calls++\n state.steps++\n\n if (streamUsage) {\n state.usage.inputTokens += streamUsage.inputTokens\n state.usage.outputTokens += streamUsage.outputTokens\n state.usage.cacheReadTokens += streamUsage.cacheReadTokens\n state.usage.cacheWriteTokens += streamUsage.cacheWriteTokens\n state.usage.reasoningTokens += streamUsage.reasoningTokens\n }\n\n recordModel(state, model.provider, model.modelId, streamModelId)\n state.lastFinishReason = streamFinishReason\n\n state.allToolCalls.push(...streamToolCalls)\n\n if (streamResponseId) {\n state.lastResponseId = streamResponseId\n }\n\n if (firstChunkTime) {\n state.lastMsToFirstChunk = firstChunkTime - streamStart\n }\n state.lastMsToFinish = Date.now() - streamStart\n\n if (streamError) state.lastError = streamError\n\n const resolvedModel = resolveProviderAndModel(model.provider, streamModelId ?? model.modelId)\n state.stepsUsage.push({\n model: resolvedModel.model,\n inputTokens: streamUsage?.inputTokens ?? 0,\n outputTokens: streamUsage?.outputTokens ?? 0,\n ...(streamToolCalls.length > 0 ? { toolCalls: [...streamToolCalls] } : {}),\n })\n\n flushState(log, state)\n },\n })\n\n return {\n stream: stream.pipeThrough(transformStream),\n ...rest,\n }\n },\n }\n}\n\n/**\n * Create an AI SDK `TelemetryIntegration` that captures tool execution\n * timing, errors, and total generation wall time into the wide event.\n *\n * Complements the middleware-based `createAILogger`: the middleware captures\n * token usage and streaming metrics at the model level, while the integration\n * captures application-level lifecycle events (tool execution, total duration).\n *\n * When passed an `AILogger`, shares its accumulator so both paths write to\n * the same `ai.*` field. Can also be used standalone with a `RequestLogger`.\n *\n * @example Combined with middleware (recommended)\n * ```ts\n * import { createAILogger, createEvlogIntegration } from 'evlog/ai'\n *\n * const log = useLogger(event)\n * const ai = createAILogger(log)\n *\n * const result = await generateText({\n * model: ai.wrap('anthropic/claude-sonnet-4.6'),\n * tools: { getWeather },\n * experimental_telemetry: {\n * isEnabled: true,\n * integrations: [createEvlogIntegration(ai)],\n * },\n * })\n * ```\n *\n * @example Standalone (no middleware wrapping)\n * ```ts\n * import { createEvlogIntegration } from 'evlog/ai'\n *\n * const integration = createEvlogIntegration(log)\n *\n * const result = await generateText({\n * model: openai('gpt-4o'),\n * experimental_telemetry: {\n * isEnabled: true,\n * integrations: [integration],\n * },\n * })\n * ```\n */\nexport function createEvlogIntegration(\n logOrAi: RequestLogger | AILogger,\n options?: AILoggerOptions,\n): TelemetryIntegration {\n let log: RequestLogger\n let state: AccumulatorState\n\n if ('_state' in logOrAi && logOrAi._state) {\n state = logOrAi._state\n log = state._log!\n } else {\n log = logOrAi as RequestLogger\n state = createAccumulatorState(options)\n state._log = log\n }\n\n class EvlogIntegration implements TelemetryIntegration {\n\n onStart(_event: OnStartEvent) {\n state.generationStartTime = Date.now()\n }\n\n onToolCallFinish(event: OnToolCallFinishEvent) {\n const execution: AIToolExecution = {\n name: (event.toolCall as { toolName: string }).toolName,\n durationMs: event.durationMs,\n success: event.success,\n }\n if (!event.success && event.error) {\n execution.error = event.error instanceof Error ? event.error.message : String(event.error)\n }\n state.toolExecutions.push(execution)\n }\n\n onFinish(_event: OnFinishEvent) {\n if (state.generationStartTime) {\n state.totalDurationMs = Date.now() - state.generationStartTime\n }\n flushState(log, state)\n }\n \n }\n\n return bindTelemetryIntegration(new EvlogIntegration())\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../src/ai/index.ts"],"sourcesContent":["import * as aiModule from 'ai'\nimport { gateway, wrapLanguageModel } from 'ai'\nimport type { LanguageModel, LanguageModelMiddleware } from 'ai'\nimport type { LanguageModelV4StreamPart } from '@ai-sdk/provider'\nimport type { RequestLogger } from '../types'\n\nconst { bindTelemetryIntegration } = aiModule as {\n bindTelemetryIntegration?: (integration: EvlogTelemetryIntegration) => EvlogTelemetry\n}\n\n/** @internal v6 tool execution event shape. */\ninterface V6ToolCallFinishEvent {\n toolCall: { toolName: string }\n durationMs: number\n success: boolean\n error?: unknown\n}\n\n/** @internal v7 tool execution event shape. */\ninterface V7ToolExecutionEndEvent {\n toolCall: { toolName: string }\n toolExecutionMs: number\n toolOutput: { type: 'tool-result', output?: unknown } | { type: 'tool-error', error: unknown }\n}\n\n/** @internal v7 embedding telemetry event shape. */\ninterface V7EmbedEndEvent {\n modelId: string\n usage: { tokens: number }\n embeddings: Array<{ length?: number }>\n values: string[]\n}\n\n/**\n * Telemetry integration returned by {@link createEvlogIntegration}.\n * Implements hooks for AI SDK v6 (`onToolCallFinish`, `onFinish`) and v7\n * (`onToolExecutionEnd`, `onEnd`, `onEmbedEnd`, `onAbort`, `onError`).\n */\nexport type EvlogTelemetry = {\n onStart?: (event: unknown) => void | PromiseLike<void>\n /** AI SDK v6 — superseded by `onToolExecutionEnd` in v7. */\n onToolCallFinish?: (event: unknown) => void | PromiseLike<void>\n onToolExecutionEnd?: (event: unknown) => void | PromiseLike<void>\n /** AI SDK v6 — superseded by `onEnd` in v7. */\n onFinish?: (event: unknown) => void | PromiseLike<void>\n onEnd?: (event: unknown) => void | PromiseLike<void>\n onEmbedEnd?: (event: unknown) => void | PromiseLike<void>\n onAbort?: (event: unknown) => void | PromiseLike<void>\n onError?: (event: unknown) => void | PromiseLike<void>\n}\n\n/** @internal Concrete integration class — both v6 and v7 hook names. */\ninterface EvlogTelemetryIntegration extends EvlogTelemetry {}\n\n/**\n * Fine-grained control over tool call input capture.\n */\nexport interface ToolInputsOptions {\n /**\n * Max character length for the stringified input JSON.\n * Inputs exceeding this limit are truncated with a `…` suffix.\n */\n maxLength?: number\n /**\n * Custom transform applied to each captured input before storing.\n * Receives the parsed input and tool name; return value is stored.\n * Runs before `maxLength` truncation.\n */\n transform?: (input: unknown, toolName: string) => unknown\n}\n\n/**\n * Pricing entry for a single model: cost per 1 million tokens in dollars.\n */\nexport interface ModelCost {\n input: number\n output: number\n}\n\n/**\n * Options for `createAILogger` and `createAIMiddleware`.\n */\nexport interface AILoggerOptions {\n /**\n * When enabled, `toolCalls` contains `{ name, input }` objects instead of plain tool name strings.\n * Opt-in because inputs can be large and may contain sensitive data.\n *\n * - `true` — capture all inputs as-is\n * - `{ maxLength, transform }` — capture with truncation or custom transform\n * @default false\n */\n toolInputs?: boolean | ToolInputsOptions\n /**\n * Pricing map for estimating request cost.\n * Keys are model IDs (e.g. `'claude-sonnet-4.6'`, `'gpt-4o'`), values are\n * `{ input, output }` in dollars per 1M tokens.\n *\n * When provided, the wide event includes `ai.estimatedCost` (in dollars).\n *\n * @example\n * ```ts\n * const ai = createAILogger(log, {\n * cost: {\n * 'claude-sonnet-4.6': { input: 3, output: 15 },\n * 'gpt-4o': { input: 2.5, output: 10 },\n * },\n * })\n * ```\n */\n cost?: Record<string, ModelCost>\n}\n\n/**\n * Per-step token usage breakdown for multi-step agent runs.\n */\nexport interface AIStepUsage {\n model: string\n inputTokens: number\n outputTokens: number\n toolCalls?: string[]\n}\n\n/**\n * Tool execution detail captured via telemetry integration hooks.\n */\nexport interface AIToolExecution {\n name: string\n durationMs: number\n success: boolean\n error?: string\n}\n\n/**\n * Embedding metadata captured via `captureEmbed`.\n */\nexport interface AIEmbeddingData {\n model?: string\n tokens: number\n dimensions?: number\n count?: number\n}\n\n/**\n * Shape of the `ai` field written to the wide event, and the public\n * snapshot returned by `AILogger.getMetadata()`.\n *\n * `model` and `provider` are populated after the first model call.\n * They may be undefined when only `captureEmbed` has been called or\n * before any AI activity has happened.\n */\nexport interface AIEventData {\n calls: number\n model?: string\n models?: string[]\n provider?: string\n inputTokens: number\n outputTokens: number\n totalTokens: number\n cacheReadTokens?: number\n cacheWriteTokens?: number\n reasoningTokens?: number\n finishReason?: string\n toolCalls?: string[] | Array<{ name: string, input: unknown }>\n responseId?: string\n steps?: number\n stepsUsage?: AIStepUsage[]\n msToFirstChunk?: number\n msToFinish?: number\n tokensPerSecond?: number\n error?: string\n tools?: AIToolExecution[]\n totalDurationMs?: number\n embedding?: AIEmbeddingData\n estimatedCost?: number\n}\n\n/**\n * Public alias for the metadata snapshot returned by `AILogger.getMetadata()`.\n *\n * Mirrors the shape of the `ai` field on the wide event, so the same object\n * can be persisted, surfaced to end-users, or compared across runs.\n */\nexport type AIMetadata = AIEventData\n\n/**\n * Callback fired on every metadata update (per step, per embed, on error,\n * and on integration completion). Receives a structured snapshot.\n */\nexport type AIMetadataListener = (metadata: AIMetadata) => void\n\nexport interface AILogger {\n /**\n * Wrap a language model with evlog middleware.\n * All `generateText` and `streamText` calls using the wrapped model\n * are captured automatically into the wide event.\n *\n * Accepts a language model object (`LanguageModelV3` or `LanguageModelV4`)\n * or a model string (e.g. `'anthropic/claude-sonnet-4.6'`).\n * Strings are resolved via the AI SDK gateway.\n *\n * On AI SDK v7, the wrapped model is a `LanguageModelV4`. On v6 it remains\n * a `LanguageModelV3`. Both are accepted as input.\n *\n * Also works with pre-wrapped models (e.g. from supermemory, guardrails):\n * `ai.wrap(withSupermemory(base, orgId))` composes correctly.\n *\n * @example Gateway model string\n * ```ts\n * const ai = createAILogger(log)\n * const model = ai.wrap('anthropic/claude-sonnet-4.6')\n * ```\n *\n * @example Provider model object (V3 or V4)\n * ```ts\n * const ai = createAILogger(log)\n * const model = ai.wrap(anthropic('claude-sonnet-4.6'))\n * ```\n */\n wrap: (model: LanguageModel) => LanguageModel\n\n /**\n * Manually capture token usage from an `embed()` or `embedMany()` result.\n * Embedding models use a different type than language models, so they\n * cannot be wrapped with middleware.\n *\n * @example\n * ```ts\n * const { embedding, usage } = await embed({ model: embeddingModel, value: query })\n * ai.captureEmbed({ usage })\n *\n * // With model info (v2)\n * ai.captureEmbed({ usage, model: 'text-embedding-3-small', dimensions: 1536 })\n *\n * // After embedMany\n * ai.captureEmbed({ usage, count: texts.length })\n * ```\n */\n captureEmbed: (result: {\n usage: { tokens: number }\n model?: string\n dimensions?: number\n count?: number\n }) => void\n\n /**\n * Get a snapshot of the current AI execution metadata.\n *\n * Returns the same structured object that is written to the `ai` field\n * of the wide event. Safe to call at any time — including inside the\n * AI SDK's `onFinish` callback, after `await generateText()`, or while a\n * stream is in progress.\n *\n * The returned snapshot is a fresh copy: mutating it does not affect\n * subsequent calls or the underlying state.\n *\n * @example Persist execution history after a run\n * ```ts\n * const ai = createAILogger(log, { cost: { ... } })\n *\n * await generateText({ model: ai.wrap('anthropic/claude-sonnet-4.6'), prompt })\n *\n * const metadata = ai.getMetadata()\n * await db.insert('ai_runs', metadata)\n * ```\n *\n * @example Surface usage to end-users in a streaming response\n * ```ts\n * const result = streamText({\n * model: ai.wrap('anthropic/claude-sonnet-4.6'),\n * messages,\n * onFinish: () => {\n * const { totalTokens, estimatedCost } = ai.getMetadata()\n * trackUsage(userId, { totalTokens, estimatedCost })\n * },\n * })\n * ```\n */\n getMetadata: () => AIMetadata\n\n /**\n * Get the current estimated cost in dollars.\n *\n * Returns `undefined` if no `cost` map was provided to `createAILogger`,\n * or if the model is not in the pricing map.\n *\n * Convenience for `getMetadata().estimatedCost`.\n *\n * @example\n * ```ts\n * const ai = createAILogger(log, {\n * cost: { 'claude-sonnet-4.6': { input: 3, output: 15 } },\n * })\n *\n * await generateText({ model: ai.wrap('anthropic/claude-sonnet-4.6'), prompt })\n *\n * console.log(`Cost: $${ai.getEstimatedCost()?.toFixed(4)}`)\n * ```\n */\n getEstimatedCost: () => number | undefined\n\n /**\n * Subscribe to metadata updates.\n *\n * The callback fires every time the underlying state flushes — once per\n * step (in multi-step agent runs), once per `captureEmbed` call, on\n * model errors, and once on `createEvlogIntegration`'s `onFinish`.\n *\n * Each invocation receives a fresh snapshot (same shape as `getMetadata`).\n * Returns an unsubscribe function.\n *\n * @example Stream incremental usage updates to the client\n * ```ts\n * const ai = createAILogger(log)\n *\n * ai.onUpdate((metadata) => {\n * pushToClient({ type: 'ai-progress', metadata })\n * })\n *\n * const result = streamText({ model: ai.wrap('...'), messages })\n * ```\n *\n * @example Cleanup\n * ```ts\n * const off = ai.onUpdate((metadata) => { ... })\n * // later\n * off()\n * ```\n */\n onUpdate: (callback: AIMetadataListener) => () => void\n\n /**\n * Internal accumulator state exposed for `createEvlogIntegration` to share.\n * @internal\n */\n _state: AccumulatorState\n}\n\ninterface UsageAccumulator {\n inputTokens: number\n outputTokens: number\n cacheReadTokens: number\n cacheWriteTokens: number\n reasoningTokens: number\n}\n\nfunction addUsage(\n acc: UsageAccumulator,\n usage: {\n inputTokens: { total: number | undefined, cacheRead?: number | undefined, cacheWrite?: number | undefined }\n outputTokens: { total: number | undefined, reasoning?: number | undefined }\n },\n): void {\n acc.inputTokens += usage.inputTokens.total ?? 0\n acc.outputTokens += usage.outputTokens.total ?? 0\n acc.cacheReadTokens += usage.inputTokens.cacheRead ?? 0\n acc.cacheWriteTokens += usage.inputTokens.cacheWrite ?? 0\n acc.reasoningTokens += usage.outputTokens.reasoning ?? 0\n}\n\n/**\n * When using `gateway('google/gemini-3-flash')`, the model object has\n * `provider: 'gateway'` and `modelId: 'google/gemini-3-flash'`.\n * This extracts the real provider and model name from the modelId.\n */\nfunction resolveProviderAndModel(provider: string, modelId: string): { provider: string, model: string } {\n if (provider !== 'gateway' || !modelId.includes('/')) {\n return { provider, model: modelId }\n }\n const slashIndex = modelId.indexOf('/')\n return {\n provider: modelId.slice(0, slashIndex),\n model: modelId.slice(slashIndex + 1),\n }\n}\n\n/**\n * Create the evlog AI middleware that captures AI SDK data into a wide event.\n *\n * Use this when you need explicit middleware composition with other wrappers\n * (e.g. supermemory, guardrails). For most cases, use `createAILogger` instead.\n *\n * Note: `captureEmbed` is not available with the raw middleware — use\n * `createAILogger` if you need embedding capture.\n *\n * @example Nuxt API route with supermemory\n * ```ts\n * import { createAIMiddleware } from 'evlog/ai'\n * import { wrapLanguageModel } from 'ai'\n *\n * export default defineEventHandler(async (event) => {\n * const log = useLogger(event)\n *\n * const model = wrapLanguageModel({\n * model: withSupermemory(base, orgId),\n * middleware: [createAIMiddleware(log, { toolInputs: true })],\n * })\n * })\n * ```\n */\nexport function createAIMiddleware(log: RequestLogger, options?: AILoggerOptions): LanguageModelMiddleware {\n return buildMiddleware(log, options)\n}\n\n/**\n * Create an AI logger that captures AI SDK data into the wide event.\n *\n * Uses model middleware (`wrapLanguageModel`) to transparently intercept\n * all LLM calls. `onFinish` and `onStepFinish` remain free for user code.\n *\n * @example\n * ```ts\n * import { createAILogger } from 'evlog/ai'\n *\n * const log = useLogger(event)\n * const ai = createAILogger(log)\n * const model = ai.wrap('anthropic/claude-sonnet-4.6')\n *\n * const result = streamText({\n * model,\n * messages,\n * onFinish: ({ text }) => saveConversation(text),\n * })\n * ```\n *\n * @example Capture tool call inputs\n * ```ts\n * const ai = createAILogger(log, { toolInputs: true })\n * ```\n */\nexport function createAILogger(log: RequestLogger, options?: AILoggerOptions): AILogger {\n const state = createAccumulatorState(options)\n state._log = log\n const middleware = buildMiddlewareFromState(log, state)\n\n return {\n wrap: (model: LanguageModel) => {\n const resolved = typeof model === 'string' ? gateway(model) : model\n return wrapLanguageModel({ model: resolved, middleware })\n },\n\n captureEmbed: (result: {\n usage: { tokens: number }\n model?: string\n dimensions?: number\n count?: number\n }) => {\n state.calls++\n state.usage.inputTokens += result.usage.tokens\n state.embedding = {\n tokens: (state.embedding?.tokens ?? 0) + result.usage.tokens,\n ...(result.model ? { model: result.model } : state.embedding?.model ? { model: state.embedding.model } : {}),\n ...(result.dimensions ? { dimensions: result.dimensions } : state.embedding?.dimensions ? { dimensions: state.embedding.dimensions } : {}),\n ...(result.count ? { count: (state.embedding?.count ?? 0) + result.count } : state.embedding?.count ? { count: state.embedding.count } : {}),\n }\n flushState(log, state)\n },\n\n getMetadata: () => buildMetadata(state),\n\n getEstimatedCost: () => computeEstimatedCost(state),\n\n onUpdate: (callback: AIMetadataListener) => {\n state.subscribers.add(callback)\n return () => {\n state.subscribers.delete(callback)\n }\n },\n\n _state: state,\n }\n}\n\n/**\n * Snapshot of how much of each cumulative array we've already sent to the\n * wide event. `flushState` reads it to compute the delta and updates it in\n * place; passing zero-watermarks to `buildMetadata` yields a full snapshot.\n */\ninterface Watermarks {\n toolCalls: number\n toolCallInputs: number\n stepsUsage: number\n toolExecutions: number\n models: Set<string>\n}\n\nfunction freshWatermarks(): Watermarks {\n return { toolCalls: 0, toolCallInputs: 0, stepsUsage: 0, toolExecutions: 0, models: new Set() }\n}\n\ninterface AccumulatorState {\n calls: number\n steps: number\n usage: UsageAccumulator\n models: string[]\n lastProvider: string | undefined\n allToolCalls: string[]\n allToolCallInputs: Array<{ name: string, input: unknown }>\n stepsUsage: AIStepUsage[]\n lastFinishReason: string | undefined\n lastMsToFirstChunk: number | undefined\n lastMsToFinish: number | undefined\n lastError: string | undefined\n lastResponseId: string | undefined\n toolInputs: boolean\n toolInputsOptions: ToolInputsOptions | undefined\n toolExecutions: AIToolExecution[]\n generationStartTime: number | undefined\n totalDurationMs: number | undefined\n embedding: AIEmbeddingData | undefined\n costMap: Record<string, ModelCost> | undefined\n subscribers: Set<AIMetadataListener>\n /** @internal What's already been written to the wide event. */\n _flushed: Watermarks\n /** @internal Logger reference for integration flush */\n _log?: RequestLogger\n}\n\nfunction resolveToolInputs(raw?: boolean | ToolInputsOptions): { enabled: boolean, options: ToolInputsOptions | undefined } {\n if (!raw) return { enabled: false, options: undefined }\n if (raw === true) return { enabled: true, options: undefined }\n return { enabled: true, options: raw }\n}\n\nfunction processToolInput(input: unknown, toolName: string, options: ToolInputsOptions | undefined): unknown {\n let value = input\n if (options?.transform) {\n value = options.transform(value, toolName)\n }\n if (options?.maxLength) {\n const str = typeof value === 'string' ? value : JSON.stringify(value)\n if (str.length > options.maxLength) {\n return `${str.slice(0, options.maxLength)}…`\n }\n }\n return value\n}\n\nfunction createAccumulatorState(options?: AILoggerOptions): AccumulatorState {\n const { enabled, options: captureOpts } = resolveToolInputs(options?.toolInputs)\n return {\n calls: 0,\n steps: 0,\n usage: {\n inputTokens: 0,\n outputTokens: 0,\n cacheReadTokens: 0,\n cacheWriteTokens: 0,\n reasoningTokens: 0,\n },\n models: [],\n lastProvider: undefined,\n allToolCalls: [],\n allToolCallInputs: [],\n stepsUsage: [],\n lastFinishReason: undefined,\n lastMsToFirstChunk: undefined,\n lastMsToFinish: undefined,\n lastError: undefined,\n lastResponseId: undefined,\n toolInputs: enabled,\n toolInputsOptions: captureOpts,\n toolExecutions: [],\n generationStartTime: undefined,\n totalDurationMs: undefined,\n embedding: undefined,\n costMap: options?.cost,\n subscribers: new Set(),\n _flushed: freshWatermarks(),\n }\n}\n\nfunction computeEstimatedCost(state: AccumulatorState): number | undefined {\n if (!state.costMap) return undefined\n const lastModel = state.models[state.models.length - 1]\n if (!lastModel) return undefined\n const pricing = state.costMap[lastModel]\n if (!pricing) return undefined\n const inputCost = (state.usage.inputTokens / 1_000_000) * pricing.input\n const outputCost = (state.usage.outputTokens / 1_000_000) * pricing.output\n const total = inputCost + outputCost\n return total > 0 ? Math.round(total * 1_000_000) / 1_000_000 : undefined\n}\n\n/**\n * Build the `ai.*` payload from the accumulator. Pass `since` to emit\n * only the array entries appended after that watermark (used by\n * `flushState` to avoid quadratic growth on the wide event, since evlog's\n * `mergeInto` concatenates arrays). The default — fresh watermarks —\n * yields the full cumulative snapshot used by `getMetadata()` and\n * delivered to `onUpdate` subscribers.\n */\nfunction buildMetadata(state: AccumulatorState, since: Watermarks = freshWatermarks()): AIMetadata {\n const lastModel = state.models[state.models.length - 1]\n\n const data: AIMetadata = {\n calls: state.calls,\n inputTokens: state.usage.inputTokens,\n outputTokens: state.usage.outputTokens,\n totalTokens: state.usage.inputTokens + state.usage.outputTokens,\n }\n\n if (lastModel) data.model = lastModel\n if (state.lastProvider) data.provider = state.lastProvider\n if (state.usage.cacheReadTokens > 0) data.cacheReadTokens = state.usage.cacheReadTokens\n if (state.usage.cacheWriteTokens > 0) data.cacheWriteTokens = state.usage.cacheWriteTokens\n if (state.usage.reasoningTokens > 0) data.reasoningTokens = state.usage.reasoningTokens\n if (state.lastFinishReason) data.finishReason = state.lastFinishReason\n if (state.lastResponseId) data.responseId = state.lastResponseId\n if (state.lastMsToFirstChunk !== undefined) data.msToFirstChunk = state.lastMsToFirstChunk\n if (state.lastMsToFinish !== undefined) {\n data.msToFinish = state.lastMsToFinish\n if (state.usage.outputTokens > 0 && state.lastMsToFinish > 0) {\n data.tokensPerSecond = Math.round((state.usage.outputTokens / state.lastMsToFinish) * 1000)\n }\n }\n if (state.lastError) data.error = state.lastError\n if (state.totalDurationMs !== undefined) data.totalDurationMs = state.totalDurationMs\n if (state.embedding) data.embedding = { ...state.embedding }\n const cost = computeEstimatedCost(state)\n if (cost !== undefined) data.estimatedCost = cost\n\n if (state.toolInputs) {\n if (state.allToolCallInputs.length > since.toolCallInputs) {\n data.toolCalls = state.allToolCallInputs.slice(since.toolCallInputs).map(t => ({ ...t }))\n }\n } else if (state.allToolCalls.length > since.toolCalls) {\n data.toolCalls = state.allToolCalls.slice(since.toolCalls)\n }\n\n if (state.steps > 1 && state.stepsUsage.length > since.stepsUsage) {\n data.steps = state.steps\n data.stepsUsage = state.stepsUsage\n .slice(since.stepsUsage)\n .map(s => ({ ...s, ...(s.toolCalls ? { toolCalls: [...s.toolCalls] } : {}) }))\n }\n\n if (state.toolExecutions.length > since.toolExecutions) {\n data.tools = state.toolExecutions.slice(since.toolExecutions).map(t => ({ ...t }))\n }\n\n // `models` is only emitted once a second distinct model appears, then\n // deduplicated across flushes so the merged wide event stays unique.\n const uniqueModels = new Set(state.models)\n if (uniqueModels.size > 1) {\n const newModels = [...uniqueModels].filter(m => !since.models.has(m))\n if (newModels.length > 0) data.models = newModels\n }\n\n return data\n}\n\nfunction notifySubscribers(state: AccumulatorState, metadata: AIMetadata): void {\n if (state.subscribers.size === 0) return\n for (const subscriber of state.subscribers) {\n try {\n subscriber(metadata)\n } catch {\n // Subscribers must not break the AI flow.\n }\n }\n}\n\n/**\n * Push the accumulator's latest changes onto the wide event using delta\n * semantics for arrays, then notify subscribers with the full snapshot.\n */\nfunction flushState(log: RequestLogger, state: AccumulatorState): void {\n const flushed = state._flushed\n const data = buildMetadata(state, flushed)\n\n flushed.toolCalls = state.allToolCalls.length\n flushed.toolCallInputs = state.allToolCallInputs.length\n flushed.toolExecutions = state.toolExecutions.length\n if (data.stepsUsage) flushed.stepsUsage = state.stepsUsage.length\n if (data.models) for (const m of data.models) flushed.models.add(m)\n\n log.set({ ai: data } as Record<string, unknown>)\n\n if (state.subscribers.size > 0) {\n notifySubscribers(state, buildMetadata(state))\n }\n}\n\nfunction recordModel(state: AccumulatorState, provider: string, modelId: string, responseModelId?: string): void {\n const resolved = resolveProviderAndModel(provider, responseModelId ?? modelId)\n state.models.push(resolved.model)\n state.lastProvider = resolved.provider\n}\n\nfunction safeParseJSON(input: string): unknown {\n try {\n return JSON.parse(input)\n } catch {\n return input\n }\n}\n\nfunction recordError(log: RequestLogger, state: AccumulatorState, model: { provider: string, modelId: string }, error: unknown): void {\n state.calls++\n state.steps++\n recordModel(state, model.provider, model.modelId)\n state.lastFinishReason = 'error'\n state.lastError = error instanceof Error ? error.message : String(error)\n\n const resolved = resolveProviderAndModel(model.provider, model.modelId)\n state.stepsUsage.push({\n model: resolved.model,\n inputTokens: 0,\n outputTokens: 0,\n })\n\n flushState(log, state)\n}\n\nfunction buildMiddleware(log: RequestLogger, options?: AILoggerOptions): LanguageModelMiddleware {\n const state = createAccumulatorState(options)\n state._log = log\n return buildMiddlewareFromState(log, state)\n}\n\nfunction buildMiddlewareFromState(log: RequestLogger, state: AccumulatorState): LanguageModelMiddleware {\n return {\n // AI SDK v7 middleware is V4-native; `LanguageModelMiddleware` also\n // accepts this on v6-compatible installs that relax the version field.\n specificationVersion: 'v4',\n wrapGenerate: async ({ doGenerate, model }) => {\n try {\n const result = await doGenerate()\n\n state.calls++\n state.steps++\n addUsage(state.usage, result.usage)\n recordModel(state, model.provider, model.modelId, result.response?.modelId)\n state.lastFinishReason = result.finishReason.unified\n\n if (result.response?.id) {\n state.lastResponseId = result.response.id\n }\n\n const stepToolCalls: string[] = []\n for (const item of result.content) {\n if (item.type === 'tool-call') {\n state.allToolCalls.push(item.toolName)\n stepToolCalls.push(item.toolName)\n if (state.toolInputs) {\n const raw = typeof item.input === 'string' ? safeParseJSON(item.input) : item.input\n state.allToolCallInputs.push({\n name: item.toolName,\n input: processToolInput(raw, item.toolName, state.toolInputsOptions),\n })\n }\n }\n }\n\n const resolvedModel = resolveProviderAndModel(model.provider, result.response?.modelId ?? model.modelId)\n state.stepsUsage.push({\n model: resolvedModel.model,\n inputTokens: result.usage.inputTokens.total ?? 0,\n outputTokens: result.usage.outputTokens.total ?? 0,\n ...(stepToolCalls.length > 0 ? { toolCalls: stepToolCalls } : {}),\n })\n\n flushState(log, state)\n return result\n } catch (error) {\n recordError(log, state, model, error)\n throw error\n }\n },\n\n wrapStream: async ({ doStream, model }) => {\n const streamStart = Date.now()\n let firstChunkTime: number | undefined\n\n let streamUsage: UsageAccumulator | undefined\n let streamFinishReason: string | undefined\n let streamModelId: string | undefined\n let streamResponseId: string | undefined\n const streamToolCalls: string[] = []\n const streamToolInputBuffers = new Map<string, { name: string, chunks: string[] }>()\n let streamError: string | undefined\n\n let doStreamResult: Awaited<ReturnType<typeof doStream>>\n try {\n doStreamResult = await doStream()\n } catch (error) {\n recordError(log, state, model, error)\n throw error\n }\n\n const { stream, ...rest } = doStreamResult\n\n const transformStream = new TransformStream<\n LanguageModelV4StreamPart,\n LanguageModelV4StreamPart\n >({\n transform(chunk, controller) {\n if (!firstChunkTime && chunk.type === 'text-delta') {\n firstChunkTime = Date.now()\n }\n\n if (chunk.type === 'tool-input-start') {\n streamToolCalls.push(chunk.toolName)\n if (state.toolInputs) {\n streamToolInputBuffers.set(chunk.id, { name: chunk.toolName, chunks: [] })\n }\n }\n\n if (chunk.type === 'tool-input-delta' && state.toolInputs) {\n const buffer = streamToolInputBuffers.get(chunk.id)\n if (buffer) {\n buffer.chunks.push(chunk.delta)\n }\n }\n\n if (chunk.type === 'tool-input-end' && state.toolInputs) {\n const buffer = streamToolInputBuffers.get(chunk.id)\n if (buffer) {\n const raw = safeParseJSON(buffer.chunks.join(''))\n state.allToolCallInputs.push({\n name: buffer.name,\n input: processToolInput(raw, buffer.name, state.toolInputsOptions),\n })\n streamToolInputBuffers.delete(chunk.id)\n }\n }\n\n if (chunk.type === 'finish') {\n streamUsage = {\n inputTokens: chunk.usage.inputTokens.total ?? 0,\n outputTokens: chunk.usage.outputTokens.total ?? 0,\n cacheReadTokens: chunk.usage.inputTokens.cacheRead ?? 0,\n cacheWriteTokens: chunk.usage.inputTokens.cacheWrite ?? 0,\n reasoningTokens: chunk.usage.outputTokens.reasoning ?? 0,\n }\n streamFinishReason = chunk.finishReason.unified\n }\n\n if (chunk.type === 'response-metadata') {\n if (chunk.modelId) streamModelId = chunk.modelId\n if (chunk.id) streamResponseId = chunk.id\n }\n\n if (chunk.type === 'error') {\n streamError = chunk.error instanceof Error ? chunk.error.message : String(chunk.error)\n }\n\n controller.enqueue(chunk)\n },\n\n flush() {\n state.calls++\n state.steps++\n\n if (streamUsage) {\n state.usage.inputTokens += streamUsage.inputTokens\n state.usage.outputTokens += streamUsage.outputTokens\n state.usage.cacheReadTokens += streamUsage.cacheReadTokens\n state.usage.cacheWriteTokens += streamUsage.cacheWriteTokens\n state.usage.reasoningTokens += streamUsage.reasoningTokens\n }\n\n recordModel(state, model.provider, model.modelId, streamModelId)\n state.lastFinishReason = streamFinishReason\n\n state.allToolCalls.push(...streamToolCalls)\n\n if (streamResponseId) {\n state.lastResponseId = streamResponseId\n }\n\n if (firstChunkTime) {\n state.lastMsToFirstChunk = firstChunkTime - streamStart\n }\n state.lastMsToFinish = Date.now() - streamStart\n\n if (streamError) state.lastError = streamError\n\n const resolvedModel = resolveProviderAndModel(model.provider, streamModelId ?? model.modelId)\n state.stepsUsage.push({\n model: resolvedModel.model,\n inputTokens: streamUsage?.inputTokens ?? 0,\n outputTokens: streamUsage?.outputTokens ?? 0,\n ...(streamToolCalls.length > 0 ? { toolCalls: [...streamToolCalls] } : {}),\n })\n\n flushState(log, state)\n },\n })\n\n return {\n stream: stream.pipeThrough(transformStream),\n ...rest,\n }\n },\n }\n}\n\nfunction formatTelemetryError(error: unknown): string {\n return error instanceof Error ? error.message : String(error)\n}\n\nfunction recordToolExecution(\n state: AccumulatorState,\n input: { name: string, durationMs: number, success: boolean, error?: string },\n): void {\n const execution: AIToolExecution = {\n name: input.name,\n durationMs: input.durationMs,\n success: input.success,\n }\n if (input.error) execution.error = input.error\n state.toolExecutions.push(execution)\n}\n\nfunction adaptV6ToolEvent(event: V6ToolCallFinishEvent) {\n return {\n name: event.toolCall.toolName,\n durationMs: event.durationMs,\n success: event.success,\n error: !event.success && event.error !== undefined\n ? formatTelemetryError(event.error)\n : undefined,\n }\n}\n\nfunction adaptV7ToolEvent(event: V7ToolExecutionEndEvent) {\n const success = event.toolOutput.type === 'tool-result'\n return {\n name: event.toolCall.toolName,\n durationMs: event.toolExecutionMs,\n success,\n error: !success && event.toolOutput.type === 'tool-error'\n ? formatTelemetryError(event.toolOutput.error)\n : undefined,\n }\n}\n\nfunction recordGenerationEnd(state: AccumulatorState, log: RequestLogger): void {\n if (state.generationStartTime !== undefined) {\n state.totalDurationMs = Date.now() - state.generationStartTime\n }\n flushState(log, state)\n}\n\nfunction recordEmbedEnd(state: AccumulatorState, log: RequestLogger, event: V7EmbedEndEvent): void {\n state.calls++\n state.usage.inputTokens += event.usage.tokens\n const dimensions = event.embeddings[0]?.length\n state.embedding = {\n tokens: (state.embedding?.tokens ?? 0) + event.usage.tokens,\n model: event.modelId,\n ...(dimensions !== undefined\n ? { dimensions }\n : state.embedding?.dimensions\n ? { dimensions: state.embedding.dimensions }\n : {}),\n count: (state.embedding?.count ?? 0) + event.values.length,\n }\n flushState(log, state)\n}\n\nfunction finalizeIntegration(integration: EvlogTelemetryIntegration): EvlogTelemetry {\n if (typeof bindTelemetryIntegration === 'function') {\n return bindTelemetryIntegration(integration)\n }\n return integration\n}\n\n/**\n * Create an AI SDK telemetry integration that captures tool execution\n * timing, errors, and total generation wall time into the wide event.\n *\n * Compatible with AI SDK v6 (`TelemetryIntegration`) and v7 (`Telemetry`).\n * On v7, also captures embeddings via `onEmbedEnd`, aborts via `onAbort`,\n * and unrecoverable errors via `onError`.\n *\n * Complements the middleware-based `createAILogger`: the middleware captures\n * token usage and streaming metrics at the model level, while the integration\n * captures application-level lifecycle events (tool execution, total duration).\n *\n * When passed an `AILogger`, shares its accumulator so both paths write to\n * the same `ai.*` field. Can also be used standalone with a `RequestLogger`.\n *\n * @example Combined with middleware (recommended)\n * ```ts\n * import { createAILogger, createEvlogIntegration } from 'evlog/ai'\n *\n * const log = useLogger(event)\n * const ai = createAILogger(log)\n *\n * const result = await generateText({\n * model: ai.wrap('anthropic/claude-sonnet-4.6'),\n * tools: { getWeather },\n * telemetry: {\n * integrations: [createEvlogIntegration(ai)],\n * },\n * })\n * ```\n *\n * @example Standalone (no middleware wrapping)\n * ```ts\n * import { createEvlogIntegration } from 'evlog/ai'\n *\n * const integration = createEvlogIntegration(log)\n *\n * const result = await generateText({\n * model: openai('gpt-4o'),\n * telemetry: {\n * integrations: [integration],\n * },\n * })\n * ```\n */\nexport function createEvlogIntegration(\n logOrAi: RequestLogger | AILogger,\n options?: AILoggerOptions,\n): EvlogTelemetry {\n let log: RequestLogger\n let state: AccumulatorState\n\n if ('_state' in logOrAi && logOrAi._state) {\n state = logOrAi._state\n log = state._log!\n } else {\n log = logOrAi as RequestLogger\n state = createAccumulatorState(options)\n state._log = log\n }\n\n class EvlogIntegration implements EvlogTelemetryIntegration {\n\n onStart(_event: unknown) {\n state.generationStartTime = Date.now()\n }\n\n onToolCallFinish(event: unknown) {\n recordToolExecution(state, adaptV6ToolEvent(event as V6ToolCallFinishEvent))\n }\n\n onToolExecutionEnd(event: unknown) {\n recordToolExecution(state, adaptV7ToolEvent(event as V7ToolExecutionEndEvent))\n }\n\n onFinish(_event: unknown) {\n recordGenerationEnd(state, log)\n }\n\n onEnd(_event: unknown) {\n recordGenerationEnd(state, log)\n }\n\n onEmbedEnd(event: unknown) {\n recordEmbedEnd(state, log, event as V7EmbedEndEvent)\n }\n\n onAbort(event: unknown) {\n state.lastFinishReason = 'abort'\n const { reason } = event as { reason?: unknown }\n if (reason !== undefined) {\n state.lastError = formatTelemetryError(reason)\n }\n recordGenerationEnd(state, log)\n }\n\n onError(error: unknown) {\n state.lastError = formatTelemetryError(error)\n flushState(log, state)\n }\n\n }\n\n return finalizeIntegration(new EvlogIntegration())\n}\n"],"mappings":"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"}
@@ -0,0 +1,81 @@
1
+ import "node:async_hooks";
2
+ //#region src/shared/asyncStorageScope.ts
3
+ /**
4
+ * Whether this runtime provides a working native `AsyncLocalStorage.enterWith()`.
5
+ *
6
+ * Cloudflare Workers expose `enterWith` on the prototype but throw when it is
7
+ * called, so a `typeof` check alone is not enough — we probe with a call.
8
+ */
9
+ function supportsAsyncLocalStorageEnterWith(storage) {
10
+ if (typeof storage.enterWith !== "function") return false;
11
+ try {
12
+ storage.enterWith(void 0);
13
+ return true;
14
+ } catch {
15
+ return false;
16
+ }
17
+ }
18
+ /**
19
+ * Bind `value` to `storage` for the current async execution context.
20
+ * Uses native `enterWith()` when available; otherwise relies on
21
+ * {@link patchAsyncLocalStorageEnterWith}.
22
+ */
23
+ function bindAsyncLocalStorage(storage, value) {
24
+ storage.enterWith(value);
25
+ }
26
+ /** Clear a value previously bound with {@link bindAsyncLocalStorage}. */
27
+ function clearAsyncLocalStorage(storage) {
28
+ storage.enterWith(void 0);
29
+ }
30
+ /**
31
+ * Polyfill `enterWith()` on a single `AsyncLocalStorage` instance.
32
+ *
33
+ * Elysia's lifecycle is split across hooks (`onRequest` → handler → `onAfterResponse`).
34
+ * Unlike Express or Fastify, there is no single `next()` boundary to wrap in
35
+ * `storage.run()`, so the integration binds the logger with `enterWith()`.
36
+ *
37
+ * The polyfill stores the value on the ALS instance when no native `run()` frame
38
+ * is active. That matches single-request `wrangler dev` flows and async work
39
+ * spawned from a handler, but it does **not** replicate native per-async-context
40
+ * isolation when multiple requests interleave in the same isolate. Prefer `{ log }`
41
+ * from derive for concurrent Workers handlers.
42
+ */
43
+ function patchAsyncLocalStorageEnterWith(storage) {
44
+ if (supportsAsyncLocalStorageEnterWith(storage)) return;
45
+ let fallbackStore;
46
+ let runDepth = 0;
47
+ const originalGetStore = storage.getStore.bind(storage);
48
+ const originalRun = storage.run.bind(storage);
49
+ Object.defineProperty(storage, "enterWith", {
50
+ configurable: true,
51
+ writable: true,
52
+ value(store) {
53
+ fallbackStore = store;
54
+ }
55
+ });
56
+ Object.defineProperty(storage, "run", {
57
+ configurable: true,
58
+ writable: true,
59
+ value(store, callback, ...args) {
60
+ runDepth++;
61
+ try {
62
+ return originalRun(store, callback, ...args);
63
+ } finally {
64
+ runDepth--;
65
+ }
66
+ }
67
+ });
68
+ Object.defineProperty(storage, "getStore", {
69
+ configurable: true,
70
+ writable: true,
71
+ value() {
72
+ if (runDepth > 0) return originalGetStore();
73
+ const active = originalGetStore();
74
+ return active !== void 0 ? active : fallbackStore;
75
+ }
76
+ });
77
+ }
78
+ //#endregion
79
+ export { clearAsyncLocalStorage as n, patchAsyncLocalStorageEnterWith as r, bindAsyncLocalStorage as t };
80
+
81
+ //# sourceMappingURL=asyncStorageScope-DnF7Xk8l.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"asyncStorageScope-DnF7Xk8l.mjs","names":[],"sources":["../src/shared/asyncStorageScope.ts"],"sourcesContent":["import { AsyncLocalStorage } from 'node:async_hooks'\n\n/**\n * Whether this runtime provides a working native `AsyncLocalStorage.enterWith()`.\n *\n * Cloudflare Workers expose `enterWith` on the prototype but throw when it is\n * called, so a `typeof` check alone is not enough — we probe with a call.\n */\nexport function supportsAsyncLocalStorageEnterWith(\n storage: { enterWith?: unknown },\n): boolean {\n if (typeof storage.enterWith !== 'function') return false\n try {\n storage.enterWith(undefined)\n return true\n } catch {\n return false\n }\n}\n\n/**\n * Bind `value` to `storage` for the current async execution context.\n * Uses native `enterWith()` when available; otherwise relies on\n * {@link patchAsyncLocalStorageEnterWith}.\n */\nexport function bindAsyncLocalStorage<T>(\n storage: AsyncLocalStorage<T>,\n value: T,\n): void {\n storage.enterWith(value)\n}\n\n/** Clear a value previously bound with {@link bindAsyncLocalStorage}. */\nexport function clearAsyncLocalStorage<T>(storage: AsyncLocalStorage<T>): void {\n storage.enterWith(undefined as unknown as T)\n}\n\n/**\n * Polyfill `enterWith()` on a single `AsyncLocalStorage` instance.\n *\n * Elysia's lifecycle is split across hooks (`onRequest` → handler → `onAfterResponse`).\n * Unlike Express or Fastify, there is no single `next()` boundary to wrap in\n * `storage.run()`, so the integration binds the logger with `enterWith()`.\n *\n * The polyfill stores the value on the ALS instance when no native `run()` frame\n * is active. That matches single-request `wrangler dev` flows and async work\n * spawned from a handler, but it does **not** replicate native per-async-context\n * isolation when multiple requests interleave in the same isolate. Prefer `{ log }`\n * from derive for concurrent Workers handlers.\n */\nexport function patchAsyncLocalStorageEnterWith<T>(\n storage: AsyncLocalStorage<T>,\n): void {\n if (supportsAsyncLocalStorageEnterWith(storage)) return\n\n let fallbackStore: T | undefined\n let runDepth = 0\n const originalGetStore = storage.getStore.bind(storage)\n const originalRun = storage.run.bind(storage)\n\n Object.defineProperty(storage, 'enterWith', {\n configurable: true,\n writable: true,\n value(store: T): void {\n fallbackStore = store\n },\n })\n\n Object.defineProperty(storage, 'run', {\n configurable: true,\n writable: true,\n value<TReturn>(\n store: T,\n callback: (...args: unknown[]) => TReturn,\n ...args: unknown[]\n ): TReturn {\n runDepth++\n try {\n return originalRun(store, callback, ...args)\n } finally {\n runDepth--\n }\n },\n })\n\n Object.defineProperty(storage, 'getStore', {\n configurable: true,\n writable: true,\n value(): T | undefined {\n if (runDepth > 0) return originalGetStore()\n const active = originalGetStore()\n return active !== undefined ? active : fallbackStore\n },\n })\n}\n"],"mappings":";;;;;;;;AAQA,SAAgB,mCACd,SACS;AACT,KAAI,OAAO,QAAQ,cAAc,WAAY,QAAO;AACpD,KAAI;AACF,UAAQ,UAAU,KAAA,EAAU;AAC5B,SAAO;SACD;AACN,SAAO;;;;;;;;AASX,SAAgB,sBACd,SACA,OACM;AACN,SAAQ,UAAU,MAAM;;;AAI1B,SAAgB,uBAA0B,SAAqC;AAC7E,SAAQ,UAAU,KAAA,EAA0B;;;;;;;;;;;;;;;AAgB9C,SAAgB,gCACd,SACM;AACN,KAAI,mCAAmC,QAAQ,CAAE;CAEjD,IAAI;CACJ,IAAI,WAAW;CACf,MAAM,mBAAmB,QAAQ,SAAS,KAAK,QAAQ;CACvD,MAAM,cAAc,QAAQ,IAAI,KAAK,QAAQ;AAE7C,QAAO,eAAe,SAAS,aAAa;EAC1C,cAAc;EACd,UAAU;EACV,MAAM,OAAgB;AACpB,mBAAgB;;EAEnB,CAAC;AAEF,QAAO,eAAe,SAAS,OAAO;EACpC,cAAc;EACd,UAAU;EACV,MACE,OACA,UACA,GAAG,MACM;AACT;AACA,OAAI;AACF,WAAO,YAAY,OAAO,UAAU,GAAG,KAAK;aACpC;AACR;;;EAGL,CAAC;AAEF,QAAO,eAAe,SAAS,YAAY;EACzC,cAAc;EACd,UAAU;EACV,QAAuB;AACrB,OAAI,WAAW,EAAG,QAAO,kBAAkB;GAC3C,MAAM,SAAS,kBAAkB;AACjC,UAAO,WAAW,KAAA,IAAY,SAAS;;EAE1C,CAAC"}
@@ -525,6 +525,7 @@ let globalSilent = false;
525
525
  /** Minimum level for the global `log` API only (`ownsEvent === false`). Default: all levels. */
526
526
  let globalMinLevel = "debug";
527
527
  let _locked = false;
528
+ let _loggerInitialized = false;
528
529
  let globalPluginRunner = getEmptyPluginRunner();
529
530
  /**
530
531
  * Initialize the logger with configuration.
@@ -552,6 +553,7 @@ function initLogger(config = {}) {
552
553
  if (globalPluginRunner.plugins.length > 0) globalPluginRunner.runSetup({ env: { ...globalEnv } });
553
554
  const hasAnyDrain = !!globalDrain || globalPluginRunner.hasDrain;
554
555
  if (globalSilent && !hasAnyDrain && !config._suppressDrainWarning) console.warn("[evlog] silent mode is enabled but no drain is configured. Events will be built and sampled but not output anywhere. Set a drain via initLogger({ drain }) or a framework hook (evlog:drain).");
556
+ _loggerInitialized = true;
555
557
  }
556
558
  /**
557
559
  * @internal Get the globally registered plugin runner.
@@ -582,6 +584,12 @@ function isLoggerLocked() {
582
584
  return _locked;
583
585
  }
584
586
  /**
587
+ * @internal Whether {@link initLogger} has completed at least once.
588
+ */
589
+ function isLoggerInitialized() {
590
+ return _loggerInitialized;
591
+ }
592
+ /**
585
593
  * @internal Get the globally configured drain callback.
586
594
  * Used by framework middleware to fall back to the global drain
587
595
  * when no middleware-level drain is provided.
@@ -907,8 +915,7 @@ function prettyPrintWideEvent(event) {
907
915
  styles.push(cssColors.dim, cssColors.reset, lc, cssColors.reset, cssColors.cyan, cssColors.reset);
908
916
  } else {
909
917
  const lc = getLevelColor(levelLabel);
910
- if (isDev()) parts.push(`${lc}${levelLabel.toUpperCase()}${colors.reset} ${colors.cyan}[${service}]${colors.reset}`);
911
- else parts.push(`${colors.dim}${ts}${colors.reset} ${lc}${levelLabel.toUpperCase()}${colors.reset} ${colors.cyan}[${service}]${colors.reset}`);
918
+ parts.push(`${colors.dim}${ts}${colors.reset} ${lc}${levelLabel.toUpperCase()}${colors.reset} ${colors.cyan}[${service}]${colors.reset}`);
912
919
  }
913
920
  if (rest.method && rest.path) {
914
921
  parts.push(browser ? ` ${escapeFormatString(String(rest.method))} ${escapeFormatString(String(rest.path))}` : ` ${rest.method} ${rest.path}`);
@@ -1154,8 +1161,9 @@ function createLogger(initialContext = {}, internalOptions) {
1154
1161
  const durationMs = Date.now() - startTime;
1155
1162
  const level = manualLevel ?? (hasError ? "error" : hasWarn ? "warn" : "info");
1156
1163
  let forceKeep = false;
1164
+ const auditForceKeep = consumeAuditForceKeep(context);
1157
1165
  if (overrides?._forceKeep) forceKeep = true;
1158
- else if (consumeAuditForceKeep(context)) forceKeep = true;
1166
+ else if (auditForceKeep) forceKeep = true;
1159
1167
  else if (globalSampling.keep?.length) forceKeep = shouldKeep({
1160
1168
  status: overrides?.status ?? context.status,
1161
1169
  duration: durationMs,
@@ -1614,14 +1622,21 @@ function consumeAuditForceKeep(context) {
1614
1622
  if (context.audit) return true;
1615
1623
  return false;
1616
1624
  }
1625
+ /** @internal True when `audit` has the minimum fields required before emit decoration. */
1626
+ function isCompleteAuditFields(a) {
1627
+ if (!a || typeof a !== "object") return false;
1628
+ const audit = a;
1629
+ return typeof audit.action === "string" && typeof audit.actor?.type === "string" && typeof audit.actor?.id === "string";
1630
+ }
1617
1631
  /**
1618
1632
  * @internal Decorate the audit field on an event right before drain — fills
1619
1633
  * in the deterministic idempotency key. Called by the logger pipeline so
1620
1634
  * it works for both `log.audit()` and direct `log.set({ audit })` paths.
1635
+ * Skips partial/legacy shapes that omit required actor fields.
1621
1636
  */
1622
1637
  function finalizeAudit(event) {
1623
1638
  const a = event.audit;
1624
- if (!a) return;
1639
+ if (!isCompleteAuditFields(a)) return;
1625
1640
  const decorated = decorateAudit(a, String(event.timestamp));
1626
1641
  event.audit = decorated;
1627
1642
  _testCollector?.(decorated, event);
@@ -1869,6 +1884,6 @@ const auditRedactPreset = { paths: [
1869
1884
  "set-cookie"
1870
1885
  ] };
1871
1886
  //#endregion
1872
- export { drainPlugin as A, isLoggerLocked as C, shouldKeep as D, mergeInto as E, redactEvent as F, resolveRedactConfig as I, getEmptyPluginRunner as M, isGloballyRedacted as N, createPluginRunner as O, normalizeRedactConfig as P, isEnabled as S, markWideEventDrainStarted as T, createRequestLogger as _, auditEnricher as a, getGlobalPluginRunner as b, buildAuditFields as c, mockAudit as d, signed as f, createLogger as g, _log as h, auditDiff as i, enricherPlugin as j, definePlugin as k, consumeAuditForceKeep as l, withAuditMethods as m, AuditDeniedError as n, auditOnly as o, withAudit as p, audit as r, auditRedactPreset as s, AUDIT_SCHEMA_VERSION as t, finalizeAudit as u, getEnvironment as v, lockLogger as w, initLogger as x, getGlobalDrain as y };
1887
+ export { definePlugin as A, isLoggerInitialized as C, mergeInto as D, markWideEventDrainStarted as E, normalizeRedactConfig as F, redactEvent as I, resolveRedactConfig as L, enricherPlugin as M, getEmptyPluginRunner as N, shouldKeep as O, isGloballyRedacted as P, isEnabled as S, lockLogger as T, createRequestLogger as _, auditEnricher as a, getGlobalPluginRunner as b, buildAuditFields as c, mockAudit as d, signed as f, createLogger as g, _log as h, auditDiff as i, drainPlugin as j, createPluginRunner as k, consumeAuditForceKeep as l, withAuditMethods as m, AuditDeniedError as n, auditOnly as o, withAudit as p, audit as r, auditRedactPreset as s, AUDIT_SCHEMA_VERSION as t, finalizeAudit as u, getEnvironment as v, isLoggerLocked as w, initLogger as x, getGlobalDrain as y };
1873
1888
 
1874
- //# sourceMappingURL=audit-CL0q7GJU.mjs.map
1889
+ //# sourceMappingURL=audit-BXCYMjcC.mjs.map