|
@@ -1,9 +1,32 @@
|
|
|
package com.pavis.admin.aigc.service.impl;
|
|
|
|
|
|
+import cn.hutool.core.bean.BeanUtil;
|
|
|
+import cn.hutool.core.util.ObjectUtil;
|
|
|
+import cn.hutool.core.util.StrUtil;
|
|
|
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
|
|
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
|
|
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
|
|
+import com.pavis.admin.aigc.constant.ModelConst;
|
|
|
+import com.pavis.admin.aigc.core.llm.provider.build.ModelBuildHandler;
|
|
|
+import com.pavis.admin.aigc.enums.ModelTypeEnum;
|
|
|
+import com.pavis.admin.aigc.mapper.ModelSecretMapper;
|
|
|
+import com.pavis.admin.aigc.model.entity.ModelSecretDO;
|
|
|
+import dev.langchain4j.model.chat.ChatLanguageModel;
|
|
|
+import dev.langchain4j.model.chat.StreamingChatLanguageModel;
|
|
|
+import dev.langchain4j.model.embedding.EmbeddingModel;
|
|
|
+import dev.langchain4j.model.image.ImageModel;
|
|
|
+import io.micrometer.core.instrument.binder.BaseUnits;
|
|
|
+import jakarta.annotation.PostConstruct;
|
|
|
import lombok.RequiredArgsConstructor;
|
|
|
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+import org.apache.poi.ss.formula.functions.T;
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
+import org.springframework.scheduling.annotation.Async;
|
|
|
import org.springframework.stereotype.Service;
|
|
|
|
|
|
+import top.continew.starter.extension.crud.model.query.SortQuery;
|
|
|
+import top.continew.starter.extension.crud.service.BaseService;
|
|
|
import top.continew.starter.extension.crud.service.BaseServiceImpl;
|
|
|
import com.pavis.admin.aigc.mapper.ModelMapper;
|
|
|
import com.pavis.admin.aigc.model.entity.ModelDO;
|
|
@@ -13,6 +36,12 @@ import com.pavis.admin.aigc.model.resp.ModelDetailResp;
|
|
|
import com.pavis.admin.aigc.model.resp.ModelResp;
|
|
|
import com.pavis.admin.aigc.service.ModelService;
|
|
|
|
|
|
+import java.util.ArrayList;
|
|
|
+import java.util.List;
|
|
|
+import java.util.Map;
|
|
|
+import java.util.Objects;
|
|
|
+import java.util.concurrent.ConcurrentHashMap;
|
|
|
+
|
|
|
/**
|
|
|
* AIGC模型配置业务实现
|
|
|
*
|
|
@@ -21,4 +50,255 @@ import com.pavis.admin.aigc.service.ModelService;
|
|
|
*/
|
|
|
@Service
|
|
|
@RequiredArgsConstructor
|
|
|
-public class ModelServiceImpl extends BaseServiceImpl<ModelMapper, ModelDO, ModelResp, ModelDetailResp, ModelQuery, ModelReq> implements ModelService {}
|
|
|
+@Slf4j
|
|
|
+public class ModelServiceImpl extends BaseServiceImpl<ModelMapper, ModelDO, ModelResp, ModelDetailResp, ModelQuery, ModelReq> implements ModelService {
|
|
|
+
|
|
|
+ private final ModelSecretMapper modelSecretMapper;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private List<ModelBuildHandler> modelBuildHandlers;
|
|
|
+
|
|
|
+ private final List<ModelResp> modelStore = new ArrayList<>();
|
|
|
+ private final Map<String, StreamingChatLanguageModel> streamingChatMap = new ConcurrentHashMap<>();
|
|
|
+ private final Map<String, ChatLanguageModel> chatLanguageMap = new ConcurrentHashMap<>();
|
|
|
+ private final Map<String, EmbeddingModel> embeddingModelMap = new ConcurrentHashMap<>();
|
|
|
+ private final Map<String, ImageModel> imageModelMap = new ConcurrentHashMap<>();
|
|
|
+
|
|
|
+ public void insModelAndSecret(ModelReq modelReq) {
|
|
|
+ // 1. 将modelReq部分字段cope到modelDO实体中,并进行insert
|
|
|
+ ModelDO modelDO = new ModelDO();
|
|
|
+ BeanUtil.copyProperties(modelReq, modelDO);
|
|
|
+ baseMapper.insert(modelDO);
|
|
|
+ // 2. 获取模型id,将模型表跟模型配置表关联起来,modelReq中已经带过来需要配置的配置数据
|
|
|
+ ModelSecretDO modelSecretDO = new ModelSecretDO();
|
|
|
+ modelSecretDO.setModelId(modelDO.getId());
|
|
|
+ BeanUtil.copyProperties(modelReq, modelSecretDO);
|
|
|
+ modelSecretMapper.insert(modelSecretDO);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public List<ModelResp> selModelAndSecrt() {
|
|
|
+ return baseMapper.selModelAndSecret();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public List<ModelResp> selModelAndSecrtList(ModelQuery modelQuery) {
|
|
|
+ return baseMapper.selModelAndSecretList(modelQuery.getType(), modelQuery.getProvider());
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void updateModelAndSecrt(ModelResp modelResp) {
|
|
|
+ ModelDO modelDO = new ModelDO();
|
|
|
+ BeanUtil.copyProperties(modelResp, modelDO);
|
|
|
+ baseMapper.updateById(modelDO);
|
|
|
+ ModelSecretDO modelSecretDO = new ModelSecretDO();
|
|
|
+ BeanUtil.copyProperties(modelResp, modelSecretDO);
|
|
|
+ modelSecretMapper.update(modelSecretDO, new QueryWrapper<ModelSecretDO>()
|
|
|
+ .eq("model_id", modelResp.getId()));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void delModelAndSecrt(List<Long> ids) {
|
|
|
+ for (Long id : ids) {
|
|
|
+ ModelDO modelDO = baseMapper.selectById(id);
|
|
|
+ if (modelDO != null) {
|
|
|
+ modelSecretMapper.delete(new QueryWrapper<ModelSecretDO>().eq("model_id", modelDO.getId()));
|
|
|
+ baseMapper.deleteById(id);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // @Override
|
|
|
+ // public List<AigcModel> getChatModels() {
|
|
|
+ // List<AigcModel> list = baseMapper.selectList(Wrappers.<AigcModel>lambdaQuery()
|
|
|
+ // .eq(AigcModel::getType, ModelTypeEnum.CHAT.name()));
|
|
|
+ // list.forEach(this::hide);
|
|
|
+ // return list;
|
|
|
+ // }
|
|
|
+
|
|
|
+ // @Override
|
|
|
+ // public List<AigcModel> getImageModels() {
|
|
|
+ // List<AigcModel> list = baseMapper.selectList(Wrappers.<AigcModel>lambdaQuery()
|
|
|
+ // .eq(AigcModel::getType, ModelTypeEnum.TEXT_IMAGE.name()));
|
|
|
+ // list.forEach(this::hide);
|
|
|
+ // return list;
|
|
|
+ // }
|
|
|
+
|
|
|
+ // @Override
|
|
|
+ // public List<AigcModel> getEmbeddingModels() {
|
|
|
+ // List<AigcModel> list = baseMapper.selectList(Wrappers.<AigcModel>lambdaQuery()
|
|
|
+ // .eq(AigcModel::getType, ModelTypeEnum.EMBEDDING.name()));
|
|
|
+ // list.forEach(this::hide);
|
|
|
+ // return list;
|
|
|
+ // }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public List<ModelResp> list(ModelResp data) {
|
|
|
+ List<ModelDO> list = this.list(Wrappers.<ModelDO>lambdaQuery()
|
|
|
+ .eq(StrUtil.isNotBlank(data.getType()), ModelDO::getType, data.getType())
|
|
|
+ .eq(StrUtil.isNotBlank(data.getProvider()), ModelDO::getProvider, data.getProvider()));
|
|
|
+ // copy实体
|
|
|
+ List<ModelResp> respList = BeanUtil.copyToList(list, ModelResp.class);
|
|
|
+ respList.forEach(this::hideResp);
|
|
|
+ return respList;
|
|
|
+ }
|
|
|
+
|
|
|
+ // @Override
|
|
|
+ // public Page<AigcModel> page(AigcModel data, QueryPage queryPage) {
|
|
|
+ // Page<AigcModel> page = new Page<>(queryPage.getPage(), queryPage.getLimit());
|
|
|
+ // Page<AigcModel> iPage = this.page(page, Wrappers.<AigcModel>lambdaQuery().eq(AigcModel::getProvider, data.getProvider()));
|
|
|
+ // iPage.getRecords().forEach(this::hide);
|
|
|
+ // return iPage;
|
|
|
+ // }
|
|
|
+
|
|
|
+ // @Override
|
|
|
+ // public AigcModel selectById(String id) {
|
|
|
+ // AigcModel model = this.getById(id);
|
|
|
+ // hide(model);
|
|
|
+ // return model;
|
|
|
+ // }
|
|
|
+
|
|
|
+ // private void hide(ModelDO model) {
|
|
|
+ // if (model == null || StrUtil.isBlank(model.getApiKey())) {
|
|
|
+ // return;
|
|
|
+ // }
|
|
|
+ // String key = StrUtil.hide(model.getApiKey(), 3, model.getApiKey().length() - 4);
|
|
|
+ // model.setApiKey(key);
|
|
|
+ //
|
|
|
+ // if (StrUtil.isBlank(model.getSecretKey())) {
|
|
|
+ // return;
|
|
|
+ // }
|
|
|
+ // String sec = StrUtil.hide(model.getSecretKey(), 3, model.getSecretKey().length() - 4);
|
|
|
+ // model.setSecretKey(sec);
|
|
|
+ // }
|
|
|
+ private void hideResp(ModelResp model) {
|
|
|
+ // 通过modelId获取相应的apikey和SecretKey
|
|
|
+ ModelSecretDO modelSecretDO = modelSecretMapper.selectOne(new QueryWrapper<ModelSecretDO>().eq("model_id", model.getModel()));
|
|
|
+ if (modelSecretDO == null || StrUtil.isBlank(modelSecretDO.getApiKey())) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ String key = StrUtil.hide(modelSecretDO.getApiKey(), 3, modelSecretDO.getApiKey().length() - 4);
|
|
|
+ model.setApiKey(key);
|
|
|
+
|
|
|
+ if (StrUtil.isBlank(modelSecretDO.getSecretKey())) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ String sec = StrUtil.hide(modelSecretDO.getSecretKey(), 3, modelSecretDO.getSecretKey().length() - 4);
|
|
|
+ model.setSecretKey(sec);
|
|
|
+ }
|
|
|
+
|
|
|
+ // LANGCHAT
|
|
|
+ @Async
|
|
|
+ @PostConstruct
|
|
|
+ public void init() {
|
|
|
+ modelStore.clear();
|
|
|
+ streamingChatMap.clear();
|
|
|
+ chatLanguageMap.clear();
|
|
|
+ embeddingModelMap.clear();
|
|
|
+ imageModelMap.clear();
|
|
|
+
|
|
|
+ List<ModelResp> list = baseMapper.selectByModel();
|
|
|
+ list.forEach(model -> {
|
|
|
+ if (Objects.equals(model.getBaseUrl(), "")) {
|
|
|
+ model.setBaseUrl(null);
|
|
|
+ }
|
|
|
+
|
|
|
+ chatHandler(model);
|
|
|
+ embeddingHandler(model);
|
|
|
+ imageHandler(model);
|
|
|
+ });
|
|
|
+
|
|
|
+ modelStore.forEach(i -> log.info("已成功注册模型:{} -- {}, 模型配置:{}", i.getProvider(), i.getType(), i));
|
|
|
+ }
|
|
|
+
|
|
|
+ private void chatHandler(ModelResp model) {
|
|
|
+ try {
|
|
|
+ String type = model.getType();
|
|
|
+ if (!ModelTypeEnum.CHAT.name().equals(type)) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ modelBuildHandlers.forEach(x -> {
|
|
|
+ StreamingChatLanguageModel streamingChatLanguageModel = x.buildStreamingChat(model);
|
|
|
+ if (ObjectUtil.isNotEmpty(streamingChatLanguageModel)) {
|
|
|
+ streamingChatMap.put(model.getId().toString(), streamingChatLanguageModel);
|
|
|
+ modelStore.add(model);
|
|
|
+ }
|
|
|
+
|
|
|
+ ChatLanguageModel languageModel = x.buildChatLanguageModel(model);
|
|
|
+ if (ObjectUtil.isNotEmpty(languageModel)) {
|
|
|
+ chatLanguageMap.put(model.getId() + ModelConst.TEXT_SUFFIX, languageModel);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("model 【 id: {} name: {}】streaming chat 配置报错", model.getId(), model.getName());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void embeddingHandler(ModelResp model) {
|
|
|
+ try {
|
|
|
+ String type = model.getType();
|
|
|
+ if (!ModelTypeEnum.EMBEDDING.name().equals(type)) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ modelBuildHandlers.forEach(x -> {
|
|
|
+ EmbeddingModel embeddingModel = x.buildEmbedding(model);
|
|
|
+ if (ObjectUtil.isNotEmpty(embeddingModel)) {
|
|
|
+ embeddingModelMap.put(model.getId().toString(), embeddingModel);
|
|
|
+ modelStore.add(model);
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("model 【id{} name{}】 embedding 配置报错", model.getId(), model.getName());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void imageHandler(ModelResp model) {
|
|
|
+ try {
|
|
|
+ String type = model.getType();
|
|
|
+ if (!ModelTypeEnum.TEXT_IMAGE.name().equals(type)) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ modelBuildHandlers.forEach(x -> {
|
|
|
+ ImageModel imageModel = x.buildImage(model);
|
|
|
+ if (ObjectUtil.isNotEmpty(imageModel)) {
|
|
|
+ imageModelMap.put(model.getId().toString(), imageModel);
|
|
|
+ modelStore.add(model);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("model 【id{} name{}】 image 配置报错", model.getId(), model.getName());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public StreamingChatLanguageModel getStreamingChatModel(String modelId) {
|
|
|
+ return streamingChatMap.get(modelId);
|
|
|
+ }
|
|
|
+
|
|
|
+ public boolean containsStreamingChatModel(String modelId) {
|
|
|
+ return streamingChatMap.containsKey(modelId);
|
|
|
+ }
|
|
|
+
|
|
|
+ public ChatLanguageModel getChatLanguageModel(String modelId) {
|
|
|
+ return chatLanguageMap.get(modelId + ModelConst.TEXT_SUFFIX);
|
|
|
+ }
|
|
|
+
|
|
|
+ public boolean containsChatLanguageModel(String modelId) {
|
|
|
+ return chatLanguageMap.containsKey(modelId + ModelConst.TEXT_SUFFIX);
|
|
|
+ }
|
|
|
+
|
|
|
+ public EmbeddingModel getEmbeddingModel(String modelId) {
|
|
|
+ return embeddingModelMap.get(modelId);
|
|
|
+ }
|
|
|
+
|
|
|
+ public boolean containsEmbeddingModel(String modelId) {
|
|
|
+ return embeddingModelMap.containsKey(modelId);
|
|
|
+ }
|
|
|
+
|
|
|
+ public ImageModel getImageModel(String modelId) {
|
|
|
+ return imageModelMap.get(modelId);
|
|
|
+ }
|
|
|
+
|
|
|
+ public boolean containsImageModel(String modelId) {
|
|
|
+ return imageModelMap.containsKey(modelId);
|
|
|
+ }
|
|
|
+}
|