浏览代码

对联调时出现的bug进行修复

zhangenzhi 2 月之前
父节点
当前提交
1c870ada10
共有 51 个文件被更改,包括 2857 次插入48 次删除
  1. 0 0
      logs/APP_NAME_IS_UNDEFINED.log
  2. 14 11
      pavis-module-aigc/pom.xml
  3. 3 1
      pavis-module-aigc/src/main/java/com/pavis/admin/aigc/common/model/req/ChatReq.java
  4. 47 0
      pavis-module-aigc/src/main/java/com/pavis/admin/aigc/core/llm/exception/AuthException.java
  5. 42 0
      pavis-module-aigc/src/main/java/com/pavis/admin/aigc/core/llm/exception/ServiceException.java
  6. 100 0
      pavis-module-aigc/src/main/java/com/pavis/admin/aigc/core/llm/provider/EmbeddingProvider.java
  7. 125 0
      pavis-module-aigc/src/main/java/com/pavis/admin/aigc/core/llm/provider/EmbeddingStoreFactory.java
  8. 71 0
      pavis-module-aigc/src/main/java/com/pavis/admin/aigc/core/llm/provider/KnowledgeStoreFactory.java
  9. 60 0
      pavis-module-aigc/src/main/java/com/pavis/admin/aigc/core/llm/provider/ModelProvider.java
  10. 171 0
      pavis-module-aigc/src/main/java/com/pavis/admin/aigc/core/llm/provider/ModelStoreFactory.java
  11. 35 0
      pavis-module-aigc/src/main/java/com/pavis/admin/aigc/core/llm/provider/ProviderListener.java
  12. 45 0
      pavis-module-aigc/src/main/java/com/pavis/admin/aigc/core/llm/provider/build/ModelBuildHandler.java
  13. 142 0
      pavis-module-aigc/src/main/java/com/pavis/admin/aigc/core/llm/provider/build/OllamaModelBuildHandler.java
  14. 184 0
      pavis-module-aigc/src/main/java/com/pavis/admin/aigc/core/llm/provider/build/OpenAIModelBuildHandler.java
  15. 149 0
      pavis-module-aigc/src/main/java/com/pavis/admin/aigc/core/llm/provider/build/QFanModelBuildHandler.java
  16. 137 0
      pavis-module-aigc/src/main/java/com/pavis/admin/aigc/core/llm/provider/build/QWenModelBuildHandler.java
  17. 175 0
      pavis-module-aigc/src/main/java/com/pavis/admin/aigc/core/llm/provider/build/ZhipuModelBuildHandler.java
  18. 32 0
      pavis-module-aigc/src/main/java/com/pavis/admin/aigc/core/llm/service/Agent.java
  19. 95 0
      pavis-module-aigc/src/main/java/com/pavis/admin/aigc/core/llm/service/impl/PersistentChatMemoryStore.java
  20. 13 1
      pavis-module-aigc/src/main/java/com/pavis/admin/aigc/mapper/AppKnowledgeMapper.java
  21. 9 1
      pavis-module-aigc/src/main/java/com/pavis/admin/aigc/mapper/EmbedStoreMapper.java
  22. 9 1
      pavis-module-aigc/src/main/java/com/pavis/admin/aigc/mapper/KnowledgeMapper.java
  23. 29 1
      pavis-module-aigc/src/main/java/com/pavis/admin/aigc/mapper/ModelMapper.java
  24. 5 0
      pavis-module-aigc/src/main/java/com/pavis/admin/aigc/model/entity/ModelSecretDO.java
  25. 11 0
      pavis-module-aigc/src/main/java/com/pavis/admin/aigc/model/req/AigcMessageReq.java
  26. 50 8
      pavis-module-aigc/src/main/java/com/pavis/admin/aigc/model/req/ModelReq.java
  27. 16 11
      pavis-module-aigc/src/main/java/com/pavis/admin/aigc/model/req/ModelSecretReq.java
  28. 15 0
      pavis-module-aigc/src/main/java/com/pavis/admin/aigc/model/resp/AigcAppResp.java
  29. 8 0
      pavis-module-aigc/src/main/java/com/pavis/admin/aigc/model/resp/EmbedStoreResp.java
  30. 6 0
      pavis-module-aigc/src/main/java/com/pavis/admin/aigc/model/resp/KnowledgeResp.java
  31. 3 0
      pavis-module-aigc/src/main/java/com/pavis/admin/aigc/model/resp/ModelDetailResp.java
  32. 17 0
      pavis-module-aigc/src/main/java/com/pavis/admin/aigc/model/resp/ModelResp.java
  33. 5 0
      pavis-module-aigc/src/main/java/com/pavis/admin/aigc/model/resp/ModelSecretDetailResp.java
  34. 5 0
      pavis-module-aigc/src/main/java/com/pavis/admin/aigc/model/resp/ModelSecretResp.java
  35. 25 1
      pavis-module-aigc/src/main/java/com/pavis/admin/aigc/service/AigcAppService.java
  36. 5 1
      pavis-module-aigc/src/main/java/com/pavis/admin/aigc/service/EmbedStoreService.java
  37. 20 1
      pavis-module-aigc/src/main/java/com/pavis/admin/aigc/service/KnowledgeService.java
  38. 3 1
      pavis-module-aigc/src/main/java/com/pavis/admin/aigc/service/ModelSecretService.java
  39. 48 1
      pavis-module-aigc/src/main/java/com/pavis/admin/aigc/service/ModelService.java
  40. 24 0
      pavis-module-aigc/src/main/java/com/pavis/admin/aigc/service/PvsChatService.java
  41. 208 1
      pavis-module-aigc/src/main/java/com/pavis/admin/aigc/service/impl/AigcAppServiceImpl.java
  42. 8 1
      pavis-module-aigc/src/main/java/com/pavis/admin/aigc/service/impl/EmbedStoreServiceImpl.java
  43. 84 1
      pavis-module-aigc/src/main/java/com/pavis/admin/aigc/service/impl/KnowledgeServiceImpl.java
  44. 8 1
      pavis-module-aigc/src/main/java/com/pavis/admin/aigc/service/impl/ModelSecretServiceImpl.java
  45. 281 1
      pavis-module-aigc/src/main/java/com/pavis/admin/aigc/service/impl/ModelServiceImpl.java
  46. 95 0
      pavis-module-aigc/src/main/java/com/pavis/admin/aigc/service/impl/PersistentChatMemoryStore.java
  47. 141 0
      pavis-module-aigc/src/main/java/com/pavis/admin/aigc/service/impl/PvsChatServiceImpl.java
  48. 15 0
      pavis-module-aigc/src/main/resources/ModelMapper.xml
  49. 29 1
      pavis-webapi/src/main/java/com/pavis/admin/controller/aigc/AigcAppController.java
  50. 33 1
      pavis-webapi/src/main/java/com/pavis/admin/controller/aigc/ModelController.java
  51. 2 1
      pavis-webapi/src/main/resources/db/changelog/mysql/main_table.sql

+ 0 - 0
logs/APP_NAME_IS_UNDEFINED.log


+ 14 - 11
pavis-module-aigc/pom.xml

@@ -12,8 +12,9 @@
     
     <properties>
         <spring-ai.version>1.0.0-M8</spring-ai.version>
-        <langchain4j-core.version>1.0.1</langchain4j-core.version>
+        <langchain4j-core.version>1.0.0-beta1</langchain4j-core.version>
         <langchain4j-community.version>1.0.1-beta6</langchain4j-community.version>
+        <langchain4j.version>1.0.0-beta1</langchain4j.version>
     </properties>
     
     <dependencies>
@@ -67,17 +68,19 @@
         <dependency>
             <groupId>dev.langchain4j</groupId>
             <artifactId>langchain4j-ollama</artifactId>
-            <version>${langchain4j-community.version}</version>
+<!--            <version>${langchain4j-community.version}</version>-->
+            <version>${langchain4j.version}</version>
+
         </dependency>
         <dependency>
             <groupId>dev.langchain4j</groupId>
             <artifactId>langchain4j-community-qianfan</artifactId>
-            <version>${langchain4j-community.version}</version>
+            <version>${langchain4j.version}</version>
         </dependency>
         <dependency>
             <groupId>dev.langchain4j</groupId>
             <artifactId>langchain4j-community-dashscope</artifactId>
-            <version>${langchain4j-community.version}</version>
+            <version>${langchain4j.version}</version>
             <exclusions>
                 <exclusion>
                     <groupId>org.slf4j</groupId>
@@ -88,32 +91,32 @@
         <dependency>
             <groupId>dev.langchain4j</groupId>
             <artifactId>langchain4j-community-zhipu-ai</artifactId>
-            <version>${langchain4j-community.version}</version>
+            <version>${langchain4j.version}</version>
         </dependency>
         <dependency>
             <groupId>dev.langchain4j</groupId>
             <artifactId>langchain4j-embeddings</artifactId>
-            <version>${langchain4j-community.version}</version>
+            <version>${langchain4j.version}</version>
         </dependency>
         <dependency>
             <groupId>dev.langchain4j</groupId>
             <artifactId>langchain4j-community-redis</artifactId>
-            <version>${langchain4j-community.version}</version>
+            <version>${langchain4j.version}</version>
         </dependency>
         <dependency>
             <groupId>dev.langchain4j</groupId>
             <artifactId>langchain4j-pgvector</artifactId>
-            <version>${langchain4j-community.version}</version>
+            <version>${langchain4j.version}</version>
         </dependency>
         <dependency>
             <groupId>dev.langchain4j</groupId>
             <artifactId>langchain4j-milvus</artifactId>
-            <version>${langchain4j-community.version}</version>
+            <version>${langchain4j.version}</version>
         </dependency>
         <dependency>
             <groupId>dev.langchain4j</groupId>
             <artifactId>langchain4j-embedding-store-filter-parser-sql</artifactId>
-            <version>${langchain4j-community.version}</version>
+            <version>${langchain4j.version}</version>
             <exclusions>
                 <exclusion>
                     <groupId>com.github.jsqlparser</groupId>
@@ -124,7 +127,7 @@
         <dependency>
             <groupId>dev.langchain4j</groupId>
             <artifactId>langchain4j-document-parser-apache-tika</artifactId>
-            <version>${langchain4j-community.version}</version>
+            <version>${langchain4j.version}</version>
         </dependency>
     </dependencies>
 </project>

+ 3 - 1
pavis-module-aigc/src/main/java/com/pavis/admin/aigc/common/model/req/ChatReq.java

