feat: add QVeris tools for dynamic tool discovery and execution
- Introduced `createQverisTools` function to create QVeris search and execute tools. - Updated `TOOL_GROUPS` to include QVeris tools. - Added configuration options for QVeris in schema and types. - Implemented pre-search optimization for short queries in QVeris tools. - Enhanced error handling and response management for QVeris API interactions.
This commit is contained in:
parent
b151b8d196
commit
69b882c6fc
@ -18,6 +18,7 @@ import { createSessionsSendTool } from "./tools/sessions-send-tool.js";
|
||||
import { createSessionsSpawnTool } from "./tools/sessions-spawn-tool.js";
|
||||
import { createWebFetchTool, createWebSearchTool } from "./tools/web-tools.js";
|
||||
import { createTtsTool } from "./tools/tts-tool.js";
|
||||
import { createQverisTools } from "./tools/qveris-tools.js";
|
||||
|
||||
export function createClawdbotTools(options?: {
|
||||
browserControlUrl?: string;
|
||||
@ -73,6 +74,11 @@ export function createClawdbotTools(options?: {
|
||||
config: options?.config,
|
||||
sandboxed: options?.sandboxed,
|
||||
});
|
||||
const qverisTools = createQverisTools({
|
||||
config: options?.config,
|
||||
sandboxed: options?.sandboxed,
|
||||
agentSessionKey: options?.agentSessionKey,
|
||||
});
|
||||
const tools: AnyAgentTool[] = [
|
||||
createBrowserTool({
|
||||
defaultControlUrl: options?.browserControlUrl,
|
||||
@ -143,6 +149,7 @@ export function createClawdbotTools(options?: {
|
||||
...(webSearchTool ? [webSearchTool] : []),
|
||||
...(webFetchTool ? [webFetchTool] : []),
|
||||
...(imageTool ? [imageTool] : []),
|
||||
...qverisTools,
|
||||
];
|
||||
|
||||
const pluginTools = resolvePluginTools({
|
||||
|
||||
@ -14,6 +14,8 @@ export const TOOL_GROUPS: Record<string, string[]> = {
|
||||
// NOTE: Keep canonical (lowercase) tool names here.
|
||||
"group:memory": ["memory_search", "memory_get"],
|
||||
"group:web": ["web_search", "web_fetch"],
|
||||
// QVeris dynamic tool discovery and execution
|
||||
"group:qveris": ["qveris_search", "qveris_execute"],
|
||||
// Basic workspace/file tools
|
||||
"group:fs": ["read", "write", "edit", "apply_patch"],
|
||||
// Host/runtime execution tools
|
||||
@ -53,6 +55,8 @@ export const TOOL_GROUPS: Record<string, string[]> = {
|
||||
"web_search",
|
||||
"web_fetch",
|
||||
"image",
|
||||
"qveris_search",
|
||||
"qveris_execute",
|
||||
],
|
||||
};
|
||||
|
||||
@ -61,7 +65,7 @@ const TOOL_PROFILES: Record<ToolProfileId, ToolProfilePolicy> = {
|
||||
allow: ["session_status"],
|
||||
},
|
||||
coding: {
|
||||
allow: ["group:fs", "group:runtime", "group:sessions", "group:memory", "image"],
|
||||
allow: ["group:fs", "group:runtime", "group:sessions", "group:memory", "image", "group:qveris"],
|
||||
},
|
||||
messaging: {
|
||||
allow: [
|
||||
|
||||
516
src/agents/tools/qveris-tools.ts
Normal file
516
src/agents/tools/qveris-tools.ts
Normal file
@ -0,0 +1,516 @@
|
||||
import { Type } from "@sinclair/typebox";
|
||||
|
||||
import type { ClawdbotConfig } from "../../config/config.js";
|
||||
import type { AnyAgentTool } from "./common.js";
|
||||
import { jsonResult, readNumberParam, readStringParam } from "./common.js";
|
||||
|
||||
// ============================================================================
|
||||
// Constants
|
||||
// ============================================================================
|
||||
|
||||
const DEFAULT_QVERIS_BASE_URL = "https://qveris.ai/api/v1";
|
||||
const DEFAULT_TIMEOUT_SECONDS = 60;
|
||||
const DEFAULT_MAX_RESPONSE_SIZE = 20480;
|
||||
const DEFAULT_SEARCH_LIMIT = 10;
|
||||
const DEFAULT_PRE_SEARCH_BYTE_THRESHOLD = 100;
|
||||
|
||||
// ============================================================================
|
||||
// Types
|
||||
// ============================================================================
|
||||
|
||||
type QverisConfig = NonNullable<ClawdbotConfig["tools"]>["qveris"];
|
||||
|
||||
/** Search result parameter from QVeris API */
|
||||
interface QverisSearchResultParam {
|
||||
name: string;
|
||||
type: string;
|
||||
required: boolean;
|
||||
description?: {
|
||||
en?: string;
|
||||
[key: string]: string | undefined;
|
||||
};
|
||||
}
|
||||
|
||||
/** Example format from QVeris search API */
|
||||
interface QverisSearchResultExamples {
|
||||
sample_parameters?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
/** Search result tool from QVeris API */
|
||||
interface QverisSearchResultTool {
|
||||
tool_id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
params?: QverisSearchResultParam[];
|
||||
provider_description?: string;
|
||||
stats?: {
|
||||
avg_execution_time_ms?: number;
|
||||
success_rate?: number;
|
||||
};
|
||||
examples?: QverisSearchResultExamples;
|
||||
}
|
||||
|
||||
/** QVeris search API response */
|
||||
interface QverisSearchResponse {
|
||||
query: string;
|
||||
total: number;
|
||||
results: QverisSearchResultTool[];
|
||||
search_id: string;
|
||||
elapsed_time_ms?: number;
|
||||
}
|
||||
|
||||
/** QVeris tool execution response */
|
||||
interface QverisExecutionResponse {
|
||||
execution_id: string;
|
||||
result: {
|
||||
data: unknown;
|
||||
};
|
||||
success: boolean;
|
||||
error_message: string | null;
|
||||
elapsed_time_ms: number;
|
||||
cost?: number;
|
||||
credits_used?: number;
|
||||
}
|
||||
|
||||
/** Mapping from sanitized function name to original tool info */
|
||||
interface QverisToolMapping {
|
||||
functionName: string;
|
||||
toolId: string;
|
||||
searchId: string;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Config Resolution
|
||||
// ============================================================================
|
||||
|
||||
function resolveQverisConfig(cfg?: ClawdbotConfig): QverisConfig {
|
||||
return cfg?.tools?.qveris;
|
||||
}
|
||||
|
||||
function resolveQverisEnabled(params: { config?: QverisConfig; sandboxed?: boolean }): boolean {
|
||||
if (typeof params.config?.enabled === "boolean") return params.config.enabled;
|
||||
// Enabled by default if API key is present
|
||||
return Boolean(resolveQverisApiKey(params.config));
|
||||
}
|
||||
|
||||
function resolveQverisApiKey(config?: QverisConfig): string | undefined {
|
||||
const fromConfig =
|
||||
config && "apiKey" in config && typeof config.apiKey === "string" ? config.apiKey.trim() : "";
|
||||
const fromEnv = (process.env.QVERIS_API_KEY ?? "").trim();
|
||||
return fromConfig || fromEnv || undefined;
|
||||
}
|
||||
|
||||
function resolveQverisBaseUrl(config?: QverisConfig): string {
|
||||
return config?.baseUrl?.trim() || DEFAULT_QVERIS_BASE_URL;
|
||||
}
|
||||
|
||||
function resolveTimeoutSeconds(config?: QverisConfig): number {
|
||||
return config?.timeoutSeconds ?? DEFAULT_TIMEOUT_SECONDS;
|
||||
}
|
||||
|
||||
function resolveMaxResponseSize(config?: QverisConfig): number {
|
||||
return config?.maxResponseSize ?? DEFAULT_MAX_RESPONSE_SIZE;
|
||||
}
|
||||
|
||||
function resolveSearchLimit(config?: QverisConfig): number {
|
||||
return config?.searchLimit ?? DEFAULT_SEARCH_LIMIT;
|
||||
}
|
||||
|
||||
function resolvePreSearchEnabled(config?: QverisConfig): boolean {
|
||||
return config?.preSearchEnabled !== false;
|
||||
}
|
||||
|
||||
function resolvePreSearchByteThreshold(config?: QverisConfig): number {
|
||||
return config?.preSearchByteThreshold ?? DEFAULT_PRE_SEARCH_BYTE_THRESHOLD;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// API Client
|
||||
// ============================================================================
|
||||
|
||||
async function qverisSearch(params: {
|
||||
query: string;
|
||||
sessionId: string;
|
||||
limit: number;
|
||||
apiKey: string;
|
||||
baseUrl: string;
|
||||
timeoutSeconds: number;
|
||||
}): Promise<QverisSearchResponse> {
|
||||
const controller = new AbortController();
|
||||
const timeoutId = setTimeout(() => controller.abort(), params.timeoutSeconds * 1000);
|
||||
|
||||
try {
|
||||
const res = await fetch(`${params.baseUrl}/search`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
query: params.query,
|
||||
limit: params.limit,
|
||||
session_id: params.sessionId,
|
||||
}),
|
||||
signal: controller.signal,
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const detail = await res.text().catch(() => "");
|
||||
throw new Error(`QVeris search failed (${res.status}): ${detail || res.statusText}`);
|
||||
}
|
||||
|
||||
return (await res.json()) as QverisSearchResponse;
|
||||
} finally {
|
||||
clearTimeout(timeoutId);
|
||||
}
|
||||
}
|
||||
|
||||
async function qverisExecute(params: {
|
||||
toolId: string;
|
||||
searchId: string;
|
||||
sessionId: string;
|
||||
parameters: Record<string, unknown>;
|
||||
maxResponseSize: number;
|
||||
apiKey: string;
|
||||
baseUrl: string;
|
||||
timeoutSeconds: number;
|
||||
}): Promise<QverisExecutionResponse> {
|
||||
const controller = new AbortController();
|
||||
const timeoutId = setTimeout(() => controller.abort(), params.timeoutSeconds * 1000);
|
||||
|
||||
try {
|
||||
const res = await fetch(
|
||||
`${params.baseUrl}/tools/execute?tool_id=${encodeURIComponent(params.toolId)}`,
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
parameters: params.parameters,
|
||||
max_response_size: params.maxResponseSize,
|
||||
search_id: params.searchId,
|
||||
session_id: params.sessionId,
|
||||
}),
|
||||
signal: controller.signal,
|
||||
},
|
||||
);
|
||||
|
||||
if (!res.ok) {
|
||||
const detail = await res.text().catch(() => "");
|
||||
throw new Error(`QVeris execute failed (${res.status}): ${detail || res.statusText}`);
|
||||
}
|
||||
|
||||
return (await res.json()) as QverisExecutionResponse;
|
||||
} finally {
|
||||
clearTimeout(timeoutId);
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Tool Schemas
|
||||
// ============================================================================
|
||||
|
||||
const QverisSearchSchema = Type.Object({
|
||||
query: Type.String({
|
||||
description:
|
||||
"Search query describing the capability of the tool you need. Describe what you want to accomplish, not specific params to pass. Example: 'weather forecast API', 'send email', 'stock prices'.",
|
||||
}),
|
||||
limit: Type.Optional(
|
||||
Type.Number({
|
||||
description: "Maximum number of results to return (1-100). Default: 10.",
|
||||
minimum: 1,
|
||||
maximum: 100,
|
||||
}),
|
||||
),
|
||||
});
|
||||
|
||||
const QverisExecuteSchema = Type.Object({
|
||||
tool_id: Type.String({
|
||||
description: "The ID of the tool to execute. Must come from a previous qveris_search call.",
|
||||
}),
|
||||
search_id: Type.String({
|
||||
description:
|
||||
"The search_id from the qveris_search response that returned this tool. Required for linking execution to the original search.",
|
||||
}),
|
||||
params_to_tool: Type.String({
|
||||
description:
|
||||
'A JSON stringified dictionary of parameters to pass to the tool. Keys are param names and values can be of any type. Example: \'{"city": "London", "units": "metric"}\'.',
|
||||
}),
|
||||
max_response_size: Type.Optional(
|
||||
Type.Number({
|
||||
description:
|
||||
"Maximum size of response data in bytes. If tool generates data longer than this, it will be truncated. Default: 20480 (20KB).",
|
||||
}),
|
||||
),
|
||||
});
|
||||
|
||||
// ============================================================================
|
||||
// Pre-Search Handler
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* PreSearchToolHandler manages the pre-search flow:
|
||||
* 1. Determines if pre-search should be performed based on first query length
|
||||
* 2. Performs pre-search and stores results
|
||||
* 3. Provides tool mappings for execution
|
||||
*/
|
||||
class PreSearchToolHandler {
|
||||
private searchId: string = "";
|
||||
private toolMappings = new Map<string, QverisToolMapping>();
|
||||
private preSearchedTools: QverisSearchResultTool[] = [];
|
||||
private preSearchPerformed: boolean = false;
|
||||
private preSearchEnabled: boolean = false;
|
||||
private byteThreshold: number;
|
||||
|
||||
constructor(byteThreshold: number = DEFAULT_PRE_SEARCH_BYTE_THRESHOLD) {
|
||||
this.byteThreshold = byteThreshold;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if pre-search should be performed
|
||||
* Conditions:
|
||||
* 1. First query length in bytes <= threshold
|
||||
*/
|
||||
shouldPreSearch(firstQuery: string): boolean {
|
||||
const byteLength = new TextEncoder().encode(firstQuery).length;
|
||||
return byteLength <= this.byteThreshold;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform pre-search using the query
|
||||
*/
|
||||
async preSearch(params: {
|
||||
query: string;
|
||||
sessionId: string;
|
||||
apiKey: string;
|
||||
baseUrl: string;
|
||||
timeoutSeconds: number;
|
||||
searchLimit: number;
|
||||
}): Promise<QverisSearchResponse | null> {
|
||||
if (this.preSearchPerformed) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await qverisSearch({
|
||||
query: params.query,
|
||||
sessionId: params.sessionId,
|
||||
limit: params.searchLimit,
|
||||
apiKey: params.apiKey,
|
||||
baseUrl: params.baseUrl,
|
||||
timeoutSeconds: params.timeoutSeconds,
|
||||
});
|
||||
|
||||
if (result.results && result.results.length > 0) {
|
||||
this.searchId = result.search_id;
|
||||
this.preSearchedTools = result.results;
|
||||
|
||||
// Store mappings for execution
|
||||
for (const tool of result.results) {
|
||||
const functionName = sanitizeToolName(tool.tool_id);
|
||||
this.toolMappings.set(functionName, {
|
||||
functionName,
|
||||
toolId: tool.tool_id,
|
||||
searchId: result.search_id,
|
||||
});
|
||||
}
|
||||
|
||||
this.preSearchEnabled = true;
|
||||
}
|
||||
|
||||
this.preSearchPerformed = true;
|
||||
return result;
|
||||
} catch (error) {
|
||||
this.preSearchPerformed = true;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
isPreSearchEnabled(): boolean {
|
||||
return this.preSearchEnabled && this.preSearchedTools.length > 0;
|
||||
}
|
||||
|
||||
getPreSearchedTools(): QverisSearchResultTool[] {
|
||||
return this.preSearchedTools;
|
||||
}
|
||||
|
||||
getSearchId(): string {
|
||||
return this.searchId;
|
||||
}
|
||||
|
||||
isPreSearchedTool(toolName: string): boolean {
|
||||
return this.toolMappings.has(toolName);
|
||||
}
|
||||
|
||||
getToolMapping(functionName: string): QverisToolMapping | undefined {
|
||||
return this.toolMappings.get(functionName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize tool ID to create a valid function name
|
||||
*/
|
||||
function sanitizeToolName(toolId: string): string {
|
||||
if (!toolId || typeof toolId !== "string") {
|
||||
toolId = "unknown_tool";
|
||||
}
|
||||
|
||||
// Replace invalid characters with underscore
|
||||
let sanitized = toolId.replace(/[^a-zA-Z0-9_-]/g, "_");
|
||||
|
||||
// Remove leading hyphens and numbers
|
||||
sanitized = sanitized.replace(/^[-0-9]+/, "");
|
||||
|
||||
// If empty after sanitization, use a default name
|
||||
if (!sanitized || sanitized === "_") {
|
||||
sanitized = "tool_" + Date.now().toString(36);
|
||||
}
|
||||
|
||||
// Ensure starts with letter or underscore
|
||||
if (sanitized.startsWith("-")) {
|
||||
sanitized = "_" + sanitized.substring(1);
|
||||
}
|
||||
|
||||
// Truncate to 64 characters
|
||||
if (sanitized.length > 64) {
|
||||
sanitized = sanitized.substring(0, 64);
|
||||
}
|
||||
|
||||
return sanitized;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Tool Creation
|
||||
// ============================================================================
|
||||
|
||||
export function createQverisTools(options?: {
|
||||
config?: ClawdbotConfig;
|
||||
sandboxed?: boolean;
|
||||
agentSessionKey?: string;
|
||||
}): AnyAgentTool[] {
|
||||
const config = resolveQverisConfig(options?.config);
|
||||
|
||||
if (!resolveQverisEnabled({ config, sandboxed: options?.sandboxed })) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const apiKey = resolveQverisApiKey(config);
|
||||
if (!apiKey) {
|
||||
// Return empty array if no API key - tools won't be available
|
||||
return [];
|
||||
}
|
||||
|
||||
const baseUrl = resolveQverisBaseUrl(config);
|
||||
const timeoutSeconds = resolveTimeoutSeconds(config);
|
||||
const maxResponseSize = resolveMaxResponseSize(config);
|
||||
const searchLimit = resolveSearchLimit(config);
|
||||
|
||||
// Generate a session ID tied to clawdbot session key
|
||||
const sessionId = options?.agentSessionKey ?? `clawdbot-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
||||
|
||||
const searchTool: AnyAgentTool = {
|
||||
label: "QVeris Search",
|
||||
name: "qveris_search",
|
||||
description:
|
||||
"Search for available third-party tools using natural language. Returns relevant tools that can help accomplish tasks. Use this to discover tools before executing them with qveris_execute.",
|
||||
parameters: QverisSearchSchema,
|
||||
execute: async (_toolCallId, args) => {
|
||||
const params = args as Record<string, unknown>;
|
||||
const query = readStringParam(params, "query", { required: true });
|
||||
const limit = readNumberParam(params, "limit", { integer: true }) ?? searchLimit;
|
||||
|
||||
const result = await qverisSearch({
|
||||
query,
|
||||
sessionId,
|
||||
limit: Math.min(Math.max(1, limit), 100),
|
||||
apiKey,
|
||||
baseUrl,
|
||||
timeoutSeconds,
|
||||
});
|
||||
|
||||
// Return simplified result for the model
|
||||
return jsonResult({
|
||||
query: result.query,
|
||||
total: result.total,
|
||||
search_id: result.search_id,
|
||||
elapsed_time_ms: result.elapsed_time_ms,
|
||||
results: result.results.map((tool) => ({
|
||||
tool_id: tool.tool_id,
|
||||
name: tool.name,
|
||||
description: tool.description,
|
||||
params: tool.params?.map((p) => ({
|
||||
name: p.name,
|
||||
type: p.type,
|
||||
required: p.required,
|
||||
description: p.description?.en ?? Object.values(p.description ?? {})[0],
|
||||
})),
|
||||
examples: tool.examples?.sample_parameters
|
||||
? { sample_parameters: tool.examples.sample_parameters }
|
||||
: undefined,
|
||||
stats: tool.stats,
|
||||
})),
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
const executeTool: AnyAgentTool = {
|
||||
label: "QVeris Execute",
|
||||
name: "qveris_execute",
|
||||
description:
|
||||
"Execute a specific third-party tool with provided parameters. The tool_id and search_id must come from a previous qveris_search call. Pass parameters to the tool through params_to_tool as a JSON string.",
|
||||
parameters: QverisExecuteSchema,
|
||||
execute: async (_toolCallId, args) => {
|
||||
const params = args as Record<string, unknown>;
|
||||
const toolId = readStringParam(params, "tool_id", { required: true });
|
||||
const searchId = readStringParam(params, "search_id", { required: true });
|
||||
const paramsToToolRaw = readStringParam(params, "params_to_tool", { required: true });
|
||||
const maxSize =
|
||||
readNumberParam(params, "max_response_size", { integer: true }) ?? maxResponseSize;
|
||||
|
||||
// Parse params_to_tool JSON
|
||||
let toolParams: Record<string, unknown>;
|
||||
try {
|
||||
toolParams = JSON.parse(paramsToToolRaw) as Record<string, unknown>;
|
||||
} catch (parseError) {
|
||||
throw new Error(
|
||||
`Invalid JSON in params_to_tool: ${parseError instanceof Error ? parseError.message : "Unknown parse error"}`,
|
||||
);
|
||||
}
|
||||
|
||||
const result = await qverisExecute({
|
||||
toolId,
|
||||
searchId,
|
||||
sessionId,
|
||||
parameters: toolParams,
|
||||
maxResponseSize: maxSize,
|
||||
apiKey,
|
||||
baseUrl,
|
||||
timeoutSeconds,
|
||||
});
|
||||
|
||||
return jsonResult({
|
||||
execution_id: result.execution_id,
|
||||
success: result.success,
|
||||
elapsed_time_ms: result.elapsed_time_ms,
|
||||
result: result.result?.data,
|
||||
error_message: result.error_message,
|
||||
cost: result.cost ?? result.credits_used,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
return [searchTool, executeTool];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if QVeris tools are enabled/available
|
||||
*/
|
||||
export function isQverisEnabled(options?: {
|
||||
config?: ClawdbotConfig;
|
||||
sandboxed?: boolean;
|
||||
}): boolean {
|
||||
const config = resolveQverisConfig(options?.config);
|
||||
return resolveQverisEnabled({ config, sandboxed: options?.sandboxed }) && Boolean(resolveQverisApiKey(config));
|
||||
}
|
||||
@ -199,6 +199,14 @@ const FIELD_LABELS: Record<string, string> = {
|
||||
"tools.web.fetch.cacheTtlMinutes": "Web Fetch Cache TTL (min)",
|
||||
"tools.web.fetch.maxRedirects": "Web Fetch Max Redirects",
|
||||
"tools.web.fetch.userAgent": "Web Fetch User-Agent",
|
||||
"tools.qveris.enabled": "Enable QVeris Tools",
|
||||
"tools.qveris.apiKey": "QVeris API Key",
|
||||
"tools.qveris.baseUrl": "QVeris API Base URL",
|
||||
"tools.qveris.timeoutSeconds": "QVeris Timeout (sec)",
|
||||
"tools.qveris.maxResponseSize": "QVeris Max Response Size",
|
||||
"tools.qveris.searchLimit": "QVeris Search Limit",
|
||||
"tools.qveris.preSearchEnabled": "QVeris Pre-Search",
|
||||
"tools.qveris.preSearchByteThreshold": "QVeris Pre-Search Threshold",
|
||||
"gateway.controlUi.basePath": "Control UI Base Path",
|
||||
"gateway.controlUi.allowInsecureAuth": "Allow Insecure Control UI Auth",
|
||||
"gateway.controlUi.dangerouslyDisableDeviceAuth": "Dangerously Disable Control UI Device Auth",
|
||||
@ -462,6 +470,14 @@ const FIELD_HELP: Record<string, string> = {
|
||||
"tools.web.fetch.firecrawl.maxAgeMs":
|
||||
"Firecrawl maxAge (ms) for cached results when supported by the API.",
|
||||
"tools.web.fetch.firecrawl.timeoutSeconds": "Timeout in seconds for Firecrawl requests.",
|
||||
"tools.qveris.enabled": "Enable QVeris dynamic tool discovery (requires API key).",
|
||||
"tools.qveris.apiKey": "QVeris API key (fallback: QVERIS_API_KEY env var).",
|
||||
"tools.qveris.baseUrl": "QVeris API base URL (default: https://qveris.ai/api/v1).",
|
||||
"tools.qveris.timeoutSeconds": "Timeout in seconds for QVeris requests (default: 60).",
|
||||
"tools.qveris.maxResponseSize": "Max response size in bytes for QVeris tool execution (default: 20480).",
|
||||
"tools.qveris.searchLimit": "Default number of tools to return from search (default: 10).",
|
||||
"tools.qveris.preSearchEnabled": "Enable pre-search optimization for short queries (default: true).",
|
||||
"tools.qveris.preSearchByteThreshold": "Byte threshold for pre-search (queries under this limit trigger pre-search; default: 100).",
|
||||
"channels.slack.allowBots":
|
||||
"Allow bot-authored messages to trigger Slack replies (default: false).",
|
||||
"channels.slack.thread.historyScope":
|
||||
|
||||
@ -443,4 +443,23 @@ export type ToolsConfig = {
|
||||
deny?: string[];
|
||||
};
|
||||
};
|
||||
/** QVeris dynamic tool discovery and execution. */
|
||||
qveris?: {
|
||||
/** Enable QVeris tools (default: true when API key is present). */
|
||||
enabled?: boolean;
|
||||
/** QVeris API key (optional; defaults to QVERIS_API_KEY env var). */
|
||||
apiKey?: string;
|
||||
/** QVeris API base URL (default: https://qveris.ai/api/v1). */
|
||||
baseUrl?: string;
|
||||
/** Timeout in seconds for QVeris requests (default: 60). */
|
||||
timeoutSeconds?: number;
|
||||
/** Max response size in bytes (default: 20480). */
|
||||
maxResponseSize?: number;
|
||||
/** Default search result limit (default: 10). */
|
||||
searchLimit?: number;
|
||||
/** Enable pre-search optimization for short queries (default: true). */
|
||||
preSearchEnabled?: boolean;
|
||||
/** Byte threshold for pre-search (default: 100). */
|
||||
preSearchByteThreshold?: number;
|
||||
};
|
||||
};
|
||||
|
||||
@ -536,6 +536,19 @@ export const ToolsSchema = z
|
||||
})
|
||||
.strict()
|
||||
.optional(),
|
||||
qveris: z
|
||||
.object({
|
||||
enabled: z.boolean().optional(),
|
||||
apiKey: z.string().optional(),
|
||||
baseUrl: z.string().optional(),
|
||||
timeoutSeconds: z.number().int().positive().optional(),
|
||||
maxResponseSize: z.number().int().positive().optional(),
|
||||
searchLimit: z.number().int().positive().optional(),
|
||||
preSearchEnabled: z.boolean().optional(),
|
||||
preSearchByteThreshold: z.number().int().positive().optional(),
|
||||
})
|
||||
.strict()
|
||||
.optional(),
|
||||
})
|
||||
.strict()
|
||||
.superRefine((value, ctx) => {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user