@flutchai/flutch-sdk 0.6.12 → 0.7.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.
package/dist/index.js CHANGED
@@ -20,6 +20,8 @@ import { dispatchCustomEvent } from '@langchain/core/callbacks/dispatch';
20
20
  import { DynamicStructuredTool } from '@langchain/core/tools';
21
21
  import axios2 from 'axios';
22
22
  import { parseCallbackConfigArg, CallbackManager } from '@langchain/core/callbacks/manager';
23
+ import { RunnableLambda } from '@langchain/core/runnables';
24
+ import { z } from 'zod';
23
25
  import { ChatOpenAI, OpenAIEmbeddings } from '@langchain/openai';
24
26
  import { ChatBedrockConverse } from '@langchain/aws';
25
27
  import { fetcher } from 'cohere-ai/core';
@@ -7056,6 +7058,314 @@ McpRuntimeHttpClient = _ts_decorate25([
7056
7058
  ])
7057
7059
  ], McpRuntimeHttpClient);
7058
7060
 
7061
+ // src/tools/tool-search/tool-search.catalog.ts
7062
+ var K1 = 1.2;
7063
+ var B = 0.75;
7064
+ var NAME_WEIGHT = 3;
7065
+ var STEM_LENGTH = 5;
7066
+ var HIT_DESCRIPTION_CHARS = 240;
7067
+ function toolSearchTerms(text) {
7068
+ return text.replace(/([a-z])([A-Z])/g, "$1 $2").toLowerCase().split(/[^\p{L}\p{N}]+/u).filter((word) => word.length > 1).map((word) => word.slice(0, STEM_LENGTH));
7069
+ }
7070
+ __name(toolSearchTerms, "toolSearchTerms");
7071
+ function schemaParams(schema) {
7072
+ const json = schema;
7073
+ const properties = json?.properties ?? json?.shape ?? {};
7074
+ return Object.entries(properties).flatMap(([name, property]) => property?.description ? [
7075
+ name,
7076
+ property.description
7077
+ ] : [
7078
+ name
7079
+ ]);
7080
+ }
7081
+ __name(schemaParams, "schemaParams");
7082
+ function shortDescription(description) {
7083
+ const flat = description.replace(/\s+/g, " ").trim();
7084
+ if (flat.length <= HIT_DESCRIPTION_CHARS) return flat;
7085
+ const cut = flat.slice(0, HIT_DESCRIPTION_CHARS);
7086
+ const sentenceEnd = cut.lastIndexOf(". ");
7087
+ return sentenceEnd > HIT_DESCRIPTION_CHARS / 2 ? cut.slice(0, sentenceEnd + 1) : `${cut.trimEnd()}\u2026`;
7088
+ }
7089
+ __name(shortDescription, "shortDescription");
7090
+ var ToolSearchCatalog = class {
7091
+ static {
7092
+ __name(this, "ToolSearchCatalog");
7093
+ }
7094
+ entries;
7095
+ documents;
7096
+ lengths;
7097
+ averageLength;
7098
+ documentFrequency = /* @__PURE__ */ new Map();
7099
+ constructor(tools) {
7100
+ this.entries = tools.map((tool) => ({
7101
+ name: tool.name,
7102
+ description: tool.description ?? "",
7103
+ params: schemaParams(tool.schema)
7104
+ }));
7105
+ this.documents = this.entries.map((entry) => {
7106
+ const nameTerms = toolSearchTerms(entry.name);
7107
+ const terms = [
7108
+ ...Array.from({
7109
+ length: NAME_WEIGHT
7110
+ }, () => nameTerms).flat(),
7111
+ ...toolSearchTerms(entry.description),
7112
+ ...entry.params.flatMap(toolSearchTerms)
7113
+ ];
7114
+ const counts = /* @__PURE__ */ new Map();
7115
+ for (const term of terms) counts.set(term, (counts.get(term) ?? 0) + 1);
7116
+ return counts;
7117
+ });
7118
+ this.lengths = this.documents.map((counts) => Array.from(counts.values()).reduce((sum, n) => sum + n, 0));
7119
+ this.averageLength = this.lengths.reduce((sum, n) => sum + n, 0) / Math.max(this.lengths.length, 1);
7120
+ for (const counts of this.documents) {
7121
+ for (const term of counts.keys()) {
7122
+ this.documentFrequency.set(term, (this.documentFrequency.get(term) ?? 0) + 1);
7123
+ }
7124
+ }
7125
+ }
7126
+ get size() {
7127
+ return this.entries.length;
7128
+ }
7129
+ has(name) {
7130
+ return this.entries.some((entry) => entry.name === name);
7131
+ }
7132
+ /**
7133
+ * The best `limit` tools for a query, best first. A query that names a tool
7134
+ * exactly returns that tool first whatever its score. Tools that share no
7135
+ * word with the query are never returned.
7136
+ */
7137
+ search(query, limit) {
7138
+ const terms = Array.from(new Set(toolSearchTerms(query)));
7139
+ const exact = query.split(/[\s,]+/).map((word) => word.trim()).filter((word) => this.has(word));
7140
+ const total = this.entries.length;
7141
+ const scored = this.entries.map((entry, index) => {
7142
+ const counts = this.documents[index];
7143
+ const length = this.lengths[index];
7144
+ let score = 0;
7145
+ for (const term of terms) {
7146
+ const frequency = counts.get(term);
7147
+ if (!frequency) continue;
7148
+ const df = this.documentFrequency.get(term) ?? 0;
7149
+ const idf = Math.log(1 + (total - df + 0.5) / (df + 0.5));
7150
+ score += idf * frequency * (K1 + 1) / (frequency + K1 * (1 - B + B * length / this.averageLength));
7151
+ }
7152
+ if (exact.includes(entry.name)) score += 1e3;
7153
+ return {
7154
+ entry,
7155
+ score
7156
+ };
7157
+ }).filter(({ score }) => score > 0).sort((a, b) => b.score - a.score);
7158
+ return scored.slice(0, Math.max(1, limit)).map(({ entry }) => ({
7159
+ name: entry.name,
7160
+ description: shortDescription(entry.description)
7161
+ }));
7162
+ }
7163
+ /**
7164
+ * What the catalog covers, as name prefixes with counts:
7165
+ * "funnel_* (4), recipe_* (5), rl_* (41)". Goes into the search tool's
7166
+ * description so the model knows a search is worth making. Sorted, so the
7167
+ * description — and the prompt cache — stays the same between requests.
7168
+ */
7169
+ summary(maxGroups = 20) {
7170
+ const groups = /* @__PURE__ */ new Map();
7171
+ for (const entry of this.entries) {
7172
+ const prefix = entry.name.includes("_") ? `${entry.name.split("_")[0]}_*` : entry.name;
7173
+ groups.set(prefix, (groups.get(prefix) ?? 0) + 1);
7174
+ }
7175
+ const sorted = Array.from(groups.entries()).sort(([a], [b]) => a.localeCompare(b));
7176
+ const shown = sorted.slice(0, maxGroups).map(([prefix, count]) => count > 1 ? `${prefix} (${count})` : prefix);
7177
+ return sorted.length > maxGroups ? `${shown.join(", ")}, \u2026` : shown.join(", ");
7178
+ }
7179
+ };
7180
+
7181
+ // src/tools/tool-search/tool-search.result.ts
7182
+ var TOOL_SEARCH_NAME = "search_tools";
7183
+ function isToolSearchCall(name) {
7184
+ return name === TOOL_SEARCH_NAME;
7185
+ }
7186
+ __name(isToolSearchCall, "isToolSearchCall");
7187
+ function formatToolSearchResult(hits) {
7188
+ if (hits.length === 0) {
7189
+ return JSON.stringify({
7190
+ tools: [],
7191
+ note: "No tools matched. Search again with other words: what the tool does, the object it works on, or a name prefix."
7192
+ });
7193
+ }
7194
+ return JSON.stringify({
7195
+ tools: hits,
7196
+ note: "These tools are available now. Call them directly."
7197
+ });
7198
+ }
7199
+ __name(formatToolSearchResult, "formatToolSearchResult");
7200
+ function parseToolSearchResult(content) {
7201
+ const text = contentText(content);
7202
+ if (!text) return [];
7203
+ try {
7204
+ const parsed = JSON.parse(text);
7205
+ return (parsed.tools ?? []).map((tool) => tool?.name).filter((name) => typeof name === "string");
7206
+ } catch {
7207
+ return [];
7208
+ }
7209
+ }
7210
+ __name(parseToolSearchResult, "parseToolSearchResult");
7211
+ function contentText(content) {
7212
+ if (typeof content === "string") return content;
7213
+ if (Array.isArray(content)) {
7214
+ return content.map((part) => typeof part === "string" ? part : part && typeof part === "object" && "text" in part ? String(part.text) : "").join("");
7215
+ }
7216
+ return "";
7217
+ }
7218
+ __name(contentText, "contentText");
7219
+ function loadedToolNames(messages) {
7220
+ const searchCallIds = /* @__PURE__ */ new Set();
7221
+ for (const message of messages) {
7222
+ const calls = message.tool_calls;
7223
+ for (const call of calls ?? []) {
7224
+ if (isToolSearchCall(call.name) && call.id) searchCallIds.add(call.id);
7225
+ }
7226
+ }
7227
+ const loaded = [];
7228
+ for (const message of messages) {
7229
+ if (message._getType() !== "tool") continue;
7230
+ const tool = message;
7231
+ const isSearch = isToolSearchCall(tool.name) || tool.tool_call_id !== void 0 && searchCallIds.has(tool.tool_call_id);
7232
+ if (!isSearch) continue;
7233
+ for (const name of parseToolSearchResult(tool.content)) {
7234
+ if (!loaded.includes(name)) loaded.push(name);
7235
+ }
7236
+ }
7237
+ return loaded;
7238
+ }
7239
+ __name(loadedToolNames, "loadedToolNames");
7240
+ var DEFAULT_TOOL_SEARCH_RESULTS = 5;
7241
+ var DEFAULT_TOOL_SEARCH_MIN_TOOLS = 15;
7242
+ function toolSearchApplies(options, toolCount) {
7243
+ return !!options?.enabled && toolCount >= (options.minTools ?? DEFAULT_TOOL_SEARCH_MIN_TOOLS);
7244
+ }
7245
+ __name(toolSearchApplies, "toolSearchApplies");
7246
+ function splitTools(tools, options, extraCore = []) {
7247
+ const listed = typeof options.alwaysLoaded === "string" ? options.alwaysLoaded.split(",") : options.alwaysLoaded ?? [];
7248
+ const always = /* @__PURE__ */ new Set([
7249
+ ...listed.map((name) => name.trim()).filter(Boolean),
7250
+ ...extraCore.map((tool) => tool.name)
7251
+ ]);
7252
+ return {
7253
+ core: tools.filter((tool) => always.has(tool.name)),
7254
+ deferred: tools.filter((tool) => !always.has(tool.name))
7255
+ };
7256
+ }
7257
+ __name(splitTools, "splitTools");
7258
+ function createToolSearchTool(catalog, maxResults = DEFAULT_TOOL_SEARCH_RESULTS) {
7259
+ return new DynamicStructuredTool({
7260
+ name: TOOL_SEARCH_NAME,
7261
+ description: `Find more tools. You see only some of your tools; ${catalog.size} more are found by search: ${catalog.summary()}. Describe the task in a few words (what to do and with what, e.g. "customer review quotes by theme"), or give exact tool names. Found tools become callable right away and stay available for the rest of the conversation. Search before saying you cannot do something.`,
7262
+ schema: z.object({
7263
+ query: z.string().describe("What the tool should do, in a few words, or tool names separated by commas"),
7264
+ limit: z.number().int().min(1).max(20).optional().describe(`How many tools to return (default ${maxResults})`)
7265
+ }),
7266
+ func: /* @__PURE__ */ __name(async ({ query, limit }) => formatToolSearchResult(catalog.search(query, limit ?? maxResults)), "func")
7267
+ });
7268
+ }
7269
+ __name(createToolSearchTool, "createToolSearchTool");
7270
+ function deferredCopy(tool) {
7271
+ const copy = Object.create(tool);
7272
+ copy.extras = {
7273
+ ...tool.extras ?? {},
7274
+ defer_loading: true
7275
+ };
7276
+ return copy;
7277
+ }
7278
+ __name(deferredCopy, "deferredCopy");
7279
+ function bindToolsWithSearch(params) {
7280
+ const { model, core, deferred, searchTool, mode, bindOptions } = params;
7281
+ const bind = /* @__PURE__ */ __name((tools) => bindOptions ? model.bindTools(tools, bindOptions) : model.bindTools(tools), "bind");
7282
+ if (mode === "deferred") {
7283
+ return bind([
7284
+ searchTool,
7285
+ ...core,
7286
+ ...deferred.map(deferredCopy)
7287
+ ]);
7288
+ }
7289
+ const byName = new Map(deferred.map((tool) => [
7290
+ tool.name,
7291
+ tool
7292
+ ]));
7293
+ const bound = /* @__PURE__ */ new Map();
7294
+ const forHistory = /* @__PURE__ */ __name((messages) => {
7295
+ const found = loadedToolNames(messages).filter((name) => byName.has(name));
7296
+ const key = found.join(",");
7297
+ let runnable = bound.get(key);
7298
+ if (!runnable) {
7299
+ runnable = bind([
7300
+ ...core,
7301
+ searchTool,
7302
+ ...found.map((name) => byName.get(name))
7303
+ ]);
7304
+ bound.set(key, runnable);
7305
+ }
7306
+ return runnable;
7307
+ }, "forHistory");
7308
+ return RunnableLambda.from(async (input, config) => {
7309
+ const messages = Array.isArray(input) ? input : [];
7310
+ return forHistory(messages).invoke(input, config);
7311
+ }).withConfig({
7312
+ runName: "ToolSearchModel"
7313
+ });
7314
+ }
7315
+ __name(bindToolsWithSearch, "bindToolsWithSearch");
7316
+
7317
+ // src/tools/tool-search/tool-reference.fetch.ts
7318
+ function rewriteToolReferences(body) {
7319
+ if (!body.includes(TOOL_SEARCH_NAME)) return body;
7320
+ let parsed;
7321
+ try {
7322
+ parsed = JSON.parse(body);
7323
+ } catch {
7324
+ return body;
7325
+ }
7326
+ const known = new Set((parsed.tools ?? []).map((tool) => tool.name));
7327
+ const searchIds = /* @__PURE__ */ new Set();
7328
+ for (const message of parsed.messages ?? []) {
7329
+ if (message.role !== "assistant" || !Array.isArray(message.content)) continue;
7330
+ for (const block of message.content) {
7331
+ if (block.type === "tool_use" && block.name === TOOL_SEARCH_NAME && block.id) {
7332
+ searchIds.add(block.id);
7333
+ }
7334
+ }
7335
+ }
7336
+ if (searchIds.size === 0) return body;
7337
+ let changed = false;
7338
+ for (const message of parsed.messages ?? []) {
7339
+ if (message.role !== "user" || !Array.isArray(message.content)) continue;
7340
+ for (const block of message.content) {
7341
+ if (block.type !== "tool_result" || !block.tool_use_id) continue;
7342
+ if (!searchIds.has(block.tool_use_id)) continue;
7343
+ const names = parseToolSearchResult(block.content).filter((name) => known.has(name));
7344
+ if (names.length === 0) continue;
7345
+ block.content = names.map((name) => ({
7346
+ type: "tool_reference",
7347
+ tool_name: name
7348
+ }));
7349
+ changed = true;
7350
+ }
7351
+ }
7352
+ return changed ? JSON.stringify(parsed) : body;
7353
+ }
7354
+ __name(rewriteToolReferences, "rewriteToolReferences");
7355
+ function withToolReferences(inner) {
7356
+ return (input, init) => {
7357
+ if (init && typeof init.body === "string") {
7358
+ const body = rewriteToolReferences(init.body);
7359
+ if (body !== init.body) return inner(input, {
7360
+ ...init,
7361
+ body
7362
+ });
7363
+ }
7364
+ return inner(input, init);
7365
+ };
7366
+ }
7367
+ __name(withToolReferences, "withToolReferences");
7368
+
7059
7369
  // src/models/enums.ts