@@ -34,7 +34,9 @@ public class ChatReq {
     private String docsName;
 
     private String knowledgeId;
-    private List<String> knowledgeIds = new ArrayList<>();
+    // private List<String> knowledgeIds = new ArrayList<>();
+
+    private List<Long> knowledgeIds = new ArrayList<>();
 
     private String docsId;
 

+ 47 - 0
pavis-module-aigc/src/main/java/com/pavis/admin/aigc/core/llm/exception/AuthException.java

@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved.
+ *
+ * Licensed under the GNU Affero General Public License, Version 3 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     https://www.gnu.org/licenses/agpl-3.0.html
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.pavis.admin.aigc.core.llm.exception;
+
+import lombok.Getter;
+import org.springframework.http.HttpStatus;
+
+/**
+ * @author tycoding
+ * @since 2024/1/2
+ */
+@Getter
+public class AuthException extends RuntimeException {
+
+    private static final long serialVersionUID = -1068765335343416833L;
+
+    private final int code;
+
+    public AuthException() {
+        super("没有操作权限");
+        this.code = HttpStatus.UNAUTHORIZED.value();
+    }
+
+    public AuthException(String message) {
+        super(message);
+        this.code = HttpStatus.INTERNAL_SERVER_ERROR.value();
+    }
+
+    public AuthException(int code, String message) {
+        super(message);
+        this.code = code;
+    }
+}

+ 42 - 0
pavis-module-aigc/src/main/java/com/pavis/admin/aigc/core/llm/exception/ServiceException.java

@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved.
+ *
+ * Licensed under the GNU Affero General Public License, Version 3 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     https://www.gnu.org/licenses/agpl-3.0.html
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.pavis.admin.aigc.core.llm.exception;
+
+import lombok.Getter;
+import org.springframework.http.HttpStatus;
+
+/**
+ * @author tycoding
+ * @since 2024/1/2
+ */
+@Getter
+public class ServiceException extends RuntimeException {
+
+    private static final long serialVersionUID = -1068765335343416833L;
+
+    private final int code;
+
+    public ServiceException(String message) {
+        super(message);
+        this.code = HttpStatus.INTERNAL_SERVER_ERROR.value();
+    }
+
+    public ServiceException(int code, String message) {
+        super(message);
+        this.code = code;
+    }
+}

+ 100 - 0
pavis-module-aigc/src/main/java/com/pavis/admin/aigc/core/llm/provider/EmbeddingProvider.java

@@ -0,0 +1,100 @@
+package com.pavis.admin.aigc.core.llm.provider;
+
+import com.pavis.admin.aigc.model.entity.KnowledgeDO;
+import com.pavis.admin.aigc.model.resp.KnowledgeResp;
+import dev.langchain4j.data.document.DocumentSplitter;
+import dev.langchain4j.data.document.splitter.DocumentSplitters;
+import dev.langchain4j.data.segment.TextSegment;
+import dev.langchain4j.model.embedding.EmbeddingModel;
+import dev.langchain4j.store.embedding.EmbeddingStore;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+import top.continew.starter.core.exception.BusinessException;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+
+/**
+ * @author tycoding
+ * @since 2024/3/8
+ */
+@Slf4j
+@Component
+@AllArgsConstructor
+public class EmbeddingProvider {
+
+    private final EmbeddingStoreFactory embeddingStoreFactory;
+    private final KnowledgeStoreFactory knowledgeStoreFactory;
+    private final ModelStoreFactory modelStoreFactory;
+
+    public static DocumentSplitter splitter() {
+        return DocumentSplitters.recursive(300, 20);
+    }
+
+    public EmbeddingModel getEmbeddingModel(List<Long> knowledgeIds) {
+        List<String> storeIds = new ArrayList<>();
+        knowledgeIds.forEach(id -> {
+            if (knowledgeStoreFactory.containsKnowledge(id)) {
+                KnowledgeResp data = knowledgeStoreFactory.getKnowledge(id);
+                if (data.getEmbedModelId() != null) {
+                    storeIds.add(data.getEmbedModelId().toString());
+                }
+            }
+        });
+        if (storeIds.isEmpty()) {
+            throw new BusinessException("知识库缺少Embedding Model配置,请先检查配置");
+        }
+
+        HashSet<String> filterIds = new HashSet<>(storeIds);
+        if (filterIds.size() > 1) {
+            throw new BusinessException("存在多个不同Embedding Model的知识库,请先检查配置");
+        }
+
+        return modelStoreFactory.getEmbeddingModel(storeIds.get(0));
+    }
+
+    public EmbeddingModel getEmbeddingModel(Long knowledgeId) {
+        if (knowledgeStoreFactory.containsKnowledge(knowledgeId)) {
+            KnowledgeResp data = knowledgeStoreFactory.getKnowledge(knowledgeId);
+            if (modelStoreFactory.containsEmbeddingModel(data.getEmbedModelId().toString())) {
+                return modelStoreFactory.getEmbeddingModel(data.getEmbedModelId().toString());
+            }
+        }
+        throw new BusinessException("没有找到匹配的Embedding向量数据库");
+    }
+
+    public EmbeddingStore<TextSegment> getEmbeddingStore(Long knowledgeId) {
+        if (knowledgeStoreFactory.containsKnowledge(knowledgeId)) {
+            KnowledgeResp data = knowledgeStoreFactory.getKnowledge(knowledgeId);
+            if (embeddingStoreFactory.containsEmbeddingStore(data.getEmbedStoreId().toString())) {
+                return embeddingStoreFactory.getEmbeddingStore(data.getEmbedStoreId().toString());
+            }
+        }
+        throw new BusinessException("没有找到匹配的Embedding向量数据库");
+    }
+
+    public EmbeddingStore<TextSegment> getEmbeddingStore(List<Long> knowledgeIds) {
+        List<String> storeIds = new ArrayList<>();
+        knowledgeIds.forEach(id -> {
+            if (knowledgeStoreFactory.containsKnowledge(id)) {
+                KnowledgeResp data = knowledgeStoreFactory.getKnowledge(id);
+                if (data.getEmbedStoreId() != null) {
+                    storeIds.add(data.getEmbedStoreId().toString());
+                }
+            }
+        });
+        if (storeIds.isEmpty()) {
+            throw new BusinessException("知识库缺少Embedding Store配置,请先检查配置");
+        }
+
+        HashSet<String> filterIds = new HashSet<>(storeIds);
+        if (filterIds.size() > 1) {
+            throw new BusinessException("存在多个不同Embedding Store数据源的知识库,请先检查配置");
+        }
+
+        return embeddingStoreFactory.getEmbeddingStore(storeIds.get(0));
+    }
+
+}

+ 125 - 0
pavis-module-aigc/src/main/java/com/pavis/admin/aigc/core/llm/provider/EmbeddingStoreFactory.java

@@ -0,0 +1,125 @@
+package com.pavis.admin.aigc.core.llm.provider;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.util.StrUtil;
+import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.pavis.admin.aigc.enums.EmbedStoreEnum;
+import com.pavis.admin.aigc.mapper.EmbedStoreMapper;
+import com.pavis.admin.aigc.model.entity.EmbedStoreDO;
+import com.pavis.admin.aigc.model.query.EmbedStoreQuery;
+import com.pavis.admin.aigc.model.resp.EmbedStoreResp;
+import com.pavis.admin.aigc.service.EmbedStoreService;
+import dev.langchain4j.community.store.embedding.redis.RedisEmbeddingStore;
+import dev.langchain4j.data.segment.TextSegment;
+import dev.langchain4j.store.embedding.EmbeddingStore;
+import dev.langchain4j.store.embedding.milvus.MilvusEmbeddingStore;
+import dev.langchain4j.store.embedding.pgvector.PgVectorEmbeddingStore;
+import jakarta.annotation.PostConstruct;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.stereotype.Component;
+import top.continew.starter.extension.crud.model.query.SortQuery;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * @author tycoding
+ * @since 2024/10/28
+ */
+@Slf4j
+@Component
+public class EmbeddingStoreFactory {
+
+    @Autowired
+    private EmbedStoreService aigcEmbedStoreService;
+
+    @Autowired
+    private EmbedStoreMapper aigcEmbedStoreMapper;
+
+    private final List<EmbedStoreResp> modelStore = new ArrayList<>();
+    private final Map<String, EmbeddingStore<TextSegment>> embedStoreMap = new ConcurrentHashMap<>();
+
+    @Async
+    @PostConstruct
+    public void init() {
+        modelStore.clear();
+        List<EmbedStoreDO> embedStoreDOS = aigcEmbedStoreMapper.selectList(new QueryWrapper<>());
+        List<EmbedStoreResp> list = BeanUtil.copyToList(embedStoreDOS, EmbedStoreResp.class);
+        // List<EmbedStoreResp> list = aigcEmbedStoreService.list(new EmbedStoreQuery(),new SortQuery());
+        list.forEach(embed -> {
+            JSONObject endpoints = JSONObject.parseObject(embed.getEndpoints());
+            embed.setHost(endpoints.get("host").toString());
+            embed.setPort((Integer) endpoints.get("port"));
+            JSONObject databaseConfig = JSONObject.parseObject(embed.getDatabaseConfig());
+            embed.setDatabaseName(databaseConfig.get("database").toString());
+            embed.setTableName(databaseConfig.get("table").toString());
+            JSONObject authConfig = JSONObject.parseObject(embed.getAuthConfig());
+            embed.setUsername(authConfig.get("username").toString());
+            embed.setPassword(authConfig.get("password").toString());
+            JSONObject vectorConfig = JSONObject.parseObject(embed.getVectorConfig());
+            embed.setDimension((Integer) vectorConfig.get("dimension"));
+            try {
+                if (EmbedStoreEnum.REDIS.name().equalsIgnoreCase(embed.getProvider())) {
+                    RedisEmbeddingStore.Builder builder = RedisEmbeddingStore.builder()
+                            .host(embed.getHost())
+                            .port(embed.getPort())
+                            .indexName(embed.getDatabaseName())
+                            .dimension(embed.getDimension());
+                    if (StrUtil.isNotBlank(embed.getUsername()) && StrUtil.isNotBlank(embed.getPassword())) {
+                        builder.user(embed.getUsername()).password(embed.getPassword());
+                    }
+                    EmbeddingStore<TextSegment> store = builder.build();
+                    embedStoreMap.put(embed.getId().toString(), store);
+                }
+                if (EmbedStoreEnum.PGVECTOR.name().equalsIgnoreCase(embed.getProvider())) {
+                    EmbeddingStore<TextSegment> store = PgVectorEmbeddingStore.builder()
+                            .host(embed.getHost())
+                            .port(embed.getPort())
+                            .database(embed.getDatabaseName())
+                            .dimension(embed.getDimension())
+                            .user(embed.getUsername())
+                            .password(embed.getPassword())
+                            .table(embed.getTableName())
+                            .indexListSize(1)
+                            .useIndex(true)
+                            .createTable(true)
+                            .dropTableFirst(false)
+                            .build();
+                    embedStoreMap.put(embed.getId().toString(), store);
+                }
+                if (EmbedStoreEnum.MILVUS.name().equalsIgnoreCase(embed.getProvider())) {
+                    EmbeddingStore<TextSegment> store = MilvusEmbeddingStore.builder()
+                            .host(embed.getHost())
+                            .port(embed.getPort())
+                            .databaseName(embed.getDatabaseName())
+                            .dimension(embed.getDimension())
+                            .username(embed.getUsername())
+                            .password(embed.getPassword())
+                            .collectionName(embed.getTableName())
+                            .build();
+                    embedStoreMap.put(embed.getId().toString(), store);
+                }
+                modelStore.add(embed);
+            } catch (Exception e) {
+                e.printStackTrace();
+                log.error("向量数据库初始化失败:[{}] --- [{}],数据库配置信息:[{}]", embed.getName(), embed.getProvider(), embed);
+            }
+        });
+
+        modelStore.forEach(i -> log.info("已成功注册Embedding Store:{}, 配置信息:{}", i.getProvider(), i));
+    }
+
+    public EmbeddingStore<TextSegment> getEmbeddingStore(String embeddingId) {
+        return embedStoreMap.get(embeddingId);
+    }
+
+    public boolean containsEmbeddingStore(String embeddingId) {
+        return embedStoreMap.containsKey(embeddingId);
+    }
+}

+ 71 - 0
pavis-module-aigc/src/main/java/com/pavis/admin/aigc/core/llm/provider/KnowledgeStoreFactory.java

@@ -0,0 +1,71 @@
+package com.pavis.admin.aigc.core.llm.provider;
+
+import com.pavis.admin.aigc.model.entity.EmbedStoreDO;
+import com.pavis.admin.aigc.model.entity.KnowledgeDO;
+import com.pavis.admin.aigc.model.entity.ModelDO;
+import com.pavis.admin.aigc.model.query.EmbedStoreQuery;
+import com.pavis.admin.aigc.model.query.KnowledgeQuery;
+import com.pavis.admin.aigc.model.query.ModelQuery;
+import com.pavis.admin.aigc.model.resp.EmbedStoreResp;
+import com.pavis.admin.aigc.model.resp.KnowledgeResp;
+import com.pavis.admin.aigc.model.resp.ModelResp;
+import com.pavis.admin.aigc.service.EmbedStoreService;
+import com.pavis.admin.aigc.service.KnowledgeService;
+import com.pavis.admin.aigc.service.ModelService;
+import jakarta.annotation.PostConstruct;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.stereotype.Component;
+import top.continew.starter.extension.crud.model.query.SortQuery;
+
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.stream.Collectors;
+
+/**
+ * @author tycoding
+ * @since 2024/10/29
+ */
+@Slf4j
+@Component
+public class KnowledgeStoreFactory {
+
+    @Autowired
+    private KnowledgeService knowledgeService;
+    @Autowired
+    private ModelService modelService;
+    @Autowired
+    private EmbedStoreService embedStoreService;
+
+    private final Map<String, KnowledgeResp> knowledgeMap = new ConcurrentHashMap<>();
+
+    @Async
+    @PostConstruct
+    public void init() {
+        knowledgeMap.clear();
+        List<KnowledgeResp> list = knowledgeService.selectKnowledge();
+        Map<Long, List<ModelResp>> modelMap = modelService.selModelAndSecrt().stream().collect(Collectors.groupingBy(ModelResp::getId));
+        Map<Long, List<EmbedStoreResp>> storeMap = embedStoreService.selEmbedStore().stream().collect(Collectors.groupingBy(EmbedStoreResp::getId));
+        list.forEach(know -> {
+            if (know.getEmbedModelId() != null) {
+                List<ModelResp> models = modelMap.get(know.getEmbedModelId());
+                know.setEmbedModel(models == null ? null : models.get(0));
+            }
+            if (know.getEmbedStoreId() != null) {
+                List<EmbedStoreResp> stores = storeMap.get(know.getEmbedStoreId());
+                know.setEmbedStore(stores == null ? null : stores.get(0));
+            }
+            knowledgeMap.put(know.getId().toString(), know);
+        });
+    }
+
+    public KnowledgeResp getKnowledge(Long knowledgeId) {
+        return knowledgeMap.get(knowledgeId);
+    }
+
+    public boolean containsKnowledge(Long knowledgeId) {
+        return knowledgeMap.containsKey(knowledgeId);
+    }
+}

+ 60 - 0
pavis-module-aigc/src/main/java/com/pavis/admin/aigc/core/llm/provider/ModelProvider.java

@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved.
+ *
+ * Licensed under the GNU Affero General Public License, Version 3 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     https://www.gnu.org/licenses/agpl-3.0.html
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.pavis.admin.aigc.core.llm.provider;
+
+import cn.hutool.core.util.ObjectUtil;
+import dev.langchain4j.model.chat.ChatLanguageModel;
+import dev.langchain4j.model.chat.StreamingChatLanguageModel;
+import dev.langchain4j.model.image.ImageModel;
+import lombok.AllArgsConstructor;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Component;
+
+/**
+ * @author tycoding
+ * @since 2024/3/8
+ */
+@Component
+@AllArgsConstructor
+public class ModelProvider {
+
+    private final ModelStoreFactory modelStoreFactory;
+
+    public StreamingChatLanguageModel stream(String modelId) {
+        StreamingChatLanguageModel streamingChatModel = modelStoreFactory.getStreamingChatModel(modelId);
+        if (ObjectUtil.isNotEmpty(streamingChatModel)) {
+            return streamingChatModel;
+        }
+        throw new RuntimeException("没有匹配到模型,请检查模型配置!");
+    }
+
+    public ChatLanguageModel text(String modelId) {
+        ChatLanguageModel chatLanguageModel = modelStoreFactory.getChatLanguageModel(modelId);
+        if (ObjectUtil.isNotEmpty(chatLanguageModel)) {
+            return chatLanguageModel;
+        }
+        throw new RuntimeException("没有匹配到模型,请检查模型配置!");
+    }
+
+    public ImageModel image(String modelId) {
+        ImageModel imageModel = modelStoreFactory.getImageModel(modelId);
+        if (ObjectUtil.isNotEmpty(imageModel)) {
+            return imageModel;
+        }
+        throw new RuntimeException("没有匹配到模型,请检查模型配置!");
+    }
+}

+ 171 - 0
pavis-module-aigc/src/main/java/com/pavis/admin/aigc/core/llm/provider/ModelStoreFactory.java

@@ -0,0 +1,171 @@
+package com.pavis.admin.aigc.core.llm.provider;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.util.ObjectUtil;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+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.ModelMapper;
+import com.pavis.admin.aigc.model.entity.ModelDO;
+import com.pavis.admin.aigc.model.query.ModelQuery;
+import com.pavis.admin.aigc.model.resp.ModelResp;
+import com.pavis.admin.aigc.service.ModelService;
+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 jakarta.annotation.PostConstruct;
+import jakarta.annotation.Resource;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.scheduling.annotation.Async;
+import top.continew.starter.extension.crud.model.query.SortQuery;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * @author tycoding
+ * @since 2024/6/16
+ */
+@Configuration
+@Slf4j
+public class ModelStoreFactory {
+
+    @Resource
+    private ModelService aigcModelService;
+
+    @Resource
+    private ModelMapper modelMapper;
+    @Resource
+    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<>();
+
+    @Async
+    @PostConstruct
+    public void init() {
+        modelStore.clear();
+        streamingChatMap.clear();
+        chatLanguageMap.clear();
+        embeddingModelMap.clear();
+        imageModelMap.clear();
+
+        List<ModelDO> modelDOS = modelMapper.selectList(new QueryWrapper<>());
+        List<ModelResp> list = BeanUtil.copyToList(modelDOS, ModelResp.class);
+        // List<ModelResp> list = aigcModelService.list(new ModelQuery(),new SortQuery());
+        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);
+    }
+}

