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
|
@@ -5771,7 +5771,7 @@ var INSTRUMENTATION_NAMES = {
|
|
|
5771
5771
|
var INTERNAL_SPAN_INSTRUMENTATION_NAME = /* @__PURE__ */ Symbol.for(
|
|
5772
5772
|
"braintrust.spanInstrumentationName"
|
|
5773
5773
|
);
|
|
5774
|
-
var SDK_VERSION = true ? "3.
|
|
5774
|
+
var SDK_VERSION = true ? "3.30.0" : "0.0.0";
|
|
5775
5775
|
function withSpanInstrumentationName(args, instrumentationName) {
|
|
5776
5776
|
return {
|
|
5777
5777
|
...args,
|
|
@@ -8487,6 +8487,9 @@ function getSpanParentObjectAndPropagatedState(options) {
|
|
|
8487
8487
|
}
|
|
8488
8488
|
return { parentObject: NOOP_SPAN, propagatedState: void 0 };
|
|
8489
8489
|
}
|
|
8490
|
+
function getSpanParentObject(options) {
|
|
8491
|
+
return getSpanParentObjectAndPropagatedState(options).parentObject;
|
|
8492
|
+
}
|
|
8490
8493
|
function currentBraintrustParent(state) {
|
|
8491
8494
|
const resolvedState = state ?? _globalState;
|
|
8492
8495
|
const experiment = currentExperiment({ state: resolvedState });
|
|
@@ -8733,6 +8736,15 @@ function _internalStartSpanWithInitialMerge(args) {
|
|
|
8733
8736
|
[INITIAL_SPAN_WRITE_AS_MERGE]: true
|
|
8734
8737
|
}).span;
|
|
8735
8738
|
}
|
|
8739
|
+
function _internalStartSpanWithInitialMergeAndParentSpanIds(args) {
|
|
8740
|
+
return startSpanAndIsLogger(
|
|
8741
|
+
{
|
|
8742
|
+
...args,
|
|
8743
|
+
[INITIAL_SPAN_WRITE_AS_MERGE]: true
|
|
8744
|
+
},
|
|
8745
|
+
{ useParentSpanIdsForObjectParent: true }
|
|
8746
|
+
).span;
|
|
8747
|
+
}
|
|
8736
8748
|
function _internalStartSpanWithContext(args, context) {
|
|
8737
8749
|
return startSpanAndIsLogger({
|
|
8738
8750
|
...args,
|
|
@@ -8743,7 +8755,7 @@ async function flush(options) {
|
|
|
8743
8755
|
const state = options?.state ?? _globalState;
|
|
8744
8756
|
return await state.bgLogger().flush();
|
|
8745
8757
|
}
|
|
8746
|
-
function startSpanAndIsLogger(args) {
|
|
8758
|
+
function startSpanAndIsLogger(args, internalOptions) {
|
|
8747
8759
|
const state = args?.state ?? _globalState;
|
|
8748
8760
|
const { parentObject, propagatedState } = getSpanParentObjectAndPropagatedState({
|
|
8749
8761
|
asyncFlush: args?.asyncFlush,
|
|
@@ -8757,7 +8769,7 @@ function startSpanAndIsLogger(args) {
|
|
|
8757
8769
|
) ? {
|
|
8758
8770
|
spanId: parentObject.data.span_id,
|
|
8759
8771
|
rootSpanId: parentObject.data.root_span_id
|
|
8760
|
-
} : void 0;
|
|
8772
|
+
} : internalOptions?.useParentSpanIdsForObjectParent ? args?.parentSpanIds : void 0;
|
|
8761
8773
|
const { parent: _ignoredParent, ...spanArgs } = args ?? {};
|
|
8762
8774
|
const span = new SpanImpl({
|
|
8763
8775
|
state,
|
|
@@ -11787,6 +11799,131 @@ function unsubscribeAll(unsubscribers) {
|
|
|
11787
11799
|
return [];
|
|
11788
11800
|
}
|
|
11789
11801
|
|
|
11802
|
+
// src/instrumentation/core/channel-definitions.ts
|
|
11803
|
+
function channel(spec) {
|
|
11804
|
+
return spec;
|
|
11805
|
+
}
|
|
11806
|
+
function defineChannels(pkg, channels, options) {
|
|
11807
|
+
const { instrumentationName } = options;
|
|
11808
|
+
return Object.fromEntries(
|
|
11809
|
+
Object.entries(channels).map(([key, spec]) => {
|
|
11810
|
+
const fullChannelName = `orchestrion:${pkg}:${spec.channelName}`;
|
|
11811
|
+
if (spec.kind === "async") {
|
|
11812
|
+
const asyncSpec = spec;
|
|
11813
|
+
const tracingChannel2 = () => isomorph_default.newTracingChannel(
|
|
11814
|
+
fullChannelName
|
|
11815
|
+
);
|
|
11816
|
+
const intercept2 = (interceptor) => {
|
|
11817
|
+
const hook = tracingChannel2();
|
|
11818
|
+
return typeof hook.intercept === "function" ? hook.intercept(interceptor) : () => {
|
|
11819
|
+
};
|
|
11820
|
+
};
|
|
11821
|
+
return [
|
|
11822
|
+
key,
|
|
11823
|
+
{
|
|
11824
|
+
...asyncSpec,
|
|
11825
|
+
instrumentationName,
|
|
11826
|
+
intercept: intercept2,
|
|
11827
|
+
invoke: (target, thisArg, args, additional) => {
|
|
11828
|
+
const hook = tracingChannel2();
|
|
11829
|
+
return typeof hook.invoke === "function" ? hook.invoke(target, thisArg, args, additional) : Reflect.apply(target, thisArg, args);
|
|
11830
|
+
},
|
|
11831
|
+
tracingChannel: tracingChannel2,
|
|
11832
|
+
tracePromise: (fn, context) => tracingChannel2().tracePromise(
|
|
11833
|
+
fn,
|
|
11834
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
11835
|
+
context
|
|
11836
|
+
)
|
|
11837
|
+
}
|
|
11838
|
+
];
|
|
11839
|
+
}
|
|
11840
|
+
const syncSpec = spec;
|
|
11841
|
+
const tracingChannel = () => isomorph_default.newTracingChannel(
|
|
11842
|
+
fullChannelName
|
|
11843
|
+
);
|
|
11844
|
+
const intercept = (interceptor) => {
|
|
11845
|
+
const hook = tracingChannel();
|
|
11846
|
+
return typeof hook.intercept === "function" ? hook.intercept(interceptor) : () => {
|
|
11847
|
+
};
|
|
11848
|
+
};
|
|
11849
|
+
return [
|
|
11850
|
+
key,
|
|
11851
|
+
{
|
|
11852
|
+
...syncSpec,
|
|
11853
|
+
instrumentationName,
|
|
11854
|
+
intercept,
|
|
11855
|
+
invoke: (target, thisArg, args, additional) => {
|
|
11856
|
+
const hook = tracingChannel();
|
|
11857
|
+
return typeof hook.invoke === "function" ? hook.invoke(target, thisArg, args, additional) : Reflect.apply(target, thisArg, args);
|
|
11858
|
+
},
|
|
11859
|
+
tracingChannel,
|
|
11860
|
+
traceSync: (fn, context) => tracingChannel().traceSync(
|
|
11861
|
+
fn,
|
|
11862
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
11863
|
+
context
|
|
11864
|
+
)
|
|
11865
|
+
}
|
|
11866
|
+
];
|
|
11867
|
+
})
|
|
11868
|
+
);
|
|
11869
|
+
}
|
|
11870
|
+
|
|
11871
|
+
// src/instrumentation/plugins/openai-channels.ts
|
|
11872
|
+
var openAIChannels = defineChannels(
|
|
11873
|
+
"openai",
|
|
11874
|
+
{
|
|
11875
|
+
filesCreateTraced: channel({
|
|
11876
|
+
channelName: "files.create-traced",
|
|
11877
|
+
kind: "async"
|
|
11878
|
+
}),
|
|
11879
|
+
batchesRetrieveTraced: channel({
|
|
11880
|
+
channelName: "batches.retrieve-traced",
|
|
11881
|
+
kind: "async"
|
|
11882
|
+
}),
|
|
11883
|
+
batchesCompleteTrace: channel({
|
|
11884
|
+
channelName: "batches.complete-trace",
|
|
11885
|
+
kind: "async"
|
|
11886
|
+
}),
|
|
11887
|
+
chatCompletionsCreate: channel({
|
|
11888
|
+
channelName: "chat.completions.create",
|
|
11889
|
+
kind: "async"
|
|
11890
|
+
}),
|
|
11891
|
+
embeddingsCreate: channel({
|
|
11892
|
+
channelName: "embeddings.create",
|
|
11893
|
+
kind: "async"
|
|
11894
|
+
}),
|
|
11895
|
+
betaChatCompletionsParse: channel({
|
|
11896
|
+
channelName: "beta.chat.completions.parse",
|
|
11897
|
+
kind: "async"
|
|
11898
|
+
}),
|
|
11899
|
+
betaChatCompletionsStream: channel({
|
|
11900
|
+
channelName: "beta.chat.completions.stream",
|
|
11901
|
+
kind: "sync-stream"
|
|
11902
|
+
}),
|
|
11903
|
+
moderationsCreate: channel({
|
|
11904
|
+
channelName: "moderations.create",
|
|
11905
|
+
kind: "async"
|
|
11906
|
+
}),
|
|
11907
|
+
responsesCreate: channel({
|
|
11908
|
+
channelName: "responses.create",
|
|
11909
|
+
kind: "async"
|
|
11910
|
+
}),
|
|
11911
|
+
responsesStream: channel({
|
|
11912
|
+
channelName: "responses.stream",
|
|
11913
|
+
kind: "sync-stream"
|
|
11914
|
+
}),
|
|
11915
|
+
responsesParse: channel({
|
|
11916
|
+
channelName: "responses.parse",
|
|
11917
|
+
kind: "async"
|
|
11918
|
+
}),
|
|
11919
|
+
responsesCompact: channel({
|
|
11920
|
+
channelName: "responses.compact",
|
|
11921
|
+
kind: "async"
|
|
11922
|
+
})
|
|
11923
|
+
},
|
|
11924
|
+
{ instrumentationName: INSTRUMENTATION_NAMES.OPENAI }
|
|
11925
|
+
);
|
|
11926
|
+
|
|
11790
11927
|
// src/wrappers/attachment-utils.ts
|
|
11791
11928
|
function getExtensionFromMediaType(mediaType) {
|
|
11792
11929
|
const extensionMap = {
|
|
@@ -11962,173 +12099,691 @@ function processInputAttachments(input) {
|
|
|
11962
12099
|
return processNode(input);
|
|
11963
12100
|
}
|
|
11964
12101
|
|
|
11965
|
-
// src/instrumentation/
|
|
11966
|
-
|
|
11967
|
-
|
|
12102
|
+
// src/instrumentation/plugins/openai-span-data.ts
|
|
12103
|
+
var OPENAI_METADATA_KEYS = [
|
|
12104
|
+
"model",
|
|
12105
|
+
"temperature",
|
|
12106
|
+
"top_p",
|
|
12107
|
+
"max_tokens",
|
|
12108
|
+
"frequency_penalty",
|
|
12109
|
+
"presence_penalty",
|
|
12110
|
+
"stop",
|
|
12111
|
+
"response_format",
|
|
12112
|
+
"tools",
|
|
12113
|
+
"tool_choice",
|
|
12114
|
+
"parallel_tool_calls",
|
|
12115
|
+
"max_tool_calls"
|
|
12116
|
+
];
|
|
12117
|
+
function batchMetadata(params) {
|
|
12118
|
+
const metadata = { provider: "openai" };
|
|
12119
|
+
for (const key of OPENAI_METADATA_KEYS) {
|
|
12120
|
+
try {
|
|
12121
|
+
const value = Reflect.get(params, key);
|
|
12122
|
+
if (value !== void 0) {
|
|
12123
|
+
metadata[key] = value;
|
|
12124
|
+
}
|
|
12125
|
+
} catch {
|
|
12126
|
+
}
|
|
12127
|
+
}
|
|
12128
|
+
return metadata;
|
|
11968
12129
|
}
|
|
11969
|
-
function
|
|
11970
|
-
const
|
|
11971
|
-
return
|
|
11972
|
-
|
|
11973
|
-
|
|
11974
|
-
|
|
11975
|
-
|
|
11976
|
-
|
|
11977
|
-
|
|
11978
|
-
|
|
11979
|
-
|
|
11980
|
-
|
|
11981
|
-
|
|
11982
|
-
|
|
11983
|
-
|
|
11984
|
-
|
|
11985
|
-
|
|
11986
|
-
|
|
11987
|
-
|
|
11988
|
-
|
|
11989
|
-
|
|
11990
|
-
|
|
11991
|
-
|
|
11992
|
-
|
|
11993
|
-
|
|
11994
|
-
|
|
11995
|
-
|
|
11996
|
-
|
|
11997
|
-
|
|
11998
|
-
|
|
11999
|
-
|
|
12000
|
-
|
|
12001
|
-
|
|
12130
|
+
function extractOpenAIBatchInput(endpoint, params) {
|
|
12131
|
+
const input = endpoint === "/v1/chat/completions" ? params.messages : params.input;
|
|
12132
|
+
return {
|
|
12133
|
+
input: processInputAttachments(input),
|
|
12134
|
+
metadata: batchMetadata(params)
|
|
12135
|
+
};
|
|
12136
|
+
}
|
|
12137
|
+
function extractOpenAIChatInput(params) {
|
|
12138
|
+
const { messages, ...metadata } = params;
|
|
12139
|
+
return {
|
|
12140
|
+
input: processInputAttachments(messages),
|
|
12141
|
+
metadata: { ...metadata, provider: "openai" }
|
|
12142
|
+
};
|
|
12143
|
+
}
|
|
12144
|
+
function extractOpenAIResponsesInput(params) {
|
|
12145
|
+
const { input, ...metadata } = params;
|
|
12146
|
+
return {
|
|
12147
|
+
input: processInputAttachments(input),
|
|
12148
|
+
metadata: { ...metadata, provider: "openai" }
|
|
12149
|
+
};
|
|
12150
|
+
}
|
|
12151
|
+
function extractOpenAIResponsesMetadata(result) {
|
|
12152
|
+
if (!result) {
|
|
12153
|
+
return void 0;
|
|
12154
|
+
}
|
|
12155
|
+
const { output: _output, usage: _usage, ...metadata } = result;
|
|
12156
|
+
return Object.keys(metadata).length > 0 ? metadata : void 0;
|
|
12157
|
+
}
|
|
12158
|
+
function processImagesInOutput(output) {
|
|
12159
|
+
if (Array.isArray(output)) {
|
|
12160
|
+
return output.map(processImagesInOutput);
|
|
12161
|
+
}
|
|
12162
|
+
if (isObject(output) && output.type === "image_generation_call" && typeof output.result === "string" && output.result) {
|
|
12163
|
+
const fileExtension = output.output_format || "png";
|
|
12164
|
+
const contentType = `image/${fileExtension}`;
|
|
12165
|
+
const baseFilename = typeof output.revised_prompt === "string" ? output.revised_prompt.slice(0, 50).replace(/[^a-zA-Z0-9]/g, "_") : "generated_image";
|
|
12166
|
+
let binaryString;
|
|
12167
|
+
try {
|
|
12168
|
+
binaryString = atob(output.result);
|
|
12169
|
+
} catch {
|
|
12170
|
+
return output;
|
|
12171
|
+
}
|
|
12172
|
+
const bytes = new Uint8Array(binaryString.length);
|
|
12173
|
+
for (let i = 0; i < binaryString.length; i++) {
|
|
12174
|
+
bytes[i] = binaryString.charCodeAt(i);
|
|
12175
|
+
}
|
|
12176
|
+
return {
|
|
12177
|
+
...output,
|
|
12178
|
+
result: new Attachment({
|
|
12179
|
+
data: new Blob([bytes], { type: contentType }),
|
|
12180
|
+
filename: `${baseFilename}.${fileExtension}`,
|
|
12181
|
+
contentType
|
|
12182
|
+
})
|
|
12183
|
+
};
|
|
12184
|
+
}
|
|
12185
|
+
return output;
|
|
12186
|
+
}
|
|
12187
|
+
|
|
12188
|
+
// src/openai-utils.ts
|
|
12189
|
+
var BRAINTRUST_CACHED_STREAM_METRIC = "__braintrust_cached_metric";
|
|
12190
|
+
var LEGACY_CACHED_HEADER = "x-cached";
|
|
12191
|
+
var X_CACHED_HEADER = "x-bt-cached";
|
|
12192
|
+
var TOKEN_NAME_MAP = {
|
|
12193
|
+
input_tokens: "prompt_tokens",
|
|
12194
|
+
output_tokens: "completion_tokens",
|
|
12195
|
+
total_tokens: "tokens"
|
|
12196
|
+
};
|
|
12197
|
+
var TOKEN_PREFIX_MAP = {
|
|
12198
|
+
input: "prompt",
|
|
12199
|
+
output: "completion"
|
|
12200
|
+
};
|
|
12201
|
+
function parseMetricsFromUsage(usage) {
|
|
12202
|
+
if (!usage) {
|
|
12203
|
+
return {};
|
|
12204
|
+
}
|
|
12205
|
+
const metrics = {};
|
|
12206
|
+
for (const [oaiName, value] of Object.entries(usage)) {
|
|
12207
|
+
if (typeof value === "number") {
|
|
12208
|
+
const metricName = TOKEN_NAME_MAP[oaiName] || oaiName;
|
|
12209
|
+
metrics[metricName] = value;
|
|
12210
|
+
continue;
|
|
12211
|
+
}
|
|
12212
|
+
if (!oaiName.endsWith("_tokens_details") || !isObject(value)) {
|
|
12213
|
+
continue;
|
|
12214
|
+
}
|
|
12215
|
+
const rawPrefix = oaiName.slice(0, -"_tokens_details".length);
|
|
12216
|
+
const prefix = TOKEN_PREFIX_MAP[rawPrefix] || rawPrefix;
|
|
12217
|
+
for (const [key, nestedValue] of Object.entries(value)) {
|
|
12218
|
+
if (typeof nestedValue !== "number") {
|
|
12219
|
+
continue;
|
|
12002
12220
|
}
|
|
12003
|
-
|
|
12004
|
-
|
|
12005
|
-
|
|
12006
|
-
|
|
12007
|
-
|
|
12008
|
-
|
|
12009
|
-
|
|
12010
|
-
|
|
12011
|
-
|
|
12012
|
-
|
|
12013
|
-
|
|
12221
|
+
metrics[`${prefix}_${key}`] = nestedValue;
|
|
12222
|
+
}
|
|
12223
|
+
}
|
|
12224
|
+
return metrics;
|
|
12225
|
+
}
|
|
12226
|
+
function parseCachedHeader(value) {
|
|
12227
|
+
if (!value) {
|
|
12228
|
+
return void 0;
|
|
12229
|
+
}
|
|
12230
|
+
return ["true", "hit"].includes(value.toLowerCase()) ? 1 : 0;
|
|
12231
|
+
}
|
|
12232
|
+
function getCachedMetricFromHeaders(headers) {
|
|
12233
|
+
if (!headers || typeof headers.get !== "function") {
|
|
12234
|
+
return void 0;
|
|
12235
|
+
}
|
|
12236
|
+
const cachedHeader = headers.get(X_CACHED_HEADER);
|
|
12237
|
+
if (cachedHeader) {
|
|
12238
|
+
return parseCachedHeader(cachedHeader);
|
|
12239
|
+
}
|
|
12240
|
+
return parseCachedHeader(headers.get(LEGACY_CACHED_HEADER));
|
|
12241
|
+
}
|
|
12242
|
+
|
|
12243
|
+
// src/instrumentation/plugins/openai-batch-instrumentation.ts
|
|
12244
|
+
var SUPPORTED_ENDPOINTS = /* @__PURE__ */ new Set(["/v1/chat/completions", "/v1/responses"]);
|
|
12245
|
+
var TERMINAL_STATUSES = /* @__PURE__ */ new Set([
|
|
12246
|
+
"completed",
|
|
12247
|
+
"failed",
|
|
12248
|
+
"expired",
|
|
12249
|
+
"cancelled"
|
|
12250
|
+
]);
|
|
12251
|
+
var pendingBatchTraces = /* @__PURE__ */ new Map();
|
|
12252
|
+
function read(value, key) {
|
|
12253
|
+
if (!isObject(value)) {
|
|
12254
|
+
return void 0;
|
|
12255
|
+
}
|
|
12256
|
+
try {
|
|
12257
|
+
return Reflect.get(value, key);
|
|
12258
|
+
} catch {
|
|
12259
|
+
return void 0;
|
|
12260
|
+
}
|
|
12261
|
+
}
|
|
12262
|
+
function isBatchRecordIterable(value) {
|
|
12263
|
+
if (value === null || typeof value !== "object" && typeof value !== "function") {
|
|
12264
|
+
return false;
|
|
12265
|
+
}
|
|
12266
|
+
return typeof read(value, Symbol.iterator) === "function" || typeof read(value, Symbol.asyncIterator) === "function";
|
|
12267
|
+
}
|
|
12268
|
+
function validCustomId(value) {
|
|
12269
|
+
return typeof value === "string" && value.length > 0 && value.length <= 64;
|
|
12270
|
+
}
|
|
12271
|
+
function logBatchInstrumentationError(context, error) {
|
|
12272
|
+
debugLogger.debug(`OpenAI Batch instrumentation ${context}:`, error);
|
|
12273
|
+
}
|
|
12274
|
+
async function exportParent(parent) {
|
|
12275
|
+
if ("toStr" in parent && typeof parent.toStr === "function") {
|
|
12276
|
+
return parent.toStr();
|
|
12277
|
+
}
|
|
12278
|
+
if ("export" in parent && typeof parent.export === "function") {
|
|
12279
|
+
return await parent.export();
|
|
12280
|
+
}
|
|
12281
|
+
return void 0;
|
|
12282
|
+
}
|
|
12283
|
+
async function deterministicDigest(namespace, ...parts) {
|
|
12284
|
+
const encoded = new TextEncoder().encode(
|
|
12285
|
+
[namespace, ...parts].map((part) => `${part.length}:${part}`).join("\0")
|
|
12286
|
+
);
|
|
12287
|
+
return new Uint8Array(
|
|
12288
|
+
await globalThis.crypto.subtle.digest("SHA-256", encoded)
|
|
12289
|
+
);
|
|
12290
|
+
}
|
|
12291
|
+
function digestHex(bytes, length) {
|
|
12292
|
+
return Array.from(bytes.slice(0, length)).map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
12293
|
+
}
|
|
12294
|
+
function digestUuid(bytes) {
|
|
12295
|
+
const hex = digestHex(bytes, 16);
|
|
12296
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
12297
|
+
}
|
|
12298
|
+
async function batchSpanIds(inputFileId) {
|
|
12299
|
+
const [row, span, root] = await Promise.all([
|
|
12300
|
+
deterministicDigest("openai:batch:row", inputFileId),
|
|
12301
|
+
deterministicDigest("openai:batch:span", inputFileId),
|
|
12302
|
+
deterministicDigest("openai:batch:root", inputFileId)
|
|
12303
|
+
]);
|
|
12304
|
+
return {
|
|
12305
|
+
rowId: digestUuid(row),
|
|
12306
|
+
spanId: digestHex(span, 8),
|
|
12307
|
+
rootSpanId: digestHex(root, 16)
|
|
12308
|
+
};
|
|
12309
|
+
}
|
|
12310
|
+
async function childSpanIds(inputFileId, customId) {
|
|
12311
|
+
const [row, span] = await Promise.all([
|
|
12312
|
+
deterministicDigest("openai:batch:child:row", inputFileId, customId),
|
|
12313
|
+
deterministicDigest("openai:batch:child:span", inputFileId, customId)
|
|
12314
|
+
]);
|
|
12315
|
+
return { rowId: digestUuid(row), spanId: digestHex(span, 8) };
|
|
12316
|
+
}
|
|
12317
|
+
async function startBatchSpan(context) {
|
|
12318
|
+
const ids = await batchSpanIds(context.inputFileId);
|
|
12319
|
+
const parent = SpanComponentsV4.fromStr(context.parent);
|
|
12320
|
+
const hasParentSpan = Boolean(
|
|
12321
|
+
parent.data.row_id && parent.data.span_id && parent.data.root_span_id
|
|
12322
|
+
);
|
|
12323
|
+
return withCurrent(
|
|
12324
|
+
NOOP_SPAN,
|
|
12325
|
+
() => _internalStartSpanWithInitialMergeAndParentSpanIds(
|
|
12326
|
+
withSpanInstrumentationName(
|
|
12014
12327
|
{
|
|
12015
|
-
|
|
12016
|
-
|
|
12017
|
-
|
|
12018
|
-
|
|
12019
|
-
|
|
12020
|
-
|
|
12021
|
-
|
|
12022
|
-
|
|
12023
|
-
|
|
12024
|
-
|
|
12025
|
-
|
|
12026
|
-
|
|
12027
|
-
|
|
12028
|
-
|
|
12029
|
-
|
|
12030
|
-
|
|
12328
|
+
name: "openai.batch",
|
|
12329
|
+
type: "task" /* TASK */,
|
|
12330
|
+
parent: context.parent,
|
|
12331
|
+
...!hasParentSpan ? {
|
|
12332
|
+
parentSpanIds: {
|
|
12333
|
+
parentSpanIds: [],
|
|
12334
|
+
rootSpanId: ids.rootSpanId
|
|
12335
|
+
}
|
|
12336
|
+
} : {},
|
|
12337
|
+
spanId: ids.spanId,
|
|
12338
|
+
startTime: context.taskStartTime,
|
|
12339
|
+
event: {
|
|
12340
|
+
id: ids.rowId,
|
|
12341
|
+
metadata: {
|
|
12342
|
+
endpoint: context.endpoint,
|
|
12343
|
+
input_file_id: context.inputFileId,
|
|
12344
|
+
provider: "openai"
|
|
12345
|
+
}
|
|
12346
|
+
}
|
|
12347
|
+
},
|
|
12348
|
+
INSTRUMENTATION_NAMES.OPENAI
|
|
12349
|
+
)
|
|
12350
|
+
)
|
|
12031
12351
|
);
|
|
12032
12352
|
}
|
|
12033
|
-
|
|
12034
|
-
|
|
12035
|
-
|
|
12036
|
-
|
|
12037
|
-
|
|
12038
|
-
|
|
12039
|
-
|
|
12040
|
-
|
|
12041
|
-
|
|
12042
|
-
|
|
12043
|
-
|
|
12044
|
-
|
|
12045
|
-
|
|
12046
|
-
|
|
12047
|
-
|
|
12048
|
-
|
|
12049
|
-
|
|
12050
|
-
|
|
12051
|
-
|
|
12052
|
-
|
|
12053
|
-
|
|
12054
|
-
|
|
12055
|
-
|
|
12056
|
-
|
|
12057
|
-
|
|
12058
|
-
|
|
12059
|
-
|
|
12060
|
-
|
|
12061
|
-
|
|
12062
|
-
|
|
12063
|
-
|
|
12064
|
-
|
|
12065
|
-
|
|
12066
|
-
|
|
12067
|
-
|
|
12068
|
-
|
|
12069
|
-
|
|
12070
|
-
|
|
12071
|
-
|
|
12072
|
-
|
|
12073
|
-
|
|
12074
|
-
|
|
12075
|
-
|
|
12076
|
-
|
|
12077
|
-
|
|
12078
|
-
|
|
12079
|
-
|
|
12080
|
-
|
|
12081
|
-
|
|
12082
|
-
|
|
12083
|
-
|
|
12084
|
-
|
|
12085
|
-
|
|
12086
|
-
|
|
12087
|
-
|
|
12088
|
-
|
|
12089
|
-
|
|
12353
|
+
async function startBatchChild(context, taskParent, input) {
|
|
12354
|
+
const ids = await childSpanIds(context.inputFileId, input.customId);
|
|
12355
|
+
return withCurrent(
|
|
12356
|
+
NOOP_SPAN,
|
|
12357
|
+
() => _internalStartSpanWithInitialMerge(
|
|
12358
|
+
withSpanInstrumentationName(
|
|
12359
|
+
{
|
|
12360
|
+
name: context.endpoint === "/v1/chat/completions" ? "Chat Completion" : "openai.responses.create",
|
|
12361
|
+
type: "llm" /* LLM */,
|
|
12362
|
+
parent: taskParent,
|
|
12363
|
+
spanId: ids.spanId,
|
|
12364
|
+
startTime: context.childStartTime,
|
|
12365
|
+
event: {
|
|
12366
|
+
id: ids.rowId,
|
|
12367
|
+
...input.spanData?.input !== void 0 ? { input: input.spanData.input } : {},
|
|
12368
|
+
metadata: {
|
|
12369
|
+
...input.spanData?.metadata,
|
|
12370
|
+
custom_id: input.customId,
|
|
12371
|
+
provider: "openai"
|
|
12372
|
+
}
|
|
12373
|
+
}
|
|
12374
|
+
},
|
|
12375
|
+
INSTRUMENTATION_NAMES.OPENAI
|
|
12376
|
+
)
|
|
12377
|
+
)
|
|
12378
|
+
);
|
|
12379
|
+
}
|
|
12380
|
+
async function* jsonlRecords(file, onIssue = () => {
|
|
12381
|
+
}) {
|
|
12382
|
+
const resolvedFile = await file;
|
|
12383
|
+
if (typeof resolvedFile === "string") {
|
|
12384
|
+
for (const line of resolvedFile.split("\n")) {
|
|
12385
|
+
if (!line.trim()) {
|
|
12386
|
+
continue;
|
|
12387
|
+
}
|
|
12388
|
+
try {
|
|
12389
|
+
yield JSON.parse(line.replace(/\r$/, ""));
|
|
12390
|
+
} catch (error) {
|
|
12391
|
+
logBatchInstrumentationError("skipped malformed JSONL", error);
|
|
12392
|
+
onIssue(new Error("OpenAI Batch file contains malformed JSONL"));
|
|
12393
|
+
}
|
|
12394
|
+
}
|
|
12395
|
+
return;
|
|
12396
|
+
}
|
|
12397
|
+
const body = read(resolvedFile, "body");
|
|
12398
|
+
const getReader = read(body, "getReader");
|
|
12399
|
+
if (isObject(body) && typeof getReader === "function") {
|
|
12400
|
+
let reader;
|
|
12401
|
+
try {
|
|
12402
|
+
reader = Reflect.apply(getReader, body, []);
|
|
12403
|
+
const decoder = new TextDecoder();
|
|
12404
|
+
let pending = "";
|
|
12405
|
+
while (true) {
|
|
12406
|
+
const readChunk = read(reader, "read");
|
|
12407
|
+
if (typeof readChunk !== "function") {
|
|
12408
|
+
throw new Error("Response body stream has no read method");
|
|
12409
|
+
}
|
|
12410
|
+
const chunk = await Reflect.apply(readChunk, reader, []);
|
|
12411
|
+
if (!isObject(chunk)) {
|
|
12412
|
+
throw new Error("Response body stream returned an invalid chunk");
|
|
12413
|
+
}
|
|
12414
|
+
if (chunk.done === true) {
|
|
12415
|
+
pending += decoder.decode();
|
|
12416
|
+
break;
|
|
12417
|
+
}
|
|
12418
|
+
if (!(chunk.value instanceof Uint8Array)) {
|
|
12419
|
+
throw new Error("Response body stream returned a non-byte chunk");
|
|
12420
|
+
}
|
|
12421
|
+
pending += decoder.decode(chunk.value, { stream: true });
|
|
12422
|
+
let newline = pending.indexOf("\n");
|
|
12423
|
+
while (newline !== -1) {
|
|
12424
|
+
const line = pending.slice(0, newline).replace(/\r$/, "");
|
|
12425
|
+
pending = pending.slice(newline + 1);
|
|
12426
|
+
if (line.trim()) {
|
|
12427
|
+
try {
|
|
12428
|
+
yield JSON.parse(line);
|
|
12429
|
+
} catch (error) {
|
|
12430
|
+
logBatchInstrumentationError("skipped malformed JSONL", error);
|
|
12431
|
+
onIssue(new Error("OpenAI Batch file contains malformed JSONL"));
|
|
12432
|
+
}
|
|
12433
|
+
}
|
|
12434
|
+
newline = pending.indexOf("\n");
|
|
12435
|
+
}
|
|
12436
|
+
}
|
|
12437
|
+
if (pending.trim()) {
|
|
12438
|
+
try {
|
|
12439
|
+
yield JSON.parse(pending.replace(/\r$/, ""));
|
|
12440
|
+
} catch (error) {
|
|
12441
|
+
logBatchInstrumentationError("skipped malformed JSONL", error);
|
|
12442
|
+
onIssue(new Error("OpenAI Batch file contains malformed JSONL"));
|
|
12443
|
+
}
|
|
12444
|
+
}
|
|
12445
|
+
} catch (error) {
|
|
12446
|
+
logBatchInstrumentationError("could not read JSONL response body", error);
|
|
12447
|
+
onIssue(new Error("OpenAI Batch response body could not be read"));
|
|
12448
|
+
} finally {
|
|
12449
|
+
const releaseLock = read(reader, "releaseLock");
|
|
12450
|
+
if (typeof releaseLock === "function") {
|
|
12451
|
+
try {
|
|
12452
|
+
Reflect.apply(releaseLock, reader, []);
|
|
12453
|
+
} catch (error) {
|
|
12454
|
+
logBatchInstrumentationError(
|
|
12455
|
+
"could not release stream reader",
|
|
12456
|
+
error
|
|
12457
|
+
);
|
|
12458
|
+
}
|
|
12459
|
+
}
|
|
12460
|
+
}
|
|
12461
|
+
return;
|
|
12462
|
+
}
|
|
12463
|
+
if (isBatchRecordIterable(resolvedFile)) {
|
|
12464
|
+
for await (const record of resolvedFile) {
|
|
12465
|
+
yield record;
|
|
12466
|
+
}
|
|
12467
|
+
return;
|
|
12468
|
+
}
|
|
12469
|
+
logBatchInstrumentationError("skipped invalid JSONL source", resolvedFile);
|
|
12470
|
+
onIssue(new Error("OpenAI Batch file source is invalid"));
|
|
12471
|
+
}
|
|
12472
|
+
async function readBatchInputs(file) {
|
|
12473
|
+
const inputs = /* @__PURE__ */ new Map();
|
|
12474
|
+
const issues = [];
|
|
12475
|
+
let endpoint;
|
|
12476
|
+
try {
|
|
12477
|
+
for await (const value of jsonlRecords(
|
|
12478
|
+
file,
|
|
12479
|
+
(issue) => issues.push(issue)
|
|
12480
|
+
)) {
|
|
12481
|
+
const customId = read(value, "custom_id");
|
|
12482
|
+
const url = read(value, "url");
|
|
12483
|
+
const body = read(value, "body");
|
|
12484
|
+
if (!validCustomId(customId) || inputs.has(customId) || read(value, "method") !== "POST" || typeof url !== "string" || !SUPPORTED_ENDPOINTS.has(url) || endpoint !== void 0 && endpoint !== url || !isObject(body)) {
|
|
12485
|
+
issues.push(new Error("OpenAI Batch input contains an invalid record"));
|
|
12486
|
+
continue;
|
|
12487
|
+
}
|
|
12488
|
+
endpoint = url;
|
|
12489
|
+
let spanData;
|
|
12490
|
+
try {
|
|
12491
|
+
spanData = extractOpenAIBatchInput(url, body);
|
|
12492
|
+
} catch (error) {
|
|
12493
|
+
logBatchInstrumentationError("could not extract batch input", error);
|
|
12494
|
+
}
|
|
12495
|
+
inputs.set(customId, { customId, spanData });
|
|
12496
|
+
}
|
|
12497
|
+
} catch (error) {
|
|
12498
|
+
logBatchInstrumentationError("could not process input file", error);
|
|
12499
|
+
issues.push(new Error("OpenAI Batch input file could not be processed"));
|
|
12500
|
+
}
|
|
12501
|
+
if (!endpoint || inputs.size === 0) {
|
|
12502
|
+
issues.push(new Error("OpenAI Batch input contains no supported records"));
|
|
12503
|
+
}
|
|
12504
|
+
return { endpoint, inputs, issues };
|
|
12505
|
+
}
|
|
12506
|
+
async function writePendingSpans(trace, endTime) {
|
|
12507
|
+
const task = await startBatchSpan(trace.context);
|
|
12508
|
+
const taskParent = await task.export();
|
|
12509
|
+
for (const input of trace.inputs.values()) {
|
|
12510
|
+
try {
|
|
12511
|
+
const child = await startBatchChild(trace.context, taskParent, input);
|
|
12512
|
+
if (endTime !== void 0) {
|
|
12513
|
+
child.end({ endTime });
|
|
12514
|
+
}
|
|
12515
|
+
} catch (error) {
|
|
12516
|
+
logBatchInstrumentationError("could not write batch request span", error);
|
|
12517
|
+
}
|
|
12518
|
+
}
|
|
12519
|
+
if (endTime !== void 0) {
|
|
12520
|
+
if (trace.status && trace.status !== "completed") {
|
|
12521
|
+
task.log({ error: new Error(`OpenAI Batch ${trace.status}`) });
|
|
12522
|
+
}
|
|
12523
|
+
task.end({ endTime });
|
|
12524
|
+
}
|
|
12525
|
+
}
|
|
12526
|
+
var interceptOpenAIFilesCreateTraced = async (target, thisArg, args) => {
|
|
12527
|
+
const startTime = getCurrentUnixTimestamp();
|
|
12528
|
+
const inputPromise = readBatchInputs(args[0].inputFileContent);
|
|
12529
|
+
const parentPromise = exportParent(args[0].parent);
|
|
12530
|
+
const file = await Reflect.apply(target, thisArg, args);
|
|
12531
|
+
try {
|
|
12532
|
+
const inputFileId = read(file, "id");
|
|
12533
|
+
const [inputData, exportedParent2] = await Promise.all([
|
|
12534
|
+
inputPromise,
|
|
12535
|
+
parentPromise
|
|
12536
|
+
]);
|
|
12537
|
+
if (typeof inputFileId !== "string" || !exportedParent2 || !inputData.endpoint || inputData.issues.length > 0) {
|
|
12538
|
+
if (inputData.issues[0]) {
|
|
12539
|
+
logBatchInstrumentationError(
|
|
12540
|
+
"skipped invalid input file",
|
|
12541
|
+
inputData.issues[0]
|
|
12542
|
+
);
|
|
12543
|
+
}
|
|
12544
|
+
return file;
|
|
12545
|
+
}
|
|
12546
|
+
const trace = {
|
|
12547
|
+
context: {
|
|
12548
|
+
inputFileId,
|
|
12549
|
+
endpoint: inputData.endpoint,
|
|
12550
|
+
parent: exportedParent2,
|
|
12551
|
+
taskStartTime: startTime,
|
|
12552
|
+
childStartTime: startTime
|
|
12553
|
+
},
|
|
12554
|
+
inputs: inputData.inputs
|
|
12555
|
+
};
|
|
12556
|
+
pendingBatchTraces.set(inputFileId, trace);
|
|
12557
|
+
await writePendingSpans(trace);
|
|
12558
|
+
} catch (error) {
|
|
12559
|
+
logBatchInstrumentationError("could not start batch spans", error);
|
|
12560
|
+
}
|
|
12561
|
+
return file;
|
|
12090
12562
|
};
|
|
12091
|
-
function
|
|
12092
|
-
|
|
12093
|
-
|
|
12563
|
+
function validTimestamp(value) {
|
|
12564
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0;
|
|
12565
|
+
}
|
|
12566
|
+
function terminalEndTime(batch, startTime) {
|
|
12567
|
+
let timestamp;
|
|
12568
|
+
switch (batch.status) {
|
|
12569
|
+
case "completed":
|
|
12570
|
+
timestamp = batch.completed_at;
|
|
12571
|
+
break;
|
|
12572
|
+
case "failed":
|
|
12573
|
+
timestamp = batch.failed_at;
|
|
12574
|
+
break;
|
|
12575
|
+
case "expired":
|
|
12576
|
+
timestamp = batch.expired_at;
|
|
12577
|
+
break;
|
|
12578
|
+
default:
|
|
12579
|
+
timestamp = batch.cancelled_at;
|
|
12094
12580
|
}
|
|
12095
|
-
|
|
12096
|
-
|
|
12097
|
-
|
|
12098
|
-
|
|
12099
|
-
|
|
12581
|
+
return validTimestamp(timestamp) ? Math.max(timestamp, startTime) : Math.max(getCurrentUnixTimestamp(), startTime);
|
|
12582
|
+
}
|
|
12583
|
+
async function updateBatchTimestamps(batch) {
|
|
12584
|
+
const trace = pendingBatchTraces.get(batch.input_file_id);
|
|
12585
|
+
if (!trace || trace.context.endpoint !== batch.endpoint) {
|
|
12586
|
+
return;
|
|
12587
|
+
}
|
|
12588
|
+
if (validTimestamp(batch.created_at)) {
|
|
12589
|
+
trace.context.taskStartTime = batch.created_at;
|
|
12590
|
+
}
|
|
12591
|
+
trace.context.childStartTime = validTimestamp(batch.in_progress_at) ? Math.max(batch.in_progress_at, trace.context.taskStartTime) : trace.context.taskStartTime;
|
|
12592
|
+
trace.status = batch.status;
|
|
12593
|
+
if (TERMINAL_STATUSES.has(batch.status)) {
|
|
12594
|
+
trace.endTime = terminalEndTime(batch, trace.context.childStartTime);
|
|
12595
|
+
}
|
|
12596
|
+
await writePendingSpans(trace, trace.endTime);
|
|
12597
|
+
}
|
|
12598
|
+
var interceptOpenAIBatchesRetrieveTraced = (target, thisArg, args) => Promise.resolve(Reflect.apply(target, thisArg, args)).then(async (batch) => {
|
|
12599
|
+
try {
|
|
12600
|
+
await updateBatchTimestamps(batch);
|
|
12601
|
+
} catch (error) {
|
|
12602
|
+
logBatchInstrumentationError("could not update batch timestamps", error);
|
|
12603
|
+
}
|
|
12604
|
+
return batch;
|
|
12605
|
+
});
|
|
12606
|
+
function errorFromResult(result) {
|
|
12607
|
+
const error = read(result.value, "error");
|
|
12608
|
+
if (isObject(error)) {
|
|
12609
|
+
const message = read(error, "message");
|
|
12610
|
+
return new Error(
|
|
12611
|
+
typeof message === "string" ? message : "OpenAI Batch request failed"
|
|
12612
|
+
);
|
|
12613
|
+
}
|
|
12614
|
+
const response = read(result.value, "response");
|
|
12615
|
+
const statusCode = read(response, "status_code");
|
|
12616
|
+
if (result.source === "error" || typeof statusCode === "number" && (statusCode < 200 || statusCode >= 300)) {
|
|
12617
|
+
const message = read(read(read(response, "body"), "error"), "message");
|
|
12618
|
+
return new Error(
|
|
12619
|
+
typeof message === "string" ? message : "OpenAI Batch request failed"
|
|
12620
|
+
);
|
|
12621
|
+
}
|
|
12622
|
+
return void 0;
|
|
12623
|
+
}
|
|
12624
|
+
async function completeBatchResult(context, taskParent, endTime, input, result) {
|
|
12625
|
+
const child = await startBatchChild(context, taskParent, input);
|
|
12626
|
+
try {
|
|
12627
|
+
const resultError = errorFromResult(result);
|
|
12628
|
+
const responseBody = read(read(result.value, "response"), "body");
|
|
12629
|
+
if (resultError) {
|
|
12630
|
+
child.log({ error: resultError });
|
|
12631
|
+
} else if (isObject(responseBody)) {
|
|
12632
|
+
const model = read(responseBody, "model");
|
|
12633
|
+
child.log({
|
|
12634
|
+
output: context.endpoint === "/v1/chat/completions" ? read(responseBody, "choices") : processImagesInOutput(read(responseBody, "output")),
|
|
12635
|
+
...typeof model === "string" ? { metadata: { model } } : {},
|
|
12636
|
+
metrics: parseMetricsFromUsage(read(responseBody, "usage"))
|
|
12637
|
+
});
|
|
12638
|
+
} else {
|
|
12639
|
+
child.log({ error: new Error("OpenAI Batch response body is missing") });
|
|
12640
|
+
}
|
|
12641
|
+
} catch (error) {
|
|
12642
|
+
child.log({ error });
|
|
12643
|
+
} finally {
|
|
12644
|
+
child.end({ endTime });
|
|
12645
|
+
}
|
|
12646
|
+
}
|
|
12647
|
+
async function completeResultFile({
|
|
12648
|
+
context,
|
|
12649
|
+
endTime,
|
|
12650
|
+
file,
|
|
12651
|
+
inputs,
|
|
12652
|
+
issues,
|
|
12653
|
+
seen,
|
|
12654
|
+
source,
|
|
12655
|
+
taskParent
|
|
12656
|
+
}) {
|
|
12657
|
+
if (file === void 0) {
|
|
12658
|
+
return;
|
|
12659
|
+
}
|
|
12660
|
+
for await (const value of jsonlRecords(file, (issue) => issues.push(issue))) {
|
|
12661
|
+
const customId = read(value, "custom_id");
|
|
12662
|
+
if (!validCustomId(customId) || !isObject(value)) {
|
|
12663
|
+
issues.push(
|
|
12664
|
+
new Error("OpenAI Batch result is missing a valid custom_id")
|
|
12665
|
+
);
|
|
12100
12666
|
continue;
|
|
12101
12667
|
}
|
|
12102
|
-
if (
|
|
12668
|
+
if (seen.has(customId)) {
|
|
12669
|
+
issues.push(new Error("OpenAI Batch result contains a duplicate"));
|
|
12103
12670
|
continue;
|
|
12104
12671
|
}
|
|
12105
|
-
const
|
|
12106
|
-
|
|
12107
|
-
|
|
12108
|
-
|
|
12109
|
-
|
|
12110
|
-
|
|
12111
|
-
metrics[`${prefix}_${key}`] = nestedValue;
|
|
12672
|
+
const input = inputs.get(customId);
|
|
12673
|
+
if (!input) {
|
|
12674
|
+
issues.push(
|
|
12675
|
+
new Error("OpenAI Batch result does not match an input record")
|
|
12676
|
+
);
|
|
12677
|
+
continue;
|
|
12112
12678
|
}
|
|
12679
|
+
seen.add(customId);
|
|
12680
|
+
await completeBatchResult(context, taskParent, endTime, input, {
|
|
12681
|
+
value,
|
|
12682
|
+
source
|
|
12683
|
+
});
|
|
12113
12684
|
}
|
|
12114
|
-
return metrics;
|
|
12115
12685
|
}
|
|
12116
|
-
function
|
|
12117
|
-
|
|
12686
|
+
function sameInputs(first, second) {
|
|
12687
|
+
return first.size === second.size && [...first.keys()].every((customId) => second.has(customId));
|
|
12688
|
+
}
|
|
12689
|
+
async function contextForCompletion(inputFileId, inputData) {
|
|
12690
|
+
if (!inputData.endpoint || inputData.issues.length > 0) {
|
|
12118
12691
|
return void 0;
|
|
12119
12692
|
}
|
|
12120
|
-
|
|
12121
|
-
|
|
12122
|
-
|
|
12123
|
-
|
|
12693
|
+
const existing = pendingBatchTraces.get(inputFileId);
|
|
12694
|
+
if (existing) {
|
|
12695
|
+
if (existing.context.endpoint !== inputData.endpoint || !sameInputs(existing.inputs, inputData.inputs)) {
|
|
12696
|
+
return void 0;
|
|
12697
|
+
}
|
|
12698
|
+
return { ...existing, inputs: inputData.inputs };
|
|
12699
|
+
}
|
|
12700
|
+
const parent = await exportParent(getSpanParentObject());
|
|
12701
|
+
if (!parent) {
|
|
12124
12702
|
return void 0;
|
|
12125
12703
|
}
|
|
12126
|
-
const
|
|
12127
|
-
|
|
12128
|
-
|
|
12704
|
+
const startTime = getCurrentUnixTimestamp();
|
|
12705
|
+
return {
|
|
12706
|
+
context: {
|
|
12707
|
+
inputFileId,
|
|
12708
|
+
endpoint: inputData.endpoint,
|
|
12709
|
+
parent,
|
|
12710
|
+
taskStartTime: startTime,
|
|
12711
|
+
childStartTime: startTime
|
|
12712
|
+
},
|
|
12713
|
+
inputs: inputData.inputs
|
|
12714
|
+
};
|
|
12715
|
+
}
|
|
12716
|
+
async function completeBatch(args) {
|
|
12717
|
+
const inputData = await readBatchInputs(args.inputFileContent);
|
|
12718
|
+
const trace = await contextForCompletion(args.inputFileId, inputData);
|
|
12719
|
+
if (!trace) {
|
|
12720
|
+
logBatchInstrumentationError(
|
|
12721
|
+
"left batch spans pending",
|
|
12722
|
+
inputData.issues[0] ?? new Error("OpenAI Batch input does not match")
|
|
12723
|
+
);
|
|
12724
|
+
return;
|
|
12129
12725
|
}
|
|
12130
|
-
|
|
12726
|
+
const endTime = trace.endTime ?? getCurrentUnixTimestamp();
|
|
12727
|
+
const task = await startBatchSpan(trace.context);
|
|
12728
|
+
const taskParent = await task.export();
|
|
12729
|
+
const issues = [];
|
|
12730
|
+
const seen = /* @__PURE__ */ new Set();
|
|
12731
|
+
await Promise.all([
|
|
12732
|
+
completeResultFile({
|
|
12733
|
+
context: trace.context,
|
|
12734
|
+
endTime,
|
|
12735
|
+
file: args.outputFileContent,
|
|
12736
|
+
inputs: trace.inputs,
|
|
12737
|
+
issues,
|
|
12738
|
+
seen,
|
|
12739
|
+
source: "output",
|
|
12740
|
+
taskParent
|
|
12741
|
+
}),
|
|
12742
|
+
completeResultFile({
|
|
12743
|
+
context: trace.context,
|
|
12744
|
+
endTime,
|
|
12745
|
+
file: args.errorFileContent,
|
|
12746
|
+
inputs: trace.inputs,
|
|
12747
|
+
issues,
|
|
12748
|
+
seen,
|
|
12749
|
+
source: "error",
|
|
12750
|
+
taskParent
|
|
12751
|
+
})
|
|
12752
|
+
]);
|
|
12753
|
+
if (issues.length > 0) {
|
|
12754
|
+
logBatchInstrumentationError("left batch spans pending", issues[0]);
|
|
12755
|
+
return;
|
|
12756
|
+
}
|
|
12757
|
+
const missing = [...trace.inputs.values()].filter(
|
|
12758
|
+
({ customId }) => !seen.has(customId)
|
|
12759
|
+
);
|
|
12760
|
+
if (!trace.status || trace.status === "completed") {
|
|
12761
|
+
if (missing.length > 0) {
|
|
12762
|
+
logBatchInstrumentationError(
|
|
12763
|
+
"left batch spans pending",
|
|
12764
|
+
new Error("OpenAI Batch result files are incomplete")
|
|
12765
|
+
);
|
|
12766
|
+
return;
|
|
12767
|
+
}
|
|
12768
|
+
} else {
|
|
12769
|
+
for (const input of missing) {
|
|
12770
|
+
const child = await startBatchChild(trace.context, taskParent, input);
|
|
12771
|
+
child.log({ error: new Error(`OpenAI Batch ${trace.status}`) });
|
|
12772
|
+
child.end({ endTime });
|
|
12773
|
+
}
|
|
12774
|
+
task.log({ error: new Error(`OpenAI Batch ${trace.status}`) });
|
|
12775
|
+
}
|
|
12776
|
+
task.end({ endTime });
|
|
12777
|
+
pendingBatchTraces.delete(args.inputFileId);
|
|
12131
12778
|
}
|
|
12779
|
+
var interceptOpenAIBatchTraceComplete = (target, thisArg, args) => Promise.resolve(Reflect.apply(target, thisArg, args)).then(async (result) => {
|
|
12780
|
+
try {
|
|
12781
|
+
await completeBatch(args[0]);
|
|
12782
|
+
} catch (error) {
|
|
12783
|
+
logBatchInstrumentationError("could not complete batch", error);
|
|
12784
|
+
}
|
|
12785
|
+
return result;
|
|
12786
|
+
});
|
|
12132
12787
|
|
|
12133
12788
|
// src/instrumentation/plugins/openai-plugin.ts
|
|
12134
12789
|
var OpenAIPlugin = class extends BasePlugin {
|
|
@@ -12136,17 +12791,22 @@ var OpenAIPlugin = class extends BasePlugin {
|
|
|
12136
12791
|
super();
|
|
12137
12792
|
}
|
|
12138
12793
|
onEnable() {
|
|
12794
|
+
this.unsubscribers.push(
|
|
12795
|
+
openAIChannels.filesCreateTraced.intercept(
|
|
12796
|
+
interceptOpenAIFilesCreateTraced
|
|
12797
|
+
),
|
|
12798
|
+
openAIChannels.batchesRetrieveTraced.intercept(
|
|
12799
|
+
interceptOpenAIBatchesRetrieveTraced
|
|
12800
|
+
),
|
|
12801
|
+
openAIChannels.batchesCompleteTrace.intercept(
|
|
12802
|
+
interceptOpenAIBatchTraceComplete
|
|
12803
|
+
)
|
|
12804
|
+
);
|
|
12139
12805
|
this.unsubscribers.push(
|
|
12140
12806
|
traceStreamingChannel(openAIChannels.chatCompletionsCreate, {
|
|
12141
12807
|
name: "Chat Completion",
|
|
12142
12808
|
type: "llm" /* LLM */,
|
|
12143
|
-
extractInput: ([params]) =>
|
|
12144
|
-
const { messages, ...metadata } = params;
|
|
12145
|
-
return {
|
|
12146
|
-
input: processInputAttachments(messages),
|
|
12147
|
-
metadata: { ...metadata, provider: "openai" }
|
|
12148
|
-
};
|
|
12149
|
-
},
|
|
12809
|
+
extractInput: ([params]) => extractOpenAIChatInput(params),
|
|
12150
12810
|
extractOutput: (result) => {
|
|
12151
12811
|
return result?.choices;
|
|
12152
12812
|
},
|
|
@@ -12192,13 +12852,7 @@ var OpenAIPlugin = class extends BasePlugin {
|
|
|
12192
12852
|
traceStreamingChannel(openAIChannels.betaChatCompletionsParse, {
|
|
12193
12853
|
name: "Chat Completion",
|
|
12194
12854
|
type: "llm" /* LLM */,
|
|
12195
|
-
extractInput: ([params]) =>
|
|
12196
|
-
const { messages, ...metadata } = params;
|
|
12197
|
-
return {
|
|
12198
|
-
input: processInputAttachments(messages),
|
|
12199
|
-
metadata: { ...metadata, provider: "openai" }
|
|
12200
|
-
};
|
|
12201
|
-
},
|
|
12855
|
+
extractInput: ([params]) => extractOpenAIChatInput(params),
|
|
12202
12856
|
extractOutput: (result) => {
|
|
12203
12857
|
return result?.choices;
|
|
12204
12858
|
},
|
|
@@ -12220,13 +12874,7 @@ var OpenAIPlugin = class extends BasePlugin {
|
|
|
12220
12874
|
traceSyncStreamChannel(openAIChannels.betaChatCompletionsStream, {
|
|
12221
12875
|
name: "Chat Completion",
|
|
12222
12876
|
type: "llm" /* LLM */,
|
|
12223
|
-
extractInput: ([params]) =>
|
|
12224
|
-
const { messages, ...metadata } = params;
|
|
12225
|
-
return {
|
|
12226
|
-
input: processInputAttachments(messages),
|
|
12227
|
-
metadata: { ...metadata, provider: "openai" }
|
|
12228
|
-
};
|
|
12229
|
-
}
|
|
12877
|
+
extractInput: ([params]) => extractOpenAIChatInput(params)
|
|
12230
12878
|
})
|
|
12231
12879
|
);
|
|
12232
12880
|
this.unsubscribers.push(
|
|
@@ -12256,23 +12904,11 @@ var OpenAIPlugin = class extends BasePlugin {
|
|
|
12256
12904
|
traceStreamingChannel(openAIChannels.responsesCreate, {
|
|
12257
12905
|
name: "openai.responses.create",
|
|
12258
12906
|
type: "llm" /* LLM */,
|
|
12259
|
-
extractInput: ([params]) =>
|
|
12260
|
-
const { input, ...metadata } = params;
|
|
12261
|
-
return {
|
|
12262
|
-
input: processInputAttachments(input),
|
|
12263
|
-
metadata: { ...metadata, provider: "openai" }
|
|
12264
|
-
};
|
|
12265
|
-
},
|
|
12907
|
+
extractInput: ([params]) => extractOpenAIResponsesInput(params),
|
|
12266
12908
|
extractOutput: (result) => {
|
|
12267
12909
|
return processImagesInOutput(result?.output);
|
|
12268
12910
|
},
|
|
12269
|
-
extractMetadata: (result) =>
|
|
12270
|
-
if (!result) {
|
|
12271
|
-
return void 0;
|
|
12272
|
-
}
|
|
12273
|
-
const { output: _output, usage: _usage, ...metadata } = result;
|
|
12274
|
-
return Object.keys(metadata).length > 0 ? metadata : void 0;
|
|
12275
|
-
},
|
|
12911
|
+
extractMetadata: (result) => extractOpenAIResponsesMetadata(result),
|
|
12276
12912
|
extractMetrics: (result, startTime, endEvent) => {
|
|
12277
12913
|
const metrics = withCachedMetric(
|
|
12278
12914
|
parseMetricsFromUsage(result?.usage),
|
|
@@ -12291,13 +12927,7 @@ var OpenAIPlugin = class extends BasePlugin {
|
|
|
12291
12927
|
traceSyncStreamChannel(openAIChannels.responsesStream, {
|
|
12292
12928
|
name: "openai.responses.create",
|
|
12293
12929
|
type: "llm" /* LLM */,
|
|
12294
|
-
extractInput: ([params]) =>
|
|
12295
|
-
const { input, ...metadata } = params;
|
|
12296
|
-
return {
|
|
12297
|
-
input: processInputAttachments(input),
|
|
12298
|
-
metadata: { ...metadata, provider: "openai" }
|
|
12299
|
-
};
|
|
12300
|
-
},
|
|
12930
|
+
extractInput: ([params]) => extractOpenAIResponsesInput(params),
|
|
12301
12931
|
extractFromEvent: (event) => {
|
|
12302
12932
|
if (event.type !== "response.completed" || !event.response) {
|
|
12303
12933
|
return {};
|
|
@@ -12320,23 +12950,11 @@ var OpenAIPlugin = class extends BasePlugin {
|
|
|
12320
12950
|
traceStreamingChannel(openAIChannels.responsesParse, {
|
|
12321
12951
|
name: "openai.responses.parse",
|
|
12322
12952
|
type: "llm" /* LLM */,
|
|
12323
|
-
extractInput: ([params]) =>
|
|
12324
|
-
const { input, ...metadata } = params;
|
|
12325
|
-
return {
|
|
12326
|
-
input: processInputAttachments(input),
|
|
12327
|
-
metadata: { ...metadata, provider: "openai" }
|
|
12328
|
-
};
|
|
12329
|
-
},
|
|
12953
|
+
extractInput: ([params]) => extractOpenAIResponsesInput(params),
|
|
12330
12954
|
extractOutput: (result) => {
|
|
12331
12955
|
return processImagesInOutput(result?.output);
|
|
12332
12956
|
},
|
|
12333
|
-
extractMetadata: (result) =>
|
|
12334
|
-
if (!result) {
|
|
12335
|
-
return void 0;
|
|
12336
|
-
}
|
|
12337
|
-
const { output: _output, usage: _usage, ...metadata } = result;
|
|
12338
|
-
return Object.keys(metadata).length > 0 ? metadata : void 0;
|
|
12339
|
-
},
|
|
12957
|
+
extractMetadata: (result) => extractOpenAIResponsesMetadata(result),
|
|
12340
12958
|
extractMetrics: (result, startTime, endEvent) => {
|
|
12341
12959
|
const metrics = withCachedMetric(
|
|
12342
12960
|
parseMetricsFromUsage(result?.usage),
|
|
@@ -12355,23 +12973,11 @@ var OpenAIPlugin = class extends BasePlugin {
|
|
|
12355
12973
|
traceAsyncChannel(openAIChannels.responsesCompact, {
|
|
12356
12974
|
name: "openai.responses.compact",
|
|
12357
12975
|
type: "llm" /* LLM */,
|
|
12358
|
-
extractInput: ([params]) =>
|
|
12359
|
-
const { input, ...metadata } = params;
|
|
12360
|
-
return {
|
|
12361
|
-
input: processInputAttachments(input),
|
|
12362
|
-
metadata: { ...metadata, provider: "openai" }
|
|
12363
|
-
};
|
|
12364
|
-
},
|
|
12976
|
+
extractInput: ([params]) => extractOpenAIResponsesInput(params),
|
|
12365
12977
|
extractOutput: (result) => {
|
|
12366
12978
|
return processImagesInOutput(result?.output);
|
|
12367
12979
|
},
|
|
12368
|
-
extractMetadata: (result) =>
|
|
12369
|
-
if (!result) {
|
|
12370
|
-
return void 0;
|
|
12371
|
-
}
|
|
12372
|
-
const { output: _output, usage: _usage, ...metadata } = result;
|
|
12373
|
-
return Object.keys(metadata).length > 0 ? metadata : void 0;
|
|
12374
|
-
},
|
|
12980
|
+
extractMetadata: (result) => extractOpenAIResponsesMetadata(result),
|
|
12375
12981
|
extractMetrics: (result, startTime, endEvent) => {
|
|
12376
12982
|
const metrics = withCachedMetric(
|
|
12377
12983
|
parseMetricsFromUsage(result?.usage),
|
|
@@ -12427,35 +13033,6 @@ function withCachedMetric(metrics, result, endEvent) {
|
|
|
12427
13033
|
cached
|
|
12428
13034
|
};
|
|
12429
13035
|
}
|
|
12430
|
-
function processImagesInOutput(output) {
|
|
12431
|
-
if (Array.isArray(output)) {
|
|
12432
|
-
return output.map(processImagesInOutput);
|
|
12433
|
-
}
|
|
12434
|
-
if (isObject(output)) {
|
|
12435
|
-
if (output.type === "image_generation_call" && output.result && typeof output.result === "string") {
|
|
12436
|
-
const fileExtension = output.output_format || "png";
|
|
12437
|
-
const contentType = `image/${fileExtension}`;
|
|
12438
|
-
const baseFilename = output.revised_prompt && typeof output.revised_prompt === "string" ? output.revised_prompt.slice(0, 50).replace(/[^a-zA-Z0-9]/g, "_") : "generated_image";
|
|
12439
|
-
const filename = `${baseFilename}.${fileExtension}`;
|
|
12440
|
-
const binaryString = atob(output.result);
|
|
12441
|
-
const bytes = new Uint8Array(binaryString.length);
|
|
12442
|
-
for (let i = 0; i < binaryString.length; i++) {
|
|
12443
|
-
bytes[i] = binaryString.charCodeAt(i);
|
|
12444
|
-
}
|
|
12445
|
-
const blob = new Blob([bytes], { type: contentType });
|
|
12446
|
-
const attachment = new Attachment({
|
|
12447
|
-
data: blob,
|
|
12448
|
-
filename,
|
|
12449
|
-
contentType
|
|
12450
|
-
});
|
|
12451
|
-
return {
|
|
12452
|
-
...output,
|
|
12453
|
-
result: attachment
|
|
12454
|
-
};
|
|
12455
|
-
}
|
|
12456
|
-
}
|
|
12457
|
-
return output;
|
|
12458
|
-
}
|
|
12459
13036
|
function mergeLogprobTokens(existing, incoming) {
|
|
12460
13037
|
if (incoming === void 0) {
|
|
12461
13038
|
return existing;
|
|
@@ -32475,7 +33052,7 @@ function extractSession(event) {
|
|
|
32475
33052
|
return isObject(candidate) && typeof candidate.prompt === "function" ? candidate : void 0;
|
|
32476
33053
|
}
|
|
32477
33054
|
function isPiAgent(value) {
|
|
32478
|
-
return isObject(value) && typeof value.streamFn === "function" && typeof value.subscribe === "function";
|
|
33055
|
+
return isObject(value) && (typeof value.streamFunction === "function" || typeof value.streamFn === "function") && typeof value.subscribe === "function";
|
|
32479
33056
|
}
|
|
32480
33057
|
function promptContextStore() {
|
|
32481
33058
|
piPromptContextStore ??= isomorph_default.newAsyncLocalStorage();
|
|
@@ -32485,17 +33062,21 @@ function currentPiPromptState() {
|
|
|
32485
33062
|
return promptContextStore().getStore();
|
|
32486
33063
|
}
|
|
32487
33064
|
function installPiAgentInstrumentation(agent) {
|
|
33065
|
+
const property = typeof agent.streamFunction === "function" ? "streamFunction" : "streamFn";
|
|
33066
|
+
const streamFunction = agent[property];
|
|
32488
33067
|
const existing = piAgentPatchStates.get(agent);
|
|
32489
|
-
if (!existing ||
|
|
33068
|
+
if (streamFunction && (!existing || existing.property !== property || streamFunction !== existing.wrappedStreamFunction)) {
|
|
32490
33069
|
const patchState = {
|
|
32491
|
-
|
|
32492
|
-
|
|
33070
|
+
originalStreamFunction: streamFunction,
|
|
33071
|
+
property,
|
|
33072
|
+
wrappedStreamFunction: streamFunction
|
|
32493
33073
|
};
|
|
32494
|
-
patchState.
|
|
33074
|
+
patchState.wrappedStreamFunction = makeInstrumentedStreamFunction(
|
|
32495
33075
|
agent,
|
|
32496
|
-
patchState.
|
|
33076
|
+
patchState.originalStreamFunction,
|
|
33077
|
+
property
|
|
32497
33078
|
);
|
|
32498
|
-
agent
|
|
33079
|
+
agent[property] = patchState.wrappedStreamFunction;
|
|
32499
33080
|
piAgentPatchStates.set(agent, patchState);
|
|
32500
33081
|
}
|
|
32501
33082
|
if (piAgentEventSubscriptions.has(agent)) {
|
|
@@ -32520,15 +33101,21 @@ function installPiAgentInstrumentation(agent) {
|
|
|
32520
33101
|
logInstrumentationError4("Pi Coding Agent event subscription", error);
|
|
32521
33102
|
}
|
|
32522
33103
|
}
|
|
32523
|
-
function
|
|
32524
|
-
return async function
|
|
32525
|
-
const invokeOriginal = () => Reflect.apply(
|
|
33104
|
+
function makeInstrumentedStreamFunction(agent, originalStreamFunction, property) {
|
|
33105
|
+
return async function instrumentedPiStreamFunction(model, context, options) {
|
|
33106
|
+
const invokeOriginal = () => Reflect.apply(originalStreamFunction, this, [model, context, options]);
|
|
32526
33107
|
const state = currentPiPromptState();
|
|
32527
33108
|
if (!state || state.agent !== agent || state.finalized) {
|
|
32528
33109
|
return invokeOriginal();
|
|
32529
33110
|
}
|
|
32530
33111
|
wrapPiToolExecutors(context.tools);
|
|
32531
|
-
const llmState = await startPiLlmSpan(
|
|
33112
|
+
const llmState = await startPiLlmSpan(
|
|
33113
|
+
state,
|
|
33114
|
+
model,
|
|
33115
|
+
context,
|
|
33116
|
+
property,
|
|
33117
|
+
options
|
|
33118
|
+
);
|
|
32532
33119
|
try {
|
|
32533
33120
|
const stream = await runWithAutoInstrumentationSuppressed(invokeOriginal);
|
|
32534
33121
|
return patchAssistantMessageStream(stream, state, llmState);
|
|
@@ -32565,12 +33152,12 @@ function wrapPiToolExecutors(tools) {
|
|
|
32565
33152
|
}
|
|
32566
33153
|
}
|
|
32567
33154
|
}
|
|
32568
|
-
async function startPiLlmSpan(state, model, context, options) {
|
|
33155
|
+
async function startPiLlmSpan(state, model, context, property, options) {
|
|
32569
33156
|
const metadata = {
|
|
32570
33157
|
...extractModelMetadata2(model),
|
|
32571
33158
|
...extractStreamOptionsMetadata(options),
|
|
32572
33159
|
...extractToolMetadata(context.tools),
|
|
32573
|
-
"pi_coding_agent.operation":
|
|
33160
|
+
"pi_coding_agent.operation": `agent.${property}`
|
|
32574
33161
|
};
|
|
32575
33162
|
const span = startSpan(
|
|
32576
33163
|
withSpanInstrumentationName(
|
|
@@ -36881,7 +37468,15 @@ var MAX_EVE_PROVIDER_CACHE_ENTRIES = 1e4;
|
|
|
36881
37468
|
function createEveInstrumentationProvider(options = {}) {
|
|
36882
37469
|
const bridge = new EveProviderBridge(options.metadata);
|
|
36883
37470
|
return {
|
|
37471
|
+
// Eve versions before tracePolicy use capture to decide whether provider
|
|
37472
|
+
// events include content. Newer versions prefer tracePolicy when both are
|
|
37473
|
+
// present, so keep the deprecated field for backwards compatibility.
|
|
36884
37474
|
capture: "content",
|
|
37475
|
+
tracePolicy: () => ({
|
|
37476
|
+
emit: true,
|
|
37477
|
+
recordInputs: true,
|
|
37478
|
+
recordOutputs: true
|
|
37479
|
+
}),
|
|
36885
37480
|
events: {
|
|
36886
37481
|
"action.completed": (event, context) => bridge.handleActionTerminal(event, context),
|
|
36887
37482
|
"action.failed": (event, context) => bridge.handleActionTerminal(event, context),
|