Skip to content

Commit 5b5e856

Browse files
committed
codify input and output parsing
1 parent 101e986 commit 5b5e856

File tree

1 file changed

+114
-10
lines changed
  • js/packages/openinference-core/src/decorators

1 file changed

+114
-10
lines changed

js/packages/openinference-core/src/decorators/index.ts

Lines changed: 114 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -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";
88
import { OITracer } from "trace";
99
import { safelyJSONStringify } from "utils";
1010

1111
const DEFAULT_TRACER_NAME = "openinference-core";
1212

1313
const { OPENINFERENCE_SPAN_KIND, INPUT_VALUE, OUTPUT_VALUE, INPUT_MIME_TYPE } =
1414
SemanticConventions;
15+
1516
export 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

Comments
 (0)