+ 35 - 0
pavis-module-aigc/src/main/java/com/pavis/admin/aigc/core/llm/provider/ProviderListener.java

@@ -0,0 +1,35 @@
+package com.pavis.admin.aigc.core.llm.provider;
+
+import com.pavis.admin.aigc.event.EmbeddingRefreshEvent;
+import com.pavis.admin.aigc.event.ProviderRefreshEvent;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.context.event.EventListener;
+import org.springframework.stereotype.Component;
+
+/**
+ * @author tycoding
+ * @since 2024/6/16
+ */
+@Slf4j
+@Component
+@AllArgsConstructor
+public class ProviderListener {
+
+    private final ModelStoreFactory providerInitialize;
+    private final EmbeddingStoreFactory embeddingStoreInitialize;
+
+    @EventListener
+    public void providerEvent(ProviderRefreshEvent event) {
+        log.info("refresh provider beans begin......");
+        providerInitialize.init();
+        log.info("refresh provider beans success......");
+    }
+
+    @EventListener
+    public void providerEvent(EmbeddingRefreshEvent event) {
+        log.info("refresh embedding beans begin......");
+        embeddingStoreInitialize.init();
+        log.info("refresh embedding beans success......");
+    }
+}

+ 45 - 0
pavis-module-aigc/src/main/java/com/pavis/admin/aigc/core/llm/provider/build/ModelBuildHandler.java

@@ -0,0 +1,45 @@
+package com.pavis.admin.aigc.core.llm.provider.build;
+
+import com.pavis.admin.aigc.model.resp.ModelResp;
+import dev.langchain4j.model.chat.ChatLanguageModel;
+import dev.langchain4j.model.chat.StreamingChatLanguageModel;
+import dev.langchain4j.model.embedding.EmbeddingModel;
+import dev.langchain4j.model.image.ImageModel;
+
+/**
+ * @author GB
+ * @since 2024-08-18 09:57
+ */
+public interface ModelBuildHandler {
+
+    /**
+     * 判断是不是当前模型
+     */
+    boolean whetherCurrentModel(ModelResp model);
+
+    /**
+     * basic check
+     */
+    boolean basicCheck(ModelResp model);
+
+    /**
+     * streaming chat build
+     */
+    StreamingChatLanguageModel buildStreamingChat(ModelResp model);
+
+    /**
+     * chat build
+     */
+    ChatLanguageModel buildChatLanguageModel(ModelResp model);
+
+    /**
+     * embedding config
+     */
+    EmbeddingModel buildEmbedding(ModelResp model);
+
+    /**
+     * image config
+     */
+    ImageModel buildImage(ModelResp model);
+
+}

+ 142 - 0
pavis-module-aigc/src/main/java/com/pavis/admin/aigc/core/llm/provider/build/OllamaModelBuildHandler.java

@@ -0,0 +1,142 @@
+package com.pavis.admin.aigc.core.llm.provider.build;
+
+import com.pavis.admin.aigc.core.llm.exception.ServiceException;
+import com.pavis.admin.aigc.enums.ChatErrorEnum;
+import com.pavis.admin.aigc.enums.ProviderEnum;
+import com.pavis.admin.aigc.model.resp.ModelResp;
+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 dev.langchain4j.model.ollama.OllamaChatModel;
+import dev.langchain4j.model.ollama.OllamaEmbeddingModel;
+import dev.langchain4j.model.ollama.OllamaStreamingChatModel;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Component;
+
+import java.time.Duration;
+
+/**
+ * @author GB
+ * @since 2024-08-19 10:08
+ */
+@Slf4j
+@Component
+public class OllamaModelBuildHandler implements ModelBuildHandler {
+
+    @Override
+    public boolean whetherCurrentModel(ModelResp model) {
+        return ProviderEnum.OLLAMA.name().equals(model.getProvider());
+    }
+
+    @Override
+    public boolean basicCheck(ModelResp model) {
+        if (StringUtils.isBlank(model.getBaseUrl())) {
+            throw new ServiceException(ChatErrorEnum.BASE_URL_IS_NULL.getErrorCode(),
+                    ChatErrorEnum.BASE_URL_IS_NULL.getErrorDesc(ProviderEnum.OLLAMA.name(), model.getType()));
+        }
+        return true;
+    }
+
+    @Override
+    public StreamingChatLanguageModel buildStreamingChat(ModelResp model) {
+        try {
+            if (!whetherCurrentModel(model)) {
+                return null;
+            }
+            if (!basicCheck(model)) {
+                return null;
+            }
+            return (StreamingChatLanguageModel) OllamaStreamingChatModel
+                    .builder()
+                    .baseUrl(model.getBaseUrl())
+                    .modelName(model.getModel())
+                    .temperature(model.getTemperature())
+                    .topP(model.getTopP())
+                    .logRequests(true)
+                    .logResponses(true)
+                    .timeout(Duration.ofMinutes(10))
+                    .build();
+        } catch (ServiceException e) {
+            log.error(e.getMessage());
+            throw e;
+        } catch (Exception e) {
+            log.error("Ollama streaming chat 配置报错", e);
+            return null;
+        }
+    }
+
+    @Override
+    public ChatLanguageModel buildChatLanguageModel(ModelResp model) {
+        try {
+            if (!whetherCurrentModel(model)) {
+                return null;
+            }
+            if (!basicCheck(model)) {
+                return null;
+            }
+            return (ChatLanguageModel) OllamaChatModel
+                    .builder()
+                    .baseUrl(model.getBaseUrl())
+                    .modelName(model.getModel())
+                    .temperature(model.getTemperature())
+                    .topP(model.getTopP())
+                    .logRequests(true)
+                    .logResponses(true)
+                    .timeout(Duration.ofMinutes(10))
+                    .build();
+        } catch (ServiceException e) {
+            log.error(e.getMessage());
+            throw e;
+        } catch (Exception e) {
+            log.error("Ollama chat 配置报错", e);
+            return null;
+        }
+    }
+
+    @Override
+    public EmbeddingModel buildEmbedding(ModelResp model) {
+        try {
+            if (!whetherCurrentModel(model)) {
+                return null;
+            }
+            if (!basicCheck(model)) {
+                return null;
+            }
+            return OllamaEmbeddingModel
+                    .builder()
+                    .baseUrl(model.getBaseUrl())
+                    .modelName(model.getModel())
+                    .logRequests(true)
+                    .logResponses(true)
+                    .timeout(Duration.ofMinutes(10))
+                    .build();
+        } catch (ServiceException e) {
+            log.error(e.getMessage());
+            throw e;
+        } catch (Exception e) {
+            log.error("Ollama embedding 配置报错", e);
+            return null;
+        }
+    }
+
+    @Override
+    public ImageModel buildImage(ModelResp model) {
+        try {
+            if (!whetherCurrentModel(model)) {
+                return null;
+            }
+            if (!basicCheck(model)) {
+                return null;
+            }
+            return null;
+        } catch (ServiceException e) {
+            log.error(e.getMessage());
+            throw e;
+        } catch (Exception e) {
+            log.error("Ollama image 配置报错", e);
+            return null;
+        }
+    }
+}

+ 184 - 0
pavis-module-aigc/src/main/java/com/pavis/admin/aigc/core/llm/provider/build/OpenAIModelBuildHandler.java

@@ -0,0 +1,184 @@
+
+
+package com.pavis.admin.aigc.core.llm.provider.build;
+
+import cn.hutool.core.util.StrUtil;
+import com.pavis.admin.aigc.core.llm.exception.ServiceException;
+import com.pavis.admin.aigc.enums.ChatErrorEnum;
+import com.pavis.admin.aigc.enums.ProviderEnum;
+import com.pavis.admin.aigc.model.resp.ModelResp;
+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 dev.langchain4j.model.openai.OpenAiChatModel;
+import dev.langchain4j.model.openai.OpenAiEmbeddingModel;
+import dev.langchain4j.model.openai.OpenAiImageModel;
+import dev.langchain4j.model.openai.OpenAiStreamingChatModel;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+import java.time.Duration;
+
+/**
+ * @author tycoding
+ * @since 2024-08-19 10:08
+ */
+@Slf4j
+@Component
+@AllArgsConstructor
+public class OpenAIModelBuildHandler implements ModelBuildHandler {
+
+    // private final LangChatProps props;
+
+    /**
+     * 合并处理支持OpenAI接口的模型
+     */
+    @Override
+    public boolean whetherCurrentModel(ModelResp model) {
+        String provider = model.getProvider();
+        return ProviderEnum.OPENAI.name().equals(provider) ||
+                ProviderEnum.GEMINI.name().equals(provider) ||
+                ProviderEnum.CLAUDE.name().equals(provider) ||
+                ProviderEnum.AZURE_OPENAI.name().equals(provider) ||
+                ProviderEnum.DOUYIN.name().equals(provider) ||
+                ProviderEnum.YI.name().equals(provider) ||
+                ProviderEnum.SILICON.name().equals(provider) ||
+                ProviderEnum.DEEPSEEK.name().equals(provider) ||
+                ProviderEnum.SPARK.name().equals(provider)
+                ;
+    }
+
+    @Override
+    public boolean basicCheck(ModelResp model) {
+        String apiKey = model.getApiKey();
+        if (StrUtil.isBlank(apiKey)) {
+            throw new ServiceException(ChatErrorEnum.API_KEY_IS_NULL.getErrorCode(),
+                    ChatErrorEnum.API_KEY_IS_NULL.getErrorDesc(model.getProvider().toUpperCase(), model.getType()));
+        }
+        return true;
+    }
+
+    @Override
+    public StreamingChatLanguageModel buildStreamingChat(ModelResp model) {
+        try {
+            if (!whetherCurrentModel(model)) {
+                return null;
+            }
+            if (!basicCheck(model)) {
+                return null;
+            }
+            return OpenAiStreamingChatModel
+                    .builder()
+                    .apiKey(model.getApiKey())
+                    .baseUrl(model.getBaseUrl())
+                    .modelName(model.getModel())
+                    .maxTokens(model.getResponseLimit())
+                    .temperature(model.getTemperature())
+                    .logRequests(true)
+                    .logResponses(true)
+                    .topP(model.getTopP())
+                    .timeout(Duration.ofMinutes(10))
+                    .build();
+        } catch (ServiceException e) {
+            log.error(e.getMessage());
+            throw e;
+        } catch (Exception e) {
+            log.error(model.getProvider() + " Streaming Chat 模型配置报错", e);
+            return null;
+        }
+    }
+
+    @Override
+    public ChatLanguageModel buildChatLanguageModel(ModelResp model) {
+        try {
+            if (!whetherCurrentModel(model)) {
+                return null;
+            }
+            if (!basicCheck(model)) {
+                return null;
+            }
+            return OpenAiChatModel
+                    .builder()
+                    .apiKey(model.getApiKey())
+                    .baseUrl(model.getBaseUrl())
+                    .modelName(model.getModel())
+                    .maxTokens(model.getResponseLimit())
+                    .temperature(model.getTemperature())
+                    .logRequests(true)
+                    .logResponses(true)
+                    .topP(model.getTopP())
+                    .timeout(Duration.ofMinutes(10))
+                    .build();
+        } catch (ServiceException e) {
+            log.error(e.getMessage());
+            throw e;
+        } catch (Exception e) {
+            log.error(model.getProvider() + " Chat 模型配置报错", e);
+            return null;
+        }
+    }
+
+    @Override
+    public EmbeddingModel buildEmbedding(ModelResp model) {
+        try {
+            if (!whetherCurrentModel(model)) {
+                return null;
+            }
+            if (!basicCheck(model)) {
+                return null;
+            }
+            OpenAiEmbeddingModel openAiEmbeddingModel = OpenAiEmbeddingModel
+                    .builder()
+                    .apiKey(model.getApiKey())
+                    .baseUrl(model.getBaseUrl())
+                    .modelName(model.getModel())
+                    .dimensions(model.getDimension())
+                    .logRequests(true)
+                    .logResponses(true)
+                    .dimensions(1024)
+                    .timeout(Duration.ofMinutes(10))
+                    .build();
+            return openAiEmbeddingModel;
+        } catch (ServiceException e) {
+            log.error(e.getMessage());
+            throw e;
+        } catch (Exception e) {
+            log.error(model.getProvider() + " Embedding 模型配置报错", e);
+            return null;
+        }
+    }
+
+    @Override
+    public ImageModel buildImage(ModelResp model) {
+        try {
+            if (!whetherCurrentModel(model)) {
+                return null;
+            }
+            if (!basicCheck(model)) {
+                return null;
+            }
+            return OpenAiImageModel
+                    .builder()
+                    .apiKey(model.getApiKey())
+                    .baseUrl(model.getBaseUrl())
+                    .modelName(model.getModel())
+                    .size(model.getImageSize())
+                    .quality(model.getImageQuality())
+                    .style(model.getImageStyle())
+                    .logRequests(true)
+                    .logResponses(true)
+                    .timeout(Duration.ofMinutes(10))
+                    .build();
+        } catch (ServiceException e) {
+            log.error(e.getMessage());
+            throw e;
+        } catch (Exception e) {
+            log.error(model.getProvider() + " Image 模型配置报错", e);
+            return null;
+        }
+
+
+    }
+}

+ 149 - 0
pavis-module-aigc/src/main/java/com/pavis/admin/aigc/core/llm/provider/build/QFanModelBuildHandler.java

