import { dbGlobal } from "drizzle-pkg/lib/db"; import { agentTools } from "drizzle-pkg/lib/schema/agent-tool"; import { eq, asc } from "drizzle-orm"; import { tool, jsonSchema } from "ai"; import { z } from "zod"; import { registerToolType, getExecutor, type ToolContext, type ToolResult } from "./registry"; import { writeToolLog } from "./log"; import { parseFetchConfig, DEFAULT_FETCH_CONFIG } from "./executors/fetch/config"; import { fetchExecutor, fetchInputSchema } from "./executors/fetch/fetch"; import { parseCalculatorConfig, DEFAULT_CALCULATOR_CONFIG } from "./executors/calculator/config"; import { calculatorExecutor, calculatorInputSchema } from "./executors/calculator/calculator"; import { parseDatetimeConfig, DEFAULT_DATETIME_CONFIG } from "./executors/datetime/config"; import { datetimeExecutor, datetimeInputSchema } from "./executors/datetime/datetime"; import { parseUuidConfig, DEFAULT_UUID_CONFIG } from "./executors/uuid/config"; import { uuidExecutor, uuidInputSchema } from "./executors/uuid/uuid"; import { parseBase64Config, DEFAULT_BASE64_CONFIG } from "./executors/base64/config"; import { base64Executor, base64InputSchema } from "./executors/base64/base64"; import { parseJsonFormatterConfig, DEFAULT_JSON_FORMATTER_CONFIG } from "./executors/json-formatter/config"; import { jsonFormatterExecutor, jsonFormatterInputSchema } from "./executors/json-formatter/json-formatter"; import { parseRegexTesterConfig, DEFAULT_REGEX_TESTER_CONFIG } from "./executors/regex-tester/config"; import { regexTesterExecutor, regexTesterInputSchema } from "./executors/regex-tester/regex-tester"; // ============ 工具类型注册表 ============ interface ToolTypeRegistration { executor: import("./registry").ToolExecutor; parseConfig: (raw: unknown) => Record; defaultConfig: Record; zodSchema: z.ZodType; } const TOOL_TYPE_REGISTRY: Record = { fetch: { executor: fetchExecutor, parseConfig: parseFetchConfig, defaultConfig: DEFAULT_FETCH_CONFIG, zodSchema: fetchInputSchema, }, calculator: { executor: calculatorExecutor, parseConfig: parseCalculatorConfig, defaultConfig: DEFAULT_CALCULATOR_CONFIG, zodSchema: calculatorInputSchema, }, datetime: { executor: datetimeExecutor, parseConfig: parseDatetimeConfig, defaultConfig: DEFAULT_DATETIME_CONFIG, zodSchema: datetimeInputSchema, }, uuid: { executor: uuidExecutor, parseConfig: parseUuidConfig, defaultConfig: DEFAULT_UUID_CONFIG, zodSchema: uuidInputSchema, }, base64: { executor: base64Executor, parseConfig: parseBase64Config, defaultConfig: DEFAULT_BASE64_CONFIG, zodSchema: base64InputSchema, }, "json-formatter": { executor: jsonFormatterExecutor, parseConfig: parseJsonFormatterConfig, defaultConfig: DEFAULT_JSON_FORMATTER_CONFIG, zodSchema: jsonFormatterInputSchema, }, "regex-tester": { executor: regexTesterExecutor, parseConfig: parseRegexTesterConfig, defaultConfig: DEFAULT_REGEX_TESTER_CONFIG, zodSchema: regexTesterInputSchema, }, }; // 立即注册所有工具类型 for (const [type, reg] of Object.entries(TOOL_TYPE_REGISTRY)) { registerToolType(type, reg.executor); } export type AgentToolRow = typeof agentTools.$inferSelect; export interface CreateAgentToolInput { name: string; slug: string; description: string; type: string; config: Record; enabled?: boolean; sortOrder?: number; } export interface UpdateAgentToolInput { name?: string; slug?: string; description?: string; type?: string; config?: Record; enabled?: boolean; sortOrder?: number; } export async function listAgentTools(): Promise { return dbGlobal.select().from(agentTools).orderBy(asc(agentTools.sortOrder), asc(agentTools.createdAt)); } export async function getAgentToolById(id: string): Promise { const rows = await dbGlobal.select().from(agentTools).where(eq(agentTools.id, id)).limit(1); return rows[0] ?? null; } export async function getAgentToolBySlug(slug: string): Promise { const rows = await dbGlobal.select().from(agentTools).where(eq(agentTools.slug, slug)).limit(1); return rows[0] ?? null; } function generateId(): string { return `at_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`; } function validateConfig(type: string, config: Record): Record { const reg = TOOL_TYPE_REGISTRY[type]; if (reg) { return reg.parseConfig(config); } return config; } function getDefaultConfig(type: string): Record { return TOOL_TYPE_REGISTRY[type]?.defaultConfig ?? {}; } function getZodSchema(type: string): z.ZodType { return TOOL_TYPE_REGISTRY[type]?.zodSchema ?? z.object({}); } export interface ToolTypeMeta { type: string; defaultConfig: Record; inputSchema: Record; } export function getToolTypeMetaList(): ToolTypeMeta[] { return Object.entries(TOOL_TYPE_REGISTRY).map(([type, reg]) => ({ type, defaultConfig: reg.defaultConfig, inputSchema: z.toJSONSchema(reg.zodSchema) as Record, })); } export function getToolTypeMeta(type: string): ToolTypeMeta | null { const reg = TOOL_TYPE_REGISTRY[type]; if (!reg) return null; return { type, defaultConfig: reg.defaultConfig, inputSchema: z.toJSONSchema(reg.zodSchema) as Record, }; } export async function createAgentTool(input: CreateAgentToolInput): Promise { const existing = await getAgentToolBySlug(input.slug); if (existing) { throw new Error(`slug 已存在: ${input.slug}`); } const validatedConfig = validateConfig(input.type, input.config); const id = generateId(); await dbGlobal.insert(agentTools).values({ id, name: input.name, slug: input.slug, description: input.description, type: input.type, config: JSON.stringify(validatedConfig), enabled: input.enabled === false ? 0 : 1, sortOrder: input.sortOrder ?? 0, }); const row = await getAgentToolById(id); if (!row) throw new Error("创建后查询失败"); return row; } export async function updateAgentTool(id: string, input: UpdateAgentToolInput): Promise { const existing = await getAgentToolById(id); if (!existing) return null; if (input.slug && input.slug !== existing.slug) { const slugConflict = await getAgentToolBySlug(input.slug); if (slugConflict) { throw new Error(`slug 已存在: ${input.slug}`); } } const updates: Partial = {}; if (input.name !== undefined) updates.name = input.name; if (input.slug !== undefined) updates.slug = input.slug; if (input.description !== undefined) updates.description = input.description; if (input.type !== undefined) updates.type = input.type; if (input.config !== undefined) { const typeToValidate = input.type ?? existing.type; updates.config = JSON.stringify(validateConfig(typeToValidate, input.config)); } if (input.enabled !== undefined) updates.enabled = input.enabled ? 1 : 0; if (input.sortOrder !== undefined) updates.sortOrder = input.sortOrder; if (Object.keys(updates).length === 0) return existing; await dbGlobal.update(agentTools).set(updates).where(eq(agentTools.id, id)); return getAgentToolById(id); } export async function deleteAgentTool(id: string): Promise { await dbGlobal.delete(agentTools).where(eq(agentTools.id, id)); } export async function executeAgentTool( id: string, input: unknown, userId: number | null, ): Promise { const agentTool = await getAgentToolById(id); if (!agentTool) { return { success: false, data: null, error: `工具不存在: ${id}`, metadata: { durationMs: 0 }, }; } if (!agentTool.enabled) { return { success: false, data: null, error: `工具已禁用: ${agentTool.slug}`, metadata: { durationMs: 0 }, }; } const executor = getExecutor(agentTool.type); if (!executor) { return { success: false, error: `工具类型未注册: ${agentTool.type}`, metadata: { durationMs: 0 }, }; } let config: unknown; try { config = JSON.parse(agentTool.config); } catch { config = getDefaultConfig(agentTool.type); } const ctx: ToolContext = { toolId: agentTool.id, toolSlug: agentTool.slug, userId, }; const result = await executor.execute(input, config, ctx); // 写日志 const status = result.success ? "success" : result.error?.includes("超时") ? "timeout" : "error"; await writeToolLog({ toolId: agentTool.id, toolSlug: agentTool.slug, userId, input: JSON.stringify(input), output: result.success ? JSON.stringify(result.data)?.slice(0, 10000) ?? null : null, status: status as "success" | "error" | "timeout", errorMessage: result.error ?? null, durationMs: result.metadata?.durationMs ?? 0, }); return result; } export interface EnabledToolInfo { slug: string; name: string; type: string; description: string; } export async function getEnabledToolInfos(): Promise { const tools = await dbGlobal .select() .from(agentTools) .where(eq(agentTools.enabled, 1)) .orderBy(asc(agentTools.sortOrder)); const result: EnabledToolInfo[] = []; for (const agentTool of tools) { const executor = getExecutor(agentTool.type); if (!executor) continue; let config: unknown; try { config = JSON.parse(agentTool.config); } catch { config = getDefaultConfig(agentTool.type); } result.push({ slug: agentTool.slug, name: agentTool.name, type: agentTool.type, description: executor.buildDescription(config), }); } return result; } export async function getEnabledToolsForLlm(): Promise>> { const tools = await dbGlobal .select() .from(agentTools) .where(eq(agentTools.enabled, 1)) .orderBy(asc(agentTools.sortOrder)); const result: Record = {}; for (const agentTool of tools) { const executor = getExecutor(agentTool.type); if (!executor) continue; let config: unknown; try { config = JSON.parse(agentTool.config); } catch { config = getDefaultConfig(agentTool.type); } const zodSchema = getZodSchema(agentTool.type); const jsonSch = z.toJSONSchema(zodSchema) as Record; result[agentTool.slug] = tool({ description: executor.buildDescription(config), parameters: jsonSchema(jsonSch, { validate: (v: unknown) => { const r = zodSchema.safeParse(v); return r.success ? { success: true as const, value: r.data } : { success: false as const, error: r.error }; }, }), execute: async (input: unknown) => { const execResult = await executeAgentTool(agentTool.id, input, null); if (!execResult.success) { return `工具执行失败: ${execResult.error ?? "未知错误"}。请停止调用此工具,基于已有信息回答用户或告知用户此工具不可用。`; } const meta = execResult.metadata; const sizeInfo = meta?.responseSize ? `${meta.responseSize} bytes` : `未知大小`; const statusInfo = meta?.statusCode ? `HTTP ${meta.statusCode}` : ""; const dataStr = typeof execResult.data === "string" ? execResult.data : JSON.stringify(execResult.data); return `[${agentTool.type} 结果 ${statusInfo} ${sizeInfo}]\n${dataStr}`; }, }); } return result; } // 导出各工具默认配置和类型 export { DEFAULT_FETCH_CONFIG } from "./executors/fetch/config"; export type { FetchToolConfig } from "./executors/fetch/config"; export { DEFAULT_CALCULATOR_CONFIG } from "./executors/calculator/config"; export type { CalculatorToolConfig } from "./executors/calculator/config"; export { DEFAULT_DATETIME_CONFIG } from "./executors/datetime/config"; export type { DatetimeToolConfig } from "./executors/datetime/config"; export { DEFAULT_UUID_CONFIG } from "./executors/uuid/config"; export type { UuidToolConfig } from "./executors/uuid/config"; export { DEFAULT_BASE64_CONFIG } from "./executors/base64/config"; export type { Base64ToolConfig } from "./executors/base64/config"; export { DEFAULT_JSON_FORMATTER_CONFIG } from "./executors/json-formatter/config"; export type { JsonFormatterToolConfig } from "./executors/json-formatter/config"; export { DEFAULT_REGEX_TESTER_CONFIG } from "./executors/regex-tester/config"; export type { RegexTesterToolConfig } from "./executors/regex-tester/config"; export type { ToolExecutor, ToolContext, ToolResult } from "./registry";