import { generateText, stepCountIs } from "ai"; import type { AgentInvocation, AgentInvocationResult } from "./types"; import { getAgentBySlug } from "./agent"; import { getAgentToolsForChatByAgentId } from "#server/service/agent-tool"; import { resolveModelForUser, resolveModelAny, toLanguageModel } from "#server/service/llm/model-resolver"; import { getConfigGlobal } from "#server/utils/context"; import log4js from "logger"; const logger = log4js.getLogger("APP"); const DEFAULT_MAX_RECURSION_DEPTH = 3; const DEFAULT_TIMEOUT_MS = 60_000; const DEFAULT_MAX_OUTPUT_TOKENS = 8192; export async function invokeAgent( invocation: AgentInvocation, options?: { maxRecursionDepth?: number; timeoutMs?: number; maxOutputTokens?: number; }, ): Promise { const { agentSlug, input, context } = invocation; const maxRecursionDepth = options?.maxRecursionDepth ?? DEFAULT_MAX_RECURSION_DEPTH; const timeoutMs = options?.timeoutMs ?? DEFAULT_TIMEOUT_MS; const maxOutputTokens = options?.maxOutputTokens ?? DEFAULT_MAX_OUTPUT_TOKENS; const userId = context?.userId ?? null; const callerAgentId = context?.callerAgentId ?? null; const recursionDepth = context?.recursionDepth ?? 0; const agent = await getAgentBySlug(agentSlug); if (!agent) { return { agentSlug, output: "", ok: false, error: `agent not found: ${agentSlug}` }; } if (!agent.isCallable) { return { agentSlug, output: "", ok: false, error: `agent not callable: ${agentSlug}` }; } if (callerAgentId !== null && callerAgentId === agent.id) { return { agentSlug, output: "", ok: false, error: `self-invocation blocked: agent ${agentSlug} cannot invoke itself` }; } if (recursionDepth >= maxRecursionDepth) { return { agentSlug, output: "", ok: false, error: `max recursion depth (${maxRecursionDepth}) reached`, }; } const globalDefaultModelId = (await getConfigGlobal("agentDefaultModelId")) ?? null; const targetModelId = agent.defaultModelId ?? globalDefaultModelId; if (!targetModelId) { return { agentSlug, output: "", ok: false, error: `agent ${agentSlug} has no defaultModelId and no global default model configured` }; } const resolved = userId ? await resolveModelForUser(targetModelId, userId) : await resolveModelAny(targetModelId); if (!resolved) { return { agentSlug, output: "", ok: false, error: `model ${targetModelId} not resolvable for agent ${agentSlug}` }; } const languageModel = toLanguageModel(resolved); const enableTools = agent.enableTools === 1; const { tools } = await getAgentToolsForChatByAgentId({ agentId: agent.id, userId, userRole: null, enableTools, recursionDepth, }); const maxSteps = agent.maxStepCount ?? 8; logger.info( "[COLLAB] invokeAgent agentSlug=%s agentId=%d callerAgentId=%s recursionDepth=%d tools=%d maxSteps=%d", agentSlug, agent.id, callerAgentId, recursionDepth, Object.keys(tools).length, maxSteps, ); try { const result = await generateText({ model: languageModel, system: agent.systemPrompt, prompt: input, ...(Object.keys(tools).length > 0 ? { tools, stopWhen: stepCountIs(maxSteps), } : {}), maxOutputTokens, abortSignal: AbortSignal.timeout(timeoutMs), }); const output = result.text ?? ""; logger.info( "[COLLAB] invokeAgent done agentSlug=%s outputLen=%d usage=%j", agentSlug, output.length, result.usage, ); return { agentSlug, output, ok: true }; } catch (err) { const errorMsg = err instanceof Error ? err.message : String(err); logger.error("[COLLAB] invokeAgent error agentSlug=%s error=%s", agentSlug, errorMsg); return { agentSlug, output: "", ok: false, error: errorMsg }; } }