braintrust 3.29.0 → 3.30.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/dev/dist/index.js +1465 -881
- package/dev/dist/index.mjs +846 -262
- package/dist/apply-auto-instrumentation.js +217 -217
- package/dist/apply-auto-instrumentation.mjs +2 -2
- package/dist/auto-instrumentations/bundler/esbuild.cjs +13 -1
- package/dist/auto-instrumentations/bundler/esbuild.mjs +2 -2
- package/dist/auto-instrumentations/bundler/next.cjs +13 -1
- package/dist/auto-instrumentations/bundler/next.mjs +3 -3
- package/dist/auto-instrumentations/bundler/rollup.cjs +13 -1
- package/dist/auto-instrumentations/bundler/rollup.mjs +2 -2
- package/dist/auto-instrumentations/bundler/vite.cjs +13 -1
- package/dist/auto-instrumentations/bundler/vite.mjs +2 -2
- package/dist/auto-instrumentations/bundler/webpack-loader.cjs +13 -1
- package/dist/auto-instrumentations/bundler/webpack.cjs +13 -1
- package/dist/auto-instrumentations/bundler/webpack.mjs +3 -3
- package/dist/auto-instrumentations/{chunk-DKTGDNA7.mjs → chunk-LW4HDHXT.mjs} +1 -1
- package/dist/auto-instrumentations/{chunk-OMCZ3MV2.mjs → chunk-OXINGIEZ.mjs} +1 -1
- package/dist/auto-instrumentations/{chunk-AOIYCVEL.mjs → chunk-U64OYU4Q.mjs} +13 -1
- package/dist/auto-instrumentations/hook.mjs +16 -4
- package/dist/auto-instrumentations/index.cjs +13 -1
- package/dist/auto-instrumentations/index.mjs +1 -1
- package/dist/browser.d.mts +133 -2
- package/dist/browser.d.ts +133 -2
- package/dist/browser.js +1143 -297
- package/dist/browser.mjs +1143 -297
- package/dist/{chunk-6Z5S7VOU.js → chunk-2XDW3UCG.js} +13 -1
- package/dist/{chunk-M6XPNJC4.mjs → chunk-BU6SM54N.mjs} +687 -115
- package/dist/{chunk-7FA6VP2S.mjs → chunk-N3JKQ3D3.mjs} +13 -1
- package/dist/{chunk-XLLGRXGR.js → chunk-TWCDSYJN.js} +1803 -1231
- package/dist/cli.js +819 -235
- package/dist/edge-light.d.mts +1 -1
- package/dist/edge-light.d.ts +1 -1
- package/dist/edge-light.js +1143 -297
- package/dist/edge-light.mjs +1143 -297
- package/dist/index.d.mts +133 -2
- package/dist/index.d.ts +133 -2
- package/dist/index.js +902 -640
- package/dist/index.mjs +336 -74
- package/dist/instrumentation/index.d.mts +15 -0
- package/dist/instrumentation/index.d.ts +15 -0
- package/dist/instrumentation/index.js +857 -262
- package/dist/instrumentation/index.mjs +857 -262
- package/dist/vitest-evals-reporter.js +16 -16
- package/dist/vitest-evals-reporter.mjs +2 -2
- package/dist/workerd.d.mts +1 -1
- package/dist/workerd.d.ts +1 -1
- package/dist/workerd.js +1143 -297
- package/dist/workerd.mjs +1143 -297
- package/package.json +1 -1
package/dev/dist/index.mjs
CHANGED
|
@@ -5972,7 +5972,7 @@ var INSTRUMENTATION_NAMES = {
|
|
|
5972
5972
|
var INTERNAL_SPAN_INSTRUMENTATION_NAME = /* @__PURE__ */ Symbol.for(
|
|
5973
5973
|
"braintrust.spanInstrumentationName"
|
|
5974
5974
|
);
|
|
5975
|
-
var SDK_VERSION = true ? "3.
|
|
5975
|
+
var SDK_VERSION = true ? "3.30.0" : "0.0.0";
|
|
5976
5976
|
function withSpanInstrumentationName(args, instrumentationName) {
|
|
5977
5977
|
return {
|
|
5978
5978
|
...args,
|
|
@@ -9487,6 +9487,15 @@ function _internalStartSpanWithInitialMerge(args) {
|
|
|
9487
9487
|
[INITIAL_SPAN_WRITE_AS_MERGE]: true
|
|
9488
9488
|
}).span;
|
|
9489
9489
|
}
|
|
9490
|
+
function _internalStartSpanWithInitialMergeAndParentSpanIds(args) {
|
|
9491
|
+
return startSpanAndIsLogger(
|
|
9492
|
+
{
|
|
9493
|
+
...args,
|
|
9494
|
+
[INITIAL_SPAN_WRITE_AS_MERGE]: true
|
|
9495
|
+
},
|
|
9496
|
+
{ useParentSpanIdsForObjectParent: true }
|
|
9497
|
+
).span;
|
|
9498
|
+
}
|
|
9490
9499
|
function _internalStartSpanWithContext(args, context) {
|
|
9491
9500
|
return startSpanAndIsLogger({
|
|
9492
9501
|
...args,
|
|
@@ -9497,7 +9506,7 @@ async function flush(options) {
|
|
|
9497
9506
|
const state = options?.state ?? _globalState;
|
|
9498
9507
|
return await state.bgLogger().flush();
|
|
9499
9508
|
}
|
|
9500
|
-
function startSpanAndIsLogger(args) {
|
|
9509
|
+
function startSpanAndIsLogger(args, internalOptions) {
|
|
9501
9510
|
const state = args?.state ?? _globalState;
|
|
9502
9511
|
const { parentObject, propagatedState } = getSpanParentObjectAndPropagatedState({
|
|
9503
9512
|
asyncFlush: args?.asyncFlush,
|
|
@@ -9511,7 +9520,7 @@ function startSpanAndIsLogger(args) {
|
|
|
9511
9520
|
) ? {
|
|
9512
9521
|
spanId: parentObject.data.span_id,
|
|
9513
9522
|
rootSpanId: parentObject.data.root_span_id
|
|
9514
|
-
} : void 0;
|
|
9523
|
+
} : internalOptions?.useParentSpanIdsForObjectParent ? args?.parentSpanIds : void 0;
|
|
9515
9524
|
const { parent: _ignoredParent, ...spanArgs } = args ?? {};
|
|
9516
9525
|
const span = new SpanImpl({
|
|
9517
9526
|
state,
|
|
@@ -12811,6 +12820,131 @@ function unsubscribeAll(unsubscribers) {
|
|
|
12811
12820
|
return [];
|
|
12812
12821
|
}
|
|
12813
12822
|
|
|
12823
|
+
// src/instrumentation/core/channel-definitions.ts
|
|
12824
|
+
function channel(spec) {
|
|
12825
|
+
return spec;
|
|
12826
|
+
}
|
|
12827
|
+
function defineChannels(pkg, channels, options) {
|
|
12828
|
+
const { instrumentationName } = options;
|
|
12829
|
+
return Object.fromEntries(
|
|
12830
|
+
Object.entries(channels).map(([key, spec]) => {
|
|
12831
|
+
const fullChannelName = `orchestrion:${pkg}:${spec.channelName}`;
|
|
12832
|
+
if (spec.kind === "async") {
|
|
12833
|
+
const asyncSpec = spec;
|
|
12834
|
+
const tracingChannel2 = () => isomorph_default.newTracingChannel(
|
|
12835
|
+
fullChannelName
|
|
12836
|
+
);
|
|
12837
|
+
const intercept2 = (interceptor) => {
|
|
12838
|
+
const hook = tracingChannel2();
|
|
12839
|
+
return typeof hook.intercept === "function" ? hook.intercept(interceptor) : () => {
|
|
12840
|
+
};
|
|
12841
|
+
};
|
|
12842
|
+
return [
|
|
12843
|
+
key,
|
|
12844
|
+
{
|
|
12845
|
+
...asyncSpec,
|
|
12846
|
+
instrumentationName,
|
|
12847
|
+
intercept: intercept2,
|
|
12848
|
+
invoke: (target, thisArg, args, additional) => {
|
|
12849
|
+
const hook = tracingChannel2();
|
|
12850
|
+
return typeof hook.invoke === "function" ? hook.invoke(target, thisArg, args, additional) : Reflect.apply(target, thisArg, args);
|
|
12851
|
+
},
|
|
12852
|
+
tracingChannel: tracingChannel2,
|
|
12853
|
+
tracePromise: (fn, context) => tracingChannel2().tracePromise(
|
|
12854
|
+
fn,
|
|
12855
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
12856
|
+
context
|
|
12857
|
+
)
|
|
12858
|
+
}
|
|
12859
|
+
];
|
|
12860
|
+
}
|
|
12861
|
+
const syncSpec = spec;
|
|
12862
|
+
const tracingChannel = () => isomorph_default.newTracingChannel(
|
|
12863
|
+
fullChannelName
|
|
12864
|
+
);
|
|
12865
|
+
const intercept = (interceptor) => {
|
|
12866
|
+
const hook = tracingChannel();
|
|
12867
|
+
return typeof hook.intercept === "function" ? hook.intercept(interceptor) : () => {
|
|
12868
|
+
};
|
|
12869
|
+
};
|
|
12870
|
+
return [
|
|
12871
|
+
key,
|
|
12872
|
+
{
|
|
12873
|
+
...syncSpec,
|
|
12874
|
+
instrumentationName,
|
|
12875
|
+
intercept,
|
|
12876
|
+
invoke: (target, thisArg, args, additional) => {
|
|
12877
|
+
const hook = tracingChannel();
|
|
12878
|
+
return typeof hook.invoke === "function" ? hook.invoke(target, thisArg, args, additional) : Reflect.apply(target, thisArg, args);
|
|
12879
|
+
},
|
|
12880
|
+
tracingChannel,
|
|
12881
|
+
traceSync: (fn, context) => tracingChannel().traceSync(
|
|
12882
|
+
fn,
|
|
12883
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
12884
|
+
context
|
|
12885
|
+
)
|
|
12886
|
+
}
|
|
12887
|
+
];
|
|
12888
|
+
})
|
|
12889
|
+
);
|
|
12890
|
+
}
|
|
12891
|
+
|
|
12892
|
+
// src/instrumentation/plugins/openai-channels.ts
|
|
12893
|
+
var openAIChannels = defineChannels(
|
|
12894
|
+
"openai",
|
|
12895
|
+
{
|
|
12896
|
+
filesCreateTraced: channel({
|
|
12897
|
+
channelName: "files.create-traced",
|
|
12898
|
+
kind: "async"
|
|
12899
|
+
}),
|
|
12900
|
+
batchesRetrieveTraced: channel({
|
|
12901
|
+
channelName: "batches.retrieve-traced",
|
|
12902
|
+
kind: "async"
|
|
12903
|
+
}),
|
|
12904
|
+
batchesCompleteTrace: channel({
|
|
12905
|
+
channelName: "batches.complete-trace",
|
|
12906
|
+
kind: "async"
|
|
12907
|
+
}),
|
|
12908
|
+
chatCompletionsCreate: channel({
|
|
12909
|
+
channelName: "chat.completions.create",
|
|
12910
|
+
kind: "async"
|
|
12911
|
+
}),
|
|
12912
|
+
embeddingsCreate: channel({
|
|
12913
|
+
channelName: "embeddings.create",
|
|
12914
|
+
kind: "async"
|
|
12915
|
+
}),
|
|
12916
|
+
betaChatCompletionsParse: channel({
|
|
12917
|
+
channelName: "beta.chat.completions.parse",
|
|
12918
|
+
kind: "async"
|
|
12919
|
+
}),
|
|
12920
|
+
betaChatCompletionsStream: channel({
|
|
12921
|
+
channelName: "beta.chat.completions.stream",
|
|
12922
|
+
kind: "sync-stream"
|
|
12923
|
+
}),
|
|
12924
|
+
moderationsCreate: channel({
|
|
12925
|
+
channelName: "moderations.create",
|
|
12926
|
+
kind: "async"
|
|
12927
|
+
}),
|
|
12928
|
+
responsesCreate: channel({
|
|
12929
|
+
channelName: "responses.create",
|
|
12930
|
+
kind: "async"
|
|
12931
|
+
}),
|
|
12932
|
+
responsesStream: channel({
|
|
12933
|
+
channelName: "responses.stream",
|
|
12934
|
+
kind: "sync-stream"
|
|
12935
|
+
}),
|
|
12936
|
+
responsesParse: channel({
|
|
12937
|
+
channelName: "responses.parse",
|
|
12938
|
+
kind: "async"
|
|
12939
|
+
}),
|
|
12940
|
+
responsesCompact: channel({
|
|
12941
|
+
channelName: "responses.compact",
|
|
12942
|
+
kind: "async"
|
|
12943
|
+
})
|
|
12944
|
+
},
|
|
12945
|
+
{ instrumentationName: INSTRUMENTATION_NAMES.OPENAI }
|
|
12946
|
+
);
|
|
12947
|
+
|
|
12814
12948
|
// src/wrappers/attachment-utils.ts
|
|
12815
12949
|
function getExtensionFromMediaType(mediaType) {
|
|
12816
12950
|
const extensionMap = {
|
|
@@ -12986,173 +13120,691 @@ function processInputAttachments(input) {
|
|
|
12986
13120
|
return processNode(input);
|
|
12987
13121
|
}
|
|
12988
13122
|
|
|
12989
|
-
// src/instrumentation/
|
|
12990
|
-
|
|
12991
|
-
|
|
13123
|
+
// src/instrumentation/plugins/openai-span-data.ts
|
|
13124
|
+
var OPENAI_METADATA_KEYS = [
|
|
13125
|
+
"model",
|
|
13126
|
+
"temperature",
|
|
13127
|
+
"top_p",
|
|
13128
|
+
"max_tokens",
|
|
13129
|
+
"frequency_penalty",
|
|
13130
|
+
"presence_penalty",
|
|
13131
|
+
"stop",
|
|
13132
|
+
"response_format",
|
|
13133
|
+
"tools",
|
|
13134
|
+
"tool_choice",
|
|
13135
|
+
"parallel_tool_calls",
|
|
13136
|
+
"max_tool_calls"
|
|
13137
|
+
];
|
|
13138
|
+
function batchMetadata(params) {
|
|
13139
|
+
const metadata = { provider: "openai" };
|
|
13140
|
+
for (const key of OPENAI_METADATA_KEYS) {
|
|
13141
|
+
try {
|
|
13142
|
+
const value = Reflect.get(params, key);
|
|
13143
|
+
if (value !== void 0) {
|
|
13144
|
+
metadata[key] = value;
|
|
13145
|
+
}
|
|
13146
|
+
} catch {
|
|
13147
|
+
}
|
|
13148
|
+
}
|
|
13149
|
+
return metadata;
|
|
12992
13150
|
}
|
|
12993
|
-
function
|
|
12994
|
-
const
|
|
12995
|
-
return
|
|
12996
|
-
|
|
12997
|
-
|
|
12998
|
-
|
|
12999
|
-
|
|
13000
|
-
|
|
13001
|
-
|
|
13002
|
-
|
|
13003
|
-
|
|
13004
|
-
|
|
13005
|
-
|
|
13006
|
-
|
|
13007
|
-
|
|
13008
|
-
|
|
13009
|
-
|
|
13010
|
-
|
|
13011
|
-
|
|
13012
|
-
|
|
13013
|
-
|
|
13014
|
-
|
|
13015
|
-
|
|
13016
|
-
|
|
13017
|
-
|
|
13018
|
-
|
|
13019
|
-
|
|
13020
|
-
|
|
13021
|
-
|
|
13022
|
-
|
|
13023
|
-
|
|
13024
|
-
|
|
13025
|
-
|
|
13151
|
+
function extractOpenAIBatchInput(endpoint, params) {
|
|
13152
|
+
const input = endpoint === "/v1/chat/completions" ? params.messages : params.input;
|
|
13153
|
+
return {
|
|
13154
|
+
input: processInputAttachments(input),
|
|
13155
|
+
metadata: batchMetadata(params)
|
|
13156
|
+
};
|
|
13157
|
+
}
|
|
13158
|
+
function extractOpenAIChatInput(params) {
|
|
13159
|
+
const { messages, ...metadata } = params;
|
|
13160
|
+
return {
|
|
13161
|
+
input: processInputAttachments(messages),
|
|
13162
|
+
metadata: { ...metadata, provider: "openai" }
|
|
13163
|
+
};
|
|
13164
|
+
}
|
|
13165
|
+
function extractOpenAIResponsesInput(params) {
|
|
13166
|
+
const { input, ...metadata } = params;
|
|
13167
|
+
return {
|
|
13168
|
+
input: processInputAttachments(input),
|
|
13169
|
+
metadata: { ...metadata, provider: "openai" }
|
|
13170
|
+
};
|
|
13171
|
+
}
|
|
13172
|
+
function extractOpenAIResponsesMetadata(result) {
|
|
13173
|
+
if (!result) {
|
|
13174
|
+
return void 0;
|
|
13175
|
+
}
|
|
13176
|
+
const { output: _output, usage: _usage, ...metadata } = result;
|
|
13177
|
+
return Object.keys(metadata).length > 0 ? metadata : void 0;
|
|
13178
|
+
}
|
|
13179
|
+
function processImagesInOutput(output) {
|
|
13180
|
+
if (Array.isArray(output)) {
|
|
13181
|
+
return output.map(processImagesInOutput);
|
|
13182
|
+
}
|
|
13183
|
+
if (isObject(output) && output.type === "image_generation_call" && typeof output.result === "string" && output.result) {
|
|
13184
|
+
const fileExtension = output.output_format || "png";
|
|
13185
|
+
const contentType = `image/${fileExtension}`;
|
|
13186
|
+
const baseFilename = typeof output.revised_prompt === "string" ? output.revised_prompt.slice(0, 50).replace(/[^a-zA-Z0-9]/g, "_") : "generated_image";
|
|
13187
|
+
let binaryString;
|
|
13188
|
+
try {
|
|
13189
|
+
binaryString = atob(output.result);
|
|
13190
|
+
} catch {
|
|
13191
|
+
return output;
|
|
13192
|
+
}
|
|
13193
|
+
const bytes = new Uint8Array(binaryString.length);
|
|
13194
|
+
for (let i = 0; i < binaryString.length; i++) {
|
|
13195
|
+
bytes[i] = binaryString.charCodeAt(i);
|
|
13196
|
+
}
|
|
13197
|
+
return {
|
|
13198
|
+
...output,
|
|
13199
|
+
result: new Attachment({
|
|
13200
|
+
data: new Blob([bytes], { type: contentType }),
|
|
13201
|
+
filename: `${baseFilename}.${fileExtension}`,
|
|
13202
|
+
contentType
|
|
13203
|
+
})
|
|
13204
|
+
};
|
|
13205
|
+
}
|
|
13206
|
+
return output;
|
|
13207
|
+
}
|
|
13208
|
+
|
|
13209
|
+
// src/openai-utils.ts
|
|
13210
|
+
var BRAINTRUST_CACHED_STREAM_METRIC = "__braintrust_cached_metric";
|
|
13211
|
+
var LEGACY_CACHED_HEADER = "x-cached";
|
|
13212
|
+
var X_CACHED_HEADER = "x-bt-cached";
|
|
13213
|
+
var TOKEN_NAME_MAP = {
|
|
13214
|
+
input_tokens: "prompt_tokens",
|
|
13215
|
+
output_tokens: "completion_tokens",
|
|
13216
|
+
total_tokens: "tokens"
|
|
13217
|
+
};
|
|
13218
|
+
var TOKEN_PREFIX_MAP = {
|
|
13219
|
+
input: "prompt",
|
|
13220
|
+
output: "completion"
|
|
13221
|
+
};
|
|
13222
|
+
function parseMetricsFromUsage(usage) {
|
|
13223
|
+
if (!usage) {
|
|
13224
|
+
return {};
|
|
13225
|
+
}
|
|
13226
|
+
const metrics = {};
|
|
13227
|
+
for (const [oaiName, value] of Object.entries(usage)) {
|
|
13228
|
+
if (typeof value === "number") {
|
|
13229
|
+
const metricName = TOKEN_NAME_MAP[oaiName] || oaiName;
|
|
13230
|
+
metrics[metricName] = value;
|
|
13231
|
+
continue;
|
|
13232
|
+
}
|
|
13233
|
+
if (!oaiName.endsWith("_tokens_details") || !isObject(value)) {
|
|
13234
|
+
continue;
|
|
13235
|
+
}
|
|
13236
|
+
const rawPrefix = oaiName.slice(0, -"_tokens_details".length);
|
|
13237
|
+
const prefix = TOKEN_PREFIX_MAP[rawPrefix] || rawPrefix;
|
|
13238
|
+
for (const [key, nestedValue] of Object.entries(value)) {
|
|
13239
|
+
if (typeof nestedValue !== "number") {
|
|
13240
|
+
continue;
|
|
13026
13241
|
}
|
|
13027
|
-
|
|
13028
|
-
|
|
13029
|
-
|
|
13030
|
-
|
|
13031
|
-
|
|
13032
|
-
|
|
13033
|
-
|
|
13034
|
-
|
|
13035
|
-
|
|
13036
|
-
|
|
13037
|
-
|
|
13242
|
+
metrics[`${prefix}_${key}`] = nestedValue;
|
|
13243
|
+
}
|
|
13244
|
+
}
|
|
13245
|
+
return metrics;
|
|
13246
|
+
}
|
|
13247
|
+
function parseCachedHeader(value) {
|
|
13248
|
+
if (!value) {
|
|
13249
|
+
return void 0;
|
|
13250
|
+
}
|
|
13251
|
+
return ["true", "hit"].includes(value.toLowerCase()) ? 1 : 0;
|
|
13252
|
+
}
|
|
13253
|
+
function getCachedMetricFromHeaders(headers) {
|
|
13254
|
+
if (!headers || typeof headers.get !== "function") {
|
|
13255
|
+
return void 0;
|
|
13256
|
+
}
|
|
13257
|
+
const cachedHeader = headers.get(X_CACHED_HEADER);
|
|
13258
|
+
if (cachedHeader) {
|
|
13259
|
+
return parseCachedHeader(cachedHeader);
|
|
13260
|
+
}
|
|
13261
|
+
return parseCachedHeader(headers.get(LEGACY_CACHED_HEADER));
|
|
13262
|
+
}
|
|
13263
|
+
|
|
13264
|
+
// src/instrumentation/plugins/openai-batch-instrumentation.ts
|
|
13265
|
+
var SUPPORTED_ENDPOINTS = /* @__PURE__ */ new Set(["/v1/chat/completions", "/v1/responses"]);
|
|
13266
|
+
var TERMINAL_STATUSES = /* @__PURE__ */ new Set([
|
|
13267
|
+
"completed",
|
|
13268
|
+
"failed",
|
|
13269
|
+
"expired",
|
|
13270
|
+
"cancelled"
|
|
13271
|
+
]);
|
|
13272
|
+
var pendingBatchTraces = /* @__PURE__ */ new Map();
|
|
13273
|
+
function read(value, key) {
|
|
13274
|
+
if (!isObject(value)) {
|
|
13275
|
+
return void 0;
|
|
13276
|
+
}
|
|
13277
|
+
try {
|
|
13278
|
+
return Reflect.get(value, key);
|
|
13279
|
+
} catch {
|
|
13280
|
+
return void 0;
|
|
13281
|
+
}
|
|
13282
|
+
}
|
|
13283
|
+
function isBatchRecordIterable(value) {
|
|
13284
|
+
if (value === null || typeof value !== "object" && typeof value !== "function") {
|
|
13285
|
+
return false;
|
|
13286
|
+
}
|
|
13287
|
+
return typeof read(value, Symbol.iterator) === "function" || typeof read(value, Symbol.asyncIterator) === "function";
|
|
13288
|
+
}
|
|
13289
|
+
function validCustomId(value) {
|
|
13290
|
+
return typeof value === "string" && value.length > 0 && value.length <= 64;
|
|
13291
|
+
}
|
|
13292
|
+
function logBatchInstrumentationError(context, error) {
|
|
13293
|
+
debugLogger.debug(`OpenAI Batch instrumentation ${context}:`, error);
|
|
13294
|
+
}
|
|
13295
|
+
async function exportParent(parent) {
|
|
13296
|
+
if ("toStr" in parent && typeof parent.toStr === "function") {
|
|
13297
|
+
return parent.toStr();
|
|
13298
|
+
}
|
|
13299
|
+
if ("export" in parent && typeof parent.export === "function") {
|
|
13300
|
+
return await parent.export();
|
|
13301
|
+
}
|
|
13302
|
+
return void 0;
|
|
13303
|
+
}
|
|
13304
|
+
async function deterministicDigest(namespace, ...parts) {
|
|
13305
|
+
const encoded = new TextEncoder().encode(
|
|
13306
|
+
[namespace, ...parts].map((part) => `${part.length}:${part}`).join("\0")
|
|
13307
|
+
);
|
|
13308
|
+
return new Uint8Array(
|
|
13309
|
+
await globalThis.crypto.subtle.digest("SHA-256", encoded)
|
|
13310
|
+
);
|
|
13311
|
+
}
|
|
13312
|
+
function digestHex(bytes, length) {
|
|
13313
|
+
return Array.from(bytes.slice(0, length)).map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
13314
|
+
}
|
|
13315
|
+
function digestUuid(bytes) {
|
|
13316
|
+
const hex = digestHex(bytes, 16);
|
|
13317
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
13318
|
+
}
|
|
13319
|
+
async function batchSpanIds(inputFileId) {
|
|
13320
|
+
const [row, span, root] = await Promise.all([
|
|
13321
|
+
deterministicDigest("openai:batch:row", inputFileId),
|
|
13322
|
+
deterministicDigest("openai:batch:span", inputFileId),
|
|
13323
|
+
deterministicDigest("openai:batch:root", inputFileId)
|
|
13324
|
+
]);
|
|
13325
|
+
return {
|
|
13326
|
+
rowId: digestUuid(row),
|
|
13327
|
+
spanId: digestHex(span, 8),
|
|
13328
|
+
rootSpanId: digestHex(root, 16)
|
|
13329
|
+
};
|
|
13330
|
+
}
|
|
13331
|
+
async function childSpanIds(inputFileId, customId) {
|
|
13332
|
+
const [row, span] = await Promise.all([
|
|
13333
|
+
deterministicDigest("openai:batch:child:row", inputFileId, customId),
|
|
13334
|
+
deterministicDigest("openai:batch:child:span", inputFileId, customId)
|
|
13335
|
+
]);
|
|
13336
|
+
return { rowId: digestUuid(row), spanId: digestHex(span, 8) };
|
|
13337
|
+
}
|
|
13338
|
+
async function startBatchSpan(context) {
|
|
13339
|
+
const ids = await batchSpanIds(context.inputFileId);
|
|
13340
|
+
const parent = SpanComponentsV4.fromStr(context.parent);
|
|
13341
|
+
const hasParentSpan = Boolean(
|
|
13342
|
+
parent.data.row_id && parent.data.span_id && parent.data.root_span_id
|
|
13343
|
+
);
|
|
13344
|
+
return withCurrent(
|
|
13345
|
+
NOOP_SPAN,
|
|
13346
|
+
() => _internalStartSpanWithInitialMergeAndParentSpanIds(
|
|
13347
|
+
withSpanInstrumentationName(
|
|
13038
13348
|
{
|
|
13039
|
-
|
|
13040
|
-
|
|
13041
|
-
|
|
13042
|
-
|
|
13043
|
-
|
|
13044
|
-
|
|
13045
|
-
|
|
13046
|
-
|
|
13047
|
-
|
|
13048
|
-
|
|
13049
|
-
|
|
13050
|
-
|
|
13051
|
-
|
|
13052
|
-
|
|
13053
|
-
|
|
13054
|
-
|
|
13349
|
+
name: "openai.batch",
|
|
13350
|
+
type: "task" /* TASK */,
|
|
13351
|
+
parent: context.parent,
|
|
13352
|
+
...!hasParentSpan ? {
|
|
13353
|
+
parentSpanIds: {
|
|
13354
|
+
parentSpanIds: [],
|
|
13355
|
+
rootSpanId: ids.rootSpanId
|
|
13356
|
+
}
|
|
13357
|
+
} : {},
|
|
13358
|
+
spanId: ids.spanId,
|
|
13359
|
+
startTime: context.taskStartTime,
|
|
13360
|
+
event: {
|
|
13361
|
+
id: ids.rowId,
|
|
13362
|
+
metadata: {
|
|
13363
|
+
endpoint: context.endpoint,
|
|
13364
|
+
input_file_id: context.inputFileId,
|
|
13365
|
+
provider: "openai"
|
|
13366
|
+
}
|
|
13367
|
+
}
|
|
13368
|
+
},
|
|
13369
|
+
INSTRUMENTATION_NAMES.OPENAI
|
|
13370
|
+
)
|
|
13371
|
+
)
|
|
13055
13372
|
);
|
|
13056
13373
|
}
|
|
13057
|
-
|
|
13058
|
-
|
|
13059
|
-
|
|
13060
|
-
|
|
13061
|
-
|
|
13062
|
-
|
|
13063
|
-
|
|
13064
|
-
|
|
13065
|
-
|
|
13066
|
-
|
|
13067
|
-
|
|
13068
|
-
|
|
13069
|
-
|
|
13070
|
-
|
|
13071
|
-
|
|
13072
|
-
|
|
13073
|
-
|
|
13074
|
-
|
|
13075
|
-
|
|
13076
|
-
|
|
13077
|
-
|
|
13078
|
-
|
|
13079
|
-
|
|
13080
|
-
|
|
13081
|
-
|
|
13082
|
-
|
|
13083
|
-
|
|
13084
|
-
|
|
13085
|
-
|
|
13086
|
-
|
|
13087
|
-
|
|
13088
|
-
|
|
13089
|
-
|
|
13090
|
-
|
|
13091
|
-
|
|
13092
|
-
|
|
13093
|
-
|
|
13094
|
-
|
|
13095
|
-
|
|
13096
|
-
|
|
13097
|
-
|
|
13098
|
-
|
|
13099
|
-
|
|
13100
|
-
|
|
13101
|
-
|
|
13102
|
-
|
|
13103
|
-
|
|
13104
|
-
|
|
13105
|
-
|
|
13106
|
-
|
|
13107
|
-
|
|
13108
|
-
|
|
13109
|
-
|
|
13110
|
-
|
|
13111
|
-
|
|
13112
|
-
|
|
13113
|
-
|
|
13374
|
+
async function startBatchChild(context, taskParent, input) {
|
|
13375
|
+
const ids = await childSpanIds(context.inputFileId, input.customId);
|
|
13376
|
+
return withCurrent(
|
|
13377
|
+
NOOP_SPAN,
|
|
13378
|
+
() => _internalStartSpanWithInitialMerge(
|
|
13379
|
+
withSpanInstrumentationName(
|
|
13380
|
+
{
|
|
13381
|
+
name: context.endpoint === "/v1/chat/completions" ? "Chat Completion" : "openai.responses.create",
|
|
13382
|
+
type: "llm" /* LLM */,
|
|
13383
|
+
parent: taskParent,
|
|
13384
|
+
spanId: ids.spanId,
|
|
13385
|
+
startTime: context.childStartTime,
|
|
13386
|
+
event: {
|
|
13387
|
+
id: ids.rowId,
|
|
13388
|
+
...input.spanData?.input !== void 0 ? { input: input.spanData.input } : {},
|
|
13389
|
+
metadata: {
|
|
13390
|
+
...input.spanData?.metadata,
|
|
13391
|
+
custom_id: input.customId,
|
|
13392
|
+
provider: "openai"
|
|
13393
|
+
}
|
|
13394
|
+
}
|
|
13395
|
+
},
|
|
13396
|
+
INSTRUMENTATION_NAMES.OPENAI
|
|
13397
|
+
)
|
|
13398
|
+
)
|
|
13399
|
+
);
|
|
13400
|
+
}
|
|
13401
|
+
async function* jsonlRecords(file, onIssue = () => {
|
|
13402
|
+
}) {
|
|
13403
|
+
const resolvedFile = await file;
|
|
13404
|
+
if (typeof resolvedFile === "string") {
|
|
13405
|
+
for (const line of resolvedFile.split("\n")) {
|
|
13406
|
+
if (!line.trim()) {
|
|
13407
|
+
continue;
|
|
13408
|
+
}
|
|
13409
|
+
try {
|
|
13410
|
+
yield JSON.parse(line.replace(/\r$/, ""));
|
|
13411
|
+
} catch (error) {
|
|
13412
|
+
logBatchInstrumentationError("skipped malformed JSONL", error);
|
|
13413
|
+
onIssue(new Error("OpenAI Batch file contains malformed JSONL"));
|
|
13414
|
+
}
|
|
13415
|
+
}
|
|
13416
|
+
return;
|
|
13417
|
+
}
|
|
13418
|
+
const body = read(resolvedFile, "body");
|
|
13419
|
+
const getReader = read(body, "getReader");
|
|
13420
|
+
if (isObject(body) && typeof getReader === "function") {
|
|
13421
|
+
let reader;
|
|
13422
|
+
try {
|
|
13423
|
+
reader = Reflect.apply(getReader, body, []);
|
|
13424
|
+
const decoder = new TextDecoder();
|
|
13425
|
+
let pending = "";
|
|
13426
|
+
while (true) {
|
|
13427
|
+
const readChunk = read(reader, "read");
|
|
13428
|
+
if (typeof readChunk !== "function") {
|
|
13429
|
+
throw new Error("Response body stream has no read method");
|
|
13430
|
+
}
|
|
13431
|
+
const chunk = await Reflect.apply(readChunk, reader, []);
|
|
13432
|
+
if (!isObject(chunk)) {
|
|
13433
|
+
throw new Error("Response body stream returned an invalid chunk");
|
|
13434
|
+
}
|
|
13435
|
+
if (chunk.done === true) {
|
|
13436
|
+
pending += decoder.decode();
|
|
13437
|
+
break;
|
|
13438
|
+
}
|
|
13439
|
+
if (!(chunk.value instanceof Uint8Array)) {
|
|
13440
|
+
throw new Error("Response body stream returned a non-byte chunk");
|
|
13441
|
+
}
|
|
13442
|
+
pending += decoder.decode(chunk.value, { stream: true });
|
|
13443
|
+
let newline = pending.indexOf("\n");
|
|
13444
|
+
while (newline !== -1) {
|
|
13445
|
+
const line = pending.slice(0, newline).replace(/\r$/, "");
|
|
13446
|
+
pending = pending.slice(newline + 1);
|
|
13447
|
+
if (line.trim()) {
|
|
13448
|
+
try {
|
|
13449
|
+
yield JSON.parse(line);
|
|
13450
|
+
} catch (error) {
|
|
13451
|
+
logBatchInstrumentationError("skipped malformed JSONL", error);
|
|
13452
|
+
onIssue(new Error("OpenAI Batch file contains malformed JSONL"));
|
|
13453
|
+
}
|
|
13454
|
+
}
|
|
13455
|
+
newline = pending.indexOf("\n");
|
|
13456
|
+
}
|
|
13457
|
+
}
|
|
13458
|
+
if (pending.trim()) {
|
|
13459
|
+
try {
|
|
13460
|
+
yield JSON.parse(pending.replace(/\r$/, ""));
|
|
13461
|
+
} catch (error) {
|
|
13462
|
+
logBatchInstrumentationError("skipped malformed JSONL", error);
|
|
13463
|
+
onIssue(new Error("OpenAI Batch file contains malformed JSONL"));
|
|
13464
|
+
}
|
|
13465
|
+
}
|
|
13466
|
+
} catch (error) {
|
|
13467
|
+
logBatchInstrumentationError("could not read JSONL response body", error);
|
|
13468
|
+
onIssue(new Error("OpenAI Batch response body could not be read"));
|
|
13469
|
+
} finally {
|
|
13470
|
+
const releaseLock = read(reader, "releaseLock");
|
|
13471
|
+
if (typeof releaseLock === "function") {
|
|
13472
|
+
try {
|
|
13473
|
+
Reflect.apply(releaseLock, reader, []);
|
|
13474
|
+
} catch (error) {
|
|
13475
|
+
logBatchInstrumentationError(
|
|
13476
|
+
"could not release stream reader",
|
|
13477
|
+
error
|
|
13478
|
+
);
|
|
13479
|
+
}
|
|
13480
|
+
}
|
|
13481
|
+
}
|
|
13482
|
+
return;
|
|
13483
|
+
}
|
|
13484
|
+
if (isBatchRecordIterable(resolvedFile)) {
|
|
13485
|
+
for await (const record of resolvedFile) {
|
|
13486
|
+
yield record;
|
|
13487
|
+
}
|
|
13488
|
+
return;
|
|
13489
|
+
}
|
|
13490
|
+
logBatchInstrumentationError("skipped invalid JSONL source", resolvedFile);
|
|
13491
|
+
onIssue(new Error("OpenAI Batch file source is invalid"));
|
|
13492
|
+
}
|
|
13493
|
+
async function readBatchInputs(file) {
|
|
13494
|
+
const inputs = /* @__PURE__ */ new Map();
|
|
13495
|
+
const issues = [];
|
|
13496
|
+
let endpoint;
|
|
13497
|
+
try {
|
|
13498
|
+
for await (const value of jsonlRecords(
|
|
13499
|
+
file,
|
|
13500
|
+
(issue) => issues.push(issue)
|
|
13501
|
+
)) {
|
|
13502
|
+
const customId = read(value, "custom_id");
|
|
13503
|
+
const url = read(value, "url");
|
|
13504
|
+
const body = read(value, "body");
|
|
13505
|
+
if (!validCustomId(customId) || inputs.has(customId) || read(value, "method") !== "POST" || typeof url !== "string" || !SUPPORTED_ENDPOINTS.has(url) || endpoint !== void 0 && endpoint !== url || !isObject(body)) {
|
|
13506
|
+
issues.push(new Error("OpenAI Batch input contains an invalid record"));
|
|
13507
|
+
continue;
|
|
13508
|
+
}
|
|
13509
|
+
endpoint = url;
|
|
13510
|
+
let spanData;
|
|
13511
|
+
try {
|
|
13512
|
+
spanData = extractOpenAIBatchInput(url, body);
|
|
13513
|
+
} catch (error) {
|
|
13514
|
+
logBatchInstrumentationError("could not extract batch input", error);
|
|
13515
|
+
}
|
|
13516
|
+
inputs.set(customId, { customId, spanData });
|
|
13517
|
+
}
|
|
13518
|
+
} catch (error) {
|
|
13519
|
+
logBatchInstrumentationError("could not process input file", error);
|
|
13520
|
+
issues.push(new Error("OpenAI Batch input file could not be processed"));
|
|
13521
|
+
}
|
|
13522
|
+
if (!endpoint || inputs.size === 0) {
|
|
13523
|
+
issues.push(new Error("OpenAI Batch input contains no supported records"));
|
|
13524
|
+
}
|
|
13525
|
+
return { endpoint, inputs, issues };
|
|
13526
|
+
}
|
|
13527
|
+
async function writePendingSpans(trace, endTime) {
|
|
13528
|
+
const task = await startBatchSpan(trace.context);
|
|
13529
|
+
const taskParent = await task.export();
|
|
13530
|
+
for (const input of trace.inputs.values()) {
|
|
13531
|
+
try {
|
|
13532
|
+
const child = await startBatchChild(trace.context, taskParent, input);
|
|
13533
|
+
if (endTime !== void 0) {
|
|
13534
|
+
child.end({ endTime });
|
|
13535
|
+
}
|
|
13536
|
+
} catch (error) {
|
|
13537
|
+
logBatchInstrumentationError("could not write batch request span", error);
|
|
13538
|
+
}
|
|
13539
|
+
}
|
|
13540
|
+
if (endTime !== void 0) {
|
|
13541
|
+
if (trace.status && trace.status !== "completed") {
|
|
13542
|
+
task.log({ error: new Error(`OpenAI Batch ${trace.status}`) });
|
|
13543
|
+
}
|
|
13544
|
+
task.end({ endTime });
|
|
13545
|
+
}
|
|
13546
|
+
}
|
|
13547
|
+
var interceptOpenAIFilesCreateTraced = async (target, thisArg, args) => {
|
|
13548
|
+
const startTime = getCurrentUnixTimestamp();
|
|
13549
|
+
const inputPromise = readBatchInputs(args[0].inputFileContent);
|
|
13550
|
+
const parentPromise = exportParent(args[0].parent);
|
|
13551
|
+
const file = await Reflect.apply(target, thisArg, args);
|
|
13552
|
+
try {
|
|
13553
|
+
const inputFileId = read(file, "id");
|
|
13554
|
+
const [inputData, exportedParent2] = await Promise.all([
|
|
13555
|
+
inputPromise,
|
|
13556
|
+
parentPromise
|
|
13557
|
+
]);
|
|
13558
|
+
if (typeof inputFileId !== "string" || !exportedParent2 || !inputData.endpoint || inputData.issues.length > 0) {
|
|
13559
|
+
if (inputData.issues[0]) {
|
|
13560
|
+
logBatchInstrumentationError(
|
|
13561
|
+
"skipped invalid input file",
|
|
13562
|
+
inputData.issues[0]
|
|
13563
|
+
);
|
|
13564
|
+
}
|
|
13565
|
+
return file;
|
|
13566
|
+
}
|
|
13567
|
+
const trace = {
|
|
13568
|
+
context: {
|
|
13569
|
+
inputFileId,
|
|
13570
|
+
endpoint: inputData.endpoint,
|
|
13571
|
+
parent: exportedParent2,
|
|
13572
|
+
taskStartTime: startTime,
|
|
13573
|
+
childStartTime: startTime
|
|
13574
|
+
},
|
|
13575
|
+
inputs: inputData.inputs
|
|
13576
|
+
};
|
|
13577
|
+
pendingBatchTraces.set(inputFileId, trace);
|
|
13578
|
+
await writePendingSpans(trace);
|
|
13579
|
+
} catch (error) {
|
|
13580
|
+
logBatchInstrumentationError("could not start batch spans", error);
|
|
13581
|
+
}
|
|
13582
|
+
return file;
|
|
13114
13583
|
};
|
|
13115
|
-
function
|
|
13116
|
-
|
|
13117
|
-
|
|
13584
|
+
function validTimestamp(value) {
|
|
13585
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0;
|
|
13586
|
+
}
|
|
13587
|
+
function terminalEndTime(batch, startTime) {
|
|
13588
|
+
let timestamp;
|
|
13589
|
+
switch (batch.status) {
|
|
13590
|
+
case "completed":
|
|
13591
|
+
timestamp = batch.completed_at;
|
|
13592
|
+
break;
|
|
13593
|
+
case "failed":
|
|
13594
|
+
timestamp = batch.failed_at;
|
|
13595
|
+
break;
|
|
13596
|
+
case "expired":
|
|
13597
|
+
timestamp = batch.expired_at;
|
|
13598
|
+
break;
|
|
13599
|
+
default:
|
|
13600
|
+
timestamp = batch.cancelled_at;
|
|
13118
13601
|
}
|
|
13119
|
-
|
|
13120
|
-
|
|
13121
|
-
|
|
13122
|
-
|
|
13123
|
-
|
|
13602
|
+
return validTimestamp(timestamp) ? Math.max(timestamp, startTime) : Math.max(getCurrentUnixTimestamp(), startTime);
|
|
13603
|
+
}
|
|
13604
|
+
async function updateBatchTimestamps(batch) {
|
|
13605
|
+
const trace = pendingBatchTraces.get(batch.input_file_id);
|
|
13606
|
+
if (!trace || trace.context.endpoint !== batch.endpoint) {
|
|
13607
|
+
return;
|
|
13608
|
+
}
|
|
13609
|
+
if (validTimestamp(batch.created_at)) {
|
|
13610
|
+
trace.context.taskStartTime = batch.created_at;
|
|
13611
|
+
}
|
|
13612
|
+
trace.context.childStartTime = validTimestamp(batch.in_progress_at) ? Math.max(batch.in_progress_at, trace.context.taskStartTime) : trace.context.taskStartTime;
|
|
13613
|
+
trace.status = batch.status;
|
|
13614
|
+
if (TERMINAL_STATUSES.has(batch.status)) {
|
|
13615
|
+
trace.endTime = terminalEndTime(batch, trace.context.childStartTime);
|
|
13616
|
+
}
|
|
13617
|
+
await writePendingSpans(trace, trace.endTime);
|
|
13618
|
+
}
|
|
13619
|
+
var interceptOpenAIBatchesRetrieveTraced = (target, thisArg, args) => Promise.resolve(Reflect.apply(target, thisArg, args)).then(async (batch) => {
|
|
13620
|
+
try {
|
|
13621
|
+
await updateBatchTimestamps(batch);
|
|
13622
|
+
} catch (error) {
|
|
13623
|
+
logBatchInstrumentationError("could not update batch timestamps", error);
|
|
13624
|
+
}
|
|
13625
|
+
return batch;
|
|
13626
|
+
});
|
|
13627
|
+
function errorFromResult(result) {
|
|
13628
|
+
const error = read(result.value, "error");
|
|
13629
|
+
if (isObject(error)) {
|
|
13630
|
+
const message = read(error, "message");
|
|
13631
|
+
return new Error(
|
|
13632
|
+
typeof message === "string" ? message : "OpenAI Batch request failed"
|
|
13633
|
+
);
|
|
13634
|
+
}
|
|
13635
|
+
const response = read(result.value, "response");
|
|
13636
|
+
const statusCode = read(response, "status_code");
|
|
13637
|
+
if (result.source === "error" || typeof statusCode === "number" && (statusCode < 200 || statusCode >= 300)) {
|
|
13638
|
+
const message = read(read(read(response, "body"), "error"), "message");
|
|
13639
|
+
return new Error(
|
|
13640
|
+
typeof message === "string" ? message : "OpenAI Batch request failed"
|
|
13641
|
+
);
|
|
13642
|
+
}
|
|
13643
|
+
return void 0;
|
|
13644
|
+
}
|
|
13645
|
+
async function completeBatchResult(context, taskParent, endTime, input, result) {
|
|
13646
|
+
const child = await startBatchChild(context, taskParent, input);
|
|
13647
|
+
try {
|
|
13648
|
+
const resultError = errorFromResult(result);
|
|
13649
|
+
const responseBody = read(read(result.value, "response"), "body");
|
|
13650
|
+
if (resultError) {
|
|
13651
|
+
child.log({ error: resultError });
|
|
13652
|
+
} else if (isObject(responseBody)) {
|
|
13653
|
+
const model = read(responseBody, "model");
|
|
13654
|
+
child.log({
|
|
13655
|
+
output: context.endpoint === "/v1/chat/completions" ? read(responseBody, "choices") : processImagesInOutput(read(responseBody, "output")),
|
|
13656
|
+
...typeof model === "string" ? { metadata: { model } } : {},
|
|
13657
|
+
metrics: parseMetricsFromUsage(read(responseBody, "usage"))
|
|
13658
|
+
});
|
|
13659
|
+
} else {
|
|
13660
|
+
child.log({ error: new Error("OpenAI Batch response body is missing") });
|
|
13661
|
+
}
|
|
13662
|
+
} catch (error) {
|
|
13663
|
+
child.log({ error });
|
|
13664
|
+
} finally {
|
|
13665
|
+
child.end({ endTime });
|
|
13666
|
+
}
|
|
13667
|
+
}
|
|
13668
|
+
async function completeResultFile({
|
|
13669
|
+
context,
|
|
13670
|
+
endTime,
|
|
13671
|
+
file,
|
|
13672
|
+
inputs,
|
|
13673
|
+
issues,
|
|
13674
|
+
seen,
|
|
13675
|
+
source,
|
|
13676
|
+
taskParent
|
|
13677
|
+
}) {
|
|
13678
|
+
if (file === void 0) {
|
|
13679
|
+
return;
|
|
13680
|
+
}
|
|
13681
|
+
for await (const value of jsonlRecords(file, (issue) => issues.push(issue))) {
|
|
13682
|
+
const customId = read(value, "custom_id");
|
|
13683
|
+
if (!validCustomId(customId) || !isObject(value)) {
|
|
13684
|
+
issues.push(
|
|
13685
|
+
new Error("OpenAI Batch result is missing a valid custom_id")
|
|
13686
|
+
);
|
|
13124
13687
|
continue;
|
|
13125
13688
|
}
|
|
13126
|
-
if (
|
|
13689
|
+
if (seen.has(customId)) {
|
|
13690
|
+
issues.push(new Error("OpenAI Batch result contains a duplicate"));
|
|
13127
13691
|
continue;
|
|
13128
13692
|
}
|
|
13129
|
-
const
|
|
13130
|
-
|
|
13131
|
-
|
|
13132
|
-
|
|
13133
|
-
|
|
13134
|
-
|
|
13135
|
-
metrics[`${prefix}_${key}`] = nestedValue;
|
|
13693
|
+
const input = inputs.get(customId);
|
|
13694
|
+
if (!input) {
|
|
13695
|
+
issues.push(
|
|
13696
|
+
new Error("OpenAI Batch result does not match an input record")
|
|
13697
|
+
);
|
|
13698
|
+
continue;
|
|
13136
13699
|
}
|
|
13700
|
+
seen.add(customId);
|
|
13701
|
+
await completeBatchResult(context, taskParent, endTime, input, {
|
|
13702
|
+
value,
|
|
13703
|
+
source
|
|
13704
|
+
});
|
|
13137
13705
|
}
|
|
13138
|
-
return metrics;
|
|
13139
13706
|
}
|
|
13140
|
-
function
|
|
13141
|
-
|
|
13707
|
+
function sameInputs(first, second) {
|
|
13708
|
+
return first.size === second.size && [...first.keys()].every((customId) => second.has(customId));
|
|
13709
|
+
}
|
|
13710
|
+
async function contextForCompletion(inputFileId, inputData) {
|
|
13711
|
+
if (!inputData.endpoint || inputData.issues.length > 0) {
|
|
13142
13712
|
return void 0;
|
|
13143
13713
|
}
|
|
13144
|
-
|
|
13145
|
-
|
|
13146
|
-
|
|
13147
|
-
|
|
13714
|
+
const existing = pendingBatchTraces.get(inputFileId);
|
|
13715
|
+
if (existing) {
|
|
13716
|
+
if (existing.context.endpoint !== inputData.endpoint || !sameInputs(existing.inputs, inputData.inputs)) {
|
|
13717
|
+
return void 0;
|
|
13718
|
+
}
|
|
13719
|
+
return { ...existing, inputs: inputData.inputs };
|
|
13720
|
+
}
|
|
13721
|
+
const parent = await exportParent(getSpanParentObject());
|
|
13722
|
+
if (!parent) {
|
|
13148
13723
|
return void 0;
|
|
13149
13724
|
}
|
|
13150
|
-
const
|
|
13151
|
-
|
|
13152
|
-
|
|
13725
|
+
const startTime = getCurrentUnixTimestamp();
|
|
13726
|
+
return {
|
|
13727
|
+
context: {
|
|
13728
|
+
inputFileId,
|
|
13729
|
+
endpoint: inputData.endpoint,
|
|
13730
|
+
parent,
|
|
13731
|
+
taskStartTime: startTime,
|
|
13732
|
+
childStartTime: startTime
|
|
13733
|
+
},
|
|
13734
|
+
inputs: inputData.inputs
|
|
13735
|
+
};
|
|
13736
|
+
}
|
|
13737
|
+
async function completeBatch(args) {
|
|
13738
|
+
const inputData = await readBatchInputs(args.inputFileContent);
|
|
13739
|
+
const trace = await contextForCompletion(args.inputFileId, inputData);
|
|
13740
|
+
if (!trace) {
|
|
13741
|
+
logBatchInstrumentationError(
|
|
13742
|
+
"left batch spans pending",
|
|
13743
|
+
inputData.issues[0] ?? new Error("OpenAI Batch input does not match")
|
|
13744
|
+
);
|
|
13745
|
+
return;
|
|
13153
13746
|
}
|
|
13154
|
-
|
|
13747
|
+
const endTime = trace.endTime ?? getCurrentUnixTimestamp();
|
|
13748
|
+
const task = await startBatchSpan(trace.context);
|
|
13749
|
+
const taskParent = await task.export();
|
|
13750
|
+
const issues = [];
|
|
13751
|
+
const seen = /* @__PURE__ */ new Set();
|
|
13752
|
+
await Promise.all([
|
|
13753
|
+
completeResultFile({
|
|
13754
|
+
context: trace.context,
|
|
13755
|
+
endTime,
|
|
13756
|
+
file: args.outputFileContent,
|
|
13757
|
+
inputs: trace.inputs,
|
|
13758
|
+
issues,
|
|
13759
|
+
seen,
|
|
13760
|
+
source: "output",
|
|
13761
|
+
taskParent
|
|
13762
|
+
}),
|
|
13763
|
+
completeResultFile({
|
|
13764
|
+
context: trace.context,
|
|
13765
|
+
endTime,
|
|
13766
|
+
file: args.errorFileContent,
|
|
13767
|
+
inputs: trace.inputs,
|
|
13768
|
+
issues,
|
|
13769
|
+
seen,
|
|
13770
|
+
source: "error",
|
|
13771
|
+
taskParent
|
|
13772
|
+
})
|
|
13773
|
+
]);
|
|
13774
|
+
if (issues.length > 0) {
|
|
13775
|
+
logBatchInstrumentationError("left batch spans pending", issues[0]);
|
|
13776
|
+
return;
|
|
13777
|
+
}
|
|
13778
|
+
const missing = [...trace.inputs.values()].filter(
|
|
13779
|
+
({ customId }) => !seen.has(customId)
|
|
13780
|
+
);
|
|
13781
|
+
if (!trace.status || trace.status === "completed") {
|
|
13782
|
+
if (missing.length > 0) {
|
|
13783
|
+
logBatchInstrumentationError(
|
|
13784
|
+
"left batch spans pending",
|
|
13785
|
+
new Error("OpenAI Batch result files are incomplete")
|
|
13786
|
+
);
|
|
13787
|
+
return;
|
|
13788
|
+
}
|
|
13789
|
+
} else {
|
|
13790
|
+
for (const input of missing) {
|
|
13791
|
+
const child = await startBatchChild(trace.context, taskParent, input);
|
|
13792
|
+
child.log({ error: new Error(`OpenAI Batch ${trace.status}`) });
|
|
13793
|
+
child.end({ endTime });
|
|
13794
|
+
}
|
|
13795
|
+
task.log({ error: new Error(`OpenAI Batch ${trace.status}`) });
|
|
13796
|
+
}
|
|
13797
|
+
task.end({ endTime });
|
|
13798
|
+
pendingBatchTraces.delete(args.inputFileId);
|
|
13155
13799
|
}
|
|
13800
|
+
var interceptOpenAIBatchTraceComplete = (target, thisArg, args) => Promise.resolve(Reflect.apply(target, thisArg, args)).then(async (result) => {
|
|
13801
|
+
try {
|
|
13802
|
+
await completeBatch(args[0]);
|
|
13803
|
+
} catch (error) {
|
|
13804
|
+
logBatchInstrumentationError("could not complete batch", error);
|
|
13805
|
+
}
|
|
13806
|
+
return result;
|
|
13807
|
+
});
|
|
13156
13808
|
|
|
13157
13809
|
// src/instrumentation/plugins/openai-plugin.ts
|
|
13158
13810
|
var OpenAIPlugin = class extends BasePlugin {
|
|
@@ -13160,17 +13812,22 @@ var OpenAIPlugin = class extends BasePlugin {
|
|
|
13160
13812
|
super();
|
|
13161
13813
|
}
|
|
13162
13814
|
onEnable() {
|
|
13815
|
+
this.unsubscribers.push(
|
|
13816
|
+
openAIChannels.filesCreateTraced.intercept(
|
|
13817
|
+
interceptOpenAIFilesCreateTraced
|
|
13818
|
+
),
|
|
13819
|
+
openAIChannels.batchesRetrieveTraced.intercept(
|
|
13820
|
+
interceptOpenAIBatchesRetrieveTraced
|
|
13821
|
+
),
|
|
13822
|
+
openAIChannels.batchesCompleteTrace.intercept(
|
|
13823
|
+
interceptOpenAIBatchTraceComplete
|
|
13824
|
+
)
|
|
13825
|
+
);
|
|
13163
13826
|
this.unsubscribers.push(
|
|
13164
13827
|
traceStreamingChannel(openAIChannels.chatCompletionsCreate, {
|
|
13165
13828
|
name: "Chat Completion",
|
|
13166
13829
|
type: "llm" /* LLM */,
|
|
13167
|
-
extractInput: ([params]) =>
|
|
13168
|
-
const { messages, ...metadata } = params;
|
|
13169
|
-
return {
|
|
13170
|
-
input: processInputAttachments(messages),
|
|
13171
|
-
metadata: { ...metadata, provider: "openai" }
|
|
13172
|
-
};
|
|
13173
|
-
},
|
|
13830
|
+
extractInput: ([params]) => extractOpenAIChatInput(params),
|
|
13174
13831
|
extractOutput: (result) => {
|
|
13175
13832
|
return result?.choices;
|
|
13176
13833
|
},
|
|
@@ -13216,13 +13873,7 @@ var OpenAIPlugin = class extends BasePlugin {
|
|
|
13216
13873
|
traceStreamingChannel(openAIChannels.betaChatCompletionsParse, {
|
|
13217
13874
|
name: "Chat Completion",
|
|
13218
13875
|
type: "llm" /* LLM */,
|
|
13219
|
-
extractInput: ([params]) =>
|
|
13220
|
-
const { messages, ...metadata } = params;
|
|
13221
|
-
return {
|
|
13222
|
-
input: processInputAttachments(messages),
|
|
13223
|
-
metadata: { ...metadata, provider: "openai" }
|
|
13224
|
-
};
|
|
13225
|
-
},
|
|
13876
|
+
extractInput: ([params]) => extractOpenAIChatInput(params),
|
|
13226
13877
|
extractOutput: (result) => {
|
|
13227
13878
|
return result?.choices;
|
|
13228
13879
|
},
|
|
@@ -13244,13 +13895,7 @@ var OpenAIPlugin = class extends BasePlugin {
|
|
|
13244
13895
|
traceSyncStreamChannel(openAIChannels.betaChatCompletionsStream, {
|
|
13245
13896
|
name: "Chat Completion",
|
|
13246
13897
|
type: "llm" /* LLM */,
|
|
13247
|
-
extractInput: ([params]) =>
|
|
13248
|
-
const { messages, ...metadata } = params;
|
|
13249
|
-
return {
|
|
13250
|
-
input: processInputAttachments(messages),
|
|
13251
|
-
metadata: { ...metadata, provider: "openai" }
|
|
13252
|
-
};
|
|
13253
|
-
}
|
|
13898
|
+
extractInput: ([params]) => extractOpenAIChatInput(params)
|
|
13254
13899
|
})
|
|
13255
13900
|
);
|
|
13256
13901
|
this.unsubscribers.push(
|
|
@@ -13280,23 +13925,11 @@ var OpenAIPlugin = class extends BasePlugin {
|
|
|
13280
13925
|
traceStreamingChannel(openAIChannels.responsesCreate, {
|
|
13281
13926
|
name: "openai.responses.create",
|
|
13282
13927
|
type: "llm" /* LLM */,
|
|
13283
|
-
extractInput: ([params]) =>
|
|
13284
|
-
const { input, ...metadata } = params;
|
|
13285
|
-
return {
|
|
13286
|
-
input: processInputAttachments(input),
|
|
13287
|
-
metadata: { ...metadata, provider: "openai" }
|
|
13288
|
-
};
|
|
13289
|
-
},
|
|
13928
|
+
extractInput: ([params]) => extractOpenAIResponsesInput(params),
|
|
13290
13929
|
extractOutput: (result) => {
|
|
13291
13930
|
return processImagesInOutput(result?.output);
|
|
13292
13931
|
},
|
|
13293
|
-
extractMetadata: (result) =>
|
|
13294
|
-
if (!result) {
|
|
13295
|
-
return void 0;
|
|
13296
|
-
}
|
|
13297
|
-
const { output: _output, usage: _usage, ...metadata } = result;
|
|
13298
|
-
return Object.keys(metadata).length > 0 ? metadata : void 0;
|
|
13299
|
-
},
|
|
13932
|
+
extractMetadata: (result) => extractOpenAIResponsesMetadata(result),
|
|
13300
13933
|
extractMetrics: (result, startTime, endEvent) => {
|
|
13301
13934
|
const metrics = withCachedMetric(
|
|
13302
13935
|
parseMetricsFromUsage(result?.usage),
|
|
@@ -13315,13 +13948,7 @@ var OpenAIPlugin = class extends BasePlugin {
|
|
|
13315
13948
|
traceSyncStreamChannel(openAIChannels.responsesStream, {
|
|
13316
13949
|
name: "openai.responses.create",
|
|
13317
13950
|
type: "llm" /* LLM */,
|
|
13318
|
-
extractInput: ([params]) =>
|
|
13319
|
-
const { input, ...metadata } = params;
|
|
13320
|
-
return {
|
|
13321
|
-
input: processInputAttachments(input),
|
|
13322
|
-
metadata: { ...metadata, provider: "openai" }
|
|
13323
|
-
};
|
|
13324
|
-
},
|
|
13951
|
+
extractInput: ([params]) => extractOpenAIResponsesInput(params),
|
|
13325
13952
|
extractFromEvent: (event) => {
|
|
13326
13953
|
if (event.type !== "response.completed" || !event.response) {
|
|
13327
13954
|
return {};
|
|
@@ -13344,23 +13971,11 @@ var OpenAIPlugin = class extends BasePlugin {
|
|
|
13344
13971
|
traceStreamingChannel(openAIChannels.responsesParse, {
|
|
13345
13972
|
name: "openai.responses.parse",
|
|
13346
13973
|
type: "llm" /* LLM */,
|
|
13347
|
-
extractInput: ([params]) =>
|
|
13348
|
-
const { input, ...metadata } = params;
|
|
13349
|
-
return {
|
|
13350
|
-
input: processInputAttachments(input),
|
|
13351
|
-
metadata: { ...metadata, provider: "openai" }
|
|
13352
|
-
};
|
|
13353
|
-
},
|
|
13974
|
+
extractInput: ([params]) => extractOpenAIResponsesInput(params),
|
|
13354
13975
|
extractOutput: (result) => {
|
|
13355
13976
|
return processImagesInOutput(result?.output);
|
|
13356
13977
|
},
|
|
13357
|
-
extractMetadata: (result) =>
|
|
13358
|
-
if (!result) {
|
|
13359
|
-
return void 0;
|
|
13360
|
-
}
|
|
13361
|
-
const { output: _output, usage: _usage, ...metadata } = result;
|
|
13362
|
-
return Object.keys(metadata).length > 0 ? metadata : void 0;
|
|
13363
|
-
},
|
|
13978
|
+
extractMetadata: (result) => extractOpenAIResponsesMetadata(result),
|
|
13364
13979
|
extractMetrics: (result, startTime, endEvent) => {
|
|
13365
13980
|
const metrics = withCachedMetric(
|
|
13366
13981
|
parseMetricsFromUsage(result?.usage),
|
|
@@ -13379,23 +13994,11 @@ var OpenAIPlugin = class extends BasePlugin {
|
|
|
13379
13994
|
traceAsyncChannel(openAIChannels.responsesCompact, {
|
|
13380
13995
|
name: "openai.responses.compact",
|
|
13381
13996
|
type: "llm" /* LLM */,
|
|
13382
|
-
extractInput: ([params]) =>
|
|
13383
|
-
const { input, ...metadata } = params;
|
|
13384
|
-
return {
|
|
13385
|
-
input: processInputAttachments(input),
|
|
13386
|
-
metadata: { ...metadata, provider: "openai" }
|
|
13387
|
-
};
|
|
13388
|
-
},
|
|
13997
|
+
extractInput: ([params]) => extractOpenAIResponsesInput(params),
|
|
13389
13998
|
extractOutput: (result) => {
|
|
13390
13999
|
return processImagesInOutput(result?.output);
|
|
13391
14000
|
},
|
|
13392
|
-
extractMetadata: (result) =>
|
|
13393
|
-
if (!result) {
|
|
13394
|
-
return void 0;
|
|
13395
|
-
}
|
|
13396
|
-
const { output: _output, usage: _usage, ...metadata } = result;
|
|
13397
|
-
return Object.keys(metadata).length > 0 ? metadata : void 0;
|
|
13398
|
-
},
|
|
14001
|
+
extractMetadata: (result) => extractOpenAIResponsesMetadata(result),
|
|
13399
14002
|
extractMetrics: (result, startTime, endEvent) => {
|
|
13400
14003
|
const metrics = withCachedMetric(
|
|
13401
14004
|
parseMetricsFromUsage(result?.usage),
|
|
@@ -13451,35 +14054,6 @@ function withCachedMetric(metrics, result, endEvent) {
|
|
|
13451
14054
|
cached
|
|
13452
14055
|
};
|
|
13453
14056
|
}
|
|
13454
|
-
function processImagesInOutput(output) {
|
|
13455
|
-
if (Array.isArray(output)) {
|
|
13456
|
-
return output.map(processImagesInOutput);
|
|
13457
|
-
}
|
|
13458
|
-
if (isObject(output)) {
|
|
13459
|
-
if (output.type === "image_generation_call" && output.result && typeof output.result === "string") {
|
|
13460
|
-
const fileExtension = output.output_format || "png";
|
|
13461
|
-
const contentType = `image/${fileExtension}`;
|
|
13462
|
-
const baseFilename = output.revised_prompt && typeof output.revised_prompt === "string" ? output.revised_prompt.slice(0, 50).replace(/[^a-zA-Z0-9]/g, "_") : "generated_image";
|
|
13463
|
-
const filename = `${baseFilename}.${fileExtension}`;
|
|
13464
|
-
const binaryString = atob(output.result);
|
|
13465
|
-
const bytes = new Uint8Array(binaryString.length);
|
|
13466
|
-
for (let i = 0; i < binaryString.length; i++) {
|
|
13467
|
-
bytes[i] = binaryString.charCodeAt(i);
|
|
13468
|
-
}
|
|
13469
|
-
const blob = new Blob([bytes], { type: contentType });
|
|
13470
|
-
const attachment = new Attachment({
|
|
13471
|
-
data: blob,
|
|
13472
|
-
filename,
|
|
13473
|
-
contentType
|
|
13474
|
-
});
|
|
13475
|
-
return {
|
|
13476
|
-
...output,
|
|
13477
|
-
result: attachment
|
|
13478
|
-
};
|
|
13479
|
-
}
|
|
13480
|
-
}
|
|
13481
|
-
return output;
|
|
13482
|
-
}
|
|
13483
14057
|
function mergeLogprobTokens(existing, incoming) {
|
|
13484
14058
|
if (incoming === void 0) {
|
|
13485
14059
|
return existing;
|
|
@@ -33499,7 +34073,7 @@ function extractSession(event) {
|
|
|
33499
34073
|
return isObject(candidate) && typeof candidate.prompt === "function" ? candidate : void 0;
|
|
33500
34074
|
}
|
|
33501
34075
|
function isPiAgent(value) {
|
|
33502
|
-
return isObject(value) && typeof value.streamFn === "function" && typeof value.subscribe === "function";
|
|
34076
|
+
return isObject(value) && (typeof value.streamFunction === "function" || typeof value.streamFn === "function") && typeof value.subscribe === "function";
|
|
33503
34077
|
}
|
|
33504
34078
|
function promptContextStore() {
|
|
33505
34079
|
piPromptContextStore ??= isomorph_default.newAsyncLocalStorage();
|
|
@@ -33509,17 +34083,21 @@ function currentPiPromptState() {
|
|
|
33509
34083
|
return promptContextStore().getStore();
|
|
33510
34084
|
}
|
|
33511
34085
|
function installPiAgentInstrumentation(agent) {
|
|
34086
|
+
const property = typeof agent.streamFunction === "function" ? "streamFunction" : "streamFn";
|
|
34087
|
+
const streamFunction = agent[property];
|
|
33512
34088
|
const existing = piAgentPatchStates.get(agent);
|
|
33513
|
-
if (!existing ||
|
|
34089
|
+
if (streamFunction && (!existing || existing.property !== property || streamFunction !== existing.wrappedStreamFunction)) {
|
|
33514
34090
|
const patchState = {
|
|
33515
|
-
|
|
33516
|
-
|
|
34091
|
+
originalStreamFunction: streamFunction,
|
|
34092
|
+
property,
|
|
34093
|
+
wrappedStreamFunction: streamFunction
|
|
33517
34094
|
};
|
|
33518
|
-
patchState.
|
|
34095
|
+
patchState.wrappedStreamFunction = makeInstrumentedStreamFunction(
|
|
33519
34096
|
agent,
|
|
33520
|
-
patchState.
|
|
34097
|
+
patchState.originalStreamFunction,
|
|
34098
|
+
property
|
|
33521
34099
|
);
|
|
33522
|
-
agent
|
|
34100
|
+
agent[property] = patchState.wrappedStreamFunction;
|
|
33523
34101
|
piAgentPatchStates.set(agent, patchState);
|
|
33524
34102
|
}
|
|
33525
34103
|
if (piAgentEventSubscriptions.has(agent)) {
|
|
@@ -33544,15 +34122,21 @@ function installPiAgentInstrumentation(agent) {
|
|
|
33544
34122
|
logInstrumentationError4("Pi Coding Agent event subscription", error);
|
|
33545
34123
|
}
|
|
33546
34124
|
}
|
|
33547
|
-
function
|
|
33548
|
-
return async function
|
|
33549
|
-
const invokeOriginal = () => Reflect.apply(
|
|
34125
|
+
function makeInstrumentedStreamFunction(agent, originalStreamFunction, property) {
|
|
34126
|
+
return async function instrumentedPiStreamFunction(model, context, options) {
|
|
34127
|
+
const invokeOriginal = () => Reflect.apply(originalStreamFunction, this, [model, context, options]);
|
|
33550
34128
|
const state = currentPiPromptState();
|
|
33551
34129
|
if (!state || state.agent !== agent || state.finalized) {
|
|
33552
34130
|
return invokeOriginal();
|
|
33553
34131
|
}
|
|
33554
34132
|
wrapPiToolExecutors(context.tools);
|
|
33555
|
-
const llmState = await startPiLlmSpan(
|
|
34133
|
+
const llmState = await startPiLlmSpan(
|
|
34134
|
+
state,
|
|
34135
|
+
model,
|
|
34136
|
+
context,
|
|
34137
|
+
property,
|
|
34138
|
+
options
|
|
34139
|
+
);
|
|
33556
34140
|
try {
|
|
33557
34141
|
const stream = await runWithAutoInstrumentationSuppressed(invokeOriginal);
|
|
33558
34142
|
return patchAssistantMessageStream(stream, state, llmState);
|
|
@@ -33589,12 +34173,12 @@ function wrapPiToolExecutors(tools) {
|
|
|
33589
34173
|
}
|
|
33590
34174
|
}
|
|
33591
34175
|
}
|
|
33592
|
-
async function startPiLlmSpan(state, model, context, options) {
|
|
34176
|
+
async function startPiLlmSpan(state, model, context, property, options) {
|
|
33593
34177
|
const metadata = {
|
|
33594
34178
|
...extractModelMetadata2(model),
|
|
33595
34179
|
...extractStreamOptionsMetadata(options),
|
|
33596
34180
|
...extractToolMetadata(context.tools),
|
|
33597
|
-
"pi_coding_agent.operation":
|
|
34181
|
+
"pi_coding_agent.operation": `agent.${property}`
|
|
33598
34182
|
};
|
|
33599
34183
|
const span = startSpan(
|
|
33600
34184
|
withSpanInstrumentationName(
|