7060
7370
  var ModelProvider = /* @__PURE__ */ (function(ModelProvider2) {
7061
7371
  ModelProvider2["MISTRAL"] = "mistral";
@@ -7237,7 +7547,8 @@ function buildOpenAIModelConfig(modelName, temperature, maxTokens, apiToken) {
7237
7547
  temperature: 1,
7238
7548
  maxCompletionTokens: maxTokens,
7239
7549
  streaming: true,
7240
- openAIApiKey: apiToken
7550
+ useResponsesApi: true,
7551
+ apiKey: apiToken
7241
7552
  };
7242
7553
  }
7243
7554
  return {
@@ -7249,7 +7560,7 @@ function buildOpenAIModelConfig(modelName, temperature, maxTokens, apiToken) {
7249
7560
  },
7250
7561
  maxTokens,
7251
7562
  streaming: true,
7252
- openAIApiKey: apiToken
7563
+ apiKey: apiToken
7253
7564
  };
7254
7565
  }
7255
7566
  __name(buildOpenAIModelConfig, "buildOpenAIModelConfig");
@@ -7422,6 +7733,8 @@ var ModelInitializer = class _ModelInitializer {
7422
7733
  modelConfigCache = /* @__PURE__ */ new Map();
7423
7734
  // Cache for model instances to avoid recreating identical models
7424
7735
  modelInstanceCache = /* @__PURE__ */ new Map();
7736
+ // Tool-search catalogs, keyed like the model instance that uses them
7737
+ toolSearchCatalogs = /* @__PURE__ */ new Map();
7425
7738
  static DEFAULT_ENV_MAP = {
7426
7739
  [ModelProvider.OPENAI]: "OPENAI_API_KEY",
7427
7740
  [ModelProvider.ANTHROPIC]: "ANTHROPIC_API_KEY",
@@ -7497,7 +7810,8 @@ var ModelInitializer = class _ModelInitializer {
7497
7810
  const withMcp = config.mcpServers && config.mcpServers.length > 0 ? `${base}:mcp:${JSON.stringify(config.mcpServers)}` : base;
7498
7811
  const withSampling = config.supportsSamplingParams === void 0 ? withMcp : `${withMcp}:sp:${config.supportsSamplingParams}`;
7499
7812
  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}`;
7813
+ const withEffort = config.effort === void 0 ? withCache : `${withCache}:eff:${config.effort}`;
7814
+ return config.toolSearch?.enabled ? `${withEffort}:ts:${JSON.stringify(config.toolSearch)}` : withEffort;
7501
7815
  }
7502
7816
  // Chat model creators
7503
7817
  chatModelCreators = {
@@ -7531,10 +7845,13 @@ var ModelInitializer = class _ModelInitializer {
7531
7845
  },
7532
7846
  anthropicApiKey: apiToken || this.resolveApiKey(ModelProvider.ANTHROPIC),
7533
7847
  ...routerURL && {
7534
- anthropicApiUrl: routerURL,
7535
- clientOptions: {
7536
- fetch: flutchFetch
7537
- }
7848
+ anthropicApiUrl: routerURL
7849
+ },
7850
+ // Tool search results become `tool_reference` blocks on the wire
7851
+ // (LangChain cannot express them). A request without a search passes
7852
+ // through untouched.
7853
+ clientOptions: {
7854
+ fetch: withToolReferences(routerURL ? flutchFetch : fetch)
7538
7855
  }
7539
7856
  });
7540
7857
  },
@@ -7778,7 +8095,11 @@ var ModelInitializer = class _ModelInitializer {
7778
8095
  // Converse takes cache control as a per-call option; the Anthropic
7779
8096
  // client already got it as a constructor field, and handing it the
7780
8097
  // same value twice would put an unknown key in its call options.
7781
- finalConfig.useBedrock ? promptCacheControl : void 0
8098
+ finalConfig.useBedrock ? promptCacheControl : void 0,
8099
+ config.toolSearch?.enabled ? {
8100
+ options: config.toolSearch,
8101
+ cacheKey
8102
+ } : void 0
7782
8103
  );
7783
8104
  this.modelInstanceCache.set(cacheKey, boundModel);
7784
8105
  return boundModel;
@@ -7804,8 +8125,9 @@ var ModelInitializer = class _ModelInitializer {
7804
8125
  * - Runnable when tools are bound (model.bindTools returns Runnable)
7805
8126
  * - BaseChatModel when no tools
7806
8127
  */
7807
- async bindToolsToModel(model, toolsConfig, customTools, mcpServers, mcpContext, promptCacheControl) {
8128
+ async bindToolsToModel(model, toolsConfig, customTools, mcpServers, mcpContext, promptCacheControl, toolSearch) {
7808
8129
  const allTools = [];
8130
+ const mcpToolsFound = [];
7809
8131
  const enabledToolsConfig = (toolsConfig || []).filter((tc) => tc.enabled !== false);
7810
8132
  const hasInlineServers = !!(mcpServers && mcpServers.length > 0);
7811
8133
  if (enabledToolsConfig.length > 0 || hasInlineServers) {
@@ -7815,6 +8137,7 @@ var ModelInitializer = class _ModelInitializer {
7815
8137
  const mcpTools = await mcpToolFilter.getFilteredTools(enabledToolsConfig, mcpServers, mcpContext);
7816
8138
  this.logger.debug(`Successfully fetched ${mcpTools.length} tools with dynamic schemas from MCP Runtime`);
7817
8139
  allTools.push(...mcpTools);
8140
+ mcpToolsFound.push(...mcpTools);
7818
8141
  } catch (error) {
7819
8142
  this.logger.error(`Failed to fetch tools from MCP Runtime: ${error instanceof Error ? error.message : String(error)}`);
7820
8143
  }
@@ -7828,11 +8151,86 @@ var ModelInitializer = class _ModelInitializer {
7828
8151
  const boundOptions = {
7829
8152
  cache_control: promptCacheControl
7830
8153
  };
8154
+ if (toolSearch && toolSearchApplies(toolSearch.options, allTools.length)) {
8155
+ const { core, deferred } = splitTools(mcpToolsFound, toolSearch.options, customTools ?? []);
8156
+ if (deferred.length > 0) {
8157
+ const maxResults = toolSearch.options.maxResults ?? DEFAULT_TOOL_SEARCH_RESULTS;
8158
+ const catalog = new ToolSearchCatalog(deferred);
8159
+ this.toolSearchCatalogs.set(toolSearch.cacheKey, {
8160
+ catalog,
8161
+ maxResults
8162
+ });
8163
+ const mode = model instanceof ChatAnthropic ? "deferred" : "rebind";
8164
+ this.logger.debug(`Tool search (${mode}): ${core.length + (customTools?.length ?? 0)} tools in sight, ${deferred.length} found on demand`);
8165
+ return bindToolsWithSearch({
8166
+ model,
8167
+ core: [
8168
+ ...core,
8169
+ ...customTools ?? []
8170
+ ],
8171
+ deferred,
8172
+ searchTool: createToolSearchTool(catalog, maxResults),
8173
+ mode,
8174
+ bindOptions: promptCacheControl ? boundOptions : void 0
8175
+ });
8176
+ }
8177
+ }
7831
8178
  const modelWithTools = promptCacheControl ? model.bindTools(allTools, boundOptions) : model.bindTools(allTools);
7832
8179
  return modelWithTools;
7833
8180
  }
7834
8181
  return model;
7835
8182
  }
8183
+ /**
8184
+ * Runs a `search_tools` call. `config` must be the one the model was
8185
+ * initialized with (same tools, same `toolSearch`): it names the catalog.
8186
+ * Emits tool start/end events like `McpRuntimeHttpClient.executeToolWithEvents`,
8187
+ * so the call shows as a finished step in the chat.
8188
+ *
8189
+ * Never throws: a failure comes back as the tool's answer.
8190
+ */
8191
+ async searchTools(config, args, runConfig) {
8192
+ const parsedConfig = parseCallbackConfigArg(runConfig);
8193
+ const callbackManager = CallbackManager.configure(parsedConfig.callbacks);
8194
+ const runManager = await callbackManager?.handleToolStart({
8195
+ name: TOOL_SEARCH_NAME,
8196
+ lc: 1,
8197
+ type: "not_implemented",
8198
+ id: [
8199
+ "langchain",
8200
+ "tools",
8201
+ TOOL_SEARCH_NAME
8202
+ ]
8203
+ }, JSON.stringify(args ?? {}), parsedConfig.runId, void 0, parsedConfig.tags, parsedConfig.metadata, TOOL_SEARCH_NAME);
8204
+ try {
8205
+ const key = this.generateModelCacheKey(config);
8206
+ if (!this.toolSearchCatalogs.has(key)) {
8207
+ await this.initializeChatModel(config);
8208
+ }
8209
+ const entry = this.toolSearchCatalogs.get(key);
8210
+ const query = typeof args?.query === "string" ? args.query : "";
8211
+ const limit = Number(args?.limit);
8212
+ const content = !entry ? JSON.stringify({
8213
+ error: "Tool search is not available for this agent: every tool is already loaded."
8214
+ }) : !query.trim() ? JSON.stringify({
8215
+ error: "query is required"
8216
+ }) : formatToolSearchResult(entry.catalog.search(query, Number.isFinite(limit) && limit > 0 ? limit : entry.maxResults));
8217
+ await runManager?.handleToolEnd(content);
8218
+ return {
8219
+ content,
8220
+ success: !!entry && !!query.trim()
8221
+ };
8222
+ } catch (error) {
8223
+ await runManager?.handleToolError(error);
8224
+ const message = error instanceof Error ? error.message : String(error);
8225
+ this.logger.error(`Tool search failed: ${message}`);
8226
+ return {
8227
+ content: JSON.stringify({
8228
+ error: `Tool search failed: ${message}`
8229
+ }),
8230
+ success: false
8231
+ };
8232
+ }
8233
+ }
7836
8234
  async initializeRerankModel(config) {
7837
8235
  const cacheKey = this.generateModelCacheKey(config);
7838
8236
  const cachedModel = this.modelInstanceCache.get(cacheKey);
@@ -8621,6 +9019,6 @@ var MongoTokenStore = class {
8621
9019
  }
8622
9020
  };
8623
9021
 
8624
- export { AbstractGraphBuilder, AttachmentType, GraphController as BaseGraphServiceController, UniversalGraphModule as BaseGraphServiceModule, BuilderRegistryService, Callback, CallbackACL, CallbackAuditAction, CallbackAuditor, CallbackController, CallbackMetrics, CallbackPatchService, CallbackRateLimiter, CallbackRegistry, CallbackStore, CallbackTokenGuard, ChatFeature, DEFAULT_ATTACHMENT_THRESHOLD, DEFAULT_ROUTER_URL, DEFAULT_TRACER_OPTIONS, ENDPOINT_METADATA_KEY, Endpoint, EndpointRegistry, EventProcessor, ExternalGraphBuilder, FileBasedDiscovery, FileTokenStore, FlutchChatBedrockConverse, GraphController, GraphEngineFactory, GraphEngineType, GraphManifestSchema, GraphManifestValidator, GraphServiceTokens, GraphTypeUtils, IdempotencyManager, IdempotencyStatus, LangGraphEngine, McpConverter, McpRuntimeHttpClient, McpToolFilter, ModelInitializer, ModelProvider, ModelType, MongoTokenStore, OAuthTokenManager, PROMPT_CACHE_MIN_VERSIONS, RetrieverSearchType, RetrieverService, SAMPLING_PARAM_FREE_THRESHOLDS, SmartCallbackRouter, StaticDiscovery, StreamChannel, TelegramPatchHandler, UIDispatchController, UIEndpoint, UIEndpointsDiscoveryService, UniversalCallbackService, UniversalGraphModule, UniversalGraphService, VersionedGraphService, VoyageAIRerank, WebPatchHandler, WithCallbacks, WithEndpoints, WithUIEndpoints, _internals, bootstrap, buildEffortRequestFields, buildOAuthAuthorizationUrl, buildOpenAIModelConfig, clearAttachmentDataStore, createEndpointDescriptors, createGraphAttachment, createMongoClientAdapter, createStaticMessage, decryptTokens, dispatchAttachments, encryptTokens, executeToolWithAttachments, findCallbackMethod, findEndpointMethod, flutchFetch, flutchHeaders, flutchMistralHook, generateAttachmentSummary, generateModelCacheKey, getAttachmentData, getCallbackMetadata, getEndpointMetadata, getFlutchContext, getOAuthProvider, getOAuthProviderNames, getUIEndpointClassMetadata, getUIEndpointMethodsMetadata, hasCallbacks, hasUIEndpoints, hashToolsConfig, isInternalMode, isReasoningBlock, isReasoningModel, isSendableReasoningBlock, joinTextSteps, loadOAuthProviders, modelAcceptsSamplingParams, modelSupportsPromptCache, normalizeToolConfigs, redactConnectionString, registerFinanceExampleCallback, registerUIEndpointsFromClass, resolveOAuthProviderConfig, resolvePromptCacheControl, resolveRouterURL, resolveSamplingParams, sanitizeMessageReasoning, sanitizeReasoningForBedrock, sanitizeTraceData, storeAttachmentData, toOptionalNumber, traceApiCall, withFlutchContext, wrapCohereFetcher };
9022
+ export { AbstractGraphBuilder, AttachmentType, GraphController as BaseGraphServiceController, UniversalGraphModule as BaseGraphServiceModule, BuilderRegistryService, Callback, CallbackACL, CallbackAuditAction, CallbackAuditor, CallbackController, CallbackMetrics, CallbackPatchService, CallbackRateLimiter, CallbackRegistry, CallbackStore, CallbackTokenGuard, ChatFeature, DEFAULT_ATTACHMENT_THRESHOLD, DEFAULT_ROUTER_URL, DEFAULT_TOOL_SEARCH_MIN_TOOLS, DEFAULT_TOOL_SEARCH_RESULTS, DEFAULT_TRACER_OPTIONS, ENDPOINT_METADATA_KEY, Endpoint, EndpointRegistry, EventProcessor, ExternalGraphBuilder, FileBasedDiscovery, FileTokenStore, FlutchChatBedrockConverse, GraphController, GraphEngineFactory, GraphEngineType, GraphManifestSchema, GraphManifestValidator, GraphServiceTokens, GraphTypeUtils, IdempotencyManager, IdempotencyStatus, LangGraphEngine, McpConverter, McpRuntimeHttpClient, McpToolFilter, ModelInitializer, ModelProvider, ModelType, MongoTokenStore, OAuthTokenManager, PROMPT_CACHE_MIN_VERSIONS, RetrieverSearchType, RetrieverService, SAMPLING_PARAM_FREE_THRESHOLDS, SmartCallbackRouter, StaticDiscovery, StreamChannel, TOOL_SEARCH_NAME, TelegramPatchHandler, ToolSearchCatalog, UIDispatchController, UIEndpoint, UIEndpointsDiscoveryService, UniversalCallbackService, UniversalGraphModule, UniversalGraphService, VersionedGraphService, VoyageAIRerank, WebPatchHandler, WithCallbacks, WithEndpoints, WithUIEndpoints, _internals, bindToolsWithSearch, bootstrap, buildEffortRequestFields, buildOAuthAuthorizationUrl, buildOpenAIModelConfig, clearAttachmentDataStore, createEndpointDescriptors, createGraphAttachment, createMongoClientAdapter, createStaticMessage, createToolSearchTool, decryptTokens, dispatchAttachments, encryptTokens, executeToolWithAttachments, findCallbackMethod, findEndpointMethod, flutchFetch, flutchHeaders, flutchMistralHook, formatToolSearchResult, generateAttachmentSummary, generateModelCacheKey, getAttachmentData, getCallbackMetadata, getEndpointMetadata, getFlutchContext, getOAuthProvider, getOAuthProviderNames, getUIEndpointClassMetadata, getUIEndpointMethodsMetadata, hasCallbacks, hasUIEndpoints, hashToolsConfig, isInternalMode, isReasoningBlock, isReasoningModel, isSendableReasoningBlock, isToolSearchCall, joinTextSteps, loadOAuthProviders, loadedToolNames, modelAcceptsSamplingParams, modelSupportsPromptCache, normalizeToolConfigs, parseToolSearchResult, redactConnectionString, registerFinanceExampleCallback, registerUIEndpointsFromClass, resolveOAuthProviderConfig, resolvePromptCacheControl, resolveRouterURL, resolveSamplingParams, rewriteToolReferences, sanitizeMessageReasoning, sanitizeReasoningForBedrock, sanitizeTraceData, splitTools, storeAttachmentData, toOptionalNumber, toolSearchApplies, toolSearchTerms, traceApiCall, withFlutchContext, withToolReferences, wrapCohereFetcher };
8625
9023
  //# sourceMappingURL=index.js.map
8626
9024
  //# sourceMappingURL=index.js.map