evlog 2.11.1 → 2.12.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 +4 -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/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.mjs +1 -1
- package/dist/enrichers.d.mts +1 -1
- package/dist/{error-plrBYLQk.d.mts → error-WRz4_F3W.d.mts} +2 -2
- package/dist/{error-plrBYLQk.d.mts.map → error-WRz4_F3W.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-J2kt7mZh.d.mts} +2 -2
- package/dist/{errors-bPoj9UZk.d.mts.map → errors-J2kt7mZh.d.mts.map} +1 -1
- package/dist/express/index.d.mts +2 -2
- package/dist/express/index.mjs +2 -2
- package/dist/fastify/index.d.mts +2 -2
- package/dist/fastify/index.mjs +2 -2
- package/dist/{headers-BSi3UHKL.mjs → headers-ht4yS2mx.mjs} +6 -4
- package/dist/headers-ht4yS2mx.mjs.map +1 -0
- package/dist/hono/index.d.mts +2 -2
- package/dist/hono/index.mjs +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-CG1eop2_.d.mts → logger-Bm0k3Hf3.d.mts} +2 -2
- package/dist/{logger-CG1eop2_.d.mts.map → logger-Bm0k3Hf3.d.mts.map} +1 -1
- package/dist/logger-DY0X5oQd.mjs +704 -0
- package/dist/logger-DY0X5oQd.mjs.map +1 -0
- package/dist/logger.d.mts +1 -1
- package/dist/logger.mjs +1 -361
- package/dist/{middleware-DojmTj9Y.d.mts → middleware-D_igVy93.d.mts} +9 -2
- package/dist/middleware-D_igVy93.d.mts.map +1 -0
- package/dist/nestjs/index.d.mts +2 -2
- package/dist/nestjs/index.mjs +2 -2
- 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 +3 -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-BeRXZcBd.d.mts} +13 -2
- package/dist/nitro-BeRXZcBd.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-DhXS_vzM.d.mts} +2 -2
- package/dist/parseError-DhXS_vzM.d.mts.map +1 -0
- package/dist/react-router/index.d.mts +2 -2
- package/dist/react-router/index.mjs +2 -2
- 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-DpLJYMoc.mjs} +1 -1
- package/dist/{storage-B6NPh8rV.mjs.map → storage-DpLJYMoc.mjs.map} +1 -1
- package/dist/sveltekit/index.d.mts +2 -2
- package/dist/sveltekit/index.mjs +4 -4
- package/dist/toolkit.d.mts +3 -3
- package/dist/toolkit.mjs +4 -4
- package/dist/{types-v_JkG_D7.d.mts → types-D5OwxZCw.d.mts} +71 -2
- package/dist/types-D5OwxZCw.d.mts.map +1 -0
- package/dist/types.d.mts +2 -2
- package/dist/{useLogger-TjKH37BO.d.mts → useLogger-Dcj1Nrsa.d.mts} +2 -2
- package/dist/{useLogger-TjKH37BO.d.mts.map → useLogger-Dcj1Nrsa.d.mts.map} +1 -1
- package/dist/utils-Bnc95-VC.d.mts +54 -0
- package/dist/utils-Bnc95-VC.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 +9 -1
- package/dist/headers-BSi3UHKL.mjs.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
|
@@ -518,7 +518,7 @@ See the full [nestjs example](https://github.com/HugoRCD/evlog/tree/main/example
|
|
|
518
518
|
Use the `log` API on the client side for structured browser logging:
|
|
519
519
|
|
|
520
520
|
```typescript
|
|
521
|
-
import { log } from 'evlog/
|
|
521
|
+
import { log } from 'evlog/client'
|
|
522
522
|
|
|
523
523
|
log.info('checkout', 'User initiated checkout')
|
|
524
524
|
log.error({ action: 'payment', error: 'validation_failed' })
|
|
@@ -556,6 +556,8 @@ When enabled:
|
|
|
556
556
|
3. `evlog:drain` hook is called with `source: 'client'`
|
|
557
557
|
4. External services receive the log
|
|
558
558
|
|
|
559
|
+
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.
|
|
560
|
+
|
|
559
561
|
## Structured Errors
|
|
560
562
|
|
|
561
563
|
Errors should tell you **what** happened, **why**, and **how to fix it**.
|
|
@@ -1154,7 +1156,7 @@ try {
|
|
|
1154
1156
|
| **Hono** | `app.use(evlog())` with `import { evlog } from 'evlog/hono'` ([example](./examples/hono)) |
|
|
1155
1157
|
| **Fastify** | `app.register(evlog)` with `import { evlog } from 'evlog/fastify'` ([example](./examples/fastify)) |
|
|
1156
1158
|
| **Elysia** | `.use(evlog())` with `import { evlog } from 'evlog/elysia'` ([example](./examples/elysia)) |
|
|
1157
|
-
| **Cloudflare Workers** | Manual setup with `import {
|
|
1159
|
+
| **Cloudflare Workers** | Manual setup with `import { initWorkersLogger, createWorkersLogger } from 'evlog/workers'` ([example](./examples/workers)) |
|
|
1158
1160
|
| **Custom** | Build your own with `import { createMiddlewareLogger } from 'evlog/toolkit'` ([guide](https://evlog.dev/frameworks/custom-integration)) |
|
|
1159
1161
|
| **Analog** | Nitro v2 module setup |
|
|
1160
1162
|
| **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-D5OwxZCw.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
|