feat(modelprovider): 补齐模型工作台聚合与转换分层
This commit is contained in:
@@ -3,36 +3,33 @@ package com.bruce.modelprovider.controller;
|
||||
import com.bruce.common.domain.model.RequestResult;
|
||||
import com.bruce.modelprovider.dto.request.ModelCallLogQueryRequest;
|
||||
import com.bruce.modelprovider.dto.response.ModelCallLogResponse;
|
||||
import com.bruce.modelprovider.factory.ModelCallLogFactory;
|
||||
import com.bruce.modelprovider.service.IModelCallLogService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/api/model/call-logs")
|
||||
@RequiredArgsConstructor
|
||||
/**
|
||||
* 该类属于模型平台模块,用于承载对应分层职责。
|
||||
*/
|
||||
public class ModelCallLogController {
|
||||
|
||||
private final IModelCallLogService modelCallLogService;
|
||||
private final ModelCallLogFactory modelCallLogFactory;
|
||||
|
||||
@PostMapping("/query")
|
||||
/**
|
||||
* 方法 query,用于执行业务逻辑处理。
|
||||
*/
|
||||
public RequestResult<List<ModelCallLogResponse>> query(@RequestBody(required = false) ModelCallLogQueryRequest request) {
|
||||
log.info("ModelCallLogController.query start, request={}", request);
|
||||
return RequestResult.success(modelCallLogService.query(request));
|
||||
}
|
||||
|
||||
@GetMapping("/detail")
|
||||
/**
|
||||
* 方法 detail,用于执行业务逻辑处理。
|
||||
*/
|
||||
public RequestResult<ModelCallLogResponse> detail(@RequestParam("id") Long id) {
|
||||
return RequestResult.success(ModelCallLogResponse.fromEntity(modelCallLogService.getById(id)));
|
||||
log.info("ModelCallLogController.detail start, id={}", id);
|
||||
return RequestResult.success(modelCallLogFactory.toResponse(modelCallLogService.getById(id)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,50 +4,43 @@ import com.bruce.common.domain.model.RequestResult;
|
||||
import com.bruce.modelprovider.dto.request.ModelConfigSaveRequest;
|
||||
import com.bruce.modelprovider.dto.response.ModelConfigResponse;
|
||||
import com.bruce.modelprovider.service.IModelConfigService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/api/model/configs")
|
||||
@RequiredArgsConstructor
|
||||
/**
|
||||
* 该类属于模型平台模块,用于承载对应分层职责。
|
||||
*/
|
||||
public class ModelConfigController {
|
||||
|
||||
private final IModelConfigService modelConfigService;
|
||||
|
||||
@PostMapping("/query")
|
||||
/**
|
||||
* 方法 query,用于执行业务逻辑处理。
|
||||
*/
|
||||
public RequestResult<List<ModelConfigResponse>> query() {
|
||||
log.info("ModelConfigController.query start");
|
||||
return RequestResult.success(modelConfigService.listResponses());
|
||||
}
|
||||
|
||||
@GetMapping("/detail")
|
||||
/**
|
||||
* 方法 detail,用于执行业务逻辑处理。
|
||||
*/
|
||||
public RequestResult<ModelConfigResponse> detail(@RequestParam("id") Long id) {
|
||||
log.info("ModelConfigController.detail start, id={}", id);
|
||||
return RequestResult.success(modelConfigService.getResponseById(id));
|
||||
}
|
||||
|
||||
@PostMapping("/save")
|
||||
/**
|
||||
* 方法 save,用于执行业务逻辑处理。
|
||||
*/
|
||||
public RequestResult<Boolean> save(@RequestBody ModelConfigSaveRequest request) {
|
||||
log.info("ModelConfigController.save start, id={}, modelCode={}",
|
||||
request == null ? null : request.getId(),
|
||||
request == null ? null : request.getModelCode());
|
||||
return RequestResult.success(modelConfigService.saveOrUpdate(request));
|
||||
}
|
||||
|
||||
@PostMapping("/delete")
|
||||
/**
|
||||
* 方法 delete,用于执行业务逻辑处理。
|
||||
*/
|
||||
public RequestResult<Boolean> delete(@RequestParam("id") Long id) {
|
||||
log.info("ModelConfigController.delete start, id={}", id);
|
||||
return RequestResult.success(modelConfigService.removeById(id));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,58 +4,49 @@ import com.bruce.common.domain.model.RequestResult;
|
||||
import com.bruce.modelprovider.dto.request.ModelProviderSaveRequest;
|
||||
import com.bruce.modelprovider.dto.response.ModelProviderResponse;
|
||||
import com.bruce.modelprovider.service.IModelProviderService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/api/model/providers")
|
||||
@RequiredArgsConstructor
|
||||
/**
|
||||
* 该类属于模型平台模块,用于承载对应分层职责。
|
||||
*/
|
||||
public class ModelProviderController {
|
||||
|
||||
private final IModelProviderService modelProviderService;
|
||||
|
||||
@PostMapping("/query")
|
||||
/**
|
||||
* 方法 query,用于执行业务逻辑处理。
|
||||
*/
|
||||
public RequestResult<List<ModelProviderResponse>> query() {
|
||||
log.info("ModelProviderController.query start");
|
||||
return RequestResult.success(modelProviderService.listResponses());
|
||||
}
|
||||
|
||||
@GetMapping("/detail")
|
||||
/**
|
||||
* 方法 detail,用于执行业务逻辑处理。
|
||||
*/
|
||||
public RequestResult<ModelProviderResponse> detail(@RequestParam("id") Long id) {
|
||||
log.info("ModelProviderController.detail start, id={}", id);
|
||||
return RequestResult.success(modelProviderService.getResponseById(id));
|
||||
}
|
||||
|
||||
@PostMapping("/save")
|
||||
/**
|
||||
* 方法 save,用于执行业务逻辑处理。
|
||||
*/
|
||||
public RequestResult<Boolean> save(@RequestBody ModelProviderSaveRequest request) {
|
||||
log.info("ModelProviderController.save start, id={}, providerCode={}",
|
||||
request == null ? null : request.getId(),
|
||||
request == null ? null : request.getProviderCode());
|
||||
return RequestResult.success(modelProviderService.saveOrUpdate(request));
|
||||
}
|
||||
|
||||
@PostMapping("/delete")
|
||||
/**
|
||||
* 方法 delete,用于执行业务逻辑处理。
|
||||
*/
|
||||
public RequestResult<Boolean> delete(@RequestParam("id") Long id) {
|
||||
log.info("ModelProviderController.delete start, id={}", id);
|
||||
return RequestResult.success(modelProviderService.removeById(id));
|
||||
}
|
||||
|
||||
@PostMapping("/checkHealth")
|
||||
/**
|
||||
* 方法 checkHealth,用于执行业务逻辑处理。
|
||||
*/
|
||||
public RequestResult<Boolean> checkHealth(@RequestParam("id") Long id) {
|
||||
log.info("ModelProviderController.checkHealth start, id={}", id);
|
||||
return RequestResult.success(modelProviderService.checkHealth(id));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,50 +4,43 @@ import com.bruce.common.domain.model.RequestResult;
|
||||
import com.bruce.modelprovider.dto.request.ModelRouteRuleSaveRequest;
|
||||
import com.bruce.modelprovider.dto.response.ModelRouteRuleResponse;
|
||||
import com.bruce.modelprovider.service.IModelRouteRuleService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/api/model/routes")
|
||||
@RequiredArgsConstructor
|
||||
/**
|
||||
* 该类属于模型平台模块,用于承载对应分层职责。
|
||||
*/
|
||||
public class ModelRouteRuleController {
|
||||
|
||||
private final IModelRouteRuleService modelRouteRuleService;
|
||||
|
||||
@PostMapping("/query")
|
||||
/**
|
||||
* 方法 query,用于执行业务逻辑处理。
|
||||
*/
|
||||
public RequestResult<List<ModelRouteRuleResponse>> query() {
|
||||
log.info("ModelRouteRuleController.query start");
|
||||
return RequestResult.success(modelRouteRuleService.listResponses());
|
||||
}
|
||||
|
||||
@GetMapping("/detail")
|
||||
/**
|
||||
* 方法 detail,用于执行业务逻辑处理。
|
||||
*/
|
||||
public RequestResult<ModelRouteRuleResponse> detail(@RequestParam("id") Long id) {
|
||||
log.info("ModelRouteRuleController.detail start, id={}", id);
|
||||
return RequestResult.success(modelRouteRuleService.getResponseById(id));
|
||||
}
|
||||
|
||||
@PostMapping("/save")
|
||||
/**
|
||||
* 方法 save,用于执行业务逻辑处理。
|
||||
*/
|
||||
public RequestResult<Boolean> save(@RequestBody ModelRouteRuleSaveRequest request) {
|
||||
log.info("ModelRouteRuleController.save start, id={}, routeCode={}",
|
||||
request == null ? null : request.getId(),
|
||||
request == null ? null : request.getRouteCode());
|
||||
return RequestResult.success(modelRouteRuleService.saveOrUpdate(request));
|
||||
}
|
||||
|
||||
@PostMapping("/delete")
|
||||
/**
|
||||
* 方法 delete,用于执行业务逻辑处理。
|
||||
*/
|
||||
public RequestResult<Boolean> delete(@RequestParam("id") Long id) {
|
||||
log.info("ModelRouteRuleController.delete start, id={}", id);
|
||||
return RequestResult.success(modelRouteRuleService.removeById(id));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.bruce.modelprovider.controller;
|
||||
|
||||
import com.bruce.common.domain.model.RequestResult;
|
||||
import com.bruce.modelprovider.service.IModelWorkspaceService;
|
||||
import com.bruce.modelprovider.vo.ModelWorkspaceVO;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* 模型工作台聚合接口。
|
||||
*/
|
||||
@Tag(name = "模型工作台")
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
@RequestMapping("/api/model/workspace")
|
||||
public class ModelWorkspaceController {
|
||||
|
||||
private final IModelWorkspaceService modelWorkspaceService;
|
||||
|
||||
@Operation(summary = "查询模型与路由工作台聚合视图")
|
||||
@GetMapping
|
||||
public RequestResult<ModelWorkspaceVO> getWorkspace() {
|
||||
log.info("ModelWorkspaceController.getWorkspace start");
|
||||
ModelWorkspaceVO workspace = modelWorkspaceService.getWorkspace();
|
||||
log.info("ModelWorkspaceController.getWorkspace success, providerCount={}, modelCount={}, routeRuleCount={}",
|
||||
workspace.getProviderCount(), workspace.getModelCount(), workspace.getRouteRuleCount());
|
||||
return RequestResult.success(workspace);
|
||||
}
|
||||
}
|
||||
@@ -4,40 +4,35 @@ import com.bruce.common.domain.model.RequestResult;
|
||||
import com.bruce.modelprovider.dto.request.RagStoreModelConfigSaveRequest;
|
||||
import com.bruce.modelprovider.dto.response.RagStoreModelConfigResponse;
|
||||
import com.bruce.modelprovider.service.IRagStoreModelConfigService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/api/rag/store")
|
||||
@RequiredArgsConstructor
|
||||
/**
|
||||
* 该类属于模型平台模块,用于承载对应分层职责。
|
||||
*/
|
||||
public class RagStoreModelConfigController {
|
||||
|
||||
private final IRagStoreModelConfigService ragStoreModelConfigService;
|
||||
|
||||
@GetMapping("/modelConfig")
|
||||
/**
|
||||
* 方法 modelConfig,用于执行业务逻辑处理。
|
||||
*/
|
||||
public RequestResult<RagStoreModelConfigResponse> modelConfig(@RequestParam("storeId") Long storeId) {
|
||||
log.info("RagStoreModelConfigController.modelConfig start, storeId={}", storeId);
|
||||
return RequestResult.success(ragStoreModelConfigService.getByStoreId(storeId));
|
||||
}
|
||||
|
||||
@PostMapping("/modelConfig/save")
|
||||
/**
|
||||
* 方法 save,用于执行业务逻辑处理。
|
||||
*/
|
||||
public RequestResult<Boolean> save(@RequestBody RagStoreModelConfigSaveRequest request) {
|
||||
log.info("RagStoreModelConfigController.save start, storeId={}, embeddingModelId={}",
|
||||
request == null ? null : request.getStoreId(),
|
||||
request == null ? null : request.getEmbeddingModelId());
|
||||
return RequestResult.success(ragStoreModelConfigService.saveOrUpdate(request));
|
||||
}
|
||||
|
||||
@PostMapping("/rebuildIndex")
|
||||
/**
|
||||
* 方法 rebuildIndex,用于执行业务逻辑处理。
|
||||
*/
|
||||
public RequestResult<Boolean> rebuildIndex(@RequestParam("storeId") Long storeId) {
|
||||
log.info("RagStoreModelConfigController.rebuildIndex start, storeId={}", storeId);
|
||||
return RequestResult.success(storeId != null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.bruce.modelprovider.factory;
|
||||
|
||||
import com.bruce.modelprovider.dto.response.ModelCallLogResponse;
|
||||
import com.bruce.modelprovider.entity.ModelCallLog;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 模型调用日志工厂。
|
||||
*/
|
||||
@Component
|
||||
public class ModelCallLogFactory {
|
||||
|
||||
public ModelCallLogResponse toResponse(ModelCallLog entity) {
|
||||
if (entity == null) {
|
||||
return null;
|
||||
}
|
||||
ModelCallLogResponse response = new ModelCallLogResponse();
|
||||
BeanUtils.copyProperties(entity, response);
|
||||
return response;
|
||||
}
|
||||
|
||||
public List<ModelCallLogResponse> toResponses(List<ModelCallLog> entities) {
|
||||
return entities == null ? List.of() : entities.stream().map(this::toResponse).toList();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package com.bruce.modelprovider.factory;
|
||||
|
||||
import com.bruce.modelprovider.dto.request.ModelConfigSaveRequest;
|
||||
import com.bruce.modelprovider.dto.response.ModelConfigResponse;
|
||||
import com.bruce.modelprovider.entity.ModelConfig;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 模型配置工厂,统一转换模型配置请求、实体和返回对象。
|
||||
*/
|
||||
@Component
|
||||
public class ModelConfigFactory {
|
||||
|
||||
public ModelConfig toEntity(ModelConfigSaveRequest request) {
|
||||
if (request == null) {
|
||||
return null;
|
||||
}
|
||||
ModelConfig entity = new ModelConfig();
|
||||
entity.setId(request.getId());
|
||||
entity.setProviderId(request.getProviderId());
|
||||
entity.setModelCode(trimToNull(request.getModelCode()));
|
||||
entity.setModelName(trimToNull(request.getModelName()));
|
||||
entity.setUpstreamModel(trimToNull(request.getUpstreamModel()));
|
||||
entity.setModelType(trimToNull(request.getModelType()));
|
||||
entity.setEmbeddingDimension(request.getEmbeddingDimension());
|
||||
entity.setLocalModel(request.getLocalModel());
|
||||
entity.setDefaultModel(request.getDefaultModel());
|
||||
entity.setOptionsJson(trimToNull(request.getOptionsJson()));
|
||||
entity.setEnabled(request.getEnabled());
|
||||
entity.setRemark(trimToNull(request.getRemark()));
|
||||
return entity;
|
||||
}
|
||||
|
||||
public ModelConfigResponse toResponse(ModelConfig entity) {
|
||||
if (entity == null) {
|
||||
return null;
|
||||
}
|
||||
ModelConfigResponse response = new ModelConfigResponse();
|
||||
BeanUtils.copyProperties(entity, response);
|
||||
return response;
|
||||
}
|
||||
|
||||
public List<ModelConfigResponse> toResponses(List<ModelConfig> entities) {
|
||||
return entities == null ? List.of() : entities.stream().map(this::toResponse).toList();
|
||||
}
|
||||
|
||||
private String trimToNull(String value) {
|
||||
if (!StringUtils.hasText(value)) {
|
||||
return null;
|
||||
}
|
||||
return value.trim();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package com.bruce.modelprovider.factory;
|
||||
|
||||
import com.bruce.modelprovider.dto.request.ModelProviderSaveRequest;
|
||||
import com.bruce.modelprovider.dto.response.ModelProviderResponse;
|
||||
import com.bruce.modelprovider.entity.ModelProvider;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 模型服务商工厂,统一负责服务商 DTO、Entity、VO 的转换。
|
||||
*/
|
||||
@Component
|
||||
public class ModelProviderFactory {
|
||||
|
||||
/**
|
||||
* 将保存请求转换为实体,统一处理字符串裁剪逻辑。
|
||||
*/
|
||||
public ModelProvider toEntity(ModelProviderSaveRequest request) {
|
||||
if (request == null) {
|
||||
return null;
|
||||
}
|
||||
ModelProvider entity = new ModelProvider();
|
||||
entity.setId(request.getId());
|
||||
entity.setProviderCode(trimToNull(request.getProviderCode()));
|
||||
entity.setProviderName(trimToNull(request.getProviderName()));
|
||||
entity.setProviderType(trimToNull(request.getProviderType()));
|
||||
entity.setProtocolType(trimToNull(request.getProtocolType()));
|
||||
entity.setBaseUrl(trimToNull(request.getBaseUrl()));
|
||||
entity.setAuthType(trimToNull(request.getAuthType()));
|
||||
entity.setSecretRef(trimToNull(request.getSecretRef()));
|
||||
entity.setApiKeyCipher(trimToNull(request.getApiKeyCipher()));
|
||||
entity.setTimeoutMs(request.getTimeoutMs());
|
||||
entity.setPriority(request.getPriority());
|
||||
entity.setEnabled(request.getEnabled());
|
||||
entity.setRemark(trimToNull(request.getRemark()));
|
||||
return entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将实体转换为前端返回对象,并隐藏密文字段,只返回是否已配置密钥。
|
||||
*/
|
||||
public ModelProviderResponse toResponse(ModelProvider entity) {
|
||||
if (entity == null) {
|
||||
return null;
|
||||
}
|
||||
ModelProviderResponse response = new ModelProviderResponse();
|
||||
BeanUtils.copyProperties(entity, response);
|
||||
response.setHasApiKey(StringUtils.hasText(entity.getApiKeyCipher()));
|
||||
return response;
|
||||
}
|
||||
|
||||
public List<ModelProviderResponse> toResponses(List<ModelProvider> entities) {
|
||||
return entities == null ? List.of() : entities.stream().map(this::toResponse).toList();
|
||||
}
|
||||
|
||||
private String trimToNull(String value) {
|
||||
if (!StringUtils.hasText(value)) {
|
||||
return null;
|
||||
}
|
||||
return value.trim();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package com.bruce.modelprovider.factory;
|
||||
|
||||
import com.bruce.modelprovider.dto.request.ModelRouteRuleSaveRequest;
|
||||
import com.bruce.modelprovider.dto.response.ModelRouteRuleResponse;
|
||||
import com.bruce.modelprovider.entity.ModelRouteRule;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 模型路由规则工厂。
|
||||
*/
|
||||
@Component
|
||||
public class ModelRouteRuleFactory {
|
||||
|
||||
public ModelRouteRule toEntity(ModelRouteRuleSaveRequest request) {
|
||||
if (request == null) {
|
||||
return null;
|
||||
}
|
||||
ModelRouteRule entity = new ModelRouteRule();
|
||||
entity.setId(request.getId());
|
||||
entity.setRouteCode(trimToNull(request.getRouteCode()));
|
||||
entity.setRouteName(trimToNull(request.getRouteName()));
|
||||
entity.setTaskType(trimToNull(request.getTaskType()));
|
||||
entity.setMatchScope(trimToNull(request.getMatchScope()));
|
||||
entity.setScopeId(request.getScopeId());
|
||||
entity.setPrimaryModelId(request.getPrimaryModelId());
|
||||
entity.setFallbackModelIdsJson(trimToNull(request.getFallbackModelIdsJson()));
|
||||
entity.setRouteStrategy(trimToNull(request.getRouteStrategy()));
|
||||
entity.setMaxLatencyMs(request.getMaxLatencyMs());
|
||||
entity.setEnabled(request.getEnabled());
|
||||
entity.setRemark(trimToNull(request.getRemark()));
|
||||
return entity;
|
||||
}
|
||||
|
||||
public ModelRouteRuleResponse toResponse(ModelRouteRule entity) {
|
||||
if (entity == null) {
|
||||
return null;
|
||||
}
|
||||
ModelRouteRuleResponse response = new ModelRouteRuleResponse();
|
||||
BeanUtils.copyProperties(entity, response);
|
||||
return response;
|
||||
}
|
||||
|
||||
public List<ModelRouteRuleResponse> toResponses(List<ModelRouteRule> entities) {
|
||||
return entities == null ? List.of() : entities.stream().map(this::toResponse).toList();
|
||||
}
|
||||
|
||||
private String trimToNull(String value) {
|
||||
if (!StringUtils.hasText(value)) {
|
||||
return null;
|
||||
}
|
||||
return value.trim();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.bruce.modelprovider.service;
|
||||
|
||||
import com.bruce.modelprovider.vo.ModelWorkspaceVO;
|
||||
|
||||
/**
|
||||
* 模型工作台聚合服务。
|
||||
*/
|
||||
public interface IModelWorkspaceService {
|
||||
|
||||
/**
|
||||
* 聚合模型服务商、模型、路由和最近失败调用摘要。
|
||||
*/
|
||||
ModelWorkspaceVO getWorkspace();
|
||||
}
|
||||
@@ -4,37 +4,38 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.bruce.modelprovider.dto.request.ModelCallLogQueryRequest;
|
||||
import com.bruce.modelprovider.dto.response.ModelCallLogResponse;
|
||||
import com.bruce.modelprovider.entity.ModelCallLog;
|
||||
import com.bruce.modelprovider.factory.ModelCallLogFactory;
|
||||
import com.bruce.modelprovider.mapper.ModelCallLogMapper;
|
||||
import com.bruce.modelprovider.service.IModelCallLogService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
/**
|
||||
* 该类属于模型平台模块,用于承载对应分层职责。
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public class ModelCallLogServiceImpl extends ServiceImpl<ModelCallLogMapper, ModelCallLog>
|
||||
implements IModelCallLogService {
|
||||
|
||||
private final ModelCallLogFactory modelCallLogFactory;
|
||||
|
||||
@Override
|
||||
/**
|
||||
* 方法 query,用于执行业务逻辑处理。
|
||||
*/
|
||||
public List<ModelCallLogResponse> query(ModelCallLogQueryRequest request) {
|
||||
log.info("查询模型调用日志开始,request={}", request);
|
||||
ModelCallLogQueryRequest q = request == null ? new ModelCallLogQueryRequest() : request;
|
||||
return lambdaQuery()
|
||||
List<ModelCallLogResponse> responses = modelCallLogFactory.toResponses(lambdaQuery()
|
||||
.eq(StringUtils.hasText(q.getTaskType()), ModelCallLog::getTaskType, q.getTaskType())
|
||||
.eq(q.getProviderId() != null, ModelCallLog::getProviderId, q.getProviderId())
|
||||
.eq(q.getModelId() != null, ModelCallLog::getModelId, q.getModelId())
|
||||
.eq(StringUtils.hasText(q.getStatus()), ModelCallLog::getStatus, q.getStatus())
|
||||
.eq(StringUtils.hasText(q.getBizType()), ModelCallLog::getBizType, q.getBizType())
|
||||
.orderByDesc(ModelCallLog::getId)
|
||||
.list()
|
||||
.stream()
|
||||
.map(ModelCallLogResponse::fromEntity)
|
||||
.toList();
|
||||
.list());
|
||||
log.info("查询模型调用日志结束,count={}", responses.size());
|
||||
return responses;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,43 +4,45 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.bruce.modelprovider.dto.request.ModelConfigSaveRequest;
|
||||
import com.bruce.modelprovider.dto.response.ModelConfigResponse;
|
||||
import com.bruce.modelprovider.entity.ModelConfig;
|
||||
import com.bruce.modelprovider.factory.ModelConfigFactory;
|
||||
import com.bruce.modelprovider.mapper.ModelConfigMapper;
|
||||
import com.bruce.modelprovider.service.IModelConfigService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
/**
|
||||
* 该类属于模型平台模块,用于承载对应分层职责。
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public class ModelConfigServiceImpl extends ServiceImpl<ModelConfigMapper, ModelConfig> implements IModelConfigService {
|
||||
|
||||
private final ModelConfigFactory modelConfigFactory;
|
||||
|
||||
@Override
|
||||
/**
|
||||
* 方法 listResponses,用于执行业务逻辑处理。
|
||||
*/
|
||||
public List<ModelConfigResponse> listResponses() {
|
||||
/**
|
||||
* 方法 list,用于定义接口能力契约。
|
||||
*/
|
||||
return list().stream().map(ModelConfigResponse::fromEntity).toList();
|
||||
log.info("查询模型配置列表开始");
|
||||
List<ModelConfigResponse> responses = modelConfigFactory.toResponses(list());
|
||||
log.info("查询模型配置列表结束,count={}", responses.size());
|
||||
return responses;
|
||||
}
|
||||
|
||||
@Override
|
||||
/**
|
||||
* 方法 getResponseById,用于执行业务逻辑处理。
|
||||
*/
|
||||
public ModelConfigResponse getResponseById(Long id) {
|
||||
return ModelConfigResponse.fromEntity(getById(id));
|
||||
log.info("查询模型配置详情开始,id={}", id);
|
||||
ModelConfigResponse response = modelConfigFactory.toResponse(getById(id));
|
||||
log.info("查询模型配置详情结束,id={}, found={}", id, response != null);
|
||||
return response;
|
||||
}
|
||||
|
||||
@Override
|
||||
/**
|
||||
* 方法 saveOrUpdate,用于执行业务逻辑处理。
|
||||
*/
|
||||
public boolean saveOrUpdate(ModelConfigSaveRequest request) {
|
||||
log.info("保存模型配置开始,id={}, providerId={}, modelCode={}",
|
||||
request == null ? null : request.getId(),
|
||||
request == null ? null : request.getProviderId(),
|
||||
request == null ? null : request.getModelCode());
|
||||
if (request == null) {
|
||||
throw new IllegalArgumentException("模型保存请求不能为空");
|
||||
}
|
||||
@@ -62,24 +64,25 @@ public class ModelConfigServiceImpl extends ServiceImpl<ModelConfigMapper, Model
|
||||
if (entity == null) {
|
||||
throw new IllegalArgumentException("模型不存在,ID: " + request.getId());
|
||||
}
|
||||
entity.setProviderId(request.getProviderId());
|
||||
entity.setModelCode(request.getModelCode().trim());
|
||||
entity.setModelName(request.getModelName());
|
||||
entity.setUpstreamModel(request.getUpstreamModel());
|
||||
entity.setModelType(request.getModelType());
|
||||
entity.setEmbeddingDimension(request.getEmbeddingDimension());
|
||||
entity.setLocalModel(request.getLocalModel());
|
||||
entity.setDefaultModel(request.getDefaultModel());
|
||||
entity.setOptionsJson(request.getOptionsJson());
|
||||
ModelConfig requestEntity = modelConfigFactory.toEntity(request);
|
||||
entity.setProviderId(requestEntity.getProviderId());
|
||||
entity.setModelCode(requestEntity.getModelCode());
|
||||
entity.setModelName(requestEntity.getModelName());
|
||||
entity.setUpstreamModel(requestEntity.getUpstreamModel());
|
||||
entity.setModelType(requestEntity.getModelType());
|
||||
entity.setEmbeddingDimension(requestEntity.getEmbeddingDimension());
|
||||
entity.setLocalModel(requestEntity.getLocalModel());
|
||||
entity.setDefaultModel(requestEntity.getDefaultModel());
|
||||
entity.setOptionsJson(requestEntity.getOptionsJson());
|
||||
entity.setEnabled(request.getEnabled() == null ? Boolean.TRUE : request.getEnabled());
|
||||
entity.setRemark(request.getRemark());
|
||||
return request.getId() == null ? save(entity) : updateById(entity);
|
||||
entity.setRemark(requestEntity.getRemark());
|
||||
boolean result = request.getId() == null ? save(entity) : updateById(entity);
|
||||
log.info("保存模型配置结束,id={}, providerId={}, modelCode={}, result={}",
|
||||
entity.getId(), entity.getProviderId(), entity.getModelCode(), result);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
/**
|
||||
* 方法 getEnabledModel,用于执行业务逻辑处理。
|
||||
*/
|
||||
public ModelConfig getEnabledModel(Long modelId) {
|
||||
ModelConfig model = getById(modelId);
|
||||
if (model == null || !Boolean.TRUE.equals(model.getEnabled())) {
|
||||
|
||||
@@ -6,9 +6,11 @@ import com.bruce.modelprovider.dto.request.ModelProviderSaveRequest;
|
||||
import com.bruce.modelprovider.dto.response.ModelProviderResponse;
|
||||
import com.bruce.modelprovider.entity.ModelProvider;
|
||||
import com.bruce.modelprovider.enums.ModelHealthStatusEnum;
|
||||
import com.bruce.modelprovider.factory.ModelProviderFactory;
|
||||
import com.bruce.modelprovider.mapper.ModelProviderMapper;
|
||||
import com.bruce.modelprovider.service.IModelProviderService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@@ -24,39 +26,35 @@ import java.util.List;
|
||||
* 3. 默认健康状态初始化;
|
||||
* 4. 对接上游健康检查并回写检查结果。
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
/**
|
||||
* ModelProviderServiceImpl,负责模型平台对应层的职责。
|
||||
*/
|
||||
public class ModelProviderServiceImpl extends ServiceImpl<ModelProviderMapper, ModelProvider>
|
||||
implements IModelProviderService {
|
||||
|
||||
private final OpenAiCompatibleModelClient openAiCompatibleModelClient;
|
||||
private final ModelProviderFactory modelProviderFactory;
|
||||
|
||||
/**
|
||||
* 查询全部服务商并转换为响应对象。
|
||||
*/
|
||||
@Override
|
||||
/**
|
||||
* 方法 listResponses,用于执行业务逻辑处理。
|
||||
*/
|
||||
public List<ModelProviderResponse> listResponses() {
|
||||
/**
|
||||
* 方法 list,用于定义接口能力契约。
|
||||
*/
|
||||
return list().stream().map(ModelProviderResponse::fromEntity).toList();
|
||||
log.info("查询模型服务商列表开始");
|
||||
List<ModelProviderResponse> responses = modelProviderFactory.toResponses(list());
|
||||
log.info("查询模型服务商列表结束,count={}", responses.size());
|
||||
return responses;
|
||||
}
|
||||
|
||||
/**
|
||||
* 按ID查询服务商详情(响应对象已脱敏,不返回明文密钥)。
|
||||
*/
|
||||
@Override
|
||||
/**
|
||||
* 方法 getResponseById,用于执行业务逻辑处理。
|
||||
*/
|
||||
public ModelProviderResponse getResponseById(Long id) {
|
||||
return ModelProviderResponse.fromEntity(getById(id));
|
||||
log.info("查询模型服务商详情开始,id={}", id);
|
||||
ModelProviderResponse response = modelProviderFactory.toResponse(getById(id));
|
||||
log.info("查询模型服务商详情结束,id={}, found={}", id, response != null);
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -64,10 +62,10 @@ public class ModelProviderServiceImpl extends ServiceImpl<ModelProviderMapper, M
|
||||
* 会执行必要校验:请求非空、编码/名称非空、编码唯一。
|
||||
*/
|
||||
@Override
|
||||
/**
|
||||
* 方法 saveOrUpdate,用于执行业务逻辑处理。
|
||||
*/
|
||||
public boolean saveOrUpdate(ModelProviderSaveRequest request) {
|
||||
log.info("保存模型服务商开始,id={}, providerCode={}",
|
||||
request == null ? null : request.getId(),
|
||||
request == null ? null : request.getProviderCode());
|
||||
if (request == null) {
|
||||
throw new IllegalArgumentException("服务商保存请求不能为空");
|
||||
}
|
||||
@@ -88,32 +86,33 @@ public class ModelProviderServiceImpl extends ServiceImpl<ModelProviderMapper, M
|
||||
if (entity == null) {
|
||||
throw new IllegalArgumentException("服务商不存在,ID: " + request.getId());
|
||||
}
|
||||
entity.setProviderCode(request.getProviderCode().trim());
|
||||
entity.setProviderName(request.getProviderName().trim());
|
||||
entity.setProviderType(request.getProviderType());
|
||||
entity.setProtocolType(request.getProtocolType());
|
||||
entity.setBaseUrl(request.getBaseUrl());
|
||||
entity.setAuthType(request.getAuthType());
|
||||
entity.setSecretRef(request.getSecretRef());
|
||||
entity.setApiKeyCipher(request.getApiKeyCipher());
|
||||
entity.setTimeoutMs(request.getTimeoutMs());
|
||||
entity.setPriority(request.getPriority());
|
||||
ModelProvider requestEntity = modelProviderFactory.toEntity(request);
|
||||
entity.setProviderCode(requestEntity.getProviderCode());
|
||||
entity.setProviderName(requestEntity.getProviderName());
|
||||
entity.setProviderType(requestEntity.getProviderType());
|
||||
entity.setProtocolType(requestEntity.getProtocolType());
|
||||
entity.setBaseUrl(requestEntity.getBaseUrl());
|
||||
entity.setAuthType(requestEntity.getAuthType());
|
||||
entity.setSecretRef(requestEntity.getSecretRef());
|
||||
entity.setApiKeyCipher(requestEntity.getApiKeyCipher());
|
||||
entity.setTimeoutMs(requestEntity.getTimeoutMs());
|
||||
entity.setPriority(requestEntity.getPriority());
|
||||
entity.setEnabled(request.getEnabled() == null ? Boolean.TRUE : request.getEnabled());
|
||||
entity.setRemark(request.getRemark());
|
||||
entity.setRemark(requestEntity.getRemark());
|
||||
if (!StringUtils.hasText(entity.getHealthStatus())) {
|
||||
entity.setHealthStatus(ModelHealthStatusEnum.UNKNOWN.name());
|
||||
}
|
||||
return request.getId() == null ? save(entity) : updateById(entity);
|
||||
boolean result = request.getId() == null ? save(entity) : updateById(entity);
|
||||
log.info("保存模型服务商结束,id={}, providerCode={}, result={}", entity.getId(), entity.getProviderCode(), result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 主动健康检查并回写健康状态与检查时间。
|
||||
*/
|
||||
@Override
|
||||
/**
|
||||
* 方法 checkHealth,用于执行业务逻辑处理。
|
||||
*/
|
||||
public boolean checkHealth(Long id) {
|
||||
log.info("模型服务商健康检查开始,id={}", id);
|
||||
ModelProvider provider = getById(id);
|
||||
if (provider == null) {
|
||||
throw new IllegalArgumentException("服务商不存在,ID: " + id);
|
||||
@@ -121,10 +120,9 @@ public class ModelProviderServiceImpl extends ServiceImpl<ModelProviderMapper, M
|
||||
boolean healthy = openAiCompatibleModelClient.health(provider);
|
||||
provider.setHealthStatus(healthy ? ModelHealthStatusEnum.HEALTHY.name() : ModelHealthStatusEnum.UNHEALTHY.name());
|
||||
provider.setLastHealthCheckTime(new Date());
|
||||
/**
|
||||
* 方法 updateById,用于定义接口能力契约。
|
||||
*/
|
||||
return updateById(provider);
|
||||
boolean result = updateById(provider);
|
||||
log.info("模型服务商健康检查结束,id={}, healthStatus={}, result={}", id, provider.getHealthStatus(), result);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,44 +4,46 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.bruce.modelprovider.dto.request.ModelRouteRuleSaveRequest;
|
||||
import com.bruce.modelprovider.dto.response.ModelRouteRuleResponse;
|
||||
import com.bruce.modelprovider.entity.ModelRouteRule;
|
||||
import com.bruce.modelprovider.factory.ModelRouteRuleFactory;
|
||||
import com.bruce.modelprovider.mapper.ModelRouteRuleMapper;
|
||||
import com.bruce.modelprovider.service.IModelRouteRuleService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
/**
|
||||
* 该类属于模型平台模块,用于承载对应分层职责。
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public class ModelRouteRuleServiceImpl extends ServiceImpl<ModelRouteRuleMapper, ModelRouteRule>
|
||||
implements IModelRouteRuleService {
|
||||
|
||||
private final ModelRouteRuleFactory modelRouteRuleFactory;
|
||||
|
||||
@Override
|
||||
/**
|
||||
* 方法 listResponses,用于执行业务逻辑处理。
|
||||
*/
|
||||
public List<ModelRouteRuleResponse> listResponses() {
|
||||
/**
|
||||
* 方法 list,用于定义接口能力契约。
|
||||
*/
|
||||
return list().stream().map(ModelRouteRuleResponse::fromEntity).toList();
|
||||
log.info("查询模型路由规则列表开始");
|
||||
List<ModelRouteRuleResponse> responses = modelRouteRuleFactory.toResponses(list());
|
||||
log.info("查询模型路由规则列表结束,count={}", responses.size());
|
||||
return responses;
|
||||
}
|
||||
|
||||
@Override
|
||||
/**
|
||||
* 方法 getResponseById,用于执行业务逻辑处理。
|
||||
*/
|
||||
public ModelRouteRuleResponse getResponseById(Long id) {
|
||||
return ModelRouteRuleResponse.fromEntity(getById(id));
|
||||
log.info("查询模型路由规则详情开始,id={}", id);
|
||||
ModelRouteRuleResponse response = modelRouteRuleFactory.toResponse(getById(id));
|
||||
log.info("查询模型路由规则详情结束,id={}, found={}", id, response != null);
|
||||
return response;
|
||||
}
|
||||
|
||||
@Override
|
||||
/**
|
||||
* 方法 saveOrUpdate,用于执行业务逻辑处理。
|
||||
*/
|
||||
public boolean saveOrUpdate(ModelRouteRuleSaveRequest request) {
|
||||
log.info("保存模型路由规则开始,id={}, routeCode={}, taskType={}",
|
||||
request == null ? null : request.getId(),
|
||||
request == null ? null : request.getRouteCode(),
|
||||
request == null ? null : request.getTaskType());
|
||||
if (request == null || !StringUtils.hasText(request.getRouteCode())) {
|
||||
throw new IllegalArgumentException("路由编码不能为空");
|
||||
}
|
||||
@@ -56,18 +58,21 @@ public class ModelRouteRuleServiceImpl extends ServiceImpl<ModelRouteRuleMapper,
|
||||
if (entity == null) {
|
||||
throw new IllegalArgumentException("路由不存在,ID: " + request.getId());
|
||||
}
|
||||
entity.setRouteCode(request.getRouteCode().trim());
|
||||
entity.setRouteName(request.getRouteName());
|
||||
entity.setTaskType(request.getTaskType());
|
||||
entity.setMatchScope(request.getMatchScope());
|
||||
entity.setScopeId(request.getScopeId());
|
||||
entity.setPrimaryModelId(request.getPrimaryModelId());
|
||||
entity.setFallbackModelIdsJson(request.getFallbackModelIdsJson());
|
||||
entity.setRouteStrategy(request.getRouteStrategy());
|
||||
entity.setMaxLatencyMs(request.getMaxLatencyMs());
|
||||
ModelRouteRule requestEntity = modelRouteRuleFactory.toEntity(request);
|
||||
entity.setRouteCode(requestEntity.getRouteCode());
|
||||
entity.setRouteName(requestEntity.getRouteName());
|
||||
entity.setTaskType(requestEntity.getTaskType());
|
||||
entity.setMatchScope(requestEntity.getMatchScope());
|
||||
entity.setScopeId(requestEntity.getScopeId());
|
||||
entity.setPrimaryModelId(requestEntity.getPrimaryModelId());
|
||||
entity.setFallbackModelIdsJson(requestEntity.getFallbackModelIdsJson());
|
||||
entity.setRouteStrategy(requestEntity.getRouteStrategy());
|
||||
entity.setMaxLatencyMs(requestEntity.getMaxLatencyMs());
|
||||
entity.setEnabled(request.getEnabled() == null ? Boolean.TRUE : request.getEnabled());
|
||||
entity.setRemark(request.getRemark());
|
||||
return request.getId() == null ? save(entity) : updateById(entity);
|
||||
entity.setRemark(requestEntity.getRemark());
|
||||
boolean result = request.getId() == null ? save(entity) : updateById(entity);
|
||||
log.info("保存模型路由规则结束,id={}, routeCode={}, result={}", entity.getId(), entity.getRouteCode(), result);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
package com.bruce.modelprovider.service.impl;
|
||||
|
||||
import com.bruce.modelprovider.dto.request.ModelCallLogQueryRequest;
|
||||
import com.bruce.modelprovider.dto.response.ModelCallLogResponse;
|
||||
import com.bruce.modelprovider.dto.response.ModelConfigResponse;
|
||||
import com.bruce.modelprovider.dto.response.ModelProviderResponse;
|
||||
import com.bruce.modelprovider.dto.response.ModelRouteRuleResponse;
|
||||
import com.bruce.modelprovider.service.IModelCallLogService;
|
||||
import com.bruce.modelprovider.service.IModelConfigService;
|
||||
import com.bruce.modelprovider.service.IModelProviderService;
|
||||
import com.bruce.modelprovider.service.IModelRouteRuleService;
|
||||
import com.bruce.modelprovider.service.IModelWorkspaceService;
|
||||
import com.bruce.modelprovider.vo.ModelWorkspaceFailureVO;
|
||||
import com.bruce.modelprovider.vo.ModelWorkspaceVO;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 模型工作台聚合服务实现。
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class ModelWorkspaceServiceImpl implements IModelWorkspaceService {
|
||||
|
||||
private static final String HEALTHY = "HEALTHY";
|
||||
private static final String UNHEALTHY = "UNHEALTHY";
|
||||
private static final String FAILED = "FAILED";
|
||||
|
||||
private final IModelProviderService modelProviderService;
|
||||
private final IModelConfigService modelConfigService;
|
||||
private final IModelRouteRuleService modelRouteRuleService;
|
||||
private final IModelCallLogService modelCallLogService;
|
||||
|
||||
@Override
|
||||
public ModelWorkspaceVO getWorkspace() {
|
||||
log.info("模型工作台查询开始");
|
||||
List<ModelProviderResponse> providers = modelProviderService.listResponses();
|
||||
List<ModelConfigResponse> models = modelConfigService.listResponses();
|
||||
List<ModelRouteRuleResponse> routes = modelRouteRuleService.listResponses();
|
||||
List<ModelCallLogResponse> logs = modelCallLogService.query(new ModelCallLogQueryRequest());
|
||||
|
||||
List<ModelCallLogResponse> failedLogs = logs.stream()
|
||||
.filter(log -> FAILED.equals(log.getStatus()))
|
||||
.limit(5)
|
||||
.toList();
|
||||
|
||||
ModelWorkspaceVO workspace = new ModelWorkspaceVO();
|
||||
workspace.setProviderCount(providers.size());
|
||||
workspace.setHealthyProviderCount((int) providers.stream().filter(provider -> HEALTHY.equals(provider.getHealthStatus())).count());
|
||||
workspace.setUnhealthyProviderCount((int) providers.stream().filter(provider -> UNHEALTHY.equals(provider.getHealthStatus())).count());
|
||||
workspace.setModelCount(models.size());
|
||||
workspace.setEnabledModelCount((int) models.stream().filter(model -> Boolean.TRUE.equals(model.getEnabled())).count());
|
||||
workspace.setRouteRuleCount(routes.size());
|
||||
workspace.setEnabledRouteRuleCount((int) routes.stream().filter(route -> Boolean.TRUE.equals(route.getEnabled())).count());
|
||||
workspace.setRecentFailedCallCount(failedLogs.size());
|
||||
workspace.setProviders(providers);
|
||||
workspace.setModels(models);
|
||||
workspace.setRoutes(routes);
|
||||
workspace.setFailedCallSummaries(failedLogs.stream().map(this::toFailureVO).toList());
|
||||
log.info("模型工作台查询结束,providerCount={}, modelCount={}, routeRuleCount={}, failedCallCount={}",
|
||||
workspace.getProviderCount(), workspace.getModelCount(), workspace.getRouteRuleCount(), workspace.getRecentFailedCallCount());
|
||||
return workspace;
|
||||
}
|
||||
|
||||
private ModelWorkspaceFailureVO toFailureVO(ModelCallLogResponse log) {
|
||||
ModelWorkspaceFailureVO vo = new ModelWorkspaceFailureVO();
|
||||
vo.setRequestId(log.getRequestId());
|
||||
vo.setStatus(log.getStatus());
|
||||
vo.setErrorCode(log.getErrorCode());
|
||||
vo.setErrorMessage(log.getErrorMessage());
|
||||
return vo;
|
||||
}
|
||||
}
|
||||
@@ -9,31 +9,28 @@ import com.bruce.modelprovider.mapper.RagStoreModelConfigMapper;
|
||||
import com.bruce.modelprovider.service.IModelConfigService;
|
||||
import com.bruce.modelprovider.service.IRagStoreModelConfigService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
/**
|
||||
* 该类属于模型平台模块,用于承载对应分层职责。
|
||||
*/
|
||||
public class RagStoreModelConfigServiceImpl extends ServiceImpl<RagStoreModelConfigMapper, RagStoreModelConfig>
|
||||
implements IRagStoreModelConfigService {
|
||||
|
||||
private final IModelConfigService modelConfigService;
|
||||
|
||||
@Override
|
||||
/**
|
||||
* 方法 getByStoreId,用于执行业务逻辑处理。
|
||||
*/
|
||||
public RagStoreModelConfigResponse getByStoreId(Long storeId) {
|
||||
log.info("查询知识库模型配置开始,storeId={}", storeId);
|
||||
return RagStoreModelConfigResponse.fromEntity(getActiveEntity(storeId));
|
||||
}
|
||||
|
||||
@Override
|
||||
/**
|
||||
* 方法 saveOrUpdate,用于执行业务逻辑处理。
|
||||
*/
|
||||
public boolean saveOrUpdate(RagStoreModelConfigSaveRequest request) {
|
||||
log.info("保存知识库模型配置开始,storeId={}, embeddingModelId={}",
|
||||
request == null ? null : request.getStoreId(),
|
||||
request == null ? null : request.getEmbeddingModelId());
|
||||
if (request == null || request.getStoreId() == null) {
|
||||
throw new IllegalArgumentException("知识库模型配置请求不能为空");
|
||||
}
|
||||
@@ -57,26 +54,23 @@ public class RagStoreModelConfigServiceImpl extends ServiceImpl<RagStoreModelCon
|
||||
entity.setRemark(request.getRemark());
|
||||
if (current == null) {
|
||||
entity.setIndexVersion(1);
|
||||
/**
|
||||
* 方法 save,用于定义接口能力契约。
|
||||
*/
|
||||
return save(entity);
|
||||
boolean result = save(entity);
|
||||
log.info("保存知识库模型配置结束,storeId={}, indexVersion={}, result={}",
|
||||
entity.getStoreId(), entity.getIndexVersion(), result);
|
||||
return result;
|
||||
}
|
||||
boolean changed = !current.getEmbeddingModelId().equals(request.getEmbeddingModelId())
|
||||
|| !current.getEmbeddingDimension().equals(request.getEmbeddingDimension());
|
||||
if (changed) {
|
||||
entity.setIndexVersion(current.getIndexVersion() == null ? 2 : current.getIndexVersion() + 1);
|
||||
}
|
||||
/**
|
||||
* 方法 updateById,用于定义接口能力契约。
|
||||
*/
|
||||
return updateById(entity);
|
||||
boolean result = updateById(entity);
|
||||
log.info("保存知识库模型配置结束,storeId={}, indexVersion={}, result={}",
|
||||
entity.getStoreId(), entity.getIndexVersion(), result);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
/**
|
||||
* 方法 getActiveEntity,用于执行业务逻辑处理。
|
||||
*/
|
||||
public RagStoreModelConfig getActiveEntity(Long storeId) {
|
||||
return lambdaQuery().eq(RagStoreModelConfig::getStoreId, storeId)
|
||||
.eq(RagStoreModelConfig::getActive, true)
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.bruce.modelprovider.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 最近失败调用摘要。
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "模型调用失败摘要")
|
||||
public class ModelWorkspaceFailureVO {
|
||||
|
||||
@Schema(description = "请求ID")
|
||||
private String requestId;
|
||||
|
||||
@Schema(description = "调用状态")
|
||||
private String status;
|
||||
|
||||
@Schema(description = "错误码")
|
||||
private String errorCode;
|
||||
|
||||
@Schema(description = "错误摘要")
|
||||
private String errorMessage;
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package com.bruce.modelprovider.vo;
|
||||
|
||||
import com.bruce.modelprovider.dto.response.ModelConfigResponse;
|
||||
import com.bruce.modelprovider.dto.response.ModelProviderResponse;
|
||||
import com.bruce.modelprovider.dto.response.ModelRouteRuleResponse;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 模型工作台聚合视图。
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "模型与路由工作台聚合视图")
|
||||
public class ModelWorkspaceVO {
|
||||
|
||||
@Schema(description = "服务商总数")
|
||||
private Integer providerCount;
|
||||
|
||||
@Schema(description = "健康服务商数量")
|
||||
private Integer healthyProviderCount;
|
||||
|
||||
@Schema(description = "异常服务商数量")
|
||||
private Integer unhealthyProviderCount;
|
||||
|
||||
@Schema(description = "模型总数")
|
||||
private Integer modelCount;
|
||||
|
||||
@Schema(description = "启用模型数量")
|
||||
private Integer enabledModelCount;
|
||||
|
||||
@Schema(description = "路由规则总数")
|
||||
private Integer routeRuleCount;
|
||||
|
||||
@Schema(description = "启用路由规则数量")
|
||||
private Integer enabledRouteRuleCount;
|
||||
|
||||
@Schema(description = "最近失败调用数量")
|
||||
private Integer recentFailedCallCount;
|
||||
|
||||
@Schema(description = "服务商列表")
|
||||
private List<ModelProviderResponse> providers = new ArrayList<>();
|
||||
|
||||
@Schema(description = "模型列表")
|
||||
private List<ModelConfigResponse> models = new ArrayList<>();
|
||||
|
||||
@Schema(description = "路由规则列表")
|
||||
private List<ModelRouteRuleResponse> routes = new ArrayList<>();
|
||||
|
||||
@Schema(description = "最近失败调用摘要")
|
||||
private List<ModelWorkspaceFailureVO> failedCallSummaries = new ArrayList<>();
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
package com.bruce.modelprovider.factory;
|
||||
|
||||
import com.bruce.modelprovider.dto.request.ModelConfigSaveRequest;
|
||||
import com.bruce.modelprovider.dto.request.ModelProviderSaveRequest;
|
||||
import com.bruce.modelprovider.dto.request.ModelRouteRuleSaveRequest;
|
||||
import com.bruce.modelprovider.dto.response.ModelCallLogResponse;
|
||||
import com.bruce.modelprovider.dto.response.ModelConfigResponse;
|
||||
import com.bruce.modelprovider.dto.response.ModelProviderResponse;
|
||||
import com.bruce.modelprovider.dto.response.ModelRouteRuleResponse;
|
||||
import com.bruce.modelprovider.entity.ModelCallLog;
|
||||
import com.bruce.modelprovider.entity.ModelConfig;
|
||||
import com.bruce.modelprovider.entity.ModelProvider;
|
||||
import com.bruce.modelprovider.entity.ModelRouteRule;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class ModelProviderFactoryTests {
|
||||
|
||||
private final ModelProviderFactory modelProviderFactory = new ModelProviderFactory();
|
||||
private final ModelConfigFactory modelConfigFactory = new ModelConfigFactory();
|
||||
private final ModelRouteRuleFactory modelRouteRuleFactory = new ModelRouteRuleFactory();
|
||||
private final ModelCallLogFactory modelCallLogFactory = new ModelCallLogFactory();
|
||||
|
||||
@Test
|
||||
void modelProviderFactoryShouldTrimRequestAndHideCipherInResponse() {
|
||||
ModelProviderSaveRequest request = new ModelProviderSaveRequest();
|
||||
request.setId(1L);
|
||||
request.setProviderCode(" OPENAI_MAIN ");
|
||||
request.setProviderName(" OpenAI 主服务 ");
|
||||
request.setProviderType(" OPENAI ");
|
||||
request.setProtocolType(" OPENAI_COMPATIBLE ");
|
||||
request.setBaseUrl(" https://api.openai.com/v1 ");
|
||||
request.setAuthType(" BEARER_TOKEN ");
|
||||
request.setSecretRef(" OPENAI_KEY ");
|
||||
request.setTimeoutMs(60000);
|
||||
request.setPriority(100);
|
||||
request.setEnabled(true);
|
||||
request.setRemark(" 主路由服务商 ");
|
||||
|
||||
ModelProvider entity = modelProviderFactory.toEntity(request);
|
||||
|
||||
assertNotNull(entity);
|
||||
assertEquals("OPENAI_MAIN", entity.getProviderCode());
|
||||
assertEquals("OpenAI 主服务", entity.getProviderName());
|
||||
assertEquals("OPENAI", entity.getProviderType());
|
||||
assertEquals("OPENAI_COMPATIBLE", entity.getProtocolType());
|
||||
assertEquals("https://api.openai.com/v1", entity.getBaseUrl());
|
||||
assertEquals("BEARER_TOKEN", entity.getAuthType());
|
||||
assertEquals("OPENAI_KEY", entity.getSecretRef());
|
||||
assertEquals("主路由服务商", entity.getRemark());
|
||||
|
||||
ModelProviderResponse response = modelProviderFactory.toResponse(entity);
|
||||
assertFalse(Boolean.TRUE.equals(response.getHasApiKey()));
|
||||
|
||||
entity.setApiKeyCipher("cipher");
|
||||
response = modelProviderFactory.toResponse(entity);
|
||||
assertTrue(Boolean.TRUE.equals(response.getHasApiKey()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void modelConfigFactoryShouldMapFullConfigFields() {
|
||||
ModelConfigSaveRequest request = new ModelConfigSaveRequest();
|
||||
request.setId(2L);
|
||||
request.setProviderId(1L);
|
||||
request.setModelCode(" EMB_1024 ");
|
||||
request.setModelName(" text-embedding-3-large ");
|
||||
request.setUpstreamModel(" text-embedding-3-large ");
|
||||
request.setModelType(" EMBEDDING ");
|
||||
request.setEmbeddingDimension(1024);
|
||||
request.setLocalModel(false);
|
||||
request.setDefaultModel(true);
|
||||
request.setOptionsJson(" {\"dimensions\":1024} ");
|
||||
request.setEnabled(true);
|
||||
request.setRemark(" 默认向量模型 ");
|
||||
|
||||
ModelConfig entity = modelConfigFactory.toEntity(request);
|
||||
assertEquals("EMB_1024", entity.getModelCode());
|
||||
assertEquals("text-embedding-3-large", entity.getModelName());
|
||||
assertEquals("text-embedding-3-large", entity.getUpstreamModel());
|
||||
assertEquals("EMBEDDING", entity.getModelType());
|
||||
assertEquals("{\"dimensions\":1024}", entity.getOptionsJson());
|
||||
assertEquals("默认向量模型", entity.getRemark());
|
||||
|
||||
ModelConfigResponse response = modelConfigFactory.toResponse(entity);
|
||||
assertEquals(1024, response.getEmbeddingDimension());
|
||||
assertTrue(Boolean.TRUE.equals(response.getDefaultModel()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void modelRouteRuleFactoryShouldMapRouteFields() {
|
||||
ModelRouteRuleSaveRequest request = new ModelRouteRuleSaveRequest();
|
||||
request.setId(3L);
|
||||
request.setRouteCode(" RAG_GLOBAL ");
|
||||
request.setRouteName(" 全局检索路由 ");
|
||||
request.setTaskType(" RAG_RETRIEVAL ");
|
||||
request.setMatchScope(" GLOBAL ");
|
||||
request.setScopeId(1001L);
|
||||
request.setPrimaryModelId(2L);
|
||||
request.setFallbackModelIdsJson(" [3,4] ");
|
||||
request.setRouteStrategy(" MANUAL ");
|
||||
request.setMaxLatencyMs(3000);
|
||||
request.setEnabled(true);
|
||||
request.setRemark(" 全局默认规则 ");
|
||||
|
||||
ModelRouteRule entity = modelRouteRuleFactory.toEntity(request);
|
||||
assertEquals("RAG_GLOBAL", entity.getRouteCode());
|
||||
assertEquals("全局检索路由", entity.getRouteName());
|
||||
assertEquals("RAG_RETRIEVAL", entity.getTaskType());
|
||||
assertEquals("[3,4]", entity.getFallbackModelIdsJson());
|
||||
assertEquals("全局默认规则", entity.getRemark());
|
||||
|
||||
ModelRouteRuleResponse response = modelRouteRuleFactory.toResponse(entity);
|
||||
assertEquals(2L, response.getPrimaryModelId());
|
||||
assertEquals("MANUAL", response.getRouteStrategy());
|
||||
}
|
||||
|
||||
@Test
|
||||
void modelCallLogFactoryShouldExposeReadonlyView() {
|
||||
ModelCallLog entity = new ModelCallLog();
|
||||
entity.setId(10L);
|
||||
entity.setRequestId("req-001");
|
||||
entity.setProviderId(1L);
|
||||
entity.setModelId(2L);
|
||||
entity.setTaskType("RAG_EMBEDDING");
|
||||
entity.setBizType("RAG_DOCUMENT_INDEX");
|
||||
entity.setBizId("doc-1");
|
||||
entity.setCallType("EMBEDDING");
|
||||
entity.setStatus("FAILED");
|
||||
entity.setDurationMs(2200);
|
||||
entity.setErrorCode("TIMEOUT");
|
||||
entity.setErrorMessage("上游超时");
|
||||
entity.setEstimatedCost(new BigDecimal("0.00250000"));
|
||||
|
||||
ModelCallLogResponse response = modelCallLogFactory.toResponse(entity);
|
||||
|
||||
assertNotNull(response);
|
||||
assertEquals("req-001", response.getRequestId());
|
||||
assertEquals("RAG_EMBEDDING", response.getTaskType());
|
||||
assertEquals("RAG_DOCUMENT_INDEX", response.getBizType());
|
||||
assertEquals("FAILED", response.getStatus());
|
||||
assertEquals("TIMEOUT", response.getErrorCode());
|
||||
assertEquals("上游超时", response.getErrorMessage());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
package com.bruce.modelprovider.workspace;
|
||||
|
||||
import com.bruce.modelprovider.dto.response.ModelCallLogResponse;
|
||||
import com.bruce.modelprovider.dto.response.ModelConfigResponse;
|
||||
import com.bruce.modelprovider.dto.response.ModelProviderResponse;
|
||||
import com.bruce.modelprovider.dto.response.ModelRouteRuleResponse;
|
||||
import com.bruce.modelprovider.service.IModelCallLogService;
|
||||
import com.bruce.modelprovider.service.IModelConfigService;
|
||||
import com.bruce.modelprovider.service.IModelProviderService;
|
||||
import com.bruce.modelprovider.service.IModelRouteRuleService;
|
||||
import com.bruce.modelprovider.service.impl.ModelWorkspaceServiceImpl;
|
||||
import com.bruce.modelprovider.vo.ModelWorkspaceVO;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class ModelWorkspaceServiceTests {
|
||||
|
||||
@Mock
|
||||
private IModelProviderService modelProviderService;
|
||||
|
||||
@Mock
|
||||
private IModelConfigService modelConfigService;
|
||||
|
||||
@Mock
|
||||
private IModelRouteRuleService modelRouteRuleService;
|
||||
|
||||
@Mock
|
||||
private IModelCallLogService modelCallLogService;
|
||||
|
||||
@InjectMocks
|
||||
private ModelWorkspaceServiceImpl modelWorkspaceService;
|
||||
|
||||
@Test
|
||||
void getWorkspaceShouldAggregateHealthRoutesAndFailureSummary() {
|
||||
when(modelProviderService.listResponses()).thenReturn(List.of(
|
||||
provider(1L, "OPENAI_MAIN", "HEALTHY", true),
|
||||
provider(2L, "LOCAL_OLLAMA", "UNHEALTHY", true),
|
||||
provider(3L, "TEST_PROVIDER", "UNKNOWN", false)
|
||||
));
|
||||
when(modelConfigService.listResponses()).thenReturn(List.of(
|
||||
model(11L, 1L, "CHAT_MAIN", "CHAT", true),
|
||||
model(12L, 1L, "EMB_1024", "EMBEDDING", true),
|
||||
model(13L, 2L, "CHAT_FALLBACK", "CHAT", false)
|
||||
));
|
||||
when(modelRouteRuleService.listResponses()).thenReturn(List.of(
|
||||
route(21L, "RAG_GLOBAL", "RAG_RETRIEVAL", 11L, "[13]", true),
|
||||
route(22L, "CHAT_GLOBAL", "AGENT_CHAT", 11L, "[]", false)
|
||||
));
|
||||
when(modelCallLogService.query(org.mockito.ArgumentMatchers.any())).thenReturn(List.of(
|
||||
log("req-001", "FAILED", "TIMEOUT", "上游超时"),
|
||||
log("req-002", "FAILED", "AUTH_ERROR", "鉴权失败"),
|
||||
log("req-003", "SUCCESS", null, null)
|
||||
));
|
||||
|
||||
ModelWorkspaceVO workspace = modelWorkspaceService.getWorkspace();
|
||||
|
||||
assertNotNull(workspace);
|
||||
assertEquals(3, workspace.getProviderCount());
|
||||
assertEquals(1, workspace.getHealthyProviderCount());
|
||||
assertEquals(1, workspace.getUnhealthyProviderCount());
|
||||
assertEquals(3, workspace.getModelCount());
|
||||
assertEquals(2, workspace.getEnabledModelCount());
|
||||
assertEquals(2, workspace.getRouteRuleCount());
|
||||
assertEquals(1, workspace.getEnabledRouteRuleCount());
|
||||
assertEquals(2, workspace.getRecentFailedCallCount());
|
||||
assertEquals(2, workspace.getFailedCallSummaries().size());
|
||||
assertEquals(3, workspace.getProviders().size());
|
||||
assertEquals(3, workspace.getModels().size());
|
||||
assertEquals(2, workspace.getRoutes().size());
|
||||
}
|
||||
|
||||
private ModelProviderResponse provider(Long id, String code, String healthStatus, boolean enabled) {
|
||||
ModelProviderResponse response = new ModelProviderResponse();
|
||||
response.setId(id);
|
||||
response.setProviderCode(code);
|
||||
response.setProviderName(code);
|
||||
response.setHealthStatus(healthStatus);
|
||||
response.setEnabled(enabled);
|
||||
return response;
|
||||
}
|
||||
|
||||
private ModelConfigResponse model(Long id, Long providerId, String code, String modelType, boolean enabled) {
|
||||
ModelConfigResponse response = new ModelConfigResponse();
|
||||
response.setId(id);
|
||||
response.setProviderId(providerId);
|
||||
response.setModelCode(code);
|
||||
response.setModelName(code);
|
||||
response.setModelType(modelType);
|
||||
response.setEnabled(enabled);
|
||||
return response;
|
||||
}
|
||||
|
||||
private ModelRouteRuleResponse route(Long id, String code, String taskType, Long primaryModelId, String fallbackJson, boolean enabled) {
|
||||
ModelRouteRuleResponse response = new ModelRouteRuleResponse();
|
||||
response.setId(id);
|
||||
response.setRouteCode(code);
|
||||
response.setRouteName(code);
|
||||
response.setTaskType(taskType);
|
||||
response.setPrimaryModelId(primaryModelId);
|
||||
response.setFallbackModelIdsJson(fallbackJson);
|
||||
response.setEnabled(enabled);
|
||||
return response;
|
||||
}
|
||||
|
||||
private ModelCallLogResponse log(String requestId, String status, String errorCode, String errorMessage) {
|
||||
ModelCallLogResponse response = new ModelCallLogResponse();
|
||||
response.setRequestId(requestId);
|
||||
response.setStatus(status);
|
||||
response.setErrorCode(errorCode);
|
||||
response.setErrorMessage(errorMessage);
|
||||
return response;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user