raindrop-ai 0.2.4 → 0.3.0-otelv2
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 +135 -0
- package/dist/{chunk-XLEPN7ZO.mjs → chunk-5Y3LWBKV.mjs} +1033 -96
- package/dist/index-DBdUrUXc.d.mts +1457 -0
- package/dist/index-DBdUrUXc.d.ts +1457 -0
- package/dist/index.d.mts +5 -1180
- package/dist/index.d.ts +5 -1180
- package/dist/index.js +1111 -116
- package/dist/index.mjs +60 -1
- package/dist/tracing/index.d.mts +3 -1
- package/dist/tracing/index.d.ts +3 -1
- package/dist/tracing/index.js +1037 -101
- package/dist/tracing/index.mjs +1 -1
- package/package.json +9 -7
|
@@ -4,7 +4,7 @@ if (!globalThis.RAINDROP_ASYNC_LOCAL_STORAGE) {
|
|
|
4
4
|
globalThis.RAINDROP_ASYNC_LOCAL_STORAGE = AsyncLocalStorage;
|
|
5
5
|
}
|
|
6
6
|
|
|
7
|
-
// ../core/dist/chunk-
|
|
7
|
+
// ../core/dist/chunk-YUIXRQWN.js
|
|
8
8
|
function normalizeFeatureFlags(input) {
|
|
9
9
|
if (Array.isArray(input)) {
|
|
10
10
|
return Object.fromEntries(input.map((name) => [name, "true"]));
|
|
@@ -13,6 +13,293 @@ function normalizeFeatureFlags(input) {
|
|
|
13
13
|
Object.entries(input).map(([k, v]) => [k, String(v)])
|
|
14
14
|
);
|
|
15
15
|
}
|
|
16
|
+
function getCrypto() {
|
|
17
|
+
const c = globalThis.crypto;
|
|
18
|
+
return c;
|
|
19
|
+
}
|
|
20
|
+
function randomBytes(length) {
|
|
21
|
+
const cryptoObj = getCrypto();
|
|
22
|
+
const out = new Uint8Array(length);
|
|
23
|
+
if (cryptoObj && typeof cryptoObj.getRandomValues === "function") {
|
|
24
|
+
cryptoObj.getRandomValues(out);
|
|
25
|
+
return out;
|
|
26
|
+
}
|
|
27
|
+
for (let i = 0; i < out.length; i++) out[i] = Math.floor(Math.random() * 256);
|
|
28
|
+
return out;
|
|
29
|
+
}
|
|
30
|
+
function randomUUID() {
|
|
31
|
+
const cryptoObj = getCrypto();
|
|
32
|
+
if (cryptoObj && typeof cryptoObj.randomUUID === "function") {
|
|
33
|
+
return cryptoObj.randomUUID();
|
|
34
|
+
}
|
|
35
|
+
const b = randomBytes(16);
|
|
36
|
+
b[6] = b[6] & 15 | 64;
|
|
37
|
+
b[8] = b[8] & 63 | 128;
|
|
38
|
+
const hex = [...b].map((x) => x.toString(16).padStart(2, "0")).join("");
|
|
39
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
40
|
+
}
|
|
41
|
+
var HANDOFF_ATTRIBUTE_SUFFIXES = {
|
|
42
|
+
mode: "raindrop.handoff.mode",
|
|
43
|
+
childEventId: "raindrop.handoff.childEventId",
|
|
44
|
+
parentEventId: "raindrop.handoff.parentEventId",
|
|
45
|
+
parentSpanId: "raindrop.handoff.parentSpanId",
|
|
46
|
+
name: "raindrop.handoff.name",
|
|
47
|
+
terminal: "raindrop.handoff.terminal",
|
|
48
|
+
agentRole: "raindrop.agent.role"
|
|
49
|
+
};
|
|
50
|
+
var AI_SDK_METADATA_PREFIX = "ai.telemetry.metadata.";
|
|
51
|
+
var DETACHED_MODE = "detached";
|
|
52
|
+
var SUBAGENT_AGENT_ROLE = "subagent";
|
|
53
|
+
var HANDOFF_TERMINAL_CANCELLED = "cancelled";
|
|
54
|
+
var TOOL_EVENTS_SUFFIX = "raindrop.toolEvents";
|
|
55
|
+
var TOOL_EVENTS_ALLOW = "allow";
|
|
56
|
+
var OPERATION_ID_ATTRIBUTE = "ai.operationId";
|
|
57
|
+
var SUBAGENT_ABORT_OPERATION_ID = "ai.subagent.failed";
|
|
58
|
+
var SUBAGENT_ABORT_SPAN_NAME = SUBAGENT_ABORT_OPERATION_ID;
|
|
59
|
+
var DEFAULT_DISPATCH_SPAN_NAME = "launch_subagent";
|
|
60
|
+
var HANDOFF_TERMINAL_PROPERTY_KEY = HANDOFF_ATTRIBUTE_SUFFIXES.terminal;
|
|
61
|
+
function defined(entries) {
|
|
62
|
+
const out = {};
|
|
63
|
+
for (const [key, value] of Object.entries(entries)) {
|
|
64
|
+
if (typeof value === "string" && value.trim().length > 0) out[key] = value;
|
|
65
|
+
}
|
|
66
|
+
return out;
|
|
67
|
+
}
|
|
68
|
+
function detachedDispatchMetadata(dispatch) {
|
|
69
|
+
return defined({
|
|
70
|
+
[HANDOFF_ATTRIBUTE_SUFFIXES.mode]: DETACHED_MODE,
|
|
71
|
+
[HANDOFF_ATTRIBUTE_SUFFIXES.childEventId]: dispatch.childEventId,
|
|
72
|
+
[HANDOFF_ATTRIBUTE_SUFFIXES.name]: dispatch.name
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
function detachedChildMetadata(link) {
|
|
76
|
+
return defined({
|
|
77
|
+
[HANDOFF_ATTRIBUTE_SUFFIXES.parentEventId]: link.parentEventId,
|
|
78
|
+
[HANDOFF_ATTRIBUTE_SUFFIXES.parentSpanId]: link.parentSpanId,
|
|
79
|
+
[HANDOFF_ATTRIBUTE_SUFFIXES.name]: link.name,
|
|
80
|
+
[HANDOFF_ATTRIBUTE_SUFFIXES.agentRole]: SUBAGENT_AGENT_ROLE
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
function cancelledTerminalMetadata() {
|
|
84
|
+
return { [HANDOFF_ATTRIBUTE_SUFFIXES.terminal]: HANDOFF_TERMINAL_CANCELLED };
|
|
85
|
+
}
|
|
86
|
+
function cancelledTerminalAttributes() {
|
|
87
|
+
const bare = cancelledTerminalMetadata();
|
|
88
|
+
return { ...bare, ...withMetadataPrefix(bare) };
|
|
89
|
+
}
|
|
90
|
+
function toolEventsAllowMetadata() {
|
|
91
|
+
return { [TOOL_EVENTS_SUFFIX]: TOOL_EVENTS_ALLOW };
|
|
92
|
+
}
|
|
93
|
+
function withMetadataPrefix(metadata) {
|
|
94
|
+
const out = {};
|
|
95
|
+
for (const [key, value] of Object.entries(metadata)) {
|
|
96
|
+
out[key.startsWith(AI_SDK_METADATA_PREFIX) ? key : `${AI_SDK_METADATA_PREFIX}${key}`] = value;
|
|
97
|
+
}
|
|
98
|
+
return out;
|
|
99
|
+
}
|
|
100
|
+
var BAGGAGE_KEYS = {
|
|
101
|
+
eventId: "raindrop-event-id",
|
|
102
|
+
childEventId: "raindrop-child-event-id",
|
|
103
|
+
name: "raindrop-handoff-name",
|
|
104
|
+
convoId: "raindrop-convo-id",
|
|
105
|
+
userId: "raindrop-user-id",
|
|
106
|
+
// The dispatch span and its trace, carried as fields as well as in
|
|
107
|
+
// `traceparent`. Not redundant: OTel's HTTP instrumentations rewrite
|
|
108
|
+
// `traceparent` from whatever span is current as the request goes out, so by
|
|
109
|
+
// the time the child reads it the ids name the HTTP client span and its trace
|
|
110
|
+
// instead of the dispatch. The child would then point its reverse reference at
|
|
111
|
+
// a span the launcher's UI does not know as a dispatch. A field states which
|
|
112
|
+
// span it means rather than meaning "whoever wrote this header last".
|
|
113
|
+
dispatchSpanId: "raindrop-dispatch-span-id",
|
|
114
|
+
traceId: "raindrop-trace-id"
|
|
115
|
+
};
|
|
116
|
+
var TRACEPARENT_HEADER = "traceparent";
|
|
117
|
+
var BAGGAGE_HEADER = "baggage";
|
|
118
|
+
var W3C_TRACE_ID = /^[0-9a-f]{32}$/;
|
|
119
|
+
var W3C_SPAN_ID = /^[0-9a-f]{16}$/;
|
|
120
|
+
function isW3CTraceId(value) {
|
|
121
|
+
return value !== void 0 && W3C_TRACE_ID.test(value);
|
|
122
|
+
}
|
|
123
|
+
function isW3CSpanId(value) {
|
|
124
|
+
return value !== void 0 && W3C_SPAN_ID.test(value);
|
|
125
|
+
}
|
|
126
|
+
var warnedNonConformantIds = false;
|
|
127
|
+
function warnNonConformantIdsOnce(traceId, spanId) {
|
|
128
|
+
if (warnedNonConformantIds) return;
|
|
129
|
+
warnedNonConformantIds = true;
|
|
130
|
+
console.warn(
|
|
131
|
+
`[raindrop] hand-off carrier ids are not W3C-shaped (traceId="${traceId}", spanId="${spanId}"). The child still reports as its own event, but its reverse reference names a span id the backend does not store, so the link back to the launcher will not resolve. A LangSmith carrier reads this way \u2014 its run ids are UUIDs; propagate Raindrop's own carrier (toHeaders) to link.`
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
var HANDOFF_HEADER = "x-raindrop-handoff";
|
|
135
|
+
var LANGSMITH_TRACE_HEADER = "langsmith-trace";
|
|
136
|
+
var LANGSMITH_METADATA_BAGGAGE_KEY = "langsmith-metadata";
|
|
137
|
+
function encodeBaggage(entries) {
|
|
138
|
+
return Object.entries(entries).filter((entry) => Boolean(entry[1])).map(([key, value]) => `${key}=${encodeURIComponent(value)}`).join(",");
|
|
139
|
+
}
|
|
140
|
+
function decodeBaggage(header) {
|
|
141
|
+
if (!header) return {};
|
|
142
|
+
const parsed = {};
|
|
143
|
+
for (const part of header.split(",")) {
|
|
144
|
+
const [rawKey, ...rest] = part.split("=");
|
|
145
|
+
if (!rawKey || rest.length === 0) continue;
|
|
146
|
+
const key = rawKey.trim();
|
|
147
|
+
let value;
|
|
148
|
+
try {
|
|
149
|
+
value = decodeURIComponent(rest.join("=").trim());
|
|
150
|
+
} catch (e) {
|
|
151
|
+
value = rest.join("=").trim();
|
|
152
|
+
}
|
|
153
|
+
if (key in parsed && parsed[key] !== value) throw new ConflictingCarrierError(key);
|
|
154
|
+
parsed[key] = value;
|
|
155
|
+
}
|
|
156
|
+
return parsed;
|
|
157
|
+
}
|
|
158
|
+
function carrierBaggage(carrier) {
|
|
159
|
+
return {
|
|
160
|
+
[BAGGAGE_KEYS.eventId]: carrier.eventId,
|
|
161
|
+
[BAGGAGE_KEYS.childEventId]: carrier.childEventId,
|
|
162
|
+
[BAGGAGE_KEYS.name]: carrier.name,
|
|
163
|
+
[BAGGAGE_KEYS.convoId]: carrier.convoId,
|
|
164
|
+
[BAGGAGE_KEYS.userId]: carrier.userId,
|
|
165
|
+
[BAGGAGE_KEYS.dispatchSpanId]: carrier.spanId,
|
|
166
|
+
[BAGGAGE_KEYS.traceId]: carrier.traceId
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
function toHeaders(carrier) {
|
|
170
|
+
const fields = encodeBaggage(carrierBaggage(carrier));
|
|
171
|
+
const headers = {
|
|
172
|
+
[BAGGAGE_HEADER]: fields,
|
|
173
|
+
[HANDOFF_HEADER]: fields
|
|
174
|
+
};
|
|
175
|
+
if (isW3CTraceId(carrier.traceId) && isW3CSpanId(carrier.spanId)) {
|
|
176
|
+
headers[TRACEPARENT_HEADER] = `00-${carrier.traceId}-${carrier.spanId}-01`;
|
|
177
|
+
}
|
|
178
|
+
return headers;
|
|
179
|
+
}
|
|
180
|
+
function langsmithStamp() {
|
|
181
|
+
return (/* @__PURE__ */ new Date()).toISOString().replace(/[-:]/g, "").replace(/\.\d+Z$/, "000000");
|
|
182
|
+
}
|
|
183
|
+
function toLangSmithHeaders(carrier) {
|
|
184
|
+
const stamp = langsmithStamp();
|
|
185
|
+
const dottedOrder = `${stamp}Z${carrier.traceId}.${stamp}Z${carrier.spanId}`;
|
|
186
|
+
return {
|
|
187
|
+
[LANGSMITH_TRACE_HEADER]: dottedOrder,
|
|
188
|
+
[BAGGAGE_HEADER]: encodeBaggage({
|
|
189
|
+
[LANGSMITH_METADATA_BAGGAGE_KEY]: JSON.stringify(carrierBaggage(carrier))
|
|
190
|
+
}),
|
|
191
|
+
// Carried here too: a LangSmith-shaped carrier is rewritten in transit
|
|
192
|
+
// exactly like the native one, and an injected `traceparent` is preferred
|
|
193
|
+
// over the dotted order on read.
|
|
194
|
+
[HANDOFF_HEADER]: encodeBaggage(carrierBaggage(carrier))
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
function isHeaderLookup(headers) {
|
|
198
|
+
return "get" in headers && typeof headers.get === "function";
|
|
199
|
+
}
|
|
200
|
+
var ConflictingCarrierError = class extends Error {
|
|
201
|
+
};
|
|
202
|
+
function readHeader(headers, name) {
|
|
203
|
+
var _a, _b, _c;
|
|
204
|
+
if (isHeaderLookup(headers)) return (_a = headers.get(name)) != null ? _a : void 0;
|
|
205
|
+
const value = (_c = headers[name]) != null ? _c : (_b = Object.entries(headers).find(([key]) => key.toLowerCase() === name)) == null ? void 0 : _b[1];
|
|
206
|
+
if (!Array.isArray(value)) return value;
|
|
207
|
+
const distinct = new Set(value);
|
|
208
|
+
if (distinct.size > 1) throw new ConflictingCarrierError(name);
|
|
209
|
+
return value[0];
|
|
210
|
+
}
|
|
211
|
+
function readTraceHeader(headers, name) {
|
|
212
|
+
const value = readHeader(headers, name);
|
|
213
|
+
if (!(value == null ? void 0 : value.includes(","))) return value;
|
|
214
|
+
const copies = value.split(",").map((copy) => copy.trim());
|
|
215
|
+
if (new Set(copies).size > 1) throw new ConflictingCarrierError(name);
|
|
216
|
+
return copies[0];
|
|
217
|
+
}
|
|
218
|
+
function fromHeaders(headers) {
|
|
219
|
+
var _a, _b;
|
|
220
|
+
let baggage;
|
|
221
|
+
let handoff;
|
|
222
|
+
let traceparent;
|
|
223
|
+
let langsmithTrace;
|
|
224
|
+
try {
|
|
225
|
+
baggage = decodeBaggage(readHeader(headers, BAGGAGE_HEADER));
|
|
226
|
+
handoff = decodeBaggage(readHeader(headers, HANDOFF_HEADER));
|
|
227
|
+
traceparent = readTraceHeader(headers, TRACEPARENT_HEADER);
|
|
228
|
+
langsmithTrace = readTraceHeader(headers, LANGSMITH_TRACE_HEADER);
|
|
229
|
+
} catch (error) {
|
|
230
|
+
if (error instanceof ConflictingCarrierError) return null;
|
|
231
|
+
throw error;
|
|
232
|
+
}
|
|
233
|
+
const langsmithMetadata = baggage[LANGSMITH_METADATA_BAGGAGE_KEY];
|
|
234
|
+
const nested = langsmithMetadata ? safeParseStringRecord(langsmithMetadata) : {};
|
|
235
|
+
for (const [key, value] of Object.entries(nested)) {
|
|
236
|
+
if (key in baggage && baggage[key] !== value) return null;
|
|
237
|
+
}
|
|
238
|
+
const fields = {
|
|
239
|
+
...baggage,
|
|
240
|
+
...nested,
|
|
241
|
+
// Last, so it wins: the standard headers may have been rewritten in transit
|
|
242
|
+
// by a propagator, this one is only ever written by us.
|
|
243
|
+
...handoff
|
|
244
|
+
};
|
|
245
|
+
let traceId;
|
|
246
|
+
let spanId;
|
|
247
|
+
if (traceparent) {
|
|
248
|
+
const [, parsedTraceId, parsedSpanId] = traceparent.split("-");
|
|
249
|
+
if (isW3CTraceId(parsedTraceId) && isW3CSpanId(parsedSpanId)) {
|
|
250
|
+
traceId = parsedTraceId;
|
|
251
|
+
spanId = parsedSpanId;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
if (!traceId && !spanId && langsmithTrace) {
|
|
255
|
+
const segments = langsmithTrace.split(".");
|
|
256
|
+
traceId = (_a = segments[0]) == null ? void 0 : _a.split("Z").pop();
|
|
257
|
+
spanId = (_b = segments[segments.length - 1]) == null ? void 0 : _b.split("Z").pop();
|
|
258
|
+
}
|
|
259
|
+
traceId = fields[BAGGAGE_KEYS.traceId] || traceId;
|
|
260
|
+
spanId = fields[BAGGAGE_KEYS.dispatchSpanId] || spanId;
|
|
261
|
+
const eventId = fields[BAGGAGE_KEYS.eventId];
|
|
262
|
+
if (!traceId || !spanId || !eventId) return null;
|
|
263
|
+
if (!isW3CTraceId(traceId) || !isW3CSpanId(spanId)) {
|
|
264
|
+
warnNonConformantIdsOnce(traceId, spanId);
|
|
265
|
+
}
|
|
266
|
+
const carrier = { traceId, spanId, eventId };
|
|
267
|
+
const childEventId = fields[BAGGAGE_KEYS.childEventId];
|
|
268
|
+
if (childEventId) carrier.childEventId = childEventId;
|
|
269
|
+
const name = fields[BAGGAGE_KEYS.name];
|
|
270
|
+
if (name) carrier.name = name;
|
|
271
|
+
const convoId = fields[BAGGAGE_KEYS.convoId];
|
|
272
|
+
if (convoId) carrier.convoId = convoId;
|
|
273
|
+
const userId = fields[BAGGAGE_KEYS.userId];
|
|
274
|
+
if (userId) carrier.userId = userId;
|
|
275
|
+
return carrier;
|
|
276
|
+
}
|
|
277
|
+
var UNRESOLVED_CARRIER_WARNING = `[raindrop] resume: headers were supplied but no hand-off carrier was found on them (searched ${HANDOFF_HEADER}, ${TRACEPARENT_HEADER}, ${BAGGAGE_HEADER}). This run reports as an UNLINKED event, and the launcher that dispatched it will stay on "queued" forever because nothing ever references its child. Passing headers means a parent handed off, so no carrier on them is almost certainly a defect: a gateway or proxy stripping ${HANDOFF_HEADER}, a middleware overwriting ${BAGGAGE_HEADER}, a request forwarded without its headers, or a hand-built carrier whose field names do not match. Send every header the launcher's dispatch returned. Logged once per process; pass no headers for a directly-invoked sub-agent to silence it.`;
|
|
278
|
+
var UNRESOLVED_CARRIER_WARNED_KEY = /* @__PURE__ */ Symbol.for("raindrop.handoff.warnedUnresolvedCarrier");
|
|
279
|
+
function warnedHolder() {
|
|
280
|
+
return globalThis;
|
|
281
|
+
}
|
|
282
|
+
function warnUnresolvedCarrier(headers, carrier) {
|
|
283
|
+
if (carrier) return;
|
|
284
|
+
if (!headers) return;
|
|
285
|
+
const holder = warnedHolder();
|
|
286
|
+
if (holder[UNRESOLVED_CARRIER_WARNED_KEY]) return;
|
|
287
|
+
holder[UNRESOLVED_CARRIER_WARNED_KEY] = true;
|
|
288
|
+
console.warn(UNRESOLVED_CARRIER_WARNING);
|
|
289
|
+
}
|
|
290
|
+
function safeParseStringRecord(raw) {
|
|
291
|
+
try {
|
|
292
|
+
const parsed = JSON.parse(raw);
|
|
293
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) return {};
|
|
294
|
+
const result = {};
|
|
295
|
+
for (const [key, value] of Object.entries(parsed)) {
|
|
296
|
+
if (typeof value === "string") result[key] = value;
|
|
297
|
+
}
|
|
298
|
+
return result;
|
|
299
|
+
} catch (e) {
|
|
300
|
+
return {};
|
|
301
|
+
}
|
|
302
|
+
}
|
|
16
303
|
function runWithTracingSuppressed(fn) {
|
|
17
304
|
const hook = globalThis.RAINDROP_SUPPRESS_TRACING;
|
|
18
305
|
if (typeof hook !== "function") return fn();
|
|
@@ -394,6 +681,62 @@ function projectIdHeaders(projectId) {
|
|
|
394
681
|
}
|
|
395
682
|
var SHUTDOWN_DEADLINE_MS = 1e4;
|
|
396
683
|
var POST_SHUTDOWN_TIMEOUT_MS = 5e3;
|
|
684
|
+
var MODEL_USAGE_ATTRIBUTES = {
|
|
685
|
+
providerName: "gen_ai.provider.name",
|
|
686
|
+
requestModel: "gen_ai.request.model",
|
|
687
|
+
responseModel: "gen_ai.response.model",
|
|
688
|
+
inputTokens: "gen_ai.usage.input_tokens",
|
|
689
|
+
outputTokens: "gen_ai.usage.output_tokens",
|
|
690
|
+
reasoningTokens: "gen_ai.usage.reasoning_tokens",
|
|
691
|
+
cacheReadInputTokens: "gen_ai.usage.cache_read_input_tokens",
|
|
692
|
+
cacheWriteInputTokens: "gen_ai.usage.cache_write_input_tokens",
|
|
693
|
+
nonCachedInputTokens: "raindrop.usage.input_tokens.non_cached",
|
|
694
|
+
nonReasoningOutputTokens: "raindrop.usage.output_tokens.non_reasoning",
|
|
695
|
+
totalOutputTokens: "raindrop.usage.output_tokens.total",
|
|
696
|
+
reasoningOutputTokens: "raindrop.usage.output_tokens.reasoning",
|
|
697
|
+
cacheReadTokens: "raindrop.usage.input_tokens.cache_read",
|
|
698
|
+
cacheWriteTokens: "raindrop.usage.input_tokens.cache_write"
|
|
699
|
+
};
|
|
700
|
+
var STRING_ALIASES = {
|
|
701
|
+
[MODEL_USAGE_ATTRIBUTES.requestModel]: ["ai.model.id", "ai.model"]
|
|
702
|
+
};
|
|
703
|
+
var NUMBER_ALIASES = {
|
|
704
|
+
[MODEL_USAGE_ATTRIBUTES.inputTokens]: [
|
|
705
|
+
"gen_ai.usage.prompt_tokens",
|
|
706
|
+
"ai.usage.prompt_tokens",
|
|
707
|
+
"ai.usage.promptTokens",
|
|
708
|
+
"ai.usage.input_tokens",
|
|
709
|
+
"ai.usage.inputTokens"
|
|
710
|
+
],
|
|
711
|
+
[MODEL_USAGE_ATTRIBUTES.outputTokens]: [
|
|
712
|
+
"gen_ai.usage.completion_tokens",
|
|
713
|
+
"ai.usage.completion_tokens",
|
|
714
|
+
"ai.usage.completionTokens",
|
|
715
|
+
"ai.usage.output_tokens",
|
|
716
|
+
"ai.usage.outputTokens"
|
|
717
|
+
],
|
|
718
|
+
[MODEL_USAGE_ATTRIBUTES.reasoningTokens]: [
|
|
719
|
+
"gen_ai.usage.reasoning.output_tokens",
|
|
720
|
+
"ai.usage.reasoningTokens",
|
|
721
|
+
"ai.usage.thoughts_tokens"
|
|
722
|
+
],
|
|
723
|
+
[MODEL_USAGE_ATTRIBUTES.cacheReadInputTokens]: [
|
|
724
|
+
"gen_ai.usage.cache_read_tokens",
|
|
725
|
+
"gen_ai.usage.cache_read.input_tokens",
|
|
726
|
+
"ai.usage.cachedInputTokens",
|
|
727
|
+
"ai.usage.cached_tokens",
|
|
728
|
+
"ai.usage.cache_read_tokens",
|
|
729
|
+
"ai.usage.cache_read_input_tokens",
|
|
730
|
+
"ai.usage.cacheReadInputTokens"
|
|
731
|
+
],
|
|
732
|
+
[MODEL_USAGE_ATTRIBUTES.cacheWriteInputTokens]: [
|
|
733
|
+
"gen_ai.usage.cache_creation_input_tokens",
|
|
734
|
+
"gen_ai.usage.cache_creation.input_tokens",
|
|
735
|
+
"ai.usage.cacheWriteInputTokens",
|
|
736
|
+
"ai.usage.cache_creation_input_tokens",
|
|
737
|
+
"ai.usage.cacheCreationInputTokens"
|
|
738
|
+
]
|
|
739
|
+
};
|
|
397
740
|
var DEFAULT_REDACT_ATTRIBUTE_KEYS = [
|
|
398
741
|
"ai.request.providerOptions",
|
|
399
742
|
"ai.response.providerMetadata"
|
|
@@ -408,8 +751,10 @@ function getWeakRefCtor() {
|
|
|
408
751
|
}
|
|
409
752
|
var MAX_BINDING_STACK = 128;
|
|
410
753
|
var EMPTY_STACK = [];
|
|
411
|
-
var
|
|
412
|
-
|
|
754
|
+
var ContextStackStorage = class {
|
|
755
|
+
/** Emitted once when this runtime cannot support an ambient push. */
|
|
756
|
+
constructor(noAmbientBindWarning) {
|
|
757
|
+
this.noAmbientBindWarning = noAmbientBindWarning;
|
|
413
758
|
this._als = null;
|
|
414
759
|
this._enterWithUsable = false;
|
|
415
760
|
this._syncStack = EMPTY_STACK;
|
|
@@ -506,17 +851,17 @@ var RoutingContextStorage = class {
|
|
|
506
851
|
warnNoAmbientBindOnce() {
|
|
507
852
|
if (this._warnedNoAmbientBind) return;
|
|
508
853
|
this._warnedNoAmbientBind = true;
|
|
509
|
-
console.warn(
|
|
510
|
-
"[raindrop] This runtime's AsyncLocalStorage does not implement enterWith (e.g. Cloudflare Workers), so begin()/finish() cannot scope auto-instrumented spans to a project without cross-contaminating concurrent requests. Manual events and explicit span routing are unaffected; use asCurrent()/withSpan (run-based) to route auto-instrumented spans on this runtime."
|
|
511
|
-
);
|
|
854
|
+
console.warn(this.noAmbientBindWarning);
|
|
512
855
|
}
|
|
513
856
|
};
|
|
857
|
+
var NO_AMBIENT_ROUTING_BIND_WARNING = "[raindrop] This runtime's AsyncLocalStorage does not implement enterWith (e.g. Cloudflare Workers), so begin()/finish() cannot scope auto-instrumented spans to a project without cross-contaminating concurrent requests. Manual events and explicit span routing are unaffected; use asCurrent()/withSpan (run-based) to route auto-instrumented spans on this runtime.";
|
|
858
|
+
var NO_AMBIENT_ATTRIBUTE_BIND_WARNING = "[raindrop] This runtime's AsyncLocalStorage does not implement enterWith (e.g. Cloudflare Workers), so contextual span attributes (the detached sub-agent hand-off reference) cannot be bound ambiently without cross-contaminating concurrent requests. The child's own event still reports; only the per-span reverse reference is skipped.";
|
|
514
859
|
var ROUTING_STORAGE_KEY = /* @__PURE__ */ Symbol.for("raindrop.tracing.routingContextStorage");
|
|
515
860
|
function routingStorage() {
|
|
516
861
|
const holder = globalThis;
|
|
517
862
|
let storage = holder[ROUTING_STORAGE_KEY];
|
|
518
863
|
if (!storage) {
|
|
519
|
-
storage = new
|
|
864
|
+
storage = new ContextStackStorage(NO_AMBIENT_ROUTING_BIND_WARNING);
|
|
520
865
|
holder[ROUTING_STORAGE_KEY] = storage;
|
|
521
866
|
}
|
|
522
867
|
return storage;
|
|
@@ -564,6 +909,104 @@ function currentRoutingBinding() {
|
|
|
564
909
|
function isBoundContext(value) {
|
|
565
910
|
return typeof value.isLive === "function";
|
|
566
911
|
}
|
|
912
|
+
var ATTRIBUTE_STORAGE_KEY = /* @__PURE__ */ Symbol.for("raindrop.tracing.spanAttributeStorage");
|
|
913
|
+
function attributeStorage() {
|
|
914
|
+
const holder = globalThis;
|
|
915
|
+
let storage = holder[ATTRIBUTE_STORAGE_KEY];
|
|
916
|
+
if (!storage) {
|
|
917
|
+
storage = new ContextStackStorage(NO_AMBIENT_ATTRIBUTE_BIND_WARNING);
|
|
918
|
+
holder[ATTRIBUTE_STORAGE_KEY] = storage;
|
|
919
|
+
}
|
|
920
|
+
return storage;
|
|
921
|
+
}
|
|
922
|
+
function makeAttributeFrame(attributes, owner) {
|
|
923
|
+
const snapshot = { ...attributes };
|
|
924
|
+
const WeakRefCtor = owner !== void 0 ? getWeakRefCtor() : void 0;
|
|
925
|
+
const ref = WeakRefCtor && owner !== void 0 ? new WeakRefCtor(owner) : void 0;
|
|
926
|
+
const owned = /* @__PURE__ */ new WeakSet();
|
|
927
|
+
let dead = false;
|
|
928
|
+
let sawError = false;
|
|
929
|
+
return {
|
|
930
|
+
attributes: snapshot,
|
|
931
|
+
sawErrorSpan: () => sawError,
|
|
932
|
+
ownsSpan: (span) => owned.has(span),
|
|
933
|
+
claimSpan(span) {
|
|
934
|
+
owned.add(span);
|
|
935
|
+
},
|
|
936
|
+
markErrorSpanSeen() {
|
|
937
|
+
sawError = true;
|
|
938
|
+
},
|
|
939
|
+
// A frame whose owner has been collected is dead even though nothing
|
|
940
|
+
// unbound it: a run that never settles must not keep handing its reverse
|
|
941
|
+
// reference to whatever runs next on a reused context.
|
|
942
|
+
isLive() {
|
|
943
|
+
if (dead) return false;
|
|
944
|
+
return ref === void 0 || ref.deref() !== void 0;
|
|
945
|
+
},
|
|
946
|
+
markDead() {
|
|
947
|
+
dead = true;
|
|
948
|
+
}
|
|
949
|
+
};
|
|
950
|
+
}
|
|
951
|
+
function bindSpanAttributes(attributes, owner) {
|
|
952
|
+
if (Object.keys(attributes).length === 0) return void 0;
|
|
953
|
+
const frame = makeAttributeFrame(attributes, owner);
|
|
954
|
+
try {
|
|
955
|
+
attributeStorage().push(frame);
|
|
956
|
+
} catch (e) {
|
|
957
|
+
return void 0;
|
|
958
|
+
}
|
|
959
|
+
return frame;
|
|
960
|
+
}
|
|
961
|
+
function unbindSpanAttributes(frame) {
|
|
962
|
+
if (!frame || !isAttributeFrame(frame)) return;
|
|
963
|
+
try {
|
|
964
|
+
attributeStorage().remove(frame);
|
|
965
|
+
} catch (e) {
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
function applySpanAttributeFrames(span, setAttribute) {
|
|
969
|
+
try {
|
|
970
|
+
for (const frame of attributeStorage().getStack()) {
|
|
971
|
+
if (!frame.isLive()) continue;
|
|
972
|
+
frame.claimSpan(span);
|
|
973
|
+
for (const [key, value] of Object.entries(frame.attributes)) {
|
|
974
|
+
setAttribute(key, value);
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
} catch (e) {
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
function claimSpanForFrames(span) {
|
|
981
|
+
try {
|
|
982
|
+
for (const frame of attributeStorage().getStack()) {
|
|
983
|
+
if (frame.isLive()) frame.claimSpan(span);
|
|
984
|
+
}
|
|
985
|
+
} catch (e) {
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
function currentSpanAttributes() {
|
|
989
|
+
const merged = {};
|
|
990
|
+
try {
|
|
991
|
+
for (const frame of attributeStorage().getStack()) {
|
|
992
|
+
if (!frame.isLive()) continue;
|
|
993
|
+
Object.assign(merged, frame.attributes);
|
|
994
|
+
}
|
|
995
|
+
} catch (e) {
|
|
996
|
+
}
|
|
997
|
+
return merged;
|
|
998
|
+
}
|
|
999
|
+
function markErrorSpanSeen(span) {
|
|
1000
|
+
try {
|
|
1001
|
+
for (const frame of attributeStorage().getStack()) {
|
|
1002
|
+
if (frame.ownsSpan(span)) frame.markErrorSpanSeen();
|
|
1003
|
+
}
|
|
1004
|
+
} catch (e) {
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
function isAttributeFrame(value) {
|
|
1008
|
+
return typeof value.isLive === "function";
|
|
1009
|
+
}
|
|
567
1010
|
|
|
568
1011
|
// ../core/dist/index.node.js
|
|
569
1012
|
import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
|
|
@@ -597,7 +1040,7 @@ installTracingSuppressionHook();
|
|
|
597
1040
|
// package.json
|
|
598
1041
|
var package_default = {
|
|
599
1042
|
name: "raindrop-ai",
|
|
600
|
-
version: "0.
|
|
1043
|
+
version: "0.3.0",
|
|
601
1044
|
main: "dist/index.js",
|
|
602
1045
|
module: "dist/index.mjs",
|
|
603
1046
|
types: "dist/index.d.ts",
|
|
@@ -717,6 +1160,7 @@ var PROJECT_ID_SPAN_ATTRIBUTE = "raindrop.project_id";
|
|
|
717
1160
|
var AUTH_HINT_SPAN_ATTRIBUTE = "raindrop.auth_hint";
|
|
718
1161
|
var EXPORT_SUCCESS = 0;
|
|
719
1162
|
var EXPORT_FAILED = 1;
|
|
1163
|
+
var SPAN_STATUS_ERROR = 2;
|
|
720
1164
|
function authHintForKey(writeKey) {
|
|
721
1165
|
if (!writeKey) return void 0;
|
|
722
1166
|
return createHash("sha256").update(writeKey, "utf8").digest("hex").slice(0, 8);
|
|
@@ -779,16 +1223,29 @@ function stampSpan(span, projectId, authHint) {
|
|
|
779
1223
|
} catch (e) {
|
|
780
1224
|
}
|
|
781
1225
|
}
|
|
1226
|
+
function stampContextAttributes(span) {
|
|
1227
|
+
try {
|
|
1228
|
+
applySpanAttributeFrames(span, (key, value) => span.setAttribute(key, value));
|
|
1229
|
+
} catch (e) {
|
|
1230
|
+
}
|
|
1231
|
+
}
|
|
782
1232
|
var RaindropContextSpanProcessor = class {
|
|
783
1233
|
onStart(span) {
|
|
784
1234
|
try {
|
|
1235
|
+
stampContextAttributes(span);
|
|
785
1236
|
const bound = currentRoutingBinding();
|
|
786
1237
|
if (!bound) return;
|
|
787
1238
|
stampSpan(span, bound.projectId, bound.authHint);
|
|
788
1239
|
} catch (e) {
|
|
789
1240
|
}
|
|
790
1241
|
}
|
|
791
|
-
onEnd() {
|
|
1242
|
+
onEnd(span) {
|
|
1243
|
+
var _a;
|
|
1244
|
+
try {
|
|
1245
|
+
if (((_a = span == null ? void 0 : span.status) == null ? void 0 : _a.code) !== SPAN_STATUS_ERROR) return;
|
|
1246
|
+
markErrorSpanSeen(span);
|
|
1247
|
+
} catch (e) {
|
|
1248
|
+
}
|
|
792
1249
|
}
|
|
793
1250
|
async forceFlush() {
|
|
794
1251
|
}
|
|
@@ -895,7 +1352,7 @@ function traceloopConfiguredWithoutClaim() {
|
|
|
895
1352
|
}
|
|
896
1353
|
|
|
897
1354
|
// src/tracing/tracer-core.ts
|
|
898
|
-
import { context as
|
|
1355
|
+
import { context as context5, SpanStatusCode as SpanStatusCode5, trace as trace5 } from "@opentelemetry/api";
|
|
899
1356
|
import { OTLPTraceExporter as OTLPHttpTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
|
|
900
1357
|
import { AnthropicInstrumentation } from "@traceloop/instrumentation-anthropic";
|
|
901
1358
|
import { BedrockInstrumentation } from "@traceloop/instrumentation-bedrock";
|
|
@@ -1005,12 +1462,12 @@ async function postJson2(url, body, headers, opts) {
|
|
|
1005
1462
|
}
|
|
1006
1463
|
|
|
1007
1464
|
// src/tracing/direct/ids.ts
|
|
1008
|
-
function
|
|
1465
|
+
function getCrypto2() {
|
|
1009
1466
|
const c = globalThis.crypto;
|
|
1010
1467
|
return c;
|
|
1011
1468
|
}
|
|
1012
1469
|
function randomBytes2(length) {
|
|
1013
|
-
const cryptoObj =
|
|
1470
|
+
const cryptoObj = getCrypto2();
|
|
1014
1471
|
const out = new Uint8Array(length);
|
|
1015
1472
|
if (cryptoObj && typeof cryptoObj.getRandomValues === "function") {
|
|
1016
1473
|
cryptoObj.getRandomValues(out);
|
|
@@ -1048,7 +1505,7 @@ function base64Encode2(bytes) {
|
|
|
1048
1505
|
}
|
|
1049
1506
|
return out;
|
|
1050
1507
|
}
|
|
1051
|
-
function
|
|
1508
|
+
function base64ToHex2(base64) {
|
|
1052
1509
|
const maybeBuffer = globalThis.Buffer;
|
|
1053
1510
|
if (maybeBuffer) {
|
|
1054
1511
|
return maybeBuffer.from(base64, "base64").toString("hex");
|
|
@@ -1215,9 +1672,18 @@ var DirectTraceShipper = class {
|
|
|
1215
1672
|
attrString2(`traceloop.association.properties.${key}`, serializeAssociationValue(value))
|
|
1216
1673
|
);
|
|
1217
1674
|
}
|
|
1675
|
+
const spanIdentity = { spanIdB64 };
|
|
1676
|
+
claimSpanForFrames(spanIdentity);
|
|
1677
|
+
for (const [key, value] of Object.entries({
|
|
1678
|
+
...currentSpanAttributes(),
|
|
1679
|
+
...params.attributes
|
|
1680
|
+
})) {
|
|
1681
|
+
attributes.push(attrString2(key, value));
|
|
1682
|
+
}
|
|
1218
1683
|
let status;
|
|
1219
1684
|
if (params.error) {
|
|
1220
1685
|
status = { code: 2, message: params.error };
|
|
1686
|
+
markErrorSpanSeen(spanIdentity);
|
|
1221
1687
|
} else {
|
|
1222
1688
|
status = {
|
|
1223
1689
|
code: 1
|
|
@@ -1367,23 +1833,311 @@ var DirectTraceShipper = class {
|
|
|
1367
1833
|
|
|
1368
1834
|
// src/tracing/liveInteraction.ts
|
|
1369
1835
|
import {
|
|
1370
|
-
context as
|
|
1836
|
+
context as context3,
|
|
1371
1837
|
isValidSpanId as isValidSpanId2,
|
|
1372
|
-
|
|
1838
|
+
isValidTraceId as isValidTraceId2,
|
|
1839
|
+
SpanStatusCode as SpanStatusCode3,
|
|
1373
1840
|
TraceFlags,
|
|
1374
|
-
trace as
|
|
1841
|
+
trace as trace3
|
|
1375
1842
|
} from "@opentelemetry/api";
|
|
1376
1843
|
import * as traceloop from "@traceloop/node-server-sdk";
|
|
1377
|
-
|
|
1844
|
+
|
|
1845
|
+
// src/tracing/subagents.ts
|
|
1846
|
+
import { SpanStatusCode as SpanStatusCode2, context as context2, trace as trace2 } from "@opentelemetry/api";
|
|
1847
|
+
var DEFAULT_SUBAGENT_NAME = "subagent";
|
|
1848
|
+
var DISPATCH_ACCEPTED = "accepted";
|
|
1849
|
+
function reasonText(reason) {
|
|
1850
|
+
var _a;
|
|
1851
|
+
if (reason instanceof Error) return `${reason.name}: ${reason.message}`;
|
|
1852
|
+
if (typeof reason === "string") return reason;
|
|
1853
|
+
if (reason === void 0 || reason === null) return "";
|
|
1854
|
+
try {
|
|
1855
|
+
return (_a = JSON.stringify(reason)) != null ? _a : String(reason);
|
|
1856
|
+
} catch (e) {
|
|
1857
|
+
return String(reason);
|
|
1858
|
+
}
|
|
1859
|
+
}
|
|
1860
|
+
function stampCurrentSpan(attributes) {
|
|
1861
|
+
try {
|
|
1862
|
+
const span = trace2.getSpan(context2.active());
|
|
1863
|
+
if (!(span == null ? void 0 : span.isRecording())) return;
|
|
1864
|
+
for (const [key, value] of Object.entries(attributes)) {
|
|
1865
|
+
span.setAttribute(key, value);
|
|
1866
|
+
}
|
|
1867
|
+
} catch (e) {
|
|
1868
|
+
}
|
|
1869
|
+
}
|
|
1870
|
+
function stampOwnSpan(attributes, frame) {
|
|
1871
|
+
if (!frame) return;
|
|
1872
|
+
try {
|
|
1873
|
+
const span = trace2.getSpan(context2.active());
|
|
1874
|
+
if (!(span == null ? void 0 : span.isRecording()) || !frame.ownsSpan(span)) return;
|
|
1875
|
+
for (const [key, value] of Object.entries(attributes)) {
|
|
1876
|
+
span.setAttribute(key, value);
|
|
1877
|
+
}
|
|
1878
|
+
} catch (e) {
|
|
1879
|
+
}
|
|
1880
|
+
}
|
|
1881
|
+
function failOwnSpan(reason, frame) {
|
|
1882
|
+
try {
|
|
1883
|
+
const span = trace2.getSpan(context2.active());
|
|
1884
|
+
if (!(span == null ? void 0 : span.isRecording())) return false;
|
|
1885
|
+
if (!(frame == null ? void 0 : frame.ownsSpan(span))) return false;
|
|
1886
|
+
const message = reasonText(reason);
|
|
1887
|
+
span.setStatus({ code: SpanStatusCode2.ERROR, message });
|
|
1888
|
+
span.recordException(reason instanceof Error ? reason : new Error(message));
|
|
1889
|
+
return true;
|
|
1890
|
+
} catch (e) {
|
|
1891
|
+
return false;
|
|
1892
|
+
}
|
|
1893
|
+
}
|
|
1894
|
+
function allowToolEvents(interaction) {
|
|
1895
|
+
const attributes = withMetadataPrefix(toolEventsAllowMetadata());
|
|
1896
|
+
interaction.bindToolEventsOptIn(attributes);
|
|
1897
|
+
stampCurrentSpan(attributes);
|
|
1898
|
+
}
|
|
1899
|
+
function recordSubagentDispatch(interaction, options) {
|
|
1900
|
+
var _a;
|
|
1901
|
+
const childEventId = options.childEventId || randomUUID();
|
|
1902
|
+
const name = options.name || DEFAULT_SUBAGENT_NAME;
|
|
1903
|
+
const parentEventId = (_a = interaction.getEventId()) != null ? _a : "";
|
|
1904
|
+
let traceIdHex;
|
|
1905
|
+
let dispatchSpanId;
|
|
1906
|
+
try {
|
|
1907
|
+
allowToolEvents(interaction);
|
|
1908
|
+
const recorded = interaction.recordDispatchSpan({
|
|
1909
|
+
name: options.toolName || DEFAULT_DISPATCH_SPAN_NAME,
|
|
1910
|
+
input: options.input,
|
|
1911
|
+
// A launch's "result" is the job handle; the answer belongs to the
|
|
1912
|
+
// child's event and never appears here.
|
|
1913
|
+
output: { jobId: childEventId, status: DISPATCH_ACCEPTED },
|
|
1914
|
+
properties: options.properties,
|
|
1915
|
+
attributes: detachedDispatchMetadata({ childEventId, name })
|
|
1916
|
+
});
|
|
1917
|
+
traceIdHex = recorded.traceIdHex;
|
|
1918
|
+
dispatchSpanId = recorded.spanIdHex;
|
|
1919
|
+
if (!dispatchSpanId) {
|
|
1920
|
+
console.warn(
|
|
1921
|
+
`[raindrop] subagent(${name}): no dispatch span was recorded, so event ${parentEventId} will not show the launch and NO carrier is returned \u2014 there is no span for the child to reference. Pass childEventId (${childEventId}) to the worker yourself if it must still report under that id. Tracing must be enabled for dispatches to be recorded.`
|
|
1922
|
+
);
|
|
1923
|
+
}
|
|
1924
|
+
} catch (error) {
|
|
1925
|
+
console.warn(
|
|
1926
|
+
`[raindrop] subagent(${name}) failed to record the dispatch; returning a carrier so the child still links back.`,
|
|
1927
|
+
error instanceof Error ? error.message : error
|
|
1928
|
+
);
|
|
1929
|
+
}
|
|
1930
|
+
if (!traceIdHex || !dispatchSpanId) {
|
|
1931
|
+
return {
|
|
1932
|
+
childEventId,
|
|
1933
|
+
name,
|
|
1934
|
+
parentEventId,
|
|
1935
|
+
dispatchSpanId: void 0,
|
|
1936
|
+
carrier: null,
|
|
1937
|
+
headers: {},
|
|
1938
|
+
langsmithHeaders: {}
|
|
1939
|
+
};
|
|
1940
|
+
}
|
|
1941
|
+
const carrier = {
|
|
1942
|
+
traceId: traceIdHex,
|
|
1943
|
+
spanId: dispatchSpanId,
|
|
1944
|
+
eventId: parentEventId,
|
|
1945
|
+
childEventId,
|
|
1946
|
+
name,
|
|
1947
|
+
...interaction.getConvoId() ? { convoId: interaction.getConvoId() } : {},
|
|
1948
|
+
...interaction.getUserId() ? { userId: interaction.getUserId() } : {}
|
|
1949
|
+
};
|
|
1950
|
+
return {
|
|
1951
|
+
childEventId,
|
|
1952
|
+
name,
|
|
1953
|
+
parentEventId,
|
|
1954
|
+
dispatchSpanId,
|
|
1955
|
+
carrier,
|
|
1956
|
+
headers: toHeaders(carrier),
|
|
1957
|
+
langsmithHeaders: toLangSmithHeaders(carrier)
|
|
1958
|
+
};
|
|
1959
|
+
}
|
|
1960
|
+
function carrierFirst(local, carried, field) {
|
|
1961
|
+
if (carried && local && local !== carried) {
|
|
1962
|
+
console.warn(
|
|
1963
|
+
`[raindrop] resumeSubagent: ignoring ${field}="${local}" in favor of the launcher's "${carried}" from the carrier; the launcher allocated it, and a child that substitutes its own breaks the link back to it.`
|
|
1964
|
+
);
|
|
1965
|
+
}
|
|
1966
|
+
return carried || local;
|
|
1967
|
+
}
|
|
1968
|
+
function isTraceCarrier(value) {
|
|
1969
|
+
const candidate = value;
|
|
1970
|
+
return typeof candidate.traceId === "string" && typeof candidate.spanId === "string" && typeof candidate.eventId === "string";
|
|
1971
|
+
}
|
|
1972
|
+
function readCarrier(carrierOrHeaders) {
|
|
1973
|
+
if (!carrierOrHeaders) return null;
|
|
1974
|
+
try {
|
|
1975
|
+
if (isTraceCarrier(carrierOrHeaders)) return carrierOrHeaders;
|
|
1976
|
+
return fromHeaders(carrierOrHeaders);
|
|
1977
|
+
} catch (error) {
|
|
1978
|
+
console.warn(
|
|
1979
|
+
`[raindrop] resumeSubagent: ignoring an unreadable hand-off carrier; the run reports as an unlinked event.`,
|
|
1980
|
+
error instanceof Error ? error.message : error
|
|
1981
|
+
);
|
|
1982
|
+
return null;
|
|
1983
|
+
}
|
|
1984
|
+
}
|
|
1985
|
+
function resolveSubagentResume(carrierOrHeaders, options = {}) {
|
|
1986
|
+
const parentStated = options.parent !== void 0;
|
|
1987
|
+
const parent = parentStated ? options.parent : readCarrier(carrierOrHeaders);
|
|
1988
|
+
if (!parentStated) warnUnresolvedCarrier(carrierOrHeaders, parent);
|
|
1989
|
+
const name = carrierFirst(options.name, parent == null ? void 0 : parent.name, "name") || DEFAULT_SUBAGENT_NAME;
|
|
1990
|
+
const eventId = carrierFirst(options.eventId, parent == null ? void 0 : parent.childEventId, "eventId") || randomUUID();
|
|
1991
|
+
const userId = carrierFirst(options.userId, parent == null ? void 0 : parent.userId, "userId") || "";
|
|
1992
|
+
const convoId = carrierFirst(options.convoId, parent == null ? void 0 : parent.convoId, "convoId");
|
|
1993
|
+
const link = parent ? {
|
|
1994
|
+
parentEventId: parent.eventId,
|
|
1995
|
+
...parent.spanId ? { parentSpanId: parent.spanId } : {},
|
|
1996
|
+
name
|
|
1997
|
+
} : null;
|
|
1998
|
+
return {
|
|
1999
|
+
parent,
|
|
2000
|
+
link,
|
|
2001
|
+
name,
|
|
2002
|
+
eventId,
|
|
2003
|
+
event: options.event || `subagent.${name}`,
|
|
2004
|
+
userId,
|
|
2005
|
+
...convoId ? { convoId } : {}
|
|
2006
|
+
};
|
|
2007
|
+
}
|
|
2008
|
+
function bindSubagentLink(link, owner) {
|
|
2009
|
+
if (!link) return void 0;
|
|
2010
|
+
return bindSpanAttributes(detachedChildMetadata(link), owner);
|
|
2011
|
+
}
|
|
2012
|
+
var LiveSubagentRun = class {
|
|
2013
|
+
constructor(params) {
|
|
2014
|
+
this.settled = false;
|
|
2015
|
+
this.interaction = params.interaction;
|
|
2016
|
+
this.eventId = params.eventId;
|
|
2017
|
+
this.name = params.name;
|
|
2018
|
+
this.convoId = params.convoId;
|
|
2019
|
+
this.parent = params.parent;
|
|
2020
|
+
this.frame = params.frame;
|
|
2021
|
+
this.frameOwner = params.frameOwner;
|
|
2022
|
+
}
|
|
2023
|
+
get linked() {
|
|
2024
|
+
return this.parent !== null;
|
|
2025
|
+
}
|
|
2026
|
+
async finish(result) {
|
|
2027
|
+
await this.close(result);
|
|
2028
|
+
}
|
|
2029
|
+
async fail(reason) {
|
|
2030
|
+
await this.abort(reason);
|
|
2031
|
+
}
|
|
2032
|
+
async cancel(reason) {
|
|
2033
|
+
const text = reason === void 0 ? "cancelled by caller" : reasonText(reason);
|
|
2034
|
+
const cancelled = cancelledTerminalAttributes();
|
|
2035
|
+
stampOwnSpan(cancelled, this.frame);
|
|
2036
|
+
await this.close({
|
|
2037
|
+
output: `Cancelled: ${text}`,
|
|
2038
|
+
// The caller's view of the child is its EVENT, not its spans, so the
|
|
2039
|
+
// marker has to be a property of the event as well.
|
|
2040
|
+
properties: { [HANDOFF_TERMINAL_PROPERTY_KEY]: HANDOFF_TERMINAL_CANCELLED }
|
|
2041
|
+
});
|
|
2042
|
+
}
|
|
2043
|
+
/**
|
|
2044
|
+
* Close the run with an abort reason, having first made sure the failure is
|
|
2045
|
+
* visible on a span.
|
|
2046
|
+
*
|
|
2047
|
+
* A failure has to leave an errored span behind or it is derived as finished.
|
|
2048
|
+
* Mark whatever is open; if nothing is and the child has not errored a span
|
|
2049
|
+
* already, record one — the killed-before-it-reached-the-model case, which is
|
|
2050
|
+
* much of what aborting is.
|
|
2051
|
+
*/
|
|
2052
|
+
async abort(reason) {
|
|
2053
|
+
var _a, _b, _c;
|
|
2054
|
+
const text = reasonText(reason);
|
|
2055
|
+
if (!this.settled && !failOwnSpan(reason, this.frame)) {
|
|
2056
|
+
if (!((_a = this.frame) == null ? void 0 : _a.sawErrorSpan())) {
|
|
2057
|
+
this.interaction.recordSubagentAbortSpan({
|
|
2058
|
+
reason,
|
|
2059
|
+
message: text,
|
|
2060
|
+
attributes: {
|
|
2061
|
+
[HANDOFF_ATTRIBUTE_SUFFIXES.name]: this.name,
|
|
2062
|
+
// The name explicitly, not only via the frame: an UNLINKED run has
|
|
2063
|
+
// no frame, and since the span is named for the operation
|
|
2064
|
+
// (`ai.subagent.failed`) rather than the sub-agent, this attribute
|
|
2065
|
+
// is the only thing left that says which sub-agent failed.
|
|
2066
|
+
...(_c = (_b = this.frame) == null ? void 0 : _b.attributes) != null ? _c : {}
|
|
2067
|
+
}
|
|
2068
|
+
});
|
|
2069
|
+
}
|
|
2070
|
+
}
|
|
2071
|
+
await this.close({ output: text ? `Aborted: ${text}` : "Aborted before producing a result." });
|
|
2072
|
+
}
|
|
2073
|
+
async close(result) {
|
|
2074
|
+
if (this.settled) return;
|
|
2075
|
+
this.settled = true;
|
|
2076
|
+
try {
|
|
2077
|
+
await this.interaction.finish({ ...result, eventId: this.eventId });
|
|
2078
|
+
} finally {
|
|
2079
|
+
unbindSpanAttributes(this.frame);
|
|
2080
|
+
this.frame = void 0;
|
|
2081
|
+
this.frameOwner = void 0;
|
|
2082
|
+
}
|
|
2083
|
+
}
|
|
2084
|
+
/**
|
|
2085
|
+
* Run `fn` and guarantee the run reports an outcome, mirroring the Python
|
|
2086
|
+
* SDK's `with rd.resume_subagent(...) as run:`.
|
|
2087
|
+
*
|
|
2088
|
+
* A run that leaves without reporting output is closed with its abort reason.
|
|
2089
|
+
* That is not cosmetic: an event is created only from a run with non-empty
|
|
2090
|
+
* output, so a child that dies silently produces no event at all and the
|
|
2091
|
+
* caller's launch stays `queued` forever — indistinguishable from a job that
|
|
2092
|
+
* never started.
|
|
2093
|
+
*/
|
|
2094
|
+
async use(fn) {
|
|
2095
|
+
try {
|
|
2096
|
+
const result = await fn(this);
|
|
2097
|
+
if (!this.settled) {
|
|
2098
|
+
console.warn(
|
|
2099
|
+
`[raindrop] sub-agent run ${this.eventId} (${this.name}) ended without reporting output; closing it with an abort reason. An event needs non-empty output to exist, so a silent child leaves its launcher showing "queued" forever \u2014 call finish({ output }), fail(reason) or cancel() before returning.`
|
|
2100
|
+
);
|
|
2101
|
+
await this.abortQuietly("sub-agent ended without reporting output");
|
|
2102
|
+
}
|
|
2103
|
+
return result;
|
|
2104
|
+
} catch (error) {
|
|
2105
|
+
await this.abortQuietly(error);
|
|
2106
|
+
throw error;
|
|
2107
|
+
}
|
|
2108
|
+
}
|
|
2109
|
+
/**
|
|
2110
|
+
* {@link abort} for the outcomes this class reports on the caller's behalf.
|
|
2111
|
+
*
|
|
2112
|
+
* `abort()` ends in `interaction.finish()`, which awaits the terminal event
|
|
2113
|
+
* POST and rejects when it fails. An explicit `fail()` may surface that — the
|
|
2114
|
+
* caller awaited it — but an implicit one must not: rejecting on the error
|
|
2115
|
+
* path replaces the caller's exception with a telemetry one, and rejecting on
|
|
2116
|
+
* the success path loses a result `fn` already produced.
|
|
2117
|
+
*/
|
|
2118
|
+
async abortQuietly(reason) {
|
|
2119
|
+
try {
|
|
2120
|
+
await this.abort(reason);
|
|
2121
|
+
} catch (error) {
|
|
2122
|
+
console.warn(
|
|
2123
|
+
`[raindrop] sub-agent run ${this.eventId} (${this.name}) could not report its outcome; the run itself is unaffected.`,
|
|
2124
|
+
error instanceof Error ? error.message : error
|
|
2125
|
+
);
|
|
2126
|
+
}
|
|
2127
|
+
}
|
|
2128
|
+
};
|
|
2129
|
+
|
|
2130
|
+
// src/tracing/liveInteraction.ts
|
|
2131
|
+
function getPropertiesFromContext(context6) {
|
|
1378
2132
|
const properties = {
|
|
1379
|
-
...
|
|
1380
|
-
...
|
|
1381
|
-
...
|
|
1382
|
-
...
|
|
1383
|
-
...
|
|
2133
|
+
...context6.userId && { user_id: context6.userId },
|
|
2134
|
+
...context6.convoId && { convo_id: context6.convoId },
|
|
2135
|
+
...context6.eventId && { event_id: context6.eventId },
|
|
2136
|
+
...context6.event && { event_name: context6.event },
|
|
2137
|
+
...context6.properties || {}
|
|
1384
2138
|
};
|
|
1385
|
-
if (
|
|
1386
|
-
properties.attachments = stringifyBounded(
|
|
2139
|
+
if (context6.attachments && context6.attachments.length > 0) {
|
|
2140
|
+
properties.attachments = stringifyBounded(context6.attachments);
|
|
1387
2141
|
}
|
|
1388
2142
|
return properties;
|
|
1389
2143
|
}
|
|
@@ -1403,12 +2157,12 @@ function serializeAssociationProperties(properties) {
|
|
|
1403
2157
|
function serializeSpanValue(value) {
|
|
1404
2158
|
return stringifyBounded(value);
|
|
1405
2159
|
}
|
|
1406
|
-
function getLocalDebuggerMetadata(
|
|
2160
|
+
function getLocalDebuggerMetadata(context6) {
|
|
1407
2161
|
return {
|
|
1408
|
-
...
|
|
1409
|
-
...
|
|
1410
|
-
...
|
|
1411
|
-
...
|
|
2162
|
+
...context6.eventId ? { eventId: context6.eventId } : {},
|
|
2163
|
+
...context6.event ? { eventName: context6.event } : {},
|
|
2164
|
+
...context6.userId ? { userId: context6.userId } : {},
|
|
2165
|
+
...context6.convoId ? { convoId: context6.convoId } : {}
|
|
1412
2166
|
};
|
|
1413
2167
|
}
|
|
1414
2168
|
var LiveToolSpan = class {
|
|
@@ -1432,13 +2186,13 @@ var LiveToolSpan = class {
|
|
|
1432
2186
|
setError(error) {
|
|
1433
2187
|
this.hasError = true;
|
|
1434
2188
|
this.errorMessage = error instanceof Error ? error.message : String(error);
|
|
1435
|
-
this.span.setStatus({ code:
|
|
2189
|
+
this.span.setStatus({ code: SpanStatusCode3.ERROR, message: this.errorMessage });
|
|
1436
2190
|
this.span.recordException(error instanceof Error ? error : new Error(this.errorMessage));
|
|
1437
2191
|
}
|
|
1438
2192
|
end() {
|
|
1439
2193
|
var _a;
|
|
1440
2194
|
if (!this.hasError) {
|
|
1441
|
-
this.span.setStatus({ code:
|
|
2195
|
+
this.span.setStatus({ code: SpanStatusCode3.OK });
|
|
1442
2196
|
}
|
|
1443
2197
|
this.span.end();
|
|
1444
2198
|
(_a = this.emitLiveEvent) == null ? void 0 : _a.call(this, {
|
|
@@ -1509,10 +2263,10 @@ var DirectToolSpan = class {
|
|
|
1509
2263
|
}
|
|
1510
2264
|
};
|
|
1511
2265
|
var LiveInteraction = class {
|
|
1512
|
-
constructor(
|
|
1513
|
-
this.context =
|
|
2266
|
+
constructor(context6, traceId, analytics, directShipper, routing) {
|
|
2267
|
+
this.context = context6;
|
|
1514
2268
|
this.analytics = analytics;
|
|
1515
|
-
this.tracer =
|
|
2269
|
+
this.tracer = trace3.getTracer("traceloop.tracer");
|
|
1516
2270
|
this.traceId = traceId;
|
|
1517
2271
|
this.directShipper = directShipper;
|
|
1518
2272
|
this.routing = routing;
|
|
@@ -1547,7 +2301,7 @@ var LiveInteraction = class {
|
|
|
1547
2301
|
this.traceId = explicitTraceId;
|
|
1548
2302
|
return explicitTraceId;
|
|
1549
2303
|
}
|
|
1550
|
-
const activeSpan =
|
|
2304
|
+
const activeSpan = trace3.getSpan(context3.active());
|
|
1551
2305
|
const activeTraceId = activeSpan == null ? void 0 : activeSpan.spanContext().traceId;
|
|
1552
2306
|
if (activeTraceId) {
|
|
1553
2307
|
this.traceId = activeTraceId;
|
|
@@ -1590,17 +2344,17 @@ var LiveInteraction = class {
|
|
|
1590
2344
|
const spanIdB64 = base64Encode2(randomBytes2(8));
|
|
1591
2345
|
const startTimeMs = Date.now();
|
|
1592
2346
|
const spanContext = {
|
|
1593
|
-
traceId:
|
|
1594
|
-
spanId:
|
|
2347
|
+
traceId: base64ToHex2(traceIdB64),
|
|
2348
|
+
spanId: base64ToHex2(spanIdB64),
|
|
1595
2349
|
traceFlags: TraceFlags.SAMPLED
|
|
1596
2350
|
};
|
|
1597
|
-
const wrappedContext =
|
|
1598
|
-
|
|
1599
|
-
|
|
2351
|
+
const wrappedContext = trace3.setSpan(
|
|
2352
|
+
context3.active(),
|
|
2353
|
+
trace3.wrapSpanContext(spanContext)
|
|
1600
2354
|
);
|
|
1601
2355
|
const invoke = () => this.routing ? withRoutingContext(this.routing, () => fn.apply(thisArg, args)) : fn.apply(thisArg, args);
|
|
1602
2356
|
try {
|
|
1603
|
-
const result = await
|
|
2357
|
+
const result = await context3.with(wrappedContext, invoke);
|
|
1604
2358
|
const endTimeMs = Date.now();
|
|
1605
2359
|
this.directShipper.sendSpan({
|
|
1606
2360
|
name: taskName,
|
|
@@ -1635,7 +2389,7 @@ var LiveInteraction = class {
|
|
|
1635
2389
|
}
|
|
1636
2390
|
}
|
|
1637
2391
|
const wrappedFn = (...fnArgs) => {
|
|
1638
|
-
const activeSpan =
|
|
2392
|
+
const activeSpan = trace3.getSpan(context3.active());
|
|
1639
2393
|
const activeTraceId = activeSpan == null ? void 0 : activeSpan.spanContext().traceId;
|
|
1640
2394
|
if (activeTraceId) {
|
|
1641
2395
|
this.traceId = activeTraceId;
|
|
@@ -1685,19 +2439,19 @@ var LiveInteraction = class {
|
|
|
1685
2439
|
metadata: params.inputParameters ? { args: params.inputParameters } : void 0
|
|
1686
2440
|
});
|
|
1687
2441
|
const spanContext = {
|
|
1688
|
-
traceId:
|
|
1689
|
-
spanId:
|
|
2442
|
+
traceId: base64ToHex2(traceIdB64),
|
|
2443
|
+
spanId: base64ToHex2(spanIdB64),
|
|
1690
2444
|
traceFlags: TraceFlags.SAMPLED
|
|
1691
2445
|
};
|
|
1692
|
-
const wrappedContext =
|
|
1693
|
-
|
|
1694
|
-
|
|
2446
|
+
const wrappedContext = trace3.setSpan(
|
|
2447
|
+
context3.active(),
|
|
2448
|
+
trace3.wrapSpanContext(spanContext)
|
|
1695
2449
|
);
|
|
1696
2450
|
const invoke = () => this.routing ? withRoutingContext(this.routing, () => fn.apply(thisArg, args)) : fn.apply(thisArg, args);
|
|
1697
2451
|
let result;
|
|
1698
2452
|
let error;
|
|
1699
2453
|
try {
|
|
1700
|
-
result = await
|
|
2454
|
+
result = await context3.with(wrappedContext, invoke);
|
|
1701
2455
|
} catch (err) {
|
|
1702
2456
|
error = err instanceof Error ? err.message : String(err);
|
|
1703
2457
|
const endTimeMs2 = Date.now();
|
|
@@ -1762,7 +2516,7 @@ var LiveInteraction = class {
|
|
|
1762
2516
|
const inputParams = params.inputParameters ? [params.inputParameters] : void 0;
|
|
1763
2517
|
const emitLive = (event) => this.emitLiveEvent(event);
|
|
1764
2518
|
const wrapped = async (...wrappedArgs) => {
|
|
1765
|
-
const activeSpan =
|
|
2519
|
+
const activeSpan = trace3.getSpan(context3.active());
|
|
1766
2520
|
const ctx = activeSpan == null ? void 0 : activeSpan.spanContext();
|
|
1767
2521
|
const liveSpanId = ctx && isValidSpanId2(ctx.spanId) ? ctx.spanId : void 0;
|
|
1768
2522
|
if (activeSpan) {
|
|
@@ -1965,10 +2719,151 @@ var LiveInteraction = class {
|
|
|
1965
2719
|
getEventId() {
|
|
1966
2720
|
return this.context.eventId;
|
|
1967
2721
|
}
|
|
2722
|
+
getUserId() {
|
|
2723
|
+
return this.context.userId;
|
|
2724
|
+
}
|
|
2725
|
+
getConvoId() {
|
|
2726
|
+
return this.context.convoId;
|
|
2727
|
+
}
|
|
2728
|
+
/**
|
|
2729
|
+
* Bind this turn's tool-call event opt-in, once.
|
|
2730
|
+
*
|
|
2731
|
+
* Owned by the interaction rather than by the execution context: the frame
|
|
2732
|
+
* lives exactly as long as the turn does (removed by `finish()`, and dead once
|
|
2733
|
+
* this interaction is collected), instead of outliving it on a context the
|
|
2734
|
+
* host may reuse for unrelated work.
|
|
2735
|
+
*/
|
|
2736
|
+
bindToolEventsOptIn(attributes) {
|
|
2737
|
+
if (this.toolEventsFrame) return;
|
|
2738
|
+
this.toolEventsFrame = bindSpanAttributes(attributes, this);
|
|
2739
|
+
}
|
|
2740
|
+
subagent(options) {
|
|
2741
|
+
return recordSubagentDispatch(this, options);
|
|
2742
|
+
}
|
|
2743
|
+
/**
|
|
2744
|
+
* Record the tool span a detached dispatch is written on.
|
|
2745
|
+
*
|
|
2746
|
+
* Distinct from {@link startToolSpan} in the two ways the hand-off needs:
|
|
2747
|
+
* the contract's keys are set as RAW span attributes (under the
|
|
2748
|
+
* `traceloop.association.properties.` prefix the backend's reader cannot see
|
|
2749
|
+
* them), and the caller needs the span's ids to build the carrier.
|
|
2750
|
+
*
|
|
2751
|
+
* @internal
|
|
2752
|
+
*/
|
|
2753
|
+
recordDispatchSpan(params) {
|
|
2754
|
+
var _a;
|
|
2755
|
+
const properties = {
|
|
2756
|
+
...getPropertiesFromContext(this.context),
|
|
2757
|
+
...(_a = params.properties) != null ? _a : {}
|
|
2758
|
+
};
|
|
2759
|
+
const input = params.input !== void 0 ? serializeSpanValue(params.input) : void 0;
|
|
2760
|
+
const output = serializeSpanValue(params.output);
|
|
2761
|
+
if (this.directShipper) {
|
|
2762
|
+
const activeContext = getActiveTraceContext();
|
|
2763
|
+
const traceIdB64 = this.ensureTraceId(activeContext.traceIdB64);
|
|
2764
|
+
const spanIdB64 = base64Encode2(randomBytes2(8));
|
|
2765
|
+
const timeMs = Date.now();
|
|
2766
|
+
this.directShipper.sendToolSpan({
|
|
2767
|
+
name: params.name,
|
|
2768
|
+
spanId: spanIdB64,
|
|
2769
|
+
input,
|
|
2770
|
+
output,
|
|
2771
|
+
durationMs: 0,
|
|
2772
|
+
startTimeMs: timeMs,
|
|
2773
|
+
endTimeMs: timeMs,
|
|
2774
|
+
properties,
|
|
2775
|
+
traceId: traceIdB64,
|
|
2776
|
+
parentSpanId: activeContext.parentSpanIdB64,
|
|
2777
|
+
attributes: params.attributes
|
|
2778
|
+
});
|
|
2779
|
+
this.emitLiveEvent({
|
|
2780
|
+
traceId: traceIdB64,
|
|
2781
|
+
spanId: spanIdB64,
|
|
2782
|
+
type: "tool_result",
|
|
2783
|
+
content: params.name,
|
|
2784
|
+
timestamp: timeMs,
|
|
2785
|
+
metadata: { result: params.output }
|
|
2786
|
+
});
|
|
2787
|
+
return { traceIdHex: base64ToHex2(traceIdB64), spanIdHex: base64ToHex2(spanIdB64) };
|
|
2788
|
+
}
|
|
2789
|
+
const span = this.tracer.startSpan(params.name);
|
|
2790
|
+
setAssociationProperties(span, properties);
|
|
2791
|
+
span.setAttribute("traceloop.span.kind", "tool");
|
|
2792
|
+
this.stampRouting(span);
|
|
2793
|
+
for (const [key, value] of Object.entries(params.attributes)) {
|
|
2794
|
+
span.setAttribute(key, value);
|
|
2795
|
+
}
|
|
2796
|
+
if (input !== void 0) {
|
|
2797
|
+
span.setAttribute("traceloop.entity.input", input);
|
|
2798
|
+
}
|
|
2799
|
+
span.setAttribute("traceloop.entity.output", output);
|
|
2800
|
+
span.setStatus({ code: SpanStatusCode3.OK });
|
|
2801
|
+
const spanContext = span.spanContext();
|
|
2802
|
+
span.end();
|
|
2803
|
+
this.emitLiveEvent({
|
|
2804
|
+
spanId: isValidSpanId2(spanContext.spanId) ? spanContext.spanId : void 0,
|
|
2805
|
+
type: "tool_result",
|
|
2806
|
+
content: params.name,
|
|
2807
|
+
timestamp: Date.now(),
|
|
2808
|
+
metadata: { result: params.output }
|
|
2809
|
+
});
|
|
2810
|
+
return {
|
|
2811
|
+
traceIdHex: isValidTraceId2(spanContext.traceId) ? spanContext.traceId : void 0,
|
|
2812
|
+
spanIdHex: isValidSpanId2(spanContext.spanId) ? spanContext.spanId : void 0
|
|
2813
|
+
};
|
|
2814
|
+
}
|
|
2815
|
+
/**
|
|
2816
|
+
* Record the errored span a detached sub-agent's failure needs when nothing
|
|
2817
|
+
* else is open.
|
|
2818
|
+
*
|
|
2819
|
+
* Frequently the child's ONLY span, so it carries the full reverse reference:
|
|
2820
|
+
* anything reading it alone still knows which launch it belongs to. Those
|
|
2821
|
+
* attributes come from the run's own frame rather than the ambient stack,
|
|
2822
|
+
* because `fail()` is often called from a different task than the one that
|
|
2823
|
+
* opened the run and this is the span that can least afford to lose the link.
|
|
2824
|
+
*
|
|
2825
|
+
* @internal
|
|
2826
|
+
*/
|
|
2827
|
+
recordSubagentAbortSpan(params) {
|
|
2828
|
+
const { reason, message, attributes } = params;
|
|
2829
|
+
const properties = getPropertiesFromContext(this.context);
|
|
2830
|
+
if (this.directShipper) {
|
|
2831
|
+
const activeContext = getActiveTraceContext();
|
|
2832
|
+
const timeMs = Date.now();
|
|
2833
|
+
this.directShipper.sendSpan({
|
|
2834
|
+
name: SUBAGENT_ABORT_SPAN_NAME,
|
|
2835
|
+
// Not "tool": the run died, it did not call anything. `ai.operationId`
|
|
2836
|
+
// below is what keeps the span (see the attribute's docs in core).
|
|
2837
|
+
spanKind: "task",
|
|
2838
|
+
spanId: base64Encode2(randomBytes2(8)),
|
|
2839
|
+
durationMs: 0,
|
|
2840
|
+
startTimeMs: timeMs,
|
|
2841
|
+
endTimeMs: timeMs,
|
|
2842
|
+
error: message,
|
|
2843
|
+
properties,
|
|
2844
|
+
traceId: this.ensureTraceId(activeContext.traceIdB64),
|
|
2845
|
+
parentSpanId: activeContext.parentSpanIdB64,
|
|
2846
|
+
attributes: { ...attributes, [OPERATION_ID_ATTRIBUTE]: SUBAGENT_ABORT_OPERATION_ID }
|
|
2847
|
+
});
|
|
2848
|
+
return;
|
|
2849
|
+
}
|
|
2850
|
+
const span = this.tracer.startSpan(SUBAGENT_ABORT_SPAN_NAME);
|
|
2851
|
+
span.setAttribute(OPERATION_ID_ATTRIBUTE, SUBAGENT_ABORT_OPERATION_ID);
|
|
2852
|
+
setAssociationProperties(span, properties);
|
|
2853
|
+
this.stampRouting(span);
|
|
2854
|
+
for (const [key, value] of Object.entries(attributes)) {
|
|
2855
|
+
span.setAttribute(key, value);
|
|
2856
|
+
}
|
|
2857
|
+
span.setStatus({ code: SpanStatusCode3.ERROR, message });
|
|
2858
|
+
span.recordException(reason instanceof Error ? reason : new Error(message));
|
|
2859
|
+
span.end();
|
|
2860
|
+
}
|
|
1968
2861
|
async finish(resultEvent) {
|
|
1969
2862
|
var _a, _b, _c, _d;
|
|
1970
2863
|
unbindRoutingContext(this.boundContext);
|
|
1971
2864
|
this.boundContext = void 0;
|
|
2865
|
+
unbindSpanAttributes(this.toolEventsFrame);
|
|
2866
|
+
this.toolEventsFrame = void 0;
|
|
1972
2867
|
if (!this.traceId) {
|
|
1973
2868
|
this.resolveLiveTraceId();
|
|
1974
2869
|
}
|
|
@@ -2078,10 +2973,10 @@ var LiveInteraction = class {
|
|
|
2078
2973
|
}
|
|
2079
2974
|
if (error) {
|
|
2080
2975
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
2081
|
-
span.setStatus({ code:
|
|
2976
|
+
span.setStatus({ code: SpanStatusCode3.ERROR, message: errorMessage });
|
|
2082
2977
|
span.recordException(error instanceof Error ? error : new Error(errorMessage));
|
|
2083
2978
|
} else {
|
|
2084
|
-
span.setStatus({ code:
|
|
2979
|
+
span.setStatus({ code: SpanStatusCode3.OK });
|
|
2085
2980
|
}
|
|
2086
2981
|
span.end(endTimeMs);
|
|
2087
2982
|
if ((_c = this.analytics) == null ? void 0 : _c.debugLogs) {
|
|
@@ -2109,11 +3004,11 @@ var LiveInteraction = class {
|
|
|
2109
3004
|
};
|
|
2110
3005
|
|
|
2111
3006
|
// src/tracing/liveTracer.ts
|
|
2112
|
-
import { context as
|
|
3007
|
+
import { context as context4, isValidSpanId as isValidSpanId3, SpanStatusCode as SpanStatusCode4, trace as trace4 } from "@opentelemetry/api";
|
|
2113
3008
|
import * as traceloop2 from "@traceloop/node-server-sdk";
|
|
2114
3009
|
var LiveTracer = class {
|
|
2115
3010
|
constructor(globalProperties, directShipper, routing) {
|
|
2116
|
-
this.tracer =
|
|
3011
|
+
this.tracer = trace4.getTracer("traceloop.tracer");
|
|
2117
3012
|
this.globalProperties = globalProperties || {};
|
|
2118
3013
|
this.directShipper = directShipper;
|
|
2119
3014
|
this.routing = routing;
|
|
@@ -2125,7 +3020,7 @@ var LiveTracer = class {
|
|
|
2125
3020
|
}
|
|
2126
3021
|
emitLiveEvent(event) {
|
|
2127
3022
|
var _a, _b;
|
|
2128
|
-
const activeSpan =
|
|
3023
|
+
const activeSpan = trace4.getSpan(context4.active());
|
|
2129
3024
|
const activeTraceId = activeSpan == null ? void 0 : activeSpan.spanContext().traceId;
|
|
2130
3025
|
const active = getActiveTraceContext();
|
|
2131
3026
|
const traceId = (_b = (_a = event.traceId) != null ? _a : activeTraceId) != null ? _b : active.traceIdB64;
|
|
@@ -2145,7 +3040,7 @@ var LiveTracer = class {
|
|
|
2145
3040
|
};
|
|
2146
3041
|
const inputParameters = typeof params === "string" ? void 0 : params.inputParameters;
|
|
2147
3042
|
const wrappedFn = (...fnArgs) => {
|
|
2148
|
-
const activeSpan =
|
|
3043
|
+
const activeSpan = trace4.getSpan(context4.active());
|
|
2149
3044
|
if (activeSpan) {
|
|
2150
3045
|
this.stampRouting(activeSpan);
|
|
2151
3046
|
}
|
|
@@ -2240,14 +3135,14 @@ var LiveTracer = class {
|
|
|
2240
3135
|
}
|
|
2241
3136
|
if (error) {
|
|
2242
3137
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
2243
|
-
span.setStatus({ code:
|
|
3138
|
+
span.setStatus({ code: SpanStatusCode4.ERROR, message: errorMessage });
|
|
2244
3139
|
span.recordException(error instanceof Error ? error : new Error(errorMessage));
|
|
2245
3140
|
} else {
|
|
2246
|
-
span.setStatus({ code:
|
|
3141
|
+
span.setStatus({ code: SpanStatusCode4.OK });
|
|
2247
3142
|
}
|
|
2248
3143
|
span.end(endTimeMs);
|
|
2249
3144
|
const ctx = span.spanContext();
|
|
2250
|
-
const traceId = ctx.traceId || ((_b =
|
|
3145
|
+
const traceId = ctx.traceId || ((_b = trace4.getSpan(context4.active())) == null ? void 0 : _b.spanContext().traceId);
|
|
2251
3146
|
const spanIdHex = isValidSpanId3(ctx.spanId) ? ctx.spanId : void 0;
|
|
2252
3147
|
if (traceId) {
|
|
2253
3148
|
this.emitLiveEvent({
|
|
@@ -2540,7 +3435,7 @@ try {
|
|
|
2540
3435
|
e instanceof Error ? e.message : e
|
|
2541
3436
|
);
|
|
2542
3437
|
span.setStatus({
|
|
2543
|
-
code:
|
|
3438
|
+
code: SpanStatusCode5.ERROR,
|
|
2544
3439
|
message: e instanceof Error ? e.message : String(e)
|
|
2545
3440
|
});
|
|
2546
3441
|
span.end();
|
|
@@ -2572,7 +3467,7 @@ try {
|
|
|
2572
3467
|
);
|
|
2573
3468
|
if (!spanEnded) {
|
|
2574
3469
|
span.setStatus({
|
|
2575
|
-
code:
|
|
3470
|
+
code: SpanStatusCode5.ERROR,
|
|
2576
3471
|
message: e instanceof Error ? e.message : String(e)
|
|
2577
3472
|
});
|
|
2578
3473
|
span.end();
|
|
@@ -2586,7 +3481,7 @@ try {
|
|
|
2586
3481
|
} catch (error) {
|
|
2587
3482
|
if (!spanEnded) {
|
|
2588
3483
|
const msg = error instanceof Error ? error.message : String(error);
|
|
2589
|
-
span.setStatus({ code:
|
|
3484
|
+
span.setStatus({ code: SpanStatusCode5.ERROR, message: msg });
|
|
2590
3485
|
span.recordException(error);
|
|
2591
3486
|
span.end();
|
|
2592
3487
|
spanEnded = true;
|
|
@@ -2609,7 +3504,7 @@ try {
|
|
|
2609
3504
|
return result;
|
|
2610
3505
|
}).catch((error) => {
|
|
2611
3506
|
span.setStatus({
|
|
2612
|
-
code:
|
|
3507
|
+
code: SpanStatusCode5.ERROR,
|
|
2613
3508
|
message: error.message
|
|
2614
3509
|
});
|
|
2615
3510
|
span.recordException(error);
|
|
@@ -2672,7 +3567,7 @@ function patchBedrockStreamingBug(inst) {
|
|
|
2672
3567
|
} catch (error) {
|
|
2673
3568
|
if (!spanEnded) {
|
|
2674
3569
|
const msg = error instanceof Error ? error.message : String(error);
|
|
2675
|
-
span.setStatus({ code:
|
|
3570
|
+
span.setStatus({ code: SpanStatusCode5.ERROR, message: msg });
|
|
2676
3571
|
span.recordException(error);
|
|
2677
3572
|
span.end();
|
|
2678
3573
|
spanEnded = true;
|
|
@@ -2695,7 +3590,7 @@ function patchBedrockStreamingBug(inst) {
|
|
|
2695
3590
|
return result;
|
|
2696
3591
|
}).catch((error) => {
|
|
2697
3592
|
span.setStatus({
|
|
2698
|
-
code:
|
|
3593
|
+
code: SpanStatusCode5.ERROR,
|
|
2699
3594
|
message: error.message
|
|
2700
3595
|
});
|
|
2701
3596
|
span.recordException(error);
|
|
@@ -2777,7 +3672,7 @@ function createManualInstrumentations(instrumentModules) {
|
|
|
2777
3672
|
}
|
|
2778
3673
|
function getCurrentTraceId() {
|
|
2779
3674
|
var _a;
|
|
2780
|
-
return (_a =
|
|
3675
|
+
return (_a = trace5.getSpan(context5.active())) == null ? void 0 : _a.spanContext().traceId;
|
|
2781
3676
|
}
|
|
2782
3677
|
var tracing = (analytics, apiUrl, writeKey, options, isDebug = false) => {
|
|
2783
3678
|
var _a;
|
|
@@ -2970,6 +3865,72 @@ var tracing = (analytics, apiUrl, writeKey, options, isDebug = false) => {
|
|
|
2970
3865
|
} catch (e) {
|
|
2971
3866
|
}
|
|
2972
3867
|
}) : void 0;
|
|
3868
|
+
const beginInteraction = (traceContext) => {
|
|
3869
|
+
var _a2;
|
|
3870
|
+
if (!traceContext.eventId) {
|
|
3871
|
+
traceContext.eventId = crypto.randomUUID();
|
|
3872
|
+
}
|
|
3873
|
+
const traceId = getCurrentTraceId();
|
|
3874
|
+
const interaction = new LiveInteraction(
|
|
3875
|
+
traceContext,
|
|
3876
|
+
traceId,
|
|
3877
|
+
analytics,
|
|
3878
|
+
directShipper,
|
|
3879
|
+
{ projectId, authHint }
|
|
3880
|
+
);
|
|
3881
|
+
const bound = bindRoutingContext({ projectId, authHint }, interaction);
|
|
3882
|
+
interaction.setBoundContext(bound);
|
|
3883
|
+
try {
|
|
3884
|
+
analytics._trackAiPartial({
|
|
3885
|
+
...traceContext,
|
|
3886
|
+
properties: {
|
|
3887
|
+
...(_a2 = traceContext.properties) != null ? _a2 : {},
|
|
3888
|
+
...interaction.getTraceId() ? { trace_id: interaction.getTraceId() } : {}
|
|
3889
|
+
}
|
|
3890
|
+
});
|
|
3891
|
+
if (interaction.getTraceId()) {
|
|
3892
|
+
activeInteractions.set(interaction.getTraceId(), interaction);
|
|
3893
|
+
}
|
|
3894
|
+
activeInteractionsByEventId.set(traceContext.eventId, interaction);
|
|
3895
|
+
} catch (error) {
|
|
3896
|
+
unbindRoutingContext(bound);
|
|
3897
|
+
throw error;
|
|
3898
|
+
}
|
|
3899
|
+
return interaction;
|
|
3900
|
+
};
|
|
3901
|
+
const openSubagentRun = (carrierOrHeaders, options2) => {
|
|
3902
|
+
const resolved = resolveSubagentResume(carrierOrHeaders, options2);
|
|
3903
|
+
if (!resolved.parent && isDebug) {
|
|
3904
|
+
console.log(
|
|
3905
|
+
`[raindrop] resumeSubagent(${resolved.name}): no hand-off carrier on the request; reporting as an unlinked event ${resolved.eventId}.`
|
|
3906
|
+
);
|
|
3907
|
+
}
|
|
3908
|
+
const frameOwner = { subagentEventId: resolved.eventId };
|
|
3909
|
+
const frame = bindSubagentLink(resolved.link, frameOwner);
|
|
3910
|
+
let interaction;
|
|
3911
|
+
try {
|
|
3912
|
+
interaction = beginInteraction({
|
|
3913
|
+
eventId: resolved.eventId,
|
|
3914
|
+
event: resolved.event,
|
|
3915
|
+
userId: resolved.userId,
|
|
3916
|
+
...resolved.convoId ? { convoId: resolved.convoId } : {},
|
|
3917
|
+
...options2.input !== void 0 ? { input: options2.input } : {},
|
|
3918
|
+
...options2.model !== void 0 ? { model: options2.model } : {},
|
|
3919
|
+
...options2.properties ? { properties: options2.properties } : {}
|
|
3920
|
+
});
|
|
3921
|
+
} catch (error) {
|
|
3922
|
+
unbindSpanAttributes(frame);
|
|
3923
|
+
throw error;
|
|
3924
|
+
}
|
|
3925
|
+
return new LiveSubagentRun({
|
|
3926
|
+
interaction,
|
|
3927
|
+
eventId: resolved.eventId,
|
|
3928
|
+
name: resolved.name,
|
|
3929
|
+
...resolved.convoId ? { convoId: resolved.convoId } : {},
|
|
3930
|
+
parent: resolved.link,
|
|
3931
|
+
...frame ? { frame, frameOwner } : {}
|
|
3932
|
+
});
|
|
3933
|
+
};
|
|
2973
3934
|
return {
|
|
2974
3935
|
/**
|
|
2975
3936
|
* Returns instrumentation instances for your NodeSDK.
|
|
@@ -3079,37 +4040,13 @@ var tracing = (analytics, apiUrl, writeKey, options, isDebug = false) => {
|
|
|
3079
4040
|
);
|
|
3080
4041
|
},
|
|
3081
4042
|
begin(traceContext) {
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
traceId,
|
|
3090
|
-
analytics,
|
|
3091
|
-
directShipper,
|
|
3092
|
-
{ projectId, authHint }
|
|
3093
|
-
);
|
|
3094
|
-
const bound = bindRoutingContext({ projectId, authHint }, interaction);
|
|
3095
|
-
interaction.setBoundContext(bound);
|
|
3096
|
-
try {
|
|
3097
|
-
analytics._trackAiPartial({
|
|
3098
|
-
...traceContext,
|
|
3099
|
-
properties: {
|
|
3100
|
-
...(_a2 = traceContext.properties) != null ? _a2 : {},
|
|
3101
|
-
...interaction.getTraceId() ? { trace_id: interaction.getTraceId() } : {}
|
|
3102
|
-
}
|
|
3103
|
-
});
|
|
3104
|
-
if (interaction.getTraceId()) {
|
|
3105
|
-
activeInteractions.set(interaction.getTraceId(), interaction);
|
|
3106
|
-
}
|
|
3107
|
-
activeInteractionsByEventId.set(traceContext.eventId, interaction);
|
|
3108
|
-
} catch (error) {
|
|
3109
|
-
unbindRoutingContext(bound);
|
|
3110
|
-
throw error;
|
|
3111
|
-
}
|
|
3112
|
-
return interaction;
|
|
4043
|
+
return beginInteraction(traceContext);
|
|
4044
|
+
},
|
|
4045
|
+
resumeSubagent(carrierOrHeaders, options2 = {}) {
|
|
4046
|
+
return openSubagentRun(carrierOrHeaders, options2);
|
|
4047
|
+
},
|
|
4048
|
+
withSubagentRun(carrierOrHeaders, fn, options2 = {}) {
|
|
4049
|
+
return openSubagentRun(carrierOrHeaders, options2).use(fn);
|
|
3113
4050
|
},
|
|
3114
4051
|
getActiveInteraction(eventId) {
|
|
3115
4052
|
const interactionByEventId = activeInteractionsByEventId.get(eventId);
|