openlayer 0.4.0 → 0.7.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.
Files changed (112) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/README.md +6 -6
  3. package/index.d.mts +5 -0
  4. package/index.d.ts +5 -0
  5. package/index.d.ts.map +1 -1
  6. package/index.js +2 -0
  7. package/index.js.map +1 -1
  8. package/index.mjs +2 -0
  9. package/index.mjs.map +1 -1
  10. package/lib/core/openai-monitor.d.ts +3 -0
  11. package/lib/core/openai-monitor.d.ts.map +1 -1
  12. package/lib/core/openai-monitor.js +7 -1
  13. package/lib/core/openai-monitor.js.map +1 -1
  14. package/lib/core/openai-monitor.mjs +4 -1
  15. package/lib/core/openai-monitor.mjs.map +1 -1
  16. package/lib/integrations/langchainCallback.d.ts +33 -0
  17. package/lib/integrations/langchainCallback.d.ts.map +1 -0
  18. package/lib/integrations/langchainCallback.js +114 -0
  19. package/lib/integrations/langchainCallback.js.map +1 -0
  20. package/lib/integrations/langchainCallback.mjs +110 -0
  21. package/lib/integrations/langchainCallback.mjs.map +1 -0
  22. package/lib/integrations/openAiTracer.d.ts +3 -0
  23. package/lib/integrations/openAiTracer.d.ts.map +1 -0
  24. package/lib/integrations/openAiTracer.js +125 -0
  25. package/lib/integrations/openAiTracer.js.map +1 -0
  26. package/lib/integrations/openAiTracer.mjs +121 -0
  27. package/lib/integrations/openAiTracer.mjs.map +1 -0
  28. package/lib/tracing/enums.d.ts +5 -0
  29. package/lib/tracing/enums.d.ts.map +1 -0
  30. package/lib/tracing/enums.js +9 -0
  31. package/lib/tracing/enums.js.map +1 -0
  32. package/lib/tracing/enums.mjs +6 -0
  33. package/lib/tracing/enums.mjs.map +1 -0
  34. package/lib/tracing/steps.d.ts +57 -0
  35. package/lib/tracing/steps.d.ts.map +1 -0
  36. package/lib/tracing/steps.js +84 -0
  37. package/lib/tracing/steps.js.map +1 -0
  38. package/lib/tracing/steps.mjs +78 -0
  39. package/lib/tracing/steps.mjs.map +1 -0
  40. package/lib/tracing/tracer.d.ts +18 -0
  41. package/lib/tracing/tracer.d.ts.map +1 -0
  42. package/lib/tracing/tracer.js +155 -0
  43. package/lib/tracing/tracer.js.map +1 -0
  44. package/lib/tracing/tracer.mjs +148 -0
  45. package/lib/tracing/tracer.mjs.map +1 -0
  46. package/lib/tracing/traces.d.ts +9 -0
  47. package/lib/tracing/traces.d.ts.map +1 -0
  48. package/lib/tracing/traces.js +17 -0
  49. package/lib/tracing/traces.js.map +1 -0
  50. package/lib/tracing/traces.mjs +13 -0
  51. package/lib/tracing/traces.mjs.map +1 -0
  52. package/package.json +2 -1
  53. package/resources/index.d.ts +2 -1
  54. package/resources/index.d.ts.map +1 -1
  55. package/resources/index.js +3 -1
  56. package/resources/index.js.map +1 -1
  57. package/resources/index.mjs +2 -1
  58. package/resources/index.mjs.map +1 -1
  59. package/resources/inference-pipelines/data.d.ts +1 -1
  60. package/resources/inference-pipelines/data.js +1 -1
  61. package/resources/inference-pipelines/data.mjs +1 -1
  62. package/resources/inference-pipelines/index.d.ts +1 -1
  63. package/resources/inference-pipelines/index.d.ts.map +1 -1
  64. package/resources/inference-pipelines/index.js.map +1 -1
  65. package/resources/inference-pipelines/index.mjs +1 -1
  66. package/resources/inference-pipelines/index.mjs.map +1 -1
  67. package/resources/inference-pipelines/inference-pipelines.d.ts +161 -0
  68. package/resources/inference-pipelines/inference-pipelines.d.ts.map +1 -1
  69. package/resources/inference-pipelines/inference-pipelines.js +22 -0
  70. package/resources/inference-pipelines/inference-pipelines.js.map +1 -1
  71. package/resources/inference-pipelines/inference-pipelines.mjs +22 -0
  72. package/resources/inference-pipelines/inference-pipelines.mjs.map +1 -1
  73. package/resources/storage/index.d.ts +3 -0
  74. package/resources/storage/index.d.ts.map +1 -0
  75. package/resources/storage/index.js +9 -0
  76. package/resources/storage/index.js.map +1 -0
  77. package/resources/storage/index.mjs +4 -0
  78. package/resources/storage/index.mjs.map +1 -0
  79. package/resources/storage/presigned-url.d.ts +34 -0
  80. package/resources/storage/presigned-url.d.ts.map +1 -0
  81. package/resources/storage/presigned-url.js +18 -0
  82. package/resources/storage/presigned-url.js.map +1 -0
  83. package/resources/storage/presigned-url.mjs +14 -0
  84. package/resources/storage/presigned-url.mjs.map +1 -0
  85. package/resources/storage/storage.d.ts +11 -0
  86. package/resources/storage/storage.d.ts.map +1 -0
  87. package/resources/storage/storage.js +40 -0
  88. package/resources/storage/storage.js.map +1 -0
  89. package/resources/storage/storage.mjs +13 -0
  90. package/resources/storage/storage.mjs.map +1 -0
  91. package/src/index.ts +6 -0
  92. package/src/lib/core/openai-monitor.ts +7 -1
  93. package/src/lib/index.d.ts +1 -0
  94. package/src/lib/integrations/langchainCallback.ts +141 -0
  95. package/src/lib/integrations/openAiTracer.ts +137 -0
  96. package/src/lib/tracing/enums.ts +4 -0
  97. package/src/lib/tracing/index.d.ts +1 -0
  98. package/src/lib/tracing/steps.ts +118 -0
  99. package/src/lib/tracing/tracer.ts +204 -0
  100. package/src/lib/tracing/traces.ts +19 -0
  101. package/src/resources/index.ts +7 -1
  102. package/src/resources/inference-pipelines/data.ts +1 -1
  103. package/src/resources/inference-pipelines/index.ts +6 -1
  104. package/src/resources/inference-pipelines/inference-pipelines.ts +227 -0
  105. package/src/resources/storage/index.ts +4 -0
  106. package/src/resources/storage/presigned-url.ts +47 -0
  107. package/src/resources/storage/storage.ts +14 -0
  108. package/src/version.ts +1 -1
  109. package/version.d.ts +1 -1
  110. package/version.js +1 -1
  111. package/version.mjs +1 -1
  112. package/src/lib/.keep +0 -4
