@langfuse/openai 4.0.0-alpha.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/LICENSE +21 -0
- package/dist/index.cjs +401 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +41 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.mjs +374 -0
- package/dist/index.mjs.map +1 -0
- package/dist/observeOpenAI.d.ts +24 -0
- package/dist/observeOpenAI.d.ts.map +1 -0
- package/dist/observeOpenAI.js +47 -0
- package/dist/parseOpenAI.d.ts +32 -0
- package/dist/parseOpenAI.d.ts.map +1 -0
- package/dist/parseOpenAI.js +217 -0
- package/dist/traceMethod.d.ts +7 -0
- package/dist/traceMethod.d.ts.map +1 -0
- package/dist/traceMethod.js +138 -0
- package/dist/types.d.ts +16 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +1 -0
- package/dist/utils.d.ts +2 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +3 -0
- package/package.json +37 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
Copyright (c) 2023 Finto Technologies GmbH
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2022 PostHog (part of Hiberly Inc)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
observeOpenAI: () => observeOpenAI
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(index_exports);
|
|
26
|
+
|
|
27
|
+
// src/traceMethod.ts
|
|
28
|
+
var import_tracing = require("@langfuse/tracing");
|
|
29
|
+
|
|
30
|
+
// src/parseOpenAI.ts
|
|
31
|
+
var parseInputArgs = (args) => {
|
|
32
|
+
let params = {};
|
|
33
|
+
params = {
|
|
34
|
+
frequency_penalty: args.frequency_penalty,
|
|
35
|
+
logit_bias: args.logit_bias,
|
|
36
|
+
logprobs: args.logprobs,
|
|
37
|
+
max_tokens: args.max_tokens,
|
|
38
|
+
n: args.n,
|
|
39
|
+
presence_penalty: args.presence_penalty,
|
|
40
|
+
seed: args.seed,
|
|
41
|
+
stop: args.stop,
|
|
42
|
+
stream: args.stream,
|
|
43
|
+
temperature: args.temperature,
|
|
44
|
+
top_p: args.top_p,
|
|
45
|
+
user: args.user,
|
|
46
|
+
response_format: args.response_format,
|
|
47
|
+
top_logprobs: args.top_logprobs
|
|
48
|
+
};
|
|
49
|
+
let input = args.input;
|
|
50
|
+
if (args && typeof args === "object" && !Array.isArray(args) && "messages" in args) {
|
|
51
|
+
input = {};
|
|
52
|
+
input.messages = args.messages;
|
|
53
|
+
if ("function_call" in args) {
|
|
54
|
+
input.function_call = args.function_call;
|
|
55
|
+
}
|
|
56
|
+
if ("functions" in args) {
|
|
57
|
+
input.functions = args.functions;
|
|
58
|
+
}
|
|
59
|
+
if ("tools" in args) {
|
|
60
|
+
input.tools = args.tools;
|
|
61
|
+
}
|
|
62
|
+
if ("tool_choice" in args) {
|
|
63
|
+
input.tool_choice = args.tool_choice;
|
|
64
|
+
}
|
|
65
|
+
} else if (!input) {
|
|
66
|
+
input = args.prompt;
|
|
67
|
+
}
|
|
68
|
+
return {
|
|
69
|
+
model: args.model,
|
|
70
|
+
input,
|
|
71
|
+
modelParameters: params
|
|
72
|
+
};
|
|
73
|
+
};
|
|
74
|
+
var parseCompletionOutput = (res) => {
|
|
75
|
+
var _a;
|
|
76
|
+
if (res instanceof Object && "output_text" in res && res["output_text"] !== "") {
|
|
77
|
+
return res["output_text"];
|
|
78
|
+
}
|
|
79
|
+
if (typeof res === "object" && res && "output" in res && Array.isArray(res["output"])) {
|
|
80
|
+
const output = res["output"];
|
|
81
|
+
if (output.length > 1) {
|
|
82
|
+
return output;
|
|
83
|
+
}
|
|
84
|
+
if (output.length === 1) {
|
|
85
|
+
return output[0];
|
|
86
|
+
}
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
if (!(res instanceof Object && "choices" in res && Array.isArray(res.choices))) {
|
|
90
|
+
return "";
|
|
91
|
+
}
|
|
92
|
+
return "message" in res.choices[0] ? res.choices[0].message : (_a = res.choices[0].text) != null ? _a : "";
|
|
93
|
+
};
|
|
94
|
+
var parseUsageDetails = (completionUsage) => {
|
|
95
|
+
if ("prompt_tokens" in completionUsage) {
|
|
96
|
+
const {
|
|
97
|
+
prompt_tokens,
|
|
98
|
+
completion_tokens,
|
|
99
|
+
total_tokens,
|
|
100
|
+
completion_tokens_details,
|
|
101
|
+
prompt_tokens_details
|
|
102
|
+
} = completionUsage;
|
|
103
|
+
return {
|
|
104
|
+
input: prompt_tokens,
|
|
105
|
+
output: completion_tokens,
|
|
106
|
+
total: total_tokens,
|
|
107
|
+
...Object.fromEntries(
|
|
108
|
+
Object.entries(prompt_tokens_details != null ? prompt_tokens_details : {}).map(([key, value]) => [
|
|
109
|
+
`input_${key}`,
|
|
110
|
+
value
|
|
111
|
+
])
|
|
112
|
+
),
|
|
113
|
+
...Object.fromEntries(
|
|
114
|
+
Object.entries(completion_tokens_details != null ? completion_tokens_details : {}).map(([key, value]) => [
|
|
115
|
+
`output_${key}`,
|
|
116
|
+
value
|
|
117
|
+
])
|
|
118
|
+
)
|
|
119
|
+
};
|
|
120
|
+
} else if ("input_tokens" in completionUsage) {
|
|
121
|
+
const {
|
|
122
|
+
input_tokens,
|
|
123
|
+
output_tokens,
|
|
124
|
+
total_tokens,
|
|
125
|
+
input_tokens_details,
|
|
126
|
+
output_tokens_details
|
|
127
|
+
} = completionUsage;
|
|
128
|
+
return {
|
|
129
|
+
input: input_tokens,
|
|
130
|
+
output: output_tokens,
|
|
131
|
+
total: total_tokens,
|
|
132
|
+
...Object.fromEntries(
|
|
133
|
+
Object.entries(input_tokens_details != null ? input_tokens_details : {}).map(([key, value]) => [
|
|
134
|
+
`input_${key}`,
|
|
135
|
+
value
|
|
136
|
+
])
|
|
137
|
+
),
|
|
138
|
+
...Object.fromEntries(
|
|
139
|
+
Object.entries(output_tokens_details != null ? output_tokens_details : {}).map(([key, value]) => [
|
|
140
|
+
`output_${key}`,
|
|
141
|
+
value
|
|
142
|
+
])
|
|
143
|
+
)
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
var parseUsageDetailsFromResponse = (res) => {
|
|
148
|
+
if (hasCompletionUsage(res)) {
|
|
149
|
+
return parseUsageDetails(res.usage);
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
var parseChunk = (rawChunk) => {
|
|
153
|
+
var _a, _b;
|
|
154
|
+
let isToolCall = false;
|
|
155
|
+
const _chunk = rawChunk;
|
|
156
|
+
const chunkData = (_a = _chunk == null ? void 0 : _chunk.choices) == null ? void 0 : _a[0];
|
|
157
|
+
try {
|
|
158
|
+
if ("delta" in chunkData && "tool_calls" in chunkData.delta && Array.isArray(chunkData.delta.tool_calls)) {
|
|
159
|
+
isToolCall = true;
|
|
160
|
+
return { isToolCall, data: chunkData.delta.tool_calls[0] };
|
|
161
|
+
}
|
|
162
|
+
if ("delta" in chunkData) {
|
|
163
|
+
return { isToolCall, data: ((_b = chunkData.delta) == null ? void 0 : _b.content) || "" };
|
|
164
|
+
}
|
|
165
|
+
if ("text" in chunkData) {
|
|
166
|
+
return { isToolCall, data: chunkData.text || "" };
|
|
167
|
+
}
|
|
168
|
+
} catch {
|
|
169
|
+
}
|
|
170
|
+
return { isToolCall: false, data: "" };
|
|
171
|
+
};
|
|
172
|
+
function hasCompletionUsage(obj) {
|
|
173
|
+
return obj instanceof Object && "usage" in obj && obj.usage instanceof Object && // Completion API Usage format
|
|
174
|
+
(typeof obj.usage.prompt_tokens === "number" && typeof obj.usage.completion_tokens === "number" && typeof obj.usage.total_tokens === "number" || // Response API Usage format
|
|
175
|
+
typeof obj.usage.input_tokens === "number" && typeof obj.usage.output_tokens === "number" && typeof obj.usage.total_tokens === "number");
|
|
176
|
+
}
|
|
177
|
+
var getToolCallOutput = (toolCallChunks) => {
|
|
178
|
+
var _a, _b;
|
|
179
|
+
let name = "";
|
|
180
|
+
let toolArguments = "";
|
|
181
|
+
for (const toolCall of toolCallChunks) {
|
|
182
|
+
name = ((_a = toolCall.function) == null ? void 0 : _a.name) || name;
|
|
183
|
+
toolArguments += ((_b = toolCall.function) == null ? void 0 : _b.arguments) || "";
|
|
184
|
+
}
|
|
185
|
+
return {
|
|
186
|
+
tool_calls: [
|
|
187
|
+
{
|
|
188
|
+
function: {
|
|
189
|
+
name,
|
|
190
|
+
arguments: toolArguments
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
]
|
|
194
|
+
};
|
|
195
|
+
};
|
|
196
|
+
var parseModelDataFromResponse = (res) => {
|
|
197
|
+
if (typeof res !== "object" || res === null) {
|
|
198
|
+
return {
|
|
199
|
+
model: void 0,
|
|
200
|
+
modelParameters: void 0,
|
|
201
|
+
metadata: void 0
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
const model = "model" in res ? res["model"] : void 0;
|
|
205
|
+
const modelParameters = {};
|
|
206
|
+
const modelParamKeys = [
|
|
207
|
+
"max_output_tokens",
|
|
208
|
+
"parallel_tool_calls",
|
|
209
|
+
"store",
|
|
210
|
+
"temperature",
|
|
211
|
+
"tool_choice",
|
|
212
|
+
"top_p",
|
|
213
|
+
"truncation",
|
|
214
|
+
"user"
|
|
215
|
+
];
|
|
216
|
+
const metadata = {};
|
|
217
|
+
const metadataKeys = [
|
|
218
|
+
"reasoning",
|
|
219
|
+
"incomplete_details",
|
|
220
|
+
"instructions",
|
|
221
|
+
"previous_response_id",
|
|
222
|
+
"tools",
|
|
223
|
+
"metadata",
|
|
224
|
+
"status",
|
|
225
|
+
"error"
|
|
226
|
+
];
|
|
227
|
+
for (const key of modelParamKeys) {
|
|
228
|
+
const val = key in res ? res[key] : null;
|
|
229
|
+
if (val !== null && val !== void 0) {
|
|
230
|
+
modelParameters[key] = val;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
for (const key of metadataKeys) {
|
|
234
|
+
const val = key in res ? res[key] : null;
|
|
235
|
+
if (val) {
|
|
236
|
+
metadata[key] = val;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
return {
|
|
240
|
+
model,
|
|
241
|
+
modelParameters: Object.keys(modelParameters).length > 0 ? modelParameters : void 0,
|
|
242
|
+
metadata: Object.keys(metadata).length > 0 ? metadata : void 0
|
|
243
|
+
};
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
// src/utils.ts
|
|
247
|
+
var isAsyncIterable = (x) => x != null && typeof x === "object" && typeof x[Symbol.asyncIterator] === "function";
|
|
248
|
+
|
|
249
|
+
// src/traceMethod.ts
|
|
250
|
+
var withTracing = (tracedMethod, config) => {
|
|
251
|
+
return (...args) => wrapMethod(tracedMethod, config, ...args);
|
|
252
|
+
};
|
|
253
|
+
var wrapMethod = (tracedMethod, config, ...args) => {
|
|
254
|
+
var _a, _b;
|
|
255
|
+
const { model, input, modelParameters } = parseInputArgs((_a = args[0]) != null ? _a : {});
|
|
256
|
+
const finalModelParams = { ...modelParameters, response_format: null };
|
|
257
|
+
const finalMetadata = {
|
|
258
|
+
...config == null ? void 0 : config.generationMetadata,
|
|
259
|
+
response_format: "response_format" in modelParameters ? modelParameters.response_format : void 0
|
|
260
|
+
};
|
|
261
|
+
const generation = (0, import_tracing.startGeneration)(
|
|
262
|
+
(_b = config == null ? void 0 : config.generationName) != null ? _b : "OpenAI-completion",
|
|
263
|
+
{
|
|
264
|
+
model,
|
|
265
|
+
input,
|
|
266
|
+
modelParameters: finalModelParams,
|
|
267
|
+
prompt: config == null ? void 0 : config.langfusePrompt,
|
|
268
|
+
metadata: finalMetadata
|
|
269
|
+
},
|
|
270
|
+
{ parentSpanContext: config == null ? void 0 : config.parentSpanContext }
|
|
271
|
+
).updateTrace({
|
|
272
|
+
userId: config == null ? void 0 : config.userId,
|
|
273
|
+
sessionId: config == null ? void 0 : config.sessionId,
|
|
274
|
+
tags: config == null ? void 0 : config.tags,
|
|
275
|
+
name: config == null ? void 0 : config.traceName
|
|
276
|
+
});
|
|
277
|
+
try {
|
|
278
|
+
const res = tracedMethod(...args);
|
|
279
|
+
if (isAsyncIterable(res)) {
|
|
280
|
+
return wrapAsyncIterable(res, generation);
|
|
281
|
+
}
|
|
282
|
+
if (res instanceof Promise) {
|
|
283
|
+
const wrappedPromise = res.then((result) => {
|
|
284
|
+
if (isAsyncIterable(result)) {
|
|
285
|
+
return wrapAsyncIterable(result, generation);
|
|
286
|
+
}
|
|
287
|
+
const output = parseCompletionOutput(result);
|
|
288
|
+
const usageDetails = parseUsageDetailsFromResponse(result);
|
|
289
|
+
const {
|
|
290
|
+
model: modelFromResponse,
|
|
291
|
+
modelParameters: modelParametersFromResponse,
|
|
292
|
+
metadata: metadataFromResponse
|
|
293
|
+
} = parseModelDataFromResponse(result);
|
|
294
|
+
generation.update({
|
|
295
|
+
output,
|
|
296
|
+
usageDetails,
|
|
297
|
+
model: modelFromResponse,
|
|
298
|
+
modelParameters: modelParametersFromResponse,
|
|
299
|
+
metadata: metadataFromResponse
|
|
300
|
+
}).end();
|
|
301
|
+
return result;
|
|
302
|
+
}).catch((err) => {
|
|
303
|
+
generation.update({
|
|
304
|
+
statusMessage: String(err),
|
|
305
|
+
level: "ERROR",
|
|
306
|
+
costDetails: {
|
|
307
|
+
input: 0,
|
|
308
|
+
output: 0,
|
|
309
|
+
total: 0
|
|
310
|
+
}
|
|
311
|
+
}).end();
|
|
312
|
+
throw err;
|
|
313
|
+
});
|
|
314
|
+
return wrappedPromise;
|
|
315
|
+
}
|
|
316
|
+
return res;
|
|
317
|
+
} catch (error) {
|
|
318
|
+
generation.update({
|
|
319
|
+
statusMessage: String(error),
|
|
320
|
+
level: "ERROR",
|
|
321
|
+
costDetails: {
|
|
322
|
+
input: 0,
|
|
323
|
+
output: 0,
|
|
324
|
+
total: 0
|
|
325
|
+
}
|
|
326
|
+
}).end();
|
|
327
|
+
throw error;
|
|
328
|
+
}
|
|
329
|
+
};
|
|
330
|
+
function wrapAsyncIterable(iterable, generation) {
|
|
331
|
+
async function* tracedOutputGenerator() {
|
|
332
|
+
const response = iterable;
|
|
333
|
+
const textChunks = [];
|
|
334
|
+
const toolCallChunks = [];
|
|
335
|
+
let usage = null;
|
|
336
|
+
let completionStartTime = void 0;
|
|
337
|
+
let usageDetails = void 0;
|
|
338
|
+
let output = null;
|
|
339
|
+
for await (const rawChunk of response) {
|
|
340
|
+
completionStartTime = completionStartTime != null ? completionStartTime : /* @__PURE__ */ new Date();
|
|
341
|
+
if (typeof rawChunk === "object" && rawChunk && "response" in rawChunk) {
|
|
342
|
+
const result = rawChunk["response"];
|
|
343
|
+
output = parseCompletionOutput(result);
|
|
344
|
+
usageDetails = parseUsageDetailsFromResponse(result);
|
|
345
|
+
const {
|
|
346
|
+
model: modelFromResponse,
|
|
347
|
+
modelParameters: modelParametersFromResponse,
|
|
348
|
+
metadata: metadataFromResponse
|
|
349
|
+
} = parseModelDataFromResponse(result);
|
|
350
|
+
generation.update({
|
|
351
|
+
model: modelFromResponse,
|
|
352
|
+
modelParameters: modelParametersFromResponse,
|
|
353
|
+
metadata: metadataFromResponse
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
if (typeof rawChunk === "object" && rawChunk != null && "usage" in rawChunk) {
|
|
357
|
+
usage = rawChunk.usage;
|
|
358
|
+
}
|
|
359
|
+
const processedChunk = parseChunk(rawChunk);
|
|
360
|
+
if (!processedChunk.isToolCall) {
|
|
361
|
+
textChunks.push(processedChunk.data);
|
|
362
|
+
} else {
|
|
363
|
+
toolCallChunks.push(processedChunk.data);
|
|
364
|
+
}
|
|
365
|
+
yield rawChunk;
|
|
366
|
+
}
|
|
367
|
+
output = output != null ? output : toolCallChunks.length > 0 ? getToolCallOutput(toolCallChunks) : textChunks.join("");
|
|
368
|
+
generation.update({
|
|
369
|
+
output,
|
|
370
|
+
completionStartTime,
|
|
371
|
+
usageDetails: usageDetails != null ? usageDetails : usage ? parseUsageDetails(usage) : void 0
|
|
372
|
+
}).end();
|
|
373
|
+
}
|
|
374
|
+
return tracedOutputGenerator();
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// src/observeOpenAI.ts
|
|
378
|
+
var observeOpenAI = (sdk, langfuseConfig) => {
|
|
379
|
+
return new Proxy(sdk, {
|
|
380
|
+
get(wrappedSdk, propKey, proxy) {
|
|
381
|
+
var _a, _b;
|
|
382
|
+
const originalProperty = wrappedSdk[propKey];
|
|
383
|
+
const defaultGenerationName = `${(_a = sdk.constructor) == null ? void 0 : _a.name}.${propKey.toString()}`;
|
|
384
|
+
const generationName = (_b = langfuseConfig == null ? void 0 : langfuseConfig.generationName) != null ? _b : defaultGenerationName;
|
|
385
|
+
const config = { ...langfuseConfig, generationName };
|
|
386
|
+
if (typeof originalProperty === "function") {
|
|
387
|
+
return withTracing(originalProperty.bind(wrappedSdk), config);
|
|
388
|
+
}
|
|
389
|
+
const isNestedOpenAIObject = originalProperty && !Array.isArray(originalProperty) && !(originalProperty instanceof Date) && typeof originalProperty === "object";
|
|
390
|
+
if (isNestedOpenAIObject) {
|
|
391
|
+
return observeOpenAI(originalProperty, config);
|
|
392
|
+
}
|
|
393
|
+
return Reflect.get(wrappedSdk, propKey, proxy);
|
|
394
|
+
}
|
|
395
|
+
});
|
|
396
|
+
};
|
|
397
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
398
|
+
0 && (module.exports = {
|
|
399
|
+
observeOpenAI
|
|
400
|
+
});
|
|
401
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/traceMethod.ts","../src/parseOpenAI.ts","../src/utils.ts","../src/observeOpenAI.ts"],"sourcesContent":["export { observeOpenAI } from \"./observeOpenAI.js\";\nexport * from \"./types.js\";\n","import { LangfuseGeneration, startGeneration } from \"@langfuse/tracing\";\nimport type OpenAI from \"openai\";\n\nimport {\n getToolCallOutput,\n parseChunk,\n parseCompletionOutput,\n parseInputArgs,\n parseUsageDetails,\n parseModelDataFromResponse,\n parseUsageDetailsFromResponse,\n} from \"./parseOpenAI.js\";\nimport type { LangfuseConfig } from \"./types.js\";\nimport { isAsyncIterable } from \"./utils.js\";\n\ntype GenericMethod = (...args: unknown[]) => unknown;\n\nexport const withTracing = <T extends GenericMethod>(\n tracedMethod: T,\n config?: LangfuseConfig & Required<{ generationName: string }>,\n): ((...args: Parameters<T>) => Promise<ReturnType<T>>) => {\n return (...args) => wrapMethod(tracedMethod, config, ...args);\n};\n\nconst wrapMethod = <T extends GenericMethod>(\n tracedMethod: T,\n config?: LangfuseConfig,\n ...args: Parameters<T>\n): ReturnType<T> | any => {\n const { model, input, modelParameters } = parseInputArgs(args[0] ?? {});\n\n const finalModelParams = { ...modelParameters, response_format: null };\n const finalMetadata = {\n ...config?.generationMetadata,\n response_format:\n \"response_format\" in modelParameters\n ? modelParameters.response_format\n : undefined,\n };\n\n const generation = startGeneration(\n config?.generationName ?? \"OpenAI-completion\",\n {\n model,\n input,\n modelParameters: finalModelParams,\n prompt: config?.langfusePrompt,\n metadata: finalMetadata,\n },\n { parentSpanContext: config?.parentSpanContext },\n ).updateTrace({\n userId: config?.userId,\n sessionId: config?.sessionId,\n tags: config?.tags,\n name: config?.traceName,\n });\n\n try {\n const res = tracedMethod(...args);\n\n // Handle stream responses\n if (isAsyncIterable(res)) {\n return wrapAsyncIterable(res, generation);\n }\n\n if (res instanceof Promise) {\n const wrappedPromise = res\n .then((result) => {\n if (isAsyncIterable(result)) {\n return wrapAsyncIterable(result, generation);\n }\n\n const output = parseCompletionOutput(result);\n const usageDetails = parseUsageDetailsFromResponse(result);\n const {\n model: modelFromResponse,\n modelParameters: modelParametersFromResponse,\n metadata: metadataFromResponse,\n } = parseModelDataFromResponse(result);\n\n generation\n .update({\n output,\n usageDetails,\n model: modelFromResponse,\n modelParameters: modelParametersFromResponse,\n metadata: metadataFromResponse,\n })\n .end();\n\n return result;\n })\n .catch((err) => {\n generation\n .update({\n statusMessage: String(err),\n level: \"ERROR\",\n costDetails: {\n input: 0,\n output: 0,\n total: 0,\n },\n })\n .end();\n\n throw err;\n });\n\n return wrappedPromise;\n }\n\n return res;\n } catch (error) {\n generation\n .update({\n statusMessage: String(error),\n level: \"ERROR\",\n costDetails: {\n input: 0,\n output: 0,\n total: 0,\n },\n })\n .end();\n\n throw error;\n }\n};\n\nfunction wrapAsyncIterable<R>(\n iterable: AsyncIterable<unknown>,\n generation: LangfuseGeneration,\n): R {\n async function* tracedOutputGenerator(): AsyncGenerator<\n unknown,\n void,\n unknown\n > {\n const response = iterable;\n const textChunks: string[] = [];\n const toolCallChunks: OpenAI.Chat.Completions.ChatCompletionChunk.Choice.Delta.ToolCall[] =\n [];\n let usage: OpenAI.CompletionUsage | null = null;\n let completionStartTime: Date | undefined = undefined;\n let usageDetails: Record<string, number> | undefined = undefined;\n let output: unknown = null;\n\n for await (const rawChunk of response as AsyncIterable<unknown>) {\n completionStartTime = completionStartTime ?? new Date();\n\n // Handle Response API chunks\n if (typeof rawChunk === \"object\" && rawChunk && \"response\" in rawChunk) {\n const result = rawChunk[\"response\"];\n output = parseCompletionOutput(result);\n usageDetails = parseUsageDetailsFromResponse(result);\n\n const {\n model: modelFromResponse,\n modelParameters: modelParametersFromResponse,\n metadata: metadataFromResponse,\n } = parseModelDataFromResponse(result);\n\n generation.update({\n model: modelFromResponse,\n modelParameters: modelParametersFromResponse,\n metadata: metadataFromResponse,\n });\n }\n\n if (\n typeof rawChunk === \"object\" &&\n rawChunk != null &&\n \"usage\" in rawChunk\n ) {\n usage = rawChunk.usage as OpenAI.CompletionUsage | null;\n }\n\n const processedChunk = parseChunk(rawChunk);\n\n if (!processedChunk.isToolCall) {\n textChunks.push(processedChunk.data);\n } else {\n toolCallChunks.push(processedChunk.data);\n }\n\n yield rawChunk;\n }\n\n output =\n output ??\n (toolCallChunks.length > 0\n ? getToolCallOutput(toolCallChunks)\n : textChunks.join(\"\"));\n\n generation\n .update({\n output,\n completionStartTime,\n usageDetails:\n usageDetails ?? (usage ? parseUsageDetails(usage) : undefined),\n })\n .end();\n }\n\n return tracedOutputGenerator() as R;\n}\n","import type OpenAI from \"openai\";\n\ntype ParsedOpenAIArguments = {\n model: string;\n input: Record<string, any> | string;\n modelParameters: Record<string, any>;\n};\n\nexport const parseInputArgs = (\n args: Record<string, any>,\n): ParsedOpenAIArguments => {\n let params: Record<string, any> = {};\n params = {\n frequency_penalty: args.frequency_penalty,\n logit_bias: args.logit_bias,\n logprobs: args.logprobs,\n max_tokens: args.max_tokens,\n n: args.n,\n presence_penalty: args.presence_penalty,\n seed: args.seed,\n stop: args.stop,\n stream: args.stream,\n temperature: args.temperature,\n top_p: args.top_p,\n user: args.user,\n response_format: args.response_format,\n top_logprobs: args.top_logprobs,\n };\n\n let input: Record<string, any> | string = args.input;\n\n if (\n args &&\n typeof args === \"object\" &&\n !Array.isArray(args) &&\n \"messages\" in args\n ) {\n input = {};\n input.messages = args.messages;\n if (\"function_call\" in args) {\n input.function_call = args.function_call;\n }\n if (\"functions\" in args) {\n input.functions = args.functions;\n }\n if (\"tools\" in args) {\n input.tools = args.tools;\n }\n\n if (\"tool_choice\" in args) {\n input.tool_choice = args.tool_choice;\n }\n } else if (!input) {\n input = args.prompt;\n }\n\n return {\n model: args.model,\n input: input,\n modelParameters: params,\n };\n};\n\nexport const parseCompletionOutput = (res: unknown): unknown => {\n if (\n res instanceof Object &&\n \"output_text\" in res &&\n res[\"output_text\"] !== \"\"\n ) {\n return res[\"output_text\"] as string;\n }\n\n if (\n typeof res === \"object\" &&\n res &&\n \"output\" in res &&\n Array.isArray(res[\"output\"])\n ) {\n const output = res[\"output\"];\n\n if (output.length > 1) {\n return output;\n }\n if (output.length === 1) {\n return output[0] as Record<string, unknown>;\n }\n\n return null;\n }\n\n if (\n !(res instanceof Object && \"choices\" in res && Array.isArray(res.choices))\n ) {\n return \"\";\n }\n\n return \"message\" in res.choices[0]\n ? res.choices[0].message\n : (res.choices[0].text ?? \"\");\n};\n\nexport const parseUsageDetails = (\n completionUsage: OpenAI.CompletionUsage,\n): Record<string, number> | undefined => {\n if (\"prompt_tokens\" in completionUsage) {\n const {\n prompt_tokens,\n completion_tokens,\n total_tokens,\n completion_tokens_details,\n prompt_tokens_details,\n } = completionUsage;\n\n return {\n input: prompt_tokens,\n output: completion_tokens,\n total: total_tokens,\n ...Object.fromEntries(\n Object.entries(prompt_tokens_details ?? {}).map(([key, value]) => [\n `input_${key}`,\n value as number,\n ]),\n ),\n ...Object.fromEntries(\n Object.entries(completion_tokens_details ?? {}).map(([key, value]) => [\n `output_${key}`,\n value as number,\n ]),\n ),\n };\n } else if (\"input_tokens\" in completionUsage) {\n const {\n input_tokens,\n output_tokens,\n total_tokens,\n input_tokens_details,\n output_tokens_details,\n } = completionUsage;\n\n return {\n input: input_tokens,\n output: output_tokens,\n total: total_tokens,\n ...Object.fromEntries(\n Object.entries(input_tokens_details ?? {}).map(([key, value]) => [\n `input_${key}`,\n value as number,\n ]),\n ),\n ...Object.fromEntries(\n Object.entries(output_tokens_details ?? {}).map(([key, value]) => [\n `output_${key}`,\n value as number,\n ]),\n ),\n };\n }\n};\n\nexport const parseUsageDetailsFromResponse = (\n res: unknown,\n): Record<string, number> | undefined => {\n if (hasCompletionUsage(res)) {\n return parseUsageDetails(res.usage);\n }\n};\n\nexport const parseChunk = (\n rawChunk: unknown,\n):\n | { isToolCall: false; data: string }\n | {\n isToolCall: true;\n data: OpenAI.Chat.Completions.ChatCompletionChunk.Choice.Delta.ToolCall;\n } => {\n let isToolCall = false;\n const _chunk = rawChunk as\n | OpenAI.ChatCompletionChunk\n | OpenAI.Completions.Completion;\n const chunkData = _chunk?.choices?.[0];\n\n try {\n if (\n \"delta\" in chunkData &&\n \"tool_calls\" in chunkData.delta &&\n Array.isArray(chunkData.delta.tool_calls)\n ) {\n isToolCall = true;\n\n return { isToolCall, data: chunkData.delta.tool_calls[0] };\n }\n if (\"delta\" in chunkData) {\n return { isToolCall, data: chunkData.delta?.content || \"\" };\n }\n\n if (\"text\" in chunkData) {\n return { isToolCall, data: chunkData.text || \"\" };\n }\n } catch {}\n\n return { isToolCall: false, data: \"\" };\n};\n\n// Type guard to check if an unknown object is a UsageResponse\nfunction hasCompletionUsage(\n obj: any,\n): obj is { usage: OpenAI.CompletionUsage } {\n return (\n obj instanceof Object &&\n \"usage\" in obj &&\n obj.usage instanceof Object &&\n // Completion API Usage format\n ((typeof obj.usage.prompt_tokens === \"number\" &&\n typeof obj.usage.completion_tokens === \"number\" &&\n typeof obj.usage.total_tokens === \"number\") ||\n // Response API Usage format\n (typeof obj.usage.input_tokens === \"number\" &&\n typeof obj.usage.output_tokens === \"number\" &&\n typeof obj.usage.total_tokens === \"number\"))\n );\n}\n\nexport const getToolCallOutput = (\n toolCallChunks: OpenAI.Chat.Completions.ChatCompletionChunk.Choice.Delta.ToolCall[],\n): {\n tool_calls: {\n function: {\n name: string;\n arguments: string;\n };\n }[];\n} => {\n let name = \"\";\n let toolArguments = \"\";\n\n for (const toolCall of toolCallChunks) {\n name = toolCall.function?.name || name;\n toolArguments += toolCall.function?.arguments || \"\";\n }\n\n return {\n tool_calls: [\n {\n function: {\n name,\n arguments: toolArguments,\n },\n },\n ],\n };\n};\n\nexport const parseModelDataFromResponse = (\n res: unknown,\n): {\n model: string | undefined;\n modelParameters: Record<string, string | number> | undefined;\n metadata: Record<string, unknown> | undefined;\n} => {\n if (typeof res !== \"object\" || res === null) {\n return {\n model: undefined,\n modelParameters: undefined,\n metadata: undefined,\n };\n }\n\n const model = \"model\" in res ? (res[\"model\"] as string) : undefined;\n const modelParameters: Record<string, string | number> = {};\n const modelParamKeys = [\n \"max_output_tokens\",\n \"parallel_tool_calls\",\n \"store\",\n \"temperature\",\n \"tool_choice\",\n \"top_p\",\n \"truncation\",\n \"user\",\n ];\n\n const metadata: Record<string, unknown> = {};\n const metadataKeys = [\n \"reasoning\",\n \"incomplete_details\",\n \"instructions\",\n \"previous_response_id\",\n \"tools\",\n \"metadata\",\n \"status\",\n \"error\",\n ];\n\n for (const key of modelParamKeys) {\n const val =\n key in res ? (res[key as keyof typeof res] as string | number) : null;\n if (val !== null && val !== undefined) {\n modelParameters[key as keyof typeof modelParameters] = val;\n }\n }\n\n for (const key of metadataKeys) {\n const val =\n key in res ? (res[key as keyof typeof res] as string | number) : null;\n if (val) {\n metadata[key as keyof typeof metadata] = val;\n }\n }\n\n return {\n model,\n modelParameters:\n Object.keys(modelParameters).length > 0 ? modelParameters : undefined,\n metadata: Object.keys(metadata).length > 0 ? metadata : undefined,\n };\n};\n","export const isAsyncIterable = (x: unknown): x is AsyncIterable<unknown> =>\n x != null &&\n typeof x === \"object\" &&\n typeof (x as any)[Symbol.asyncIterator] === \"function\";\n","import { withTracing } from \"./traceMethod.js\";\nimport type { LangfuseConfig } from \"./types.js\";\n\n/**\n * Wraps an OpenAI SDK object with Langfuse tracing. Function calls are extended with a tracer that logs detailed information about the call, including the method name,\n * input parameters, and output.\n * \n * @param {T} sdk - The OpenAI SDK object to be wrapped.\n * @param {LangfuseConfig} [langfuseConfig] - Optional configuration object for the wrapper.\n * @param {string} [langfuseConfig.traceName] - The name to use for tracing. If not provided, a default name based on the SDK's constructor name and the method name will be used.\n * @param {string} [langfuseConfig.sessionId] - Optional session ID for tracing.\n * @param {string} [langfuseConfig.userId] - Optional user ID for tracing.\n * @param {string} [langfuseConfig.tags] - Optional tags for tracing.\n * @returns {T} - A proxy of the original SDK object with methods wrapped for tracing.\n *\n * @example\n * const client = new OpenAI();\n * const res = observeOpenAI(client, { traceName: \"My.OpenAI.Chat.Trace\" }).chat.completions.create({\n * messages: [{ role: \"system\", content: \"Say this is a test!\" }],\n model: \"gpt-3.5-turbo\",\n user: \"langfuse\",\n max_tokens: 300\n * });\n * */\nexport const observeOpenAI = <SDKType extends object>(\n sdk: SDKType,\n langfuseConfig?: LangfuseConfig,\n): SDKType => {\n return new Proxy(sdk, {\n get(wrappedSdk, propKey, proxy) {\n const originalProperty = wrappedSdk[propKey as keyof SDKType];\n\n const defaultGenerationName = `${sdk.constructor?.name}.${propKey.toString()}`;\n const generationName =\n langfuseConfig?.generationName ?? defaultGenerationName;\n const config = { ...langfuseConfig, generationName };\n\n // Trace methods of the OpenAI SDK\n if (typeof originalProperty === \"function\") {\n return withTracing(originalProperty.bind(wrappedSdk), config);\n }\n\n const isNestedOpenAIObject =\n originalProperty &&\n !Array.isArray(originalProperty) &&\n !(originalProperty instanceof Date) &&\n typeof originalProperty === \"object\";\n\n // Recursively wrap nested objects to ensure all nested properties or methods are also traced\n if (isNestedOpenAIObject) {\n return observeOpenAI(originalProperty, config);\n }\n\n // Fallback to returning the original value\n return Reflect.get(wrappedSdk, propKey, proxy);\n },\n });\n};\n"],"mappings":"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","names":[]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { SpanContext } from '@opentelemetry/api';
|
|
2
|
+
|
|
3
|
+
type LangfuseConfig = {
|
|
4
|
+
parentSpanContext?: SpanContext;
|
|
5
|
+
traceName?: string;
|
|
6
|
+
sessionId?: string;
|
|
7
|
+
userId?: string;
|
|
8
|
+
tags?: string[];
|
|
9
|
+
generationName?: string;
|
|
10
|
+
generationMetadata?: Record<string, unknown>;
|
|
11
|
+
langfusePrompt?: {
|
|
12
|
+
name: string;
|
|
13
|
+
version: number;
|
|
14
|
+
isFallback: boolean;
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Wraps an OpenAI SDK object with Langfuse tracing. Function calls are extended with a tracer that logs detailed information about the call, including the method name,
|
|
20
|
+
* input parameters, and output.
|
|
21
|
+
*
|
|
22
|
+
* @param {T} sdk - The OpenAI SDK object to be wrapped.
|
|
23
|
+
* @param {LangfuseConfig} [langfuseConfig] - Optional configuration object for the wrapper.
|
|
24
|
+
* @param {string} [langfuseConfig.traceName] - The name to use for tracing. If not provided, a default name based on the SDK's constructor name and the method name will be used.
|
|
25
|
+
* @param {string} [langfuseConfig.sessionId] - Optional session ID for tracing.
|
|
26
|
+
* @param {string} [langfuseConfig.userId] - Optional user ID for tracing.
|
|
27
|
+
* @param {string} [langfuseConfig.tags] - Optional tags for tracing.
|
|
28
|
+
* @returns {T} - A proxy of the original SDK object with methods wrapped for tracing.
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* const client = new OpenAI();
|
|
32
|
+
* const res = observeOpenAI(client, { traceName: "My.OpenAI.Chat.Trace" }).chat.completions.create({
|
|
33
|
+
* messages: [{ role: "system", content: "Say this is a test!" }],
|
|
34
|
+
model: "gpt-3.5-turbo",
|
|
35
|
+
user: "langfuse",
|
|
36
|
+
max_tokens: 300
|
|
37
|
+
* });
|
|
38
|
+
* */
|
|
39
|
+
declare const observeOpenAI: <SDKType extends object>(sdk: SDKType, langfuseConfig?: LangfuseConfig) => SDKType;
|
|
40
|
+
|
|
41
|
+
export { type LangfuseConfig, observeOpenAI };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,cAAc,YAAY,CAAC"}
|
package/dist/index.js
ADDED