agents 0.20.0 → 0.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +50 -12
- package/dist/{agent-tool-types-Btk9ETS-.d.ts → agent-tool-types-CzGGB-20.d.ts} +403 -115
- package/dist/agent-tool-types.d.ts +1 -1
- package/dist/{agent-tools-UuScsJg3.d.ts → agent-tools-zR2d5uij.d.ts} +2 -2
- package/dist/agent-tools.d.ts +24 -8
- package/dist/agent-tools.js +14 -6
- package/dist/agent-tools.js.map +1 -1
- package/dist/browser/ai.d.ts +5 -3
- package/dist/browser/ai.js +86 -7
- package/dist/browser/ai.js.map +1 -1
- package/dist/browser/index.d.ts +1 -1
- package/dist/browser/index.js +1 -1
- package/dist/browser/tanstack-ai.js +13 -1
- package/dist/browser/tanstack-ai.js.map +1 -1
- package/dist/chat/index.d.ts +31 -2
- package/dist/chat/index.js +57 -2
- package/dist/chat/index.js.map +1 -1
- package/dist/chat/react.d.ts +5 -179
- package/dist/chat/react.js +18 -555
- package/dist/chat/react.js.map +1 -1
- package/dist/chat/transport.d.ts +10 -0
- package/dist/chat/transport.js +2 -0
- package/dist/chat-sdk/index.d.ts +1 -1
- package/dist/client.d.ts +1 -1
- package/dist/cloudflare-BduZwmYK.js +204 -0
- package/dist/cloudflare-BduZwmYK.js.map +1 -0
- package/dist/{connector-v2M1zlZp.d.ts → connector-CkQD4MK3.d.ts} +20 -3
- package/dist/{connector-KEJnl6e5.js → connector-CptFKzRh.js} +158 -40
- package/dist/connector-CptFKzRh.js.map +1 -0
- package/dist/{handler-stateless-8hQN_kC3.js → handler-stateless-CIkKPETH.js} +20 -69
- package/dist/handler-stateless-CIkKPETH.js.map +1 -0
- package/dist/index.d.ts +21 -11
- package/dist/index.js +31 -21
- package/dist/index.js.map +1 -1
- package/dist/mcp/client.d.ts +20 -20
- package/dist/mcp/index.d.ts +35 -35
- package/dist/mcp/index.js +32 -98
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/server.js +1 -1
- package/dist/observability/ai/index.d.ts +10 -131
- package/dist/observability/ai/index.js +209 -444
- package/dist/observability/ai/index.js.map +1 -1
- package/dist/{wire-types-CU9rLoeS.js → protocol-Dqc2MQxo.js} +2 -46
- package/dist/protocol-Dqc2MQxo.js.map +1 -0
- package/dist/react.d.ts +1 -1
- package/dist/react.js +5 -8
- package/dist/react.js.map +1 -1
- package/dist/serializable.d.ts +1 -1
- package/dist/sub-routing.d.ts +18 -6
- package/dist/sub-routing.js +92 -2
- package/dist/sub-routing.js.map +1 -1
- package/dist/wire-types-CnMt6_HR.js +47 -0
- package/dist/wire-types-CnMt6_HR.js.map +1 -0
- package/dist/workflow-types.d.ts +25 -25
- package/dist/workflow-types.js.map +1 -1
- package/dist/workflows.d.ts +22 -22
- package/dist/ws-chat-transport-CIoOBbO7.js +561 -0
- package/dist/ws-chat-transport-CIoOBbO7.js.map +1 -0
- package/dist/ws-chat-transport-UNRIS2xl.d.ts +184 -0
- package/docs/adding-to-existing-project.md +4 -2
- package/docs/agent-class.md +1 -1
- package/docs/agent-tools.md +29 -0
- package/docs/browse-the-web.md +16 -1
- package/docs/chat-agents.md +3 -1
- package/docs/client-sdk.md +12 -8
- package/docs/configuration.md +7 -1
- package/docs/cross-domain-authentication.md +7 -35
- package/docs/email.md +2 -13
- package/docs/human-in-the-loop.md +15 -12
- package/docs/long-running-agents.md +11 -11
- package/docs/mcp-client.md +1 -1
- package/docs/mcp-servers.md +7 -1
- package/docs/mcp-transports.md +18 -12
- package/docs/migration-to-ai-sdk-v5.md +2 -2
- package/docs/migration-to-ai-sdk-v6.md +5 -1
- package/docs/observability.md +24 -67
- package/docs/routing.md +27 -0
- package/docs/sub-agents.md +47 -2
- package/docs/webhooks.md +109 -136
- package/docs/workflows.md +9 -1
- package/package.json +19 -10
- package/dist/cloudflare-BldFV0Pa.js +0 -117
- package/dist/cloudflare-BldFV0Pa.js.map +0 -1
- package/dist/connector-KEJnl6e5.js.map +0 -1
- package/dist/handler-stateless-8hQN_kC3.js.map +0 -1
- package/dist/wire-types-CU9rLoeS.js.map +0 -1
- package/dist/workflow-types-Baz_PO5v.d.ts +0 -280
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { __DO_NOT_USE_WILL_BREAK__agentContext } from "../../internal_context.js";
|
|
2
|
+
import { r as writeSpanAttributes, t as tracer } from "../../cloudflare-BduZwmYK.js";
|
|
2
3
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
3
4
|
//#region src/observability/ai/read.ts
|
|
4
5
|
/** Narrows an unknown value to a string. */
|
|
@@ -37,11 +38,11 @@ function readNestedTokenField(value, key) {
|
|
|
37
38
|
const TraceAttribute = {
|
|
38
39
|
Cloudflare: {
|
|
39
40
|
AIGatewayLogID: "cloudflare.ai_gateway.log.id",
|
|
40
|
-
CallID: "cloudflare.agents.call.id",
|
|
41
41
|
IntegrationName: "cloudflare.agents.integration.name",
|
|
42
42
|
MetadataPrefix: "cloudflare.agents.metadata.",
|
|
43
43
|
OperationName: "cloudflare.agents.operation.name",
|
|
44
44
|
ResponseFinishReason: "cloudflare.agents.response.finish_reason",
|
|
45
|
+
RuntimeContextPrefix: "cloudflare.agents.runtime_context.",
|
|
45
46
|
ToolApprovalState: "cloudflare.agents.tool.approval.state",
|
|
46
47
|
ToolCount: "cloudflare.agents.tool.count",
|
|
47
48
|
TurnAdmission: "cloudflare.agents.turn.admission",
|
|
@@ -159,19 +160,24 @@ const CONSUMED_METADATA_KEYS = /* @__PURE__ */ new Set([
|
|
|
159
160
|
"gen_ai.conversation.id"
|
|
160
161
|
]);
|
|
161
162
|
/**
|
|
162
|
-
* Projects
|
|
163
|
-
*
|
|
164
|
-
*
|
|
165
|
-
*
|
|
163
|
+
* Projects a per-call telemetry record onto root span attributes: reserved
|
|
164
|
+
* keys map to their dedicated attributes, any other SCALAR entry passes
|
|
165
|
+
* through under `passthroughPrefix`, and object/array values are dropped
|
|
166
|
+
* (scalar-only attribute rule).
|
|
167
|
+
*
|
|
168
|
+
* v6 passes `experimental_telemetry.metadata` and keeps the default prefix;
|
|
169
|
+
* v7 has no metadata option and passes the included subset of `runtimeContext`
|
|
170
|
+
* under its own prefix. Reserved keys land on the same attribute either way,
|
|
171
|
+
* so turn identity does not move namespace between SDK majors.
|
|
166
172
|
*/
|
|
167
|
-
function metadataAttributes(metadata) {
|
|
173
|
+
function metadataAttributes(metadata, passthroughPrefix = TraceAttribute.Cloudflare.MetadataPrefix) {
|
|
168
174
|
if (metadata === void 0) return {};
|
|
169
175
|
const attributes = {};
|
|
170
176
|
for (const [key, value] of Object.entries(metadata)) {
|
|
171
177
|
if (CONSUMED_METADATA_KEYS.has(key)) continue;
|
|
172
178
|
if (typeof value !== "string" && typeof value !== "number" && typeof value !== "boolean") continue;
|
|
173
179
|
const reserved = Object.hasOwn(RESERVED_METADATA_ATTRIBUTES, key) ? RESERVED_METADATA_ATTRIBUTES[key] : void 0;
|
|
174
|
-
attributes[reserved ?? `${
|
|
180
|
+
attributes[reserved ?? `${passthroughPrefix}${key}`] = value;
|
|
175
181
|
}
|
|
176
182
|
return attributes;
|
|
177
183
|
}
|
|
@@ -188,7 +194,7 @@ function operationSpan(input) {
|
|
|
188
194
|
return {
|
|
189
195
|
attributes: {
|
|
190
196
|
...input.attributes,
|
|
191
|
-
[TraceAttribute.Cloudflare.IntegrationName]:
|
|
197
|
+
[TraceAttribute.Cloudflare.IntegrationName]: "ai-sdk",
|
|
192
198
|
[TraceAttribute.Cloudflare.OperationName]: input.operation,
|
|
193
199
|
[TraceAttribute.GenAI.AgentID]: input.context?.agentId,
|
|
194
200
|
[TraceAttribute.GenAI.AgentName]: input.context?.agentName,
|
|
@@ -206,7 +212,7 @@ function modelCallSpan(input) {
|
|
|
206
212
|
return {
|
|
207
213
|
attributes: {
|
|
208
214
|
...input.attributes,
|
|
209
|
-
[TraceAttribute.Cloudflare.IntegrationName]:
|
|
215
|
+
[TraceAttribute.Cloudflare.IntegrationName]: "ai-sdk",
|
|
210
216
|
[TraceAttribute.Cloudflare.OperationName]: input.operation,
|
|
211
217
|
[TraceAttribute.GenAI.OperationName]: TraceAttribute.GenAI.OperationNameValueChat,
|
|
212
218
|
[TraceAttribute.GenAI.ProviderName]: normalizeProviderName(input.provider),
|
|
@@ -233,7 +239,7 @@ function requestAttributes(request, model) {
|
|
|
233
239
|
function toolCallSpan(input) {
|
|
234
240
|
return {
|
|
235
241
|
attributes: {
|
|
236
|
-
[TraceAttribute.Cloudflare.IntegrationName]:
|
|
242
|
+
[TraceAttribute.Cloudflare.IntegrationName]: "ai-sdk",
|
|
237
243
|
[TraceAttribute.Cloudflare.OperationName]: input.operation,
|
|
238
244
|
[TraceAttribute.GenAI.OperationName]: TraceAttribute.GenAI.OperationNameValueExecuteTool,
|
|
239
245
|
[TraceAttribute.GenAI.ToolCallID]: input.toolCallId,
|
|
@@ -284,14 +290,14 @@ function totalTokens(usage) {
|
|
|
284
290
|
return usage?.inputTokens !== void 0 && usage.outputTokens !== void 0 ? usage.inputTokens + usage.outputTokens : void 0;
|
|
285
291
|
}
|
|
286
292
|
//#endregion
|
|
287
|
-
//#region src/observability/ai/
|
|
293
|
+
//#region src/observability/ai/wrapper/extract.ts
|
|
288
294
|
function finishAttributesFromResult(result, options = {}) {
|
|
289
295
|
return finishAttributes({
|
|
290
296
|
aiGatewayLogId: options.aiGatewayLogId,
|
|
291
|
-
finishReason: extractFinishReason
|
|
297
|
+
finishReason: extractFinishReason(result),
|
|
292
298
|
response: options.includeResponse ? extractResponseInfo(result) : void 0,
|
|
293
299
|
toolCallCount: extractToolCallCount(result),
|
|
294
|
-
usage:
|
|
300
|
+
usage: extractAISDKTokenUsage(result)
|
|
295
301
|
});
|
|
296
302
|
}
|
|
297
303
|
function extractRequestSummary(params, operation) {
|
|
@@ -307,7 +313,7 @@ function extractRequestSummary(params, operation) {
|
|
|
307
313
|
topP: readNumber(params.topP)
|
|
308
314
|
};
|
|
309
315
|
}
|
|
310
|
-
/** Extracts model identity from an AI SDK
|
|
316
|
+
/** Extracts model identity from an AI SDK model object. */
|
|
311
317
|
function extractModelInfo(value) {
|
|
312
318
|
if (typeof value === "string") return value.length > 0 ? {
|
|
313
319
|
modelId: value,
|
|
@@ -324,13 +330,11 @@ function extractModelInfo(value) {
|
|
|
324
330
|
};
|
|
325
331
|
}
|
|
326
332
|
/**
|
|
327
|
-
* Extracts token usage from an AI SDK
|
|
328
|
-
*
|
|
329
|
-
*
|
|
330
|
-
* where `inputTokens`/`outputTokens` may be plain numbers or nested objects
|
|
331
|
-
* like `{ total, cacheRead, cacheWrite }` / `{ total, reasoning }`.
|
|
333
|
+
* Extracts token usage from an AI SDK result or stream chunk across supported
|
|
334
|
+
* majors. Token counts may be plain numbers or nested objects such as
|
|
335
|
+
* `{ total, cacheRead, cacheWrite }` / `{ total, reasoning }`.
|
|
332
336
|
*/
|
|
333
|
-
function
|
|
337
|
+
function extractAISDKTokenUsage(value) {
|
|
334
338
|
if (typeof value !== "object" || value === null) return;
|
|
335
339
|
const record = value;
|
|
336
340
|
const raw = record.totalUsage ?? record.usage;
|
|
@@ -358,9 +362,9 @@ function extractToolCallCount(value) {
|
|
|
358
362
|
return Array.isArray(toolCalls) && toolCalls.length > 0 ? toolCalls.length : void 0;
|
|
359
363
|
}
|
|
360
364
|
/**
|
|
361
|
-
* Reads a finish reason from an AI SDK
|
|
365
|
+
* Reads a finish reason from an AI SDK result or `finish`-type stream chunk.
|
|
362
366
|
*/
|
|
363
|
-
function extractFinishReason
|
|
367
|
+
function extractFinishReason(value) {
|
|
364
368
|
if (typeof value !== "object" || value === null) return;
|
|
365
369
|
const finishReason = value.finishReason;
|
|
366
370
|
if (typeof finishReason === "string") return finishReason;
|
|
@@ -798,14 +802,15 @@ function byteLength(value) {
|
|
|
798
802
|
return new TextEncoder().encode(value).length;
|
|
799
803
|
}
|
|
800
804
|
//#endregion
|
|
801
|
-
//#region src/observability/ai/
|
|
805
|
+
//#region src/observability/ai/wrapper/streams.ts
|
|
802
806
|
function finishWhenStreamCompletes(result, span, options = {}) {
|
|
803
807
|
return patchStreamFields(result, {
|
|
804
808
|
onComplete: (summary) => {
|
|
805
809
|
span.finish(finishAttributesFromStreamSummary(summary, options.includeResponse === true, options.includeAIGatewayLog === true, options.aiGatewayLogId, options.storeMessages === true));
|
|
806
810
|
},
|
|
807
|
-
onError: (cause, observedAIGatewayLogId) => {
|
|
811
|
+
onError: (cause, observedAIGatewayLogId, observed) => {
|
|
808
812
|
if (options.includeAIGatewayLog) writeSpanAttributes(span, aiGatewayLogAttributes(observedAIGatewayLogId ?? extractAIGatewayLogId(cause) ?? options.aiGatewayLogId));
|
|
813
|
+
if (observed !== void 0) writeSpanAttributes(span, finishAttributesFromStreamSummary(observed, options.includeResponse === true, options.includeAIGatewayLog === true, options.aiGatewayLogId, options.storeMessages === true));
|
|
809
814
|
span.fail(cause);
|
|
810
815
|
}
|
|
811
816
|
}, options.startedAtMs, options.includeAIGatewayLog ? options.aiGatewayLogId : void 0, options.storeMessages === true);
|
|
@@ -836,10 +841,10 @@ function patchStreamFields(result, hooks, startedAtMs, aiGatewayLogId, storeMess
|
|
|
836
841
|
closed = true;
|
|
837
842
|
hooks.onComplete(summary);
|
|
838
843
|
};
|
|
839
|
-
const errorOnce = (cause, observedAIGatewayLogId) => {
|
|
844
|
+
const errorOnce = (cause, observedAIGatewayLogId, observed) => {
|
|
840
845
|
if (closed) return;
|
|
841
846
|
closed = true;
|
|
842
|
-
hooks.onError(cause, observedAIGatewayLogId);
|
|
847
|
+
hooks.onError(cause, observedAIGatewayLogId, observed);
|
|
843
848
|
};
|
|
844
849
|
try {
|
|
845
850
|
if (isReadableStream(record.baseStream)) {
|
|
@@ -977,13 +982,23 @@ function createStreamState(hooks, startedAtMs, initialAIGatewayLogId, storeMessa
|
|
|
977
982
|
let observedAbort = false;
|
|
978
983
|
let firstChunkAtMs;
|
|
979
984
|
const output = createStreamMessages();
|
|
985
|
+
/** What the stream reported before it stopped, complete or not. */
|
|
986
|
+
const observedSummary = () => streamSummaryFromParts({
|
|
987
|
+
aiGatewayLogId,
|
|
988
|
+
finishReason,
|
|
989
|
+
outputMessages: storeMessages ? output.messages(finishReason) : void 0,
|
|
990
|
+
response,
|
|
991
|
+
timeToFirstChunkSeconds: firstChunkAtMs === void 0 || startedAtMs === void 0 ? void 0 : (firstChunkAtMs - startedAtMs) / 1e3,
|
|
992
|
+
toolCallCount,
|
|
993
|
+
usage
|
|
994
|
+
});
|
|
980
995
|
const settleObserved = () => {
|
|
981
996
|
if (observedError) {
|
|
982
|
-
hooks.onError(observedError.cause, aiGatewayLogId);
|
|
997
|
+
hooks.onError(observedError.cause, aiGatewayLogId, observedSummary());
|
|
983
998
|
return true;
|
|
984
999
|
}
|
|
985
1000
|
if (observedAbort) {
|
|
986
|
-
hooks.onError({ name: "AbortError" }, aiGatewayLogId);
|
|
1001
|
+
hooks.onError({ name: "AbortError" }, aiGatewayLogId, observedSummary());
|
|
987
1002
|
return true;
|
|
988
1003
|
}
|
|
989
1004
|
return false;
|
|
@@ -1025,8 +1040,8 @@ function createStreamState(hooks, startedAtMs, initialAIGatewayLogId, storeMessa
|
|
|
1025
1040
|
if (isErrorChunk(chunk)) observedError = { cause: chunk.error };
|
|
1026
1041
|
if (isAbortChunk(chunk)) observedAbort = true;
|
|
1027
1042
|
if (isToolCallChunk(chunk)) toolCallCount += 1;
|
|
1028
|
-
finishReason = extractFinishReason
|
|
1029
|
-
usage =
|
|
1043
|
+
finishReason = extractFinishReason(chunk) ?? finishReason;
|
|
1044
|
+
usage = extractAISDKTokenUsage(chunk) ?? usage;
|
|
1030
1045
|
response = extractResponseInfo(chunk) ?? response;
|
|
1031
1046
|
}
|
|
1032
1047
|
};
|
|
@@ -1069,8 +1084,8 @@ function isAsyncIterable$1(value) {
|
|
|
1069
1084
|
return typeof value === "object" && value !== null && Symbol.asyncIterator in value && typeof value[Symbol.asyncIterator] === "function";
|
|
1070
1085
|
}
|
|
1071
1086
|
//#endregion
|
|
1072
|
-
//#region src/observability/ai/
|
|
1073
|
-
function wrapModel(tracer, wrapLanguageModel, model, parentOperation, storeMessages) {
|
|
1087
|
+
//#region src/observability/ai/wrapper/model.ts
|
|
1088
|
+
function wrapModel(tracer, wrapLanguageModel, model, parentOperation, storeMessages, boundToInvocation = false) {
|
|
1074
1089
|
if (!wrapLanguageModel) return model;
|
|
1075
1090
|
if (typeof model !== "object" || model === null) return model;
|
|
1076
1091
|
const modelInfo = extractModelInfo(model);
|
|
@@ -1096,7 +1111,7 @@ function wrapModel(tracer, wrapLanguageModel, model, parentOperation, storeMessa
|
|
|
1096
1111
|
recordAIGatewayLogOnError(modelCall, cause, aiGatewayLog.get());
|
|
1097
1112
|
throw cause;
|
|
1098
1113
|
}
|
|
1099
|
-
});
|
|
1114
|
+
}, boundToInvocation ? { boundToInvocation: true } : void 0);
|
|
1100
1115
|
},
|
|
1101
1116
|
wrapStream: async ({ doStream, params }) => {
|
|
1102
1117
|
const span = modelCallSpanForModel("doStream", modelInfo, params, parentOperation, storeMessages);
|
|
@@ -1117,7 +1132,7 @@ function wrapModel(tracer, wrapLanguageModel, model, parentOperation, storeMessa
|
|
|
1117
1132
|
modelCall.fail(cause);
|
|
1118
1133
|
throw cause;
|
|
1119
1134
|
}
|
|
1120
|
-
});
|
|
1135
|
+
}, boundToInvocation ? { boundToInvocation: true } : void 0);
|
|
1121
1136
|
}
|
|
1122
1137
|
}
|
|
1123
1138
|
});
|
|
@@ -1128,7 +1143,6 @@ function recordAIGatewayLogOnError(span, cause, capturedLogId) {
|
|
|
1128
1143
|
function modelCallSpanForModel(operation, model, params, parentOperation, storeMessages) {
|
|
1129
1144
|
const record = typeof params === "object" && params !== null ? params : {};
|
|
1130
1145
|
const span = modelCallSpan({
|
|
1131
|
-
integration: "ai-sdk",
|
|
1132
1146
|
model: model?.modelId,
|
|
1133
1147
|
operation,
|
|
1134
1148
|
provider: model?.provider,
|
|
@@ -1143,29 +1157,28 @@ function modelCallSpanForModel(operation, model, params, parentOperation, storeM
|
|
|
1143
1157
|
};
|
|
1144
1158
|
}
|
|
1145
1159
|
//#endregion
|
|
1146
|
-
//#region src/observability/ai/
|
|
1147
|
-
function wrapTools(tracer, tools, storeTools) {
|
|
1160
|
+
//#region src/observability/ai/wrapper/tools.ts
|
|
1161
|
+
function wrapTools(tracer, tools, storeTools, boundToInvocation = false, approvedToolCalls) {
|
|
1148
1162
|
if (typeof tools !== "object" || tools === null) return tools;
|
|
1149
1163
|
const toolRecord = tools;
|
|
1150
1164
|
const wrappedTools = {};
|
|
1151
|
-
for (const [toolName, tool] of Object.entries(toolRecord)) wrappedTools[toolName] = wrapTool(tracer, toolName, tool, storeTools);
|
|
1165
|
+
for (const [toolName, tool] of Object.entries(toolRecord)) wrappedTools[toolName] = wrapTool(tracer, toolName, tool, storeTools, boundToInvocation, approvedToolCalls);
|
|
1152
1166
|
return wrappedTools;
|
|
1153
1167
|
}
|
|
1154
|
-
function wrapTool(tracer, toolName, tool, storeTools) {
|
|
1168
|
+
function wrapTool(tracer, toolName, tool, storeTools, boundToInvocation, approvedToolCalls) {
|
|
1155
1169
|
if (typeof tool !== "object" || tool === null) return tool;
|
|
1156
1170
|
const toolRecord = tool;
|
|
1157
1171
|
const hasExecute = typeof toolRecord.execute === "function";
|
|
1158
1172
|
const hasApproval = typeof toolRecord.needsApproval === "boolean" || typeof toolRecord.needsApproval === "function";
|
|
1159
1173
|
if (!hasExecute && !hasApproval) return tool;
|
|
1160
1174
|
const wrappedTool = Object.assign(Object.create(Object.getPrototypeOf(tool)), tool);
|
|
1161
|
-
if (hasApproval) wrapApprovalCheck(tracer, wrappedTool, toolRecord, tool, toolName);
|
|
1175
|
+
if (hasApproval) wrapApprovalCheck(tracer, wrappedTool, toolRecord, tool, toolName, boundToInvocation);
|
|
1162
1176
|
if (!hasExecute) return wrappedTool;
|
|
1163
1177
|
const execute = toolRecord.execute;
|
|
1164
1178
|
if (typeof execute !== "function") return wrappedTool;
|
|
1165
1179
|
const originalExecute = execute.bind(tool);
|
|
1166
1180
|
wrappedTool.execute = (...args) => {
|
|
1167
1181
|
const span = toolCallSpan({
|
|
1168
|
-
integration: "ai-sdk",
|
|
1169
1182
|
operation: "tool.execute",
|
|
1170
1183
|
toolCallId: extractToolCallId(args[1]),
|
|
1171
1184
|
toolName
|
|
@@ -1176,53 +1189,74 @@ function wrapTool(tracer, toolName, tool, storeTools) {
|
|
|
1176
1189
|
};
|
|
1177
1190
|
return tracer.openSpan(span.name, attributes, (toolSpan) => {
|
|
1178
1191
|
const inSpanContext = AsyncLocalStorage.snapshot();
|
|
1179
|
-
const
|
|
1180
|
-
if (
|
|
1192
|
+
const toolCallId = extractToolCallId(args[1]);
|
|
1193
|
+
if (approvalResponseForOptions(args[1], toolCallId)?.approved === true || toolCallId !== void 0 && approvedToolCalls?.get(toolCallId) === toolName) {
|
|
1194
|
+
recordApprovalChild(tracer, toolName, toolCallId, "approved", boundToInvocation);
|
|
1195
|
+
if (toolCallId !== void 0) approvedToolCalls?.delete(toolCallId);
|
|
1196
|
+
}
|
|
1181
1197
|
const result = originalExecute(...args);
|
|
1182
1198
|
if (isPromiseLike(result)) return Promise.resolve(result).then((resolved) => settleToolResult(resolved, toolSpan, inSpanContext, storeTools), (cause) => {
|
|
1183
1199
|
toolSpan.fail(cause);
|
|
1184
1200
|
throw cause;
|
|
1185
1201
|
});
|
|
1186
1202
|
return settleToolResult(result, toolSpan, inSpanContext, storeTools);
|
|
1187
|
-
});
|
|
1203
|
+
}, boundToInvocation ? { boundToInvocation: true } : void 0);
|
|
1188
1204
|
};
|
|
1189
1205
|
return wrappedTool;
|
|
1190
1206
|
}
|
|
1191
|
-
function wrapApprovalCheck(tracer, wrappedTool, toolRecord, tool, toolName) {
|
|
1207
|
+
function wrapApprovalCheck(tracer, wrappedTool, toolRecord, tool, toolName, boundToInvocation) {
|
|
1192
1208
|
const approval = toolRecord.needsApproval;
|
|
1193
1209
|
const original = typeof approval === "function" ? approval.bind(tool) : void 0;
|
|
1194
1210
|
wrappedTool.needsApproval = (...args) => {
|
|
1195
1211
|
const result = original ? original(...args) : approval;
|
|
1196
1212
|
const recordRequested = (needed) => {
|
|
1197
1213
|
const toolCallId = extractToolCallId(args[1]);
|
|
1198
|
-
if (needed === true && !hasApprovalResponse(args[1], toolCallId)) recordApprovalSegment(tracer, toolName, toolCallId, "requested");
|
|
1214
|
+
if (needed === true && !hasApprovalResponse(args[1], toolCallId)) recordApprovalSegment(tracer, toolName, toolCallId, "requested", boundToInvocation);
|
|
1199
1215
|
return needed;
|
|
1200
1216
|
};
|
|
1201
1217
|
return isPromiseLike(result) ? Promise.resolve(result).then(recordRequested) : recordRequested(result);
|
|
1202
1218
|
};
|
|
1203
1219
|
}
|
|
1220
|
+
/** Instruments AI SDK v7's top-level tool approval policy. */
|
|
1221
|
+
function wrapToolApprovalPolicy(tracer, policy, approvedToolCalls, boundToInvocation = false) {
|
|
1222
|
+
if (typeof policy === "function") return (...args) => {
|
|
1223
|
+
const toolCall = recordValue(recordValue(args[0])?.toolCall);
|
|
1224
|
+
return observePolicyResult(policy(...args), readString(toolCall?.toolName) ?? "tool", readString(toolCall?.toolCallId), tracer, approvedToolCalls, boundToInvocation);
|
|
1225
|
+
};
|
|
1226
|
+
if (typeof policy !== "object" || policy === null) return policy;
|
|
1227
|
+
return Object.fromEntries(Object.entries(policy).map(([toolName, setting]) => [toolName, (...args) => observePolicyResult(typeof setting === "function" ? setting(...args) : setting, toolName, extractToolCallId(args[1]), tracer, approvedToolCalls, boundToInvocation)]));
|
|
1228
|
+
}
|
|
1229
|
+
function observePolicyResult(result, toolName, toolCallId, tracer, approvedToolCalls, boundToInvocation = false) {
|
|
1230
|
+
const observe = (status) => {
|
|
1231
|
+
if (toolCallId === void 0) return status;
|
|
1232
|
+
const type = typeof status === "string" ? status : readString(recordValue(status)?.type);
|
|
1233
|
+
if (type === "approved") approvedToolCalls.set(toolCallId, toolName);
|
|
1234
|
+
else if (type === "user-approval" || type === "denied") recordApprovalSegment(tracer, toolName, toolCallId, type === "denied" ? "denied" : "requested", boundToInvocation);
|
|
1235
|
+
return status;
|
|
1236
|
+
};
|
|
1237
|
+
return isPromiseLike(result) ? Promise.resolve(result).then(observe) : observe(result);
|
|
1238
|
+
}
|
|
1204
1239
|
/** Records denied responses, whose tool never reaches execute(). */
|
|
1205
1240
|
function recordDeniedApprovalResponses(tracer, messages) {
|
|
1206
1241
|
for (const response of approvalResponses(messages)) if (!response.approved) recordApprovalSegment(tracer, response.toolName, response.toolCallId, "denied");
|
|
1207
1242
|
}
|
|
1208
|
-
function recordApprovalSegment(tracer, toolName, toolCallId, state) {
|
|
1243
|
+
function recordApprovalSegment(tracer, toolName, toolCallId, state, boundToInvocation = false) {
|
|
1209
1244
|
const tool = toolCallSpan({
|
|
1210
|
-
integration: "ai-sdk",
|
|
1211
1245
|
operation: "tool.approval",
|
|
1212
1246
|
toolCallId,
|
|
1213
1247
|
toolName
|
|
1214
1248
|
});
|
|
1215
1249
|
tracer.withSpan(tool.name, tool.attributes, () => {
|
|
1216
|
-
recordApprovalChild(tracer, toolName, toolCallId, state);
|
|
1217
|
-
});
|
|
1250
|
+
recordApprovalChild(tracer, toolName, toolCallId, state, boundToInvocation);
|
|
1251
|
+
}, boundToInvocation ? { boundToInvocation: true } : void 0);
|
|
1218
1252
|
}
|
|
1219
|
-
function recordApprovalChild(tracer, toolName, toolCallId, state) {
|
|
1253
|
+
function recordApprovalChild(tracer, toolName, toolCallId, state, boundToInvocation = false) {
|
|
1220
1254
|
const approval = toolApprovalSpan({
|
|
1221
1255
|
state,
|
|
1222
1256
|
toolCallId,
|
|
1223
1257
|
toolName
|
|
1224
1258
|
});
|
|
1225
|
-
tracer.withSpan(approval.name, approval.attributes, () => void 0);
|
|
1259
|
+
tracer.withSpan(approval.name, approval.attributes, () => void 0, boundToInvocation ? { boundToInvocation: true } : void 0);
|
|
1226
1260
|
}
|
|
1227
1261
|
function hasApprovalResponse(options, toolCallId) {
|
|
1228
1262
|
return approvalResponseForOptions(options, toolCallId) !== void 0;
|
|
@@ -1321,16 +1355,19 @@ function extractToolCallId(options) {
|
|
|
1321
1355
|
function isAsyncIterable(value) {
|
|
1322
1356
|
return typeof value === "object" && value !== null && Symbol.asyncIterator in value && typeof value[Symbol.asyncIterator] === "function";
|
|
1323
1357
|
}
|
|
1358
|
+
function recordValue(value) {
|
|
1359
|
+
return typeof value === "object" && value !== null ? value : void 0;
|
|
1360
|
+
}
|
|
1324
1361
|
function isPromiseLike(value) {
|
|
1325
1362
|
return value !== null && value !== void 0 && (typeof value === "object" || typeof value === "function") && "then" in value && typeof value.then === "function";
|
|
1326
1363
|
}
|
|
1327
1364
|
//#endregion
|
|
1328
|
-
//#region src/observability/ai/
|
|
1365
|
+
//#region src/observability/ai/wrapper/wrap.ts
|
|
1329
1366
|
/**
|
|
1330
|
-
* Wraps an AI SDK namespace object with
|
|
1331
|
-
* shape and overloaded call signatures.
|
|
1367
|
+
* Wraps an AI SDK namespace object with tracing while preserving its public
|
|
1368
|
+
* shape and overloaded call signatures across supported majors.
|
|
1332
1369
|
*/
|
|
1333
|
-
function
|
|
1370
|
+
function createAISDKWrapper(ai, instrumentation) {
|
|
1334
1371
|
const target = isModuleNamespace(ai) ? Object.setPrototypeOf({}, ai) : ai;
|
|
1335
1372
|
const wrapperCache = /* @__PURE__ */ new Map();
|
|
1336
1373
|
return new Proxy(target, { get(proxyTarget, property, receiver) {
|
|
@@ -1338,7 +1375,7 @@ function createAISDKV6Wrapper(ai, instrumentation) {
|
|
|
1338
1375
|
if (isWrappedOperationName(property) && typeof original === "function") {
|
|
1339
1376
|
let wrapper = wrapperCache.get(property);
|
|
1340
1377
|
if (!wrapper) {
|
|
1341
|
-
wrapper = createOperationWrapper(property,
|
|
1378
|
+
wrapper = createOperationWrapper(property, toAISDKOperation(original), readWrapLanguageModel(ai), instrumentation);
|
|
1342
1379
|
wrapperCache.set(property, wrapper);
|
|
1343
1380
|
}
|
|
1344
1381
|
return wrapper;
|
|
@@ -1351,7 +1388,7 @@ function readWrapLanguageModel(ai) {
|
|
|
1351
1388
|
if (typeof value !== "function") return;
|
|
1352
1389
|
return value;
|
|
1353
1390
|
}
|
|
1354
|
-
function
|
|
1391
|
+
function toAISDKOperation(value) {
|
|
1355
1392
|
return value;
|
|
1356
1393
|
}
|
|
1357
1394
|
function isModuleNamespace(value) {
|
|
@@ -1371,7 +1408,8 @@ function createOperationWrapper(operationName, operation, wrapLanguageModel, ins
|
|
|
1371
1408
|
storeMessages: instrumentation.options?.storeMessages === true,
|
|
1372
1409
|
storeTools: instrumentation.options?.storeTools === true
|
|
1373
1410
|
};
|
|
1374
|
-
if (isStreamOperation
|
|
1411
|
+
if (isStreamOperation(operationName)) return (params, ...args) => {
|
|
1412
|
+
const boundToInvocation = isAISDKInvocationBounded(params);
|
|
1375
1413
|
return instrumentation.tracer.openSpan(operationSpanName(agentNameForCall(params)), {}, (operationSpan) => {
|
|
1376
1414
|
if (!operationSpan.isTraced) {
|
|
1377
1415
|
operationSpan.finish();
|
|
@@ -1380,46 +1418,51 @@ function createOperationWrapper(operationName, operation, wrapLanguageModel, ins
|
|
|
1380
1418
|
writeSpanAttributes(operationSpan, operationSpanForCall(operationName, extractModelInfo(params.model), params, instrumentation.options).attributes);
|
|
1381
1419
|
recordDeniedApprovalResponses(instrumentation.tracer, params.messages);
|
|
1382
1420
|
const startedAtMs = Date.now();
|
|
1383
|
-
const result = operation(operationParamsForCall(params, operationName, wrapLanguageModel, instrumentation.tracer, storage), ...args);
|
|
1421
|
+
const result = operation(operationParamsForCall(params, operationName, wrapLanguageModel, instrumentation.tracer, storage, boundToInvocation), ...args);
|
|
1384
1422
|
const hasModelSpan = canWrapModel(wrapLanguageModel, params.model);
|
|
1385
1423
|
return finishWhenStreamCompletes(result, operationSpan, {
|
|
1386
1424
|
includeResponse: !hasModelSpan,
|
|
1387
1425
|
startedAtMs: hasModelSpan ? void 0 : startedAtMs
|
|
1388
1426
|
});
|
|
1389
|
-
});
|
|
1427
|
+
}, boundToInvocation ? { boundToInvocation: true } : void 0);
|
|
1390
1428
|
};
|
|
1391
1429
|
return async (params, ...args) => {
|
|
1430
|
+
const boundToInvocation = isAISDKInvocationBounded(params);
|
|
1392
1431
|
return instrumentation.tracer.withSpan(operationSpanName(agentNameForCall(params)), {}, async (operationSpan) => {
|
|
1393
1432
|
if (!operationSpan.isTraced) return operation(params, ...args);
|
|
1394
1433
|
writeSpanAttributes(operationSpan, operationSpanForCall(operationName, extractModelInfo(params.model), params, instrumentation.options).attributes);
|
|
1395
1434
|
recordDeniedApprovalResponses(instrumentation.tracer, params.messages);
|
|
1396
|
-
const result = await operation(operationParamsForCall(params, operationName, wrapLanguageModel, instrumentation.tracer, storage), ...args);
|
|
1435
|
+
const result = await operation(operationParamsForCall(params, operationName, wrapLanguageModel, instrumentation.tracer, storage, boundToInvocation), ...args);
|
|
1397
1436
|
operationSpan.finish(finishAttributesFromResult(result, { includeResponse: !canWrapModel(wrapLanguageModel, params.model) }));
|
|
1398
1437
|
return result;
|
|
1399
|
-
});
|
|
1438
|
+
}, boundToInvocation ? { boundToInvocation: true } : void 0);
|
|
1400
1439
|
};
|
|
1401
1440
|
}
|
|
1402
1441
|
/**
|
|
1403
|
-
* Reads only the agent name
|
|
1404
|
-
*
|
|
1405
|
-
*
|
|
1442
|
+
* Reads only the agent name for the span name, from the same sources and in
|
|
1443
|
+
* the same order as {@link semanticContext}, so the span name and
|
|
1444
|
+
* `gen_ai.agent.name` never disagree. `functionId` is the AI SDK's canonical
|
|
1445
|
+
* projection; an explicit name from v6 metadata or v7 runtime context wins.
|
|
1406
1446
|
*/
|
|
1407
1447
|
function agentNameForCall(params) {
|
|
1408
|
-
const telemetry =
|
|
1409
|
-
const metadata =
|
|
1410
|
-
|
|
1448
|
+
const telemetry = telemetryOptions(params);
|
|
1449
|
+
const metadata = telemetryMetadata(params);
|
|
1450
|
+
const runtimeContext = runtimeContextRecord(params);
|
|
1451
|
+
return metadataValue(metadata, "agentName", "gen_ai.agent.name") ?? metadataValue(runtimeContext, "agentName", "gen_ai.agent.name") ?? readString(telemetry?.functionId);
|
|
1411
1452
|
}
|
|
1412
|
-
function operationParamsForCall(params, operationName, wrapLanguageModel, tracer, storage) {
|
|
1453
|
+
function operationParamsForCall(params, operationName, wrapLanguageModel, tracer, storage, boundToInvocation = false) {
|
|
1454
|
+
const approvedToolCalls = /* @__PURE__ */ new Map();
|
|
1413
1455
|
return {
|
|
1414
1456
|
...params,
|
|
1415
|
-
...shouldWrapTools(operationName) && params.tools !== void 0 ? { tools: wrapTools(tracer, params.tools, storage.storeTools) } : {},
|
|
1416
|
-
...params.
|
|
1457
|
+
...shouldWrapTools(operationName) && params.tools !== void 0 ? { tools: wrapTools(tracer, params.tools, storage.storeTools, boundToInvocation, approvedToolCalls) } : {},
|
|
1458
|
+
...params.toolApproval !== void 0 ? { toolApproval: wrapToolApprovalPolicy(tracer, params.toolApproval, approvedToolCalls, boundToInvocation) } : {},
|
|
1459
|
+
...params.model !== void 0 ? { model: wrapModel(tracer, wrapLanguageModel, params.model, operationName, storage.storeMessages, boundToInvocation) } : {}
|
|
1417
1460
|
};
|
|
1418
1461
|
}
|
|
1419
1462
|
function canWrapModel(wrapLanguageModel, model) {
|
|
1420
1463
|
return wrapLanguageModel !== void 0 && typeof model === "object" && model !== null;
|
|
1421
1464
|
}
|
|
1422
|
-
function isStreamOperation
|
|
1465
|
+
function isStreamOperation(operationName) {
|
|
1423
1466
|
return operationName === "streamObject" || operationName === "streamText";
|
|
1424
1467
|
}
|
|
1425
1468
|
function shouldWrapTools(operationName) {
|
|
@@ -1431,11 +1474,11 @@ function isWrappedOperationName(value) {
|
|
|
1431
1474
|
function operationSpanForCall(operation, model, params, options) {
|
|
1432
1475
|
return operationSpan({
|
|
1433
1476
|
attributes: {
|
|
1477
|
+
...metadataAttributes(includedRuntimeContext(params), TraceAttribute.Cloudflare.RuntimeContextPrefix),
|
|
1434
1478
|
...metadataAttributes(telemetryMetadata(params)),
|
|
1435
1479
|
...contextAttributes(params, options)
|
|
1436
1480
|
},
|
|
1437
1481
|
context: semanticContext(params),
|
|
1438
|
-
integration: "ai-sdk",
|
|
1439
1482
|
model: model?.modelId,
|
|
1440
1483
|
operation,
|
|
1441
1484
|
provider: model?.provider,
|
|
@@ -1444,405 +1487,127 @@ function operationSpanForCall(operation, model, params, options) {
|
|
|
1444
1487
|
}
|
|
1445
1488
|
/** Reads the per-call `experimental_telemetry.metadata` record, if present. */
|
|
1446
1489
|
function telemetryMetadata(params) {
|
|
1447
|
-
const telemetry =
|
|
1490
|
+
const telemetry = telemetryOptions(params);
|
|
1448
1491
|
return typeof telemetry?.metadata === "object" && telemetry.metadata !== null ? telemetry.metadata : void 0;
|
|
1449
1492
|
}
|
|
1450
1493
|
/**
|
|
1451
|
-
*
|
|
1452
|
-
* `
|
|
1453
|
-
*
|
|
1454
|
-
* takes priority. Other semantic fields come only from metadata.
|
|
1494
|
+
* The caller's application-data channel: `runtimeContext` on v7, the
|
|
1495
|
+
* `experimental_context` it replaced on v6. Read through one accessor so the
|
|
1496
|
+
* two majors cannot drift on which one identity and metadata come from.
|
|
1455
1497
|
*/
|
|
1456
|
-
function
|
|
1457
|
-
const
|
|
1458
|
-
|
|
1459
|
-
return {
|
|
1460
|
-
agentId: metadataValue$1(metadata, "agentId", "gen_ai.agent.id"),
|
|
1461
|
-
agentName: metadataValue$1(metadata, "agentName", "gen_ai.agent.name") ?? readString(telemetry?.functionId),
|
|
1462
|
-
agentVersion: metadataValue$1(metadata, "agentVersion", "gen_ai.agent.version"),
|
|
1463
|
-
conversationId: metadataValue$1(metadata, "conversationId", "gen_ai.conversation.id")
|
|
1464
|
-
};
|
|
1465
|
-
}
|
|
1466
|
-
function metadataValue$1(metadata, key, semanticKey) {
|
|
1467
|
-
return readString(metadata?.[key] ?? metadata?.[semanticKey]);
|
|
1468
|
-
}
|
|
1469
|
-
function contextAttributes(params, options) {
|
|
1470
|
-
const attributes = {};
|
|
1471
|
-
const runtimeContext = typeof params.experimental_context === "object" && params.experimental_context !== null ? params.experimental_context : void 0;
|
|
1472
|
-
for (const key of options?.includeRuntimeContext ?? []) {
|
|
1473
|
-
const value = runtimeContext?.[key];
|
|
1474
|
-
if (isScalarAttributeValue$1(value)) attributes[`cloudflare.agents.runtime_context.${key}`] = value;
|
|
1475
|
-
}
|
|
1476
|
-
return Object.keys(attributes).length > 0 ? attributes : void 0;
|
|
1477
|
-
}
|
|
1478
|
-
function isScalarAttributeValue$1(value) {
|
|
1479
|
-
return typeof value === "string" || typeof value === "number" || typeof value === "boolean";
|
|
1498
|
+
function runtimeContextRecord(params) {
|
|
1499
|
+
const value = params.runtimeContext ?? params.experimental_context;
|
|
1500
|
+
return typeof value === "object" && value !== null ? value : void 0;
|
|
1480
1501
|
}
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
function operationNameFromId(operationId) {
|
|
1485
|
-
const value = readString(operationId);
|
|
1486
|
-
if (value === void 0) return "ai-sdk";
|
|
1487
|
-
return value.startsWith("ai.") ? value.slice(3) : value;
|
|
1502
|
+
function telemetryOptions(params) {
|
|
1503
|
+
const telemetryValue = params.telemetry ?? params.experimental_telemetry;
|
|
1504
|
+
return typeof telemetryValue === "object" && telemetryValue !== null ? telemetryValue : void 0;
|
|
1488
1505
|
}
|
|
1489
1506
|
/**
|
|
1490
|
-
*
|
|
1491
|
-
*
|
|
1492
|
-
*
|
|
1493
|
-
*
|
|
1507
|
+
* The v7 stand-in for `experimental_telemetry.metadata`.
|
|
1508
|
+
*
|
|
1509
|
+
* v7 dropped `metadata` from its telemetry options; callers put the same
|
|
1510
|
+
* values in `runtimeContext` and mark the telemetry-visible ones through the
|
|
1511
|
+
* SDK's own `telemetry.includeRuntimeContext`. Running the included subset
|
|
1512
|
+
* through {@link metadataAttributes} is what keeps reserved keys — Think's
|
|
1513
|
+
* `cloudflare.agents.turn.*` above all — on the attribute names v6 emits, so
|
|
1514
|
+
* a query written against v6 traces still matches v7 ones. Everything else
|
|
1515
|
+
* passes through as documented, under `cloudflare.agents.runtime_context.*`.
|
|
1516
|
+
*
|
|
1517
|
+
* Runtime context the caller did not mark as included stays off the span as a
|
|
1518
|
+
* passthrough attribute: on v7 this is a general application-data channel, not
|
|
1519
|
+
* a telemetry bag. Identity (`agentId`, `agentName`, `agentVersion`,
|
|
1520
|
+
* `conversationId`) is separate — {@link semanticContext} reads it regardless,
|
|
1521
|
+
* because it names the operation rather than describing it, and v7 left
|
|
1522
|
+
* callers nowhere else to put it.
|
|
1494
1523
|
*/
|
|
1495
|
-
function
|
|
1496
|
-
const
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
};
|
|
1524
|
+
function includedRuntimeContext(params) {
|
|
1525
|
+
const runtimeContext = runtimeContextRecord(params);
|
|
1526
|
+
if (runtimeContext === void 0) return;
|
|
1527
|
+
const included = includedContextKeys(telemetryOptions(params));
|
|
1528
|
+
if (included === void 0) return;
|
|
1529
|
+
const projected = {};
|
|
1530
|
+
for (const key of included) if (Object.hasOwn(runtimeContext, key)) projected[key] = runtimeContext[key];
|
|
1531
|
+
return projected;
|
|
1504
1532
|
}
|
|
1505
|
-
/**
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1533
|
+
/**
|
|
1534
|
+
* The keys a caller marked as telemetry-visible.
|
|
1535
|
+
*
|
|
1536
|
+
* The SDK's own shape is `{ [key]: boolean }`, included only when explicitly
|
|
1537
|
+
* true; a plain array of key names is accepted too, since that is the shape of
|
|
1538
|
+
* the wrapper's option of the same name and silently ignoring it would be
|
|
1539
|
+
* worse than honouring it. There is deliberately no "include everything"
|
|
1540
|
+
* shorthand — runtime context routinely carries credentials and user data that
|
|
1541
|
+
* no one asked to put on a span.
|
|
1542
|
+
*/
|
|
1543
|
+
function includedContextKeys(telemetry) {
|
|
1544
|
+
const included = telemetry?.includeRuntimeContext;
|
|
1545
|
+
if (Array.isArray(included)) return included.filter((key) => typeof key === "string");
|
|
1546
|
+
if (typeof included !== "object" || included === null) return;
|
|
1547
|
+
return Object.entries(included).filter(([, enabled]) => enabled === true).map(([key]) => key);
|
|
1519
1548
|
}
|
|
1520
|
-
/**
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
});
|
|
1549
|
+
/**
|
|
1550
|
+
* Whether the caller explicitly opted a key OUT. Identity is read from runtime
|
|
1551
|
+
* context without an opt-in — on v7 there is nowhere else to put it, and
|
|
1552
|
+
* requiring one would silently cost every caller `gen_ai.agent.id` — but an
|
|
1553
|
+
* explicit `false` is a stated intention and is honoured.
|
|
1554
|
+
*/
|
|
1555
|
+
function isExcludedFromContext(params, key) {
|
|
1556
|
+
const included = telemetryOptions(params)?.includeRuntimeContext;
|
|
1557
|
+
return typeof included === "object" && included !== null && !Array.isArray(included) && included[key] === false;
|
|
1530
1558
|
}
|
|
1531
|
-
/**
|
|
1532
|
-
|
|
1559
|
+
/**
|
|
1560
|
+
* Reads agent/conversation semantic context from the AI SDK's own telemetry
|
|
1561
|
+
* fields. The AI SDK maps `functionId` to `gen_ai.agent.name`; an explicit
|
|
1562
|
+
* name takes priority. Each field comes from v6 `telemetry.metadata` or, on
|
|
1563
|
+
* v7 where that option no longer exists, from `runtimeContext`.
|
|
1564
|
+
*/
|
|
1565
|
+
function semanticContext(params) {
|
|
1566
|
+
const telemetry = telemetryOptions(params);
|
|
1567
|
+
const metadata = telemetryMetadata(params);
|
|
1568
|
+
const runtimeContext = runtimeContextRecord(params);
|
|
1569
|
+
const fromContext = (key, semanticKey) => isExcludedFromContext(params, key) ? void 0 : metadataValue(runtimeContext, key, semanticKey);
|
|
1533
1570
|
return {
|
|
1534
|
-
|
|
1535
|
-
|
|
1571
|
+
agentId: metadataValue(metadata, "agentId", "gen_ai.agent.id") ?? fromContext("agentId", "gen_ai.agent.id"),
|
|
1572
|
+
agentName: metadataValue(metadata, "agentName", "gen_ai.agent.name") ?? fromContext("agentName", "gen_ai.agent.name") ?? readString(telemetry?.functionId),
|
|
1573
|
+
agentVersion: metadataValue(metadata, "agentVersion", "gen_ai.agent.version") ?? fromContext("agentVersion", "gen_ai.agent.version"),
|
|
1574
|
+
conversationId: metadataValue(metadata, "conversationId", "gen_ai.conversation.id") ?? fromContext("conversationId", "gen_ai.conversation.id")
|
|
1536
1575
|
};
|
|
1537
1576
|
}
|
|
1538
1577
|
function metadataValue(metadata, key, semanticKey) {
|
|
1539
1578
|
return readString(metadata?.[key] ?? metadata?.[semanticKey]);
|
|
1540
1579
|
}
|
|
1541
|
-
function eventRecord(event) {
|
|
1542
|
-
return event;
|
|
1543
|
-
}
|
|
1544
|
-
function extractFinishReason(event) {
|
|
1545
|
-
const finishReason = event.finishReason;
|
|
1546
|
-
if (typeof finishReason === "string") return finishReason;
|
|
1547
|
-
if (typeof finishReason === "object" && finishReason !== null) return readString(finishReason.unified);
|
|
1548
|
-
}
|
|
1549
|
-
function responseSummaryFromEvent(event) {
|
|
1550
|
-
const response = typeof event.response === "object" && event.response !== null ? event.response : void 0;
|
|
1551
|
-
const id = readString(event.responseId ?? response?.id);
|
|
1552
|
-
const model = readString(event.responseModel ?? response?.modelId ?? response?.model);
|
|
1553
|
-
if (id === void 0 && model === void 0) return;
|
|
1554
|
-
return {
|
|
1555
|
-
...id !== void 0 ? { id } : {},
|
|
1556
|
-
...model !== void 0 ? { model } : {}
|
|
1557
|
-
};
|
|
1558
|
-
}
|
|
1559
|
-
function tokenUsageFromEvent(event) {
|
|
1560
|
-
const raw = event.totalUsage ?? event.usage;
|
|
1561
|
-
if (typeof raw !== "object" || raw === null) return;
|
|
1562
|
-
const usage = raw;
|
|
1563
|
-
const inputTokens = readTokenCount(usage.inputTokens);
|
|
1564
|
-
const outputTokens = readTokenCount(usage.outputTokens);
|
|
1565
|
-
const cacheReadInputTokens = readNestedTokenField(usage.inputTokenDetails, "cacheReadTokens") ?? readNestedTokenField(usage.inputTokens, "cacheRead") ?? readNumber(usage.cachedInputTokens);
|
|
1566
|
-
const cacheCreationInputTokens = readNestedTokenField(usage.inputTokenDetails, "cacheWriteTokens") ?? readNestedTokenField(usage.inputTokens, "cacheWrite");
|
|
1567
|
-
const reasoningTokens = readNestedTokenField(usage.outputTokenDetails, "reasoningTokens") ?? readNestedTokenField(usage.outputTokens, "reasoning") ?? readNumber(usage.reasoningTokens);
|
|
1568
|
-
if (inputTokens === void 0 && outputTokens === void 0 && cacheReadInputTokens === void 0 && cacheCreationInputTokens === void 0 && reasoningTokens === void 0) return;
|
|
1569
|
-
return {
|
|
1570
|
-
...cacheCreationInputTokens !== void 0 ? { cacheCreationInputTokens } : {},
|
|
1571
|
-
...cacheReadInputTokens !== void 0 ? { cacheReadInputTokens } : {},
|
|
1572
|
-
...inputTokens !== void 0 ? { inputTokens } : {},
|
|
1573
|
-
...outputTokens !== void 0 ? { outputTokens } : {},
|
|
1574
|
-
...reasoningTokens !== void 0 ? { reasoningTokens } : {}
|
|
1575
|
-
};
|
|
1576
|
-
}
|
|
1577
|
-
function timeToFirstChunkSeconds(event) {
|
|
1578
|
-
const milliseconds = readNumber((typeof event.performance === "object" && event.performance !== null ? event.performance : void 0)?.timeToFirstOutputMs);
|
|
1579
|
-
return milliseconds === void 0 ? void 0 : milliseconds / 1e3;
|
|
1580
|
-
}
|
|
1581
|
-
//#endregion
|
|
1582
|
-
//#region src/observability/ai/v7/telemetry.ts
|
|
1583
1580
|
/**
|
|
1584
|
-
*
|
|
1585
|
-
*
|
|
1581
|
+
* The wrapper-level allowlist, set once for the instrumentation rather than
|
|
1582
|
+
* per call. It selects from the same context and lands on the same attributes
|
|
1583
|
+
* as the SDK's per-call allowlist: selecting a key through both must produce
|
|
1584
|
+
* one attribute, not a canonical one and a `runtime_context.*` near-duplicate.
|
|
1586
1585
|
*/
|
|
1587
|
-
function
|
|
1588
|
-
const
|
|
1589
|
-
|
|
1590
|
-
const
|
|
1591
|
-
|
|
1592
|
-
const
|
|
1593
|
-
const
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
state.span.finish(finishAttributesFromEvent(event));
|
|
1600
|
-
operations.delete(event.callId);
|
|
1601
|
-
};
|
|
1602
|
-
return {
|
|
1603
|
-
onStart(event) {
|
|
1604
|
-
const operationName = supportedOperationName(operationNameFromId(event.operationId));
|
|
1605
|
-
if (!operationName) return;
|
|
1606
|
-
const span = operationSpan({
|
|
1607
|
-
attributes: {
|
|
1608
|
-
...correlationAttributes({ callId: event.callId }),
|
|
1609
|
-
...runtimeContextAttributes(event.runtimeContext)
|
|
1610
|
-
},
|
|
1611
|
-
context: semanticContextFromEvent(event),
|
|
1612
|
-
integration: "ai-sdk",
|
|
1613
|
-
model: readString(event.modelId),
|
|
1614
|
-
operation: operationName,
|
|
1615
|
-
provider: readString(event.provider),
|
|
1616
|
-
request: requestSummaryFromEvent(event, operationName)
|
|
1617
|
-
});
|
|
1618
|
-
const operation = instrumentation.tracer.openSpan(span.name, span.attributes, (activeSpan) => activeSpan);
|
|
1619
|
-
operations.set(event.callId, {
|
|
1620
|
-
callId: event.callId,
|
|
1621
|
-
operationName,
|
|
1622
|
-
span: operation
|
|
1623
|
-
});
|
|
1624
|
-
},
|
|
1625
|
-
onLanguageModelCallStart(event) {
|
|
1626
|
-
const state = operations.get(event.callId);
|
|
1627
|
-
if (!state) return;
|
|
1628
|
-
const span = modelCallSpan({
|
|
1629
|
-
attributes: {
|
|
1630
|
-
...correlationAttributes({ callId: event.callId }),
|
|
1631
|
-
...inputMessageAttributes(event, storeMessages)
|
|
1632
|
-
},
|
|
1633
|
-
integration: "ai-sdk",
|
|
1634
|
-
model: readString(event.modelId),
|
|
1635
|
-
operation: isStreamOperation(state.operationName) ? "doStream" : "doGenerate",
|
|
1636
|
-
provider: readString(event.provider),
|
|
1637
|
-
request: requestSummaryFromEvent(event, state.operationName)
|
|
1638
|
-
});
|
|
1639
|
-
const spans = modelSpans.get(event.callId) ?? [];
|
|
1640
|
-
spans.push({ spanSpec: span });
|
|
1641
|
-
modelSpans.set(event.callId, spans);
|
|
1642
|
-
},
|
|
1643
|
-
onLanguageModelCallEnd(event) {
|
|
1644
|
-
const state = shiftModelSpan(modelSpans, event.callId);
|
|
1645
|
-
if (!state) return;
|
|
1646
|
-
(state.span ?? instrumentation.tracer.openSpan(state.spanSpec.name, state.spanSpec.attributes, (activeSpan) => activeSpan)).finish({
|
|
1647
|
-
...finishAttributesFromEvent(event, {
|
|
1648
|
-
includeAIGatewayLog: true,
|
|
1649
|
-
includePerformance: true,
|
|
1650
|
-
includeResponse: true
|
|
1651
|
-
}),
|
|
1652
|
-
...outputMessageAttributes(event, storeMessages)
|
|
1653
|
-
});
|
|
1654
|
-
},
|
|
1655
|
-
onToolExecutionStart(event) {
|
|
1656
|
-
const toolCallId = readString(event.toolCall.toolCallId);
|
|
1657
|
-
if (toolCallId === void 0 || !operations.has(event.callId)) return;
|
|
1658
|
-
const toolName = readString(event.toolCall.toolName) ?? "tool";
|
|
1659
|
-
const span = toolCallSpan({
|
|
1660
|
-
integration: "ai-sdk",
|
|
1661
|
-
operation: "tool.execute",
|
|
1662
|
-
toolName
|
|
1663
|
-
});
|
|
1664
|
-
toolSpans.set(toolSpanKey(event.callId, toolCallId), {
|
|
1665
|
-
callId: event.callId,
|
|
1666
|
-
spanSpec: {
|
|
1667
|
-
name: span.name,
|
|
1668
|
-
attributes: {
|
|
1669
|
-
...span.attributes,
|
|
1670
|
-
...correlationAttributes({
|
|
1671
|
-
callId: event.callId,
|
|
1672
|
-
toolCallId
|
|
1673
|
-
}),
|
|
1674
|
-
...toolInputAttributes(event.toolCall.input, storeTools),
|
|
1675
|
-
...toolContextAttributes(toolName, event.toolContext)
|
|
1676
|
-
}
|
|
1677
|
-
}
|
|
1678
|
-
});
|
|
1679
|
-
},
|
|
1680
|
-
onToolExecutionEnd(event) {
|
|
1681
|
-
const toolCallId = readString(event.toolCall.toolCallId);
|
|
1682
|
-
if (toolCallId === void 0) return;
|
|
1683
|
-
const state = toolSpans.get(toolSpanKey(event.callId, toolCallId));
|
|
1684
|
-
if (!state) return;
|
|
1685
|
-
const span = state.span ?? instrumentation.tracer.openSpan(state.spanSpec.name, state.spanSpec.attributes, (activeSpan) => activeSpan);
|
|
1686
|
-
if (event.toolOutput?.type === "tool-error") span.fail(event.toolOutput.error);
|
|
1687
|
-
else span.finish(toolOutputAttributes(event.toolOutput?.output, storeTools));
|
|
1688
|
-
toolSpans.delete(toolSpanKey(event.callId, toolCallId));
|
|
1689
|
-
},
|
|
1690
|
-
onAbort(event) {
|
|
1691
|
-
const cause = { name: "AbortError" };
|
|
1692
|
-
finishOpenModelSpans(event.callId, cause, modelSpans, instrumentation.tracer);
|
|
1693
|
-
finishOpenToolSpans(event.callId, cause, toolSpans, instrumentation.tracer);
|
|
1694
|
-
const state = operations.get(event.callId);
|
|
1695
|
-
if (!state) return;
|
|
1696
|
-
state.span.fail(cause);
|
|
1697
|
-
operations.delete(event.callId);
|
|
1698
|
-
},
|
|
1699
|
-
onEnd: finishOperation,
|
|
1700
|
-
onError(event) {
|
|
1701
|
-
const errorEvent = eventObject(event);
|
|
1702
|
-
const callId = readString(errorEvent.callId);
|
|
1703
|
-
if (callId === void 0) return;
|
|
1704
|
-
const cause = errorEvent.error ?? event;
|
|
1705
|
-
finishOpenModelSpans(callId, cause, modelSpans, instrumentation.tracer);
|
|
1706
|
-
finishOpenToolSpans(callId, cause, toolSpans, instrumentation.tracer);
|
|
1707
|
-
const state = operations.get(callId);
|
|
1708
|
-
if (!state) return;
|
|
1709
|
-
state.span.fail(cause);
|
|
1710
|
-
operations.delete(callId);
|
|
1711
|
-
},
|
|
1712
|
-
executeLanguageModelCall(options) {
|
|
1713
|
-
const state = modelSpans.get(options.callId)?.find((candidate) => candidate.span === void 0);
|
|
1714
|
-
if (!state) return options.execute();
|
|
1715
|
-
return instrumentation.tracer.openSpan(state.spanSpec.name, state.spanSpec.attributes, (span) => {
|
|
1716
|
-
state.span = span;
|
|
1717
|
-
try {
|
|
1718
|
-
return Promise.resolve(options.execute()).catch((cause) => {
|
|
1719
|
-
writeSpanAttributes(span, aiGatewayLogAttributes(extractAIGatewayLogId(cause)));
|
|
1720
|
-
span.fail(cause);
|
|
1721
|
-
removeModelState(modelSpans, options.callId, state);
|
|
1722
|
-
throw cause;
|
|
1723
|
-
});
|
|
1724
|
-
} catch (cause) {
|
|
1725
|
-
writeSpanAttributes(span, aiGatewayLogAttributes(extractAIGatewayLogId(cause)));
|
|
1726
|
-
span.fail(cause);
|
|
1727
|
-
removeModelState(modelSpans, options.callId, state);
|
|
1728
|
-
throw cause;
|
|
1729
|
-
}
|
|
1730
|
-
});
|
|
1731
|
-
},
|
|
1732
|
-
executeTool(options) {
|
|
1733
|
-
const state = toolSpans.get(toolSpanKey(options.callId, options.toolCallId));
|
|
1734
|
-
if (!state) return options.execute();
|
|
1735
|
-
return instrumentation.tracer.openSpan(state.spanSpec.name, state.spanSpec.attributes, (span) => {
|
|
1736
|
-
state.span = span;
|
|
1737
|
-
try {
|
|
1738
|
-
return Promise.resolve(options.execute()).catch((cause) => {
|
|
1739
|
-
span.fail(cause);
|
|
1740
|
-
toolSpans.delete(toolSpanKey(options.callId, options.toolCallId));
|
|
1741
|
-
throw cause;
|
|
1742
|
-
});
|
|
1743
|
-
} catch (cause) {
|
|
1744
|
-
span.fail(cause);
|
|
1745
|
-
toolSpans.delete(toolSpanKey(options.callId, options.toolCallId));
|
|
1746
|
-
throw cause;
|
|
1747
|
-
}
|
|
1748
|
-
});
|
|
1749
|
-
}
|
|
1750
|
-
};
|
|
1751
|
-
}
|
|
1752
|
-
function supportedOperationName(operationName) {
|
|
1753
|
-
if (operationName === "generateObject" || operationName === "generateText" || operationName === "streamObject" || operationName === "streamText") return operationName;
|
|
1754
|
-
}
|
|
1755
|
-
function isStreamOperation(operationName) {
|
|
1756
|
-
return operationName === "streamObject" || operationName === "streamText";
|
|
1757
|
-
}
|
|
1758
|
-
function shiftModelSpan(spansByCallId, callId) {
|
|
1759
|
-
const spans = spansByCallId.get(callId);
|
|
1760
|
-
const span = spans?.shift();
|
|
1761
|
-
if (spans && spans.length === 0) spansByCallId.delete(callId);
|
|
1762
|
-
return span;
|
|
1763
|
-
}
|
|
1764
|
-
function removeModelState(statesByCallId, callId, state) {
|
|
1765
|
-
const states = statesByCallId.get(callId);
|
|
1766
|
-
if (!states) return;
|
|
1767
|
-
const index = states.indexOf(state);
|
|
1768
|
-
if (index !== -1) states.splice(index, 1);
|
|
1769
|
-
if (states.length === 0) statesByCallId.delete(callId);
|
|
1770
|
-
}
|
|
1771
|
-
function finishOpenModelSpans(callId, cause, spansByCallId, tracer) {
|
|
1772
|
-
const states = spansByCallId.get(callId);
|
|
1773
|
-
if (!states) return;
|
|
1774
|
-
for (const state of states) {
|
|
1775
|
-
const span = state.span ?? tracer.openSpan(state.spanSpec.name, state.spanSpec.attributes, (activeSpan) => activeSpan);
|
|
1776
|
-
if (cause === void 0) span.finish();
|
|
1777
|
-
else span.fail(cause);
|
|
1778
|
-
}
|
|
1779
|
-
spansByCallId.delete(callId);
|
|
1780
|
-
}
|
|
1781
|
-
function finishOpenToolSpans(callId, cause, spansByToolCallId, tracer) {
|
|
1782
|
-
for (const [toolCallId, state] of spansByToolCallId) {
|
|
1783
|
-
if (state.callId !== callId) continue;
|
|
1784
|
-
const span = state.span ?? tracer.openSpan(state.spanSpec.name, state.spanSpec.attributes, (activeSpan) => activeSpan);
|
|
1785
|
-
if (cause === void 0) span.finish();
|
|
1786
|
-
else span.fail(cause);
|
|
1787
|
-
spansByToolCallId.delete(toolCallId);
|
|
1788
|
-
}
|
|
1789
|
-
}
|
|
1790
|
-
function eventObject(event) {
|
|
1791
|
-
return typeof event === "object" && event !== null ? event : {};
|
|
1792
|
-
}
|
|
1793
|
-
const SEMANTIC_CONTEXT_KEYS = /* @__PURE__ */ new Set([
|
|
1794
|
-
"agentId",
|
|
1795
|
-
"agentName",
|
|
1796
|
-
"agentVersion",
|
|
1797
|
-
"conversationId",
|
|
1798
|
-
"gen_ai.agent.id",
|
|
1799
|
-
"gen_ai.agent.name",
|
|
1800
|
-
"gen_ai.agent.version",
|
|
1801
|
-
"gen_ai.conversation.id"
|
|
1802
|
-
]);
|
|
1803
|
-
function runtimeContextAttributes(runtimeContextValue) {
|
|
1804
|
-
const attributes = {};
|
|
1805
|
-
const runtimeContext = recordValue(runtimeContextValue);
|
|
1806
|
-
for (const [key, value] of Object.entries(runtimeContext ?? {})) if (!SEMANTIC_CONTEXT_KEYS.has(key) && isScalarAttributeValue(value)) attributes[key.startsWith("cloudflare.agents.") ? key : `cloudflare.agents.runtime_context.${key}`] = value;
|
|
1807
|
-
return attributes;
|
|
1808
|
-
}
|
|
1809
|
-
function toolContextAttributes(toolName, toolContextValue) {
|
|
1810
|
-
const attributes = {};
|
|
1811
|
-
const toolContext = recordValue(toolContextValue);
|
|
1812
|
-
for (const [key, value] of Object.entries(toolContext ?? {})) if (isScalarAttributeValue(value)) attributes[`cloudflare.agents.tool_context.${toolName}.${key}`] = value;
|
|
1813
|
-
return attributes;
|
|
1814
|
-
}
|
|
1815
|
-
function recordValue(value) {
|
|
1816
|
-
return typeof value === "object" && value !== null ? value : void 0;
|
|
1817
|
-
}
|
|
1818
|
-
function isScalarAttributeValue(value) {
|
|
1819
|
-
return typeof value === "string" || typeof value === "number" || typeof value === "boolean";
|
|
1586
|
+
function contextAttributes(params, options) {
|
|
1587
|
+
const included = options?.includeRuntimeContext;
|
|
1588
|
+
if (included === void 0 || included.length === 0) return;
|
|
1589
|
+
const runtimeContext = runtimeContextRecord(params);
|
|
1590
|
+
if (runtimeContext === void 0) return;
|
|
1591
|
+
const projected = {};
|
|
1592
|
+
for (const key of included) if (Object.hasOwn(runtimeContext, key)) projected[key] = runtimeContext[key];
|
|
1593
|
+
return metadataAttributes(projected, TraceAttribute.Cloudflare.RuntimeContextPrefix);
|
|
1594
|
+
}
|
|
1595
|
+
const invocationBounded = Symbol.for("cloudflare.agents.ai-sdk.invocation-bounded");
|
|
1596
|
+
function isAISDKInvocationBounded(params) {
|
|
1597
|
+
return params[invocationBounded] === true || __DO_NOT_USE_WILL_BREAK__agentContext.getStore()?.connection !== void 0;
|
|
1820
1598
|
}
|
|
1821
1599
|
//#endregion
|
|
1822
1600
|
//#region src/observability/ai/index.ts
|
|
1823
|
-
const agentsAISDKTelemetryBrand = Symbol.for("cloudflare.agents.ai-sdk-telemetry");
|
|
1824
1601
|
/**
|
|
1825
1602
|
* Wraps an AI SDK namespace with tracing.
|
|
1826
1603
|
*/
|
|
1827
1604
|
function wrapAISDK(ai, options = {}) {
|
|
1828
|
-
return
|
|
1829
|
-
options,
|
|
1830
|
-
tracer
|
|
1831
|
-
});
|
|
1832
|
-
}
|
|
1833
|
-
/**
|
|
1834
|
-
* Creates an AI SDK v7 telemetry adapter for use with `registerTelemetry` or
|
|
1835
|
-
* per-call telemetry configuration.
|
|
1836
|
-
*/
|
|
1837
|
-
function createAISDKTelemetry(options = {}) {
|
|
1838
|
-
const telemetry = createAISDKV7Telemetry({
|
|
1605
|
+
return createAISDKWrapper(ai, {
|
|
1839
1606
|
options,
|
|
1840
1607
|
tracer
|
|
1841
1608
|
});
|
|
1842
|
-
Object.defineProperty(telemetry, agentsAISDKTelemetryBrand, { value: true });
|
|
1843
|
-
return telemetry;
|
|
1844
1609
|
}
|
|
1845
1610
|
//#endregion
|
|
1846
|
-
export {
|
|
1611
|
+
export { wrapAISDK };
|
|
1847
1612
|
|
|
1848
1613
|
//# sourceMappingURL=index.js.map
|