evlog 2.8.0 → 2.9.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 (89) hide show
  1. package/README.md +28 -0
  2. package/dist/adapters/axiom.d.mts +1 -1
  3. package/dist/adapters/better-stack.d.mts +1 -1
  4. package/dist/adapters/fs.d.mts +1 -1
  5. package/dist/adapters/otlp.d.mts +1 -1
  6. package/dist/adapters/posthog.d.mts +1 -1
  7. package/dist/adapters/sentry.d.mts +1 -1
  8. package/dist/ai/index.d.mts +82 -5
  9. package/dist/ai/index.d.mts.map +1 -1
  10. package/dist/ai/index.mjs +237 -96
  11. package/dist/ai/index.mjs.map +1 -1
  12. package/dist/browser.d.mts +1 -1
  13. package/dist/elysia/index.d.mts +2 -2
  14. package/dist/elysia/index.d.mts.map +1 -1
  15. package/dist/elysia/index.mjs +7 -7
  16. package/dist/elysia/index.mjs.map +1 -1
  17. package/dist/enrichers.d.mts +1 -1
  18. package/dist/{error-BheHTFFB.d.mts → error-BjaGNgoo.d.mts} +2 -2
  19. package/dist/{error-BheHTFFB.d.mts.map → error-BjaGNgoo.d.mts.map} +1 -1
  20. package/dist/error.d.mts +1 -1
  21. package/dist/{errors-D8WVZclz.d.mts → errors-BBJmxg3d.d.mts} +2 -2
  22. package/dist/{errors-D8WVZclz.d.mts.map → errors-BBJmxg3d.d.mts.map} +1 -1
  23. package/dist/{errors-BQgyQ9xe.mjs → errors-BJRXUfMg.mjs} +1 -1
  24. package/dist/{errors-BQgyQ9xe.mjs.map → errors-BJRXUfMg.mjs.map} +1 -1
  25. package/dist/express/index.d.mts +2 -2
  26. package/dist/express/index.mjs +1 -1
  27. package/dist/fastify/index.d.mts +2 -2
  28. package/dist/fastify/index.mjs +1 -1
  29. package/dist/{headers-DJ_YZbxT.mjs → headers-DmzJ3sQ-.mjs} +2 -2
  30. package/dist/{headers-DJ_YZbxT.mjs.map → headers-DmzJ3sQ-.mjs.map} +1 -1
  31. package/dist/hono/index.d.mts +2 -2
  32. package/dist/hono/index.mjs +1 -1
  33. package/dist/index.d.mts +5 -5
  34. package/dist/{logger-BkXYNnHP.d.mts → logger-3ZE3g6GW.d.mts} +2 -2
  35. package/dist/{logger-BkXYNnHP.d.mts.map → logger-3ZE3g6GW.d.mts.map} +1 -1
  36. package/dist/logger.d.mts +1 -1
  37. package/dist/{middleware-B-4hPOVG.d.mts → middleware-B9uwQ5B4.d.mts} +2 -2
  38. package/dist/{middleware-B-4hPOVG.d.mts.map → middleware-B9uwQ5B4.d.mts.map} +1 -1
  39. package/dist/nestjs/index.d.mts +2 -2
  40. package/dist/nestjs/index.mjs +1 -1
  41. package/dist/next/client.d.mts +1 -1
  42. package/dist/next/index.d.mts +4 -4
  43. package/dist/nitro/errorHandler.mjs +2 -2
  44. package/dist/nitro/module.d.mts +2 -2
  45. package/dist/nitro/plugin.mjs +4 -3
  46. package/dist/nitro/plugin.mjs.map +1 -1
  47. package/dist/nitro/v3/errorHandler.mjs +2 -2
  48. package/dist/nitro/v3/middleware.d.mts +3 -1
  49. package/dist/nitro/v3/middleware.d.mts.map +1 -1
  50. package/dist/nitro/v3/middleware.mjs +2 -1
  51. package/dist/nitro/v3/middleware.mjs.map +1 -1
  52. package/dist/nitro/v3/module.d.mts +1 -1
  53. package/dist/nitro/v3/plugin.mjs +6 -5
  54. package/dist/nitro/v3/plugin.mjs.map +1 -1
  55. package/dist/nitro/v3/useLogger.d.mts +1 -1
  56. package/dist/{nitro-DCNNxY_7.d.mts → nitro-BbTINVdZ.d.mts} +2 -2
  57. package/dist/{nitro-DCNNxY_7.d.mts.map → nitro-BbTINVdZ.d.mts.map} +1 -1
  58. package/dist/{nitro-CzyGROOC.mjs → nitro-OmT_M4Pb.mjs} +2 -2
  59. package/dist/{nitro-CzyGROOC.mjs.map → nitro-OmT_M4Pb.mjs.map} +1 -1
  60. package/dist/nuxt/module.d.mts +1 -1
  61. package/dist/nuxt/module.mjs +2 -2
  62. package/dist/{parseError-B08FS7EQ.d.mts → parseError-DO1qtmGL.d.mts} +2 -2
  63. package/dist/parseError-DO1qtmGL.d.mts.map +1 -0
  64. package/dist/react-router/index.d.mts +47 -0
  65. package/dist/react-router/index.d.mts.map +1 -0
  66. package/dist/react-router/index.mjs +59 -0
  67. package/dist/react-router/index.mjs.map +1 -0
  68. package/dist/runtime/client/log.d.mts +1 -1
  69. package/dist/runtime/server/useLogger.d.mts +1 -1
  70. package/dist/runtime/utils/parseError.d.mts +2 -2
  71. package/dist/runtime/utils/parseError.mjs +2 -1
  72. package/dist/runtime/utils/parseError.mjs.map +1 -1
  73. package/dist/{source-location-B1VVgXkh.mjs → source-location-DRvDDqfq.mjs} +1 -1
  74. package/dist/{source-location-B1VVgXkh.mjs.map → source-location-DRvDDqfq.mjs.map} +1 -1
  75. package/dist/sveltekit/index.d.mts +2 -2
  76. package/dist/sveltekit/index.mjs +3 -3
  77. package/dist/toolkit.d.mts +3 -3
  78. package/dist/toolkit.mjs +2 -2
  79. package/dist/{types-CBpJBj_7.d.mts → types-BpsDbwHU.d.mts} +3 -1
  80. package/dist/{types-CBpJBj_7.d.mts.map → types-BpsDbwHU.d.mts.map} +1 -1
  81. package/dist/types.d.mts +1 -1
  82. package/dist/{useLogger-DBPGEDf_.d.mts → useLogger-C_8vgz0g.d.mts} +2 -2
  83. package/dist/{useLogger-DBPGEDf_.d.mts.map → useLogger-C_8vgz0g.d.mts.map} +1 -1
  84. package/dist/utils.d.mts +1 -1
  85. package/dist/vite/index.d.mts +1 -1
  86. package/dist/vite/index.mjs +1 -1
  87. package/dist/workers.d.mts +1 -1
  88. package/package.json +22 -8
  89. package/dist/parseError-B08FS7EQ.d.mts.map +0 -1
