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