@@ -0,0 +1,149 @@
+package com.pavis.admin.aigc.core.llm.provider.build;
+
+import com.pavis.admin.aigc.core.llm.exception.ServiceException;
+import com.pavis.admin.aigc.enums.ChatErrorEnum;
+import com.pavis.admin.aigc.enums.ProviderEnum;
+import com.pavis.admin.aigc.model.resp.ModelResp;
+import dev.langchain4j.community.model.qianfan.QianfanChatModel;
+import dev.langchain4j.community.model.qianfan.QianfanEmbeddingModel;
+import dev.langchain4j.community.model.qianfan.QianfanStreamingChatModel;
+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 lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Component;
+
+/**
+ * @author GB
+ * @since 2024-08-19 10:08
+ */
+@Slf4j
+@Component
+public class QFanModelBuildHandler implements ModelBuildHandler {
+
+    @Override
+    public boolean whetherCurrentModel(ModelResp model) {
+        return ProviderEnum.Q_FAN.name().equals(model.getProvider());
+    }
+
+    @Override
+    public boolean basicCheck(ModelResp model) {
+        if (StringUtils.isBlank(model.getApiKey())) {
+            throw new ServiceException(ChatErrorEnum.API_KEY_IS_NULL.getErrorCode(),
+                    ChatErrorEnum.API_KEY_IS_NULL.getErrorDesc(ProviderEnum.Q_FAN.name(), model.getType()));
+        }
+        if (StringUtils.isBlank(model.getSecretKey())) {
+            throw new ServiceException(ChatErrorEnum.SECRET_KEY_IS_NULL.getErrorCode(),
+                    ChatErrorEnum.SECRET_KEY_IS_NULL.getErrorDesc(ProviderEnum.Q_FAN.name(), model.getType()));
+        }
+        return true;
+    }
+
+    @Override
+    public StreamingChatLanguageModel buildStreamingChat(ModelResp model) {
+        try {
+            if (!whetherCurrentModel(model)) {
+                return null;
+            }
+            if (!basicCheck(model)) {
+                return null;
+            }
+            return (StreamingChatLanguageModel) QianfanStreamingChatModel
+                    .builder()
+                    .apiKey(model.getApiKey())
+                    .secretKey(model.getSecretKey())
+                    .modelName(model.getModel())
+                    .baseUrl(model.getBaseUrl())
+                    .temperature(model.getTemperature())
+                    .topP(model.getTopP())
+                    .logRequests(true)
+                    .logResponses(true)
+                    .build();
+        } catch (ServiceException e) {
+            log.error(e.getMessage());
+            throw e;
+        } catch (Exception e) {
+            log.error("Qianfan  streaming chat 配置报错", e);
+            return null;
+        }
+    }
+
+    @Override
+    public ChatLanguageModel buildChatLanguageModel(ModelResp model) {
+        try {
+            if (!whetherCurrentModel(model)) {
+                return null;
+            }
+            if (!basicCheck(model)) {
+                return null;
+            }
+            return (ChatLanguageModel) QianfanChatModel
+                    .builder()
+                    .apiKey(model.getApiKey())
+                    .secretKey(model.getSecretKey())
+                    .modelName(model.getModel())
+                    .baseUrl(model.getBaseUrl())
+                    .temperature(model.getTemperature())
+                    .topP(model.getTopP())
+                    .logRequests(true)
+                    .logResponses(true)
+                    .build();
+        } catch (ServiceException e) {
+            log.error(e.getMessage());
+            throw e;
+        } catch (Exception e) {
+            log.error("Qianfan chat 配置报错", e);
+            return null;
+        }
+
+
+    }
+
+    @Override
+    public EmbeddingModel buildEmbedding(ModelResp model) {
+        try {
+            if (!whetherCurrentModel(model)) {
+                return null;
+            }
+            if (!basicCheck(model)) {
+                return null;
+            }
+            return QianfanEmbeddingModel
+                    .builder()
+                    .apiKey(model.getApiKey())
+                    .modelName(model.getModel())
+                    .secretKey(model.getSecretKey())
+                    .logRequests(true)
+                    .logResponses(true)
+                    .build();
+        } catch (ServiceException e) {
+            log.error(e.getMessage());
+            throw e;
+        } catch (Exception e) {
+            log.error("Qianfan embedding 配置报错", e);
+            return null;
+        }
+    }
+
+    @Override
+    public ImageModel buildImage(ModelResp model) {
+        try {
+            if (!whetherCurrentModel(model)) {
+                return null;
+            }
+            if (!basicCheck(model)) {
+                return null;
+            }
+            return null;
+        } catch (ServiceException e) {
+            log.error(e.getMessage());
+            throw e;
+        } catch (Exception e) {
+            log.error("Qianfan image 配置报错", e);
+            return null;
+        }
+
+    }
+}

+ 137 - 0
pavis-module-aigc/src/main/java/com/pavis/admin/aigc/core/llm/provider/build/QWenModelBuildHandler.java

@@ -0,0 +1,137 @@
+package com.pavis.admin.aigc.core.llm.provider.build;
+
+import com.pavis.admin.aigc.core.llm.exception.ServiceException;
+import com.pavis.admin.aigc.enums.ChatErrorEnum;
+import com.pavis.admin.aigc.enums.ProviderEnum;
+import com.pavis.admin.aigc.model.resp.ModelResp;
+import dev.langchain4j.community.model.dashscope.QwenChatModel;
+import dev.langchain4j.community.model.dashscope.QwenEmbeddingModel;
+import dev.langchain4j.community.model.dashscope.QwenStreamingChatModel;
+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 lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Component;
+
+/**
+ * @author GB
+ * @since 2024-08-19 10:08
+ */
+@Slf4j
+@Component
+public class QWenModelBuildHandler implements ModelBuildHandler {
+
+    @Override
+    public boolean whetherCurrentModel(ModelResp model) {
+        return ProviderEnum.Q_WEN.name().equals(model.getProvider());
+    }
+
+    @Override
+    public boolean basicCheck(ModelResp model) {
+        if (StringUtils.isBlank(model.getApiKey())) {
+            throw new ServiceException(ChatErrorEnum.API_KEY_IS_NULL.getErrorCode(),
+                    ChatErrorEnum.API_KEY_IS_NULL.getErrorDesc(ProviderEnum.Q_WEN.name(), model.getType()));
+        }
+        return true;
+    }
+
+    @Override
+    public StreamingChatLanguageModel buildStreamingChat(ModelResp model) {
+        if (!whetherCurrentModel(model)) {
+            return null;
+        }
+        if (!basicCheck(model)) {
+            return null;
+        }
+        try {
+            return (StreamingChatLanguageModel) QwenStreamingChatModel
+                    .builder()
+                    .apiKey(model.getApiKey())
+                    .modelName(model.getModel())
+                    .baseUrl(model.getBaseUrl())
+                    .maxTokens(model.getResponseLimit())
+                    .temperature(Float.parseFloat(model.getTemperature().toString()))
+                    .topP(model.getTopP())
+                    .build();
+        } catch (ServiceException e) {
+            log.error(e.getMessage());
+            throw e;
+        } catch (Exception e) {
+            log.error("qian wen streaming chat 配置报错", e);
+            return null;
+        }
+    }
+
+    @Override
+    public ChatLanguageModel buildChatLanguageModel(ModelResp model) {
+        try {
+            if (!whetherCurrentModel(model)) {
+                return null;
+            }
+            if (!basicCheck(model)) {
+                return null;
+            }
+            return (ChatLanguageModel) QwenChatModel
+                    .builder()
+                    .apiKey(model.getApiKey())
+                    .modelName(model.getModel())
+                    .baseUrl(model.getBaseUrl())
+                    .enableSearch(true)
+                    .maxTokens(model.getResponseLimit())
+                    .temperature(Float.parseFloat(model.getTemperature().toString()))
+                    .topP(model.getTopP())
+                    .build();
+        } catch (ServiceException e) {
+            log.error(e.getMessage());
+            throw e;
+        } catch (Exception e) {
+            log.error("qian wen chat 配置报错", e);
+            return null;
+        }
+    }
+
+    @Override
+    public EmbeddingModel buildEmbedding(ModelResp model) {
+        try {
+            if (!whetherCurrentModel(model)) {
+                return null;
+            }
+            if (!basicCheck(model)) {
+                return null;
+            }
+            return QwenEmbeddingModel
+                    .builder()
+                    .apiKey(model.getApiKey())
+                    .modelName(model.getModel())
+                    .build();
+        } catch (ServiceException e) {
+            log.error(e.getMessage());
+            throw e;
+        } catch (Exception e) {
+            log.error("qian wen embedding 配置报错", e);
+            return null;
+        }
+    }
+
+    @Override
+    public ImageModel buildImage(ModelResp model) {
+        try {
+            if (!whetherCurrentModel(model)) {
+                return null;
+            }
+            if (!basicCheck(model)) {
+                return null;
+            }
+            return null;
+        } catch (ServiceException e) {
+            log.error(e.getMessage());
+            throw e;
+        } catch (Exception e) {
+            log.error("qian wen image 配置报错", e);
+            return null;
+        }
+
+    }
+}

+ 175 - 0
pavis-module-aigc/src/main/java/com/pavis/admin/aigc/core/llm/provider/build/ZhipuModelBuildHandler.java

@@ -0,0 +1,175 @@
+package com.pavis.admin.aigc.core.llm.provider.build;
+
+import com.pavis.admin.aigc.core.llm.exception.ServiceException;
+import com.pavis.admin.aigc.enums.ChatErrorEnum;
+import com.pavis.admin.aigc.enums.ProviderEnum;
+import com.pavis.admin.aigc.model.resp.ModelResp;
+import dev.langchain4j.community.model.zhipu.ZhipuAiChatModel;
+import dev.langchain4j.community.model.zhipu.ZhipuAiEmbeddingModel;
+import dev.langchain4j.community.model.zhipu.ZhipuAiImageModel;
+import dev.langchain4j.community.model.zhipu.ZhipuAiStreamingChatModel;
+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 lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Component;
+
+import java.time.Duration;
+
+/**
+ * @author GB
+ * @since 2024-08-19
+ */
+@Slf4j
+@Component
+@AllArgsConstructor
+public class ZhipuModelBuildHandler implements ModelBuildHandler {
+
+    // private final LangChatProps props;
+
+    @Override
+    public boolean whetherCurrentModel(ModelResp model) {
+        return ProviderEnum.ZHIPU.name().equals(model.getProvider());
+    }
+
+    @Override
+    public boolean basicCheck(ModelResp model) {
+        if (StringUtils.isBlank(model.getApiKey())) {
+            throw new ServiceException(ChatErrorEnum.API_KEY_IS_NULL.getErrorCode(),
+                    ChatErrorEnum.API_KEY_IS_NULL.getErrorDesc(ProviderEnum.ZHIPU.name(), model.getType()));
+        }
+        return true;
+    }
+
+    @Override
+    public StreamingChatLanguageModel buildStreamingChat(ModelResp model) {
+        try {
+            if (!whetherCurrentModel(model)) {
+                return null;
+            }
+            if (!basicCheck(model)) {
+                return null;
+            }
+            return (StreamingChatLanguageModel) ZhipuAiStreamingChatModel
+                    .builder()
+                    .apiKey(model.getApiKey())
+                    .baseUrl(model.getBaseUrl())
+                    .model(model.getModel())
+                    .maxToken(model.getResponseLimit())
+                    .temperature(model.getTemperature())
+                    .topP(model.getTopP())
+                    .logRequests(true)
+                    .logResponses(true)
+                    // .callTimeout(Duration.ofMinutes(10))
+                    .connectTimeout(Duration.ofMinutes(10))
+                    // .writeTimeout(Duration.ofMinutes(10))
+                    .readTimeout(Duration.ofMinutes(10))
+                    .build();
+        } catch (ServiceException e) {
+            log.error(e.getMessage());
+            throw e;
+        } catch (Exception e) {
+            log.error("zhipu streaming chat 配置报错", e);
+            return null;
+        }
+
+    }
+
+    @Override
+    public ChatLanguageModel buildChatLanguageModel(ModelResp model) {
+        try {
+            if (!whetherCurrentModel(model)) {
+                return null;
+            }
+            if (!basicCheck(model)) {
+                return null;
+            }
+            return (ChatLanguageModel) ZhipuAiChatModel
+                    .builder()
+                    .apiKey(model.getApiKey())
+                    .baseUrl(model.getBaseUrl())
+                    .model(model.getModel())
+                    .maxToken(model.getResponseLimit())
+                    .temperature(model.getTemperature())
+                    .topP(model.getTopP())
+                    .logRequests(true)
+                    .logResponses(true)
+                    // .callTimeout(Duration.ofMinutes(10))
+                    .connectTimeout(Duration.ofMinutes(10))
+                    // .writeTimeout(Duration.ofMinutes(10))
+                    .readTimeout(Duration.ofMinutes(10))
+                    .build();
+        } catch (ServiceException e) {
+            log.error(e.getMessage());
+            throw e;
+        } catch (Exception e) {
+            log.error("zhipu chat 配置报错", e);
+            return null;
+        }
+
+    }
+
+    @Override
+    public EmbeddingModel buildEmbedding(ModelResp model) {
+        try {
+            if (!whetherCurrentModel(model)) {
+                return null;
+            }
+            if (!basicCheck(model)) {
+                return null;
+            }
+            return ZhipuAiEmbeddingModel
+                    .builder()
+                    .apiKey(model.getApiKey())
+                    .model(model.getModel())
+                    .baseUrl(model.getBaseUrl())
+                    .logRequests(true)
+                    .logResponses(true)
+                    // .callTimeout(Duration.ofMinutes(10))
+                    .connectTimeout(Duration.ofMinutes(10))
+                    // .writeTimeout(Duration.ofMinutes(10))
+                    .readTimeout(Duration.ofMinutes(10))
+                    .dimensions(1024)
+                    .build();
+        } catch (ServiceException e) {
+            log.error(e.getMessage());
+            throw e;
+        } catch (Exception e) {
+            log.error("zhipu embedding 配置报错", e);
+            return null;
+        }
+    }
+
+    @Override
+    public ImageModel buildImage(ModelResp model) {
+        try {
+            if (!whetherCurrentModel(model)) {
+                return null;
+            }
+            if (!basicCheck(model)) {
+                return null;
+            }
+            return ZhipuAiImageModel
+                    .builder()
+                    .apiKey(model.getApiKey())
+                    .model(model.getModel())
+                    .baseUrl(model.getBaseUrl())
+                    .logRequests(true)
+                    .logResponses(true)
+                    // .callTimeout(Duration.ofMinutes(10))
+                    .connectTimeout(Duration.ofMinutes(10))
+                    // .writeTimeout(Duration.ofMinutes(10))
+                    .readTimeout(Duration.ofMinutes(10))
+                    .build();
+        } catch (ServiceException e) {
+            log.error(e.getMessage());
+            throw e;
+        } catch (Exception e) {
+            log.error("zhipu image 配置报错", e);
+            return null;
+        }
+    }
+}