package/README.md CHANGED
@@ -464,6 +464,33 @@ Use `useLogger()` to access the logger from anywhere in the call stack.
464
464
 
465
465
  See the full [elysia example](https://github.com/HugoRCD/evlog/tree/main/examples/elysia) for a complete working project.
466
466
 
467
+ ## React Router
468
+
469
+ ```typescript
470
+ // app/root.tsx
471
+ import { initLogger } from 'evlog'
472
+ import { evlog, loggerContext } from 'evlog/react-router'
473
+
474
+ initLogger({ env: { service: 'react-router-api' } })
475
+
476
+ export const middleware: Route.MiddlewareFunction[] = [
477
+ evlog(),
478
+ ]
479
+
480
+ // app/routes/api.users.$id.tsx
481
+ import { loggerContext } from 'evlog/react-router'
482
+
483
+ export async function loader({ params, context }: Route.LoaderArgs) {
484
+ const log = context.get(loggerContext)
485
+ log.set({ users: { count: 42 } })
486
+ return { users: [] }
487
+ }
488
+ ```
489
+
490
+ Use `context.get(loggerContext)` in loaders/actions, or `useLogger()` from anywhere in the call stack. Requires `v8_middleware: true` in `react-router.config.ts`.
491
+
492
+ See the full [react-router example](https://github.com/HugoRCD/evlog/tree/main/examples/react-router) for a complete working project.
493
+
467
494
  ## NestJS
468
495
 
469
496
  ```typescript
@@ -1095,6 +1122,7 @@ try {
1095
1122
  | **Nitro v3** | `modules: [evlog()]` with `import evlog from 'evlog/nitro/v3'` |
1096
1123
  | **Nitro v2** | `modules: [evlog()]` with `import evlog from 'evlog/nitro'` |
1097
1124
  | **TanStack Start** | Nitro v3 module setup ([example](./examples/tanstack-start)) |
1125
+ | **React Router** | `evlog()` middleware with `import { evlog } from 'evlog/react-router'` ([example](./examples/react-router)) |
1098
1126
  | **NestJS** | `EvlogModule.forRoot()` with `import { EvlogModule } from 'evlog/nestjs'` ([example](./examples/nestjs)) |
1099
1127
  | **Express** | `app.use(evlog())` with `import { evlog } from 'evlog/express'` ([example](./examples/express)) |
1100
1128
  | **Hono** | `app.use(evlog())` with `import { evlog } from 'evlog/hono'` ([example](./examples/hono)) |
@@ -1,4 +1,4 @@
1
- import { T as WideEvent, r as DrainContext } from "../types-CBpJBj_7.mjs";
1
+ import { T as WideEvent, r as DrainContext } from "../types-BpsDbwHU.mjs";
2
2
  //#region src/adapters/axiom.d.ts
3
3
  interface BaseAxiomConfig {
4
4
  /** Axiom dataset name */
@@ -1,4 +1,4 @@
1
- import { T as WideEvent, r as DrainContext } from "../types-CBpJBj_7.mjs";
1
+ import { T as WideEvent, r as DrainContext } from "../types-BpsDbwHU.mjs";
2
2
  //#region src/adapters/better-stack.d.ts
3
3
  interface BetterStackConfig {
4
4
  /** Better Stack source token */
@@ -1,4 +1,4 @@
1
- import { T as WideEvent, r as DrainContext } from "../types-CBpJBj_7.mjs";
1
+ import { T as WideEvent, r as DrainContext } from "../types-BpsDbwHU.mjs";
2
2
  //#region src/adapters/fs.d.ts
3
3
  interface FsConfig {
4
4
  /** Directory for log files */
@@ -1,4 +1,4 @@
1
- import { T as WideEvent, r as DrainContext } from "../types-CBpJBj_7.mjs";
1
+ import { T as WideEvent, r as DrainContext } from "../types-BpsDbwHU.mjs";
2
2
  //#region src/adapters/otlp.d.ts
3
3
  interface OTLPConfig {
4
4
  /** OTLP HTTP endpoint (e.g., http://localhost:4318) */
@@ -1,4 +1,4 @@
1
- import { T as WideEvent, r as DrainContext } from "../types-CBpJBj_7.mjs";
1
+ import { T as WideEvent, r as DrainContext } from "../types-BpsDbwHU.mjs";
2
2
  //#region src/adapters/posthog.d.ts
3
3
  interface PostHogConfig {
4
4
  /** PostHog project API key */
@@ -1,4 +1,4 @@
1
- import { T as WideEvent, r as DrainContext } from "../types-CBpJBj_7.mjs";
1
+ import { T as WideEvent, r as DrainContext } from "../types-BpsDbwHU.mjs";
2
2
  //#region src/adapters/sentry.d.ts
3
3
  interface SentryConfig {
4
4
  /** Sentry DSN */
@@ -1,8 +1,47 @@
1
- import { g as RequestLogger } from "../types-CBpJBj_7.mjs";
1
+ import { g as RequestLogger } from "../types-BpsDbwHU.mjs";
2
2
  import { GatewayModelId } from "ai";
3
- import { LanguageModelV3 } from "@ai-sdk/provider";
3
+ import { LanguageModelV3, LanguageModelV3Middleware } from "@ai-sdk/provider";
4
4
 
5
5
  //#region src/ai/index.d.ts
6
+ /**
7
+ * Fine-grained control over tool call input capture.
8
+ */
9
+ interface ToolInputsOptions {
10
+ /**
11
+ * Max character length for the stringified input JSON.
12
+ * Inputs exceeding this limit are truncated with a `…` suffix.
13
+ */
14
+ maxLength?: number;
15
+ /**
16
+ * Custom transform applied to each captured input before storing.
17
+ * Receives the parsed input and tool name; return value is stored.
18
+ * Runs before `maxLength` truncation.
19
+ */
20
+ transform?: (input: unknown, toolName: string) => unknown;
21
+ }
22
+ /**
23
+ * Options for `createAILogger` and `createAIMiddleware`.
24
+ */
25
+ interface AILoggerOptions {
26
+ /**
27
+ * When enabled, `toolCalls` contains `{ name, input }` objects instead of plain tool name strings.
28
+ * Opt-in because inputs can be large and may contain sensitive data.
29
+ *
30
+ * - `true` — capture all inputs as-is
31
+ * - `{ maxLength, transform }` — capture with truncation or custom transform
32
+ * @default false
33
+ */
34
+ toolInputs?: boolean | ToolInputsOptions;
35
+ }
36
+ /**
37
+ * Per-step token usage breakdown for multi-step agent runs.
38
+ */
39
+ interface AIStepUsage {
40
+ model: string;
41
+ inputTokens: number;
42
+ outputTokens: number;
43
+ toolCalls?: string[];
44
+ }
6
45
  /**
7
46
  * Shape of the `ai` field written to the wide event.
8
47
  */
@@ -18,8 +57,13 @@ interface AIEventData {
18
57
  cacheWriteTokens?: number;
19
58
  reasoningTokens?: number;
20
59
  finishReason?: string;
21
- toolCalls?: string[];
60
+ toolCalls?: string[] | Array<{
61
+ name: string;
62
+ input: unknown;
63
+ }>;
64
+ responseId?: string;
22
65
  steps?: number;
66
+ stepsUsage?: AIStepUsage[];
23
67
  msToFirstChunk?: number;
24
68
  msToFinish?: number;
25
69
  tokensPerSecond?: number;
@@ -34,6 +78,9 @@ interface AILogger {
34
78
  * Accepts a `LanguageModelV3` object or a model string (e.g. `'anthropic/claude-sonnet-4.6'`).
35
79
  * Strings are resolved via the AI SDK gateway.
36
80
  *
81
+ * Also works with pre-wrapped models (e.g. from supermemory, guardrails):
82
+ * `ai.wrap(withSupermemory(base, orgId))` composes correctly.
83
+ *
37
84
  * @example
38
85
  * ```ts
39
86
  * const ai = createAILogger(log)
@@ -61,6 +108,31 @@ interface AILogger {
61
108
  };
62
109
  }) => void;
