@silmaril-security/sdk 0.7.10 → 0.8.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.
@@ -148,7 +148,81 @@ function extractTextFromDocuments(documents) {
148
148
  return parts.join("\n");
149
149
  }
150
150
 
151
+ // src/adapters/thread-identity.ts
152
+ function record(value) {
153
+ return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
154
+ }
155
+ function validThreadId(value) {
156
+ return typeof value === "string" && value.trim().length > 0 ? value : void 0;
157
+ }
158
+ function nativeThreadId(runtime, metadata, inherited, currentConfig) {
159
+ const explicit = record(record(runtime)?.configurable);
160
+ const current = record(record(currentConfig)?.configurable);
161
+ for (const candidate of [explicit, current, record(metadata)]) {
162
+ if (candidate && Object.prototype.hasOwnProperty.call(candidate, "thread_id")) {
163
+ return validThreadId(candidate.thread_id);
164
+ }
165
+ }
166
+ return inherited;
167
+ }
168
+ function rejectConversationOptions(options) {
169
+ if ("conversationId" in options || "conversation_id" in options) {
170
+ throw new TypeError("conversationId/conversation_id was removed. Pass the native configurable.thread_id when invoking the agent or runnable instead.");
171
+ }
172
+ }
173
+ var RunThreadIdentity = class {
174
+ constructor(boundIdentity, currentConfig = () => void 0) {
175
+ this.boundIdentity = boundIdentity;
176
+ this.currentConfig = currentConfig;
177
+ }
178
+ boundIdentity;
179
+ currentConfig;
180
+ runs = /* @__PURE__ */ new Map();
181
+ children = /* @__PURE__ */ new Map();
182
+ start(runId, parentRunId, metadata) {
183
+ const discarded = this.finish(runId);
184
+ if (this.runs.size >= 4096) {
185
+ const oldestRoot = [...this.runs].find(([, run]) => run.parentRunId === void 0 || !this.runs.has(run.parentRunId))?.[0] ?? this.runs.keys().next().value;
186
+ if (oldestRoot !== void 0) discarded.push(...this.finish(oldestRoot));
187
+ }
188
+ const inherited = parentRunId === void 0 ? void 0 : this.runs.get(parentRunId)?.threadId;
189
+ const exposed = record(metadata);
190
+ const threadId = this.boundIdentity && (parentRunId === void 0 || !this.runs.has(parentRunId)) ? this.boundIdentity.threadId : exposed && Object.prototype.hasOwnProperty.call(exposed, "thread_id") ? typeof exposed.langgraph_node === "string" ? nativeThreadId(void 0, exposed, inherited, this.currentConfig()) : validThreadId(exposed.thread_id) : inherited;
191
+ this.runs.set(runId, { threadId, parentRunId });
192
+ if (parentRunId !== void 0 && parentRunId !== runId) {
193
+ const children = this.children.get(parentRunId) ?? /* @__PURE__ */ new Set();
194
+ children.add(runId);
195
+ this.children.set(parentRunId, children);
196
+ }
197
+ return discarded;
198
+ }
199
+ get(runId) {
200
+ const run = this.runs.get(runId);
201
+ return run ? run.threadId : this.boundIdentity?.threadId;
202
+ }
203
+ finish(runId) {
204
+ const pending = [runId];
205
+ const seen = /* @__PURE__ */ new Set();
206
+ while (pending.length) {
207
+ const id = pending.pop();
208
+ if (seen.has(id)) continue;
209
+ seen.add(id);
210
+ pending.push(...this.children.get(id) ?? []);
211
+ this.children.delete(id);
212
+ const parent = this.runs.get(id)?.parentRunId;
213
+ this.runs.delete(id);
214
+ if (parent !== void 0) {
215
+ const siblings = this.children.get(parent);
216
+ siblings?.delete(id);
217
+ if (siblings?.size === 0) this.children.delete(parent);
218
+ }
219
+ }
220
+ return [...seen];
221
+ }
222
+ };
223
+
151
224
  // src/adapters/langchain.ts
225
+ var handlerCleanups = /* @__PURE__ */ new WeakMap();
152
226
  var USER_ROLES = /* @__PURE__ */ new Set(["human", "user"]);
