evlog 2.14.1 → 2.16.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 (187) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +4 -4
  3. package/dist/adapters/axiom.d.mts +18 -27
  4. package/dist/adapters/axiom.d.mts.map +1 -1
  5. package/dist/adapters/axiom.mjs +40 -30
  6. package/dist/adapters/axiom.mjs.map +1 -1
  7. package/dist/adapters/better-stack.d.mts +11 -24
  8. package/dist/adapters/better-stack.d.mts.map +1 -1
  9. package/dist/adapters/better-stack.mjs +34 -29
  10. package/dist/adapters/better-stack.mjs.map +1 -1
  11. package/dist/adapters/datadog.d.mts +1 -1
  12. package/dist/adapters/datadog.d.mts.map +1 -1
  13. package/dist/adapters/datadog.mjs +10 -4
  14. package/dist/adapters/datadog.mjs.map +1 -1
  15. package/dist/adapters/fs.d.mts +2 -2
  16. package/dist/adapters/fs.d.mts.map +1 -1
  17. package/dist/adapters/fs.mjs +19 -7
  18. package/dist/adapters/fs.mjs.map +1 -1
  19. package/dist/adapters/hyperdx.d.mts +1 -1
  20. package/dist/adapters/hyperdx.mjs +1 -2
  21. package/dist/adapters/hyperdx.mjs.map +1 -1
  22. package/dist/adapters/otlp.d.mts +1 -1
  23. package/dist/adapters/otlp.d.mts.map +1 -1
  24. package/dist/adapters/otlp.mjs +36 -31
  25. package/dist/adapters/otlp.mjs.map +1 -1
  26. package/dist/adapters/posthog.d.mts +50 -70
  27. package/dist/adapters/posthog.d.mts.map +1 -1
  28. package/dist/adapters/posthog.mjs +50 -85
  29. package/dist/adapters/posthog.mjs.map +1 -1
  30. package/dist/adapters/sentry.d.mts +1 -1
  31. package/dist/adapters/sentry.d.mts.map +1 -1
  32. package/dist/adapters/sentry.mjs +15 -5
  33. package/dist/adapters/sentry.mjs.map +1 -1
  34. package/dist/ai/index.d.mts +15 -1
  35. package/dist/ai/index.d.mts.map +1 -1
  36. package/dist/ai/index.mjs +48 -16
  37. package/dist/ai/index.mjs.map +1 -1
  38. package/dist/{audit-CTIviX3P.d.mts → audit-X1uUukm3.d.mts} +145 -2
  39. package/dist/audit-X1uUukm3.d.mts.map +1 -0
  40. package/dist/{audit-DQoBo7Dl.mjs → audit-pV5aLGP0.mjs} +153 -13
  41. package/dist/audit-pV5aLGP0.mjs.map +1 -0
  42. package/dist/better-auth/index.d.mts +1 -1
  43. package/dist/browser.d.mts +1 -1
  44. package/dist/define-CuXOqecD.d.mts +57 -0
  45. package/dist/define-CuXOqecD.d.mts.map +1 -0
  46. package/dist/define-D6OJdSUH.mjs +63 -0
  47. package/dist/define-D6OJdSUH.mjs.map +1 -0
  48. package/dist/{dist-Do8P4zWd.mjs → dist-BIlS38vi.mjs} +1 -1
  49. package/dist/dist-BIlS38vi.mjs.map +1 -0
  50. package/dist/drain-ByWUeOQC.mjs +160 -0
  51. package/dist/drain-ByWUeOQC.mjs.map +1 -0
  52. package/dist/elysia/index.d.mts +25 -2
  53. package/dist/elysia/index.d.mts.map +1 -1
  54. package/dist/elysia/index.mjs +53 -20
  55. package/dist/elysia/index.mjs.map +1 -1
  56. package/dist/enricher-DYTr9I16.d.mts +42 -0
  57. package/dist/enricher-DYTr9I16.d.mts.map +1 -0
  58. package/dist/enricher-Dy06T17G.mjs +95 -0
  59. package/dist/enricher-Dy06T17G.mjs.map +1 -0
  60. package/dist/enrichers.d.mts +16 -9
  61. package/dist/enrichers.d.mts.map +1 -1
  62. package/dist/enrichers.mjs +81 -64
  63. package/dist/enrichers.mjs.map +1 -1
  64. package/dist/{error-C7gSQVqk.d.mts → error-Cpc7RVz6.d.mts} +7 -2
  65. package/dist/error-Cpc7RVz6.d.mts.map +1 -0
  66. package/dist/error.d.mts +1 -1
  67. package/dist/error.mjs +8 -1
  68. package/dist/error.mjs.map +1 -1
  69. package/dist/{errors-BJRXUfMg.mjs → errors-BQgyQ9xe.mjs} +1 -1
  70. package/dist/{errors-BJRXUfMg.mjs.map → errors-BQgyQ9xe.mjs.map} +1 -1
  71. package/dist/{errors-4MPmTzjY.d.mts → errors-prnQ3kES.d.mts} +2 -2
  72. package/dist/{errors-4MPmTzjY.d.mts.map → errors-prnQ3kES.d.mts.map} +1 -1
  73. package/dist/event-DcHmEm3O.mjs +55 -0
  74. package/dist/event-DcHmEm3O.mjs.map +1 -0
  75. package/dist/express/index.d.mts +2 -2
  76. package/dist/express/index.d.mts.map +1 -1
  77. package/dist/express/index.mjs +17 -15
  78. package/dist/express/index.mjs.map +1 -1
  79. package/dist/fastify/index.d.mts +2 -2
  80. package/dist/fastify/index.d.mts.map +1 -1
  81. package/dist/fastify/index.mjs +19 -20
  82. package/dist/fastify/index.mjs.map +1 -1
  83. package/dist/fork-DPN8aL8O.mjs +227 -0
  84. package/dist/fork-DPN8aL8O.mjs.map +1 -0
  85. package/dist/{headers-D74M0wsg.mjs → headers-CU-QqnYg.mjs} +19 -2
  86. package/dist/headers-CU-QqnYg.mjs.map +1 -0
  87. package/dist/hono/index.d.mts +2 -2
  88. package/dist/hono/index.d.mts.map +1 -1
  89. package/dist/hono/index.mjs +14 -10
  90. package/dist/hono/index.mjs.map +1 -1
  91. package/dist/http.d.mts +1 -1
  92. package/dist/index.d.mts +8 -7
  93. package/dist/index.mjs +3 -2
  94. package/dist/integration-DSZPbI9N.mjs +75 -0
  95. package/dist/integration-DSZPbI9N.mjs.map +1 -0
  96. package/dist/{logger-DttRJRGa.d.mts → logger-U8lgdc9x.d.mts} +9 -3
  97. package/dist/logger-U8lgdc9x.d.mts.map +1 -0
  98. package/dist/logger.d.mts +2 -2
  99. package/dist/logger.mjs +2 -2
  100. package/dist/middleware-CAQHJRN1.d.mts +72 -0
  101. package/dist/middleware-CAQHJRN1.d.mts.map +1 -0
  102. package/dist/nestjs/index.d.mts +2 -2
  103. package/dist/nestjs/index.mjs +3 -4
  104. package/dist/nestjs/index.mjs.map +1 -1
  105. package/dist/next/client.d.mts +1 -1
  106. package/dist/next/index.d.mts +4 -4
  107. package/dist/next/index.mjs +3 -3
  108. package/dist/next/instrumentation.d.mts +1 -1
  109. package/dist/next/instrumentation.mjs +1 -1
  110. package/dist/nitro/errorHandler.mjs +2 -2
  111. package/dist/nitro/module.d.mts +2 -2
  112. package/dist/nitro/plugin.mjs +21 -11
  113. package/dist/nitro/plugin.mjs.map +1 -1
  114. package/dist/nitro/v3/errorHandler.mjs +3 -3
  115. package/dist/nitro/v3/index.d.mts +2 -2
  116. package/dist/nitro/v3/module.d.mts +1 -1
  117. package/dist/nitro/v3/plugin.mjs +29 -17
  118. package/dist/nitro/v3/plugin.mjs.map +1 -1
  119. package/dist/nitro/v3/useLogger.d.mts +1 -1
  120. package/dist/{nitro-CPPRCPbG.d.mts → nitro-C6Bd682U.d.mts} +2 -2
  121. package/dist/{nitro-CPPRCPbG.d.mts.map → nitro-C6Bd682U.d.mts.map} +1 -1
  122. package/dist/{nitro-OmT_M4Pb.mjs → nitro-DavLelNz.mjs} +2 -2
  123. package/dist/nitro-DavLelNz.mjs.map +1 -0
  124. package/dist/{nitroConfigBridge-C37lXaNm.mjs → nitroConfigBridge-aZ1e5upQ.mjs} +1 -1
  125. package/dist/nitroConfigBridge-aZ1e5upQ.mjs.map +1 -0
  126. package/dist/nuxt/module.d.mts +1 -1
  127. package/dist/nuxt/module.mjs +2 -2
  128. package/dist/{parseError-o1GpZEOR.d.mts → parseError-B-dKF6Fd.d.mts} +2 -2
  129. package/dist/parseError-B-dKF6Fd.d.mts.map +1 -0
  130. package/dist/react-router/index.d.mts +2 -2
  131. package/dist/react-router/index.mjs +3 -4
  132. package/dist/react-router/index.mjs.map +1 -1
  133. package/dist/{routes-CGPmbzCZ.mjs → routes-B48wm7Pb.mjs} +1 -1
  134. package/dist/{routes-CGPmbzCZ.mjs.map → routes-B48wm7Pb.mjs.map} +1 -1
  135. package/dist/runtime/client/log.d.mts +1 -1
  136. package/dist/runtime/server/routes/_evlog/ingest.post.mjs +21 -10
  137. package/dist/runtime/server/routes/_evlog/ingest.post.mjs.map +1 -1
  138. package/dist/runtime/server/useLogger.d.mts +1 -1
  139. package/dist/runtime/utils/parseError.d.mts +2 -2
  140. package/dist/runtime/utils/parseError.mjs +9 -1
  141. package/dist/runtime/utils/parseError.mjs.map +1 -1
  142. package/dist/{_severity-CQijvfhU.mjs → severity-BYWZ96Sb.mjs} +6 -2
  143. package/dist/severity-BYWZ96Sb.mjs.map +1 -0
  144. package/dist/{source-location-DRvDDqfq.mjs → source-location-Dco0cRTz.mjs} +3 -3
  145. package/dist/source-location-Dco0cRTz.mjs.map +1 -0
  146. package/dist/storage-BT-3fT1-.mjs +27 -0
  147. package/dist/storage-BT-3fT1-.mjs.map +1 -0
  148. package/dist/sveltekit/index.d.mts +2 -2
  149. package/dist/sveltekit/index.mjs +5 -6
  150. package/dist/sveltekit/index.mjs.map +1 -1
  151. package/dist/toolkit.d.mts +288 -12
  152. package/dist/toolkit.d.mts.map +1 -1
  153. package/dist/toolkit.mjs +13 -7
  154. package/dist/types.d.mts +1 -1
  155. package/dist/{useLogger-CyPP1sVB.d.mts → useLogger-CoNgTjp5.d.mts} +2 -2
  156. package/dist/{useLogger-CyPP1sVB.d.mts.map → useLogger-CoNgTjp5.d.mts.map} +1 -1
  157. package/dist/{utils-Dmin7wVL.d.mts → utils-Db4qhBWn.d.mts} +2 -2
  158. package/dist/{utils-Dmin7wVL.d.mts.map → utils-Db4qhBWn.d.mts.map} +1 -1
  159. package/dist/utils.d.mts +1 -1
  160. package/dist/vite/index.d.mts +1 -1
  161. package/dist/vite/index.mjs +1 -1
  162. package/dist/workers.d.mts +1 -1
  163. package/dist/workers.mjs +1 -1
  164. package/package.json +22 -19
  165. package/dist/_drain-CmCtsuF6.mjs +0 -23
  166. package/dist/_drain-CmCtsuF6.mjs.map +0 -1
  167. package/dist/_http-BY1e9pwC.mjs +0 -78
  168. package/dist/_http-BY1e9pwC.mjs.map +0 -1
  169. package/dist/_severity-CQijvfhU.mjs.map +0 -1
  170. package/dist/audit-CTIviX3P.d.mts.map +0 -1
  171. package/dist/audit-DQoBo7Dl.mjs.map +0 -1
  172. package/dist/dist-Do8P4zWd.mjs.map +0 -1
  173. package/dist/error-C7gSQVqk.d.mts.map +0 -1
  174. package/dist/fork-D1j1Fuzy.mjs +0 -72
  175. package/dist/fork-D1j1Fuzy.mjs.map +0 -1
  176. package/dist/headers-D74M0wsg.mjs.map +0 -1
  177. package/dist/logger-DttRJRGa.d.mts.map +0 -1
  178. package/dist/middleware-CTnDsST-.d.mts +0 -93
  179. package/dist/middleware-CTnDsST-.d.mts.map +0 -1
  180. package/dist/middleware-oAccqyPp.mjs +0 -123
  181. package/dist/middleware-oAccqyPp.mjs.map +0 -1
  182. package/dist/nitro-OmT_M4Pb.mjs.map +0 -1
  183. package/dist/nitroConfigBridge-C37lXaNm.mjs.map +0 -1
  184. package/dist/parseError-o1GpZEOR.d.mts.map +0 -1
  185. package/dist/source-location-DRvDDqfq.mjs.map +0 -1
  186. package/dist/storage-CFGTn37X.mjs +0 -46
  187. package/dist/storage-CFGTn37X.mjs.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\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 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 }\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\nfunction buildMetadata(state: AccumulatorState): AIMetadata {\n const uniqueModels = [...new Set(state.models)]\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 (uniqueModels.length > 1) data.models = uniqueModels\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.toolInputs && state.allToolCallInputs.length > 0) {\n data.toolCalls = state.allToolCallInputs.map(t => ({ ...t }))\n } else if (state.allToolCalls.length > 0) {\n data.toolCalls = [...state.allToolCalls]\n }\n if (state.lastResponseId) data.responseId = state.lastResponseId\n if (state.steps > 1) {\n data.steps = state.steps\n data.stepsUsage = state.stepsUsage.map(s => ({ ...s, ...(s.toolCalls ? { toolCalls: [...s.toolCalls] } : {}) }))\n }\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.toolExecutions.length > 0) data.tools = state.toolExecutions.map(t => ({ ...t }))\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 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\nfunction flushState(log: RequestLogger, state: AccumulatorState): void {\n const data = buildMetadata(state)\n log.set({ ai: data } as Record<string, unknown>)\n notifySubscribers(state, data)\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 { 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,3 +1,110 @@
1
+ //#region src/shared/plugin.d.ts
2
+ /** Context passed to {@link EvlogPlugin.setup} when the plugin is registered. */
3
+ interface PluginSetupContext {
4
+ env: EnvironmentContext;
5
+ }
6
+ /** Per-request context for `onRequestStart` / `onRequestFinish`. */
7
+ interface RequestLifecycleContext {
8
+ logger: RequestLogger;
9
+ request: {
10
+ method: string;
11
+ path: string;
12
+ requestId?: string;
13
+ };
14
+ /** Pre-filtered safe request headers (sensitive headers stripped). */
15
+ headers?: Record<string, string>;
16
+ }
17
+ interface RequestFinishContext extends RequestLifecycleContext {
18
+ /** `null` when the event was sampled out or disabled. */
19
+ event: WideEvent | null;
20
+ status?: number;
21
+ durationMs: number;
22
+ error?: Error;
23
+ }
24
+ /** Context passed to {@link EvlogPlugin.onClientLog} for client-submitted events. */
25
+ interface ClientLogContext {
26
+ /** Raw client payload, before normalization. */
27
+ payload: Record<string, unknown>;
28
+ request?: {
29
+ method?: string;
30
+ path?: string;
31
+ };
32
+ headers?: Record<string, string>;
33
+ }
34
+ /**
35
+ * Canonical extension point for evlog. A plugin can opt into any subset of
36
+ * hooks; drains and enrichers are special cases (see {@link drainPlugin} and
37
+ * {@link enricherPlugin}).
38
+ *
39
+ * @example
40
+ * ```ts
41
+ * export const tenantPlugin = definePlugin({
42
+ * name: 'tenant',
43
+ * onRequestStart({ logger, headers }) {
44
+ * const tenantId = headers?.['x-tenant-id']
45
+ * if (tenantId) logger.set({ tenant: { id: tenantId } })
46
+ * },
47
+ * enrich({ event }) {
48
+ * event.region = process.env.REGION
49
+ * },
50
+ * })
51
+ * ```
52
+ */
53
+ interface EvlogPlugin {
54
+ /** Stable identifier. Used for de-duplication and error messages. */
55
+ name: string;
56
+ /** Run-once when the plugin is registered. */
57
+ setup?: (ctx: PluginSetupContext) => void | Promise<void>;
58
+ /** Runs before drain. */
59
+ enrich?: (ctx: EnrichContext) => void | Promise<void>;
60
+ /** Called for every emitted event. */
61
+ drain?: (ctx: DrainContext) => void | Promise<void>;
62
+ /** Tail sampling hook. Set `ctx.shouldKeep = true` to force-keep. */
63
+ keep?: (ctx: TailSamplingContext) => void | Promise<void>;
64
+ onRequestStart?: (ctx: RequestLifecycleContext) => void;
65
+ onRequestFinish?: (ctx: RequestFinishContext) => void;
66
+ /** Observe events submitted from browser/edge clients. */
67
+ onClientLog?: (ctx: ClientLogContext) => void;
68
+ /**
69
+ * Decorate per-request loggers with extra methods. Augment `RequestLogger`
70
+ * in a `.d.ts` to expose them on `useLogger()`.
71
+ */
72
+ extendLogger?: (logger: RequestLogger) => void;
73
+ }
74
+ /** Identity helper preserving plugin type inference. */
75
+ declare function definePlugin(plugin: EvlogPlugin): EvlogPlugin;
76
+ /** Wrap a standalone drain callback as an {@link EvlogPlugin}. */
77
+ declare function drainPlugin(name: string, drain: NonNullable<EvlogPlugin['drain']>): EvlogPlugin;
78
+ /** Wrap a standalone enricher callback as an {@link EvlogPlugin}. */
79
+ declare function enricherPlugin(name: string, enrich: NonNullable<EvlogPlugin['enrich']>): EvlogPlugin;
80
+ /**
81
+ * Compiled view of a plugin set. Errors from individual hooks are caught and
82
+ * logged to `console.error` with the plugin name — they never break the request.
83
+ */
84
+ interface PluginRunner {
85
+ readonly plugins: readonly EvlogPlugin[];
86
+ /** `true` when at least one plugin implements the matching hook. */
87
+ readonly hasEnrich: boolean;
88
+ readonly hasDrain: boolean;
89
+ readonly hasKeep: boolean;
90
+ readonly hasRequestLifecycle: boolean;
91
+ readonly hasClientLog: boolean;
92
+ readonly hasExtendLogger: boolean;
93
+ applyExtendLogger: (logger: RequestLogger) => void;
94
+ runOnRequestStart: (ctx: RequestLifecycleContext) => void;
95
+ runOnRequestFinish: (ctx: RequestFinishContext) => void;
96
+ runEnrich: (ctx: EnrichContext) => Promise<void>;
97
+ /** Drains run concurrently (`Promise.allSettled`). */
98
+ runDrain: (ctx: DrainContext) => Promise<void>;
99
+ runKeep: (ctx: TailSamplingContext) => Promise<void>;
100
+ runOnClientLog: (ctx: ClientLogContext) => void;
101
+ runSetup: (ctx: PluginSetupContext) => Promise<void>;
102
+ }
103
+ /** De-duplicates by `name` — last registration wins. */
104
+ declare function createPluginRunner(plugins?: EvlogPlugin[]): PluginRunner;
105
+ /** Shared no-op runner used when no plugins are registered. */
106
+ declare function getEmptyPluginRunner(): PluginRunner;
107
+ //#endregion
1
108
  //#region src/types.d.ts
2
109
  declare module 'nitropack/types' {
3
110
  interface NitroRuntimeHooks {
@@ -359,6 +466,31 @@ interface LoggerConfig {
359
466
  * ```
360
467
  */
361
468
  drain?: (ctx: DrainContext) => void | Promise<void>;
469
+ /**
470
+ * Plugins registered globally with the logger.
471
+ *
472
+ * Plugins are the canonical extension contract for evlog: they can opt into
473
+ * `enrich`, `drain`, `keep`, lifecycle hooks (`onRequestStart`, `onRequestFinish`),
474
+ * client-log observation, and per-request logger decoration. See
475
+ * {@link import('./shared/plugin').EvlogPlugin} for the full surface.
476
+ *
477
+ * Plugin drains run alongside the standalone `drain` callback (composed with
478
+ * `Promise.allSettled`), so you can mix one-off drains and plugin-shaped
479
+ * extensions safely.
480
+ *
481
+ * @example
482
+ * ```ts
483
+ * import { initLogger, definePlugin } from 'evlog'
484
+ * import { createAxiomDrain } from 'evlog/axiom'
485
+ *
486
+ * initLogger({
487
+ * plugins: [
488
+ * definePlugin({ name: 'axiom', drain: createAxiomDrain() }),
489
+ * ],
490
+ * })
491
+ * ```
492
+ */
493
+ plugins?: Array<EvlogPlugin>;
362
494
  /** @internal Suppress the "silent without drain" warning (used by hook-based frameworks like Nitro/Nuxt) */
363
495
  _suppressDrainWarning?: boolean;
364
496
  }
@@ -677,6 +809,12 @@ interface Log {
677
809
  interface ErrorOptions {
678
810
  /** What actually happened */
679
811
  message: string;
812
+ /**
813
+ * Stable, machine-readable identifier for this error (e.g. `'PAYMENT_DECLINED'`,
814
+ * `'auth/invalid-token'`). Surfaces in HTTP responses, `parseError`, and wide
815
+ * events so clients can branch on `err.code` and dashboards can group by code.
816
+ */
817
+ code?: string;
680
818
  /** HTTP status code (default: 500) */
681
819
  status?: number;
682
820
  /** Why this error occurred */
@@ -753,6 +891,11 @@ interface ServerEvent {
753
891
  interface ParsedError {
754
892
  message: string;
755
893
  status: number;
894
+ /**
895
+ * Stable, machine-readable identifier copied from `EvlogError.code`,
896
+ * h3-style `data.code`, or a Node-style `Error.code` (e.g. `'ENOENT'`).
897
+ */
898
+ code?: string;
756
899
  why?: string;
757
900
  fix?: string;
758
901
  link?: string;
@@ -1136,5 +1279,5 @@ declare function signed(drain: DrainFn, options: SignedOptions): DrainFn;
1136
1279
  */
1137
1280
  declare const auditRedactPreset: RedactConfig;
1138
1281
  //#endregion
1139
- export { SamplingRates as $, AuditFields as A, H3EventContext as B, buildAuditFields as C, withAudit as D, signed as E, DrainContext as F, LoggerConfig as G, InternalFields as H, EnrichContext as I, RequestLogEntry as J, ParsedError as K, EnvironmentContext as L, AuditTarget as M, BaseWideEvent as N, withAuditMethods as O, DeepPartial as P, SamplingConfig as Q, ErrorOptions as R, auditRedactPreset as S, mockAudit as T, Log as U, IngestPayload as V, LogLevel as W, RequestLoggerOptions as X, RequestLogger as Y, RouteConfig as Z, WithAuditOptions as _, AuditInput as a, auditEnricher as b, AuditOnlyOptions as c, DefinedAuditAction as d, ServerEvent as et, DrainFn as f, WithAuditContext as g, SignedOptions as h, AuditEnricherOptions as i, WideEvent as it, AuditLoggerMethod as j, AuditActor as k, AuditPatchOp as l, SignedChainState as m, AuditDeniedError as n, TailSamplingContext as nt, AuditMatcher as o, MockAudit as p, RedactConfig as q, AuditDiffOptions as r, TransportConfig as rt, AuditMethod as s, AUDIT_SCHEMA_VERSION as t, TailSamplingCondition as tt, AuditableLogger as u, audit as v, defineAuditAction as w, auditOnly as x, auditDiff as y, FieldContext as z };
1140
- //# sourceMappingURL=audit-CTIviX3P.d.mts.map
1282
+ export { SamplingRates as $, AuditFields as A, H3EventContext as B, buildAuditFields as C, withAudit as D, signed as E, DrainContext as F, LoggerConfig as G, InternalFields as H, EnrichContext as I, RequestLogEntry as J, ParsedError as K, EnvironmentContext as L, AuditTarget as M, BaseWideEvent as N, withAuditMethods as O, DeepPartial as P, SamplingConfig as Q, ErrorOptions as R, auditRedactPreset as S, mockAudit as T, Log as U, IngestPayload as V, LogLevel as W, RequestLoggerOptions as X, RequestLogger as Y, RouteConfig as Z, WithAuditOptions as _, AuditInput as a, ClientLogContext as at, auditEnricher as b, AuditOnlyOptions as c, PluginSetupContext as ct, DefinedAuditAction as d, createPluginRunner as dt, ServerEvent as et, DrainFn as f, definePlugin as ft, WithAuditContext as g, SignedOptions as h, getEmptyPluginRunner as ht, AuditEnricherOptions as i, WideEvent as it, AuditLoggerMethod as j, AuditActor as k, AuditPatchOp as l, RequestFinishContext as lt, SignedChainState as m, enricherPlugin as mt, AuditDeniedError as n, TailSamplingContext as nt, AuditMatcher as o, EvlogPlugin as ot, MockAudit as p, drainPlugin as pt, RedactConfig as q, AuditDiffOptions as r, TransportConfig as rt, AuditMethod as s, PluginRunner as st, AUDIT_SCHEMA_VERSION as t, TailSamplingCondition as tt, AuditableLogger as u, RequestLifecycleContext as ut, audit as v, defineAuditAction as w, auditOnly as x, auditDiff as y, FieldContext as z };
1283
+ //# sourceMappingURL=audit-X1uUukm3.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit-X1uUukm3.d.mts","names":[],"sources":["../src/shared/plugin.ts","../src/types.ts","../src/audit.ts"],"mappings":"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"}
@@ -1,4 +1,5 @@
1
1
  import { colors, cssColors, detectEnvironment, escapeFormatString, formatDuration, getConsoleMethod, getCssLevelColor, getLevelColor, isBrowser, isDev, isLevelEnabled, matchesPattern } from "./utils.mjs";
2
+ import { r as getHeader$1 } from "./headers-CU-QqnYg.mjs";
2
3
  //#region src/redact.ts
3
4
  const DEFAULT_REPLACEMENT = "[REDACTED]";
4
5
  /**
@@ -188,6 +189,130 @@ function normalizeRedactConfig(raw) {
188
189
  return resolveRedactConfig(config);
189
190
  }
190
191
  //#endregion
192
+ //#region src/shared/plugin.ts
193
+ /** Identity helper preserving plugin type inference. */
194
+ function definePlugin(plugin) {
195
+ return plugin;
196
+ }
197
+ /** Wrap a standalone drain callback as an {@link EvlogPlugin}. */
198
+ function drainPlugin(name, drain) {
199
+ return {
200
+ name,
201
+ drain
202
+ };
203
+ }
204
+ /** Wrap a standalone enricher callback as an {@link EvlogPlugin}. */
205
+ function enricherPlugin(name, enrich) {
206
+ return {
207
+ name,
208
+ enrich
209
+ };
210
+ }
211
+ function logPluginError(name, hook, err) {
212
+ console.error(`[evlog/${name}] ${hook} failed:`, err);
213
+ }
214
+ /** De-duplicates by `name` — last registration wins. */
215
+ function createPluginRunner(plugins = []) {
216
+ const byName = /* @__PURE__ */ new Map();
217
+ for (const plugin of plugins) byName.set(plugin.name, plugin);
218
+ const list = Array.from(byName.values());
219
+ return {
220
+ plugins: list,
221
+ hasEnrich: list.some((p) => typeof p.enrich === "function"),
222
+ hasDrain: list.some((p) => typeof p.drain === "function"),
223
+ hasKeep: list.some((p) => typeof p.keep === "function"),
224
+ hasRequestLifecycle: list.some((p) => typeof p.onRequestStart === "function" || typeof p.onRequestFinish === "function"),
225
+ hasClientLog: list.some((p) => typeof p.onClientLog === "function"),
226
+ hasExtendLogger: list.some((p) => typeof p.extendLogger === "function"),
227
+ applyExtendLogger(logger) {
228
+ for (const plugin of list) {
229
+ if (!plugin.extendLogger) continue;
230
+ try {
231
+ plugin.extendLogger(logger);
232
+ } catch (err) {
233
+ logPluginError(plugin.name, "extendLogger", err);
234
+ }
235
+ }
236
+ },
237
+ runOnRequestStart(ctx) {
238
+ for (const plugin of list) {
239
+ if (!plugin.onRequestStart) continue;
240
+ try {
241
+ plugin.onRequestStart(ctx);
242
+ } catch (err) {
243
+ logPluginError(plugin.name, "onRequestStart", err);
244
+ }
245
+ }
246
+ },
247
+ runOnRequestFinish(ctx) {
248
+ for (const plugin of list) {
249
+ if (!plugin.onRequestFinish) continue;
250
+ try {
251
+ plugin.onRequestFinish(ctx);
252
+ } catch (err) {
253
+ logPluginError(plugin.name, "onRequestFinish", err);
254
+ }
255
+ }
256
+ },
257
+ async runEnrich(ctx) {
258
+ for (const plugin of list) {
259
+ if (!plugin.enrich) continue;
260
+ try {
261
+ await plugin.enrich(ctx);
262
+ } catch (err) {
263
+ logPluginError(plugin.name, "enrich", err);
264
+ }
265
+ }
266
+ },
267
+ async runDrain(ctx) {
268
+ const drains = list.filter((p) => typeof p.drain === "function");
269
+ if (drains.length === 0) return;
270
+ await Promise.allSettled(drains.map(async (plugin) => {
271
+ try {
272
+ await plugin.drain(ctx);
273
+ } catch (err) {
274
+ logPluginError(plugin.name, "drain", err);
275
+ }
276
+ }));
277
+ },
278
+ async runKeep(ctx) {
279
+ for (const plugin of list) {
280
+ if (!plugin.keep) continue;
281
+ try {
282
+ await plugin.keep(ctx);
283
+ } catch (err) {
284
+ logPluginError(plugin.name, "keep", err);
285
+ }
286
+ }
287
+ },
288
+ runOnClientLog(ctx) {
289
+ for (const plugin of list) {
290
+ if (!plugin.onClientLog) continue;
291
+ try {
292
+ plugin.onClientLog(ctx);
293
+ } catch (err) {
294
+ logPluginError(plugin.name, "onClientLog", err);
295
+ }
296
+ }
297
+ },
298
+ async runSetup(ctx) {
299
+ for (const plugin of list) {
300
+ if (!plugin.setup) continue;
301
+ try {
302
+ await plugin.setup(ctx);
303
+ } catch (err) {
304
+ logPluginError(plugin.name, "setup", err);
305
+ }
306
+ }
307
+ }
308
+ };
309
+ }
310
+ const emptyRunner = createPluginRunner([]);
311
+ /** Shared no-op runner used when no plugins are registered. */
312
+ function getEmptyPluginRunner() {
313
+ return emptyRunner;
314
+ }
315
+ //#endregion
191
316
  //#region src/logger.ts
192
317
  function isPlainObject(val) {
193
318
  return val !== null && typeof val === "object" && !Array.isArray(val);
@@ -226,6 +351,7 @@ let globalSilent = false;
226
351
  /** Minimum level for the global `log` API only (`ownsEvent === false`). Default: all levels. */
227
352
  let globalMinLevel = "debug";
228
353
  let _locked = false;
354
+ let globalPluginRunner = getEmptyPluginRunner();
229
355
  /**
230
356
  * Initialize the logger with configuration.
231
357
  * Call this once at application startup.
@@ -247,7 +373,18 @@ function initLogger(config = {}) {
247
373
  globalRedact = resolveRedactConfig(config.redact ?? !isDev());
248
374
  globalSilent = config.silent ?? false;
249
375
  globalMinLevel = config.minLevel ?? "debug";
250
- if (globalSilent && !globalDrain && !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).");
376
+ globalPluginRunner = config.plugins?.length ? createPluginRunner(config.plugins) : getEmptyPluginRunner();
377
+ if (globalPluginRunner.plugins.length > 0) globalPluginRunner.runSetup({ env: { ...globalEnv } });
378
+ const hasAnyDrain = !!globalDrain || globalPluginRunner.hasDrain;
379
+ 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).");
380
+ }
381
+ /**
382
+ * @internal Get the globally registered plugin runner.
383
+ * Used by framework middleware so plugins also fire on routes that pre-date
384
+ * the middleware-level options.
385
+ */
386
+ function getGlobalPluginRunner() {
387
+ return globalPluginRunner;
251
388
  }
252
389
  /**
253
390
  * Check if logging is globally enabled.
@@ -331,11 +468,17 @@ function emitWideEvent(level, event, options = {}) {
331
468
  if (!globalSilent) if (globalPretty) prettyPrintWideEvent(formatted);
332
469
  else if (globalStringify) console[getConsoleMethod(level)](JSON.stringify(formatted));
333
470
  else console[getConsoleMethod(level)](formatted);
334
- if (globalDrain && !deferDrain) {
335
- const drainPromise = Promise.resolve(globalDrain({ event: formatted })).catch((err) => {
336
- console.error("[evlog] drain failed:", err);
337
- });
338
- if (waitUntil) waitUntil(drainPromise);
471
+ if (!deferDrain) {
472
+ const drainPromises = [];
473
+ if (globalDrain) drainPromises.push((async () => {
474
+ try {
475
+ await globalDrain({ event: formatted });
476
+ } catch (err) {
477
+ console.error("[evlog] drain failed:", err);
478
+ }
479
+ })());
480
+ if (globalPluginRunner.hasDrain) drainPromises.push(globalPluginRunner.runDrain({ event: formatted }));
481
+ if (drainPromises.length > 0 && waitUntil) waitUntil(Promise.all(drainPromises));
339
482
  }
340
483
  return formatted;
341
484
  }
@@ -681,6 +824,7 @@ function createLogger(initialContext = {}, internalOptions) {
681
824
  };
682
825
  const errRecord = err;
683
826
  for (const k of [
827
+ "code",
684
828
  "status",
685
829
  "statusText",
686
830
  "statusCode",
@@ -1262,11 +1406,7 @@ function auditEnricher(options = {}) {
1262
1406
  };
1263
1407
  }
1264
1408
  function getHeader(headers, name) {
1265
- if (!headers) return void 0;
1266
- if (headers[name] !== void 0) return headers[name];
1267
- const lower = name.toLowerCase();
1268
- if (headers[lower] !== void 0) return headers[lower];
1269
- for (const [k, v] of Object.entries(headers)) if (k.toLowerCase() === lower) return v;
1409
+ return getHeader$1(headers, name);
1270
1410
  }
1271
1411
  /**
1272
1412
  * Wrap any drain so it only receives events that carry an `audit` field.
@@ -1478,6 +1618,6 @@ const auditRedactPreset = { paths: [
1478
1618
  "audit.context.headers.cookie"
1479
1619
  ] };
1480
1620
  //#endregion
1481
- export { shouldKeep as C, resolveRedactConfig as E, lockLogger as S, redactEvent as T, getEnvironment as _, auditEnricher as a, isEnabled as b, buildAuditFields as c, signed as d, withAudit as f, createRequestLogger as g, createLogger as h, auditDiff as i, defineAuditAction as l, _log as m, AuditDeniedError as n, auditOnly as o, withAuditMethods as p, audit as r, auditRedactPreset as s, AUDIT_SCHEMA_VERSION as t, mockAudit as u, getGlobalDrain as v, normalizeRedactConfig as w, isLoggerLocked as x, initLogger as y };
1621
+ export { normalizeRedactConfig as A, lockLogger as C, drainPlugin as D, definePlugin as E, resolveRedactConfig as M, enricherPlugin as O, isLoggerLocked as S, createPluginRunner as T, getEnvironment as _, auditEnricher as a, initLogger as b, buildAuditFields as c, signed as d, withAudit as f, createRequestLogger as g, createLogger as h, auditDiff as i, redactEvent as j, getEmptyPluginRunner as k, defineAuditAction as l, _log as m, AuditDeniedError as n, auditOnly as o, withAuditMethods as p, audit as r, auditRedactPreset as s, AUDIT_SCHEMA_VERSION as t, mockAudit as u, getGlobalDrain as v, shouldKeep as w, isEnabled as x, getGlobalPluginRunner as y };
1482
1622
 
1483
- //# sourceMappingURL=audit-DQoBo7Dl.mjs.map
1623
+ //# sourceMappingURL=audit-pV5aLGP0.mjs.map