63
110
  }
111
+ /**
112
+ * Create the evlog AI middleware that captures AI SDK data into a wide event.
113
+ *
114
+ * Use this when you need explicit middleware composition with other wrappers
115
+ * (e.g. supermemory, guardrails). For most cases, use `createAILogger` instead.
116
+ *
117
+ * Note: `captureEmbed` is not available with the raw middleware — use
118
+ * `createAILogger` if you need embedding capture.
119
+ *
120
+ * @example Nuxt API route with supermemory
121
+ * ```ts
122
+ * import { createAIMiddleware } from 'evlog/ai'
123
+ * import { wrapLanguageModel } from 'ai'
124
+ *
125
+ * export default defineEventHandler(async (event) => {
126
+ * const log = useLogger(event)
127
+ *
128
+ * const model = wrapLanguageModel({
129
+ * model: withSupermemory(base, orgId),
130
+ * middleware: [createAIMiddleware(log, { toolInputs: true })],
131
+ * })
132
+ * })
133
+ * ```
134
+ */
135
+ declare function createAIMiddleware(log: RequestLogger, options?: AILoggerOptions): LanguageModelV3Middleware;
64
136
  /**
65
137
  * Create an AI logger that captures AI SDK data into the wide event.
66
138
  *
@@ -81,8 +153,13 @@ interface AILogger {
81
153
  * onFinish: ({ text }) => saveConversation(text),
82
154
  * })
83
155
  * ```
156
+ *
157
+ * @example Capture tool call inputs
158
+ * ```ts
159
+ * const ai = createAILogger(log, { toolInputs: true })
160
+ * ```
84
161
  */
