import { dbGlobal } from "drizzle-pkg/lib/db"; import { agents, agentToolAssociations } from "drizzle-pkg/lib/schema/agent"; import { agentTools } from "drizzle-pkg/lib/schema/agent-tool"; import { eq, and, asc, sql } from "drizzle-orm"; import type { AgentToolRow } from "#server/service/agent-tool"; export type AgentRow = typeof agents.$inferSelect; export type AgentTitleStrategy = "llm" | "first-line" | "none"; export interface AgentWithTools extends AgentRow { tools: AgentToolRow[]; } export interface CreateAgentInput { slug: string; name: string; description?: string; systemPrompt: string; defaultModelId?: number | null; titleModelId?: number | null; titleStrategy?: AgentTitleStrategy; enableThinking?: number; enableTools?: number; maxStepCount?: number; isDefault?: number; isCallable?: number; sortOrder?: number; enabled?: number; toolIds?: string[]; } export interface UpdateAgentInput { slug?: string; name?: string; description?: string; systemPrompt?: string; defaultModelId?: number | null; titleModelId?: number | null; titleStrategy?: AgentTitleStrategy; enableThinking?: number; enableTools?: number; maxStepCount?: number; isDefault?: number; isCallable?: number; sortOrder?: number; enabled?: number; toolIds?: string[]; } export async function getAgentBySlug(slug: string): Promise { const [row] = await dbGlobal .select() .from(agents) .where(and(eq(agents.slug, slug), eq(agents.enabled, 1))) .limit(1); return row ?? null; } export async function getAgentBySlugAny(slug: string): Promise { const [row] = await dbGlobal .select() .from(agents) .where(eq(agents.slug, slug)) .limit(1); return row ?? null; } export async function getAgentById(id: number): Promise { const [row] = await dbGlobal.select().from(agents).where(eq(agents.id, id)).limit(1); return row ?? null; } export async function getDefaultAgent(): Promise { const [row] = await dbGlobal .select() .from(agents) .where(and(eq(agents.isDefault, 1), eq(agents.enabled, 1))) .limit(1); if (row) return row; const [fallback] = await dbGlobal .select() .from(agents) .where(eq(agents.enabled, 1)) .orderBy(asc(agents.sortOrder)) .limit(1); return fallback ?? null; } export async function listAgents(onlyEnabled = true): Promise { const where = onlyEnabled ? eq(agents.enabled, 1) : sql`1=1`; return dbGlobal.select().from(agents).where(where).orderBy(asc(agents.sortOrder), asc(agents.id)); } export async function getAgentWithTools(slug: string): Promise { const agent = await getAgentBySlug(slug); if (!agent) return null; const tools = await getAgentTools(agent.id); return { ...agent, tools }; } export async function getAgentTools(agentId: number): Promise { const rows = await dbGlobal .select({ tool: agentTools, sortOrder: agentToolAssociations.sortOrder, }) .from(agentToolAssociations) .innerJoin(agentTools, eq(agentToolAssociations.toolId, agentTools.id)) .where(and(eq(agentToolAssociations.agentId, agentId), eq(agentTools.enabled, 1))) .orderBy(asc(agentToolAssociations.sortOrder), asc(agentTools.sortOrder)); return rows.map((r) => r.tool); } export async function getAgentToolIds(agentId: number): Promise { const tools = await getAgentTools(agentId); return tools.map((t) => t.id); } export async function createAgent(input: CreateAgentInput): Promise { const existing = await getAgentBySlugAny(input.slug); if (existing) { throw new Error(`slug 已存在: ${input.slug}`); } const [row] = await dbGlobal .insert(agents) .values({ slug: input.slug, name: input.name, description: input.description ?? null, systemPrompt: input.systemPrompt, defaultModelId: input.defaultModelId ?? null, titleModelId: input.titleModelId ?? null, titleStrategy: input.titleStrategy ?? "llm", enableThinking: input.enableThinking ?? 0, enableTools: input.enableTools ?? 1, maxStepCount: input.maxStepCount ?? 6, isDefault: input.isDefault ?? 0, isCallable: input.isCallable ?? 0, sortOrder: input.sortOrder ?? 0, enabled: input.enabled ?? 1, }) .returning(); if (!row) throw new Error("创建 agent 失败"); if (input.isDefault === 1) { await dbGlobal .update(agents) .set({ isDefault: 0 }) .where(and(sql`${agents.id} != ${row.id}`)); } if (input.toolIds && input.toolIds.length > 0) { for (let i = 0; i < input.toolIds.length; i++) { const toolId = input.toolIds[i]!; await dbGlobal.insert(agentToolAssociations).values({ agentId: row.id, toolId, sortOrder: i, }); } } return row; } export async function updateAgent(id: number, input: UpdateAgentInput): Promise { const existing = await getAgentById(id); if (!existing) return null; if (input.slug && input.slug !== existing.slug) { const slugConflict = await getAgentBySlugAny(input.slug); if (slugConflict) { throw new Error(`slug 已存在: ${input.slug}`); } } const updates: Partial = {}; if (input.slug !== undefined) updates.slug = input.slug; if (input.name !== undefined) updates.name = input.name; if (input.description !== undefined) updates.description = input.description; if (input.systemPrompt !== undefined) updates.systemPrompt = input.systemPrompt; if (input.defaultModelId !== undefined) updates.defaultModelId = input.defaultModelId; if (input.titleModelId !== undefined) updates.titleModelId = input.titleModelId; if (input.titleStrategy !== undefined) updates.titleStrategy = input.titleStrategy; if (input.enableThinking !== undefined) updates.enableThinking = input.enableThinking; if (input.enableTools !== undefined) updates.enableTools = input.enableTools; if (input.maxStepCount !== undefined) updates.maxStepCount = input.maxStepCount; if (input.isCallable !== undefined) updates.isCallable = input.isCallable; if (input.sortOrder !== undefined) updates.sortOrder = input.sortOrder; if (input.enabled !== undefined) updates.enabled = input.enabled; if (input.isDefault !== undefined) { updates.isDefault = input.isDefault; if (input.isDefault === 1) { await dbGlobal .update(agents) .set({ isDefault: 0 }) .where(and(sql`${agents.id} != ${id}`)); } } if (Object.keys(updates).length > 0) { await dbGlobal.update(agents).set(updates).where(eq(agents.id, id)); } if (input.toolIds !== undefined) { await dbGlobal.delete(agentToolAssociations).where(eq(agentToolAssociations.agentId, id)); for (let i = 0; i < input.toolIds.length; i++) { const toolId = input.toolIds[i]!; await dbGlobal.insert(agentToolAssociations).values({ agentId: id, toolId, sortOrder: i, }); } } return getAgentById(id); } export async function deleteAgent(id: number): Promise { await dbGlobal.delete(agentToolAssociations).where(eq(agentToolAssociations.agentId, id)); await dbGlobal.delete(agents).where(eq(agents.id, id)); }