@@ -0,0 +1,141 @@
1
+ import { BaseCallbackHandler } from '@langchain/core/callbacks/base';
2
+ import { LLMResult } from '@langchain/core/dist/outputs';
3
+ import type { Serialized } from '@langchain/core/load/serializable';
4
+ import { AIMessage, BaseMessage, SystemMessage } from '@langchain/core/messages';
5
+ import { addChatCompletionStepToTrace } from '../tracing/tracer';
6
+
7
+ const LANGCHAIN_TO_OPENLAYER_PROVIDER_MAP: Record<string, string> = {
8
+ openai: 'OpenAI',
9
+ 'openai-chat': 'OpenAI',
10
+ 'chat-ollama': 'Ollama',
11
+ vertexai: 'Google',
12
+ };
13
+ const PROVIDER_TO_STEP_NAME: Record<string, string> = {
14
+ OpenAI: 'OpenAI Chat Completion',
15
+ Ollama: 'Ollama Chat Completion',
16
+ Google: 'Google Vertex AI Chat Completion',
17
+ };
18
+
19
+ export class OpenlayerHandler extends BaseCallbackHandler {
20
+ name = 'OpenlayerHandler';
21
+ startTime: number | null = null;
22
+ endTime: number | null = null;
23
+ prompt: Array<{ role: string; content: string }> | null = null;
24
+ latency: number = 0;
25
+ provider: string | undefined;
26
+ model: string | null = null;
27
+ modelParameters: Record<string, any> | null = null;
28
+ promptTokens: number | null = 0;
29
+ completionTokens: number | null = 0;
30
+ totalTokens: number | null = 0;
31
+ output: string = '';
32
+ metadata: Record<string, any>;
33
+
34
+ constructor(kwargs: Record<string, any> = {}) {
35
+ super();
36
+ this.metadata = kwargs;
37
+ }
38
+ override async handleChatModelStart(
39
+ llm: Serialized,
40
+ messages: BaseMessage[][],
41
+ runId: string,
42
+ parentRunId?: string | undefined,
43
+ extraParams?: Record<string, unknown> | undefined,
44
+ tags?: string[] | undefined,
45
+ metadata?: Record<string, unknown> | undefined,
46
+ name?: string,
47
+ ): Promise<void> {
48
+ this.initializeRun(extraParams || {}, metadata || {});
49
+ this.prompt = this.langchainMassagesToPrompt(messages);
50
+ this.startTime = performance.now();
51
+ }
52
+
53
+ private initializeRun(extraParams: Record<string, any>, metadata: Record<string, unknown>): void {
54
+ this.modelParameters = extraParams['invocation_params'] || {};
55
+
56
+ const provider = metadata?.['ls_provider'] as string;
57
+ if (provider && LANGCHAIN_TO_OPENLAYER_PROVIDER_MAP[provider]) {
58
+ this.provider = LANGCHAIN_TO_OPENLAYER_PROVIDER_MAP[provider];
59
+ }
60
+ this.model = (this.modelParameters?.['model'] as string) || (metadata['ls_model_name'] as string) || null;
61
+ this.output = '';
62
+ }
63
+
64
+ private langchainMassagesToPrompt(messages: BaseMessage[][]): Array<{ role: string; content: string }> {
65
+ let prompt: Array<{ role: string; content: string }> = [];
66
+ for (const message of messages) {
67
+ for (const m of message) {
68
+ if (m instanceof AIMessage) {
69
+ prompt.push({ role: 'assistant', content: m.content as string });
70
+ } else if (m instanceof SystemMessage) {
71
+ prompt.push({ role: 'system', content: m.content as string });
72
+ } else {
73
+ prompt.push({ role: 'user', content: m.content as string });
74
+ }
75
+ }
76
+ }
77
+ return prompt;
78
+ }
79
+
80
+ override async handleLLMStart(
81
+ llm: Serialized,
82
+ prompts: string[],
83
+ runId: string,
84
+ parentRunId?: string,
85
+ extraParams?: Record<string, unknown>,
86
+ tags?: string[],
87
+ metadata?: Record<string, unknown>,
88
+ runName?: string,
89
+ ) {
90
+ this.initializeRun(extraParams || {}, metadata || {});
91
+ this.prompt = prompts.map((p) => ({ role: 'user', content: p }));
92
+ this.startTime = performance.now();
93
+ }
94
+
95
+ override async handleLLMEnd(output: LLMResult, runId: string, parentRunId?: string, tags?: string[]) {
96
+ this.endTime = performance.now();
97
+ this.latency = this.endTime - this.startTime!;
98
+ this.extractTokenInformation(output);
99
+ this.extractOutput(output);
100
+ this.addToTrace();
101
+ }
102
+
103
+ private extractTokenInformation(output: LLMResult) {
104
+ if (this.provider === 'OpenAI') {
105
+ this.openaiTokenInformation(output);
106
+ }
107
+ }
108
+
109
+ private openaiTokenInformation(output: LLMResult) {
110
+ if (output.llmOutput && 'tokenUsage' in output.llmOutput) {
111
+ this.promptTokens = output.llmOutput?.['tokenUsage']?.promptTokens ?? 0;
112
+ this.completionTokens = output.llmOutput?.['tokenUsage']?.completionTokens ?? 0;
113
+ this.totalTokens = output.llmOutput?.['tokenUsage']?.totalTokens ?? 0;
114
+ }
115
+ }
116
+
117
+ private extractOutput(output: LLMResult) {
118
+ const lastResponse = output?.generations?.at(-1)?.at(-1) ?? undefined;
119
+ this.output += lastResponse?.text ?? '';
120
+ }
121
+
122
+ private addToTrace() {
123
+ let name = 'Chat Completion Model';
124
+ if (this.provider && this.provider in PROVIDER_TO_STEP_NAME) {
125
+ name = PROVIDER_TO_STEP_NAME[this.provider] ?? 'Chat Completion Model';
126
+ }
127
+ addChatCompletionStepToTrace({
128
+ name: name,
129
+ inputs: { prompt: this.prompt },
130
+ output: this.output,
131
+ latency: this.latency,
132
+ tokens: this.totalTokens,
133
+ promptTokens: this.promptTokens,
134
+ completionTokens: this.completionTokens,
135
+ model: this.model,
136
+ modelParameters: this.modelParameters,
137
+ metadata: this.metadata,
138
+ provider: this.provider ?? '',
139
+ });
140
+ }
141
+ }
@@ -0,0 +1,137 @@
1
+ import OpenAI from 'openai';
2
+ import { Stream } from 'openai/src/streaming';
3
+ import { addChatCompletionStepToTrace } from '../tracing/tracer';
4
+
5
+ export function traceOpenAI(openai: OpenAI): OpenAI {
6
+ const createFunction = openai.chat.completions.create;
7
+
8
+ openai.chat.completions.create = async function (
9
+ this: typeof openai.chat.completions,
10
+ ...args: Parameters<typeof createFunction>
11
+ ): Promise<Stream<OpenAI.Chat.Completions.ChatCompletionChunk> | OpenAI.Chat.Completions.ChatCompletion> {
12
+ const [params, options] = args;
13
+ const stream = params?.stream ?? false;
14
+
15
+ try {
16
+ const startTime = performance.now();
17
+
18
+ // Call the original `create` function
19
+ let response = await createFunction.apply(this, args);
20
+
21
+ if (stream) {
22
+ // Handle streaming responses
23
+ const chunks: OpenAI.Chat.Completions.ChatCompletionChunk[] = [];
24
+ let collectedOutputData: any[] = [];
25
+ let firstTokenTime: number | undefined;
26
+ let completionTokens: number = 0;
27
+ if (isAsyncIterable(response)) {
28
+ async function* tracedOutputGenerator(): AsyncGenerator<
29
+ OpenAI.Chat.Completions.ChatCompletionChunk,
30
+ void,
31
+ unknown
32
+ > {
33
+ for await (const rawChunk of response as AsyncIterable<OpenAI.Chat.Completions.ChatCompletionChunk>) {
34
+ if (chunks.length === 0) {
35
+ firstTokenTime = performance.now();
36
+ }
37
+ chunks.push(rawChunk);
38
+ const delta = rawChunk.choices[0]?.delta;
39
+ if (delta?.content) {
40
+ collectedOutputData.push(delta?.content);
41
+ } else if (delta?.tool_calls) {
42
+ const tool_call = delta.tool_calls[0];
43
+ if (tool_call?.function?.name) {
44
+ const functionName: string =
45
+ '{\n "name": ' + '"' + tool_call.function.name + '"' + '\n "arguments": ';
46
+ collectedOutputData.push(functionName);
47
+ } else if (tool_call?.function?.arguments) {
48
+ collectedOutputData.push(tool_call.function.arguments);
49
+ }
50
+ }
51
+
52
+ if (rawChunk.choices[0]?.finish_reason === 'tool_calls') {
53
+ collectedOutputData.push('\n}');
54
+ }
55
+ completionTokens += 1;
56
+ yield rawChunk;
57
+ }
58
+ const endTime = performance.now();
59
+ const traceData = {
60
+ name: 'OpenAI Chat Completion',
61
+ inputs: { prompt: params.messages },
62
+ output: collectedOutputData.join(''),
63
+ latency: endTime - startTime,
64
+ model: chunks[0]?.model as string,
65
+ modelParameters: getModelParameters(args),
66
+ rawOutput: chunks.map((chunk) => JSON.stringify(chunk, null, 2)).join('\n'),
67
+ metadata: { timeToFistToken: firstTokenTime ? firstTokenTime - startTime : null },
68
+ provider: 'OpenAI',
69
+ completionTokens: completionTokens,
70
+ promptTokens: 0,
71
+ tokens: completionTokens,
72
+ };
73
+ addChatCompletionStepToTrace(traceData);
74
+ }
75
+ return tracedOutputGenerator() as unknown as Stream<OpenAI.Chat.Completions.ChatCompletionChunk>;
76
+ }
77
+ } else {
78
+ // Handle non-streaming responses
79
+ response = response as OpenAI.Chat.Completions.ChatCompletion;
80
+ const completion = response.choices[0];
81
+ const endTime = performance.now();
82
+
83
+ let output: string = '';
84
+ if (completion?.message?.content) {
85
+ output = completion.message.content;
86
+ } else if (completion?.message.tool_calls) {
87
+ const tool_call = completion.message.tool_calls[0];
88
+ output = JSON.stringify(tool_call?.function, null, 2);
89
+ }
90
+
91
+ const traceData = {
92
+ name: 'OpenAI Chat Completion',
93
+ inputs: { prompt: params.messages },
94
+ output: output,
95
+ latency: endTime - startTime,
96
+ tokens: response.usage?.total_tokens ?? null,
97
+ promptTokens: response.usage?.prompt_tokens ?? null,
98
+ completionTokens: response.usage?.completion_tokens ?? null,
99
+ model: response.model,
100
+ modelParameters: getModelParameters(args),
101
+ rawOutput: JSON.stringify(response, null, 2),
102
+ metadata: {},
103
+ provider: 'OpenAI',
104
+ };
105
+ addChatCompletionStepToTrace(traceData);
106
+ return response;
107
+ }
108
+ } catch (error) {
109
+ console.error('Failed to trace the create chat completion request with Openlayer', error);
110
+ throw error;
111
+ }
112
+ // Ensure a return statement is present
113
+ return undefined as any;
114
+ } as typeof createFunction;
115
+
116
+ return openai;
117
+ }
118
+
119
+ function getModelParameters(args: any): Record<string, any> {
120
+ const params = args[0];
121
+ return {
122
+ frequency_penalty: params?.frequencyPenalty ?? 0,
123
+ logit_bias: params?.logitBias ?? null,
124
+ logprobs: params?.logprobs ?? false,
125
+ top_logprobs: params?.topLogprobs ?? null,
126
+ max_tokens: params?.maxTokens ?? null,
127
+ n: params?.n ?? 1,
128
+ presence_penalty: params?.presencePenalty ?? 0,
129
+ seed: params?.seed ?? null,
130
+ stop: params?.stop ?? null,
131
+ temperature: params?.temperature ?? 1,
132
+ top_p: params?.topP ?? 1,
133
+ };
134
+ }
135
+
136
+ const isAsyncIterable = (x: any) =>
137
+ x != null && typeof x === 'object' && typeof x[Symbol.asyncIterator] === 'function';
@@ -0,0 +1,4 @@
1
+ export enum StepType {
2
+ USER_CALL = 'user_call',
3
+ CHAT_COMPLETION = 'chat_completion',
4
+ }
@@ -0,0 +1 @@
1
+ export * as tracer from './tracer';
@@ -0,0 +1,118 @@
1
+ import { v4 as uuidv4 } from 'uuid';
2
+ import { StepType } from './enums';
3
+
4
+ export interface StepData {
5
+ name: string;
6
+ id: string;
7
+ type: StepType;
8
+ inputs: any;
9
+ output: any;
10
+ groundTruth: any;
11
+ metadata: Record<string, any>;
12
+ steps: StepData[];
13
+ latency: number | null;
14
+ startTime: number;
15
+ endTime: number | null;
16
+ }
17
+
18
+ export interface ChatCompletionStepData extends StepData {
19
+ provider: string | null;
20
+ promptTokens: number | null;
21
+ completionTokens: number | null;
22
+ tokens: number | null;
23
+ cost: number | null;
24
+ model: string | null;
25
+ modelParameters: Record<string, any> | null;
26
+ rawOutput: string | null;
27
+ }
28
+
29
+ export class Step {
30
+ name: string;
31
+ id: string;
32
+ inputs: any;
33
+ output: any;
34
+ metadata: Record<string, any>;
35
+ stepType: StepType | null = null;
36
+ startTime: number;
37
+ endTime: number | null = null;
38
+ groundTruth: any = null;
39
+ latency: number | null = null;
40
+ steps: Step[] = [];
41
+
42
+ constructor(name: string, inputs: any = null, output: any = null, metadata: Record<string, any> = {}) {
43
+ this.name = name;
44
+ this.id = uuidv4();
45
+ this.inputs = inputs;
46
+ this.output = output;
47
+ this.metadata = metadata;
48
+
49
+ this.startTime = Date.now();
50
+ }
51
+
52
+ addNestedStep(nestedStep: Step): void {
53
+ this.steps.push(nestedStep);
54
+ }
55
+
56
+ log(data: Partial<Record<keyof this, any>>): void {
57
+ Object.keys(data).forEach((key) => {
58
+ if (key in this) {
59
+ // @ts-ignore
60
+ this[key] = data[key];
61
+ }
62
+ });
63
+ }
64
+
65
+ toJSON(): StepData {
66
+ return {
67
+ name: this.name,
68
+ id: this.id,
69
+ type: this.stepType!,
70
+ inputs: this.inputs,
71
+ output: this.output,
72
+ groundTruth: this.groundTruth,
73
+ metadata: this.metadata,
74
+ steps: this.steps.map((nestedStep) => nestedStep.toJSON()),
75
+ latency: this.latency,
76
+ startTime: this.startTime,
77
+ endTime: this.endTime,
78
+ };
79
+ }
80
+ }
81
+
82
+ export class UserCallStep extends Step {
83
+ constructor(name: string, inputs: any = null, output: any = null, metadata: Record<string, any> = {}) {
84
+ super(name, inputs, output, metadata);
85
+ this.stepType = StepType.USER_CALL;
86
+ }
87
+ }
88
+
89
+ export class ChatCompletionStep extends Step {
90
+ provider: string | null = null;
91
+ promptTokens: number | null = null;
92
+ completionTokens: number | null = null;
93
+ tokens: number | null = null;
94
+ cost: number | null = null;
95
+ model: string | null = null;
96
+ modelParameters: Record<string, any> | null = null;
97
+ rawOutput: string | null = null;
98
+
99
+ constructor(name: string, inputs: any = null, output: any = null, metadata: Record<string, any> = {}) {
100
+ super(name, inputs, output, metadata);
101
+ this.stepType = StepType.CHAT_COMPLETION;
102
+ }
103
+
104
+ override toJSON(): ChatCompletionStepData {
105
+ const stepData = super.toJSON();
106
+ return {
107
+ ...stepData,
108
+ provider: this.provider,
109
+ promptTokens: this.promptTokens,
110
+ completionTokens: this.completionTokens,
111
+ tokens: this.tokens,
112
+ cost: this.cost,
113
+ model: this.model,
114
+ modelParameters: this.modelParameters,
115
+ rawOutput: this.rawOutput,
116
+ };
117
+ }
118
+ }
@@ -0,0 +1,204 @@
1
+ // tracing/tracer.ts
2
+
3
+ import { Trace } from './traces';
4
+ import { Step, UserCallStep, ChatCompletionStep } from './steps';
5
+ import { StepType } from './enums';
6
+ import Openlayer from '../../index';
7
+
8
+ let currentTrace: Trace | null = null;
9
+
10
+ const publish = process.env['OPENLAYER_DISABLE_PUBLISH'] != 'true';
11
+ let client: Openlayer | null = null;
12
+ if (publish) {
13
+ console.debug('Publishing is enabled');
14
+ client = new Openlayer();
15
+ }
16
+
17
+ function getCurrentTrace(): Trace | null {
18
+ return currentTrace;
19
+ }
20
+
21
+ function setCurrentTrace(trace: Trace | null) {
22
+ currentTrace = trace;
23
+ }
24
+
25
+ // Function to create a new step
26
+ const stepStack: Step[] = [];
27
+
28
+ function createStep(
29
+ name: string,
30
+ stepType: StepType = StepType.USER_CALL,
31
+ inputs?: any,
32
+ output?: any,
33
+ metadata: Record<string, any> | null = null,
34
+ ): [Step, () => void] {
35
+ metadata = metadata || {};
36
+ let newStep: Step;
37
+ if (stepType === StepType.CHAT_COMPLETION) {
38
+ newStep = new ChatCompletionStep(name, inputs, output, metadata);
39
+ } else {
40
+ newStep = new UserCallStep(name, inputs, output, metadata);
41
+ }
42
+ newStep.startTime = performance.now();
43
+
44
+ const parentStep = getCurrentStep();
45
+ const isRootStep = parentStep === null;
46
+
47
+ if (isRootStep) {
48
+ console.debug('Starting a new trace...');
49
+ console.debug(`Adding step ${name} as the root step`);
50
+ const currentTrace = new Trace();
51
+ setCurrentTrace(currentTrace);
52
+ currentTrace.addStep(newStep);
53
+ } else {
54
+ console.debug(`Adding step ${name} as a nested step to ${parentStep!.name}`);
55
+ currentTrace = getCurrentTrace()!;
56
+ parentStep!.addNestedStep(newStep);
57
+ }
58
+
59
+ stepStack.push(newStep);
60
+
61
+ const endStep = () => {
62
+ newStep.endTime = performance.now();
63
+ newStep.latency = newStep.endTime - newStep.startTime;
64
+
65
+ stepStack.pop(); // Remove the current step from the stack
66
+
67
+ if (isRootStep) {
68
+ console.debug('Ending the trace...');
69
+ const traceData = getCurrentTrace();
70
+ // Post process trace and get the input variable names
71
+ const { traceData: processedTraceData, inputVariableNames } = postProcessTrace(traceData!);
72
+
73
+ if (publish && process.env['OPENLAYER_INFERENCE_PIPELINE_ID']) {
74
+ client!.inferencePipelines.data.stream(process.env['OPENLAYER_INFERENCE_PIPELINE_ID'], {
75
+ config: {
76
+ outputColumnName: 'output',
77
+ inputVariableNames: inputVariableNames,
78
+ groundTruthColumnName: 'groundTruth',
79
+ latencyColumnName: 'latency',
80
+ costColumnName: 'cost',
81
+ timestampColumnName: 'inferenceTimestamp',
82
+ inferenceIdColumnName: 'inferenceId',
83
+ numOfTokenColumnName: 'tokens',
84
+ },
85
+ rows: [processedTraceData],
86
+ });
87
+ }
88
+
89
+ // Reset the entire trace state
90
+ setCurrentTrace(null);
91
+ stepStack.length = 0; // Clear the step stack
92
+ } else {
93
+ console.debug(`Ending step ${name}`);
94
+ }
95
+ };
96
+
97
+ return [newStep, endStep];
98
+ }
99
+
100
+ function getCurrentStep(): Step | null | undefined {
101
+ const currentStep = stepStack.length > 0 ? stepStack[stepStack.length - 1] : null;
102
+ return currentStep;
103
+ }
104
+
105
+ function getParamNames(func: Function): string[] {
106
+ const STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/gm;
107
+ const ARGUMENT_NAMES = /([^\s,]+)/g;
108
+ const fnStr = func.toString().replace(STRIP_COMMENTS, '');
109
+ const result = fnStr.slice(fnStr.indexOf('(') + 1, fnStr.indexOf(')')).match(ARGUMENT_NAMES);
110
+ return result || [];
111
+ }
112
+
113
+ // Higher-order function to trace synchronous or asynchronous functions
114
+ function trace(fn: Function, stepType: StepType = StepType.USER_CALL, stepName?: string): Function {
115
+ return async function (...args: any[]) {
116
+ const name = stepName || fn.name;
117
+ const paramNames = getParamNames(fn);
118
+ const inputs = Object.fromEntries(paramNames.map((name, index) => [name, args[index]]));
119
+ const [step, endStep] = createStep(name, stepType, args);
120
+
121
+ try {
122
+ const result = await fn(...args);
123
+ step.log({ inputs, output: result });
124
+ return result;
125
+ } catch (error: any) {
126
+ step.log({ inputs, metadata: { error: error.message } });
127
+ throw error;
128
+ } finally {
129
+ endStep();
130
+ }
131
+ };
132
+ }
133
+
134
+ export function addChatCompletionStepToTrace({
135
+ name,
136
+ inputs,
137
+ output,
138
+ latency,
139
+ tokens = null,
140
+ promptTokens = null,
141
+ completionTokens = null,
142
+ model = null,
143
+ modelParameters = null,
144
+ rawOutput = null,
145
+ metadata = {},
146
+ provider = 'OpenAI',
147
+ }: {
148
+ name: string;
149
+ inputs: any;
150
+ output: any;
151
+ latency: number;
152
+ tokens?: number | null;
153
+ promptTokens?: number | null;
154
+ completionTokens?: number | null;
155
+ model?: string | null;
156
+ modelParameters?: Record<string, any> | null;
157
+ rawOutput?: string | null;
158
+ metadata?: Record<string, any>;
159
+ provider?: string;
160
+ }) {
161
+ const [step, endStep] = createStep(name, StepType.CHAT_COMPLETION, inputs, output, metadata);
162
+
163
+ if (step instanceof ChatCompletionStep) {
164
+ step.provider = provider;
165
+ step.promptTokens = promptTokens;
166
+ step.completionTokens = completionTokens;
167
+ step.tokens = tokens;
168
+ step.model = model;
169
+ step.modelParameters = modelParameters;
170
+ step.rawOutput = rawOutput;
171
+ step.latency = latency;
172
+ }
173
+
174
+ step.log({ inputs, output, metadata });
175
+ endStep();
176
+ }
177
+
178
+ function postProcessTrace(traceObj: Trace): { traceData: any; inputVariableNames: string[] } {
179
+ const rootStep = traceObj.steps[0];
180
+
181
+ const input_variables = rootStep!.inputs;
182
+ const inputVariableNames = input_variables ? Object.keys(input_variables) : [];
183
+
184
+ const processed_steps = traceObj.toJSON();
185
+
186
+ const traceData = {
187
+ inferenceTimestamp: Date.now(),
188
+ inferenceId: rootStep!.id.toString(),
189
+ output: rootStep!.output,
190
+ groundTruth: rootStep!.groundTruth,
191
+ latency: rootStep!.latency,
192
+ cost: 0, // fix
193
+ tokens: 0, // fix
194
+ steps: processed_steps,
195
+ };
196
+
197
+ if (input_variables) {
198
+ Object.assign(traceData, input_variables);
199
+ }
200
+
201
+ return { traceData, inputVariableNames };
202
+ }
203
+
204
+ export default trace;
@@ -0,0 +1,19 @@
1
+ import { Step } from './steps';
2
+
3
+ export class Trace {
4
+ public steps: Step[];
5
+ private currentStep: Step | null;
6
+
7
+ constructor() {
8
+ this.steps = [];
9
+ this.currentStep = null;
10
+ }
11
+
12
+ public addStep(step: Step): void {
13
+ this.steps.push(step);
14
+ }
15
+
16
+ public toJSON(): Array<Record<string, any>> {
17
+ return this.steps.map((step) => step.toJSON());
18
+ }
19
+ }
@@ -1,7 +1,12 @@
1
1
  // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2
