assistant-cloud 0.1.43 → 0.2.1
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 +66 -0
- package/dist/AssistantCloud.d.ts +6 -1
- package/dist/AssistantCloud.d.ts.map +1 -1
- package/dist/AssistantCloud.js +13 -5
- package/dist/AssistantCloud.js.map +1 -1
- package/dist/AssistantCloudAPI.d.ts +28 -3
- package/dist/AssistantCloudAPI.d.ts.map +1 -1
- package/dist/AssistantCloudAPI.js +37 -10
- package/dist/AssistantCloudAPI.js.map +1 -1
- package/dist/AssistantCloudAuthStrategy.d.ts +4 -1
- package/dist/AssistantCloudAuthStrategy.d.ts.map +1 -1
- package/dist/AssistantCloudAuthStrategy.js +15 -3
- package/dist/AssistantCloudAuthStrategy.js.map +1 -1
- package/dist/AssistantCloudEvents.d.ts +32 -0
- package/dist/AssistantCloudEvents.d.ts.map +1 -0
- package/dist/AssistantCloudEvents.js +119 -0
- package/dist/AssistantCloudEvents.js.map +1 -0
- package/dist/AssistantCloudFiles.d.ts +12 -1
- package/dist/AssistantCloudFiles.d.ts.map +1 -1
- package/dist/AssistantCloudFiles.js +13 -1
- package/dist/AssistantCloudFiles.js.map +1 -1
- package/dist/AssistantCloudRuns.d.ts +12 -0
- package/dist/AssistantCloudRuns.d.ts.map +1 -1
- package/dist/AssistantCloudRuns.js +2 -1
- package/dist/AssistantCloudRuns.js.map +1 -1
- package/dist/AssistantCloudScores.d.ts +27 -0
- package/dist/AssistantCloudScores.d.ts.map +1 -0
- package/dist/AssistantCloudScores.js +30 -0
- package/dist/AssistantCloudScores.js.map +1 -0
- package/dist/AssistantCloudThreadMessages.d.ts +11 -1
- package/dist/AssistantCloudThreadMessages.d.ts.map +1 -1
- package/dist/AssistantCloudThreadMessages.js +12 -1
- package/dist/AssistantCloudThreadMessages.js.map +1 -1
- package/dist/AssistantCloudThreads.d.ts +8 -0
- package/dist/AssistantCloudThreads.d.ts.map +1 -1
- package/dist/AssistantCloudThreads.js +9 -1
- package/dist/AssistantCloudThreads.js.map +1 -1
- package/dist/CloudEngagementReporter.d.ts +74 -0
- package/dist/CloudEngagementReporter.d.ts.map +1 -0
- package/dist/CloudEngagementReporter.js +132 -0
- package/dist/CloudEngagementReporter.js.map +1 -0
- package/dist/CloudMessagePersistence.d.ts +12 -9
- package/dist/CloudMessagePersistence.d.ts.map +1 -1
- package/dist/CloudMessagePersistence.js +54 -20
- package/dist/CloudMessagePersistence.js.map +1 -1
- package/dist/CloudRunReporter.d.ts +20 -0
- package/dist/CloudRunReporter.d.ts.map +1 -0
- package/dist/CloudRunReporter.js +38 -0
- package/dist/CloudRunReporter.js.map +1 -0
- package/dist/FormattedCloudPersistence.d.ts +3 -8
- package/dist/FormattedCloudPersistence.d.ts.map +1 -1
- package/dist/FormattedCloudPersistence.js +3 -8
- package/dist/FormattedCloudPersistence.js.map +1 -1
- package/dist/ai-sdk/index.d.ts +33 -0
- package/dist/ai-sdk/index.d.ts.map +1 -0
- package/dist/ai-sdk/index.js +147 -0
- package/dist/ai-sdk/index.js.map +1 -0
- package/dist/cloudResponse.d.ts +4 -1
- package/dist/cloudResponse.d.ts.map +1 -1
- package/dist/cloudResponse.js +14 -1
- package/dist/cloudResponse.js.map +1 -1
- package/dist/generateThreadTitle.js +1 -0
- package/dist/generateThreadTitle.js.map +1 -1
- package/dist/index.d.ts +10 -4
- package/dist/index.js +7 -2
- package/dist/instrumentMcpSampling.d.ts.map +1 -1
- package/dist/instrumentMcpSampling.js +12 -2
- package/dist/instrumentMcpSampling.js.map +1 -1
- package/dist/runTelemetry.d.ts +86 -7
- package/dist/runTelemetry.d.ts.map +1 -1
- package/dist/runTelemetry.js +144 -1
- package/dist/runTelemetry.js.map +1 -1
- package/dist/telemetry/index.d.ts +32 -0
- package/dist/telemetry/index.d.ts.map +1 -0
- package/dist/telemetry/index.js +62 -0
- package/dist/telemetry/index.js.map +1 -0
- package/dist/version.d.ts +5 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +6 -0
- package/dist/version.js.map +1 -0
- package/package.json +43 -6
- package/src/AssistantCloud.ts +23 -8
- package/src/AssistantCloudAPI.ts +72 -10
- package/src/AssistantCloudAuthStrategy.ts +17 -2
- package/src/AssistantCloudEvents.test.ts +149 -0
- package/src/AssistantCloudEvents.ts +190 -0
- package/src/AssistantCloudFiles.test.ts +59 -0
- package/src/AssistantCloudFiles.ts +39 -0
- package/src/AssistantCloudRuns.ts +13 -1
- package/src/AssistantCloudScores.test.ts +113 -0
- package/src/AssistantCloudScores.ts +61 -0
- package/src/AssistantCloudThreadMessages.test.ts +51 -0
- package/src/AssistantCloudThreadMessages.ts +33 -0
- package/src/AssistantCloudThreads.test.ts +21 -0
- package/src/AssistantCloudThreads.ts +21 -0
- package/src/CloudEngagementReporter.ts +231 -0
- package/src/CloudMessagePersistence.ts +70 -27
- package/src/CloudRunReporter.ts +38 -0
- package/src/FormattedCloudPersistence.ts +3 -8
- package/src/ai-sdk/index.test.ts +258 -0
- package/src/ai-sdk/index.ts +222 -0
- package/src/cloudResponse.test.ts +34 -1
- package/src/cloudResponse.ts +30 -0
- package/src/generateThreadTitle.test.ts +32 -0
- package/src/generateThreadTitle.ts +1 -0
- package/src/index.ts +35 -2
- package/src/instrumentMcpSampling.test.ts +102 -0
- package/src/instrumentMcpSampling.ts +16 -2
- package/src/runTelemetry.test.ts +325 -0
- package/src/runTelemetry.ts +274 -6
- package/src/telemetry/index.test.ts +252 -0
- package/src/telemetry/index.ts +98 -0
- package/src/tests/AssistantCloud.test.ts +55 -1
- package/src/tests/AssistantCloudAPI.test.ts +95 -4
- package/src/tests/AssistantCloudAuthStrategy.test.ts +93 -2
- package/src/tests/AssistantCloudProjects.test.ts +1 -5
- package/src/tests/CloudEngagementReporter.test.ts +184 -0
- package/src/tests/CloudMessagePersistence.test.ts +350 -0
- package/src/tests/CloudRunReporter.test.ts +100 -0
- package/src/version.ts +4 -0
package/dist/runTelemetry.js
CHANGED
|
@@ -2,6 +2,54 @@
|
|
|
2
2
|
const MAX_TELEMETRY_TEXT_LENGTH = 5e4;
|
|
3
3
|
const BASE64_PATTERN = /^[A-Za-z0-9+/]{100,}={0,2}$/;
|
|
4
4
|
/**
|
|
5
|
+
* Maps a finish event to the report status and outcome. `fallbackStatus`
|
|
6
|
+
* applies when the event carries neither a finish reason nor a failure flag.
|
|
7
|
+
*/
|
|
8
|
+
function deriveRunOutcome(input, fallbackStatus = "completed") {
|
|
9
|
+
if (input.isError) return { status: "error" };
|
|
10
|
+
if (input.isAbort) return {
|
|
11
|
+
status: "incomplete",
|
|
12
|
+
outcome: "aborted"
|
|
13
|
+
};
|
|
14
|
+
if (input.isDisconnect) return {
|
|
15
|
+
status: "incomplete",
|
|
16
|
+
outcome: "disconnected"
|
|
17
|
+
};
|
|
18
|
+
switch (input.finishReason) {
|
|
19
|
+
case "length": return {
|
|
20
|
+
status: "incomplete",
|
|
21
|
+
outcome: "length"
|
|
22
|
+
};
|
|
23
|
+
case "content-filter":
|
|
24
|
+
case "content_filter": return {
|
|
25
|
+
status: "incomplete",
|
|
26
|
+
outcome: "content_filter"
|
|
27
|
+
};
|
|
28
|
+
case "cancelled": return {
|
|
29
|
+
status: "incomplete",
|
|
30
|
+
outcome: "aborted"
|
|
31
|
+
};
|
|
32
|
+
case "error": return { status: "error" };
|
|
33
|
+
default: return { status: input.finishReason === void 0 ? fallbackStatus : "completed" };
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
const MAX_RUN_ERROR_CODE_LENGTH = 64;
|
|
37
|
+
const MAX_RUN_ERROR_LENGTH = 2048;
|
|
38
|
+
/**
|
|
39
|
+
* Reads the message and code the runs endpoint stores for a failed run. The
|
|
40
|
+
* code is the error's `code` when it has one, else its class name.
|
|
41
|
+
*/
|
|
42
|
+
function describeRunError(error) {
|
|
43
|
+
if (error == null) return {};
|
|
44
|
+
const record = typeof error === "object" ? error : void 0;
|
|
45
|
+
const message = typeof error === "string" ? error : typeof record?.message === "string" ? record.message : void 0;
|
|
46
|
+
const code = typeof record?.code === "string" ? record.code : typeof record?.name === "string" && record.name !== "Error" ? record.name : void 0;
|
|
47
|
+
return {
|
|
48
|
+
...message ? { error: message.slice(0, MAX_RUN_ERROR_LENGTH) } : void 0,
|
|
49
|
+
...code ? { errorCode: code.slice(0, MAX_RUN_ERROR_CODE_LENGTH) } : void 0
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
5
53
|
* Clamps a string to the size the runs endpoint accepts for a single span
|
|
6
54
|
* field.
|
|
7
55
|
*/
|
|
@@ -58,6 +106,101 @@ function createRunTelemetryToolCall(init) {
|
|
|
58
106
|
return call;
|
|
59
107
|
}
|
|
60
108
|
/**
|
|
109
|
+
* Resolves the model ID a run reports, in the order an app can supply it: an
|
|
110
|
+
* explicit `modelId`, the `custom` bag, then the per-step `response.modelId`
|
|
111
|
+
* that a `messageMetadata` callback copies off the AI SDK's finish-step part.
|
|
112
|
+
* The AI SDK puts no model ID on a UI message part, so message metadata is the
|
|
113
|
+
* only channel one arrives on.
|
|
114
|
+
*/
|
|
115
|
+
function extractRunTelemetryModelId(metadata) {
|
|
116
|
+
if (!metadata) return void 0;
|
|
117
|
+
if (typeof metadata.modelId === "string") return metadata.modelId;
|
|
118
|
+
const custom = metadata.custom;
|
|
119
|
+
if (typeof custom?.modelId === "string") return custom.modelId;
|
|
120
|
+
const steps = metadata.steps;
|
|
121
|
+
if (!Array.isArray(steps)) return void 0;
|
|
122
|
+
for (const step of steps) {
|
|
123
|
+
if (!step || typeof step !== "object") continue;
|
|
124
|
+
const response = step.response;
|
|
125
|
+
if (!response || typeof response !== "object") continue;
|
|
126
|
+
const modelId = response.modelId;
|
|
127
|
+
if (typeof modelId === "string") return modelId;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
function assignUsage(report, usage) {
|
|
131
|
+
if (!usage) return;
|
|
132
|
+
const normalized = normalizeRunTelemetryUsage(usage);
|
|
133
|
+
if (!normalized) return;
|
|
134
|
+
if (normalized.inputTokens !== void 0) report.input_tokens = normalized.inputTokens;
|
|
135
|
+
if (normalized.outputTokens !== void 0) report.output_tokens = normalized.outputTokens;
|
|
136
|
+
if (normalized.reasoningTokens !== void 0) report.reasoning_tokens = normalized.reasoningTokens;
|
|
137
|
+
if (normalized.cachedInputTokens !== void 0) report.cached_input_tokens = normalized.cachedInputTokens;
|
|
138
|
+
}
|
|
139
|
+
function createRunReportStep(init) {
|
|
140
|
+
const step = {};
|
|
141
|
+
const usage = init.usage ? normalizeRunTelemetryUsage(init.usage) : void 0;
|
|
142
|
+
if (usage?.inputTokens !== void 0) step.input_tokens = usage.inputTokens;
|
|
143
|
+
if (usage?.outputTokens !== void 0) step.output_tokens = usage.outputTokens;
|
|
144
|
+
if (usage?.reasoningTokens !== void 0) step.reasoning_tokens = usage.reasoningTokens;
|
|
145
|
+
if (usage?.cachedInputTokens !== void 0) step.cached_input_tokens = usage.cachedInputTokens;
|
|
146
|
+
if (init.toolCalls !== void 0) step.tool_calls = init.toolCalls;
|
|
147
|
+
if (init.startMs !== void 0) step.start_ms = init.startMs;
|
|
148
|
+
if (init.endMs !== void 0) step.end_ms = init.endMs;
|
|
149
|
+
if (init.finishReason !== void 0) step.finish_reason = init.finishReason.slice(0, 32);
|
|
150
|
+
return step;
|
|
151
|
+
}
|
|
152
|
+
function normalizeRunReportTags(tags) {
|
|
153
|
+
if (!tags) return void 0;
|
|
154
|
+
const result = [];
|
|
155
|
+
const seen = /* @__PURE__ */ new Set();
|
|
156
|
+
for (const tag of tags) {
|
|
157
|
+
const normalized = tag.trim().slice(0, 64).trim();
|
|
158
|
+
if (!normalized || seen.has(normalized)) continue;
|
|
159
|
+
seen.add(normalized);
|
|
160
|
+
result.push(normalized);
|
|
161
|
+
if (result.length === 20) break;
|
|
162
|
+
}
|
|
163
|
+
return result.length > 0 ? result : void 0;
|
|
164
|
+
}
|
|
165
|
+
function normalizeRunReportMilliseconds(value) {
|
|
166
|
+
if (value === void 0 || !Number.isFinite(value)) return void 0;
|
|
167
|
+
return Math.max(0, Math.round(value));
|
|
168
|
+
}
|
|
169
|
+
function createRunReport(init) {
|
|
170
|
+
const report = {
|
|
171
|
+
thread_id: init.threadId,
|
|
172
|
+
status: init.status
|
|
173
|
+
};
|
|
174
|
+
const traceId = init.traceId?.toLowerCase();
|
|
175
|
+
if (traceId && /^[0-9a-f]{32}$/.test(traceId)) report.trace_id = traceId;
|
|
176
|
+
if (init.outcome !== void 0) report.outcome_type = init.outcome;
|
|
177
|
+
if (init.errorCode !== void 0) report.error_code = init.errorCode;
|
|
178
|
+
if (init.error !== void 0) report.error = init.error;
|
|
179
|
+
if (init.messageId !== void 0) report.message_id = init.messageId;
|
|
180
|
+
if (init.modelId !== void 0) report.model_id = init.modelId;
|
|
181
|
+
if (init.provider !== void 0) {
|
|
182
|
+
report.provider = init.provider;
|
|
183
|
+
report.provider_type = init.provider;
|
|
184
|
+
}
|
|
185
|
+
assignUsage(report, init.usage);
|
|
186
|
+
if (init.steps !== void 0) {
|
|
187
|
+
report.steps = init.steps.map(createRunReportStep);
|
|
188
|
+
report.total_steps = init.steps.length;
|
|
189
|
+
} else if (init.totalSteps !== void 0) report.total_steps = init.totalSteps;
|
|
190
|
+
if (init.toolCalls !== void 0) report.tool_calls = init.toolCalls;
|
|
191
|
+
const durationMs = normalizeRunReportMilliseconds(init.durationMs);
|
|
192
|
+
if (durationMs !== void 0) report.duration_ms = durationMs;
|
|
193
|
+
const firstTokenMs = normalizeRunReportMilliseconds(init.firstTokenMs);
|
|
194
|
+
if (firstTokenMs !== void 0) report.first_token_ms = firstTokenMs;
|
|
195
|
+
if (init.outputText !== void 0) report.output_text = truncateRunTelemetryText(init.outputText);
|
|
196
|
+
if (init.metadata !== void 0) report.metadata = init.metadata;
|
|
197
|
+
if (init.telemetry?.environment !== void 0) report.environment = init.telemetry.environment;
|
|
198
|
+
if (init.telemetry?.release !== void 0) report.release = init.telemetry.release;
|
|
199
|
+
const tags = normalizeRunReportTags(init.telemetry?.tags);
|
|
200
|
+
if (tags !== void 0) report.tags = tags;
|
|
201
|
+
return report;
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
61
204
|
* Resolves the token counts a provider reports under any of the names the AI
|
|
62
205
|
* SDK has used: the current top-level ones, the legacy prompt/completion pair,
|
|
63
206
|
* and the v7 token detail objects. Returns undefined when no count is present,
|
|
@@ -77,6 +220,6 @@ function normalizeRunTelemetryUsage(usage) {
|
|
|
77
220
|
};
|
|
78
221
|
}
|
|
79
222
|
//#endregion
|
|
80
|
-
export { createRunTelemetryToolCall, normalizeRunTelemetryUsage, truncateRunTelemetryText };
|
|
223
|
+
export { createRunReport, createRunTelemetryToolCall, deriveRunOutcome, describeRunError, extractRunTelemetryModelId, normalizeRunTelemetryUsage, truncateRunTelemetryText };
|
|
81
224
|
|
|
82
225
|
//# sourceMappingURL=runTelemetry.js.map
|
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\";\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\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\nexport type RunTelemetryUsage = {\n inputTokens?: number;\n outputTokens?: number;\n reasoningTokens?: number;\n cachedInputTokens?: number;\n};\n\nexport type RunTelemetryUsageInit = RunTelemetryUsage & {\n promptTokens?: number;\n completionTokens?: number;\n inputTokenDetails?: { cacheReadTokens?: number };\n outputTokenDetails?: { reasoningTokens?: number };\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":";AAEA,MAAM,4BAA4B;AAElC,MAAM,iBAAiB;;;;;AAiBvB,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,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,GAAG;GACzB,MAAM,aAAa,OAAO,KAAK,SAAS;IACtC,IAAI,QAAQ,OAAO,SAAS,YAAY,KAAK,MAExC;UAAA,KAAK,SAAS,WAAW,KAAK,SAAS,YACxC,OAAO,KAAK,SAAS,YACrB,eAAe,KAAK,KAAK,KAAK,MAAM,GAAG,GAAG,CAAC,GAC3C;MACA,MAAM,UAAW,KAAK,KAAK,SAAS,IAAK,IAAI,KAAA,CAAM,QAAQ,CAAC;MAC5D,OAAO;OAAE,GAAG;OAAM,MAAM,IAAI,KAAK,KAAK,IAAI,OAAO;MAAK;KACxD;;IAEF,OAAO;GACT,CAAC;GACD,OAAO,yBAAyB,KAAK,UAAU,UAAU,CAAC;EAC5D;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;;;;;;;AAsBA,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
|
+
{"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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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { ReadableSpan, SpanExporter, SpanProcessor } from "@opentelemetry/sdk-trace-base";
|
|
2
|
+
//#region src/telemetry/index.d.ts
|
|
3
|
+
type AssistantCloudTraceExportOptions = {
|
|
4
|
+
apiKey: string;
|
|
5
|
+
baseUrl?: string;
|
|
6
|
+
headers?: Record<string, string>;
|
|
7
|
+
};
|
|
8
|
+
type AssistantCloudSpanProcessorOptions = {
|
|
9
|
+
filter?: (span: ReadableSpan) => boolean;
|
|
10
|
+
};
|
|
11
|
+
declare function assistantCloudTraceExportOptions({ apiKey, baseUrl, headers }: AssistantCloudTraceExportOptions): {
|
|
12
|
+
url: string;
|
|
13
|
+
headers: Record<string, string>;
|
|
14
|
+
};
|
|
15
|
+
declare function createAssistantCloudTraceExporter(options: AssistantCloudTraceExportOptions): SpanExporter;
|
|
16
|
+
declare function isAssistantCloudSpan(span: ReadableSpan): boolean;
|
|
17
|
+
declare function createAssistantCloudSpanProcessor(exporter: SpanExporter, options?: AssistantCloudSpanProcessorOptions): SpanProcessor;
|
|
18
|
+
declare function assistantCloudTraceMetadata(): {
|
|
19
|
+
traceId?: string;
|
|
20
|
+
};
|
|
21
|
+
declare function withAssistantCloudTraceMetadata<Part extends {
|
|
22
|
+
type: string;
|
|
23
|
+
} = {
|
|
24
|
+
type: string;
|
|
25
|
+
}>(messageMetadata?: (options: {
|
|
26
|
+
part: Part;
|
|
27
|
+
}) => Record<string, unknown> | undefined): (options: {
|
|
28
|
+
part: Part;
|
|
29
|
+
}) => Record<string, unknown> | undefined;
|
|
30
|
+
//#endregion
|
|
31
|
+
export { AssistantCloudSpanProcessorOptions, AssistantCloudTraceExportOptions, assistantCloudTraceExportOptions, assistantCloudTraceMetadata, createAssistantCloudSpanProcessor, createAssistantCloudTraceExporter, isAssistantCloudSpan, withAssistantCloudTraceMetadata };
|
|
32
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../../src/telemetry/index.ts"],"mappings":";;KAYY;EACV;EACA;EACA,UAAU;;KAGA;EACV,UAAU,MAAM;;iBAGF,mCACd,QACA,SACA,WACC;EACD;EACA,SAAS;;iBAmBK,kCACd,SAAS,mCACR;iBAIa,qBAAqB,MAAM;iBAO3B,kCACd,UAAU,cACV,UAAS,qCACR;iBAca;EAAiC;;iBAOjC,gCACd;EAAe;;EAAmB;GAElC,mBAAmB;EACjB,MAAM;MACF,uCACJ;EAAW,MAAM;MAAW"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { trace } from "@opentelemetry/api";
|
|
2
|
+
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
|
|
3
|
+
import { BatchSpanProcessor } from "@opentelemetry/sdk-trace-base";
|
|
4
|
+
//#region src/telemetry/index.ts
|
|
5
|
+
const DEFAULT_BACKEND_BASE_URL = "https://backend.assistant-api.com";
|
|
6
|
+
const AI_SPAN_PREFIXES = [
|
|
7
|
+
"gen_ai.",
|
|
8
|
+
"ai.",
|
|
9
|
+
"llm."
|
|
10
|
+
];
|
|
11
|
+
function assistantCloudTraceExportOptions({ apiKey, baseUrl = DEFAULT_BACKEND_BASE_URL, headers }) {
|
|
12
|
+
if (!apiKey) throw new Error("An Assistant Cloud API key is required");
|
|
13
|
+
return {
|
|
14
|
+
url: `${trimTrailingSlashes(baseUrl)}/v1/traces`,
|
|
15
|
+
headers: {
|
|
16
|
+
Authorization: `Bearer ${apiKey}`,
|
|
17
|
+
...headers
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
function trimTrailingSlashes(url) {
|
|
22
|
+
let end = url.length;
|
|
23
|
+
while (end > 0 && url[end - 1] === "/") end--;
|
|
24
|
+
return url.slice(0, end);
|
|
25
|
+
}
|
|
26
|
+
function createAssistantCloudTraceExporter(options) {
|
|
27
|
+
return new OTLPTraceExporter(assistantCloudTraceExportOptions(options));
|
|
28
|
+
}
|
|
29
|
+
function isAssistantCloudSpan(span) {
|
|
30
|
+
return typeof span.attributes["gen_ai.operation.name"] === "string" || AI_SPAN_PREFIXES.some((prefix) => span.name.startsWith(prefix));
|
|
31
|
+
}
|
|
32
|
+
function createAssistantCloudSpanProcessor(exporter, options = {}) {
|
|
33
|
+
const processor = new BatchSpanProcessor(exporter);
|
|
34
|
+
const filter = options.filter ?? isAssistantCloudSpan;
|
|
35
|
+
return {
|
|
36
|
+
onStart: (span, parentContext) => processor.onStart(span, parentContext),
|
|
37
|
+
onEnd: (span) => {
|
|
38
|
+
if (filter(span)) processor.onEnd(span);
|
|
39
|
+
},
|
|
40
|
+
forceFlush: () => processor.forceFlush(),
|
|
41
|
+
shutdown: () => processor.shutdown()
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
function assistantCloudTraceMetadata() {
|
|
45
|
+
const spanContext = trace.getActiveSpan()?.spanContext();
|
|
46
|
+
return spanContext && trace.isSpanContextValid(spanContext) ? { traceId: spanContext.traceId } : {};
|
|
47
|
+
}
|
|
48
|
+
function withAssistantCloudTraceMetadata(messageMetadata) {
|
|
49
|
+
return (options) => {
|
|
50
|
+
const metadata = messageMetadata?.(options);
|
|
51
|
+
if (options.part.type !== "start") return metadata;
|
|
52
|
+
const trace = assistantCloudTraceMetadata();
|
|
53
|
+
return trace.traceId === void 0 ? metadata : {
|
|
54
|
+
...metadata,
|
|
55
|
+
...trace
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
//#endregion
|
|
60
|
+
export { assistantCloudTraceExportOptions, assistantCloudTraceMetadata, createAssistantCloudSpanProcessor, createAssistantCloudTraceExporter, isAssistantCloudSpan, withAssistantCloudTraceMetadata };
|
|
61
|
+
|
|
62
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/telemetry/index.ts"],"sourcesContent":["import { trace } from \"@opentelemetry/api\";\nimport { OTLPTraceExporter } from \"@opentelemetry/exporter-trace-otlp-http\";\nimport {\n BatchSpanProcessor,\n type ReadableSpan,\n type SpanExporter,\n type SpanProcessor,\n} from \"@opentelemetry/sdk-trace-base\";\n\nconst DEFAULT_BACKEND_BASE_URL = \"https://backend.assistant-api.com\";\nconst AI_SPAN_PREFIXES = [\"gen_ai.\", \"ai.\", \"llm.\"];\n\nexport type AssistantCloudTraceExportOptions = {\n apiKey: string;\n baseUrl?: string;\n headers?: Record<string, string>;\n};\n\nexport type AssistantCloudSpanProcessorOptions = {\n filter?: (span: ReadableSpan) => boolean;\n};\n\nexport function assistantCloudTraceExportOptions({\n apiKey,\n baseUrl = DEFAULT_BACKEND_BASE_URL,\n headers,\n}: AssistantCloudTraceExportOptions): {\n url: string;\n headers: Record<string, string>;\n} {\n if (!apiKey) throw new Error(\"An Assistant Cloud API key is required\");\n\n return {\n url: `${trimTrailingSlashes(baseUrl)}/v1/traces`,\n headers: {\n Authorization: `Bearer ${apiKey}`,\n ...headers,\n },\n };\n}\n\nfunction trimTrailingSlashes(url: string): string {\n let end = url.length;\n while (end > 0 && url[end - 1] === \"/\") end--;\n return url.slice(0, end);\n}\n\nexport function createAssistantCloudTraceExporter(\n options: AssistantCloudTraceExportOptions,\n): SpanExporter {\n return new OTLPTraceExporter(assistantCloudTraceExportOptions(options));\n}\n\nexport function isAssistantCloudSpan(span: ReadableSpan): boolean {\n return (\n typeof span.attributes[\"gen_ai.operation.name\"] === \"string\" ||\n AI_SPAN_PREFIXES.some((prefix) => span.name.startsWith(prefix))\n );\n}\n\nexport function createAssistantCloudSpanProcessor(\n exporter: SpanExporter,\n options: AssistantCloudSpanProcessorOptions = {},\n): SpanProcessor {\n const processor = new BatchSpanProcessor(exporter);\n const filter = options.filter ?? isAssistantCloudSpan;\n\n return {\n onStart: (span, parentContext) => processor.onStart(span, parentContext),\n onEnd: (span) => {\n if (filter(span)) processor.onEnd(span);\n },\n forceFlush: () => processor.forceFlush(),\n shutdown: () => processor.shutdown(),\n };\n}\n\nexport function assistantCloudTraceMetadata(): { traceId?: string } {\n const spanContext = trace.getActiveSpan()?.spanContext();\n return spanContext && trace.isSpanContextValid(spanContext)\n ? { traceId: spanContext.traceId }\n : {};\n}\n\nexport function withAssistantCloudTraceMetadata<\n Part extends { type: string } = { type: string },\n>(\n messageMetadata?: (options: {\n part: Part;\n }) => Record<string, unknown> | undefined,\n): (options: { part: Part }) => Record<string, unknown> | undefined {\n return (options) => {\n const metadata = messageMetadata?.(options);\n if (options.part.type !== \"start\") return metadata;\n const trace = assistantCloudTraceMetadata();\n return trace.traceId === undefined ? metadata : { ...metadata, ...trace };\n };\n}\n"],"mappings":";;;;AASA,MAAM,2BAA2B;AACjC,MAAM,mBAAmB;CAAC;CAAW;CAAO;AAAM;AAYlD,SAAgB,iCAAiC,EAC/C,QACA,UAAU,0BACV,WAIA;CACA,IAAI,CAAC,QAAQ,MAAM,IAAI,MAAM,wCAAwC;CAErE,OAAO;EACL,KAAK,GAAG,oBAAoB,OAAO,EAAE;EACrC,SAAS;GACP,eAAe,UAAU;GACzB,GAAG;EACL;CACF;AACF;AAEA,SAAS,oBAAoB,KAAqB;CAChD,IAAI,MAAM,IAAI;CACd,OAAO,MAAM,KAAK,IAAI,MAAM,OAAO,KAAK;CACxC,OAAO,IAAI,MAAM,GAAG,GAAG;AACzB;AAEA,SAAgB,kCACd,SACc;CACd,OAAO,IAAI,kBAAkB,iCAAiC,OAAO,CAAC;AACxE;AAEA,SAAgB,qBAAqB,MAA6B;CAChE,OACE,OAAO,KAAK,WAAW,6BAA6B,YACpD,iBAAiB,MAAM,WAAW,KAAK,KAAK,WAAW,MAAM,CAAC;AAElE;AAEA,SAAgB,kCACd,UACA,UAA8C,CAAC,GAChC;CACf,MAAM,YAAY,IAAI,mBAAmB,QAAQ;CACjD,MAAM,SAAS,QAAQ,UAAU;CAEjC,OAAO;EACL,UAAU,MAAM,kBAAkB,UAAU,QAAQ,MAAM,aAAa;EACvE,QAAQ,SAAS;GACf,IAAI,OAAO,IAAI,GAAG,UAAU,MAAM,IAAI;EACxC;EACA,kBAAkB,UAAU,WAAW;EACvC,gBAAgB,UAAU,SAAS;CACrC;AACF;AAEA,SAAgB,8BAAoD;CAClE,MAAM,cAAc,MAAM,cAAc,CAAC,EAAE,YAAY;CACvD,OAAO,eAAe,MAAM,mBAAmB,WAAW,IACtD,EAAE,SAAS,YAAY,QAAQ,IAC/B,CAAC;AACP;AAEA,SAAgB,gCAGd,iBAGkE;CAClE,QAAQ,YAAY;EAClB,MAAM,WAAW,kBAAkB,OAAO;EAC1C,IAAI,QAAQ,KAAK,SAAS,SAAS,OAAO;EAC1C,MAAM,QAAQ,4BAA4B;EAC1C,OAAO,MAAM,YAAY,KAAA,IAAY,WAAW;GAAE,GAAG;GAAU,GAAG;EAAM;CAC1E;AACF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version.d.ts","names":[],"sources":["../src/version.ts"],"mappings":";cAAa"}
|
package/dist/version.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version.js","names":[],"sources":["../src/version.ts"],"sourcesContent":["export const ASSISTANT_CLOUD_VERSION: string =\n typeof __AUI_PACKAGE_VERSION__ === \"string\"\n ? __AUI_PACKAGE_VERSION__\n : \"0.0.0\";\n"],"mappings":";AAAA,MAAa,0BAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "assistant-cloud",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Cloud integration for assistant-ui",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"assistant",
|
|
@@ -16,6 +16,14 @@
|
|
|
16
16
|
".": {
|
|
17
17
|
"types": "./dist/index.d.ts",
|
|
18
18
|
"default": "./dist/index.js"
|
|
19
|
+
},
|
|
20
|
+
"./telemetry": {
|
|
21
|
+
"types": "./dist/telemetry/index.d.ts",
|
|
22
|
+
"default": "./dist/telemetry/index.js"
|
|
23
|
+
},
|
|
24
|
+
"./ai-sdk": {
|
|
25
|
+
"types": "./dist/ai-sdk/index.d.ts",
|
|
26
|
+
"default": "./dist/ai-sdk/index.js"
|
|
19
27
|
}
|
|
20
28
|
},
|
|
21
29
|
"main": "./dist/index.js",
|
|
@@ -27,12 +35,38 @@
|
|
|
27
35
|
],
|
|
28
36
|
"sideEffects": false,
|
|
29
37
|
"dependencies": {
|
|
30
|
-
"assistant-stream": "^0.3.
|
|
38
|
+
"assistant-stream": "^0.3.43"
|
|
39
|
+
},
|
|
40
|
+
"peerDependencies": {
|
|
41
|
+
"@opentelemetry/api": "^1.9.0",
|
|
42
|
+
"@opentelemetry/exporter-trace-otlp-http": ">=0.200.0",
|
|
43
|
+
"@opentelemetry/sdk-trace-base": "^2.1.0",
|
|
44
|
+
"ai": "^6.0.0 || ^7.0.0"
|
|
45
|
+
},
|
|
46
|
+
"peerDependenciesMeta": {
|
|
47
|
+
"@opentelemetry/api": {
|
|
48
|
+
"optional": true
|
|
49
|
+
},
|
|
50
|
+
"@opentelemetry/exporter-trace-otlp-http": {
|
|
51
|
+
"optional": true
|
|
52
|
+
},
|
|
53
|
+
"@opentelemetry/sdk-trace-base": {
|
|
54
|
+
"optional": true
|
|
55
|
+
},
|
|
56
|
+
"ai": {
|
|
57
|
+
"optional": true
|
|
58
|
+
}
|
|
31
59
|
},
|
|
32
60
|
"devDependencies": {
|
|
33
|
-
"@assistant-ui/x-buildutils": "0.0.
|
|
34
|
-
"@
|
|
35
|
-
"
|
|
61
|
+
"@assistant-ui/x-buildutils": "0.0.27",
|
|
62
|
+
"@opentelemetry/api": "^1.9.0",
|
|
63
|
+
"@opentelemetry/exporter-trace-otlp-http": "^0.205.0",
|
|
64
|
+
"@opentelemetry/sdk-trace-base": "^2.1.0",
|
|
65
|
+
"@types/node": "^26.4.1",
|
|
66
|
+
"ai": "^7.0.93",
|
|
67
|
+
"ai-v6": "npm:ai@^6.0.277",
|
|
68
|
+
"jsdom": "^30.0.1",
|
|
69
|
+
"vitest": "^5.0.0"
|
|
36
70
|
},
|
|
37
71
|
"publishConfig": {
|
|
38
72
|
"access": "public",
|
|
@@ -49,7 +83,10 @@
|
|
|
49
83
|
},
|
|
50
84
|
"scripts": {
|
|
51
85
|
"build": "aui-build",
|
|
52
|
-
"test": "
|
|
86
|
+
"test": "pnpm run test:v7 && pnpm run test:peer-v6 && pnpm run test:types:peer-v6",
|
|
87
|
+
"test:v7": "vitest run",
|
|
88
|
+
"test:peer-v6": "vitest run --config vitest.peer-v6.config.ts",
|
|
89
|
+
"test:types:peer-v6": "tsc --noEmit --project tsconfig.peer-v6.json",
|
|
53
90
|
"test:watch": "vitest"
|
|
54
91
|
}
|
|
55
92
|
}
|
package/src/AssistantCloud.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
AssistantCloudAPI,
|
|
3
3
|
type AssistantCloudConfig,
|
|
4
|
+
type SdkIdentity,
|
|
4
5
|
type AssistantCloudTelemetryConfig,
|
|
5
6
|
} from "./AssistantCloudAPI";
|
|
6
7
|
import { AssistantCloudAuthTokens } from "./AssistantCloudAuthTokens";
|
|
@@ -8,6 +9,8 @@ import { AssistantCloudProjects } from "./AssistantCloudProjects";
|
|
|
8
9
|
import { AssistantCloudRuns } from "./AssistantCloudRuns";
|
|
9
10
|
import { AssistantCloudThreads } from "./AssistantCloudThreads";
|
|
10
11
|
import { AssistantCloudFiles } from "./AssistantCloudFiles";
|
|
12
|
+
import { AssistantCloudEvents } from "./AssistantCloudEvents";
|
|
13
|
+
import { AssistantCloudScores } from "./AssistantCloudScores";
|
|
11
14
|
|
|
12
15
|
export class AssistantCloud {
|
|
13
16
|
public readonly threads;
|
|
@@ -15,10 +18,25 @@ export class AssistantCloud {
|
|
|
15
18
|
public readonly auth;
|
|
16
19
|
public readonly runs;
|
|
17
20
|
public readonly files;
|
|
21
|
+
public readonly events;
|
|
22
|
+
public readonly scores;
|
|
18
23
|
public readonly telemetry: AssistantCloudTelemetryConfig;
|
|
24
|
+
public readonly registerSdk: (sdk: SdkIdentity) => void;
|
|
19
25
|
|
|
20
26
|
constructor(config: AssistantCloudConfig) {
|
|
21
27
|
const api = new AssistantCloudAPI(config);
|
|
28
|
+
this.registerSdk = api.registerSdk;
|
|
29
|
+
const t = config.telemetry;
|
|
30
|
+
this.telemetry =
|
|
31
|
+
t === false
|
|
32
|
+
? { enabled: false }
|
|
33
|
+
: t === true || t === undefined
|
|
34
|
+
? { enabled: true }
|
|
35
|
+
: {
|
|
36
|
+
...t,
|
|
37
|
+
enabled: t.enabled !== false,
|
|
38
|
+
};
|
|
39
|
+
|
|
22
40
|
this.threads = new AssistantCloudThreads(api);
|
|
23
41
|
this.projects = new AssistantCloudProjects(api);
|
|
24
42
|
this.auth = {
|
|
@@ -26,13 +44,10 @@ export class AssistantCloud {
|
|
|
26
44
|
};
|
|
27
45
|
this.runs = new AssistantCloudRuns(api);
|
|
28
46
|
this.files = new AssistantCloudFiles(api);
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
: t === true || t === undefined
|
|
35
|
-
? { enabled: true }
|
|
36
|
-
: { ...t, enabled: t.enabled !== false };
|
|
47
|
+
this.events = new AssistantCloudEvents(
|
|
48
|
+
api,
|
|
49
|
+
() => this.telemetry.enabled !== false && this.telemetry.events !== false,
|
|
50
|
+
);
|
|
51
|
+
this.scores = new AssistantCloudScores(api);
|
|
37
52
|
}
|
|
38
53
|
}
|
package/src/AssistantCloudAPI.ts
CHANGED
|
@@ -3,11 +3,31 @@ import {
|
|
|
3
3
|
AssistantCloudJWTAuthStrategy,
|
|
4
4
|
AssistantCloudAPIKeyAuthStrategy,
|
|
5
5
|
AssistantCloudAnonymousAuthStrategy,
|
|
6
|
+
normalizeBaseUrl,
|
|
6
7
|
} from "./AssistantCloudAuthStrategy";
|
|
7
8
|
import type { AssistantCloudRunReport } from "./AssistantCloudRuns";
|
|
9
|
+
import { ASSISTANT_CLOUD_VERSION } from "./version";
|
|
10
|
+
|
|
11
|
+
export type SdkIdentity = {
|
|
12
|
+
name: string;
|
|
13
|
+
version: string;
|
|
14
|
+
};
|
|
8
15
|
|
|
9
16
|
export type AssistantCloudTelemetryConfig = {
|
|
17
|
+
/**
|
|
18
|
+
* Enables Assistant Cloud telemetry. Defaults to `true`. Set to `false` to
|
|
19
|
+
* disable both run reports and engagement events.
|
|
20
|
+
*/
|
|
10
21
|
enabled?: boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Enables Assistant Cloud engagement events. Defaults to `true` when
|
|
24
|
+
* telemetry is enabled. Set to `false` to keep run reports while disabling
|
|
25
|
+
* engagement events.
|
|
26
|
+
*/
|
|
27
|
+
events?: boolean;
|
|
28
|
+
release?: string;
|
|
29
|
+
environment?: string;
|
|
30
|
+
tags?: string[];
|
|
11
31
|
/**
|
|
12
32
|
* Called before each telemetry report is sent.
|
|
13
33
|
* Return a modified report to enrich it (e.g. add `model_id`),
|
|
@@ -39,7 +59,10 @@ export type AssistantCloudConfig = (
|
|
|
39
59
|
*
|
|
40
60
|
* When enabled, the SDK automatically reports run metadata (status, step
|
|
41
61
|
* count, tool calls, and token usage) to Assistant Cloud after each
|
|
42
|
-
* assistant message is saved.
|
|
62
|
+
* assistant message is saved. Reports can also include assistant output,
|
|
63
|
+
* tool arguments and results, errors, and metadata, which may contain
|
|
64
|
+
* sensitive content. Use `beforeReport` to redact or drop reports, or
|
|
65
|
+
* `telemetry: false` to disable reporting.
|
|
43
66
|
*
|
|
44
67
|
* - `true` / `undefined` — enabled with defaults
|
|
45
68
|
* - `false` — disabled
|
|
@@ -50,10 +73,19 @@ export type AssistantCloudConfig = (
|
|
|
50
73
|
|
|
51
74
|
export class CloudAPIError extends Error {
|
|
52
75
|
public readonly status: number;
|
|
53
|
-
|
|
54
|
-
|
|
76
|
+
public readonly code?: string;
|
|
77
|
+
public readonly details?: Record<string, unknown>;
|
|
78
|
+
|
|
79
|
+
constructor(
|
|
80
|
+
message: string,
|
|
81
|
+
status: number,
|
|
82
|
+
code?: string,
|
|
83
|
+
details?: Record<string, unknown>,
|
|
84
|
+
) {
|
|
55
85
|
super(message);
|
|
56
86
|
this.status = status;
|
|
87
|
+
if (code !== undefined) this.code = code;
|
|
88
|
+
if (details !== undefined) this.details = details;
|
|
57
89
|
this.name = "CloudAPIError";
|
|
58
90
|
}
|
|
59
91
|
}
|
|
@@ -63,18 +95,34 @@ type MakeRequestOptions = {
|
|
|
63
95
|
headers?: Record<string, string> | undefined;
|
|
64
96
|
query?: Record<string, string | number | boolean> | undefined;
|
|
65
97
|
body?: object | undefined;
|
|
98
|
+
keepalive?: boolean | undefined;
|
|
66
99
|
};
|
|
67
100
|
|
|
68
|
-
const
|
|
69
|
-
if (!baseUrl || !baseUrl.endsWith("/")) return baseUrl;
|
|
70
|
-
return baseUrl.slice(0, -1);
|
|
71
|
-
};
|
|
101
|
+
const HEADER_TOKEN = /^[\x21-\x7e]+$/;
|
|
72
102
|
|
|
73
103
|
export class AssistantCloudAPI {
|
|
74
104
|
public _auth: AssistantCloudAuthStrategy;
|
|
75
105
|
public _baseUrl;
|
|
106
|
+
public readonly registerSdk: (sdk: SdkIdentity) => void;
|
|
107
|
+
public readonly sdkHeader: () => string;
|
|
76
108
|
|
|
77
109
|
constructor(config: AssistantCloudConfig) {
|
|
110
|
+
const sdks = new Map<string, SdkIdentity>();
|
|
111
|
+
this.registerSdk = (sdk) => {
|
|
112
|
+
const name = sdk.name.trim();
|
|
113
|
+
const version = sdk.version.trim();
|
|
114
|
+
if (!HEADER_TOKEN.test(name) || !HEADER_TOKEN.test(version)) return;
|
|
115
|
+
sdks.set(`${name}/${version}`, { name, version });
|
|
116
|
+
};
|
|
117
|
+
this.sdkHeader = () =>
|
|
118
|
+
[
|
|
119
|
+
`assistant-cloud/${ASSISTANT_CLOUD_VERSION}`,
|
|
120
|
+
...Array.from(
|
|
121
|
+
sdks.values(),
|
|
122
|
+
({ name, version }) => `${name}/${version}`,
|
|
123
|
+
),
|
|
124
|
+
].join(" ");
|
|
125
|
+
|
|
78
126
|
if ("authToken" in config) {
|
|
79
127
|
this._baseUrl = normalizeBaseUrl(config.baseUrl);
|
|
80
128
|
this._auth = new AssistantCloudJWTAuthStrategy(config.authToken);
|
|
@@ -112,6 +160,7 @@ export class AssistantCloudAPI {
|
|
|
112
160
|
...authHeaders,
|
|
113
161
|
...options.headers,
|
|
114
162
|
"Content-Type": "application/json",
|
|
163
|
+
"Aui-Sdk": this.sdkHeader(),
|
|
115
164
|
};
|
|
116
165
|
|
|
117
166
|
const queryParams = new URLSearchParams();
|
|
@@ -133,6 +182,7 @@ export class AssistantCloudAPI {
|
|
|
133
182
|
method: options.method ?? "GET",
|
|
134
183
|
headers,
|
|
135
184
|
body: options.body ? JSON.stringify(options.body) : null,
|
|
185
|
+
...(options.keepalive ? { keepalive: true } : {}),
|
|
136
186
|
});
|
|
137
187
|
|
|
138
188
|
this._auth.readAuthHeaders(response.headers);
|
|
@@ -140,15 +190,27 @@ export class AssistantCloudAPI {
|
|
|
140
190
|
if (!response.ok) {
|
|
141
191
|
const text = await response.text();
|
|
142
192
|
let message: string | undefined;
|
|
193
|
+
let code: string | undefined;
|
|
194
|
+
let details: Record<string, unknown> | undefined;
|
|
143
195
|
try {
|
|
144
|
-
const body = JSON.parse(text);
|
|
145
|
-
if (typeof body
|
|
146
|
-
|
|
196
|
+
const body = JSON.parse(text) as unknown;
|
|
197
|
+
if (typeof body === "object" && body !== null && !Array.isArray(body)) {
|
|
198
|
+
const record = body as Record<string, unknown>;
|
|
199
|
+
if (typeof record.message === "string" && record.message.length > 0) {
|
|
200
|
+
message = record.message;
|
|
201
|
+
}
|
|
202
|
+
if (typeof record.error === "string") {
|
|
203
|
+
code = record.error;
|
|
204
|
+
details = { ...record };
|
|
205
|
+
delete details.error;
|
|
206
|
+
}
|
|
147
207
|
}
|
|
148
208
|
} catch {}
|
|
149
209
|
throw new CloudAPIError(
|
|
150
210
|
message ?? `Request failed with status ${response.status}, ${text}`,
|
|
151
211
|
response.status,
|
|
212
|
+
code,
|
|
213
|
+
details,
|
|
152
214
|
);
|
|
153
215
|
}
|
|
154
216
|
|