+ 32 - 0
pavis-module-aigc/src/main/java/com/pavis/admin/aigc/core/llm/service/Agent.java

@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved.
+ *
+ * Licensed under the GNU Affero General Public License, Version 3 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     https://www.gnu.org/licenses/agpl-3.0.html
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.pavis.admin.aigc.core.llm.service;
+
+import dev.langchain4j.service.MemoryId;
+import dev.langchain4j.service.TokenStream;
+import dev.langchain4j.service.UserMessage;
+
+/**
+ * @author tycoding
+ * @since 2024/3/8
+ */
+public interface Agent {
+
+    TokenStream stream(@MemoryId String id, @UserMessage String message);
+
+    String text(@MemoryId String id, @UserMessage String message);
+}

+ 95 - 0
pavis-module-aigc/src/main/java/com/pavis/admin/aigc/core/llm/service/impl/PersistentChatMemoryStore.java

@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved.
+ *
+ * Licensed under the GNU Affero General Public License, Version 3 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     https://www.gnu.org/licenses/agpl-3.0.html
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.pavis.admin.aigc.core.llm.service.impl;
+
+import dev.langchain4j.data.message.ChatMessage;
+import dev.langchain4j.data.message.SystemMessage;
+import dev.langchain4j.store.memory.chat.ChatMemoryStore;
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author tycoding
+ * @since 2024/8/15
+ */
+@Slf4j
+public class PersistentChatMemoryStore implements ChatMemoryStore {
+
+    private static final Map<Object, List<ChatMessage>> store = new HashMap<>();
+    private static final Map<Object, Boolean> initSystemMessageStore = new HashMap<>();
+    private static final Map<Object, Boolean> initMessageStore = new HashMap<>();
+
+    public static void clean(Object memoryId) {
+        log.info("clean message memory store to: {}", memoryId);
+        store.remove(memoryId);
+    }
+
+    public static void init(Object memoryId, SystemMessage message) {
+        Boolean isInitSystemMessage = initSystemMessageStore.get(memoryId);
+        if (isInitSystemMessage != null && isInitSystemMessage) {
+            return;
+        }
+
+        List<ChatMessage> list = store.get(memoryId);
+        if (list == null) {
+            store.put(memoryId, new ArrayList<>(List.of(message)));
+        } else {
+            list.add(message);
+        }
+        initSystemMessageStore.put(memoryId, true);
+    }
+
+    public static void init(Object memoryId, List<ChatMessage> messages) {
+        log.info("initialize message memory store to: {}", memoryId);
+
+        Boolean isInitMessage = initMessageStore.get(memoryId);
+        if (isInitMessage != null && isInitMessage) {
+            return;
+        }
+
+        List<ChatMessage> list = store.get(memoryId);
+        if (list == null) {
+            store.put(memoryId, messages);
+        } else {
+            list.addAll(messages);
+        }
+        initMessageStore.put(memoryId, true);
+    }
+
+    @Override
+    public List<ChatMessage> getMessages(Object memoryId) {
+        List<ChatMessage> list = store.get(memoryId);
+        if (list == null) {
+            return new ArrayList<>();
+        }
+        return list;
+    }
+
+    @Override
+    public void updateMessages(Object memoryId, List<ChatMessage> messages) {
+        store.put(memoryId, messages);
+    }
+
+    @Override
+    public void deleteMessages(Object memoryId) {
+        store.remove(memoryId);
+    }
+}

+ 13 - 1
pavis-module-aigc/src/main/java/com/pavis/admin/aigc/mapper/AppKnowledgeMapper.java

@@ -1,12 +1,24 @@
 package com.pavis.admin.aigc.mapper;
 
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Select;
 import top.continew.starter.data.mp.base.BaseMapper;
 import com.pavis.admin.aigc.model.entity.AppKnowledgeDO;
 
+import java.util.List;
+
 /**
 * 应用和知识库关联 Mapper
 *
 * @author semi
 * @since 2025/05/26 17:28
 */
-public interface AppKnowledgeMapper extends BaseMapper<AppKnowledgeDO> {}
+public interface AppKnowledgeMapper extends BaseMapper<AppKnowledgeDO> {
+    /**
+     * 根据appId查询knowledgeIds集合
+     * @param id app主键id
+     * @return 用户信息
+     */
+    @Select("SELECT knowledge_id FROM aigc_app_knowledge WHERE app_id = #{id}")
+    List<Long> selectByKnowledgeIds(@Param("id") Long id);
+}

+ 9 - 1
pavis-module-aigc/src/main/java/com/pavis/admin/aigc/mapper/EmbedStoreMapper.java

@@ -1,12 +1,20 @@
 package com.pavis.admin.aigc.mapper;
 
+import com.pavis.admin.aigc.model.resp.EmbedStoreResp;
+import com.pavis.admin.aigc.model.resp.ModelResp;
+import org.apache.ibatis.annotations.Select;
 import top.continew.starter.data.mp.base.BaseMapper;
 import com.pavis.admin.aigc.model.entity.EmbedStoreDO;
 
+import java.util.List;
+
 /**
 * 向量存储配置 Mapper
 *
 * @author semi
 * @since 2025/05/26 17:28
 */
-public interface EmbedStoreMapper extends BaseMapper<EmbedStoreDO> {}
+public interface EmbedStoreMapper extends BaseMapper<EmbedStoreDO> {
+    @Select("SELECT * FROM aigc_embed_store")
+    List<EmbedStoreResp> selEmbedStore();
+}

+ 9 - 1
pavis-module-aigc/src/main/java/com/pavis/admin/aigc/mapper/KnowledgeMapper.java

@@ -1,12 +1,20 @@
 package com.pavis.admin.aigc.mapper;
 
+import com.pavis.admin.aigc.model.resp.KnowledgeResp;
+import com.pavis.admin.aigc.model.resp.ModelResp;
+import org.apache.ibatis.annotations.Select;
 import top.continew.starter.data.mp.base.BaseMapper;
 import com.pavis.admin.aigc.model.entity.KnowledgeDO;
 
+import java.util.List;
+
 /**
 * AIGC知识库 Mapper
 *
 * @author semi
 * @since 2025/05/26 17:28
 */
-public interface KnowledgeMapper extends BaseMapper<KnowledgeDO> {}
+public interface KnowledgeMapper extends BaseMapper<KnowledgeDO> {
+    @Select("SELECT * FROM aigc_knowledge")
+    List<KnowledgeResp> selectKnowledge();
+}

+ 29 - 1
pavis-module-aigc/src/main/java/com/pavis/admin/aigc/mapper/ModelMapper.java

@@ -1,12 +1,40 @@
 package com.pavis.admin.aigc.mapper;
 
+import com.pavis.admin.aigc.model.resp.ModelResp;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Select;
 import top.continew.starter.data.mp.base.BaseMapper;
 import com.pavis.admin.aigc.model.entity.ModelDO;
 
+import java.util.List;
+
 /**
 * AIGC模型配置 Mapper
 *
 * @author semi
 * @since 2025/05/26 17:28
 */
-public interface ModelMapper extends BaseMapper<ModelDO> {}
+public interface ModelMapper extends BaseMapper<ModelDO> {
+    /**
+     * 获取模型
+     * @return 列表
+     */
+    @Select("SELECT * FROM aigc_model")
+    List<ModelResp> selectByModel();
+
+
+    @Select("SELECT * from aigc_model am left join aigc_model_secret ams on am.id = ams.model_id")
+    List<ModelResp> selModelAndSecret();
+
+    @Select("<script>"
+            + "SELECT * FROM aigc_model am LEFT JOIN aigc_model_secret ams ON am.id = ams.model_id "
+            + "WHERE 1=1 "
+            + "<if test='type != null'>"
+            + "  AND am.type=#{type}"
+            + "</if>"
+            + "<if test='provider != null'>"
+            + "  AND am.provider=#{provider}"
+            + "</if>"
+            + "</script>")
+    List<ModelResp> selModelAndSecretList(@Param("type") String type, @Param("provider") String provider);
+}

+ 5 - 0
pavis-module-aigc/src/main/java/com/pavis/admin/aigc/model/entity/ModelSecretDO.java

@@ -46,4 +46,9 @@ public class ModelSecretDO extends BaseDO {
      * API版本
      */
     private String apiVersion;
+
+    /**
+     * 向量维度
+     */
+    private Integer dimension;
 }

+ 11 - 0
pavis-module-aigc/src/main/java/com/pavis/admin/aigc/model/req/AigcMessageReq.java

@@ -1,5 +1,6 @@
 package com.pavis.admin.aigc.model.req;
 
+import com.pavis.admin.aigc.common.utils.StreamEmitter;
 import jakarta.validation.constraints.*;
 
 import lombok.Data;
@@ -10,6 +11,8 @@ import org.hibernate.validator.constraints.Length;
 import java.io.Serial;
 import java.io.Serializable;
 import java.time.*;
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * 智能对话消息创建或修改参数
@@ -74,4 +77,12 @@ public class AigcMessageReq implements Serializable {
     @Schema(description = "创建时间")
     @NotNull(message = "创建时间不能为空")
     private LocalDateTime createTime;
+
+    private StreamEmitter emitter;
+
+    private String appId;
+
+    private String promptText;
+
+    private List<Long> knowledgeIds = new ArrayList<>();
 }

+ 50 - 8
pavis-module-aigc/src/main/java/com/pavis/admin/aigc/model/req/ModelReq.java

@@ -57,16 +57,58 @@ public class ModelReq implements Serializable {
     private String name;
 
     /**
-     * 创建人
+     * API密钥
      */
-    @Schema(description = "创建人")
-    @NotNull(message = "创建人不能为空")
-    private Long createUser;
+    @Schema(description = "API密钥")
+    private String apiKey;
 
     /**
-     * 创建时间
+     * 密钥
      */
-    @Schema(description = "创建时间")
-    @NotNull(message = "创建时间不能为空")
-    private LocalDateTime createTime;
+    @Schema(description = "密钥")
+    private String secretKey;
+
+    /**
+     * 基础URL
+     */
+    @Schema(description = "基础URL")
+    private String baseUrl;
+
+    /**
+     * API版本
+     */
+    @Schema(description = "API版本")
+    private String apiVersion;
+
+    /**
+     * 最大响应长度
+     */
+    @Schema(description = "最大响应长度")
+    private Integer responseLimit;
+
+    /**
+     * 随机性控制
+     */
+    @Schema(description = "随机性控制")
+    private Double temperature;
+
+    /**
+     * 核心采样概率
+     */
+    @Schema(description = "核心采样概率")
+    private Double topP;
+
+    // /**
+    //  * 创建人
+    //  */
+    // @Schema(description = "创建人")
+    // @NotNull(message = "创建人不能为空")
+    // private Long createUser;
+    //
+    // /**
+    //  * 创建时间
+    //  */
+    // @Schema(description = "创建时间")
+    // @NotNull(message = "创建时间不能为空")
+    // private LocalDateTime createTime;
 }

+ 16 - 11
pavis-module-aigc/src/main/java/com/pavis/admin/aigc/model/req/ModelSecretReq.java

@@ -32,16 +32,21 @@ public class ModelSecretReq implements Serializable {
     private Long modelId;
 
     /**
-     * 创建人
+     * 向量维度
      */
-    @Schema(description = "创建人")
-    @NotNull(message = "创建人不能为空")
-    private Long createUser;
-
-    /**
-     * 创建时间
-     */
-    @Schema(description = "创建时间")
-    @NotNull(message = "创建时间不能为空")
-    private LocalDateTime createTime;
+    private Integer dimension;
+
+    // /**
+    //  * 创建人
+    //  */
+    // @Schema(description = "创建人")
+    // @NotNull(message = "创建人不能为空")
+    // private Long createUser;
+    //
+    // /**
+    //  * 创建时间
+    //  */
+    // @Schema(description = "创建时间")
+    // @NotNull(message = "创建时间不能为空")
+    // private LocalDateTime createTime;
 }

+ 15 - 0
pavis-module-aigc/src/main/java/com/pavis/admin/aigc/model/resp/AigcAppResp.java

@@ -1,13 +1,21 @@
 package com.pavis.admin.aigc.model.resp;
 
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.extension.handlers.FastjsonTypeHandler;
+import com.pavis.admin.aigc.model.entity.KnowledgeDO;
+import com.pavis.admin.aigc.model.entity.ModelDO;
 import lombok.Data;
 
 import io.swagger.v3.oas.annotations.media.Schema;
 
 import com.pavis.admin.common.model.resp.BaseResp;
+import org.apache.ibatis.type.JdbcType;
+
 import java.io.Serial;
 import java.time.*;
 import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * AIGC应用配置信息
@@ -117,4 +125,11 @@ public class AigcAppResp extends BaseResp {
      */
     @Schema(description = "修改时间")
     private LocalDateTime updateTime;
+
+    private ModelResp model;
+
+    private List<KnowledgeResp> knowledges = new ArrayList<>();
+
+    @TableField(typeHandler = FastjsonTypeHandler.class, jdbcType = JdbcType.VARCHAR)
+    private List<String> knowledgeIds;
 }

+ 8 - 0
pavis-module-aigc/src/main/java/com/pavis/admin/aigc/model/resp/EmbedStoreResp.java

@@ -119,4 +119,12 @@ public class EmbedStoreResp extends BaseResp {
      */
     @Schema(description = "修改时间")
     private LocalDateTime updateTime;
+
+    private String host;
+    private Integer port;
+    private String databaseName;
+    private Integer dimension;
+    private String username;
+    private String password;
+    private String tableName;
 }

+ 6 - 0
pavis-module-aigc/src/main/java/com/pavis/admin/aigc/model/resp/KnowledgeResp.java

@@ -1,5 +1,6 @@
 package com.pavis.admin.aigc.model.resp;
 
