@@ -4,14 +4,15 @@ import {
44 MimeType ,
55 OUTPUT_MIME_TYPE ,
66} from "@arizeai/openinference-semantic-conventions" ;
7- import { type Tracer , trace } from "@opentelemetry/api" ;
7+ import { Attributes , type Tracer , trace } from "@opentelemetry/api" ;
88import { OITracer } from "trace" ;
99import { safelyJSONStringify } from "utils" ;
1010
1111const DEFAULT_TRACER_NAME = "openinference-core" ;
1212
1313const { OPENINFERENCE_SPAN_KIND , INPUT_VALUE , OUTPUT_VALUE , INPUT_MIME_TYPE } =
1414 SemanticConventions ;
15+
1516export interface TraceDecoratorOptions {
1617 /**
1718 * An optional name for the span. If not provided, the name if the decorated function will be used
@@ -24,13 +25,37 @@ export interface TraceDecoratorOptions {
2425 /**
2526 * A callback function that will be used to set the input attribute of the span from the arguments of the function.
2627 */
27- processInput ?: ( args : unknown ) => string ;
28+ processInput ?:
29+ | ( ( args : unknown ) => string )
30+ | ( ( args : unknown [ ] ) => SpanInput | undefined ) ;
2831 /**
2932 * A callback function that will be used to set the output attribute of the span from the result of the function.
3033 */
3134 processOutput ?: ( result : unknown ) => string ;
3235}
3336
37+ export type SpanInput = {
38+ /**
39+ * the textual representation of the input
40+ */
41+ value : string ;
42+ /**
43+ * the MIME type of the input
44+ */
45+ mimeType : MimeType ;
46+ } ;
47+
48+ export type SpanOutput = {
49+ /**
50+ * the textual representation of the output
51+ */
52+ value : string ;
53+ /**
54+ * the MIME type of the output
55+ */
56+ mimeType : MimeType ;
57+ } ;
58+
3459/**
3560 * A decorator factory for tracing chain operations in an LLM application.
3661 */
@@ -42,8 +67,8 @@ export function chain<Target>(options: TraceDecoratorOptions) {
4267 processOutput : _processOutput ,
4368 } = options ;
4469 const tracer : OITracer = _tracer ? wrapTracer ( _tracer ) : getTracer ( ) ;
45- const processInput = _processInput ?? safelyJSONStringify ;
46- const processOutput = _processOutput ?? safelyJSONStringify ;
70+ const processInput = _processInput ?? defaultProcessInput ;
71+ const processOutput = _processOutput ?? defaultProcessOutput ;
4772 // TODO: infer the name from the target
4873 return function (
4974 target : Target ,
@@ -53,22 +78,19 @@ export function chain<Target>(options: TraceDecoratorOptions) {
5378 const originalFn = descriptor . value ! ;
5479 // override the value to wrap the original function in a span
5580 descriptor . value = function ( ...args : unknown [ ] ) {
81+ const input = processInput ( args ) ;
5682 return tracer . startActiveSpan (
5783 name ,
5884 {
5985 attributes : {
6086 [ OPENINFERENCE_SPAN_KIND ] : OpenInferenceSpanKind . CHAIN ,
61- [ INPUT_VALUE ] : processInput ( args ) ?? undefined ,
62- // TODO: infer the mime type from the arguments
63- [ INPUT_MIME_TYPE ] : MimeType . JSON ,
87+ ...toInputAttributes ( input ) ,
6488 } ,
6589 } ,
6690 ( span ) => {
6791 const result = originalFn . apply ( this , args ) ;
6892 span . setAttributes ( {
69- [ OUTPUT_VALUE ] : processOutput ( result ) ?? undefined ,
70- // TODO: infer the mime type from the result
71- [ OUTPUT_MIME_TYPE ] : MimeType . JSON ,
93+ ...toOutputAttributes ( processOutput ( result ) ) ,
7294 } ) ;
7395 // TODO: set the status of the span based on the result
7496 span . end ( ) ;
@@ -80,6 +102,88 @@ export function chain<Target>(options: TraceDecoratorOptions) {
80102 return descriptor ;
81103 } ;
82104}
105+
106+ /**
107+ * The default input processor that safely JSON stringifies the arguments.
108+ * @param args The arguments to process
109+ * @returns The safely JSON stringified arguments
110+ */
111+ function defaultProcessInput ( args : unknown [ ] ) : SpanInput | undefined {
112+ if ( args . length === 1 ) {
113+ const value = args [ 0 ] ;
114+ if ( typeof value === "string" ) {
115+ return {
116+ value,
117+ mimeType : MimeType . TEXT ,
118+ } ;
119+ }
120+ return {
121+ value : safelyJSONStringify ( value ) ?? "{}" ,
122+ mimeType : MimeType . JSON ,
123+ } ;
124+ }
125+ const value = safelyJSONStringify ( args ) ;
126+ if ( value == null ) {
127+ return undefined ;
128+ }
129+ return {
130+ value,
131+ mimeType : MimeType . JSON ,
132+ } ;
133+ }
134+
135+ function defaultProcessOutput ( result : unknown ) : SpanOutput | undefined {
136+ if ( result == null ) {
137+ return undefined ;
138+ }
139+ if ( typeof result === "string" ) {
140+ return {
141+ value : result ,
142+ mimeType : MimeType . TEXT ,
143+ } ;
144+ }
145+ return {
146+ value : safelyJSONStringify ( result ) ?? "{}" ,
147+ mimeType : MimeType . JSON ,
148+ } ;
149+ }
150+ /**
151+ * A helper function to convert a SpanOutput to OpenTelemetry attributes.
152+ * @param output The SpanOutput to convert
153+ * @returns The OpenTelemetry attributes
154+ */
155+ function toOutputAttributes (
156+ output : SpanOutput | string | undefined ,
157+ ) : Attributes {
158+ if ( output == null ) {
159+ return { } ;
160+ }
161+ if ( typeof output === "string" ) {
162+ return {
163+ [ OUTPUT_VALUE ] : output ,
164+ [ OUTPUT_MIME_TYPE ] : MimeType . TEXT ,
165+ } ;
166+ }
167+ return {
168+ [ OUTPUT_VALUE ] : output . value ,
169+ [ OUTPUT_MIME_TYPE ] : output . mimeType ,
170+ } ;
171+ }
172+ function toInputAttributes ( input : SpanInput | string | undefined ) : Attributes {
173+ if ( input == null ) {
174+ return { } ;
175+ }
176+ if ( typeof input === "string" ) {
177+ return {
178+ [ INPUT_VALUE ] : input ,
179+ [ INPUT_MIME_TYPE ] : MimeType . TEXT ,
180+ } ;
181+ }
182+ return {
183+ [ INPUT_VALUE ] : input . value ,
184+ [ INPUT_MIME_TYPE ] : input . mimeType ,
185+ } ;
186+ }
83187/**
84188 * A function that ensures the tracer is wrapped in an OITracer if necessary.
85189 * @param tracer The tracer to wrap if necessary
0 commit comments