@flutchai/flutch-sdk 0.6.5 → 0.6.7

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
@@ -5113,6 +5113,16 @@ function _ts_decorate21(decorators, target, key, desc) {
5113
5113
  return c > 3 && r && Object.defineProperty(target, key, r), r;
5114
5114
  }
5115
5115
  __name(_ts_decorate21, "_ts_decorate");
5116
+ function joinTextSteps(parts) {
5117
+ let out = "";
5118
+ for (const part of parts) {
5119
+ if (!part) continue;
5120
+ if (!out || /\s$/.test(out) || /^\s/.test(part)) out += part;
5121
+ else out += "\n\n" + part;
5122
+ }
5123
+ return out;
5124
+ }
5125
+ __name(joinTextSteps, "joinTextSteps");
5116
5126
  exports.EventProcessor = class _EventProcessor {
5117
5127
  static {
5118
5128
  __name(this, "EventProcessor");
@@ -5548,7 +5558,7 @@ exports.EventProcessor = class _EventProcessor {
5548
5558
  totalEvents: acc.traceEvents.length
5549
5559
  } : null;
5550
5560
  const textChain = allChains.find((c) => c.channel === "text");
5551
- const text = textChain ? textChain.steps.filter((step) => step.type === "text").map((step) => step.text || "").join("") : "";
5561
+ const text = textChain ? joinTextSteps(textChain.steps.filter((step) => step.type === "text").map((step) => step.text || "")) : "";
5552
5562
  this.logger.log("\u{1F4CA} [EventProcessor] Final result assembled", {
5553
5563
  totalChains: allChains.length,
5554
5564
  textChains: allChains.filter((c) => c.channel === "text").length,
@@ -7113,6 +7123,48 @@ function modelAcceptsSamplingParams(modelIdentifier) {
7113
7123
  return !atOrAboveThreshold;
7114
7124
  }
7115
7125
  __name(modelAcceptsSamplingParams, "modelAcceptsSamplingParams");
7126
+ var PROMPT_CACHE_MIN_VERSIONS = {
7127
+ opus: {
7128
+ major: 4,
7129
+ minor: 0
7130
+ },
7131
+ sonnet: {
7132
+ major: 4,
7133
+ minor: 0
7134
+ },
7135
+ haiku: {
7136
+ major: 4,
7137
+ minor: 5
7138
+ }
7139
+ };
7140
+ function modelSupportsPromptCache(modelIdentifier) {
7141
+ if (!modelIdentifier) return false;
7142
+ const match = CLAUDE_FAMILY_VERSION.exec(modelIdentifier.toLowerCase());
7143
+ if (!match) return false;
7144
+ const [, family, majorRaw, minorRaw] = match;
7145
+ const threshold = PROMPT_CACHE_MIN_VERSIONS[family];
7146
+ if (!threshold) return false;
7147
+ const major = Number(majorRaw);
7148
+ const minor = minorRaw === void 0 ? 0 : Number(minorRaw);
7149
+ return major > threshold.major || major === threshold.major && minor >= threshold.minor;
7150
+ }
7151
+ __name(modelSupportsPromptCache, "modelSupportsPromptCache");
7152
+ function resolvePromptCacheControl(modelIdentifiers, promptCache, ttl) {
7153
+ const enabled = promptCache ?? modelIdentifiers.some((id) => modelSupportsPromptCache(id));
7154
+ return enabled ? {
7155
+ type: "ephemeral",
7156
+ ttl: ttl ?? "5m"
7157
+ } : void 0;
7158
+ }
7159
+ __name(resolvePromptCacheControl, "resolvePromptCacheControl");
7160
+ function buildEffortRequestFields(effort) {
7161
+ return effort ? {
7162
+ output_config: {
7163
+ effort
7164
+ }
7165
+ } : void 0;
7166
+ }
7167
+ __name(buildEffortRequestFields, "buildEffortRequestFields");
7116
7168
  function resolveSamplingParams(modelIdentifiers, requested, supportsSamplingParams) {
7117
7169
  const accepted = supportsSamplingParams ?? modelIdentifiers.every((id) => modelAcceptsSamplingParams(id));
7118
7170
  if (accepted) {
@@ -7443,7 +7495,9 @@ var ModelInitializer = class _ModelInitializer {
7443
7495
  generateModelCacheKey(config) {
7444
7496
  const base = generateModelCacheKey(config.modelId, config.temperature, config.maxTokens, config.toolsConfig, config.baseURL);
7445
7497
  const withMcp = config.mcpServers && config.mcpServers.length > 0 ? `${base}:mcp:${JSON.stringify(config.mcpServers)}` : base;
7446
- return config.supportsSamplingParams === void 0 ? withMcp : `${withMcp}:sp:${config.supportsSamplingParams}`;
7498
+ const withSampling = config.supportsSamplingParams === void 0 ? withMcp : `${withMcp}:sp:${config.supportsSamplingParams}`;
7499
+ const withCache = config.promptCache === void 0 && config.promptCacheTtl === void 0 ? withSampling : `${withSampling}:pc:${config.promptCache ?? "auto"}:${config.promptCacheTtl ?? "default"}`;
7500
+ return config.effort === void 0 ? withCache : `${withCache}:eff:${config.effort}`;
7447
7501
  }
7448
7502
  // Chat model creators
7449
7503
  chatModelCreators = {
@@ -7458,7 +7512,7 @@ var ModelInitializer = class _ModelInitializer {
7458
7512
  }
7459
7513
  return new openai.ChatOpenAI(config);
7460
7514
  },
7461
- [ModelProvider.ANTHROPIC]: ({ modelName, defaultTemperature, defaultMaxTokens, apiToken, baseURL }) => {
7515
+ [ModelProvider.ANTHROPIC]: ({ modelName, defaultTemperature, defaultMaxTokens, apiToken, baseURL }, promptCacheControl) => {
7462
7516
  const routerURL = resolveRouterURL(baseURL);
7463
7517
  return new anthropic.ChatAnthropic({
7464
7518
  modelName,
@@ -7468,6 +7522,13 @@ var ModelInitializer = class _ModelInitializer {
7468
7522
  temperature: defaultTemperature
7469
7523
  },
7470
7524
  maxTokens: defaultMaxTokens,
7525
+ // Top-level prompt caching: one breakpoint on the last cacheable block,
7526
+ // advanced automatically as the conversation grows. Survives the router
7527
+ // (which forwards the Anthropic body untouched) and is accepted by
7528
+ // Bedrock's InvokeModel schema behind it.
7529
+ ...promptCacheControl && {
7530
+ cache_control: promptCacheControl
7531
+ },
7471
7532
  anthropicApiKey: apiToken || this.resolveApiKey(ModelProvider.ANTHROPIC),
7472
7533
  ...routerURL && {
7473
7534
  anthropicApiUrl: routerURL,
@@ -7672,9 +7733,17 @@ var ModelInitializer = class _ModelInitializer {
7672
7733
  defaultMaxTokens: toOptionalNumber(config.maxTokens) ?? toOptionalNumber(modelConfig.defaultMaxTokens),
7673
7734
  baseURL: config.baseURL ?? modelConfig.baseURL
7674
7735
  };
7736
+ const promptCacheControl = resolvePromptCacheControl([
7737
+ finalConfig.bedrockModelId,
7738
+ finalConfig.modelName
7739
+ ], config.promptCache ?? modelConfig.promptCache, config.promptCacheTtl ?? modelConfig.promptCacheTtl);
7740
+ if (promptCacheControl) {
7741
+ this.logger.debug(`Prompt caching enabled for ${finalConfig.bedrockModelId ?? finalConfig.modelName} (ttl=${promptCacheControl.ttl})`);
7742
+ }
7675
7743
  this.logger.debug(`Creating new chat model instance: ${cacheKey}`);
7676
7744
  let model;
7677
7745
  if (finalConfig.useBedrock && finalConfig.bedrockModelId) {
7746
+ const effortFields = buildEffortRequestFields(config.effort ?? modelConfig.defaultEffort);
7678
7747
  model = new FlutchChatBedrockConverse({
7679
7748
  model: finalConfig.bedrockModelId,
7680
7749
  region: this.resolveBedrockRegion(),
@@ -7682,6 +7751,9 @@ var ModelInitializer = class _ModelInitializer {
7682
7751
  ...finalConfig.defaultTemperature !== void 0 && {
7683
7752
  temperature: finalConfig.defaultTemperature
7684
7753
  },
7754
+ ...effortFields && {
7755
+ additionalModelRequestFields: effortFields
7756
+ },
7685
7757
  maxTokens: finalConfig.defaultMaxTokens,
7686
7758
  streaming: true
7687
7759
  });
@@ -7690,14 +7762,24 @@ var ModelInitializer = class _ModelInitializer {
7690
7762
  if (!creator) {
7691
7763
  throw new Error(`Chat models not supported for provider: ${modelConfig.provider}`);
7692
7764
  }
7693
- model = creator(finalConfig);
7765
+ model = creator(finalConfig, promptCacheControl);
7694
7766
  }
7695
7767
  model.metadata = {
7696
7768
  ...model.metadata,
7697
7769
  modelId: config.modelId
7698
7770
  };
7699
7771
  if (config.toolsConfig || config.customTools || config.mcpServers && config.mcpServers.length > 0) {
7700
- const boundModel = await this.bindToolsToModel(model, config.toolsConfig, config.customTools, config.mcpServers, config.mcpContext);
7772
+ const boundModel = await this.bindToolsToModel(
7773
+ model,
7774
+ config.toolsConfig,
7775
+ config.customTools,
7776
+ config.mcpServers,
7777
+ config.mcpContext,
7778
+ // Converse takes cache control as a per-call option; the Anthropic
7779
+ // client already got it as a constructor field, and handing it the
7780
+ // same value twice would put an unknown key in its call options.
7781
+ finalConfig.useBedrock ? promptCacheControl : void 0
7782
+ );
7701
7783
  this.modelInstanceCache.set(cacheKey, boundModel);
7702
7784
  return boundModel;
7703
7785
  }
@@ -7709,11 +7791,20 @@ var ModelInitializer = class _ModelInitializer {
7709
7791
  * For toolsConfig: fetch tool executors from MCP Runtime
7710
7792
  * For customTools: use as-is (already prepared DynamicStructuredTool)
7711
7793
  *
7794
+ * `promptCacheControl` is the **Bedrock-only** half of prompt caching: it is
7795
+ * bound alongside the tools as a default call option, and `ChatBedrockConverse`
7796
+ * turns it into `cachePoint` blocks after the tool schemas, the system prompt
7797
+ * and the last message. Tool schemas dominate the request, so this is the one
7798
+ * place worth caching — a model with no tools keeps its plain `BaseChatModel`
7799
+ * type and no cache points (callers there rely on `withStructuredOutput` and
7800
+ * friends). The Anthropic client takes its cache settings in the constructor
7801
+ * instead and must not be handed them again here.
7802
+ *
7712
7803
  * Returns:
7713
7804
  * - Runnable when tools are bound (model.bindTools returns Runnable)
7714
7805
  * - BaseChatModel when no tools
7715
7806
  */
7716
- async bindToolsToModel(model, toolsConfig, customTools, mcpServers, mcpContext) {
7807
+ async bindToolsToModel(model, toolsConfig, customTools, mcpServers, mcpContext, promptCacheControl) {
7717
7808
  const allTools = [];
7718
7809
  const enabledToolsConfig = (toolsConfig || []).filter((tc) => tc.enabled !== false);
7719
7810
  const hasInlineServers = !!(mcpServers && mcpServers.length > 0);
@@ -7734,7 +7825,10 @@ var ModelInitializer = class _ModelInitializer {
7734
7825
  }
7735
7826
  if (allTools.length > 0) {
7736
7827
  this.logger.debug(`Binding ${allTools.length} tools to model`);
7737
- const modelWithTools = model.bindTools(allTools);
7828
+ const boundOptions = {
7829
+ cache_control: promptCacheControl
7830
+ };
7831
+ const modelWithTools = promptCacheControl ? model.bindTools(allTools, boundOptions) : model.bindTools(allTools);
7738
7832
  return modelWithTools;
7739
7833
  }
7740
7834
  return model;
@@ -8553,6 +8647,7 @@ exports.ModelProvider = ModelProvider;
8553
8647
  exports.ModelType = ModelType;
8554
8648
  exports.MongoTokenStore = MongoTokenStore;
8555
8649
  exports.OAuthTokenManager = OAuthTokenManager;
8650
+ exports.PROMPT_CACHE_MIN_VERSIONS = PROMPT_CACHE_MIN_VERSIONS;
8556
8651
  exports.RetrieverSearchType = RetrieverSearchType;
8557
8652
  exports.SAMPLING_PARAM_FREE_THRESHOLDS = SAMPLING_PARAM_FREE_THRESHOLDS;
8558
8653
  exports.StreamChannel = StreamChannel;
@@ -8564,6 +8659,7 @@ exports.WithEndpoints = WithEndpoints;
8564
8659
  exports.WithUIEndpoints = WithUIEndpoints;
8565
8660
  exports._internals = _internals;
8566
8661
  exports.bootstrap = bootstrap;
8662
+ exports.buildEffortRequestFields = buildEffortRequestFields;
8567
8663
  exports.buildOAuthAuthorizationUrl = buildOAuthAuthorizationUrl;
8568
8664
  exports.buildOpenAIModelConfig = buildOpenAIModelConfig;
8569
8665
  exports.clearAttachmentDataStore = clearAttachmentDataStore;
@@ -8597,12 +8693,15 @@ exports.isInternalMode = isInternalMode;
8597
8693
  exports.isReasoningBlock = isReasoningBlock;
8598
8694
  exports.isReasoningModel = isReasoningModel;
8599
8695
  exports.isSendableReasoningBlock = isSendableReasoningBlock;
8696
+ exports.joinTextSteps = joinTextSteps;
8600
8697
  exports.loadOAuthProviders = loadOAuthProviders;
8601
8698
  exports.modelAcceptsSamplingParams = modelAcceptsSamplingParams;
8699
+ exports.modelSupportsPromptCache = modelSupportsPromptCache;
8602
8700
  exports.normalizeToolConfigs = normalizeToolConfigs;
8603
8701
  exports.registerFinanceExampleCallback = registerFinanceExampleCallback;
8604
8702
  exports.registerUIEndpointsFromClass = registerUIEndpointsFromClass;
8605
8703
  exports.resolveOAuthProviderConfig = resolveOAuthProviderConfig;
8704
+ exports.resolvePromptCacheControl = resolvePromptCacheControl;
8606
8705
  exports.resolveRouterURL = resolveRouterURL;
8607
8706
  exports.resolveSamplingParams = resolveSamplingParams;
8608
8707
  exports.sanitizeMessageReasoning = sanitizeMessageReasoning;