85
- declare function createAILogger(log: RequestLogger): AILogger;
162
+ declare function createAILogger(log: RequestLogger, options?: AILoggerOptions): AILogger;
86
163
  //#endregion
87
- export { AIEventData, AILogger, createAILogger };
164
+ export { AIEventData, AILogger, AILoggerOptions, AIStepUsage, ToolInputsOptions, createAILogger, createAIMiddleware };
88
165
  //# sourceMappingURL=index.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/ai/index.ts"],"mappings":";;;;;;;AAQA;UAAiB,WAAA;EACf,KAAA;EACA,KAAA;EACA,MAAA;EACA,QAAA;EACA,WAAA;EACA,YAAA;EACA,WAAA;EACA,eAAA;EACA,gBAAA;EACA,eAAA;EACA,YAAA;EACA,SAAA;EACA,KAAA;EACA,cAAA;EACA,UAAA;EACA,eAAA;EACA,KAAA;AAAA;AAAA,UAGe,QAAA;EAHV;;AAGP;;;;;;;;;;;;;;;EAkBE,IAAA,GAAO,KAAA,EAAO,eAAA,GAAkB,cAAA,KAAmB,eAAA;EAapC;;;AA8DjB;;;;;;;;EA9DE,YAAA,GAAe,MAAA;IAAU,KAAA;MAAS,MAAA;IAAA;EAAA;AAAA;;;;;;;;;;;;;;;;;;;;;;iBA8DpB,cAAA,CAAe,GAAA,EAAK,aAAA,GAAgB,QAAA"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/ai/index.ts"],"mappings":";;;;;;;AAQA;UAAiB,iBAAA;;;;;EAKf,SAAA;EAM6B;;;AAM/B;;EANE,SAAA,IAAa,KAAA,WAAgB,QAAA;AAAA;;AAqB/B;;UAfiB,eAAA;EAeW;;;;;;;AAU5B;EAhBE,UAAA,aAAuB,iBAAA;AAAA;;;;UAMR,WAAA;EACf,KAAA;EACA,WAAA;EACA,YAAA;EACA,SAAA;AAAA;;;;UAMe,WAAA;EACf,KAAA;EACA,KAAA;EACA,MAAA;EACA,QAAA;EACA,WAAA;EACA,YAAA;EACA,WAAA;EACA,eAAA;EACA,gBAAA;EACA,eAAA;EACA,YAAA;EACA,SAAA,cAAuB,KAAA;IAAQ,IAAA;IAAc,KAAA;EAAA;EAC7C,UAAA;EACA,KAAA;EACA,UAAA,GAAa,WAAA;EACb,cAAA;EACA,UAAA;EACA,eAAA;EACA,KAAA;AAAA;AAAA,UAGe,QAAA;EAqBR;;;;;;;;AA8ET;;;;;;;;;;;;EA9EE,IAAA,GAAO,KAAA,EAAO,eAAA,GAAkB,cAAA,KAAmB,eAAA;EA8EuD;;AA8B5G;;;;;;;;;EA/FE,YAAA,GAAe,MAAA;IAAU,KAAA;MAAS,MAAA;IAAA;EAAA;AAAA;;;;;;;;;;;;;;;;;;;;;;;;;iBAiEpB,kBAAA,CAAmB,GAAA,EAAK,aAAA,EAAe,OAAA,GAAU,eAAA,GAAkB,yBAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA8BnE,cAAA,CAAe,GAAA,EAAK,aAAA,EAAe,OAAA,GAAU,eAAA,GAAkB,QAAA"}
