@flutchai/flutch-sdk 0.2.21 → 0.4.0

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
@@ -15,6 +15,7 @@ var crypto = require('crypto');
15
15
  var promClient = require('prom-client');
16
16
  var messages = require('@langchain/core/messages');
17
17
  var LangGraph = require('@langchain/langgraph');
18
+ var async_hooks = require('async_hooks');
18
19
  var dispatch = require('@langchain/core/callbacks/dispatch');
19
20
  var tools = require('@langchain/core/tools');
20
21
  var zod = require('zod');
@@ -23,6 +24,7 @@ var zodToJsonSchema = require('zod-to-json-schema');
23
24
  var manager = require('@langchain/core/callbacks/manager');
24
25
  var openai = require('@langchain/openai');
25
26
  var aws = require('@langchain/aws');
27
+ var core$1 = require('cohere-ai/core');
26
28
  var anthropic = require('@langchain/anthropic');
27
29
  var cohere = require('@langchain/cohere');
28
30
  var cohereAi = require('cohere-ai');
@@ -5302,6 +5304,92 @@ exports.EventProcessor = class EventProcessor {
5302
5304
  exports.EventProcessor = __decorateClass([
5303
5305
  common.Injectable()
5304
5306
  ], exports.EventProcessor);
5307
+ var als = new async_hooks.AsyncLocalStorage();
5308
+ function withFlutchContext(ctx, fn) {
5309
+ return als.run(ctx, fn);
5310
+ }
5311
+ function getFlutchContext() {
5312
+ return als.getStore();
5313
+ }
5314
+ var HEADER_MAP = {
5315
+ messageId: "x-flutch-message-id",
5316
+ threadId: "x-flutch-thread-id",
5317
+ agentId: "x-flutch-agent-id",
5318
+ userId: "x-flutch-user-id",
5319
+ nodeName: "x-flutch-node",
5320
+ companyId: "x-flutch-company-id",
5321
+ accountId: "x-flutch-account-id"
5322
+ };
5323
+ var INTERNAL_TOKEN_HEADER = "x-flutch-internal-token";
5324
+ function isInternalMode() {
5325
+ return !!process.env.FLUTCHROUTER_INTERNAL_TOKEN;
5326
+ }
5327
+ function internalToken() {
5328
+ const v = process.env.FLUTCHROUTER_INTERNAL_TOKEN;
5329
+ return v && v.length > 0 ? v : void 0;
5330
+ }
5331
+ var flutchFetch = (input, init) => {
5332
+ const ctx = als.getStore();
5333
+ const tok = internalToken();
5334
+ if (!ctx && !tok) {
5335
+ return fetch(input, init);
5336
+ }
5337
+ const headers = new Headers(init?.headers);
5338
+ if (ctx) {
5339
+ for (const key of Object.keys(HEADER_MAP)) {
5340
+ const value = ctx[key];
5341
+ if (value) {
5342
+ headers.set(HEADER_MAP[key], String(value));
5343
+ }
5344
+ }
5345
+ }
5346
+ if (tok) {
5347
+ headers.set(INTERNAL_TOKEN_HEADER, tok);
5348
+ }
5349
+ return fetch(input, { ...init, headers });
5350
+ };
5351
+ function flutchHeaders() {
5352
+ const ctx = als.getStore();
5353
+ const tok = internalToken();
5354
+ const out = {};
5355
+ if (ctx) {
5356
+ for (const key of Object.keys(HEADER_MAP)) {
5357
+ const value = ctx[key];
5358
+ if (value) {
5359
+ out[HEADER_MAP[key]] = String(value);
5360
+ }
5361
+ }
5362
+ }
5363
+ if (tok) {
5364
+ out[INTERNAL_TOKEN_HEADER] = tok;
5365
+ }
5366
+ return out;
5367
+ }
5368
+ function flutchMistralHook(req) {
5369
+ const extras = flutchHeaders();
5370
+ if (Object.keys(extras).length === 0) {
5371
+ return req;
5372
+ }
5373
+ const headers = new Headers(req.headers);
5374
+ for (const [k, v] of Object.entries(extras)) {
5375
+ headers.set(k, v);
5376
+ }
5377
+ return new Request(req, { headers });
5378
+ }
5379
+ function wrapCohereFetcher(inner) {
5380
+ return ((args) => {
5381
+ const extras = flutchHeaders();
5382
+ if (Object.keys(extras).length === 0) {
5383
+ return inner(args);
5384
+ }
5385
+ return inner({
5386
+ ...args,
5387
+ headers: { ...args.headers ?? {}, ...extras }
5388
+ });
5389
+ });
5390
+ }
5391
+
5392
+ // src/engines/langgraph/langgraph-engine.ts
5305
5393
  process.setMaxListeners(0);
5306
5394
  exports.LangGraphEngine = class LangGraphEngine {
5307
5395
  constructor(eventProcessor, configService) {
@@ -5353,29 +5441,42 @@ exports.LangGraphEngine = class LangGraphEngine {
5353
5441
  * Method to invoke LangGraph
5354
5442
  */
5355
5443
  async invokeGraph(graph, preparedPayload, signal) {
5356
- this.logger.debug("invokeGraph preparedPayload", preparedPayload);
5357
- if (signal) {
5358
- preparedPayload.signal = signal;
5359
- this.logger.debug("[ENGINE] Signal assigned to preparedPayload.signal");
5360
- }
5361
- const input = await this.deserializeInput(preparedPayload.input || {});
5362
- try {
5363
- const result = await graph.invoke(input, {
5364
- ...preparedPayload.config,
5365
- signal: preparedPayload.signal
5366
- });
5367
- return this.processGraphResult(result);
5368
- } finally {
5369
- const threadId = this.extractThreadId(preparedPayload);
5370
- if (threadId) {
5371
- clearAttachmentDataStore(threadId);
5372
- this.logger.debug(
5373
- `[ENGINE] Cleared attachment data store for thread: ${threadId}`
5374
- );
5444
+ return withFlutchContext(
5445
+ this.extractFlutchContext(preparedPayload),
5446
+ async () => {
5447
+ this.logger.debug("invokeGraph preparedPayload", preparedPayload);
5448
+ if (signal) {
5449
+ preparedPayload.signal = signal;
5450
+ this.logger.debug(
5451
+ "[ENGINE] Signal assigned to preparedPayload.signal"
5452
+ );
5453
+ }
5454
+ const input = await this.deserializeInput(preparedPayload.input || {});
5455
+ try {
5456
+ const result = await graph.invoke(input, {
5457
+ ...preparedPayload.config,
5458
+ signal: preparedPayload.signal
5459
+ });
5460
+ return this.processGraphResult(result);
5461
+ } finally {
5462
+ const threadId = this.extractThreadId(preparedPayload);
5463
+ if (threadId) {
5464
+ clearAttachmentDataStore(threadId);
5465
+ this.logger.debug(
5466
+ `[ENGINE] Cleared attachment data store for thread: ${threadId}`
5467
+ );
5468
+ }
5469
+ }
5375
5470
  }
5376
- }
5471
+ );
5377
5472
  }
5378
5473
  async streamGraph(graph, preparedPayload, onPartial, signal) {
5474
+ return withFlutchContext(
5475
+ this.extractFlutchContext(preparedPayload),
5476
+ () => this.streamGraphInner(graph, preparedPayload, onPartial, signal)
5477
+ );
5478
+ }
5479
+ async streamGraphInner(graph, preparedPayload, onPartial, signal) {
5379
5480
  const acc = this.eventProcessor.createAccumulator();
5380
5481
  let streamError = null;
5381
5482
  this.logger.debug({
@@ -5491,8 +5592,8 @@ exports.LangGraphEngine = class LangGraphEngine {
5491
5592
  async sendMetricsWebhook(payload) {
5492
5593
  try {
5493
5594
  const backendUrl = this.configService?.get("API_URL") || "http://amelie-service";
5494
- const internalToken = this.configService?.get("INTERNAL_API_TOKEN");
5495
- if (!internalToken) {
5595
+ const internalToken2 = this.configService?.get("INTERNAL_API_TOKEN");
5596
+ if (!internalToken2) {
5496
5597
  this.logger.warn(
5497
5598
  "[METRICS-WEBHOOK] INTERNAL_API_TOKEN not configured, skipping webhook"
5498
5599
  );
@@ -5508,7 +5609,7 @@ exports.LangGraphEngine = class LangGraphEngine {
5508
5609
  method: "POST",
5509
5610
  headers: {
5510
5611
  "Content-Type": "application/json",
5511
- "x-internal-token": internalToken
5612
+ "x-internal-token": internalToken2
5512
5613
  },
5513
5614
  body: JSON.stringify(payload)
5514
5615
  });
@@ -5531,8 +5632,8 @@ exports.LangGraphEngine = class LangGraphEngine {
5531
5632
  async sendTraceEventsBatch(payload) {
5532
5633
  try {
5533
5634
  const backendUrl = this.configService?.get("API_URL") || "http://amelie-service";
5534
- const internalToken = this.configService?.get("INTERNAL_API_TOKEN");
5535
- if (!internalToken) {
5635
+ const internalToken2 = this.configService?.get("INTERNAL_API_TOKEN");
5636
+ if (!internalToken2) {
5536
5637
  this.logger.warn(
5537
5638
  "[TRACE-EVENTS-BATCH] INTERNAL_API_TOKEN not configured, skipping batch webhook"
5538
5639
  );
@@ -5575,7 +5676,7 @@ exports.LangGraphEngine = class LangGraphEngine {
5575
5676
  method: "POST",
5576
5677
  headers: {
5577
5678
  "Content-Type": "application/json",
5578
- "x-internal-token": internalToken
5679
+ "x-internal-token": internalToken2
5579
5680
  },
5580
5681
  body: JSON.stringify(batchPayload)
5581
5682
  });
@@ -5605,6 +5706,22 @@ exports.LangGraphEngine = class LangGraphEngine {
5605
5706
  extractThreadId(preparedPayload) {
5606
5707
  return preparedPayload.configurable?.thread_id || preparedPayload.configurable?.context?.threadId || preparedPayload.config?.configurable?.thread_id || preparedPayload.config?.configurable?.context?.threadId || void 0;
5607
5708
  }
5709
+ /**
5710
+ * Extract Flutch billing/attribution context from the prepared payload.
5711
+ *
5712
+ * Read by `flutchFetch` (in node-sdk/src/models/flutch-context.ts) to
5713
+ * attach X-Flutch-* headers to every router-bound LLM call made during
5714
+ * this graph run.
5715
+ */
5716
+ extractFlutchContext(preparedPayload) {
5717
+ const ctx = preparedPayload?.config?.configurable?.context || preparedPayload?.configurable?.context || {};
5718
+ return {
5719
+ messageId: ctx.messageId,
5720
+ threadId: ctx.threadId || preparedPayload?.config?.configurable?.thread_id,
5721
+ agentId: ctx.agentId,
5722
+ userId: ctx.userId
5723
+ };
5724
+ }
5608
5725
  /**
5609
5726
  * Process graph execution result
5610
5727
  */
@@ -6760,7 +6877,10 @@ var ModelInitializer = class _ModelInitializer {
6760
6877
  );
6761
6878
  const routerURL = resolveRouterURL(baseURL);
6762
6879
  if (routerURL) {
6763
- config.configuration = { baseURL: `${routerURL}/v1` };
6880
+ config.configuration = {
6881
+ baseURL: `${routerURL}/v1`,
6882
+ fetch: flutchFetch
6883
+ };
6764
6884
  }
6765
6885
  return new openai.ChatOpenAI(config);
6766
6886
  },
@@ -6770,15 +6890,19 @@ var ModelInitializer = class _ModelInitializer {
6770
6890
  defaultMaxTokens,
6771
6891
  apiToken,
6772
6892
  baseURL
6773
- }) => new anthropic.ChatAnthropic({
6774
- modelName,
6775
- temperature: defaultTemperature,
6776
- maxTokens: defaultMaxTokens,
6777
- anthropicApiKey: apiToken || this.resolveApiKey("anthropic" /* ANTHROPIC */),
6778
- ...resolveRouterURL(baseURL) && {
6779
- anthropicApiUrl: resolveRouterURL(baseURL)
6780
- }
6781
- }),
6893
+ }) => {
6894
+ const routerURL = resolveRouterURL(baseURL);
6895
+ return new anthropic.ChatAnthropic({
6896
+ modelName,
6897
+ temperature: defaultTemperature,
6898
+ maxTokens: defaultMaxTokens,
6899
+ anthropicApiKey: apiToken || this.resolveApiKey("anthropic" /* ANTHROPIC */),
6900
+ ...routerURL && {
6901
+ anthropicApiUrl: routerURL,
6902
+ clientOptions: { fetch: flutchFetch }
6903
+ }
6904
+ });
6905
+ },
6782
6906
  ["cohere" /* COHERE */]: ({
6783
6907
  modelName,
6784
6908
  defaultTemperature,
@@ -6791,7 +6915,13 @@ var ModelInitializer = class _ModelInitializer {
6791
6915
  return routerURL ? new cohere.ChatCohere({
6792
6916
  model: modelName,
6793
6917
  temperature: defaultTemperature,
6794
- client: new cohereAi.CohereClient({ token, baseUrl: routerURL })
6918
+ client: new cohereAi.CohereClient({
6919
+ token,
6920
+ baseUrl: routerURL,
6921
+ // Inject X-Flutch-* headers from the ALS context on every
6922
+ // outbound request via the wrapped default fetcher.
6923
+ fetcher: wrapCohereFetcher(core$1.fetcher)
6924
+ })
6795
6925
  }) : new cohere.ChatCohere({
6796
6926
  model: modelName,
6797
6927
  temperature: defaultTemperature,
@@ -6811,7 +6941,13 @@ var ModelInitializer = class _ModelInitializer {
6811
6941
  temperature: defaultTemperature,
6812
6942
  maxTokens: defaultMaxTokens,
6813
6943
  apiKey: apiToken || this.resolveApiKey("mistral" /* MISTRAL */),
6814
- ...routerURL && { serverURL: `${routerURL}/v1` }
6944
+ ...routerURL && {
6945
+ serverURL: `${routerURL}/v1`,
6946
+ // Mistral SDK doesn't accept a custom fetch directly — its
6947
+ // beforeRequestHooks let us mutate the outgoing Request to add
6948
+ // X-Flutch-* headers from the ALS context.
6949
+ beforeRequestHooks: [flutchMistralHook]
6950
+ }
6815
6951
  });
6816
6952
  },
6817
6953
  ["voyageai" /* VOYAGEAI */]: () => {
@@ -6866,7 +7002,7 @@ var ModelInitializer = class _ModelInitializer {
6866
7002
  model: modelName,
6867
7003
  apiKey: apiToken || this.resolveApiKey("openai" /* OPENAI */),
6868
7004
  ...routerURL && {
6869
- configuration: { baseURL: `${routerURL}/v1` }
7005
+ configuration: { baseURL: `${routerURL}/v1`, fetch: flutchFetch }
6870
7006
  }
6871
7007
  });
6872
7008
  },
@@ -7860,11 +7996,15 @@ exports.encryptTokens = encryptTokens;
7860
7996
  exports.executeToolWithAttachments = executeToolWithAttachments;
7861
7997
  exports.findCallbackMethod = findCallbackMethod;
7862
7998
  exports.findEndpointMethod = findEndpointMethod;
7999
+ exports.flutchFetch = flutchFetch;
8000
+ exports.flutchHeaders = flutchHeaders;
8001
+ exports.flutchMistralHook = flutchMistralHook;
7863
8002
  exports.generateAttachmentSummary = generateAttachmentSummary;
7864
8003
  exports.generateModelCacheKey = generateModelCacheKey;
7865
8004
  exports.getAttachmentData = getAttachmentData;
7866
8005
  exports.getCallbackMetadata = getCallbackMetadata;
7867
8006
  exports.getEndpointMetadata = getEndpointMetadata;
8007
+ exports.getFlutchContext = getFlutchContext;
7868
8008
  exports.getOAuthProvider = getOAuthProvider;
7869
8009
  exports.getOAuthProviderNames = getOAuthProviderNames;
7870
8010
  exports.getUIEndpointClassMetadata = getUIEndpointClassMetadata;
@@ -7872,6 +8012,7 @@ exports.getUIEndpointMethodsMetadata = getUIEndpointMethodsMetadata;
7872
8012
  exports.hasCallbacks = hasCallbacks;
7873
8013
  exports.hasUIEndpoints = hasUIEndpoints;
7874
8014
  exports.hashToolsConfig = hashToolsConfig;
8015
+ exports.isInternalMode = isInternalMode;
7875
8016
  exports.isReasoningModel = isReasoningModel;
7876
8017
  exports.loadOAuthProviders = loadOAuthProviders;
7877
8018
  exports.normalizeToolConfigs = normalizeToolConfigs;
@@ -7882,5 +8023,7 @@ exports.resolveRouterURL = resolveRouterURL;
7882
8023
  exports.sanitizeTraceData = sanitizeTraceData;
7883
8024
  exports.storeAttachmentData = storeAttachmentData;
7884
8025
  exports.traceApiCall = traceApiCall;
8026
+ exports.withFlutchContext = withFlutchContext;
8027
+ exports.wrapCohereFetcher = wrapCohereFetcher;
7885
8028
  //# sourceMappingURL=index.cjs.map
7886
8029
  //# sourceMappingURL=index.cjs.map