evlog 2.11.1 → 2.13.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 +42 -2
- package/dist/{_drain-YH8ERc5l.mjs → _drain-CmCtsuF6.mjs} +1 -1
- package/dist/{_drain-YH8ERc5l.mjs.map → _drain-CmCtsuF6.mjs.map} +1 -1
- package/dist/{_http-C_2wbJw3.mjs → _http-CHSsrWDJ.mjs} +2 -2
- package/dist/{_http-C_2wbJw3.mjs.map → _http-CHSsrWDJ.mjs.map} +1 -1
- package/dist/{_severity-BZhz3f9e.mjs → _severity-CQijvfhU.mjs} +1 -1
- package/dist/{_severity-BZhz3f9e.mjs.map → _severity-CQijvfhU.mjs.map} +1 -1
- package/dist/adapters/axiom.d.mts +1 -1
- package/dist/adapters/axiom.mjs +2 -2
- package/dist/adapters/better-stack.d.mts +1 -1
- package/dist/adapters/better-stack.mjs +2 -2
- package/dist/adapters/datadog.d.mts +1 -1
- package/dist/adapters/datadog.mjs +2 -2
- package/dist/adapters/fs.d.mts +1 -1
- package/dist/adapters/fs.mjs +1 -1
- package/dist/adapters/hyperdx.d.mts +1 -1
- package/dist/adapters/hyperdx.mjs +2 -2
- package/dist/adapters/otlp.d.mts +1 -1
- package/dist/adapters/otlp.mjs +3 -3
- package/dist/adapters/posthog.d.mts +1 -1
- package/dist/adapters/posthog.mjs +2 -2
- package/dist/adapters/sentry.d.mts +1 -1
- package/dist/adapters/sentry.mjs +3 -3
- package/dist/ai/index.d.mts +144 -5
- package/dist/ai/index.d.mts.map +1 -1
- package/dist/ai/index.mjs +108 -5
- package/dist/ai/index.mjs.map +1 -1
- package/dist/better-auth/index.d.mts +220 -0
- package/dist/better-auth/index.d.mts.map +1 -0
- package/dist/better-auth/index.mjs +205 -0
- package/dist/better-auth/index.mjs.map +1 -0
- package/dist/browser.d.mts +13 -52
- package/dist/browser.d.mts.map +1 -1
- package/dist/browser.mjs +5 -81
- package/dist/browser.mjs.map +1 -1
- package/dist/client.d.mts +2 -2
- package/dist/client.mjs +2 -2
- package/dist/{dist-BFn8qsRC.mjs → dist-Do8P4zWd.mjs} +1 -1
- package/dist/{dist-BFn8qsRC.mjs.map → dist-Do8P4zWd.mjs.map} +1 -1
- package/dist/elysia/index.d.mts +2 -2
- package/dist/elysia/index.d.mts.map +1 -1
- package/dist/elysia/index.mjs +16 -4
- package/dist/elysia/index.mjs.map +1 -1
- package/dist/enrichers.d.mts +1 -1
- package/dist/{error-plrBYLQk.d.mts → error-B9CiGK_i.d.mts} +2 -2
- package/dist/{error-plrBYLQk.d.mts.map → error-B9CiGK_i.d.mts.map} +1 -1
- package/dist/error.d.mts +1 -1
- package/dist/{errors-gH4C9KSC.mjs → errors-BJRXUfMg.mjs} +1 -1
- package/dist/{errors-gH4C9KSC.mjs.map → errors-BJRXUfMg.mjs.map} +1 -1
- package/dist/{errors-bPoj9UZk.d.mts → errors-Dr0r4OpR.d.mts} +2 -2
- package/dist/{errors-bPoj9UZk.d.mts.map → errors-Dr0r4OpR.d.mts.map} +1 -1
- package/dist/express/index.d.mts +2 -2
- package/dist/express/index.d.mts.map +1 -1
- package/dist/express/index.mjs +8 -4
- package/dist/express/index.mjs.map +1 -1
- package/dist/fastify/index.d.mts +2 -2
- package/dist/fastify/index.d.mts.map +1 -1
- package/dist/fastify/index.mjs +8 -4
- package/dist/fastify/index.mjs.map +1 -1
- package/dist/fork-Y4z8iHti.mjs +72 -0
- package/dist/fork-Y4z8iHti.mjs.map +1 -0
- package/dist/headers-D74M0wsg.mjs +30 -0
- package/dist/headers-D74M0wsg.mjs.map +1 -0
- package/dist/hono/index.d.mts +2 -2
- package/dist/hono/index.mjs +2 -1
- package/dist/hono/index.mjs.map +1 -1
- package/dist/http.d.mts +65 -0
- package/dist/http.d.mts.map +1 -0
- package/dist/http.mjs +94 -0
- package/dist/http.mjs.map +1 -0
- package/dist/index.d.mts +7 -6
- package/dist/index.mjs +3 -2
- package/dist/logger-DnobymUQ.mjs +741 -0
- package/dist/logger-DnobymUQ.mjs.map +1 -0
- package/dist/{logger-CG1eop2_.d.mts → logger-Dp6nYWjH.d.mts} +6 -2
- package/dist/logger-Dp6nYWjH.d.mts.map +1 -0
- package/dist/logger.d.mts +1 -1
- package/dist/logger.mjs +1 -361
- package/dist/{headers-BSi3UHKL.mjs → middleware-BtBuosFV.mjs} +13 -32
- package/dist/middleware-BtBuosFV.mjs.map +1 -0
- package/dist/{middleware-DojmTj9Y.d.mts → middleware-FgC1OdOD.d.mts} +21 -3
- package/dist/middleware-FgC1OdOD.d.mts.map +1 -0
- package/dist/nestjs/index.d.mts +2 -2
- package/dist/nestjs/index.d.mts.map +1 -1
- package/dist/nestjs/index.mjs +8 -4
- package/dist/nestjs/index.mjs.map +1 -1
- package/dist/next/client.d.mts +9 -3
- package/dist/next/client.d.mts.map +1 -1
- package/dist/next/client.mjs +5 -3
- package/dist/next/client.mjs.map +1 -1
- package/dist/next/index.d.mts +9 -4
- package/dist/next/index.d.mts.map +1 -1
- package/dist/next/index.mjs +17 -2
- package/dist/next/index.mjs.map +1 -1
- package/dist/next/instrumentation.d.mts +3 -1
- package/dist/next/instrumentation.d.mts.map +1 -1
- package/dist/next/instrumentation.mjs +2 -1
- package/dist/next/instrumentation.mjs.map +1 -1
- package/dist/nitro/errorHandler.mjs +2 -2
- package/dist/nitro/module.d.mts +2 -2
- package/dist/nitro/plugin.mjs +7 -4
- package/dist/nitro/plugin.mjs.map +1 -1
- package/dist/nitro/v3/errorHandler.mjs +3 -3
- package/dist/nitro/v3/index.d.mts +2 -2
- package/dist/nitro/v3/module.d.mts +1 -1
- package/dist/nitro/v3/plugin.mjs +8 -5
- package/dist/nitro/v3/plugin.mjs.map +1 -1
- package/dist/nitro/v3/useLogger.d.mts +1 -1
- package/dist/{nitro-CfGx0wDJ.d.mts → nitro-CDHLfRdw.d.mts} +13 -2
- package/dist/nitro-CDHLfRdw.d.mts.map +1 -0
- package/dist/{nitro-Dpq5ZmcM.mjs → nitro-OmT_M4Pb.mjs} +2 -2
- package/dist/nitro-OmT_M4Pb.mjs.map +1 -0
- package/dist/{nitroConfigBridge-fidbf-Y_.mjs → nitroConfigBridge-C37lXaNm.mjs} +1 -1
- package/dist/{nitroConfigBridge-fidbf-Y_.mjs.map → nitroConfigBridge-C37lXaNm.mjs.map} +1 -1
- package/dist/nuxt/module.d.mts +26 -1
- package/dist/nuxt/module.d.mts.map +1 -1
- package/dist/nuxt/module.mjs +7 -2
- package/dist/nuxt/module.mjs.map +1 -1
- package/dist/{parseError-B_qXj8x4.d.mts → parseError-DM-lyezZ.d.mts} +2 -2
- package/dist/parseError-DM-lyezZ.d.mts.map +1 -0
- package/dist/react-router/index.d.mts +2 -2
- package/dist/react-router/index.d.mts.map +1 -1
- package/dist/react-router/index.mjs +8 -4
- package/dist/react-router/index.mjs.map +1 -1
- package/dist/{routes-CE3_c-iZ.mjs → routes-CGPmbzCZ.mjs} +1 -1
- package/dist/{routes-CE3_c-iZ.mjs.map → routes-CGPmbzCZ.mjs.map} +1 -1
- package/dist/runtime/client/log.d.mts +7 -2
- package/dist/runtime/client/log.d.mts.map +1 -1
- package/dist/runtime/client/log.mjs +24 -6
- package/dist/runtime/client/log.mjs.map +1 -1
- package/dist/runtime/client/plugin.mjs +1 -0
- package/dist/runtime/client/plugin.mjs.map +1 -1
- package/dist/runtime/server/routes/_evlog/ingest.post.mjs +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 +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/{storage-B6NPh8rV.mjs → storage-CFGTn37X.mjs} +1 -1
- package/dist/{storage-B6NPh8rV.mjs.map → storage-CFGTn37X.mjs.map} +1 -1
- package/dist/sveltekit/index.d.mts +2 -2
- package/dist/sveltekit/index.d.mts.map +1 -1
- package/dist/sveltekit/index.mjs +10 -6
- package/dist/sveltekit/index.mjs.map +1 -1
- package/dist/toolkit.d.mts +41 -4
- package/dist/toolkit.d.mts.map +1 -1
- package/dist/toolkit.mjs +7 -5
- package/dist/{types-v_JkG_D7.d.mts → types-DbzDln7O.d.mts} +120 -4
- package/dist/types-DbzDln7O.d.mts.map +1 -0
- package/dist/types.d.mts +2 -2
- package/dist/{useLogger-TjKH37BO.d.mts → useLogger-N5A-d5l9.d.mts} +2 -2
- package/dist/{useLogger-TjKH37BO.d.mts.map → useLogger-N5A-d5l9.d.mts.map} +1 -1
- package/dist/utils-DnX6VMNi.d.mts +54 -0
- package/dist/utils-DnX6VMNi.d.mts.map +1 -0
- package/dist/utils.d.mts +2 -50
- package/dist/utils.mjs +13 -1
- package/dist/utils.mjs.map +1 -1
- package/dist/vite/index.d.mts +5 -1
- package/dist/vite/index.d.mts.map +1 -1
- package/dist/vite/index.mjs +3 -1
- package/dist/vite/index.mjs.map +1 -1
- package/dist/workers.d.mts +1 -1
- package/dist/workers.mjs +1 -1
- package/package.json +24 -3
- package/dist/headers-BSi3UHKL.mjs.map +0 -1
- package/dist/logger-CG1eop2_.d.mts.map +0 -1
- package/dist/logger.mjs.map +0 -1
- package/dist/middleware-DojmTj9Y.d.mts.map +0 -1
- package/dist/nitro-CfGx0wDJ.d.mts.map +0 -1
- package/dist/nitro-Dpq5ZmcM.mjs.map +0 -1
- package/dist/parseError-B_qXj8x4.d.mts.map +0 -1
- package/dist/types-v_JkG_D7.d.mts.map +0 -1
- package/dist/utils.d.mts.map +0 -1
package/README.md
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="https://raw.githubusercontent.com/HugoRCD/evlog/main/assets/evlog-banner.gif" width="100%" alt="evlog — Digging through logs is not observability. It's hope" />
|
|
3
|
+
</p>
|
|
4
|
+
|
|
1
5
|
# evlog
|
|
2
6
|
|
|
3
7
|
[](https://npmjs.com/package/evlog)
|
|
@@ -518,7 +522,7 @@ See the full [nestjs example](https://github.com/HugoRCD/evlog/tree/main/example
|
|
|
518
522
|
Use the `log` API on the client side for structured browser logging:
|
|
519
523
|
|
|
520
524
|
```typescript
|
|
521
|
-
import { log } from 'evlog/
|
|
525
|
+
import { log } from 'evlog/client'
|
|
522
526
|
|
|
523
527
|
log.info('checkout', 'User initiated checkout')
|
|
524
528
|
log.error({ action: 'payment', error: 'validation_failed' })
|
|
@@ -556,6 +560,8 @@ When enabled:
|
|
|
556
560
|
3. `evlog:drain` hook is called with `source: 'client'`
|
|
557
561
|
4. External services receive the log
|
|
558
562
|
|
|
563
|
+
For a **framework-agnostic** batched HTTP drain (e.g. vanilla JS or custom endpoints), use `createHttpLogDrain` from [`evlog/http`](https://www.evlog.dev/adapters/http). The legacy import path `evlog/browser` is deprecated and will be removed in the next major release.
|
|
564
|
+
|
|
559
565
|
## Structured Errors
|
|
560
566
|
|
|
561
567
|
Errors should tell you **what** happened, **why**, and **how to fix it**.
|
|
@@ -1060,6 +1066,40 @@ log.emit() // Emit final event
|
|
|
1060
1066
|
log.getContext() // Get current context
|
|
1061
1067
|
```
|
|
1062
1068
|
|
|
1069
|
+
### Wide event lifecycle and `log.fork()`
|
|
1070
|
+
|
|
1071
|
+
The framework emits **one wide event per HTTP request** when the response finishes (or on error). After `emit()` runs — including when head sampling drops the event (`emit()` returns `null`) — that logger instance is **sealed**: further `set`, `error`, `info`, and `warn` calls are ignored and emit a **`[evlog]` console warning** listing dropped keys. A second `emit()` is ignored with a warning. This avoids silent data loss when async work (unawaited promises, `setTimeout`, etc.) still resolves `useLogger()` to the same logger via `AsyncLocalStorage` after the response has already been logged.
|
|
1072
|
+
|
|
1073
|
+
**`log.fork(label, fn)`** runs work under a **child** request logger: inside `fn`, `useLogger()` returns the child. When `fn` settles, the child emits its **own** wide event with `operation` set to `label` and `_parentRequestId` set to the parent’s `requestId` (query and dashboard correlation). The parent event may be emitted **before** the child event; they are two separate events ordered by time.
|
|
1074
|
+
|
|
1075
|
+
`fork` is attached by integrations that use `AsyncLocalStorage` for `useLogger()`. Standalone `createLogger()` instances do not have `fork`.
|
|
1076
|
+
|
|
1077
|
+
| Integration | `log.fork()` |
|
|
1078
|
+
|-------------|----------------|
|
|
1079
|
+
| Express, Fastify, NestJS, SvelteKit, React Router, Elysia | Yes |
|
|
1080
|
+
| Next.js `withEvlog` | Yes |
|
|
1081
|
+
| Hono (`c.get('log')` only) | Not yet |
|
|
1082
|
+
| Nitro / Nuxt `useLogger(event)` | Not yet — use post-emit warnings; see [Wide events](https://evlog.dev/logging/wide-events) |
|
|
1083
|
+
|
|
1084
|
+
```typescript
|
|
1085
|
+
import { evlog, useLogger } from 'evlog/express'
|
|
1086
|
+
|
|
1087
|
+
app.post('/checkout', (req, res) => {
|
|
1088
|
+
const log = req.log
|
|
1089
|
+
log.set({ order_dispatched: true })
|
|
1090
|
+
|
|
1091
|
+
log.fork!('process_order', async () => {
|
|
1092
|
+
const childLog = useLogger()
|
|
1093
|
+
childLog.set({ inventory_checked: true })
|
|
1094
|
+
// child emits automatically when this async function completes
|
|
1095
|
+
})
|
|
1096
|
+
|
|
1097
|
+
res.json({ ok: true })
|
|
1098
|
+
})
|
|
1099
|
+
```
|
|
1100
|
+
|
|
1101
|
+
Use optional chaining if `fork` might be absent: `log.fork?.('task', async () => { ... })`.
|
|
1102
|
+
|
|
1063
1103
|
### `initWorkersLogger(options?)`
|
|
1064
1104
|
|
|
1065
1105
|
Initialize evlog for Cloudflare Workers (object logs + correct severity).
|
|
@@ -1154,7 +1194,7 @@ try {
|
|
|
1154
1194
|
| **Hono** | `app.use(evlog())` with `import { evlog } from 'evlog/hono'` ([example](./examples/hono)) |
|
|
1155
1195
|
| **Fastify** | `app.register(evlog)` with `import { evlog } from 'evlog/fastify'` ([example](./examples/fastify)) |
|
|
1156
1196
|
| **Elysia** | `.use(evlog())` with `import { evlog } from 'evlog/elysia'` ([example](./examples/elysia)) |
|
|
1157
|
-
| **Cloudflare Workers** | Manual setup with `import {
|
|
1197
|
+
| **Cloudflare Workers** | Manual setup with `import { initWorkersLogger, createWorkersLogger } from 'evlog/workers'` ([example](./examples/workers)) |
|
|
1158
1198
|
| **Custom** | Build your own with `import { createMiddlewareLogger } from 'evlog/toolkit'` ([guide](https://evlog.dev/frameworks/custom-integration)) |
|
|
1159
1199
|
| **Analog** | Nitro v2 module setup |
|
|
1160
1200
|
| **Vinxi** | Nitro v2 module setup |
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"_drain-
|
|
1
|
+
{"version":3,"file":"_drain-CmCtsuF6.mjs","names":[],"sources":["../src/adapters/_drain.ts"],"sourcesContent":["import type { DrainContext, WideEvent } from '../types'\n\nexport interface DrainOptions<TConfig> {\n name: string\n resolve: () => TConfig | null | Promise<TConfig | null>\n send: (events: WideEvent[], config: TConfig) => Promise<void>\n}\n\n/**\n * Build a drain callback for `evlog:drain` (or `initLogger({ drain })`).\n * The returned function is async so `resolve` can load Nitro runtime config; hosts typically attach\n * the resulting promise to `waitUntil` so the HTTP response is not blocked (see Nitro plugin).\n */\nexport function defineDrain<TConfig>(options: DrainOptions<TConfig>): (ctx: DrainContext | DrainContext[]) => Promise<void> {\n return async (ctx: DrainContext | DrainContext[]) => {\n const contexts = Array.isArray(ctx) ? ctx : [ctx]\n if (contexts.length === 0) return\n\n const config = await options.resolve()\n if (!config) return\n\n try {\n await options.send(contexts.map(c => c.event), config)\n } catch (error) {\n console.error(`[evlog/${options.name}] Failed to send events:`, error)\n }\n }\n}\n"],"mappings":";;;;;;AAaA,SAAgB,YAAqB,SAAuF;AAC1H,QAAO,OAAO,QAAuC;EACnD,MAAM,WAAW,MAAM,QAAQ,IAAI,GAAG,MAAM,CAAC,IAAI;AACjD,MAAI,SAAS,WAAW,EAAG;EAE3B,MAAM,SAAS,MAAM,QAAQ,SAAS;AACtC,MAAI,CAAC,OAAQ;AAEb,MAAI;AACF,SAAM,QAAQ,KAAK,SAAS,KAAI,MAAK,EAAE,MAAM,EAAE,OAAO;WAC/C,OAAO;AACd,WAAQ,MAAM,UAAU,QAAQ,KAAK,2BAA2B,MAAM"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as getNitroRuntimeConfigRecord } from "./nitroConfigBridge-
|
|
1
|
+
import { t as getNitroRuntimeConfigRecord } from "./nitroConfigBridge-C37lXaNm.mjs";
|
|
2
2
|
//#region src/adapters/_config.ts
|
|
3
3
|
/**
|
|
4
4
|
* Adapter runtime-config reads go through `getNitroRuntimeConfigRecord` in
|
|
@@ -68,4 +68,4 @@ async function httpPost({ url, headers, body, timeout, label, retries = 2 }) {
|
|
|
68
68
|
//#endregion
|
|
69
69
|
export { resolveAdapterConfig as n, httpPost as t };
|
|
70
70
|
|
|
71
|
-
//# sourceMappingURL=_http-
|
|
71
|
+
//# sourceMappingURL=_http-CHSsrWDJ.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"_http-
|
|
1
|
+
{"version":3,"file":"_http-CHSsrWDJ.mjs","names":[],"sources":["../src/adapters/_config.ts","../src/adapters/_http.ts"],"sourcesContent":["import { getNitroRuntimeConfigRecord } from '../shared/nitroConfigBridge'\n\n/**\n * Adapter runtime-config reads go through `getNitroRuntimeConfigRecord` in\n * `shared/nitroConfigBridge.ts` (documented there — Workers-safe dynamic imports).\n *\n * Drain handlers remain non-blocking when the host provides `waitUntil`.\n */\n\nexport function getRuntimeConfig(): Promise<Record<string, any> | undefined> {\n return getNitroRuntimeConfigRecord()\n}\n\nexport interface ConfigField<T> {\n key: keyof T & string\n env?: string[]\n}\n\nexport async function resolveAdapterConfig<T>(\n namespace: string,\n fields: ConfigField<T>[],\n overrides?: Partial<T>,\n): Promise<Partial<T>> {\n const runtimeConfig = await getRuntimeConfig()\n const evlogNs = runtimeConfig?.evlog?.[namespace]\n const rootNs = runtimeConfig?.[namespace]\n\n const config: Record<string, unknown> = {}\n\n for (const { key, env } of fields) {\n config[key] =\n overrides?.[key]\n ?? evlogNs?.[key]\n ?? rootNs?.[key]\n ?? resolveEnv(env)\n }\n\n return config as Partial<T>\n}\n\nfunction resolveEnv(envKeys?: string[]): string | undefined {\n if (!envKeys) return undefined\n for (const key of envKeys) {\n const val = process.env[key]\n if (val) return val\n }\n return undefined\n}\n","export interface HttpPostOptions {\n url: string\n headers: Record<string, string>\n body: string\n timeout: number\n label: string\n retries?: number\n}\n\nfunction isRetryable(error: unknown): boolean {\n if (error instanceof DOMException && error.name === 'AbortError') return true\n if (error instanceof TypeError) return true\n if (error instanceof Error) {\n const match = error.message.match(/API error: (\\d+)/)\n if (match) return Number.parseInt(match[1]) >= 500\n }\n return false\n}\n\nexport async function httpPost({ url, headers, body, timeout, label, retries = 2 }: HttpPostOptions): Promise<void> {\n const normalizedRetries = Number.isFinite(retries) && retries >= 0 ? Math.floor(retries) : 2\n\n let lastError: Error | undefined\n\n for (let attempt = 0; attempt <= normalizedRetries; attempt++) {\n const controller = new AbortController()\n const timeoutId = setTimeout(() => controller.abort(), timeout)\n\n try {\n const response = await fetch(url, {\n method: 'POST',\n headers,\n body,\n signal: controller.signal,\n })\n\n if (!response.ok) {\n const text = await response.text().catch(() => 'Unknown error')\n const safeText = text.length > 200 ? `${text.slice(0, 200)}...[truncated]` : text\n throw new Error(`${label} API error: ${response.status} ${response.statusText} - ${safeText}`)\n }\n\n clearTimeout(timeoutId)\n return\n } catch (error) {\n clearTimeout(timeoutId)\n\n if (error instanceof DOMException && error.name === 'AbortError') {\n lastError = new Error(`${label} request timed out after ${timeout}ms`)\n } else {\n lastError = error as Error\n }\n\n if (!isRetryable(error) || attempt === normalizedRetries) {\n throw lastError\n }\n\n await new Promise<void>(r => setTimeout(r, 200 * 2 ** attempt))\n }\n }\n\n throw lastError!\n}\n"],"mappings":";;;;;;;;AASA,SAAgB,mBAA6D;AAC3E,QAAO,6BAA6B;;AAQtC,eAAsB,qBACpB,WACA,QACA,WACqB;CACrB,MAAM,gBAAgB,MAAM,kBAAkB;CAC9C,MAAM,UAAU,eAAe,QAAQ;CACvC,MAAM,SAAS,gBAAgB;CAE/B,MAAM,SAAkC,EAAE;AAE1C,MAAK,MAAM,EAAE,KAAK,SAAS,OACzB,QAAO,OACL,YAAY,QACT,UAAU,QACV,SAAS,QACT,WAAW,IAAI;AAGtB,QAAO;;AAGT,SAAS,WAAW,SAAwC;AAC1D,KAAI,CAAC,QAAS,QAAO,KAAA;AACrB,MAAK,MAAM,OAAO,SAAS;EACzB,MAAM,MAAM,QAAQ,IAAI;AACxB,MAAI,IAAK,QAAO;;;;;ACnCpB,SAAS,YAAY,OAAyB;AAC5C,KAAI,iBAAiB,gBAAgB,MAAM,SAAS,aAAc,QAAO;AACzE,KAAI,iBAAiB,UAAW,QAAO;AACvC,KAAI,iBAAiB,OAAO;EAC1B,MAAM,QAAQ,MAAM,QAAQ,MAAM,mBAAmB;AACrD,MAAI,MAAO,QAAO,OAAO,SAAS,MAAM,GAAG,IAAI;;AAEjD,QAAO;;AAGT,eAAsB,SAAS,EAAE,KAAK,SAAS,MAAM,SAAS,OAAO,UAAU,KAAqC;CAClH,MAAM,oBAAoB,OAAO,SAAS,QAAQ,IAAI,WAAW,IAAI,KAAK,MAAM,QAAQ,GAAG;CAE3F,IAAI;AAEJ,MAAK,IAAI,UAAU,GAAG,WAAW,mBAAmB,WAAW;EAC7D,MAAM,aAAa,IAAI,iBAAiB;EACxC,MAAM,YAAY,iBAAiB,WAAW,OAAO,EAAE,QAAQ;AAE/D,MAAI;GACF,MAAM,WAAW,MAAM,MAAM,KAAK;IAChC,QAAQ;IACR;IACA;IACA,QAAQ,WAAW;IACpB,CAAC;AAEF,OAAI,CAAC,SAAS,IAAI;IAChB,MAAM,OAAO,MAAM,SAAS,MAAM,CAAC,YAAY,gBAAgB;IAC/D,MAAM,WAAW,KAAK,SAAS,MAAM,GAAG,KAAK,MAAM,GAAG,IAAI,CAAC,kBAAkB;AAC7E,UAAM,IAAI,MAAM,GAAG,MAAM,cAAc,SAAS,OAAO,GAAG,SAAS,WAAW,KAAK,WAAW;;AAGhG,gBAAa,UAAU;AACvB;WACO,OAAO;AACd,gBAAa,UAAU;AAEvB,OAAI,iBAAiB,gBAAgB,MAAM,SAAS,aAClD,6BAAY,IAAI,MAAM,GAAG,MAAM,2BAA2B,QAAQ,IAAI;OAEtE,aAAY;AAGd,OAAI,CAAC,YAAY,MAAM,IAAI,YAAY,kBACrC,OAAM;AAGR,SAAM,IAAI,SAAc,MAAK,WAAW,GAAG,MAAM,KAAK,QAAQ,CAAC;;;AAInE,OAAM"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"_severity-
|
|
1
|
+
{"version":3,"file":"_severity-CQijvfhU.mjs","names":[],"sources":["../src/adapters/_severity.ts"],"sourcesContent":["import type { LogLevel } from '../types'\n\nexport const OTEL_SEVERITY_NUMBER: Record<LogLevel, number> = {\n debug: 5,\n info: 9,\n warn: 13,\n error: 17,\n}\n\nexport const OTEL_SEVERITY_TEXT: Record<LogLevel, string> = {\n debug: 'DEBUG',\n info: 'INFO',\n warn: 'WARN',\n error: 'ERROR',\n}\n"],"mappings":";AAEA,MAAa,uBAAiD;CAC5D,OAAO;CACP,MAAM;CACN,MAAM;CACN,OAAO;CACR;AAED,MAAa,qBAA+C;CAC1D,OAAO;CACP,MAAM;CACN,MAAM;CACN,OAAO;CACR"}
|
package/dist/adapters/axiom.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { n as resolveAdapterConfig, t as httpPost } from "../_http-
|
|
2
|
-
import { t as defineDrain } from "../_drain-
|
|
1
|
+
import { n as resolveAdapterConfig, t as httpPost } from "../_http-CHSsrWDJ.mjs";
|
|
2
|
+
import { t as defineDrain } from "../_drain-CmCtsuF6.mjs";
|
|
3
3
|
//#region src/adapters/axiom.ts
|
|
4
4
|
const AXIOM_FIELDS = [
|
|
5
5
|
{
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { n as resolveAdapterConfig, t as httpPost } from "../_http-
|
|
2
|
-
import { t as defineDrain } from "../_drain-
|
|
1
|
+
import { n as resolveAdapterConfig, t as httpPost } from "../_http-CHSsrWDJ.mjs";
|
|
2
|
+
import { t as defineDrain } from "../_drain-CmCtsuF6.mjs";
|
|
3
3
|
//#region src/adapters/better-stack.ts
|
|
4
4
|
const BETTER_STACK_FIELDS = [
|
|
5
5
|
{
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { n as resolveAdapterConfig, t as httpPost } from "../_http-
|
|
2
|
-
import { t as defineDrain } from "../_drain-
|
|
1
|
+
import { n as resolveAdapterConfig, t as httpPost } from "../_http-CHSsrWDJ.mjs";
|
|
2
|
+
import { t as defineDrain } from "../_drain-CmCtsuF6.mjs";
|
|
3
3
|
//#region src/adapters/datadog.ts
|
|
4
4
|
const DATADOG_FIELDS = [
|
|
5
5
|
{
|
package/dist/adapters/fs.d.mts
CHANGED
package/dist/adapters/fs.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as defineDrain } from "../_drain-
|
|
1
|
+
import { t as defineDrain } from "../_drain-CmCtsuF6.mjs";
|
|
2
2
|
import { join, sep } from "node:path";
|
|
3
3
|
import { appendFile, mkdir, readdir, stat, unlink, writeFile } from "node:fs/promises";
|
|
4
4
|
//#region src/adapters/fs.ts
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { n as resolveAdapterConfig } from "../_http-
|
|
2
|
-
import { t as defineDrain } from "../_drain-
|
|
1
|
+
import { n as resolveAdapterConfig } from "../_http-CHSsrWDJ.mjs";
|
|
2
|
+
import { t as defineDrain } from "../_drain-CmCtsuF6.mjs";
|
|
3
3
|
import { sendBatchToOTLP } from "./otlp.mjs";
|
|
4
4
|
//#region src/adapters/hyperdx.ts
|
|
5
5
|
/**
|
package/dist/adapters/otlp.d.mts
CHANGED
package/dist/adapters/otlp.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { n as resolveAdapterConfig, t as httpPost } from "../_http-
|
|
2
|
-
import { t as defineDrain } from "../_drain-
|
|
3
|
-
import { n as OTEL_SEVERITY_TEXT, t as OTEL_SEVERITY_NUMBER } from "../_severity-
|
|
1
|
+
import { n as resolveAdapterConfig, t as httpPost } from "../_http-CHSsrWDJ.mjs";
|
|
2
|
+
import { t as defineDrain } from "../_drain-CmCtsuF6.mjs";
|
|
3
|
+
import { n as OTEL_SEVERITY_TEXT, t as OTEL_SEVERITY_NUMBER } from "../_severity-CQijvfhU.mjs";
|
|
4
4
|
//#region src/adapters/otlp.ts
|
|
5
5
|
const OTLP_FIELDS = [
|
|
6
6
|
{
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { n as resolveAdapterConfig, t as httpPost } from "../_http-
|
|
2
|
-
import { t as defineDrain } from "../_drain-
|
|
1
|
+
import { n as resolveAdapterConfig, t as httpPost } from "../_http-CHSsrWDJ.mjs";
|
|
2
|
+
import { t as defineDrain } from "../_drain-CmCtsuF6.mjs";
|
|
3
3
|
import { sendBatchToOTLP } from "./otlp.mjs";
|
|
4
4
|
//#region src/adapters/posthog.ts
|
|
5
5
|
const POSTHOG_FIELDS = [
|
package/dist/adapters/sentry.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { n as resolveAdapterConfig, t as httpPost } from "../_http-
|
|
2
|
-
import { t as defineDrain } from "../_drain-
|
|
3
|
-
import { t as OTEL_SEVERITY_NUMBER } from "../_severity-
|
|
1
|
+
import { n as resolveAdapterConfig, t as httpPost } from "../_http-CHSsrWDJ.mjs";
|
|
2
|
+
import { t as defineDrain } from "../_drain-CmCtsuF6.mjs";
|
|
3
|
+
import { t as OTEL_SEVERITY_NUMBER } from "../_severity-CQijvfhU.mjs";
|
|
4
4
|
//#region src/adapters/sentry.ts
|
|
5
5
|
const SENTRY_FIELDS = [
|
|
6
6
|
{
|
package/dist/ai/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { GatewayModelId } from "ai";
|
|
1
|
+
import { _ as RequestLogger } from "../types-DbzDln7O.mjs";
|
|
2
|
+
import { GatewayModelId, TelemetryIntegration } from "ai";
|
|
3
3
|
import { LanguageModelV3, LanguageModelV3Middleware } from "@ai-sdk/provider";
|
|
4
4
|
|
|
5
5
|
//#region src/ai/index.d.ts
|
|
@@ -19,6 +19,13 @@ interface ToolInputsOptions {
|
|
|
19
19
|
*/
|
|
20
20
|
transform?: (input: unknown, toolName: string) => unknown;
|
|
21
21
|
}
|
|
22
|
+
/**
|
|
23
|
+
* Pricing entry for a single model: cost per 1 million tokens in dollars.
|
|
24
|
+
*/
|
|
25
|
+
interface ModelCost {
|
|
26
|
+
input: number;
|
|
27
|
+
output: number;
|
|
28
|
+
}
|
|
22
29
|
/**
|
|
23
30
|
* Options for `createAILogger` and `createAIMiddleware`.
|
|
24
31
|
*/
|
|
@@ -32,6 +39,24 @@ interface AILoggerOptions {
|
|
|
32
39
|
* @default false
|
|
33
40
|
*/
|
|
34
41
|
toolInputs?: boolean | ToolInputsOptions;
|
|
42
|
+
/**
|
|
43
|
+
* Pricing map for estimating request cost.
|
|
44
|
+
* Keys are model IDs (e.g. `'claude-sonnet-4.6'`, `'gpt-4o'`), values are
|
|
45
|
+
* `{ input, output }` in dollars per 1M tokens.
|
|
46
|
+
*
|
|
47
|
+
* When provided, the wide event includes `ai.estimatedCost` (in dollars).
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* ```ts
|
|
51
|
+
* const ai = createAILogger(log, {
|
|
52
|
+
* cost: {
|
|
53
|
+
* 'claude-sonnet-4.6': { input: 3, output: 15 },
|
|
54
|
+
* 'gpt-4o': { input: 2.5, output: 10 },
|
|
55
|
+
* },
|
|
56
|
+
* })
|
|
57
|
+
* ```
|
|
58
|
+
*/
|
|
59
|
+
cost?: Record<string, ModelCost>;
|
|
35
60
|
}
|
|
36
61
|
/**
|
|
37
62
|
* Per-step token usage breakdown for multi-step agent runs.
|
|
@@ -42,6 +67,24 @@ interface AIStepUsage {
|
|
|
42
67
|
outputTokens: number;
|
|
43
68
|
toolCalls?: string[];
|
|
44
69
|
}
|
|
70
|
+
/**
|
|
71
|
+
* Tool execution detail captured via `TelemetryIntegration`.
|
|
72
|
+
*/
|
|
73
|
+
interface AIToolExecution {
|
|
74
|
+
name: string;
|
|
75
|
+
durationMs: number;
|
|
76
|
+
success: boolean;
|
|
77
|
+
error?: string;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Embedding metadata captured via `captureEmbed`.
|
|
81
|
+
*/
|
|
82
|
+
interface AIEmbeddingData {
|
|
83
|
+
model?: string;
|
|
84
|
+
tokens: number;
|
|
85
|
+
dimensions?: number;
|
|
86
|
+
count?: number;
|
|
87
|
+
}
|
|
45
88
|
/**
|
|
46
89
|
* Shape of the `ai` field written to the wide event.
|
|
47
90
|
*/
|
|
@@ -68,12 +111,16 @@ interface AIEventData {
|
|
|
68
111
|
msToFinish?: number;
|
|
69
112
|
tokensPerSecond?: number;
|
|
70
113
|
error?: string;
|
|
114
|
+
tools?: AIToolExecution[];
|
|
115
|
+
totalDurationMs?: number;
|
|
116
|
+
embedding?: AIEmbeddingData;
|
|
117
|
+
estimatedCost?: number;
|
|
71
118
|
}
|
|
72
119
|
interface AILogger {
|
|
73
120
|
/**
|
|
74
121
|
* Wrap a language model with evlog middleware.
|
|
75
|
-
* All `generateText
|
|
76
|
-
*
|
|
122
|
+
* All `generateText` and `streamText` calls using the wrapped model
|
|
123
|
+
* are captured automatically into the wide event.
|
|
77
124
|
*
|
|
78
125
|
* Accepts a `LanguageModelV3` object or a model string (e.g. `'anthropic/claude-sonnet-4.6'`).
|
|
79
126
|
* Strings are resolved via the AI SDK gateway.
|
|
@@ -100,13 +147,34 @@ interface AILogger {
|
|
|
100
147
|
* ```ts
|
|
101
148
|
* const { embedding, usage } = await embed({ model: embeddingModel, value: query })
|
|
102
149
|
* ai.captureEmbed({ usage })
|
|
150
|
+
*
|
|
151
|
+
* // With model info (v2)
|
|
152
|
+
* ai.captureEmbed({ usage, model: 'text-embedding-3-small', dimensions: 1536 })
|
|
153
|
+
*
|
|
154
|
+
* // After embedMany
|
|
155
|
+
* ai.captureEmbed({ usage, count: texts.length })
|
|
103
156
|
* ```
|
|
104
157
|
*/
|
|
105
158
|
captureEmbed: (result: {
|
|
106
159
|
usage: {
|
|
107
160
|
tokens: number;
|
|
108
161
|
};
|
|
162
|
+
model?: string;
|
|
163
|
+
dimensions?: number;
|
|
164
|
+
count?: number;
|
|
109
165
|
}) => void;
|
|
166
|
+
/**
|
|
167
|
+
* Internal accumulator state exposed for `createEvlogIntegration` to share.
|
|
168
|
+
* @internal
|
|
169
|
+
*/
|
|
170
|
+
_state: AccumulatorState;
|
|
171
|
+
}
|
|
172
|
+
interface UsageAccumulator {
|
|
173
|
+
inputTokens: number;
|
|
174
|
+
outputTokens: number;
|
|
175
|
+
cacheReadTokens: number;
|
|
176
|
+
cacheWriteTokens: number;
|
|
177
|
+
reasoningTokens: number;
|
|
110
178
|
}
|
|
111
179
|
/**
|
|
112
180
|
* Create the evlog AI middleware that captures AI SDK data into a wide event.
|
|
@@ -160,6 +228,77 @@ declare function createAIMiddleware(log: RequestLogger, options?: AILoggerOption
|
|
|
160
228
|
* ```
|
|
161
229
|
*/
|
|
162
230
|
declare function createAILogger(log: RequestLogger, options?: AILoggerOptions): AILogger;
|
|
231
|
+
interface AccumulatorState {
|
|
232
|
+
calls: number;
|
|
233
|
+
steps: number;
|
|
234
|
+
usage: UsageAccumulator;
|
|
235
|
+
models: string[];
|
|
236
|
+
lastProvider: string | undefined;
|
|
237
|
+
allToolCalls: string[];
|
|
238
|
+
allToolCallInputs: Array<{
|
|
239
|
+
name: string;
|
|
240
|
+
input: unknown;
|
|
241
|
+
}>;
|
|
242
|
+
stepsUsage: AIStepUsage[];
|
|
243
|
+
lastFinishReason: string | undefined;
|
|
244
|
+
lastMsToFirstChunk: number | undefined;
|
|
245
|
+
lastMsToFinish: number | undefined;
|
|
246
|
+
lastError: string | undefined;
|
|
247
|
+
lastResponseId: string | undefined;
|
|
248
|
+
toolInputs: boolean;
|
|
249
|
+
toolInputsOptions: ToolInputsOptions | undefined;
|
|
250
|
+
toolExecutions: AIToolExecution[];
|
|
251
|
+
generationStartTime: number | undefined;
|
|
252
|
+
totalDurationMs: number | undefined;
|
|
253
|
+
embedding: AIEmbeddingData | undefined;
|
|
254
|
+
costMap: Record<string, ModelCost> | undefined;
|
|
255
|
+
/** @internal Logger reference for integration flush */
|
|
256
|
+
_log?: RequestLogger;
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Create an AI SDK `TelemetryIntegration` that captures tool execution
|
|
260
|
+
* timing, errors, and total generation wall time into the wide event.
|
|
261
|
+
*
|
|
262
|
+
* Complements the middleware-based `createAILogger`: the middleware captures
|
|
263
|
+
* token usage and streaming metrics at the model level, while the integration
|
|
264
|
+
* captures application-level lifecycle events (tool execution, total duration).
|
|
265
|
+
*
|
|
266
|
+
* When passed an `AILogger`, shares its accumulator so both paths write to
|
|
267
|
+
* the same `ai.*` field. Can also be used standalone with a `RequestLogger`.
|
|
268
|
+
*
|
|
269
|
+
* @example Combined with middleware (recommended)
|
|
270
|
+
* ```ts
|
|
271
|
+
* import { createAILogger, createEvlogIntegration } from 'evlog/ai'
|
|
272
|
+
*
|
|
273
|
+
* const log = useLogger(event)
|
|
274
|
+
* const ai = createAILogger(log)
|
|
275
|
+
*
|
|
276
|
+
* const result = await generateText({
|
|
277
|
+
* model: ai.wrap('anthropic/claude-sonnet-4.6'),
|
|
278
|
+
* tools: { getWeather },
|
|
279
|
+
* experimental_telemetry: {
|
|
280
|
+
* isEnabled: true,
|
|
281
|
+
* integrations: [createEvlogIntegration(ai)],
|
|
282
|
+
* },
|
|
283
|
+
* })
|
|
284
|
+
* ```
|
|
285
|
+
*
|
|
286
|
+
* @example Standalone (no middleware wrapping)
|
|
287
|
+
* ```ts
|
|
288
|
+
* import { createEvlogIntegration } from 'evlog/ai'
|
|
289
|
+
*
|
|
290
|
+
* const integration = createEvlogIntegration(log)
|
|
291
|
+
*
|
|
292
|
+
* const result = await generateText({
|
|
293
|
+
* model: openai('gpt-4o'),
|
|
294
|
+
* experimental_telemetry: {
|
|
295
|
+
* isEnabled: true,
|
|
296
|
+
* integrations: [integration],
|
|
297
|
+
* },
|
|
298
|
+
* })
|
|
299
|
+
* ```
|
|
300
|
+
*/
|
|
301
|
+
declare function createEvlogIntegration(logOrAi: RequestLogger | AILogger, options?: AILoggerOptions): TelemetryIntegration;
|
|
163
302
|
//#endregion
|
|
164
|
-
export { AIEventData, AILogger, AILoggerOptions, AIStepUsage, ToolInputsOptions, createAILogger, createAIMiddleware };
|
|
303
|
+
export { AIEmbeddingData, AIEventData, AILogger, AILoggerOptions, AIStepUsage, AIToolExecution, ModelCost, ToolInputsOptions, createAILogger, createAIMiddleware, createEvlogIntegration };
|
|
165
304
|
//# 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,iBAAA;;;;;EAKf,SAAA;EAM6B;;;AAM/B;;EANE,SAAA,IAAa,KAAA,WAAgB,QAAA;AAAA;;
|
|
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;;AAc/B;;UARiB,SAAA;EACf,KAAA;EACA,MAAA;AAAA;;;;UAMe,eAAA;EA2Bf;;;;;AAMF;;;EAxBE,UAAA,aAAuB,iBAAA;EAyBvB;;;;;;AASF;;;;;;;;;;AAUA;EA1BE,IAAA,GAAO,MAAA,SAAe,SAAA;AAAA;;;;UAMP,WAAA;EACf,KAAA;EACA,WAAA;EACA,YAAA;EACA,SAAA;AAAA;;;;UAMe,eAAA;EACf,IAAA;EACA,UAAA;EACA,OAAA;EACA,KAAA;AAAA;;;;UAMe,eAAA;EACf,KAAA;EACA,MAAA;EACA,UAAA;EACA,KAAA;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;EACA,KAAA,GAAQ,eAAA;EACR,eAAA;EACA,SAAA,GAAY,eAAA;EACZ,aAAA;AAAA;AAAA,UAGe,QAAA;EAqBR;;;;;;;;;;;;;AA+BR;;;;;;;EA/BC,IAAA,GAAO,KAAA,EAAO,eAAA,GAAkB,cAAA,KAAmB,eAAA;EAsCnD;;;AAyDF;;;;;;;;;;;;;;EA5EE,YAAA,GAAe,MAAA;IACb,KAAA;MAAS,MAAA;IAAA;IACT,KAAA;IACA,UAAA;IACA,KAAA;EAAA;EAsGmF;;;;EA/FrF,MAAA,EAAQ,gBAAA;AAAA;AAAA,UAGA,gBAAA;EACR,WAAA;EACA,YAAA;EACA,eAAA;EACA,gBAAA;EACA,eAAA;AAAA;;;;;;;;;;;;;;;;;;;;;;;;;iBAyDc,kBAAA,CAAmB,GAAA,EAAK,aAAA,EAAe,OAAA,GAAU,eAAA,GAAkB,yBAAA;;;;;;;;;;;;;;;;;;;AAocnF;;;;;;;;iBAtagB,cAAA,CAAe,GAAA,EAAK,aAAA,EAAe,OAAA,GAAU,eAAA,GAAkB,QAAA;AAAA,UAgCrE,gBAAA;EACR,KAAA;EACA,KAAA;EACA,KAAA,EAAO,gBAAA;EACP,MAAA;EACA,YAAA;EACA,YAAA;EACA,iBAAA,EAAmB,KAAA;IAAQ,IAAA;IAAc,KAAA;EAAA;EACzC,UAAA,EAAY,WAAA;EACZ,gBAAA;EACA,kBAAA;EACA,cAAA;EACA,SAAA;EACA,cAAA;EACA,UAAA;EACA,iBAAA,EAAmB,iBAAA;EACnB,cAAA,EAAgB,eAAA;EAChB,mBAAA;EACA,eAAA;EACA,SAAA,EAAW,eAAA;EACX,OAAA,EAAS,MAAA,SAAe,SAAA;;EAExB,IAAA,GAAO,aAAA;AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAgXO,sBAAA,CACd,OAAA,EAAS,aAAA,GAAgB,QAAA,EACzB,OAAA,GAAU,eAAA,GACT,oBAAA"}
|
package/dist/ai/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { gateway, wrapLanguageModel } from "ai";
|
|
1
|
+
import { bindTelemetryIntegration, gateway, wrapLanguageModel } from "ai";
|
|
2
2
|
//#region src/ai/index.ts
|
|
3
3
|
function addUsage(acc, usage) {
|
|
4
4
|
acc.inputTokens += usage.inputTokens.total ?? 0;
|
|
@@ -78,6 +78,7 @@ function createAIMiddleware(log, options) {
|
|
|
78
78
|
*/
|
|
79
79
|
function createAILogger(log, options) {
|
|
80
80
|
const state = createAccumulatorState(options);
|
|
81
|
+
state._log = log;
|
|
81
82
|
const middleware = buildMiddlewareFromState(log, state);
|
|
82
83
|
return {
|
|
83
84
|
wrap: (model) => {
|
|
@@ -89,8 +90,15 @@ function createAILogger(log, options) {
|
|
|
89
90
|
captureEmbed: (result) => {
|
|
90
91
|
state.calls++;
|
|
91
92
|
state.usage.inputTokens += result.usage.tokens;
|
|
93
|
+
state.embedding = {
|
|
94
|
+
tokens: (state.embedding?.tokens ?? 0) + result.usage.tokens,
|
|
95
|
+
...result.model ? { model: result.model } : state.embedding?.model ? { model: state.embedding.model } : {},
|
|
96
|
+
...result.dimensions ? { dimensions: result.dimensions } : state.embedding?.dimensions ? { dimensions: state.embedding.dimensions } : {},
|
|
97
|
+
...result.count ? { count: (state.embedding?.count ?? 0) + result.count } : state.embedding?.count ? { count: state.embedding.count } : {}
|
|
98
|
+
};
|
|
92
99
|
flushState(log, state);
|
|
93
|
-
}
|
|
100
|
+
},
|
|
101
|
+
_state: state
|
|
94
102
|
};
|
|
95
103
|
}
|
|
96
104
|
function resolveToolInputs(raw) {
|
|
@@ -139,9 +147,23 @@ function createAccumulatorState(options) {
|
|
|
139
147
|
lastError: void 0,
|
|
140
148
|
lastResponseId: void 0,
|
|
141
149
|
toolInputs: enabled,
|
|
142
|
-
toolInputsOptions: captureOpts
|
|
150
|
+
toolInputsOptions: captureOpts,
|
|
151
|
+
toolExecutions: [],
|
|
152
|
+
generationStartTime: void 0,
|
|
153
|
+
totalDurationMs: void 0,
|
|
154
|
+
embedding: void 0,
|
|
155
|
+
costMap: options?.cost
|
|
143
156
|
};
|
|
144
157
|
}
|
|
158
|
+
function computeEstimatedCost(state) {
|
|
159
|
+
if (!state.costMap) return void 0;
|
|
160
|
+
const lastModel = state.models[state.models.length - 1];
|
|
161
|
+
if (!lastModel) return void 0;
|
|
162
|
+
const pricing = state.costMap[lastModel];
|
|
163
|
+
if (!pricing) return void 0;
|
|
164
|
+
const total = state.usage.inputTokens / 1e6 * pricing.input + state.usage.outputTokens / 1e6 * pricing.output;
|
|
165
|
+
return total > 0 ? Math.round(total * 1e6) / 1e6 : void 0;
|
|
166
|
+
}
|
|
145
167
|
function flushState(log, state) {
|
|
146
168
|
const uniqueModels = [...new Set(state.models)];
|
|
147
169
|
const lastModel = state.models[state.models.length - 1];
|
|
@@ -171,6 +193,11 @@ function flushState(log, state) {
|
|
|
171
193
|
if (state.usage.outputTokens > 0 && state.lastMsToFinish > 0) data.tokensPerSecond = Math.round(state.usage.outputTokens / state.lastMsToFinish * 1e3);
|
|
172
194
|
}
|
|
173
195
|
if (state.lastError) data.error = state.lastError;
|
|
196
|
+
if (state.toolExecutions.length > 0) data.tools = [...state.toolExecutions];
|
|
197
|
+
if (state.totalDurationMs !== void 0) data.totalDurationMs = state.totalDurationMs;
|
|
198
|
+
if (state.embedding) data.embedding = { ...state.embedding };
|
|
199
|
+
const cost = computeEstimatedCost(state);
|
|
200
|
+
if (cost !== void 0) data.estimatedCost = cost;
|
|
174
201
|
log.set({ ai: data });
|
|
175
202
|
}
|
|
176
203
|
function recordModel(state, provider, modelId, responseModelId) {
|
|
@@ -200,7 +227,9 @@ function recordError(log, state, model, error) {
|
|
|
200
227
|
flushState(log, state);
|
|
201
228
|
}
|
|
202
229
|
function buildMiddleware(log, options) {
|
|
203
|
-
|
|
230
|
+
const state = createAccumulatorState(options);
|
|
231
|
+
state._log = log;
|
|
232
|
+
return buildMiddlewareFromState(log, state);
|
|
204
233
|
}
|
|
205
234
|
function buildMiddlewareFromState(log, state) {
|
|
206
235
|
return {
|
|
@@ -334,7 +363,81 @@ function buildMiddlewareFromState(log, state) {
|
|
|
334
363
|
}
|
|
335
364
|
};
|
|
336
365
|
}
|
|
366
|
+
/**
|
|
367
|
+
* Create an AI SDK `TelemetryIntegration` that captures tool execution
|
|
368
|
+
* timing, errors, and total generation wall time into the wide event.
|
|
369
|
+
*
|
|
370
|
+
* Complements the middleware-based `createAILogger`: the middleware captures
|
|
371
|
+
* token usage and streaming metrics at the model level, while the integration
|
|
372
|
+
* captures application-level lifecycle events (tool execution, total duration).
|
|
373
|
+
*
|
|
374
|
+
* When passed an `AILogger`, shares its accumulator so both paths write to
|
|
375
|
+
* the same `ai.*` field. Can also be used standalone with a `RequestLogger`.
|
|
376
|
+
*
|
|
377
|
+
* @example Combined with middleware (recommended)
|
|
378
|
+
* ```ts
|
|
379
|
+
* import { createAILogger, createEvlogIntegration } from 'evlog/ai'
|
|
380
|
+
*
|
|
381
|
+
* const log = useLogger(event)
|
|
382
|
+
* const ai = createAILogger(log)
|
|
383
|
+
*
|
|
384
|
+
* const result = await generateText({
|
|
385
|
+
* model: ai.wrap('anthropic/claude-sonnet-4.6'),
|
|
386
|
+
* tools: { getWeather },
|
|
387
|
+
* experimental_telemetry: {
|
|
388
|
+
* isEnabled: true,
|
|
389
|
+
* integrations: [createEvlogIntegration(ai)],
|
|
390
|
+
* },
|
|
391
|
+
* })
|
|
392
|
+
* ```
|
|
393
|
+
*
|
|
394
|
+
* @example Standalone (no middleware wrapping)
|
|
395
|
+
* ```ts
|
|
396
|
+
* import { createEvlogIntegration } from 'evlog/ai'
|
|
397
|
+
*
|
|
398
|
+
* const integration = createEvlogIntegration(log)
|
|
399
|
+
*
|
|
400
|
+
* const result = await generateText({
|
|
401
|
+
* model: openai('gpt-4o'),
|
|
402
|
+
* experimental_telemetry: {
|
|
403
|
+
* isEnabled: true,
|
|
404
|
+
* integrations: [integration],
|
|
405
|
+
* },
|
|
406
|
+
* })
|
|
407
|
+
* ```
|
|
408
|
+
*/
|
|
409
|
+
function createEvlogIntegration(logOrAi, options) {
|
|
410
|
+
let log;
|
|
411
|
+
let state;
|
|
412
|
+
if ("_state" in logOrAi && logOrAi._state) {
|
|
413
|
+
state = logOrAi._state;
|
|
414
|
+
log = state._log;
|
|
415
|
+
} else {
|
|
416
|
+
log = logOrAi;
|
|
417
|
+
state = createAccumulatorState(options);
|
|
418
|
+
state._log = log;
|
|
419
|
+
}
|
|
420
|
+
class EvlogIntegration {
|
|
421
|
+
onStart(_event) {
|
|
422
|
+
state.generationStartTime = Date.now();
|
|
423
|
+
}
|
|
424
|
+
onToolCallFinish(event) {
|
|
425
|
+
const execution = {
|
|
426
|
+
name: event.toolCall.toolName,
|
|
427
|
+
durationMs: event.durationMs,
|
|
428
|
+
success: event.success
|
|
429
|
+
};
|
|
430
|
+
if (!event.success && event.error) execution.error = event.error instanceof Error ? event.error.message : String(event.error);
|
|
431
|
+
state.toolExecutions.push(execution);
|
|
432
|
+
}
|
|
433
|
+
onFinish(_event) {
|
|
434
|
+
if (state.generationStartTime) state.totalDurationMs = Date.now() - state.generationStartTime;
|
|
435
|
+
flushState(log, state);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
return bindTelemetryIntegration(new EvlogIntegration());
|
|
439
|
+
}
|
|
337
440
|
//#endregion
|
|
338
|
-
export { createAILogger, createAIMiddleware };
|
|
441
|
+
export { createAILogger, createAIMiddleware, createEvlogIntegration };
|
|
339
442
|
|
|
340
443
|
//# sourceMappingURL=index.mjs.map
|