vern-llm-logger 0.0.0-stage → 0.1.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/LICENSE.md +7 -0
- package/README.md +42 -2
- package/dist/index.cjs +6 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +259 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +259 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +6 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +101 -3
package/LICENSE.md
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
Copyright 2026 LakBud
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
4
|
+
|
|
5
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
6
|
+
|
|
7
|
+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,43 @@
|
|
|
1
|
-
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="https://raw.githubusercontent.com/LakBud/vernLLM/main/apps/docs/public/extensions/vernllm-logger.png" alt="vern-llm-logger logo" width="96" />
|
|
3
|
+
</p>
|
|
2
4
|
|
|
3
|
-
|
|
5
|
+
<h1 align="center">vern-llm-logger</h1>
|
|
6
|
+
|
|
7
|
+
<p align="center">Structured call logs, rolling latency percentiles, and slow call and error detection for <a href="https://github.com/LakBud/vernLLM/tree/main/packages/vern-llm">vern-llm</a>.</p>
|
|
8
|
+
|
|
9
|
+
One middleware that writes a structured record for every call, keeps rolling latency percentiles per provider and model, marks slow calls, groups repeated errors, samples successes, and writes periodic summaries. A failure inside the logger never changes a call.
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import { VernLLM } from 'vern-llm';
|
|
15
|
+
import { jsonSink, loggerMiddleware } from 'vern-llm-logger';
|
|
16
|
+
|
|
17
|
+
const llm = new VernLLM({
|
|
18
|
+
client,
|
|
19
|
+
model: 'gpt-4o',
|
|
20
|
+
middleware: [loggerMiddleware({ sink: jsonSink() })],
|
|
21
|
+
});
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Each call writes one record, for example `{ kind: 'call', outcome: 'ok', provider: 'primary', model: 'gpt-4o', durationMs: 842, attempts: 1, usage: { totalTokens: 52 } }`, plus the path, retries, cache and stream details. Prompt text, output text and error messages are never recorded.
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
const logger = loggerMiddleware({
|
|
28
|
+
sink: consoleSink(),
|
|
29
|
+
slowCall: { aboveLivePercentile: 0.99 },
|
|
30
|
+
sample: { success: 0.1 },
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
logger.stats.quantile({ provider: 'primary' }, 0.99); // rolling p99 in ms
|
|
34
|
+
logger.stats.quantile({ provider: 'primary' }, 0.5, 'outputTokens'); // typical answer length
|
|
35
|
+
logger.stats.failureRate({ provider: 'primary' }, 'quality'); // share of answers rejected as unusable
|
|
36
|
+
logger.dispose(); // on shutdown
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Register one instance per `VernLLM`. `vern-llm` is a peer dependency, and the package has no other dependency. Sinks are `jsonSink`, `consoleSink` and `memorySink`, or any object with a `write(level, entry)` method.
|
|
40
|
+
|
|
41
|
+
Pair it with `vern-llm-otel` to put trace ids in every record: `loggerMiddleware({ enrich: [otelLogFields] })`.
|
|
42
|
+
|
|
43
|
+
See the [documentation](https://vernllm.dev/docs/extensions/logger) for every option.
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require("vern-llm");function t(e,t){return n=>{let r=n.custom?.[e],i=r?.[r.length-1];if(typeof i!=`object`||!i||Array.isArray(i))return;let a=i[t];return typeof a==`string`?a:void 0}}function n(){return performance.now()}function r(e,t=n()){return!Number.isFinite(e)||!Number.isFinite(t)?0:Math.max(0,t-e)}var i=class{windowMs;now;groups=/* @__PURE__ */ new Map;pending=[];closed=!1;constructor(e,t=n){this.windowMs=e,this.now=t}offer(e){if(!e.error||this.closed)return!0;let t=this.now(),n=e.provider??e.path[e.path.length-1]?.provider??`unknown`,{type:r,code:i}=e.error,a=JSON.stringify([n,r,i??null]),o=this.groups.get(a);return o&&t-o.startedAtMs<this.windowMs?(o.suppressed++,o.lastRequestId=e.requestId,!1):(o&&this.pending.push(...this.entryOf(o)),!o&&this.groups.size>=500||this.groups.set(a,{provider:n,type:r,code:i,startedAtMs:t,suppressed:0,firstRequestId:e.requestId,lastRequestId:e.requestId}),!0)}flush(e=!1){let t=this.now(),n=this.pending.splice(0);for(let[r,i]of this.groups)!e&&t-i.startedAtMs<this.windowMs||(n.push(...this.entryOf(i)),this.groups.delete(r));return n}close(){this.closed=!0}clear(){this.groups.clear(),this.pending.length=0}entryOf(e){if(e.suppressed===0)return[];let t={kind:`error_group`,v:1,provider:e.provider,type:e.type,count:e.suppressed+1,windowMs:this.windowMs,firstRequestId:e.firstRequestId,lastRequestId:e.lastRequestId};return e.code!==void 0&&(t.code=e.code),[t]}};function a(t){let n=t===`silent`?void 0:t??new e.ConsoleLogger(!1),r=(e,t)=>{if(n)try{n.error(`[VernLLM] logger: ${e} failed`,{message:t instanceof Error?t.message:String(t),stack:t instanceof Error?t.stack:void 0})}catch{}},i=((e,t,n)=>{try{return t()}catch(t){return r(e,t),n}});return i.report=r,i.warn=e=>{if(n)try{n.warn(`[VernLLM] logger: ${e}`)}catch{}},i}function o(e){return{entry:{kind:`breaker`,v:1,provider:e.provider,model:e.model,from:e.from,to:e.to,consecutiveFailures:e.consecutiveFailures},level:e.to===`open`?`warn`:`info`}}function s(e,t){let n=(n,r)=>{let i=e(`finish`,()=>n.finish(r),void 0);i&&t(i)};return{finish:n,observeStream:(t,r,i)=>{try{i.finalResult.then(e=>n(t,{kind:`result`,result:{value:e,meta:r.meta,cache:r.cache}}),e=>n(t,{kind:`error`,error:e}))}catch(i){e.report(`observeStream`,i),n(t,{kind:`result`,result:r})}}}}function c(e){throw Error(`loggerMiddleware: ${e}`)}function l(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function u(e,t){e!==void 0&&typeof e!=`boolean`&&c(`${t} must be a boolean`)}function d(e,t){e!==void 0&&typeof e!=`function`&&c(`${t} must be a function`)}function f(e,t){e!==void 0&&!l(e)&&c(`${t} must be an object`)}function p(e,t,n){return e===void 0?n:((typeof e!=`number`||!Number.isFinite(e)||e<=0)&&c(`${t} must be a positive number`),e)}function m(e,t,n){return e===void 0?n:((typeof e!=`number`||!Number.isInteger(e)||e<=0)&&c(`${t} must be a positive integer`),e)}function h(e,t){return(typeof e!=`number`||!Number.isFinite(e)||e<=0||e>=1)&&c(`${t} must be a number between 0 and 1, exclusive`),e}function g(e,t){Array.isArray(e)||c(`${t} must be an array of strings`);for(let n of e)(typeof n!=`string`||n.trim()===``)&&c(`${t} must contain only non empty strings`);return[...new Set(e)]}const ee=[`usage.*`,`policy.*`,`router.*`,`guard.*`,`hedge.*`],_=/^[A-Za-z0-9_-]+(\.[A-Za-z0-9_-]+)*(\.\*)?$/;function te(e){let t=[],n=!0;Array.isArray(e)?t=g(e,`customEvents`):e!==void 0&&(l(e)||c(`customEvents must be an array or an object`),u(e.defaults,`customEvents.defaults`),n=e.defaults??!0,e.names!==void 0&&(t=g(e.names,`customEvents.names`)));for(let e of t)_.test(e)||c(`customEvents pattern "${e}" must be a plain name or "prefix.*"`);let r=n?[...ee,...t]:t,i=/* @__PURE__ */ new Set,a=[];for(let e of r)e.endsWith(`.*`)?a.push(e.slice(0,-1)):i.add(e);return e=>i.has(e)||a.some(t=>e.startsWith(t))}function ne(e){f(e,`sample`);let t=e?.success===void 0?1:e.success;return typeof t==`number`&&t>=0&&t<=1||c(`sample.success must be a number from 0 to 1`),t}function re(e){return u(e,`errorGrouping`),e??!0}function ie(e){return e!==void 0&&(typeof e!=`string`||e.trim()===``)&&c(`name must be a non-empty string`),e??`logger`}function ae(e){return e===void 0||e===`silent`||typeof e==`object`&&e||c(`logger must be a Logger or 'silent'`),e}const oe=/* @__PURE__ */ new Set([`metric`,`provider`,`model`,`streaming`]);function se(e){return Object.freeze(e===void 0?[]:g(e,`context`))}function ce(e){let t=/* @__PURE__ */ new Map;if(e===void 0)return t;l(e)||c(`dimensions must be an object`);for(let[n,r]of Object.entries(e))n.trim()===``&&c(`dimensions names must be non empty`),oe.has(n)&&c(`dimensions.${n} is a reserved name`),typeof r!=`function`&&c(`dimensions.${n} must be a function`),t.set(n,r);return t}function le(e){if(e===void 0)return Object.freeze([]);let t=Array.isArray(e)?e:[e];return t.forEach((t,n)=>{typeof t!=`function`&&c(`enrich${Array.isArray(e)?`[${n}]`:``} must be a function`)}),Object.freeze([...t])}const v=e=>e>=1e3?`${(e/1e3).toFixed(2)}s`:`${Math.round(e)}ms`,y=({type:e,code:t})=>t?`${e}/${t}`:e;function ue(e){let t=e.provider?` ${e.provider}/${e.model}`:``,n=[`${e.outcome}${t}`,v(e.durationMs)];return e.ttftMs!==void 0&&n.push(`ttft ${v(e.ttftMs)}`),e.cache?n.push(`cache ${e.cache}`):e.attempts!==1&&n.push(`${e.attempts} attempts`),e.reason&&n.push(e.reason),e.error&&n.push(y(e.error)),n.push(e.requestId),n.join(` `)}function de(e){return`error_group ${e.provider} ${y(e)} x${e.count} in ${v(e.windowMs)}`}function fe(e){return`breaker ${e.provider}/${e.model} ${e.from} -> ${e.to}`}function pe({series:e}){let t=Object.values(e.dimensions).join(`/`)||`all`,n=e.quantiles[`0.99`],r=n===void 0?``:` p99 ${v(n)}`;return`summary ${e.metric} ${t} n=${e.count}${r} errors ${e.errors}`}function b(e){switch(e.kind){case`call`:return ue(e);case`error_group`:return de(e);case`breaker`:return fe(e);case`summary`:return pe(e)}}function x(e={}){let t=e.write??(e=>console.log(e));return{write:(e,n)=>t(JSON.stringify(n))}}function me(e={}){let t=e.write??((e,t)=>{console[t===`info`?`log`:t](e)});return{write:(e,n)=>t(`[VernLLM] ${b(n)}`,e)}}function S(){let e=[];return{entries:e,write:(t,n)=>{e.push({level:t,entry:n})},clear:()=>{e.length=0}}}const C=[`debug`,`info`,`warn`,`error`];function he(e){let t=e===void 0?x():e;return l(t)&&typeof t.write==`function`||c(`sink must be an object with a write function`),t}function ge(e){let t=e===void 0?`info`:e;return C.includes(t)||c(`level must be one of ${C.join(`, `)}`),t}const _e=[.5,.9,.95,.99],w=6e4,T=1e3;function ve(e){if(e===void 0)return _e;Array.isArray(e)&&e.length!==0||c(`percentiles must be a non empty array`);let t=e.map((e,t)=>h(e,`percentiles[${t}]`));for(let e=1;e<t.length;e++)t[e]<=t[e-1]&&c(`percentiles must be sorted ascending and unique`);return Object.freeze(t)}function ye(e){f(e,`window`);let t=m(e?.slots,`window.slots`,6),n=p(e?.durationMs,`window.durationMs`,w);return(t<2||t>60)&&c(`window.slots must be an integer from 2 to 60`),n/t<T&&c(`window.durationMs divided by window.slots must be at least ${T} ms`),Object.freeze({durationMs:n,slots:t})}function be(e){f(e,`slowCall`);let t=p(e?.thresholdMs,`slowCall.thresholdMs`),n=e?.aboveLivePercentile===void 0?void 0:h(e.aboveLivePercentile,`slowCall.aboveLivePercentile`);return Object.freeze({thresholdMs:t,aboveLivePercentile:n})}function xe(e){if(e===void 0)return;l(e)||c(`summary must be an object`),d(e.onSummary,`summary.onSummary`);let t=p(e.everyMs,`summary.everyMs`,w);return t<T&&c(`summary.everyMs must be at least ${T} ms`),Object.freeze({everyMs:t,onSummary:e.onSummary})}function Se(e){let t=e===void 0?{}:e;return l(t)||c(`options must be an object`),Object.freeze({sink:he(t.sink),level:ge(t.level),percentiles:ve(t.percentiles),window:ye(t.window),slowCall:be(t.slowCall),minSamples:m(t.minSamples,`minSamples`,20),maxSeries:m(t.maxSeries,`maxSeries`,200),sampleSuccess:ne(t.sample),errorGrouping:re(t.errorGrouping),context:se(t.context),keepCustomEvent:te(t.customEvents),dimensions:ce(t.dimensions),enrich:le(t.enrich),summary:xe(t.summary),name:ie(t.name),logger:ae(t.logger)})}function E(e,t,n){if(t.thresholdMs!==void 0&&e.durationMs>t.thresholdMs)return!0;if(t.aboveLivePercentile===void 0)return!1;let r=n();return r!==void 0&&e.durationMs>r}const D=[`durationMs`,`ttftMs`,`attemptMs`,`queueWaitMs`,`outputTokensPerSec`,`outputTokens`];function O(e){return e.outcome===`ok`&&e.cache===void 0&&e.attempts>0&&e.shortCircuitedBy===void 0}function k(e,t){if(t>=1)return!0;if(t<=0)return!1;let n=2166136261;for(let t=0;t<e.length;t++)n^=e.charCodeAt(t),n=Math.imul(n,16777619);return(n>>>0)/4294967296<t}const A={debug:0,info:1,warn:2,error:3};function j(e,t,n,r){let i=!1,a=A[e.level],o=(n,r)=>{if(A[n]<a)return;let o=t(`sink.write`,()=>e.sink.write(n,r),void 0);typeof o?.then==`function`&&o.then(void 0,e=>{i||(i=!0,t.report(`sink.write`,e))})},s=i=>{t(`stats.observe`,()=>n.observe(i),void 0);let a=i;t(`slowCall`,()=>O(a)&&E(a,e.slowCall,()=>n.livePercentileFor(a)),!1)&&(a=Object.freeze({...a,level:A[a.level]<A.warn?`warn`:a.level,reason:`slow`})),(!e.errorGrouping||a.outcome!==`error`||t(`errorGroup`,()=>r.offer(a),!0))&&(a.outcome!==`ok`||a.reason!==void 0||a.breaker?.length||t(`sample`,()=>k(a.requestId,e.sampleSuccess),!0))&&o(a.level,a)};return(e,t)=>{e.kind===`call`?s(e):o(t??`info`,e)}}function Ce(e,t,n){switch(t.kind){case`rate_limited`:e.queued(t.waitedMs);return;case`retry`:e.retried(t.delayMs,t.error,t.chain);return;case`fallback`:e.fellBack(t.elapsedMs,t.error,t.chain);return;case`circuit_state`:e.noteBreaker({provider:t.provider,model:t.model,from:t.from,to:t.to});return;case`middleware`:{let n={kind:t.hook,name:t.middleware};e.noteMiddleware(t.patchedFields?{...n,patchedFields:[...t.patchedFields]}:n);return}case`usage`:e.setUsage(t.usage,!1,t.chain);return;case`usage_failure`:e.setUsage(t.usage,!0,t.chain);return;case`custom`:n(t.name)&&e.addCustom(t.name,t.data);return}}const M=/* @__PURE__ */ new WeakMap;function we(e,t){t.length>0&&M.set(e,t)}function Te(e){return M.get(e)}function Ee(e,t){if(!e||t.length===0)return;let n={};for(let r of t)Object.hasOwn(e,r)&&e[r]!==void 0&&(n[r]=e[r]);return Object.keys(n).length>0?n:void 0}function De(e,t,n){if(e.length===0)return;let r={};return e.forEach((e,i)=>{let a=`enrich[${i}]`,o=n(a,()=>e(t),void 0);if(typeof o==`object`&&o){if(typeof o.then==`function`)Promise.resolve(o).catch(e=>n.report(a,e));else for(let[e,t]of Object.entries(o))e!==`__proto__`&&(r[e]=t)}}),Object.keys(r).length>0?r:void 0}const N=e=>typeof e==`number`&&Number.isFinite(e);function Oe(e){let t={promptTokens:e.promptTokens,completionTokens:e.completionTokens,totalTokens:e.totalTokens};if(N(e.reasoningTokens)&&(t.reasoningTokens=e.reasoningTokens),N(e.cacheReadTokens)&&(t.cacheReadTokens=e.cacheReadTokens),N(e.cacheWriteTokens)&&(t.cacheWriteTokens=e.cacheWriteTokens),e.cacheWriteTokensByTtl){let n={};for(let[t,r]of Object.entries(e.cacheWriteTokensByTtl))t!==`__proto__`&&N(r)&&(n[t]=r);t.cacheWriteTokensByTtl=n}return t}const P=e=>Math.round(e*1e3)/1e3;function ke(e,t){if(e!==void 0&&t!==void 0&&!(!Number.isFinite(e)||e<=0||t<=0))return P(e/(t/1e3))}function F(e){return Object.freeze(e.map(e=>Object.freeze({...e})))}function Ae(e){let t={};for(let[n,r]of e)t[n]=Object.freeze([...r]);return Object.freeze(t)}function je(e){return{kind:`call`,v:1,ts:e.endedAt.toISOString(),requestId:e.requestId,outcome:e.classified.outcome,level:e.classified.level,attempts:e.attempts,retries:e.retries,streaming:e.streaming,durationMs:P(e.durationMs),queueWaitMs:P(e.queueWaitMs),retryDelayMs:P(e.retryDelayMs),path:F(e.path)}}function Me(e,t){let{answered:n}=t;n&&(e.provider=n.provider,e.model=n.model,e.fallbackIndex=n.fallbackIndex,n.finishReason&&(e.finishReason=n.finishReason)),t.chains&&(e.chains=t.chains.count,e.chain=t.chains.kept),t.cache!==void 0&&(e.cache=t.cache),t.attemptMs!==void 0&&(e.attemptMs=P(t.attemptMs)),t.ttftMs!==void 0&&(e.ttftMs=P(t.ttftMs)),t.shortCircuitedBy!==void 0&&(e.shortCircuitedBy=t.shortCircuitedBy)}function Ne(e,t){if(!t.usage)return;e.usage=Object.freeze(Oe(t.usage)),t.usageFailed&&(e.usageFailed=!0);let n=ke(t.usage.completionTokens,t.generationMs);n!==void 0&&(e.outputTokensPerSec=n)}function Pe(e,t){t.classified.error&&(e.error=Object.freeze({...t.classified.error})),t.breaker.length>0&&(e.breaker=F(t.breaker)),t.middleware.length>0&&(e.middleware=F(t.middleware)),t.custom.size>0&&(e.custom=Ae(t.custom)),t.context&&(e.context=Object.freeze(t.context)),t.fields&&(e.fields=Object.freeze(t.fields))}function Fe(e){let t=je(e);return Me(t,e),Ne(t,e),Pe(t,e),Object.freeze(t)}function I(e){if(typeof e!=`object`||!e)return!1;let t=e;return t.name===`LLMError`&&typeof t.type==`string`}const L=e=>typeof e==`string`&&e!==``;function R(e){let{status:t}=e;return typeof t==`number`&&Number.isInteger(t)&&t>=100&&t<=599?t:void 0}const Ie=/* @__PURE__ */ new Set([`policy_denied`,`content_blocked`,`model_refused`]);function z(e){if(e.kind===`result`)return{outcome:`ok`,level:`info`};let{error:t}=e;if(!I(t))return{outcome:`error`,level:`error`,error:{type:`unknown`}};let n={type:t.type};L(t.code)&&(n.code=t.code);let r=R(t);return r!==void 0&&(n.status=r),t.type===`aborted`?{outcome:`aborted`,level:`info`,error:n}:t.type===`quota_exceeded`||n.code!==void 0&&Ie.has(n.code)?{outcome:`rejected`,level:`warn`,error:n}:{outcome:`error`,level:`error`,error:n}}function B(e){return I(e)?L(e.code)?e.code:e.type:`unknown`}function V(e){if(!I(e)||e.type!==`fallback_exhausted`||!Array.isArray(e.attempts)||e.attempts.length===0)return e;let t=e.attempts[e.attempts.length-1]?.error;if(typeof t!=`object`||!t)return e;let{type:n,code:r}=t;return typeof n==`string`?{name:`LLMError`,type:n,code:r}:e}function Le(e){return I(e)?e.type===`validation`||e.type===`parse`||e.code===`soft_failure_detected`||e.code===`empty_response`:!1}const Re=/* @__PURE__ */ new Set([`unsupported_capability`,`middleware_threw`]);function ze(e){return I(e)&&e.type===`invalid_params`&&Re.has(e.code)&&R(e)===void 0}function Be(e,t){let r=e[e.length-1];return(!r||r.closed||r.provider!==t.requestedProvider)&&(r={provider:t.requestedProvider,model:t.requestedModel,attempts:0,startedAtMs:n(),closed:!1,outcome:`error`,elapsedMs:0,errorCode:void 0},e.push(r)),r.attempts++,r}function Ve(e,t){if(t.path.attempts--,t.path.attempts!==0)return;let n=e.indexOf(t.path);
|
|
2
|
+
/* v8 ignore next */
|
|
3
|
+
n!==-1&&e.splice(n,1)}function He(e,t,n){let i=e[e.length-1];i&&!i.closed&&(i.closed=!0,i.outcome=`error`,i.errorCode=B(n),i.elapsedMs=Number.isFinite(t)&&t>=0?t:r(i.startedAtMs))}function Ue(e,t,n){let i=e[e.length-1];i&&!i.closed&&(i.closed=!0,i.elapsedMs=r(i.startedAtMs,t),n.failed?(i.outcome=`error`,i.errorCode=B(V(n.error))):i.outcome=`ok`)}function We(e){let t={provider:e.provider,model:e.model,attempts:e.attempts,outcome:e.outcome,elapsedMs:P(e.elapsedMs)};return e.errorCode!==void 0&&(t.errorCode=e.errorCode),t}function Ge(e){let{open:t,answered:n,streaming:i,startedAtMs:a,endedAtMs:o}=e,s=t?.respondedAtMs,c,l;t&&n&&(c=r(t.startedAtMs,s??o),l=i?r(s??o,o):c);let u=i&&s!==void 0?r(a,s):void 0;return{attemptMs:c,generationMs:l,ttftMs:u}}var Ke=class e{ctx;config;guard;startedAtMs=n();ended=!1;streaming=!1;attemptCount=0;chains=/* @__PURE__ */ new Map;retries=0;retryDelayMs=0;queueWaitMs=0;usages=/* @__PURE__ */ new Map;shortCircuitedBy;breaker=[];middleware=[];custom=/* @__PURE__ */ new Map;tallies=/* @__PURE__ */ new Map;constructor(e,t,n){this.ctx=e,this.config=t,this.guard=n}static start(t,n,r){return new e(t,n,r)}openAttempt(e){if(this.ended)return;let t=this.chainState(e.chain??0);t.current=void 0,this.attemptCount++;let r=Be(t.path,e);t.current={startedAtMs:n(),respondedAtMs:void 0,path:r}}responded(e=0){let t=this.chains.get(e)?.current;t&&(t.respondedAtMs=n())}attemptFailed(e,t=0){let n=this.chains.get(t),r=n?.current;n&&r&&(n.current=void 0,ze(e)?(this.attemptCount--,Ve(n.path,r)):this.settle(r,{error:e}))}retried(e,t,n=0){this.ended||(this.settleOpen(this.chainState(n),{error:t}),this.retries++,Number.isFinite(e)&&e>0&&(this.retryDelayMs+=e))}fellBack(e,t,n=0){if(this.ended)return;let r=this.chainState(n);this.settleOpen(r,{error:t}),He(r.path,e,t)}queued(e){!this.ended&&Number.isFinite(e)&&e>0&&(this.queueWaitMs+=e)}setUsage(e,t,n=0){this.ended||this.usages.set(n,{usage:e,failed:t})}noteBreaker(e){this.ended||this.breaker.push(e)}noteMiddleware(e){this.ended||(e.kind===`wrap_short_circuit`&&(this.shortCircuitedBy=e.name),this.middleware.push(e))}addCustom(e,t){if(this.ended)return;let n=this.custom.get(e);n?n.push(t??null):this.custom.set(e,[t??null])}markStream(){this.ended||(this.streaming=!0)}finish(e){if(this.ended)return;this.ended=!0;let t=n(),i=e.kind===`error`?e.error:void 0,a=e.kind===`result`?e.result.meta?.chain??0:0,o=this.chains.get(a),s=o?.path??[],c=o?.current;for(let e of this.chains.values())e.current=void 0;c&&this.settle(c,e.kind===`error`?{error:V(i)}:void 0);let l=Ge({open:c,answered:e.kind===`result`,streaming:this.streaming,startedAtMs:this.startedAtMs,endedAtMs:t});Ue(s,t,{failed:e.kind===`error`,error:i});let u=e.kind===`result`?e.result.meta:void 0,d=z(e),f=Fe({requestId:this.ctx.requestId,endedAt:/* @__PURE__ */ new Date,classified:d,answered:u&&{provider:u.provider,model:u.model,fallbackIndex:u.fallbackIndex,finishReason:u.finishReason},attempts:this.attemptCount,retries:this.retries,streaming:this.streaming,cache:d.outcome===`ok`&&e.kind===`result`?e.result.cache:void 0,ttftMs:l.ttftMs,shortCircuitedBy:this.shortCircuitedBy,durationMs:r(this.startedAtMs,t),queueWaitMs:this.queueWaitMs,retryDelayMs:this.retryDelayMs,attemptMs:l.attemptMs,generationMs:l.generationMs,chains:this.chains.size>1?{count:this.chains.size,kept:a}:void 0,path:s.map(e=>We(e)),usage:this.usages.get(a)?.usage,usageFailed:this.usages.get(a)?.failed??!1,breaker:this.breaker,middleware:this.middleware,custom:this.custom,context:Ee(this.ctx.context,this.config.context),fields:De(this.config.enrich,this.ctx,this.guard)});return we(f,[...this.tallies.values()]),f}chainState(e){let t=this.chains.get(e);return t||(t={current:void 0,path:[]},this.chains.set(e,t)),t}settleOpen(e,t){let n=e.current;e.current=void 0,n&&this.settle(n,t)}settle(e,t){let n=t!==void 0&&Le(t.error);if(!(t===void 0||n||e.respondedAtMs!==void 0))return;let r=this.tallies.get(e.path);r||(r={provider:e.path.provider,model:e.path.model,responded:0,qualityFailures:0},this.tallies.set(e.path,r)),r.responded++,n&&r.qualityFailures++}};function qe(e){return e.snapshot().series.map(t=>{let n={kind:`summary`,v:1,series:t},r=t.metric===`durationMs`?e.exemplarOf(t.dimensions):void 0;return r&&(n.exemplar=r),n})}function Je(e){e.unref?.()}var Ye=class{deps;timer;disposed=!1;now;lastSummaryAtMs;constructor(e){this.deps=e,this.now=e.now??n,this.lastSummaryAtMs=this.now()}ensureStarted(){if(this.timer||this.disposed)return;let{durationMs:e,slots:t}=this.deps.config.window,n=Math.min(e/t,this.deps.config.summary?.everyMs??1/0);this.lastSummaryAtMs=this.now(),this.timer=setInterval(()=>this.tick(),n),Je(this.timer)}tick(){let{config:e,guard:t,groups:n,registry:r,write:i}=this.deps,{summary:a}=e;t(`flushErrorGroups`,()=>n.flush().forEach(e=>i(e,`error`)),void 0),!a||this.now()-this.lastSummaryAtMs<a.everyMs||(this.lastSummaryAtMs=this.now(),t(`summary`,()=>{let e=qe(r);if(e.length===0)return;e.forEach(e=>i(e,`info`));let{onSummary:n}=a;n&&t(`onSummary`,()=>n(e),void 0)},void 0))}dispose(){this.timer&&clearInterval(this.timer),this.timer=void 0,this.disposed=!0;let{guard:e,groups:t,write:n}=this.deps;t.close(),e(`disposeFlush`,()=>t.flush(!0).forEach(e=>n(e,`error`)),void 0)}};function Xe(e,t,n){let r=e.path[e.path.length-1],i=e.provider??r?.provider,a=e.model??r?.model;if(i===void 0||a===void 0)return;let o={provider:i,model:a,streaming:String(e.streaming)};for(let[r,i]of t){let t=n(`dimension.${r}`,()=>i(e),void 0);typeof t==`string`&&t!==``&&(o[r]=t.slice(0,128))}return o}const H=1.01/.99,Ze=Math.log(H),U=2048;var W=class{counts=/* @__PURE__ */ new Float64Array(16);offset=0;span=0;zeroCount=0;count=0;sum=0;min=1/0;max=-1/0;add(e,t=1){!Number.isFinite(e)||e<0||t<=0||(this.count+=t,this.sum+=e*t,e<this.min&&(this.min=e),e>this.max&&(this.max=e),e<=.001?this.zeroCount+=t:this.addToBucket(Math.ceil(Math.log(e)/Ze),t))}quantile(e){if(this.count===0||!(e>=0&&e<=1))return;let t=e*(this.count-1),n=this.zeroCount;if(n>t)return 0;for(let e=0;e<this.span;e++)if(n+=this.counts[e],n>t){let t=2*H**(this.offset+e)/2.02020202020202;return Math.min(this.max,Math.max(this.min,t))}
|
|
4
|
+
/* v8 ignore next */
|
|
5
|
+
return this.max}merge(e){if(e.count!==0){this.count+=e.count,this.sum+=e.sum,this.min=Math.min(this.min,e.min),this.max=Math.max(this.max,e.max),this.zeroCount+=e.zeroCount;for(let t=0;t<e.span;t++){let n=e.counts[t];n>0&&this.addToBucket(e.offset+t,n)}}}clear(){this.counts.fill(0),this.offset=0,this.span=0,this.zeroCount=0,this.count=0,this.sum=0,this.min=1/0,this.max=-1/0}addToBucket(e,t){this.span===0?(this.offset=e,this.span=1):e<this.offset?(this.offset+this.span-e>U&&(e=this.offset+this.span-U),e<this.offset?this.shiftDown(e):e=this.offset):e>=this.offset+this.span&&(e-this.offset+1>U&&this.collapseLowest(e-U+1),this.span=e-this.offset+1),this.ensureCapacity(this.span),this.counts[e-this.offset]=this.counts[e-this.offset]+t}shiftDown(e){let t=this.offset-e,n=new Float64Array(Math.max(this.counts.length,this.span+t));n.set(this.counts.subarray(0,this.span),t),this.counts=n,this.offset=e,this.span+=t}collapseLowest(e){let t=Math.min(e-this.offset,this.span),n=0;for(let e=0;e<t;e++)n+=this.counts[e];this.counts.copyWithin(0,t,this.span),this.counts.fill(0,this.span-t,this.span),this.span-=t,this.offset=e,this.span===0&&(this.span=1),this.counts[0]=this.counts[0]+n}ensureCapacity(e){if(e<=this.counts.length)return;let t=new Float64Array(Math.min(U,Math.max(e,this.counts.length*2)));t.set(this.counts),this.counts=t}},G=class{slots;reset;items;periods;slotMs;constructor(e,t,n,r){this.slots=e,this.reset=r,this.slotMs=t/e,this.items=Array.from({length:e},n),this.periods=Array(e).fill(-1/0)}periodAt(e){return Math.floor(e/this.slotMs)}at(e){let t=this.periodAt(e),n=(t%this.slots+this.slots)%this.slots;return this.periods[n]!==t&&(this.reset(this.items[n]),this.periods[n]=t),this.items[n]}live(e){let t=this.periodAt(e)-this.slots+1;return this.items.filter((e,n)=>this.periods[n]>=t)}clear(){this.items.forEach(e=>this.reset(e)),this.periods.fill(-1/0)}},Qe=class{cachedQuantileOf;ring;scratch=new W;cachedPeriod=-1/0;cachedValue;constructor(e,t,n){this.cachedQuantileOf=n,this.ring=new G(t,e,()=>({sketch:new W,maxTag:void 0}),e=>{e.sketch.clear(),e.maxTag=void 0})}add(e,t,n){let r=this.ring.at(e),i=r.sketch.max;r.sketch.add(t),n!==void 0&&r.sketch.max>i&&(r.maxTag=n)}count(e){let t=0;for(let n of this.ring.live(e))t+=n.sketch.count;return t}quantiles(e,t){return this.mergeLive(e),t.map(e=>this.scratch.quantile(e))}mergeInto(e,t){for(let n of this.ring.live(e))t.merge(n.sketch)}slowest(e){let t;for(let n of this.ring.live(e))n.maxTag!==void 0&&(!t||n.sketch.max>t.value)&&(t={tag:n.maxTag,value:n.sketch.max});return t}cachedQuantile(e){if(this.cachedQuantileOf===void 0)return;let t=this.ring.periodAt(e);return t!==this.cachedPeriod&&(this.cachedPeriod=t,this.mergeLive(e),this.cachedValue=this.scratch.quantile(this.cachedQuantileOf)),this.cachedValue}clear(){this.ring.clear(),this.cachedPeriod=-1/0,this.cachedValue=void 0}mergeLive(e){this.scratch.clear(),this.mergeInto(e,this.scratch)}};const K=`__other__`,q=()=>({errors:0,rejections:0,aborts:0,cacheHits:0}),J=()=>({responded:0,qualityFailures:0});function Y(e){return JSON.stringify(Object.entries(e).sort(([e],[t])=>e<t?-1:1))}function X(e,t){let{durationMs:n,slots:r}=e.window,i=/* @__PURE__ */ new Map;for(let t of D)i.set(t,new Qe(n,r,t===`durationMs`?e.slowCall.aboveLivePercentile:void 0));return{dimensions:t,series:i,counts:new G(r,n,q,e=>Object.assign(e,q())),quality:new G(r,n,J,e=>Object.assign(e,J()))}}function $e(e,t){let n=q();for(let r of e.counts.live(t))n.errors+=r.errors,n.rejections+=r.rejections,n.aborts+=r.aborts,n.cacheHits+=r.cacheHits;return n}function et(e,t){let n=J();for(let r of e.quality.live(t))n.responded+=r.responded,n.qualityFailures+=r.qualityFailures;return n}function Z(e,t,n){let r=e.quality.at(n);r.responded+=t.responded,r.qualityFailures+=t.qualityFailures}function Q(e,t,n,r){let i=e.counts.at(r);if(t.outcome===`error`?i.errors++:t.outcome===`rejected`?i.rejections++:t.outcome===`aborted`&&i.aborts++,t.cache!==void 0&&i.cacheHits++,!n)return;let a=(t,n,i)=>{n!==void 0&&e.series.get(t)?.add(r,n,i)};a(`durationMs`,t.durationMs,t),a(`queueWaitMs`,t.queueWaitMs),a(`ttftMs`,t.ttftMs),a(`attemptMs`,t.attemptMs),a(`outputTokensPerSec`,t.outputTokensPerSec),a(`outputTokens`,t.usage?.completionTokens)}function tt(e,t,n,r,i,a,o){let s={};if(i>=e.minSamples){let t=r.quantiles(o,e.percentiles);e.percentiles.forEach((e,n)=>{s[String(e)]=P(t[n])})}return{metric:t,dimensions:{...n.dimensions},count:i,quantiles:s,...a}}function nt(e,t,n){let r=[];for(let i of t){let t=$e(i,n),a=t.errors+t.rejections+t.aborts+t.cacheHits>0,o=!1;for(let a of D){let s=i.series.get(a),c=s.count(n);c!==0&&(o=!0,r.push(tt(e,a,i,s,c,t,n)))}!o&&a&&r.push({metric:`durationMs`,dimensions:{...i.dimensions},count:0,quantiles:{},...t})}return{windowMs:e.window.durationMs,series:r}}var rt=class{config;guard;now;groups=/* @__PURE__ */ new Map;everything;scratch=new W;memo;constructor(e,t,r=n){this.config=e,this.guard=t,this.now=r,this.everything=X(e,{})}observe(e){let t=this.now(),n=this.dimensionsMemo(e),r=O(e);Q(this.everything,e,r,t),n&&Q(this.groupFor(n),e,r,t),this.observeAttempts(e,t)}failureRate(e,t){if(t!==`quality`)return;let n=this.now(),r=0,i=0;for(let t of this.matching(e)){let e=et(t,n);r+=e.responded,i+=e.qualityFailures}if(!(r<this.config.minSamples))return i/r}quantile(e,t,n=`durationMs`){let r=this.now();this.scratch.clear();for(let t of this.matching(e))t.series.get(n)?.mergeInto(r,this.scratch);if(!(this.scratch.count<this.config.minSamples))return this.scratch.quantile(t)}exemplarOf(e){let t=(Object.keys(e).length===0?this.everything:this.groups.get(`__other__`in e?K:Y(e)))?.series.get(`durationMs`)?.slowest(this.now());if(!t)return;let n={requestId:t.tag.requestId,durationMs:P(t.value)},{context:r}=t.tag;return r&&Object.keys(r).length>0&&(n.fields={...r}),n}livePercentileFor(e){let t=this.now(),n=this.dimensionsMemo(e),r=(n&&this.groups.get(Y(n))||this.everything).series.get(`durationMs`);if(!(r.count(t)<this.config.minSamples))return r.cachedQuantile(t)}snapshot(){return nt(this.config,[this.everything,...this.groups.values()],this.now())}reset(){this.everything.series.forEach(e=>e.clear()),this.everything.counts.clear(),this.everything.quality.clear(),this.groups.clear(),this.memo=void 0}observeAttempts(e,t){let n=Te(e);if(!n)return;let r=this.dimensionsMemo(e);for(let e of n)if(Z(this.everything,e,t),r){let n={...r,provider:e.provider,model:e.model};Z(this.groupFor(n),e,t)}}matching(e){let t=Object.keys(e);return t.length===0?[this.everything]:[...this.groups.values()].filter(n=>t.every(t=>n.dimensions[t]===e[t]))}dimensionsMemo(e){if(this.memo?.record===e)return this.memo.dimensions;let t=Xe(e,this.config.dimensions,this.guard);return this.memo={record:e,dimensions:t},t}groupFor(e){let t=Y(e),n=this.groups.get(t);if(n)return n;if(this.groups.size>=this.config.maxSeries){let e=this.groups.get(K);if(e)return e;this.guard.warn(`more than ${this.config.maxSeries} distinct series, the rest are grouped as ${K}`);let t=X(this.config,{[K]:`true`});return this.groups.set(K,t),t}let r=X(this.config,e);return this.groups.set(t,r),r}};function it(e){return{snapshot:()=>e.snapshot(),quantile:(t,n,r)=>e.quantile(t,n,r),failureRate:(t,n)=>e.failureRate(t,n),reset:()=>e.reset()}}const $=(0,e.createMiddlewareRef)(`logger`);function at(t){let n=Se(t),r=a(n.logger),c=new rt(n,r),l=new i(n.window.durationMs),u=j(n,r,c,l),d=new Ye({config:n,guard:r,registry:c,groups:l,write:(e,t)=>u(e,t)}),f=(0,e.createStateKey)(`logger.recorder`),{finish:p,observeStream:m}=s(r,e=>u(e));return{stats:it(c),dispose:()=>d.dispose(),name:n.name,ref:$,position:`outermost`,wrap:async(t,i,a)=>{r(`scheduler.start`,()=>d.ensureStarted(),void 0);let o=r(`startCall`,()=>Ke.start(a,n,r),void 0);if(!o)return i();try{a.state.set(f,o)}catch(e){return r.report(`registerRecorder`,e),i()}let s;try{s=await i()}catch(e){throw p(o,{kind:`error`,error:e}),e}return(0,e.isStreamResult)(s.value)?(r(`markStream`,()=>o.markStream(),void 0),m(o,s,s.value),s):(p(o,{kind:`result`,result:s}),s)},dispatch:async(e,t,n)=>{let i=r(`readRecorder`,()=>n.state.get(f),void 0);if(!i)return t();r(`openAttempt`,()=>i.openAttempt(n),void 0);try{await t()}catch(e){throw r(`attemptFailed`,()=>i.attemptFailed(e,n.chain),void 0),e}r(`responded`,()=>i.responded(n.chain),void 0)},onEvent:(e,t)=>{r(`handleEvent`,()=>{let r=t?.state.get(f);if(r)Ce(r,e,n.keepCustomEvent);else if(e.kind===`circuit_state`){let{entry:t,level:n}=o(e);u(t,n)}},void 0)}}}exports.consoleSink=me,exports.fromEvent=t,exports.jsonSink=x,exports.loggerMiddleware=at,exports.loggerMiddlewareRef=$,exports.memorySink=S;
|
|
6
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["ConsoleLogger","createMiddlewareRef","createStateKey","isStreamResult","vern_llm","fromEvent","events","record","name","last","value","field","nowMs","elapsedMs","startMs","endMs","ErrorGrouper","windowMs","now","provider","key","type","code","group","entries","force","entry","createGuard","logger","option","report","operation","error","guard","fn","fallback","message","toBreakerEntry","event","createCallFinisher","finish","recorder","outcome","emit","stream","result","fail","isPlainObject","optionalBoolean","optionalFunction","optionalObject","positiveNumber","positiveInteger","quantile","stringList","item","DEFAULT_CUSTOM_EVENTS","PATTERN","normalizeCustomEvents","names","defaults","pattern","patterns","exact","prefixes","prefix","normalizeSampleSuccess","success","normalizeErrorGrouping","normalizeName","normalizeLogger","RESERVED_DIMENSIONS","normalizeContext","normalizeDimensions","extractor","normalizeEnrich","list","index","seconds","ms","errorLabel","describeCall","target","parts","describeErrorGroup","describeBreaker","describeSummary","dims","series","p99","tail","describeEntry","jsonSink","write","options","line","consoleSink","level","memorySink","LEVELS","normalizeSink","sink","normalizeLevel","DEFAULT_PERCENTILES","DEFAULT_WINDOW_MS","MIN_SLOT_MS","normalizePercentiles","checked","i","normalizeWindow","slots","durationMs","normalizeSlowCall","thresholdMs","aboveLivePercentile","normalizeSummary","everyMs","normalizeOptions","opts","isSlow","slowCall","live","livePercentile","METRICS","isTimed","shouldKeep","rate","hash","requestId","LEVEL_RANK","createFinalize","reportedRejection","minimum","config","pending","callPipeline","registry","original","groups","applyEvent","note","keepCustomEvent","tallies","attachTallies","talliesOf","pickContext","context","allowed","picked","runEnrichers","enrichers","merged","fields","enrich","ctx","isCount","pickUsage","usage","byTtl","ttl","tokens","roundMs","outputTokensPerSec","completionTokens","attemptMs","frozenCopies","items","frozenCustom","copy","values","custom","baseRecord","input","addAnswer","answered","addUsage","addNotes","buildRecord","isLLMErrorLike","candidate","isNonEmptyString","statusOf","status","REJECTED_CODES","classifyOutcome","summary","errorCodeOf","lastAttemptErrorOf","snapshot","isQualityFailure","LOCAL_REJECTION_CODES","isLocalRejection","enterTarget","path","leaveTarget","attempt","closeTargetAsFailed","elapsedMsOnTarget","closeLastTarget","endedAtMs","failure","pathEntryOf","timingsOf","respondedAtMs","open","generationMs","streaming","ttftMs","startedAtMs","CallRecorder","chain","state","delayMs","waitedMs","change","data","kept","keptState","keptPath","timings","meta","classified","failedQuality","tally","buildSummary","exemplar","unrefTimer","timer","Scheduler","deps","period","onSummary","dimensionsOf","model","dimensions","extractors","GAMMA","LN_GAMMA","MAX_BUCKETS","Sketch","weight","q","rank","running","estimate","other","grow","newOffset","next","drop","folded","needed","Ring","reset","make","oldest","RollingSketch","cachedQuantileOf","slot","before","tag","total","qs","best","OTHER","emptyCounts","emptyQuality","keyOf","a","b","makeGroup","metric","totalsOf","qualityTotalsOf","recordQuality","recordInto","counts","timed","add","seriesOf","quantiles","count","rolling","snapshotOf","counted","emitted","SeriesRegistry","kind","responded","failures","filter","totals","slowest","base","keys","existing","overflow","created","createStatsApi","loggerMiddlewareRef","loggerMiddleware","finalize","scheduler","recorderKey","observeStream"],"sources":["../src/internal/stats/fromEvent.utils.ts","../src/internal/clock.utils.ts","../src/internal/detectors/errorGrouper.ts","../src/internal/guard.utils.ts","../src/internal/middleware/breakerEntry.utils.ts","../src/internal/middleware/callFinisher.utils.ts","../src/internal/options/validate.utils.ts","../src/internal/options/customEventsOptions.utils.ts","../src/internal/options/pipelineOptions.utils.ts","../src/internal/options/recordOptions.utils.ts","../src/internal/sinks/describeEntry.utils.ts","../src/sinks.ts","../src/internal/options/sinkOptions.utils.ts","../src/internal/options/statsOptions.utils.ts","../src/internal/options/normalizeOptions.utils.ts","../src/internal/detectors/slowCall.utils.ts","../src/internal/stats/metrics.utils.ts","../src/internal/pipeline/sample.utils.ts","../src/internal/pipeline/finalize.ts","../src/internal/recorder/applyEvent.utils.ts","../src/internal/stats/attemptTally.utils.ts","../src/internal/recorder/inputs.utils.ts","../src/internal/recorder/recordMetrics.utils.ts","../src/internal/recorder/buildRecord.utils.ts","../src/internal/recorder/outcome.utils.ts","../src/internal/recorder/callPath.utils.ts","../src/internal/recorder/callTimings.utils.ts","../src/internal/recorder/callRecorder.ts","../src/internal/summary.utils.ts","../src/internal/timers.utils.ts","../src/internal/scheduler.ts","../src/internal/stats/dimensions.utils.ts","../src/internal/stats/sketch.ts","../src/internal/stats/rollingSketch.ts","../src/internal/stats/group.utils.ts","../src/internal/stats/snapshot.utils.ts","../src/internal/stats/seriesRegistry.ts","../src/internal/stats/statsApi.utils.ts","../src/loggerMiddleware.ts"],"sourcesContent":["import type { DimensionExtractor } from '../../types/index.js';\n\n/** A dimension read from `field` of the last kept custom event called `name`. */\nexport function fromEvent(name: string, field: string): DimensionExtractor {\n return (record) => {\n const events = record.custom?.[name];\n const last = events?.[events.length - 1];\n if (typeof last !== 'object' || last === null || Array.isArray(last)) return undefined;\n\n const value = last[field];\n return typeof value === 'string' ? value : undefined;\n };\n}\n","/** Monotonic clock, so a wall clock adjustment can never produce a negative duration. */\nexport function nowMs(): number {\n return performance.now();\n}\n\n/** Milliseconds from `startMs` to `endMs`. Never negative and never NaN. */\nexport function elapsedMs(startMs: number, endMs: number = nowMs()): number {\n if (!Number.isFinite(startMs) || !Number.isFinite(endMs)) return 0;\n return Math.max(0, endMs - startMs);\n}\n","import { nowMs } from '../clock.utils.js';\n\nimport type { CallLogRecord, ErrorGroupEntry } from '../../types/index.js';\n\nconst MAX_FINGERPRINTS = 500;\n\ninterface Group {\n provider: string;\n type: string;\n code: string | undefined;\n startedAtMs: number;\n /** Errors after the first, which was let through on its own. */\n suppressed: number;\n firstRequestId: string;\n lastRequestId: string;\n}\n\n/** Collapses a burst of one failure into its first record plus one `error_group`. */\nexport class ErrorGrouper {\n private readonly groups = new Map<string, Group>();\n /** Closed windows found by `offer`, waiting for the next flush. */\n private readonly pending: ErrorGroupEntry[] = [];\n private closed = false;\n\n constructor(\n private readonly windowMs: number,\n private readonly now: () => number = nowMs,\n ) {}\n\n /** `true` when the record is written on its own, `false` when it was counted. */\n offer(record: Readonly<CallLogRecord>): boolean {\n if (!record.error || this.closed) return true;\n\n const now = this.now();\n const provider = record.provider ?? record.path[record.path.length - 1]?.provider ?? 'unknown';\n const { type, code } = record.error;\n const key = JSON.stringify([provider, type, code ?? null]);\n\n const group = this.groups.get(key);\n if (group && now - group.startedAtMs < this.windowMs) {\n group.suppressed++;\n group.lastRequestId = record.requestId;\n return false;\n }\n\n // A window that has closed is reported before the next one starts under the same key.\n if (group) this.pending.push(...this.entryOf(group));\n if (!group && this.groups.size >= MAX_FINGERPRINTS) return true;\n\n this.groups.set(key, {\n provider,\n type,\n code,\n startedAtMs: now,\n suppressed: 0,\n firstRequestId: record.requestId,\n lastRequestId: record.requestId,\n });\n return true;\n }\n\n /** Entries for windows that have closed, or for every window when `force` is set. */\n flush(force = false): ErrorGroupEntry[] {\n const now = this.now();\n const entries = this.pending.splice(0);\n\n for (const [key, group] of this.groups) {\n if (!force && now - group.startedAtMs < this.windowMs) continue;\n entries.push(...this.entryOf(group));\n this.groups.delete(key);\n }\n return entries;\n }\n\n /** After this every error passes on its own: nothing is left to flush a suppressed one. */\n close(): void {\n this.closed = true;\n }\n\n clear(): void {\n this.groups.clear();\n this.pending.length = 0;\n }\n\n private entryOf(group: Group): ErrorGroupEntry[] {\n if (group.suppressed === 0) return [];\n\n const entry: ErrorGroupEntry = {\n kind: 'error_group',\n v: 1,\n provider: group.provider,\n type: group.type,\n count: group.suppressed + 1,\n windowMs: this.windowMs,\n firstRequestId: group.firstRequestId,\n lastRequestId: group.lastRequestId,\n };\n if (group.code !== undefined) entry.code = group.code;\n return [entry];\n }\n}\n","import { ConsoleLogger, type Logger } from 'vern-llm';\n\n/** Same shape as `VernLLMOptions.logger`. */\nexport type LoggerOption = Logger | 'silent';\n\n/** Runs `fn`, and returns `fallback` after logging if it throws. */\nexport interface Guard {\n <T>(operation: string, fn: () => T, fallback: T): T;\n /** Logs a failure in the same shape without running anything. */\n report(operation: string, error: unknown): void;\n /** Logs a configuration warning, not a failure. */\n warn(message: string): void;\n}\n\nexport function createGuard(option: LoggerOption | undefined): Guard {\n const logger: Logger | undefined =\n option === 'silent' ? undefined : (option ?? new ConsoleLogger(false));\n\n const report = (operation: string, error: unknown): void => {\n if (!logger) return;\n\n try {\n logger.error(`[VernLLM] logger: ${operation} failed`, {\n message: error instanceof Error ? error.message : String(error),\n stack: error instanceof Error ? error.stack : undefined,\n });\n } catch {\n // A logger that throws must not be able to break a call.\n }\n };\n\n const guard = (<T>(operation: string, fn: () => T, fallback: T): T => {\n try {\n return fn();\n } catch (error) {\n report(operation, error);\n return fallback;\n }\n }) as Guard;\n\n guard.report = report;\n guard.warn = (message) => {\n if (!logger) return;\n try {\n logger.warn(`[VernLLM] logger: ${message}`);\n } catch {\n // Same as `report`: a throwing logger must not break a call.\n }\n };\n return guard;\n}\n","import type { BreakerEntry, LogLevel } from '../../types/index.js';\n\n/** The circuit state event, as far as an entry needs it. */\ninterface CircuitStateEvent {\n provider: string;\n model: string;\n from: string;\n to: string;\n consecutiveFailures: number;\n}\n\n/** A breaker change outside any call has no record to join, so it is written on its own. */\nexport function toBreakerEntry(event: CircuitStateEvent): { entry: BreakerEntry; level: LogLevel } {\n return {\n entry: {\n kind: 'breaker',\n v: 1,\n provider: event.provider,\n model: event.model,\n from: event.from,\n to: event.to,\n consecutiveFailures: event.consecutiveFailures,\n },\n level: event.to === 'open' ? 'warn' : 'info',\n };\n}\n","import type { CallLogRecord } from '../../types/index.js';\nimport type { Guard } from '../guard.utils.js';\nimport type { CallRecorder } from '../recorder/callRecorder.js';\nimport type { CallResult } from 'vern-llm';\n\ntype Outcome = Parameters<CallRecorder['finish']>[0];\n\nexport interface CallFinisher {\n /** Closes the recorder and hands its record on. Never throws. */\n finish(recorder: CallRecorder, outcome: Outcome): void;\n /** Writes the record when the stream settles. The stream itself is only observed. */\n observeStream(\n recorder: CallRecorder,\n result: CallResult,\n stream: { finalResult: PromiseLike<unknown> },\n ): void;\n}\n\nexport function createCallFinisher(\n guard: Guard,\n emit: (record: CallLogRecord) => void,\n): CallFinisher {\n const finish: CallFinisher['finish'] = (recorder, outcome) => {\n const record = guard('finish', () => recorder.finish(outcome), undefined);\n if (record) emit(record);\n };\n\n const observeStream: CallFinisher['observeStream'] = (recorder, result, stream) => {\n try {\n void stream.finalResult.then(\n (value) =>\n finish(recorder, {\n kind: 'result',\n result: { value, meta: result.meta, cache: result.cache },\n }),\n (error: unknown) => finish(recorder, { kind: 'error', error }),\n );\n } catch (error) {\n guard.report('observeStream', error);\n // Nothing will ever settle this call, so record it as it stands rather than lose it.\n finish(recorder, { kind: 'result', result });\n }\n };\n\n return { finish, observeStream };\n}\n","const PREFIX = 'loggerMiddleware';\n\nexport function fail(message: string): never {\n throw new Error(`${PREFIX}: ${message}`);\n}\n\n// A plain boolean on purpose: as a type predicate it narrows the typed options to `{}`.\nexport function isPlainObject(value: unknown): boolean {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nexport function optionalBoolean(value: unknown, name: string): void {\n if (value !== undefined && typeof value !== 'boolean') fail(`${name} must be a boolean`);\n}\n\nexport function optionalFunction(value: unknown, name: string): void {\n if (value !== undefined && typeof value !== 'function') fail(`${name} must be a function`);\n}\n\nexport function optionalObject(value: unknown, name: string): void {\n if (value !== undefined && !isPlainObject(value)) fail(`${name} must be an object`);\n}\n\n/** A finite number strictly greater than zero, or `fallback` when absent. */\nexport function positiveNumber(value: unknown, name: string, fallback: number): number;\nexport function positiveNumber(\n value: unknown,\n name: string,\n fallback?: undefined,\n): number | undefined;\nexport function positiveNumber(\n value: unknown,\n name: string,\n fallback?: number,\n): number | undefined {\n if (value === undefined) return fallback;\n if (typeof value !== 'number' || !Number.isFinite(value) || value <= 0) {\n fail(`${name} must be a positive number`);\n }\n return value;\n}\n\nexport function positiveInteger(value: unknown, name: string, fallback: number): number {\n if (value === undefined) return fallback;\n if (typeof value !== 'number' || !Number.isInteger(value) || value <= 0) {\n fail(`${name} must be a positive integer`);\n }\n return value;\n}\n\n/** A number strictly between 0 and 1, the range a quantile can take. */\nexport function quantile(value: unknown, name: string): number {\n if (typeof value !== 'number' || !Number.isFinite(value) || value <= 0 || value >= 1) {\n fail(`${name} must be a number between 0 and 1, exclusive`);\n }\n return value;\n}\n\nexport function stringList(value: unknown, name: string): string[] {\n if (!Array.isArray(value)) fail(`${name} must be an array of strings`);\n for (const item of value) {\n if (typeof item !== 'string' || item.trim() === '') {\n fail(`${name} must contain only non empty strings`);\n }\n }\n return [...new Set(value as string[])];\n}\n","import { fail, isPlainObject, optionalBoolean, stringList } from './validate.utils.js';\n\nimport type { CustomEventsOptions } from '../../types/index.js';\n\n/** First party extensions show up with no configuration. */\nexport const DEFAULT_CUSTOM_EVENTS: readonly string[] = [\n 'usage.*',\n 'policy.*',\n 'router.*',\n 'guard.*',\n 'hedge.*',\n];\n\n// A plain name (`myapp.cache_warm`) or a prefix pattern (`myapp.*`). Nothing fancier, so a\n// pattern can never become an expensive regular expression.\nconst PATTERN = /^[A-Za-z0-9_-]+(\\.[A-Za-z0-9_-]+)*(\\.\\*)?$/;\n\n/** Compiles the patterns once into a predicate. */\nexport function normalizeCustomEvents(\n option: string[] | CustomEventsOptions | undefined,\n): (name: string) => boolean {\n let names: string[] = [];\n let defaults = true;\n\n if (Array.isArray(option)) {\n names = stringList(option, 'customEvents');\n } else if (option !== undefined) {\n if (!isPlainObject(option)) fail('customEvents must be an array or an object');\n optionalBoolean(option.defaults, 'customEvents.defaults');\n defaults = option.defaults ?? true;\n if (option.names !== undefined) names = stringList(option.names, 'customEvents.names');\n }\n\n for (const pattern of names) {\n if (!PATTERN.test(pattern)) {\n fail(`customEvents pattern \"${pattern}\" must be a plain name or \"prefix.*\"`);\n }\n }\n\n const patterns = defaults ? [...DEFAULT_CUSTOM_EVENTS, ...names] : names;\n const exact = new Set<string>();\n const prefixes: string[] = [];\n\n for (const pattern of patterns) {\n if (pattern.endsWith('.*')) prefixes.push(pattern.slice(0, -1));\n else exact.add(pattern);\n }\n\n return (name) => exact.has(name) || prefixes.some((prefix) => name.startsWith(prefix));\n}\n","import { fail, optionalBoolean, optionalObject } from './validate.utils.js';\n\nimport type { LoggerMiddlewareOptions } from '../../types/index.js';\n\nexport const DEFAULT_NAME = 'logger';\n\n/** The share of plain successes kept, from 0 to 1. Everything else is always kept. */\nexport function normalizeSampleSuccess(value: LoggerMiddlewareOptions['sample']): number {\n optionalObject(value, 'sample');\n const success = value?.success === undefined ? 1 : value.success;\n if (typeof success !== 'number' || !(success >= 0 && success <= 1)) {\n fail('sample.success must be a number from 0 to 1');\n }\n return success;\n}\n\nexport function normalizeErrorGrouping(value: boolean | undefined): boolean {\n optionalBoolean(value, 'errorGrouping');\n return value ?? true;\n}\n\nexport function normalizeName(value: string | undefined): string {\n if (value !== undefined && (typeof value !== 'string' || value.trim() === '')) {\n fail('name must be a non-empty string');\n }\n return value ?? DEFAULT_NAME;\n}\n\nexport function normalizeLogger(\n value: LoggerMiddlewareOptions['logger'],\n): LoggerMiddlewareOptions['logger'] {\n if (value !== undefined && value !== 'silent' && (typeof value !== 'object' || value === null)) {\n fail(\"logger must be a Logger or 'silent'\");\n }\n return value;\n}\n","import { fail, isPlainObject, stringList } from './validate.utils.js';\n\nimport type { DimensionExtractor, Enricher, LoggerMiddlewareOptions } from '../../types/index.js';\n\n// Series are keyed by these, so a user dimension with the same name would silently collide.\nconst RESERVED_DIMENSIONS: ReadonlySet<string> = new Set([\n 'metric',\n 'provider',\n 'model',\n 'streaming',\n]);\n\nexport function normalizeContext(value: string[] | undefined): readonly string[] {\n return Object.freeze(value === undefined ? [] : stringList(value, 'context'));\n}\n\nexport function normalizeDimensions(\n value: LoggerMiddlewareOptions['dimensions'],\n): ReadonlyMap<string, DimensionExtractor> {\n const result = new Map<string, DimensionExtractor>();\n if (value === undefined) return result;\n if (!isPlainObject(value)) fail('dimensions must be an object');\n\n for (const [name, extractor] of Object.entries(value)) {\n if (name.trim() === '') fail('dimensions names must be non empty');\n if (RESERVED_DIMENSIONS.has(name)) fail(`dimensions.${name} is a reserved name`);\n if (typeof extractor !== 'function') fail(`dimensions.${name} must be a function`);\n result.set(name, extractor);\n }\n return result;\n}\n\nexport function normalizeEnrich(value: LoggerMiddlewareOptions['enrich']): readonly Enricher[] {\n if (value === undefined) return Object.freeze([]);\n\n const list = Array.isArray(value) ? value : [value];\n list.forEach((item, index) => {\n if (typeof item !== 'function') {\n fail(`enrich${Array.isArray(value) ? `[${index}]` : ''} must be a function`);\n }\n });\n return Object.freeze([...list]);\n}\n","import type {\n BreakerEntry,\n CallLogRecord,\n ErrorGroupEntry,\n LogEntry,\n SummaryEntry,\n} from '../../types/index.js';\n\nconst seconds = (ms: number): string =>\n ms >= 1000 ? `${(ms / 1000).toFixed(2)}s` : `${Math.round(ms)}ms`;\n\n/** `type`, or `type/code` when the error has a code. */\nconst errorLabel = ({ type, code }: { type: string; code?: string | undefined }): string =>\n code ? `${type}/${code}` : type;\n\nfunction describeCall(entry: CallLogRecord): string {\n const target = entry.provider ? ` ${entry.provider}/${entry.model}` : '';\n const parts = [`${entry.outcome}${target}`, seconds(entry.durationMs)];\n if (entry.ttftMs !== undefined) parts.push(`ttft ${seconds(entry.ttftMs)}`);\n if (entry.cache) parts.push(`cache ${entry.cache}`);\n else if (entry.attempts !== 1) parts.push(`${entry.attempts} attempts`);\n if (entry.reason) parts.push(entry.reason);\n if (entry.error) parts.push(errorLabel(entry.error));\n parts.push(entry.requestId);\n return parts.join(' ');\n}\n\nfunction describeErrorGroup(entry: ErrorGroupEntry): string {\n return `error_group ${entry.provider} ${errorLabel(entry)} x${entry.count} in ${seconds(entry.windowMs)}`;\n}\n\nfunction describeBreaker(entry: BreakerEntry): string {\n return `breaker ${entry.provider}/${entry.model} ${entry.from} -> ${entry.to}`;\n}\n\nfunction describeSummary({ series }: SummaryEntry): string {\n const dims = Object.values(series.dimensions).join('/') || 'all';\n const p99 = series.quantiles['0.99'];\n const tail = p99 === undefined ? '' : ` p99 ${seconds(p99)}`;\n return `summary ${series.metric} ${dims} n=${series.count}${tail} errors ${series.errors}`;\n}\n\n/** One readable line for an entry, without the sink's own prefix. */\nexport function describeEntry(entry: LogEntry): string {\n switch (entry.kind) {\n case 'call':\n return describeCall(entry);\n case 'error_group':\n return describeErrorGroup(entry);\n case 'breaker':\n return describeBreaker(entry);\n case 'summary':\n return describeSummary(entry);\n }\n}\n","import { describeEntry } from './internal/sinks/describeEntry.utils.js';\n\nimport type { LogEntry, LogLevel, LogSink } from './types/index.js';\n\nexport interface JsonSinkOptions {\n /** Receives one JSON line per entry. Default `console.log`, which works on every runtime. */\n write?: (line: string) => void;\n}\n\n/** One JSON line per entry, ready for any log shipper. */\nexport function jsonSink(options: JsonSinkOptions = {}): LogSink {\n const write = options.write ?? ((line: string) => console.log(line));\n return { write: (_level, entry) => write(JSON.stringify(entry)) };\n}\n\nexport interface ConsoleSinkOptions {\n /** Receives one readable line. Default is the `console` method for the level. */\n write?: (line: string, level: LogLevel) => void;\n}\n\n/** One readable line per entry, for a terminal. */\nexport function consoleSink(options: ConsoleSinkOptions = {}): LogSink {\n const write =\n options.write ??\n ((line: string, level: LogLevel) => {\n const method = level === 'info' ? 'log' : level;\n console[method](line);\n });\n return { write: (level, entry) => write(`[VernLLM] ${describeEntry(entry)}`, level) };\n}\n\nexport interface MemorySink extends LogSink {\n /** Everything written so far, in order, with the level it was written at. */\n readonly entries: ReadonlyArray<{ level: LogLevel; entry: LogEntry }>;\n clear(): void;\n}\n\n/** Keeps entries in an array. The main tool in tests. */\nexport function memorySink(): MemorySink {\n const entries: Array<{ level: LogLevel; entry: LogEntry }> = [];\n return {\n entries,\n write: (level, entry) => {\n entries.push({ level, entry });\n },\n clear: () => {\n entries.length = 0;\n },\n };\n}\n","import { jsonSink } from '../../sinks.js';\nimport { fail, isPlainObject } from './validate.utils.js';\n\nimport type { LoggerMiddlewareOptions, LogLevel, LogSink } from '../../types/index.js';\n\nconst LEVELS: readonly LogLevel[] = ['debug', 'info', 'warn', 'error'];\n\nexport function normalizeSink(value: LoggerMiddlewareOptions['sink']): LogSink {\n // Not `??`: an explicit `null` is a mistake to report, not a request for the default.\n const sink = value === undefined ? jsonSink() : value;\n if (!isPlainObject(sink) || typeof sink.write !== 'function') {\n fail('sink must be an object with a write function');\n }\n return sink;\n}\n\nexport function normalizeLevel(value: LoggerMiddlewareOptions['level']): LogLevel {\n const level = value === undefined ? 'info' : value;\n if (!LEVELS.includes(level)) fail(`level must be one of ${LEVELS.join(', ')}`);\n return level;\n}\n","import {\n fail,\n isPlainObject,\n optionalFunction,\n optionalObject,\n positiveInteger,\n positiveNumber,\n quantile,\n} from './validate.utils.js';\n\nimport type { LoggerMiddlewareOptions, ResolvedConfig } from '../../types/index.js';\n\nconst DEFAULT_PERCENTILES: readonly number[] = [0.5, 0.9, 0.95, 0.99];\nconst DEFAULT_WINDOW_MS = 60_000;\nconst DEFAULT_SLOTS = 6;\nconst MIN_SLOT_MS = 1000;\n\nexport function normalizePercentiles(value: number[] | undefined): readonly number[] {\n if (value === undefined) return DEFAULT_PERCENTILES;\n if (!Array.isArray(value) || value.length === 0) fail('percentiles must be a non empty array');\n\n const checked = value.map((item, index) => quantile(item, `percentiles[${index}]`));\n for (let i = 1; i < checked.length; i++) {\n if ((checked[i] as number) <= (checked[i - 1] as number)) {\n fail('percentiles must be sorted ascending and unique');\n }\n }\n return Object.freeze(checked);\n}\n\nexport function normalizeWindow(\n value: LoggerMiddlewareOptions['window'],\n): ResolvedConfig['window'] {\n optionalObject(value, 'window');\n const slots = positiveInteger(value?.slots, 'window.slots', DEFAULT_SLOTS);\n const durationMs = positiveNumber(value?.durationMs, 'window.durationMs', DEFAULT_WINDOW_MS);\n if (slots < 2 || slots > 60) fail('window.slots must be an integer from 2 to 60');\n if (durationMs / slots < MIN_SLOT_MS) {\n fail(`window.durationMs divided by window.slots must be at least ${MIN_SLOT_MS} ms`);\n }\n return Object.freeze({ durationMs, slots });\n}\n\nexport function normalizeSlowCall(\n value: LoggerMiddlewareOptions['slowCall'],\n): ResolvedConfig['slowCall'] {\n optionalObject(value, 'slowCall');\n const thresholdMs = positiveNumber(value?.thresholdMs, 'slowCall.thresholdMs');\n const aboveLivePercentile =\n value?.aboveLivePercentile === undefined\n ? undefined\n : quantile(value.aboveLivePercentile, 'slowCall.aboveLivePercentile');\n return Object.freeze({ thresholdMs, aboveLivePercentile });\n}\n\nexport function normalizeSummary(\n value: LoggerMiddlewareOptions['summary'],\n): ResolvedConfig['summary'] {\n if (value === undefined) return undefined;\n if (!isPlainObject(value)) fail('summary must be an object');\n\n optionalFunction(value.onSummary, 'summary.onSummary');\n const everyMs = positiveNumber(value.everyMs, 'summary.everyMs', DEFAULT_WINDOW_MS);\n if (everyMs < MIN_SLOT_MS) fail(`summary.everyMs must be at least ${MIN_SLOT_MS} ms`);\n\n return Object.freeze({ everyMs, onSummary: value.onSummary });\n}\n","import { normalizeCustomEvents } from './customEventsOptions.utils.js';\nimport {\n normalizeErrorGrouping,\n normalizeLogger,\n normalizeName,\n normalizeSampleSuccess,\n} from './pipelineOptions.utils.js';\nimport { normalizeContext, normalizeDimensions, normalizeEnrich } from './recordOptions.utils.js';\nimport { normalizeLevel, normalizeSink } from './sinkOptions.utils.js';\nimport {\n normalizePercentiles,\n normalizeSlowCall,\n normalizeSummary,\n normalizeWindow,\n} from './statsOptions.utils.js';\nimport { fail, isPlainObject, positiveInteger } from './validate.utils.js';\n\nimport type { LoggerMiddlewareOptions, ResolvedConfig } from '../../types/index.js';\n\n/** Validates every option once and applies the defaults, throwing on the first bad value. */\nexport function normalizeOptions(options: LoggerMiddlewareOptions | undefined): ResolvedConfig {\n const opts: LoggerMiddlewareOptions = options === undefined ? {} : options;\n if (!isPlainObject(opts)) fail('options must be an object');\n\n return Object.freeze({\n sink: normalizeSink(opts.sink),\n level: normalizeLevel(opts.level),\n percentiles: normalizePercentiles(opts.percentiles),\n window: normalizeWindow(opts.window),\n slowCall: normalizeSlowCall(opts.slowCall),\n minSamples: positiveInteger(opts.minSamples, 'minSamples', 20),\n maxSeries: positiveInteger(opts.maxSeries, 'maxSeries', 200),\n sampleSuccess: normalizeSampleSuccess(opts.sample),\n errorGrouping: normalizeErrorGrouping(opts.errorGrouping),\n context: normalizeContext(opts.context),\n keepCustomEvent: normalizeCustomEvents(opts.customEvents),\n dimensions: normalizeDimensions(opts.dimensions),\n enrich: normalizeEnrich(opts.enrich),\n summary: normalizeSummary(opts.summary),\n name: normalizeName(opts.name),\n logger: normalizeLogger(opts.logger),\n });\n}\n","import type { CallLogRecord, ResolvedConfig } from '../../types/index.js';\n\n/** Whether a timed call is over the threshold or its series' live percentile. */\nexport function isSlow(\n record: Readonly<CallLogRecord>,\n slowCall: ResolvedConfig['slowCall'],\n livePercentile: () => number | undefined,\n): boolean {\n if (slowCall.thresholdMs !== undefined && record.durationMs > slowCall.thresholdMs) return true;\n if (slowCall.aboveLivePercentile === undefined) return false;\n\n const live = livePercentile();\n return live !== undefined && record.durationMs > live;\n}\n","import type { CallLogRecord, MetricName } from '../../types/index.js';\n\nexport const METRICS: readonly MetricName[] = [\n 'durationMs',\n 'ttftMs',\n 'attemptMs',\n 'queueWaitMs',\n 'outputTokensPerSec',\n 'outputTokens',\n];\n\n/** A successful call that went to a provider: the only kind whose latency is a latency. */\nexport function isTimed(record: Readonly<CallLogRecord>): boolean {\n return (\n record.outcome === 'ok' &&\n record.cache === undefined &&\n record.attempts > 0 &&\n record.shortCircuitedBy === undefined\n );\n}\n","/** Whether a record is kept at `rate`, by a hash of its request id. */\nexport function shouldKeep(requestId: string, rate: number): boolean {\n if (rate >= 1) return true;\n if (rate <= 0) return false;\n\n // FNV-1a, 32 bit.\n let hash = 0x811c9dc5;\n for (let i = 0; i < requestId.length; i++) {\n hash ^= requestId.charCodeAt(i);\n hash = Math.imul(hash, 0x01000193);\n }\n return (hash >>> 0) / 4294967296 < rate;\n}\n","import { isSlow } from '../detectors/slowCall.utils.js';\nimport { isTimed } from '../stats/metrics.utils.js';\nimport { shouldKeep } from './sample.utils.js';\n\nimport type {\n BreakerEntry,\n CallLogRecord,\n LogEntry,\n LogLevel,\n ResolvedConfig,\n} from '../../types/index.js';\nimport type { ErrorGrouper } from '../detectors/errorGrouper.js';\nimport type { Guard } from '../guard.utils.js';\nimport type { SeriesRegistry } from '../stats/seriesRegistry.js';\n\nconst LEVEL_RANK: Readonly<Record<LogLevel, number>> = { debug: 0, info: 1, warn: 2, error: 3 };\n\n/**\n * Takes a finished entry to the sink: stats, slow call, error grouping, sampling, level.\n * Entries other than calls only pass the level filter.\n */\nexport function createFinalize(\n config: ResolvedConfig,\n guard: Guard,\n registry: SeriesRegistry,\n groups: ErrorGrouper,\n) {\n let reportedRejection = false;\n const minimum = LEVEL_RANK[config.level];\n\n const write = (level: LogLevel, entry: LogEntry): void => {\n if (LEVEL_RANK[level] < minimum) return;\n\n const pending: unknown = guard('sink.write', () => config.sink.write(level, entry), undefined);\n\n if (typeof (pending as { then?: unknown } | undefined)?.then === 'function') {\n (pending as PromiseLike<void>).then(undefined, (error: unknown) => {\n if (reportedRejection) return;\n reportedRejection = true;\n guard.report('sink.write', error);\n });\n }\n };\n\n const callPipeline = (original: CallLogRecord): void => {\n guard('stats.observe', () => registry.observe(original), undefined);\n\n let record = original;\n const slow = guard(\n 'slowCall',\n () =>\n isTimed(record) &&\n isSlow(record, config.slowCall, () => registry.livePercentileFor(record)),\n false,\n );\n // Raised to `warn`, never lowered: a failure that is also slow keeps its own level.\n if (slow) {\n record = Object.freeze({\n ...record,\n level: LEVEL_RANK[record.level] < LEVEL_RANK.warn ? ('warn' as const) : record.level,\n reason: 'slow' as const,\n });\n }\n\n if (config.errorGrouping && record.outcome === 'error') {\n if (!guard('errorGroup', () => groups.offer(record), true)) return;\n }\n\n // Tail sampling: only plain successes are ever dropped. Aborted, slow, rejected,\n // errored and breaker changing calls are always kept.\n if (\n record.outcome === 'ok' &&\n record.reason === undefined &&\n !record.breaker?.length &&\n !guard('sample', () => shouldKeep(record.requestId, config.sampleSuccess), true)\n ) {\n return;\n }\n\n write(record.level, record);\n };\n\n return (entry: CallLogRecord | BreakerEntry | LogEntry, level?: LogLevel): void => {\n if (entry.kind === 'call') callPipeline(entry);\n else write(level ?? 'info', entry);\n };\n}\n","import type { CallRecorder } from './callRecorder.js';\nimport type { VernLLMEvent } from 'vern-llm';\n\n/** Maps one event to a change on the recorder. `circuit_state` is handled by the caller. */\nexport function applyEvent(\n recorder: CallRecorder,\n event: VernLLMEvent,\n keepCustomEvent: (name: string) => boolean,\n): void {\n switch (event.kind) {\n case 'rate_limited':\n recorder.queued(event.waitedMs);\n return;\n case 'retry':\n recorder.retried(event.delayMs, event.error, event.chain);\n return;\n case 'fallback':\n recorder.fellBack(event.elapsedMs, event.error, event.chain);\n return;\n case 'circuit_state':\n recorder.noteBreaker({\n provider: event.provider,\n model: event.model,\n from: event.from,\n to: event.to,\n });\n return;\n case 'middleware': {\n const note = { kind: event.hook, name: event.middleware } as const;\n recorder.noteMiddleware(\n event.patchedFields ? { ...note, patchedFields: [...event.patchedFields] } : note,\n );\n return;\n }\n case 'usage':\n recorder.setUsage(event.usage, false, event.chain);\n return;\n case 'usage_failure':\n recorder.setUsage(event.usage, true, event.chain);\n return;\n case 'custom':\n if (keepCustomEvent(event.name)) recorder.addCustom(event.name, event.data);\n return;\n }\n}\n","/** What one call's attempts did on one target, for the quality failure rate. */\nexport interface AttemptTally {\n provider: string;\n model: string;\n /** Attempts that got a response, usable or not. An outage is neither good nor bad quality. */\n responded: number;\n /** Responded attempts whose answer was rejected as unusable. */\n qualityFailures: number;\n}\n\n/**\n * Tallies ride beside the record, not in it, so the frozen record sinks receive stays as it is.\n * Keyed by the record object, so they are gone when it is.\n */\nconst tallies = new WeakMap<object, readonly AttemptTally[]>();\n\nexport function attachTallies(record: object, list: readonly AttemptTally[]): void {\n if (list.length > 0) tallies.set(record, list);\n}\n\nexport function talliesOf(record: object): readonly AttemptTally[] | undefined {\n return tallies.get(record);\n}\n","import type { Enricher, JsonValue, UsageFields } from '../../types/index.js';\nimport type { Guard } from '../guard.utils.js';\nimport type { PreDispatchContext, TokenUsage } from 'vern-llm';\n\n/** The allowlisted `context` keys, and only those. `undefined` when none are present. */\nexport function pickContext(\n context: PreDispatchContext['context'],\n allowed: readonly string[],\n): Record<string, JsonValue> | undefined {\n if (!context || allowed.length === 0) return undefined;\n\n const picked: Record<string, JsonValue> = {};\n for (const key of allowed) {\n if (Object.hasOwn(context, key) && context[key] !== undefined) picked[key] = context[key];\n }\n return Object.keys(picked).length > 0 ? picked : undefined;\n}\n\n/** Runs each enricher on its own guard and merges the results left to right. */\nexport function runEnrichers(\n enrichers: readonly Enricher[],\n ctx: PreDispatchContext,\n guard: Guard,\n): Record<string, JsonValue> | undefined {\n if (enrichers.length === 0) return undefined;\n\n const merged: Record<string, JsonValue> = {};\n enrichers.forEach((enrich, index) => {\n const operation = `enrich[${index}]`;\n const fields: unknown = guard(operation, () => enrich(ctx), undefined);\n if (typeof fields !== 'object' || fields === null) return;\n\n // An async enricher cannot be waited for, so its rejection is reported.\n if (typeof (fields as { then?: unknown }).then === 'function') {\n Promise.resolve(fields).catch((error: unknown) => guard.report(operation, error));\n return;\n }\n\n for (const [key, value] of Object.entries(fields)) {\n // `__proto__` would change the record's prototype instead of adding a field.\n if (key !== '__proto__') merged[key] = value as JsonValue;\n }\n });\n return Object.keys(merged).length > 0 ? merged : undefined;\n}\n\nconst isCount = (value: unknown): value is number =>\n typeof value === 'number' && Number.isFinite(value);\n\n/** The token counts of a usage object, copied field by field so nothing else leaks. */\nexport function pickUsage(usage: TokenUsage): UsageFields {\n const picked: UsageFields = {\n promptTokens: usage.promptTokens,\n completionTokens: usage.completionTokens,\n totalTokens: usage.totalTokens,\n };\n if (isCount(usage.reasoningTokens)) picked.reasoningTokens = usage.reasoningTokens;\n if (isCount(usage.cacheReadTokens)) picked.cacheReadTokens = usage.cacheReadTokens;\n if (isCount(usage.cacheWriteTokens)) picked.cacheWriteTokens = usage.cacheWriteTokens;\n\n if (usage.cacheWriteTokensByTtl) {\n const byTtl: Record<string, number> = {};\n for (const [ttl, tokens] of Object.entries(usage.cacheWriteTokensByTtl)) {\n if (ttl !== '__proto__' && isCount(tokens)) byTtl[ttl] = tokens;\n }\n picked.cacheWriteTokensByTtl = byTtl;\n }\n return picked;\n}\n","/** Milliseconds to the microsecond: raw clock floats are noise in a log line. */\nexport const roundMs = (value: number): number => Math.round(value * 1000) / 1000;\n\n/** `completionTokens` per second of the attempt, or `undefined` without both. */\nexport function outputTokensPerSec(\n completionTokens: number | undefined,\n attemptMs: number | undefined,\n): number | undefined {\n if (completionTokens === undefined || attemptMs === undefined) return undefined;\n if (!Number.isFinite(completionTokens) || completionTokens <= 0 || attemptMs <= 0) {\n return undefined;\n }\n return roundMs(completionTokens / (attemptMs / 1000));\n}\n","import { pickUsage } from './inputs.utils.js';\nimport { outputTokensPerSec, roundMs } from './recordMetrics.utils.js';\n\nimport type { CallLogRecord, JsonValue } from '../../types/index.js';\nimport type { RecordInput } from './recordInput.types.js';\n\n/** Frozen copies in a frozen list, so nothing a sink holds can change what the next one reads. */\nfunction frozenCopies<T extends object>(items: readonly T[]): T[] {\n return Object.freeze(items.map((item) => Object.freeze({ ...item }))) as T[];\n}\n\n/** Frozen copies of each event list, so the recorder's live lists never reach a sink. */\nfunction frozenCustom(custom: RecordInput['custom']): NonNullable<CallLogRecord['custom']> {\n const copy: NonNullable<CallLogRecord['custom']> = {};\n for (const [name, values] of custom) copy[name] = Object.freeze([...values]) as JsonValue[];\n return Object.freeze(copy);\n}\n\n/** The fields every record has, whatever happened. */\nfunction baseRecord(input: RecordInput): CallLogRecord {\n return {\n kind: 'call',\n v: 1,\n ts: input.endedAt.toISOString(),\n requestId: input.requestId,\n outcome: input.classified.outcome,\n level: input.classified.level,\n attempts: input.attempts,\n retries: input.retries,\n streaming: input.streaming,\n durationMs: roundMs(input.durationMs),\n queueWaitMs: roundMs(input.queueWaitMs),\n retryDelayMs: roundMs(input.retryDelayMs),\n path: frozenCopies(input.path),\n };\n}\n\n/** Who answered, how long it took, and what short circuited the call. */\nfunction addAnswer(record: CallLogRecord, input: RecordInput): void {\n const { answered } = input;\n if (answered) {\n record.provider = answered.provider;\n record.model = answered.model;\n record.fallbackIndex = answered.fallbackIndex;\n if (answered.finishReason) record.finishReason = answered.finishReason;\n }\n if (input.chains) {\n record.chains = input.chains.count;\n record.chain = input.chains.kept;\n }\n if (input.cache !== undefined) record.cache = input.cache;\n if (input.attemptMs !== undefined) record.attemptMs = roundMs(input.attemptMs);\n if (input.ttftMs !== undefined) record.ttftMs = roundMs(input.ttftMs);\n if (input.shortCircuitedBy !== undefined) record.shortCircuitedBy = input.shortCircuitedBy;\n}\n\nfunction addUsage(record: CallLogRecord, input: RecordInput): void {\n if (!input.usage) return;\n\n record.usage = Object.freeze(pickUsage(input.usage));\n if (input.usageFailed) record.usageFailed = true;\n const rate = outputTokensPerSec(input.usage.completionTokens, input.generationMs);\n if (rate !== undefined) record.outputTokensPerSec = rate;\n}\n\n/** What other parts of the call left behind, written only when there is something to write. */\nfunction addNotes(record: CallLogRecord, input: RecordInput): void {\n if (input.classified.error) record.error = Object.freeze({ ...input.classified.error });\n if (input.breaker.length > 0) record.breaker = frozenCopies(input.breaker);\n if (input.middleware.length > 0) record.middleware = frozenCopies(input.middleware);\n if (input.custom.size > 0) record.custom = frozenCustom(input.custom);\n if (input.context) record.context = Object.freeze(input.context);\n if (input.fields) record.fields = Object.freeze(input.fields);\n}\n\n/** Builds the one frozen record. Absent fields are left out, never written as `undefined`. */\nexport function buildRecord(input: RecordInput): CallLogRecord {\n const record = baseRecord(input);\n addAnswer(record, input);\n addUsage(record, input);\n addNotes(record, input);\n return Object.freeze(record);\n}\n","import type { CallLogRecord, LogLevel } from '../../types/index.js';\nimport type { CallResult } from 'vern-llm';\n\n// Errors are recognised by shape, never `instanceof`: the ESM and CJS builds of vern-llm\n// have separate `LLMError` classes.\n\ninterface LLMErrorLike {\n name: 'LLMError';\n type: string;\n code?: unknown;\n status?: unknown;\n attempts?: unknown;\n}\n\nexport function isLLMErrorLike(error: unknown): error is LLMErrorLike {\n if (typeof error !== 'object' || error === null) return false;\n const candidate = error as { name?: unknown; type?: unknown };\n return candidate.name === 'LLMError' && typeof candidate.type === 'string';\n}\n\nexport type RecorderOutcome =\n | { kind: 'result'; result: CallResult }\n | { kind: 'error'; error: unknown };\n\nexport interface ClassifiedOutcome {\n outcome: CallLogRecord['outcome'];\n level: LogLevel;\n /** Only `type`, `code` and `status`, never the message: it can contain prompt text. */\n error?: NonNullable<CallLogRecord['error']>;\n}\n\nconst isNonEmptyString = (value: unknown): value is string =>\n typeof value === 'string' && value !== '';\n\nfunction statusOf(error: LLMErrorLike): number | undefined {\n const { status } = error;\n return typeof status === 'number' && Number.isInteger(status) && status >= 100 && status <= 599\n ? status\n : undefined;\n}\n\n/** Codes of a middleware saying no on purpose, which is not a fault of the provider or the app. */\nconst REJECTED_CODES: ReadonlySet<string> = new Set([\n 'policy_denied',\n /** Refused by a middleware for its content. */\n 'content_blocked',\n /** The provider declined the call. */\n 'model_refused',\n]);\n\n/** The rules that turn how a call ended into an outcome and a level. */\nexport function classifyOutcome(outcome: RecorderOutcome): ClassifiedOutcome {\n if (outcome.kind === 'result') return { outcome: 'ok', level: 'info' };\n\n const { error } = outcome;\n if (!isLLMErrorLike(error))\n return { outcome: 'error', level: 'error', error: { type: 'unknown' } };\n\n const summary: NonNullable<ClassifiedOutcome['error']> = { type: error.type };\n if (isNonEmptyString(error.code)) summary.code = error.code;\n const status = statusOf(error);\n if (status !== undefined) summary.status = status;\n\n // The caller cancelled, which is not a fault of the provider or of this app.\n if (error.type === 'aborted') return { outcome: 'aborted', level: 'info', error: summary };\n const rejected =\n error.type === 'quota_exceeded' ||\n (summary.code !== undefined && REJECTED_CODES.has(summary.code));\n if (rejected) return { outcome: 'rejected', level: 'warn', error: summary };\n return { outcome: 'error', level: 'error', error: summary };\n}\n\n/** `code` when the error has one, else `type`. Both are enumerated. */\nexport function errorCodeOf(error: unknown): string {\n if (!isLLMErrorLike(error)) return 'unknown';\n return isNonEmptyString(error.code) ? error.code : error.type;\n}\n\n/** The error that ended the last attempt, which an exhausted fallback chain hides. */\nexport function lastAttemptErrorOf(error: unknown): unknown {\n if (!isLLMErrorLike(error) || error.type !== 'fallback_exhausted') return error;\n if (!Array.isArray(error.attempts) || error.attempts.length === 0) return error;\n\n const snapshot = (error.attempts[error.attempts.length - 1] as { error?: unknown } | null)?.error;\n if (typeof snapshot !== 'object' || snapshot === null) return error;\n\n const { type, code } = snapshot as { type?: unknown; code?: unknown };\n return typeof type === 'string' ? { name: 'LLMError', type, code } : error;\n}\n\n/**\n * Whether an attempt failed because its answer was unusable: the provider responded, but what\n * came back was invalid, unparseable, truncated, empty, a soft failure or broke the tool\n * contract. Everything about reaching the provider, the caller or a limit is something else.\n */\nexport function isQualityFailure(error: unknown): boolean {\n if (!isLLMErrorLike(error)) return false;\n if (error.type === 'validation' || error.type === 'parse') return true;\n return error.code === 'soft_failure_detected' || error.code === 'empty_response';\n}\n\n// Codes the core and its adapters throw from inside the provider request before anything is\n// sent: a capability the model rejects, or an inner `dispatch` hook that never called `next`.\nconst LOCAL_REJECTION_CODES: ReadonlySet<unknown> = new Set([\n 'unsupported_capability',\n 'middleware_threw',\n]);\n\n/** Whether a failed dispatch never reached the provider, so it is not an attempt. */\nexport function isLocalRejection(error: unknown): boolean {\n return (\n isLLMErrorLike(error) &&\n error.type === 'invalid_params' &&\n LOCAL_REJECTION_CODES.has(error.code) &&\n statusOf(error) === undefined\n );\n}\n","import { elapsedMs, nowMs } from '../clock.utils.js';\nimport { errorCodeOf, lastAttemptErrorOf } from './outcome.utils.js';\nimport { roundMs } from './recordMetrics.utils.js';\n\nimport type { PathEntry } from '../../types/index.js';\nimport type { AttemptContext } from 'vern-llm';\n\n/** One target tried during the call. Mutable until the target is left or the call ends. */\nexport interface PathState {\n provider: string;\n model: string;\n attempts: number;\n startedAtMs: number;\n closed: boolean;\n outcome: PathEntry['outcome'];\n elapsedMs: number;\n errorCode: string | undefined;\n}\n\n/** The attempt whose outcome is not known yet. */\nexport interface OpenAttempt {\n startedAtMs: number;\n /** When `dispatch` resolved. Parsing and validation may still fail after it. */\n respondedAtMs: number | undefined;\n path: PathState;\n}\n\n/** Counts one more attempt on the current target, or starts a new entry for a new target. */\nexport function enterTarget(path: PathState[], ctx: AttemptContext): PathState {\n let entry = path[path.length - 1];\n if (!entry || entry.closed || entry.provider !== ctx.requestedProvider) {\n entry = {\n provider: ctx.requestedProvider,\n model: ctx.requestedModel,\n attempts: 0,\n startedAtMs: nowMs(),\n closed: false,\n outcome: 'error',\n elapsedMs: 0,\n errorCode: undefined,\n };\n path.push(entry);\n }\n entry.attempts++;\n return entry;\n}\n\n/** Takes back an attempt that never reached a provider, and its entry if it was the only one. */\nexport function leaveTarget(path: PathState[], attempt: OpenAttempt): void {\n attempt.path.attempts--;\n if (attempt.path.attempts !== 0) return;\n\n const index = path.indexOf(attempt.path);\n // The entry was pushed by `openAttempt`, so it is always found.\n /* v8 ignore next */\n if (index !== -1) path.splice(index, 1);\n}\n\n/** The call moves on: the last target is finished as failed. */\nexport function closeTargetAsFailed(\n path: readonly PathState[],\n elapsedMsOnTarget: number,\n error: unknown,\n): void {\n const entry = path[path.length - 1];\n if (!entry || entry.closed) return;\n\n entry.closed = true;\n entry.outcome = 'error';\n entry.errorCode = errorCodeOf(error);\n // The core's own figure, measured here only if it is missing.\n entry.elapsedMs =\n Number.isFinite(elapsedMsOnTarget) && elapsedMsOnTarget >= 0\n ? elapsedMsOnTarget\n : elapsedMs(entry.startedAtMs);\n}\n\n/** The call ends: the last target, if still open, answered or failed as the call did. */\nexport function closeLastTarget(\n path: readonly PathState[],\n endedAtMs: number,\n failure: { failed: boolean; error: unknown },\n): void {\n const last = path[path.length - 1];\n if (!last || last.closed) return;\n\n last.closed = true;\n last.elapsedMs = elapsedMs(last.startedAtMs, endedAtMs);\n if (failure.failed) {\n last.outcome = 'error';\n last.errorCode = errorCodeOf(lastAttemptErrorOf(failure.error));\n } else {\n last.outcome = 'ok';\n }\n}\n\nexport function pathEntryOf(entry: PathState): PathEntry {\n const result: PathEntry = {\n provider: entry.provider,\n model: entry.model,\n attempts: entry.attempts,\n outcome: entry.outcome,\n elapsedMs: roundMs(entry.elapsedMs),\n };\n if (entry.errorCode !== undefined) result.errorCode = entry.errorCode;\n return result;\n}\n","import { elapsedMs } from '../clock.utils.js';\n\nimport type { OpenAttempt } from './callPath.utils.js';\n\nexport interface CallTimings {\n /** How long the answering attempt took to respond. */\n attemptMs: number | undefined;\n /** The window the output tokens were produced in. */\n generationMs: number | undefined;\n /** Call start to first content chunk, streams only. */\n ttftMs: number | undefined;\n}\n\n/**\n * The token rate of a stream is the generation window after its first chunk, not the wait\n * before it. That wait is `ttftMs`, measured from the call's start.\n */\nexport function timingsOf(input: {\n open: OpenAttempt | undefined;\n answered: boolean;\n streaming: boolean;\n startedAtMs: number;\n endedAtMs: number;\n}): CallTimings {\n const { open, answered, streaming, startedAtMs, endedAtMs } = input;\n const respondedAtMs = open?.respondedAtMs;\n\n let attemptMs: number | undefined;\n let generationMs: number | undefined;\n if (open && answered) {\n attemptMs = elapsedMs(open.startedAtMs, respondedAtMs ?? endedAtMs);\n generationMs = streaming ? elapsedMs(respondedAtMs ?? endedAtMs, endedAtMs) : attemptMs;\n }\n const ttftMs =\n streaming && respondedAtMs !== undefined ? elapsedMs(startedAtMs, respondedAtMs) : undefined;\n\n return { attemptMs, generationMs, ttftMs };\n}\n","import { elapsedMs, nowMs } from '../clock.utils.js';\nimport { attachTallies, type AttemptTally } from '../stats/attemptTally.utils.js';\nimport { buildRecord } from './buildRecord.utils.js';\nimport {\n closeLastTarget,\n closeTargetAsFailed,\n enterTarget,\n leaveTarget,\n pathEntryOf,\n type OpenAttempt,\n type PathState,\n} from './callPath.utils.js';\nimport { timingsOf } from './callTimings.utils.js';\nimport { pickContext, runEnrichers } from './inputs.utils.js';\nimport {\n classifyOutcome,\n isLocalRejection,\n isQualityFailure,\n lastAttemptErrorOf,\n type RecorderOutcome,\n} from './outcome.utils.js';\n\nimport type {\n BreakerChange,\n CallLogRecord,\n JsonValue,\n MiddlewareNote,\n ResolvedConfig,\n} from '../../types/index.js';\nimport type { Guard } from '../guard.utils.js';\nimport type { AttemptContext, CallMeta, PreDispatchContext, TokenUsage } from 'vern-llm';\n\n/** What one chain of the call has done. A call with no reruns has only chain 0. */\ninterface ChainState {\n current: OpenAttempt | undefined;\n path: PathState[];\n}\n\n/**\n * All the state for one logical call. An attempt stays open after `dispatch` resolves, since\n * validation can still fail and retry, and is closed by the next retry, fallback or the end.\n *\n * Attempts, the path and usage are kept per chain, because chains run at the same time. The record\n * takes its path, timings and usage from the chain whose answer was kept, and counts the attempts\n * of every chain.\n */\nexport class CallRecorder {\n private readonly startedAtMs = nowMs();\n private ended = false;\n private streaming = false;\n private attemptCount = 0;\n private readonly chains = new Map<number, ChainState>();\n private retries = 0;\n private retryDelayMs = 0;\n private queueWaitMs = 0;\n private readonly usages = new Map<number, { usage: TokenUsage; failed: boolean }>();\n private shortCircuitedBy: string | undefined;\n private readonly breaker: BreakerChange[] = [];\n private readonly middleware: MiddlewareNote[] = [];\n private readonly custom = new Map<string, JsonValue[]>();\n private readonly tallies = new Map<PathState, AttemptTally>();\n\n private constructor(\n private readonly ctx: PreDispatchContext,\n private readonly config: ResolvedConfig,\n private readonly guard: Guard,\n ) {}\n\n static start(ctx: PreDispatchContext, config: ResolvedConfig, guard: Guard): CallRecorder {\n return new CallRecorder(ctx, config, guard);\n }\n\n /** `dispatch` entered: one request is about to go to one target. */\n openAttempt(ctx: AttemptContext): void {\n if (this.ended) return;\n\n // A new attempt while one is still open in the same chain means a closing signal was missed,\n // so how that attempt ended is unknown. It still counts, but its outcome is never guessed.\n const chain = this.chainState(ctx.chain ?? 0);\n chain.current = undefined;\n this.attemptCount++;\n\n const entry = enterTarget(chain.path, ctx);\n chain.current = { startedAtMs: nowMs(), respondedAtMs: undefined, path: entry };\n }\n\n /** `dispatch` resolved. The attempt stays open, see the class comment. */\n responded(chain = 0): void {\n const attempt = this.chains.get(chain)?.current;\n if (attempt) attempt.respondedAtMs = nowMs();\n }\n\n /** `dispatch` rejected: the adapter failed. */\n attemptFailed(error: unknown, chain = 0): void {\n const state = this.chains.get(chain);\n const attempt = state?.current;\n if (!state || !attempt) return;\n state.current = undefined;\n\n // Rejected before anything was sent, such as a capability the model does not have. That\n // never reached a provider, so it is not an attempt and leaves no trace of one.\n if (isLocalRejection(error)) {\n this.attemptCount--;\n leaveTarget(state.path, attempt);\n return;\n }\n\n this.settle(attempt, { error });\n }\n\n /** A retry follows: the open attempt failed with `error`. */\n retried(delayMs: number, error: unknown, chain = 0): void {\n if (this.ended) return;\n this.settleOpen(this.chainState(chain), { error });\n this.retries++;\n if (Number.isFinite(delayMs) && delayMs > 0) this.retryDelayMs += delayMs;\n }\n\n /** The call moves on to the next target: the open attempt and its target are finished. */\n fellBack(elapsedMsOnTarget: number, error: unknown, chain = 0): void {\n if (this.ended) return;\n const state = this.chainState(chain);\n this.settleOpen(state, { error });\n closeTargetAsFailed(state.path, elapsedMsOnTarget, error);\n }\n\n queued(waitedMs: number): void {\n if (!this.ended && Number.isFinite(waitedMs) && waitedMs > 0) this.queueWaitMs += waitedMs;\n }\n\n setUsage(usage: TokenUsage, failed: boolean, chain = 0): void {\n if (this.ended) return;\n this.usages.set(chain, { usage, failed });\n }\n\n noteBreaker(change: BreakerChange): void {\n if (!this.ended) this.breaker.push(change);\n }\n\n noteMiddleware(note: MiddlewareNote): void {\n if (this.ended) return;\n if (note.kind === 'wrap_short_circuit') this.shortCircuitedBy = note.name;\n this.middleware.push(note);\n }\n\n /** The caller decides whether the event is wanted. */\n addCustom(name: string, data: JsonValue | undefined): void {\n if (this.ended) return;\n const list = this.custom.get(name);\n // The core already caps custom events per call, so this list needs no cap of its own.\n if (list) list.push(data ?? null);\n else this.custom.set(name, [data ?? null]);\n }\n\n /** The call returned a stream, so the record is written when `finalResult` settles. */\n markStream(): void {\n if (!this.ended) this.streaming = true;\n }\n\n /** The only exit: builds the record once. Events after this are dropped. */\n finish(outcome: RecorderOutcome): CallLogRecord | undefined {\n if (this.ended) return undefined;\n this.ended = true;\n\n const endedAtMs = nowMs();\n const failure = outcome.kind === 'error' ? outcome.error : undefined;\n\n // The kept chain is the one whose answer came back, or the main chain when the call failed.\n // The other chains were cancelled, so how their open attempts ended is never guessed.\n const kept = outcome.kind === 'result' ? (outcome.result.meta?.chain ?? 0) : 0;\n const keptState = this.chains.get(kept);\n const keptPath = keptState?.path ?? [];\n\n // The attempt still open here answered, or failed for the reason the call failed.\n const open = keptState?.current;\n for (const state of this.chains.values()) state.current = undefined;\n if (open) {\n this.settle(\n open,\n outcome.kind === 'error' ? { error: lastAttemptErrorOf(failure) } : undefined,\n );\n }\n\n const timings = timingsOf({\n open,\n answered: outcome.kind === 'result',\n streaming: this.streaming,\n startedAtMs: this.startedAtMs,\n endedAtMs,\n });\n closeLastTarget(keptPath, endedAtMs, { failed: outcome.kind === 'error', error: failure });\n\n const meta: CallMeta | undefined = outcome.kind === 'result' ? outcome.result.meta : undefined;\n const classified = classifyOutcome(outcome);\n\n const record = buildRecord({\n requestId: this.ctx.requestId,\n endedAt: new Date(),\n classified,\n answered: meta && {\n provider: meta.provider,\n model: meta.model,\n fallbackIndex: meta.fallbackIndex,\n finishReason: meta.finishReason,\n },\n attempts: this.attemptCount,\n retries: this.retries,\n streaming: this.streaming,\n cache:\n classified.outcome === 'ok' && outcome.kind === 'result' ? outcome.result.cache : undefined,\n ttftMs: timings.ttftMs,\n shortCircuitedBy: this.shortCircuitedBy,\n durationMs: elapsedMs(this.startedAtMs, endedAtMs),\n queueWaitMs: this.queueWaitMs,\n retryDelayMs: this.retryDelayMs,\n attemptMs: timings.attemptMs,\n generationMs: timings.generationMs,\n chains: this.chains.size > 1 ? { count: this.chains.size, kept } : undefined,\n path: keptPath.map((entry) => pathEntryOf(entry)),\n usage: this.usages.get(kept)?.usage,\n usageFailed: this.usages.get(kept)?.failed ?? false,\n breaker: this.breaker,\n middleware: this.middleware,\n custom: this.custom,\n context: pickContext(this.ctx.context, this.config.context),\n fields: runEnrichers(this.config.enrich, this.ctx, this.guard),\n });\n\n attachTallies(record, [...this.tallies.values()]);\n return record;\n }\n\n /** The state of one chain, created when the chain first shows up. */\n private chainState(chain: number): ChainState {\n let state = this.chains.get(chain);\n if (!state) {\n state = { current: undefined, path: [] };\n this.chains.set(chain, state);\n }\n return state;\n }\n\n /** Settles a chain's open attempt, if any, and leaves none open. */\n private settleOpen(state: ChainState, failure: { error: unknown }): void {\n const attempt = state.current;\n state.current = undefined;\n if (attempt) this.settle(attempt, failure);\n }\n\n /**\n * Counts one finished attempt for the quality failure rate. A `failure` is the error that ended\n * it, absent when it answered. It got a response when `dispatch` resolved, or when its error\n * says the answer arrived and was unusable.\n */\n private settle(attempt: OpenAttempt, failure?: { error: unknown }): void {\n const failedQuality = failure !== undefined && isQualityFailure(failure.error);\n const responded = failure === undefined || failedQuality || attempt.respondedAtMs !== undefined;\n if (!responded) return;\n\n let tally = this.tallies.get(attempt.path);\n if (!tally) {\n tally = {\n provider: attempt.path.provider,\n model: attempt.path.model,\n responded: 0,\n qualityFailures: 0,\n };\n this.tallies.set(attempt.path, tally);\n }\n tally.responded++;\n if (failedQuality) tally.qualityFailures++;\n }\n}\n","import type { SummaryEntry } from '../types/index.js';\nimport type { SeriesRegistry } from './stats/seriesRegistry.js';\n\n/** One entry per live series, with an exemplar on the duration series. */\nexport function buildSummary(registry: SeriesRegistry): SummaryEntry[] {\n return registry.snapshot().series.map((series) => {\n const entry: SummaryEntry = { kind: 'summary', v: 1, series };\n const exemplar =\n series.metric === 'durationMs' ? registry.exemplarOf(series.dimensions) : undefined;\n if (exemplar) entry.exemplar = exemplar;\n return entry;\n });\n}\n","/** A `setInterval` or `setTimeout` handle. */\nexport type TimerHandle = ReturnType<typeof setInterval>;\n\n/** Lets a background timer not keep the process alive, where `unref` exists. */\nexport function unrefTimer(timer: TimerHandle): void {\n (timer as { unref?: () => void }).unref?.();\n}\n","import { nowMs } from './clock.utils.js';\nimport { buildSummary } from './summary.utils.js';\nimport { unrefTimer, type TimerHandle } from './timers.utils.js';\n\nimport type { LogEntry, LogLevel, ResolvedConfig } from '../types/index.js';\nimport type { ErrorGrouper } from './detectors/errorGrouper.js';\nimport type { Guard } from './guard.utils.js';\nimport type { SeriesRegistry } from './stats/seriesRegistry.js';\n\ninterface SchedulerDeps {\n config: ResolvedConfig;\n guard: Guard;\n registry: SeriesRegistry;\n groups: ErrorGrouper;\n write: (entry: LogEntry, level: LogLevel) => void;\n now?: () => number;\n}\n\n/** One unref'd timer per instance, started by the first call. It only writes output. */\nexport class Scheduler {\n private timer: TimerHandle | undefined;\n private disposed = false;\n private readonly now: () => number;\n private lastSummaryAtMs: number;\n\n constructor(private readonly deps: SchedulerDeps) {\n this.now = deps.now ?? nowMs;\n this.lastSummaryAtMs = this.now();\n }\n\n ensureStarted(): void {\n if (this.timer || this.disposed) return;\n const { durationMs, slots } = this.deps.config.window;\n // Never slower than the summary cadence.\n const period = Math.min(durationMs / slots, this.deps.config.summary?.everyMs ?? Infinity);\n\n this.lastSummaryAtMs = this.now();\n this.timer = setInterval(() => this.tick(), period);\n unrefTimer(this.timer);\n }\n\n /** Flushes closed error windows, and emits a summary when its period has passed. */\n tick(): void {\n const { config, guard, groups, registry, write } = this.deps;\n const { summary } = config;\n\n guard(\n 'flushErrorGroups',\n () => groups.flush().forEach((entry) => write(entry, 'error')),\n undefined,\n );\n\n if (!summary || this.now() - this.lastSummaryAtMs < summary.everyMs) return;\n this.lastSummaryAtMs = this.now();\n\n guard(\n 'summary',\n () => {\n const entries = buildSummary(registry);\n if (entries.length === 0) return;\n entries.forEach((entry) => write(entry, 'info'));\n const { onSummary } = summary;\n if (onSummary) guard('onSummary', () => onSummary(entries), undefined);\n },\n undefined,\n );\n }\n\n /** Stops the timer for good and writes every pending error group. */\n dispose(): void {\n if (this.timer) clearInterval(this.timer);\n this.timer = undefined;\n this.disposed = true;\n\n const { guard, groups, write } = this.deps;\n groups.close();\n guard(\n 'disposeFlush',\n () => groups.flush(true).forEach((entry) => write(entry, 'error')),\n undefined,\n );\n }\n}\n","import type { CallLogRecord, DimensionExtractor } from '../../types/index.js';\nimport type { Guard } from '../guard.utils.js';\n\n/** A longer dimension value is cut, so one runaway string cannot bloat a key or a log line. */\nconst MAX_DIMENSION_LENGTH = 128;\n\n/** The built in dimensions plus every user dimension that yields a non empty string. */\nexport function dimensionsOf(\n record: Readonly<CallLogRecord>,\n extractors: ReadonlyMap<string, DimensionExtractor>,\n guard: Guard,\n): Record<string, string> | undefined {\n const last = record.path[record.path.length - 1];\n const provider = record.provider ?? last?.provider;\n const model = record.model ?? last?.model;\n if (provider === undefined || model === undefined) return undefined;\n\n const dimensions: Record<string, string> = {\n provider,\n model,\n streaming: String(record.streaming),\n };\n for (const [name, extractor] of extractors) {\n const value = guard(`dimension.${name}`, () => extractor(record), undefined);\n if (typeof value === 'string' && value !== '') {\n dimensions[name] = value.slice(0, MAX_DIMENSION_LENGTH);\n }\n }\n return dimensions;\n}\n","// A log bucketed quantile sketch in the style of DDSketch. Pure numbers, no imports.\n\nconst ALPHA = 0.01;\nconst GAMMA = (1 + ALPHA) / (1 - ALPHA);\nconst LN_GAMMA = Math.log(GAMMA);\n/** Values at or below this share one bucket, which keeps `ln` away from zero. */\nconst ZERO_BOUND = 0.001;\n/** About 700 buckets cover 1 ms to 1,000 s, so this is only a memory bound. */\nconst MAX_BUCKETS = 2048;\nconst INITIAL_BUCKETS = 16;\n\n/** Relative error of every quantile estimate. */\nexport const SKETCH_RELATIVE_ERROR = ALPHA;\n\nexport class Sketch {\n private counts = new Float64Array(INITIAL_BUCKETS);\n /** Bucket index held by `counts[0]`. */\n private offset = 0;\n /** Number of `counts` slots in use, from `offset`. */\n private span = 0;\n private zeroCount = 0;\n count = 0;\n sum = 0;\n min = Infinity;\n max = -Infinity;\n\n /** Ignores anything that is not a finite, non negative number. */\n add(value: number, weight = 1): void {\n if (!Number.isFinite(value) || value < 0 || weight <= 0) return;\n\n this.count += weight;\n this.sum += value * weight;\n if (value < this.min) this.min = value;\n if (value > this.max) this.max = value;\n\n if (value <= ZERO_BOUND) {\n this.zeroCount += weight;\n return;\n }\n this.addToBucket(Math.ceil(Math.log(value) / LN_GAMMA), weight);\n }\n\n /** The value at `q` in [0, 1], or `undefined` when empty. */\n quantile(q: number): number | undefined {\n if (this.count === 0 || !(q >= 0 && q <= 1)) return undefined;\n\n const rank = q * (this.count - 1);\n let running = this.zeroCount;\n if (running > rank) return 0;\n\n for (let i = 0; i < this.span; i++) {\n running += this.counts[i] as number;\n if (running > rank) {\n const estimate = (2 * GAMMA ** (this.offset + i)) / (GAMMA + 1);\n return Math.min(this.max, Math.max(this.min, estimate));\n }\n }\n // Counts are whole numbers, so the loop always passes `rank` before it ends.\n /* v8 ignore next */\n return this.max;\n }\n\n merge(other: Sketch): void {\n if (other.count === 0) return;\n\n this.count += other.count;\n this.sum += other.sum;\n this.min = Math.min(this.min, other.min);\n this.max = Math.max(this.max, other.max);\n this.zeroCount += other.zeroCount;\n\n for (let i = 0; i < other.span; i++) {\n const weight = other.counts[i] as number;\n if (weight > 0) this.addToBucket(other.offset + i, weight);\n }\n }\n\n clear(): void {\n this.counts.fill(0);\n this.offset = 0;\n this.span = 0;\n this.zeroCount = 0;\n this.count = 0;\n this.sum = 0;\n this.min = Infinity;\n this.max = -Infinity;\n }\n\n private addToBucket(index: number, weight: number): void {\n if (this.span === 0) {\n this.offset = index;\n this.span = 1;\n } else if (index < this.offset) {\n // Below the range: grow downwards, or fold into the lowest bucket once at the bound.\n const needed = this.offset + this.span - index;\n if (needed > MAX_BUCKETS) index = this.offset + this.span - MAX_BUCKETS;\n if (index < this.offset) this.shiftDown(index);\n else index = this.offset;\n } else if (index >= this.offset + this.span) {\n const needed = index - this.offset + 1;\n if (needed > MAX_BUCKETS) this.collapseLowest(index - MAX_BUCKETS + 1);\n this.span = index - this.offset + 1;\n }\n\n this.ensureCapacity(this.span);\n this.counts[index - this.offset] = (this.counts[index - this.offset] as number) + weight;\n }\n\n private shiftDown(newOffset: number): void {\n const grow = this.offset - newOffset;\n const next = new Float64Array(Math.max(this.counts.length, this.span + grow));\n next.set(this.counts.subarray(0, this.span), grow);\n this.counts = next;\n this.offset = newOffset;\n this.span += grow;\n }\n\n /** Folds every bucket below `newOffset` into the one at `newOffset`, the lowest kept. */\n private collapseLowest(newOffset: number): void {\n const drop = Math.min(newOffset - this.offset, this.span);\n let folded = 0;\n for (let i = 0; i < drop; i++) folded += this.counts[i] as number;\n\n this.counts.copyWithin(0, drop, this.span);\n this.counts.fill(0, this.span - drop, this.span);\n this.span -= drop;\n this.offset = newOffset;\n if (this.span === 0) this.span = 1;\n this.counts[0] = (this.counts[0] as number) + folded;\n }\n\n private ensureCapacity(needed: number): void {\n if (needed <= this.counts.length) return;\n const next = new Float64Array(Math.min(MAX_BUCKETS, Math.max(needed, this.counts.length * 2)));\n next.set(this.counts);\n this.counts = next;\n }\n}\n","import { Sketch } from './sketch.js';\n\n/** A ring of slots over a window. A slot from an older period is cleared before reuse. */\nexport class Ring<T> {\n private readonly items: T[];\n private readonly periods: number[];\n private readonly slotMs: number;\n\n constructor(\n private readonly slots: number,\n durationMs: number,\n make: () => T,\n private readonly reset: (item: T) => void,\n ) {\n this.slotMs = durationMs / slots;\n this.items = Array.from({ length: slots }, make);\n this.periods = new Array<number>(slots).fill(-Infinity);\n }\n\n periodAt(now: number): number {\n return Math.floor(now / this.slotMs);\n }\n\n /** The slot for `now`, cleared first if it still holds an older period. */\n at(now: number): T {\n const period = this.periodAt(now);\n const index = ((period % this.slots) + this.slots) % this.slots;\n if (this.periods[index] !== period) {\n this.reset(this.items[index] as T);\n this.periods[index] = period;\n }\n return this.items[index] as T;\n }\n\n /** Every slot still inside the window that ends at `now`. */\n live(now: number): T[] {\n const oldest = this.periodAt(now) - this.slots + 1;\n return this.items.filter((_, index) => (this.periods[index] as number) >= oldest);\n }\n\n clear(): void {\n this.items.forEach((item) => this.reset(item));\n this.periods.fill(-Infinity);\n }\n}\n\ninterface SketchSlot<Tag> {\n sketch: Sketch;\n /** What produced this slot's largest value, for an exemplar. */\n maxTag: Tag | undefined;\n}\n\n/** A window of sketches. Reads merge the live slots into one scratch sketch that is reused. */\nexport class RollingSketch<Tag = string> {\n private readonly ring: Ring<SketchSlot<Tag>>;\n private readonly scratch = new Sketch();\n private cachedPeriod = -Infinity;\n private cachedValue: number | undefined;\n\n constructor(\n durationMs: number,\n slots: number,\n private readonly cachedQuantileOf?: number,\n ) {\n this.ring = new Ring<SketchSlot<Tag>>(\n slots,\n durationMs,\n () => ({ sketch: new Sketch(), maxTag: undefined }),\n (slot) => {\n slot.sketch.clear();\n slot.maxTag = undefined;\n },\n );\n }\n\n add(now: number, value: number, tag?: Tag): void {\n const slot = this.ring.at(now);\n const before = slot.sketch.max;\n slot.sketch.add(value);\n if (tag !== undefined && slot.sketch.max > before) slot.maxTag = tag;\n }\n\n /** How many samples are in the window. Cheap: sums slot counts, merges nothing. */\n count(now: number): number {\n let total = 0;\n for (const slot of this.ring.live(now)) total += slot.sketch.count;\n return total;\n }\n\n /** The values at each `q`, or `undefined` when the window is empty. */\n quantiles(now: number, qs: readonly number[]): (number | undefined)[] {\n this.mergeLive(now);\n return qs.map((q) => this.scratch.quantile(q));\n }\n\n /** Adds the live slots into `target`, for reads that span several series. */\n mergeInto(now: number, target: Sketch): void {\n for (const slot of this.ring.live(now)) target.merge(slot.sketch);\n }\n\n /** The largest value in the window and the tag that produced it. */\n slowest(now: number): { tag: Tag; value: number } | undefined {\n let best: { tag: Tag; value: number } | undefined;\n for (const slot of this.ring.live(now)) {\n if (slot.maxTag !== undefined && (!best || slot.sketch.max > best.value)) {\n best = { tag: slot.maxTag, value: slot.sketch.max };\n }\n }\n return best;\n }\n\n /** The configured quantile, recomputed once per slot period. */\n cachedQuantile(now: number): number | undefined {\n if (this.cachedQuantileOf === undefined) return undefined;\n const period = this.ring.periodAt(now);\n if (period !== this.cachedPeriod) {\n this.cachedPeriod = period;\n this.mergeLive(now);\n this.cachedValue = this.scratch.quantile(this.cachedQuantileOf);\n }\n return this.cachedValue;\n }\n\n clear(): void {\n this.ring.clear();\n this.cachedPeriod = -Infinity;\n this.cachedValue = undefined;\n }\n\n private mergeLive(now: number): void {\n this.scratch.clear();\n this.mergeInto(now, this.scratch);\n }\n}\n","import { METRICS } from './metrics.utils.js';\nimport { Ring, RollingSketch } from './rollingSketch.js';\n\nimport type { CallLogRecord, MetricName, ResolvedConfig } from '../../types/index.js';\nimport type { AttemptTally } from './attemptTally.utils.js';\n\n/** Key and dimension of the group that takes every series past `maxSeries`. */\nexport const OTHER = '__other__';\n\nexport interface Counts {\n errors: number;\n rejections: number;\n aborts: number;\n cacheHits: number;\n}\n\nexport const emptyCounts = (): Counts => ({ errors: 0, rejections: 0, aborts: 0, cacheHits: 0 });\n\n/** Attempts that got a response, and how many of those were rejected as unusable. */\nexport interface QualityCounts {\n responded: number;\n qualityFailures: number;\n}\n\nconst emptyQuality = (): QualityCounts => ({ responded: 0, qualityFailures: 0 });\n\n/** One set of dimension values, with a rolling histogram per metric and rolling counters. */\nexport interface Group {\n dimensions: Record<string, string>;\n series: Map<MetricName, RollingSketch<Readonly<CallLogRecord>>>;\n counts: Ring<Counts>;\n quality: Ring<QualityCounts>;\n}\n\n/** Sorted and JSON encoded, so no two dimension sets share a key. */\nexport function keyOf(dimensions: Record<string, string>): string {\n return JSON.stringify(Object.entries(dimensions).sort(([a], [b]) => (a < b ? -1 : 1)));\n}\n\nexport function makeGroup(config: ResolvedConfig, dimensions: Record<string, string>): Group {\n const { durationMs, slots } = config.window;\n const series = new Map<MetricName, RollingSketch<Readonly<CallLogRecord>>>();\n for (const metric of METRICS) {\n series.set(\n metric,\n new RollingSketch(\n durationMs,\n slots,\n metric === 'durationMs' ? config.slowCall.aboveLivePercentile : undefined,\n ),\n );\n }\n const counts = new Ring<Counts>(slots, durationMs, emptyCounts, (item) =>\n Object.assign(item, emptyCounts()),\n );\n const quality = new Ring<QualityCounts>(slots, durationMs, emptyQuality, (item) =>\n Object.assign(item, emptyQuality()),\n );\n return { dimensions, series, counts, quality };\n}\n\n/** The counters summed over the live slots. */\nexport function totalsOf(group: Group, now: number): Counts {\n const total = emptyCounts();\n for (const slot of group.counts.live(now)) {\n total.errors += slot.errors;\n total.rejections += slot.rejections;\n total.aborts += slot.aborts;\n total.cacheHits += slot.cacheHits;\n }\n return total;\n}\n\n/** The quality counters summed over the live slots. */\nexport function qualityTotalsOf(group: Group, now: number): QualityCounts {\n const total = emptyQuality();\n for (const slot of group.quality.live(now)) {\n total.responded += slot.responded;\n total.qualityFailures += slot.qualityFailures;\n }\n return total;\n}\n\n/** Adds one call's attempts on one target to the group. */\nexport function recordQuality(group: Group, tally: AttemptTally, now: number): void {\n const slot = group.quality.at(now);\n slot.responded += tally.responded;\n slot.qualityFailures += tally.qualityFailures;\n}\n\n/** Counts the call's outcome, then its timings when it is one whose latency counts. */\nexport function recordInto(\n group: Group,\n record: Readonly<CallLogRecord>,\n timed: boolean,\n now: number,\n): void {\n const counts = group.counts.at(now);\n if (record.outcome === 'error') counts.errors++;\n else if (record.outcome === 'rejected') counts.rejections++;\n else if (record.outcome === 'aborted') counts.aborts++;\n if (record.cache !== undefined) counts.cacheHits++;\n if (!timed) return;\n\n const add = (metric: MetricName, value: number | undefined, tag?: Readonly<CallLogRecord>) => {\n if (value !== undefined) group.series.get(metric)?.add(now, value, tag);\n };\n add('durationMs', record.durationMs, record);\n add('queueWaitMs', record.queueWaitMs);\n add('ttftMs', record.ttftMs);\n add('attemptMs', record.attemptMs);\n add('outputTokensPerSec', record.outputTokensPerSec);\n add('outputTokens', record.usage?.completionTokens);\n}\n","import { roundMs } from '../recorder/recordMetrics.utils.js';\nimport { totalsOf, type Counts, type Group } from './group.utils.js';\nimport { METRICS } from './metrics.utils.js';\n\nimport type {\n CallLogRecord,\n MetricName,\n ResolvedConfig,\n SeriesSnapshot,\n StatsSnapshot,\n} from '../../types/index.js';\nimport type { RollingSketch } from './rollingSketch.js';\n\nfunction seriesOf(\n config: ResolvedConfig,\n metric: MetricName,\n group: Group,\n rolling: RollingSketch<Readonly<CallLogRecord>>,\n count: number,\n counts: Counts,\n now: number,\n): SeriesSnapshot {\n const quantiles: Record<string, number> = {};\n if (count >= config.minSamples) {\n const values = rolling.quantiles(now, config.percentiles);\n // At least `minSamples` are in the window, so every quantile has a value.\n config.percentiles.forEach((q, index) => {\n quantiles[String(q)] = roundMs(values[index] as number);\n });\n }\n return { metric, dimensions: { ...group.dimensions }, count, quantiles, ...counts };\n}\n\n/** Every series with data in the window, group by group, the all calls group first. */\nexport function snapshotOf(\n config: ResolvedConfig,\n groups: Iterable<Group>,\n now: number,\n): StatsSnapshot {\n const series: SeriesSnapshot[] = [];\n\n for (const group of groups) {\n const counts = totalsOf(group, now);\n const counted = counts.errors + counts.rejections + counts.aborts + counts.cacheHits > 0;\n let emitted = false;\n\n for (const metric of METRICS) {\n const rolling = group.series.get(metric) as RollingSketch<Readonly<CallLogRecord>>;\n const count = rolling.count(now);\n if (count === 0) continue;\n\n emitted = true;\n series.push(seriesOf(config, metric, group, rolling, count, counts, now));\n }\n\n // A group that only failed still has counters worth showing.\n if (!emitted && counted) {\n series.push({\n metric: 'durationMs',\n dimensions: { ...group.dimensions },\n count: 0,\n quantiles: {},\n ...counts,\n });\n }\n }\n return { windowMs: config.window.durationMs, series };\n}\n","import { nowMs } from '../clock.utils.js';\nimport { roundMs } from '../recorder/recordMetrics.utils.js';\nimport { talliesOf } from './attemptTally.utils.js';\nimport { dimensionsOf } from './dimensions.utils.js';\nimport {\n keyOf,\n makeGroup,\n OTHER,\n qualityTotalsOf,\n recordInto,\n recordQuality,\n type Group,\n} from './group.utils.js';\nimport { isTimed } from './metrics.utils.js';\nimport { RollingSketch } from './rollingSketch.js';\nimport { Sketch } from './sketch.js';\nimport { snapshotOf } from './snapshot.utils.js';\n\nimport type {\n CallLogRecord,\n JsonValue,\n MetricName,\n ResolvedConfig,\n StatsFilter,\n StatsSnapshot,\n} from '../../types/index.js';\nimport type { Guard } from '../guard.utils.js';\n\n/** Rolling series by dimension. Only successful provider calls are timed. */\nexport class SeriesRegistry {\n private readonly groups = new Map<string, Group>();\n private readonly everything: Group;\n private readonly scratch = new Sketch();\n /** The last record's dimensions, so the extractors run once per call. */\n private memo: { record: object; dimensions: Record<string, string> | undefined } | undefined;\n\n constructor(\n private readonly config: ResolvedConfig,\n private readonly guard: Guard,\n private readonly now: () => number = nowMs,\n ) {\n this.everything = makeGroup(config, {});\n }\n\n observe(record: Readonly<CallLogRecord>): void {\n const now = this.now();\n const dimensions = this.dimensionsMemo(record);\n const timed = isTimed(record);\n\n // The all calls group always sees the call, so a global figure exists without a filter.\n recordInto(this.everything, record, timed, now);\n if (dimensions) recordInto(this.groupFor(dimensions), record, timed, now);\n\n this.observeAttempts(record, now);\n }\n\n /**\n * The share of responded attempts rejected as unusable, over every group `filter` matches, or\n * `undefined` below `minSamples` responded attempts.\n */\n failureRate(filter: StatsFilter, kind: 'quality'): number | undefined {\n if (kind !== 'quality') return undefined;\n\n const now = this.now();\n let responded = 0;\n let failures = 0;\n for (const group of this.matching(filter)) {\n const totals = qualityTotalsOf(group, now);\n responded += totals.responded;\n failures += totals.qualityFailures;\n }\n\n if (responded < this.config.minSamples) return undefined;\n return failures / responded;\n }\n\n quantile(filter: StatsFilter, q: number, metric: MetricName = 'durationMs'): number | undefined {\n const now = this.now();\n this.scratch.clear();\n for (const group of this.matching(filter))\n group.series.get(metric)?.mergeInto(now, this.scratch);\n\n if (this.scratch.count < this.config.minSamples) return undefined;\n return this.scratch.quantile(q);\n }\n\n /** The slowest call in the window for exactly these dimensions, for a summary's exemplar. */\n exemplarOf(\n dimensions: Readonly<Record<string, string>>,\n ): { requestId: string; durationMs: number; fields?: Record<string, JsonValue> } | undefined {\n const group =\n Object.keys(dimensions).length === 0\n ? this.everything\n : this.groups.get(OTHER in dimensions ? OTHER : keyOf(dimensions));\n const slowest = group?.series.get('durationMs')?.slowest(this.now());\n if (!slowest) return undefined;\n\n const exemplar: { requestId: string; durationMs: number; fields?: Record<string, JsonValue> } =\n { requestId: slowest.tag.requestId, durationMs: roundMs(slowest.value) };\n const { context } = slowest.tag;\n if (context && Object.keys(context).length > 0) exemplar.fields = { ...context };\n return exemplar;\n }\n\n /** The cached live percentile of the record's series, or `undefined` below `minSamples`. */\n livePercentileFor(record: Readonly<CallLogRecord>): number | undefined {\n const now = this.now();\n const dimensions = this.dimensionsMemo(record);\n const group = (dimensions && this.groups.get(keyOf(dimensions))) || this.everything;\n const series = group.series.get('durationMs') as RollingSketch<Readonly<CallLogRecord>>;\n\n if (series.count(now) < this.config.minSamples) return undefined;\n return series.cachedQuantile(now);\n }\n\n snapshot(): StatsSnapshot {\n return snapshotOf(this.config, [this.everything, ...this.groups.values()], this.now());\n }\n\n reset(): void {\n this.everything.series.forEach((series) => series.clear());\n this.everything.counts.clear();\n this.everything.quality.clear();\n this.groups.clear();\n this.memo = undefined;\n }\n\n /**\n * Counts each target's attempts under that target, not the one that answered, so a call a\n * fallback rescued still counts its failures against the target that produced them.\n */\n private observeAttempts(record: Readonly<CallLogRecord>, now: number): void {\n const tallies = talliesOf(record);\n if (!tallies) return;\n\n // The call's own dimensions, so each extractor still runs once per call. Only the target\n // changes: the user dimensions, such as a route, are the same for every attempt of the call.\n const base = this.dimensionsMemo(record);\n\n for (const tally of tallies) {\n recordQuality(this.everything, tally, now);\n if (base) {\n const dimensions = { ...base, provider: tally.provider, model: tally.model };\n recordQuality(this.groupFor(dimensions), tally, now);\n }\n }\n }\n\n /** The groups `filter` matches. The empty filter is the all calls group alone. */\n private matching(filter: StatsFilter): Group[] {\n const keys = Object.keys(filter);\n if (keys.length === 0) return [this.everything];\n\n return [...this.groups.values()].filter((group) =>\n keys.every((key) => group.dimensions[key] === filter[key]),\n );\n }\n\n private dimensionsMemo(record: Readonly<CallLogRecord>): Record<string, string> | undefined {\n if (this.memo?.record === record) return this.memo.dimensions;\n const dimensions = dimensionsOf(record, this.config.dimensions, this.guard);\n this.memo = { record, dimensions };\n return dimensions;\n }\n\n private groupFor(dimensions: Record<string, string>): Group {\n const key = keyOf(dimensions);\n const existing = this.groups.get(key);\n if (existing) return existing;\n\n if (this.groups.size >= this.config.maxSeries) {\n const overflow = this.groups.get(OTHER);\n if (overflow) return overflow;\n this.guard.warn(\n `more than ${this.config.maxSeries} distinct series, the rest are grouped as ${OTHER}`,\n );\n const created = makeGroup(this.config, { [OTHER]: 'true' });\n this.groups.set(OTHER, created);\n return created;\n }\n\n const created = makeGroup(this.config, dimensions);\n this.groups.set(key, created);\n return created;\n }\n}\n","import type { StatsApi } from '../../types/index.js';\nimport type { SeriesRegistry } from './seriesRegistry.js';\n\n/** The public read side of the registry. Nothing here changes what the registry records. */\nexport function createStatsApi(registry: SeriesRegistry): StatsApi {\n return {\n snapshot: () => registry.snapshot(),\n quantile: (filter, q, metric) => registry.quantile(filter, q, metric),\n failureRate: (filter, kind) => registry.failureRate(filter, kind),\n reset: () => registry.reset(),\n };\n}\n","import {\n createMiddlewareRef,\n createStateKey,\n isStreamResult,\n type CallResult,\n type VernLLMMiddleware,\n} from 'vern-llm';\n\nimport { ErrorGrouper } from './internal/detectors/errorGrouper.js';\nimport { createGuard } from './internal/guard.utils.js';\nimport { toBreakerEntry } from './internal/middleware/breakerEntry.utils.js';\nimport { createCallFinisher } from './internal/middleware/callFinisher.utils.js';\nimport { normalizeOptions } from './internal/options/normalizeOptions.utils.js';\nimport { createFinalize } from './internal/pipeline/finalize.js';\nimport { applyEvent } from './internal/recorder/applyEvent.utils.js';\nimport { CallRecorder } from './internal/recorder/callRecorder.js';\nimport { Scheduler } from './internal/scheduler.js';\nimport { SeriesRegistry } from './internal/stats/seriesRegistry.js';\nimport { createStatsApi } from './internal/stats/statsApi.utils.js';\n\nimport type { LoggerMiddlewareOptions, StatsApi } from './types/index.js';\n\n/** One module level ref, so registering two instances hits the core's duplicate ref check. */\nexport const loggerMiddlewareRef = createMiddlewareRef('logger');\n\n/** The middleware plus the rolling stats it keeps. */\nexport type LoggerMiddleware = VernLLMMiddleware & {\n readonly stats: StatsApi;\n /** Stops the timer and writes any pending error groups. Call it on shutdown. */\n dispose(): void;\n};\n\nexport function loggerMiddleware(options?: LoggerMiddlewareOptions): LoggerMiddleware {\n const config = normalizeOptions(options);\n const guard = createGuard(config.logger);\n const registry = new SeriesRegistry(config, guard);\n const groups = new ErrorGrouper(config.window.durationMs);\n const finalize = createFinalize(config, guard, registry, groups);\n const scheduler = new Scheduler({\n config,\n guard,\n registry,\n groups,\n write: (entry, level) => finalize(entry, level),\n });\n\n // Created per factory call, so it can never collide with another instance or a user key.\n const recorderKey = createStateKey<CallRecorder>('logger.recorder');\n\n const { finish, observeStream } = createCallFinisher(guard, (record) => finalize(record));\n\n // No `enabled` on purpose: a function `enabled` makes the core deliver `onEvent`\n // asynchronously, and the record relies on `usage` landing before the call finishes.\n return {\n stats: createStatsApi(registry),\n dispose: () => scheduler.dispose(),\n name: config.name,\n ref: loggerMiddlewareRef,\n // Outermost, so the record sees the whole call, including rejections.\n position: 'outermost',\n\n wrap: async (_request, next, ctx) => {\n guard('scheduler.start', () => scheduler.ensureStarted(), undefined);\n const recorder = guard<CallRecorder | undefined>(\n 'startCall',\n () => CallRecorder.start(ctx, config, guard),\n undefined,\n );\n if (!recorder) return next();\n\n try {\n ctx.state.set(recorderKey, recorder);\n } catch (error) {\n guard.report('registerRecorder', error);\n return next();\n }\n\n let result: CallResult;\n try {\n result = await next();\n } catch (error) {\n finish(recorder, { kind: 'error', error });\n // The exact error `next` threw, so callers see what they would without this entry.\n throw error;\n }\n\n if (isStreamResult(result.value)) {\n guard('markStream', () => recorder.markStream(), undefined);\n observeStream(recorder, result, result.value);\n return result;\n }\n\n finish(recorder, { kind: 'result', result });\n return result;\n },\n\n dispatch: async (_request, next, ctx) => {\n const recorder = guard('readRecorder', () => ctx.state.get(recorderKey), undefined);\n if (!recorder) return next();\n\n guard('openAttempt', () => recorder.openAttempt(ctx), undefined);\n try {\n await next();\n } catch (error) {\n guard('attemptFailed', () => recorder.attemptFailed(error, ctx.chain), undefined);\n throw error;\n }\n guard('responded', () => recorder.responded(ctx.chain), undefined);\n },\n\n onEvent: (event, ctx) => {\n guard(\n 'handleEvent',\n () => {\n const recorder = ctx?.state.get(recorderKey);\n if (recorder) {\n applyEvent(recorder, event, config.keepCustomEvent);\n return;\n }\n\n if (event.kind === 'circuit_state') {\n const { entry, level } = toBreakerEntry(event);\n finalize(entry, level);\n }\n },\n undefined,\n );\n },\n };\n}\n"],"mappings":"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"}
|