+import com.baomidou.mybatisplus.annotation.TableField;
 import lombok.Data;
 
 import io.swagger.v3.oas.annotations.media.Schema;
@@ -134,4 +135,9 @@ public class KnowledgeResp extends BaseResp {
      */
     @Schema(description = "修改时间")
     private LocalDateTime updateTime;
+
+
+    private EmbedStoreResp embedStore;
+
+    private ModelResp embedModel;
 }

+ 3 - 0
pavis-module-aigc/src/main/java/com/pavis/admin/aigc/model/resp/ModelDetailResp.java

@@ -1,5 +1,7 @@
 package com.pavis.admin.aigc.model.resp;
 
+import cn.crane4j.annotation.Assemble;
+import com.pavis.admin.common.constant.ContainerConstants;
 import lombok.Data;
 
 import io.swagger.v3.oas.annotations.media.Schema;
@@ -164,4 +166,5 @@ public class ModelDetailResp extends BaseDetailResp {
     @Schema(description = "每分钟最大请求数")
     @ExcelProperty(value = "每分钟最大请求数")
     private Integer rateLimit;
+
 }

+ 17 - 0
pavis-module-aigc/src/main/java/com/pavis/admin/aigc/model/resp/ModelResp.java

@@ -5,6 +5,8 @@ import lombok.Data;
 import io.swagger.v3.oas.annotations.media.Schema;
 
 import com.pavis.admin.common.model.resp.BaseResp;
+import lombok.experimental.Accessors;
+
 import java.io.Serial;
 import java.time.*;
 
@@ -16,6 +18,7 @@ import java.time.*;
  */
 @Data
 @Schema(description = "AIGC模型配置信息")
