openlit 1.0.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/.prettierrc.json +7 -0
- package/LICENSE +201 -0
- package/README.md +175 -0
- package/eslint.config.mjs +10 -0
- package/package.json +60 -0
- package/src/config.ts +45 -0
- package/src/constant.ts +5 -0
- package/src/helpers.ts +122 -0
- package/src/index.ts +72 -0
- package/src/instrumentation/anthropic/index.ts +59 -0
- package/src/instrumentation/anthropic/wrapper.ts +276 -0
- package/src/instrumentation/index.ts +66 -0
- package/src/instrumentation/openai/index.ts +114 -0
- package/src/instrumentation/openai/wrapper.ts +658 -0
- package/src/semantic-convention.ts +113 -0
- package/src/tracing.ts +59 -0
- package/src/types.ts +48 -0
- package/tsconfig.json +15 -0
|
@@ -0,0 +1,658 @@
|
|
|
1
|
+
import { Span, SpanKind, SpanStatusCode, Tracer, context, trace } from '@opentelemetry/api';
|
|
2
|
+
import OpenlitConfig from '../../config';
|
|
3
|
+
import OpenLitHelper from '../../helpers';
|
|
4
|
+
import SemanticConvention from '../../semantic-convention';
|
|
5
|
+
import { SDK_NAME, TELEMETRY_SDK_NAME } from '../../constant';
|
|
6
|
+
|
|
7
|
+
export default class OpenAIWrapper {
|
|
8
|
+
static setBaseSpanAttributes(
|
|
9
|
+
span: any,
|
|
10
|
+
{ genAIEndpoint, model, user, cost, environment, applicationName }: any
|
|
11
|
+
) {
|
|
12
|
+
span.setAttributes({
|
|
13
|
+
[TELEMETRY_SDK_NAME]: SDK_NAME,
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
span.setAttribute(TELEMETRY_SDK_NAME, SDK_NAME);
|
|
17
|
+
span.setAttribute(SemanticConvention.GEN_AI_SYSTEM, SemanticConvention.GEN_AI_SYSTEM_OPENAI);
|
|
18
|
+
span.setAttribute(SemanticConvention.GEN_AI_ENDPOINT, genAIEndpoint);
|
|
19
|
+
span.setAttribute(SemanticConvention.GEN_AI_ENVIRONMENT, environment);
|
|
20
|
+
span.setAttribute(SemanticConvention.GEN_AI_APPLICATION_NAME, applicationName);
|
|
21
|
+
span.setAttribute(SemanticConvention.GEN_AI_REQUEST_MODEL, model);
|
|
22
|
+
span.setAttribute(SemanticConvention.GEN_AI_REQUEST_USER, user);
|
|
23
|
+
if (cost !== undefined) span.setAttribute(SemanticConvention.GEN_AI_USAGE_COST, cost);
|
|
24
|
+
|
|
25
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
static _patchChatCompletionCreate(tracer: Tracer): any {
|
|
29
|
+
const genAIEndpoint = 'openai.resources.chat.completions';
|
|
30
|
+
return (originalMethod: (...args: any[]) => any) => {
|
|
31
|
+
return async function (this: any, ...args: any[]) {
|
|
32
|
+
const span = tracer.startSpan(genAIEndpoint, { kind: SpanKind.CLIENT });
|
|
33
|
+
return context
|
|
34
|
+
.with(trace.setSpan(context.active(), span), async () => {
|
|
35
|
+
return originalMethod.apply(this, args);
|
|
36
|
+
})
|
|
37
|
+
.then((response) => {
|
|
38
|
+
const { stream = false } = args[0];
|
|
39
|
+
|
|
40
|
+
if (!!stream) {
|
|
41
|
+
return OpenLitHelper.createStreamProxy(
|
|
42
|
+
response,
|
|
43
|
+
OpenAIWrapper._chatCompletionGenerator({
|
|
44
|
+
args,
|
|
45
|
+
genAIEndpoint,
|
|
46
|
+
response,
|
|
47
|
+
span,
|
|
48
|
+
})
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return OpenAIWrapper._chatCompletion({ args, genAIEndpoint, response, span });
|
|
53
|
+
})
|
|
54
|
+
.catch((e: any) => {
|
|
55
|
+
OpenLitHelper.handleException(span, e);
|
|
56
|
+
span.end();
|
|
57
|
+
});
|
|
58
|
+
};
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
static async _chatCompletion({
|
|
63
|
+
args,
|
|
64
|
+
genAIEndpoint,
|
|
65
|
+
response,
|
|
66
|
+
span,
|
|
67
|
+
}: {
|
|
68
|
+
args: any[];
|
|
69
|
+
genAIEndpoint: string;
|
|
70
|
+
response: any;
|
|
71
|
+
span: Span;
|
|
72
|
+
}): Promise<any> {
|
|
73
|
+
try {
|
|
74
|
+
await OpenAIWrapper._chatCompletionCommonSetter({
|
|
75
|
+
args,
|
|
76
|
+
genAIEndpoint,
|
|
77
|
+
result: response,
|
|
78
|
+
span,
|
|
79
|
+
});
|
|
80
|
+
return response;
|
|
81
|
+
} catch (e: any) {
|
|
82
|
+
OpenLitHelper.handleException(span, e);
|
|
83
|
+
} finally {
|
|
84
|
+
span.end();
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
static async *_chatCompletionGenerator({
|
|
89
|
+
args,
|
|
90
|
+
genAIEndpoint,
|
|
91
|
+
response,
|
|
92
|
+
span,
|
|
93
|
+
}: {
|
|
94
|
+
args: any[];
|
|
95
|
+
genAIEndpoint: string;
|
|
96
|
+
response: any;
|
|
97
|
+
span: Span;
|
|
98
|
+
}): AsyncGenerator<unknown, any, unknown> {
|
|
99
|
+
try {
|
|
100
|
+
const { messages } = args[0];
|
|
101
|
+
let { tools } = args[0];
|
|
102
|
+
const result = {
|
|
103
|
+
id: '0',
|
|
104
|
+
created: -1,
|
|
105
|
+
model: '',
|
|
106
|
+
choices: [
|
|
107
|
+
{
|
|
108
|
+
index: 0,
|
|
109
|
+
logprobs: null,
|
|
110
|
+
finish_reason: 'stop',
|
|
111
|
+
message: { role: 'assistant', content: '' },
|
|
112
|
+
},
|
|
113
|
+
],
|
|
114
|
+
usage: {
|
|
115
|
+
prompt_tokens: 0,
|
|
116
|
+
completion_tokens: 0,
|
|
117
|
+
total_tokens: 0,
|
|
118
|
+
},
|
|
119
|
+
};
|
|
120
|
+
for await (const chunk of response) {
|
|
121
|
+
result.id = chunk.id;
|
|
122
|
+
result.created = chunk.created;
|
|
123
|
+
result.model = chunk.model;
|
|
124
|
+
|
|
125
|
+
if (chunk.choices[0]?.finish_reason) {
|
|
126
|
+
result.choices[0].finish_reason = chunk.choices[0].finish_reason;
|
|
127
|
+
}
|
|
128
|
+
if (chunk.choices[0]?.logprobs) {
|
|
129
|
+
result.choices[0].logprobs = chunk.choices[0].logprobs;
|
|
130
|
+
}
|
|
131
|
+
if (chunk.choices[0]?.delta.content) {
|
|
132
|
+
result.choices[0].message.content += chunk.choices[0].delta.content;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (chunk.choices[0]?.delta.tool_calls) {
|
|
136
|
+
tools = true;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
yield chunk;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
let promptTokens = 0;
|
|
143
|
+
for (const message of messages || []) {
|
|
144
|
+
promptTokens += OpenLitHelper.openaiTokens(message.content as string, result.model) ?? 0;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const completionTokens = OpenLitHelper.openaiTokens(
|
|
148
|
+
result.choices[0].message.content ?? '',
|
|
149
|
+
result.model
|
|
150
|
+
);
|
|
151
|
+
if (completionTokens) {
|
|
152
|
+
result.usage = {
|
|
153
|
+
prompt_tokens: promptTokens,
|
|
154
|
+
completion_tokens: completionTokens,
|
|
155
|
+
total_tokens: promptTokens + completionTokens,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
args[0].tools = tools;
|
|
160
|
+
|
|
161
|
+
await OpenAIWrapper._chatCompletionCommonSetter({
|
|
162
|
+
args,
|
|
163
|
+
genAIEndpoint,
|
|
164
|
+
result,
|
|
165
|
+
span,
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
return result;
|
|
169
|
+
} catch (e: any) {
|
|
170
|
+
OpenLitHelper.handleException(span, e);
|
|
171
|
+
} finally {
|
|
172
|
+
span.end();
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
static async _chatCompletionCommonSetter({
|
|
177
|
+
args,
|
|
178
|
+
genAIEndpoint,
|
|
179
|
+
result,
|
|
180
|
+
span,
|
|
181
|
+
}: {
|
|
182
|
+
args: any[];
|
|
183
|
+
genAIEndpoint: string;
|
|
184
|
+
result: any;
|
|
185
|
+
span: Span;
|
|
186
|
+
}) {
|
|
187
|
+
const applicationName = OpenlitConfig.applicationName;
|
|
188
|
+
const environment = OpenlitConfig.environment;
|
|
189
|
+
const traceContent = OpenlitConfig.traceContent;
|
|
190
|
+
const {
|
|
191
|
+
messages,
|
|
192
|
+
frequency_penalty = 0,
|
|
193
|
+
max_tokens = null,
|
|
194
|
+
n = 1,
|
|
195
|
+
presence_penalty = 0,
|
|
196
|
+
seed = null,
|
|
197
|
+
temperature = 1,
|
|
198
|
+
top_p,
|
|
199
|
+
user,
|
|
200
|
+
stream = false,
|
|
201
|
+
tools,
|
|
202
|
+
} = args[0];
|
|
203
|
+
|
|
204
|
+
// Request Params attributes : Start
|
|
205
|
+
span.setAttribute(SemanticConvention.GEN_AI_REQUEST_TOP_P, top_p || 1);
|
|
206
|
+
span.setAttribute(SemanticConvention.GEN_AI_REQUEST_MAX_TOKENS, max_tokens);
|
|
207
|
+
span.setAttribute(SemanticConvention.GEN_AI_REQUEST_TEMPERATURE, temperature);
|
|
208
|
+
span.setAttribute(SemanticConvention.GEN_AI_REQUEST_PRESENCE_PENALTY, presence_penalty);
|
|
209
|
+
span.setAttribute(SemanticConvention.GEN_AI_REQUEST_FREQUENCY_PENALTY, frequency_penalty);
|
|
210
|
+
span.setAttribute(SemanticConvention.GEN_AI_REQUEST_SEED, seed);
|
|
211
|
+
span.setAttribute(SemanticConvention.GEN_AI_REQUEST_IS_STREAM, stream);
|
|
212
|
+
|
|
213
|
+
if (traceContent) {
|
|
214
|
+
// Format 'messages' into a single string
|
|
215
|
+
const messagePrompt = messages || [];
|
|
216
|
+
const formattedMessages = [];
|
|
217
|
+
|
|
218
|
+
for (const message of messagePrompt) {
|
|
219
|
+
const role = message.role;
|
|
220
|
+
const content = message.content;
|
|
221
|
+
|
|
222
|
+
if (Array.isArray(content)) {
|
|
223
|
+
const contentStr = content
|
|
224
|
+
.map((item) => {
|
|
225
|
+
if ('type' in item) {
|
|
226
|
+
return `${item.type}: ${item.text ? item.text : item.image_url}`;
|
|
227
|
+
} else {
|
|
228
|
+
return `text: ${item.text}`;
|
|
229
|
+
}
|
|
230
|
+
})
|
|
231
|
+
.join(', ');
|
|
232
|
+
formattedMessages.push(`${role}: ${contentStr}`);
|
|
233
|
+
} else {
|
|
234
|
+
formattedMessages.push(`${role}: ${content}`);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const prompt = formattedMessages.join('\n');
|
|
239
|
+
span.setAttribute(SemanticConvention.GEN_AI_CONTENT_PROMPT, prompt);
|
|
240
|
+
}
|
|
241
|
+
// Request Params attributes : End
|
|
242
|
+
|
|
243
|
+
span.setAttribute(SemanticConvention.GEN_AI_TYPE, SemanticConvention.GEN_AI_TYPE_CHAT);
|
|
244
|
+
|
|
245
|
+
span.setAttribute(SemanticConvention.GEN_AI_RESPONSE_ID, result.id);
|
|
246
|
+
|
|
247
|
+
const model = result.model || 'gpt-3.5-turbo';
|
|
248
|
+
|
|
249
|
+
const pricingInfo = await OpenlitConfig.updatePricingJson(OpenlitConfig.pricing_json);
|
|
250
|
+
|
|
251
|
+
// Calculate cost of the operation
|
|
252
|
+
const cost = OpenLitHelper.getChatModelCost(
|
|
253
|
+
model,
|
|
254
|
+
pricingInfo,
|
|
255
|
+
result.usage.prompt_tokens,
|
|
256
|
+
result.usage.completion_tokens
|
|
257
|
+
);
|
|
258
|
+
|
|
259
|
+
OpenAIWrapper.setBaseSpanAttributes(span, {
|
|
260
|
+
genAIEndpoint,
|
|
261
|
+
model,
|
|
262
|
+
user,
|
|
263
|
+
cost,
|
|
264
|
+
applicationName,
|
|
265
|
+
environment,
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
span.setAttribute(SemanticConvention.GEN_AI_USAGE_PROMPT_TOKENS, result.usage.prompt_tokens);
|
|
269
|
+
span.setAttribute(
|
|
270
|
+
SemanticConvention.GEN_AI_USAGE_COMPLETION_TOKENS,
|
|
271
|
+
result.usage.completion_tokens
|
|
272
|
+
);
|
|
273
|
+
span.setAttribute(SemanticConvention.GEN_AI_USAGE_TOTAL_TOKENS, result.usage.total_tokens);
|
|
274
|
+
|
|
275
|
+
if (result.choices[0].finish_reason) {
|
|
276
|
+
span.setAttribute(
|
|
277
|
+
SemanticConvention.GEN_AI_RESPONSE_FINISH_REASON,
|
|
278
|
+
result.choices[0].finish_reason
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
if (tools) {
|
|
283
|
+
span.setAttribute(SemanticConvention.GEN_AI_CONTENT_COMPLETION, 'Function called with tools');
|
|
284
|
+
} else {
|
|
285
|
+
if (traceContent) {
|
|
286
|
+
if (n === 1) {
|
|
287
|
+
span.setAttribute(
|
|
288
|
+
SemanticConvention.GEN_AI_CONTENT_COMPLETION,
|
|
289
|
+
result.choices[0].message.content
|
|
290
|
+
);
|
|
291
|
+
} else {
|
|
292
|
+
let i = 0;
|
|
293
|
+
while (i < n) {
|
|
294
|
+
const attribute_name = `${SemanticConvention.GEN_AI_CONTENT_COMPLETION}.[i]`;
|
|
295
|
+
span.setAttribute(attribute_name, result.choices[i].message.content);
|
|
296
|
+
i += 1;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
static _patchEmbedding(tracer: Tracer): any {
|
|
304
|
+
const genAIEndpoint = 'openai.resources.embeddings';
|
|
305
|
+
const applicationName = OpenlitConfig.applicationName;
|
|
306
|
+
const environment = OpenlitConfig.environment;
|
|
307
|
+
const traceContent = OpenlitConfig.traceContent;
|
|
308
|
+
|
|
309
|
+
return (originalMethod: (...args: any[]) => any) => {
|
|
310
|
+
return async function (this: any, ...args: any[]) {
|
|
311
|
+
const span = tracer.startSpan(genAIEndpoint, { kind: SpanKind.CLIENT });
|
|
312
|
+
return context.with(trace.setSpan(context.active(), span), async () => {
|
|
313
|
+
try {
|
|
314
|
+
const response = await originalMethod.apply(this, args);
|
|
315
|
+
span.setAttributes({
|
|
316
|
+
[TELEMETRY_SDK_NAME]: SDK_NAME,
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
const model = response.model || 'text-embedding-ada-002';
|
|
320
|
+
const pricingInfo = await OpenlitConfig.updatePricingJson(OpenlitConfig.pricing_json);
|
|
321
|
+
const cost = OpenLitHelper.getEmbedModelCost(
|
|
322
|
+
model,
|
|
323
|
+
pricingInfo,
|
|
324
|
+
response.usage.prompt_tokens
|
|
325
|
+
);
|
|
326
|
+
|
|
327
|
+
span.setAttribute(
|
|
328
|
+
SemanticConvention.GEN_AI_TYPE,
|
|
329
|
+
SemanticConvention.GEN_AI_TYPE_EMBEDDING
|
|
330
|
+
);
|
|
331
|
+
|
|
332
|
+
const { dimensions, encoding_format = 'float', input, user } = args[0];
|
|
333
|
+
// Set base span attribues
|
|
334
|
+
OpenAIWrapper.setBaseSpanAttributes(span, {
|
|
335
|
+
genAIEndpoint,
|
|
336
|
+
model,
|
|
337
|
+
user,
|
|
338
|
+
cost,
|
|
339
|
+
applicationName,
|
|
340
|
+
environment,
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
// Request Params attributes : Start
|
|
344
|
+
|
|
345
|
+
span.setAttribute(SemanticConvention.GEN_AI_REQUEST_EMBEDDING_FORMAT, encoding_format);
|
|
346
|
+
span.setAttribute(SemanticConvention.GEN_AI_REQUEST_EMBEDDING_DIMENSION, dimensions);
|
|
347
|
+
if (traceContent) {
|
|
348
|
+
span.setAttribute(SemanticConvention.GEN_AI_CONTENT_PROMPT, input);
|
|
349
|
+
}
|
|
350
|
+
// Request Params attributes : End
|
|
351
|
+
|
|
352
|
+
span.setAttribute(
|
|
353
|
+
SemanticConvention.GEN_AI_USAGE_PROMPT_TOKENS,
|
|
354
|
+
response.usage.prompt_tokens
|
|
355
|
+
);
|
|
356
|
+
span.setAttribute(
|
|
357
|
+
SemanticConvention.GEN_AI_USAGE_TOTAL_TOKENS,
|
|
358
|
+
response.usage.total_tokens
|
|
359
|
+
);
|
|
360
|
+
|
|
361
|
+
return response;
|
|
362
|
+
} catch (e: any) {
|
|
363
|
+
OpenLitHelper.handleException(span, e);
|
|
364
|
+
} finally {
|
|
365
|
+
span.end();
|
|
366
|
+
}
|
|
367
|
+
});
|
|
368
|
+
};
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
static _patchFineTune(tracer: Tracer): any {
|
|
373
|
+
const genAIEndpoint = 'openai.resources.fine_tuning.jobs';
|
|
374
|
+
const applicationName = OpenlitConfig.applicationName;
|
|
375
|
+
const environment = OpenlitConfig.environment;
|
|
376
|
+
|
|
377
|
+
return (originalMethod: (...args: any[]) => any) => {
|
|
378
|
+
return async function (this: any, ...args: any[]) {
|
|
379
|
+
const span = tracer.startSpan(genAIEndpoint, { kind: SpanKind.CLIENT });
|
|
380
|
+
return context.with(trace.setSpan(context.active(), span), async () => {
|
|
381
|
+
try {
|
|
382
|
+
const response = await originalMethod.apply(this, args);
|
|
383
|
+
span.setAttributes({
|
|
384
|
+
[TELEMETRY_SDK_NAME]: SDK_NAME,
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
const model = response.model || 'gpt-3.5-turbo';
|
|
388
|
+
const {
|
|
389
|
+
hyperparameters = {},
|
|
390
|
+
suffix = '',
|
|
391
|
+
training_file,
|
|
392
|
+
user,
|
|
393
|
+
validation_file,
|
|
394
|
+
} = args[0];
|
|
395
|
+
|
|
396
|
+
// Set base span attribues
|
|
397
|
+
OpenAIWrapper.setBaseSpanAttributes(span, {
|
|
398
|
+
genAIEndpoint,
|
|
399
|
+
model,
|
|
400
|
+
user,
|
|
401
|
+
applicationName,
|
|
402
|
+
environment,
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
span.setAttribute(
|
|
406
|
+
SemanticConvention.GEN_AI_TYPE,
|
|
407
|
+
SemanticConvention.GEN_AI_TYPE_FINETUNING
|
|
408
|
+
);
|
|
409
|
+
|
|
410
|
+
// Request Params attributes : Start
|
|
411
|
+
|
|
412
|
+
span.setAttribute(SemanticConvention.GEN_AI_REQUEST_TRAINING_FILE, training_file);
|
|
413
|
+
span.setAttribute(SemanticConvention.GEN_AI_REQUEST_VALIDATION_FILE, validation_file);
|
|
414
|
+
span.setAttribute(
|
|
415
|
+
SemanticConvention.GEN_AI_REQUEST_FINETUNE_BATCH_SIZE,
|
|
416
|
+
hyperparameters?.batch_size || 'auto'
|
|
417
|
+
);
|
|
418
|
+
span.setAttribute(
|
|
419
|
+
SemanticConvention.GEN_AI_REQUEST_FINETUNE_MODEL_LRM,
|
|
420
|
+
hyperparameters?.learning_rate_multiplier || 'auto'
|
|
421
|
+
);
|
|
422
|
+
span.setAttribute(
|
|
423
|
+
SemanticConvention.GEN_AI_REQUEST_FINETUNE_MODEL_EPOCHS,
|
|
424
|
+
hyperparameters?.n_epochs || 'auto'
|
|
425
|
+
);
|
|
426
|
+
span.setAttribute(SemanticConvention.GEN_AI_REQUEST_FINETUNE_MODEL_SUFFIX, suffix);
|
|
427
|
+
// Request Params attributes : End
|
|
428
|
+
|
|
429
|
+
span.setAttribute(SemanticConvention.GEN_AI_RESPONSE_ID, response.id);
|
|
430
|
+
span.setAttribute(
|
|
431
|
+
SemanticConvention.GEN_AI_USAGE_PROMPT_TOKENS,
|
|
432
|
+
response.usage.prompt_tokens
|
|
433
|
+
);
|
|
434
|
+
span.setAttribute(SemanticConvention.GEN_AI_REQUEST_FINETUNE_STATUS, response.status);
|
|
435
|
+
|
|
436
|
+
return response;
|
|
437
|
+
} catch (e: any) {
|
|
438
|
+
OpenLitHelper.handleException(span, e);
|
|
439
|
+
} finally {
|
|
440
|
+
span.end();
|
|
441
|
+
}
|
|
442
|
+
});
|
|
443
|
+
};
|
|
444
|
+
};
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
static _patchImageGenerate(tracer: Tracer): any {
|
|
448
|
+
const genAIEndpoint = 'openai.resources.images';
|
|
449
|
+
const applicationName = OpenlitConfig.applicationName;
|
|
450
|
+
const environment = OpenlitConfig.environment;
|
|
451
|
+
const traceContent = OpenlitConfig.traceContent;
|
|
452
|
+
return (originalMethod: (...args: any[]) => any) => {
|
|
453
|
+
return async function (this: any, ...args: any[]) {
|
|
454
|
+
const span = tracer.startSpan(genAIEndpoint, { kind: SpanKind.CLIENT });
|
|
455
|
+
return context.with(trace.setSpan(context.active(), span), async () => {
|
|
456
|
+
try {
|
|
457
|
+
const response = await originalMethod.apply(this, args);
|
|
458
|
+
|
|
459
|
+
const {
|
|
460
|
+
prompt,
|
|
461
|
+
quality = 'standard',
|
|
462
|
+
response_format = 'url',
|
|
463
|
+
size = '1024x1024',
|
|
464
|
+
style = 'vivid',
|
|
465
|
+
user,
|
|
466
|
+
} = args[0];
|
|
467
|
+
|
|
468
|
+
span.setAttribute(SemanticConvention.GEN_AI_TYPE, SemanticConvention.GEN_AI_TYPE_IMAGE);
|
|
469
|
+
|
|
470
|
+
const model = response.model || 'dall-e-2';
|
|
471
|
+
|
|
472
|
+
const pricingInfo = await OpenlitConfig.updatePricingJson(OpenlitConfig.pricing_json);
|
|
473
|
+
|
|
474
|
+
// Calculate cost of the operation
|
|
475
|
+
const cost =
|
|
476
|
+
(response.data?.length || 1) *
|
|
477
|
+
OpenLitHelper.getImageModelCost(model, pricingInfo, size, quality);
|
|
478
|
+
|
|
479
|
+
OpenAIWrapper.setBaseSpanAttributes(span, {
|
|
480
|
+
genAIEndpoint,
|
|
481
|
+
model,
|
|
482
|
+
user,
|
|
483
|
+
cost,
|
|
484
|
+
applicationName,
|
|
485
|
+
environment,
|
|
486
|
+
});
|
|
487
|
+
|
|
488
|
+
// Request Params attributes : Start
|
|
489
|
+
span.setAttribute(SemanticConvention.GEN_AI_REQUEST_IMAGE_SIZE, size);
|
|
490
|
+
span.setAttribute(SemanticConvention.GEN_AI_REQUEST_IMAGE_QUALITY, quality);
|
|
491
|
+
span.setAttribute(SemanticConvention.GEN_AI_REQUEST_IMAGE_STYLE, style);
|
|
492
|
+
|
|
493
|
+
if (traceContent) {
|
|
494
|
+
span.setAttribute(SemanticConvention.GEN_AI_CONTENT_PROMPT, prompt);
|
|
495
|
+
}
|
|
496
|
+
// Request Params attributes : End
|
|
497
|
+
|
|
498
|
+
let imagesCount = 0;
|
|
499
|
+
|
|
500
|
+
if (response.data) {
|
|
501
|
+
for (const items of response.data) {
|
|
502
|
+
span.setAttribute(
|
|
503
|
+
`${SemanticConvention.GEN_AI_CONTENT_REVISED_PROMPT}.${imagesCount}`,
|
|
504
|
+
items.revised_prompt || ''
|
|
505
|
+
);
|
|
506
|
+
|
|
507
|
+
const attributeName = `${SemanticConvention.GEN_AI_RESPONSE_IMAGE}.${imagesCount}`;
|
|
508
|
+
span.setAttribute(attributeName, items[response_format]);
|
|
509
|
+
|
|
510
|
+
imagesCount++;
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
return response;
|
|
515
|
+
} catch (e: any) {
|
|
516
|
+
OpenLitHelper.handleException(span, e);
|
|
517
|
+
} finally {
|
|
518
|
+
span.end();
|
|
519
|
+
}
|
|
520
|
+
});
|
|
521
|
+
};
|
|
522
|
+
};
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
static _patchImageVariation(tracer: Tracer): any {
|
|
526
|
+
const genAIEndpoint = 'openai.resources.images';
|
|
527
|
+
const applicationName = OpenlitConfig.applicationName;
|
|
528
|
+
const environment = OpenlitConfig.environment;
|
|
529
|
+
const traceContent = OpenlitConfig.traceContent;
|
|
530
|
+
return (originalMethod: (...args: any[]) => any) => {
|
|
531
|
+
return async function (this: any, ...args: any[]) {
|
|
532
|
+
const span = tracer.startSpan(genAIEndpoint, { kind: SpanKind.CLIENT });
|
|
533
|
+
return context.with(trace.setSpan(context.active(), span), async () => {
|
|
534
|
+
try {
|
|
535
|
+
const response = await originalMethod.apply(this, args);
|
|
536
|
+
|
|
537
|
+
const {
|
|
538
|
+
prompt,
|
|
539
|
+
quality = 'standard',
|
|
540
|
+
response_format = 'url',
|
|
541
|
+
size = '1024x1024',
|
|
542
|
+
style = 'vivid',
|
|
543
|
+
user,
|
|
544
|
+
} = args[0];
|
|
545
|
+
|
|
546
|
+
span.setAttribute(SemanticConvention.GEN_AI_TYPE, SemanticConvention.GEN_AI_TYPE_IMAGE);
|
|
547
|
+
span.setAttribute(SemanticConvention.GEN_AI_RESPONSE_ID, response.created);
|
|
548
|
+
|
|
549
|
+
const model = response.model || 'dall-e-2';
|
|
550
|
+
|
|
551
|
+
const pricingInfo = await OpenlitConfig.updatePricingJson(OpenlitConfig.pricing_json);
|
|
552
|
+
|
|
553
|
+
// Calculate cost of the operation
|
|
554
|
+
const cost =
|
|
555
|
+
(response.data?.length || 1) *
|
|
556
|
+
OpenLitHelper.getImageModelCost(model, pricingInfo, size, quality);
|
|
557
|
+
|
|
558
|
+
OpenAIWrapper.setBaseSpanAttributes(span, {
|
|
559
|
+
genAIEndpoint,
|
|
560
|
+
model,
|
|
561
|
+
user,
|
|
562
|
+
cost,
|
|
563
|
+
applicationName,
|
|
564
|
+
environment,
|
|
565
|
+
});
|
|
566
|
+
|
|
567
|
+
// Request Params attributes : Start
|
|
568
|
+
span.setAttribute(SemanticConvention.GEN_AI_REQUEST_IMAGE_SIZE, size);
|
|
569
|
+
span.setAttribute(SemanticConvention.GEN_AI_REQUEST_IMAGE_QUALITY, quality);
|
|
570
|
+
span.setAttribute(SemanticConvention.GEN_AI_REQUEST_IMAGE_STYLE, style);
|
|
571
|
+
|
|
572
|
+
if (traceContent) {
|
|
573
|
+
span.setAttribute(SemanticConvention.GEN_AI_CONTENT_PROMPT, prompt);
|
|
574
|
+
}
|
|
575
|
+
// Request Params attributes : End
|
|
576
|
+
|
|
577
|
+
let imagesCount = 0;
|
|
578
|
+
if (response.data) {
|
|
579
|
+
for (const items of response.data) {
|
|
580
|
+
span.setAttribute(
|
|
581
|
+
`${SemanticConvention.GEN_AI_CONTENT_REVISED_PROMPT}.${imagesCount}`,
|
|
582
|
+
items.revised_prompt || ''
|
|
583
|
+
);
|
|
584
|
+
|
|
585
|
+
const attributeName = `${SemanticConvention.GEN_AI_RESPONSE_IMAGE}.${imagesCount}`;
|
|
586
|
+
span.setAttribute(attributeName, items[response_format]);
|
|
587
|
+
|
|
588
|
+
imagesCount++;
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
return response;
|
|
593
|
+
} catch (e: any) {
|
|
594
|
+
OpenLitHelper.handleException(span, e);
|
|
595
|
+
} finally {
|
|
596
|
+
span.end();
|
|
597
|
+
}
|
|
598
|
+
});
|
|
599
|
+
};
|
|
600
|
+
};
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
static _patchAudioCreate(tracer: Tracer): any {
|
|
604
|
+
const genAIEndpoint = 'openai.resources.audio.speech';
|
|
605
|
+
const applicationName = OpenlitConfig.applicationName;
|
|
606
|
+
const environment = OpenlitConfig.environment;
|
|
607
|
+
const traceContent = OpenlitConfig.traceContent;
|
|
608
|
+
return (originalMethod: (...args: any[]) => any) => {
|
|
609
|
+
return async function (this: any, ...args: any[]) {
|
|
610
|
+
const span = tracer.startSpan(genAIEndpoint, { kind: SpanKind.CLIENT });
|
|
611
|
+
return context.with(trace.setSpan(context.active(), span), async () => {
|
|
612
|
+
try {
|
|
613
|
+
const response = await originalMethod.apply(this, args);
|
|
614
|
+
|
|
615
|
+
const { input, user, voice, response_format = 'mp3', speed = 1 } = args[0];
|
|
616
|
+
|
|
617
|
+
span.setAttribute(SemanticConvention.GEN_AI_TYPE, SemanticConvention.GEN_AI_TYPE_AUDIO);
|
|
618
|
+
|
|
619
|
+
const model = response.model || 'tts-1';
|
|
620
|
+
|
|
621
|
+
const pricingInfo = await OpenlitConfig.updatePricingJson(OpenlitConfig.pricing_json);
|
|
622
|
+
|
|
623
|
+
// Calculate cost of the operation
|
|
624
|
+
const cost = OpenLitHelper.getAudioModelCost(model, pricingInfo, input);
|
|
625
|
+
|
|
626
|
+
OpenAIWrapper.setBaseSpanAttributes(span, {
|
|
627
|
+
genAIEndpoint,
|
|
628
|
+
model,
|
|
629
|
+
user,
|
|
630
|
+
cost,
|
|
631
|
+
applicationName,
|
|
632
|
+
environment,
|
|
633
|
+
});
|
|
634
|
+
|
|
635
|
+
// Request Params attributes : Start
|
|
636
|
+
span.setAttribute(SemanticConvention.GEN_AI_REQUEST_AUDIO_VOICE, voice);
|
|
637
|
+
span.setAttribute(
|
|
638
|
+
SemanticConvention.GEN_AI_REQUEST_AUDIO_RESPONSE_FORMAT,
|
|
639
|
+
response_format
|
|
640
|
+
);
|
|
641
|
+
span.setAttribute(SemanticConvention.GEN_AI_REQUEST_AUDIO_SPEED, speed);
|
|
642
|
+
|
|
643
|
+
if (traceContent) {
|
|
644
|
+
span.setAttribute(SemanticConvention.GEN_AI_CONTENT_PROMPT, input);
|
|
645
|
+
}
|
|
646
|
+
// Request Params attributes : End
|
|
647
|
+
|
|
648
|
+
return response;
|
|
649
|
+
} catch (e: any) {
|
|
650
|
+
OpenLitHelper.handleException(span, e);
|
|
651
|
+
} finally {
|
|
652
|
+
span.end();
|
|
653
|
+
}
|
|
654
|
+
});
|
|
655
|
+
};
|
|
656
|
+
};
|
|
657
|
+
}
|
|
658
|
+
}
|