@flutchai/flutch-sdk 0.2.14 → 0.2.16

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
@@ -522,6 +522,43 @@ Currently supports LangGraph.js. The architecture is designed to support other g
522
522
  - **Multi-step Workflows**: Orchestrate complex AI pipelines
523
523
  - **Interactive Assistants**: Create agents with callbacks and dynamic UIs
524
524
 
525
+ ## Publishing
526
+
527
+ The package is automatically published to [npmjs.org](https://www.npmjs.com/package/@flutchai/flutch-sdk) on every merge to `main` via GitHub Actions.
528
+
529
+ ### How it works
530
+
531
+ 1. On merge to `main`, the [release workflow](.github/workflows/release.yml) runs
532
+ 2. It reads the version from `package.json`
533
+ 3. If a git tag `v{version}` does not exist yet — it creates the tag and publishes to npm
534
+ 4. If the tag already exists — all publish steps are skipped (idempotent)
535
+
536
+ Publishing uses [npm OIDC Trusted Publishing](https://docs.npmjs.com/generating-provenance-statements) — no npm token secret is required. The GitHub Actions runner authenticates directly with npmjs.org via OpenID Connect.
537
+
538
+ ### Rules
539
+
540
+ - **Bump the version before merging** — if `package.json` version matches an existing git tag, nothing will be published
541
+ - **Use semantic versioning** — `MAJOR.MINOR.PATCH` (e.g. `0.2.14`)
542
+ - `PATCH` — bug fixes, minor improvements
543
+ - `MINOR` — new backward-compatible features
544
+ - `MAJOR` — breaking changes
545
+ - **One version bump per PR** — keep version changes isolated from feature/fix changes when possible
546
+
547
+ ### Releasing a new version
548
+
549
+ ```bash
550
+ # 1. Create a branch
551
+ git checkout -b chore/bump-version-X.Y.Z
552
+
553
+ # 2. Update version in package.json
554
+ # "version": "X.Y.Z"
555
+
556
+ # 3. Push and open a PR
557
+ git push origin chore/bump-version-X.Y.Z
558
+
559
+ # 4. Merge the PR — publishing happens automatically
560
+ ```
561
+
525
562
  ## Links
526
563
 
527
564
  - [GitHub Repository](https://github.com/flutchai/node-sdk)
package/dist/index.cjs CHANGED
@@ -25,6 +25,7 @@ var openai = require('@langchain/openai');
25
25
  var aws = require('@langchain/aws');
26
26
  var anthropic = require('@langchain/anthropic');
27
27
  var cohere = require('@langchain/cohere');
28
+ var cohereAi = require('cohere-ai');
28
29
  var document_compressors = require('@langchain/core/retrievers/document_compressors');
29
30
  var mistralai = require('@langchain/mistralai');
30
31
 
@@ -6466,6 +6467,12 @@ function hashToolsConfig(toolsConfig) {
6466
6467
  const sorted = toolsConfig.map((t) => `${t.toolName}:${t.enabled}:${JSON.stringify(t.config || {})}`).sort().join("|");
6467
6468
  return crypto.createHash("md5").update(sorted).digest("hex").slice(0, 16);
6468
6469
  }
6470
+ function normalizeToolConfigs(tools) {
6471
+ if (!tools || tools.length === 0) return void 0;
6472
+ return tools.map(
6473
+ (t) => typeof t === "string" ? { toolName: t, enabled: true } : { toolName: t.name, enabled: t.enabled !== false, config: t.config }
6474
+ );
6475
+ }
6469
6476
  var DEFAULT_ROUTER_URL = "https://router.flutch.ai";
6470
6477
  function resolveRouterURL(baseURL) {
6471
6478
  return baseURL ?? process.env.FLUTCH_ROUTER_URL ?? DEFAULT_ROUTER_URL;
@@ -6508,13 +6515,14 @@ var VoyageAIRerank = class extends document_compressors.BaseDocumentCompressor {
6508
6515
  model;
6509
6516
  topN;
6510
6517
  truncation;
6511
- baseUrl = "https://api.voyageai.com/v1/rerank";
6518
+ baseUrl;
6512
6519
  constructor(config) {
6513
6520
  super();
6514
6521
  this.apiKey = config.apiKey || process.env.VOYAGEAI_API_KEY || "";
6515
6522
  this.model = config.model || "rerank-2";
6516
6523
  this.topN = config.topN || 20;
6517
6524
  this.truncation = config.truncation ?? true;
6525
+ this.baseUrl = config.baseUrl ? `${config.baseUrl}/v1/rerank` : "https://api.voyageai.com/v1/rerank";
6518
6526
  if (!this.apiKey) {
6519
6527
  throw new Error(
6520
6528
  "VoyageAI API key is required. Set VOYAGEAI_API_KEY environment variable or pass apiKey in config."
@@ -6645,22 +6653,31 @@ var ModelInitializer = class _ModelInitializer {
6645
6653
  modelName,
6646
6654
  defaultTemperature,
6647
6655
  defaultMaxTokens,
6648
- apiToken
6656
+ apiToken,
6657
+ baseURL
6649
6658
  }) => new cohere.ChatCohere({
6650
6659
  model: modelName,
6651
6660
  temperature: defaultTemperature,
6652
- apiKey: apiToken || this.resolveApiKey("cohere" /* COHERE */)
6661
+ client: new cohereAi.CohereClient({
6662
+ token: apiToken || this.resolveApiKey("cohere" /* COHERE */),
6663
+ baseUrl: resolveRouterURL(baseURL)
6664
+ })
6653
6665
  }),
6654
6666
  ["mistral" /* MISTRAL */]: ({
6655
6667
  modelName,
6656
6668
  defaultTemperature,
6657
6669
  defaultMaxTokens,
6658
- apiToken
6670
+ apiToken,
6671
+ baseURL
6659
6672
  }) => new mistralai.ChatMistralAI({
6660
6673
  model: modelName,
6661
6674
  temperature: defaultTemperature,
6662
6675
  maxTokens: defaultMaxTokens,
6663
- apiKey: apiToken || this.resolveApiKey("mistral" /* MISTRAL */)
6676
+ apiKey: apiToken || this.resolveApiKey("mistral" /* MISTRAL */),
6677
+ // Route through the same gateway as OpenAI/Anthropic.
6678
+ // Without serverURL, ChatMistralAI ignores FLUTCH_ROUTER_URL and calls
6679
+ // api.mistral.ai directly — inconsistent with other providers.
6680
+ serverURL: `${resolveRouterURL(baseURL)}/v1`
6664
6681
  }),
6665
6682
  ["voyageai" /* VOYAGEAI */]: () => {
6666
6683
  throw new Error("VoyageAI chat models not implemented");
@@ -6668,18 +6685,32 @@ var ModelInitializer = class _ModelInitializer {
6668
6685
  };
6669
6686
  // Rerank model creators
6670
6687
  rerankModelCreators = {
6671
- ["cohere" /* COHERE */]: ({ modelName, apiToken, maxDocuments }) => {
6688
+ ["cohere" /* COHERE */]: ({
6689
+ modelName,
6690
+ apiToken,
6691
+ maxDocuments,
6692
+ baseURL
6693
+ }) => {
6672
6694
  return new cohere.CohereRerank({
6673
- apiKey: apiToken || this.resolveApiKey("cohere" /* COHERE */),
6674
6695
  model: modelName,
6675
- topN: maxDocuments || 20
6696
+ topN: maxDocuments || 20,
6697
+ client: new cohereAi.CohereClient({
6698
+ token: apiToken || this.resolveApiKey("cohere" /* COHERE */),
6699
+ baseUrl: resolveRouterURL(baseURL)
6700
+ })
6676
6701
  });
6677
6702
  },
6678
- ["voyageai" /* VOYAGEAI */]: ({ modelName, apiToken, maxDocuments }) => {
6703
+ ["voyageai" /* VOYAGEAI */]: ({
6704
+ modelName,
6705
+ apiToken,
6706
+ maxDocuments,
6707
+ baseURL
6708
+ }) => {
6679
6709
  return new VoyageAIRerank({
6680
6710
  apiKey: apiToken || this.resolveApiKey("voyageai" /* VOYAGEAI */),
6681
6711
  model: modelName,
6682
- topN: maxDocuments || 20
6712
+ topN: maxDocuments || 20,
6713
+ baseUrl: resolveRouterURL(baseURL)
6683
6714
  });
6684
6715
  },
6685
6716
  // Other providers don't support rerank yet
@@ -6690,9 +6721,10 @@ var ModelInitializer = class _ModelInitializer {
6690
6721
  };
6691
6722
  // Embedding model creators
6692
6723
  embeddingModelCreators = {
6693
- ["openai" /* OPENAI */]: ({ modelName, apiToken }) => new openai.OpenAIEmbeddings({
6724
+ ["openai" /* OPENAI */]: ({ modelName, apiToken, baseURL }) => new openai.OpenAIEmbeddings({
6694
6725
  model: modelName,
6695
- apiKey: apiToken || this.resolveApiKey("openai" /* OPENAI */)
6726
+ apiKey: apiToken || this.resolveApiKey("openai" /* OPENAI */),
6727
+ configuration: { baseURL: `${resolveRouterURL(baseURL)}/v1` }
6696
6728
  }),
6697
6729
  // Other providers not yet implemented for embeddings
6698
6730
  ["anthropic" /* ANTHROPIC */]: void 0,
@@ -6701,7 +6733,82 @@ var ModelInitializer = class _ModelInitializer {
6701
6733
  ["aws" /* AWS */]: void 0,
6702
6734
  ["voyageai" /* VOYAGEAI */]: void 0
6703
6735
  };
6704
- async initializeChatModel(config) {
6736
+ // ══════════════════════════════════════════════════════════════
6737
+ // initializeChatModel — overloaded: ModelConfig | ModelByIdConfig
6738
+ // ══════════════════════════════════════════════════════════════
6739
+ async initializeChatModel(config, customTools) {
6740
+ if ("provider" in config && "modelName" in config) {
6741
+ return this.initializeChatModelDirect(config, customTools);
6742
+ }
6743
+ return this.initializeChatModelByIdInternal(config);
6744
+ }
6745
+ /**
6746
+ * Direct initialization by provider + modelName (no DB lookup).
6747
+ */
6748
+ async initializeChatModelDirect(config, customTools) {
6749
+ const toolsConfig = normalizeToolConfigs(config.tools);
6750
+ const modelIdentifier = `${config.provider}:${config.modelName}`;
6751
+ const cacheKey = generateModelCacheKey(
6752
+ modelIdentifier,
6753
+ config.temperature,
6754
+ config.maxTokens,
6755
+ toolsConfig,
6756
+ config.baseURL
6757
+ );
6758
+ const cached = this.modelInstanceCache.get(cacheKey);
6759
+ if (cached) {
6760
+ this.logger.debug(`Using cached chat model instance: ${cacheKey}`);
6761
+ return cached;
6762
+ }
6763
+ const provider = config.provider;
6764
+ if (!Object.values(ModelProvider).includes(provider)) {
6765
+ throw new Error(
6766
+ `Unknown provider "${provider}". Valid: ${Object.values(ModelProvider).join(", ")}`
6767
+ );
6768
+ }
6769
+ const apiToken = this.resolveApiKey(provider);
6770
+ const temperature = config.temperature ?? 0.7;
6771
+ const maxTokens = config.maxTokens ?? 4096;
6772
+ const modelConfig = {
6773
+ modelId: modelIdentifier,
6774
+ modelName: config.modelName,
6775
+ provider,
6776
+ modelType: "chat" /* CHAT */,
6777
+ defaultTemperature: Number(temperature),
6778
+ defaultMaxTokens: Number(maxTokens),
6779
+ apiToken,
6780
+ requiresApiKey: true,
6781
+ baseURL: config.baseURL
6782
+ };
6783
+ this.logger.debug(
6784
+ `Creating chat model: ${modelIdentifier} (apiKeyResolved=${!!apiToken})`
6785
+ );
6786
+ const creator = this.chatModelCreators[provider];
6787
+ if (!creator) {
6788
+ throw new Error(`Chat models not supported for provider: ${provider}`);
6789
+ }
6790
+ const model = creator(modelConfig);
6791
+ model.metadata = {
6792
+ ...model.metadata,
6793
+ modelId: modelIdentifier
6794
+ };
6795
+ if (toolsConfig || customTools) {
6796
+ const boundModel = await this.bindToolsToModel(
6797
+ model,
6798
+ toolsConfig,
6799
+ customTools
6800
+ );
6801
+ this.modelInstanceCache.set(cacheKey, boundModel);
6802
+ return boundModel;
6803
+ }
6804
+ this.modelInstanceCache.set(cacheKey, model);
6805
+ return model;
6806
+ }
6807
+ /**
6808
+ * Legacy initialization by model ID (DB lookup via configFetcher).
6809
+ * @deprecated Pass ModelConfig with provider + modelName instead.
6810
+ */
6811
+ async initializeChatModelByIdInternal(config) {
6705
6812
  const cacheKey = this.generateModelCacheKey(config);
6706
6813
  const cachedModel = this.modelInstanceCache.get(cacheKey);
6707
6814
  if (cachedModel) {
@@ -6727,9 +6834,6 @@ var ModelInitializer = class _ModelInitializer {
6727
6834
  this.logger.debug(`Creating new chat model instance: ${cacheKey}`);
6728
6835
  let model;
6729
6836
  if (finalConfig.useBedrock && finalConfig.bedrockModelId) {
6730
- this.logger.debug(
6731
- `Using Bedrock for model ${finalConfig.modelName}, bedrockModelId: ${finalConfig.bedrockModelId}`
6732
- );
6733
6837
  model = new aws.ChatBedrockConverse({
6734
6838
  model: finalConfig.bedrockModelId,
6735
6839
  region: this.resolveBedrockRegion(),
@@ -6750,29 +6854,12 @@ var ModelInitializer = class _ModelInitializer {
6750
6854
  ...model.metadata,
6751
6855
  modelId: config.modelId
6752
6856
  };
6753
- this.logger.debug("\u{1F527} Model initialized with metadata", {
6754
- modelId: config.modelId,
6755
- metadataKeys: Object.keys(model.metadata || {}),
6756
- hasModelId: !!model.metadata?.modelId
6757
- });
6758
- this.logger.debug(
6759
- `[TOOLS CHECK] toolsConfig exists: ${!!config.toolsConfig}, customTools exists: ${!!config.customTools}`
6760
- );
6761
- if (config.toolsConfig) {
6762
- this.logger.debug(
6763
- `[TOOLS CHECK] toolsConfig length: ${config.toolsConfig.length}, content: ${JSON.stringify(config.toolsConfig)}`
6764
- );
6765
- }
6766
6857
  if (config.toolsConfig || config.customTools) {
6767
- this.logger.debug(
6768
- `[TOOLS] Calling bindToolsToModel with toolsConfig: ${JSON.stringify(config.toolsConfig)}`
6769
- );
6770
6858
  const boundModel = await this.bindToolsToModel(
6771
6859
  model,
6772
6860
  config.toolsConfig,
6773
6861
  config.customTools
6774
6862
  );
6775
- this.logger.debug(`[TOOLS] bindToolsToModel returned successfully`);
6776
6863
  this.modelInstanceCache.set(cacheKey, boundModel);
6777
6864
  return boundModel;
6778
6865
  }
@@ -7607,6 +7694,7 @@ exports.CallbackRegistry = CallbackRegistry;
7607
7694
  exports.CallbackStore = CallbackStore;
7608
7695
  exports.ChatFeature = ChatFeature;
7609
7696
  exports.DEFAULT_ATTACHMENT_THRESHOLD = DEFAULT_ATTACHMENT_THRESHOLD;
7697
+ exports.DEFAULT_ROUTER_URL = DEFAULT_ROUTER_URL;
7610
7698
  exports.DEFAULT_TRACER_OPTIONS = DEFAULT_TRACER_OPTIONS;
7611
7699
  exports.Endpoint = Endpoint;
7612
7700
  exports.FileTokenStore = FileTokenStore;
@@ -7633,6 +7721,7 @@ exports.WithUIEndpoints = WithUIEndpoints;
7633
7721
  exports._internals = _internals;
7634
7722
  exports.bootstrap = bootstrap;
7635
7723
  exports.buildOAuthAuthorizationUrl = buildOAuthAuthorizationUrl;
7724
+ exports.buildOpenAIModelConfig = buildOpenAIModelConfig;
7636
7725
  exports.clearAttachmentDataStore = clearAttachmentDataStore;
7637
7726
  exports.createEndpointDescriptors = createEndpointDescriptors;
7638
7727
  exports.createGraphAttachment = createGraphAttachment;
@@ -7645,6 +7734,7 @@ exports.executeToolWithAttachments = executeToolWithAttachments;
7645
7734
  exports.findCallbackMethod = findCallbackMethod;
7646
7735
  exports.findEndpointMethod = findEndpointMethod;
7647
7736
  exports.generateAttachmentSummary = generateAttachmentSummary;
7737
+ exports.generateModelCacheKey = generateModelCacheKey;
7648
7738
  exports.getAttachmentData = getAttachmentData;
7649
7739
  exports.getCallbackMetadata = getCallbackMetadata;
7650
7740
  exports.getEndpointMetadata = getEndpointMetadata;
@@ -7654,10 +7744,14 @@ exports.getUIEndpointClassMetadata = getUIEndpointClassMetadata;
7654
7744
  exports.getUIEndpointMethodsMetadata = getUIEndpointMethodsMetadata;
7655
7745
  exports.hasCallbacks = hasCallbacks;
7656
7746
  exports.hasUIEndpoints = hasUIEndpoints;
7747
+ exports.hashToolsConfig = hashToolsConfig;
7748
+ exports.isReasoningModel = isReasoningModel;
7657
7749
  exports.loadOAuthProviders = loadOAuthProviders;
7750
+ exports.normalizeToolConfigs = normalizeToolConfigs;
7658
7751
  exports.registerFinanceExampleCallback = registerFinanceExampleCallback;
7659
7752
  exports.registerUIEndpointsFromClass = registerUIEndpointsFromClass;
7660
7753
  exports.resolveOAuthProviderConfig = resolveOAuthProviderConfig;
7754
+ exports.resolveRouterURL = resolveRouterURL;
7661
7755
  exports.sanitizeTraceData = sanitizeTraceData;
7662
7756
  exports.storeAttachmentData = storeAttachmentData;
7663
7757
  exports.traceApiCall = traceApiCall;