assistant-cloud 0.2.2 → 0.2.3
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 +2 -2
- package/dist/AssistantCloud.d.ts +15 -17
- package/dist/AssistantCloud.d.ts.map +1 -1
- package/dist/AssistantCloud.js +7 -2
- package/dist/AssistantCloud.js.map +1 -1
- package/dist/AssistantCloudAPI.d.ts +69 -66
- package/dist/AssistantCloudAPI.d.ts.map +1 -1
- package/dist/AssistantCloudAPI.js.map +1 -1
- package/dist/AssistantCloudAuthStrategy.d.ts +23 -25
- package/dist/AssistantCloudAuthStrategy.d.ts.map +1 -1
- package/dist/AssistantCloudAuthTokens.d.ts +6 -7
- package/dist/AssistantCloudAuthTokens.d.ts.map +1 -1
- package/dist/AssistantCloudEvents.d.ts +32 -25
- package/dist/AssistantCloudEvents.d.ts.map +1 -1
- package/dist/AssistantCloudEvents.js +65 -16
- package/dist/AssistantCloudEvents.js.map +1 -1
- package/dist/AssistantCloudFiles.d.ts +25 -26
- package/dist/AssistantCloudFiles.d.ts.map +1 -1
- package/dist/AssistantCloudProjectThreadMessages.d.ts +10 -11
- package/dist/AssistantCloudProjectThreadMessages.d.ts.map +1 -1
- package/dist/AssistantCloudProjectThreads.d.ts +11 -12
- package/dist/AssistantCloudProjectThreads.d.ts.map +1 -1
- package/dist/AssistantCloudProjects.d.ts +3 -5
- package/dist/AssistantCloudProjects.d.ts.map +1 -1
- package/dist/AssistantCloudRuns.d.ts +65 -53
- package/dist/AssistantCloudRuns.d.ts.map +1 -1
- package/dist/AssistantCloudRuns.js +9 -4
- package/dist/AssistantCloudRuns.js.map +1 -1
- package/dist/AssistantCloudScores.d.ts +17 -19
- package/dist/AssistantCloudScores.d.ts.map +1 -1
- package/dist/AssistantCloudThreadMessages.d.ts +33 -31
- package/dist/AssistantCloudThreadMessages.d.ts.map +1 -1
- package/dist/AssistantCloudThreadMessages.js +9 -2
- package/dist/AssistantCloudThreadMessages.js.map +1 -1
- package/dist/AssistantCloudThreads.d.ts +37 -38
- package/dist/AssistantCloudThreads.d.ts.map +1 -1
- package/dist/CloudEngagementReporter.d.ts +53 -54
- package/dist/CloudEngagementReporter.d.ts.map +1 -1
- package/dist/CloudEngagementReporter.js +4 -1
- package/dist/CloudEngagementReporter.js.map +1 -1
- package/dist/CloudMessagePersistence.d.ts +55 -56
- package/dist/CloudMessagePersistence.d.ts.map +1 -1
- package/dist/CloudMessagePersistence.js +3 -0
- package/dist/CloudMessagePersistence.js.map +1 -1
- package/dist/CloudRunReporter.d.ts +6 -8
- package/dist/CloudRunReporter.d.ts.map +1 -1
- package/dist/FormattedCloudPersistence.d.ts +34 -36
- package/dist/FormattedCloudPersistence.d.ts.map +1 -1
- package/dist/ai-sdk/index.d.ts +10 -12
- package/dist/ai-sdk/index.d.ts.map +1 -1
- package/dist/cloudResponse.d.ts +2 -4
- package/dist/cloudResponse.d.ts.map +1 -1
- package/dist/generateThreadTitle.d.ts +8 -10
- package/dist/generateThreadTitle.d.ts.map +1 -1
- package/dist/index.d.ts +18 -17
- package/dist/index.d.ts.map +1 -0
- package/dist/instrumentMcpSampling.d.ts +30 -32
- package/dist/instrumentMcpSampling.d.ts.map +1 -1
- package/dist/runTelemetry.d.ts +86 -78
- package/dist/runTelemetry.d.ts.map +1 -1
- package/dist/runTelemetry.js +57 -15
- package/dist/runTelemetry.js.map +1 -1
- package/dist/telemetry/index.d.ts +13 -15
- package/dist/telemetry/index.d.ts.map +1 -1
- package/dist/version.d.ts +0 -2
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +1 -1
- package/package.json +3 -3
- package/src/AssistantCloud.ts +17 -2
- package/src/AssistantCloudAPI.ts +4 -0
- package/src/AssistantCloudEvents.test.ts +116 -0
- package/src/AssistantCloudEvents.ts +81 -20
- package/src/AssistantCloudRuns.ts +35 -5
- package/src/AssistantCloudThreadMessages.ts +21 -1
- package/src/BackendTranscriptCopy.test.ts +79 -0
- package/src/CloudEngagementReporter.ts +9 -3
- package/src/CloudMessagePersistence.ts +4 -0
- package/src/ai-sdk/index.test.ts +2 -2
- package/src/runTelemetry.test.ts +226 -2
- package/src/runTelemetry.ts +121 -17
- package/src/tests/AssistantCloud.test.ts +22 -0
- package/src/tests/AssistantCloudRuns.test.ts +28 -0
- package/src/tests/CloudEngagementReporter.test.ts +45 -0
- package/src/tests/CloudRunReporter.test.ts +14 -2
- package/dist/tests/setup.d.ts +0 -1
- package/dist/tests/setup.js +0 -7
- package/dist/tests/setup.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runTelemetry.d.ts","
|
|
1
|
+
{"version":3,"file":"runTelemetry.d.ts","sourceRoot":"","sources":["../src/runTelemetry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,mCAAgC;AAChE,OAAO,KAAK,EAAE,uBAAuB,EAAE,gCAA6B;AAMpE,MAAM,MAAM,+BAA+B,GAAG;IAC5C,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,KAAK,GAAG,UAAU,GAAG,SAAS,CAAC;IAC7C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,gBAAgB,EAAE,CAAC;CACrC,CAAC;AAEF,MAAM,MAAM,gBAAgB,GACxB,SAAS,GACT,cAAc,GACd,QAAQ,GACR,gBAAgB,CAAC;AAErB;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE;IACL,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC9B,YAAY,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACnC,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CAC/B,EACD,cAAc,GAAE,WAAW,GAAG,YAA0B,GACvD;IACD,MAAM,EAAE,WAAW,GAAG,YAAY,GAAG,OAAO,CAAC;IAC7C,OAAO,CAAC,EAAE,gBAAgB,CAAC;CAC5B,CAqBA;AAKD;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG;IAChD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAwBA;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAG9D;AA0FD,MAAM,MAAM,wBAAwB,GAAG;IACrC,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,UAAU,CAAC,EAAE,KAAK,GAAG,UAAU,GAAG,SAAS,GAAG,SAAS,CAAC;CACzD,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,0BAA0B,CACxC,IAAI,EAAE,wBAAwB,GAC7B,+BAA+B,CAcjC;AAED;;;;;;GAMG;AACH,wBAAgB,0BAA0B,CACxC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,GAC5C,MAAM,GAAG,SAAS,CAgBpB;AAED,MAAM,MAAM,iBAAiB,GAAG;IAC9B,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,eAAe,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACrC,iBAAiB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACxC,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG,iBAAiB,GAAG;IACtD,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,gBAAgB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,iBAAiB,CAAC,EAAE;QAAE,eAAe,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACjD,kBAAkB,CAAC,EAAE;QAAE,eAAe,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CACnD,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,KAAK,CAAC,EAAE,qBAAqB,GAAG,SAAS,CAAC;IAC1C,SAAS,CAAC,EAAE,+BAA+B,EAAE,GAAG,SAAS,CAAC;IAC1D,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC5B,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAChC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,MAAM,EAAE,WAAW,GAAG,YAAY,CAAC;IACnC,SAAS,CAAC,EAAE,+BAA+B,EAAE,CAAC;IAC9C,KAAK,CAAC,EAAE,iBAAiB,EAAE,CAAC;IAC5B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,iBAAiB,CAAC;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,uBAAuB,CAAC,QAAQ,CAAC,CAAC;IAC1C,OAAO,CAAC,EAAE,uBAAuB,CAAC,cAAc,CAAC,GAAG,SAAS,CAAC;IAC9D,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,KAAK,CAAC,EAAE,qBAAqB,GAAG,SAAS,CAAC;IAC1C,KAAK,CAAC,EAAE,iBAAiB,EAAE,GAAG,SAAS,CAAC;IACxC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,SAAS,CAAC,EAAE,+BAA+B,EAAE,GAAG,SAAS,CAAC;IAC1D,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,WAAW,CAAC,EACR;QACE,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAC3B,iBAAiB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QACvC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAC5B,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;KAC5B,GACD,SAAS,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC;IACjD,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC;IAC/C,SAAS,CAAC,EAAE;QACV,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QACjC,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAC7B,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;KACtC,CAAC;CACH,CAAC;AAoGF,wBAAgB,eAAe,CAAC,IAAI,EAAE,aAAa,GAAG,uBAAuB,CAiD5E;AAED;;;;;GAKG;AACH,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,qBAAqB,GAC3B,iBAAiB,GAAG,SAAS,CAwB/B"}
|
package/dist/runTelemetry.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
//#region src/runTelemetry.ts
|
|
2
2
|
const MAX_TELEMETRY_TEXT_LENGTH = 5e4;
|
|
3
|
-
const BASE64_PATTERN = /^[A-Za-z0-9+/]{
|
|
3
|
+
const BASE64_PATTERN = /^[A-Za-z0-9+/]+={0,2}$/;
|
|
4
4
|
/**
|
|
5
5
|
* Maps a finish event to the report status and outcome. `fallbackStatus`
|
|
6
6
|
* applies when the event carries neither a finish reason nor a failure flag.
|
|
@@ -65,24 +65,42 @@ function safeStringify(value) {
|
|
|
65
65
|
return;
|
|
66
66
|
}
|
|
67
67
|
}
|
|
68
|
+
const base64SizeKB = (value) => (value.length * 3 / 4 / 1024).toFixed(1);
|
|
69
|
+
const isInlineBase64 = (value) => {
|
|
70
|
+
if (typeof value !== "string") return false;
|
|
71
|
+
const head = value.slice(0, 200);
|
|
72
|
+
return head.length > 0 && head.length % 4 === 0 && BASE64_PATTERN.test(head);
|
|
73
|
+
};
|
|
74
|
+
function summarizeMcpContentBlock(item) {
|
|
75
|
+
if (!item || typeof item !== "object") return item;
|
|
76
|
+
const block = item;
|
|
77
|
+
if ((block.type === "image" || block.type === "audio") && isInlineBase64(block.data)) return {
|
|
78
|
+
...block,
|
|
79
|
+
data: `[${String(block.type)}: ${base64SizeKB(block.data)}KB]`
|
|
80
|
+
};
|
|
81
|
+
if (block.type === "resource" && block.resource && typeof block.resource === "object") {
|
|
82
|
+
const resource = block.resource;
|
|
83
|
+
if (isInlineBase64(resource.blob)) return {
|
|
84
|
+
...block,
|
|
85
|
+
resource: {
|
|
86
|
+
...resource,
|
|
87
|
+
blob: `[resource: ${base64SizeKB(resource.blob)}KB]`
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
return item;
|
|
92
|
+
}
|
|
68
93
|
function summarizeMcpResult(value) {
|
|
69
94
|
if (value == null) return void 0;
|
|
70
95
|
try {
|
|
71
96
|
const parsed = typeof value === "string" ? JSON.parse(value) : value;
|
|
72
|
-
if (Array.isArray(parsed))
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
data: `[${item.type}: ${sizeKB}KB]`
|
|
80
|
-
};
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
return item;
|
|
84
|
-
});
|
|
85
|
-
return truncateRunTelemetryText(JSON.stringify(summarized));
|
|
97
|
+
if (Array.isArray(parsed)) return truncateRunTelemetryText(JSON.stringify(parsed.map(summarizeMcpContentBlock)));
|
|
98
|
+
if (typeof parsed === "object") {
|
|
99
|
+
const content = parsed.content;
|
|
100
|
+
if (Array.isArray(content)) return truncateRunTelemetryText(JSON.stringify({
|
|
101
|
+
...parsed,
|
|
102
|
+
content: content.map(summarizeMcpContentBlock)
|
|
103
|
+
}));
|
|
86
104
|
}
|
|
87
105
|
} catch {}
|
|
88
106
|
return safeStringify(value);
|
|
@@ -147,6 +165,7 @@ function createRunReportStep(init) {
|
|
|
147
165
|
if (init.startMs !== void 0) step.start_ms = init.startMs;
|
|
148
166
|
if (init.endMs !== void 0) step.end_ms = init.endMs;
|
|
149
167
|
if (init.finishReason !== void 0) step.finish_reason = init.finishReason.slice(0, 32);
|
|
168
|
+
if (init.input !== void 0) step.input = truncateRunTelemetryText(init.input);
|
|
150
169
|
return step;
|
|
151
170
|
}
|
|
152
171
|
function normalizeRunReportTags(tags) {
|
|
@@ -166,6 +185,23 @@ function normalizeRunReportMilliseconds(value) {
|
|
|
166
185
|
if (value === void 0 || !Number.isFinite(value)) return void 0;
|
|
167
186
|
return Math.max(0, Math.round(value));
|
|
168
187
|
}
|
|
188
|
+
function normalizeRunReportCost(value) {
|
|
189
|
+
if (value === void 0 || !Number.isFinite(value) || value < 0) return;
|
|
190
|
+
return value;
|
|
191
|
+
}
|
|
192
|
+
function assignCostDetails(report, costDetails) {
|
|
193
|
+
if (!costDetails) return;
|
|
194
|
+
const normalized = {};
|
|
195
|
+
const input = normalizeRunReportCost(costDetails.input);
|
|
196
|
+
if (input !== void 0) normalized.input = input;
|
|
197
|
+
const inputCachedTokens = normalizeRunReportCost(costDetails.inputCachedTokens);
|
|
198
|
+
if (inputCachedTokens !== void 0) normalized.input_cached_tokens = inputCachedTokens;
|
|
199
|
+
const output = normalizeRunReportCost(costDetails.output);
|
|
200
|
+
if (output !== void 0) normalized.output = output;
|
|
201
|
+
const total = normalizeRunReportCost(costDetails.total);
|
|
202
|
+
if (total !== void 0) normalized.total = total;
|
|
203
|
+
if (Object.keys(normalized).length > 0) report.cost_details = normalized;
|
|
204
|
+
}
|
|
169
205
|
function createRunReport(init) {
|
|
170
206
|
const report = {
|
|
171
207
|
thread_id: init.threadId,
|
|
@@ -173,6 +209,8 @@ function createRunReport(init) {
|
|
|
173
209
|
};
|
|
174
210
|
const traceId = init.traceId?.toLowerCase();
|
|
175
211
|
if (traceId && /^[0-9a-f]{32}$/.test(traceId)) report.trace_id = traceId;
|
|
212
|
+
const rootSpanId = init.rootSpanId?.toLowerCase();
|
|
213
|
+
if (rootSpanId && /^[0-9a-f]{16}$/.test(rootSpanId)) report.root_span_id = rootSpanId;
|
|
176
214
|
if (init.outcome !== void 0) report.outcome_type = init.outcome;
|
|
177
215
|
if (init.errorCode !== void 0) report.error_code = init.errorCode;
|
|
178
216
|
if (init.error !== void 0) report.error = init.error;
|
|
@@ -192,7 +230,11 @@ function createRunReport(init) {
|
|
|
192
230
|
if (durationMs !== void 0) report.duration_ms = durationMs;
|
|
193
231
|
const firstTokenMs = normalizeRunReportMilliseconds(init.firstTokenMs);
|
|
194
232
|
if (firstTokenMs !== void 0) report.first_token_ms = firstTokenMs;
|
|
233
|
+
const costUsd = normalizeRunReportCost(init.costUsd);
|
|
234
|
+
if (costUsd !== void 0) report.cost_usd = costUsd;
|
|
235
|
+
assignCostDetails(report, init.costDetails);
|
|
195
236
|
if (init.outputText !== void 0) report.output_text = truncateRunTelemetryText(init.outputText);
|
|
237
|
+
if (init.attributes !== void 0) report.attributes = init.attributes;
|
|
196
238
|
if (init.metadata !== void 0) report.metadata = init.metadata;
|
|
197
239
|
if (init.telemetry?.environment !== void 0) report.environment = init.telemetry.environment;
|
|
198
240
|
if (init.telemetry?.release !== void 0) report.release = init.telemetry.release;
|
package/dist/runTelemetry.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runTelemetry.js","names":[],"sources":["../src/runTelemetry.ts"],"sourcesContent":["import type { SamplingCallData } from \"./instrumentMcpSampling\";\nimport type { AssistantCloudRunReport } from \"./AssistantCloudRuns\";\n\nconst MAX_TELEMETRY_TEXT_LENGTH = 50_000;\n\nconst BASE64_PATTERN = /^[A-Za-z0-9+/]{100,}={0,2}$/;\n\nexport type AssistantCloudRunReportToolCall = {\n tool_name: string;\n tool_call_id: string;\n tool_args?: string;\n tool_result?: string;\n tool_source?: \"mcp\" | \"frontend\" | \"backend\";\n start_ms?: number;\n end_ms?: number;\n sampling_calls?: SamplingCallData[];\n};\n\nexport type RunReportOutcome =\n | \"aborted\"\n | \"disconnected\"\n | \"length\"\n | \"content_filter\";\n\n/**\n * Maps a finish event to the report status and outcome. `fallbackStatus`\n * applies when the event carries neither a finish reason nor a failure flag.\n */\nexport function deriveRunOutcome(\n input: {\n finishReason?: string | undefined;\n isAbort?: boolean | undefined;\n isDisconnect?: boolean | undefined;\n isError?: boolean | undefined;\n },\n fallbackStatus: \"completed\" | \"incomplete\" = \"completed\",\n): {\n status: \"completed\" | \"incomplete\" | \"error\";\n outcome?: RunReportOutcome;\n} {\n if (input.isError) return { status: \"error\" };\n if (input.isAbort) return { status: \"incomplete\", outcome: \"aborted\" };\n if (input.isDisconnect) {\n return { status: \"incomplete\", outcome: \"disconnected\" };\n }\n switch (input.finishReason) {\n case \"length\":\n return { status: \"incomplete\", outcome: \"length\" };\n case \"content-filter\":\n case \"content_filter\":\n return { status: \"incomplete\", outcome: \"content_filter\" };\n case \"cancelled\":\n return { status: \"incomplete\", outcome: \"aborted\" };\n case \"error\":\n return { status: \"error\" };\n default:\n return {\n status: input.finishReason === undefined ? fallbackStatus : \"completed\",\n };\n }\n}\n\nconst MAX_RUN_ERROR_CODE_LENGTH = 64;\nconst MAX_RUN_ERROR_LENGTH = 2048;\n\n/**\n * Reads the message and code the runs endpoint stores for a failed run. The\n * code is the error's `code` when it has one, else its class name.\n */\nexport function describeRunError(error: unknown): {\n error?: string;\n errorCode?: string;\n} {\n if (error == null) return {};\n const record =\n typeof error === \"object\" ? (error as Record<string, unknown>) : undefined;\n const message =\n typeof error === \"string\"\n ? error\n : typeof record?.message === \"string\"\n ? record.message\n : undefined;\n const code =\n typeof record?.code === \"string\"\n ? record.code\n : typeof record?.name === \"string\" && record.name !== \"Error\"\n ? record.name\n : undefined;\n return {\n ...(message\n ? { error: message.slice(0, MAX_RUN_ERROR_LENGTH) }\n : undefined),\n ...(code\n ? { errorCode: code.slice(0, MAX_RUN_ERROR_CODE_LENGTH) }\n : undefined),\n };\n}\n\n/**\n * Clamps a string to the size the runs endpoint accepts for a single span\n * field.\n */\nexport function truncateRunTelemetryText(value: string): string {\n if (value.length <= MAX_TELEMETRY_TEXT_LENGTH) return value;\n return value.slice(0, MAX_TELEMETRY_TEXT_LENGTH);\n}\n\nfunction safeStringify(value: unknown): string | undefined {\n if (value == null) return undefined;\n try {\n return truncateRunTelemetryText(JSON.stringify(value));\n } catch {\n return undefined;\n }\n}\n\nfunction summarizeMcpResult(value: unknown): string | undefined {\n if (value == null) return undefined;\n try {\n const parsed = typeof value === \"string\" ? JSON.parse(value) : value;\n if (Array.isArray(parsed)) {\n const summarized = parsed.map((item) => {\n if (item && typeof item === \"object\" && item.type) {\n if (\n (item.type === \"image\" || item.type === \"audio\") &&\n typeof item.data === \"string\" &&\n BASE64_PATTERN.test(item.data.slice(0, 200))\n ) {\n const sizeKB = ((item.data.length * 3) / 4 / 1024).toFixed(1);\n return { ...item, data: `[${item.type}: ${sizeKB}KB]` };\n }\n }\n return item;\n });\n return truncateRunTelemetryText(JSON.stringify(summarized));\n }\n } catch {\n // not JSON array, fall through\n }\n return safeStringify(value);\n}\n\nexport type RunTelemetryToolCallInit = {\n toolName: string;\n toolCallId: string;\n args?: unknown;\n /**\n * Pre-serialized arguments, used in place of serializing `args`. Values over\n * the span size are clamped before they are included in the report.\n */\n argsText?: string | undefined;\n result?: unknown;\n toolSource?: \"mcp\" | \"frontend\" | \"backend\" | undefined;\n};\n\n/**\n * Serializes one tool call into the shape the runs endpoint accepts. An `mcp`\n * source has its result summarized, because MCP content blocks carry inline\n * base64 image and audio payloads that would otherwise dominate the report.\n */\nexport function createRunTelemetryToolCall(\n init: RunTelemetryToolCallInit,\n): AssistantCloudRunReportToolCall {\n const { toolName, toolCallId, args, argsText, result, toolSource } = init;\n const call: AssistantCloudRunReportToolCall = {\n tool_name: toolName,\n tool_call_id: toolCallId,\n };\n const toolArgs =\n argsText != null ? truncateRunTelemetryText(argsText) : safeStringify(args);\n if (toolArgs !== undefined) call.tool_args = toolArgs;\n const toolResult =\n toolSource === \"mcp\" ? summarizeMcpResult(result) : safeStringify(result);\n if (toolResult !== undefined) call.tool_result = toolResult;\n if (toolSource) call.tool_source = toolSource;\n return call;\n}\n\n/**\n * Resolves the model ID a run reports, in the order an app can supply it: an\n * explicit `modelId`, the `custom` bag, then the per-step `response.modelId`\n * that a `messageMetadata` callback copies off the AI SDK's finish-step part.\n * The AI SDK puts no model ID on a UI message part, so message metadata is the\n * only channel one arrives on.\n */\nexport function extractRunTelemetryModelId(\n metadata: Record<string, unknown> | undefined,\n): string | undefined {\n if (!metadata) return undefined;\n if (typeof metadata.modelId === \"string\") return metadata.modelId;\n const custom = metadata.custom as Record<string, unknown> | undefined;\n if (typeof custom?.modelId === \"string\") return custom.modelId;\n\n const steps: unknown = metadata.steps;\n if (!Array.isArray(steps)) return undefined;\n for (const step of steps as unknown[]) {\n if (!step || typeof step !== \"object\") continue;\n const response = (step as Record<string, unknown>).response;\n if (!response || typeof response !== \"object\") continue;\n const modelId = (response as Record<string, unknown>).modelId;\n if (typeof modelId === \"string\") return modelId;\n }\n return undefined;\n}\n\nexport type RunTelemetryUsage = {\n inputTokens?: number | undefined;\n outputTokens?: number | undefined;\n reasoningTokens?: number | undefined;\n cachedInputTokens?: number | undefined;\n};\n\nexport type RunTelemetryUsageInit = RunTelemetryUsage & {\n promptTokens?: number | undefined;\n completionTokens?: number | undefined;\n inputTokenDetails?: { cacheReadTokens?: number };\n outputTokenDetails?: { reasoningTokens?: number };\n};\n\nexport type RunReportStepInit = {\n usage?: RunTelemetryUsageInit | undefined;\n toolCalls?: AssistantCloudRunReportToolCall[] | undefined;\n startMs?: number | undefined;\n endMs?: number | undefined;\n finishReason?: string | undefined;\n};\n\n/**\n * The run report fields read from the messages of one run, in whichever\n * format they were stored: the status the messages imply, the tool calls, the\n * steps, the text, the usage and the model.\n */\nexport type RunMessageTelemetry = {\n assistantMessageId?: string;\n status: \"completed\" | \"incomplete\";\n toolCalls?: AssistantCloudRunReportToolCall[];\n steps?: RunReportStepInit[];\n totalSteps?: number;\n outputText?: string;\n usage?: RunTelemetryUsage;\n modelId?: string;\n metadata?: Record<string, unknown>;\n};\n\nexport type RunReportInit = {\n threadId: string;\n status: AssistantCloudRunReport[\"status\"];\n outcome?: RunReportOutcome | undefined;\n errorCode?: string | undefined;\n error?: string | undefined;\n messageId?: string | undefined;\n traceId?: string | undefined;\n modelId?: string | undefined;\n provider?: string | undefined;\n usage?: RunTelemetryUsageInit | undefined;\n steps?: RunReportStepInit[] | undefined;\n totalSteps?: number | undefined;\n toolCalls?: AssistantCloudRunReportToolCall[] | undefined;\n durationMs?: number | undefined;\n firstTokenMs?: number | undefined;\n outputText?: string | undefined;\n metadata?: Record<string, unknown> | undefined;\n telemetry?: {\n environment?: string | undefined;\n release?: string | undefined;\n tags?: readonly string[] | undefined;\n };\n};\n\nfunction assignUsage(\n report: AssistantCloudRunReport,\n usage: RunTelemetryUsageInit | undefined,\n): void {\n if (!usage) return;\n const normalized = normalizeRunTelemetryUsage(usage);\n if (!normalized) return;\n if (normalized.inputTokens !== undefined) {\n report.input_tokens = normalized.inputTokens;\n }\n if (normalized.outputTokens !== undefined) {\n report.output_tokens = normalized.outputTokens;\n }\n if (normalized.reasoningTokens !== undefined) {\n report.reasoning_tokens = normalized.reasoningTokens;\n }\n if (normalized.cachedInputTokens !== undefined) {\n report.cached_input_tokens = normalized.cachedInputTokens;\n }\n}\n\nfunction createRunReportStep(\n init: RunReportStepInit,\n): NonNullable<AssistantCloudRunReport[\"steps\"]>[number] {\n const step: NonNullable<AssistantCloudRunReport[\"steps\"]>[number] = {};\n const usage = init.usage ? normalizeRunTelemetryUsage(init.usage) : undefined;\n if (usage?.inputTokens !== undefined) step.input_tokens = usage.inputTokens;\n if (usage?.outputTokens !== undefined)\n step.output_tokens = usage.outputTokens;\n if (usage?.reasoningTokens !== undefined) {\n step.reasoning_tokens = usage.reasoningTokens;\n }\n if (usage?.cachedInputTokens !== undefined) {\n step.cached_input_tokens = usage.cachedInputTokens;\n }\n if (init.toolCalls !== undefined) step.tool_calls = init.toolCalls;\n if (init.startMs !== undefined) step.start_ms = init.startMs;\n if (init.endMs !== undefined) step.end_ms = init.endMs;\n if (init.finishReason !== undefined) {\n step.finish_reason = init.finishReason.slice(0, 32);\n }\n return step;\n}\n\nfunction normalizeRunReportTags(\n tags: readonly string[] | undefined,\n): string[] | undefined {\n if (!tags) return undefined;\n const result: string[] = [];\n const seen = new Set<string>();\n for (const tag of tags) {\n const normalized = tag.trim().slice(0, 64).trim();\n if (!normalized || seen.has(normalized)) continue;\n seen.add(normalized);\n result.push(normalized);\n if (result.length === 20) break;\n }\n return result.length > 0 ? result : undefined;\n}\n\nfunction normalizeRunReportMilliseconds(\n value: number | undefined,\n): number | undefined {\n if (value === undefined || !Number.isFinite(value)) return undefined;\n return Math.max(0, Math.round(value));\n}\n\nexport function createRunReport(init: RunReportInit): AssistantCloudRunReport {\n const report: AssistantCloudRunReport = {\n thread_id: init.threadId,\n status: init.status,\n };\n const traceId = init.traceId?.toLowerCase();\n if (traceId && /^[0-9a-f]{32}$/.test(traceId)) report.trace_id = traceId;\n if (init.outcome !== undefined) report.outcome_type = init.outcome;\n if (init.errorCode !== undefined) report.error_code = init.errorCode;\n if (init.error !== undefined) report.error = init.error;\n if (init.messageId !== undefined) report.message_id = init.messageId;\n if (init.modelId !== undefined) report.model_id = init.modelId;\n if (init.provider !== undefined) {\n report.provider = init.provider;\n report.provider_type = init.provider;\n }\n assignUsage(report, init.usage);\n if (init.steps !== undefined) {\n report.steps = init.steps.map(createRunReportStep);\n report.total_steps = init.steps.length;\n } else if (init.totalSteps !== undefined) {\n report.total_steps = init.totalSteps;\n }\n if (init.toolCalls !== undefined) report.tool_calls = init.toolCalls;\n const durationMs = normalizeRunReportMilliseconds(init.durationMs);\n if (durationMs !== undefined) report.duration_ms = durationMs;\n const firstTokenMs = normalizeRunReportMilliseconds(init.firstTokenMs);\n if (firstTokenMs !== undefined) report.first_token_ms = firstTokenMs;\n if (init.outputText !== undefined) {\n report.output_text = truncateRunTelemetryText(init.outputText);\n }\n if (init.metadata !== undefined) report.metadata = init.metadata;\n if (init.telemetry?.environment !== undefined) {\n report.environment = init.telemetry.environment;\n }\n if (init.telemetry?.release !== undefined) {\n report.release = init.telemetry.release;\n }\n const tags = normalizeRunReportTags(init.telemetry?.tags);\n if (tags !== undefined) report.tags = tags;\n return report;\n}\n\n/**\n * Resolves the token counts a provider reports under any of the names the AI\n * SDK has used: the current top-level ones, the legacy prompt/completion pair,\n * and the v7 token detail objects. Returns undefined when no count is present,\n * so callers can tell an empty usage object from a zeroed one.\n */\nexport function normalizeRunTelemetryUsage(\n usage: RunTelemetryUsageInit,\n): RunTelemetryUsage | undefined {\n const inputTokens = usage.inputTokens ?? usage.promptTokens;\n const outputTokens = usage.outputTokens ?? usage.completionTokens;\n // AI SDK v7 moved these under token detail objects; v6 kept them top-level.\n const reasoningTokens =\n usage.reasoningTokens ?? usage.outputTokenDetails?.reasoningTokens;\n const cachedInputTokens =\n usage.cachedInputTokens ?? usage.inputTokenDetails?.cacheReadTokens;\n\n if (\n inputTokens == null &&\n outputTokens == null &&\n reasoningTokens == null &&\n cachedInputTokens == null\n ) {\n return undefined;\n }\n\n return {\n ...(inputTokens != null ? { inputTokens } : undefined),\n ...(outputTokens != null ? { outputTokens } : undefined),\n ...(reasoningTokens != null ? { reasoningTokens } : undefined),\n ...(cachedInputTokens != null ? { cachedInputTokens } : undefined),\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"runTelemetry.js","names":[],"sources":["../src/runTelemetry.ts"],"sourcesContent":["import type { SamplingCallData } from \"./instrumentMcpSampling\";\nimport type { AssistantCloudRunReport } from \"./AssistantCloudRuns\";\n\nconst MAX_TELEMETRY_TEXT_LENGTH = 50_000;\n\nconst BASE64_PATTERN = /^[A-Za-z0-9+/]+={0,2}$/;\n\nexport type AssistantCloudRunReportToolCall = {\n tool_name: string;\n tool_call_id: string;\n tool_args?: string;\n tool_result?: string;\n tool_source?: \"mcp\" | \"frontend\" | \"backend\";\n start_ms?: number;\n end_ms?: number;\n sampling_calls?: SamplingCallData[];\n};\n\nexport type RunReportOutcome =\n | \"aborted\"\n | \"disconnected\"\n | \"length\"\n | \"content_filter\";\n\n/**\n * Maps a finish event to the report status and outcome. `fallbackStatus`\n * applies when the event carries neither a finish reason nor a failure flag.\n */\nexport function deriveRunOutcome(\n input: {\n finishReason?: string | undefined;\n isAbort?: boolean | undefined;\n isDisconnect?: boolean | undefined;\n isError?: boolean | undefined;\n },\n fallbackStatus: \"completed\" | \"incomplete\" = \"completed\",\n): {\n status: \"completed\" | \"incomplete\" | \"error\";\n outcome?: RunReportOutcome;\n} {\n if (input.isError) return { status: \"error\" };\n if (input.isAbort) return { status: \"incomplete\", outcome: \"aborted\" };\n if (input.isDisconnect) {\n return { status: \"incomplete\", outcome: \"disconnected\" };\n }\n switch (input.finishReason) {\n case \"length\":\n return { status: \"incomplete\", outcome: \"length\" };\n case \"content-filter\":\n case \"content_filter\":\n return { status: \"incomplete\", outcome: \"content_filter\" };\n case \"cancelled\":\n return { status: \"incomplete\", outcome: \"aborted\" };\n case \"error\":\n return { status: \"error\" };\n default:\n return {\n status: input.finishReason === undefined ? fallbackStatus : \"completed\",\n };\n }\n}\n\nconst MAX_RUN_ERROR_CODE_LENGTH = 64;\nconst MAX_RUN_ERROR_LENGTH = 2048;\n\n/**\n * Reads the message and code the runs endpoint stores for a failed run. The\n * code is the error's `code` when it has one, else its class name.\n */\nexport function describeRunError(error: unknown): {\n error?: string;\n errorCode?: string;\n} {\n if (error == null) return {};\n const record =\n typeof error === \"object\" ? (error as Record<string, unknown>) : undefined;\n const message =\n typeof error === \"string\"\n ? error\n : typeof record?.message === \"string\"\n ? record.message\n : undefined;\n const code =\n typeof record?.code === \"string\"\n ? record.code\n : typeof record?.name === \"string\" && record.name !== \"Error\"\n ? record.name\n : undefined;\n return {\n ...(message\n ? { error: message.slice(0, MAX_RUN_ERROR_LENGTH) }\n : undefined),\n ...(code\n ? { errorCode: code.slice(0, MAX_RUN_ERROR_CODE_LENGTH) }\n : undefined),\n };\n}\n\n/**\n * Clamps a string to the size the runs endpoint accepts for a single span\n * field.\n */\nexport function truncateRunTelemetryText(value: string): string {\n if (value.length <= MAX_TELEMETRY_TEXT_LENGTH) return value;\n return value.slice(0, MAX_TELEMETRY_TEXT_LENGTH);\n}\n\nfunction safeStringify(value: unknown): string | undefined {\n if (value == null) return undefined;\n try {\n return truncateRunTelemetryText(JSON.stringify(value));\n } catch {\n return undefined;\n }\n}\n\nconst base64SizeKB = (value: string) =>\n ((value.length * 3) / 4 / 1024).toFixed(1);\n\n// Both call sites read a field the MCP content grammar already defines as\n// base64, so the test only has to recognise the encoding's own shape: its\n// alphabet, and a length that is a multiple of four. A size floor would leave\n// a short payload — `aGk=` is a whole image in this repo's own fixtures —\n// serialized raw.\nconst isInlineBase64 = (value: unknown): value is string => {\n if (typeof value !== \"string\") return false;\n const head = value.slice(0, 200);\n return head.length > 0 && head.length % 4 === 0 && BASE64_PATTERN.test(head);\n};\n\nfunction summarizeMcpContentBlock(item: unknown): unknown {\n if (!item || typeof item !== \"object\") return item;\n const block = item as {\n type?: unknown;\n data?: unknown;\n resource?: unknown;\n };\n if (\n (block.type === \"image\" || block.type === \"audio\") &&\n isInlineBase64(block.data)\n ) {\n return {\n ...block,\n data: `[${String(block.type)}: ${base64SizeKB(block.data)}KB]`,\n };\n }\n // An EmbeddedResource is the third inline carrier: a binary resource arrives\n // as base64 under `resource.blob` rather than as a top-level `data` field.\n if (\n block.type === \"resource\" &&\n block.resource &&\n typeof block.resource === \"object\"\n ) {\n const resource = block.resource as { blob?: unknown };\n if (isInlineBase64(resource.blob)) {\n return {\n ...block,\n resource: {\n ...resource,\n blob: `[resource: ${base64SizeKB(resource.blob)}KB]`,\n },\n };\n }\n }\n return item;\n}\n\nfunction summarizeMcpResult(value: unknown): string | undefined {\n if (value == null) return undefined;\n try {\n const parsed = typeof value === \"string\" ? JSON.parse(value) : value;\n if (Array.isArray(parsed)) {\n return truncateRunTelemetryText(\n JSON.stringify(parsed.map(summarizeMcpContentBlock)),\n );\n }\n // `callTool` resolves to a CallToolResult, so the blocks carrying base64\n // arrive under `content` rather than as the result itself.\n if (typeof parsed === \"object\") {\n const content = (parsed as { content?: unknown }).content;\n if (Array.isArray(content)) {\n return truncateRunTelemetryText(\n JSON.stringify({\n ...(parsed as Record<string, unknown>),\n content: content.map(summarizeMcpContentBlock),\n }),\n );\n }\n }\n } catch {\n // not a shape with summarizable content, fall through\n }\n return safeStringify(value);\n}\n\nexport type RunTelemetryToolCallInit = {\n toolName: string;\n toolCallId: string;\n args?: unknown;\n /**\n * Pre-serialized arguments, used in place of serializing `args`. Values over\n * the span size are clamped before they are included in the report.\n */\n argsText?: string | undefined;\n result?: unknown;\n toolSource?: \"mcp\" | \"frontend\" | \"backend\" | undefined;\n};\n\n/**\n * Serializes one tool call into the shape the runs endpoint accepts. An `mcp`\n * source has its result summarized, because MCP content blocks carry inline\n * base64 image and audio payloads that would otherwise dominate the report.\n */\nexport function createRunTelemetryToolCall(\n init: RunTelemetryToolCallInit,\n): AssistantCloudRunReportToolCall {\n const { toolName, toolCallId, args, argsText, result, toolSource } = init;\n const call: AssistantCloudRunReportToolCall = {\n tool_name: toolName,\n tool_call_id: toolCallId,\n };\n const toolArgs =\n argsText != null ? truncateRunTelemetryText(argsText) : safeStringify(args);\n if (toolArgs !== undefined) call.tool_args = toolArgs;\n const toolResult =\n toolSource === \"mcp\" ? summarizeMcpResult(result) : safeStringify(result);\n if (toolResult !== undefined) call.tool_result = toolResult;\n if (toolSource) call.tool_source = toolSource;\n return call;\n}\n\n/**\n * Resolves the model ID a run reports, in the order an app can supply it: an\n * explicit `modelId`, the `custom` bag, then the per-step `response.modelId`\n * that a `messageMetadata` callback copies off the AI SDK's finish-step part.\n * The AI SDK puts no model ID on a UI message part, so message metadata is the\n * only channel one arrives on.\n */\nexport function extractRunTelemetryModelId(\n metadata: Record<string, unknown> | undefined,\n): string | undefined {\n if (!metadata) return undefined;\n if (typeof metadata.modelId === \"string\") return metadata.modelId;\n const custom = metadata.custom as Record<string, unknown> | undefined;\n if (typeof custom?.modelId === \"string\") return custom.modelId;\n\n const steps: unknown = metadata.steps;\n if (!Array.isArray(steps)) return undefined;\n for (const step of steps as unknown[]) {\n if (!step || typeof step !== \"object\") continue;\n const response = (step as Record<string, unknown>).response;\n if (!response || typeof response !== \"object\") continue;\n const modelId = (response as Record<string, unknown>).modelId;\n if (typeof modelId === \"string\") return modelId;\n }\n return undefined;\n}\n\nexport type RunTelemetryUsage = {\n inputTokens?: number | undefined;\n outputTokens?: number | undefined;\n reasoningTokens?: number | undefined;\n cachedInputTokens?: number | undefined;\n};\n\nexport type RunTelemetryUsageInit = RunTelemetryUsage & {\n promptTokens?: number | undefined;\n completionTokens?: number | undefined;\n inputTokenDetails?: { cacheReadTokens?: number };\n outputTokenDetails?: { reasoningTokens?: number };\n};\n\nexport type RunReportStepInit = {\n usage?: RunTelemetryUsageInit | undefined;\n toolCalls?: AssistantCloudRunReportToolCall[] | undefined;\n startMs?: number | undefined;\n endMs?: number | undefined;\n finishReason?: string | undefined;\n input?: string | undefined;\n};\n\n/**\n * The run report fields read from the messages of one run, in whichever\n * format they were stored: the status the messages imply, the tool calls, the\n * steps, the text, the usage and the model.\n */\nexport type RunMessageTelemetry = {\n assistantMessageId?: string;\n status: \"completed\" | \"incomplete\";\n toolCalls?: AssistantCloudRunReportToolCall[];\n steps?: RunReportStepInit[];\n totalSteps?: number;\n outputText?: string;\n usage?: RunTelemetryUsage;\n modelId?: string;\n metadata?: Record<string, unknown>;\n};\n\nexport type RunReportInit = {\n threadId: string;\n status: AssistantCloudRunReport[\"status\"];\n outcome?: AssistantCloudRunReport[\"outcome_type\"] | undefined;\n errorCode?: string | undefined;\n error?: string | undefined;\n messageId?: string | undefined;\n traceId?: string | undefined;\n rootSpanId?: string | undefined;\n modelId?: string | undefined;\n provider?: string | undefined;\n usage?: RunTelemetryUsageInit | undefined;\n steps?: RunReportStepInit[] | undefined;\n totalSteps?: number | undefined;\n toolCalls?: AssistantCloudRunReportToolCall[] | undefined;\n durationMs?: number | undefined;\n firstTokenMs?: number | undefined;\n costUsd?: number | undefined;\n costDetails?:\n | {\n input?: number | undefined;\n inputCachedTokens?: number | undefined;\n output?: number | undefined;\n total?: number | undefined;\n }\n | undefined;\n outputText?: string | undefined;\n attributes?: Record<string, unknown> | undefined;\n metadata?: Record<string, unknown> | undefined;\n telemetry?: {\n environment?: string | undefined;\n release?: string | undefined;\n tags?: readonly string[] | undefined;\n };\n};\n\nfunction assignUsage(\n report: AssistantCloudRunReport,\n usage: RunTelemetryUsageInit | undefined,\n): void {\n if (!usage) return;\n const normalized = normalizeRunTelemetryUsage(usage);\n if (!normalized) return;\n if (normalized.inputTokens !== undefined) {\n report.input_tokens = normalized.inputTokens;\n }\n if (normalized.outputTokens !== undefined) {\n report.output_tokens = normalized.outputTokens;\n }\n if (normalized.reasoningTokens !== undefined) {\n report.reasoning_tokens = normalized.reasoningTokens;\n }\n if (normalized.cachedInputTokens !== undefined) {\n report.cached_input_tokens = normalized.cachedInputTokens;\n }\n}\n\nfunction createRunReportStep(\n init: RunReportStepInit,\n): NonNullable<AssistantCloudRunReport[\"steps\"]>[number] {\n const step: NonNullable<AssistantCloudRunReport[\"steps\"]>[number] = {};\n const usage = init.usage ? normalizeRunTelemetryUsage(init.usage) : undefined;\n if (usage?.inputTokens !== undefined) step.input_tokens = usage.inputTokens;\n if (usage?.outputTokens !== undefined)\n step.output_tokens = usage.outputTokens;\n if (usage?.reasoningTokens !== undefined) {\n step.reasoning_tokens = usage.reasoningTokens;\n }\n if (usage?.cachedInputTokens !== undefined) {\n step.cached_input_tokens = usage.cachedInputTokens;\n }\n if (init.toolCalls !== undefined) step.tool_calls = init.toolCalls;\n if (init.startMs !== undefined) step.start_ms = init.startMs;\n if (init.endMs !== undefined) step.end_ms = init.endMs;\n if (init.finishReason !== undefined) {\n step.finish_reason = init.finishReason.slice(0, 32);\n }\n if (init.input !== undefined) {\n step.input = truncateRunTelemetryText(init.input);\n }\n return step;\n}\n\nfunction normalizeRunReportTags(\n tags: readonly string[] | undefined,\n): string[] | undefined {\n if (!tags) return undefined;\n const result: string[] = [];\n const seen = new Set<string>();\n for (const tag of tags) {\n const normalized = tag.trim().slice(0, 64).trim();\n if (!normalized || seen.has(normalized)) continue;\n seen.add(normalized);\n result.push(normalized);\n if (result.length === 20) break;\n }\n return result.length > 0 ? result : undefined;\n}\n\nfunction normalizeRunReportMilliseconds(\n value: number | undefined,\n): number | undefined {\n if (value === undefined || !Number.isFinite(value)) return undefined;\n return Math.max(0, Math.round(value));\n}\n\nfunction normalizeRunReportCost(value: number | undefined): number | undefined {\n if (value === undefined || !Number.isFinite(value) || value < 0) {\n return undefined;\n }\n return value;\n}\n\nfunction assignCostDetails(\n report: AssistantCloudRunReport,\n costDetails: RunReportInit[\"costDetails\"],\n): void {\n if (!costDetails) return;\n const normalized: NonNullable<AssistantCloudRunReport[\"cost_details\"]> = {};\n const input = normalizeRunReportCost(costDetails.input);\n if (input !== undefined) normalized.input = input;\n const inputCachedTokens = normalizeRunReportCost(\n costDetails.inputCachedTokens,\n );\n if (inputCachedTokens !== undefined) {\n normalized.input_cached_tokens = inputCachedTokens;\n }\n const output = normalizeRunReportCost(costDetails.output);\n if (output !== undefined) normalized.output = output;\n const total = normalizeRunReportCost(costDetails.total);\n if (total !== undefined) normalized.total = total;\n if (Object.keys(normalized).length > 0) report.cost_details = normalized;\n}\n\nexport function createRunReport(init: RunReportInit): AssistantCloudRunReport {\n const report: AssistantCloudRunReport = {\n thread_id: init.threadId,\n status: init.status,\n };\n const traceId = init.traceId?.toLowerCase();\n if (traceId && /^[0-9a-f]{32}$/.test(traceId)) report.trace_id = traceId;\n const rootSpanId = init.rootSpanId?.toLowerCase();\n if (rootSpanId && /^[0-9a-f]{16}$/.test(rootSpanId)) {\n report.root_span_id = rootSpanId;\n }\n if (init.outcome !== undefined) report.outcome_type = init.outcome;\n if (init.errorCode !== undefined) report.error_code = init.errorCode;\n if (init.error !== undefined) report.error = init.error;\n if (init.messageId !== undefined) report.message_id = init.messageId;\n if (init.modelId !== undefined) report.model_id = init.modelId;\n if (init.provider !== undefined) {\n report.provider = init.provider;\n report.provider_type = init.provider;\n }\n assignUsage(report, init.usage);\n if (init.steps !== undefined) {\n report.steps = init.steps.map(createRunReportStep);\n report.total_steps = init.steps.length;\n } else if (init.totalSteps !== undefined) {\n report.total_steps = init.totalSteps;\n }\n if (init.toolCalls !== undefined) report.tool_calls = init.toolCalls;\n const durationMs = normalizeRunReportMilliseconds(init.durationMs);\n if (durationMs !== undefined) report.duration_ms = durationMs;\n const firstTokenMs = normalizeRunReportMilliseconds(init.firstTokenMs);\n if (firstTokenMs !== undefined) report.first_token_ms = firstTokenMs;\n const costUsd = normalizeRunReportCost(init.costUsd);\n if (costUsd !== undefined) report.cost_usd = costUsd;\n assignCostDetails(report, init.costDetails);\n if (init.outputText !== undefined) {\n report.output_text = truncateRunTelemetryText(init.outputText);\n }\n if (init.attributes !== undefined) report.attributes = init.attributes;\n if (init.metadata !== undefined) report.metadata = init.metadata;\n if (init.telemetry?.environment !== undefined) {\n report.environment = init.telemetry.environment;\n }\n if (init.telemetry?.release !== undefined) {\n report.release = init.telemetry.release;\n }\n const tags = normalizeRunReportTags(init.telemetry?.tags);\n if (tags !== undefined) report.tags = tags;\n return report;\n}\n\n/**\n * Resolves the token counts a provider reports under any of the names the AI\n * SDK has used: the current top-level ones, the legacy prompt/completion pair,\n * and the v7 token detail objects. Returns undefined when no count is present,\n * so callers can tell an empty usage object from a zeroed one.\n */\nexport function normalizeRunTelemetryUsage(\n usage: RunTelemetryUsageInit,\n): RunTelemetryUsage | undefined {\n const inputTokens = usage.inputTokens ?? usage.promptTokens;\n const outputTokens = usage.outputTokens ?? usage.completionTokens;\n // AI SDK v7 moved these under token detail objects; v6 kept them top-level.\n const reasoningTokens =\n usage.reasoningTokens ?? usage.outputTokenDetails?.reasoningTokens;\n const cachedInputTokens =\n usage.cachedInputTokens ?? usage.inputTokenDetails?.cacheReadTokens;\n\n if (\n inputTokens == null &&\n outputTokens == null &&\n reasoningTokens == null &&\n cachedInputTokens == null\n ) {\n return undefined;\n }\n\n return {\n ...(inputTokens != null ? { inputTokens } : undefined),\n ...(outputTokens != null ? { outputTokens } : undefined),\n ...(reasoningTokens != null ? { reasoningTokens } : undefined),\n ...(cachedInputTokens != null ? { cachedInputTokens } : undefined),\n };\n}\n"],"mappings":";AAGA,MAAM,4BAA4B;AAElC,MAAM,iBAAiB;;;;;AAuBvB,SAAgB,iBACd,OAMA,iBAA6C,aAI7C;CACA,IAAI,MAAM,SAAS,OAAO,EAAE,QAAQ,QAAQ;CAC5C,IAAI,MAAM,SAAS,OAAO;EAAE,QAAQ;EAAc,SAAS;CAAU;CACrE,IAAI,MAAM,cACR,OAAO;EAAE,QAAQ;EAAc,SAAS;CAAe;CAEzD,QAAQ,MAAM,cAAd;EACE,KAAK,UACH,OAAO;GAAE,QAAQ;GAAc,SAAS;EAAS;EACnD,KAAK;EACL,KAAK,kBACH,OAAO;GAAE,QAAQ;GAAc,SAAS;EAAiB;EAC3D,KAAK,aACH,OAAO;GAAE,QAAQ;GAAc,SAAS;EAAU;EACpD,KAAK,SACH,OAAO,EAAE,QAAQ,QAAQ;EAC3B,SACE,OAAO,EACL,QAAQ,MAAM,iBAAiB,KAAA,IAAY,iBAAiB,YAC9D;CACJ;AACF;AAEA,MAAM,4BAA4B;AAClC,MAAM,uBAAuB;;;;;AAM7B,SAAgB,iBAAiB,OAG/B;CACA,IAAI,SAAS,MAAM,OAAO,CAAC;CAC3B,MAAM,SACJ,OAAO,UAAU,WAAY,QAAoC,KAAA;CACnE,MAAM,UACJ,OAAO,UAAU,WACb,QACA,OAAO,QAAQ,YAAY,WACzB,OAAO,UACP,KAAA;CACR,MAAM,OACJ,OAAO,QAAQ,SAAS,WACpB,OAAO,OACP,OAAO,QAAQ,SAAS,YAAY,OAAO,SAAS,UAClD,OAAO,OACP,KAAA;CACR,OAAO;EACL,GAAI,UACA,EAAE,OAAO,QAAQ,MAAM,GAAG,oBAAoB,EAAE,IAChD,KAAA;EACJ,GAAI,OACA,EAAE,WAAW,KAAK,MAAM,GAAG,yBAAyB,EAAE,IACtD,KAAA;CACN;AACF;;;;;AAMA,SAAgB,yBAAyB,OAAuB;CAC9D,IAAI,MAAM,UAAU,2BAA2B,OAAO;CACtD,OAAO,MAAM,MAAM,GAAG,yBAAyB;AACjD;AAEA,SAAS,cAAc,OAAoC;CACzD,IAAI,SAAS,MAAM,OAAO,KAAA;CAC1B,IAAI;EACF,OAAO,yBAAyB,KAAK,UAAU,KAAK,CAAC;CACvD,QAAQ;EACN;CACF;AACF;AAEA,MAAM,gBAAgB,WAClB,MAAM,SAAS,IAAK,IAAI,KAAA,CAAM,QAAQ,CAAC;AAO3C,MAAM,kBAAkB,UAAoC;CAC1D,IAAI,OAAO,UAAU,UAAU,OAAO;CACtC,MAAM,OAAO,MAAM,MAAM,GAAG,GAAG;CAC/B,OAAO,KAAK,SAAS,KAAK,KAAK,SAAS,MAAM,KAAK,eAAe,KAAK,IAAI;AAC7E;AAEA,SAAS,yBAAyB,MAAwB;CACxD,IAAI,CAAC,QAAQ,OAAO,SAAS,UAAU,OAAO;CAC9C,MAAM,QAAQ;CAKd,KACG,MAAM,SAAS,WAAW,MAAM,SAAS,YAC1C,eAAe,MAAM,IAAI,GAEzB,OAAO;EACL,GAAG;EACH,MAAM,IAAI,OAAO,MAAM,IAAI,EAAE,IAAI,aAAa,MAAM,IAAI,EAAE;CAC5D;CAIF,IACE,MAAM,SAAS,cACf,MAAM,YACN,OAAO,MAAM,aAAa,UAC1B;EACA,MAAM,WAAW,MAAM;EACvB,IAAI,eAAe,SAAS,IAAI,GAC9B,OAAO;GACL,GAAG;GACH,UAAU;IACR,GAAG;IACH,MAAM,cAAc,aAAa,SAAS,IAAI,EAAE;GAClD;EACF;CAEJ;CACA,OAAO;AACT;AAEA,SAAS,mBAAmB,OAAoC;CAC9D,IAAI,SAAS,MAAM,OAAO,KAAA;CAC1B,IAAI;EACF,MAAM,SAAS,OAAO,UAAU,WAAW,KAAK,MAAM,KAAK,IAAI;EAC/D,IAAI,MAAM,QAAQ,MAAM,GACtB,OAAO,yBACL,KAAK,UAAU,OAAO,IAAI,wBAAwB,CAAC,CACrD;EAIF,IAAI,OAAO,WAAW,UAAU;GAC9B,MAAM,UAAW,OAAiC;GAClD,IAAI,MAAM,QAAQ,OAAO,GACvB,OAAO,yBACL,KAAK,UAAU;IACb,GAAI;IACJ,SAAS,QAAQ,IAAI,wBAAwB;GAC/C,CAAC,CACH;EAEJ;CACF,QAAQ,CAER;CACA,OAAO,cAAc,KAAK;AAC5B;;;;;;AAoBA,SAAgB,2BACd,MACiC;CACjC,MAAM,EAAE,UAAU,YAAY,MAAM,UAAU,QAAQ,eAAe;CACrE,MAAM,OAAwC;EAC5C,WAAW;EACX,cAAc;CAChB;CACA,MAAM,WACJ,YAAY,OAAO,yBAAyB,QAAQ,IAAI,cAAc,IAAI;CAC5E,IAAI,aAAa,KAAA,GAAW,KAAK,YAAY;CAC7C,MAAM,aACJ,eAAe,QAAQ,mBAAmB,MAAM,IAAI,cAAc,MAAM;CAC1E,IAAI,eAAe,KAAA,GAAW,KAAK,cAAc;CACjD,IAAI,YAAY,KAAK,cAAc;CACnC,OAAO;AACT;;;;;;;;AASA,SAAgB,2BACd,UACoB;CACpB,IAAI,CAAC,UAAU,OAAO,KAAA;CACtB,IAAI,OAAO,SAAS,YAAY,UAAU,OAAO,SAAS;CAC1D,MAAM,SAAS,SAAS;CACxB,IAAI,OAAO,QAAQ,YAAY,UAAU,OAAO,OAAO;CAEvD,MAAM,QAAiB,SAAS;CAChC,IAAI,CAAC,MAAM,QAAQ,KAAK,GAAG,OAAO,KAAA;CAClC,KAAK,MAAM,QAAQ,OAAoB;EACrC,IAAI,CAAC,QAAQ,OAAO,SAAS,UAAU;EACvC,MAAM,WAAY,KAAiC;EACnD,IAAI,CAAC,YAAY,OAAO,aAAa,UAAU;EAC/C,MAAM,UAAW,SAAqC;EACtD,IAAI,OAAO,YAAY,UAAU,OAAO;CAC1C;AAEF;AA8EA,SAAS,YACP,QACA,OACM;CACN,IAAI,CAAC,OAAO;CACZ,MAAM,aAAa,2BAA2B,KAAK;CACnD,IAAI,CAAC,YAAY;CACjB,IAAI,WAAW,gBAAgB,KAAA,GAC7B,OAAO,eAAe,WAAW;CAEnC,IAAI,WAAW,iBAAiB,KAAA,GAC9B,OAAO,gBAAgB,WAAW;CAEpC,IAAI,WAAW,oBAAoB,KAAA,GACjC,OAAO,mBAAmB,WAAW;CAEvC,IAAI,WAAW,sBAAsB,KAAA,GACnC,OAAO,sBAAsB,WAAW;AAE5C;AAEA,SAAS,oBACP,MACuD;CACvD,MAAM,OAA8D,CAAC;CACrE,MAAM,QAAQ,KAAK,QAAQ,2BAA2B,KAAK,KAAK,IAAI,KAAA;CACpE,IAAI,OAAO,gBAAgB,KAAA,GAAW,KAAK,eAAe,MAAM;CAChE,IAAI,OAAO,iBAAiB,KAAA,GAC1B,KAAK,gBAAgB,MAAM;CAC7B,IAAI,OAAO,oBAAoB,KAAA,GAC7B,KAAK,mBAAmB,MAAM;CAEhC,IAAI,OAAO,sBAAsB,KAAA,GAC/B,KAAK,sBAAsB,MAAM;CAEnC,IAAI,KAAK,cAAc,KAAA,GAAW,KAAK,aAAa,KAAK;CACzD,IAAI,KAAK,YAAY,KAAA,GAAW,KAAK,WAAW,KAAK;CACrD,IAAI,KAAK,UAAU,KAAA,GAAW,KAAK,SAAS,KAAK;CACjD,IAAI,KAAK,iBAAiB,KAAA,GACxB,KAAK,gBAAgB,KAAK,aAAa,MAAM,GAAG,EAAE;CAEpD,IAAI,KAAK,UAAU,KAAA,GACjB,KAAK,QAAQ,yBAAyB,KAAK,KAAK;CAElD,OAAO;AACT;AAEA,SAAS,uBACP,MACsB;CACtB,IAAI,CAAC,MAAM,OAAO,KAAA;CAClB,MAAM,SAAmB,CAAC;CAC1B,MAAM,uBAAO,IAAI,IAAY;CAC7B,KAAK,MAAM,OAAO,MAAM;EACtB,MAAM,aAAa,IAAI,KAAK,CAAC,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC,KAAK;EAChD,IAAI,CAAC,cAAc,KAAK,IAAI,UAAU,GAAG;EACzC,KAAK,IAAI,UAAU;EACnB,OAAO,KAAK,UAAU;EACtB,IAAI,OAAO,WAAW,IAAI;CAC5B;CACA,OAAO,OAAO,SAAS,IAAI,SAAS,KAAA;AACtC;AAEA,SAAS,+BACP,OACoB;CACpB,IAAI,UAAU,KAAA,KAAa,CAAC,OAAO,SAAS,KAAK,GAAG,OAAO,KAAA;CAC3D,OAAO,KAAK,IAAI,GAAG,KAAK,MAAM,KAAK,CAAC;AACtC;AAEA,SAAS,uBAAuB,OAA+C;CAC7E,IAAI,UAAU,KAAA,KAAa,CAAC,OAAO,SAAS,KAAK,KAAK,QAAQ,GAC5D;CAEF,OAAO;AACT;AAEA,SAAS,kBACP,QACA,aACM;CACN,IAAI,CAAC,aAAa;CAClB,MAAM,aAAmE,CAAC;CAC1E,MAAM,QAAQ,uBAAuB,YAAY,KAAK;CACtD,IAAI,UAAU,KAAA,GAAW,WAAW,QAAQ;CAC5C,MAAM,oBAAoB,uBACxB,YAAY,iBACd;CACA,IAAI,sBAAsB,KAAA,GACxB,WAAW,sBAAsB;CAEnC,MAAM,SAAS,uBAAuB,YAAY,MAAM;CACxD,IAAI,WAAW,KAAA,GAAW,WAAW,SAAS;CAC9C,MAAM,QAAQ,uBAAuB,YAAY,KAAK;CACtD,IAAI,UAAU,KAAA,GAAW,WAAW,QAAQ;CAC5C,IAAI,OAAO,KAAK,UAAU,CAAC,CAAC,SAAS,GAAG,OAAO,eAAe;AAChE;AAEA,SAAgB,gBAAgB,MAA8C;CAC5E,MAAM,SAAkC;EACtC,WAAW,KAAK;EAChB,QAAQ,KAAK;CACf;CACA,MAAM,UAAU,KAAK,SAAS,YAAY;CAC1C,IAAI,WAAW,iBAAiB,KAAK,OAAO,GAAG,OAAO,WAAW;CACjE,MAAM,aAAa,KAAK,YAAY,YAAY;CAChD,IAAI,cAAc,iBAAiB,KAAK,UAAU,GAChD,OAAO,eAAe;CAExB,IAAI,KAAK,YAAY,KAAA,GAAW,OAAO,eAAe,KAAK;CAC3D,IAAI,KAAK,cAAc,KAAA,GAAW,OAAO,aAAa,KAAK;CAC3D,IAAI,KAAK,UAAU,KAAA,GAAW,OAAO,QAAQ,KAAK;CAClD,IAAI,KAAK,cAAc,KAAA,GAAW,OAAO,aAAa,KAAK;CAC3D,IAAI,KAAK,YAAY,KAAA,GAAW,OAAO,WAAW,KAAK;CACvD,IAAI,KAAK,aAAa,KAAA,GAAW;EAC/B,OAAO,WAAW,KAAK;EACvB,OAAO,gBAAgB,KAAK;CAC9B;CACA,YAAY,QAAQ,KAAK,KAAK;CAC9B,IAAI,KAAK,UAAU,KAAA,GAAW;EAC5B,OAAO,QAAQ,KAAK,MAAM,IAAI,mBAAmB;EACjD,OAAO,cAAc,KAAK,MAAM;CAClC,OAAO,IAAI,KAAK,eAAe,KAAA,GAC7B,OAAO,cAAc,KAAK;CAE5B,IAAI,KAAK,cAAc,KAAA,GAAW,OAAO,aAAa,KAAK;CAC3D,MAAM,aAAa,+BAA+B,KAAK,UAAU;CACjE,IAAI,eAAe,KAAA,GAAW,OAAO,cAAc;CACnD,MAAM,eAAe,+BAA+B,KAAK,YAAY;CACrE,IAAI,iBAAiB,KAAA,GAAW,OAAO,iBAAiB;CACxD,MAAM,UAAU,uBAAuB,KAAK,OAAO;CACnD,IAAI,YAAY,KAAA,GAAW,OAAO,WAAW;CAC7C,kBAAkB,QAAQ,KAAK,WAAW;CAC1C,IAAI,KAAK,eAAe,KAAA,GACtB,OAAO,cAAc,yBAAyB,KAAK,UAAU;CAE/D,IAAI,KAAK,eAAe,KAAA,GAAW,OAAO,aAAa,KAAK;CAC5D,IAAI,KAAK,aAAa,KAAA,GAAW,OAAO,WAAW,KAAK;CACxD,IAAI,KAAK,WAAW,gBAAgB,KAAA,GAClC,OAAO,cAAc,KAAK,UAAU;CAEtC,IAAI,KAAK,WAAW,YAAY,KAAA,GAC9B,OAAO,UAAU,KAAK,UAAU;CAElC,MAAM,OAAO,uBAAuB,KAAK,WAAW,IAAI;CACxD,IAAI,SAAS,KAAA,GAAW,OAAO,OAAO;CACtC,OAAO;AACT;;;;;;;AAQA,SAAgB,2BACd,OAC+B;CAC/B,MAAM,cAAc,MAAM,eAAe,MAAM;CAC/C,MAAM,eAAe,MAAM,gBAAgB,MAAM;CAEjD,MAAM,kBACJ,MAAM,mBAAmB,MAAM,oBAAoB;CACrD,MAAM,oBACJ,MAAM,qBAAqB,MAAM,mBAAmB;CAEtD,IACE,eAAe,QACf,gBAAgB,QAChB,mBAAmB,QACnB,qBAAqB,MAErB;CAGF,OAAO;EACL,GAAI,eAAe,OAAO,EAAE,YAAY,IAAI,KAAA;EAC5C,GAAI,gBAAgB,OAAO,EAAE,aAAa,IAAI,KAAA;EAC9C,GAAI,mBAAmB,OAAO,EAAE,gBAAgB,IAAI,KAAA;EACpD,GAAI,qBAAqB,OAAO,EAAE,kBAAkB,IAAI,KAAA;CAC1D;AACF"}
|
|
@@ -1,31 +1,29 @@
|
|
|
1
|
-
import { ReadableSpan, SpanExporter, SpanProcessor } from "@opentelemetry/sdk-trace-base";
|
|
2
|
-
//#region src/telemetry/index.d.ts
|
|
1
|
+
import { type ReadableSpan, type SpanExporter, type SpanProcessor } from "@opentelemetry/sdk-trace-base";
|
|
3
2
|
export type AssistantCloudTraceExportOptions = {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
apiKey: string;
|
|
4
|
+
baseUrl?: string;
|
|
5
|
+
headers?: Record<string, string>;
|
|
7
6
|
};
|
|
8
7
|
export type AssistantCloudSpanProcessorOptions = {
|
|
9
|
-
|
|
8
|
+
filter?: (span: ReadableSpan) => boolean;
|
|
10
9
|
};
|
|
11
|
-
export declare function assistantCloudTraceExportOptions({ apiKey, baseUrl, headers }: AssistantCloudTraceExportOptions): {
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
export declare function assistantCloudTraceExportOptions({ apiKey, baseUrl, headers, }: AssistantCloudTraceExportOptions): {
|
|
11
|
+
url: string;
|
|
12
|
+
headers: Record<string, string>;
|
|
14
13
|
};
|
|
15
14
|
export declare function createAssistantCloudTraceExporter(options: AssistantCloudTraceExportOptions): SpanExporter;
|
|
16
15
|
export declare function isAssistantCloudSpan(span: ReadableSpan): boolean;
|
|
17
16
|
export declare function createAssistantCloudSpanProcessor(exporter: SpanExporter, options?: AssistantCloudSpanProcessorOptions): SpanProcessor;
|
|
18
17
|
export declare function assistantCloudTraceMetadata(): {
|
|
19
|
-
|
|
18
|
+
traceId?: string;
|
|
20
19
|
};
|
|
21
20
|
export declare function withAssistantCloudTraceMetadata<Part extends {
|
|
22
|
-
|
|
21
|
+
type: string;
|
|
23
22
|
} = {
|
|
24
|
-
|
|
23
|
+
type: string;
|
|
25
24
|
}>(messageMetadata?: (options: {
|
|
26
|
-
|
|
25
|
+
part: Part;
|
|
27
26
|
}) => Record<string, unknown> | undefined): (options: {
|
|
28
|
-
|
|
27
|
+
part: Part;
|
|
29
28
|
}) => Record<string, unknown> | undefined;
|
|
30
|
-
//#endregion
|
|
31
29
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/telemetry/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,aAAa,EACnB,MAAM,+BAA+B,CAAC;AAKvC,MAAM,MAAM,gCAAgC,GAAG;IAC7C,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC,CAAC;AAEF,MAAM,MAAM,kCAAkC,GAAG;IAC/C,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,YAAY,KAAK,OAAO,CAAC;CAC1C,CAAC;AAEF,wBAAgB,gCAAgC,CAAC,EAC/C,MAAM,EACN,OAAkC,EAClC,OAAO,GACR,EAAE,gCAAgC,GAAG;IACpC,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC,CAUA;AAQD,wBAAgB,iCAAiC,CAC/C,OAAO,EAAE,gCAAgC,GACxC,YAAY,CAEd;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAKhE;AAED,wBAAgB,iCAAiC,CAC/C,QAAQ,EAAE,YAAY,EACtB,OAAO,GAAE,kCAAuC,GAC/C,aAAa,CAYf;AAED,wBAAgB,2BAA2B,IAAI;IAAE,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,CAKlE;AAED,wBAAgB,+BAA+B,CAC7C,IAAI,SAAS;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,EAEhD,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE;IAC1B,IAAI,EAAE,IAAI,CAAC;CACZ,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,GACxC,CAAC,OAAO,EAAE;IAAE,IAAI,EAAE,IAAI,CAAA;CAAE,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAOlE"}
|
package/dist/version.d.ts
CHANGED
package/dist/version.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"version.d.ts","
|
|
1
|
+
{"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,uBAAuB,EAAE,MAGzB,CAAC"}
|
package/dist/version.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "assistant-cloud",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3",
|
|
4
4
|
"description": "Cloud integration for assistant-ui",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"assistant",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
],
|
|
36
36
|
"sideEffects": false,
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"assistant-stream": "^0.3.
|
|
38
|
+
"assistant-stream": "^0.3.45"
|
|
39
39
|
},
|
|
40
40
|
"peerDependencies": {
|
|
41
41
|
"@opentelemetry/api": "^1.9.0",
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
}
|
|
59
59
|
},
|
|
60
60
|
"devDependencies": {
|
|
61
|
-
"@assistant-ui/x-buildutils": "0.0.
|
|
61
|
+
"@assistant-ui/x-buildutils": "0.0.29",
|
|
62
62
|
"@opentelemetry/api": "^1.9.1",
|
|
63
63
|
"@opentelemetry/exporter-trace-otlp-http": "^0.222.0",
|
|
64
64
|
"@opentelemetry/sdk-trace-base": "^2.11.0",
|
package/src/AssistantCloud.ts
CHANGED
|
@@ -9,7 +9,10 @@ import { AssistantCloudProjects } from "./AssistantCloudProjects";
|
|
|
9
9
|
import { AssistantCloudRuns } from "./AssistantCloudRuns";
|
|
10
10
|
import { AssistantCloudThreads } from "./AssistantCloudThreads";
|
|
11
11
|
import { AssistantCloudFiles } from "./AssistantCloudFiles";
|
|
12
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
AssistantCloudEvents,
|
|
14
|
+
clearPendingAssistantCloudEvents,
|
|
15
|
+
} from "./AssistantCloudEvents";
|
|
13
16
|
import { AssistantCloudScores } from "./AssistantCloudScores";
|
|
14
17
|
|
|
15
18
|
export class AssistantCloud {
|
|
@@ -27,7 +30,7 @@ export class AssistantCloud {
|
|
|
27
30
|
const api = new AssistantCloudAPI(config);
|
|
28
31
|
this.registerSdk = api.registerSdk;
|
|
29
32
|
const t = config.telemetry;
|
|
30
|
-
|
|
33
|
+
const telemetry =
|
|
31
34
|
t === false
|
|
32
35
|
? { enabled: false }
|
|
33
36
|
: t === true || t === undefined
|
|
@@ -36,6 +39,18 @@ export class AssistantCloud {
|
|
|
36
39
|
...t,
|
|
37
40
|
enabled: t.enabled !== false,
|
|
38
41
|
};
|
|
42
|
+
this.telemetry = new Proxy(telemetry, {
|
|
43
|
+
set: (target, property, value) => {
|
|
44
|
+
const updated = Reflect.set(target, property, value);
|
|
45
|
+
if (
|
|
46
|
+
(property === "enabled" || property === "events") &&
|
|
47
|
+
(target.enabled === false || target.events === false)
|
|
48
|
+
) {
|
|
49
|
+
clearPendingAssistantCloudEvents(this.events);
|
|
50
|
+
}
|
|
51
|
+
return updated;
|
|
52
|
+
},
|
|
53
|
+
});
|
|
39
54
|
|
|
40
55
|
this.threads = new AssistantCloudThreads(api);
|
|
41
56
|
this.projects = new AssistantCloudProjects(api);
|
package/src/AssistantCloudAPI.ts
CHANGED
|
@@ -25,6 +25,10 @@ export type AssistantCloudTelemetryConfig = {
|
|
|
25
25
|
* engagement events.
|
|
26
26
|
*/
|
|
27
27
|
events?: boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Stores the messages of runtimes whose backend keeps the transcript (LangGraph, LangChain, Google ADK, custom external stores), so the dashboard can show them. Defaults to `true` when telemetry is enabled. Set to `false` to keep run reports and events without storing those messages.
|
|
30
|
+
*/
|
|
31
|
+
messages?: boolean;
|
|
28
32
|
release?: string;
|
|
29
33
|
environment?: string;
|
|
30
34
|
tags?: string[];
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
4
4
|
import {
|
|
5
5
|
AssistantCloudEvents,
|
|
6
|
+
clearPendingAssistantCloudEvents,
|
|
6
7
|
type AssistantCloudEvent,
|
|
7
8
|
} from "./AssistantCloudEvents";
|
|
8
9
|
import type { AssistantCloudAPI } from "./AssistantCloudAPI";
|
|
@@ -84,6 +85,121 @@ describe("AssistantCloudEvents", () => {
|
|
|
84
85
|
expect(makeRequest).toHaveBeenCalledOnce();
|
|
85
86
|
});
|
|
86
87
|
|
|
88
|
+
it("retries a failed event batch with bounded backoff", async () => {
|
|
89
|
+
vi.useFakeTimers();
|
|
90
|
+
const { events, makeRequest } = createEvents();
|
|
91
|
+
makeRequest.mockRejectedValueOnce(new Error("temporary failure"));
|
|
92
|
+
|
|
93
|
+
for (let index = 0; index < 20; index++) events.track(event(index));
|
|
94
|
+
await vi.waitFor(() => expect(makeRequest).toHaveBeenCalledOnce());
|
|
95
|
+
|
|
96
|
+
await vi.advanceTimersByTimeAsync(249);
|
|
97
|
+
expect(makeRequest).toHaveBeenCalledOnce();
|
|
98
|
+
await vi.advanceTimersByTimeAsync(1);
|
|
99
|
+
|
|
100
|
+
expect(makeRequest).toHaveBeenCalledTimes(2);
|
|
101
|
+
expect(makeRequest.mock.calls[1]).toEqual(makeRequest.mock.calls[0]);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it("does not retry a cleared batch", async () => {
|
|
105
|
+
vi.useFakeTimers();
|
|
106
|
+
const { events, makeRequest } = createEvents();
|
|
107
|
+
makeRequest.mockRejectedValueOnce(new Error("temporary failure"));
|
|
108
|
+
|
|
109
|
+
for (let index = 0; index < 20; index++) events.track(event(index));
|
|
110
|
+
await vi.waitFor(() => expect(makeRequest).toHaveBeenCalledOnce());
|
|
111
|
+
|
|
112
|
+
clearPendingAssistantCloudEvents(events);
|
|
113
|
+
await vi.runAllTimersAsync();
|
|
114
|
+
|
|
115
|
+
expect(makeRequest).toHaveBeenCalledOnce();
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it("does not let an invalidated flush consume new events", async () => {
|
|
119
|
+
vi.useFakeTimers();
|
|
120
|
+
let resolveFirstRequest!: (value: { accepted: number }) => void;
|
|
121
|
+
const { events, makeRequest } = createEvents();
|
|
122
|
+
makeRequest
|
|
123
|
+
.mockImplementationOnce(
|
|
124
|
+
() =>
|
|
125
|
+
new Promise((resolve) => {
|
|
126
|
+
resolveFirstRequest = resolve;
|
|
127
|
+
}),
|
|
128
|
+
)
|
|
129
|
+
.mockRejectedValueOnce(new Error("temporary failure"));
|
|
130
|
+
|
|
131
|
+
for (let index = 0; index < 20; index++) events.track(event(index));
|
|
132
|
+
await vi.waitFor(() => expect(makeRequest).toHaveBeenCalledOnce());
|
|
133
|
+
|
|
134
|
+
window.dispatchEvent(new Event("pagehide"));
|
|
135
|
+
clearPendingAssistantCloudEvents(events);
|
|
136
|
+
events.track(event(20));
|
|
137
|
+
resolveFirstRequest({ accepted: 20 });
|
|
138
|
+
|
|
139
|
+
await vi.waitFor(() => expect(makeRequest).toHaveBeenCalledTimes(2));
|
|
140
|
+
await vi.advanceTimersByTimeAsync(250);
|
|
141
|
+
|
|
142
|
+
expect(makeRequest).toHaveBeenCalledTimes(3);
|
|
143
|
+
expect(makeRequest.mock.calls[1]?.[1].body.events).toEqual([event(20)]);
|
|
144
|
+
expect(makeRequest.mock.calls[2]).toEqual(makeRequest.mock.calls[1]);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it("stops retrying an event batch after three attempts", async () => {
|
|
148
|
+
vi.useFakeTimers();
|
|
149
|
+
const { events, makeRequest } = createEvents();
|
|
150
|
+
makeRequest.mockRejectedValue(new Error("persistent failure"));
|
|
151
|
+
|
|
152
|
+
for (let index = 0; index < 20; index++) events.track(event(index));
|
|
153
|
+
await vi.waitFor(() => expect(makeRequest).toHaveBeenCalledOnce());
|
|
154
|
+
|
|
155
|
+
await vi.advanceTimersByTimeAsync(250);
|
|
156
|
+
expect(makeRequest).toHaveBeenCalledTimes(2);
|
|
157
|
+
await vi.advanceTimersByTimeAsync(999);
|
|
158
|
+
expect(makeRequest).toHaveBeenCalledTimes(2);
|
|
159
|
+
await vi.advanceTimersByTimeAsync(1);
|
|
160
|
+
expect(makeRequest).toHaveBeenCalledTimes(3);
|
|
161
|
+
await vi.runAllTimersAsync();
|
|
162
|
+
|
|
163
|
+
expect(makeRequest).toHaveBeenCalledTimes(3);
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
it("keeps dispose delivery best effort", async () => {
|
|
167
|
+
vi.useFakeTimers();
|
|
168
|
+
const { events, makeRequest } = createEvents();
|
|
169
|
+
makeRequest.mockRejectedValue(new Error("offline"));
|
|
170
|
+
|
|
171
|
+
events.track(event(1));
|
|
172
|
+
events.dispose();
|
|
173
|
+
await vi.waitFor(() => expect(makeRequest).toHaveBeenCalledOnce());
|
|
174
|
+
await vi.runAllTimersAsync();
|
|
175
|
+
|
|
176
|
+
expect(makeRequest).toHaveBeenCalledOnce();
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it.each(["dispose", "pagehide"] as const)(
|
|
180
|
+
"interrupts retry backoff on %s and drains buffered events once",
|
|
181
|
+
async (trigger) => {
|
|
182
|
+
vi.useFakeTimers();
|
|
183
|
+
const { events, makeRequest } = createEvents();
|
|
184
|
+
makeRequest.mockRejectedValue(new Error("offline"));
|
|
185
|
+
|
|
186
|
+
for (let index = 0; index < 20; index++) events.track(event(index));
|
|
187
|
+
await vi.waitFor(() => expect(makeRequest).toHaveBeenCalledOnce());
|
|
188
|
+
events.track(event(20));
|
|
189
|
+
|
|
190
|
+
if (trigger === "dispose") {
|
|
191
|
+
events.dispose();
|
|
192
|
+
} else {
|
|
193
|
+
window.dispatchEvent(new Event("pagehide"));
|
|
194
|
+
}
|
|
195
|
+
await vi.waitFor(() => expect(makeRequest).toHaveBeenCalledTimes(2));
|
|
196
|
+
await vi.runAllTimersAsync();
|
|
197
|
+
|
|
198
|
+
expect(makeRequest).toHaveBeenCalledTimes(2);
|
|
199
|
+
expect(makeRequest.mock.calls[1]?.[1].body.events).toEqual([event(20)]);
|
|
200
|
+
},
|
|
201
|
+
);
|
|
202
|
+
|
|
87
203
|
it("flushes when the document becomes hidden", async () => {
|
|
88
204
|
const { events, makeRequest } = createEvents();
|
|
89
205
|
events.track(event(1));
|