opik-vercel 2.0.74 → 2.0.76

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/dist/index.cjs CHANGED
@@ -1 +1 @@
1
- 'use strict';var api=require('@opentelemetry/api'),opik=require('opik');var C=new opik.Opik,f=class{constructor({client:n=C,tags:e=[],metadata:t={},threadId:s}={}){this.traces=new Map;this.spans=new Map;this.getSpanInput=n=>{let e={},{attributes:t}=n;return Object.keys(t).forEach(a=>{if(a==="ai.prompt"||a==="gen_ai.request"){let o=T(t[a]);o&&(e={...e,...o});}if(a.startsWith("ai.prompt.")){let o=a.replace("ai.prompt.","");e[o]=l(t[a]);}if(a.startsWith("gen_ai.request.")){let o=a.replace("gen_ai.request.","");e[o]=l(t[a]);}}),Object.keys(e).length>0||("ai.toolCall.name"in t&&(e.toolName=t["ai.toolCall.name"]),"ai.toolCall.args"in t&&(e.args=t["ai.toolCall.args"])),e};this.getSpanOutput=n=>{let{attributes:e}=n;return e["ai.response.text"]?{text:e["ai.response.text"]}:e["ai.response.object"]?{object:l(e["ai.response.object"])}:e["ai.toolCall.result"]?{result:e["ai.toolCall.result"]}:e["ai.response.toolCalls"]?{toolCalls:l(e["ai.response.toolCalls"])}:{}};this.getSpanMetadata=n=>{let{attributes:e}=n,t={};return e["gen_ai.response.model"]&&(t.model=e["gen_ai.response.model"]),e["gen_ai.system"]&&(t.system=e["gen_ai.system"]),t};this.getSpanUsage=n=>{let{attributes:e}=n,t={};return "ai.usage.promptTokens"in e&&(t.prompt_tokens=e["ai.usage.promptTokens"]),"gen_ai.usage.input_tokens"in e&&(t.prompt_tokens=e["gen_ai.usage.input_tokens"]),"ai.usage.completionTokens"in e&&(t.completion_tokens=e["ai.usage.completionTokens"]),"gen_ai.usage.output_tokens"in e&&(t.completion_tokens=e["gen_ai.usage.output_tokens"]),("prompt_tokens"in t||"completion_tokens"in t)&&(t.total_tokens=(t.prompt_tokens||0)+(t.completion_tokens||0)),t};this.getThreadId=n=>{var t;let{attributes:e}=n;return e["ai.telemetry.metadata.threadId"]!=null?(t=e["ai.telemetry.metadata.threadId"])==null?void 0:t.toString():this.threadId};this.getErrorInfo=n=>{var u,r,p;if(n.status.code!==api.SpanStatusCode.ERROR)return;let e=n.events.find(c=>c.name==="exception");if(!e)return {exceptionType:"Error",message:n.status.message||"An error occurred",traceback:""};let{attributes:t}=e,s=((u=t==null?void 0:t["exception.type"])==null?void 0:u.toString())||"Error",a=(r=t==null?void 0:t["exception.message"])==null?void 0:r.toString(),o=((p=t==null?void 0:t["exception.stacktrace"])==null?void 0:p.toString())||"";return {exceptionType:s,message:a,traceback:o}};this.processSpan=({otelSpan:n,parentSpan:e,trace:t})=>{let s=this.getErrorInfo(n);return t.span({name:n.name,startTime:new Date(g(n.startTime)),endTime:new Date(g(n.endTime)),parentSpanId:e==null?void 0:e.data.id,input:this.getSpanInput(n),output:this.getSpanOutput(n),metadata:this.getSpanMetadata(n),usage:this.getSpanUsage(n),type:"llm",...s&&{errorInfo:s}})};this.shutdown=async()=>{await this.client.flush();};this.forceFlush=async()=>{await this.client.flush();};this.export=async(n,e)=>{let t=n.filter(o=>O(o)==="ai"),s=n.length-t.length;if(s>0&&opik.logger.debug(`Ignored ${s} non-AI SDK spans`),t.length===0){opik.logger.debug("No AI SDK spans found"),e({code:0});return}let a=w(t);opik.logger.debug("Exporting spans",t),Object.entries(a).forEach(([o,u])=>{var h,b;let[r,...p]=u,c=this.getErrorInfo(r),S=this.client.trace({startTime:new Date(g(r.startTime)),endTime:new Date(g(r.endTime)),name:(b=(h=r.attributes["ai.telemetry.metadata.traceName"])==null?void 0:h.toString())!=null?b:r.name,input:this.getSpanInput(r),output:this.getSpanOutput(r),metadata:{...this.getSpanMetadata(r),...this.metadata},tags:this.tags,usage:this.getSpanUsage(r),threadId:this.getThreadId(r),...c&&{errorInfo:c}});this.traces.set(o,S),p.forEach(m=>{var y,I;let x=this.spans.get((I=(y=m.parentSpanContext)==null?void 0:y.spanId)!=null?I:""),R=this.processSpan({parentSpan:x,otelSpan:m,trace:S});this.spans.set(m.spanContext().spanId,R);});});try{await this.client.flush(),e({code:0});}catch(o){opik.logger.error("Error exporting spans",o),e({code:1,error:o instanceof Error?o:new Error("Unknown error")});}};this.client=n,this.tags=[...e],this.metadata={...t},this.threadId=s;}static getSettings(n){var t,s,a;let e={...n.metadata};return n.name&&(e.traceName=n.name),{isEnabled:(t=n.isEnabled)!=null?t:true,recordInputs:(s=n.recordInputs)!=null?s:true,recordOutputs:(a=n.recordOutputs)!=null?a:true,functionId:n.functionId,metadata:e}}};function w(i){let n={};return i.forEach(e=>{let t=e.spanContext();n[t.traceId]||(n[t.traceId]=[]),n[t.traceId].push(e);}),Object.entries(n).forEach(([e,t])=>{n[e]=_(t);}),n}function O(i){var e,t,s,a;let n=i;return (a=(s=(e=n.instrumentationScope)==null?void 0:e.name)!=null?s:(t=n.instrumentationLibrary)==null?void 0:t.name)!=null?a:void 0}function g(i){return i[0]*1e3+i[1]/1e6}function l(i){try{return JSON.parse(i)}catch{return i}}function T(i){if(typeof i=="string")try{let n=JSON.parse(i);if(n!==null&&typeof n=="object"&&!Array.isArray(n))return n}catch{return}}function _(i){var o,u;let n=new Map,e=new Map;for(let r of i){let{spanId:p}=r.spanContext(),c=(u=(o=r.parentSpanContext)==null?void 0:o.spanId)!=null?u:"";n.set(p,r),c&&(e.has(c)||e.set(c,[]),e.get(c).push(r));}let t=i.filter(r=>{var p;return !((p=r.parentSpanContext)!=null&&p.spanId)||!n.has(r.parentSpanContext.spanId)}),s=[],a=[...t];for(;a.length>0;){let r=a.shift();s.push(r);let p=r.spanContext().spanId,c=e.get(p)||[];a.push(...c);}return s}exports.OpikExporter=f;
1
+ 'use strict';var api=require('@opentelemetry/api'),opik=require('opik');function l(e){try{return JSON.parse(e)}catch{return e}}function F(e){if(typeof e=="string")try{let n=JSON.parse(e);if(n!==null&&typeof n=="object"&&!Array.isArray(n))return n}catch{return}}function T(e){return typeof e=="string"?e:JSON.stringify(e)}function z(e){var a,p,d;if(e==null||typeof e!="object")return [];let{role:n,parts:t}=e,o=n!=null?n:"user";if(!Array.isArray(t))return [e];let r=[],s=[],i=[];for(let g of t){if(g==null||typeof g!="object")continue;let c=g;c.type==="text"?s.push(String((p=(a=c.content)!=null?a:c.text)!=null?p:"")):c.type==="tool_call"?i.push({id:c.id,type:"function",function:{name:c.name,arguments:T(c.arguments)}}):c.type==="tool_call_response"&&r.push({role:"tool",tool_call_id:c.id,content:T((d=c.response)!=null?d:c.output)});}if(s.length>0||i.length>0){let g={role:o};(s.length>0||i.length===0)&&(g.content=s.join("")),i.length>0&&(g.tool_calls=i),r.unshift(g);}return r}function L(e){return Array.isArray(e)?e.flatMap(z):[]}var I={input(e){let n={};for(let t of Object.keys(e))if(t==="ai.prompt"){let o=F(e[t]);o&&(n={...n,...o});}else t.startsWith("ai.prompt.")&&(n[t.slice(10)]=l(e[t]));return "ai.toolCall.name"in e&&(n.toolName=e["ai.toolCall.name"]),"ai.toolCall.args"in e&&(n.args=l(e["ai.toolCall.args"])),n},output(e){let n={};return e["ai.response.text"]&&(n.text=e["ai.response.text"]),e["ai.response.object"]&&(n.object=l(e["ai.response.object"])),e["ai.response.toolCalls"]&&(n.toolCalls=l(e["ai.response.toolCalls"])),e["ai.toolCall.result"]&&(n.result=l(e["ai.toolCall.result"])),n},usage(e){let n={};return "ai.usage.promptTokens"in e&&(n.prompt_tokens=Number(e["ai.usage.promptTokens"])),"ai.usage.completionTokens"in e&&(n.completion_tokens=Number(e["ai.usage.completionTokens"])),n}},m={input(e){let n={};for(let t of Object.keys(e))if(t==="gen_ai.request"){let o=F(e[t]);o&&(n={...n,...o});}else t.startsWith("gen_ai.request.")&&(n[t.slice(15)]=l(e[t]));if("gen_ai.input.messages"in e){let t=[];if("gen_ai.system_instructions"in e){let o=l(e["gen_ai.system_instructions"]),r=Array.isArray(o)?o.map(s=>{var i,a;return String(s&&typeof s=="object"?(a=(i=s.content)!=null?i:s.text)!=null?a:"":s)}).join(""):T(o);t.push({role:"system",content:r});}n.messages=[...t,...L(l(e["gen_ai.input.messages"]))];}return "gen_ai.tool.name"in e&&(n.toolName=e["gen_ai.tool.name"]),"gen_ai.tool.call.arguments"in e&&(n.args=l(e["gen_ai.tool.call.arguments"])),n},output(e){let n={};if("gen_ai.output.messages"in e){let t=l(e["gen_ai.output.messages"]),o=Array.isArray(t)?t:[];n.choices=L(t).map((r,s)=>{var p;let i=o[s],a=i&&typeof i=="object"&&(p=i.finish_reason)!=null?p:null;return {index:s,message:r,finish_reason:a}});}return "gen_ai.tool.call.result"in e&&(n.result=l(e["gen_ai.tool.call.result"])),n},usage(e){let n={};return "gen_ai.usage.input_tokens"in e&&(n.prompt_tokens=Number(e["gen_ai.usage.input_tokens"])),"gen_ai.usage.output_tokens"in e&&(n.completion_tokens=Number(e["gen_ai.usage.output_tokens"])),"gen_ai.usage.cache_read.input_tokens"in e&&(n["original_usage.cache_read_input_tokens"]=Number(e["gen_ai.usage.cache_read.input_tokens"])),"gen_ai.usage.cache_creation.input_tokens"in e&&(n["original_usage.cache_creation_input_tokens"]=Number(e["gen_ai.usage.cache_creation.input_tokens"])),n},metadata(e){let n={},t=R(e);t&&(n.model=t);let o=w(e);o&&(n.provider=o),"gen_ai.operation.name"in e&&(n.operation=e["gen_ai.operation.name"]),"gen_ai.response.finish_reasons"in e&&(n.finish_reasons=l(e["gen_ai.response.finish_reasons"]));for(let r of Object.keys(e))r.startsWith("eve.")?n[r]=e[r]:r.startsWith("ai.settings.context.eve.")&&(n[r.slice(20)]=e[r]);return n}};function h(e){return {...I.input(e),...m.input(e)}}function S(e){return {...I.output(e),...m.output(e)}}function x(e){return m.metadata(e)}function U(e){let n={...I.usage(e),...m.usage(e)};return ("prompt_tokens"in n||"completion_tokens"in n)&&(n.total_tokens=(n.prompt_tokens||0)+(n.completion_tokens||0)),n}var Q=new Set(["chat","text_completion","generate_content","embeddings"]);function R(e){var t;let n=(t=e["gen_ai.response.model"])!=null?t:e["gen_ai.request.model"];return n!=null?String(n):void 0}function w(e){var t;let n=(t=e["gen_ai.system"])!=null?t:e["gen_ai.provider.name"];return n!=null?String(n):void 0}function b(e){let n=e["gen_ai.operation.name"];return n==="execute_tool"||"ai.toolCall.name"in e||"gen_ai.tool.name"in e?"tool":typeof n=="string"?Q.has(n)?"llm":"general":"ai.prompt"in e||"ai.response.text"in e||"ai.response.object"in e||"ai.usage.promptTokens"in e||"ai.usage.completionTokens"in e?"llm":"general"}function K(e){if(e["ai.telemetry.metadata.threadId"]!=null)return e["ai.telemetry.metadata.threadId"].toString();if(e["eve.session.id"]!=null)return e["eve.session.id"].toString();if(e["ai.settings.context.eve.session.id"]!=null)return e["ai.settings.context.eve.session.id"].toString()}var H=new opik.Opik,X=600*1e3,Y=new Set(["ai","gen_ai","eve"]),C=class{constructor({client:n=H,tags:t=[],metadata:o={},threadId:r,traceTtlMs:s=X}={}){this.otelSpansByTrace=new Map;this.traceIds=new Map;this.spanIds=new Map;this.lastSeenByTrace=new Map;this.opikTraceId=n=>{let t=this.traceIds.get(n);return t||(t=opik.generateId(),this.traceIds.set(n,t)),t};this.opikSpanId=n=>{let t=this.spanIds.get(n);return t||(t=opik.generateId(),this.spanIds.set(n,t)),t};this.pruneStaleTraces=n=>{for(let[t,o]of this.lastSeenByTrace){if(n-o<=this.traceTtlMs)continue;let r=this.otelSpansByTrace.get(t);if(r)for(let s of r.keys())this.spanIds.delete(s);this.otelSpansByTrace.delete(t),this.traceIds.delete(t),this.lastSeenByTrace.delete(t);}};this.getErrorInfo=n=>{var a,p,d;if(n.status.code!==api.SpanStatusCode.ERROR)return;let t=n.events.find(g=>g.name==="exception");if(!t)return {exceptionType:"Error",message:n.status.message||"An error occurred",traceback:""};let{attributes:o}=t,r=((a=o==null?void 0:o["exception.type"])==null?void 0:a.toString())||"Error",s=(p=o==null?void 0:o["exception.message"])==null?void 0:p.toString(),i=((d=o==null?void 0:o["exception.stacktrace"])==null?void 0:d.toString())||"";return {exceptionType:r,message:s,traceback:i}};this.resolveThreadId=n=>{for(let t of n){let o=K(t.attributes);if(o!=null)return o}return this.threadId};this.shutdown=async()=>{await this.client.flush();};this.forceFlush=async()=>{await this.client.flush();};this.export=async(n,t)=>{let o=n.filter(a=>{var p;return Y.has((p=Z(a))!=null?p:"")}),r=n.length-o.length;if(r>0&&opik.logger.debug(`Ignored ${r} non-AI SDK spans`),o.length===0){opik.logger.debug("No AI SDK spans found"),t({code:0});return}opik.logger.debug("Exporting spans",o);let s=new Set;for(let a of o){let p=a.spanContext().traceId;s.add(p);let d=this.otelSpansByTrace.get(p);d||(d=new Map,this.otelSpansByTrace.set(p,d)),d.set(a.spanContext().spanId,a);}let i=Date.now();for(let a of s)this.lastSeenByTrace.set(a,i),this.exportTrace(a);this.pruneStaleTraces(i);try{await this.client.flush(),t({code:0});}catch(a){opik.logger.error("Error exporting spans",a),t({code:1,error:a instanceof Error?a:new Error("Unknown error")});}};this.client=n,this.tags=[...t],this.metadata={...o},this.threadId=r,this.traceTtlMs=s;}exportTrace(n){var M,A,O,N,j;let t=[...(A=(M=this.otelSpansByTrace.get(n))==null?void 0:M.values())!=null?A:[]];if(t.length===0)return;let o=this.opikTraceId(n),r=this.client.config.projectName,s=se(t),i=ae(t,s),a=i.spanContext().spanId,p=v(t.filter(u=>b(u.attributes)==="llm")),d=q(h(i.attributes),()=>ne(p,h)),g=q(S(i.attributes),()=>te(p,S)),c=(O=this.getErrorInfo(i))!=null?O:ie(t,this.getErrorInfo);this.client.traceBatchQueue.create({id:o,name:(j=(N=i.attributes["ai.telemetry.metadata.traceName"])==null?void 0:N.toString())!=null?j:i.name,startTime:new Date(Math.min(...t.map(f))),endTime:new Date(Math.max(...t.map(E))),input:d,output:g,metadata:{...oe(t),...this.metadata},tags:this.tags,threadId:this.resolveThreadId(t),projectName:r,...c&&{errorInfo:c}});for(let u of t){let _=u.spanContext().spanId;if(_===a)continue;let k=s.get(_),B=k!=null&&k!==a?this.opikSpanId(k):void 0,D=this.getErrorInfo(u),P=b(u.attributes),V=P==="llm"?{...re(u.attributes),usage:U(u.attributes)}:{};this.client.spanBatchQueue.create({id:this.opikSpanId(_),traceId:o,...B?{parentSpanId:B}:{},name:u.name,type:P,startTime:new Date(f(u)),endTime:new Date(E(u)),input:h(u.attributes),output:S(u.attributes),metadata:x(u.attributes),...V,projectName:r,...D&&{errorInfo:D}});}}static getSettings(n){var o,r,s;let t={...n.metadata};return n.name&&(t.traceName=n.name),{isEnabled:(o=n.isEnabled)!=null?o:true,recordInputs:(r=n.recordInputs)!=null?r:true,recordOutputs:(s=n.recordOutputs)!=null?s:true,functionId:n.functionId,metadata:t}}};function Z(e){var t,o,r,s;let n=e;return (s=(r=(t=n.instrumentationScope)==null?void 0:t.name)!=null?r:(o=n.instrumentationLibrary)==null?void 0:o.name)!=null?s:void 0}function W(e){return e[0]*1e3+e[1]/1e6}function f(e){return W(e.startTime)}function E(e){return W(e.endTime)}function v(e){return [...e].sort((n,t)=>f(n)-f(t))}function ee(e){let n=e.map(o=>{let r=f(o),s=E(o);return {id:o.spanContext().spanId,start:r,end:s,duration:s-r}}),t=new Map;for(let o of n){let r;for(let s of n){if(s.id===o.id)continue;s.start<=o.start&&s.end>=o.end&&s.duration>o.duration&&(!r||s.duration<r.duration)&&(r=s);}t.set(o.id,r==null?void 0:r.id);}return t}function ne(e,n){for(let t of e){let o=n(t.attributes);if(Object.keys(o).length>0)return o}return {}}function te(e,n){for(let t=e.length-1;t>=0;t--){let o=n(e[t].attributes);if(Object.keys(o).length>0)return o}return {}}function oe(e){let n={};for(let t of e)n={...n,...x(t.attributes)};return n}function re(e){let n=R(e),t=w(e);return {...n?{model:n}:{},...t?{provider:t}:{}}}function se(e){var r;let n=new Set(e.map(s=>s.spanContext().spanId)),t=ee(e),o=new Map;for(let s of e){let i=s.spanContext().spanId,a=(r=s.parentSpanContext)==null?void 0:r.spanId;o.set(i,a!=null&&n.has(a)?a:t.get(i));}return o}function ae(e,n){let t=e.filter(o=>n.get(o.spanContext().spanId)==null);return v(t.length>0?t:e)[0]}function q(e,n){return Object.keys(e).length>0?e:n()}function ie(e,n){for(let t of v(e)){let o=n(t);if(o)return o}}exports.OpikExporter=C;
package/dist/index.d.cts CHANGED
@@ -1,10 +1,9 @@
1
1
  import { AttributeValue, Tracer } from '@opentelemetry/api';
