feat(#129): add LLM provider admin API endpoints

Implement REST API endpoints for managing LLM provider instances.

Changes:
- Created DTOs for provider CRUD operations (CreateLlmProviderDto, UpdateLlmProviderDto, LlmProviderResponseDto)
- Implemented LlmProviderAdminController with full CRUD endpoints:
  - GET /llm/admin/providers - List all providers
  - GET /llm/admin/providers/:id - Get provider details
  - POST /llm/admin/providers - Create new provider
  - PATCH /llm/admin/providers/:id - Update provider
  - DELETE /llm/admin/providers/:id - Delete provider
  - POST /llm/admin/providers/:id/test - Test connection
  - POST /llm/admin/reload - Reload from database
- Updated llm-manager.service.ts to support OpenAI and Claude providers
- Added comprehensive test suite with 97.95% coverage
- Proper validation, error handling, and type safety

All tests pass. Pre-commit hooks pass.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-31 14:37:55 -06:00
parent 772776bfd9
commit 4b4d21c732
6 changed files with 904 additions and 6 deletions

View File

@@ -1,2 +1,3 @@
export * from "./chat.dto";
export * from "./embed.dto";
export * from "./provider-admin.dto";

View File

@@ -0,0 +1,169 @@
import { IsString, IsIn, IsOptional, IsBoolean, IsUUID, IsObject } from "class-validator";
import type { JsonValue } from "@prisma/client/runtime/library";
/**
* DTO for creating a new LLM provider instance.
*
* @example
* ```typescript
* const dto: CreateLlmProviderDto = {
* providerType: "ollama",
* displayName: "Local Ollama",
* config: {
* endpoint: "http://localhost:11434",
* timeout: 30000
* },
* isDefault: true,
* isEnabled: true
* };
* ```
*/
export class CreateLlmProviderDto {
/**
* Provider type (ollama, openai, or claude)
*/
@IsString()
@IsIn(["ollama", "openai", "claude"])
providerType!: string;
/**
* Human-readable display name for the provider
*/
@IsString()
displayName!: string;
/**
* User ID for user-specific providers (null for system-level)
*/
@IsOptional()
@IsUUID()
userId?: string;
/**
* Provider-specific configuration (endpoint, apiKey, etc.)
*/
@IsObject()
config!: JsonValue;
/**
* Whether this is the default provider
*/
@IsOptional()
@IsBoolean()
isDefault?: boolean;
/**
* Whether this provider is enabled
*/
@IsOptional()
@IsBoolean()
isEnabled?: boolean;
}
/**
* DTO for updating an existing LLM provider instance.
* All fields are optional - only provided fields will be updated.
*
* @example
* ```typescript
* const dto: UpdateLlmProviderDto = {
* displayName: "Updated Ollama",
* isEnabled: false
* };
* ```
*/
export class UpdateLlmProviderDto {
/**
* Human-readable display name for the provider
*/
@IsOptional()
@IsString()
displayName?: string;
/**
* Provider-specific configuration (endpoint, apiKey, etc.)
*/
@IsOptional()
@IsObject()
config?: JsonValue;
/**
* Whether this is the default provider
*/
@IsOptional()
@IsBoolean()
isDefault?: boolean;
/**
* Whether this provider is enabled
*/
@IsOptional()
@IsBoolean()
isEnabled?: boolean;
}
/**
* Response DTO for LLM provider instance.
* Matches the Prisma LlmProviderInstance model.
*
* @example
* ```typescript
* const response: LlmProviderResponseDto = {
* id: "provider-123",
* providerType: "ollama",
* displayName: "Local Ollama",
* userId: null,
* config: { endpoint: "http://localhost:11434" },
* isDefault: true,
* isEnabled: true,
* createdAt: new Date(),
* updatedAt: new Date()
* };
* ```
*/
export class LlmProviderResponseDto {
/**
* Unique identifier for the provider instance
*/
id!: string;
/**
* Provider type (ollama, openai, or claude)
*/
providerType!: string;
/**
* Human-readable display name for the provider
*/
displayName!: string;
/**
* User ID for user-specific providers (null for system-level)
*/
userId?: string | null;
/**
* Provider-specific configuration (endpoint, apiKey, etc.)
*/
config!: JsonValue;
/**
* Whether this is the default provider
*/
isDefault!: boolean;
/**
* Whether this provider is enabled
*/
isEnabled!: boolean;
/**
* Timestamp when the provider was created
*/
createdAt!: Date;
/**
* Timestamp when the provider was last updated
*/
updatedAt!: Date;
}

View File

@@ -6,6 +6,8 @@ import type {
LlmProviderHealthStatus,
} from "./providers/llm-provider.interface";
import { OllamaProvider, type OllamaProviderConfig } from "./providers/ollama.provider";
import { OpenAiProvider, type OpenAiProviderConfig } from "./providers/openai.provider";
import { ClaudeProvider, type ClaudeProviderConfig } from "./providers/claude.provider";
/**
* Provider information returned by getAllProviders
@@ -296,11 +298,11 @@ export class LlmManagerService implements OnModuleInit {
case "ollama":
return new OllamaProvider(instance.config as OllamaProviderConfig);
// Future providers:
// case "claude":
// return new ClaudeProvider(instance.config);
// case "openai":
// return new OpenAIProvider(instance.config);
case "openai":
return new OpenAiProvider(instance.config as OpenAiProviderConfig);
case "claude":
return new ClaudeProvider(instance.config as ClaudeProviderConfig);
default:
throw new Error(`Unknown provider type: ${instance.providerType}`);

View File

@@ -0,0 +1,447 @@
import { describe, it, expect, beforeEach, vi } from "vitest";
import { Test, TestingModule } from "@nestjs/testing";
import { NotFoundException, BadRequestException } from "@nestjs/common";
import { LlmProviderAdminController } from "./llm-provider-admin.controller";
import { LlmManagerService } from "./llm-manager.service";
import { PrismaService } from "../prisma/prisma.service";
import { CreateLlmProviderDto, UpdateLlmProviderDto } from "./dto";
describe("LlmProviderAdminController", () => {
let controller: LlmProviderAdminController;
let prisma: PrismaService;
let llmManager: LlmManagerService;
const mockProviderId = "provider-123";
const mockUserId = "user-123";
const mockOllamaProvider = {
id: mockProviderId,
providerType: "ollama",
displayName: "Local Ollama",
userId: null,
config: {
endpoint: "http://localhost:11434",
timeout: 30000,
},
isDefault: true,
isEnabled: true,
createdAt: new Date(),
updatedAt: new Date(),
};
const mockOpenAiProvider = {
id: "provider-456",
providerType: "openai",
displayName: "OpenAI GPT-4",
userId: null,
config: {
endpoint: "https://api.openai.com/v1",
apiKey: "sk-test-key",
timeout: 30000,
},
isDefault: false,
isEnabled: true,
createdAt: new Date(),
updatedAt: new Date(),
};
const mockPrismaService = {
llmProviderInstance: {
findMany: vi.fn(),
findUnique: vi.fn(),
create: vi.fn(),
update: vi.fn(),
delete: vi.fn(),
},
};
const mockLlmManagerService = {
registerProvider: vi.fn(),
unregisterProvider: vi.fn(),
getProviderById: vi.fn(),
reloadFromDatabase: vi.fn(),
};
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [LlmProviderAdminController],
providers: [
{
provide: PrismaService,
useValue: mockPrismaService,
},
{
provide: LlmManagerService,
useValue: mockLlmManagerService,
},
],
}).compile();
controller = module.get<LlmProviderAdminController>(LlmProviderAdminController);
prisma = module.get<PrismaService>(PrismaService);
llmManager = module.get<LlmManagerService>(LlmManagerService);
// Reset mocks
vi.clearAllMocks();
});
describe("listProviders", () => {
it("should return all providers from database", async () => {
const mockProviders = [mockOllamaProvider, mockOpenAiProvider];
mockPrismaService.llmProviderInstance.findMany.mockResolvedValue(mockProviders);
const result = await controller.listProviders();
expect(result).toEqual(mockProviders);
expect(prisma.llmProviderInstance.findMany).toHaveBeenCalledWith({
orderBy: { createdAt: "asc" },
});
});
it("should return empty array when no providers exist", async () => {
mockPrismaService.llmProviderInstance.findMany.mockResolvedValue([]);
const result = await controller.listProviders();
expect(result).toEqual([]);
});
});
describe("getProvider", () => {
it("should return a provider by id", async () => {
mockPrismaService.llmProviderInstance.findUnique.mockResolvedValue(mockOllamaProvider);
const result = await controller.getProvider(mockProviderId);
expect(result).toEqual(mockOllamaProvider);
expect(prisma.llmProviderInstance.findUnique).toHaveBeenCalledWith({
where: { id: mockProviderId },
});
});
it("should throw NotFoundException when provider not found", async () => {
mockPrismaService.llmProviderInstance.findUnique.mockResolvedValue(null);
await expect(controller.getProvider("nonexistent")).rejects.toThrow(NotFoundException);
await expect(controller.getProvider("nonexistent")).rejects.toThrow(
"LLM provider with ID nonexistent not found"
);
});
});
describe("createProvider", () => {
it("should create an ollama provider", async () => {
const createDto: CreateLlmProviderDto = {
providerType: "ollama",
displayName: "Local Ollama",
config: {
endpoint: "http://localhost:11434",
timeout: 30000,
},
isDefault: true,
isEnabled: true,
};
mockPrismaService.llmProviderInstance.create.mockResolvedValue(mockOllamaProvider);
mockLlmManagerService.registerProvider.mockResolvedValue(undefined);
const result = await controller.createProvider(createDto);
expect(result).toEqual(mockOllamaProvider);
expect(prisma.llmProviderInstance.create).toHaveBeenCalledWith({
data: {
providerType: "ollama",
displayName: "Local Ollama",
userId: null,
config: {
endpoint: "http://localhost:11434",
timeout: 30000,
},
isDefault: true,
isEnabled: true,
},
});
expect(llmManager.registerProvider).toHaveBeenCalledWith(mockOllamaProvider);
});
it("should create an openai provider", async () => {
const createDto: CreateLlmProviderDto = {
providerType: "openai",
displayName: "OpenAI GPT-4",
config: {
endpoint: "https://api.openai.com/v1",
apiKey: "sk-test-key",
timeout: 30000,
},
isDefault: false,
isEnabled: true,
};
mockPrismaService.llmProviderInstance.create.mockResolvedValue(mockOpenAiProvider);
mockLlmManagerService.registerProvider.mockResolvedValue(undefined);
const result = await controller.createProvider(createDto);
expect(result).toEqual(mockOpenAiProvider);
expect(prisma.llmProviderInstance.create).toHaveBeenCalledWith({
data: {
providerType: "openai",
displayName: "OpenAI GPT-4",
userId: null,
config: {
endpoint: "https://api.openai.com/v1",
apiKey: "sk-test-key",
timeout: 30000,
},
isDefault: false,
isEnabled: true,
},
});
expect(llmManager.registerProvider).toHaveBeenCalledWith(mockOpenAiProvider);
});
it("should create a user-specific provider", async () => {
const createDto: CreateLlmProviderDto = {
providerType: "ollama",
displayName: "User Ollama",
userId: mockUserId,
config: {
endpoint: "http://localhost:11434",
},
isDefault: false,
isEnabled: true,
};
const userProvider = {
...mockOllamaProvider,
userId: mockUserId,
displayName: "User Ollama",
isDefault: false,
};
mockPrismaService.llmProviderInstance.create.mockResolvedValue(userProvider);
mockLlmManagerService.registerProvider.mockResolvedValue(undefined);
const result = await controller.createProvider(createDto);
expect(result).toEqual(userProvider);
expect(prisma.llmProviderInstance.create).toHaveBeenCalledWith({
data: {
providerType: "ollama",
displayName: "User Ollama",
userId: mockUserId,
config: {
endpoint: "http://localhost:11434",
},
isDefault: false,
isEnabled: true,
},
});
});
it("should handle registration failure gracefully", async () => {
const createDto: CreateLlmProviderDto = {
providerType: "ollama",
displayName: "Local Ollama",
config: {
endpoint: "http://localhost:11434",
},
};
mockPrismaService.llmProviderInstance.create.mockResolvedValue(mockOllamaProvider);
mockLlmManagerService.registerProvider.mockRejectedValue(
new Error("Provider initialization failed")
);
await expect(controller.createProvider(createDto)).rejects.toThrow(
"Provider initialization failed"
);
});
});
describe("updateProvider", () => {
it("should update provider settings", async () => {
const updateDto: UpdateLlmProviderDto = {
displayName: "Updated Ollama",
isEnabled: false,
};
const updatedProvider = {
...mockOllamaProvider,
...updateDto,
};
mockPrismaService.llmProviderInstance.findUnique.mockResolvedValue(mockOllamaProvider);
mockPrismaService.llmProviderInstance.update.mockResolvedValue(updatedProvider);
mockLlmManagerService.unregisterProvider.mockResolvedValue(undefined);
const result = await controller.updateProvider(mockProviderId, updateDto);
expect(result).toEqual(updatedProvider);
expect(prisma.llmProviderInstance.update).toHaveBeenCalledWith({
where: { id: mockProviderId },
data: updateDto,
});
expect(llmManager.unregisterProvider).toHaveBeenCalledWith(mockProviderId);
});
it("should re-register provider when updated and still enabled", async () => {
const updateDto: UpdateLlmProviderDto = {
displayName: "Updated Ollama",
config: {
endpoint: "http://new-endpoint:11434",
},
};
const updatedProvider = {
...mockOllamaProvider,
...updateDto,
};
mockPrismaService.llmProviderInstance.findUnique.mockResolvedValue(mockOllamaProvider);
mockPrismaService.llmProviderInstance.update.mockResolvedValue(updatedProvider);
mockLlmManagerService.unregisterProvider.mockResolvedValue(undefined);
mockLlmManagerService.registerProvider.mockResolvedValue(undefined);
const result = await controller.updateProvider(mockProviderId, updateDto);
expect(result).toEqual(updatedProvider);
expect(llmManager.unregisterProvider).toHaveBeenCalledWith(mockProviderId);
expect(llmManager.registerProvider).toHaveBeenCalledWith(updatedProvider);
});
it("should throw NotFoundException when provider not found", async () => {
const updateDto: UpdateLlmProviderDto = {
displayName: "Updated",
};
mockPrismaService.llmProviderInstance.findUnique.mockResolvedValue(null);
await expect(controller.updateProvider("nonexistent", updateDto)).rejects.toThrow(
NotFoundException
);
});
});
describe("deleteProvider", () => {
it("should delete a non-default provider", async () => {
const nonDefaultProvider = {
...mockOllamaProvider,
isDefault: false,
};
mockPrismaService.llmProviderInstance.findUnique.mockResolvedValue(nonDefaultProvider);
mockPrismaService.llmProviderInstance.delete.mockResolvedValue(nonDefaultProvider);
mockLlmManagerService.unregisterProvider.mockResolvedValue(undefined);
await controller.deleteProvider(mockProviderId);
expect(prisma.llmProviderInstance.delete).toHaveBeenCalledWith({
where: { id: mockProviderId },
});
expect(llmManager.unregisterProvider).toHaveBeenCalledWith(mockProviderId);
});
it("should throw NotFoundException when provider not found", async () => {
mockPrismaService.llmProviderInstance.findUnique.mockResolvedValue(null);
await expect(controller.deleteProvider("nonexistent")).rejects.toThrow(NotFoundException);
});
it("should prevent deleting the default provider", async () => {
mockPrismaService.llmProviderInstance.findUnique.mockResolvedValue(mockOllamaProvider);
await expect(controller.deleteProvider(mockProviderId)).rejects.toThrow(BadRequestException);
await expect(controller.deleteProvider(mockProviderId)).rejects.toThrow(
"Cannot delete the default provider. Set another provider as default first."
);
});
});
describe("testProvider", () => {
it("should return healthy status when provider is healthy", async () => {
mockPrismaService.llmProviderInstance.findUnique.mockResolvedValue(mockOllamaProvider);
mockLlmManagerService.getProviderById.mockResolvedValue({
checkHealth: vi.fn().mockResolvedValue({
healthy: true,
provider: "ollama",
endpoint: "http://localhost:11434",
}),
});
const result = await controller.testProvider(mockProviderId);
expect(result).toEqual({ healthy: true });
expect(llmManager.getProviderById).toHaveBeenCalledWith(mockProviderId);
});
it("should return unhealthy status with error message when provider fails", async () => {
mockPrismaService.llmProviderInstance.findUnique.mockResolvedValue(mockOllamaProvider);
mockLlmManagerService.getProviderById.mockResolvedValue({
checkHealth: vi.fn().mockResolvedValue({
healthy: false,
provider: "ollama",
error: "Connection refused",
}),
});
const result = await controller.testProvider(mockProviderId);
expect(result).toEqual({
healthy: false,
error: "Connection refused",
});
});
it("should throw NotFoundException when provider not found", async () => {
mockPrismaService.llmProviderInstance.findUnique.mockResolvedValue(null);
await expect(controller.testProvider("nonexistent")).rejects.toThrow(NotFoundException);
});
it("should handle provider not loaded in manager", async () => {
mockPrismaService.llmProviderInstance.findUnique.mockResolvedValue(mockOllamaProvider);
mockLlmManagerService.getProviderById.mockRejectedValue(
new Error("Provider with ID provider-123 not found")
);
const result = await controller.testProvider(mockProviderId);
expect(result).toEqual({
healthy: false,
error: "Provider not loaded in manager. Try reloading providers.",
});
});
});
describe("reloadProviders", () => {
it("should reload all providers from database", async () => {
const mockProviders = [mockOllamaProvider, mockOpenAiProvider];
mockPrismaService.llmProviderInstance.findMany.mockResolvedValue(mockProviders);
mockLlmManagerService.reloadFromDatabase.mockResolvedValue(undefined);
const result = await controller.reloadProviders();
expect(result).toEqual({
message: "Providers reloaded successfully",
count: 2,
});
expect(llmManager.reloadFromDatabase).toHaveBeenCalled();
expect(prisma.llmProviderInstance.findMany).toHaveBeenCalledWith({
where: { isEnabled: true },
});
});
it("should handle reload with no enabled providers", async () => {
mockPrismaService.llmProviderInstance.findMany.mockResolvedValue([]);
mockLlmManagerService.reloadFromDatabase.mockResolvedValue(undefined);
const result = await controller.reloadProviders();
expect(result).toEqual({
message: "Providers reloaded successfully",
count: 0,
});
});
});
});

View File

@@ -0,0 +1,278 @@
import {
Controller,
Get,
Post,
Patch,
Delete,
Body,
Param,
HttpCode,
HttpStatus,
NotFoundException,
BadRequestException,
} 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 {
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 {
// Provider not loaded in manager (might be disabled)
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,
};
}
}

View File

@@ -1,12 +1,13 @@
import { Module } from "@nestjs/common";
import { LlmController } from "./llm.controller";
import { LlmProviderAdminController } from "./llm-provider-admin.controller";
import { LlmService } from "./llm.service";
import { LlmManagerService } from "./llm-manager.service";
import { PrismaModule } from "../prisma/prisma.module";
@Module({
imports: [PrismaModule],
controllers: [LlmController],
controllers: [LlmController, LlmProviderAdminController],
providers: [LlmService, LlmManagerService],
exports: [LlmService, LlmManagerService],
})