@raindrop-ai/pi-agent 0.0.10 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -24,7 +24,7 @@ __export(index_exports, {
24
24
  });
25
25
  module.exports = __toCommonJS(index_exports);
26
26
 
27
- // ../core/dist/chunk-267IXHO5.js
27
+ // ../core/dist/chunk-YUIXRQWN.js
28
28
  function getCrypto() {
29
29
  const c = globalThis.crypto;
30
30
  return c;
@@ -79,6 +79,20 @@ function base64Encode(bytes) {
79
79
  }
80
80
  return out;
81
81
  }
82
+ var HANDOFF_ATTRIBUTE_SUFFIXES = {
83
+ mode: "raindrop.handoff.mode",
84
+ childEventId: "raindrop.handoff.childEventId",
85
+ parentEventId: "raindrop.handoff.parentEventId",
86
+ parentSpanId: "raindrop.handoff.parentSpanId",
87
+ name: "raindrop.handoff.name",
88
+ terminal: "raindrop.handoff.terminal",
89
+ agentRole: "raindrop.agent.role"
90
+ };
91
+ var HANDOFF_TERMINAL_PROPERTY_KEY = HANDOFF_ATTRIBUTE_SUFFIXES.terminal;
92
+ var TRACEPARENT_HEADER = "traceparent";
93
+ var BAGGAGE_HEADER = "baggage";
94
+ var HANDOFF_HEADER = "x-raindrop-handoff";
95
+ 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.`;
82
96
  function runWithTracingSuppressed(fn) {
83
97
  const hook = globalThis.RAINDROP_SUPPRESS_TRACING;
84
98
  if (typeof hook !== "function") return fn();
@@ -414,19 +428,52 @@ function projectIdHeaders(projectId) {
414
428
  var SHUTDOWN_DEADLINE_MS = 1e4;
415
429
  var POST_SHUTDOWN_TIMEOUT_MS = 5e3;
416
430
  function mergePatches(target, source) {
417
- var _a, _b, _c, _d, _e, _f;
431
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i;
418
432
  const out = { ...target, ...source };
419
433
  if (target.properties || source.properties) {
420
434
  out.properties = { ...(_a = target.properties) != null ? _a : {}, ...(_b = source.properties) != null ? _b : {} };
421
435
  }
436
+ if (target.usage || source.usage) {
437
+ out.usage = { ...(_c = target.usage) != null ? _c : {} };
438
+ if (((_d = source.usage) == null ? void 0 : _d.promptTokens) !== void 0) {
439
+ out.usage.promptTokens = source.usage.promptTokens;
440
+ }
441
+ if (((_e = source.usage) == null ? void 0 : _e.completionTokens) !== void 0) {
442
+ out.usage.completionTokens = source.usage.completionTokens;
443
+ }
444
+ }
445
+ if (source.error === void 0) {
446
+ out.error = target.error;
447
+ }
422
448
  if (target.featureFlags || source.featureFlags) {
423
- out.featureFlags = { ...(_c = target.featureFlags) != null ? _c : {}, ...(_d = source.featureFlags) != null ? _d : {} };
449
+ out.featureFlags = { ...(_f = target.featureFlags) != null ? _f : {}, ...(_g = source.featureFlags) != null ? _g : {} };
424
450
  }
425
451
  if (target.attachments || source.attachments) {
426
- out.attachments = [...(_e = target.attachments) != null ? _e : [], ...(_f = source.attachments) != null ? _f : []];
452
+ out.attachments = [...(_h = target.attachments) != null ? _h : [], ...(_i = source.attachments) != null ? _i : []];
427
453
  }
428
454
  return out;
429
455
  }
456
+ function isErrorLike(value) {
457
+ return value !== null && typeof value === "object";
458
+ }
459
+ function eventTelemetryProperties(patch) {
460
+ var _a, _b;
461
+ const properties = {};
462
+ if (((_a = patch.usage) == null ? void 0 : _a.promptTokens) !== void 0 && Number.isFinite(patch.usage.promptTokens)) {
463
+ properties["ai.usage.prompt_tokens"] = patch.usage.promptTokens;
464
+ }
465
+ if (((_b = patch.usage) == null ? void 0 : _b.completionTokens) !== void 0 && Number.isFinite(patch.usage.completionTokens)) {
466
+ properties["ai.usage.completion_tokens"] = patch.usage.completionTokens;
467
+ }
468
+ if (patch.error !== void 0 && patch.error !== null) {
469
+ const constructorName = patch.error instanceof Error && patch.error.constructor !== Error ? patch.error.constructor.name : void 0;
470
+ const errorType = patch.error instanceof Error ? patch.error.name && patch.error.name !== "Error" ? patch.error.name : constructorName || patch.error.name || "Error" : isErrorLike(patch.error) && typeof patch.error.name === "string" && patch.error.name.length > 0 ? patch.error.name : "Error";
471
+ const errorMessage = patch.error instanceof Error ? patch.error.message : isErrorLike(patch.error) && typeof patch.error.message === "string" ? patch.error.message : String(patch.error);
472
+ properties["error.type"] = errorType;
473
+ properties["error.message"] = errorMessage;
474
+ }
475
+ return properties;
476
+ }
430
477
  var EventShipper = class {
431
478
  constructor(opts) {
432
479
  this.buffers = /* @__PURE__ */ new Map();
@@ -675,6 +722,11 @@ var EventShipper = class {
675
722
  return;
676
723
  }
677
724
  const { wizardSession, ...restProperties } = (_e = accumulated.properties) != null ? _e : {};
725
+ let telemetryProperties = {};
726
+ try {
727
+ telemetryProperties = eventTelemetryProperties(accumulated);
728
+ } catch (e) {
729
+ }
678
730
  const convoId = (_f = accumulated.convoId) != null ? _f : sticky.convoId;
679
731
  const isPending = (_h = (_g = accumulated.isPending) != null ? _g : sticky.isPending) != null ? _h : true;
680
732
  const payload = {
@@ -684,12 +736,13 @@ var EventShipper = class {
684
736
  timestamp: (_i = accumulated.timestamp) != null ? _i : (/* @__PURE__ */ new Date()).toISOString(),
685
737
  ai_data: {
686
738
  input: accumulated.input,
687
- output: accumulated.output,
739
+ output: accumulated.error !== void 0 && accumulated.error !== null ? void 0 : accumulated.output,
688
740
  model: accumulated.model,
689
741
  convo_id: convoId
690
742
  },
691
743
  properties: {
692
744
  ...restProperties,
745
+ ...telemetryProperties,
693
746
  ...wizardSession ? { "raindrop.wizardSession": wizardSession } : {},
694
747
  $context: this.context
695
748
  },
@@ -758,6 +811,194 @@ var EventShipper = class {
758
811
  }
759
812
  }
760
813
  };
814
+ var MODEL_USAGE_ATTRIBUTES = {
815
+ providerName: "gen_ai.provider.name",
816
+ requestModel: "gen_ai.request.model",
817
+ responseModel: "gen_ai.response.model",
818
+ inputTokens: "gen_ai.usage.input_tokens",
819
+ outputTokens: "gen_ai.usage.output_tokens",
820
+ reasoningTokens: "gen_ai.usage.reasoning_tokens",
821
+ cacheReadInputTokens: "gen_ai.usage.cache_read_input_tokens",
822
+ cacheWriteInputTokens: "gen_ai.usage.cache_write_input_tokens",
823
+ nonCachedInputTokens: "raindrop.usage.input_tokens.non_cached",
824
+ nonReasoningOutputTokens: "raindrop.usage.output_tokens.non_reasoning",
825
+ totalOutputTokens: "raindrop.usage.output_tokens.total",
826
+ reasoningOutputTokens: "raindrop.usage.output_tokens.reasoning",
827
+ cacheReadTokens: "raindrop.usage.input_tokens.cache_read",
828
+ cacheWriteTokens: "raindrop.usage.input_tokens.cache_write"
829
+ };
830
+ var MODEL_PROVIDER_NAMES = [
831
+ ["google.vertex", "google-vertex"],
832
+ ["vertex.anthropic", "google-vertex"],
833
+ ["amazon-bedrock", "amazon-bedrock"],
834
+ ["bedrock", "amazon-bedrock"],
835
+ ["anthropic", "anthropic"],
836
+ ["openai", "openai"],
837
+ ["google", "google"],
838
+ ["azure", "azure"],
839
+ ["openrouter", "openrouter"]
840
+ ];
841
+ function canonicalModelProvider(provider) {
842
+ var _a;
843
+ const normalizedProvider = provider.trim().toLowerCase();
844
+ if (normalizedProvider === "gateway") return "vercel";
845
+ const match = MODEL_PROVIDER_NAMES.find(
846
+ ([family]) => normalizedProvider === family || normalizedProvider.startsWith(`${family}.`)
847
+ );
848
+ return (_a = match == null ? void 0 : match[1]) != null ? _a : provider;
849
+ }
850
+ function tokenCount(value) {
851
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : void 0;
852
+ }
853
+ function exclusiveTokens({
854
+ explicit,
855
+ total,
856
+ parts,
857
+ semantics
858
+ }) {
859
+ if (explicit !== void 0) return explicit;
860
+ if (total === void 0) return void 0;
861
+ if (semantics === "exclusive") return total;
862
+ const observedParts = parts.filter((part) => part !== void 0 && part > 0);
863
+ if (semantics === void 0 && observedParts.length > 0) return void 0;
864
+ const exclusive = total - observedParts.reduce((sum, part) => sum + part, 0);
865
+ return exclusive >= 0 ? exclusive : void 0;
866
+ }
867
+ function buildModelUsageAttributes(usage) {
868
+ const inputTokens = tokenCount(usage.inputTokens);
869
+ const outputTokens = tokenCount(usage.outputTokens);
870
+ const explicitNonCachedInputTokens = tokenCount(usage.nonCachedInputTokens);
871
+ const explicitNonReasoningOutputTokens = tokenCount(usage.nonReasoningOutputTokens);
872
+ const reasoningTokens = tokenCount(usage.reasoningTokens);
873
+ const cacheReadInputTokens = tokenCount(usage.cacheReadInputTokens);
874
+ const cacheWriteInputTokens = tokenCount(usage.cacheWriteInputTokens);
875
+ const nonCachedInputTokens = exclusiveTokens({
876
+ explicit: explicitNonCachedInputTokens,
877
+ total: inputTokens,
878
+ parts: [cacheReadInputTokens, cacheWriteInputTokens],
879
+ semantics: usage.inputTokenSemantics
880
+ });
881
+ const nonReasoningOutputTokens = exclusiveTokens({
882
+ explicit: explicitNonReasoningOutputTokens,
883
+ total: outputTokens,
884
+ parts: [reasoningTokens],
885
+ semantics: usage.outputTokenSemantics
886
+ });
887
+ const exclusivePoolAttributes = nonCachedInputTokens !== void 0 && nonReasoningOutputTokens !== void 0 ? [
888
+ // backend consumes this complete raindrop.* set as one atomic billing-pool contract.
889
+ attrInt(MODEL_USAGE_ATTRIBUTES.nonCachedInputTokens, nonCachedInputTokens),
890
+ attrInt(MODEL_USAGE_ATTRIBUTES.nonReasoningOutputTokens, nonReasoningOutputTokens),
891
+ attrInt(MODEL_USAGE_ATTRIBUTES.reasoningOutputTokens, reasoningTokens),
892
+ attrInt(MODEL_USAGE_ATTRIBUTES.cacheReadTokens, cacheReadInputTokens),
893
+ attrInt(MODEL_USAGE_ATTRIBUTES.cacheWriteTokens, cacheWriteInputTokens)
894
+ ] : [];
895
+ return [
896
+ attrInt(MODEL_USAGE_ATTRIBUTES.inputTokens, inputTokens),
897
+ attrInt(MODEL_USAGE_ATTRIBUTES.outputTokens, outputTokens),
898
+ attrInt(MODEL_USAGE_ATTRIBUTES.reasoningTokens, reasoningTokens),
899
+ attrInt(MODEL_USAGE_ATTRIBUTES.cacheReadInputTokens, cacheReadInputTokens),
900
+ attrInt(MODEL_USAGE_ATTRIBUTES.cacheWriteInputTokens, cacheWriteInputTokens),
901
+ ...exclusivePoolAttributes
902
+ ].filter((attribute) => attribute !== void 0);
903
+ }
904
+ var STRING_ALIASES = {
905
+ [MODEL_USAGE_ATTRIBUTES.requestModel]: ["ai.model.id", "ai.model"]
906
+ };
907
+ var NUMBER_ALIASES = {
908
+ [MODEL_USAGE_ATTRIBUTES.inputTokens]: [
909
+ "gen_ai.usage.prompt_tokens",
910
+ "ai.usage.prompt_tokens",
911
+ "ai.usage.promptTokens",
912
+ "ai.usage.input_tokens",
913
+ "ai.usage.inputTokens"
914
+ ],
915
+ [MODEL_USAGE_ATTRIBUTES.outputTokens]: [
916
+ "gen_ai.usage.completion_tokens",
917
+ "ai.usage.completion_tokens",
918
+ "ai.usage.completionTokens",
919
+ "ai.usage.output_tokens",
920
+ "ai.usage.outputTokens"
921
+ ],
922
+ [MODEL_USAGE_ATTRIBUTES.reasoningTokens]: [
923
+ "gen_ai.usage.reasoning.output_tokens",
924
+ "ai.usage.reasoningTokens",
925
+ "ai.usage.thoughts_tokens"
926
+ ],
927
+ [MODEL_USAGE_ATTRIBUTES.cacheReadInputTokens]: [
928
+ "gen_ai.usage.cache_read_tokens",
929
+ "gen_ai.usage.cache_read.input_tokens",
930
+ "ai.usage.cachedInputTokens",
931
+ "ai.usage.cached_tokens",
932
+ "ai.usage.cache_read_tokens",
933
+ "ai.usage.cache_read_input_tokens",
934
+ "ai.usage.cacheReadInputTokens"
935
+ ],
936
+ [MODEL_USAGE_ATTRIBUTES.cacheWriteInputTokens]: [
937
+ "gen_ai.usage.cache_creation_input_tokens",
938
+ "gen_ai.usage.cache_creation.input_tokens",
939
+ "ai.usage.cacheWriteInputTokens",
940
+ "ai.usage.cache_creation_input_tokens",
941
+ "ai.usage.cacheCreationInputTokens"
942
+ ]
943
+ };
944
+ function hasAttribute(attributes, key) {
945
+ return attributes.some((attribute) => attribute.key === key);
946
+ }
947
+ function findStringAttribute(attributes, keys) {
948
+ return attributes.find(
949
+ (attribute) => keys.includes(attribute.key) && typeof attribute.value.stringValue === "string" && attribute.value.stringValue.length > 0
950
+ );
951
+ }
952
+ function findNumberAttribute(attributes, keys) {
953
+ return attributes.find((attribute) => {
954
+ if (!keys.includes(attribute.key)) return false;
955
+ if (typeof attribute.value.doubleValue === "number" && Number.isSafeInteger(attribute.value.doubleValue) && attribute.value.doubleValue >= 0) {
956
+ return true;
957
+ }
958
+ const intValue = attribute.value.intValue;
959
+ return integerValue(intValue) !== void 0;
960
+ });
961
+ }
962
+ function integerValue(value) {
963
+ if (typeof value === "number") {
964
+ return Number.isSafeInteger(value) && value >= 0 ? value : void 0;
965
+ }
966
+ if (typeof value !== "string") return void 0;
967
+ const parsed = Number(value);
968
+ return Number.isSafeInteger(parsed) && parsed >= 0 ? parsed : void 0;
969
+ }
970
+ function appendAlias(attributes, target, source) {
971
+ if (!source || hasAttribute(attributes, target)) return attributes;
972
+ return [...attributes, { key: target, value: { ...source.value } }];
973
+ }
974
+ function appendProviderAlias(attributes) {
975
+ if (hasAttribute(attributes, MODEL_USAGE_ATTRIBUTES.providerName)) return attributes;
976
+ const source = findStringAttribute(attributes, ["gen_ai.system", "ai.model.provider"]);
977
+ if (!(source == null ? void 0 : source.value.stringValue)) return attributes;
978
+ const attribute = attrString(
979
+ MODEL_USAGE_ATTRIBUTES.providerName,
980
+ canonicalModelProvider(source.value.stringValue)
981
+ );
982
+ return attribute ? [...attributes, attribute] : attributes;
983
+ }
984
+ function normalizeModelUsageSpan(span) {
985
+ const original = span.attributes;
986
+ if (!original || original.length === 0) return span;
987
+ const responseModel = findStringAttribute(original, [
988
+ MODEL_USAGE_ATTRIBUTES.responseModel,
989
+ "ai.response.model"
990
+ ]);
991
+ if (!responseModel) return span;
992
+ let attributes = appendAlias(original, MODEL_USAGE_ATTRIBUTES.responseModel, responseModel);
993
+ attributes = appendProviderAlias(attributes);
994
+ for (const [target, aliases] of Object.entries(STRING_ALIASES)) {
995
+ attributes = appendAlias(attributes, target, findStringAttribute(attributes, aliases));
996
+ }
997
+ for (const [target, aliases] of Object.entries(NUMBER_ALIASES)) {
998
+ attributes = appendAlias(attributes, target, findNumberAttribute(attributes, aliases));
999
+ }
1000
+ return attributes === original ? span : { ...span, attributes };
1001
+ }
761
1002
  var DEFAULT_SECRET_KEY_NAMES = [
762
1003
  "apikey",
763
1004
  "apisecret",
@@ -847,6 +1088,13 @@ function redactJsonAttributeValue(key, value) {
847
1088
  if (scrubbedJson === json) return void 0;
848
1089
  return { stringValue: scrubbedJson };
849
1090
  }
1091
+ function identityAttrs(args) {
1092
+ return [
1093
+ attrString("ai.telemetry.metadata.raindrop.ai.userId", args.userId),
1094
+ attrString("ai.telemetry.metadata.raindrop.convoId", args.convoId),
1095
+ attrString("ai.telemetry.metadata.raindrop.eventName", args.eventName)
1096
+ ];
1097
+ }
850
1098
  function applyOtelSpanAttributeLimit(limit) {
851
1099
  var _a, _b;
852
1100
  try {
@@ -951,6 +1199,10 @@ var TraceShipper = class {
951
1199
  return null;
952
1200
  }
953
1201
  }
1202
+ try {
1203
+ current = normalizeModelUsageSpan(current);
1204
+ } catch (e) {
1205
+ }
954
1206
  if (!this.disableDefaultRedaction) {
955
1207
  current = defaultTransformSpan(current);
956
1208
  }
@@ -971,6 +1223,7 @@ var TraceShipper = class {
971
1223
  const started = (_a = args.startTimeUnixNano) != null ? _a : nowUnixNanoString();
972
1224
  const attrs = [
973
1225
  attrString("ai.telemetry.metadata.raindrop.eventId", args.eventId),
1226
+ ...identityAttrs(args),
974
1227
  attrString("ai.operationId", args.operationId)
975
1228
  ];
976
1229
  if ((_b = args.attributes) == null ? void 0 : _b.length) attrs.push(...args.attributes);
@@ -1026,7 +1279,8 @@ var TraceShipper = class {
1026
1279
  var _a;
1027
1280
  const ids = createSpanIds(args.parent);
1028
1281
  const attrs = [
1029
- attrString("ai.telemetry.metadata.raindrop.eventId", args.eventId)
1282
+ attrString("ai.telemetry.metadata.raindrop.eventId", args.eventId),
1283
+ ...identityAttrs(args)
1030
1284
  ];
1031
1285
  if ((_a = args.attributes) == null ? void 0 : _a.length) attrs.push(...args.attributes);
1032
1286
  const otlp = buildOtlpSpan({
@@ -1194,7 +1448,7 @@ installTracingSuppressionHook();
1194
1448
  // package.json
1195
1449
  var package_default = {
1196
1450
  name: "@raindrop-ai/pi-agent",
1197
- version: "0.0.10",
1451
+ version: "0.1.1",
1198
1452
  description: "Raindrop observability for Pi Agent \u2014 automatic tracing via subscriber or pi-coding-agent extension",
1199
1453
  type: "module",
1200
1454
  license: "MIT",
@@ -1349,9 +1603,22 @@ function extractTokenUsage(message) {
1349
1603
  return {
1350
1604
  input: usage.input,
1351
1605
  output: usage.output,
1352
- ...typeof usage.cacheRead === "number" && usage.cacheRead > 0 ? { cacheRead: usage.cacheRead } : {}
1606
+ cacheRead: typeof usage.cacheRead === "number" ? usage.cacheRead : 0,
1607
+ cacheWrite: typeof usage.cacheWrite === "number" ? usage.cacheWrite : 0
1353
1608
  };
1354
1609
  }
1610
+ function modelSpendSpanAttributes(input) {
1611
+ const providerAttr = input.provider ? attrString("gen_ai.provider.name", canonicalModelProvider(input.provider)) : void 0;
1612
+ const usageAttrs = input.usage ? buildModelUsageAttributes({
1613
+ inputTokens: input.usage.input,
1614
+ outputTokens: input.usage.output,
1615
+ cacheReadInputTokens: input.usage.cacheRead,
1616
+ cacheWriteInputTokens: input.usage.cacheWrite,
1617
+ reasoningTokens: 0,
1618
+ inputTokenSemantics: "exclusive"
1619
+ }) : [];
1620
+ return providerAttr ? [providerAttr, ...usageAttrs] : usageAttrs;
1621
+ }
1355
1622
  function extractAssistantText(message) {
1356
1623
  if (!("role" in message) || message.role !== "assistant") return void 0;
1357
1624
  if (!Array.isArray(message.content)) return void 0;
@@ -1493,14 +1760,27 @@ function getUsername() {
1493
1760
  }
1494
1761
 
1495
1762
  // src/internal/subscriber.ts
1763
+ function pushAttr(attrs, attr) {
1764
+ if (attr) attrs.push(attr);
1765
+ }
1766
+ function resolveEventId(source) {
1767
+ if (typeof source !== "function") return randomUUID();
1768
+ try {
1769
+ const id = source();
1770
+ return typeof id === "string" && id.trim() !== "" ? id : randomUUID();
1771
+ } catch (e) {
1772
+ return randomUUID();
1773
+ }
1774
+ }
1496
1775
  function createSubscriber(agent, eventShipper, traceShipper, defaultOptions, options, debug) {
1497
- var _a, _b, _c, _d, _e;
1776
+ var _a, _b, _c, _d, _e, _f;
1498
1777
  const userId = (_a = options.userId) != null ? _a : defaultOptions.userId;
1499
1778
  const convoId = (_b = options.convoId) != null ? _b : defaultOptions.convoId;
1500
1779
  const eventName = (_c = options.eventName) != null ? _c : defaultOptions.eventName;
1780
+ const eventIdSource = (_d = options.eventId) != null ? _d : defaultOptions.eventId;
1501
1781
  const properties = {
1502
- ...(_d = defaultOptions.properties) != null ? _d : {},
1503
- ...(_e = options.properties) != null ? _e : {}
1782
+ ...(_e = defaultOptions.properties) != null ? _e : {},
1783
+ ...(_f = options.properties) != null ? _f : {}
1504
1784
  };
1505
1785
  let currentRun;
1506
1786
  function log(msg) {
@@ -1531,7 +1811,7 @@ function createSubscriber(agent, eventShipper, traceShipper, defaultOptions, opt
1531
1811
  }
1532
1812
  currentRun = void 0;
1533
1813
  }
1534
- const eventId = randomUUID();
1814
+ const eventId = resolveEventId(eventIdSource);
1535
1815
  const rootSpan = traceShipper ? traceShipper.startSpan({
1536
1816
  name: "ai.event",
1537
1817
  eventId,
@@ -1600,41 +1880,30 @@ function createSubscriber(agent, eventShipper, traceShipper, defaultOptions, opt
1600
1880
  if (usage) {
1601
1881
  currentRun.totalInputTokens += usage.input;
1602
1882
  currentRun.totalOutputTokens += usage.output;
1603
- if (usage.cacheRead) currentRun.totalCacheReadTokens += usage.cacheRead;
1883
+ if (usage.cacheRead > 0) currentRun.totalCacheReadTokens += usage.cacheRead;
1604
1884
  }
1605
1885
  if (!traceShipper || !currentRun.currentTurnSpan) return;
1606
1886
  const rawProvider = provider;
1607
1887
  const rawModelId = bareModel;
1608
1888
  const stopReason = "stopReason" in message && typeof message.stopReason === "string" ? message.stopReason : void 0;
1609
- const llmAttrs = [
1610
- attrString("ai.operationId", "generateText")
1611
- ];
1889
+ const llmAttrs = [];
1890
+ pushAttr(llmAttrs, attrString("ai.operationId", "generateText"));
1612
1891
  if (rawProvider) {
1613
- llmAttrs.push(attrString("gen_ai.system", rawProvider));
1892
+ pushAttr(llmAttrs, attrString("gen_ai.system", rawProvider));
1614
1893
  }
1615
1894
  if (rawModelId) {
1616
- llmAttrs.push(
1617
- attrString("gen_ai.response.model", rawModelId),
1618
- attrString("gen_ai.request.model", rawModelId)
1619
- );
1620
- }
1621
- if (usage) {
1622
- llmAttrs.push(
1623
- attrInt("gen_ai.usage.input_tokens", usage.input),
1624
- attrInt("gen_ai.usage.output_tokens", usage.output)
1625
- );
1626
- if (usage.cacheRead) {
1627
- llmAttrs.push(attrInt("gen_ai.usage.cache_read_tokens", usage.cacheRead));
1628
- }
1895
+ pushAttr(llmAttrs, attrString("gen_ai.response.model", rawModelId));
1896
+ pushAttr(llmAttrs, attrString("gen_ai.request.model", rawModelId));
1629
1897
  }
1898
+ llmAttrs.push(...modelSpendSpanAttributes({ provider: rawProvider, usage }));
1630
1899
  if (assistantText) {
1631
- llmAttrs.push(attrString("ai.response.text", truncate(assistantText)));
1900
+ pushAttr(llmAttrs, attrString("ai.response.text", truncate(assistantText)));
1632
1901
  }
1633
1902
  if (currentRun.currentInput) {
1634
- llmAttrs.push(attrString("ai.prompt", truncate(currentRun.currentInput)));
1903
+ pushAttr(llmAttrs, attrString("ai.prompt", truncate(currentRun.currentInput)));
1635
1904
  }
1636
1905
  if (stopReason) {
1637
- llmAttrs.push(attrString("ai.stop_reason", stopReason));
1906
+ pushAttr(llmAttrs, attrString("ai.stop_reason", stopReason));
1638
1907
  }
1639
1908
  const llmSpan = traceShipper.startSpan({
1640
1909
  name: model != null ? model : "llm",
@@ -1643,6 +1912,13 @@ function createSubscriber(agent, eventShipper, traceShipper, defaultOptions, opt
1643
1912
  attributes: llmAttrs
1644
1913
  });
1645
1914
  const errorForSpan = stopReason === "error" || stopReason === "aborted" ? "errorMessage" in message && typeof message.errorMessage === "string" ? message.errorMessage : `Assistant ${stopReason}` : void 0;
1915
+ if (errorForSpan) {
1916
+ const assistantError = new Error(errorForSpan);
1917
+ assistantError.name = stopReason === "aborted" ? "AbortError" : "PiAgentError";
1918
+ currentRun.error = assistantError;
1919
+ } else {
1920
+ currentRun.error = void 0;
1921
+ }
1646
1922
  traceShipper.endSpan(llmSpan, errorForSpan ? { error: errorForSpan } : void 0);
1647
1923
  const toolCallIds = extractToolCallIds(message);
1648
1924
  for (const tcId of toolCallIds) {
@@ -1729,9 +2005,9 @@ function createSubscriber(agent, eventShipper, traceShipper, defaultOptions, opt
1729
2005
  if (!traceShipper || !toolSpan) return;
1730
2006
  const endAttrs = [];
1731
2007
  const argsStr = truncate(safeStringify(args));
1732
- if (argsStr) endAttrs.push(attrString("ai.toolCall.args", argsStr));
2008
+ if (argsStr) pushAttr(endAttrs, attrString("ai.toolCall.args", argsStr));
1733
2009
  const resultStr = truncate(safeStringify(result));
1734
- if (resultStr) endAttrs.push(attrString("ai.toolCall.result", resultStr));
2010
+ if (resultStr) pushAttr(endAttrs, attrString("ai.toolCall.result", resultStr));
1735
2011
  traceShipper.endSpan(toolSpan, {
1736
2012
  attributes: endAttrs,
1737
2013
  ...isError ? { error: `Tool "${toolName}" failed` } : {}
@@ -1758,42 +2034,44 @@ function createSubscriber(agent, eventShipper, traceShipper, defaultOptions, opt
1758
2034
  }
1759
2035
  const outputText = capText2(run.outputParts.join(""));
1760
2036
  if (traceShipper && run.rootSpan) {
1761
- const rootAttrs = [
1762
- attrString("ai.operationId", "generateText")
1763
- ];
2037
+ const rootAttrs = [];
2038
+ pushAttr(rootAttrs, attrString("ai.operationId", "generateText"));
1764
2039
  if (run.lastModel) {
1765
2040
  run.rootSpan.name = run.lastProvider ? `${run.lastProvider}/${run.lastModel}` : run.lastModel;
1766
- rootAttrs.push(attrString("gen_ai.response.model", run.lastModel));
2041
+ pushAttr(rootAttrs, attrString("gen_ai.response.model", run.lastModel));
1767
2042
  }
1768
2043
  if (run.currentInput) {
1769
- rootAttrs.push(attrString("ai.prompt", truncate(run.currentInput)));
2044
+ pushAttr(rootAttrs, attrString("ai.prompt", truncate(run.currentInput)));
1770
2045
  }
1771
2046
  if (outputText) {
1772
- rootAttrs.push(attrString("ai.response.text", truncate(outputText)));
2047
+ pushAttr(rootAttrs, attrString("ai.response.text", truncate(outputText)));
1773
2048
  }
1774
2049
  if (run.totalInputTokens > 0) {
1775
- rootAttrs.push(attrInt("gen_ai.usage.input_tokens", run.totalInputTokens));
2050
+ pushAttr(rootAttrs, attrInt("gen_ai.usage.input_tokens", run.totalInputTokens));
1776
2051
  }
1777
2052
  if (run.totalOutputTokens > 0) {
1778
- rootAttrs.push(attrInt("gen_ai.usage.output_tokens", run.totalOutputTokens));
2053
+ pushAttr(rootAttrs, attrInt("gen_ai.usage.output_tokens", run.totalOutputTokens));
1779
2054
  }
1780
2055
  if (run.totalCacheReadTokens > 0) {
1781
- rootAttrs.push(attrInt("gen_ai.usage.cache_read_tokens", run.totalCacheReadTokens));
2056
+ pushAttr(rootAttrs, attrInt("gen_ai.usage.cache_read_tokens", run.totalCacheReadTokens));
1782
2057
  }
1783
- rootAttrs.push(attrInt("ai.total_turns", run.turnNumber));
2058
+ pushAttr(rootAttrs, attrInt("ai.total_turns", run.turnNumber));
1784
2059
  traceShipper.endSpan(run.rootSpan, { attributes: rootAttrs });
1785
2060
  }
1786
2061
  if (eventShipper) {
1787
2062
  eventShipper.finish(run.eventId, {
1788
2063
  userId: userId != null ? userId : "anonymous",
1789
2064
  model: run.lastModel || void 0,
1790
- output: outputText || void 0,
2065
+ output: run.error ? void 0 : outputText || void 0,
2066
+ usage: {
2067
+ promptTokens: run.totalInputTokens > 0 ? run.totalInputTokens : void 0,
2068
+ completionTokens: run.totalOutputTokens > 0 ? run.totalOutputTokens : void 0
2069
+ },
2070
+ error: run.error,
1791
2071
  properties: {
1792
2072
  ...properties,
1793
2073
  sdk_version: libraryVersion,
1794
2074
  ...run.lastProvider ? { "ai.provider": run.lastProvider } : {},
1795
- ...run.totalInputTokens > 0 ? { "ai.usage.prompt_tokens": run.totalInputTokens } : {},
1796
- ...run.totalOutputTokens > 0 ? { "ai.usage.completion_tokens": run.totalOutputTokens } : {},
1797
2075
  ...run.totalCacheReadTokens > 0 ? { "ai.usage.cache_read_tokens": run.totalCacheReadTokens } : {}
1798
2076
  }
1799
2077
  }).catch(() => {
@@ -1894,6 +2172,7 @@ function createRaindropPiAgent(opts) {
1894
2172
  userId: opts.userId,
1895
2173
  convoId: opts.convoId,
1896
2174
  eventName: opts.eventName,
2175
+ eventId: opts.eventId,
1897
2176
  properties: opts.properties
1898
2177
  };
1899
2178
  return {
@@ -45,6 +45,10 @@ type IdentifyInput = {
45
45
  userId: string;
46
46
  traits?: Record<string, unknown>;
47
47
  };
48
+ type EventUsage = {
49
+ promptTokens?: number;
50
+ completionTokens?: number;
51
+ };
48
52
  type Patch = {
49
53
  eventName?: string;
50
54
  userId?: string;
@@ -52,6 +56,8 @@ type Patch = {
52
56
  input?: string;
53
57
  output?: string;
54
58
  model?: string;
59
+ usage?: EventUsage;
60
+ error?: unknown;
55
61
  properties?: Record<string, unknown>;
56
62
  featureFlags?: Record<string, string>;
57
63
  attachments?: Attachment[];
@@ -150,13 +156,7 @@ declare class EventShipper {
150
156
  */
151
157
  private requestOpts;
152
158
  patch(eventId: string, patch: Patch): Promise<void>;
153
- finish(eventId: string, patch: {
154
- output?: string;
155
- model?: string;
156
- properties?: Record<string, unknown>;
157
- featureFlags?: Record<string, string>;
158
- userId?: string;
159
- }): Promise<void>;
159
+ finish(eventId: string, patch: Pick<Patch, "output" | "model" | "usage" | "error" | "properties" | "featureFlags" | "userId">): Promise<void>;
160
160
  flush(): Promise<void>;
161
161
  shutdown(): Promise<void>;
162
162
  trackSignal(signal: SignalInput): Promise<void>;
@@ -329,6 +329,9 @@ declare class TraceShipper {
329
329
  spanIdB64: string;
330
330
  };
331
331
  eventId: string;
332
+ userId?: string;
333
+ convoId?: string;
334
+ eventName?: string;
332
335
  operationId?: string;
333
336
  attributes?: Array<OtlpKeyValue | undefined>;
334
337
  startTimeUnixNano?: string;
@@ -347,6 +350,9 @@ declare class TraceShipper {
347
350
  spanIdB64: string;
348
351
  };
349
352
  eventId: string;
353
+ userId?: string;
354
+ convoId?: string;
355
+ eventName?: string;
350
356
  startTimeUnixNano: string;
351
357
  endTimeUnixNano: string;
352
358
  attributes?: Array<OtlpKeyValue | undefined>;
@@ -45,6 +45,10 @@ type IdentifyInput = {
45
45
  userId: string;
46
46
  traits?: Record<string, unknown>;
47
47
  };
48
+ type EventUsage = {
49
+ promptTokens?: number;
50
+ completionTokens?: number;
51
+ };
48
52
  type Patch = {
49
53
  eventName?: string;
50
54
  userId?: string;
@@ -52,6 +56,8 @@ type Patch = {
52
56
  input?: string;
53
57
  output?: string;
54
58
  model?: string;
59
+ usage?: EventUsage;
60
+ error?: unknown;
55
61
  properties?: Record<string, unknown>;
56
62
  featureFlags?: Record<string, string>;
57
63
  attachments?: Attachment[];
@@ -150,13 +156,7 @@ declare class EventShipper {
150
156
  */
151
157
  private requestOpts;
152
158
  patch(eventId: string, patch: Patch): Promise<void>;
153
- finish(eventId: string, patch: {
154
- output?: string;
155
- model?: string;
156
- properties?: Record<string, unknown>;
157
- featureFlags?: Record<string, string>;
158
- userId?: string;
159
- }): Promise<void>;
159
+ finish(eventId: string, patch: Pick<Patch, "output" | "model" | "usage" | "error" | "properties" | "featureFlags" | "userId">): Promise<void>;
160
160
  flush(): Promise<void>;
161
161
  shutdown(): Promise<void>;
162
162
  trackSignal(signal: SignalInput): Promise<void>;
@@ -329,6 +329,9 @@ declare class TraceShipper {
329
329
  spanIdB64: string;
330
330
  };
331
331
  eventId: string;
332
+ userId?: string;
333
+ convoId?: string;
334
+ eventName?: string;
332
335
  operationId?: string;
333
336
  attributes?: Array<OtlpKeyValue | undefined>;
334
337
  startTimeUnixNano?: string;
@@ -347,6 +350,9 @@ declare class TraceShipper {
347
350
  spanIdB64: string;
348
351
  };
349
352
  eventId: string;
353
+ userId?: string;
354
+ convoId?: string;
355
+ eventName?: string;
350
356
  startTimeUnixNano: string;
351
357
  endTimeUnixNano: string;
352
358
  attributes?: Array<OtlpKeyValue | undefined>;