@raindrop-ai/langchain 0.0.7 → 0.0.9

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 CHANGED
@@ -47,6 +47,7 @@ await raindrop.flush();
47
47
  | `userId` | `string` | - | Associate all events with a user |
48
48
  | `convoId` | `string` | - | Group events into a conversation |
49
49
  | `projectId` | `string` | - | Route events to a specific project (slug); omit for the default **Production** project |
50
+ | `eventName` | `string` | `ai_generation` | Event name applied to every event (the `event` field in the dashboard) |
50
51
  | `debug` | `boolean` | `false` | Enable verbose logging |
51
52
  | `traceChains` | `boolean` | `true` | Create spans for chain execution |
52
53
  | `traceRetrievers` | `boolean` | `true` | Create spans for retriever calls |
package/dist/index.d.mts CHANGED
@@ -61,6 +61,7 @@ type Patch = {
61
61
  output?: string;
62
62
  model?: string;
63
63
  properties?: Record<string, unknown>;
64
+ featureFlags?: Record<string, string>;
64
65
  attachments?: Attachment[];
65
66
  isPending?: boolean;
66
67
  timestamp?: string;
@@ -161,6 +162,7 @@ declare class EventShipper {
161
162
  output?: string;
162
163
  model?: string;
163
164
  properties?: Record<string, unknown>;
165
+ featureFlags?: Record<string, string>;
164
166
  userId?: string;
165
167
  }): Promise<void>;
166
168
  flush(): Promise<void>;
@@ -365,6 +367,42 @@ declare class TraceShipper {
365
367
  shutdown(): Promise<void>;
366
368
  }
367
369
 
