Files
stack/apps/api/src/llm/llm-provider-admin.controller.ts
T
jason.woltjeandClaude Opus 4.5 3d6159ae15 fix: address code review issues and cleanup QA reports
Code review fixes:
- Add error logging to LlmProviderAdminController.testProvider catch block
- Use atomic increment operations in TokenBudgetService.updateUsage to prevent race conditions
- Update test expectations for atomic increment pattern

Cleanup:
- Remove obsolete QA automation reports

All 1169 tests passing.

Co-Authored-By: Claude Opus 4.5 <[email protected]>
2026-01-31 15:01:18 -06:00

285 lines
7.8 KiB
TypeScript

import {
Controller,
Get,
Post,
Patch,
Delete,
Body,
Param,
HttpCode,
HttpStatus,
NotFoundException,
BadRequestException,
Logger,
} from "@nestjs/common";
import type { InputJsonValue } from "@prisma/client/runtime/library";
import { PrismaService } from "../prisma/prisma.service";
import { LlmManagerService } from "./llm-manager.service";
import { CreateLlmProviderDto, UpdateLlmProviderDto, LlmProviderResponseDto } from "./dto";
/**
* Controller for LLM provider administration.
* Provides CRUD operations for managing LLM provider instances.
*
* @example
* ```typescript
* // List all providers
* GET /llm/admin/providers
*
* // Create a new provider
* POST /llm/admin/providers
* {
* "providerType": "ollama",
* "displayName": "Local Ollama",
* "config": { "endpoint": "http://localhost:11434" },
* "isDefault": true
* }
*
* // Test provider connection
* POST /llm/admin/providers/:id/test
*
* // Reload providers from database
* POST /llm/admin/reload
* ```
*/
@Controller("llm/admin")
export class LlmProviderAdminController {
private readonly logger = new Logger(LlmProviderAdminController.name);
constructor(
private readonly prisma: PrismaService,
private readonly llmManager: LlmManagerService
) {}
/**
* List all LLM provider instances from the database.
* Returns both enabled and disabled providers.
*
* @returns Array of all provider instances
*/
@Get("providers")
async listProviders(): Promise<LlmProviderResponseDto[]> {
const providers = await this.prisma.llmProviderInstance.findMany({
orderBy: { createdAt: "asc" },
});
return providers;
}
/**
* Get a specific LLM provider instance by ID.
*
* @param id - Provider instance ID
* @returns Provider instance
* @throws {NotFoundException} If provider not found
*/
@Get("providers/:id")
async getProvider(@Param("id") id: string): Promise<LlmProviderResponseDto> {
const provider = await this.prisma.llmProviderInstance.findUnique({
where: { id },
});
if (!provider) {
throw new NotFoundException(`LLM provider with ID ${id} not found`);
}
return provider;
}
/**
* Create a new LLM provider instance.
* If enabled, the provider will be automatically registered with the LLM manager.
*
* @param dto - Provider creation data
* @returns Created provider instance
* @throws {BadRequestException} If validation fails
*/
@Post("providers")
@HttpCode(HttpStatus.CREATED)
async createProvider(@Body() dto: CreateLlmProviderDto): Promise<LlmProviderResponseDto> {
// Create provider in database
const provider = await this.prisma.llmProviderInstance.create({
data: {
providerType: dto.providerType,
displayName: dto.displayName,
userId: dto.userId ?? null,
config: dto.config as InputJsonValue,
isDefault: dto.isDefault ?? false,
isEnabled: dto.isEnabled ?? true,
},
});
// Register with LLM manager if enabled
if (provider.isEnabled) {
await this.llmManager.registerProvider(provider);
}
return provider;
}
/**
* Update an existing LLM provider instance.
* The provider will be unregistered and re-registered if it's enabled.
*
* @param id - Provider instance ID
* @param dto - Provider update data
* @returns Updated provider instance
* @throws {NotFoundException} If provider not found
*/
@Patch("providers/:id")
async updateProvider(
@Param("id") id: string,
@Body() dto: UpdateLlmProviderDto
): Promise<LlmProviderResponseDto> {
// Verify provider exists
const existingProvider = await this.prisma.llmProviderInstance.findUnique({
where: { id },
});
if (!existingProvider) {
throw new NotFoundException(`LLM provider with ID ${id} not found`);
}
// Build update data with only provided fields
const updateData: {
displayName?: string;
config?: InputJsonValue;
isDefault?: boolean;
isEnabled?: boolean;
} = {};
if (dto.displayName !== undefined) {
updateData.displayName = dto.displayName;
}
if (dto.config !== undefined) {
updateData.config = dto.config as InputJsonValue;
}
if (dto.isDefault !== undefined) {
updateData.isDefault = dto.isDefault;
}
if (dto.isEnabled !== undefined) {
updateData.isEnabled = dto.isEnabled;
}
// Update provider in database
const updatedProvider = await this.prisma.llmProviderInstance.update({
where: { id },
data: updateData,
});
// Unregister old provider instance from manager
await this.llmManager.unregisterProvider(id);
// Re-register if still enabled
if (updatedProvider.isEnabled) {
await this.llmManager.registerProvider(updatedProvider);
}
return updatedProvider;
}
/**
* Delete an LLM provider instance.
* Cannot delete the default provider - set another provider as default first.
*
* @param id - Provider instance ID
* @throws {NotFoundException} If provider not found
* @throws {BadRequestException} If trying to delete default provider
*/
@Delete("providers/:id")
@HttpCode(HttpStatus.NO_CONTENT)
async deleteProvider(@Param("id") id: string): Promise<void> {
// Verify provider exists
const provider = await this.prisma.llmProviderInstance.findUnique({
where: { id },
});
if (!provider) {
throw new NotFoundException(`LLM provider with ID ${id} not found`);
}
// Prevent deleting default provider
if (provider.isDefault) {
throw new BadRequestException(
"Cannot delete the default provider. Set another provider as default first."
);
}
// Unregister from manager
await this.llmManager.unregisterProvider(id);
// Delete from database
await this.prisma.llmProviderInstance.delete({
where: { id },
});
}
/**
* Test connection to an LLM provider.
* Checks if the provider is healthy and can respond to requests.
*
* @param id - Provider instance ID
* @returns Health check result
* @throws {NotFoundException} If provider not found
*/
@Post("providers/:id/test")
async testProvider(@Param("id") id: string): Promise<{ healthy: boolean; error?: string }> {
// Verify provider exists in database
const provider = await this.prisma.llmProviderInstance.findUnique({
where: { id },
});
if (!provider) {
throw new NotFoundException(`LLM provider with ID ${id} not found`);
}
// Try to get provider from manager and check health
try {
const providerInstance = await this.llmManager.getProviderById(id);
const health = await providerInstance.checkHealth();
if (health.error !== undefined) {
return {
healthy: health.healthy,
error: health.error,
};
}
return {
healthy: health.healthy,
};
} catch (error: unknown) {
// Provider not loaded in manager (might be disabled)
const errorMessage = error instanceof Error ? error.message : String(error);
this.logger.warn(`Failed to test provider ${id}: ${errorMessage}`);
return {
healthy: false,
error: "Provider not loaded in manager. Try reloading providers.",
};
}
}
/**
* Reload all enabled providers from the database.
* This will clear the current provider cache and reload fresh state.
*
* @returns Reload result with count of loaded providers
*/
@Post("reload")
async reloadProviders(): Promise<{ message: string; count: number }> {
// Reload providers in manager
await this.llmManager.reloadFromDatabase();
// Get count of enabled providers
const enabledProviders = await this.prisma.llmProviderInstance.findMany({
where: { isEnabled: true },
});
return {
message: "Providers reloaded successfully",
count: enabledProviders.length,
};
}
}