package/dist/ai/index.mjs CHANGED
@@ -24,6 +24,33 @@ function resolveProviderAndModel(provider, modelId) {
24
24
  };
25
25
  }
26
26
  /**
27
+ * Create the evlog AI middleware that captures AI SDK data into a wide event.
28
+ *
29
+ * Use this when you need explicit middleware composition with other wrappers
30
+ * (e.g. supermemory, guardrails). For most cases, use `createAILogger` instead.
31
+ *
32
+ * Note: `captureEmbed` is not available with the raw middleware — use
33
+ * `createAILogger` if you need embedding capture.
34
+ *
35
+ * @example Nuxt API route with supermemory
36
+ * ```ts
37
+ * import { createAIMiddleware } from 'evlog/ai'
38
+ * import { wrapLanguageModel } from 'ai'
39
+ *
40
+ * export default defineEventHandler(async (event) => {
41
+ * const log = useLogger(event)
42
+ *
43
+ * const model = wrapLanguageModel({
44
+ * model: withSupermemory(base, orgId),
45
+ * middleware: [createAIMiddleware(log, { toolInputs: true })],
46
+ * })
47
+ * })
48
+ * ```
49
+ */
50
+ function createAIMiddleware(log, options) {
51
+ return buildMiddleware(log, options);
52
+ }
53
+ /**
27
54
  * Create an AI logger that captures AI SDK data into the wide event.
28
55
  *
29
56
  * Uses model middleware (`wrapLanguageModel`) to transparently intercept
@@ -43,75 +70,173 @@ function resolveProviderAndModel(provider, modelId) {
43
70
  * onFinish: ({ text }) => saveConversation(text),
44
71
  * })
45
72
  * ```
73
+ *
74
+ * @example Capture tool call inputs
75
+ * ```ts
76
+ * const ai = createAILogger(log, { toolInputs: true })
77
+ * ```
46
78
  */