370
+ /**
371
+ * Run telemetry egress with OpenTelemetry tracing suppressed.
372
+ *
373
+ * Why this exists
374
+ * ---------------
375
+ * Raindrop integrations ship spans/events over HTTP with the global `fetch`
376
+ * (see {@link ../http.ts `postJson`}). When the host app also runs an OTel
377
+ * fetch/undici instrumentation — e.g. `@vercel/otel`'s `registerOTel`, which
378
+ * every Eve agent installs — that instrumentation wraps *our own* telemetry
379
+ * POSTs in a `fetch POST <endpoint>` span. Those spans are then handed to the
380
+ * very exporter that issued the request, so they get shipped right back to
381
+ * Raindrop and Workshop as standalone "runs" (and, because each export issues
382
+ * another fetch, they feed back on themselves). The result is a run list
383
+ * flooded with `fetch POST .../v1/traces`, `.../events/track_partial` and
384
+ * `.../live` entries that drown out the real agent turns — especially with
385
+ * sub-agents, where every sandbox runs its own instrumentation.
386
+ *
387
+ * The OTel-blessed fix is to mark the active context as "tracing suppressed"
388
+ * around the request; instrumentations check `isTracingSuppressed` and return
389
+ * a no-op span instead of recording one. We do this through a hook stashed on
390
+ * `globalThis` by the Node entrypoint ({@link ../index.node.ts}) so that:
391
+ * - `@opentelemetry/api` / `@opentelemetry/core` stay *optional* — core never
392
+ * hard-depends on them, and the hook is simply absent when they (and thus
393
+ * any instrumentation to suppress) are not installed; and
394
+ * - the browser bundle never pulls in `node:module`, mirroring how core
395
+ * injects `AsyncLocalStorage` via `RAINDROP_ASYNC_LOCAL_STORAGE`.
396
+ *
397
+ * When no hook is present the callback runs unchanged, so suppression is a
398
+ * best-effort no-op rather than a hard requirement.
399
+ */
400
+ /** Hook signature: run `fn` with OTel tracing suppressed, returning its value. */
401
+ type SuppressTracingHook = <T>(fn: () => T) => T;
402
+ declare global {
403
+ var RAINDROP_SUPPRESS_TRACING: SuppressTracingHook | undefined;
404
+ }
405
+
368
406
  type ParentSpanContext = {
369
407
  traceIdB64: string;
370
408
  spanIdB64: string;
@@ -465,6 +503,13 @@ interface LangChainOptions {
465
503
  * resolves to `default` server-side; byte-identical to prior behavior).
466
504
  */
467
505
  projectId?: string;
506
+ /**
507
+ * Event name applied to every event produced by this integration (the
508
+ * `event` field in the Raindrop dashboard). Lets you distinguish LangChain
509
+ * traffic — e.g. `"support_agent"` vs `"billing_agent"` — instead of the
510
+ * generic default. Defaults to `"ai_generation"`.
511
+ */
512
+ eventName?: string;
468
513
  traceChains?: boolean;
469
514
  traceRetrievers?: boolean;
470
515
  /**
package/dist/index.d.ts CHANGED
@@ -61,6 +61,7 @@ type Patch = {
61
61
  output?: string;
62
62
  model?: string;
63
63
  properties?: Record<string, unknown>;
64
+ featureFlags?: Record<string, string>;
64
65
  attachments?: Attachment[];
65
66
  isPending?: boolean;
66
67
  timestamp?: string;
@@ -161,6 +162,7 @@ declare class EventShipper {
161
162
  output?: string;
162
163
  model?: string;
163
164
  properties?: Record<string, unknown>;
165
+ featureFlags?: Record<string, string>;
164
166
  userId?: string;
165
167
  }): Promise<void>;
166
168
  flush(): Promise<void>;
@@ -365,6 +367,42 @@ declare class TraceShipper {
365
367
  shutdown(): Promise<void>;
366
368
  }
367
369
 
370
+ /**
371
+ * Run telemetry egress with OpenTelemetry tracing suppressed.
372
+ *
373
+ * Why this exists
374
+ * ---------------
375
+ * Raindrop integrations ship spans/events over HTTP with the global `fetch`
376
+ * (see {@link ../http.ts `postJson`}). When the host app also runs an OTel
377
+ * fetch/undici instrumentation — e.g. `@vercel/otel`'s `registerOTel`, which
378
+ * every Eve agent installs — that instrumentation wraps *our own* telemetry
379
+ * POSTs in a `fetch POST <endpoint>` span. Those spans are then handed to the
380
+ * very exporter that issued the request, so they get shipped right back to
381
+ * Raindrop and Workshop as standalone "runs" (and, because each export issues
382
+ * another fetch, they feed back on themselves). The result is a run list
383
+ * flooded with `fetch POST .../v1/traces`, `.../events/track_partial` and
384
+ * `.../live` entries that drown out the real agent turns — especially with
385
+ * sub-agents, where every sandbox runs its own instrumentation.
386
+ *
387
+ * The OTel-blessed fix is to mark the active context as "tracing suppressed"
388
+ * around the request; instrumentations check `isTracingSuppressed` and return
389
+ * a no-op span instead of recording one. We do this through a hook stashed on
390
+ * `globalThis` by the Node entrypoint ({@link ../index.node.ts}) so that:
391
+ * - `@opentelemetry/api` / `@opentelemetry/core` stay *optional* — core never
392
+ * hard-depends on them, and the hook is simply absent when they (and thus
393
+ * any instrumentation to suppress) are not installed; and
394
+ * - the browser bundle never pulls in `node:module`, mirroring how core
395
+ * injects `AsyncLocalStorage` via `RAINDROP_ASYNC_LOCAL_STORAGE`.
396
+ *
397
+ * When no hook is present the callback runs unchanged, so suppression is a
398
+ * best-effort no-op rather than a hard requirement.
399
+ */
400
+ /** Hook signature: run `fn` with OTel tracing suppressed, returning its value. */
401
+ type SuppressTracingHook = <T>(fn: () => T) => T;
402
+ declare global {
403
+ var RAINDROP_SUPPRESS_TRACING: SuppressTracingHook | undefined;
404
+ }
405
+
368
406
  type ParentSpanContext = {
369
407
  traceIdB64: string;
370
408
  spanIdB64: string;
@@ -465,6 +503,13 @@ interface LangChainOptions {
465
503
  * resolves to `default` server-side; byte-identical to prior behavior).
466
504
  */
467
505
  projectId?: string;
506
+ /**
507
+ * Event name applied to every event produced by this integration (the
508
+ * `event` field in the Raindrop dashboard). Lets you distinguish LangChain
509
+ * traffic — e.g. `"support_agent"` vs `"billing_agent"` — instead of the
510
+ * generic default. Defaults to `"ai_generation"`.
511
+ */
512
+ eventName?: string;
468
513
  traceChains?: boolean;
469
514
  traceRetrievers?: boolean;
470
515
  /**
package/dist/index.js CHANGED
@@ -28,7 +28,7 @@ module.exports = __toCommonJS(index_exports);
28
28
  // src/callback-handler.ts
29
29
  var import_base = require("@langchain/core/callbacks/base");
30
30
 
31
- // ../core/dist/chunk-SK6EJEO7.js
31
+ // ../core/dist/chunk-ITAZONWL.js
32
32
  function getCrypto() {
33
33
  const c = globalThis.crypto;
34
34
  return c;
@@ -83,6 +83,20 @@ function base64Encode(bytes) {
83
83
  }
84
84
  return out;
85
85
  }
86
+ function runWithTracingSuppressed(fn) {
87
+ const hook = globalThis.RAINDROP_SUPPRESS_TRACING;
88
+ if (typeof hook !== "function") return fn();
89
+ let started = false;
90
+ try {
91
+ return hook(() => {
92
+ started = true;
93
+ return fn();
94
+ });
95
+ } catch (err) {
96
+ if (started) throw err;
97
+ return fn();
98
+ }
99
+ }
86
100
  var DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
87
101
  var MAX_RETRY_DELAY_MS = 3e4;
88
102
  function wait(ms) {
@@ -193,15 +207,17 @@ async function postJson(url, body, headers, opts) {
193
207
  const timeoutMs = (_a = opts.timeoutMs) != null ? _a : DEFAULT_REQUEST_TIMEOUT_MS;
194
208
  await withRetry(
195
209
  async () => {
196
- const resp = await fetch(url, {
197
- method: "POST",
198
- headers: {
199
- "Content-Type": "application/json",
200
- ...headers
201
- },
202
- body: JSON.stringify(body),
203
- signal: AbortSignal.timeout(timeoutMs)
204
- });
210
+ const resp = await runWithTracingSuppressed(
211
+ () => fetch(url, {
212
+ method: "POST",
213
+ headers: {
214
+ "Content-Type": "application/json",
215
+ ...headers
216
+ },
217
+ body: JSON.stringify(body),
218
+ signal: AbortSignal.timeout(timeoutMs)
219
+ })
220
+ );
205
221
  if (!resp.ok) {
206
222
  const text = await resp.text().catch(() => "");
207
223
  const err = new Error(
@@ -402,13 +418,16 @@ function projectIdHeaders(projectId) {
402
418
  var SHUTDOWN_DEADLINE_MS = 1e4;
403
419
  var POST_SHUTDOWN_TIMEOUT_MS = 5e3;
404
420
  function mergePatches(target, source) {
405
- var _a, _b, _c, _d;
421
+ var _a, _b, _c, _d, _e, _f;
406
422
  const out = { ...target, ...source };
407
423
  if (target.properties || source.properties) {
408
424
  out.properties = { ...(_a = target.properties) != null ? _a : {}, ...(_b = source.properties) != null ? _b : {} };
409
425
  }
426
+ if (target.featureFlags || source.featureFlags) {
427
+ out.featureFlags = { ...(_c = target.featureFlags) != null ? _c : {}, ...(_d = source.featureFlags) != null ? _d : {} };
428
+ }
410
429
  if (target.attachments || source.attachments) {
411
- out.attachments = [...(_c = target.attachments) != null ? _c : [], ...(_d = source.attachments) != null ? _d : []];
430
+ out.attachments = [...(_e = target.attachments) != null ? _e : [], ...(_f = source.attachments) != null ? _f : []];
412
431
  }
413
432
  return out;
414
433
  }
@@ -678,6 +697,7 @@ var EventShipper = class {
678
697
  ...wizardSession ? { "raindrop.wizardSession": wizardSession } : {},
679
698
  $context: this.context
680
699
  },
700
+ ...accumulated.featureFlags && Object.keys(accumulated.featureFlags).length > 0 ? { feature_flags: accumulated.featureFlags } : {},
681
701
  attachments: accumulated.attachments,
682
702
  is_pending: isPending
683
703
  };
@@ -1149,6 +1169,31 @@ var TraceShipper = class {
1149
1169
  // ../core/dist/index.node.js
1150
1170
  var import_async_hooks = require("async_hooks");
1151
1171
  globalThis.RAINDROP_ASYNC_LOCAL_STORAGE = import_async_hooks.AsyncLocalStorage;
1172
+ var SUPPRESS_TRACING_KEY = /* @__PURE__ */ Symbol.for(
1173
+ "OpenTelemetry SDK Context Key SUPPRESS_TRACING"
1174
+ );
1175
+ function findOtelContextManager() {
1176
+ var _a;
1177
+ for (const sym of Object.getOwnPropertySymbols(globalThis)) {
1178
+ if (!((_a = sym.description) == null ? void 0 : _a.startsWith("opentelemetry.js.api."))) continue;
1179
+ const api = globalThis[sym];
1180
+ const cm = api == null ? void 0 : api.context;
1181
+ if (cm && typeof cm.with === "function" && typeof cm.active === "function") {
1182
+ return cm;
1183
+ }
1184
+ }
1185
+ return void 0;
1186
+ }
1187
+ function installTracingSuppressionHook() {
1188
+ if (typeof globalThis.RAINDROP_SUPPRESS_TRACING === "function") return;
1189
+ const hook = (fn) => {
1190
+ const cm = findOtelContextManager();
1191
+ if (!cm) return fn();
1192
+ return cm.with(cm.active().setValue(SUPPRESS_TRACING_KEY, true), fn);
1193
+ };
1194
+ globalThis.RAINDROP_SUPPRESS_TRACING = hook;
1195
+ }
1196
+ installTracingSuppressionHook();
1152
1197
 
1153
1198
  // src/truncation.ts
1154
1199
  var TRUNCATION_MARKER2 = "...[truncated by raindrop]";
@@ -1675,7 +1720,7 @@ function extractLLMMetadata(output) {
1675
1720
  // package.json
1676
1721
  var package_default = {
1677
1722
  name: "@raindrop-ai/langchain",
1678
- version: "0.0.7",
1723
+ version: "0.0.9",
1679
1724
  description: "Raindrop integration for LangChain",
1680
1725
  main: "dist/index.js",
1681
1726
  module: "dist/index.mjs",
@@ -1756,7 +1801,8 @@ function createRaindropLangChain(opts) {
1756
1801
  projectId: opts.projectId,
1757
1802
  sdkName: "langchain",
1758
1803
  libraryName,
1759
- libraryVersion
1804
+ libraryVersion,
1805
+ defaultEventName: opts.eventName
1760
1806
  });
1761
1807
  const traceShipper = new TraceShipper({
1762
1808
  writeKey,
package/dist/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  // src/callback-handler.ts
2
2
  import { BaseCallbackHandler } from "@langchain/core/callbacks/base";
3
3
 
4
- // ../core/dist/chunk-SK6EJEO7.js
4
+ // ../core/dist/chunk-ITAZONWL.js
5
5
  function getCrypto() {
6
6
  const c = globalThis.crypto;
7
7
  return c;
@@ -56,6 +56,20 @@ function base64Encode(bytes) {
56
56
  }
57
57
  return out;
58
58
  }
59
+ function runWithTracingSuppressed(fn) {
60
+ const hook = globalThis.RAINDROP_SUPPRESS_TRACING;
61
+ if (typeof hook !== "function") return fn();
62
+ let started = false;
63
+ try {
64
+ return hook(() => {
65
+ started = true;
66
+ return fn();
67
+ });
68
+ } catch (err) {
69
+ if (started) throw err;
70
+ return fn();
71
+ }
72
+ }
59
73
  var DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
60
74
  var MAX_RETRY_DELAY_MS = 3e4;
61
75
  function wait(ms) {
@@ -166,15 +180,17 @@ async function postJson(url, body, headers, opts) {
166
180
  const timeoutMs = (_a = opts.timeoutMs) != null ? _a : DEFAULT_REQUEST_TIMEOUT_MS;
167
181
  await withRetry(
168
182
  async () => {
169
- const resp = await fetch(url, {
170
- method: "POST",
171
- headers: {
172
- "Content-Type": "application/json",
173
- ...headers
174
- },
175
- body: JSON.stringify(body),
176
- signal: AbortSignal.timeout(timeoutMs)
177
- });
183
+ const resp = await runWithTracingSuppressed(
184
+ () => fetch(url, {
185
+ method: "POST",
186
+ headers: {
187
+ "Content-Type": "application/json",
188
+ ...headers
189
+ },
190
+ body: JSON.stringify(body),
191
+ signal: AbortSignal.timeout(timeoutMs)
192
+ })
193
+ );
178
194
  if (!resp.ok) {
179
195
  const text = await resp.text().catch(() => "");
180
196
  const err = new Error(
@@ -375,13 +391,16 @@ function projectIdHeaders(projectId) {
375
391
  var SHUTDOWN_DEADLINE_MS = 1e4;
376
392
  var POST_SHUTDOWN_TIMEOUT_MS = 5e3;
377
393
  function mergePatches(target, source) {
378
- var _a, _b, _c, _d;
394
+ var _a, _b, _c, _d, _e, _f;
379
395
  const out = { ...target, ...source };
380
396
  if (target.properties || source.properties) {
381
397
  out.properties = { ...(_a = target.properties) != null ? _a : {}, ...(_b = source.properties) != null ? _b : {} };
382
398
  }
399
+ if (target.featureFlags || source.featureFlags) {
400
+ out.featureFlags = { ...(_c = target.featureFlags) != null ? _c : {}, ...(_d = source.featureFlags) != null ? _d : {} };
401
+ }
383
402
  if (target.attachments || source.attachments) {
384
- out.attachments = [...(_c = target.attachments) != null ? _c : [], ...(_d = source.attachments) != null ? _d : []];
403
+ out.attachments = [...(_e = target.attachments) != null ? _e : [], ...(_f = source.attachments) != null ? _f : []];
385
404
  }
386
405
  return out;
387
406
  }
@@ -651,6 +670,7 @@ var EventShipper = class {
651
670
  ...wizardSession ? { "raindrop.wizardSession": wizardSession } : {},
652
671
  $context: this.context
653
672
  },
673
+ ...accumulated.featureFlags && Object.keys(accumulated.featureFlags).length > 0 ? { feature_flags: accumulated.featureFlags } : {},
654
674
  attachments: accumulated.attachments,
655
675
  is_pending: isPending
656
676
  };
@@ -1122,6 +1142,31 @@ var TraceShipper = class {
1122
1142
  // ../core/dist/index.node.js
1123
1143
  import { AsyncLocalStorage } from "async_hooks";
1124
1144
  globalThis.RAINDROP_ASYNC_LOCAL_STORAGE = AsyncLocalStorage;
1145
+ var SUPPRESS_TRACING_KEY = /* @__PURE__ */ Symbol.for(
1146
+ "OpenTelemetry SDK Context Key SUPPRESS_TRACING"
1147
+ );
1148
+ function findOtelContextManager() {
1149
+ var _a;
1150
+ for (const sym of Object.getOwnPropertySymbols(globalThis)) {
1151
+ if (!((_a = sym.description) == null ? void 0 : _a.startsWith("opentelemetry.js.api."))) continue;
1152
+ const api = globalThis[sym];
1153
+ const cm = api == null ? void 0 : api.context;
1154
+ if (cm && typeof cm.with === "function" && typeof cm.active === "function") {
1155
+ return cm;
1156
+ }
1157
+ }
1158
+ return void 0;
1159
+ }
1160
+ function installTracingSuppressionHook() {
1161
+ if (typeof globalThis.RAINDROP_SUPPRESS_TRACING === "function") return;
1162
+ const hook = (fn) => {
1163
+ const cm = findOtelContextManager();
1164
+ if (!cm) return fn();
1165
+ return cm.with(cm.active().setValue(SUPPRESS_TRACING_KEY, true), fn);
1166
+ };
1167
+ globalThis.RAINDROP_SUPPRESS_TRACING = hook;
1168
+ }
1169
+ installTracingSuppressionHook();
1125
1170
 
1126
1171
  // src/truncation.ts
1127
1172
  var TRUNCATION_MARKER2 = "...[truncated by raindrop]";
@@ -1648,7 +1693,7 @@ function extractLLMMetadata(output) {
1648
1693
  // package.json
1649
1694
  var package_default = {
1650
1695
  name: "@raindrop-ai/langchain",
1651
- version: "0.0.7",
1696
+ version: "0.0.9",
1652
1697
  description: "Raindrop integration for LangChain",
1653
1698
  main: "dist/index.js",
1654
1699
  module: "dist/index.mjs",
@@ -1729,7 +1774,8 @@ function createRaindropLangChain(opts) {
1729
1774
  projectId: opts.projectId,
1730
1775
  sdkName: "langchain",
1731
1776
  libraryName,
1732
- libraryVersion
1777
+ libraryVersion,
1778
+ defaultEventName: opts.eventName
1733
1779
  });
1734
1780
  const traceShipper = new TraceShipper({
1735
1781
  writeKey,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@raindrop-ai/langchain",
3
- "version": "0.0.7",
3
+ "version": "0.0.9",
4
4
  "description": "Raindrop integration for LangChain",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -31,7 +31,7 @@
31
31
  "tsup": "^8.4.0",
32
32
  "typescript": "^5.3.3",
33
33
  "vitest": "^2.1.9",
34
- "@raindrop-ai/core": "0.0.4"
34
+ "@raindrop-ai/core": "0.1.1"
35
35
  },
36
36
  "tsup": {
37
37
  "entry": [