2
 
3
3
  export { Commits } from './commits/commits';
4
- export { InferencePipelines } from './inference-pipelines/inference-pipelines';
4
+ export {
5
+ InferencePipelineRetrieveResponse,
6
+ InferencePipelineUpdateResponse,
7
+ InferencePipelineUpdateParams,
8
+ InferencePipelines,
9
+ } from './inference-pipelines/inference-pipelines';
5
10
  export {
6
11
  ProjectCreateResponse,
7
12
  ProjectListResponse,
@@ -9,3 +14,4 @@ export {
9
14
  ProjectListParams,
10
15
  Projects,
11
16
  } from './projects/projects';
17
+ export { Storage } from './storage/storage';
@@ -6,7 +6,7 @@ import * as DataAPI from './data';
6
6
 
7
7
  export class Data extends APIResource {
8
8
  /**
9
- * Create an inference data point in an inference pipeline.
9
+ * Publish an inference data point to an inference pipeline.
10
10
  */
11
11
  stream(
12
12
  inferencePipelineId: string,
@@ -1,6 +1,11 @@
1
1
  // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2
2
 
3
3
  export { DataStreamResponse, DataStreamParams, Data } from './data';
4
- export { InferencePipelines } from './inference-pipelines';
4
+ export {
5
+ InferencePipelineRetrieveResponse,
6
+ InferencePipelineUpdateResponse,
7
+ InferencePipelineUpdateParams,
8
+ InferencePipelines,
9
+ } from './inference-pipelines';
5
10
  export { RowUpdateResponse, RowUpdateParams, Rows } from './rows';
6
11
  export { TestResultListResponse, TestResultListParams, TestResults } from './test-results';