diff --git a/apps/api/src/agent-template/agent-template.controller.ts b/apps/api/src/agent-template/agent-template.controller.ts new file mode 100644 index 0000000..6f09cf3 --- /dev/null +++ b/apps/api/src/agent-template/agent-template.controller.ts @@ -0,0 +1,47 @@ +import { + Controller, + Get, + Post, + Patch, + Delete, + Body, + Param, + UseGuards, + ParseUUIDPipe, +} from "@nestjs/common"; +import { AgentTemplateService } from "./agent-template.service"; +import { CreateAgentTemplateDto } from "./dto/create-agent-template.dto"; +import { UpdateAgentTemplateDto } from "./dto/update-agent-template.dto"; +import { AuthGuard } from "../auth/guards/auth.guard"; +import { AdminGuard } from "../auth/guards/admin.guard"; + +@Controller("admin/agent-templates") +@UseGuards(AuthGuard, AdminGuard) +export class AgentTemplateController { + constructor(private readonly agentTemplateService: AgentTemplateService) {} + + @Get() + findAll() { + return this.agentTemplateService.findAll(); + } + + @Get(":id") + findOne(@Param("id", ParseUUIDPipe) id: string) { + return this.agentTemplateService.findOne(id); + } + + @Post() + create(@Body() dto: CreateAgentTemplateDto) { + return this.agentTemplateService.create(dto); + } + + @Patch(":id") + update(@Param("id", ParseUUIDPipe) id: string, @Body() dto: UpdateAgentTemplateDto) { + return this.agentTemplateService.update(id, dto); + } + + @Delete(":id") + remove(@Param("id", ParseUUIDPipe) id: string) { + return this.agentTemplateService.remove(id); + } +} diff --git a/apps/api/src/agent-template/agent-template.module.ts b/apps/api/src/agent-template/agent-template.module.ts new file mode 100644 index 0000000..0737390 --- /dev/null +++ b/apps/api/src/agent-template/agent-template.module.ts @@ -0,0 +1,12 @@ +import { Module } from "@nestjs/common"; +import { AgentTemplateService } from "./agent-template.service"; +import { AgentTemplateController } from "./agent-template.controller"; +import { PrismaModule } from "../prisma/prisma.module"; + +@Module({ + imports: [PrismaModule], + controllers: [AgentTemplateController], + providers: [AgentTemplateService], + exports: [AgentTemplateService], +}) +export class AgentTemplateModule {} diff --git a/apps/api/src/agent-template/agent-template.service.ts b/apps/api/src/agent-template/agent-template.service.ts new file mode 100644 index 0000000..f288672 --- /dev/null +++ b/apps/api/src/agent-template/agent-template.service.ts @@ -0,0 +1,57 @@ +import { Injectable, NotFoundException, ConflictException } from "@nestjs/common"; +import { PrismaService } from "../prisma/prisma.service"; +import { CreateAgentTemplateDto } from "./dto/create-agent-template.dto"; +import { UpdateAgentTemplateDto } from "./dto/update-agent-template.dto"; + +@Injectable() +export class AgentTemplateService { + constructor(private readonly prisma: PrismaService) {} + + async findAll() { + return this.prisma.agentTemplate.findMany({ + orderBy: { createdAt: "asc" }, + }); + } + + async findOne(id: string) { + const template = await this.prisma.agentTemplate.findUnique({ where: { id } }); + if (!template) throw new NotFoundException(`AgentTemplate ${id} not found`); + return template; + } + + async findByName(name: string) { + const template = await this.prisma.agentTemplate.findUnique({ where: { name } }); + if (!template) throw new NotFoundException(`AgentTemplate "${name}" not found`); + return template; + } + + async create(dto: CreateAgentTemplateDto) { + const existing = await this.prisma.agentTemplate.findUnique({ where: { name: dto.name } }); + if (existing) throw new ConflictException(`AgentTemplate "${dto.name}" already exists`); + + return this.prisma.agentTemplate.create({ + data: { + name: dto.name, + displayName: dto.displayName, + role: dto.role, + personality: dto.personality, + primaryModel: dto.primaryModel, + fallbackModels: dto.fallbackModels ?? ([] as string[]), + toolPermissions: dto.toolPermissions ?? ([] as string[]), + ...(dto.discordChannel !== undefined && { discordChannel: dto.discordChannel }), + isActive: dto.isActive ?? true, + isDefault: dto.isDefault ?? false, + }, + }); + } + + async update(id: string, dto: UpdateAgentTemplateDto) { + await this.findOne(id); + return this.prisma.agentTemplate.update({ where: { id }, data: dto }); + } + + async remove(id: string) { + await this.findOne(id); + return this.prisma.agentTemplate.delete({ where: { id } }); + } +} diff --git a/apps/api/src/agent-template/dto/create-agent-template.dto.ts b/apps/api/src/agent-template/dto/create-agent-template.dto.ts new file mode 100644 index 0000000..ef26ce7 --- /dev/null +++ b/apps/api/src/agent-template/dto/create-agent-template.dto.ts @@ -0,0 +1,43 @@ +import { IsString, IsBoolean, IsOptional, IsArray, MinLength } from "class-validator"; + +export class CreateAgentTemplateDto { + @IsString() + @MinLength(1) + name!: string; + + @IsString() + @MinLength(1) + displayName!: string; + + @IsString() + @MinLength(1) + role!: string; + + @IsString() + @MinLength(1) + personality!: string; + + @IsString() + @MinLength(1) + primaryModel!: string; + + @IsArray() + @IsOptional() + fallbackModels?: string[]; + + @IsArray() + @IsOptional() + toolPermissions?: string[]; + + @IsString() + @IsOptional() + discordChannel?: string; + + @IsBoolean() + @IsOptional() + isActive?: boolean; + + @IsBoolean() + @IsOptional() + isDefault?: boolean; +} diff --git a/apps/api/src/agent-template/dto/update-agent-template.dto.ts b/apps/api/src/agent-template/dto/update-agent-template.dto.ts new file mode 100644 index 0000000..b4d8441 --- /dev/null +++ b/apps/api/src/agent-template/dto/update-agent-template.dto.ts @@ -0,0 +1,4 @@ +import { PartialType } from "@nestjs/mapped-types"; +import { CreateAgentTemplateDto } from "./create-agent-template.dto"; + +export class UpdateAgentTemplateDto extends PartialType(CreateAgentTemplateDto) {} diff --git a/apps/api/src/app.module.ts b/apps/api/src/app.module.ts index bc82a31..3013608 100644 --- a/apps/api/src/app.module.ts +++ b/apps/api/src/app.module.ts @@ -48,6 +48,7 @@ import { TerminalModule } from "./terminal/terminal.module"; import { PersonalitiesModule } from "./personalities/personalities.module"; import { WorkspacesModule } from "./workspaces/workspaces.module"; import { AdminModule } from "./admin/admin.module"; +import { AgentTemplateModule } from "./agent-template/agent-template.module"; import { TeamsModule } from "./teams/teams.module"; import { ImportModule } from "./import/import.module"; import { ConversationArchiveModule } from "./conversation-archive/conversation-archive.module"; @@ -129,6 +130,7 @@ import { OrchestratorModule } from "./orchestrator/orchestrator.module"; PersonalitiesModule, WorkspacesModule, AdminModule, + AgentTemplateModule, TeamsModule, ImportModule, ConversationArchiveModule,