+@Accessors(chain = true)
 public class ModelResp extends BaseResp {
 
     @Serial
@@ -152,4 +155,18 @@ public class ModelResp extends BaseResp {
      */
     @Schema(description = "修改时间")
     private LocalDateTime updateTime;
+
+    /**
+     * API密钥
+     */
+    private String apiKey;
+
+    /**
+     * 密钥
+     */
+    private String secretKey;
+
+    private String baseUrl;
+
+    private Integer dimension;
 }

+ 5 - 0
pavis-module-aigc/src/main/java/com/pavis/admin/aigc/model/resp/ModelSecretDetailResp.java

@@ -59,4 +59,9 @@ public class ModelSecretDetailResp extends BaseDetailResp {
     @Schema(description = "API版本")
     @ExcelProperty(value = "API版本")
     private String apiVersion;
+
+    /**
+     * 向量维度
+     */
+    private Integer dimension;
 }

+ 5 - 0
pavis-module-aigc/src/main/java/com/pavis/admin/aigc/model/resp/ModelSecretResp.java

@@ -51,6 +51,11 @@ public class ModelSecretResp extends BaseResp {
     @Schema(description = "API版本")
     private String apiVersion;
 
+    /**
+     * 向量维度
+     */
+    private Integer dimension;
+
     /**
      * 修改人
      */

+ 25 - 1
pavis-module-aigc/src/main/java/com/pavis/admin/aigc/service/AigcAppService.java

@@ -1,15 +1,39 @@
 package com.pavis.admin.aigc.service;
 
+import com.pavis.admin.aigc.common.model.req.ChatReq;
+import com.pavis.admin.aigc.common.model.req.ImageReq;
+import com.pavis.admin.aigc.model.entity.AigcAppDO;
+import dev.langchain4j.data.image.Image;
+import dev.langchain4j.model.output.Response;
+import dev.langchain4j.service.TokenStream;
 import top.continew.starter.extension.crud.service.BaseService;
 import com.pavis.admin.aigc.model.query.AigcAppQuery;
 import com.pavis.admin.aigc.model.req.AigcAppReq;
 import com.pavis.admin.aigc.model.resp.AigcAppDetailResp;
 import com.pavis.admin.aigc.model.resp.AigcAppResp;
 
+import java.util.List;
+
 /**
  * AIGC应用配置业务接口
  *
  * @author semi
  * @since 2025/05/26 17:28
  */
-public interface AigcAppService extends BaseService<AigcAppResp, AigcAppDetailResp, AigcAppQuery, AigcAppReq> {}
+public interface AigcAppService extends BaseService<AigcAppResp, AigcAppDetailResp, AigcAppQuery, AigcAppReq> {
+    List<AigcAppDO> list(AigcAppDO data);
+
+    AigcAppDO getById(String id);
+
+    AigcAppDO get(String appId);
+
+    // langchat内的
+
+    TokenStream chat(ChatReq req);
+
+    TokenStream singleChat(ChatReq req);
+
+    String text(ChatReq req);
+
+    Response<Image> image(ImageReq req);
+}

+ 5 - 1
pavis-module-aigc/src/main/java/com/pavis/admin/aigc/service/EmbedStoreService.java

@@ -6,10 +6,14 @@ import com.pavis.admin.aigc.model.req.EmbedStoreReq;
 import com.pavis.admin.aigc.model.resp.EmbedStoreDetailResp;
 import com.pavis.admin.aigc.model.resp.EmbedStoreResp;
 
+import java.util.List;
+
 /**
  * 向量存储配置业务接口
  *
  * @author semi
  * @since 2025/05/26 17:28
  */
-public interface EmbedStoreService extends BaseService<EmbedStoreResp, EmbedStoreDetailResp, EmbedStoreQuery, EmbedStoreReq> {}
+public interface EmbedStoreService extends BaseService<EmbedStoreResp, EmbedStoreDetailResp, EmbedStoreQuery, EmbedStoreReq> {
+    List<EmbedStoreResp> selEmbedStore();
+}

+ 20 - 1
pavis-module-aigc/src/main/java/com/pavis/admin/aigc/service/KnowledgeService.java

@@ -6,10 +6,29 @@ import com.pavis.admin.aigc.model.req.KnowledgeReq;
 import com.pavis.admin.aigc.model.resp.KnowledgeDetailResp;
 import com.pavis.admin.aigc.model.resp.KnowledgeResp;
 
+import java.util.List;
+
 /**
  * AIGC知识库业务接口
  *
  * @author semi
  * @since 2025/05/26 17:28
  */
-public interface KnowledgeService extends BaseService<KnowledgeResp, KnowledgeDetailResp, KnowledgeQuery, KnowledgeReq> {}
+public interface KnowledgeService extends BaseService<KnowledgeResp, KnowledgeDetailResp, KnowledgeQuery, KnowledgeReq> {
+    List<KnowledgeResp>selectKnowledge();
+    // void addDocs(AigcDocs data);
+    //
+    // void updateDocs(AigcDocs data);
+    //
+    // void addDocsSlice(AigcDocsSlice data);
+    //
+    // void updateDocsSlice(AigcDocsSlice data);
+
+    // List<String> listSliceVectorIdsOfDoc(String docsId);
+
+    // List<AigcDocs> getDocsByKb(String knowledgeId);
+
+    // void removeKnowledge(String knowledgeId);
+
+    // void removeSlicesOfDoc(String docsId);
+}

+ 3 - 1
pavis-module-aigc/src/main/java/com/pavis/admin/aigc/service/ModelSecretService.java

@@ -12,4 +12,6 @@ import com.pavis.admin.aigc.model.resp.ModelSecretResp;
  * @author semi
  * @since 2025/05/26 17:28
  */
-public interface ModelSecretService extends BaseService<ModelSecretResp, ModelSecretDetailResp, ModelSecretQuery, ModelSecretReq> {}
+public interface ModelSecretService extends BaseService<ModelSecretResp, ModelSecretDetailResp, ModelSecretQuery, ModelSecretReq> {
+
+}

+ 48 - 1
pavis-module-aigc/src/main/java/com/pavis/admin/aigc/service/ModelService.java

@@ -1,15 +1,62 @@
 package com.pavis.admin.aigc.service;
 
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.pavis.admin.aigc.model.entity.ModelDO;
 import top.continew.starter.extension.crud.service.BaseService;
 import com.pavis.admin.aigc.model.query.ModelQuery;
 import com.pavis.admin.aigc.model.req.ModelReq;
 import com.pavis.admin.aigc.model.resp.ModelDetailResp;
 import com.pavis.admin.aigc.model.resp.ModelResp;
 
+import java.util.List;
+
 /**
  * AIGC模型配置业务接口
  *
  * @author semi
  * @since 2025/05/26 17:28
  */
-public interface ModelService extends BaseService<ModelResp, ModelDetailResp, ModelQuery, ModelReq> {}
+public interface ModelService extends BaseService<ModelResp, ModelDetailResp, ModelQuery, ModelReq> {
+
+    /**
+     * 模型和配置新增接口
+     * @param modelReq 参数
+     */
+    void insModelAndSecret(ModelReq modelReq);
+    // List<ModelDO> getChatModels();
+    //
+    // List<ModelDO> getImageModels();
+    //
+    // List<ModelDO> getEmbeddingModels();
+
+    /**
+     * 模型和配置查询接口
+     * @return 模型列表
+     */
+    List<ModelResp> selModelAndSecrt();
+
+    List<ModelResp> list(ModelResp data);
+
+    /**
+     * 模型和配置更新接口
+     * @param modelResp 更新数据
+     */
+    void updateModelAndSecrt(ModelResp modelResp);
+
+    /**
+     * 模型和配置--根据调剂进行查询接口
+     * @param modelQuery
+     * @return
+     */
+    List<ModelResp> selModelAndSecrtList(ModelQuery modelQuery);
+
+    /**
+     * 模型和配置--根据调剂进行删除接口
+     * @param ids
+     */
+    void delModelAndSecrt(List<Long> ids);
+
+    // Page<ModelDO> page(ModelDO data, QueryPage queryPage);
+    //
+    // ModelDO selectById(String id);
+}

+ 24 - 0
pavis-module-aigc/src/main/java/com/pavis/admin/aigc/service/PvsChatService.java

@@ -0,0 +1,24 @@
+package com.pavis.admin.aigc.service;
+
+import com.pavis.admin.aigc.common.model.req.ChatReq;
+import com.pavis.admin.aigc.common.model.req.ImageReq;
+
+/**
+ * @author tycoding
+ * @since 2024/1/4
+ */
+public interface PvsChatService {
+
+    void chat(ChatReq req);
+
+
+    /**
+     * 文本请求
+     */
+    String text(ChatReq req);
+
+    // /**
+    //  * 文生图
+    //  */
+    // AigcOss image(ImageReq req);
+}

+ 208 - 1
pavis-module-aigc/src/main/java/com/pavis/admin/aigc/service/impl/AigcAppServiceImpl.java

@@ -1,9 +1,45 @@
 package com.pavis.admin.aigc.service.impl;
 
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.util.IdUtil;
+import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.pavis.admin.aigc.common.model.req.ChatReq;
+import com.pavis.admin.aigc.common.model.req.ImageReq;
+import com.pavis.admin.aigc.common.utils.PromptUtil;
+import com.pavis.admin.aigc.core.llm.provider.EmbeddingProvider;
+import com.pavis.admin.aigc.core.llm.provider.ModelProvider;
+import com.pavis.admin.aigc.core.llm.service.Agent;
+import com.pavis.admin.aigc.mapper.AppKnowledgeMapper;
+import com.pavis.admin.aigc.mapper.KnowledgeMapper;
+import com.pavis.admin.aigc.model.entity.KnowledgeDO;
+
+import com.pavis.admin.aigc.model.resp.KnowledgeResp;
+import com.pavis.admin.aigc.model.resp.ModelResp;
+import com.pavis.admin.aigc.properties.ChatProps;
+import com.pavis.admin.aigc.service.KnowledgeService;
+import com.pavis.admin.aigc.service.ModelService;
+import dev.langchain4j.data.image.Image;
+import dev.langchain4j.memory.chat.MessageWindowChatMemory;
+import dev.langchain4j.model.chat.ChatLanguageModel;
+import dev.langchain4j.model.chat.StreamingChatLanguageModel;
+import dev.langchain4j.model.image.ImageModel;
+import dev.langchain4j.model.output.Response;
+import dev.langchain4j.rag.DefaultRetrievalAugmentor;
+import dev.langchain4j.rag.content.retriever.ContentRetriever;
+import dev.langchain4j.rag.content.retriever.EmbeddingStoreContentRetriever;
+import dev.langchain4j.rag.query.Query;
+import dev.langchain4j.service.AiServices;
+import dev.langchain4j.service.TokenStream;
+import dev.langchain4j.store.embedding.filter.Filter;
+import jakarta.annotation.PostConstruct;
 import lombok.RequiredArgsConstructor;
 
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
 
+import top.continew.starter.core.exception.BusinessException;
 import top.continew.starter.extension.crud.service.BaseServiceImpl;
 import com.pavis.admin.aigc.mapper.AigcAppMapper;
 import com.pavis.admin.aigc.model.entity.AigcAppDO;
@@ -13,6 +49,15 @@ import com.pavis.admin.aigc.model.resp.AigcAppDetailResp;
 import com.pavis.admin.aigc.model.resp.AigcAppResp;
 import com.pavis.admin.aigc.service.AigcAppService;
 
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+import static dev.langchain4j.store.embedding.filter.MetadataFilterBuilder.metadataKey;
+
 /**
  * AIGC应用配置业务实现
  *
@@ -21,4 +66,166 @@ import com.pavis.admin.aigc.service.AigcAppService;
  */
 @Service
 @RequiredArgsConstructor
-public class AigcAppServiceImpl extends BaseServiceImpl<AigcAppMapper, AigcAppDO, AigcAppResp, AigcAppDetailResp, AigcAppQuery, AigcAppReq> implements AigcAppService {}
+@Slf4j
+public class AigcAppServiceImpl extends BaseServiceImpl<AigcAppMapper, AigcAppDO, AigcAppResp, AigcAppDetailResp, AigcAppQuery, AigcAppReq> implements AigcAppService {
+
+    private static final Map<String, AigcAppDO> appMap = new HashMap<>();
+    private final ModelService aigcModelService;
+    private final KnowledgeService aigcKnowledgeService;
+    private final KnowledgeMapper knowledgeMapper;
+    private final AppKnowledgeMapper appKnowledgeMapper;
+    // longchat中的
+    private final ModelProvider provider;
+    private final EmbeddingProvider embeddingProvider;
+    private final ChatProps chatProps;
+
+    @Override
+    public List<AigcAppDO> list(AigcAppDO data) {
+        List<AigcAppDO> list = baseMapper.selectList(Wrappers.<AigcAppDO>lambdaQuery()
+                .like(StrUtil.isNotBlank(data.getName()), AigcAppDO::getName, data.getName()));
+        // copy--》AigcAppDO实体
+        List<AigcAppResp> aigcAppResps = BeanUtil.copyToList(list, AigcAppResp.class);
+
+        Map<Long, List<ModelResp>> modelMap = aigcModelService.list(new ModelResp()).stream().collect(Collectors.groupingBy(ModelResp::getId));
+        Map<Long, List<KnowledgeDO>> knowledgeMap = knowledgeMapper.selectList(new QueryWrapper<>()).stream().collect(Collectors.groupingBy(KnowledgeDO::getId));
+        aigcAppResps.forEach(i -> {
+            List<ModelResp> models = modelMap.get(i.getModelId());
+            if (models != null) {
+                i.setModel(models.get(0));
+            }
+            // 通过appid获取knowledgeIds
+            List<Long> appKnowledgeIds = appKnowledgeMapper.selectByKnowledgeIds(i.getId());
+            if (appKnowledgeIds != null) {
+                List<KnowledgeResp> knowledges = new ArrayList<>();
+                appKnowledgeIds.forEach(k -> {
+                    List<KnowledgeDO> items = knowledgeMap.get(k);
+                    KnowledgeResp knowledgeResp=new KnowledgeResp();
+                    BeanUtil.copyProperties(items.get(0),knowledgeResp);
+                    if (items != null) {
+                        knowledges.add(knowledgeResp);
+                    }
+                });
+                i.setKnowledges(knowledges);
+            }
+        });
+        return list;
+    }
+
+    @Override
+    public AigcAppDO getById(String id) {
+        // AigcAppDO app = baseMapper.selectById(id);
+        // if (app != null) {
+        //     String modelId = app.getModelId();
+        //     if (modelId != null) {
+        //         app.setModel(aigcModelService.selectById(modelId));
+        //     }
+        //     List<String> knowledgeIds = app.getKnowledgeIds();
+        //     if (knowledgeIds != null && !knowledgeIds.isEmpty()) {
+        //         app.setKnowledges(aigcKnowledgeService.list(Wrappers.<AigcKnowledge>lambdaQuery().in(AigcKnowledge::getId, knowledgeIds)));
+        //     }
+        // }
+        // return app;
+        return null;
+    }
+
+    @PostConstruct
+    public void init() {
+        log.info("initialize app config list...");
+        List<AigcAppDO> list = baseMapper.selectList(new QueryWrapper<>());
+        list.forEach(i -> appMap.put(i.getId().toString(), i));
+    }
+
+    public AigcAppDO get(String appId) {
+        return appMap.get(appId);
+    }
+
+    private AiServices<Agent> build(StreamingChatLanguageModel streamModel, ChatLanguageModel model, ChatReq req) {
+        AiServices<Agent> aiServices = AiServices.builder(Agent.class)
+                .chatMemoryProvider(memoryId -> MessageWindowChatMemory.builder()
+                        .id(req.getConversationId())
+                        .chatMemoryStore(new PersistentChatMemoryStore())
+                        .maxMessages(chatProps.getMemoryMaxMessage())
+                        .build());
+        if (StrUtil.isNotBlank(req.getPromptText())) {
+            aiServices.systemMessageProvider(memoryId -> req.getPromptText());
+        }
+        if (streamModel != null) {
+            aiServices.streamingChatLanguageModel(streamModel);
+        }
+        if (model != null) {
+            aiServices.chatLanguageModel(model);
+        }
+        return aiServices;
+    }
+
+    @Override
+    public TokenStream chat(ChatReq req) {
+        StreamingChatLanguageModel model = provider.stream(req.getModelId());
+        if (StrUtil.isBlank(req.getConversationId())) {
+            req.setConversationId(IdUtil.simpleUUID());
+        }
+
+        AiServices<Agent> aiServices = build(model, null, req);
+
+        if (StrUtil.isNotBlank(req.getKnowledgeId())) {
+            req.getKnowledgeIds().add(Long.valueOf(req.getKnowledgeId()));
+        }
+
+        if (req.getKnowledgeIds() != null && !req.getKnowledgeIds().isEmpty()) {
+            Function<Query, Filter> filter = (query) -> metadataKey("knowledgeId").isIn(req.getKnowledgeIds());
+            ContentRetriever contentRetriever = EmbeddingStoreContentRetriever.builder()
+                    .embeddingStore(embeddingProvider.getEmbeddingStore(req.getKnowledgeIds()))
+                    .embeddingModel(embeddingProvider.getEmbeddingModel(req.getKnowledgeIds()))
+                    .dynamicFilter(filter)
+                    .build();
+            aiServices.retrievalAugmentor(DefaultRetrievalAugmentor
+                    .builder()
+                    .contentRetriever(contentRetriever)
+                    .build());
+        }
+        Agent agent = aiServices.build();
+        return agent.stream(req.getConversationId(), req.getMessage());
+    }
+
+    @Override
+    public TokenStream singleChat(ChatReq req) {
+        StreamingChatLanguageModel model = provider.stream(req.getModelId());
+        if (StrUtil.isBlank(req.getConversationId())) {
+            req.setConversationId(IdUtil.simpleUUID());
+        }
+
+        Agent agent = build(model, null, req).build();
+        if (req.getPrompt() == null) {
+            req.setPrompt(PromptUtil.build(req.getMessage(), req.getPromptText()));
+        }
+        return agent.stream(req.getConversationId(), req.getPrompt().text());
+    }
+
+    @Override
+    public String text(ChatReq req) {
+        if (StrUtil.isBlank(req.getConversationId())) {
+            req.setConversationId(IdUtil.simpleUUID());
+        }
+
+        try {
+            ChatLanguageModel model = provider.text(req.getModelId());
+            Agent agent = build(null, model, req).build();
+            String text = agent.text(req.getConversationId(), req.getMessage());
+            return text;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+    @Override
+    public Response<Image> image(ImageReq req) {
+        try {
+            ImageModel model = provider.image(req.getModelId());
+            return model.generate(req.getPrompt().text());
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new BusinessException("图片生成失败");
+        }
+    }
+}

+ 8 - 1
pavis-module-aigc/src/main/java/com/pavis/admin/aigc/service/impl/EmbedStoreServiceImpl.java

@@ -13,6 +13,8 @@ import com.pavis.admin.aigc.model.resp.EmbedStoreDetailResp;
 import com.pavis.admin.aigc.model.resp.EmbedStoreResp;
 import com.pavis.admin.aigc.service.EmbedStoreService;
 
+import java.util.List;
+
 /**
  * 向量存储配置业务实现
  *
@@ -21,4 +23,9 @@ import com.pavis.admin.aigc.service.EmbedStoreService;
  */
 @Service
 @RequiredArgsConstructor
-public class EmbedStoreServiceImpl extends BaseServiceImpl<EmbedStoreMapper, EmbedStoreDO, EmbedStoreResp, EmbedStoreDetailResp, EmbedStoreQuery, EmbedStoreReq> implements EmbedStoreService {}
+public class EmbedStoreServiceImpl extends BaseServiceImpl<EmbedStoreMapper, EmbedStoreDO, EmbedStoreResp, EmbedStoreDetailResp, EmbedStoreQuery, EmbedStoreReq> implements EmbedStoreService {
+    @Override
+    public List<EmbedStoreResp>selEmbedStore(){
+       return baseMapper.selEmbedStore();
+    }
+}

+ 84 - 1
pavis-module-aigc/src/main/java/com/pavis/admin/aigc/service/impl/KnowledgeServiceImpl.java

@@ -1,9 +1,14 @@
 package com.pavis.admin.aigc.service.impl;
 
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.pavis.admin.aigc.mapper.DocChunkMapper;
+import com.pavis.admin.aigc.mapper.DocMapper;
 import lombok.RequiredArgsConstructor;
 
 import org.springframework.stereotype.Service;
 
+import org.springframework.transaction.annotation.Transactional;
 import top.continew.starter.extension.crud.service.BaseServiceImpl;
 import com.pavis.admin.aigc.mapper.KnowledgeMapper;
 import com.pavis.admin.aigc.model.entity.KnowledgeDO;
@@ -13,6 +18,9 @@ import com.pavis.admin.aigc.model.resp.KnowledgeDetailResp;
 import com.pavis.admin.aigc.model.resp.KnowledgeResp;
 import com.pavis.admin.aigc.service.KnowledgeService;
 
+import java.util.Date;
+import java.util.List;
+
 /**
  * AIGC知识库业务实现
  *
@@ -21,4 +29,79 @@ import com.pavis.admin.aigc.service.KnowledgeService;
  */
 @Service
 @RequiredArgsConstructor
-public class KnowledgeServiceImpl extends BaseServiceImpl<KnowledgeMapper, KnowledgeDO, KnowledgeResp, KnowledgeDetailResp, KnowledgeQuery, KnowledgeReq> implements KnowledgeService {}
+public class KnowledgeServiceImpl extends BaseServiceImpl<KnowledgeMapper, KnowledgeDO, KnowledgeResp, KnowledgeDetailResp, KnowledgeQuery, KnowledgeReq> implements KnowledgeService {
+
+    private final DocMapper aigcDocsMapper;
+    private final DocChunkMapper aigcDocsSliceMapper;
+
+    @Override
+    public List<KnowledgeResp>selectKnowledge(){
+        return baseMapper.selectKnowledge();
+    }
+
+    // @Override
+    // @Transactional
+    // public void addDocs(AigcDocs data) {
+    //     data.setCreateTime(new Date());
+    //     aigcDocsMapper.insert(data);
+    // }
+
+    // @Override
+    // @Transactional
+    // public void updateDocs(AigcDocs data) {
+    //     aigcDocsMapper.updateById(data);
+    // }
+
+    // @Override
+    // @Transactional
+    // public void addDocsSlice(AigcDocsSlice data) {
+    //     data.setCreateTime(new Date())
+    //             .setWordNum(data.getContent().length())
+    //             .setStatus(true)
+    //     ;
+    //     aigcDocsSliceMapper.insert(data);
+    // }
+
+    // @Override
+    // @Transactional
+    // public void updateDocsSlice(AigcDocsSlice data) {
+    //     aigcDocsSliceMapper.updateById(data);
+    // }
+
+    // @Override
+    // public List<String> listSliceVectorIdsOfDoc(String docsId) {
+    //     LambdaQueryWrapper<AigcDocsSlice> selectWrapper = Wrappers.<AigcDocsSlice>lambdaQuery()
+    //             .select(AigcDocsSlice::getVectorId)
+    //             .eq(AigcDocsSlice::getDocsId, docsId);
+    //     List<String> vectorIds = aigcDocsSliceMapper.selectList(selectWrapper)
+    //             .stream()
+    //             .map(AigcDocsSlice::getVectorId)
+    //             .toList();
+    //     log.debug("slices of doc: [{}], count: [{}]", docsId, vectorIds.size());
+    //     return vectorIds;
+    // }
+
+    // @Override
+    // public List<AigcDocs> getDocsByKb(String knowledgeId) {
+    //     return aigcDocsMapper.selectList(Wrappers.<AigcDocs>lambdaQuery()
+    //             .eq(AigcDocs::getKnowledgeId, knowledgeId));
+    // }
+
+    // @Override
+    // @Transactional
+    // public void removeKnowledge(String knowledgeId) {
+    //     baseMapper.deleteById(knowledgeId);
+    //     // del docs & docsSlice
+    //     List<String> docsIds = getDocsByKb(knowledgeId).stream().map(AigcDocs::getId).toList();
+    //     docsIds.forEach(this::removeSlicesOfDoc);
+    // }
+    //
+    // @Override
+    // @Transactional
+    // public void removeSlicesOfDoc(String docsId) {
+    //     LambdaQueryWrapper<AigcDocsSlice> deleteWrapper = Wrappers.<AigcDocsSlice>lambdaQuery()
+    //             .eq(AigcDocsSlice::getDocsId, docsId);
+    //     int count = aigcDocsSliceMapper.delete(deleteWrapper);
+    //     log.debug("remove all slices of doc: [{}], count: [{}]", docsId, count);
+    // }
+}

+ 8 - 1
pavis-module-aigc/src/main/java/com/pavis/admin/aigc/service/impl/ModelSecretServiceImpl.java

@@ -1,5 +1,10 @@
 package com.pavis.admin.aigc.service.impl;
 
+import cn.crane4j.annotation.ContainerMethod;
+import cn.crane4j.annotation.MappingType;
+import cn.hutool.core.bean.BeanUtil;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.pavis.admin.common.constant.ContainerConstants;
 import lombok.RequiredArgsConstructor;
 
 import org.springframework.stereotype.Service;
@@ -21,4 +26,6 @@ import com.pavis.admin.aigc.service.ModelSecretService;
  */
 @Service
 @RequiredArgsConstructor
-public class ModelSecretServiceImpl extends BaseServiceImpl<ModelSecretMapper, ModelSecretDO, ModelSecretResp, ModelSecretDetailResp, ModelSecretQuery, ModelSecretReq> implements ModelSecretService {}
+public class ModelSecretServiceImpl extends BaseServiceImpl<ModelSecretMapper, ModelSecretDO, ModelSecretResp, ModelSecretDetailResp, ModelSecretQuery, ModelSecretReq> implements ModelSecretService {
+
+}

+ 281 - 1
pavis-module-aigc/src/main/java/com/pavis/admin/aigc/service/impl/ModelServiceImpl.java

@@ -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);
+    }
+}

+ 95 - 0
pavis-module-aigc/src/main/java/com/pavis/admin/aigc/service/impl/PersistentChatMemoryStore.java

@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved.
+ *
+ * Licensed under the GNU Affero General Public License, Version 3 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     https://www.gnu.org/licenses/agpl-3.0.html
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.pavis.admin.aigc.service.impl;
+
+import dev.langchain4j.data.message.ChatMessage;
+import dev.langchain4j.data.message.SystemMessage;
+import dev.langchain4j.store.memory.chat.ChatMemoryStore;
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author tycoding
+ * @since 2024/8/15
+ */
+@Slf4j
+public class PersistentChatMemoryStore implements ChatMemoryStore {
+
+    private static final Map<Object, List<ChatMessage>> store = new HashMap<>();
+    private static final Map<Object, Boolean> initSystemMessageStore = new HashMap<>();
+    private static final Map<Object, Boolean> initMessageStore = new HashMap<>();
+
+    public static void clean(Object memoryId) {
+        log.info("clean message memory store to: {}", memoryId);
+        store.remove(memoryId);
+    }
+
+    public static void init(Object memoryId, SystemMessage message) {
+        Boolean isInitSystemMessage = initSystemMessageStore.get(memoryId);
+        if (isInitSystemMessage != null && isInitSystemMessage) {
+            return;
+        }
+
+        List<ChatMessage> list = store.get(memoryId);
+        if (list == null) {
+            store.put(memoryId, new ArrayList<>(List.of(message)));
+        } else {
+            list.add(message);
+        }
+        initSystemMessageStore.put(memoryId, true);
+    }
+
+    public static void init(Object memoryId, List<ChatMessage> messages) {
+        log.info("initialize message memory store to: {}", memoryId);
+
+        Boolean isInitMessage = initMessageStore.get(memoryId);
+        if (isInitMessage != null && isInitMessage) {
+            return;
+        }
+
+        List<ChatMessage> list = store.get(memoryId);
+        if (list == null) {
+            store.put(memoryId, messages);
+        } else {
+            list.addAll(messages);
+        }
+        initMessageStore.put(memoryId, true);
+    }
+
+    @Override
+    public List<ChatMessage> getMessages(Object memoryId) {
+        List<ChatMessage> list = store.get(memoryId);
+        if (list == null) {
+            return new ArrayList<>();
+        }
+        return list;
+    }
+
+    @Override
+    public void updateMessages(Object memoryId, List<ChatMessage> messages) {
+        store.put(memoryId, messages);
+    }
+
+    @Override
+    public void deleteMessages(Object memoryId) {
+        store.remove(memoryId);
+    }
+}

+ 141 - 0
pavis-module-aigc/src/main/java/com/pavis/admin/aigc/service/impl/PvsChatServiceImpl.java

@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved.
+ *
+ * Licensed under the GNU Affero General Public License, Version 3 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     https://www.gnu.org/licenses/agpl-3.0.html
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.pavis.admin.aigc.service.impl;
+
+import cn.hutool.core.util.StrUtil;
+import com.pavis.admin.aigc.common.model.req.ChatReq;
+import com.pavis.admin.aigc.common.model.req.ImageReq;
+import com.pavis.admin.aigc.common.model.resp.ChatResp;
+import com.pavis.admin.aigc.common.utils.StreamEmitter;
+import com.pavis.admin.aigc.enums.RoleEnum;
+import com.pavis.admin.aigc.mapper.AigcMessageMapper;
+import com.pavis.admin.aigc.mapper.AppKnowledgeMapper;
+import com.pavis.admin.aigc.model.entity.AigcAppDO;
+import com.pavis.admin.aigc.model.entity.AigcMessageDO;
+import com.pavis.admin.aigc.service.AigcAppService;
+import com.pavis.admin.aigc.service.AigcMessageService;
+import com.pavis.admin.aigc.service.PvsChatService;
+import dev.langchain4j.data.image.Image;
+import dev.langchain4j.model.output.Response;
+import dev.langchain4j.model.output.TokenUsage;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.BeanUtils;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+/**
+ * @author tycoding
+ * @since 2024/1/4
+ */
+@Slf4j
+@Service
+@AllArgsConstructor
+public class PvsChatServiceImpl implements PvsChatService {
+
+    private final AigcAppService aigcAppService;
+    private final AigcMessageService aigcMessageService;
+    // private final AppStore appStore;
+    // private final AigcAppService aigcAppService;
+    private final AppKnowledgeMapper appKnowledgeMapper;
+    private final AigcMessageMapper aigcMessageMapper;
+
+    @Override
+    public void chat(ChatReq req) {
+        StreamEmitter emitter = req.getEmitter();
+        long startTime = System.currentTimeMillis();
+        StringBuilder text = new StringBuilder();
+
+        if (StrUtil.isNotBlank(req.getAppId())) {
+            AigcAppDO app = aigcAppService.get(req.getAppId());
+            if (app != null) {
+                req.setModelId(app.getModelId().toString());
+                req.setPromptText(app.getPrompt());
+                List<Long> longs = appKnowledgeMapper.selectByKnowledgeIds(app.getId());
+                req.setKnowledgeIds(longs);
+            }
+        }
+
+        // save user message
+        req.setRole(RoleEnum.USER.getName());
+        saveMessage(req, 0, 0);
+
+        try {
+            aigcAppService
+                    .chat(req)
+                    .onNext(e -> {
+                        text.append(e);
+                        emitter.send(new ChatResp(e));
+                    })
+                    .onComplete((e) -> {
+                        TokenUsage tokenUsage = e.tokenUsage();
+                        ChatResp res = new ChatResp(tokenUsage.totalTokenCount(), startTime);
+                        emitter.send(res);
+                        emitter.complete();
+
+                        // save assistant message
+                        req.setMessage(text.toString());
+                        req.setRole(RoleEnum.ASSISTANT.getName());
+                        saveMessage(req, tokenUsage.inputTokenCount(), tokenUsage.outputTokenCount());
+                    })
+                    .onError((e) -> {
+                        emitter.error(e.getMessage());
+                        throw new RuntimeException(e.getMessage());
+                    })
+                    .start();
+        } catch (Exception e) {
+            e.printStackTrace();
+            emitter.error(e.getMessage());
+            throw new RuntimeException(e.getMessage());
+        }
+    }
+
+    private void saveMessage(ChatReq req, Integer inputToken, Integer outputToken) {
+        if (req.getConversationId() != null) {
+            AigcMessageDO message = new AigcMessageDO();
+            BeanUtils.copyProperties(req, message);
+            // message.setIp(ServletUtil.getIpAddr());
+            // message.setPromptTokens(inputToken);
+            message.setUsage(outputToken.toString());
+            // aigcMessageService.addMessage(message);
+            aigcMessageMapper.insert(message);
+        }
+    }
+
+    @Override
+    public String text(ChatReq req) {
+        String text;
+        try {
+            text = aigcAppService.text(req);
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new RuntimeException(e.getMessage());
+        }
+        return text;
+    }
+
+    // @Override
+    // public AigcOss image(ImageReq req) {
+    //     Response<Image> res = aigcAppService.image(req);
+    //
+    //     String path = res.content().url().toString();
+    //     AigcOss oss = new AigcOss();
+    //     oss.setUrl(path);
+    //     return oss;
+    // }
+}

+ 15 - 0
pavis-module-aigc/src/main/resources/ModelMapper.xml

@@ -1,4 +1,19 @@
 <?xml version="1.0" encoding="UTF-8" ?>
 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
 <mapper namespace="com.pavis.admin.aigc.mapper.ModelMapper">
+
+<!--    <select id="selModelAndSecretList" resultType="java.util.List">-->
+<!--        SELECT-->
+<!--        *-->
+<!--        FROM-->
+<!--        aigc_model am-->
+<!--        LEFT JOIN aigc_model_secret ams ON am.id = ams.model_id-->
+<!--        WHERE 1=1-->
+<!--        <if test="type != null">-->
+<!--            AND am.type=#{type}-->
+<!--        </if>-->
+<!--        <if test="provider != null">-->
+<!--            AND am.provider=#{provider}-->
+<!--        </if>-->
+<!--    </select>-->
 </mapper>

+ 29 - 1
pavis-webapi/src/main/java/com/pavis/admin/controller/aigc/AigcAppController.java

@@ -1,5 +1,13 @@
 package com.pavis.admin.controller.aigc;
 
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import com.pavis.admin.aigc.common.model.req.ChatReq;
+import com.pavis.admin.aigc.common.utils.StreamEmitter;
+import com.pavis.admin.aigc.service.PvsChatService;
+import com.pavis.admin.common.context.UserContextHolder;
+import io.swagger.v3.oas.annotations.Operation;
+import lombok.AllArgsConstructor;
+import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
 import top.continew.starter.extension.crud.enums.Api;
 
 import io.swagger.v3.oas.annotations.tags.Tag;
@@ -14,6 +22,9 @@ import com.pavis.admin.aigc.model.resp.AigcAppDetailResp;
 import com.pavis.admin.aigc.model.resp.AigcAppResp;
 import com.pavis.admin.aigc.service.AigcAppService;
 
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
 /**
  * AIGC应用配置管理 API
  *
@@ -23,4 +34,21 @@ import com.pavis.admin.aigc.service.AigcAppService;
 @Tag(name = "AIGC应用配置管理 API")
 @RestController
 @CrudRequestMapping(value = "/aigc/app", api = {Api.PAGE, Api.GET, Api.CREATE, Api.UPDATE, Api.DELETE, Api.EXPORT})
-public class AigcAppController extends BaseController<AigcAppService, AigcAppResp, AigcAppDetailResp, AigcAppQuery, AigcAppReq> {}
+@AllArgsConstructor
+
+public class AigcAppController extends BaseController<AigcAppService, AigcAppResp, AigcAppDetailResp, AigcAppQuery, AigcAppReq> {
+    private final PvsChatService pvsChatService;
+    @PostMapping("/chat/completions")
+    @Operation(summary = "聊天接口", description = "聊天接口")
+    public SseEmitter chat(@RequestBody ChatReq req) {
+        StreamEmitter emitter = new StreamEmitter();
+        req.setEmitter(emitter);
+        req.setUserId(UserContextHolder.getUserId().toString());
+        req.setUsername(UserContextHolder.getUsername());
+        ExecutorService executor = Executors.newSingleThreadExecutor();
+        req.setExecutor(executor);
+        return emitter.streaming(executor, () -> {
+            pvsChatService.chat(req);
+        });
+    }
+}

+ 33 - 1
pavis-webapi/src/main/java/com/pavis/admin/controller/aigc/ModelController.java

@@ -1,5 +1,9 @@
 package com.pavis.admin.controller.aigc;
 
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import io.swagger.v3.oas.annotations.Operation;
+import jakarta.servlet.http.HttpServletResponse;
+import org.springframework.http.MediaType;
 import top.continew.starter.extension.crud.enums.Api;
 
 import io.swagger.v3.oas.annotations.tags.Tag;
@@ -13,6 +17,10 @@ import com.pavis.admin.aigc.model.req.ModelReq;
 import com.pavis.admin.aigc.model.resp.ModelDetailResp;
 import com.pavis.admin.aigc.model.resp.ModelResp;
 import com.pavis.admin.aigc.service.ModelService;
+import top.continew.starter.extension.crud.service.BaseService;
+
+import java.io.IOException;
+import java.util.List;
 
 /**
  * AIGC模型配置管理 API
@@ -23,4 +31,28 @@ import com.pavis.admin.aigc.service.ModelService;
 @Tag(name = "AIGC模型配置管理 API")
 @RestController
 @CrudRequestMapping(value = "/aigc/model", api = {Api.PAGE, Api.GET, Api.CREATE, Api.UPDATE, Api.DELETE, Api.EXPORT})
-public class ModelController extends BaseController<ModelService, ModelResp, ModelDetailResp, ModelQuery, ModelReq> {}
+public class ModelController extends BaseController<ModelService, ModelResp, ModelDetailResp, ModelQuery, ModelReq> {
+    @Operation(summary = "模型和配置新增接口", description = "模型和配置新增接口")
+    @PostMapping(value = "/insModelAndSecret")
+    public void insModelAndSecret(@RequestBody ModelReq modelReq){
+        baseService.insModelAndSecret(modelReq);
+    }
+
+    @Operation(summary = "模型和配置查询接口", description = "模型和配置查询接口")
+    @PostMapping(value = "/selModelAndSecrt")
+    public List<ModelResp> selModelAndSecrt(@RequestBody ModelQuery modelQuery){
+        return baseService.selModelAndSecrtList(modelQuery);
+    }
+
+    @Operation(summary = "模型和配置更新接口", description = "模型和配置更新接口")
+    @PostMapping(value = "/updateModelAndSecrt")
+    public void updateModelAndSecrt(@RequestBody ModelResp modelResp){
+        baseService.updateModelAndSecrt(modelResp);
+    }
+
+    @Operation(summary = "模型和配置删除接口", description = "模型和配置删除接口")
+    @PostMapping(value = "/delModelAndSecrt")
+    public void delModelAndSecrt(List<Long>ids){
+        baseService.delModelAndSecrt(ids);
+    }
+}

+ 2 - 1
pavis-webapi/src/main/resources/db/changelog/mysql/main_table.sql

@@ -451,6 +451,7 @@ CREATE TABLE IF NOT EXISTS `aigc_model_secret`
     `secret_key`  VARCHAR(255) DEFAULT NULL COMMENT '密钥',
     `base_url`    VARCHAR(255) DEFAULT NULL COMMENT '基础URL',
     `api_version` VARCHAR(50)  DEFAULT NULL COMMENT 'API版本',
+    `dimension`     INT                     COMMENT '向量维数',
     `create_user` BIGINT          NOT NULL COMMENT '创建人',
     `create_time` DATETIME        NOT NULL COMMENT '创建时间',
     `update_user` BIGINT       DEFAULT NULL COMMENT '修改人',
@@ -749,7 +750,7 @@ CREATE TABLE IF NOT EXISTS `aigc_message`
     `content_metadata`  JSON         DEFAULT NULL COMMENT '元数据',
     `model_id`          BIGINT UNSIGNED                                      NOT NULL COMMENT '调用模型ID',
     `model_version`     VARCHAR(50)  DEFAULT NULL COMMENT '模型版本快照',
-    `usage`            json  NOT NULL COMMENT 'token消耗 {"prompt_tokens":20,"completion_tokens":50,"total_tokens":70}',
+    `usage`             json                                                 NOT NULL COMMENT 'token消耗 {"prompt_tokens":20,"completion_tokens":50,"total_tokens":70}',
     `status`            TINYINT(1)   DEFAULT 1 COMMENT '状态:0删除 1正常 2标记',
     `error_info`        TEXT         DEFAULT NULL COMMENT '错误信息',
     `is_sensitive`      TINYINT(1)   DEFAULT 0 COMMENT '敏感内容标记',