47
- function createAILogger(log) {
48
- let calls = 0;
49
- let steps = 0;
50
- const usage = {
51
- inputTokens: 0,
52
- outputTokens: 0,
53
- cacheReadTokens: 0,
54
- cacheWriteTokens: 0,
55
- reasoningTokens: 0
56
- };
57
- const models = [];
58
- const providers = [];
59
- const allToolCalls = [];
60
- let lastFinishReason;
61
- let lastMsToFirstChunk;
62
- let lastMsToFinish;
63
- let lastError;
64
- function flush() {
65
- const uniqueModels = [...new Set(models)];
66
- const lastModel = models[models.length - 1];
67
- const lastProvider = providers[providers.length - 1];
68
- const data = {
69
- calls,
70
- inputTokens: usage.inputTokens,
71
- outputTokens: usage.outputTokens,
72
- totalTokens: usage.inputTokens + usage.outputTokens
73
- };
74
- if (lastModel) data.model = lastModel;
75
- if (lastProvider) data.provider = lastProvider;
76
- if (uniqueModels.length > 1) data.models = uniqueModels;
77
- if (usage.cacheReadTokens > 0) data.cacheReadTokens = usage.cacheReadTokens;
78
- if (usage.cacheWriteTokens > 0) data.cacheWriteTokens = usage.cacheWriteTokens;
79
- if (usage.reasoningTokens > 0) data.reasoningTokens = usage.reasoningTokens;
80
- if (lastFinishReason) data.finishReason = lastFinishReason;
81
- if (allToolCalls.length > 0) data.toolCalls = [...allToolCalls];
82
- if (steps > 1) data.steps = steps;
83
- if (lastMsToFirstChunk !== void 0) data.msToFirstChunk = lastMsToFirstChunk;
84
- if (lastMsToFinish !== void 0) {
85
- data.msToFinish = lastMsToFinish;
86
- if (usage.outputTokens > 0 && lastMsToFinish > 0) data.tokensPerSecond = Math.round(usage.outputTokens / lastMsToFinish * 1e3);
79
+ function createAILogger(log, options) {
80
+ const state = createAccumulatorState(options);
81
+ const middleware = buildMiddlewareFromState(log, state);
82
+ return {
83
+ wrap: (model) => {
84
+ return wrapLanguageModel({
85
+ model: typeof model === "string" ? gateway(model) : model,
86
+ middleware
87
+ });
88
+ },
89
+ captureEmbed: (result) => {
90
+ state.calls++;
91
+ state.usage.inputTokens += result.usage.tokens;
92
+ flushState(log, state);
87
93
  }
88
- if (lastError) data.error = lastError;
89
- log.set({ ai: data });
94
+ };
95
+ }
96
+ function resolveToolInputs(raw) {
97
+ if (!raw) return {
98
+ enabled: false,
99
+ options: void 0
100
+ };
101
+ if (raw === true) return {
102
+ enabled: true,
103
+ options: void 0
104
+ };
105
+ return {
106
+ enabled: true,
107
+ options: raw
108
+ };
109
+ }
110
+ function processToolInput(input, toolName, options) {
111
+ let value = input;
112
+ if (options?.transform) value = options.transform(value, toolName);
113
+ if (options?.maxLength) {
114
+ const str = typeof value === "string" ? value : JSON.stringify(value);
115
+ if (str.length > options.maxLength) return `${str.slice(0, options.maxLength)}…`;
90
116
  }
91
- function recordModel(provider, modelId, responseModelId) {
92
- const resolved = resolveProviderAndModel(provider, responseModelId ?? modelId);
93
- models.push(resolved.model);
94
- providers.push(resolved.provider);
117
+ return value;
118
+ }
119
+ function createAccumulatorState(options) {
120
+ const { enabled, options: captureOpts } = resolveToolInputs(options?.toolInputs);
121
+ return {
122
+ calls: 0,
123
+ steps: 0,
124
+ usage: {
125
+ inputTokens: 0,
126
+ outputTokens: 0,
127
+ cacheReadTokens: 0,
128
+ cacheWriteTokens: 0,
129
+ reasoningTokens: 0
130
+ },
131
+ models: [],
132
+ lastProvider: void 0,
133
+ allToolCalls: [],
134
+ allToolCallInputs: [],
135
+ stepsUsage: [],
136
+ lastFinishReason: void 0,
137
+ lastMsToFirstChunk: void 0,
138
+ lastMsToFinish: void 0,
139
+ lastError: void 0,
140
+ lastResponseId: void 0,
141
+ toolInputs: enabled,
142
+ toolInputsOptions: captureOpts
143
+ };
144
+ }
145
+ function flushState(log, state) {
146
+ const uniqueModels = [...new Set(state.models)];
147
+ const lastModel = state.models[state.models.length - 1];
148
+ const data = {
149
+ calls: state.calls,
150
+ inputTokens: state.usage.inputTokens,
151
+ outputTokens: state.usage.outputTokens,
152
+ totalTokens: state.usage.inputTokens + state.usage.outputTokens
153
+ };
154
+ if (lastModel) data.model = lastModel;
155
+ if (state.lastProvider) data.provider = state.lastProvider;
156
+ if (uniqueModels.length > 1) data.models = uniqueModels;
157
+ if (state.usage.cacheReadTokens > 0) data.cacheReadTokens = state.usage.cacheReadTokens;
158
+ if (state.usage.cacheWriteTokens > 0) data.cacheWriteTokens = state.usage.cacheWriteTokens;
159
+ if (state.usage.reasoningTokens > 0) data.reasoningTokens = state.usage.reasoningTokens;
160
+ if (state.lastFinishReason) data.finishReason = state.lastFinishReason;
161
+ if (state.toolInputs && state.allToolCallInputs.length > 0) data.toolCalls = [...state.allToolCallInputs];
162
+ else if (state.allToolCalls.length > 0) data.toolCalls = [...state.allToolCalls];
163
+ if (state.lastResponseId) data.responseId = state.lastResponseId;
164
+ if (state.steps > 1) {
165
+ data.steps = state.steps;
166
+ data.stepsUsage = [...state.stepsUsage];
167
+ }
168
+ if (state.lastMsToFirstChunk !== void 0) data.msToFirstChunk = state.lastMsToFirstChunk;
169
+ if (state.lastMsToFinish !== void 0) {
170
+ data.msToFinish = state.lastMsToFinish;
171
+ if (state.usage.outputTokens > 0 && state.lastMsToFinish > 0) data.tokensPerSecond = Math.round(state.usage.outputTokens / state.lastMsToFinish * 1e3);
172
+ }
173
+ if (state.lastError) data.error = state.lastError;
174
+ log.set({ ai: data });
175
+ }
176
+ function recordModel(state, provider, modelId, responseModelId) {
177
+ const resolved = resolveProviderAndModel(provider, responseModelId ?? modelId);
178
+ state.models.push(resolved.model);
179
+ state.lastProvider = resolved.provider;
180
+ }
181
+ function safeParseJSON(input) {
182
+ try {
183
+ return JSON.parse(input);
184
+ } catch {
185
+ return input;
95
186
  }
96
- const middleware = {
187
+ }
188
+ function recordError(log, state, model, error) {
189
+ state.calls++;
190
+ state.steps++;
191
+ recordModel(state, model.provider, model.modelId);
192
+ state.lastFinishReason = "error";
193
+ state.lastError = error instanceof Error ? error.message : String(error);
194
+ const resolved = resolveProviderAndModel(model.provider, model.modelId);
195
+ state.stepsUsage.push({
196
+ model: resolved.model,
197
+ inputTokens: 0,
198
+ outputTokens: 0
199
+ });
200
+ flushState(log, state);
201
+ }
202
+ function buildMiddleware(log, options) {
203
+ return buildMiddlewareFromState(log, createAccumulatorState(options));
204
+ }
205
+ function buildMiddlewareFromState(log, state) {
206
+ return {
207
+ specificationVersion: "v3",
97
208
  wrapGenerate: async ({ doGenerate, model }) => {
98
209
  try {
99
210
  const result = await doGenerate();
100
- calls++;
101
- steps++;
102
- addUsage(usage, result.usage);
103
- recordModel(model.provider, model.modelId, result.response?.modelId);
104
- lastFinishReason = result.finishReason.unified;
105
- for (const item of result.content) if (item.type === "tool-call") allToolCalls.push(item.toolName);
106
- flush();
211
+ state.calls++;
212
+ state.steps++;
213
+ addUsage(state.usage, result.usage);
214
+ recordModel(state, model.provider, model.modelId, result.response?.modelId);
215
+ state.lastFinishReason = result.finishReason.unified;
216
+ if (result.response?.id) state.lastResponseId = result.response.id;
217
+ const stepToolCalls = [];
218
+ for (const item of result.content) if (item.type === "tool-call") {
219
+ state.allToolCalls.push(item.toolName);
220
+ stepToolCalls.push(item.toolName);
221
+ if (state.toolInputs) {
222
+ const raw = typeof item.input === "string" ? safeParseJSON(item.input) : item.input;
223
+ state.allToolCallInputs.push({
224
+ name: item.toolName,
225
+ input: processToolInput(raw, item.toolName, state.toolInputsOptions)
226
+ });
227
+ }
228
+ }
229
+ const resolvedModel = resolveProviderAndModel(model.provider, result.response?.modelId ?? model.modelId);
230
+ state.stepsUsage.push({
231
+ model: resolvedModel.model,
232
+ inputTokens: result.usage.inputTokens.total ?? 0,
233
+ outputTokens: result.usage.outputTokens.total ?? 0,
234
+ ...stepToolCalls.length > 0 ? { toolCalls: stepToolCalls } : {}
235
+ });
236
+ flushState(log, state);
107
237
  return result;
108
238
  } catch (error) {
109
- calls++;
110
- steps++;
111
- recordModel(model.provider, model.modelId);
112
- lastFinishReason = "error";
113
- lastError = error instanceof Error ? error.message : String(error);
114
- flush();
239
+ recordError(log, state, model, error);
115
240
  throw error;
116
241
  }
117
242
  },
@@ -121,25 +246,43 @@ function createAILogger(log) {
121
246
  let streamUsage;
122
247
  let streamFinishReason;
123
248
  let streamModelId;
249
+ let streamResponseId;
124
250
  const streamToolCalls = [];
251
+ const streamToolInputBuffers = /* @__PURE__ */ new Map();
125
252
  let streamError;
126
253
  let doStreamResult;
127
254
  try {
128
255
  doStreamResult = await doStream();
129
256
  } catch (error) {
130
- calls++;
131
- steps++;
132
- recordModel(model.provider, model.modelId);
133
- lastFinishReason = "error";
134
- lastError = error instanceof Error ? error.message : String(error);
135
- flush();
257
+ recordError(log, state, model, error);
136
258
  throw error;
137
259
  }
138
260
  const { stream, ...rest } = doStreamResult;
139
261
  const transformStream = new TransformStream({
140
262
  transform(chunk, controller) {
141
263
  if (!firstChunkTime && chunk.type === "text-delta") firstChunkTime = Date.now();
142
- if (chunk.type === "tool-input-start") streamToolCalls.push(chunk.toolName);
264
+ if (chunk.type === "tool-input-start") {
265
+ streamToolCalls.push(chunk.toolName);
266
+ if (state.toolInputs) streamToolInputBuffers.set(chunk.id, {
267
+ name: chunk.toolName,
268
+ chunks: []
269
+ });
270
+ }
271
+ if (chunk.type === "tool-input-delta" && state.toolInputs) {
272
+ const buffer = streamToolInputBuffers.get(chunk.id);
273
+ if (buffer) buffer.chunks.push(chunk.delta);
274
+ }
275
+ if (chunk.type === "tool-input-end" && state.toolInputs) {
276
+ const buffer = streamToolInputBuffers.get(chunk.id);
277
+ if (buffer) {
278
+ const raw = safeParseJSON(buffer.chunks.join(""));
279
+ state.allToolCallInputs.push({
280
+ name: buffer.name,
281
+ input: processToolInput(raw, buffer.name, state.toolInputsOptions)
282
+ });
283
+ streamToolInputBuffers.delete(chunk.id);
284
+ }
285
+ }
143
286
  if (chunk.type === "finish") {
144
287
  streamUsage = {
145
288
  inputTokens: chunk.usage.inputTokens.total ?? 0,
@@ -150,27 +293,38 @@ function createAILogger(log) {
150
293
  };
151
294
  streamFinishReason = chunk.finishReason.unified;
152
295
  }
153
- if (chunk.type === "response-metadata" && "modelId" in chunk && chunk.modelId) streamModelId = chunk.modelId;
296
+ if (chunk.type === "response-metadata") {
297
+ if (chunk.modelId) streamModelId = chunk.modelId;
298
+ if (chunk.id) streamResponseId = chunk.id;
299
+ }
154
300
  if (chunk.type === "error") streamError = chunk.error instanceof Error ? chunk.error.message : String(chunk.error);
155
301
  controller.enqueue(chunk);
156
302
  },
157
303
  flush() {
158
- calls++;
159
- steps++;
304
+ state.calls++;
305
+ state.steps++;
160
306
  if (streamUsage) {
161
- usage.inputTokens += streamUsage.inputTokens;
162
- usage.outputTokens += streamUsage.outputTokens;
163
- usage.cacheReadTokens += streamUsage.cacheReadTokens;
164
- usage.cacheWriteTokens += streamUsage.cacheWriteTokens;
165
- usage.reasoningTokens += streamUsage.reasoningTokens;
307
+ state.usage.inputTokens += streamUsage.inputTokens;
308
+ state.usage.outputTokens += streamUsage.outputTokens;
309
+ state.usage.cacheReadTokens += streamUsage.cacheReadTokens;
310
+ state.usage.cacheWriteTokens += streamUsage.cacheWriteTokens;
311
+ state.usage.reasoningTokens += streamUsage.reasoningTokens;
166
312
  }
167
- recordModel(model.provider, model.modelId, streamModelId);
168
- lastFinishReason = streamFinishReason;
169
- for (const name of streamToolCalls) allToolCalls.push(name);
170
- if (firstChunkTime) lastMsToFirstChunk = firstChunkTime - streamStart;
171
- lastMsToFinish = Date.now() - streamStart;
172
- if (streamError) lastError = streamError;
173
- flush();
313
+ recordModel(state, model.provider, model.modelId, streamModelId);
314
+ state.lastFinishReason = streamFinishReason;
315
+ state.allToolCalls.push(...streamToolCalls);
316
+ if (streamResponseId) state.lastResponseId = streamResponseId;
317
+ if (firstChunkTime) state.lastMsToFirstChunk = firstChunkTime - streamStart;
318
+ state.lastMsToFinish = Date.now() - streamStart;
319
+ if (streamError) state.lastError = streamError;
320
+ const resolvedModel = resolveProviderAndModel(model.provider, streamModelId ?? model.modelId);
321
+ state.stepsUsage.push({
322
+ model: resolvedModel.model,
323
+ inputTokens: streamUsage?.inputTokens ?? 0,
324
+ outputTokens: streamUsage?.outputTokens ?? 0,
325
+ ...streamToolCalls.length > 0 ? { toolCalls: [...streamToolCalls] } : {}
326
+ });
327
+ flushState(log, state);
174
328
  }
175
329
  });
176
330
  return {
@@ -179,21 +333,8 @@ function createAILogger(log) {
179
333
  };
180
334
  }
181
335
  };
182
- return {
183
- wrap: (model) => {
184
- return wrapLanguageModel({
185
- model: typeof model === "string" ? gateway(model) : model,
186
- middleware
187
- });
188
- },
189
- captureEmbed: (result) => {
190
- calls++;
191
- usage.inputTokens += result.usage.tokens;
192
- flush();
193
- }
194
- };
195
336
  }
196
337
  //#endregion
197
- export { createAILogger };
338
+ export { createAILogger, createAIMiddleware };
198
339
 
199
340
  //# sourceMappingURL=index.mjs.map