Merge branch 'main' of github.com:QVerisAI/QVerisBot

This commit is contained in:
alex xiang 2026-01-28 17:15:01 +08:00
commit d0408c3dbf
2 changed files with 6 additions and 151 deletions

View File

@ -206,6 +206,10 @@ export function buildAgentSystemPrompt(params: {
session_status:
"Show a /status-equivalent status card (usage + time + Reasoning/Verbose/Elevated); use for model-use questions (📊 session_status); optional per-session model override",
image: "Analyze an image with the configured image model",
qveris_search:
"Search for third-party API tools by capability description (e.g. 'weather forecast', 'stock prices'). Describe what you want to accomplish, not specific params. Returns tool_id, params, and examples.",
qveris_execute:
"Execute a discovered third-party tool. Requires tool_id and search_id from qveris_search. Pass params as JSON string in params_to_tool. Consider stats (success_rate, avg_execution_time) when selecting tools.",
};
const toolOrder = [
@ -220,6 +224,8 @@ export function buildAgentSystemPrompt(params: {
"process",
"web_search",
"web_fetch",
"qveris_search",
"qveris_execute",
"browser",
"canvas",
"nodes",

View File

@ -12,7 +12,6 @@ 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
@ -72,13 +71,6 @@ interface QverisExecutionResponse {
credits_used?: number;
}
/** Mapping from sanitized function name to original tool info */
interface QverisToolMapping {
functionName: string;
toolId: string;
searchId: string;
}
// ============================================================================
// Config Resolution
// ============================================================================
@ -116,14 +108,6 @@ 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
// ============================================================================
@ -246,141 +230,6 @@ const QverisExecuteSchema = Type.Object({
),
});
// ============================================================================
// 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
// ============================================================================