model-config.service.ts 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. import { Injectable, NotFoundException, ForbiddenException, BadRequestException, forwardRef, Inject } from '@nestjs/common';
  2. import { InjectRepository } from '@nestjs/typeorm';
  3. import { Repository } from 'typeorm';
  4. import { ModelConfig } from './model-config.entity';
  5. import { CreateModelConfigDto } from './dto/create-model-config.dto';
  6. import { UpdateModelConfigDto } from './dto/update-model-config.dto';
  7. import { GLOBAL_TENANT_ID } from '../common/constants';
  8. import { TenantService } from '../tenant/tenant.service';
  9. import { ModelType } from '../types';
  10. @Injectable()
  11. export class ModelConfigService {
  12. constructor(
  13. @InjectRepository(ModelConfig)
  14. private modelConfigRepository: Repository<ModelConfig>,
  15. @Inject(forwardRef(() => TenantService))
  16. private readonly tenantService: TenantService,
  17. ) { }
  18. async create(
  19. userId: string,
  20. tenantId: string,
  21. createModelConfigDto: CreateModelConfigDto,
  22. ): Promise<ModelConfig> {
  23. const modelConfig = this.modelConfigRepository.create({
  24. ...createModelConfigDto,
  25. userId,
  26. tenantId,
  27. });
  28. return this.modelConfigRepository.save(modelConfig);
  29. }
  30. async findAll(userId: string, tenantId: string): Promise<ModelConfig[]> {
  31. return this.modelConfigRepository.createQueryBuilder('model')
  32. .where('model.tenantId = :tenantId OR model.tenantId IS NULL OR model.tenantId = :globalTenantId', {
  33. tenantId,
  34. globalTenantId: GLOBAL_TENANT_ID
  35. })
  36. .getMany();
  37. }
  38. async findOne(id: string, userId: string, tenantId: string): Promise<ModelConfig> {
  39. const modelConfig = await this.modelConfigRepository.createQueryBuilder('model')
  40. .where('model.id = :id', { id })
  41. .andWhere('(model.tenantId = :tenantId OR model.tenantId IS NULL OR model.tenantId = :globalTenantId)', {
  42. tenantId,
  43. globalTenantId: GLOBAL_TENANT_ID
  44. })
  45. .getOne();
  46. if (!modelConfig) {
  47. throw new NotFoundException(
  48. `ModelConfig with ID "${id}" not found.`,
  49. );
  50. }
  51. return modelConfig;
  52. }
  53. async findByType(userId: string, tenantId: string, type: string): Promise<ModelConfig[]> {
  54. return this.modelConfigRepository.createQueryBuilder('model')
  55. .where('model.type = :type', { type })
  56. .andWhere('(model.tenantId = :tenantId OR model.tenantId IS NULL OR model.tenantId = :globalTenantId)', {
  57. tenantId,
  58. globalTenantId: GLOBAL_TENANT_ID
  59. })
  60. .getMany();
  61. }
  62. async update(
  63. userId: string,
  64. tenantId: string,
  65. id: string,
  66. updateModelConfigDto: UpdateModelConfigDto,
  67. ): Promise<ModelConfig> {
  68. const modelConfig = await this.findOne(id, userId, tenantId);
  69. if (!modelConfig) {
  70. throw new NotFoundException(
  71. `ModelConfig with ID "${id}" not found.`,
  72. );
  73. }
  74. // Only allow updating if it belongs to the tenant, or if it's a global admin (not fully implemented, so we check tenantId)
  75. if (modelConfig.tenantId && modelConfig.tenantId !== tenantId) {
  76. throw new ForbiddenException('Cannot update models from another tenant');
  77. }
  78. // Update the model
  79. const updated = this.modelConfigRepository.merge(
  80. modelConfig,
  81. updateModelConfigDto,
  82. );
  83. return this.modelConfigRepository.save(updated);
  84. }
  85. async remove(userId: string, tenantId: string, id: string): Promise<void> {
  86. // Only allow removing if it exists and accessible in current tenant context
  87. const model = await this.findOne(id, userId, tenantId);
  88. if (model.tenantId && model.tenantId !== tenantId) {
  89. throw new ForbiddenException('Cannot delete models from another tenant');
  90. }
  91. const result = await this.modelConfigRepository.delete({ id });
  92. if (result.affected === 0) {
  93. throw new NotFoundException(`ModelConfig with ID "${id}" not found.`);
  94. }
  95. }
  96. /**
  97. * 指定されたモデルをデフォルトに設定
  98. */
  99. async setDefault(userId: string, tenantId: string, id: string): Promise<ModelConfig> {
  100. const modelConfig = await this.findOne(id, userId, tenantId);
  101. // 同じタイプの他のモデルのデフォルトフラグをクリア (現在のテナント内またはglobal)
  102. // 厳密には、現在のテナントのIsDefault設定といった方が正しいですが、シンプルにするため全体のIsDefaultを操作します
  103. await this.modelConfigRepository
  104. .createQueryBuilder()
  105. .update(ModelConfig)
  106. .set({ isDefault: false })
  107. .where('type = :type', { type: modelConfig.type })
  108. .andWhere('(tenantId = :tenantId OR tenantId IS NULL OR tenantId = :globalTenantId)', {
  109. tenantId,
  110. globalTenantId: GLOBAL_TENANT_ID
  111. })
  112. .execute();
  113. modelConfig.isDefault = true;
  114. return this.modelConfigRepository.save(modelConfig);
  115. }
  116. /**
  117. * 指定されたタイプのデフォルトモデルを取得
  118. * 厳密なルール:Index Chat Configで指定されたモデルのみを返し、なければエラーを投げる
  119. */
  120. async findDefaultByType(tenantId: string, type: ModelType): Promise<ModelConfig> {
  121. const settings = await this.tenantService.getSettings(tenantId);
  122. if (!settings) {
  123. throw new BadRequestException(`Organization settings not found for tenant: ${tenantId}`);
  124. }
  125. let modelId: string | undefined;
  126. if (type === ModelType.LLM) {
  127. modelId = settings.selectedLLMId;
  128. } else if (type === ModelType.EMBEDDING) {
  129. modelId = settings.selectedEmbeddingId;
  130. } else if (type === ModelType.RERANK) {
  131. modelId = settings.selectedRerankId;
  132. }
  133. if (!modelId) {
  134. throw new BadRequestException(`Model of type "${type}" is not configured in Index Chat Config for this organization.`);
  135. }
  136. const model = await this.modelConfigRepository.findOne({
  137. where: { id: modelId, isEnabled: true }
  138. });
  139. if (!model) {
  140. throw new BadRequestException(`The configured model for "${type}" (ID: ${modelId}) is either missing or disabled in model management.`);
  141. }
  142. return model;
  143. }
  144. }