153
227
  function getMessageRole(message) {
154
228
  if (typeof message.role === "string") {
@@ -157,7 +231,8 @@ function getMessageRole(message) {
157
231
  if (typeof message.type === "string") {
158
232
  return message.type.toLowerCase();
159
233
  }
160
- return "";
234
+ const legacyType = message.getType?.() ?? message._getType?.();
235
+ return typeof legacyType === "string" ? legacyType.toLowerCase() : "";
161
236
  }
162
237
  function extractMessageText(content) {
163
238
  if (content === void 0) {
@@ -186,7 +261,16 @@ function findLastUserMessage(messages) {
186
261
  return void 0;
187
262
  }
188
263
  async function createLangChainHandler(firewall, options = {}) {
189
- const { BaseCallbackHandler } = await import("@langchain/core/callbacks/base");
264
+ return buildLangChainHandler(firewall, options);
265
+ }
266
+ async function buildLangChainHandler(firewall, options, boundIdentity) {
267
+ rejectConversationOptions(options);
268
+ const { ensureConfig } = await import("@langchain/core/runnables");
269
+ const threads = new RunThreadIdentity(boundIdentity, () => ensureConfig());
270
+ const { BaseTracer } = await import("@langchain/core/tracers/base");
271
+ let finishRun = (runId) => {
272
+ threads.finish(runId);
273
+ };
190
274
  const enabledHooks = resolveHooks(options.hooks);
191
275
  const includeTool = options.includeTool ?? true;
192
276
  const failOpen = options.failOpen ?? true;
@@ -205,14 +289,14 @@ async function createLangChainHandler(firewall, options = {}) {
205
289
  logger("onClassify callback threw", err);
206
290
  }
207
291
  };
208
- const classify = async (text, hookLabel, runId, toolName) => {
292
+ const classifyEvent = async (text, hookLabel, runId, toolName) => {
209
293
  let result;
210
294
  try {
211
295
  result = await firewall.classify(text, {
212
296
  hook: hookLabel,
213
297
  metadata: {
214
298
  langgraph: { run_id: runId },
215
- ...options.conversationId === void 0 ? {} : { conversationId: options.conversationId }
299
+ ...threads.get(runId) === void 0 ? {} : { conversationId: threads.get(runId) }
216
300
  },
217
301
  ...requestedMode !== void 0 ? { mode: requestedMode } : {},
218
302
  ...toolName !== void 0 ? { toolName } : {}
@@ -256,97 +340,353 @@ async function createLangChainHandler(firewall, options = {}) {
256
340
  result
257
341
  });
258
342
  };
259
- class SilmarilFirewallHandler extends BaseCallbackHandler {
343
+ const classify = async (text, hook, runId, toolName) => {
344
+ try {
345
+ await classifyEvent(text, hook, runId, toolName);
346
+ } catch (error) {
347
+ finishRun(runId);
348
+ throw error;
349
+ }
350
+ };
351
+ function serialized(value, name) {
352
+ if (value && typeof value === "object" && "id" in value && Array.isArray(value.id)) return value;
353
+ return { lc: 1, type: "not_implemented", id: [name] };
354
+ }
355
+ class SilmarilFirewallHandler extends BaseTracer {
260
356
  name = "silmaril_firewall_handler";
261
357
  raiseError = true;
262
358
  awaitHandlers = true;
263
- async handleChatModelStart(_llm, messages, runId) {
264
- if (!enabledHooks.has(FirewallHook.CHAT_MODEL_START)) {
265
- return;
359
+ // LangChain clears runnable context inside ordinary callbacks. Its synchronous
360
+ // tracer pre-start hooks capture native config before that boundary. Runs are
361
+ // memory-only and every terminal path discards both native tracer maps.
362
+ async persistRun() {
363
+ }
364
+ getRunById(runId) {
365
+ return runId === void 0 ? void 0 : this.runMap.get(runId);
366
+ }
367
+ // Only lifecycle identity is needed. Do not retain prompts, outputs, token
368
+ // events, or completed children when upstream omits a cancellation callback.
369
+ _addRunToRunMap(run) {
370
+ return super._addRunToRunMap({
371
+ ...run,
372
+ inputs: {},
373
+ extra: {},
374
+ serialized: {},
375
+ tags: [],
376
+ child_runs: [],
377
+ events: []
378
+ });
379
+ }
380
+ _addChildRun() {
381
+ }
382
+ async _endTrace(run) {
383
+ try {
384
+ await super._endTrace(run);
385
+ } finally {
386
+ run.inputs = {};
387
+ delete run.outputs;
388
+ delete run.error;
389
+ run.extra = {};
390
+ run.events = [];
391
+ run.child_runs = [];
266
392
  }
267
- const batches = messages;
268
- const flat = [];
269
- for (const batch of batches) {
270
- for (const m of batch) {
271
- flat.push(m);
272
- }
393
+ }
394
+ async handleText() {
395
+ }
396
+ async handleAgentAction() {
397
+ }
398
+ async handleAgentEnd() {
399
+ }
400
+ async handleLLMNewToken(_token, _idx, runId) {
401
+ return this.getRunById(runId) ?? this._createRunForLLMStart(serialized(void 0, "llm"), [], runId);
402
+ }
403
+ capture(runId, parentRunId, metadata) {
404
+ for (const id of threads.start(runId, parentRunId, metadata)) {
405
+ this.runMap.delete(id);
406
+ this.runTreeMap?.delete(id);
273
407
  }
274
- const lastUser = findLastUserMessage(flat);
275
- if (!lastUser) {
276
- return;
408
+ }
409
+ finishAll() {
410
+ for (const id of [...this.runMap.keys()]) this.finishRun(id);
411
+ this.runTreeMap?.clear();
412
+ }
413
+ finishRun(runId) {
414
+ for (const id of threads.finish(runId)) {
415
+ this.runMap.delete(id);
416
+ this.runTreeMap?.delete(id);
277
417
  }
278
- const text = extractMessageText(lastUser.content).trim();
279
- if (!text) {
280
- return;
418
+ }
419
+ _createRunForLLMStart(...args) {
420
+ this.capture(args[2], args[3], args[6]);
421
+ return super._createRunForLLMStart(...args);
422
+ }
423
+ _createRunForChatModelStart(...args) {
424
+ this.capture(args[2], args[3], args[6]);
425
+ return super._createRunForChatModelStart(...args);
426
+ }
427
+ _createRunForChainStart(...args) {
428
+ this.capture(args[2], args[3], args[5]);
429
+ return super._createRunForChainStart(...args);
430
+ }
431
+ _createRunForToolStart(...args) {
432
+ this.capture(args[2], args[3], args[5]);
433
+ return super._createRunForToolStart(...args);
434
+ }
435
+ _createRunForRetrieverStart(...args) {
436
+ this.capture(args[2], args[3], args[5]);
437
+ return super._createRunForRetrieverStart(...args);
438
+ }
439
+ async handleChatModelStart(llm, messages, runId, parentRunId, extraParams, tags, metadata) {
440
+ const batches = messages;
441
+ const run = this.getRunById(runId) ?? this._createRunForChatModelStart(
442
+ serialized(llm, "chat_model"),
443
+ messages,
444
+ runId,
445
+ parentRunId,
446
+ extraParams,
447
+ tags,
448
+ metadata
449
+ );
450
+ if (enabledHooks.has(FirewallHook.CHAT_MODEL_START)) {
451
+ const lastUser = findLastUserMessage(batches.flat());
452
+ const text = lastUser ? extractMessageText(lastUser.content).trim() : "";
453
+ if (text) await classify(text, FIREWALL_HOOK_TO_LABEL[FirewallHook.CHAT_MODEL_START], runId);
281
454
  }
282
- await classify(text, FIREWALL_HOOK_TO_LABEL[FirewallHook.CHAT_MODEL_START], runId);
455
+ return run;
283
456
  }
284
- async handleLLMStart(_llm, prompts, runId) {
285
- if (!enabledHooks.has(FirewallHook.LLM_START)) {
286
- return;
457
+ async handleLLMStart(llm, prompts, runId, parentRunId, extraParams, tags, metadata) {
458
+ const run = this.getRunById(runId) ?? this._createRunForLLMStart(
459
+ serialized(llm, "llm"),
460
+ prompts,
461
+ runId,
462
+ parentRunId,
463
+ extraParams,
464
+ tags,
465
+ metadata
466
+ );
467
+ if (enabledHooks.has(FirewallHook.LLM_START)) {
468
+ const text = extractTextFromPrompts(prompts);
469
+ if (text) await classify(text, FIREWALL_HOOK_TO_LABEL[FirewallHook.LLM_START], runId);
287
470
  }
288
- const text = extractTextFromPrompts(prompts);
289
- if (!text) {
290
- return;
471
+ return run;
472
+ }
473
+ async handleToolStart(tool, inputStr, runId, parentRunId, tags, metadata) {
474
+ const run = this.getRunById(runId) ?? this._createRunForToolStart(
475
+ serialized(tool, tool?.name ?? "tool"),
476
+ inputStr,
477
+ runId,
478
+ parentRunId,
479
+ tags,
480
+ metadata
481
+ );
482
+ if (includeTool && enabledHooks.has(FirewallHook.TOOL_START)) {
483
+ const text = extractTextFromToolInput(inputStr);
484
+ if (text) await classify(text, FIREWALL_HOOK_TO_LABEL[FirewallHook.TOOL_START], runId, tool?.name);
291
485
  }
292
- await classify(text, FIREWALL_HOOK_TO_LABEL[FirewallHook.LLM_START], runId);
486
+ return run;
293
487
  }
294
- async handleToolStart(tool, inputStr, runId) {
295
- if (!includeTool || !enabledHooks.has(FirewallHook.TOOL_START)) {
296
- return;
488
+ async handleRetrieverStart(retriever, query, runId, parentRunId, tags, metadata) {
489
+ const run = this.getRunById(runId) ?? this._createRunForRetrieverStart(
490
+ serialized(retriever, "retriever"),
491
+ query,
492
+ runId,
493
+ parentRunId,
494
+ tags,
495
+ metadata
496
+ );
497
+ if (enabledHooks.has(FirewallHook.RETRIEVER_START) && query.trim()) {
498
+ await classify(query.trim(), FIREWALL_HOOK_TO_LABEL[FirewallHook.RETRIEVER_START], runId);
297
499
  }
298
- const text = extractTextFromToolInput(inputStr);
299
- if (!text) {
300
- return;
500
+ return run;
501
+ }
502
+ async handleLLMEnd(output, runId) {
503
+ try {
504
+ if (!this.getRunById(runId)) this._createRunForLLMStart(serialized(void 0, "llm"), [], runId);
505
+ if (enabledHooks.has(FirewallHook.LLM_END)) {
506
+ const text = extractTextFromLLMResult(output);
507
+ if (text) await classify(text, FIREWALL_HOOK_TO_LABEL[FirewallHook.LLM_END], runId);
508
+ }
509
+ return await super.handleLLMEnd(output, runId);
510
+ } finally {
511
+ finishRun(runId);
301
512
  }
302
- const toolName = tool?.name;
303
- await classify(text, FIREWALL_HOOK_TO_LABEL[FirewallHook.TOOL_START], runId, toolName);
304
513
  }
305
- async handleRetrieverStart(_retriever, query, runId) {
306
- if (!enabledHooks.has(FirewallHook.RETRIEVER_START)) {
307
- return;
514
+ async handleToolEnd(output, runId, _parentRunId, _tags, kwargs) {
515
+ try {
516
+ if (!this.getRunById(runId)) this._createRunForToolStart(serialized(void 0, "tool"), "", runId);
517
+ if (includeTool && enabledHooks.has(FirewallHook.TOOL_END)) {
518
+ const text = String(output).trim();
519
+ if (text) await classify(text, FIREWALL_HOOK_TO_LABEL[FirewallHook.TOOL_END], runId, kwargs?.name);
520
+ }
521
+ return await super.handleToolEnd(output, runId);
522
+ } finally {
523
+ finishRun(runId);
308
524
  }
309
- const text = query.trim();
310
- if (!text) {
311
- return;
525
+ }
526
+ async handleRetrieverEnd(documents, runId) {
527
+ try {
528
+ if (!this.getRunById(runId)) this._createRunForRetrieverStart(serialized(void 0, "retriever"), "", runId);
529
+ if (enabledHooks.has(FirewallHook.RETRIEVER_END)) {
530
+ const text = extractTextFromDocuments(documents);
531
+ if (text) await classify(text, FIREWALL_HOOK_TO_LABEL[FirewallHook.RETRIEVER_END], runId);
532
+ }
533
+ return await super.handleRetrieverEnd(documents, runId);
534
+ } finally {
535
+ finishRun(runId);
312
536
  }
313
- await classify(text, FIREWALL_HOOK_TO_LABEL[FirewallHook.RETRIEVER_START], runId);
314
537
  }
315
- async handleLLMEnd(output, runId) {
316
- if (!enabledHooks.has(FirewallHook.LLM_END)) {
317
- return;
538
+ async handleChainStart(chain, inputs, runId, parentRunId, tags, metadata, runType, name) {
539
+ return super.handleChainStart(serialized(chain, "chain"), inputs, runId, parentRunId, tags, metadata, runType, name);
540
+ }
541
+ async handleChainEnd(...args) {
542
+ if (!this.getRunById(args[1])) this._createRunForChainStart(serialized(void 0, "chain"), {}, args[1]);
543
+ try {
544
+ return await super.handleChainEnd(...args);
545
+ } finally {
546
+ finishRun(args[1]);
318
547
  }
319
- const text = extractTextFromLLMResult(output);
320
- if (!text) {
321
- return;
548
+ }
549
+ async handleChainError(...args) {
550
+ if (!this.getRunById(args[1])) this._createRunForChainStart(serialized(void 0, "chain"), {}, args[1]);
551
+ try {
552
+ return await super.handleChainError(...args);
553
+ } finally {
554
+ finishRun(args[1]);
322
555
  }
323
- await classify(text, FIREWALL_HOOK_TO_LABEL[FirewallHook.LLM_END], runId);
324
556
  }
325
- async handleToolEnd(output, runId, _parentRunId, _tags, _kwargs) {
326
- if (!includeTool || !enabledHooks.has(FirewallHook.TOOL_END)) {
327
- return;
557
+ async handleLLMError(...args) {
558
+ if (!this.getRunById(args[1])) this._createRunForLLMStart(serialized(void 0, "llm"), [], args[1]);
559
+ try {
560
+ return await super.handleLLMError(...args);
561
+ } finally {
562
+ finishRun(args[1]);
328
563
  }
329
- const text = String(output).trim();
330
- if (!text) {
331
- return;
564
+ }
565
+ async handleToolError(...args) {
566
+ if (!this.getRunById(args[1])) this._createRunForToolStart(serialized(void 0, "tool"), "", args[1]);
567
+ try {
568
+ return await super.handleToolError(...args);
569
+ } finally {
570
+ finishRun(args[1]);
332
571
  }
333
- const toolName = _kwargs?.name;
334
- await classify(text, FIREWALL_HOOK_TO_LABEL[FirewallHook.TOOL_END], runId, toolName);
335
572
  }
336
- async handleRetrieverEnd(documents, runId) {
337
- if (!enabledHooks.has(FirewallHook.RETRIEVER_END)) {
338
- return;
573
+ async handleRetrieverError(...args) {
574
+ if (!this.getRunById(args[1])) this._createRunForRetrieverStart(serialized(void 0, "retriever"), "", args[1]);
575
+ try {
576
+ return await super.handleRetrieverError(...args);
577
+ } finally {
578
+ finishRun(args[1]);
339
579
  }
340
- const text = extractTextFromDocuments(documents);
341
- if (!text) {
342
- return;
580
+ }
581
+ }
582
+ const handler = new SilmarilFirewallHandler();
583
+ finishRun = (runId) => {
584
+ handler.finishRun(runId);
585
+ };
586
+ handlerCleanups.set(handler, () => handler.finishAll());
587
+ return handler;
588
+ }
589
+ async function protectLangChainRunnable(firewall, runnable, options = {}) {
590
+ rejectConversationOptions(options);
591
+ const { RunnableBinding, RunnableRetry, ensureConfig, mergeConfigs } = await import("@langchain/core/runnables");
592
+ const { IterableReadableStream } = await import("@langchain/core/utils/stream");
593
+ const configFactories = [async (config) => {
594
+ const effective = ensureConfig(config);
595
+ const handler = await buildLangChainHandler(firewall, options, {
596
+ threadId: validThreadId(effective.configurable?.thread_id)
597
+ });
598
+ return { callbacks: [handler] };
599
+ }];
600
+ class ProtectedBinding extends RunnableBinding {
601
+ async scopedConfig(...configs) {
602
+ const created = [];
603
+ const scopedFactories = configFactories.map((factory) => async (config2) => {
604
+ const update = await factory(config2);
605
+ if (Array.isArray(update.callbacks)) created.push(...update.callbacks);
606
+ return update;
607
+ });
608
+ const scoped = new RunnableBinding({ bound: this.bound, config: this.config, configFactories: scopedFactories });
609
+ const config = await scoped._mergeConfig(...configs);
610
+ return { config, dispose: () => {
611
+ for (const handler of created) handlerCleanups.get(handler)?.();
612
+ } };
613
+ }
614
+ async invoke(input, options2) {
615
+ const scoped = await this.scopedConfig(options2, this.kwargs);
616
+ try {
617
+ return await this.bound.invoke(input, scoped.config);
618
+ } finally {
619
+ scoped.dispose();
620
+ }
621
+ }
622
+ async stream(input, options2) {
623
+ const scoped = await this.scopedConfig(ensureConfig(options2), this.kwargs);
624
+ let upstream;
625
+ try {
626
+ upstream = await this.bound.stream(input, scoped.config);
627
+ } catch (error) {
628
+ scoped.dispose();
629
+ throw error;
343
630
  }
344
- await classify(text, FIREWALL_HOOK_TO_LABEL[FirewallHook.RETRIEVER_END], runId);
631
+ const managed = async function* () {
632
+ try {
633
+ yield* upstream;
634
+ } finally {
635
+ scoped.dispose();
636
+ }
637
+ };
638
+ return IterableReadableStream.fromAsyncGenerator(managed());
639
+ }
640
+ async *_streamIterator(input, options2) {
641
+ const stream = await this.stream(input, options2);
642
+ yield* stream;
643
+ }
644
+ withConfig(config) {
645
+ return new ProtectedBinding({ bound: this.bound, config: mergeConfigs(this.config, config), configFactories });
646
+ }
647
+ withListeners(fields) {
648
+ return new RunnableBinding({ bound: this, config: {} }).withListeners(fields);
649
+ }
650
+ withRetry(fields) {
651
+ return new RunnableRetry({
652
+ bound: this,
653
+ config: {},
654
+ ...fields?.stopAfterAttempt === void 0 ? {} : { maxAttemptNumber: fields.stopAfterAttempt },
655
+ ...fields?.onFailedAttempt === void 0 ? {} : { onFailedAttempt: fields.onFailedAttempt }
656
+ });
657
+ }
658
+ }
659
+ const bindings = /* @__PURE__ */ new Set();
660
+ const installBoundary = (candidate) => {
661
+ if (!RunnableBinding.isRunnableBinding(candidate)) {
662
+ return new ProtectedBinding({ bound: candidate, config: {}, configFactories });
663
+ }
664
+ if (bindings.has(candidate)) throw new TypeError("Cannot protect cyclic runnable bindings");
665
+ bindings.add(candidate);
666
+ const binding = candidate;
667
+ const Binding = binding.constructor;
668
+ return new Binding({ ...binding, bound: installBoundary(binding.bound) });
669
+ };
670
+ class ComposableBinding extends RunnableBinding {
671
+ withConfig(config) {
672
+ return new ComposableBinding({ bound: this.bound, config: mergeConfigs(this.config, config) });
673
+ }
674
+ withListeners(fields) {
675
+ return new RunnableBinding({ bound: this, config: {} }).withListeners(fields);
676
+ }
677
+ withRetry(fields) {
678
+ return new RunnableRetry({
679
+ bound: this,
680
+ config: {},
681
+ ...fields?.stopAfterAttempt === void 0 ? {} : { maxAttemptNumber: fields.stopAfterAttempt },
682
+ ...fields?.onFailedAttempt === void 0 ? {} : { onFailedAttempt: fields.onFailedAttempt }
683
+ });
345
684
  }
346
685
  }
347
- return new SilmarilFirewallHandler();
686
+ return new ComposableBinding({ bound: installBoundary(runnable), config: {} });
348
687
  }
349
688
  export {
350
- createLangChainHandler
689
+ createLangChainHandler,
690
+ protectLangChainRunnable
351
691
  };
352
692
  //# sourceMappingURL=langchain.js.map