2
2
  import { NodeSDKConfiguration } from '@opentelemetry/sdk-node';
3
- import { Opik, Span, Trace } from 'opik';
3
+ import { Opik } from 'opik';
4
4
 
5
5
  type SpanExporter = NodeSDKConfiguration["traceExporter"];
6
6
  type ExportFunction = SpanExporter["export"];
7
- type ReadableSpan = Parameters<ExportFunction>[0][0];
8
7
  type TelemetrySettings = {
9
8
  isEnabled?: boolean;
10
9
  recordInputs?: boolean;
@@ -21,29 +20,33 @@ type OpikExporterOptions = {
21
20
  tags?: string[];
22
21
  metadata?: Record<string, AttributeValue>;
23
22
  threadId?: string;
23
+ /**
24
+ * How long (ms) to retain a trace's accumulated spans/ids after its last
25
+ * activity before pruning them, to bound memory in long-running processes.
26
+ * A trace idle longer than this is assumed complete. Defaults to 10 minutes.
27
+ */
28
+ traceTtlMs?: number;
24
29
  };
25
30
  declare class OpikExporter implements SpanExporter {
26
- private readonly traces;
27
- private readonly spans;
31
+ private readonly otelSpansByTrace;
32
+ private readonly traceIds;
33
+ private readonly spanIds;
34
+ private readonly lastSeenByTrace;
28
35
  private readonly client;
29
36
  private readonly tags;
30
37
  private readonly metadata;
31
38
  private readonly threadId?;
32
- constructor({ client, tags, metadata, threadId, }?: OpikExporterOptions);
33
- private getSpanInput;
34
- private getSpanOutput;
35
- private getSpanMetadata;
36
- private getSpanUsage;
37
- private getThreadId;
39
+ private readonly traceTtlMs;
40
+ constructor({ client, tags, metadata, threadId, traceTtlMs, }?: OpikExporterOptions);
41
+ private opikTraceId;
42
+ private opikSpanId;
43
+ private pruneStaleTraces;
38
44
  private getErrorInfo;
39
- processSpan: ({ otelSpan, parentSpan, trace, }: {
40
- otelSpan: ReadableSpan;
41
- parentSpan?: Span;
42
- trace: Trace;
43
- }) => Span;
45
+ private resolveThreadId;
44
46
  shutdown: () => Promise<void>;
45
47
  forceFlush: () => Promise<void>;
46
48
  export: ExportFunction;
49
+ private exportTrace;
47
50
  static getSettings(settings: OpikExporterSettings): TelemetrySettings;
48
51
  }
49
52
 
package/dist/index.d.ts CHANGED
@@ -1,10 +1,9 @@
1
1
  import { AttributeValue, Tracer } from '@opentelemetry/api';
2
2
  import { NodeSDKConfiguration } from '@opentelemetry/sdk-node';
3
- import { Opik, Span, Trace } from 'opik';
3
+ import { Opik } from 'opik';
4
4
 
5
5
  type SpanExporter = NodeSDKConfiguration["traceExporter"];
6
6
  type ExportFunction = SpanExporter["export"];
7
- type ReadableSpan = Parameters<ExportFunction>[0][0];
8
7
  type TelemetrySettings = {
9
8
  isEnabled?: boolean;
10
9
  recordInputs?: boolean;
@@ -21,29 +20,33 @@ type OpikExporterOptions = {
21
20
  tags?: string[];
22
21
  metadata?: Record<string, AttributeValue>;
23
22
  threadId?: string;
23
+ /**
24
+ * How long (ms) to retain a trace's accumulated spans/ids after its last
25
+ * activity before pruning them, to bound memory in long-running processes.
26
+ * A trace idle longer than this is assumed complete. Defaults to 10 minutes.
27
+ */
28
+ traceTtlMs?: number;
24
29
  };
25
30
  declare class OpikExporter implements SpanExporter {
26
- private readonly traces;
27
- private readonly spans;
31
+ private readonly otelSpansByTrace;
32
+ private readonly traceIds;
33
+ private readonly spanIds;
34
+ private readonly lastSeenByTrace;
28
35
  private readonly client;
29
36
  private readonly tags;
30
37
  private readonly metadata;
31
38
  private readonly threadId?;
32
- constructor({ client, tags, metadata, threadId, }?: OpikExporterOptions);
33
- private getSpanInput;
34
- private getSpanOutput;
35
- private getSpanMetadata;
36
- private getSpanUsage;
37
- private getThreadId;
39
+ private readonly traceTtlMs;
40
+ constructor({ client, tags, metadata, threadId, traceTtlMs, }?: OpikExporterOptions);
41
+ private opikTraceId;
42
+ private opikSpanId;
43
+ private pruneStaleTraces;
38
44
  private getErrorInfo;
39
- processSpan: ({ otelSpan, parentSpan, trace, }: {
40
- otelSpan: ReadableSpan;
41
- parentSpan?: Span;
42
- trace: Trace;
43
- }) => Span;
45
+ private resolveThreadId;
44
46
  shutdown: () => Promise<void>;
45
47
  forceFlush: () => Promise<void>;
46
48
  export: ExportFunction;
49
+ private exportTrace;
47
50
  static getSettings(settings: OpikExporterSettings): TelemetrySettings;
48
51
  }
49
52
 
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- import {SpanStatusCode}from'@opentelemetry/api';import {Opik,logger}from'opik';var C=new Opik,f=class{constructor({client:n=C,tags:e=[],metadata:t={},threadId:s}={}){this.traces=new Map;this.spans=new Map;this.getSpanInput=n=>{let e={},{attributes:t}=n;return Object.keys(t).forEach(a=>{if(a==="ai.prompt"||a==="gen_ai.request"){let o=T(t[a]);o&&(e={...e,...o});}if(a.startsWith("ai.prompt.")){let o=a.replace("ai.prompt.","");e[o]=l(t[a]);}if(a.startsWith("gen_ai.request.")){let o=a.replace("gen_ai.request.","");e[o]=l(t[a]);}}),Object.keys(e).length>0||("ai.toolCall.name"in t&&(e.toolName=t["ai.toolCall.name"]),"ai.toolCall.args"in t&&(e.args=t["ai.toolCall.args"])),e};this.getSpanOutput=n=>{let{attributes:e}=n;return e["ai.response.text"]?{text:e["ai.response.text"]}:e["ai.response.object"]?{object:l(e["ai.response.object"])}:e["ai.toolCall.result"]?{result:e["ai.toolCall.result"]}:e["ai.response.toolCalls"]?{toolCalls:l(e["ai.response.toolCalls"])}:{}};this.getSpanMetadata=n=>{let{attributes:e}=n,t={};return e["gen_ai.response.model"]&&(t.model=e["gen_ai.response.model"]),e["gen_ai.system"]&&(t.system=e["gen_ai.system"]),t};this.getSpanUsage=n=>{let{attributes:e}=n,t={};return "ai.usage.promptTokens"in e&&(t.prompt_tokens=e["ai.usage.promptTokens"]),"gen_ai.usage.input_tokens"in e&&(t.prompt_tokens=e["gen_ai.usage.input_tokens"]),"ai.usage.completionTokens"in e&&(t.completion_tokens=e["ai.usage.completionTokens"]),"gen_ai.usage.output_tokens"in e&&(t.completion_tokens=e["gen_ai.usage.output_tokens"]),("prompt_tokens"in t||"completion_tokens"in t)&&(t.total_tokens=(t.prompt_tokens||0)+(t.completion_tokens||0)),t};this.getThreadId=n=>{var t;let{attributes:e}=n;return e["ai.telemetry.metadata.threadId"]!=null?(t=e["ai.telemetry.metadata.threadId"])==null?void 0:t.toString():this.threadId};this.getErrorInfo=n=>{var u,r,p;if(n.status.code!==SpanStatusCode.ERROR)return;let e=n.events.find(c=>c.name==="exception");if(!e)return {exceptionType:"Error",message:n.status.message||"An error occurred",traceback:""};let{attributes:t}=e,s=((u=t==null?void 0:t["exception.type"])==null?void 0:u.toString())||"Error",a=(r=t==null?void 0:t["exception.message"])==null?void 0:r.toString(),o=((p=t==null?void 0:t["exception.stacktrace"])==null?void 0:p.toString())||"";return {exceptionType:s,message:a,traceback:o}};this.processSpan=({otelSpan:n,parentSpan:e,trace:t})=>{let s=this.getErrorInfo(n);return t.span({name:n.name,startTime:new Date(g(n.startTime)),endTime:new Date(g(n.endTime)),parentSpanId:e==null?void 0:e.data.id,input:this.getSpanInput(n),output:this.getSpanOutput(n),metadata:this.getSpanMetadata(n),usage:this.getSpanUsage(n),type:"llm",...s&&{errorInfo:s}})};this.shutdown=async()=>{await this.client.flush();};this.forceFlush=async()=>{await this.client.flush();};this.export=async(n,e)=>{let t=n.filter(o=>O(o)==="ai"),s=n.length-t.length;if(s>0&&logger.debug(`Ignored ${s} non-AI SDK spans`),t.length===0){logger.debug("No AI SDK spans found"),e({code:0});return}let a=w(t);logger.debug("Exporting spans",t),Object.entries(a).forEach(([o,u])=>{var h,b;let[r,...p]=u,c=this.getErrorInfo(r),S=this.client.trace({startTime:new Date(g(r.startTime)),endTime:new Date(g(r.endTime)),name:(b=(h=r.attributes["ai.telemetry.metadata.traceName"])==null?void 0:h.toString())!=null?b:r.name,input:this.getSpanInput(r),output:this.getSpanOutput(r),metadata:{...this.getSpanMetadata(r),...this.metadata},tags:this.tags,usage:this.getSpanUsage(r),threadId:this.getThreadId(r),...c&&{errorInfo:c}});this.traces.set(o,S),p.forEach(m=>{var y,I;let x=this.spans.get((I=(y=m.parentSpanContext)==null?void 0:y.spanId)!=null?I:""),R=this.processSpan({parentSpan:x,otelSpan:m,trace:S});this.spans.set(m.spanContext().spanId,R);});});try{await this.client.flush(),e({code:0});}catch(o){logger.error("Error exporting spans",o),e({code:1,error:o instanceof Error?o:new Error("Unknown error")});}};this.client=n,this.tags=[...e],this.metadata={...t},this.threadId=s;}static getSettings(n){var t,s,a;let e={...n.metadata};return n.name&&(e.traceName=n.name),{isEnabled:(t=n.isEnabled)!=null?t:true,recordInputs:(s=n.recordInputs)!=null?s:true,recordOutputs:(a=n.recordOutputs)!=null?a:true,functionId:n.functionId,metadata:e}}};function w(i){let n={};return i.forEach(e=>{let t=e.spanContext();n[t.traceId]||(n[t.traceId]=[]),n[t.traceId].push(e);}),Object.entries(n).forEach(([e,t])=>{n[e]=_(t);}),n}function O(i){var e,t,s,a;let n=i;return (a=(s=(e=n.instrumentationScope)==null?void 0:e.name)!=null?s:(t=n.instrumentationLibrary)==null?void 0:t.name)!=null?a:void 0}function g(i){return i[0]*1e3+i[1]/1e6}function l(i){try{return JSON.parse(i)}catch{return i}}function T(i){if(typeof i=="string")try{let n=JSON.parse(i);if(n!==null&&typeof n=="object"&&!Array.isArray(n))return n}catch{return}}function _(i){var o,u;let n=new Map,e=new Map;for(let r of i){let{spanId:p}=r.spanContext(),c=(u=(o=r.parentSpanContext)==null?void 0:o.spanId)!=null?u:"";n.set(p,r),c&&(e.has(c)||e.set(c,[]),e.get(c).push(r));}let t=i.filter(r=>{var p;return !((p=r.parentSpanContext)!=null&&p.spanId)||!n.has(r.parentSpanContext.spanId)}),s=[],a=[...t];for(;a.length>0;){let r=a.shift();s.push(r);let p=r.spanContext().spanId,c=e.get(p)||[];a.push(...c);}return s}export{f as OpikExporter};
1
+ import {SpanStatusCode}from'@opentelemetry/api';import {Opik,generateId,logger}from'opik';function l(e){try{return JSON.parse(e)}catch{return e}}function F(e){if(typeof e=="string")try{let n=JSON.parse(e);if(n!==null&&typeof n=="object"&&!Array.isArray(n))return n}catch{return}}function T(e){return typeof e=="string"?e:JSON.stringify(e)}function z(e){var a,p,d;if(e==null||typeof e!="object")return [];let{role:n,parts:t}=e,o=n!=null?n:"user";if(!Array.isArray(t))return [e];let r=[],s=[],i=[];for(let g of t){if(g==null||typeof g!="object")continue;let c=g;c.type==="text"?s.push(String((p=(a=c.content)!=null?a:c.text)!=null?p:"")):c.type==="tool_call"?i.push({id:c.id,type:"function",function:{name:c.name,arguments:T(c.arguments)}}):c.type==="tool_call_response"&&r.push({role:"tool",tool_call_id:c.id,content:T((d=c.response)!=null?d:c.output)});}if(s.length>0||i.length>0){let g={role:o};(s.length>0||i.length===0)&&(g.content=s.join("")),i.length>0&&(g.tool_calls=i),r.unshift(g);}return r}function L(e){return Array.isArray(e)?e.flatMap(z):[]}var I={input(e){let n={};for(let t of Object.keys(e))if(t==="ai.prompt"){let o=F(e[t]);o&&(n={...n,...o});}else t.startsWith("ai.prompt.")&&(n[t.slice(10)]=l(e[t]));return "ai.toolCall.name"in e&&(n.toolName=e["ai.toolCall.name"]),"ai.toolCall.args"in e&&(n.args=l(e["ai.toolCall.args"])),n},output(e){let n={};return e["ai.response.text"]&&(n.text=e["ai.response.text"]),e["ai.response.object"]&&(n.object=l(e["ai.response.object"])),e["ai.response.toolCalls"]&&(n.toolCalls=l(e["ai.response.toolCalls"])),e["ai.toolCall.result"]&&(n.result=l(e["ai.toolCall.result"])),n},usage(e){let n={};return "ai.usage.promptTokens"in e&&(n.prompt_tokens=Number(e["ai.usage.promptTokens"])),"ai.usage.completionTokens"in e&&(n.completion_tokens=Number(e["ai.usage.completionTokens"])),n}},m={input(e){let n={};for(let t of Object.keys(e))if(t==="gen_ai.request"){let o=F(e[t]);o&&(n={...n,...o});}else t.startsWith("gen_ai.request.")&&(n[t.slice(15)]=l(e[t]));if("gen_ai.input.messages"in e){let t=[];if("gen_ai.system_instructions"in e){let o=l(e["gen_ai.system_instructions"]),r=Array.isArray(o)?o.map(s=>{var i,a;return String(s&&typeof s=="object"?(a=(i=s.content)!=null?i:s.text)!=null?a:"":s)}).join(""):T(o);t.push({role:"system",content:r});}n.messages=[...t,...L(l(e["gen_ai.input.messages"]))];}return "gen_ai.tool.name"in e&&(n.toolName=e["gen_ai.tool.name"]),"gen_ai.tool.call.arguments"in e&&(n.args=l(e["gen_ai.tool.call.arguments"])),n},output(e){let n={};if("gen_ai.output.messages"in e){let t=l(e["gen_ai.output.messages"]),o=Array.isArray(t)?t:[];n.choices=L(t).map((r,s)=>{var p;let i=o[s],a=i&&typeof i=="object"&&(p=i.finish_reason)!=null?p:null;return {index:s,message:r,finish_reason:a}});}return "gen_ai.tool.call.result"in e&&(n.result=l(e["gen_ai.tool.call.result"])),n},usage(e){let n={};return "gen_ai.usage.input_tokens"in e&&(n.prompt_tokens=Number(e["gen_ai.usage.input_tokens"])),"gen_ai.usage.output_tokens"in e&&(n.completion_tokens=Number(e["gen_ai.usage.output_tokens"])),"gen_ai.usage.cache_read.input_tokens"in e&&(n["original_usage.cache_read_input_tokens"]=Number(e["gen_ai.usage.cache_read.input_tokens"])),"gen_ai.usage.cache_creation.input_tokens"in e&&(n["original_usage.cache_creation_input_tokens"]=Number(e["gen_ai.usage.cache_creation.input_tokens"])),n},metadata(e){let n={},t=R(e);t&&(n.model=t);let o=w(e);o&&(n.provider=o),"gen_ai.operation.name"in e&&(n.operation=e["gen_ai.operation.name"]),"gen_ai.response.finish_reasons"in e&&(n.finish_reasons=l(e["gen_ai.response.finish_reasons"]));for(let r of Object.keys(e))r.startsWith("eve.")?n[r]=e[r]:r.startsWith("ai.settings.context.eve.")&&(n[r.slice(20)]=e[r]);return n}};function h(e){return {...I.input(e),...m.input(e)}}function S(e){return {...I.output(e),...m.output(e)}}function x(e){return m.metadata(e)}function U(e){let n={...I.usage(e),...m.usage(e)};return ("prompt_tokens"in n||"completion_tokens"in n)&&(n.total_tokens=(n.prompt_tokens||0)+(n.completion_tokens||0)),n}var Q=new Set(["chat","text_completion","generate_content","embeddings"]);function R(e){var t;let n=(t=e["gen_ai.response.model"])!=null?t:e["gen_ai.request.model"];return n!=null?String(n):void 0}function w(e){var t;let n=(t=e["gen_ai.system"])!=null?t:e["gen_ai.provider.name"];return n!=null?String(n):void 0}function b(e){let n=e["gen_ai.operation.name"];return n==="execute_tool"||"ai.toolCall.name"in e||"gen_ai.tool.name"in e?"tool":typeof n=="string"?Q.has(n)?"llm":"general":"ai.prompt"in e||"ai.response.text"in e||"ai.response.object"in e||"ai.usage.promptTokens"in e||"ai.usage.completionTokens"in e?"llm":"general"}function K(e){if(e["ai.telemetry.metadata.threadId"]!=null)return e["ai.telemetry.metadata.threadId"].toString();if(e["eve.session.id"]!=null)return e["eve.session.id"].toString();if(e["ai.settings.context.eve.session.id"]!=null)return e["ai.settings.context.eve.session.id"].toString()}var H=new Opik,X=600*1e3,Y=new Set(["ai","gen_ai","eve"]),C=class{constructor({client:n=H,tags:t=[],metadata:o={},threadId:r,traceTtlMs:s=X}={}){this.otelSpansByTrace=new Map;this.traceIds=new Map;this.spanIds=new Map;this.lastSeenByTrace=new Map;this.opikTraceId=n=>{let t=this.traceIds.get(n);return t||(t=generateId(),this.traceIds.set(n,t)),t};this.opikSpanId=n=>{let t=this.spanIds.get(n);return t||(t=generateId(),this.spanIds.set(n,t)),t};this.pruneStaleTraces=n=>{for(let[t,o]of this.lastSeenByTrace){if(n-o<=this.traceTtlMs)continue;let r=this.otelSpansByTrace.get(t);if(r)for(let s of r.keys())this.spanIds.delete(s);this.otelSpansByTrace.delete(t),this.traceIds.delete(t),this.lastSeenByTrace.delete(t);}};this.getErrorInfo=n=>{var a,p,d;if(n.status.code!==SpanStatusCode.ERROR)return;let t=n.events.find(g=>g.name==="exception");if(!t)return {exceptionType:"Error",message:n.status.message||"An error occurred",traceback:""};let{attributes:o}=t,r=((a=o==null?void 0:o["exception.type"])==null?void 0:a.toString())||"Error",s=(p=o==null?void 0:o["exception.message"])==null?void 0:p.toString(),i=((d=o==null?void 0:o["exception.stacktrace"])==null?void 0:d.toString())||"";return {exceptionType:r,message:s,traceback:i}};this.resolveThreadId=n=>{for(let t of n){let o=K(t.attributes);if(o!=null)return o}return this.threadId};this.shutdown=async()=>{await this.client.flush();};this.forceFlush=async()=>{await this.client.flush();};this.export=async(n,t)=>{let o=n.filter(a=>{var p;return Y.has((p=Z(a))!=null?p:"")}),r=n.length-o.length;if(r>0&&logger.debug(`Ignored ${r} non-AI SDK spans`),o.length===0){logger.debug("No AI SDK spans found"),t({code:0});return}logger.debug("Exporting spans",o);let s=new Set;for(let a of o){let p=a.spanContext().traceId;s.add(p);let d=this.otelSpansByTrace.get(p);d||(d=new Map,this.otelSpansByTrace.set(p,d)),d.set(a.spanContext().spanId,a);}let i=Date.now();for(let a of s)this.lastSeenByTrace.set(a,i),this.exportTrace(a);this.pruneStaleTraces(i);try{await this.client.flush(),t({code:0});}catch(a){logger.error("Error exporting spans",a),t({code:1,error:a instanceof Error?a:new Error("Unknown error")});}};this.client=n,this.tags=[...t],this.metadata={...o},this.threadId=r,this.traceTtlMs=s;}exportTrace(n){var M,A,O,N,j;let t=[...(A=(M=this.otelSpansByTrace.get(n))==null?void 0:M.values())!=null?A:[]];if(t.length===0)return;let o=this.opikTraceId(n),r=this.client.config.projectName,s=se(t),i=ae(t,s),a=i.spanContext().spanId,p=v(t.filter(u=>b(u.attributes)==="llm")),d=q(h(i.attributes),()=>ne(p,h)),g=q(S(i.attributes),()=>te(p,S)),c=(O=this.getErrorInfo(i))!=null?O:ie(t,this.getErrorInfo);this.client.traceBatchQueue.create({id:o,name:(j=(N=i.attributes["ai.telemetry.metadata.traceName"])==null?void 0:N.toString())!=null?j:i.name,startTime:new Date(Math.min(...t.map(f))),endTime:new Date(Math.max(...t.map(E))),input:d,output:g,metadata:{...oe(t),...this.metadata},tags:this.tags,threadId:this.resolveThreadId(t),projectName:r,...c&&{errorInfo:c}});for(let u of t){let _=u.spanContext().spanId;if(_===a)continue;let k=s.get(_),B=k!=null&&k!==a?this.opikSpanId(k):void 0,D=this.getErrorInfo(u),P=b(u.attributes),V=P==="llm"?{...re(u.attributes),usage:U(u.attributes)}:{};this.client.spanBatchQueue.create({id:this.opikSpanId(_),traceId:o,...B?{parentSpanId:B}:{},name:u.name,type:P,startTime:new Date(f(u)),endTime:new Date(E(u)),input:h(u.attributes),output:S(u.attributes),metadata:x(u.attributes),...V,projectName:r,...D&&{errorInfo:D}});}}static getSettings(n){var o,r,s;let t={...n.metadata};return n.name&&(t.traceName=n.name),{isEnabled:(o=n.isEnabled)!=null?o:true,recordInputs:(r=n.recordInputs)!=null?r:true,recordOutputs:(s=n.recordOutputs)!=null?s:true,functionId:n.functionId,metadata:t}}};function Z(e){var t,o,r,s;let n=e;return (s=(r=(t=n.instrumentationScope)==null?void 0:t.name)!=null?r:(o=n.instrumentationLibrary)==null?void 0:o.name)!=null?s:void 0}function W(e){return e[0]*1e3+e[1]/1e6}function f(e){return W(e.startTime)}function E(e){return W(e.endTime)}function v(e){return [...e].sort((n,t)=>f(n)-f(t))}function ee(e){let n=e.map(o=>{let r=f(o),s=E(o);return {id:o.spanContext().spanId,start:r,end:s,duration:s-r}}),t=new Map;for(let o of n){let r;for(let s of n){if(s.id===o.id)continue;s.start<=o.start&&s.end>=o.end&&s.duration>o.duration&&(!r||s.duration<r.duration)&&(r=s);}t.set(o.id,r==null?void 0:r.id);}return t}function ne(e,n){for(let t of e){let o=n(t.attributes);if(Object.keys(o).length>0)return o}return {}}function te(e,n){for(let t=e.length-1;t>=0;t--){let o=n(e[t].attributes);if(Object.keys(o).length>0)return o}return {}}function oe(e){let n={};for(let t of e)n={...n,...x(t.attributes)};return n}function re(e){let n=R(e),t=w(e);return {...n?{model:n}:{},...t?{provider:t}:{}}}function se(e){var r;let n=new Set(e.map(s=>s.spanContext().spanId)),t=ee(e),o=new Map;for(let s of e){let i=s.spanContext().spanId,a=(r=s.parentSpanContext)==null?void 0:r.spanId;o.set(i,a!=null&&n.has(a)?a:t.get(i));}return o}function ae(e,n){let t=e.filter(o=>n.get(o.spanContext().spanId)==null);return v(t.length>0?t:e)[0]}function q(e,n){return Object.keys(e).length>0?e:n()}function ie(e,n){for(let t of v(e)){let o=n(t);if(o)return o}}export{C as OpikExporter};
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "opik-vercel",
3
3
  "description": "Opik TypeScript and JavaScript SDK integration with Vercel AI SDK",
4
- "version": "2.0.74",
4
+ "version": "2.0.76",
5
5
  "engines": {
6
6
  "node": ">=18"
7
7
  },
@@ -64,6 +64,7 @@
64
64
  "@opentelemetry/sdk-node": ">=0.56.0"
65
65
  },
66
66
  "devDependencies": {
67
+ "@ai-sdk/anthropic": "^3.0.85",
67
68
  "@eslint/js": "^9.38.0",
68
69
  "@opentelemetry/auto-instrumentations-node": "^0.66.0",
69
70
  "ai": "^6.0.13",