You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

120 lines
3.8 KiB

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<AgentInvocationResult> {
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 };
}
}