import { Injectable } from "@nestjs/common"; import { Prisma } from "@prisma/client"; import { PrismaService } from "../prisma/prisma.service"; import type { WorkspaceLlmSettings, LlmProviderInstance, Personality } from "@prisma/client"; import type { UpdateWorkspaceSettingsDto } from "./dto"; /** * Service for managing workspace LLM settings * Handles configuration hierarchy: workspace > user > system */ @Injectable() export class WorkspaceSettingsService { constructor(private readonly prisma: PrismaService) {} /** * Get settings for a workspace (creates default if not exists) * * @param workspaceId - Workspace ID * @returns Workspace LLM settings */ async getSettings(workspaceId: string): Promise { let settings = await this.prisma.workspaceLlmSettings.findUnique({ where: { workspaceId }, }); // Create default settings if they don't exist settings ??= await this.prisma.workspaceLlmSettings.create({ data: { workspaceId, settings: {} as unknown as Prisma.InputJsonValue, }, }); return settings; } /** * Update workspace LLM settings * * @param workspaceId - Workspace ID * @param dto - Update data * @returns Updated settings */ async updateSettings( workspaceId: string, dto: UpdateWorkspaceSettingsDto ): Promise { const data: Prisma.WorkspaceLlmSettingsUncheckedUpdateInput = {}; if (dto.defaultLlmProviderId !== undefined) { data.defaultLlmProviderId = dto.defaultLlmProviderId; } if (dto.defaultPersonalityId !== undefined) { data.defaultPersonalityId = dto.defaultPersonalityId; } if (dto.settings !== undefined) { data.settings = dto.settings as unknown as Prisma.InputJsonValue; } const settings = await this.prisma.workspaceLlmSettings.update({ where: { workspaceId }, data, }); return settings; } /** * Get effective LLM provider for a workspace * Priority: workspace > user > system default * * @param workspaceId - Workspace ID * @param userId - Optional user ID for user-level provider * @returns Effective LLM provider instance * @throws {Error} If no provider available */ async getEffectiveLlmProvider( workspaceId: string, userId?: string ): Promise { // Get workspace settings const settings = await this.prisma.workspaceLlmSettings.findUnique({ where: { workspaceId }, }); // 1. Check workspace-level provider if (settings?.defaultLlmProviderId) { const provider = await this.prisma.llmProviderInstance.findUnique({ where: { id: settings.defaultLlmProviderId }, }); if (!provider) { throw new Error(`LLM provider ${settings.defaultLlmProviderId} not found`); } return provider; } // 2. Check user-level provider if (userId) { const userProvider = await this.prisma.llmProviderInstance.findFirst({ where: { userId, isEnabled: true, }, }); if (userProvider) { return userProvider; } } // 3. Fall back to system default const systemDefault = await this.prisma.llmProviderInstance.findFirst({ where: { userId: null, isDefault: true, isEnabled: true, }, }); if (!systemDefault) { throw new Error(`No LLM provider available for workspace ${workspaceId}`); } return systemDefault; } /** * Get effective personality for a workspace * Priority: workspace default > workspace enabled > any enabled * * @param workspaceId - Workspace ID * @returns Effective personality * @throws {Error} If no personality available */ async getEffectivePersonality(workspaceId: string): Promise { // Get workspace settings const settings = await this.prisma.workspaceLlmSettings.findUnique({ where: { workspaceId }, }); // 1. Check workspace-configured personality if (settings?.defaultPersonalityId) { const personality = await this.prisma.personality.findUnique({ where: { id: settings.defaultPersonalityId, }, }); if (!personality) { throw new Error(`Personality ${settings.defaultPersonalityId} not found`); } return personality; } // 2. Fall back to default personality in workspace const defaultPersonality = await this.prisma.personality.findFirst({ where: { workspaceId, isDefault: true, isEnabled: true, }, }); if (defaultPersonality) { return defaultPersonality; } // 3. Fall back to any enabled personality const anyPersonality = await this.prisma.personality.findFirst({ where: { workspaceId, isEnabled: true, }, }); if (!anyPersonality) { throw new Error(`No personality available for workspace ${workspaceId}`); } return anyPersonality; } }