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.
- package/README.md +28 -0
- package/dist/adapters/axiom.d.mts +1 -1
- package/dist/adapters/better-stack.d.mts +1 -1
- package/dist/adapters/fs.d.mts +1 -1
- package/dist/adapters/otlp.d.mts +1 -1
- package/dist/adapters/posthog.d.mts +1 -1
- package/dist/adapters/sentry.d.mts +1 -1
- package/dist/ai/index.d.mts +82 -5
- package/dist/ai/index.d.mts.map +1 -1
- package/dist/ai/index.mjs +237 -96
- package/dist/ai/index.mjs.map +1 -1
- package/dist/browser.d.mts +1 -1
- package/dist/elysia/index.d.mts +2 -2
- package/dist/elysia/index.d.mts.map +1 -1
- package/dist/elysia/index.mjs +7 -7
- package/dist/elysia/index.mjs.map +1 -1
- package/dist/enrichers.d.mts +1 -1
- package/dist/{error-BheHTFFB.d.mts → error-BjaGNgoo.d.mts} +2 -2
- package/dist/{error-BheHTFFB.d.mts.map → error-BjaGNgoo.d.mts.map} +1 -1
- package/dist/error.d.mts +1 -1
- package/dist/{errors-D8WVZclz.d.mts → errors-BBJmxg3d.d.mts} +2 -2
- package/dist/{errors-D8WVZclz.d.mts.map → errors-BBJmxg3d.d.mts.map} +1 -1
- package/dist/{errors-BQgyQ9xe.mjs → errors-BJRXUfMg.mjs} +1 -1
- package/dist/{errors-BQgyQ9xe.mjs.map → errors-BJRXUfMg.mjs.map} +1 -1
- package/dist/express/index.d.mts +2 -2
- package/dist/express/index.mjs +1 -1
- package/dist/fastify/index.d.mts +2 -2
- package/dist/fastify/index.mjs +1 -1
- package/dist/{headers-DJ_YZbxT.mjs → headers-DmzJ3sQ-.mjs} +2 -2
- package/dist/{headers-DJ_YZbxT.mjs.map → headers-DmzJ3sQ-.mjs.map} +1 -1
- package/dist/hono/index.d.mts +2 -2
- package/dist/hono/index.mjs +1 -1
- package/dist/index.d.mts +5 -5
- package/dist/{logger-BkXYNnHP.d.mts → logger-3ZE3g6GW.d.mts} +2 -2
- package/dist/{logger-BkXYNnHP.d.mts.map → logger-3ZE3g6GW.d.mts.map} +1 -1
- package/dist/logger.d.mts +1 -1
- package/dist/{middleware-B-4hPOVG.d.mts → middleware-B9uwQ5B4.d.mts} +2 -2
- package/dist/{middleware-B-4hPOVG.d.mts.map → middleware-B9uwQ5B4.d.mts.map} +1 -1
- package/dist/nestjs/index.d.mts +2 -2
- package/dist/nestjs/index.mjs +1 -1
- package/dist/next/client.d.mts +1 -1
- package/dist/next/index.d.mts +4 -4
- package/dist/nitro/errorHandler.mjs +2 -2
- package/dist/nitro/module.d.mts +2 -2
- package/dist/nitro/plugin.mjs +4 -3
- package/dist/nitro/plugin.mjs.map +1 -1
- package/dist/nitro/v3/errorHandler.mjs +2 -2
- package/dist/nitro/v3/middleware.d.mts +3 -1
- package/dist/nitro/v3/middleware.d.mts.map +1 -1
- package/dist/nitro/v3/middleware.mjs +2 -1
- package/dist/nitro/v3/middleware.mjs.map +1 -1
- package/dist/nitro/v3/module.d.mts +1 -1
- package/dist/nitro/v3/plugin.mjs +6 -5
- package/dist/nitro/v3/plugin.mjs.map +1 -1
- package/dist/nitro/v3/useLogger.d.mts +1 -1
- package/dist/{nitro-DCNNxY_7.d.mts → nitro-BbTINVdZ.d.mts} +2 -2
- package/dist/{nitro-DCNNxY_7.d.mts.map → nitro-BbTINVdZ.d.mts.map} +1 -1
- package/dist/{nitro-CzyGROOC.mjs → nitro-OmT_M4Pb.mjs} +2 -2
- package/dist/{nitro-CzyGROOC.mjs.map → nitro-OmT_M4Pb.mjs.map} +1 -1
- package/dist/nuxt/module.d.mts +1 -1
- package/dist/nuxt/module.mjs +2 -2
- package/dist/{parseError-B08FS7EQ.d.mts → parseError-DO1qtmGL.d.mts} +2 -2
- package/dist/parseError-DO1qtmGL.d.mts.map +1 -0
- package/dist/react-router/index.d.mts +47 -0
- package/dist/react-router/index.d.mts.map +1 -0
- package/dist/react-router/index.mjs +59 -0
- package/dist/react-router/index.mjs.map +1 -0
- package/dist/runtime/client/log.d.mts +1 -1
- package/dist/runtime/server/useLogger.d.mts +1 -1
- package/dist/runtime/utils/parseError.d.mts +2 -2
- package/dist/runtime/utils/parseError.mjs +2 -1
- package/dist/runtime/utils/parseError.mjs.map +1 -1
- package/dist/{source-location-B1VVgXkh.mjs → source-location-DRvDDqfq.mjs} +1 -1
- package/dist/{source-location-B1VVgXkh.mjs.map → source-location-DRvDDqfq.mjs.map} +1 -1
- package/dist/sveltekit/index.d.mts +2 -2
- package/dist/sveltekit/index.mjs +3 -3
- package/dist/toolkit.d.mts +3 -3
- package/dist/toolkit.mjs +2 -2
- package/dist/{types-CBpJBj_7.d.mts → types-BpsDbwHU.d.mts} +3 -1
- package/dist/{types-CBpJBj_7.d.mts.map → types-BpsDbwHU.d.mts.map} +1 -1
- package/dist/types.d.mts +1 -1
- package/dist/{useLogger-DBPGEDf_.d.mts → useLogger-C_8vgz0g.d.mts} +2 -2
- package/dist/{useLogger-DBPGEDf_.d.mts.map → useLogger-C_8vgz0g.d.mts.map} +1 -1
- package/dist/utils.d.mts +1 -1
- package/dist/vite/index.d.mts +1 -1
- package/dist/vite/index.mjs +1 -1
- package/dist/workers.d.mts +1 -1
- package/package.json +22 -8
- 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)) |
|
package/dist/adapters/fs.d.mts
CHANGED
package/dist/adapters/otlp.d.mts
CHANGED
package/dist/ai/index.d.mts
CHANGED
|
@@ -1,8 +1,47 @@
|
|
|
1
|
-
import { g as RequestLogger } from "../types-
|
|
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
|
package/dist/ai/index.d.mts.map
CHANGED
|
@@ -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;
|
|
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
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
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
|
-
|
|
89
|
-
|
|
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
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
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
|
-
|
|
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
|
-
|
|
106
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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")
|
|
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"
|
|
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
|
-
|
|
170
|
-
if (
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
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
|