braintrust 3.1.0 → 3.2.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/README.md +73 -0
- package/dev/dist/index.d.mts +187 -903
- package/dev/dist/index.d.ts +187 -903
- package/dev/dist/index.js +2942 -323
- package/dev/dist/index.mjs +2704 -85
- package/dist/auto-instrumentations/bundler/esbuild.cjs +401 -0
- package/dist/auto-instrumentations/bundler/esbuild.d.mts +8 -0
- package/dist/auto-instrumentations/bundler/esbuild.d.ts +8 -0
- package/dist/auto-instrumentations/bundler/esbuild.mjs +10 -0
- package/dist/auto-instrumentations/bundler/rollup.cjs +401 -0
- package/dist/auto-instrumentations/bundler/rollup.d.mts +8 -0
- package/dist/auto-instrumentations/bundler/rollup.d.ts +8 -0
- package/dist/auto-instrumentations/bundler/rollup.mjs +10 -0
- package/dist/auto-instrumentations/bundler/vite.cjs +401 -0
- package/dist/auto-instrumentations/bundler/vite.d.mts +8 -0
- package/dist/auto-instrumentations/bundler/vite.d.ts +8 -0
- package/dist/auto-instrumentations/bundler/vite.mjs +10 -0
- package/dist/auto-instrumentations/bundler/webpack.cjs +401 -0
- package/dist/auto-instrumentations/bundler/webpack.d.mts +8 -0
- package/dist/auto-instrumentations/bundler/webpack.d.ts +8 -0
- package/dist/auto-instrumentations/bundler/webpack.mjs +10 -0
- package/dist/auto-instrumentations/chunk-KVX7OFPD.mjs +288 -0
- package/dist/auto-instrumentations/chunk-OLOPGWTJ.mjs +89 -0
- package/dist/auto-instrumentations/chunk-XDBPUTVE.mjs +22 -0
- package/dist/auto-instrumentations/chunk-ZEC7BCL4.mjs +22 -0
- package/dist/auto-instrumentations/hook.mjs +378 -0
- package/dist/auto-instrumentations/index.cjs +318 -0
- package/dist/auto-instrumentations/index.d.mts +69 -0
- package/dist/auto-instrumentations/index.d.ts +69 -0
- package/dist/auto-instrumentations/index.mjs +14 -0
- package/dist/auto-instrumentations/loader/cjs-patch.cjs +116 -0
- package/dist/auto-instrumentations/loader/cjs-patch.d.mts +28 -0
- package/dist/auto-instrumentations/loader/cjs-patch.d.ts +28 -0
- package/dist/auto-instrumentations/loader/cjs-patch.mjs +66 -0
- package/dist/auto-instrumentations/loader/esm-hook.mjs +72 -0
- package/dist/auto-instrumentations/loader/get-package-version.cjs +46 -0
- package/dist/auto-instrumentations/loader/get-package-version.d.mts +7 -0
- package/dist/auto-instrumentations/loader/get-package-version.d.ts +7 -0
- package/dist/auto-instrumentations/loader/get-package-version.mjs +6 -0
- package/dist/auto-instrumentations/plugin-Df3qKIl2.d.mts +22 -0
- package/dist/auto-instrumentations/plugin-Df3qKIl2.d.ts +22 -0
- package/dist/browser.d.mts +484 -980
- package/dist/browser.d.ts +484 -980
- package/dist/browser.js +3437 -386
- package/dist/browser.mjs +3437 -386
- package/dist/cli.js +2890 -228
- package/dist/edge-light.d.mts +1 -1
- package/dist/edge-light.d.ts +1 -1
- package/dist/edge-light.js +3134 -86
- package/dist/edge-light.mjs +3134 -86
- package/dist/index.d.mts +484 -980
- package/dist/index.d.ts +484 -980
- package/dist/index.js +3754 -703
- package/dist/index.mjs +3615 -564
- package/dist/instrumentation/index.d.mts +323 -0
- package/dist/instrumentation/index.d.ts +323 -0
- package/dist/instrumentation/index.js +9942 -0
- package/dist/instrumentation/index.mjs +9902 -0
- package/dist/workerd.d.mts +1 -1
- package/dist/workerd.d.ts +1 -1
- package/dist/workerd.js +3134 -86
- package/dist/workerd.mjs +3134 -86
- package/package.json +46 -7
package/dev/dist/index.mjs
CHANGED
|
@@ -47,7 +47,7 @@ async function currentRepo() {
|
|
|
47
47
|
} else {
|
|
48
48
|
return null;
|
|
49
49
|
}
|
|
50
|
-
} catch
|
|
50
|
+
} catch {
|
|
51
51
|
return null;
|
|
52
52
|
}
|
|
53
53
|
}
|
|
@@ -103,7 +103,7 @@ async function getBaseBranchAncestor(remote = void 0) {
|
|
|
103
103
|
`${remoteName}/${baseBranch}`
|
|
104
104
|
]);
|
|
105
105
|
return ancestor.trim();
|
|
106
|
-
} catch
|
|
106
|
+
} catch {
|
|
107
107
|
return void 0;
|
|
108
108
|
}
|
|
109
109
|
}
|
|
@@ -130,7 +130,7 @@ async function getPastNAncestors(n = 1e3, remote = void 0) {
|
|
|
130
130
|
async function attempt(fn) {
|
|
131
131
|
try {
|
|
132
132
|
return await fn();
|
|
133
|
-
} catch
|
|
133
|
+
} catch {
|
|
134
134
|
return void 0;
|
|
135
135
|
}
|
|
136
136
|
}
|
|
@@ -1739,6 +1739,7 @@ var ApiKey = z6.object({
|
|
|
1739
1739
|
var TriggeredFunctionState = z6.object({
|
|
1740
1740
|
triggered_xact_id: z6.string(),
|
|
1741
1741
|
completed_xact_id: z6.union([z6.string(), z6.null()]).optional(),
|
|
1742
|
+
idempotency_key: z6.union([z6.string(), z6.null()]).optional(),
|
|
1742
1743
|
attempts: z6.number().int().gte(0).optional().default(0),
|
|
1743
1744
|
scope: z6.union([
|
|
1744
1745
|
z6.object({ type: z6.literal("span") }),
|
|
@@ -1771,7 +1772,8 @@ var AsyncScoringControl = z6.union([
|
|
|
1771
1772
|
scope: z6.union([
|
|
1772
1773
|
z6.object({ type: z6.literal("span") }),
|
|
1773
1774
|
z6.object({ type: z6.literal("trace") })
|
|
1774
|
-
])
|
|
1775
|
+
]),
|
|
1776
|
+
idempotency_key: z6.string().optional()
|
|
1775
1777
|
})
|
|
1776
1778
|
).min(1)
|
|
1777
1779
|
}),
|
|
@@ -1816,7 +1818,8 @@ var FunctionTypeEnum = z6.enum([
|
|
|
1816
1818
|
"facet",
|
|
1817
1819
|
"classifier",
|
|
1818
1820
|
"tag",
|
|
1819
|
-
"parameters"
|
|
1821
|
+
"parameters",
|
|
1822
|
+
"sandbox"
|
|
1820
1823
|
]);
|
|
1821
1824
|
var NullableSavedFunctionId = z6.union([
|
|
1822
1825
|
z6.object({
|
|
@@ -1831,66 +1834,14 @@ var NullableSavedFunctionId = z6.union([
|
|
|
1831
1834
|
}),
|
|
1832
1835
|
z6.null()
|
|
1833
1836
|
]);
|
|
1834
|
-
var TopicMapReport = z6.object({
|
|
1835
|
-
version: z6.literal(1),
|
|
1836
|
-
created_at: z6.string().optional(),
|
|
1837
|
-
settings: z6.object({
|
|
1838
|
-
algorithm: z6.enum(["hdbscan", "kmeans", "hierarchical"]),
|
|
1839
|
-
dimension_reduction: z6.enum(["umap", "pca", "none"]),
|
|
1840
|
-
vector_field: z6.string(),
|
|
1841
|
-
embedding_model: z6.string(),
|
|
1842
|
-
n_clusters: z6.union([z6.number(), z6.null()]).optional(),
|
|
1843
|
-
umap_dimensions: z6.union([z6.number(), z6.null()]).optional(),
|
|
1844
|
-
min_cluster_size: z6.union([z6.number(), z6.null()]).optional(),
|
|
1845
|
-
min_samples: z6.union([z6.number(), z6.null()]).optional()
|
|
1846
|
-
}),
|
|
1847
|
-
query_settings: z6.object({
|
|
1848
|
-
hierarchy_threshold: z6.union([z6.number(), z6.null()]),
|
|
1849
|
-
auto_naming: z6.boolean(),
|
|
1850
|
-
skip_cache: z6.boolean(),
|
|
1851
|
-
viz_mode: z6.enum(["bar", "scatter"]),
|
|
1852
|
-
naming_model: z6.string()
|
|
1853
|
-
}).partial(),
|
|
1854
|
-
clusters: z6.array(
|
|
1855
|
-
z6.object({
|
|
1856
|
-
cluster_id: z6.number(),
|
|
1857
|
-
parent_cluster_id: z6.union([z6.number(), z6.null()]).optional(),
|
|
1858
|
-
topic_id: z6.string(),
|
|
1859
|
-
count: z6.number(),
|
|
1860
|
-
sample_texts: z6.array(z6.string()),
|
|
1861
|
-
samples: z6.array(
|
|
1862
|
-
z6.object({
|
|
1863
|
-
id: z6.string(),
|
|
1864
|
-
text: z6.string(),
|
|
1865
|
-
root_span_id: z6.string(),
|
|
1866
|
-
span_id: z6.string()
|
|
1867
|
-
})
|
|
1868
|
-
),
|
|
1869
|
-
name: z6.string().optional(),
|
|
1870
|
-
description: z6.string().optional(),
|
|
1871
|
-
keywords: z6.array(z6.string()).optional(),
|
|
1872
|
-
centroid: z6.array(z6.number()).optional(),
|
|
1873
|
-
parent_id: z6.union([z6.number(), z6.null()]).optional(),
|
|
1874
|
-
is_leaf: z6.boolean().optional(),
|
|
1875
|
-
depth: z6.number().optional()
|
|
1876
|
-
})
|
|
1877
|
-
),
|
|
1878
|
-
embedding_points: z6.array(
|
|
1879
|
-
z6.object({
|
|
1880
|
-
x: z6.number(),
|
|
1881
|
-
y: z6.number(),
|
|
1882
|
-
cluster: z6.number(),
|
|
1883
|
-
text: z6.string().optional()
|
|
1884
|
-
})
|
|
1885
|
-
).optional()
|
|
1886
|
-
});
|
|
1887
1837
|
var TopicMapData = z6.object({
|
|
1888
1838
|
type: z6.literal("topic_map"),
|
|
1889
1839
|
source_facet: z6.string(),
|
|
1890
1840
|
embedding_model: z6.string(),
|
|
1891
|
-
bundle_key: z6.string(),
|
|
1892
|
-
|
|
1893
|
-
|
|
1841
|
+
bundle_key: z6.string().optional(),
|
|
1842
|
+
report_key: z6.string().optional(),
|
|
1843
|
+
topic_names: z6.record(z6.string()).optional(),
|
|
1844
|
+
distance_threshold: z6.number().optional()
|
|
1894
1845
|
});
|
|
1895
1846
|
var BatchedFacetData = z6.object({
|
|
1896
1847
|
type: z6.literal("batched_facet"),
|
|
@@ -1905,11 +1856,13 @@ var BatchedFacetData = z6.object({
|
|
|
1905
1856
|
})
|
|
1906
1857
|
),
|
|
1907
1858
|
topic_maps: z6.record(
|
|
1908
|
-
z6.
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1859
|
+
z6.array(
|
|
1860
|
+
z6.object({
|
|
1861
|
+
function_name: z6.string(),
|
|
1862
|
+
topic_map_id: z6.string().optional(),
|
|
1863
|
+
topic_map_data: TopicMapData
|
|
1864
|
+
})
|
|
1865
|
+
)
|
|
1913
1866
|
).optional()
|
|
1914
1867
|
});
|
|
1915
1868
|
var BraintrustModelParams = z6.object({
|
|
@@ -2088,9 +2041,20 @@ var CodeBundle = z6.object({
|
|
|
2088
2041
|
z6.object({ type: z6.literal("scorer"), index: z6.number().int().gte(0) })
|
|
2089
2042
|
])
|
|
2090
2043
|
}),
|
|
2091
|
-
z6.object({ type: z6.literal("function"), index: z6.number().int().gte(0) })
|
|
2044
|
+
z6.object({ type: z6.literal("function"), index: z6.number().int().gte(0) }),
|
|
2045
|
+
z6.object({
|
|
2046
|
+
type: z6.literal("sandbox"),
|
|
2047
|
+
sandbox_spec: z6.union([
|
|
2048
|
+
z6.object({ provider: z6.literal("modal"), snapshot_ref: z6.string() }),
|
|
2049
|
+
z6.object({ provider: z6.literal("lambda") })
|
|
2050
|
+
]),
|
|
2051
|
+
entrypoints: z6.array(z6.string()).optional(),
|
|
2052
|
+
eval_name: z6.string(),
|
|
2053
|
+
parameters: z6.object({}).partial().passthrough().optional(),
|
|
2054
|
+
evaluator_definition: z6.unknown().optional()
|
|
2055
|
+
})
|
|
2092
2056
|
]),
|
|
2093
|
-
bundle_id: z6.string(),
|
|
2057
|
+
bundle_id: z6.union([z6.string(), z6.null()]).optional(),
|
|
2094
2058
|
preview: z6.union([z6.string(), z6.null()]).optional()
|
|
2095
2059
|
});
|
|
2096
2060
|
var Dataset = z6.object({
|
|
@@ -2192,7 +2156,7 @@ var EnvVar = z6.object({
|
|
|
2192
2156
|
used: z6.union([z6.string(), z6.null()]).optional(),
|
|
2193
2157
|
metadata: z6.union([z6.object({}).partial().passthrough(), z6.null()]).optional(),
|
|
2194
2158
|
secret_type: z6.union([z6.string(), z6.null()]).optional(),
|
|
2195
|
-
secret_category: z6.enum(["env_var", "ai_provider"]).optional().default("env_var")
|
|
2159
|
+
secret_category: z6.enum(["env_var", "ai_provider", "sandbox_provider"]).optional().default("env_var")
|
|
2196
2160
|
});
|
|
2197
2161
|
var EvalStatusPageTheme = z6.enum(["light", "dark"]);
|
|
2198
2162
|
var EvalStatusPageConfig = z6.object({
|
|
@@ -2494,7 +2458,8 @@ var FunctionTypeEnumNullish = z6.union([
|
|
|
2494
2458
|
"facet",
|
|
2495
2459
|
"classifier",
|
|
2496
2460
|
"tag",
|
|
2497
|
-
"parameters"
|
|
2461
|
+
"parameters",
|
|
2462
|
+
"sandbox"
|
|
2498
2463
|
]),
|
|
2499
2464
|
z6.null()
|
|
2500
2465
|
]);
|
|
@@ -2727,7 +2692,8 @@ var FunctionObjectType = z6.enum([
|
|
|
2727
2692
|
"preprocessor",
|
|
2728
2693
|
"facet",
|
|
2729
2694
|
"classifier",
|
|
2730
|
-
"parameters"
|
|
2695
|
+
"parameters",
|
|
2696
|
+
"sandbox"
|
|
2731
2697
|
]);
|
|
2732
2698
|
var FunctionOutputType = z6.enum([
|
|
2733
2699
|
"completion",
|
|
@@ -2863,6 +2829,7 @@ var Organization = z6.object({
|
|
|
2863
2829
|
name: z6.string(),
|
|
2864
2830
|
api_url: z6.union([z6.string(), z6.null()]).optional(),
|
|
2865
2831
|
is_universal_api: z6.union([z6.boolean(), z6.null()]).optional(),
|
|
2832
|
+
is_dataplane_private: z6.union([z6.boolean(), z6.null()]).optional(),
|
|
2866
2833
|
proxy_url: z6.union([z6.string(), z6.null()]).optional(),
|
|
2867
2834
|
realtime_url: z6.union([z6.string(), z6.null()]).optional(),
|
|
2868
2835
|
created: z6.union([z6.string(), z6.null()]).optional(),
|
|
@@ -2887,7 +2854,7 @@ var ProjectSettings = z6.union([
|
|
|
2887
2854
|
z6.array(
|
|
2888
2855
|
z6.object({
|
|
2889
2856
|
url: z6.string(),
|
|
2890
|
-
name: z6.string(),
|
|
2857
|
+
name: z6.union([z6.string(), z6.null()]).optional(),
|
|
2891
2858
|
description: z6.union([z6.string(), z6.null()]).optional()
|
|
2892
2859
|
})
|
|
2893
2860
|
),
|
|
@@ -2913,6 +2880,25 @@ var RetentionObjectType = z6.enum([
|
|
|
2913
2880
|
"experiment",
|
|
2914
2881
|
"dataset"
|
|
2915
2882
|
]);
|
|
2883
|
+
var TopicMapFunctionAutomation = z6.object({
|
|
2884
|
+
function: SavedFunctionId.and(z6.unknown()),
|
|
2885
|
+
btql_filter: z6.union([z6.string(), z6.null()]).optional()
|
|
2886
|
+
});
|
|
2887
|
+
var TopicAutomationDataScope = z6.union([
|
|
2888
|
+
z6.object({ type: z6.literal("project_logs") }),
|
|
2889
|
+
z6.object({ type: z6.literal("project_experiments") }),
|
|
2890
|
+
z6.object({ type: z6.literal("experiment"), experiment_id: z6.string() }),
|
|
2891
|
+
z6.null()
|
|
2892
|
+
]);
|
|
2893
|
+
var TopicAutomationConfig = z6.object({
|
|
2894
|
+
event_type: z6.literal("topic"),
|
|
2895
|
+
sampling_rate: z6.number().gte(0).lte(1),
|
|
2896
|
+
facet_functions: z6.array(SavedFunctionId),
|
|
2897
|
+
topic_map_functions: z6.array(TopicMapFunctionAutomation),
|
|
2898
|
+
scope: z6.union([SpanScope, TraceScope, GroupScope, z6.null()]).optional(),
|
|
2899
|
+
data_scope: TopicAutomationDataScope.optional(),
|
|
2900
|
+
btql_filter: z6.union([z6.string(), z6.null()]).optional()
|
|
2901
|
+
});
|
|
2916
2902
|
var ProjectAutomation = z6.object({
|
|
2917
2903
|
id: z6.string().uuid(),
|
|
2918
2904
|
project_id: z6.string().uuid(),
|
|
@@ -2969,7 +2955,8 @@ var ProjectAutomation = z6.object({
|
|
|
2969
2955
|
message_template: z6.string().optional()
|
|
2970
2956
|
})
|
|
2971
2957
|
])
|
|
2972
|
-
})
|
|
2958
|
+
}),
|
|
2959
|
+
TopicAutomationConfig
|
|
2973
2960
|
])
|
|
2974
2961
|
});
|
|
2975
2962
|
var ProjectLogsEvent = z6.object({
|
|
@@ -3159,6 +3146,8 @@ var RunEval = z6.object({
|
|
|
3159
3146
|
}),
|
|
3160
3147
|
z6.object({ data: z6.array(z6.unknown()) })
|
|
3161
3148
|
]),
|
|
3149
|
+
name: z6.string().optional(),
|
|
3150
|
+
parameters: z6.object({}).partial().passthrough().optional(),
|
|
3162
3151
|
task: FunctionId.and(z6.unknown()),
|
|
3163
3152
|
scores: z6.array(FunctionId),
|
|
3164
3153
|
experiment_name: z6.string().optional(),
|
|
@@ -4838,7 +4827,7 @@ var HTTPConnection = class _HTTPConnection {
|
|
|
4838
4827
|
try {
|
|
4839
4828
|
const resp = await this.get("ping");
|
|
4840
4829
|
return resp.status === 200;
|
|
4841
|
-
} catch
|
|
4830
|
+
} catch {
|
|
4842
4831
|
return false;
|
|
4843
4832
|
}
|
|
4844
4833
|
}
|
|
@@ -5242,9 +5231,9 @@ function logFeedbackImpl(state, parentObjectType, parentObjectId, {
|
|
|
5242
5231
|
expected,
|
|
5243
5232
|
tags
|
|
5244
5233
|
});
|
|
5245
|
-
|
|
5246
|
-
updateEvent = Object.fromEntries(
|
|
5247
|
-
Object.entries(
|
|
5234
|
+
const { metadata, ...rawUpdateEvent } = deepCopyEvent(validatedEvent);
|
|
5235
|
+
const updateEvent = Object.fromEntries(
|
|
5236
|
+
Object.entries(rawUpdateEvent).filter(([_, v]) => !isEmpty2(v))
|
|
5248
5237
|
);
|
|
5249
5238
|
const parentIds = async () => new SpanComponentsV3({
|
|
5250
5239
|
object_type: parentObjectType,
|
|
@@ -6811,7 +6800,8 @@ function validateAndSanitizeExperimentLogPartialArgs(event) {
|
|
|
6811
6800
|
if (Array.isArray(event.scores)) {
|
|
6812
6801
|
throw new Error("scores must be an object, not an array");
|
|
6813
6802
|
}
|
|
6814
|
-
for (
|
|
6803
|
+
for (const [name, rawScore] of Object.entries(event.scores)) {
|
|
6804
|
+
let score = rawScore;
|
|
6815
6805
|
if (typeof name !== "string") {
|
|
6816
6806
|
throw new Error("score names must be strings");
|
|
6817
6807
|
}
|
|
@@ -7253,7 +7243,11 @@ var Experiment2 = class extends ObjectFetcher {
|
|
|
7253
7243
|
* @returns A summary of the experiment, including the scores (compared to the closest reference experiment) and metadata.
|
|
7254
7244
|
*/
|
|
7255
7245
|
async summarize(options = {}) {
|
|
7256
|
-
|
|
7246
|
+
const {
|
|
7247
|
+
summarizeScores = true,
|
|
7248
|
+
comparisonExperimentId: comparisonExperimentIdOpt
|
|
7249
|
+
} = options || {};
|
|
7250
|
+
let comparisonExperimentId = comparisonExperimentIdOpt;
|
|
7257
7251
|
const state = await this.getState();
|
|
7258
7252
|
const projectUrl = `${state.appPublicUrl}/app/${encodeURIComponent(
|
|
7259
7253
|
state.orgName
|
|
@@ -8437,12 +8431,2636 @@ var RemoteEvalParameters = class {
|
|
|
8437
8431
|
return true;
|
|
8438
8432
|
}
|
|
8439
8433
|
static isParameters(x) {
|
|
8440
|
-
return typeof x === "object" && x !== null && "__braintrust_parameters_marker" in x &&
|
|
8441
|
-
x.__braintrust_parameters_marker === true;
|
|
8434
|
+
return typeof x === "object" && x !== null && "__braintrust_parameters_marker" in x && x.__braintrust_parameters_marker === true;
|
|
8442
8435
|
}
|
|
8443
8436
|
};
|
|
8444
8437
|
var TEST_API_KEY = "___TEST_API_KEY__THIS_IS_NOT_REAL___";
|
|
8445
8438
|
|
|
8439
|
+
// src/instrumentation/core/plugin.ts
|
|
8440
|
+
import { tracingChannel } from "dc-browser";
|
|
8441
|
+
|
|
8442
|
+
// src/instrumentation/core/stream-patcher.ts
|
|
8443
|
+
function isAsyncIterable(value) {
|
|
8444
|
+
return value !== null && typeof value === "object" && Symbol.asyncIterator in value && typeof value[Symbol.asyncIterator] === "function";
|
|
8445
|
+
}
|
|
8446
|
+
function patchStreamIfNeeded(stream, options) {
|
|
8447
|
+
if (!isAsyncIterable(stream)) {
|
|
8448
|
+
return stream;
|
|
8449
|
+
}
|
|
8450
|
+
if (Object.isFrozen(stream) || Object.isSealed(stream)) {
|
|
8451
|
+
console.warn(
|
|
8452
|
+
"Cannot patch frozen/sealed stream. Stream output will not be collected."
|
|
8453
|
+
);
|
|
8454
|
+
return stream;
|
|
8455
|
+
}
|
|
8456
|
+
const originalIteratorFn = stream[Symbol.asyncIterator];
|
|
8457
|
+
if (originalIteratorFn.__braintrust_patched) {
|
|
8458
|
+
return stream;
|
|
8459
|
+
}
|
|
8460
|
+
try {
|
|
8461
|
+
const patchedIteratorFn = function() {
|
|
8462
|
+
const iterator = originalIteratorFn.call(this);
|
|
8463
|
+
const originalNext = iterator.next.bind(iterator);
|
|
8464
|
+
const chunks = [];
|
|
8465
|
+
let completed = false;
|
|
8466
|
+
iterator.next = async function(...args) {
|
|
8467
|
+
try {
|
|
8468
|
+
const result = await originalNext(...args);
|
|
8469
|
+
if (result.done) {
|
|
8470
|
+
if (!completed) {
|
|
8471
|
+
completed = true;
|
|
8472
|
+
try {
|
|
8473
|
+
options.onComplete(chunks);
|
|
8474
|
+
} catch (error) {
|
|
8475
|
+
console.error("Error in stream onComplete handler:", error);
|
|
8476
|
+
}
|
|
8477
|
+
}
|
|
8478
|
+
} else {
|
|
8479
|
+
const chunk = result.value;
|
|
8480
|
+
const shouldCollect = options.shouldCollect ? options.shouldCollect(chunk) : true;
|
|
8481
|
+
if (shouldCollect) {
|
|
8482
|
+
chunks.push(chunk);
|
|
8483
|
+
if (options.onChunk) {
|
|
8484
|
+
try {
|
|
8485
|
+
options.onChunk(chunk);
|
|
8486
|
+
} catch (error) {
|
|
8487
|
+
console.error("Error in stream onChunk handler:", error);
|
|
8488
|
+
}
|
|
8489
|
+
}
|
|
8490
|
+
}
|
|
8491
|
+
}
|
|
8492
|
+
return result;
|
|
8493
|
+
} catch (error) {
|
|
8494
|
+
if (!completed) {
|
|
8495
|
+
completed = true;
|
|
8496
|
+
if (options.onError) {
|
|
8497
|
+
try {
|
|
8498
|
+
options.onError(error, chunks);
|
|
8499
|
+
} catch (handlerError) {
|
|
8500
|
+
console.error("Error in stream onError handler:", handlerError);
|
|
8501
|
+
}
|
|
8502
|
+
}
|
|
8503
|
+
}
|
|
8504
|
+
throw error;
|
|
8505
|
+
}
|
|
8506
|
+
};
|
|
8507
|
+
if (iterator.return) {
|
|
8508
|
+
const originalReturn = iterator.return.bind(iterator);
|
|
8509
|
+
iterator.return = async function(...args) {
|
|
8510
|
+
if (!completed) {
|
|
8511
|
+
completed = true;
|
|
8512
|
+
try {
|
|
8513
|
+
options.onComplete(chunks);
|
|
8514
|
+
} catch (error) {
|
|
8515
|
+
console.error("Error in stream onComplete handler:", error);
|
|
8516
|
+
}
|
|
8517
|
+
}
|
|
8518
|
+
return originalReturn(...args);
|
|
8519
|
+
};
|
|
8520
|
+
}
|
|
8521
|
+
if (iterator.throw) {
|
|
8522
|
+
const originalThrow = iterator.throw.bind(iterator);
|
|
8523
|
+
iterator.throw = async function(...args) {
|
|
8524
|
+
if (!completed) {
|
|
8525
|
+
completed = true;
|
|
8526
|
+
const error = args[0];
|
|
8527
|
+
if (options.onError) {
|
|
8528
|
+
try {
|
|
8529
|
+
options.onError(error, chunks);
|
|
8530
|
+
} catch (handlerError) {
|
|
8531
|
+
console.error("Error in stream onError handler:", handlerError);
|
|
8532
|
+
}
|
|
8533
|
+
}
|
|
8534
|
+
}
|
|
8535
|
+
return originalThrow(...args);
|
|
8536
|
+
};
|
|
8537
|
+
}
|
|
8538
|
+
return iterator;
|
|
8539
|
+
};
|
|
8540
|
+
patchedIteratorFn.__braintrust_patched = true;
|
|
8541
|
+
stream[Symbol.asyncIterator] = patchedIteratorFn;
|
|
8542
|
+
return stream;
|
|
8543
|
+
} catch (error) {
|
|
8544
|
+
console.warn("Failed to patch stream:", error);
|
|
8545
|
+
return stream;
|
|
8546
|
+
}
|
|
8547
|
+
}
|
|
8548
|
+
|
|
8549
|
+
// src/instrumentation/core/plugin.ts
|
|
8550
|
+
var BasePlugin = class {
|
|
8551
|
+
enabled = false;
|
|
8552
|
+
unsubscribers = [];
|
|
8553
|
+
/**
|
|
8554
|
+
* Enables the plugin. Must be called before the plugin will receive events.
|
|
8555
|
+
*/
|
|
8556
|
+
enable() {
|
|
8557
|
+
if (this.enabled) {
|
|
8558
|
+
return;
|
|
8559
|
+
}
|
|
8560
|
+
this.enabled = true;
|
|
8561
|
+
this.onEnable();
|
|
8562
|
+
}
|
|
8563
|
+
/**
|
|
8564
|
+
* Disables the plugin. After this, the plugin will no longer receive events.
|
|
8565
|
+
*/
|
|
8566
|
+
disable() {
|
|
8567
|
+
if (!this.enabled) {
|
|
8568
|
+
return;
|
|
8569
|
+
}
|
|
8570
|
+
this.enabled = false;
|
|
8571
|
+
this.onDisable();
|
|
8572
|
+
}
|
|
8573
|
+
/**
|
|
8574
|
+
* Helper to subscribe to a channel with raw handlers.
|
|
8575
|
+
*
|
|
8576
|
+
* @param channelName - The channel name to subscribe to
|
|
8577
|
+
* @param handlers - Event handlers
|
|
8578
|
+
*/
|
|
8579
|
+
subscribe(channelName, handlers) {
|
|
8580
|
+
const channel = tracingChannel(channelName);
|
|
8581
|
+
channel.subscribe(handlers);
|
|
8582
|
+
}
|
|
8583
|
+
/**
|
|
8584
|
+
* Subscribe to a channel for async methods (non-streaming).
|
|
8585
|
+
* Creates a span and logs input/output/metrics.
|
|
8586
|
+
*/
|
|
8587
|
+
subscribeToChannel(channelName, config) {
|
|
8588
|
+
const channel = tracingChannel(channelName);
|
|
8589
|
+
const spans = /* @__PURE__ */ new WeakMap();
|
|
8590
|
+
const handlers = {
|
|
8591
|
+
start: (event) => {
|
|
8592
|
+
const span = startSpan({
|
|
8593
|
+
name: config.name,
|
|
8594
|
+
spanAttributes: {
|
|
8595
|
+
type: config.type
|
|
8596
|
+
}
|
|
8597
|
+
});
|
|
8598
|
+
const startTime = getCurrentUnixTimestamp();
|
|
8599
|
+
spans.set(event, { span, startTime });
|
|
8600
|
+
try {
|
|
8601
|
+
const { input, metadata } = config.extractInput(event.arguments);
|
|
8602
|
+
span.log({
|
|
8603
|
+
input,
|
|
8604
|
+
metadata
|
|
8605
|
+
});
|
|
8606
|
+
} catch (error) {
|
|
8607
|
+
console.error(`Error extracting input for ${channelName}:`, error);
|
|
8608
|
+
}
|
|
8609
|
+
},
|
|
8610
|
+
asyncEnd: (event) => {
|
|
8611
|
+
const spanData = spans.get(event);
|
|
8612
|
+
if (!spanData) {
|
|
8613
|
+
return;
|
|
8614
|
+
}
|
|
8615
|
+
const { span, startTime } = spanData;
|
|
8616
|
+
try {
|
|
8617
|
+
const output = config.extractOutput(event.result);
|
|
8618
|
+
const metrics = config.extractMetrics(event.result, startTime);
|
|
8619
|
+
span.log({
|
|
8620
|
+
output,
|
|
8621
|
+
metrics
|
|
8622
|
+
});
|
|
8623
|
+
} catch (error) {
|
|
8624
|
+
console.error(`Error extracting output for ${channelName}:`, error);
|
|
8625
|
+
} finally {
|
|
8626
|
+
span.end();
|
|
8627
|
+
spans.delete(event);
|
|
8628
|
+
}
|
|
8629
|
+
},
|
|
8630
|
+
error: (event) => {
|
|
8631
|
+
const spanData = spans.get(event);
|
|
8632
|
+
if (!spanData) {
|
|
8633
|
+
return;
|
|
8634
|
+
}
|
|
8635
|
+
const { span } = spanData;
|
|
8636
|
+
span.log({
|
|
8637
|
+
error: event.error.message
|
|
8638
|
+
});
|
|
8639
|
+
span.end();
|
|
8640
|
+
spans.delete(event);
|
|
8641
|
+
}
|
|
8642
|
+
};
|
|
8643
|
+
channel.subscribe(handlers);
|
|
8644
|
+
this.unsubscribers.push(() => {
|
|
8645
|
+
channel.unsubscribe(handlers);
|
|
8646
|
+
});
|
|
8647
|
+
}
|
|
8648
|
+
/**
|
|
8649
|
+
* Subscribe to a channel for async methods that may return streams.
|
|
8650
|
+
* Handles both streaming and non-streaming responses.
|
|
8651
|
+
*/
|
|
8652
|
+
subscribeToStreamingChannel(channelName, config) {
|
|
8653
|
+
const channel = tracingChannel(channelName);
|
|
8654
|
+
const spans = /* @__PURE__ */ new WeakMap();
|
|
8655
|
+
const handlers = {
|
|
8656
|
+
start: (event) => {
|
|
8657
|
+
const span = startSpan({
|
|
8658
|
+
name: config.name,
|
|
8659
|
+
spanAttributes: {
|
|
8660
|
+
type: config.type
|
|
8661
|
+
}
|
|
8662
|
+
});
|
|
8663
|
+
const startTime = getCurrentUnixTimestamp();
|
|
8664
|
+
spans.set(event, { span, startTime });
|
|
8665
|
+
try {
|
|
8666
|
+
const { input, metadata } = config.extractInput(event.arguments);
|
|
8667
|
+
span.log({
|
|
8668
|
+
input,
|
|
8669
|
+
metadata
|
|
8670
|
+
});
|
|
8671
|
+
} catch (error) {
|
|
8672
|
+
console.error(`Error extracting input for ${channelName}:`, error);
|
|
8673
|
+
}
|
|
8674
|
+
},
|
|
8675
|
+
asyncEnd: (event) => {
|
|
8676
|
+
const spanData = spans.get(event);
|
|
8677
|
+
if (!spanData) {
|
|
8678
|
+
return;
|
|
8679
|
+
}
|
|
8680
|
+
const { span, startTime } = spanData;
|
|
8681
|
+
if (isAsyncIterable(event.result)) {
|
|
8682
|
+
patchStreamIfNeeded(event.result, {
|
|
8683
|
+
onComplete: (chunks) => {
|
|
8684
|
+
try {
|
|
8685
|
+
let output;
|
|
8686
|
+
let metrics;
|
|
8687
|
+
if (config.aggregateChunks) {
|
|
8688
|
+
const aggregated = config.aggregateChunks(chunks);
|
|
8689
|
+
output = aggregated.output;
|
|
8690
|
+
metrics = aggregated.metrics;
|
|
8691
|
+
} else {
|
|
8692
|
+
output = config.extractOutput(chunks);
|
|
8693
|
+
metrics = config.extractMetrics(chunks, startTime);
|
|
8694
|
+
}
|
|
8695
|
+
if (!metrics.time_to_first_token && chunks.length > 0) {
|
|
8696
|
+
metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
|
|
8697
|
+
}
|
|
8698
|
+
span.log({
|
|
8699
|
+
output,
|
|
8700
|
+
metrics
|
|
8701
|
+
});
|
|
8702
|
+
} catch (error) {
|
|
8703
|
+
console.error(
|
|
8704
|
+
`Error extracting output for ${channelName}:`,
|
|
8705
|
+
error
|
|
8706
|
+
);
|
|
8707
|
+
} finally {
|
|
8708
|
+
span.end();
|
|
8709
|
+
}
|
|
8710
|
+
},
|
|
8711
|
+
onError: (error) => {
|
|
8712
|
+
span.log({
|
|
8713
|
+
error: error.message
|
|
8714
|
+
});
|
|
8715
|
+
span.end();
|
|
8716
|
+
}
|
|
8717
|
+
});
|
|
8718
|
+
} else {
|
|
8719
|
+
try {
|
|
8720
|
+
const output = config.extractOutput(event.result);
|
|
8721
|
+
const metrics = config.extractMetrics(event.result, startTime);
|
|
8722
|
+
span.log({
|
|
8723
|
+
output,
|
|
8724
|
+
metrics
|
|
8725
|
+
});
|
|
8726
|
+
} catch (error) {
|
|
8727
|
+
console.error(`Error extracting output for ${channelName}:`, error);
|
|
8728
|
+
} finally {
|
|
8729
|
+
span.end();
|
|
8730
|
+
spans.delete(event);
|
|
8731
|
+
}
|
|
8732
|
+
}
|
|
8733
|
+
},
|
|
8734
|
+
error: (event) => {
|
|
8735
|
+
const spanData = spans.get(event);
|
|
8736
|
+
if (!spanData) {
|
|
8737
|
+
return;
|
|
8738
|
+
}
|
|
8739
|
+
const { span } = spanData;
|
|
8740
|
+
span.log({
|
|
8741
|
+
error: event.error.message
|
|
8742
|
+
});
|
|
8743
|
+
span.end();
|
|
8744
|
+
spans.delete(event);
|
|
8745
|
+
}
|
|
8746
|
+
};
|
|
8747
|
+
channel.subscribe(handlers);
|
|
8748
|
+
this.unsubscribers.push(() => {
|
|
8749
|
+
channel.unsubscribe(handlers);
|
|
8750
|
+
});
|
|
8751
|
+
}
|
|
8752
|
+
/**
|
|
8753
|
+
* Subscribe to a channel for sync methods that return event-based streams.
|
|
8754
|
+
* Used for methods like beta.chat.completions.stream() and responses.stream().
|
|
8755
|
+
*/
|
|
8756
|
+
subscribeToSyncStreamChannel(channelName, config) {
|
|
8757
|
+
const channel = tracingChannel(channelName);
|
|
8758
|
+
const spans = /* @__PURE__ */ new WeakMap();
|
|
8759
|
+
const handlers = {
|
|
8760
|
+
start: (event) => {
|
|
8761
|
+
const span = startSpan({
|
|
8762
|
+
name: config.name,
|
|
8763
|
+
spanAttributes: {
|
|
8764
|
+
type: config.type
|
|
8765
|
+
}
|
|
8766
|
+
});
|
|
8767
|
+
const startTime = getCurrentUnixTimestamp();
|
|
8768
|
+
spans.set(event, { span, startTime });
|
|
8769
|
+
try {
|
|
8770
|
+
const { input, metadata } = config.extractInput(event.arguments);
|
|
8771
|
+
span.log({
|
|
8772
|
+
input,
|
|
8773
|
+
metadata
|
|
8774
|
+
});
|
|
8775
|
+
} catch (error) {
|
|
8776
|
+
console.error(`Error extracting input for ${channelName}:`, error);
|
|
8777
|
+
}
|
|
8778
|
+
},
|
|
8779
|
+
end: (event) => {
|
|
8780
|
+
const spanData = spans.get(event);
|
|
8781
|
+
if (!spanData) {
|
|
8782
|
+
return;
|
|
8783
|
+
}
|
|
8784
|
+
const { span, startTime } = spanData;
|
|
8785
|
+
const stream = event.result;
|
|
8786
|
+
if (!stream || typeof stream.on !== "function") {
|
|
8787
|
+
span.end();
|
|
8788
|
+
spans.delete(event);
|
|
8789
|
+
return;
|
|
8790
|
+
}
|
|
8791
|
+
let first = true;
|
|
8792
|
+
stream.on("chunk", (chunk) => {
|
|
8793
|
+
if (first) {
|
|
8794
|
+
const now2 = getCurrentUnixTimestamp();
|
|
8795
|
+
span.log({
|
|
8796
|
+
metrics: {
|
|
8797
|
+
time_to_first_token: now2 - startTime
|
|
8798
|
+
}
|
|
8799
|
+
});
|
|
8800
|
+
first = false;
|
|
8801
|
+
}
|
|
8802
|
+
});
|
|
8803
|
+
stream.on("chatCompletion", (completion) => {
|
|
8804
|
+
try {
|
|
8805
|
+
span.log({
|
|
8806
|
+
output: completion.choices
|
|
8807
|
+
});
|
|
8808
|
+
} catch (error) {
|
|
8809
|
+
console.error(
|
|
8810
|
+
`Error extracting chatCompletion for ${channelName}:`,
|
|
8811
|
+
error
|
|
8812
|
+
);
|
|
8813
|
+
}
|
|
8814
|
+
});
|
|
8815
|
+
stream.on("event", (streamEvent) => {
|
|
8816
|
+
if (config.extractFromEvent) {
|
|
8817
|
+
try {
|
|
8818
|
+
if (first) {
|
|
8819
|
+
const now2 = getCurrentUnixTimestamp();
|
|
8820
|
+
span.log({
|
|
8821
|
+
metrics: {
|
|
8822
|
+
time_to_first_token: now2 - startTime
|
|
8823
|
+
}
|
|
8824
|
+
});
|
|
8825
|
+
first = false;
|
|
8826
|
+
}
|
|
8827
|
+
const extracted = config.extractFromEvent(streamEvent);
|
|
8828
|
+
if (extracted && Object.keys(extracted).length > 0) {
|
|
8829
|
+
span.log(extracted);
|
|
8830
|
+
}
|
|
8831
|
+
} catch (error) {
|
|
8832
|
+
console.error(
|
|
8833
|
+
`Error extracting event for ${channelName}:`,
|
|
8834
|
+
error
|
|
8835
|
+
);
|
|
8836
|
+
}
|
|
8837
|
+
}
|
|
8838
|
+
});
|
|
8839
|
+
stream.on("end", () => {
|
|
8840
|
+
span.end();
|
|
8841
|
+
spans.delete(event);
|
|
8842
|
+
});
|
|
8843
|
+
},
|
|
8844
|
+
error: (event) => {
|
|
8845
|
+
const spanData = spans.get(event);
|
|
8846
|
+
if (!spanData) {
|
|
8847
|
+
return;
|
|
8848
|
+
}
|
|
8849
|
+
const { span } = spanData;
|
|
8850
|
+
span.log({
|
|
8851
|
+
error: event.error.message
|
|
8852
|
+
});
|
|
8853
|
+
span.end();
|
|
8854
|
+
spans.delete(event);
|
|
8855
|
+
}
|
|
8856
|
+
};
|
|
8857
|
+
channel.subscribe(handlers);
|
|
8858
|
+
this.unsubscribers.push(() => {
|
|
8859
|
+
channel.unsubscribe(handlers);
|
|
8860
|
+
});
|
|
8861
|
+
}
|
|
8862
|
+
};
|
|
8863
|
+
|
|
8864
|
+
// src/wrappers/attachment-utils.ts
|
|
8865
|
+
function getExtensionFromMediaType(mediaType) {
|
|
8866
|
+
const extensionMap = {
|
|
8867
|
+
"image/png": "png",
|
|
8868
|
+
"image/jpeg": "jpg",
|
|
8869
|
+
"image/gif": "gif",
|
|
8870
|
+
"image/webp": "webp",
|
|
8871
|
+
"image/svg+xml": "svg",
|
|
8872
|
+
"audio/mpeg": "mp3",
|
|
8873
|
+
"audio/wav": "wav",
|
|
8874
|
+
"audio/ogg": "ogg",
|
|
8875
|
+
"video/mp4": "mp4",
|
|
8876
|
+
"video/webm": "webm",
|
|
8877
|
+
"application/pdf": "pdf",
|
|
8878
|
+
"application/json": "json",
|
|
8879
|
+
"text/plain": "txt",
|
|
8880
|
+
"text/html": "html",
|
|
8881
|
+
"text/csv": "csv"
|
|
8882
|
+
};
|
|
8883
|
+
return extensionMap[mediaType] || "bin";
|
|
8884
|
+
}
|
|
8885
|
+
function convertDataToBlob(data, mediaType) {
|
|
8886
|
+
try {
|
|
8887
|
+
if (typeof data === "string") {
|
|
8888
|
+
if (data.startsWith("data:")) {
|
|
8889
|
+
const base64Match = data.match(/^data:[^;]+;base64,(.+)$/);
|
|
8890
|
+
if (base64Match) {
|
|
8891
|
+
const base64 = base64Match[1];
|
|
8892
|
+
const binaryString = atob(base64);
|
|
8893
|
+
const bytes = new Uint8Array(binaryString.length);
|
|
8894
|
+
for (let i = 0; i < binaryString.length; i++) {
|
|
8895
|
+
bytes[i] = binaryString.charCodeAt(i);
|
|
8896
|
+
}
|
|
8897
|
+
return new Blob([bytes], { type: mediaType });
|
|
8898
|
+
}
|
|
8899
|
+
} else if (data.startsWith("http://") || data.startsWith("https://")) {
|
|
8900
|
+
return null;
|
|
8901
|
+
} else {
|
|
8902
|
+
const binaryString = atob(data);
|
|
8903
|
+
const bytes = new Uint8Array(binaryString.length);
|
|
8904
|
+
for (let i = 0; i < binaryString.length; i++) {
|
|
8905
|
+
bytes[i] = binaryString.charCodeAt(i);
|
|
8906
|
+
}
|
|
8907
|
+
return new Blob([bytes], { type: mediaType });
|
|
8908
|
+
}
|
|
8909
|
+
} else if (data instanceof Uint8Array) {
|
|
8910
|
+
return new Blob([data], { type: mediaType });
|
|
8911
|
+
} else if (data instanceof ArrayBuffer) {
|
|
8912
|
+
return new Blob([data], { type: mediaType });
|
|
8913
|
+
} else if (typeof Buffer !== "undefined" && data instanceof Buffer) {
|
|
8914
|
+
return new Blob([data], { type: mediaType });
|
|
8915
|
+
}
|
|
8916
|
+
} catch {
|
|
8917
|
+
return null;
|
|
8918
|
+
}
|
|
8919
|
+
return null;
|
|
8920
|
+
}
|
|
8921
|
+
function processInputAttachments(input) {
|
|
8922
|
+
if (!input) {
|
|
8923
|
+
return input;
|
|
8924
|
+
}
|
|
8925
|
+
let attachmentIndex = 0;
|
|
8926
|
+
const processContentPart = (part) => {
|
|
8927
|
+
if (!part || typeof part !== "object") {
|
|
8928
|
+
return part;
|
|
8929
|
+
}
|
|
8930
|
+
if (part.type === "image" && part.image) {
|
|
8931
|
+
let mediaType = "image/png";
|
|
8932
|
+
if (typeof part.image === "string" && part.image.startsWith("data:")) {
|
|
8933
|
+
const mediaTypeMatch = part.image.match(/^data:([^;]+);/);
|
|
8934
|
+
if (mediaTypeMatch) {
|
|
8935
|
+
mediaType = mediaTypeMatch[1];
|
|
8936
|
+
}
|
|
8937
|
+
} else if (part.mediaType) {
|
|
8938
|
+
mediaType = part.mediaType;
|
|
8939
|
+
}
|
|
8940
|
+
const blob = convertDataToBlob(part.image, mediaType);
|
|
8941
|
+
if (blob) {
|
|
8942
|
+
const filename = `input_image_${attachmentIndex}.${getExtensionFromMediaType(mediaType)}`;
|
|
8943
|
+
attachmentIndex++;
|
|
8944
|
+
const attachment = new Attachment({
|
|
8945
|
+
data: blob,
|
|
8946
|
+
filename,
|
|
8947
|
+
contentType: mediaType
|
|
8948
|
+
});
|
|
8949
|
+
return {
|
|
8950
|
+
...part,
|
|
8951
|
+
image: attachment
|
|
8952
|
+
};
|
|
8953
|
+
}
|
|
8954
|
+
}
|
|
8955
|
+
if (part.type === "file" && part.data) {
|
|
8956
|
+
const mediaType = part.mediaType || "application/octet-stream";
|
|
8957
|
+
const blob = convertDataToBlob(part.data, mediaType);
|
|
8958
|
+
if (blob) {
|
|
8959
|
+
const filename = part.filename || `input_file_${attachmentIndex}.${getExtensionFromMediaType(mediaType)}`;
|
|
8960
|
+
attachmentIndex++;
|
|
8961
|
+
const attachment = new Attachment({
|
|
8962
|
+
data: blob,
|
|
8963
|
+
filename,
|
|
8964
|
+
contentType: mediaType
|
|
8965
|
+
});
|
|
8966
|
+
return {
|
|
8967
|
+
...part,
|
|
8968
|
+
data: attachment
|
|
8969
|
+
};
|
|
8970
|
+
}
|
|
8971
|
+
}
|
|
8972
|
+
return part;
|
|
8973
|
+
};
|
|
8974
|
+
const processMessage = (message) => {
|
|
8975
|
+
if (!message || typeof message !== "object") {
|
|
8976
|
+
return message;
|
|
8977
|
+
}
|
|
8978
|
+
if (Array.isArray(message.content)) {
|
|
8979
|
+
return {
|
|
8980
|
+
...message,
|
|
8981
|
+
content: message.content.map(processContentPart)
|
|
8982
|
+
};
|
|
8983
|
+
}
|
|
8984
|
+
return message;
|
|
8985
|
+
};
|
|
8986
|
+
if (Array.isArray(input)) {
|
|
8987
|
+
return input.map(processMessage);
|
|
8988
|
+
} else if (typeof input === "object" && input.content) {
|
|
8989
|
+
return processMessage(input);
|
|
8990
|
+
}
|
|
8991
|
+
return input;
|
|
8992
|
+
}
|
|
8993
|
+
|
|
8994
|
+
// src/instrumentation/plugins/openai-plugin.ts
|
|
8995
|
+
var OpenAIPlugin = class extends BasePlugin {
|
|
8996
|
+
constructor() {
|
|
8997
|
+
super();
|
|
8998
|
+
}
|
|
8999
|
+
onEnable() {
|
|
9000
|
+
this.subscribeToStreamingChannel(
|
|
9001
|
+
"orchestrion:openai:chat.completions.create",
|
|
9002
|
+
{
|
|
9003
|
+
name: "Chat Completion",
|
|
9004
|
+
type: "llm" /* LLM */,
|
|
9005
|
+
extractInput: (args) => {
|
|
9006
|
+
const params = args[0] || {};
|
|
9007
|
+
const { messages, ...metadata } = params;
|
|
9008
|
+
return {
|
|
9009
|
+
input: processInputAttachments(messages),
|
|
9010
|
+
metadata: { ...metadata, provider: "openai" }
|
|
9011
|
+
};
|
|
9012
|
+
},
|
|
9013
|
+
extractOutput: (result) => {
|
|
9014
|
+
return result?.choices;
|
|
9015
|
+
},
|
|
9016
|
+
extractMetrics: (result, startTime) => {
|
|
9017
|
+
const metrics = parseMetricsFromUsage(result?.usage);
|
|
9018
|
+
if (startTime) {
|
|
9019
|
+
metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
|
|
9020
|
+
}
|
|
9021
|
+
return metrics;
|
|
9022
|
+
},
|
|
9023
|
+
aggregateChunks: aggregateChatCompletionChunks
|
|
9024
|
+
}
|
|
9025
|
+
);
|
|
9026
|
+
this.subscribeToChannel("orchestrion:openai:embeddings.create", {
|
|
9027
|
+
name: "Embedding",
|
|
9028
|
+
type: "llm" /* LLM */,
|
|
9029
|
+
extractInput: (args) => {
|
|
9030
|
+
const params = args[0] || {};
|
|
9031
|
+
const { input, ...metadata } = params;
|
|
9032
|
+
return {
|
|
9033
|
+
input,
|
|
9034
|
+
metadata: { ...metadata, provider: "openai" }
|
|
9035
|
+
};
|
|
9036
|
+
},
|
|
9037
|
+
extractOutput: (result) => {
|
|
9038
|
+
return result?.data?.map((d) => d.embedding);
|
|
9039
|
+
},
|
|
9040
|
+
extractMetrics: (result) => {
|
|
9041
|
+
return parseMetricsFromUsage(result?.usage);
|
|
9042
|
+
}
|
|
9043
|
+
});
|
|
9044
|
+
this.subscribeToStreamingChannel(
|
|
9045
|
+
"orchestrion:openai:beta.chat.completions.parse",
|
|
9046
|
+
{
|
|
9047
|
+
name: "Chat Completion",
|
|
9048
|
+
type: "llm" /* LLM */,
|
|
9049
|
+
extractInput: (args) => {
|
|
9050
|
+
const params = args[0] || {};
|
|
9051
|
+
const { messages, ...metadata } = params;
|
|
9052
|
+
return {
|
|
9053
|
+
input: processInputAttachments(messages),
|
|
9054
|
+
metadata: { ...metadata, provider: "openai" }
|
|
9055
|
+
};
|
|
9056
|
+
},
|
|
9057
|
+
extractOutput: (result) => {
|
|
9058
|
+
return result?.choices;
|
|
9059
|
+
},
|
|
9060
|
+
extractMetrics: (result, startTime) => {
|
|
9061
|
+
const metrics = parseMetricsFromUsage(result?.usage);
|
|
9062
|
+
if (startTime) {
|
|
9063
|
+
metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
|
|
9064
|
+
}
|
|
9065
|
+
return metrics;
|
|
9066
|
+
},
|
|
9067
|
+
aggregateChunks: aggregateChatCompletionChunks
|
|
9068
|
+
}
|
|
9069
|
+
);
|
|
9070
|
+
this.subscribeToSyncStreamChannel(
|
|
9071
|
+
"orchestrion:openai:beta.chat.completions.stream",
|
|
9072
|
+
{
|
|
9073
|
+
name: "Chat Completion",
|
|
9074
|
+
type: "llm" /* LLM */,
|
|
9075
|
+
extractInput: (args) => {
|
|
9076
|
+
const params = args[0] || {};
|
|
9077
|
+
const { messages, ...metadata } = params;
|
|
9078
|
+
return {
|
|
9079
|
+
input: processInputAttachments(messages),
|
|
9080
|
+
metadata: { ...metadata, provider: "openai" }
|
|
9081
|
+
};
|
|
9082
|
+
}
|
|
9083
|
+
}
|
|
9084
|
+
);
|
|
9085
|
+
this.subscribeToChannel("orchestrion:openai:moderations.create", {
|
|
9086
|
+
name: "Moderation",
|
|
9087
|
+
type: "llm" /* LLM */,
|
|
9088
|
+
extractInput: (args) => {
|
|
9089
|
+
const params = args[0] || {};
|
|
9090
|
+
const { input, ...metadata } = params;
|
|
9091
|
+
return {
|
|
9092
|
+
input,
|
|
9093
|
+
metadata: { ...metadata, provider: "openai" }
|
|
9094
|
+
};
|
|
9095
|
+
},
|
|
9096
|
+
extractOutput: (result) => {
|
|
9097
|
+
return result?.results;
|
|
9098
|
+
},
|
|
9099
|
+
extractMetrics: () => {
|
|
9100
|
+
return {};
|
|
9101
|
+
}
|
|
9102
|
+
});
|
|
9103
|
+
this.subscribeToStreamingChannel("orchestrion:openai:responses.create", {
|
|
9104
|
+
name: "openai.responses.create",
|
|
9105
|
+
type: "llm" /* LLM */,
|
|
9106
|
+
extractInput: (args) => {
|
|
9107
|
+
const params = args[0] || {};
|
|
9108
|
+
const { input, ...metadata } = params;
|
|
9109
|
+
return {
|
|
9110
|
+
input: processInputAttachments(input),
|
|
9111
|
+
metadata: { ...metadata, provider: "openai" }
|
|
9112
|
+
};
|
|
9113
|
+
},
|
|
9114
|
+
extractOutput: (result) => {
|
|
9115
|
+
return processImagesInOutput(result?.output);
|
|
9116
|
+
},
|
|
9117
|
+
extractMetrics: (result, startTime) => {
|
|
9118
|
+
const metrics = parseMetricsFromUsage(result?.usage);
|
|
9119
|
+
if (startTime) {
|
|
9120
|
+
metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
|
|
9121
|
+
}
|
|
9122
|
+
return metrics;
|
|
9123
|
+
}
|
|
9124
|
+
});
|
|
9125
|
+
this.subscribeToSyncStreamChannel("orchestrion:openai:responses.stream", {
|
|
9126
|
+
name: "openai.responses.stream",
|
|
9127
|
+
type: "llm" /* LLM */,
|
|
9128
|
+
extractInput: (args) => {
|
|
9129
|
+
const params = args[0] || {};
|
|
9130
|
+
const { input, ...metadata } = params;
|
|
9131
|
+
return {
|
|
9132
|
+
input: processInputAttachments(input),
|
|
9133
|
+
metadata: { ...metadata, provider: "openai" }
|
|
9134
|
+
};
|
|
9135
|
+
},
|
|
9136
|
+
extractFromEvent: (event) => {
|
|
9137
|
+
if (!event || !event.type || !event.response) {
|
|
9138
|
+
return {};
|
|
9139
|
+
}
|
|
9140
|
+
const response = event.response;
|
|
9141
|
+
if (event.type === "response.completed") {
|
|
9142
|
+
const data = {};
|
|
9143
|
+
if (response?.output !== void 0) {
|
|
9144
|
+
data.output = processImagesInOutput(response.output);
|
|
9145
|
+
}
|
|
9146
|
+
if (response) {
|
|
9147
|
+
const { usage: _usage, output: _output, ...metadata } = response;
|
|
9148
|
+
if (Object.keys(metadata).length > 0) {
|
|
9149
|
+
data.metadata = metadata;
|
|
9150
|
+
}
|
|
9151
|
+
}
|
|
9152
|
+
data.metrics = parseMetricsFromUsage(response?.usage);
|
|
9153
|
+
return data;
|
|
9154
|
+
}
|
|
9155
|
+
return {};
|
|
9156
|
+
}
|
|
9157
|
+
});
|
|
9158
|
+
this.subscribeToStreamingChannel("orchestrion:openai:responses.parse", {
|
|
9159
|
+
name: "openai.responses.parse",
|
|
9160
|
+
type: "llm" /* LLM */,
|
|
9161
|
+
extractInput: (args) => {
|
|
9162
|
+
const params = args[0] || {};
|
|
9163
|
+
const { input, ...metadata } = params;
|
|
9164
|
+
return {
|
|
9165
|
+
input: processInputAttachments(input),
|
|
9166
|
+
metadata: { ...metadata, provider: "openai" }
|
|
9167
|
+
};
|
|
9168
|
+
},
|
|
9169
|
+
extractOutput: (result) => {
|
|
9170
|
+
return processImagesInOutput(result?.output);
|
|
9171
|
+
},
|
|
9172
|
+
extractMetrics: (result, startTime) => {
|
|
9173
|
+
const metrics = parseMetricsFromUsage(result?.usage);
|
|
9174
|
+
if (startTime) {
|
|
9175
|
+
metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
|
|
9176
|
+
}
|
|
9177
|
+
return metrics;
|
|
9178
|
+
}
|
|
9179
|
+
});
|
|
9180
|
+
}
|
|
9181
|
+
onDisable() {
|
|
9182
|
+
}
|
|
9183
|
+
};
|
|
9184
|
+
var TOKEN_NAME_MAP = {
|
|
9185
|
+
input_tokens: "prompt_tokens",
|
|
9186
|
+
output_tokens: "completion_tokens",
|
|
9187
|
+
total_tokens: "tokens"
|
|
9188
|
+
};
|
|
9189
|
+
var TOKEN_PREFIX_MAP = {
|
|
9190
|
+
input: "prompt",
|
|
9191
|
+
output: "completion"
|
|
9192
|
+
};
|
|
9193
|
+
function parseMetricsFromUsage(usage) {
|
|
9194
|
+
if (!usage) {
|
|
9195
|
+
return {};
|
|
9196
|
+
}
|
|
9197
|
+
const metrics = {};
|
|
9198
|
+
for (const [oai_name, value] of Object.entries(usage)) {
|
|
9199
|
+
if (typeof value === "number") {
|
|
9200
|
+
const metricName = TOKEN_NAME_MAP[oai_name] || oai_name;
|
|
9201
|
+
metrics[metricName] = value;
|
|
9202
|
+
} else if (oai_name.endsWith("_tokens_details")) {
|
|
9203
|
+
if (!isObject(value)) {
|
|
9204
|
+
continue;
|
|
9205
|
+
}
|
|
9206
|
+
const rawPrefix = oai_name.slice(0, -"_tokens_details".length);
|
|
9207
|
+
const prefix = TOKEN_PREFIX_MAP[rawPrefix] || rawPrefix;
|
|
9208
|
+
for (const [key, n] of Object.entries(value)) {
|
|
9209
|
+
if (typeof n !== "number") {
|
|
9210
|
+
continue;
|
|
9211
|
+
}
|
|
9212
|
+
const metricName = `${prefix}_${key}`;
|
|
9213
|
+
metrics[metricName] = n;
|
|
9214
|
+
}
|
|
9215
|
+
}
|
|
9216
|
+
}
|
|
9217
|
+
return metrics;
|
|
9218
|
+
}
|
|
9219
|
+
function processImagesInOutput(output) {
|
|
9220
|
+
if (Array.isArray(output)) {
|
|
9221
|
+
return output.map(processImagesInOutput);
|
|
9222
|
+
}
|
|
9223
|
+
if (isObject(output)) {
|
|
9224
|
+
if (output.type === "image_generation_call" && output.result && typeof output.result === "string") {
|
|
9225
|
+
const fileExtension = output.output_format || "png";
|
|
9226
|
+
const contentType = `image/${fileExtension}`;
|
|
9227
|
+
const baseFilename = output.revised_prompt && typeof output.revised_prompt === "string" ? output.revised_prompt.slice(0, 50).replace(/[^a-zA-Z0-9]/g, "_") : "generated_image";
|
|
9228
|
+
const filename = `${baseFilename}.${fileExtension}`;
|
|
9229
|
+
const binaryString = atob(output.result);
|
|
9230
|
+
const bytes = new Uint8Array(binaryString.length);
|
|
9231
|
+
for (let i = 0; i < binaryString.length; i++) {
|
|
9232
|
+
bytes[i] = binaryString.charCodeAt(i);
|
|
9233
|
+
}
|
|
9234
|
+
const blob = new Blob([bytes], { type: contentType });
|
|
9235
|
+
const attachment = new Attachment({
|
|
9236
|
+
data: blob,
|
|
9237
|
+
filename,
|
|
9238
|
+
contentType
|
|
9239
|
+
});
|
|
9240
|
+
return {
|
|
9241
|
+
...output,
|
|
9242
|
+
result: attachment
|
|
9243
|
+
};
|
|
9244
|
+
}
|
|
9245
|
+
}
|
|
9246
|
+
return output;
|
|
9247
|
+
}
|
|
9248
|
+
function aggregateChatCompletionChunks(chunks) {
|
|
9249
|
+
let role = void 0;
|
|
9250
|
+
let content = void 0;
|
|
9251
|
+
let tool_calls = void 0;
|
|
9252
|
+
let finish_reason = void 0;
|
|
9253
|
+
let metrics = {};
|
|
9254
|
+
for (const chunk of chunks) {
|
|
9255
|
+
if (chunk.usage) {
|
|
9256
|
+
metrics = {
|
|
9257
|
+
...metrics,
|
|
9258
|
+
...parseMetricsFromUsage(chunk.usage)
|
|
9259
|
+
};
|
|
9260
|
+
}
|
|
9261
|
+
const delta = chunk.choices?.[0]?.delta;
|
|
9262
|
+
if (!delta) {
|
|
9263
|
+
continue;
|
|
9264
|
+
}
|
|
9265
|
+
if (!role && delta.role) {
|
|
9266
|
+
role = delta.role;
|
|
9267
|
+
}
|
|
9268
|
+
if (delta.finish_reason) {
|
|
9269
|
+
finish_reason = delta.finish_reason;
|
|
9270
|
+
}
|
|
9271
|
+
if (delta.content) {
|
|
9272
|
+
content = (content || "") + delta.content;
|
|
9273
|
+
}
|
|
9274
|
+
if (delta.tool_calls) {
|
|
9275
|
+
const toolDelta = delta.tool_calls[0];
|
|
9276
|
+
if (!tool_calls || toolDelta.id && tool_calls[tool_calls.length - 1].id !== toolDelta.id) {
|
|
9277
|
+
tool_calls = [
|
|
9278
|
+
...tool_calls || [],
|
|
9279
|
+
{
|
|
9280
|
+
id: toolDelta.id,
|
|
9281
|
+
type: toolDelta.type,
|
|
9282
|
+
function: toolDelta.function
|
|
9283
|
+
}
|
|
9284
|
+
];
|
|
9285
|
+
} else {
|
|
9286
|
+
tool_calls[tool_calls.length - 1].function.arguments += toolDelta.function.arguments;
|
|
9287
|
+
}
|
|
9288
|
+
}
|
|
9289
|
+
}
|
|
9290
|
+
return {
|
|
9291
|
+
metrics,
|
|
9292
|
+
output: [
|
|
9293
|
+
{
|
|
9294
|
+
index: 0,
|
|
9295
|
+
message: {
|
|
9296
|
+
role,
|
|
9297
|
+
content,
|
|
9298
|
+
tool_calls
|
|
9299
|
+
},
|
|
9300
|
+
logprobs: null,
|
|
9301
|
+
finish_reason
|
|
9302
|
+
}
|
|
9303
|
+
]
|
|
9304
|
+
};
|
|
9305
|
+
}
|
|
9306
|
+
|
|
9307
|
+
// src/instrumentation/plugins/anthropic-plugin.ts
|
|
9308
|
+
import { tracingChannel as tracingChannel2 } from "dc-browser";
|
|
9309
|
+
|
|
9310
|
+
// src/wrappers/anthropic-tokens-util.ts
|
|
9311
|
+
function finalizeAnthropicTokens(metrics) {
|
|
9312
|
+
const prompt_tokens = (metrics.prompt_tokens || 0) + (metrics.prompt_cached_tokens || 0) + (metrics.prompt_cache_creation_tokens || 0);
|
|
9313
|
+
return {
|
|
9314
|
+
...metrics,
|
|
9315
|
+
prompt_tokens,
|
|
9316
|
+
tokens: prompt_tokens + (metrics.completion_tokens || 0)
|
|
9317
|
+
};
|
|
9318
|
+
}
|
|
9319
|
+
function extractAnthropicCacheTokens(cacheReadTokens = 0, cacheCreationTokens = 0) {
|
|
9320
|
+
const cacheTokens = {};
|
|
9321
|
+
if (cacheReadTokens > 0) {
|
|
9322
|
+
cacheTokens.prompt_cached_tokens = cacheReadTokens;
|
|
9323
|
+
}
|
|
9324
|
+
if (cacheCreationTokens > 0) {
|
|
9325
|
+
cacheTokens.prompt_cache_creation_tokens = cacheCreationTokens;
|
|
9326
|
+
}
|
|
9327
|
+
return cacheTokens;
|
|
9328
|
+
}
|
|
9329
|
+
|
|
9330
|
+
// src/instrumentation/plugins/anthropic-plugin.ts
|
|
9331
|
+
var AnthropicPlugin = class extends BasePlugin {
|
|
9332
|
+
unsubscribers = [];
|
|
9333
|
+
onEnable() {
|
|
9334
|
+
this.subscribeToAnthropicChannels();
|
|
9335
|
+
}
|
|
9336
|
+
onDisable() {
|
|
9337
|
+
for (const unsubscribe of this.unsubscribers) {
|
|
9338
|
+
unsubscribe();
|
|
9339
|
+
}
|
|
9340
|
+
this.unsubscribers = [];
|
|
9341
|
+
}
|
|
9342
|
+
subscribeToAnthropicChannels() {
|
|
9343
|
+
this.subscribeToStreamingChannel("orchestrion:anthropic:messages.create", {
|
|
9344
|
+
name: "anthropic.messages.create",
|
|
9345
|
+
type: "llm" /* LLM */,
|
|
9346
|
+
extractInput: (args) => {
|
|
9347
|
+
const params = args[0] || {};
|
|
9348
|
+
const input = coalesceInput(params.messages || [], params.system);
|
|
9349
|
+
const metadata = filterFrom(params, ["messages", "system"]);
|
|
9350
|
+
return {
|
|
9351
|
+
input: processAttachmentsInInput(input),
|
|
9352
|
+
metadata: { ...metadata, provider: "anthropic" }
|
|
9353
|
+
};
|
|
9354
|
+
},
|
|
9355
|
+
extractOutput: (result) => {
|
|
9356
|
+
return result ? { role: result.role, content: result.content } : null;
|
|
9357
|
+
},
|
|
9358
|
+
extractMetrics: (result, startTime) => {
|
|
9359
|
+
const metrics = parseMetricsFromUsage2(result?.usage);
|
|
9360
|
+
if (startTime) {
|
|
9361
|
+
metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
|
|
9362
|
+
}
|
|
9363
|
+
const finalized = finalizeAnthropicTokens(metrics);
|
|
9364
|
+
return Object.fromEntries(
|
|
9365
|
+
Object.entries(finalized).filter(([, v]) => v !== void 0)
|
|
9366
|
+
);
|
|
9367
|
+
},
|
|
9368
|
+
extractMetadata: (result) => {
|
|
9369
|
+
const metadata = {};
|
|
9370
|
+
const metas = ["stop_reason", "stop_sequence"];
|
|
9371
|
+
for (const m of metas) {
|
|
9372
|
+
if (result?.[m] !== void 0) {
|
|
9373
|
+
metadata[m] = result[m];
|
|
9374
|
+
}
|
|
9375
|
+
}
|
|
9376
|
+
return metadata;
|
|
9377
|
+
},
|
|
9378
|
+
aggregateChunks: aggregateAnthropicStreamChunks,
|
|
9379
|
+
isStreaming: (args) => {
|
|
9380
|
+
return args[0]?.stream === true;
|
|
9381
|
+
}
|
|
9382
|
+
});
|
|
9383
|
+
this.subscribeToStreamingChannel(
|
|
9384
|
+
"orchestrion:anthropic:beta.messages.create",
|
|
9385
|
+
{
|
|
9386
|
+
name: "anthropic.beta.messages.create",
|
|
9387
|
+
type: "llm" /* LLM */,
|
|
9388
|
+
extractInput: (args) => {
|
|
9389
|
+
const params = args[0] || {};
|
|
9390
|
+
const input = coalesceInput(params.messages || [], params.system);
|
|
9391
|
+
const metadata = filterFrom(params, ["messages", "system"]);
|
|
9392
|
+
return {
|
|
9393
|
+
input: processAttachmentsInInput(input),
|
|
9394
|
+
metadata: { ...metadata, provider: "anthropic" }
|
|
9395
|
+
};
|
|
9396
|
+
},
|
|
9397
|
+
extractOutput: (result) => {
|
|
9398
|
+
return result ? { role: result.role, content: result.content } : null;
|
|
9399
|
+
},
|
|
9400
|
+
extractMetrics: (result, startTime) => {
|
|
9401
|
+
const metrics = parseMetricsFromUsage2(result?.usage);
|
|
9402
|
+
if (startTime) {
|
|
9403
|
+
metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
|
|
9404
|
+
}
|
|
9405
|
+
const finalized = finalizeAnthropicTokens(metrics);
|
|
9406
|
+
return Object.fromEntries(
|
|
9407
|
+
Object.entries(finalized).filter(([, v]) => v !== void 0)
|
|
9408
|
+
);
|
|
9409
|
+
},
|
|
9410
|
+
extractMetadata: (result) => {
|
|
9411
|
+
const metadata = {};
|
|
9412
|
+
const metas = ["stop_reason", "stop_sequence"];
|
|
9413
|
+
for (const m of metas) {
|
|
9414
|
+
if (result?.[m] !== void 0) {
|
|
9415
|
+
metadata[m] = result[m];
|
|
9416
|
+
}
|
|
9417
|
+
}
|
|
9418
|
+
return metadata;
|
|
9419
|
+
},
|
|
9420
|
+
aggregateChunks: aggregateAnthropicStreamChunks,
|
|
9421
|
+
isStreaming: (args) => {
|
|
9422
|
+
return args[0]?.stream === true;
|
|
9423
|
+
}
|
|
9424
|
+
}
|
|
9425
|
+
);
|
|
9426
|
+
}
|
|
9427
|
+
/**
|
|
9428
|
+
* Subscribe to a channel for async methods that may return streams.
|
|
9429
|
+
* Handles both streaming and non-streaming responses based on the stream parameter.
|
|
9430
|
+
*/
|
|
9431
|
+
subscribeToStreamingChannel(channelName, config) {
|
|
9432
|
+
const channel = tracingChannel2(channelName);
|
|
9433
|
+
const spans = /* @__PURE__ */ new WeakMap();
|
|
9434
|
+
const handlers = {
|
|
9435
|
+
start: (event) => {
|
|
9436
|
+
const span = startSpan({
|
|
9437
|
+
name: config.name,
|
|
9438
|
+
spanAttributes: {
|
|
9439
|
+
type: config.type
|
|
9440
|
+
}
|
|
9441
|
+
});
|
|
9442
|
+
const startTime = getCurrentUnixTimestamp();
|
|
9443
|
+
spans.set(event, { span, startTime });
|
|
9444
|
+
try {
|
|
9445
|
+
const { input, metadata } = config.extractInput(event.arguments);
|
|
9446
|
+
span.log({
|
|
9447
|
+
input,
|
|
9448
|
+
metadata
|
|
9449
|
+
});
|
|
9450
|
+
} catch (error) {
|
|
9451
|
+
console.error(`Error extracting input for ${channelName}:`, error);
|
|
9452
|
+
}
|
|
9453
|
+
},
|
|
9454
|
+
asyncEnd: (event) => {
|
|
9455
|
+
const spanData = spans.get(event);
|
|
9456
|
+
if (!spanData) {
|
|
9457
|
+
return;
|
|
9458
|
+
}
|
|
9459
|
+
const { span, startTime } = spanData;
|
|
9460
|
+
const isStreaming = config.isStreaming ? config.isStreaming(event.arguments) : isAsyncIterable(event.result);
|
|
9461
|
+
if (isStreaming && isAsyncIterable(event.result)) {
|
|
9462
|
+
patchStreamIfNeeded(event.result, {
|
|
9463
|
+
onComplete: (chunks) => {
|
|
9464
|
+
try {
|
|
9465
|
+
let output;
|
|
9466
|
+
let metrics;
|
|
9467
|
+
let metadata = {};
|
|
9468
|
+
if (config.aggregateChunks) {
|
|
9469
|
+
const aggregated = config.aggregateChunks(chunks);
|
|
9470
|
+
output = aggregated.output;
|
|
9471
|
+
metrics = aggregated.metrics;
|
|
9472
|
+
metadata = aggregated.metadata || {};
|
|
9473
|
+
} else {
|
|
9474
|
+
output = config.extractOutput(chunks);
|
|
9475
|
+
metrics = config.extractMetrics(chunks, startTime);
|
|
9476
|
+
if (config.extractMetadata) {
|
|
9477
|
+
metadata = config.extractMetadata(chunks);
|
|
9478
|
+
}
|
|
9479
|
+
}
|
|
9480
|
+
if (!metrics.time_to_first_token && chunks.length > 0) {
|
|
9481
|
+
metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
|
|
9482
|
+
}
|
|
9483
|
+
span.log({
|
|
9484
|
+
output,
|
|
9485
|
+
metrics,
|
|
9486
|
+
metadata
|
|
9487
|
+
});
|
|
9488
|
+
} catch (error) {
|
|
9489
|
+
console.error(
|
|
9490
|
+
`Error extracting output for ${channelName}:`,
|
|
9491
|
+
error
|
|
9492
|
+
);
|
|
9493
|
+
} finally {
|
|
9494
|
+
span.end();
|
|
9495
|
+
}
|
|
9496
|
+
},
|
|
9497
|
+
onError: (error) => {
|
|
9498
|
+
span.log({
|
|
9499
|
+
error: error.message
|
|
9500
|
+
});
|
|
9501
|
+
span.end();
|
|
9502
|
+
}
|
|
9503
|
+
});
|
|
9504
|
+
} else {
|
|
9505
|
+
try {
|
|
9506
|
+
const output = config.extractOutput(event.result);
|
|
9507
|
+
const metrics = config.extractMetrics(event.result, startTime);
|
|
9508
|
+
const metadata = config.extractMetadata ? config.extractMetadata(event.result) : {};
|
|
9509
|
+
span.log({
|
|
9510
|
+
output,
|
|
9511
|
+
metrics,
|
|
9512
|
+
metadata
|
|
9513
|
+
});
|
|
9514
|
+
} catch (error) {
|
|
9515
|
+
console.error(`Error extracting output for ${channelName}:`, error);
|
|
9516
|
+
} finally {
|
|
9517
|
+
span.end();
|
|
9518
|
+
spans.delete(event);
|
|
9519
|
+
}
|
|
9520
|
+
}
|
|
9521
|
+
},
|
|
9522
|
+
error: (event) => {
|
|
9523
|
+
const spanData = spans.get(event);
|
|
9524
|
+
if (!spanData) {
|
|
9525
|
+
return;
|
|
9526
|
+
}
|
|
9527
|
+
const { span } = spanData;
|
|
9528
|
+
span.log({
|
|
9529
|
+
error: event.error.message
|
|
9530
|
+
});
|
|
9531
|
+
span.end();
|
|
9532
|
+
spans.delete(event);
|
|
9533
|
+
}
|
|
9534
|
+
};
|
|
9535
|
+
channel.subscribe(handlers);
|
|
9536
|
+
this.unsubscribers.push(() => {
|
|
9537
|
+
channel.unsubscribe(handlers);
|
|
9538
|
+
});
|
|
9539
|
+
}
|
|
9540
|
+
};
|
|
9541
|
+
function parseMetricsFromUsage2(usage) {
|
|
9542
|
+
if (!usage) {
|
|
9543
|
+
return {};
|
|
9544
|
+
}
|
|
9545
|
+
const metrics = {};
|
|
9546
|
+
function saveIfExistsTo(source, target) {
|
|
9547
|
+
const value = usage[source];
|
|
9548
|
+
if (value !== void 0 && value !== null && typeof value === "number") {
|
|
9549
|
+
metrics[target] = value;
|
|
9550
|
+
}
|
|
9551
|
+
}
|
|
9552
|
+
saveIfExistsTo("input_tokens", "prompt_tokens");
|
|
9553
|
+
saveIfExistsTo("output_tokens", "completion_tokens");
|
|
9554
|
+
saveIfExistsTo("cache_read_input_tokens", "prompt_cached_tokens");
|
|
9555
|
+
saveIfExistsTo("cache_creation_input_tokens", "prompt_cache_creation_tokens");
|
|
9556
|
+
return metrics;
|
|
9557
|
+
}
|
|
9558
|
+
function aggregateAnthropicStreamChunks(chunks) {
|
|
9559
|
+
const deltas = [];
|
|
9560
|
+
let metrics = {};
|
|
9561
|
+
let metadata = {};
|
|
9562
|
+
for (const chunk of chunks) {
|
|
9563
|
+
switch (chunk?.type) {
|
|
9564
|
+
case "message_start":
|
|
9565
|
+
if (chunk.message?.usage) {
|
|
9566
|
+
const initialMetrics = parseMetricsFromUsage2(chunk.message.usage);
|
|
9567
|
+
metrics = { ...metrics, ...initialMetrics };
|
|
9568
|
+
}
|
|
9569
|
+
break;
|
|
9570
|
+
case "content_block_delta":
|
|
9571
|
+
if (chunk.delta?.type === "text_delta") {
|
|
9572
|
+
const text = chunk.delta?.text;
|
|
9573
|
+
if (text) {
|
|
9574
|
+
deltas.push(text);
|
|
9575
|
+
}
|
|
9576
|
+
}
|
|
9577
|
+
break;
|
|
9578
|
+
case "message_delta":
|
|
9579
|
+
if (chunk.usage) {
|
|
9580
|
+
const finalMetrics = parseMetricsFromUsage2(chunk.usage);
|
|
9581
|
+
metrics = { ...metrics, ...finalMetrics };
|
|
9582
|
+
}
|
|
9583
|
+
if (chunk.delta) {
|
|
9584
|
+
metadata = { ...metadata, ...chunk.delta };
|
|
9585
|
+
}
|
|
9586
|
+
break;
|
|
9587
|
+
}
|
|
9588
|
+
}
|
|
9589
|
+
const output = deltas.join("");
|
|
9590
|
+
const finalized = finalizeAnthropicTokens(metrics);
|
|
9591
|
+
const filteredMetrics = Object.fromEntries(
|
|
9592
|
+
Object.entries(finalized).filter(([, v]) => v !== void 0)
|
|
9593
|
+
);
|
|
9594
|
+
return {
|
|
9595
|
+
output,
|
|
9596
|
+
metrics: filteredMetrics,
|
|
9597
|
+
metadata
|
|
9598
|
+
};
|
|
9599
|
+
}
|
|
9600
|
+
function convertBase64ToAttachment(source, contentType) {
|
|
9601
|
+
const mediaType = typeof source.media_type === "string" ? source.media_type : "image/png";
|
|
9602
|
+
const base64Data = source.data;
|
|
9603
|
+
if (base64Data && typeof base64Data === "string") {
|
|
9604
|
+
const binaryString = atob(base64Data);
|
|
9605
|
+
const bytes = new Uint8Array(binaryString.length);
|
|
9606
|
+
for (let i = 0; i < binaryString.length; i++) {
|
|
9607
|
+
bytes[i] = binaryString.charCodeAt(i);
|
|
9608
|
+
}
|
|
9609
|
+
const blob = new Blob([bytes], { type: mediaType });
|
|
9610
|
+
const extension = mediaType.split("/")[1] || "bin";
|
|
9611
|
+
const prefix = contentType === "document" ? "document" : "image";
|
|
9612
|
+
const filename = `${prefix}.${extension}`;
|
|
9613
|
+
const attachment = new Attachment({
|
|
9614
|
+
data: blob,
|
|
9615
|
+
filename,
|
|
9616
|
+
contentType: mediaType
|
|
9617
|
+
});
|
|
9618
|
+
return {
|
|
9619
|
+
...source,
|
|
9620
|
+
data: attachment
|
|
9621
|
+
};
|
|
9622
|
+
}
|
|
9623
|
+
return source;
|
|
9624
|
+
}
|
|
9625
|
+
function processAttachmentsInInput(input) {
|
|
9626
|
+
if (Array.isArray(input)) {
|
|
9627
|
+
return input.map(processAttachmentsInInput);
|
|
9628
|
+
}
|
|
9629
|
+
if (isObject(input)) {
|
|
9630
|
+
if ((input.type === "image" || input.type === "document") && isObject(input.source) && input.source.type === "base64") {
|
|
9631
|
+
return {
|
|
9632
|
+
...input,
|
|
9633
|
+
source: convertBase64ToAttachment(input.source, input.type)
|
|
9634
|
+
};
|
|
9635
|
+
}
|
|
9636
|
+
const processed = {};
|
|
9637
|
+
for (const [key, value] of Object.entries(input)) {
|
|
9638
|
+
processed[key] = processAttachmentsInInput(value);
|
|
9639
|
+
}
|
|
9640
|
+
return processed;
|
|
9641
|
+
}
|
|
9642
|
+
return input;
|
|
9643
|
+
}
|
|
9644
|
+
function coalesceInput(messages, system) {
|
|
9645
|
+
const input = (messages || []).slice();
|
|
9646
|
+
if (system) {
|
|
9647
|
+
input.push({ role: "system", content: system });
|
|
9648
|
+
}
|
|
9649
|
+
return input;
|
|
9650
|
+
}
|
|
9651
|
+
function filterFrom(obj, fieldsToRemove) {
|
|
9652
|
+
const result = {};
|
|
9653
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
9654
|
+
if (!fieldsToRemove.includes(key)) {
|
|
9655
|
+
result[key] = value;
|
|
9656
|
+
}
|
|
9657
|
+
}
|
|
9658
|
+
return result;
|
|
9659
|
+
}
|
|
9660
|
+
|
|
9661
|
+
// src/instrumentation/plugins/ai-sdk-plugin.ts
|
|
9662
|
+
import { tracingChannel as tracingChannel3 } from "dc-browser";
|
|
9663
|
+
var DEFAULT_DENY_OUTPUT_PATHS = [
|
|
9664
|
+
// v3
|
|
9665
|
+
"roundtrips[].request.body",
|
|
9666
|
+
"roundtrips[].response.headers",
|
|
9667
|
+
"rawResponse.headers",
|
|
9668
|
+
"responseMessages",
|
|
9669
|
+
// v5
|
|
9670
|
+
"request.body",
|
|
9671
|
+
"response.body",
|
|
9672
|
+
"response.headers",
|
|
9673
|
+
"steps[].request.body",
|
|
9674
|
+
"steps[].response.body",
|
|
9675
|
+
"steps[].response.headers"
|
|
9676
|
+
];
|
|
9677
|
+
var AISDKPlugin = class extends BasePlugin {
|
|
9678
|
+
unsubscribers = [];
|
|
9679
|
+
config;
|
|
9680
|
+
constructor(config = {}) {
|
|
9681
|
+
super();
|
|
9682
|
+
this.config = config;
|
|
9683
|
+
}
|
|
9684
|
+
onEnable() {
|
|
9685
|
+
this.subscribeToAISDK();
|
|
9686
|
+
}
|
|
9687
|
+
onDisable() {
|
|
9688
|
+
for (const unsubscribe of this.unsubscribers) {
|
|
9689
|
+
unsubscribe();
|
|
9690
|
+
}
|
|
9691
|
+
this.unsubscribers = [];
|
|
9692
|
+
}
|
|
9693
|
+
subscribeToAISDK() {
|
|
9694
|
+
const denyOutputPaths = this.config.denyOutputPaths || DEFAULT_DENY_OUTPUT_PATHS;
|
|
9695
|
+
this.subscribeToStreamingChannel("orchestrion:ai-sdk:generateText", {
|
|
9696
|
+
name: "generateText",
|
|
9697
|
+
type: "llm" /* LLM */,
|
|
9698
|
+
extractInput: (args) => {
|
|
9699
|
+
const params = args[0] || {};
|
|
9700
|
+
return {
|
|
9701
|
+
input: processAISDKInput(params),
|
|
9702
|
+
metadata: extractMetadataFromParams(params)
|
|
9703
|
+
};
|
|
9704
|
+
},
|
|
9705
|
+
extractOutput: (result) => {
|
|
9706
|
+
return processAISDKOutput(result, denyOutputPaths);
|
|
9707
|
+
},
|
|
9708
|
+
extractMetrics: (result, startTime) => {
|
|
9709
|
+
const metrics = extractTokenMetrics(result);
|
|
9710
|
+
if (startTime) {
|
|
9711
|
+
metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
|
|
9712
|
+
}
|
|
9713
|
+
return metrics;
|
|
9714
|
+
},
|
|
9715
|
+
aggregateChunks: aggregateAISDKChunks
|
|
9716
|
+
});
|
|
9717
|
+
this.subscribeToStreamingChannel("orchestrion:ai-sdk:streamText", {
|
|
9718
|
+
name: "streamText",
|
|
9719
|
+
type: "llm" /* LLM */,
|
|
9720
|
+
extractInput: (args) => {
|
|
9721
|
+
const params = args[0] || {};
|
|
9722
|
+
return {
|
|
9723
|
+
input: processAISDKInput(params),
|
|
9724
|
+
metadata: extractMetadataFromParams(params)
|
|
9725
|
+
};
|
|
9726
|
+
},
|
|
9727
|
+
extractOutput: (result) => {
|
|
9728
|
+
return processAISDKOutput(result, denyOutputPaths);
|
|
9729
|
+
},
|
|
9730
|
+
extractMetrics: (result, startTime) => {
|
|
9731
|
+
const metrics = extractTokenMetrics(result);
|
|
9732
|
+
if (startTime) {
|
|
9733
|
+
metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
|
|
9734
|
+
}
|
|
9735
|
+
return metrics;
|
|
9736
|
+
},
|
|
9737
|
+
aggregateChunks: aggregateAISDKChunks
|
|
9738
|
+
});
|
|
9739
|
+
this.subscribeToStreamingChannel("orchestrion:ai-sdk:generateObject", {
|
|
9740
|
+
name: "generateObject",
|
|
9741
|
+
type: "llm" /* LLM */,
|
|
9742
|
+
extractInput: (args) => {
|
|
9743
|
+
const params = args[0] || {};
|
|
9744
|
+
return {
|
|
9745
|
+
input: processAISDKInput(params),
|
|
9746
|
+
metadata: extractMetadataFromParams(params)
|
|
9747
|
+
};
|
|
9748
|
+
},
|
|
9749
|
+
extractOutput: (result) => {
|
|
9750
|
+
return processAISDKOutput(result, denyOutputPaths);
|
|
9751
|
+
},
|
|
9752
|
+
extractMetrics: (result, startTime) => {
|
|
9753
|
+
const metrics = extractTokenMetrics(result);
|
|
9754
|
+
if (startTime) {
|
|
9755
|
+
metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
|
|
9756
|
+
}
|
|
9757
|
+
return metrics;
|
|
9758
|
+
},
|
|
9759
|
+
aggregateChunks: aggregateAISDKChunks
|
|
9760
|
+
});
|
|
9761
|
+
this.subscribeToStreamingChannel("orchestrion:ai-sdk:streamObject", {
|
|
9762
|
+
name: "streamObject",
|
|
9763
|
+
type: "llm" /* LLM */,
|
|
9764
|
+
extractInput: (args) => {
|
|
9765
|
+
const params = args[0] || {};
|
|
9766
|
+
return {
|
|
9767
|
+
input: processAISDKInput(params),
|
|
9768
|
+
metadata: extractMetadataFromParams(params)
|
|
9769
|
+
};
|
|
9770
|
+
},
|
|
9771
|
+
extractOutput: (result) => {
|
|
9772
|
+
return processAISDKOutput(result, denyOutputPaths);
|
|
9773
|
+
},
|
|
9774
|
+
extractMetrics: (result, startTime) => {
|
|
9775
|
+
const metrics = extractTokenMetrics(result);
|
|
9776
|
+
if (startTime) {
|
|
9777
|
+
metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
|
|
9778
|
+
}
|
|
9779
|
+
return metrics;
|
|
9780
|
+
},
|
|
9781
|
+
aggregateChunks: aggregateAISDKChunks
|
|
9782
|
+
});
|
|
9783
|
+
this.subscribeToStreamingChannel("orchestrion:ai-sdk:Agent.generate", {
|
|
9784
|
+
name: "Agent.generate",
|
|
9785
|
+
type: "llm" /* LLM */,
|
|
9786
|
+
extractInput: (args) => {
|
|
9787
|
+
const params = args[0] || {};
|
|
9788
|
+
return {
|
|
9789
|
+
input: processAISDKInput(params),
|
|
9790
|
+
metadata: extractMetadataFromParams(params)
|
|
9791
|
+
};
|
|
9792
|
+
},
|
|
9793
|
+
extractOutput: (result) => {
|
|
9794
|
+
return processAISDKOutput(result, denyOutputPaths);
|
|
9795
|
+
},
|
|
9796
|
+
extractMetrics: (result, startTime) => {
|
|
9797
|
+
const metrics = extractTokenMetrics(result);
|
|
9798
|
+
if (startTime) {
|
|
9799
|
+
metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
|
|
9800
|
+
}
|
|
9801
|
+
return metrics;
|
|
9802
|
+
},
|
|
9803
|
+
aggregateChunks: aggregateAISDKChunks
|
|
9804
|
+
});
|
|
9805
|
+
this.subscribeToStreamingChannel("orchestrion:ai-sdk:Agent.stream", {
|
|
9806
|
+
name: "Agent.stream",
|
|
9807
|
+
type: "llm" /* LLM */,
|
|
9808
|
+
extractInput: (args) => {
|
|
9809
|
+
const params = args[0] || {};
|
|
9810
|
+
return {
|
|
9811
|
+
input: processAISDKInput(params),
|
|
9812
|
+
metadata: extractMetadataFromParams(params)
|
|
9813
|
+
};
|
|
9814
|
+
},
|
|
9815
|
+
extractOutput: (result) => {
|
|
9816
|
+
return processAISDKOutput(result, denyOutputPaths);
|
|
9817
|
+
},
|
|
9818
|
+
extractMetrics: (result, startTime) => {
|
|
9819
|
+
const metrics = extractTokenMetrics(result);
|
|
9820
|
+
if (startTime) {
|
|
9821
|
+
metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
|
|
9822
|
+
}
|
|
9823
|
+
return metrics;
|
|
9824
|
+
},
|
|
9825
|
+
aggregateChunks: aggregateAISDKChunks
|
|
9826
|
+
});
|
|
9827
|
+
}
|
|
9828
|
+
/**
|
|
9829
|
+
* Subscribe to a channel for async methods that may return streams.
|
|
9830
|
+
* Handles both streaming and non-streaming responses.
|
|
9831
|
+
*/
|
|
9832
|
+
subscribeToStreamingChannel(channelName, config) {
|
|
9833
|
+
const channel = tracingChannel3(channelName);
|
|
9834
|
+
const spans = /* @__PURE__ */ new WeakMap();
|
|
9835
|
+
const handlers = {
|
|
9836
|
+
start: (event) => {
|
|
9837
|
+
const span = startSpan({
|
|
9838
|
+
name: config.name,
|
|
9839
|
+
spanAttributes: {
|
|
9840
|
+
type: config.type
|
|
9841
|
+
}
|
|
9842
|
+
});
|
|
9843
|
+
const startTime = getCurrentUnixTimestamp();
|
|
9844
|
+
spans.set(event, { span, startTime });
|
|
9845
|
+
try {
|
|
9846
|
+
const { input, metadata } = config.extractInput(event.arguments);
|
|
9847
|
+
span.log({
|
|
9848
|
+
input,
|
|
9849
|
+
metadata
|
|
9850
|
+
});
|
|
9851
|
+
} catch (error) {
|
|
9852
|
+
console.error(`Error extracting input for ${channelName}:`, error);
|
|
9853
|
+
}
|
|
9854
|
+
},
|
|
9855
|
+
asyncEnd: (event) => {
|
|
9856
|
+
const spanData = spans.get(event);
|
|
9857
|
+
if (!spanData) {
|
|
9858
|
+
return;
|
|
9859
|
+
}
|
|
9860
|
+
const { span, startTime } = spanData;
|
|
9861
|
+
if (isAsyncIterable(event.result)) {
|
|
9862
|
+
patchStreamIfNeeded(event.result, {
|
|
9863
|
+
onComplete: (chunks) => {
|
|
9864
|
+
try {
|
|
9865
|
+
let output;
|
|
9866
|
+
let metrics;
|
|
9867
|
+
if (config.aggregateChunks) {
|
|
9868
|
+
const aggregated = config.aggregateChunks(chunks);
|
|
9869
|
+
output = aggregated.output;
|
|
9870
|
+
metrics = aggregated.metrics;
|
|
9871
|
+
} else {
|
|
9872
|
+
output = config.extractOutput(chunks);
|
|
9873
|
+
metrics = config.extractMetrics(chunks, startTime);
|
|
9874
|
+
}
|
|
9875
|
+
if (!metrics.time_to_first_token && chunks.length > 0) {
|
|
9876
|
+
metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
|
|
9877
|
+
}
|
|
9878
|
+
span.log({
|
|
9879
|
+
output,
|
|
9880
|
+
metrics
|
|
9881
|
+
});
|
|
9882
|
+
} catch (error) {
|
|
9883
|
+
console.error(
|
|
9884
|
+
`Error extracting output for ${channelName}:`,
|
|
9885
|
+
error
|
|
9886
|
+
);
|
|
9887
|
+
} finally {
|
|
9888
|
+
span.end();
|
|
9889
|
+
}
|
|
9890
|
+
},
|
|
9891
|
+
onError: (error) => {
|
|
9892
|
+
span.log({
|
|
9893
|
+
error: error.message
|
|
9894
|
+
});
|
|
9895
|
+
span.end();
|
|
9896
|
+
}
|
|
9897
|
+
});
|
|
9898
|
+
} else {
|
|
9899
|
+
try {
|
|
9900
|
+
const output = config.extractOutput(event.result);
|
|
9901
|
+
const metrics = config.extractMetrics(event.result, startTime);
|
|
9902
|
+
span.log({
|
|
9903
|
+
output,
|
|
9904
|
+
metrics
|
|
9905
|
+
});
|
|
9906
|
+
} catch (error) {
|
|
9907
|
+
console.error(`Error extracting output for ${channelName}:`, error);
|
|
9908
|
+
} finally {
|
|
9909
|
+
span.end();
|
|
9910
|
+
spans.delete(event);
|
|
9911
|
+
}
|
|
9912
|
+
}
|
|
9913
|
+
},
|
|
9914
|
+
error: (event) => {
|
|
9915
|
+
const spanData = spans.get(event);
|
|
9916
|
+
if (!spanData) {
|
|
9917
|
+
return;
|
|
9918
|
+
}
|
|
9919
|
+
const { span } = spanData;
|
|
9920
|
+
span.log({
|
|
9921
|
+
error: event.error.message
|
|
9922
|
+
});
|
|
9923
|
+
span.end();
|
|
9924
|
+
spans.delete(event);
|
|
9925
|
+
}
|
|
9926
|
+
};
|
|
9927
|
+
channel.subscribe(handlers);
|
|
9928
|
+
this.unsubscribers.push(() => {
|
|
9929
|
+
channel.unsubscribe(handlers);
|
|
9930
|
+
});
|
|
9931
|
+
}
|
|
9932
|
+
};
|
|
9933
|
+
function processAISDKInput(params) {
|
|
9934
|
+
if (!params) return params;
|
|
9935
|
+
return processInputAttachments(params);
|
|
9936
|
+
}
|
|
9937
|
+
function extractMetadataFromParams(params) {
|
|
9938
|
+
const metadata = {
|
|
9939
|
+
braintrust: {
|
|
9940
|
+
integration_name: "ai-sdk",
|
|
9941
|
+
sdk_language: "typescript"
|
|
9942
|
+
}
|
|
9943
|
+
};
|
|
9944
|
+
const { model, provider } = serializeModelWithProvider(params.model);
|
|
9945
|
+
if (model) {
|
|
9946
|
+
metadata.model = model;
|
|
9947
|
+
}
|
|
9948
|
+
if (provider) {
|
|
9949
|
+
metadata.provider = provider;
|
|
9950
|
+
}
|
|
9951
|
+
return metadata;
|
|
9952
|
+
}
|
|
9953
|
+
function processAISDKOutput(output, denyOutputPaths) {
|
|
9954
|
+
if (!output) return output;
|
|
9955
|
+
const getterValues = extractGetterValues(output);
|
|
9956
|
+
const merged = { ...output, ...getterValues };
|
|
9957
|
+
return omit(merged, denyOutputPaths);
|
|
9958
|
+
}
|
|
9959
|
+
function extractTokenMetrics(result) {
|
|
9960
|
+
const metrics = {};
|
|
9961
|
+
let usage = result?.totalUsage || result?.usage;
|
|
9962
|
+
if (!usage && result) {
|
|
9963
|
+
try {
|
|
9964
|
+
if ("totalUsage" in result && typeof result.totalUsage !== "function") {
|
|
9965
|
+
usage = result.totalUsage;
|
|
9966
|
+
} else if ("usage" in result && typeof result.usage !== "function") {
|
|
9967
|
+
usage = result.usage;
|
|
9968
|
+
}
|
|
9969
|
+
} catch {
|
|
9970
|
+
}
|
|
9971
|
+
}
|
|
9972
|
+
if (!usage) {
|
|
9973
|
+
return metrics;
|
|
9974
|
+
}
|
|
9975
|
+
const promptTokens = firstNumber(
|
|
9976
|
+
usage.inputTokens?.total,
|
|
9977
|
+
usage.inputTokens,
|
|
9978
|
+
usage.promptTokens,
|
|
9979
|
+
usage.prompt_tokens
|
|
9980
|
+
);
|
|
9981
|
+
if (promptTokens !== void 0) {
|
|
9982
|
+
metrics.prompt_tokens = promptTokens;
|
|
9983
|
+
}
|
|
9984
|
+
const completionTokens = firstNumber(
|
|
9985
|
+
usage.outputTokens?.total,
|
|
9986
|
+
usage.outputTokens,
|
|
9987
|
+
usage.completionTokens,
|
|
9988
|
+
usage.completion_tokens
|
|
9989
|
+
);
|
|
9990
|
+
if (completionTokens !== void 0) {
|
|
9991
|
+
metrics.completion_tokens = completionTokens;
|
|
9992
|
+
}
|
|
9993
|
+
const totalTokens = firstNumber(
|
|
9994
|
+
usage.totalTokens,
|
|
9995
|
+
usage.tokens,
|
|
9996
|
+
usage.total_tokens
|
|
9997
|
+
);
|
|
9998
|
+
if (totalTokens !== void 0) {
|
|
9999
|
+
metrics.tokens = totalTokens;
|
|
10000
|
+
}
|
|
10001
|
+
const cost = extractCostFromResult(result);
|
|
10002
|
+
if (cost !== void 0) {
|
|
10003
|
+
metrics.estimated_cost = cost;
|
|
10004
|
+
}
|
|
10005
|
+
return metrics;
|
|
10006
|
+
}
|
|
10007
|
+
function aggregateAISDKChunks(chunks) {
|
|
10008
|
+
const lastChunk = chunks[chunks.length - 1];
|
|
10009
|
+
const output = {};
|
|
10010
|
+
let metrics = {};
|
|
10011
|
+
if (lastChunk) {
|
|
10012
|
+
metrics = extractTokenMetrics(lastChunk);
|
|
10013
|
+
if (lastChunk.text !== void 0) {
|
|
10014
|
+
output.text = lastChunk.text;
|
|
10015
|
+
}
|
|
10016
|
+
if (lastChunk.object !== void 0) {
|
|
10017
|
+
output.object = lastChunk.object;
|
|
10018
|
+
}
|
|
10019
|
+
if (lastChunk.finishReason !== void 0) {
|
|
10020
|
+
output.finishReason = lastChunk.finishReason;
|
|
10021
|
+
}
|
|
10022
|
+
if (lastChunk.toolCalls !== void 0) {
|
|
10023
|
+
output.toolCalls = lastChunk.toolCalls;
|
|
10024
|
+
}
|
|
10025
|
+
}
|
|
10026
|
+
return { output, metrics };
|
|
10027
|
+
}
|
|
10028
|
+
function extractGetterValues(obj) {
|
|
10029
|
+
const getterValues = {};
|
|
10030
|
+
const getterNames = [
|
|
10031
|
+
"text",
|
|
10032
|
+
"object",
|
|
10033
|
+
"finishReason",
|
|
10034
|
+
"usage",
|
|
10035
|
+
"totalUsage",
|
|
10036
|
+
"toolCalls",
|
|
10037
|
+
"toolResults",
|
|
10038
|
+
"warnings",
|
|
10039
|
+
"experimental_providerMetadata",
|
|
10040
|
+
"providerMetadata",
|
|
10041
|
+
"rawResponse",
|
|
10042
|
+
"response"
|
|
10043
|
+
];
|
|
10044
|
+
for (const name of getterNames) {
|
|
10045
|
+
try {
|
|
10046
|
+
if (obj && name in obj && typeof obj[name] !== "function") {
|
|
10047
|
+
getterValues[name] = obj[name];
|
|
10048
|
+
}
|
|
10049
|
+
} catch {
|
|
10050
|
+
}
|
|
10051
|
+
}
|
|
10052
|
+
return getterValues;
|
|
10053
|
+
}
|
|
10054
|
+
function serializeModelWithProvider(model) {
|
|
10055
|
+
const modelId = typeof model === "string" ? model : model?.modelId;
|
|
10056
|
+
const explicitProvider = typeof model === "object" ? model?.provider : void 0;
|
|
10057
|
+
if (!modelId) {
|
|
10058
|
+
return { model: modelId, provider: explicitProvider };
|
|
10059
|
+
}
|
|
10060
|
+
const parsed = parseGatewayModelString(modelId);
|
|
10061
|
+
return {
|
|
10062
|
+
model: parsed.model,
|
|
10063
|
+
provider: explicitProvider || parsed.provider
|
|
10064
|
+
};
|
|
10065
|
+
}
|
|
10066
|
+
function parseGatewayModelString(modelString) {
|
|
10067
|
+
if (!modelString || typeof modelString !== "string") {
|
|
10068
|
+
return { model: modelString };
|
|
10069
|
+
}
|
|
10070
|
+
const slashIndex = modelString.indexOf("/");
|
|
10071
|
+
if (slashIndex > 0 && slashIndex < modelString.length - 1) {
|
|
10072
|
+
return {
|
|
10073
|
+
provider: modelString.substring(0, slashIndex),
|
|
10074
|
+
model: modelString.substring(slashIndex + 1)
|
|
10075
|
+
};
|
|
10076
|
+
}
|
|
10077
|
+
return { model: modelString };
|
|
10078
|
+
}
|
|
10079
|
+
function extractCostFromResult(result) {
|
|
10080
|
+
if (result?.steps && Array.isArray(result.steps) && result.steps.length > 0) {
|
|
10081
|
+
let totalCost = 0;
|
|
10082
|
+
let foundCost = false;
|
|
10083
|
+
for (const step of result.steps) {
|
|
10084
|
+
const gateway2 = step?.providerMetadata?.gateway;
|
|
10085
|
+
const stepCost = parseGatewayCost(gateway2?.cost) || parseGatewayCost(gateway2?.marketCost);
|
|
10086
|
+
if (stepCost !== void 0 && stepCost > 0) {
|
|
10087
|
+
totalCost += stepCost;
|
|
10088
|
+
foundCost = true;
|
|
10089
|
+
}
|
|
10090
|
+
}
|
|
10091
|
+
if (foundCost) {
|
|
10092
|
+
return totalCost;
|
|
10093
|
+
}
|
|
10094
|
+
}
|
|
10095
|
+
const gateway = result?.providerMetadata?.gateway;
|
|
10096
|
+
const directCost = parseGatewayCost(gateway?.cost) || parseGatewayCost(gateway?.marketCost);
|
|
10097
|
+
if (directCost !== void 0 && directCost > 0) {
|
|
10098
|
+
return directCost;
|
|
10099
|
+
}
|
|
10100
|
+
return void 0;
|
|
10101
|
+
}
|
|
10102
|
+
function parseGatewayCost(cost) {
|
|
10103
|
+
if (cost === void 0 || cost === null) {
|
|
10104
|
+
return void 0;
|
|
10105
|
+
}
|
|
10106
|
+
if (typeof cost === "number") {
|
|
10107
|
+
return cost;
|
|
10108
|
+
}
|
|
10109
|
+
if (typeof cost === "string") {
|
|
10110
|
+
const parsed = parseFloat(cost);
|
|
10111
|
+
if (!isNaN(parsed)) {
|
|
10112
|
+
return parsed;
|
|
10113
|
+
}
|
|
10114
|
+
}
|
|
10115
|
+
return void 0;
|
|
10116
|
+
}
|
|
10117
|
+
function firstNumber(...values) {
|
|
10118
|
+
for (const v of values) {
|
|
10119
|
+
if (typeof v === "number") {
|
|
10120
|
+
return v;
|
|
10121
|
+
}
|
|
10122
|
+
}
|
|
10123
|
+
return void 0;
|
|
10124
|
+
}
|
|
10125
|
+
function deepCopy(obj) {
|
|
10126
|
+
return JSON.parse(JSON.stringify(obj));
|
|
10127
|
+
}
|
|
10128
|
+
function parsePath(path2) {
|
|
10129
|
+
const keys = [];
|
|
10130
|
+
let current = "";
|
|
10131
|
+
for (let i = 0; i < path2.length; i++) {
|
|
10132
|
+
const char = path2[i];
|
|
10133
|
+
if (char === ".") {
|
|
10134
|
+
if (current) {
|
|
10135
|
+
keys.push(current);
|
|
10136
|
+
current = "";
|
|
10137
|
+
}
|
|
10138
|
+
} else if (char === "[") {
|
|
10139
|
+
if (current) {
|
|
10140
|
+
keys.push(current);
|
|
10141
|
+
current = "";
|
|
10142
|
+
}
|
|
10143
|
+
let bracketContent = "";
|
|
10144
|
+
i++;
|
|
10145
|
+
while (i < path2.length && path2[i] !== "]") {
|
|
10146
|
+
bracketContent += path2[i];
|
|
10147
|
+
i++;
|
|
10148
|
+
}
|
|
10149
|
+
if (bracketContent === "") {
|
|
10150
|
+
keys.push("[]");
|
|
10151
|
+
} else {
|
|
10152
|
+
const index = parseInt(bracketContent, 10);
|
|
10153
|
+
keys.push(isNaN(index) ? bracketContent : index);
|
|
10154
|
+
}
|
|
10155
|
+
} else {
|
|
10156
|
+
current += char;
|
|
10157
|
+
}
|
|
10158
|
+
}
|
|
10159
|
+
if (current) {
|
|
10160
|
+
keys.push(current);
|
|
10161
|
+
}
|
|
10162
|
+
return keys;
|
|
10163
|
+
}
|
|
10164
|
+
function omitAtPath(obj, keys) {
|
|
10165
|
+
if (keys.length === 0) return;
|
|
10166
|
+
const firstKey = keys[0];
|
|
10167
|
+
const remainingKeys = keys.slice(1);
|
|
10168
|
+
if (firstKey === "[]") {
|
|
10169
|
+
if (Array.isArray(obj)) {
|
|
10170
|
+
obj.forEach((item) => {
|
|
10171
|
+
if (remainingKeys.length > 0) {
|
|
10172
|
+
omitAtPath(item, remainingKeys);
|
|
10173
|
+
}
|
|
10174
|
+
});
|
|
10175
|
+
}
|
|
10176
|
+
} else if (remainingKeys.length === 0) {
|
|
10177
|
+
if (obj && typeof obj === "object" && firstKey in obj) {
|
|
10178
|
+
obj[firstKey] = "<omitted>";
|
|
10179
|
+
}
|
|
10180
|
+
} else {
|
|
10181
|
+
if (obj && typeof obj === "object" && firstKey in obj) {
|
|
10182
|
+
omitAtPath(obj[firstKey], remainingKeys);
|
|
10183
|
+
}
|
|
10184
|
+
}
|
|
10185
|
+
}
|
|
10186
|
+
function omit(obj, paths) {
|
|
10187
|
+
const result = deepCopy(obj);
|
|
10188
|
+
for (const path2 of paths) {
|
|
10189
|
+
const keys = parsePath(path2);
|
|
10190
|
+
omitAtPath(result, keys);
|
|
10191
|
+
}
|
|
10192
|
+
return result;
|
|
10193
|
+
}
|
|
10194
|
+
|
|
10195
|
+
// src/instrumentation/plugins/claude-agent-sdk-plugin.ts
|
|
10196
|
+
import { tracingChannel as tracingChannel4 } from "dc-browser";
|
|
10197
|
+
function filterSerializableOptions(options) {
|
|
10198
|
+
const allowedKeys = [
|
|
10199
|
+
"model",
|
|
10200
|
+
"maxTurns",
|
|
10201
|
+
"cwd",
|
|
10202
|
+
"continue",
|
|
10203
|
+
"allowedTools",
|
|
10204
|
+
"disallowedTools",
|
|
10205
|
+
"additionalDirectories",
|
|
10206
|
+
"permissionMode",
|
|
10207
|
+
"debug",
|
|
10208
|
+
"apiKey",
|
|
10209
|
+
"apiKeySource",
|
|
10210
|
+
"agentName",
|
|
10211
|
+
"instructions"
|
|
10212
|
+
];
|
|
10213
|
+
const filtered = {};
|
|
10214
|
+
for (const key of allowedKeys) {
|
|
10215
|
+
if (options[key] !== void 0) {
|
|
10216
|
+
filtered[key] = options[key];
|
|
10217
|
+
}
|
|
10218
|
+
}
|
|
10219
|
+
return filtered;
|
|
10220
|
+
}
|
|
10221
|
+
function getNumberProperty(obj, key) {
|
|
10222
|
+
if (!obj || typeof obj !== "object" || !(key in obj)) {
|
|
10223
|
+
return void 0;
|
|
10224
|
+
}
|
|
10225
|
+
const value = Reflect.get(obj, key);
|
|
10226
|
+
return typeof value === "number" ? value : void 0;
|
|
10227
|
+
}
|
|
10228
|
+
function extractUsageFromMessage(message) {
|
|
10229
|
+
const metrics = {};
|
|
10230
|
+
let usage;
|
|
10231
|
+
if (message.type === "assistant") {
|
|
10232
|
+
usage = message.message?.usage;
|
|
10233
|
+
} else if (message.type === "result") {
|
|
10234
|
+
usage = message.usage;
|
|
10235
|
+
}
|
|
10236
|
+
if (!usage || typeof usage !== "object") {
|
|
10237
|
+
return metrics;
|
|
10238
|
+
}
|
|
10239
|
+
const inputTokens = getNumberProperty(usage, "input_tokens");
|
|
10240
|
+
if (inputTokens !== void 0) {
|
|
10241
|
+
metrics.prompt_tokens = inputTokens;
|
|
10242
|
+
}
|
|
10243
|
+
const outputTokens = getNumberProperty(usage, "output_tokens");
|
|
10244
|
+
if (outputTokens !== void 0) {
|
|
10245
|
+
metrics.completion_tokens = outputTokens;
|
|
10246
|
+
}
|
|
10247
|
+
const cacheReadTokens = getNumberProperty(usage, "cache_read_input_tokens") || 0;
|
|
10248
|
+
const cacheCreationTokens = getNumberProperty(usage, "cache_creation_input_tokens") || 0;
|
|
10249
|
+
if (cacheReadTokens > 0 || cacheCreationTokens > 0) {
|
|
10250
|
+
const cacheTokens = extractAnthropicCacheTokens(
|
|
10251
|
+
cacheReadTokens,
|
|
10252
|
+
cacheCreationTokens
|
|
10253
|
+
);
|
|
10254
|
+
Object.assign(metrics, cacheTokens);
|
|
10255
|
+
}
|
|
10256
|
+
if (Object.keys(metrics).length > 0) {
|
|
10257
|
+
Object.assign(metrics, finalizeAnthropicTokens(metrics));
|
|
10258
|
+
}
|
|
10259
|
+
return metrics;
|
|
10260
|
+
}
|
|
10261
|
+
function buildLLMInput(prompt, conversationHistory) {
|
|
10262
|
+
const promptMessage = typeof prompt === "string" ? { content: prompt, role: "user" } : void 0;
|
|
10263
|
+
const inputParts = [
|
|
10264
|
+
...promptMessage ? [promptMessage] : [],
|
|
10265
|
+
...conversationHistory
|
|
10266
|
+
];
|
|
10267
|
+
return inputParts.length > 0 ? inputParts : void 0;
|
|
10268
|
+
}
|
|
10269
|
+
async function createLLMSpanForMessages(messages, prompt, conversationHistory, options, startTime, parentSpan) {
|
|
10270
|
+
if (messages.length === 0) return void 0;
|
|
10271
|
+
const lastMessage = messages[messages.length - 1];
|
|
10272
|
+
if (lastMessage.type !== "assistant" || !lastMessage.message?.usage) {
|
|
10273
|
+
return void 0;
|
|
10274
|
+
}
|
|
10275
|
+
const model = lastMessage.message.model || options.model;
|
|
10276
|
+
const usage = extractUsageFromMessage(lastMessage);
|
|
10277
|
+
const input = buildLLMInput(prompt, conversationHistory);
|
|
10278
|
+
const outputs = messages.map(
|
|
10279
|
+
(m) => m.message?.content && m.message?.role ? { content: m.message.content, role: m.message.role } : void 0
|
|
10280
|
+
).filter((c) => c !== void 0);
|
|
10281
|
+
const span = startSpan({
|
|
10282
|
+
name: "anthropic.messages.create",
|
|
10283
|
+
spanAttributes: {
|
|
10284
|
+
type: "llm" /* LLM */
|
|
10285
|
+
},
|
|
10286
|
+
startTime,
|
|
10287
|
+
parent: parentSpan
|
|
10288
|
+
});
|
|
10289
|
+
span.log({
|
|
10290
|
+
input,
|
|
10291
|
+
output: outputs,
|
|
10292
|
+
metadata: model ? { model } : void 0,
|
|
10293
|
+
metrics: usage
|
|
10294
|
+
});
|
|
10295
|
+
await span.end();
|
|
10296
|
+
return lastMessage.message?.content && lastMessage.message?.role ? { content: lastMessage.message.content, role: lastMessage.message.role } : void 0;
|
|
10297
|
+
}
|
|
10298
|
+
var ClaudeAgentSDKPlugin = class extends BasePlugin {
|
|
10299
|
+
unsubscribers = [];
|
|
10300
|
+
onEnable() {
|
|
10301
|
+
this.subscribeToQuery();
|
|
10302
|
+
}
|
|
10303
|
+
onDisable() {
|
|
10304
|
+
for (const unsubscribe of this.unsubscribers) {
|
|
10305
|
+
unsubscribe();
|
|
10306
|
+
}
|
|
10307
|
+
this.unsubscribers = [];
|
|
10308
|
+
}
|
|
10309
|
+
/**
|
|
10310
|
+
* Subscribe to the query channel for agent interactions.
|
|
10311
|
+
* Handles streaming responses and traces both the top-level agent task
|
|
10312
|
+
* and individual LLM calls.
|
|
10313
|
+
*/
|
|
10314
|
+
subscribeToQuery() {
|
|
10315
|
+
const channel = tracingChannel4("orchestrion:claude-agent-sdk:query");
|
|
10316
|
+
const spans = /* @__PURE__ */ new WeakMap();
|
|
10317
|
+
const handlers = {
|
|
10318
|
+
start: (event) => {
|
|
10319
|
+
const params = event.arguments[0] ?? {};
|
|
10320
|
+
const { prompt, options = {} } = params;
|
|
10321
|
+
const span = startSpan({
|
|
10322
|
+
name: "Claude Agent",
|
|
10323
|
+
spanAttributes: {
|
|
10324
|
+
type: "task" /* TASK */
|
|
10325
|
+
}
|
|
10326
|
+
});
|
|
10327
|
+
const startTime = getCurrentUnixTimestamp();
|
|
10328
|
+
try {
|
|
10329
|
+
span.log({
|
|
10330
|
+
input: typeof prompt === "string" ? prompt : {
|
|
10331
|
+
type: "streaming",
|
|
10332
|
+
description: "AsyncIterable<SDKMessage>"
|
|
10333
|
+
},
|
|
10334
|
+
metadata: filterSerializableOptions(options)
|
|
10335
|
+
});
|
|
10336
|
+
} catch (error) {
|
|
10337
|
+
console.error("Error extracting input for Claude Agent SDK:", error);
|
|
10338
|
+
}
|
|
10339
|
+
spans.set(event, {
|
|
10340
|
+
span,
|
|
10341
|
+
startTime,
|
|
10342
|
+
conversationHistory: [],
|
|
10343
|
+
currentMessages: [],
|
|
10344
|
+
currentMessageId: void 0,
|
|
10345
|
+
currentMessageStartTime: startTime,
|
|
10346
|
+
accumulatedOutputTokens: 0
|
|
10347
|
+
});
|
|
10348
|
+
},
|
|
10349
|
+
asyncEnd: (event) => {
|
|
10350
|
+
const spanData = spans.get(event);
|
|
10351
|
+
if (!spanData) {
|
|
10352
|
+
return;
|
|
10353
|
+
}
|
|
10354
|
+
if (isAsyncIterable(event.result)) {
|
|
10355
|
+
patchStreamIfNeeded(event.result, {
|
|
10356
|
+
onChunk: async (message) => {
|
|
10357
|
+
const currentTime = getCurrentUnixTimestamp();
|
|
10358
|
+
const params = event.arguments[0];
|
|
10359
|
+
const { prompt, options = {} } = params;
|
|
10360
|
+
const messageId = message.message?.id;
|
|
10361
|
+
if (messageId && messageId !== spanData.currentMessageId) {
|
|
10362
|
+
if (spanData.currentMessages.length > 0) {
|
|
10363
|
+
const finalMessage = await createLLMSpanForMessages(
|
|
10364
|
+
spanData.currentMessages,
|
|
10365
|
+
prompt,
|
|
10366
|
+
spanData.conversationHistory,
|
|
10367
|
+
options,
|
|
10368
|
+
spanData.currentMessageStartTime,
|
|
10369
|
+
await spanData.span.export()
|
|
10370
|
+
);
|
|
10371
|
+
if (finalMessage) {
|
|
10372
|
+
spanData.conversationHistory.push(finalMessage);
|
|
10373
|
+
}
|
|
10374
|
+
const lastMessage = spanData.currentMessages[spanData.currentMessages.length - 1];
|
|
10375
|
+
if (lastMessage?.message?.usage) {
|
|
10376
|
+
const outputTokens = getNumberProperty(
|
|
10377
|
+
lastMessage.message.usage,
|
|
10378
|
+
"output_tokens"
|
|
10379
|
+
) || 0;
|
|
10380
|
+
spanData.accumulatedOutputTokens += outputTokens;
|
|
10381
|
+
}
|
|
10382
|
+
spanData.currentMessages = [];
|
|
10383
|
+
}
|
|
10384
|
+
spanData.currentMessageId = messageId;
|
|
10385
|
+
spanData.currentMessageStartTime = currentTime;
|
|
10386
|
+
}
|
|
10387
|
+
if (message.type === "assistant" && message.message?.usage) {
|
|
10388
|
+
spanData.currentMessages.push(message);
|
|
10389
|
+
}
|
|
10390
|
+
if (message.type === "result" && message.usage) {
|
|
10391
|
+
const finalUsageMetrics = extractUsageFromMessage(message);
|
|
10392
|
+
if (spanData.currentMessages.length > 0 && finalUsageMetrics.completion_tokens !== void 0) {
|
|
10393
|
+
const lastMessage = spanData.currentMessages[spanData.currentMessages.length - 1];
|
|
10394
|
+
if (lastMessage?.message?.usage) {
|
|
10395
|
+
const adjustedTokens = finalUsageMetrics.completion_tokens - spanData.accumulatedOutputTokens;
|
|
10396
|
+
if (adjustedTokens >= 0) {
|
|
10397
|
+
lastMessage.message.usage.output_tokens = adjustedTokens;
|
|
10398
|
+
}
|
|
10399
|
+
}
|
|
10400
|
+
}
|
|
10401
|
+
const result_metadata = {};
|
|
10402
|
+
if (message.num_turns !== void 0) {
|
|
10403
|
+
result_metadata.num_turns = message.num_turns;
|
|
10404
|
+
}
|
|
10405
|
+
if (message.session_id !== void 0) {
|
|
10406
|
+
result_metadata.session_id = message.session_id;
|
|
10407
|
+
}
|
|
10408
|
+
if (Object.keys(result_metadata).length > 0) {
|
|
10409
|
+
spanData.span.log({
|
|
10410
|
+
metadata: result_metadata
|
|
10411
|
+
});
|
|
10412
|
+
}
|
|
10413
|
+
}
|
|
10414
|
+
},
|
|
10415
|
+
onComplete: async () => {
|
|
10416
|
+
try {
|
|
10417
|
+
const params = event.arguments[0];
|
|
10418
|
+
const { prompt, options = {} } = params;
|
|
10419
|
+
if (spanData.currentMessages.length > 0) {
|
|
10420
|
+
const finalMessage = await createLLMSpanForMessages(
|
|
10421
|
+
spanData.currentMessages,
|
|
10422
|
+
prompt,
|
|
10423
|
+
spanData.conversationHistory,
|
|
10424
|
+
options,
|
|
10425
|
+
spanData.currentMessageStartTime,
|
|
10426
|
+
await spanData.span.export()
|
|
10427
|
+
);
|
|
10428
|
+
if (finalMessage) {
|
|
10429
|
+
spanData.conversationHistory.push(finalMessage);
|
|
10430
|
+
}
|
|
10431
|
+
}
|
|
10432
|
+
spanData.span.log({
|
|
10433
|
+
output: spanData.conversationHistory.length > 0 ? spanData.conversationHistory[spanData.conversationHistory.length - 1] : void 0
|
|
10434
|
+
});
|
|
10435
|
+
} catch (error) {
|
|
10436
|
+
console.error(
|
|
10437
|
+
"Error extracting output for Claude Agent SDK:",
|
|
10438
|
+
error
|
|
10439
|
+
);
|
|
10440
|
+
} finally {
|
|
10441
|
+
spanData.span.end();
|
|
10442
|
+
spans.delete(event);
|
|
10443
|
+
}
|
|
10444
|
+
},
|
|
10445
|
+
onError: (error) => {
|
|
10446
|
+
spanData.span.log({
|
|
10447
|
+
error: error.message
|
|
10448
|
+
});
|
|
10449
|
+
spanData.span.end();
|
|
10450
|
+
spans.delete(event);
|
|
10451
|
+
}
|
|
10452
|
+
});
|
|
10453
|
+
} else {
|
|
10454
|
+
try {
|
|
10455
|
+
spanData.span.log({
|
|
10456
|
+
output: event.result
|
|
10457
|
+
});
|
|
10458
|
+
} catch (error) {
|
|
10459
|
+
console.error(
|
|
10460
|
+
"Error extracting output for Claude Agent SDK:",
|
|
10461
|
+
error
|
|
10462
|
+
);
|
|
10463
|
+
} finally {
|
|
10464
|
+
spanData.span.end();
|
|
10465
|
+
spans.delete(event);
|
|
10466
|
+
}
|
|
10467
|
+
}
|
|
10468
|
+
},
|
|
10469
|
+
error: (event) => {
|
|
10470
|
+
const spanData = spans.get(event);
|
|
10471
|
+
if (!spanData) {
|
|
10472
|
+
return;
|
|
10473
|
+
}
|
|
10474
|
+
const { span } = spanData;
|
|
10475
|
+
span.log({
|
|
10476
|
+
error: event.error.message
|
|
10477
|
+
});
|
|
10478
|
+
span.end();
|
|
10479
|
+
spans.delete(event);
|
|
10480
|
+
}
|
|
10481
|
+
};
|
|
10482
|
+
channel.subscribe(handlers);
|
|
10483
|
+
this.unsubscribers.push(() => {
|
|
10484
|
+
channel.unsubscribe(handlers);
|
|
10485
|
+
});
|
|
10486
|
+
}
|
|
10487
|
+
};
|
|
10488
|
+
|
|
10489
|
+
// src/instrumentation/plugins/google-genai-plugin.ts
|
|
10490
|
+
import { tracingChannel as tracingChannel5 } from "dc-browser";
|
|
10491
|
+
var GoogleGenAIPlugin = class extends BasePlugin {
|
|
10492
|
+
unsubscribers = [];
|
|
10493
|
+
onEnable() {
|
|
10494
|
+
this.subscribeToGoogleGenAIChannels();
|
|
10495
|
+
}
|
|
10496
|
+
onDisable() {
|
|
10497
|
+
for (const unsubscribe of this.unsubscribers) {
|
|
10498
|
+
unsubscribe();
|
|
10499
|
+
}
|
|
10500
|
+
this.unsubscribers = [];
|
|
10501
|
+
}
|
|
10502
|
+
subscribeToGoogleGenAIChannels() {
|
|
10503
|
+
this.subscribeToChannel("orchestrion:google-genai:models.generateContent", {
|
|
10504
|
+
name: "google-genai.generateContent",
|
|
10505
|
+
type: "llm" /* LLM */,
|
|
10506
|
+
extractInput: (args) => {
|
|
10507
|
+
const params = args[0] || {};
|
|
10508
|
+
const input = serializeInput(params);
|
|
10509
|
+
const metadata = extractMetadata(params);
|
|
10510
|
+
return {
|
|
10511
|
+
input,
|
|
10512
|
+
metadata: { ...metadata, provider: "google-genai" }
|
|
10513
|
+
};
|
|
10514
|
+
},
|
|
10515
|
+
extractOutput: (result) => {
|
|
10516
|
+
return result;
|
|
10517
|
+
},
|
|
10518
|
+
extractMetrics: (result, startTime) => {
|
|
10519
|
+
return extractGenerateContentMetrics(result, startTime);
|
|
10520
|
+
}
|
|
10521
|
+
});
|
|
10522
|
+
this.subscribeToGoogleStreamingChannel(
|
|
10523
|
+
"orchestrion:google-genai:models.generateContentStream",
|
|
10524
|
+
{
|
|
10525
|
+
name: "google-genai.generateContentStream",
|
|
10526
|
+
type: "llm" /* LLM */,
|
|
10527
|
+
extractInput: (args) => {
|
|
10528
|
+
const params = args[0] || {};
|
|
10529
|
+
const input = serializeInput(params);
|
|
10530
|
+
const metadata = extractMetadata(params);
|
|
10531
|
+
return {
|
|
10532
|
+
input,
|
|
10533
|
+
metadata: { ...metadata, provider: "google-genai" }
|
|
10534
|
+
};
|
|
10535
|
+
},
|
|
10536
|
+
aggregateChunks: aggregateGenerateContentChunks
|
|
10537
|
+
}
|
|
10538
|
+
);
|
|
10539
|
+
}
|
|
10540
|
+
subscribeToChannel(channelName, config) {
|
|
10541
|
+
const channel = tracingChannel5(channelName);
|
|
10542
|
+
const spans = /* @__PURE__ */ new WeakMap();
|
|
10543
|
+
const handlers = {
|
|
10544
|
+
start: (event) => {
|
|
10545
|
+
const span = startSpan({
|
|
10546
|
+
name: config.name,
|
|
10547
|
+
spanAttributes: {
|
|
10548
|
+
type: config.type
|
|
10549
|
+
}
|
|
10550
|
+
});
|
|
10551
|
+
const startTime = getCurrentUnixTimestamp();
|
|
10552
|
+
spans.set(event, { span, startTime });
|
|
10553
|
+
try {
|
|
10554
|
+
const { input, metadata } = config.extractInput(event.arguments);
|
|
10555
|
+
span.log({
|
|
10556
|
+
input,
|
|
10557
|
+
metadata
|
|
10558
|
+
});
|
|
10559
|
+
} catch (error) {
|
|
10560
|
+
console.error(`Error extracting input for ${channelName}:`, error);
|
|
10561
|
+
}
|
|
10562
|
+
},
|
|
10563
|
+
asyncEnd: (event) => {
|
|
10564
|
+
const spanData = spans.get(event);
|
|
10565
|
+
if (!spanData) {
|
|
10566
|
+
return;
|
|
10567
|
+
}
|
|
10568
|
+
const { span, startTime } = spanData;
|
|
10569
|
+
try {
|
|
10570
|
+
const output = config.extractOutput(event.result);
|
|
10571
|
+
const metrics = config.extractMetrics(event.result, startTime);
|
|
10572
|
+
span.log({
|
|
10573
|
+
output,
|
|
10574
|
+
metrics
|
|
10575
|
+
});
|
|
10576
|
+
} catch (error) {
|
|
10577
|
+
console.error(`Error extracting output for ${channelName}:`, error);
|
|
10578
|
+
} finally {
|
|
10579
|
+
span.end();
|
|
10580
|
+
spans.delete(event);
|
|
10581
|
+
}
|
|
10582
|
+
},
|
|
10583
|
+
error: (event) => {
|
|
10584
|
+
const spanData = spans.get(event);
|
|
10585
|
+
if (!spanData) {
|
|
10586
|
+
return;
|
|
10587
|
+
}
|
|
10588
|
+
const { span } = spanData;
|
|
10589
|
+
span.log({
|
|
10590
|
+
error: event.error.message
|
|
10591
|
+
});
|
|
10592
|
+
span.end();
|
|
10593
|
+
spans.delete(event);
|
|
10594
|
+
}
|
|
10595
|
+
};
|
|
10596
|
+
channel.subscribe(handlers);
|
|
10597
|
+
this.unsubscribers.push(() => {
|
|
10598
|
+
channel.unsubscribe(handlers);
|
|
10599
|
+
});
|
|
10600
|
+
}
|
|
10601
|
+
subscribeToGoogleStreamingChannel(channelName, config) {
|
|
10602
|
+
const channel = tracingChannel5(channelName);
|
|
10603
|
+
const spans = /* @__PURE__ */ new WeakMap();
|
|
10604
|
+
const handlers = {
|
|
10605
|
+
start: (event) => {
|
|
10606
|
+
const span = startSpan({
|
|
10607
|
+
name: config.name,
|
|
10608
|
+
spanAttributes: {
|
|
10609
|
+
type: config.type
|
|
10610
|
+
}
|
|
10611
|
+
});
|
|
10612
|
+
const startTime = getCurrentUnixTimestamp();
|
|
10613
|
+
spans.set(event, { span, startTime });
|
|
10614
|
+
try {
|
|
10615
|
+
const { input, metadata } = config.extractInput(event.arguments);
|
|
10616
|
+
span.log({
|
|
10617
|
+
input,
|
|
10618
|
+
metadata
|
|
10619
|
+
});
|
|
10620
|
+
} catch (error) {
|
|
10621
|
+
console.error(`Error extracting input for ${channelName}:`, error);
|
|
10622
|
+
}
|
|
10623
|
+
},
|
|
10624
|
+
asyncEnd: (event) => {
|
|
10625
|
+
const spanData = spans.get(event);
|
|
10626
|
+
if (!spanData) {
|
|
10627
|
+
return;
|
|
10628
|
+
}
|
|
10629
|
+
const { span, startTime } = spanData;
|
|
10630
|
+
if (isAsyncIterable(event.result)) {
|
|
10631
|
+
patchStreamIfNeeded(event.result, {
|
|
10632
|
+
onComplete: (chunks) => {
|
|
10633
|
+
try {
|
|
10634
|
+
const { output, metrics } = config.aggregateChunks(
|
|
10635
|
+
chunks,
|
|
10636
|
+
startTime
|
|
10637
|
+
);
|
|
10638
|
+
span.log({
|
|
10639
|
+
output,
|
|
10640
|
+
metrics
|
|
10641
|
+
});
|
|
10642
|
+
} catch (error) {
|
|
10643
|
+
console.error(
|
|
10644
|
+
`Error extracting output for ${channelName}:`,
|
|
10645
|
+
error
|
|
10646
|
+
);
|
|
10647
|
+
} finally {
|
|
10648
|
+
span.end();
|
|
10649
|
+
}
|
|
10650
|
+
},
|
|
10651
|
+
onError: (error) => {
|
|
10652
|
+
span.log({
|
|
10653
|
+
error: error.message
|
|
10654
|
+
});
|
|
10655
|
+
span.end();
|
|
10656
|
+
}
|
|
10657
|
+
});
|
|
10658
|
+
} else {
|
|
10659
|
+
span.end();
|
|
10660
|
+
spans.delete(event);
|
|
10661
|
+
}
|
|
10662
|
+
},
|
|
10663
|
+
error: (event) => {
|
|
10664
|
+
const spanData = spans.get(event);
|
|
10665
|
+
if (!spanData) {
|
|
10666
|
+
return;
|
|
10667
|
+
}
|
|
10668
|
+
const { span } = spanData;
|
|
10669
|
+
span.log({
|
|
10670
|
+
error: event.error.message
|
|
10671
|
+
});
|
|
10672
|
+
span.end();
|
|
10673
|
+
spans.delete(event);
|
|
10674
|
+
}
|
|
10675
|
+
};
|
|
10676
|
+
channel.subscribe(handlers);
|
|
10677
|
+
this.unsubscribers.push(() => {
|
|
10678
|
+
channel.unsubscribe(handlers);
|
|
10679
|
+
});
|
|
10680
|
+
}
|
|
10681
|
+
};
|
|
10682
|
+
function serializeInput(params) {
|
|
10683
|
+
const input = {
|
|
10684
|
+
model: params.model,
|
|
10685
|
+
contents: serializeContents(params.contents)
|
|
10686
|
+
};
|
|
10687
|
+
if (params.config) {
|
|
10688
|
+
const config = tryToDict(params.config);
|
|
10689
|
+
if (config) {
|
|
10690
|
+
const tools = serializeTools(params);
|
|
10691
|
+
if (tools) {
|
|
10692
|
+
config.tools = tools;
|
|
10693
|
+
}
|
|
10694
|
+
input.config = config;
|
|
10695
|
+
}
|
|
10696
|
+
}
|
|
10697
|
+
return input;
|
|
10698
|
+
}
|
|
10699
|
+
function serializeContents(contents) {
|
|
10700
|
+
if (contents === null || contents === void 0) {
|
|
10701
|
+
return null;
|
|
10702
|
+
}
|
|
10703
|
+
if (Array.isArray(contents)) {
|
|
10704
|
+
return contents.map((item) => serializeContentItem(item));
|
|
10705
|
+
}
|
|
10706
|
+
return serializeContentItem(contents);
|
|
10707
|
+
}
|
|
10708
|
+
function serializeContentItem(item) {
|
|
10709
|
+
if (typeof item === "object" && item !== null) {
|
|
10710
|
+
if (item.parts && Array.isArray(item.parts)) {
|
|
10711
|
+
return {
|
|
10712
|
+
...item,
|
|
10713
|
+
parts: item.parts.map((part) => serializePart(part))
|
|
10714
|
+
};
|
|
10715
|
+
}
|
|
10716
|
+
return item;
|
|
10717
|
+
}
|
|
10718
|
+
if (typeof item === "string") {
|
|
10719
|
+
return { text: item };
|
|
10720
|
+
}
|
|
10721
|
+
return item;
|
|
10722
|
+
}
|
|
10723
|
+
function serializePart(part) {
|
|
10724
|
+
if (!part || typeof part !== "object") {
|
|
10725
|
+
return part;
|
|
10726
|
+
}
|
|
10727
|
+
if (part.inlineData && part.inlineData.data) {
|
|
10728
|
+
const { data, mimeType } = part.inlineData;
|
|
10729
|
+
if (data instanceof Uint8Array || typeof Buffer !== "undefined" && Buffer.isBuffer(data) || typeof data === "string") {
|
|
10730
|
+
const extension = mimeType ? mimeType.split("/")[1] : "bin";
|
|
10731
|
+
const filename = `file.${extension}`;
|
|
10732
|
+
const buffer = typeof data === "string" ? typeof Buffer !== "undefined" ? Buffer.from(data, "base64") : new Uint8Array(
|
|
10733
|
+
atob(data).split("").map((c) => c.charCodeAt(0))
|
|
10734
|
+
) : typeof Buffer !== "undefined" ? Buffer.from(data) : new Uint8Array(data);
|
|
10735
|
+
const attachment = new Attachment({
|
|
10736
|
+
data: buffer,
|
|
10737
|
+
filename,
|
|
10738
|
+
contentType: mimeType || "application/octet-stream"
|
|
10739
|
+
});
|
|
10740
|
+
return {
|
|
10741
|
+
image_url: { url: attachment }
|
|
10742
|
+
};
|
|
10743
|
+
}
|
|
10744
|
+
}
|
|
10745
|
+
return part;
|
|
10746
|
+
}
|
|
10747
|
+
function serializeTools(params) {
|
|
10748
|
+
if (!params.config?.tools) {
|
|
10749
|
+
return null;
|
|
10750
|
+
}
|
|
10751
|
+
try {
|
|
10752
|
+
return params.config.tools.map((tool) => {
|
|
10753
|
+
if (typeof tool === "object" && tool.functionDeclarations) {
|
|
10754
|
+
return tool;
|
|
10755
|
+
}
|
|
10756
|
+
return tool;
|
|
10757
|
+
});
|
|
10758
|
+
} catch {
|
|
10759
|
+
return null;
|
|
10760
|
+
}
|
|
10761
|
+
}
|
|
10762
|
+
function extractMetadata(params) {
|
|
10763
|
+
const metadata = {};
|
|
10764
|
+
if (params.model) {
|
|
10765
|
+
metadata.model = params.model;
|
|
10766
|
+
}
|
|
10767
|
+
if (params.config) {
|
|
10768
|
+
const config = tryToDict(params.config);
|
|
10769
|
+
if (config) {
|
|
10770
|
+
Object.keys(config).forEach((key) => {
|
|
10771
|
+
if (key !== "tools") {
|
|
10772
|
+
metadata[key] = config[key];
|
|
10773
|
+
}
|
|
10774
|
+
});
|
|
10775
|
+
}
|
|
10776
|
+
}
|
|
10777
|
+
return metadata;
|
|
10778
|
+
}
|
|
10779
|
+
function extractGenerateContentMetrics(response, startTime) {
|
|
10780
|
+
const metrics = {};
|
|
10781
|
+
if (startTime) {
|
|
10782
|
+
const end = getCurrentUnixTimestamp();
|
|
10783
|
+
metrics.duration = end - startTime;
|
|
10784
|
+
}
|
|
10785
|
+
if (response.usageMetadata) {
|
|
10786
|
+
const usage = response.usageMetadata;
|
|
10787
|
+
if (usage.promptTokenCount !== void 0) {
|
|
10788
|
+
metrics.prompt_tokens = usage.promptTokenCount;
|
|
10789
|
+
}
|
|
10790
|
+
if (usage.candidatesTokenCount !== void 0) {
|
|
10791
|
+
metrics.completion_tokens = usage.candidatesTokenCount;
|
|
10792
|
+
}
|
|
10793
|
+
if (usage.totalTokenCount !== void 0) {
|
|
10794
|
+
metrics.tokens = usage.totalTokenCount;
|
|
10795
|
+
}
|
|
10796
|
+
if (usage.cachedContentTokenCount !== void 0) {
|
|
10797
|
+
metrics.prompt_cached_tokens = usage.cachedContentTokenCount;
|
|
10798
|
+
}
|
|
10799
|
+
if (usage.thoughtsTokenCount !== void 0) {
|
|
10800
|
+
metrics.completion_reasoning_tokens = usage.thoughtsTokenCount;
|
|
10801
|
+
}
|
|
10802
|
+
}
|
|
10803
|
+
return metrics;
|
|
10804
|
+
}
|
|
10805
|
+
function aggregateGenerateContentChunks(chunks, startTime) {
|
|
10806
|
+
const end = getCurrentUnixTimestamp();
|
|
10807
|
+
const metrics = {
|
|
10808
|
+
duration: end - startTime
|
|
10809
|
+
};
|
|
10810
|
+
let firstTokenTime = null;
|
|
10811
|
+
if (chunks.length > 0 && firstTokenTime === null) {
|
|
10812
|
+
firstTokenTime = getCurrentUnixTimestamp();
|
|
10813
|
+
metrics.time_to_first_token = firstTokenTime - startTime;
|
|
10814
|
+
}
|
|
10815
|
+
if (chunks.length === 0) {
|
|
10816
|
+
return { output: {}, metrics };
|
|
10817
|
+
}
|
|
10818
|
+
let text = "";
|
|
10819
|
+
let thoughtText = "";
|
|
10820
|
+
const otherParts = [];
|
|
10821
|
+
let usageMetadata = null;
|
|
10822
|
+
let lastResponse = null;
|
|
10823
|
+
for (const chunk of chunks) {
|
|
10824
|
+
lastResponse = chunk;
|
|
10825
|
+
if (chunk.usageMetadata) {
|
|
10826
|
+
usageMetadata = chunk.usageMetadata;
|
|
10827
|
+
}
|
|
10828
|
+
if (chunk.candidates && Array.isArray(chunk.candidates)) {
|
|
10829
|
+
for (const candidate of chunk.candidates) {
|
|
10830
|
+
if (candidate.content?.parts) {
|
|
10831
|
+
for (const part of candidate.content.parts) {
|
|
10832
|
+
if (part.text !== void 0) {
|
|
10833
|
+
if (part.thought) {
|
|
10834
|
+
thoughtText += part.text;
|
|
10835
|
+
} else {
|
|
10836
|
+
text += part.text;
|
|
10837
|
+
}
|
|
10838
|
+
} else if (part.functionCall) {
|
|
10839
|
+
otherParts.push({ functionCall: part.functionCall });
|
|
10840
|
+
} else if (part.codeExecutionResult) {
|
|
10841
|
+
otherParts.push({
|
|
10842
|
+
codeExecutionResult: part.codeExecutionResult
|
|
10843
|
+
});
|
|
10844
|
+
} else if (part.executableCode) {
|
|
10845
|
+
otherParts.push({ executableCode: part.executableCode });
|
|
10846
|
+
}
|
|
10847
|
+
}
|
|
10848
|
+
}
|
|
10849
|
+
}
|
|
10850
|
+
}
|
|
10851
|
+
}
|
|
10852
|
+
const output = {};
|
|
10853
|
+
const parts = [];
|
|
10854
|
+
if (thoughtText) {
|
|
10855
|
+
parts.push({ text: thoughtText, thought: true });
|
|
10856
|
+
}
|
|
10857
|
+
if (text) {
|
|
10858
|
+
parts.push({ text });
|
|
10859
|
+
}
|
|
10860
|
+
parts.push(...otherParts);
|
|
10861
|
+
if (parts.length > 0 && lastResponse?.candidates) {
|
|
10862
|
+
const candidates = [];
|
|
10863
|
+
for (const candidate of lastResponse.candidates) {
|
|
10864
|
+
const candidateDict = {
|
|
10865
|
+
content: {
|
|
10866
|
+
parts,
|
|
10867
|
+
role: "model"
|
|
10868
|
+
}
|
|
10869
|
+
};
|
|
10870
|
+
if (candidate.finishReason !== void 0) {
|
|
10871
|
+
candidateDict.finishReason = candidate.finishReason;
|
|
10872
|
+
}
|
|
10873
|
+
if (candidate.safetyRatings) {
|
|
10874
|
+
candidateDict.safetyRatings = candidate.safetyRatings;
|
|
10875
|
+
}
|
|
10876
|
+
candidates.push(candidateDict);
|
|
10877
|
+
}
|
|
10878
|
+
output.candidates = candidates;
|
|
10879
|
+
}
|
|
10880
|
+
if (usageMetadata) {
|
|
10881
|
+
output.usageMetadata = usageMetadata;
|
|
10882
|
+
if (usageMetadata.promptTokenCount !== void 0) {
|
|
10883
|
+
metrics.prompt_tokens = usageMetadata.promptTokenCount;
|
|
10884
|
+
}
|
|
10885
|
+
if (usageMetadata.candidatesTokenCount !== void 0) {
|
|
10886
|
+
metrics.completion_tokens = usageMetadata.candidatesTokenCount;
|
|
10887
|
+
}
|
|
10888
|
+
if (usageMetadata.totalTokenCount !== void 0) {
|
|
10889
|
+
metrics.tokens = usageMetadata.totalTokenCount;
|
|
10890
|
+
}
|
|
10891
|
+
if (usageMetadata.cachedContentTokenCount !== void 0) {
|
|
10892
|
+
metrics.prompt_cached_tokens = usageMetadata.cachedContentTokenCount;
|
|
10893
|
+
}
|
|
10894
|
+
if (usageMetadata.thoughtsTokenCount !== void 0) {
|
|
10895
|
+
metrics.completion_reasoning_tokens = usageMetadata.thoughtsTokenCount;
|
|
10896
|
+
}
|
|
10897
|
+
}
|
|
10898
|
+
if (text) {
|
|
10899
|
+
output.text = text;
|
|
10900
|
+
}
|
|
10901
|
+
return { output, metrics };
|
|
10902
|
+
}
|
|
10903
|
+
function tryToDict(obj) {
|
|
10904
|
+
if (obj === null || obj === void 0) {
|
|
10905
|
+
return null;
|
|
10906
|
+
}
|
|
10907
|
+
if (typeof obj === "object") {
|
|
10908
|
+
if (typeof obj.toJSON === "function") {
|
|
10909
|
+
return obj.toJSON();
|
|
10910
|
+
}
|
|
10911
|
+
return obj;
|
|
10912
|
+
}
|
|
10913
|
+
return null;
|
|
10914
|
+
}
|
|
10915
|
+
|
|
10916
|
+
// src/instrumentation/braintrust-plugin.ts
|
|
10917
|
+
var BraintrustPlugin = class extends BasePlugin {
|
|
10918
|
+
config;
|
|
10919
|
+
openaiPlugin = null;
|
|
10920
|
+
anthropicPlugin = null;
|
|
10921
|
+
aiSDKPlugin = null;
|
|
10922
|
+
claudeAgentSDKPlugin = null;
|
|
10923
|
+
googleGenAIPlugin = null;
|
|
10924
|
+
constructor(config = {}) {
|
|
10925
|
+
super();
|
|
10926
|
+
this.config = config;
|
|
10927
|
+
}
|
|
10928
|
+
onEnable() {
|
|
10929
|
+
const integrations = this.config.integrations || {};
|
|
10930
|
+
if (integrations.openai !== false) {
|
|
10931
|
+
this.openaiPlugin = new OpenAIPlugin();
|
|
10932
|
+
this.openaiPlugin.enable();
|
|
10933
|
+
}
|
|
10934
|
+
if (integrations.anthropic !== false) {
|
|
10935
|
+
this.anthropicPlugin = new AnthropicPlugin();
|
|
10936
|
+
this.anthropicPlugin.enable();
|
|
10937
|
+
}
|
|
10938
|
+
if (integrations.aisdk !== false && integrations.vercel !== false) {
|
|
10939
|
+
this.aiSDKPlugin = new AISDKPlugin();
|
|
10940
|
+
this.aiSDKPlugin.enable();
|
|
10941
|
+
}
|
|
10942
|
+
if (integrations.claudeAgentSDK !== false) {
|
|
10943
|
+
this.claudeAgentSDKPlugin = new ClaudeAgentSDKPlugin();
|
|
10944
|
+
this.claudeAgentSDKPlugin.enable();
|
|
10945
|
+
}
|
|
10946
|
+
if (integrations.googleGenAI !== false && integrations.google !== false) {
|
|
10947
|
+
this.googleGenAIPlugin = new GoogleGenAIPlugin();
|
|
10948
|
+
this.googleGenAIPlugin.enable();
|
|
10949
|
+
}
|
|
10950
|
+
}
|
|
10951
|
+
onDisable() {
|
|
10952
|
+
if (this.openaiPlugin) {
|
|
10953
|
+
this.openaiPlugin.disable();
|
|
10954
|
+
this.openaiPlugin = null;
|
|
10955
|
+
}
|
|
10956
|
+
if (this.anthropicPlugin) {
|
|
10957
|
+
this.anthropicPlugin.disable();
|
|
10958
|
+
this.anthropicPlugin = null;
|
|
10959
|
+
}
|
|
10960
|
+
if (this.aiSDKPlugin) {
|
|
10961
|
+
this.aiSDKPlugin.disable();
|
|
10962
|
+
this.aiSDKPlugin = null;
|
|
10963
|
+
}
|
|
10964
|
+
if (this.claudeAgentSDKPlugin) {
|
|
10965
|
+
this.claudeAgentSDKPlugin.disable();
|
|
10966
|
+
this.claudeAgentSDKPlugin = null;
|
|
10967
|
+
}
|
|
10968
|
+
if (this.googleGenAIPlugin) {
|
|
10969
|
+
this.googleGenAIPlugin.disable();
|
|
10970
|
+
this.googleGenAIPlugin = null;
|
|
10971
|
+
}
|
|
10972
|
+
}
|
|
10973
|
+
};
|
|
10974
|
+
|
|
10975
|
+
// src/instrumentation/registry.ts
|
|
10976
|
+
var PluginRegistry = class {
|
|
10977
|
+
braintrustPlugin = null;
|
|
10978
|
+
config = {};
|
|
10979
|
+
enabled = false;
|
|
10980
|
+
/**
|
|
10981
|
+
* Configure which integrations should be enabled.
|
|
10982
|
+
* This must be called before any SDK imports to take effect.
|
|
10983
|
+
*/
|
|
10984
|
+
configure(config) {
|
|
10985
|
+
if (this.enabled) {
|
|
10986
|
+
console.warn(
|
|
10987
|
+
"Braintrust: Cannot configure instrumentation after it has been enabled. Call configureInstrumentation() before importing any AI SDKs."
|
|
10988
|
+
);
|
|
10989
|
+
return;
|
|
10990
|
+
}
|
|
10991
|
+
this.config = { ...this.config, ...config };
|
|
10992
|
+
}
|
|
10993
|
+
/**
|
|
10994
|
+
* Enable all configured plugins.
|
|
10995
|
+
* Called automatically when the library is loaded.
|
|
10996
|
+
*/
|
|
10997
|
+
enable() {
|
|
10998
|
+
if (this.enabled) {
|
|
10999
|
+
return;
|
|
11000
|
+
}
|
|
11001
|
+
this.enabled = true;
|
|
11002
|
+
const envConfig = this.readEnvConfig();
|
|
11003
|
+
const finalConfig = {
|
|
11004
|
+
integrations: {
|
|
11005
|
+
...this.getDefaultConfig(),
|
|
11006
|
+
...this.config.integrations,
|
|
11007
|
+
...envConfig.integrations
|
|
11008
|
+
}
|
|
11009
|
+
};
|
|
11010
|
+
this.braintrustPlugin = new BraintrustPlugin(finalConfig);
|
|
11011
|
+
this.braintrustPlugin.enable();
|
|
11012
|
+
}
|
|
11013
|
+
/**
|
|
11014
|
+
* Disable all plugins.
|
|
11015
|
+
* Primarily used for testing.
|
|
11016
|
+
*/
|
|
11017
|
+
disable() {
|
|
11018
|
+
if (!this.enabled) {
|
|
11019
|
+
return;
|
|
11020
|
+
}
|
|
11021
|
+
this.enabled = false;
|
|
11022
|
+
if (this.braintrustPlugin) {
|
|
11023
|
+
this.braintrustPlugin.disable();
|
|
11024
|
+
this.braintrustPlugin = null;
|
|
11025
|
+
}
|
|
11026
|
+
}
|
|
11027
|
+
/**
|
|
11028
|
+
* Check if instrumentation is enabled.
|
|
11029
|
+
*/
|
|
11030
|
+
isEnabled() {
|
|
11031
|
+
return this.enabled;
|
|
11032
|
+
}
|
|
11033
|
+
/**
|
|
11034
|
+
* Get default configuration (all integrations enabled).
|
|
11035
|
+
*/
|
|
11036
|
+
getDefaultConfig() {
|
|
11037
|
+
return {
|
|
11038
|
+
openai: true,
|
|
11039
|
+
anthropic: true,
|
|
11040
|
+
vercel: true,
|
|
11041
|
+
aisdk: true,
|
|
11042
|
+
google: true,
|
|
11043
|
+
claudeAgentSDK: true
|
|
11044
|
+
};
|
|
11045
|
+
}
|
|
11046
|
+
/**
|
|
11047
|
+
* Read configuration from environment variables.
|
|
11048
|
+
* Supports: BRAINTRUST_DISABLE_INSTRUMENTATION=openai,anthropic,...
|
|
11049
|
+
*/
|
|
11050
|
+
readEnvConfig() {
|
|
11051
|
+
const integrations = {};
|
|
11052
|
+
const disabledList = isomorph_default.getEnv("BRAINTRUST_DISABLE_INSTRUMENTATION");
|
|
11053
|
+
if (disabledList) {
|
|
11054
|
+
const disabled = disabledList.split(",").map((s) => s.trim().toLowerCase()).filter((s) => s.length > 0);
|
|
11055
|
+
for (const sdk of disabled) {
|
|
11056
|
+
integrations[sdk] = false;
|
|
11057
|
+
}
|
|
11058
|
+
}
|
|
11059
|
+
return { integrations };
|
|
11060
|
+
}
|
|
11061
|
+
};
|
|
11062
|
+
var registry = new PluginRegistry();
|
|
11063
|
+
|
|
8446
11064
|
// src/node/config.ts
|
|
8447
11065
|
function configureNode() {
|
|
8448
11066
|
isomorph_default.buildType = "node";
|
|
@@ -8477,6 +11095,7 @@ function configureNode() {
|
|
|
8477
11095
|
isomorph_default.gunzip = promisify(zlib.gunzip);
|
|
8478
11096
|
isomorph_default.hash = (data) => crypto.createHash("sha256").update(data).digest("hex");
|
|
8479
11097
|
_internalSetInitialState();
|
|
11098
|
+
registry.enable();
|
|
8480
11099
|
}
|
|
8481
11100
|
|
|
8482
11101
|
// dev/server.ts
|
|
@@ -8554,7 +11173,7 @@ function isAsync(fn) {
|
|
|
8554
11173
|
function isAsyncGenerator(fn) {
|
|
8555
11174
|
return fn[Symbol.toStringTag] === "AsyncGenerator";
|
|
8556
11175
|
}
|
|
8557
|
-
function
|
|
11176
|
+
function isAsyncIterable2(obj) {
|
|
8558
11177
|
return typeof obj[Symbol.asyncIterator] === "function";
|
|
8559
11178
|
}
|
|
8560
11179
|
function wrapAsync(asyncFn) {
|
|
@@ -8725,7 +11344,7 @@ var eachOfLimit$2 = (limit) => {
|
|
|
8725
11344
|
if (isAsyncGenerator(obj)) {
|
|
8726
11345
|
return asyncEachOfLimit(obj, limit, iteratee, callback);
|
|
8727
11346
|
}
|
|
8728
|
-
if (
|
|
11347
|
+
if (isAsyncIterable2(obj)) {
|
|
8729
11348
|
return asyncEachOfLimit(obj[Symbol.asyncIterator](), limit, iteratee, callback);
|
|
8730
11349
|
}
|
|
8731
11350
|
var nextElem = createIterator(obj);
|
|
@@ -10035,7 +12654,7 @@ function callEvaluatorData(data) {
|
|
|
10035
12654
|
baseExperiment
|
|
10036
12655
|
};
|
|
10037
12656
|
}
|
|
10038
|
-
function
|
|
12657
|
+
function isAsyncIterable3(value) {
|
|
10039
12658
|
return typeof value === "object" && value !== null && typeof value[Symbol.asyncIterator] === "function";
|
|
10040
12659
|
}
|
|
10041
12660
|
function isIterable(value) {
|
|
@@ -10238,7 +12857,7 @@ async function runEvaluatorInternal(experiment, evaluator, progressReporter, fil
|
|
|
10238
12857
|
}
|
|
10239
12858
|
const resolvedDataResult = dataResult instanceof Promise ? await dataResult : dataResult;
|
|
10240
12859
|
const dataIterable = (() => {
|
|
10241
|
-
if (
|
|
12860
|
+
if (isAsyncIterable3(resolvedDataResult)) {
|
|
10242
12861
|
return resolvedDataResult;
|
|
10243
12862
|
}
|
|
10244
12863
|
if (Array.isArray(resolvedDataResult) || isIterable(resolvedDataResult)) {
|