raindrop-ai 0.3.0 → 0.3.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.js CHANGED
@@ -5778,7 +5778,7 @@ if (!globalThis.RAINDROP_ASYNC_LOCAL_STORAGE) {
5778
5778
  globalThis.RAINDROP_ASYNC_LOCAL_STORAGE = import_node_async_hooks.AsyncLocalStorage;
5779
5779
  }
5780
5780
 
5781
- // ../core/dist/chunk-YUIXRQWN.js
5781
+ // ../core/dist/chunk-3V4MIA75.js
5782
5782
  function normalizeFeatureFlags(input) {
5783
5783
  if (Array.isArray(input)) {
5784
5784
  return Object.fromEntries(input.map((name) => [name, "true"]));
@@ -6326,6 +6326,20 @@ function stringifyBounded(value, limit) {
6326
6326
  }
6327
6327
  return json.length <= max ? json : truncateToLimit(json, max);
6328
6328
  }
6329
+ function attrString(key, value) {
6330
+ if (value === void 0) return void 0;
6331
+ return { key, value: { stringValue: value } };
6332
+ }
6333
+ function attrInt(key, value) {
6334
+ if (value === void 0) return void 0;
6335
+ if (!Number.isFinite(value)) return void 0;
6336
+ return { key, value: { intValue: String(Math.trunc(value)) } };
6337
+ }
6338
+ function attrDouble(key, value) {
6339
+ if (value === void 0) return void 0;
6340
+ if (!Number.isFinite(value)) return void 0;
6341
+ return { key, value: { doubleValue: value } };
6342
+ }
6329
6343
  var LOCAL_DEBUGGER_ENV_VAR = "RAINDROP_LOCAL_DEBUGGER";
6330
6344
  var WORKSHOP_ENV_VAR = "RAINDROP_WORKSHOP";
6331
6345
  var DEFAULT_LOCAL_WORKSHOP_URL = "http://localhost:5899/v1/";
@@ -6456,6 +6470,7 @@ function projectIdHeaders(projectId) {
6456
6470
  var SHUTDOWN_DEADLINE_MS = 1e4;
6457
6471
  var POST_SHUTDOWN_TIMEOUT_MS = 5e3;
6458
6472
  var MODEL_USAGE_ATTRIBUTES = {
6473
+ usageSource: "raindrop.usage.source",
6459
6474
  providerName: "gen_ai.provider.name",
6460
6475
  requestModel: "gen_ai.request.model",
6461
6476
  responseModel: "gen_ai.response.model",
@@ -6464,6 +6479,7 @@ var MODEL_USAGE_ATTRIBUTES = {
6464
6479
  reasoningTokens: "gen_ai.usage.reasoning_tokens",
6465
6480
  cacheReadInputTokens: "gen_ai.usage.cache_read_input_tokens",
6466
6481
  cacheWriteInputTokens: "gen_ai.usage.cache_write_input_tokens",
6482
+ providerReportedCost: "gen_ai.usage.provider_reported_cost",
6467
6483
  nonCachedInputTokens: "raindrop.usage.input_tokens.non_cached",
6468
6484
  nonReasoningOutputTokens: "raindrop.usage.output_tokens.non_reasoning",
6469
6485
  totalOutputTokens: "raindrop.usage.output_tokens.total",
@@ -6471,6 +6487,47 @@ var MODEL_USAGE_ATTRIBUTES = {
6471
6487
  cacheReadTokens: "raindrop.usage.input_tokens.cache_read",
6472
6488
  cacheWriteTokens: "raindrop.usage.input_tokens.cache_write"
6473
6489
  };
6490
+ var MODEL_PROVIDER_NAMES = [
6491
+ ["google.vertex.anthropic", "google-vertex"],
6492
+ ["google.vertex_ai", "google-vertex"],
6493
+ ["google.vertex", "google-vertex"],
6494
+ ["gcp.vertex_ai", "google-vertex"],
6495
+ ["gcp.gemini", "google"],
6496
+ ["google_vertexai", "google-vertex"],
6497
+ ["vertex.anthropic", "google-vertex"],
6498
+ ["amazon-bedrock", "amazon-bedrock"],
6499
+ ["aws.bedrock", "amazon-bedrock"],
6500
+ ["bedrock", "amazon-bedrock"],
6501
+ ["anthropic", "anthropic"],
6502
+ ["openai", "openai"],
6503
+ ["google", "google"],
6504
+ ["az.ai.openai", "azure"],
6505
+ ["azure", "azure"],
6506
+ ["openrouter", "openrouter"]
6507
+ ];
6508
+ function canonicalModelProvider(provider) {
6509
+ var _a;
6510
+ const normalizedProvider = provider.trim().toLowerCase();
6511
+ if (normalizedProvider === "gateway") return "vercel";
6512
+ const match = MODEL_PROVIDER_NAMES.find(
6513
+ ([family]) => normalizedProvider === family || normalizedProvider.startsWith(`${family}.`)
6514
+ );
6515
+ return (_a = match == null ? void 0 : match[1]) != null ? _a : normalizedProvider;
6516
+ }
6517
+ function tokenCount(value) {
6518
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : void 0;
6519
+ }
6520
+ function buildRawModelUsageAttributes(usage) {
6521
+ const providerReportedCost = typeof usage.providerReportedCost === "number" && Number.isFinite(usage.providerReportedCost) && usage.providerReportedCost >= 0 ? usage.providerReportedCost : void 0;
6522
+ return [
6523
+ attrInt(MODEL_USAGE_ATTRIBUTES.inputTokens, tokenCount(usage.inputTokens)),
6524
+ attrInt(MODEL_USAGE_ATTRIBUTES.outputTokens, tokenCount(usage.outputTokens)),
6525
+ attrInt(MODEL_USAGE_ATTRIBUTES.reasoningTokens, tokenCount(usage.reasoningTokens)),
6526
+ attrInt(MODEL_USAGE_ATTRIBUTES.cacheReadInputTokens, tokenCount(usage.cacheReadInputTokens)),
6527
+ attrInt(MODEL_USAGE_ATTRIBUTES.cacheWriteInputTokens, tokenCount(usage.cacheWriteInputTokens)),
6528
+ attrDouble(MODEL_USAGE_ATTRIBUTES.providerReportedCost, providerReportedCost)
6529
+ ].filter((attribute) => attribute !== void 0);
6530
+ }
6474
6531
  var STRING_ALIASES = {
6475
6532
  [MODEL_USAGE_ATTRIBUTES.requestModel]: ["ai.model.id", "ai.model"]
6476
6533
  };
@@ -10920,7 +10977,7 @@ var SignalEventSchema = external_exports.object({
10920
10977
  // package.json
10921
10978
  var package_default = {
10922
10979
  name: "raindrop-ai",
10923
- version: "0.3.0",
10980
+ version: "0.3.1",
10924
10981
  main: "dist/index.js",
10925
10982
  module: "dist/index.mjs",
10926
10983
  types: "dist/index.d.ts",
@@ -11602,7 +11659,7 @@ var DirectTraceShipper = class {
11602
11659
  * so spans still appear as children in the trace tree.
11603
11660
  */
11604
11661
  sendSpan(params) {
11605
- var _a, _b;
11662
+ var _a, _b, _c;
11606
11663
  if (!this.enabled) return;
11607
11664
  let traceIdB64 = params.traceId;
11608
11665
  let parentSpanIdB64 = params.parentSpanId;
@@ -11646,6 +11703,7 @@ var DirectTraceShipper = class {
11646
11703
  })) {
11647
11704
  attributes.push(attrString2(key, value));
11648
11705
  }
11706
+ attributes.push(...(_c = params.otlpAttributes) != null ? _c : []);
11649
11707
  let status;
11650
11708
  if (params.error) {
11651
11709
  status = { code: 2, message: params.error };
@@ -11797,9 +11855,62 @@ var DirectTraceShipper = class {
11797
11855
  }
11798
11856
  };
11799
11857
 
11858
+ // src/tracing/exit-flush.ts
11859
+ var traceloop = __toESM(require("@traceloop/node-server-sdk"));
11860
+ var STATE_KEY2 = /* @__PURE__ */ Symbol.for("raindrop.tracing.exitFlushState");
11861
+ function getState2() {
11862
+ const holder = globalThis;
11863
+ let state = holder[STATE_KEY2];
11864
+ if (!state) {
11865
+ state = {
11866
+ clientFlushers: /* @__PURE__ */ new Set(),
11867
+ pipelineFlushNeeded: false,
11868
+ handlerRegistered: false
11869
+ };
11870
+ holder[STATE_KEY2] = state;
11871
+ }
11872
+ return state;
11873
+ }
11874
+ function ensureExitHandler(state) {
11875
+ if (state.handlerRegistered) return;
11876
+ if (typeof process === "undefined" || typeof process.once !== "function") return;
11877
+ state.handlerRegistered = true;
11878
+ process.once("beforeExit", () => {
11879
+ void flushAll();
11880
+ });
11881
+ }
11882
+ function registerClientFlusher(flush) {
11883
+ const state = getState2();
11884
+ state.clientFlushers.add(flush);
11885
+ ensureExitHandler(state);
11886
+ return () => {
11887
+ state.clientFlushers.delete(flush);
11888
+ };
11889
+ }
11890
+ function markPipelineForExitFlush() {
11891
+ const state = getState2();
11892
+ state.pipelineFlushNeeded = true;
11893
+ ensureExitHandler(state);
11894
+ }
11895
+ async function flushAll() {
11896
+ const state = getState2();
11897
+ const tasks = [];
11898
+ for (const flush of state.clientFlushers) {
11899
+ tasks.push(Promise.resolve().then(flush).catch(() => {
11900
+ }));
11901
+ }
11902
+ if (state.pipelineFlushNeeded) {
11903
+ tasks.push(
11904
+ Promise.resolve().then(() => traceloop.forceFlush()).catch(() => {
11905
+ })
11906
+ );
11907
+ }
11908
+ await Promise.all(tasks);
11909
+ }
11910
+
11800
11911
  // src/tracing/liveInteraction.ts
11801
11912
  var import_api3 = require("@opentelemetry/api");
11802
- var traceloop = __toESM(require("@traceloop/node-server-sdk"));
11913
+ var traceloop2 = __toESM(require("@traceloop/node-server-sdk"));
11803
11914
 
11804
11915
  // src/tracing/subagents.ts
11805
11916
  var import_api2 = require("@opentelemetry/api");
@@ -12223,6 +12334,7 @@ var DirectToolSpan = class {
12223
12334
  };
12224
12335
  var LiveInteraction = class {
12225
12336
  constructor(context6, traceId, analytics, directShipper, routing) {
12337
+ this.usageEmitted = false;
12226
12338
  this.context = context6;
12227
12339
  this.analytics = analytics;
12228
12340
  this.tracer = import_api3.trace.getTracer("traceloop.tracer");
@@ -12307,10 +12419,7 @@ var LiveInteraction = class {
12307
12419
  spanId: base64ToHex2(spanIdB64),
12308
12420
  traceFlags: import_api3.TraceFlags.SAMPLED
12309
12421
  };
12310
- const wrappedContext = import_api3.trace.setSpan(
12311
- import_api3.context.active(),
12312
- import_api3.trace.wrapSpanContext(spanContext)
12313
- );
12422
+ const wrappedContext = import_api3.trace.setSpan(import_api3.context.active(), import_api3.trace.wrapSpanContext(spanContext));
12314
12423
  const invoke = () => this.routing ? withRoutingContext(this.routing, () => fn.apply(thisArg, args)) : fn.apply(thisArg, args);
12315
12424
  try {
12316
12425
  const result = await import_api3.context.with(wrappedContext, invoke);
@@ -12361,7 +12470,7 @@ var LiveInteraction = class {
12361
12470
  }
12362
12471
  return fn.apply(thisArg, fnArgs);
12363
12472
  };
12364
- return traceloop.withTask(
12473
+ return traceloop2.withTask(
12365
12474
  {
12366
12475
  name: taskName,
12367
12476
  associationProperties: serializeAssociationProperties(properties),
@@ -12402,10 +12511,7 @@ var LiveInteraction = class {
12402
12511
  spanId: base64ToHex2(spanIdB64),
12403
12512
  traceFlags: import_api3.TraceFlags.SAMPLED
12404
12513
  };
12405
- const wrappedContext = import_api3.trace.setSpan(
12406
- import_api3.context.active(),
12407
- import_api3.trace.wrapSpanContext(spanContext)
12408
- );
12514
+ const wrappedContext = import_api3.trace.setSpan(import_api3.context.active(), import_api3.trace.wrapSpanContext(spanContext));
12409
12515
  const invoke = () => this.routing ? withRoutingContext(this.routing, () => fn.apply(thisArg, args)) : fn.apply(thisArg, args);
12410
12516
  let result;
12411
12517
  let error;
@@ -12515,7 +12621,7 @@ var LiveInteraction = class {
12515
12621
  throw error;
12516
12622
  }
12517
12623
  };
12518
- return traceloop.withTool(
12624
+ return traceloop2.withTool(
12519
12625
  {
12520
12626
  name: toolName,
12521
12627
  associationProperties: serializeAssociationProperties(properties),
@@ -12818,7 +12924,7 @@ var LiveInteraction = class {
12818
12924
  span.end();
12819
12925
  }
12820
12926
  async finish(resultEvent) {
12821
- var _a, _b, _c, _d;
12927
+ var _a, _b, _c, _d, _e, _f, _g;
12822
12928
  unbindRoutingContext(this.boundContext);
12823
12929
  this.boundContext = void 0;
12824
12930
  unbindSpanAttributes(this.toolEventsFrame);
@@ -12826,6 +12932,15 @@ var LiveInteraction = class {
12826
12932
  if (!this.traceId) {
12827
12933
  this.resolveLiveTraceId();
12828
12934
  }
12935
+ const eventId = resultEvent.eventId || this.context.eventId;
12936
+ if (!this.usageEmitted) {
12937
+ (_c = this.analytics) == null ? void 0 : _c._recordModelUsage(
12938
+ eventId,
12939
+ (_a = resultEvent.model) != null ? _a : this.context.model,
12940
+ (_b = resultEvent.usage) != null ? _b : this.context.usage
12941
+ );
12942
+ this.usageEmitted = true;
12943
+ }
12829
12944
  const spanFlush = (async () => {
12830
12945
  var _a2;
12831
12946
  try {
@@ -12833,21 +12948,20 @@ var LiveInteraction = class {
12833
12948
  } catch (e) {
12834
12949
  }
12835
12950
  })();
12836
- if ((_b = (_a = this.analytics) == null ? void 0 : _a._shouldBlockFinishFlush()) != null ? _b : true) {
12951
+ if ((_e = (_d = this.analytics) == null ? void 0 : _d._shouldBlockFinishFlush()) != null ? _e : true) {
12837
12952
  await spanFlush;
12838
12953
  } else {
12839
12954
  void spanFlush.catch(() => {
12840
12955
  });
12841
12956
  }
12842
- const eventId = resultEvent.eventId || this.context.eventId;
12843
12957
  try {
12844
- await ((_c = this.analytics) == null ? void 0 : _c._drainInFlightPartials(eventId));
12958
+ await ((_f = this.analytics) == null ? void 0 : _f._drainInFlightPartials(eventId));
12845
12959
  } catch (e) {
12846
12960
  }
12847
12961
  if (this.traceId) {
12848
12962
  this.setProperty("trace_id", this.traceId);
12849
12963
  }
12850
- await ((_d = this.analytics) == null ? void 0 : _d._trackAiPartial({
12964
+ await ((_g = this.analytics) == null ? void 0 : _g._trackAiPartial({
12851
12965
  ...resultEvent,
12852
12966
  eventId,
12853
12967
  isPending: false
@@ -12964,7 +13078,7 @@ var LiveInteraction = class {
12964
13078
 
12965
13079
  // src/tracing/liveTracer.ts
12966
13080
  var import_api4 = require("@opentelemetry/api");
12967
- var traceloop2 = __toESM(require("@traceloop/node-server-sdk"));
13081
+ var traceloop3 = __toESM(require("@traceloop/node-server-sdk"));
12968
13082
  var LiveTracer = class {
12969
13083
  constructor(globalProperties, directShipper, routing) {
12970
13084
  this.tracer = import_api4.trace.getTracer("traceloop.tracer");
@@ -13008,7 +13122,7 @@ var LiveTracer = class {
13008
13122
  }
13009
13123
  return fn.apply(thisArg, fnArgs);
13010
13124
  };
13011
- return traceloop2.withTask(
13125
+ return traceloop3.withTask(
13012
13126
  {
13013
13127
  name: taskName,
13014
13128
  associationProperties: properties,
@@ -13120,59 +13234,6 @@ var LiveTracer = class {
13120
13234
  }
13121
13235
  };
13122
13236
 
13123
- // src/tracing/exit-flush.ts
13124
- var traceloop3 = __toESM(require("@traceloop/node-server-sdk"));
13125
- var STATE_KEY2 = /* @__PURE__ */ Symbol.for("raindrop.tracing.exitFlushState");
13126
- function getState2() {
13127
- const holder = globalThis;
13128
- let state = holder[STATE_KEY2];
13129
- if (!state) {
13130
- state = {
13131
- clientFlushers: /* @__PURE__ */ new Set(),
13132
- pipelineFlushNeeded: false,
13133
- handlerRegistered: false
13134
- };
13135
- holder[STATE_KEY2] = state;
13136
- }
13137
- return state;
13138
- }
13139
- function ensureExitHandler(state) {
13140
- if (state.handlerRegistered) return;
13141
- if (typeof process === "undefined" || typeof process.once !== "function") return;
13142
- state.handlerRegistered = true;
13143
- process.once("beforeExit", () => {
13144
- void flushAll();
13145
- });
13146
- }
13147
- function registerClientFlusher(flush) {
13148
- const state = getState2();
13149
- state.clientFlushers.add(flush);
13150
- ensureExitHandler(state);
13151
- return () => {
13152
- state.clientFlushers.delete(flush);
13153
- };
13154
- }
13155
- function markPipelineForExitFlush() {
13156
- const state = getState2();
13157
- state.pipelineFlushNeeded = true;
13158
- ensureExitHandler(state);
13159
- }
13160
- async function flushAll() {
13161
- const state = getState2();
13162
- const tasks = [];
13163
- for (const flush of state.clientFlushers) {
13164
- tasks.push(Promise.resolve().then(flush).catch(() => {
13165
- }));
13166
- }
13167
- if (state.pipelineFlushNeeded) {
13168
- tasks.push(
13169
- Promise.resolve().then(() => traceloop3.forceFlush()).catch(() => {
13170
- })
13171
- );
13172
- }
13173
- await Promise.all(tasks);
13174
- }
13175
-
13176
13237
  // src/tracing/tracer-core.ts
13177
13238
  if (localDebuggerEnabled()) {
13178
13239
  if (!process.env.OTEL_EXPORTER_OTLP_PROTOCOL) {
@@ -13890,6 +13951,59 @@ var tracing = (analytics, apiUrl, writeKey, options, isDebug = false) => {
13890
13951
  ...frame ? { frame, frameOwner } : {}
13891
13952
  });
13892
13953
  };
13954
+ const recordModelUsage = (params) => {
13955
+ try {
13956
+ const attributes = [
13957
+ attrString(MODEL_USAGE_ATTRIBUTES.usageSource, "manual"),
13958
+ attrString(
13959
+ MODEL_USAGE_ATTRIBUTES.providerName,
13960
+ canonicalModelProvider(params.usage.provider)
13961
+ ),
13962
+ attrString(MODEL_USAGE_ATTRIBUTES.requestModel, params.model),
13963
+ attrString(MODEL_USAGE_ATTRIBUTES.responseModel, params.model),
13964
+ ...buildRawModelUsageAttributes({
13965
+ inputTokens: params.usage.inputTokens,
13966
+ outputTokens: params.usage.outputTokens,
13967
+ cacheReadInputTokens: params.usage.cacheReadTokens,
13968
+ cacheWriteInputTokens: params.usage.cacheWriteTokens,
13969
+ reasoningTokens: params.usage.reasoningTokens,
13970
+ providerReportedCost: params.usage.providerReportedCost
13971
+ })
13972
+ ].filter((attribute) => attribute !== void 0);
13973
+ if (directShipper) {
13974
+ const timeMs = Date.now();
13975
+ directShipper.sendSpan({
13976
+ name: "ai.model.usage",
13977
+ spanKind: "llm",
13978
+ properties: { event_id: params.eventId },
13979
+ startTimeMs: timeMs,
13980
+ endTimeMs: timeMs,
13981
+ durationMs: 0,
13982
+ otlpAttributes: attributes
13983
+ });
13984
+ return;
13985
+ }
13986
+ const span = import_api5.trace.getTracer("traceloop.tracer").startSpan("ai.model.usage");
13987
+ span.setAttribute("traceloop.span.kind", "llm");
13988
+ span.setAttribute("traceloop.association.properties.event_id", params.eventId);
13989
+ stampSpan(span, projectId, authHint);
13990
+ for (const attribute of attributes) {
13991
+ const value = attribute.value;
13992
+ if (value.stringValue !== void 0) {
13993
+ span.setAttribute(attribute.key, value.stringValue);
13994
+ } else if (value.intValue !== void 0) {
13995
+ const parsed = Number(value.intValue);
13996
+ if (Number.isSafeInteger(parsed)) span.setAttribute(attribute.key, parsed);
13997
+ } else if (value.doubleValue !== void 0) {
13998
+ span.setAttribute(attribute.key, value.doubleValue);
13999
+ } else if (value.boolValue !== void 0) {
14000
+ span.setAttribute(attribute.key, value.boolValue);
14001
+ }
14002
+ }
14003
+ span.end();
14004
+ } catch (e) {
14005
+ }
14006
+ };
13893
14007
  return {
13894
14008
  /**
13895
14009
  * Returns instrumentation instances for your NodeSDK.
@@ -14001,6 +14115,7 @@ var tracing = (analytics, apiUrl, writeKey, options, isDebug = false) => {
14001
14115
  begin(traceContext) {
14002
14116
  return beginInteraction(traceContext);
14003
14117
  },
14118
+ recordModelUsage,
14004
14119
  resumeSubagent(carrierOrHeaders, options2 = {}) {
14005
14120
  return openSubagentRun(carrierOrHeaders, options2);
14006
14121
  },
@@ -14081,6 +14196,15 @@ var PartialClientAiTrack = ClientAiTrack.extend({
14081
14196
  convo_id: import_zod.z.string().optional().nullable()
14082
14197
  }).optional()
14083
14198
  });
14199
+ var AiTrackUsageSchema = import_zod.z.object({
14200
+ provider: import_zod.z.string().trim().min(1),
14201
+ inputTokens: import_zod.z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER),
14202
+ outputTokens: import_zod.z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER),
14203
+ cacheReadTokens: import_zod.z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER).optional(),
14204
+ cacheWriteTokens: import_zod.z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER).optional(),
14205
+ reasoningTokens: import_zod.z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER).optional(),
14206
+ providerReportedCost: import_zod.z.number().finite().nonnegative().optional()
14207
+ });
14084
14208
  var AttachmentTypeSchema = import_zod.z.enum(["code", "text", "image", "iframe"]);
14085
14209
 
14086
14210
  // src/index.ts
@@ -14588,6 +14712,7 @@ var Raindrop = class {
14588
14712
  attachments,
14589
14713
  properties,
14590
14714
  featureFlags,
14715
+ usage,
14591
14716
  timestamp,
14592
14717
  ...rest
14593
14718
  } = e;
@@ -14637,6 +14762,7 @@ var Raindrop = class {
14637
14762
  type: "ai",
14638
14763
  ...toSave
14639
14764
  });
14765
+ this._recordModelUsage(customEventId, model, usage);
14640
14766
  return customEventId;
14641
14767
  };
14642
14768
  if (Array.isArray(event)) {
@@ -15062,6 +15188,7 @@ var Raindrop = class {
15062
15188
  attachments,
15063
15189
  properties,
15064
15190
  featureFlags,
15191
+ usage: _usage,
15065
15192
  ...rest
15066
15193
  } = accumulatedEvent;
15067
15194
  const normalizedFlags = featureFlags ? normalizeFeatureFlags(featureFlags) : void 0;
@@ -15205,6 +15332,29 @@ var Raindrop = class {
15205
15332
  } catch (e) {
15206
15333
  }
15207
15334
  }
15335
+ /**
15336
+ * Emit one model span carrying raw provider usage facts.
15337
+ * @internal
15338
+ */
15339
+ _recordModelUsage(eventId, model, usage) {
15340
+ if (usage === void 0) return;
15341
+ const parsed = AiTrackUsageSchema.safeParse(usage);
15342
+ if (!parsed.success) {
15343
+ console.warn(
15344
+ `[raindrop] Invalid usage provided to .track_ai(): ${this.formatZodError(parsed.error)}`
15345
+ );
15346
+ return;
15347
+ }
15348
+ if (typeof model !== "string" || model.trim() === "") {
15349
+ console.warn("[raindrop] .track_ai() usage requires a non-empty model.");
15350
+ return;
15351
+ }
15352
+ this._tracing.recordModelUsage({
15353
+ eventId,
15354
+ model,
15355
+ usage: parsed.data
15356
+ });
15357
+ }
15208
15358
  /**
15209
15359
  * Wait for already-issued partial-event POSTs to settle. Used to order the
15210
15360
  * terminal partial-event POST after any earlier in-flight POST for the same
package/dist/index.mjs CHANGED
@@ -21,7 +21,7 @@ import {
21
21
  stringifyBounded,
22
22
  tracing,
23
23
  withRoutingContext
24
- } from "./chunk-5Y3LWBKV.mjs";
24
+ } from "./chunk-4TM4IWHB.mjs";
25
25
  import {
26
26
  __commonJS,
27
27
  __toESM
@@ -9973,6 +9973,15 @@ var PartialClientAiTrack = ClientAiTrack.extend({
9973
9973
  convo_id: z.string().optional().nullable()
9974
9974
  }).optional()
9975
9975
  });
9976
+ var AiTrackUsageSchema = z.object({
9977
+ provider: z.string().trim().min(1),
9978
+ inputTokens: z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER),
9979
+ outputTokens: z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER),
9980
+ cacheReadTokens: z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER).optional(),
9981
+ cacheWriteTokens: z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER).optional(),
9982
+ reasoningTokens: z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER).optional(),
9983
+ providerReportedCost: z.number().finite().nonnegative().optional()
9984
+ });
9976
9985
  var AttachmentTypeSchema = z.enum(["code", "text", "image", "iframe"]);
9977
9986
 
9978
9987
  // src/index.ts
@@ -10480,6 +10489,7 @@ var Raindrop = class {
10480
10489
  attachments,
10481
10490
  properties,
10482
10491
  featureFlags,
10492
+ usage,
10483
10493
  timestamp,
10484
10494
  ...rest
10485
10495
  } = e;
@@ -10529,6 +10539,7 @@ var Raindrop = class {
10529
10539
  type: "ai",
10530
10540
  ...toSave
10531
10541
  });
10542
+ this._recordModelUsage(customEventId, model, usage);
10532
10543
  return customEventId;
10533
10544
  };
10534
10545
  if (Array.isArray(event)) {
@@ -10954,6 +10965,7 @@ var Raindrop = class {
10954
10965
  attachments,
10955
10966
  properties,
10956
10967
  featureFlags,
10968
+ usage: _usage,
10957
10969
  ...rest
10958
10970
  } = accumulatedEvent;
10959
10971
  const normalizedFlags = featureFlags ? normalizeFeatureFlags(featureFlags) : void 0;
@@ -11097,6 +11109,29 @@ var Raindrop = class {
11097
11109
  } catch (e) {
11098
11110
  }
11099
11111
  }
11112
+ /**
11113
+ * Emit one model span carrying raw provider usage facts.
11114
+ * @internal
11115
+ */
11116
+ _recordModelUsage(eventId, model, usage) {
11117
+ if (usage === void 0) return;
11118
+ const parsed = AiTrackUsageSchema.safeParse(usage);
11119
+ if (!parsed.success) {
11120
+ console.warn(
11121
+ `[raindrop] Invalid usage provided to .track_ai(): ${this.formatZodError(parsed.error)}`
11122
+ );
11123
+ return;
11124
+ }
11125
+ if (typeof model !== "string" || model.trim() === "") {
11126
+ console.warn("[raindrop] .track_ai() usage requires a non-empty model.");
11127
+ return;
11128
+ }
11129
+ this._tracing.recordModelUsage({
11130
+ eventId,
11131
+ model,
11132
+ usage: parsed.data
11133
+ });
11134
+ }
11100
11135
  /**
11101
11136
  * Wait for already-issued partial-event POSTs to settle. Used to order the
11102
11137
  * terminal partial-event POST after any earlier in-flight POST for the same
@@ -1,24 +1,9 @@
1
- import { R as Raindrop, a as RaindropSpanProcessor, P as PartialAiTrackEvent, I as Interaction, C as CarrierHeaders, T as TraceCarrier, S as SubagentResumeOptions, b as SubagentRun, c as Tracer } from '../index-DBdUrUXc.mjs';
2
1
  import { Instrumentation } from '@opentelemetry/instrumentation';
2
+ import { R as Raindrop, a as RaindropSpanProcessor, P as PartialAiTrackEvent, I as Interaction, A as AiTrackUsage, C as CarrierHeaders, T as TraceCarrier, S as SubagentResumeOptions, b as SubagentRun, c as Tracer } from '../index-DuXeVnSq.mjs';
3
3
  import * as traceloop from '@traceloop/node-server-sdk';
4
4
  import '@opentelemetry/api';
5
5
  import 'zod';
6
6
 
7
- /**
8
- * Tracing core - the real tracing implementation.
9
- *
10
- * Two modes:
11
- *
12
- * 1. DEFAULT: Raindrop manages everything
13
- * - Creates NodeSDK, registers instrumentations, exports traces
14
- * - Customer just uses raindrop.begin(), withSpan(), etc.
15
- *
16
- * 2. EXTERNAL OTEL (useExternalOtel: true): Customer has their own NodeSDK
17
- * - Raindrop does NOT create a NodeSDK (would conflict!)
18
- * - Customer adds raindrop.createSpanProcessor() to their NodeSDK
19
- * - Customer calls raindrop.getInstrumentations() to get configured instrumentations
20
- */
21
-
22
7
  interface TracingOptions extends traceloop.InitializeOptions {
23
8
  /**
24
9
  * Set to true when you have your own OpenTelemetry NodeSDK.
@@ -103,6 +88,11 @@ declare const tracing: (analytics: Raindrop, apiUrl: string, writeKey: string, o
103
88
  */
104
89
  createSpanProcessor(processorOptions?: traceloop.SpanProcessorOptions): RaindropSpanProcessor;
105
90
  begin(traceContext: PartialAiTrackEvent): Interaction;
91
+ recordModelUsage: (params: {
92
+ eventId: string;
93
+ model: string;
94
+ usage: AiTrackUsage;
95
+ }) => void;
106
96
  resumeSubagent(carrierOrHeaders: CarrierHeaders | TraceCarrier | null | undefined, options?: SubagentResumeOptions): SubagentRun;
107
97
  withSubagentRun<T>(carrierOrHeaders: CarrierHeaders | TraceCarrier | null | undefined, fn: (run: SubagentRun) => Promise<T> | T, options?: SubagentResumeOptions): Promise<T>;
108
98
  getActiveInteraction(eventId: string): Interaction;
@@ -1,24 +1,9 @@
1
- import { R as Raindrop, a as RaindropSpanProcessor, P as PartialAiTrackEvent, I as Interaction, C as CarrierHeaders, T as TraceCarrier, S as SubagentResumeOptions, b as SubagentRun, c as Tracer } from '../index-DBdUrUXc.js';
2
1
  import { Instrumentation } from '@opentelemetry/instrumentation';
2
+ import { R as Raindrop, a as RaindropSpanProcessor, P as PartialAiTrackEvent, I as Interaction, A as AiTrackUsage, C as CarrierHeaders, T as TraceCarrier, S as SubagentResumeOptions, b as SubagentRun, c as Tracer } from '../index-DuXeVnSq.js';
3
3
  import * as traceloop from '@traceloop/node-server-sdk';
4
4
  import '@opentelemetry/api';
5
5
  import 'zod';
6
6
 
7
- /**
8
- * Tracing core - the real tracing implementation.
9
- *
10
- * Two modes:
11
- *
12
- * 1. DEFAULT: Raindrop manages everything
13
- * - Creates NodeSDK, registers instrumentations, exports traces
14
- * - Customer just uses raindrop.begin(), withSpan(), etc.
15
- *
16
- * 2. EXTERNAL OTEL (useExternalOtel: true): Customer has their own NodeSDK
17
- * - Raindrop does NOT create a NodeSDK (would conflict!)
18
- * - Customer adds raindrop.createSpanProcessor() to their NodeSDK
19
- * - Customer calls raindrop.getInstrumentations() to get configured instrumentations
20
- */
21
-
22
7
  interface TracingOptions extends traceloop.InitializeOptions {
23
8
  /**
24
9
  * Set to true when you have your own OpenTelemetry NodeSDK.
@@ -103,6 +88,11 @@ declare const tracing: (analytics: Raindrop, apiUrl: string, writeKey: string, o
103
88
  */
104
89
  createSpanProcessor(processorOptions?: traceloop.SpanProcessorOptions): RaindropSpanProcessor;
105
90
  begin(traceContext: PartialAiTrackEvent): Interaction;
91
+ recordModelUsage: (params: {
92
+ eventId: string;
93
+ model: string;
94
+ usage: AiTrackUsage;
95
+ }) => void;
106
96
  resumeSubagent(carrierOrHeaders: CarrierHeaders | TraceCarrier | null | undefined, options?: SubagentResumeOptions): SubagentRun;
107
97
  withSubagentRun<T>(carrierOrHeaders: CarrierHeaders | TraceCarrier | null | undefined, fn: (run: SubagentRun) => Promise<T> | T, options?: SubagentResumeOptions): Promise<T>;
108
98
  getActiveInteraction(eventId: string): Interaction;