fix: chatai代码迁移及修改
This commit is contained in:
parent
27a28c6817
commit
dab9fa382a
|
@ -0,0 +1,27 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>cn.iocoder.cloud</groupId>
|
||||
<artifactId>yudao-module-langchat</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>yudao-module-langchat-api</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>${project.artifactId}</name>
|
||||
|
||||
<description>
|
||||
langchat 模块 API,暴露给其它模块调用
|
||||
</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.cloud</groupId>
|
||||
<artifactId>yudao-common</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
|
@ -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 cn.iocoder.yudao.module.langchat.enums;
|
||||
|
||||
/**
|
||||
* @author tycoding
|
||||
* @since 2024/1/15
|
||||
*/
|
||||
public interface CacheConst {
|
||||
|
||||
/**
|
||||
* 系统所有Redis缓存Key前缀 prefix
|
||||
*/
|
||||
String REDIS_KEY_PREFIX = "langchat:";
|
||||
|
||||
/**
|
||||
* Auth缓存前缀
|
||||
*/
|
||||
String AUTH_PREFIX = REDIS_KEY_PREFIX + "auth:";
|
||||
|
||||
/**
|
||||
* Auth Session缓存前缀
|
||||
*/
|
||||
String AUTH_SESSION_PREFIX = AUTH_PREFIX + "session:";
|
||||
|
||||
/**
|
||||
* Auth Session缓存变量前缀
|
||||
*/
|
||||
String AUTH_USER_INFO_KEY = "USER_INFO";
|
||||
|
||||
/**
|
||||
* Auth Token缓存变量前缀
|
||||
*/
|
||||
String AUTH_TOKEN_INFO_KEY = "TOKEN_INFO";
|
||||
|
||||
/**
|
||||
* 用户信息缓存
|
||||
*/
|
||||
String USER_DETAIL_KEY = REDIS_KEY_PREFIX + "user_details";
|
||||
|
||||
/**
|
||||
* 验证码缓存前缀
|
||||
*/
|
||||
String CAPTCHA_PREFIX = REDIS_KEY_PREFIX + "auth:captcha:";
|
||||
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
package cn.iocoder.yudao.module.langchat.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
/**
|
||||
* @author GB
|
||||
* @desc
|
||||
* @since 2024-08-21
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
public enum ChatErrorEnum {
|
||||
API_KEY_IS_NULL(1000, "模型 %s %s api key 为空,请检查配置"),
|
||||
BASE_URL_IS_NULL(1003, "模型 %s %s base url 为空,请检查配置"),
|
||||
SECRET_KEY_IS_NULL(1005, "模型 %s %s base secret Key 为空,请检查配置"),
|
||||
;
|
||||
|
||||
/**
|
||||
* 错误码
|
||||
*/
|
||||
private int errorCode;
|
||||
/**
|
||||
* 错误描述,用于展示给用户
|
||||
*/
|
||||
private String errorDesc;
|
||||
|
||||
public int getErrorCode() {
|
||||
return this.errorCode;
|
||||
}
|
||||
|
||||
public String getErrorDesc(String modelName, String type) {
|
||||
return this.errorDesc.formatted(modelName, type);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* 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 cn.iocoder.yudao.module.langchat.enums;
|
||||
|
||||
/**
|
||||
* @author tycoding
|
||||
* @since 2024/6/6
|
||||
*/
|
||||
public interface EmbedConst {
|
||||
|
||||
String ORIGIN_TYPE_INPUT = "INPUT";
|
||||
String ORIGIN_TYPE_UPLOAD = "UPLOAD";
|
||||
|
||||
String KNOWLEDGE = "knowledgeId";
|
||||
String FILENAME = "docsName";
|
||||
|
||||
String CLAZZ_NAME_OPENAI = "OpenAiEmbeddingModel";
|
||||
String CLAZZ_NAME_QIANFAN = "QianfanEmbeddingModel";
|
||||
String CLAZZ_NAME_QIANWEN = "QwenEmbeddingModel";
|
||||
String CLAZZ_NAME_ZHIPU = "ZhipuAiEmbeddingModel";
|
||||
String CLAZZ_NAME_OLLAMA = "OllamaEmbeddingModel";
|
||||
}
|
|
@ -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 cn.iocoder.yudao.module.langchat.enums;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* @author tycoding
|
||||
* @since 2024/10/28
|
||||
*/
|
||||
@Getter
|
||||
public enum EmbedStoreEnum {
|
||||
|
||||
REDIS,
|
||||
PGVECTOR,
|
||||
MILVUS,
|
||||
;
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* 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 cn.iocoder.yudao.module.langchat.enums;
|
||||
|
||||
/**
|
||||
* @author tycoding
|
||||
* @since 2024/1/6
|
||||
*/
|
||||
public interface ModelConst {
|
||||
|
||||
String TEXT_SUFFIX = "_text";
|
||||
String IMAGE_SUFFIX = "_image";
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* 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 cn.iocoder.yudao.module.langchat.enums;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* @author tycoding
|
||||
* @since 2024/6/16
|
||||
*/
|
||||
@Getter
|
||||
public enum ProviderEnum {
|
||||
|
||||
OPENAI,
|
||||
AZURE_OPENAI,
|
||||
GEMINI,
|
||||
OLLAMA,
|
||||
CLAUDE,
|
||||
Q_FAN,
|
||||
Q_WEN,
|
||||
ZHIPU,
|
||||
YI,
|
||||
DOUYIN,
|
||||
DEEPSEEK,
|
||||
SILICON,
|
||||
SPARK,
|
||||
;
|
||||
|
||||
}
|
|
@ -12,11 +12,100 @@
|
|||
<packaging>jar</packaging>
|
||||
|
||||
<name>${project.artifactId}</name>
|
||||
|
||||
<properties>
|
||||
<langchain4j.version>1.0.0-beta1</langchain4j.version>
|
||||
</properties>
|
||||
|
||||
<description>
|
||||
|
||||
</description>
|
||||
|
||||
<dependencies>
|
||||
<!--langchat4j-->
|
||||
<dependency>
|
||||
<groupId>dev.langchain4j</groupId>
|
||||
<artifactId>langchain4j</artifactId>
|
||||
<version>${langchain4j.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>dev.langchain4j</groupId>
|
||||
<artifactId>langchain4j-core</artifactId>
|
||||
<version>${langchain4j.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>dev.langchain4j</groupId>
|
||||
<artifactId>langchain4j-community-redis</artifactId>
|
||||
<version>${langchain4j.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>dev.langchain4j</groupId>
|
||||
<artifactId>langchain4j-pgvector</artifactId>
|
||||
<version>${langchain4j.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>dev.langchain4j</groupId>
|
||||
<artifactId>langchain4j-milvus</artifactId>
|
||||
<version>${langchain4j.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>dev.langchain4j</groupId>
|
||||
<artifactId>langchain4j-document-parser-apache-tika</artifactId>
|
||||
<version>${langchain4j.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>dev.langchain4j</groupId>
|
||||
<artifactId>langchain4j-ollama</artifactId>
|
||||
<version>${langchain4j.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>dev.langchain4j</groupId>
|
||||
<artifactId>langchain4j-open-ai</artifactId>
|
||||
<version>${langchain4j.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>dev.langchain4j</groupId>
|
||||
<artifactId>langchain4j-community-qianfan</artifactId>
|
||||
<version>${langchain4j.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>dev.langchain4j</groupId>
|
||||
<artifactId>langchain4j-community-dashscope</artifactId>
|
||||
<version>${langchain4j.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-simple</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>dev.langchain4j</groupId>
|
||||
<artifactId>langchain4j-community-zhipu-ai</artifactId>
|
||||
<version>${langchain4j.version}</version>
|
||||
</dependency>
|
||||
<!-- OSS -->
|
||||
<dependency>
|
||||
<groupId>org.dromara.x-file-storage</groupId>
|
||||
<artifactId>x-file-storage-core</artifactId>
|
||||
<version>2.2.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.qiniu</groupId>
|
||||
<artifactId>qiniu-java-sdk</artifactId>
|
||||
<version>7.12.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.aliyun.oss</groupId>
|
||||
<artifactId>aliyun-sdk-oss</artifactId>
|
||||
<version>3.16.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.qcloud</groupId>
|
||||
<artifactId>cos_api</artifactId>
|
||||
<version>5.6.137</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Spring Cloud 基础 -->
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.cloud</groupId>
|
||||
|
@ -24,13 +113,15 @@
|
|||
</dependency>
|
||||
|
||||
<!-- 依赖服务 -->
|
||||
|
||||
<!-- <dependency>
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.cloud</groupId>
|
||||
<artifactId>yudao-module-infra-api</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>-->
|
||||
|
||||
<artifactId>yudao-common</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.cloud</groupId>
|
||||
<artifactId>yudao-module-langchat-api</artifactId>
|
||||
<version>2.3.0-jdk8-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<!-- 业务组件 -->
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.cloud</groupId>
|
||||
|
|
|
@ -16,17 +16,21 @@
|
|||
|
||||
package cn.iocoder.yudao.module.langchat;
|
||||
|
||||
import org.mybatis.spring.annotation.MapperScan;
|
||||
import cn.iocoder.yudao.module.langchat.config.EnableFileStorage;
|
||||
import cn.iocoder.yudao.module.langchat.config.SpringFileStorageProperties;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
|
||||
/**
|
||||
* @author tycoding
|
||||
* @since 2024/2/4
|
||||
*/
|
||||
//@EnableFileStorage
|
||||
@EnableFileStorage
|
||||
@SpringBootApplication
|
||||
@MapperScan("cn.iocoder.yudao.module.langchat.server.mapper")
|
||||
@EnableConfigurationProperties({
|
||||
SpringFileStorageProperties.class,
|
||||
})
|
||||
public class LangChatApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.iocoder.yudao.module.langchat.server.annotation;
|
||||
package cn.iocoder.yudao.module.langchat.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.iocoder.yudao.module.langchat.server.component;
|
||||
package cn.iocoder.yudao.module.langchat.component;
|
||||
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.iocoder.yudao.module.langchat.server.component;
|
||||
package cn.iocoder.yudao.module.langchat.component;
|
||||
|
||||
import lombok.Getter;
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.iocoder.yudao.module.langchat.server.component;
|
||||
package cn.iocoder.yudao.module.langchat.component;
|
||||
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.iocoder.yudao.module.langchat.server.component;
|
||||
package cn.iocoder.yudao.module.langchat.component;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* 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 cn.iocoder.yudao.module.langchat.config;
|
||||
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 启用文件存储,会自动根据配置文件进行加载
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.TYPE})
|
||||
@Documented
|
||||
@Import({FileStorageAutoConfiguration.class, SpringFileStorageProperties.class})
|
||||
public @interface EnableFileStorage {}
|
|
@ -0,0 +1,203 @@
|
|||
/*
|
||||
* 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 cn.iocoder.yudao.module.langchat.config;
|
||||
|
||||
import cn.iocoder.yudao.module.langchat.wrapper.MultipartFileWrapperAdapter;
|
||||
import lombok.NonNull;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.x.file.storage.core.FileStorageService;
|
||||
import org.dromara.x.file.storage.core.FileStorageServiceBuilder;
|
||||
import org.dromara.x.file.storage.core.aspect.FileStorageAspect;
|
||||
import org.dromara.x.file.storage.core.file.FileWrapperAdapter;
|
||||
import org.dromara.x.file.storage.core.platform.FileStorage;
|
||||
import org.dromara.x.file.storage.core.platform.FileStorageClientFactory;
|
||||
import org.dromara.x.file.storage.core.recorder.DefaultFileRecorder;
|
||||
import org.dromara.x.file.storage.core.recorder.FileRecorder;
|
||||
import org.dromara.x.file.storage.core.tika.ContentTypeDetect;
|
||||
import org.dromara.x.file.storage.core.tika.DefaultTikaFactory;
|
||||
import org.dromara.x.file.storage.core.tika.TikaContentTypeDetect;
|
||||
import org.dromara.x.file.storage.core.tika.TikaFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.event.ContextRefreshedEvent;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Slf4j
|
||||
@Configuration
|
||||
@ConditionalOnMissingBean(FileStorageService.class)
|
||||
public class FileStorageAutoConfiguration {
|
||||
|
||||
@Autowired
|
||||
private SpringFileStorageProperties properties;
|
||||
|
||||
@Autowired
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
/**
|
||||
* 当没有找到 FileRecorder 时使用默认的 FileRecorder
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(FileRecorder.class)
|
||||
public FileRecorder fileRecorder() {
|
||||
log.warn("没有找到 FileRecorder 的实现类,文件上传之外的部分功能无法正常使用,必须实现该接口才能使用完整功能!");
|
||||
return new DefaultFileRecorder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tika 工厂类型,用于识别上传的文件的 MINE
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(TikaFactory.class)
|
||||
public TikaFactory tikaFactory() {
|
||||
return new DefaultTikaFactory();
|
||||
}
|
||||
|
||||
/**
|
||||
* 识别文件的 MIME 类型
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(ContentTypeDetect.class)
|
||||
public ContentTypeDetect contentTypeDetect(TikaFactory tikaFactory) {
|
||||
return new TikaContentTypeDetect(tikaFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件存储服务
|
||||
*/
|
||||
@Bean(destroyMethod = "destroy")
|
||||
public FileStorageService fileStorageService(
|
||||
FileRecorder fileRecorder,
|
||||
@Autowired(required = false) List<List<? extends FileStorage>> fileStorageLists,
|
||||
@Autowired(required = false) List<FileStorageAspect> aspectList,
|
||||
@Autowired(required = false) List<FileWrapperAdapter> fileWrapperAdapterList,
|
||||
ContentTypeDetect contentTypeDetect,
|
||||
@Autowired(required = false) List<List<FileStorageClientFactory<?>>> clientFactoryList) {
|
||||
|
||||
if (fileStorageLists == null) fileStorageLists = new ArrayList<>();
|
||||
if (aspectList == null) aspectList = new ArrayList<>();
|
||||
if (fileWrapperAdapterList == null) fileWrapperAdapterList = new ArrayList<>();
|
||||
if (clientFactoryList == null) clientFactoryList = new ArrayList<>();
|
||||
|
||||
FileStorageServiceBuilder builder = FileStorageServiceBuilder.create(properties.toFileStorageProperties())
|
||||
.setFileRecorder(fileRecorder)
|
||||
.setAspectList(aspectList)
|
||||
.setContentTypeDetect(contentTypeDetect)
|
||||
.setFileWrapperAdapterList(fileWrapperAdapterList)
|
||||
.setClientFactoryList(clientFactoryList);
|
||||
|
||||
fileStorageLists.forEach(builder::addFileStorage);
|
||||
|
||||
if (properties.getEnableByteFileWrapper()) {
|
||||
builder.addByteFileWrapperAdapter();
|
||||
}
|
||||
if (properties.getEnableUriFileWrapper()) {
|
||||
builder.addUriFileWrapperAdapter();
|
||||
}
|
||||
if (properties.getEnableInputStreamFileWrapper()) {
|
||||
builder.addInputStreamFileWrapperAdapter();
|
||||
}
|
||||
if (properties.getEnableLocalFileWrapper()) {
|
||||
builder.addLocalFileWrapperAdapter();
|
||||
}
|
||||
if (properties.getEnableHttpServletRequestFileWrapper()) {
|
||||
if (FileStorageServiceBuilder.doesNotExistClass("javax.servlet.http.HttpServletRequest")
|
||||
&& FileStorageServiceBuilder.doesNotExistClass("jakarta.servlet.http.HttpServletRequest")) {
|
||||
log.warn(
|
||||
"当前未检测到 Servlet 环境,无法加载 HttpServletRequest 的文件包装适配器,请将参数【dromara.x-file-storage.enable-http-servlet-request-file-wrapper】设置为 【false】来消除此警告");
|
||||
} else {
|
||||
builder.addHttpServletRequestFileWrapperAdapter();
|
||||
}
|
||||
}
|
||||
if (properties.getEnableMultipartFileWrapper()) {
|
||||
if (FileStorageServiceBuilder.doesNotExistClass("org.springframework.web.multipart.MultipartFile")) {
|
||||
log.warn(
|
||||
"当前未检测到 SpringWeb 环境,无法加载 MultipartFile 的文件包装适配器,请将参数【dromara.x-file-storage.enable-multipart-file-wrapper】设置为 【false】来消除此警告");
|
||||
} else {
|
||||
builder.addFileWrapperAdapter(new MultipartFileWrapperAdapter());
|
||||
}
|
||||
}
|
||||
|
||||
if (FileStorageServiceBuilder.doesNotExistClass("org.springframework.web.servlet.config.annotation.WebMvcConfigurer")) {
|
||||
long localAccessNum = properties.getLocal().stream()
|
||||
.filter(SpringFileStorageProperties.SpringLocalConfig::getEnableStorage)
|
||||
.filter(SpringFileStorageProperties.SpringLocalConfig::getEnableAccess)
|
||||
.count();
|
||||
long localPlusAccessNum = properties.getLocalPlus().stream()
|
||||
.filter(SpringFileStorageProperties.SpringLocalPlusConfig::getEnableStorage)
|
||||
.filter(SpringFileStorageProperties.SpringLocalPlusConfig::getEnableAccess)
|
||||
.count();
|
||||
|
||||
if (localAccessNum + localPlusAccessNum > 0) {
|
||||
log.warn("当前未检测到 SpringWeb 环境,无法开启本地存储平台的本地访问功能,请将关闭本地访问来消除此警告");
|
||||
}
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 对 FileStorageService 注入自己的代理对象,不然会导致针对 FileStorageService 的代理方法不生效
|
||||
*/
|
||||
@EventListener(ContextRefreshedEvent.class)
|
||||
public void onContextRefreshedEvent() {
|
||||
FileStorageService service = applicationContext.getBean(FileStorageService.class);
|
||||
service.setSelf(service);
|
||||
}
|
||||
|
||||
/**
|
||||
* 本地存储文件访问自动配置类
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnClass(name = "org.springframework.web.servlet.config.annotation.WebMvcConfigurer")
|
||||
public static class FileStorageLocalFileAccessAutoConfiguration {
|
||||
@Autowired
|
||||
private SpringFileStorageProperties properties;
|
||||
|
||||
/**
|
||||
* 配置本地存储的访问地址
|
||||
*/
|
||||
@Bean
|
||||
public WebMvcConfigurer fileStorageWebMvcConfigurer() {
|
||||
return new WebMvcConfigurer() {
|
||||
@Override
|
||||
public void addResourceHandlers(@NonNull ResourceHandlerRegistry registry) {
|
||||
for (SpringFileStorageProperties.SpringLocalConfig local : properties.getLocal()) {
|
||||
if (local.getEnableStorage() && local.getEnableAccess()) {
|
||||
registry.addResourceHandler(local.getPathPatterns())
|
||||
.addResourceLocations("file:" + local.getBasePath());
|
||||
}
|
||||
}
|
||||
for (SpringFileStorageProperties.SpringLocalPlusConfig local : properties.getLocalPlus()) {
|
||||
if (local.getEnableStorage() && local.getEnableAccess()) {
|
||||
registry.addResourceHandler(local.getPathPatterns())
|
||||
.addResourceLocations("file:" + local.getStoragePath());
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,449 @@
|
|||
/*
|
||||
* 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 cn.iocoder.yudao.module.langchat.config;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
import org.dromara.x.file.storage.core.FileStorageProperties;
|
||||
import org.dromara.x.file.storage.core.FileStorageProperties.*;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@Component
|
||||
@ConditionalOnMissingBean(SpringFileStorageProperties.class)
|
||||
@ConfigurationProperties(prefix = "langchat.oss")
|
||||
public class SpringFileStorageProperties {
|
||||
|
||||
/**
|
||||
* 默认存储平台
|
||||
*/
|
||||
private String defaultPlatform = "local";
|
||||
/**
|
||||
* 缩略图后缀,例如【.min.jpg】【.png】
|
||||
*/
|
||||
private String thumbnailSuffix = ".min.jpg";
|
||||
/**
|
||||
* 上传时不支持元数据时抛出异常
|
||||
*/
|
||||
private Boolean uploadNotSupportMetadataThrowException = true;
|
||||
/**
|
||||
* 上传时不支持 ACL 时抛出异常
|
||||
*/
|
||||
private Boolean uploadNotSupportAclThrowException = true;
|
||||
/**
|
||||
* 复制时不支持元数据时抛出异常
|
||||
*/
|
||||
private Boolean copyNotSupportMetadataThrowException = true;
|
||||
/**
|
||||
* 复制时不支持 ACL 时抛出异常
|
||||
*/
|
||||
private Boolean copyNotSupportAclThrowException = true;
|
||||
/**
|
||||
* 移动时不支持元数据时抛出异常
|
||||
*/
|
||||
private Boolean moveNotSupportMetadataThrowException = true;
|
||||
/**
|
||||
* 移动时不支持 ACL 时抛出异常
|
||||
*/
|
||||
private Boolean moveNotSupportAclThrowException = true;
|
||||
/**
|
||||
* 启用 byte[] 文件包装适配器
|
||||
*/
|
||||
private Boolean enableByteFileWrapper = true;
|
||||
/**
|
||||
* 启用 URI 文件包装适配器,包含 URL 和 String
|
||||
*/
|
||||
private Boolean enableUriFileWrapper = true;
|
||||
/**
|
||||
* 启用 InputStream 文件包装适配器
|
||||
*/
|
||||
private Boolean enableInputStreamFileWrapper = true;
|
||||
/**
|
||||
* 启用本地文件包装适配器
|
||||
*/
|
||||
private Boolean enableLocalFileWrapper = true;
|
||||
/**
|
||||
* 启用 HttpServletRequest 文件包装适配器
|
||||
*/
|
||||
private Boolean enableHttpServletRequestFileWrapper = true;
|
||||
/**
|
||||
* 启用 MultipartFile 文件包装适配器
|
||||
*/
|
||||
private Boolean enableMultipartFileWrapper = true;
|
||||
/**
|
||||
* 本地存储
|
||||
*/
|
||||
@Deprecated
|
||||
private List<? extends SpringLocalConfig> local = new ArrayList<>();
|
||||
/**
|
||||
* 本地存储
|
||||
*/
|
||||
private List<? extends SpringLocalPlusConfig> localPlus = new ArrayList<>();
|
||||
/**
|
||||
* 华为云 OBS
|
||||
*/
|
||||
private List<? extends SpringHuaweiObsConfig> huaweiObs = new ArrayList<>();
|
||||
/**
|
||||
* 阿里云 OSS
|
||||
*/
|
||||
private List<? extends SpringAliyunOssConfig> aliyunOss = new ArrayList<>();
|
||||
/**
|
||||
* 七牛云 Kodo
|
||||
*/
|
||||
private List<? extends SpringQiniuKodoConfig> qiniuKodo = new ArrayList<>();
|
||||
/**
|
||||
* 腾讯云 COS
|
||||
*/
|
||||
private List<? extends SpringTencentCosConfig> tencentCos = new ArrayList<>();
|
||||
/**
|
||||
* 百度云 BOS
|
||||
*/
|
||||
private List<? extends SpringBaiduBosConfig> baiduBos = new ArrayList<>();
|
||||
/**
|
||||
* 又拍云 USS
|
||||
*/
|
||||
private List<? extends SpringUpyunUssConfig> upyunUss = new ArrayList<>();
|
||||
/**
|
||||
* MinIO USS
|
||||
*/
|
||||
private List<? extends SpringMinioConfig> minio = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Amazon S3
|
||||
*/
|
||||
private List<? extends SpringAmazonS3Config> amazonS3 = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* FTP
|
||||
*/
|
||||
private List<? extends SpringFtpConfig> ftp = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* FTP
|
||||
*/
|
||||
private List<? extends SpringSftpConfig> sftp = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* WebDAV
|
||||
*/
|
||||
private List<? extends SpringWebDavConfig> webdav = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* GoogleCloud Storage
|
||||
*/
|
||||
private List<? extends SpringGoogleCloudStorageConfig> googleCloudStorage = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* FastDFS
|
||||
*/
|
||||
private List<? extends SpringFastDfsConfig> fastdfs = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Azure Blob Storage
|
||||
*/
|
||||
private List<? extends SpringAzureBlobStorageConfig> azureBlob = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* 转换成 FileStorageProperties ,并过滤掉没有启用的存储平台
|
||||
*/
|
||||
public FileStorageProperties toFileStorageProperties() {
|
||||
FileStorageProperties properties = new FileStorageProperties();
|
||||
properties.setDefaultPlatform(defaultPlatform);
|
||||
properties.setThumbnailSuffix(thumbnailSuffix);
|
||||
properties.setUploadNotSupportMetadataThrowException(uploadNotSupportMetadataThrowException);
|
||||
properties.setUploadNotSupportAclThrowException(uploadNotSupportAclThrowException);
|
||||
properties.setCopyNotSupportMetadataThrowException(copyNotSupportMetadataThrowException);
|
||||
properties.setCopyNotSupportAclThrowException(copyNotSupportAclThrowException);
|
||||
properties.setMoveNotSupportMetadataThrowException(moveNotSupportMetadataThrowException);
|
||||
properties.setMoveNotSupportAclThrowException(moveNotSupportAclThrowException);
|
||||
properties.setLocal(
|
||||
local.stream().filter(SpringLocalConfig::getEnableStorage).collect(Collectors.toList()));
|
||||
properties.setLocalPlus(localPlus.stream()
|
||||
.filter(SpringLocalPlusConfig::getEnableStorage)
|
||||
.collect(Collectors.toList()));
|
||||
properties.setHuaweiObs(huaweiObs.stream()
|
||||
.filter(SpringHuaweiObsConfig::getEnableStorage)
|
||||
.collect(Collectors.toList()));
|
||||
properties.setAliyunOss(aliyunOss.stream()
|
||||
.filter(SpringAliyunOssConfig::getEnableStorage)
|
||||
.collect(Collectors.toList()));
|
||||
properties.setQiniuKodo(qiniuKodo.stream()
|
||||
.filter(SpringQiniuKodoConfig::getEnableStorage)
|
||||
.collect(Collectors.toList()));
|
||||
properties.setTencentCos(tencentCos.stream()
|
||||
.filter(SpringTencentCosConfig::getEnableStorage)
|
||||
.collect(Collectors.toList()));
|
||||
properties.setBaiduBos(
|
||||
baiduBos.stream().filter(SpringBaiduBosConfig::getEnableStorage).collect(Collectors.toList()));
|
||||
properties.setUpyunUss(
|
||||
upyunUss.stream().filter(SpringUpyunUssConfig::getEnableStorage).collect(Collectors.toList()));
|
||||
properties.setMinio(
|
||||
minio.stream().filter(SpringMinioConfig::getEnableStorage).collect(Collectors.toList()));
|
||||
properties.setAmazonS3(
|
||||
amazonS3.stream().filter(SpringAmazonS3Config::getEnableStorage).collect(Collectors.toList()));
|
||||
properties.setFtp(ftp.stream().filter(SpringFtpConfig::getEnableStorage).collect(Collectors.toList()));
|
||||
properties.setSftp(
|
||||
sftp.stream().filter(SpringSftpConfig::getEnableStorage).collect(Collectors.toList()));
|
||||
properties.setWebdav(
|
||||
webdav.stream().filter(SpringWebDavConfig::getEnableStorage).collect(Collectors.toList()));
|
||||
properties.setGoogleCloudStorage(googleCloudStorage.stream()
|
||||
.filter(SpringGoogleCloudStorageConfig::getEnableStorage)
|
||||
.collect(Collectors.toList()));
|
||||
properties.setFastdfs(
|
||||
fastdfs.stream().filter(SpringFastDfsConfig::getEnableStorage).collect(Collectors.toList()));
|
||||
properties.setAzureBlob(azureBlob.stream()
|
||||
.filter(SpringAzureBlobStorageConfig::getEnableStorage)
|
||||
.collect(Collectors.toList()));
|
||||
|
||||
return properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* 本地存储
|
||||
*/
|
||||
@Deprecated
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public static class SpringLocalConfig extends LocalConfig {
|
||||
/**
|
||||
* 本地存储访问路径
|
||||
*/
|
||||
private String[] pathPatterns = new String[0];
|
||||
/**
|
||||
* 启用本地存储
|
||||
*/
|
||||
private Boolean enableStorage = false;
|
||||
/**
|
||||
* 启用本地访问
|
||||
*/
|
||||
private Boolean enableAccess = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 本地存储升级版
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public static class SpringLocalPlusConfig extends LocalPlusConfig {
|
||||
/**
|
||||
* 本地存储访问路径
|
||||
*/
|
||||
private String[] pathPatterns = new String[0];
|
||||
/**
|
||||
* 启用本地存储
|
||||
*/
|
||||
private Boolean enableStorage = false;
|
||||
/**
|
||||
* 启用本地访问
|
||||
*/
|
||||
private Boolean enableAccess = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 华为云 OBS
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public static class SpringHuaweiObsConfig extends HuaweiObsConfig {
|
||||
/**
|
||||
* 启用存储
|
||||
*/
|
||||
private Boolean enableStorage = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 阿里云 OSS
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public static class SpringAliyunOssConfig extends AliyunOssConfig {
|
||||
/**
|
||||
* 启用存储
|
||||
*/
|
||||
private Boolean enableStorage = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 七牛云 Kodo
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public static class SpringQiniuKodoConfig extends QiniuKodoConfig {
|
||||
/**
|
||||
* 启用存储
|
||||
*/
|
||||
private Boolean enableStorage = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 腾讯云 COS
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public static class SpringTencentCosConfig extends TencentCosConfig {
|
||||
/**
|
||||
* 启用存储
|
||||
*/
|
||||
private Boolean enableStorage = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 百度云 BOS
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public static class SpringBaiduBosConfig extends BaiduBosConfig {
|
||||
/**
|
||||
* 启用存储
|
||||
*/
|
||||
private Boolean enableStorage = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 又拍云 USS
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public static class SpringUpyunUssConfig extends UpyunUssConfig {
|
||||
/**
|
||||
* 启用存储
|
||||
*/
|
||||
private Boolean enableStorage = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* MinIO
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public static class SpringMinioConfig extends MinioConfig {
|
||||
/**
|
||||
* 启用存储
|
||||
*/
|
||||
private Boolean enableStorage = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Amazon S3
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public static class SpringAmazonS3Config extends AmazonS3Config {
|
||||
/**
|
||||
* 启用存储
|
||||
*/
|
||||
private Boolean enableStorage = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* FTP
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public static class SpringFtpConfig extends FtpConfig {
|
||||
/**
|
||||
* 启用存储
|
||||
*/
|
||||
private Boolean enableStorage = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* SFTP
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public static class SpringSftpConfig extends SftpConfig {
|
||||
/**
|
||||
* 启用存储
|
||||
*/
|
||||
private Boolean enableStorage = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* WebDAV
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public static class SpringWebDavConfig extends WebDavConfig {
|
||||
/**
|
||||
* 启用存储
|
||||
*/
|
||||
private Boolean enableStorage = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* GoogleCloud Storage
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public static class SpringGoogleCloudStorageConfig extends GoogleCloudStorageConfig {
|
||||
/**
|
||||
* 启用存储
|
||||
*/
|
||||
private Boolean enableStorage = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* FastDFS Storage
|
||||
* @author XS <wanghaiqi@beeplay123.com>
|
||||
* @date 2023/10/23
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public static class SpringFastDfsConfig extends FastDfsConfig {
|
||||
/**
|
||||
* 启用存储
|
||||
*/
|
||||
private Boolean enableStorage = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* AzureBlob Storage
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public static class SpringAzureBlobStorageConfig extends AzureBlobStorageConfig {
|
||||
/**
|
||||
* 启用存储
|
||||
*/
|
||||
private Boolean enableStorage = false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
* 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 cn.iocoder.yudao.module.langchat.controller.admin.aigc;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
|
||||
import cn.iocoder.yudao.module.langchat.annotation.ApiLog;
|
||||
import cn.iocoder.yudao.module.langchat.entity.AigcDocs;
|
||||
import cn.iocoder.yudao.module.langchat.mapper.AigcDocsMapper;
|
||||
import cn.iocoder.yudao.module.langchat.utils.MybatisUtil;
|
||||
import cn.iocoder.yudao.module.langchat.utils.QueryPage;
|
||||
import cn.iocoder.yudao.module.langchat.service.core.EmbeddingService;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author tycoding
|
||||
* @since 2024/4/15
|
||||
*/
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
@RequestMapping("/chat/aigc/docs")
|
||||
public class AigcDocsController {
|
||||
|
||||
private final AigcDocsMapper docsMapper;
|
||||
private final EmbeddingService embeddingService;
|
||||
|
||||
@GetMapping("/list")
|
||||
@TenantIgnore
|
||||
public CommonResult<List<AigcDocs>> list(AigcDocs data) {
|
||||
return CommonResult.success(docsMapper.selectList(Wrappers.<AigcDocs>lambdaQuery().orderByDesc(AigcDocs::getCreateTime)));
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@TenantIgnore
|
||||
public CommonResult list(AigcDocs data, QueryPage queryPage) {
|
||||
Page<AigcDocs> page = new Page<>(queryPage.getPageNo(), queryPage.getPageSize());
|
||||
return CommonResult.success(MybatisUtil.getData(docsMapper.selectPage(page, Wrappers.<AigcDocs>lambdaQuery()
|
||||
.eq(data.getKnowledgeId() != null, AigcDocs::getKnowledgeId, data.getKnowledgeId())
|
||||
.eq(data.getSliceStatus() != null, AigcDocs::getSliceStatus, data.getSliceStatus())
|
||||
.orderByDesc(AigcDocs::getCreateTime)
|
||||
)));
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
@TenantIgnore
|
||||
public CommonResult<AigcDocs> findById(@PathVariable String id) {
|
||||
return CommonResult.success(docsMapper.selectById(id));
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
@ApiLog("新增文档")
|
||||
@TenantIgnore
|
||||
@PreAuthorize("@ss.hasPermission('aigc:docs:add')")
|
||||
public CommonResult add(@RequestBody AigcDocs data) {
|
||||
docsMapper.insert(data);
|
||||
return CommonResult.success("success");
|
||||
}
|
||||
|
||||
@PutMapping
|
||||
@ApiLog("修改文档")
|
||||
@TenantIgnore
|
||||
@PreAuthorize("@ss.hasPermission('aigc:docs:update')")
|
||||
public CommonResult update(@RequestBody AigcDocs data) {
|
||||
docsMapper.updateById(data);
|
||||
return CommonResult.success("success");
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
@ApiLog("删除文档")
|
||||
@TenantIgnore
|
||||
@PreAuthorize("@ss.hasPermission('aigc:docs:delete')")
|
||||
@Transactional
|
||||
public CommonResult delete(@PathVariable String id) {
|
||||
// 删除切面数据
|
||||
embeddingService.clearDocSlices(id);
|
||||
|
||||
// 删除文档
|
||||
docsMapper.deleteById(id);
|
||||
return CommonResult.success("success");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
* 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 cn.iocoder.yudao.module.langchat.controller.admin.aigc;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
|
||||
import cn.iocoder.yudao.module.langchat.annotation.ApiLog;
|
||||
import cn.iocoder.yudao.module.langchat.entity.AigcDocsSlice;
|
||||
import cn.iocoder.yudao.module.langchat.mapper.AigcDocsSliceMapper;
|
||||
import cn.iocoder.yudao.module.langchat.utils.MybatisUtil;
|
||||
import cn.iocoder.yudao.module.langchat.utils.QueryPage;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author tycoding
|
||||
* @since 2024/4/15
|
||||
*/
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
@RequestMapping("/chat/aigc/docs/slice")
|
||||
public class AigcDocsSliceController {
|
||||
|
||||
private final AigcDocsSliceMapper docsSliceMapper;
|
||||
|
||||
@GetMapping("/list")
|
||||
@TenantIgnore
|
||||
public CommonResult<List<AigcDocsSlice>> list(AigcDocsSlice data) {
|
||||
return CommonResult.success(docsSliceMapper.selectList(Wrappers.<AigcDocsSlice>lambdaQuery().orderByDesc(AigcDocsSlice::getCreateTime)));
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@TenantIgnore
|
||||
public CommonResult list(AigcDocsSlice data, QueryPage queryPage) {
|
||||
Page<AigcDocsSlice> page = new Page<>(queryPage.getPageNo(), queryPage.getPageSize());
|
||||
return CommonResult.success(MybatisUtil.getData(docsSliceMapper.selectPage(page, Wrappers.<AigcDocsSlice>lambdaQuery()
|
||||
.eq(data.getKnowledgeId() != null, AigcDocsSlice::getKnowledgeId, data.getKnowledgeId())
|
||||
.eq(data.getDocsId() != null, AigcDocsSlice::getDocsId, data.getDocsId())
|
||||
.orderByDesc(AigcDocsSlice::getCreateTime)
|
||||
)));
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
@TenantIgnore
|
||||
public CommonResult<AigcDocsSlice> findById(@PathVariable String id) {
|
||||
return CommonResult.success(docsSliceMapper.selectById(id));
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
@ApiLog("新增切片数据")
|
||||
@TenantIgnore
|
||||
@PreAuthorize("@ss.hasPermission('aigc:docs:slice:add')")
|
||||
public CommonResult add(@RequestBody AigcDocsSlice data) {
|
||||
data.setCreateTime(new Date());
|
||||
docsSliceMapper.insert(data);
|
||||
return CommonResult.success("success");
|
||||
}
|
||||
|
||||
@PutMapping
|
||||
@ApiLog("修改切片数据")
|
||||
@TenantIgnore
|
||||
@PreAuthorize("@ss.hasPermission('aigc:docs:slice:update')")
|
||||
public CommonResult update(@RequestBody AigcDocsSlice data) {
|
||||
docsSliceMapper.updateById(data);
|
||||
return CommonResult.success("success");
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
@ApiLog("删除切片数据")
|
||||
@TenantIgnore
|
||||
@PreAuthorize("@ss.hasPermission('aigc:docs:slice:delete')")
|
||||
public CommonResult delete(@PathVariable String id) {
|
||||
docsSliceMapper.deleteById(id);
|
||||
return CommonResult.success("success");
|
||||
}
|
||||
}
|
||||
|
|
@ -14,20 +14,21 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.iocoder.yudao.module.langchat.server.controller;
|
||||
package cn.iocoder.yudao.module.langchat.controller.admin.aigc;
|
||||
|
||||
|
||||
import cn.hutool.core.lang.Dict;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.yudao.module.langchat.server.component.EmbeddingRefreshEvent;
|
||||
import cn.iocoder.yudao.module.langchat.server.entity.AigcEmbedStore;
|
||||
import cn.iocoder.yudao.module.langchat.server.service.AigcEmbedStoreService;
|
||||
import cn.iocoder.yudao.module.langchat.server.annotation.ApiLog;
|
||||
import cn.iocoder.yudao.module.langchat.server.component.SpringContextHolder;
|
||||
import cn.iocoder.yudao.module.langchat.server.utils.MybatisUtil;
|
||||
import cn.iocoder.yudao.module.langchat.server.utils.QueryPage;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
|
||||
import cn.iocoder.yudao.module.langchat.component.EmbeddingRefreshEvent;
|
||||
import cn.iocoder.yudao.module.langchat.entity.AigcEmbedStore;
|
||||
import cn.iocoder.yudao.module.langchat.service.aigc.AigcEmbedStoreService;
|
||||
import cn.iocoder.yudao.module.langchat.annotation.ApiLog;
|
||||
import cn.iocoder.yudao.module.langchat.component.SpringContextHolder;
|
||||
import cn.iocoder.yudao.module.langchat.utils.MybatisUtil;
|
||||
import cn.iocoder.yudao.module.langchat.utils.QueryPage;
|
||||
|
||||
import cn.iocoder.yudao.module.langchat.server.utils.R;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
@ -42,68 +43,74 @@ import java.util.List;
|
|||
*/
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
@RequestMapping("/aigc/embed-store")
|
||||
@RequestMapping("/chat/aigc/embed-store")
|
||||
public class AigcEmbedStoreController {
|
||||
|
||||
private final AigcEmbedStoreService embedStoreService;
|
||||
private final SpringContextHolder contextHolder;
|
||||
|
||||
@GetMapping("/list")
|
||||
public R<List<AigcEmbedStore>> list(AigcEmbedStore data) {
|
||||
@TenantIgnore
|
||||
public CommonResult<List<AigcEmbedStore>> list(AigcEmbedStore data) {
|
||||
List<AigcEmbedStore> list = embedStoreService.list(Wrappers.lambdaQuery());
|
||||
list.forEach(this::hide);
|
||||
return R.ok(list);
|
||||
return CommonResult.success(list);
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
public R<Dict> page(AigcEmbedStore embedStore, QueryPage queryPage) {
|
||||
@TenantIgnore
|
||||
public CommonResult<Dict> page(AigcEmbedStore embedStore, QueryPage queryPage) {
|
||||
IPage<AigcEmbedStore> page = embedStoreService.page(MybatisUtil.wrap(embedStore, queryPage),
|
||||
Wrappers.lambdaQuery());
|
||||
page.getRecords().forEach(this::hide);
|
||||
return R.ok(MybatisUtil.getData(page));
|
||||
return CommonResult.success(MybatisUtil.getData(page));
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
public R<AigcEmbedStore> findById(@PathVariable String id) {
|
||||
@TenantIgnore
|
||||
public CommonResult<AigcEmbedStore> findById(@PathVariable String id) {
|
||||
AigcEmbedStore store = embedStoreService.getById(id);
|
||||
hide(store);
|
||||
return R.ok(store);
|
||||
return CommonResult.success(store);
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
@ApiLog("新增向量库")
|
||||
@TenantIgnore
|
||||
@PreAuthorize("@ss.hasPermission('aigc:embed-store:add')")
|
||||
public R<AigcEmbedStore> add(@RequestBody AigcEmbedStore data) {
|
||||
public CommonResult<AigcEmbedStore> add(@RequestBody AigcEmbedStore data) {
|
||||
if (StrUtil.isNotBlank(data.getPassword()) && data.getPassword().contains("*")) {
|
||||
data.setPassword(null);
|
||||
}
|
||||
embedStoreService.save(data);
|
||||
SpringContextHolder.publishEvent(new EmbeddingRefreshEvent(data));
|
||||
return R.ok();
|
||||
return CommonResult.success(data);
|
||||
}
|
||||
|
||||
@PutMapping
|
||||
@ApiLog("修改向量库")
|
||||
@TenantIgnore
|
||||
@PreAuthorize("@ss.hasPermission('aigc:embed-store:update')")
|
||||
public R update(@RequestBody AigcEmbedStore data) {
|
||||
public CommonResult update(@RequestBody AigcEmbedStore data) {
|
||||
if (StrUtil.isNotBlank(data.getPassword()) && data.getPassword().contains("*")) {
|
||||
data.setPassword(null);
|
||||
}
|
||||
embedStoreService.updateById(data);
|
||||
SpringContextHolder.publishEvent(new EmbeddingRefreshEvent(data));
|
||||
return R.ok();
|
||||
return CommonResult.success("success");
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
@ApiLog("删除向量库")
|
||||
@TenantIgnore
|
||||
@PreAuthorize("@ss.hasPermission('aigc:embed-store:delete')")
|
||||
public R delete(@PathVariable String id) {
|
||||
public CommonResult delete(@PathVariable String id) {
|
||||
AigcEmbedStore store = embedStoreService.getById(id);
|
||||
if (store != null) {
|
||||
embedStoreService.removeById(id);
|
||||
contextHolder.unregisterBean(id);
|
||||
}
|
||||
return R.ok();
|
||||
return CommonResult.success("success");
|
||||
}
|
||||
|
||||
private void hide(AigcEmbedStore data) {
|
|
@ -14,24 +14,25 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.iocoder.yudao.module.langchat.server.controller;
|
||||
package cn.iocoder.yudao.module.langchat.controller.admin.aigc;
|
||||
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.yudao.module.langchat.server.entity.AigcDocs;
|
||||
import cn.iocoder.yudao.module.langchat.server.entity.AigcEmbedStore;
|
||||
import cn.iocoder.yudao.module.langchat.server.entity.AigcKnowledge;
|
||||
import cn.iocoder.yudao.module.langchat.server.entity.AigcModel;
|
||||
import cn.iocoder.yudao.module.langchat.server.mapper.AigcDocsMapper;
|
||||
import cn.iocoder.yudao.module.langchat.server.provider.KnowledgeStoreFactory;
|
||||
import cn.iocoder.yudao.module.langchat.server.service.AigcEmbedStoreService;
|
||||
import cn.iocoder.yudao.module.langchat.server.service.AigcKnowledgeService;
|
||||
import cn.iocoder.yudao.module.langchat.server.service.AigcModelService;
|
||||
import cn.iocoder.yudao.module.langchat.server.annotation.ApiLog;
|
||||
import cn.iocoder.yudao.module.langchat.server.utils.MybatisUtil;
|
||||
import cn.iocoder.yudao.module.langchat.server.utils.QueryPage;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
|
||||
import cn.iocoder.yudao.module.langchat.entity.AigcDocs;
|
||||
import cn.iocoder.yudao.module.langchat.entity.AigcEmbedStore;
|
||||
import cn.iocoder.yudao.module.langchat.entity.AigcKnowledge;
|
||||
import cn.iocoder.yudao.module.langchat.entity.AigcModel;
|
||||
import cn.iocoder.yudao.module.langchat.mapper.AigcDocsMapper;
|
||||
import cn.iocoder.yudao.module.langchat.provider.KnowledgeStoreFactory;
|
||||
import cn.iocoder.yudao.module.langchat.service.aigc.AigcEmbedStoreService;
|
||||
import cn.iocoder.yudao.module.langchat.service.aigc.AigcKnowledgeService;
|
||||
import cn.iocoder.yudao.module.langchat.service.aigc.AigcModelService;
|
||||
import cn.iocoder.yudao.module.langchat.annotation.ApiLog;
|
||||
import cn.iocoder.yudao.module.langchat.utils.MybatisUtil;
|
||||
import cn.iocoder.yudao.module.langchat.utils.QueryPage;
|
||||
|
||||
import cn.iocoder.yudao.module.langchat.server.utils.R;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
|
@ -49,7 +50,7 @@ import java.util.stream.Collectors;
|
|||
*/
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
@RequestMapping("/aigc/knowledge")
|
||||
@RequestMapping("/chat/aigc/knowledge")
|
||||
public class AigcKnowledgeController {
|
||||
|
||||
private final AigcKnowledgeService kbService;
|
||||
|
@ -59,10 +60,11 @@ public class AigcKnowledgeController {
|
|||
private final KnowledgeStoreFactory knowledgeStore;
|
||||
|
||||
@GetMapping("/list")
|
||||
public R<List<AigcKnowledge>> list(AigcKnowledge data) {
|
||||
@TenantIgnore
|
||||
public CommonResult<List<AigcKnowledge>> list(AigcKnowledge data) {
|
||||
List<AigcKnowledge> list = kbService.list(Wrappers.<AigcKnowledge>lambdaQuery().orderByDesc(AigcKnowledge::getCreateTime));
|
||||
build(list);
|
||||
return R.ok(list);
|
||||
return CommonResult.success(list);
|
||||
}
|
||||
|
||||
private void build(List<AigcKnowledge> records) {
|
||||
|
@ -87,8 +89,9 @@ public class AigcKnowledgeController {
|
|||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
public R list(AigcKnowledge data, QueryPage queryPage) {
|
||||
Page<AigcKnowledge> page = new Page<>(queryPage.getPage(), queryPage.getLimit());
|
||||
@TenantIgnore
|
||||
public CommonResult list(AigcKnowledge data, QueryPage queryPage) {
|
||||
Page<AigcKnowledge> page = new Page<>(queryPage.getPageNo(), queryPage.getPageSize());
|
||||
LambdaQueryWrapper<AigcKnowledge> queryWrapper = Wrappers.<AigcKnowledge>lambdaQuery()
|
||||
.like(!StrUtil.isBlank(data.getName()), AigcKnowledge::getName, data.getName())
|
||||
.orderByDesc(AigcKnowledge::getCreateTime);
|
||||
|
@ -96,11 +99,12 @@ public class AigcKnowledgeController {
|
|||
|
||||
build(iPage.getRecords());
|
||||
|
||||
return R.ok(MybatisUtil.getData(iPage));
|
||||
return CommonResult.success(MybatisUtil.getData(iPage));
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
public R<AigcKnowledge> findById(@PathVariable String id) {
|
||||
@TenantIgnore
|
||||
public CommonResult<AigcKnowledge> findById(@PathVariable String id) {
|
||||
AigcKnowledge knowledge = kbService.getById(id);
|
||||
if (knowledge.getEmbedStoreId() != null) {
|
||||
knowledge.setEmbedStore(embedStoreService.getById(knowledge.getEmbedStoreId()));
|
||||
|
@ -108,35 +112,38 @@ public class AigcKnowledgeController {
|
|||
if (knowledge.getEmbedModelId() != null) {
|
||||
knowledge.setEmbedModel(modelService.getById(knowledge.getEmbedModelId()));
|
||||
}
|
||||
return R.ok(knowledge);
|
||||
return CommonResult.success(knowledge);
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
@ApiLog("新增知识库")
|
||||
@TenantIgnore
|
||||
@PreAuthorize("@ss.hasPermission('aigc:knowledge:add')")
|
||||
public R add(@RequestBody AigcKnowledge data) {
|
||||
public CommonResult add(@RequestBody AigcKnowledge data) {
|
||||
data.setCreateTime(String.valueOf(System.currentTimeMillis()));
|
||||
kbService.save(data);
|
||||
knowledgeStore.init();
|
||||
return R.ok();
|
||||
return CommonResult.success("success");
|
||||
}
|
||||
|
||||
@PutMapping
|
||||
@ApiLog("更新知识库")
|
||||
@TenantIgnore
|
||||
@PreAuthorize("@ss.hasPermission('aigc:knowledge:update')")
|
||||
public R update(@RequestBody AigcKnowledge data) {
|
||||
public CommonResult update(@RequestBody AigcKnowledge data) {
|
||||
kbService.updateById(data);
|
||||
knowledgeStore.init();
|
||||
return R.ok();
|
||||
return CommonResult.success("success");
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
@ApiLog("删除知识库")
|
||||
@TenantIgnore
|
||||
@PreAuthorize("@ss.hasPermission('aigc:knowledge:delete')")
|
||||
public R delete(@PathVariable String id) {
|
||||
public CommonResult delete(@PathVariable String id) {
|
||||
kbService.removeKnowledge(id);
|
||||
knowledgeStore.init();
|
||||
return R.ok();
|
||||
return CommonResult.success("success");
|
||||
}
|
||||
}
|
||||
|
|
@ -14,17 +14,18 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.iocoder.yudao.module.langchat.server.controller;
|
||||
package cn.iocoder.yudao.module.langchat.controller.admin.aigc;
|
||||
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.yudao.module.langchat.server.entity.AigcMessage;
|
||||
import cn.iocoder.yudao.module.langchat.server.service.AigcMessageService;
|
||||
import cn.iocoder.yudao.module.langchat.server.annotation.ApiLog;
|
||||
import cn.iocoder.yudao.module.langchat.server.utils.MybatisUtil;
|
||||
import cn.iocoder.yudao.module.langchat.server.utils.QueryPage;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
|
||||
import cn.iocoder.yudao.module.langchat.entity.AigcMessage;
|
||||
import cn.iocoder.yudao.module.langchat.service.aigc.AigcMessageService;
|
||||
import cn.iocoder.yudao.module.langchat.annotation.ApiLog;
|
||||
import cn.iocoder.yudao.module.langchat.utils.MybatisUtil;
|
||||
import cn.iocoder.yudao.module.langchat.utils.QueryPage;
|
||||
|
||||
import cn.iocoder.yudao.module.langchat.server.utils.R;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
|
@ -36,7 +37,7 @@ import org.springframework.web.bind.annotation.*;
|
|||
* @author tycoding
|
||||
* @since 2024/1/19
|
||||
*/
|
||||
@RequestMapping("/aigc/message")
|
||||
@RequestMapping("/chat/aigc/message")
|
||||
@RestController
|
||||
@AllArgsConstructor
|
||||
public class AigcMessageController {
|
||||
|
@ -44,26 +45,29 @@ public class AigcMessageController {
|
|||
private final AigcMessageService aigcMessageService;
|
||||
|
||||
@GetMapping("/page")
|
||||
public R list(AigcMessage data, QueryPage queryPage) {
|
||||
@TenantIgnore
|
||||
public CommonResult list(AigcMessage data, QueryPage queryPage) {
|
||||
LambdaQueryWrapper<AigcMessage> queryWrapper = Wrappers.<AigcMessage>lambdaQuery()
|
||||
.like(!StrUtil.isBlank(data.getMessage()), AigcMessage::getMessage, data.getMessage())
|
||||
.like(!StrUtil.isBlank(data.getUsername()), AigcMessage::getUsername, data.getUsername())
|
||||
.eq(!StrUtil.isBlank(data.getRole()), AigcMessage::getRole, data.getRole())
|
||||
.orderByDesc(AigcMessage::getCreateTime);
|
||||
IPage<AigcMessage> iPage = aigcMessageService.page(MybatisUtil.wrap(data, queryPage), queryWrapper);
|
||||
return R.ok(MybatisUtil.getData(iPage));
|
||||
return CommonResult.success(MybatisUtil.getData(iPage));
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
public R getById(@PathVariable String id) {
|
||||
return R.ok(aigcMessageService.getById(id));
|
||||
@TenantIgnore
|
||||
public CommonResult getById(@PathVariable String id) {
|
||||
return CommonResult.success(aigcMessageService.getById(id));
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
@ApiLog("删除会话消息")
|
||||
@TenantIgnore
|
||||
@PreAuthorize("@ss.hasPermission('aigc:message:delete')")
|
||||
public R del(@PathVariable String id) {
|
||||
return R.ok(aigcMessageService.removeById(id));
|
||||
public CommonResult del(@PathVariable String id) {
|
||||
return CommonResult.success(aigcMessageService.removeById(id));
|
||||
}
|
||||
|
||||
}
|
|
@ -14,19 +14,20 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.iocoder.yudao.module.langchat.server.controller;
|
||||
package cn.iocoder.yudao.module.langchat.controller.admin.aigc;
|
||||
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.yudao.module.langchat.server.component.ProviderRefreshEvent;
|
||||
import cn.iocoder.yudao.module.langchat.server.component.SpringContextHolder;
|
||||
import cn.iocoder.yudao.module.langchat.server.entity.AigcModel;
|
||||
import cn.iocoder.yudao.module.langchat.server.service.AigcModelService;
|
||||
import cn.iocoder.yudao.module.langchat.server.annotation.ApiLog;
|
||||
import cn.iocoder.yudao.module.langchat.server.utils.MybatisUtil;
|
||||
import cn.iocoder.yudao.module.langchat.server.utils.QueryPage;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
|
||||
import cn.iocoder.yudao.module.langchat.component.ProviderRefreshEvent;
|
||||
import cn.iocoder.yudao.module.langchat.component.SpringContextHolder;
|
||||
import cn.iocoder.yudao.module.langchat.entity.AigcModel;
|
||||
import cn.iocoder.yudao.module.langchat.service.aigc.AigcModelService;
|
||||
import cn.iocoder.yudao.module.langchat.annotation.ApiLog;
|
||||
import cn.iocoder.yudao.module.langchat.utils.MybatisUtil;
|
||||
import cn.iocoder.yudao.module.langchat.utils.QueryPage;
|
||||
|
||||
import cn.iocoder.yudao.module.langchat.server.utils.R;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
|
@ -40,32 +41,36 @@ import java.util.List;
|
|||
*/
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
@RequestMapping("/aigc/model")
|
||||
@RequestMapping("/chat/aigc/model")
|
||||
public class AigcModelController {
|
||||
|
||||
private final AigcModelService modelService;
|
||||
private final SpringContextHolder contextHolder;
|
||||
|
||||
@GetMapping("/list")
|
||||
public R<List<AigcModel>> list(AigcModel data) {
|
||||
return R.ok(modelService.list(data));
|
||||
@TenantIgnore
|
||||
public CommonResult<List<AigcModel>> list(AigcModel data) {
|
||||
return CommonResult.success(modelService.list(data));
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
public R list(AigcModel data, QueryPage queryPage) {
|
||||
@TenantIgnore
|
||||
public CommonResult list(AigcModel data, QueryPage queryPage) {
|
||||
Page<AigcModel> iPage = modelService.page(data, queryPage);
|
||||
return R.ok(MybatisUtil.getData(iPage));
|
||||
return CommonResult.success(MybatisUtil.getData(iPage));
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
public R<AigcModel> findById(@PathVariable String id) {
|
||||
return R.ok(modelService.selectById(id));
|
||||
@TenantIgnore
|
||||
public CommonResult<AigcModel> findById(@PathVariable String id) {
|
||||
return CommonResult.success(modelService.selectById(id));
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
@ApiLog("添加模型")
|
||||
@PreAuthorize("@ss.hasPermission('aigc:model:add')")
|
||||
public R add(@RequestBody AigcModel data) {
|
||||
@TenantIgnore
|
||||
public CommonResult add(@RequestBody AigcModel data) {
|
||||
if (StrUtil.isNotBlank(data.getApiKey()) && data.getApiKey().contains("*")) {
|
||||
data.setApiKey(null);
|
||||
}
|
||||
|
@ -74,13 +79,14 @@ public class AigcModelController {
|
|||
}
|
||||
modelService.save(data);
|
||||
SpringContextHolder.publishEvent(new ProviderRefreshEvent(data));
|
||||
return R.ok();
|
||||
return CommonResult.success("success");
|
||||
}
|
||||
|
||||
@PutMapping
|
||||
@ApiLog("修改模型")
|
||||
@PreAuthorize("@ss.hasPermission('aigc:model:update')")
|
||||
public R update(@RequestBody AigcModel data) {
|
||||
@TenantIgnore
|
||||
public CommonResult update(@RequestBody AigcModel data) {
|
||||
if (StrUtil.isNotBlank(data.getApiKey()) && data.getApiKey().contains("*")) {
|
||||
data.setApiKey(null);
|
||||
}
|
||||
|
@ -89,18 +95,19 @@ public class AigcModelController {
|
|||
}
|
||||
modelService.updateById(data);
|
||||
SpringContextHolder.publishEvent(new ProviderRefreshEvent(data));
|
||||
return R.ok();
|
||||
return CommonResult.success("success");
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
@ApiLog("删除模型")
|
||||
@PreAuthorize("@ss.hasPermission('aigc:model:delete')")
|
||||
public R delete(@PathVariable String id) {
|
||||
@TenantIgnore
|
||||
public CommonResult delete(@PathVariable String id) {
|
||||
modelService.removeById(id);
|
||||
|
||||
// Delete dynamically registered beans, according to ID
|
||||
contextHolder.unregisterBean(id);
|
||||
return R.ok();
|
||||
return CommonResult.success("success");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,147 @@
|
|||
/*
|
||||
* 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 cn.iocoder.yudao.module.langchat.controller.admin.endpoint;
|
||||
|
||||
import ch.qos.logback.core.net.LoginAuthenticator;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.exception.ServiceException;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
|
||||
import cn.iocoder.yudao.module.langchat.dal.dto.ChatReq;
|
||||
import cn.iocoder.yudao.module.langchat.dal.dto.EmbeddingR;
|
||||
import cn.iocoder.yudao.module.langchat.entity.AigcDocs;
|
||||
import cn.iocoder.yudao.module.langchat.entity.AigcDocsSlice;
|
||||
import cn.iocoder.yudao.module.langchat.entity.AigcOss;
|
||||
import cn.iocoder.yudao.module.langchat.enums.EmbedConst;
|
||||
import cn.iocoder.yudao.module.langchat.mapper.AigcDocsMapper;
|
||||
import cn.iocoder.yudao.module.langchat.service.aigc.AigcKnowledgeService;
|
||||
import cn.iocoder.yudao.module.langchat.service.aigc.AigcOssService;
|
||||
import cn.iocoder.yudao.module.langchat.service.core.EmbeddingService;
|
||||
import cn.iocoder.yudao.module.langchat.service.core.LangEmbeddingService;
|
||||
import cn.iocoder.yudao.module.langchat.task.TaskManager;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
||||
|
||||
/**
|
||||
* @author tycoding
|
||||
* @since 2024/4/25
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@AllArgsConstructor
|
||||
@RequestMapping("/chat/aigc/embedding")
|
||||
public class EmbeddingEndpoint {
|
||||
|
||||
private final LangEmbeddingService langEmbeddingService;
|
||||
private final AigcKnowledgeService aigcKnowledgeService;
|
||||
private final AigcDocsMapper aigcDocsMapper;
|
||||
private final AigcOssService aigcOssService;
|
||||
private final EmbeddingService embeddingService;
|
||||
|
||||
@PostMapping("/text")
|
||||
@PreAuthorize("@ss.hasPermission('aigc:embedding:text')")
|
||||
@TenantIgnore
|
||||
public CommonResult text(@RequestBody AigcDocs data) {
|
||||
if (StrUtil.isBlankIfStr(data.getContent())) {
|
||||
throw new ServiceException(-1003,"文档内容不能为空");
|
||||
}
|
||||
if (StrUtil.isBlank(data.getId())) {
|
||||
aigcKnowledgeService.addDocs(data);
|
||||
}
|
||||
data.setType(EmbedConst.ORIGIN_TYPE_INPUT).setSliceStatus(false);
|
||||
|
||||
try {
|
||||
EmbeddingR embeddingR = langEmbeddingService.embeddingText(
|
||||
new ChatReq().setMessage(data.getContent())
|
||||
.setDocsName(data.getType())
|
||||
.setDocsId(data.getId())
|
||||
.setKnowledgeId(data.getKnowledgeId()));
|
||||
|
||||
aigcKnowledgeService.addDocsSlice(new AigcDocsSlice()
|
||||
.setKnowledgeId(data.getKnowledgeId())
|
||||
.setDocsId(data.getId())
|
||||
.setVectorId(embeddingR.getVectorId())
|
||||
.setName(data.getName())
|
||||
.setContent(embeddingR.getText())
|
||||
);
|
||||
|
||||
aigcKnowledgeService.updateDocs(new AigcDocs().setId(data.getId()).setSliceStatus(true).setSliceNum(1));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
|
||||
// del data
|
||||
aigcKnowledgeService.removeSlicesOfDoc(data.getId());
|
||||
}
|
||||
return CommonResult.success("success");
|
||||
}
|
||||
|
||||
@PostMapping("/docs/{knowledgeId}")
|
||||
@PreAuthorize("@ss.hasPermission('aigc:embedding:docs')")
|
||||
@TenantIgnore
|
||||
public CommonResult docs(MultipartFile file, @PathVariable String knowledgeId) {
|
||||
// 1.1 获得用户信息
|
||||
String userId = getLoginUserId().toString();
|
||||
AigcOss oss = aigcOssService.upload(file, userId);
|
||||
AigcDocs data = new AigcDocs()
|
||||
.setName(oss.getOriginalFilename())
|
||||
.setSliceStatus(false)
|
||||
.setUrl(oss.getUrl())
|
||||
.setSize(file.getSize())
|
||||
.setType(EmbedConst.ORIGIN_TYPE_UPLOAD)
|
||||
.setKnowledgeId(knowledgeId);
|
||||
aigcKnowledgeService.addDocs(data);
|
||||
TaskManager.submitTask(userId, Executors.callable(() -> {
|
||||
embeddingService.embedDocsSlice(data, oss.getUrl());
|
||||
}));
|
||||
return CommonResult.success("success");
|
||||
}
|
||||
|
||||
@GetMapping("/re-embed/{docsId}")
|
||||
@TenantIgnore
|
||||
public CommonResult reEmbed(@PathVariable String docsId) {
|
||||
String userId = getLoginUserId().toString();
|
||||
AigcDocs docs = aigcDocsMapper.selectById(docsId);
|
||||
if (docs == null) {
|
||||
throw new ServiceException(-1003,"没有查询到文档数据");
|
||||
}
|
||||
if (EmbedConst.ORIGIN_TYPE_INPUT.equals(docs.getType())) {
|
||||
text(docs);
|
||||
}
|
||||
if (EmbedConst.ORIGIN_TYPE_UPLOAD.equals(docs.getType())) {
|
||||
// clear before re-embed
|
||||
embeddingService.clearDocSlices(docsId);
|
||||
TaskManager.submitTask(userId, Executors.callable(() -> {
|
||||
embeddingService.embedDocsSlice(docs, docs.getUrl());
|
||||
}));
|
||||
}
|
||||
return CommonResult.success("success");
|
||||
}
|
||||
|
||||
@PostMapping("/search")
|
||||
@TenantIgnore
|
||||
public CommonResult search(@RequestBody AigcDocs data) {
|
||||
return CommonResult.success(embeddingService.search(data));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* 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 cn.iocoder.yudao.module.langchat.dal.dto;
|
||||
|
||||
|
||||
import cn.iocoder.yudao.module.langchat.utils.StreamEmitter;
|
||||
import dev.langchain4j.model.input.Prompt;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
/**
|
||||
* @author tycoding
|
||||
* @since 2024/1/30
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class ChatReq {
|
||||
|
||||
private String appId;
|
||||
private String modelId;
|
||||
private String modelName;
|
||||
private String modelProvider;
|
||||
|
||||
private String message;
|
||||
|
||||
private String conversationId;
|
||||
|
||||
private String userId;
|
||||
|
||||
private String username;
|
||||
|
||||
private String chatId;
|
||||
|
||||
private String promptText;
|
||||
|
||||
private String docsName;
|
||||
|
||||
private String knowledgeId;
|
||||
private List<String> knowledgeIds = new ArrayList<>();
|
||||
|
||||
private String docsId;
|
||||
|
||||
private String url;
|
||||
|
||||
private String role;
|
||||
|
||||
private Prompt prompt;
|
||||
|
||||
private StreamEmitter emitter;
|
||||
private Executor executor;
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* 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 cn.iocoder.yudao.module.langchat.dal.dto;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
* @author tycoding
|
||||
* @since 2024/4/26
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class EmbeddingR {
|
||||
|
||||
/**
|
||||
* 写入到vector store的ID
|
||||
*/
|
||||
private String vectorId;
|
||||
|
||||
/**
|
||||
* 文档ID
|
||||
*/
|
||||
private String docsId;
|
||||
|
||||
/**
|
||||
* 知识库ID
|
||||
*/
|
||||
private String knowledgeId;
|
||||
|
||||
/**
|
||||
* Embedding后切片的文本
|
||||
*/
|
||||
private String text;
|
||||
}
|
|
@ -15,7 +15,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.iocoder.yudao.module.langchat.server.entity;
|
||||
package cn.iocoder.yudao.module.langchat.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
|
@ -15,7 +15,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.iocoder.yudao.module.langchat.server.entity;
|
||||
package cn.iocoder.yudao.module.langchat.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.iocoder.yudao.module.langchat.server.entity;
|
||||
package cn.iocoder.yudao.module.langchat.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.iocoder.yudao.module.langchat.server.entity;
|
||||
package cn.iocoder.yudao.module.langchat.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.iocoder.yudao.module.langchat.server.entity;
|
||||
package cn.iocoder.yudao.module.langchat.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.iocoder.yudao.module.langchat.server.entity;
|
||||
package cn.iocoder.yudao.module.langchat.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.iocoder.yudao.module.langchat.server.entity;
|
||||
package cn.iocoder.yudao.module.langchat.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.iocoder.yudao.module.langchat.server.entity;
|
||||
package cn.iocoder.yudao.module.langchat.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.FieldFill;
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.iocoder.yudao.module.langchat.server.entity;
|
||||
package cn.iocoder.yudao.module.langchat.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* 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 cn.iocoder.yudao.module.langchat.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
* @author tycoding
|
||||
* @since 2024/1/6
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class AigcOss extends OssR {
|
||||
private static final long serialVersionUID = -250127374910520163L;
|
||||
|
||||
/**
|
||||
* 主键
|
||||
*/
|
||||
@TableId(type = IdType.ASSIGN_UUID)
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
private String userId;
|
||||
}
|
|
@ -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 cn.iocoder.yudao.module.langchat.entity;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* OSS 文件对象
|
||||
*
|
||||
* @author tycoding
|
||||
* @since 2024/1/30
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class OssR implements Serializable {
|
||||
private static final long serialVersionUID = 5117927170776709434L;
|
||||
|
||||
private String ossId;
|
||||
private String url;
|
||||
private Long size;
|
||||
private String filename;
|
||||
private String originalFilename;
|
||||
private String basePath;
|
||||
private String path;
|
||||
private String ext;
|
||||
private String contentType;
|
||||
private String platform;
|
||||
private Date createTime;
|
||||
}
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.iocoder.yudao.module.langchat.server.entity;
|
||||
package cn.iocoder.yudao.module.langchat.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.iocoder.yudao.module.langchat.server.entity;
|
||||
package cn.iocoder.yudao.module.langchat.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.iocoder.yudao.module.langchat.server.entity;
|
||||
package cn.iocoder.yudao.module.langchat.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.iocoder.yudao.module.langchat.server.entity;
|
||||
package cn.iocoder.yudao.module.langchat.entity;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
|
@ -3,4 +3,4 @@
|
|||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
package cn.iocoder.yudao.module.langchat.server.framework;
|
||||
package cn.iocoder.yudao.module.langchat.framework;
|
|
@ -1,4 +1,4 @@
|
|||
package cn.iocoder.yudao.module.langchat.server.framework.security;
|
||||
package cn.iocoder.yudao.module.langchat.framework.security;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.enums.RpcConstants;
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
package cn.iocoder.yudao.module.langchat.server.framework.security.config;
|
||||
package cn.iocoder.yudao.module.langchat.framework.security.config;
|
||||
|
||||
import cn.iocoder.yudao.framework.security.config.AuthorizeRequestsCustomizer;
|
||||
import cn.iocoder.yudao.module.langchat.server.framework.security.ApiConstants;
|
||||
import cn.iocoder.yudao.module.langchat.framework.security.ApiConstants;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
|
@ -0,0 +1,4 @@
|
|||
/**
|
||||
* 占位
|
||||
*/
|
||||
package cn.iocoder.yudao.module.langchat.framework.security.core;
|
|
@ -14,9 +14,9 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.iocoder.yudao.module.langchat.server.mapper;
|
||||
package cn.iocoder.yudao.module.langchat.mapper;
|
||||
|
||||
import cn.iocoder.yudao.module.langchat.server.entity.AigcConversation;
|
||||
import cn.iocoder.yudao.module.langchat.entity.AigcConversation;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
|
@ -14,9 +14,9 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.iocoder.yudao.module.langchat.server.mapper;
|
||||
package cn.iocoder.yudao.module.langchat.mapper;
|
||||
|
||||
import cn.iocoder.yudao.module.langchat.server.entity.AigcDocs;
|
||||
import cn.iocoder.yudao.module.langchat.entity.AigcDocs;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
|
@ -14,9 +14,9 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.iocoder.yudao.module.langchat.server.mapper;
|
||||
package cn.iocoder.yudao.module.langchat.mapper;
|
||||
|
||||
import cn.iocoder.yudao.module.langchat.server.entity.AigcDocsSlice;
|
||||
import cn.iocoder.yudao.module.langchat.entity.AigcDocsSlice;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
|
@ -14,9 +14,9 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.iocoder.yudao.module.langchat.server.mapper;
|
||||
package cn.iocoder.yudao.module.langchat.mapper;
|
||||
|
||||
import cn.iocoder.yudao.module.langchat.server.entity.AigcEmbedStore;
|
||||
import cn.iocoder.yudao.module.langchat.entity.AigcEmbedStore;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
|
@ -14,9 +14,9 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.iocoder.yudao.module.langchat.server.mapper;
|
||||
package cn.iocoder.yudao.module.langchat.mapper;
|
||||
|
||||
import cn.iocoder.yudao.module.langchat.server.entity.AigcKnowledge;
|
||||
import cn.iocoder.yudao.module.langchat.entity.AigcKnowledge;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
|
@ -14,10 +14,10 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.iocoder.yudao.module.langchat.server.mapper;
|
||||
package cn.iocoder.yudao.module.langchat.mapper;
|
||||
|
||||
import cn.hutool.core.lang.Dict;
|
||||
import cn.iocoder.yudao.module.langchat.server.entity.AigcMessage;
|
||||
import cn.iocoder.yudao.module.langchat.entity.AigcMessage;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Select;
|
|
@ -14,9 +14,9 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.iocoder.yudao.module.langchat.server.mapper;
|
||||
package cn.iocoder.yudao.module.langchat.mapper;
|
||||
|
||||
import cn.iocoder.yudao.module.langchat.server.entity.AigcModel;
|
||||
import cn.iocoder.yudao.module.langchat.entity.AigcModel;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* 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 cn.iocoder.yudao.module.langchat.mapper;
|
||||
|
||||
import cn.iocoder.yudao.module.langchat.entity.AigcOss;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* @author tycoding
|
||||
* @since 2024/1/19
|
||||
*/
|
||||
@Mapper
|
||||
public interface AigcOssMapper extends BaseMapper<AigcOss> {
|
||||
|
||||
}
|
|
@ -14,11 +14,11 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.iocoder.yudao.module.langchat.server.mapper;
|
||||
package cn.iocoder.yudao.module.langchat.mapper;
|
||||
|
||||
import cn.hutool.core.lang.Dict;
|
||||
import cn.iocoder.yudao.module.langchat.server.entity.SysUser;
|
||||
import cn.iocoder.yudao.module.langchat.server.entity.UserInfo;
|
||||
import cn.iocoder.yudao.module.langchat.entity.SysUser;
|
||||
import cn.iocoder.yudao.module.langchat.entity.UserInfo;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* 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 cn.iocoder.yudao.module.langchat.properties;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* @author tycoding
|
||||
* @since 2024/4/15
|
||||
*/
|
||||
@Data
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "langchat")
|
||||
public class LangChatProps {
|
||||
}
|
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
* 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 cn.iocoder.yudao.module.langchat.provider;
|
||||
|
||||
import cn.iocoder.yudao.module.langchat.entity.AigcKnowledge;
|
||||
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 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<String> knowledgeIds) {
|
||||
List<String> storeIds = new ArrayList<>();
|
||||
knowledgeIds.forEach(id -> {
|
||||
if (knowledgeStoreFactory.containsKnowledge(id)) {
|
||||
AigcKnowledge data = knowledgeStoreFactory.getKnowledge(id);
|
||||
if (data.getEmbedModelId() != null) {
|
||||
storeIds.add(data.getEmbedModelId());
|
||||
}
|
||||
}
|
||||
});
|
||||
if (storeIds.isEmpty()) {
|
||||
throw new UnsupportedOperationException("知识库缺少Embedding Model配置,请先检查配置");
|
||||
}
|
||||
|
||||
HashSet<String> filterIds = new HashSet<>(storeIds);
|
||||
if (filterIds.size() > 1) {
|
||||
throw new UnsupportedOperationException("存在多个不同Embedding Model的知识库,请先检查配置");
|
||||
}
|
||||
|
||||
return modelStoreFactory.getEmbeddingModel(storeIds.get(0));
|
||||
}
|
||||
|
||||
public EmbeddingModel getEmbeddingModel(String knowledgeId) {
|
||||
if (knowledgeStoreFactory.containsKnowledge(knowledgeId)) {
|
||||
AigcKnowledge data = knowledgeStoreFactory.getKnowledge(knowledgeId);
|
||||
if (modelStoreFactory.containsEmbeddingModel(data.getEmbedModelId())) {
|
||||
return modelStoreFactory.getEmbeddingModel(data.getEmbedModelId());
|
||||
}
|
||||
}
|
||||
throw new UnsupportedOperationException("没有找到匹配的Embedding向量数据库");
|
||||
}
|
||||
|
||||
public EmbeddingStore<TextSegment> getEmbeddingStore(String knowledgeId) {
|
||||
if (knowledgeStoreFactory.containsKnowledge(knowledgeId)) {
|
||||
AigcKnowledge data = knowledgeStoreFactory.getKnowledge(knowledgeId);
|
||||
if (embeddingStoreFactory.containsEmbeddingStore(data.getEmbedStoreId())) {
|
||||
return embeddingStoreFactory.getEmbeddingStore(data.getEmbedStoreId());
|
||||
}
|
||||
}
|
||||
throw new UnsupportedOperationException("没有找到匹配的Embedding向量数据库");
|
||||
}
|
||||
|
||||
public EmbeddingStore<TextSegment> getEmbeddingStore(List<String> knowledgeIds) {
|
||||
List<String> storeIds = new ArrayList<>();
|
||||
knowledgeIds.forEach(id -> {
|
||||
if (knowledgeStoreFactory.containsKnowledge(id)) {
|
||||
AigcKnowledge data = knowledgeStoreFactory.getKnowledge(id);
|
||||
if (data.getEmbedStoreId() != null) {
|
||||
storeIds.add(data.getEmbedStoreId());
|
||||
}
|
||||
}
|
||||
});
|
||||
if (storeIds.isEmpty()) {
|
||||
throw new UnsupportedOperationException("知识库缺少Embedding Store配置,请先检查配置");
|
||||
}
|
||||
|
||||
HashSet<String> filterIds = new HashSet<>(storeIds);
|
||||
if (filterIds.size() > 1) {
|
||||
throw new UnsupportedOperationException("存在多个不同Embedding Store数据源的知识库,请先检查配置");
|
||||
}
|
||||
|
||||
return embeddingStoreFactory.getEmbeddingStore(storeIds.get(0));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
* 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 cn.iocoder.yudao.module.langchat.provider;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.yudao.module.langchat.enums.EmbedStoreEnum;
|
||||
import cn.iocoder.yudao.module.langchat.entity.AigcEmbedStore;
|
||||
import cn.iocoder.yudao.module.langchat.service.aigc.AigcEmbedStoreService;
|
||||
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 lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
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 AigcEmbedStoreService aigcEmbedStoreService;
|
||||
|
||||
private final List<AigcEmbedStore> modelStore = new ArrayList<>();
|
||||
private final Map<String, EmbeddingStore<TextSegment>> embedStoreMap = new ConcurrentHashMap<>();
|
||||
|
||||
@Async
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
modelStore.clear();
|
||||
List<AigcEmbedStore> list = aigcEmbedStoreService.list();
|
||||
list.forEach(embed -> {
|
||||
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(), 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(), 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(), 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);
|
||||
}
|
||||
}
|
|
@ -14,15 +14,15 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.iocoder.yudao.module.langchat.server.provider;
|
||||
package cn.iocoder.yudao.module.langchat.provider;
|
||||
|
||||
|
||||
import cn.iocoder.yudao.module.langchat.server.entity.AigcEmbedStore;
|
||||
import cn.iocoder.yudao.module.langchat.server.entity.AigcKnowledge;
|
||||
import cn.iocoder.yudao.module.langchat.server.entity.AigcModel;
|
||||
import cn.iocoder.yudao.module.langchat.server.service.AigcEmbedStoreService;
|
||||
import cn.iocoder.yudao.module.langchat.server.service.AigcKnowledgeService;
|
||||
import cn.iocoder.yudao.module.langchat.server.service.AigcModelService;
|
||||
import cn.iocoder.yudao.module.langchat.entity.AigcEmbedStore;
|
||||
import cn.iocoder.yudao.module.langchat.entity.AigcKnowledge;
|
||||
import cn.iocoder.yudao.module.langchat.entity.AigcModel;
|
||||
import cn.iocoder.yudao.module.langchat.service.aigc.AigcEmbedStoreService;
|
||||
import cn.iocoder.yudao.module.langchat.service.aigc.AigcKnowledgeService;
|
||||
import cn.iocoder.yudao.module.langchat.service.aigc.AigcModelService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.scheduling.annotation.Async;
|
|
@ -0,0 +1,174 @@
|
|||
/*
|
||||
* 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 cn.iocoder.yudao.module.langchat.provider;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.iocoder.yudao.module.langchat.enums.ModelConst;
|
||||
import cn.iocoder.yudao.module.langchat.component.ModelTypeEnum;
|
||||
import cn.iocoder.yudao.module.langchat.entity.AigcModel;
|
||||
import cn.iocoder.yudao.module.langchat.provider.build.ModelBuildHandler;
|
||||
import cn.iocoder.yudao.module.langchat.service.aigc.AigcModelService;
|
||||
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.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
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 {
|
||||
|
||||
@Autowired
|
||||
private AigcModelService aigcModelService;
|
||||
@Autowired
|
||||
private List<ModelBuildHandler> modelBuildHandlers;
|
||||
|
||||
private final List<AigcModel> 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<AigcModel> list = aigcModelService.list();
|
||||
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(AigcModel 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(), 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(AigcModel 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(), embeddingModel);
|
||||
modelStore.add(model);
|
||||
}
|
||||
});
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("model 【id{} name{}】 embedding 配置报错", model.getId(), model.getName());
|
||||
}
|
||||
}
|
||||
|
||||
private void imageHandler(AigcModel 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(), 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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* 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 cn.iocoder.yudao.module.langchat.provider.build;
|
||||
|
||||
import cn.iocoder.yudao.module.langchat.entity.AigcModel;
|
||||
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 org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* @author GB
|
||||
* @since 2024-08-18 09:57
|
||||
*/
|
||||
public interface ModelBuildHandler {
|
||||
|
||||
/**
|
||||
* 判断是不是当前模型
|
||||
*/
|
||||
boolean whetherCurrentModel(AigcModel model);
|
||||
|
||||
/**
|
||||
* basic check
|
||||
*/
|
||||
boolean basicCheck(AigcModel model);
|
||||
|
||||
/**
|
||||
* streaming chat build
|
||||
*/
|
||||
StreamingChatLanguageModel buildStreamingChat(AigcModel model);
|
||||
|
||||
/**
|
||||
* chat build
|
||||
*/
|
||||
ChatLanguageModel buildChatLanguageModel(AigcModel model);
|
||||
|
||||
/**
|
||||
* embedding config
|
||||
*/
|
||||
EmbeddingModel buildEmbedding(AigcModel model);
|
||||
|
||||
/**
|
||||
* image config
|
||||
*/
|
||||
ImageModel buildImage(AigcModel model);
|
||||
|
||||
}
|
|
@ -0,0 +1,159 @@
|
|||
/*
|
||||
* 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 cn.iocoder.yudao.module.langchat.provider.build;
|
||||
|
||||
|
||||
import cn.iocoder.yudao.framework.common.exception.ServiceException;
|
||||
import cn.iocoder.yudao.module.langchat.entity.AigcModel;
|
||||
import cn.iocoder.yudao.module.langchat.enums.ChatErrorEnum;
|
||||
import cn.iocoder.yudao.module.langchat.enums.ProviderEnum;
|
||||
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(AigcModel model) {
|
||||
return ProviderEnum.OLLAMA.name().equals(model.getProvider());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean basicCheck(AigcModel 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(AigcModel model) {
|
||||
try {
|
||||
if (!whetherCurrentModel(model)) {
|
||||
return null;
|
||||
}
|
||||
if (!basicCheck(model)) {
|
||||
return null;
|
||||
}
|
||||
return 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(AigcModel model) {
|
||||
try {
|
||||
if (!whetherCurrentModel(model)) {
|
||||
return null;
|
||||
}
|
||||
if (!basicCheck(model)) {
|
||||
return null;
|
||||
}
|
||||
return 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(AigcModel 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(AigcModel 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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,199 @@
|
|||
/*
|
||||
* 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 cn.iocoder.yudao.module.langchat.provider.build;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.yudao.framework.common.exception.ServiceException;
|
||||
import cn.iocoder.yudao.module.langchat.entity.AigcModel;
|
||||
import cn.iocoder.yudao.module.langchat.enums.ChatErrorEnum;
|
||||
import cn.iocoder.yudao.module.langchat.enums.ProviderEnum;
|
||||
import cn.iocoder.yudao.module.langchat.properties.LangChatProps;
|
||||
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(AigcModel 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(AigcModel 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(AigcModel 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(AigcModel 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(AigcModel 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(AigcModel 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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,165 @@
|
|||
/*
|
||||
* 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 cn.iocoder.yudao.module.langchat.provider.build;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.exception.ServiceException;
|
||||
import cn.iocoder.yudao.module.langchat.entity.AigcModel;
|
||||
import cn.iocoder.yudao.module.langchat.enums.ChatErrorEnum;
|
||||
import cn.iocoder.yudao.module.langchat.enums.ProviderEnum;
|
||||
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(AigcModel model) {
|
||||
return ProviderEnum.Q_FAN.name().equals(model.getProvider());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean basicCheck(AigcModel 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(AigcModel model) {
|
||||
try {
|
||||
if (!whetherCurrentModel(model)) {
|
||||
return null;
|
||||
}
|
||||
if (!basicCheck(model)) {
|
||||
return null;
|
||||
}
|
||||
return 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(AigcModel model) {
|
||||
try {
|
||||
if (!whetherCurrentModel(model)) {
|
||||
return null;
|
||||
}
|
||||
if (!basicCheck(model)) {
|
||||
return null;
|
||||
}
|
||||
return 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(AigcModel 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(AigcModel 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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,154 @@
|
|||
/*
|
||||
* 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 cn.iocoder.yudao.module.langchat.provider.build;
|
||||
|
||||
|
||||
import cn.iocoder.yudao.framework.common.exception.ServiceException;
|
||||
import cn.iocoder.yudao.module.langchat.entity.AigcModel;
|
||||
import cn.iocoder.yudao.module.langchat.enums.ChatErrorEnum;
|
||||
import cn.iocoder.yudao.module.langchat.enums.ProviderEnum;
|
||||
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(AigcModel model) {
|
||||
return ProviderEnum.Q_WEN.name().equals(model.getProvider());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean basicCheck(AigcModel 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(AigcModel model) {
|
||||
if (!whetherCurrentModel(model)) {
|
||||
return null;
|
||||
}
|
||||
if (!basicCheck(model)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return 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(AigcModel model) {
|
||||
try {
|
||||
if (!whetherCurrentModel(model)) {
|
||||
return null;
|
||||
}
|
||||
if (!basicCheck(model)) {
|
||||
return null;
|
||||
}
|
||||
return 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(AigcModel 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(AigcModel 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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,192 @@
|
|||
/*
|
||||
* 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 cn.iocoder.yudao.module.langchat.provider.build;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.exception.ServiceException;
|
||||
import cn.iocoder.yudao.module.langchat.entity.AigcModel;
|
||||
import cn.iocoder.yudao.module.langchat.enums.ChatErrorEnum;
|
||||
import cn.iocoder.yudao.module.langchat.enums.ProviderEnum;
|
||||
import cn.iocoder.yudao.module.langchat.properties.LangChatProps;
|
||||
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(AigcModel model) {
|
||||
return ProviderEnum.ZHIPU.name().equals(model.getProvider());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean basicCheck(AigcModel 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(AigcModel model) {
|
||||
try {
|
||||
if (!whetherCurrentModel(model)) {
|
||||
return null;
|
||||
}
|
||||
if (!basicCheck(model)) {
|
||||
return null;
|
||||
}
|
||||
return 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(AigcModel model) {
|
||||
try {
|
||||
if (!whetherCurrentModel(model)) {
|
||||
return null;
|
||||
}
|
||||
if (!basicCheck(model)) {
|
||||
return null;
|
||||
}
|
||||
return 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(AigcModel 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(AigcModel 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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
/**
|
||||
* 占位
|
||||
*/
|
||||
package cn.iocoder.yudao.module.langchat.server.framework.security.core;
|
|
@ -1,104 +0,0 @@
|
|||
/*
|
||||
* 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 cn.iocoder.yudao.module.langchat.server.utils;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* @author tycoding
|
||||
* @since 2024/1/2
|
||||
*/
|
||||
@Data
|
||||
public class R<T> implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private int code = HttpStatus.OK.value();
|
||||
|
||||
private String message = HttpStatus.OK.getReasonPhrase();
|
||||
|
||||
private T result;
|
||||
|
||||
public R() {
|
||||
super();
|
||||
}
|
||||
|
||||
public R(int code, String message) {
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
protected R(T result) {
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
protected R(HttpStatus httpStatus) {
|
||||
this.code = httpStatus.value();
|
||||
this.message = httpStatus.getReasonPhrase();
|
||||
}
|
||||
|
||||
protected R(T result, HttpStatus httpStatus) {
|
||||
this.result = result;
|
||||
this.code = httpStatus.value();
|
||||
this.message = httpStatus.getReasonPhrase();
|
||||
}
|
||||
|
||||
protected R(Throwable e) {
|
||||
super();
|
||||
this.code = HttpStatus.INTERNAL_SERVER_ERROR.value();
|
||||
this.message = e.getMessage();
|
||||
}
|
||||
|
||||
public static <T> R<T> ok(T result) {
|
||||
return new R(result);
|
||||
}
|
||||
|
||||
public static <T> R<T> ok(T result, HttpStatus httpStatus) {
|
||||
return new R(result, httpStatus);
|
||||
}
|
||||
|
||||
public static <T> R<T> ok() {
|
||||
return new R<>();
|
||||
}
|
||||
|
||||
public static <T> R<T> ok(String message) {
|
||||
return new R<>(HttpStatus.OK.value(), message);
|
||||
}
|
||||
|
||||
public static <T> R<T> ok(HttpStatus httpStatus) {
|
||||
return new R<>(httpStatus);
|
||||
}
|
||||
|
||||
public static <T> R<T> fail(String message) {
|
||||
return new R<>(HttpStatus.INTERNAL_SERVER_ERROR.value(), message);
|
||||
}
|
||||
|
||||
public static <T> R<T> fail(int code, String message) {
|
||||
return new R<>(code, message);
|
||||
}
|
||||
|
||||
public static <T> R<T> fail(HttpStatus status) {
|
||||
return new R<>(status.value(), status.getReasonPhrase());
|
||||
}
|
||||
|
||||
public static <T> R<T> fail(Throwable e) {
|
||||
return new R<>(e);
|
||||
}
|
||||
}
|
|
@ -14,9 +14,9 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.iocoder.yudao.module.langchat.server.service;
|
||||
package cn.iocoder.yudao.module.langchat.service.aigc;
|
||||
|
||||
import cn.iocoder.yudao.module.langchat.server.entity.AigcEmbedStore;
|
||||
import cn.iocoder.yudao.module.langchat.entity.AigcEmbedStore;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
|
||||
/**
|
|
@ -14,11 +14,11 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.iocoder.yudao.module.langchat.server.service;
|
||||
package cn.iocoder.yudao.module.langchat.service.aigc;
|
||||
|
||||
import cn.iocoder.yudao.module.langchat.server.entity.AigcDocs;
|
||||
import cn.iocoder.yudao.module.langchat.server.entity.AigcDocsSlice;
|
||||
import cn.iocoder.yudao.module.langchat.server.entity.AigcKnowledge;
|
||||
import cn.iocoder.yudao.module.langchat.entity.AigcDocs;
|
||||
import cn.iocoder.yudao.module.langchat.entity.AigcDocsSlice;
|
||||
import cn.iocoder.yudao.module.langchat.entity.AigcKnowledge;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
|
||||
import java.util.List;
|
|
@ -14,11 +14,11 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.iocoder.yudao.module.langchat.server.service;
|
||||
package cn.iocoder.yudao.module.langchat.service.aigc;
|
||||
|
||||
import cn.iocoder.yudao.module.langchat.server.entity.AigcConversation;
|
||||
import cn.iocoder.yudao.module.langchat.server.entity.AigcMessage;
|
||||
import cn.iocoder.yudao.module.langchat.server.utils.QueryPage;
|
||||
import cn.iocoder.yudao.module.langchat.entity.AigcConversation;
|
||||
import cn.iocoder.yudao.module.langchat.entity.AigcMessage;
|
||||
import cn.iocoder.yudao.module.langchat.utils.QueryPage;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
|
|
@ -14,10 +14,10 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.iocoder.yudao.module.langchat.server.service;
|
||||
package cn.iocoder.yudao.module.langchat.service.aigc;
|
||||
|
||||
import cn.iocoder.yudao.module.langchat.server.entity.AigcModel;
|
||||
import cn.iocoder.yudao.module.langchat.server.utils.QueryPage;
|
||||
import cn.iocoder.yudao.module.langchat.entity.AigcModel;
|
||||
import cn.iocoder.yudao.module.langchat.utils.QueryPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* 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 cn.iocoder.yudao.module.langchat.service.aigc;
|
||||
|
||||
import cn.iocoder.yudao.module.langchat.entity.AigcOss;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
/**
|
||||
* @author tycoding
|
||||
* @since 2024/1/4
|
||||
*/
|
||||
public interface AigcOssService extends IService<AigcOss> {
|
||||
|
||||
AigcOss upload(MultipartFile file, String userId);
|
||||
}
|
||||
|
|
@ -14,11 +14,11 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.iocoder.yudao.module.langchat.server.service.impl;
|
||||
package cn.iocoder.yudao.module.langchat.service.aigc.impl;
|
||||
|
||||
import cn.iocoder.yudao.module.langchat.server.entity.AigcEmbedStore;
|
||||
import cn.iocoder.yudao.module.langchat.server.mapper.AigcEmbedStoreMapper;
|
||||
import cn.iocoder.yudao.module.langchat.server.service.AigcEmbedStoreService;
|
||||
import cn.iocoder.yudao.module.langchat.entity.AigcEmbedStore;
|
||||
import cn.iocoder.yudao.module.langchat.mapper.AigcEmbedStoreMapper;
|
||||
import cn.iocoder.yudao.module.langchat.service.aigc.AigcEmbedStoreService;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
|
@ -14,15 +14,15 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.iocoder.yudao.module.langchat.server.service.impl;
|
||||
package cn.iocoder.yudao.module.langchat.service.aigc.impl;
|
||||
|
||||
import cn.iocoder.yudao.module.langchat.server.entity.AigcDocs;
|
||||
import cn.iocoder.yudao.module.langchat.server.entity.AigcDocsSlice;
|
||||
import cn.iocoder.yudao.module.langchat.server.entity.AigcKnowledge;
|
||||
import cn.iocoder.yudao.module.langchat.server.mapper.AigcDocsMapper;
|
||||
import cn.iocoder.yudao.module.langchat.server.mapper.AigcDocsSliceMapper;
|
||||
import cn.iocoder.yudao.module.langchat.server.mapper.AigcKnowledgeMapper;
|
||||
import cn.iocoder.yudao.module.langchat.server.service.AigcKnowledgeService;
|
||||
import cn.iocoder.yudao.module.langchat.entity.AigcDocs;
|
||||
import cn.iocoder.yudao.module.langchat.entity.AigcDocsSlice;
|
||||
import cn.iocoder.yudao.module.langchat.entity.AigcKnowledge;
|
||||
import cn.iocoder.yudao.module.langchat.mapper.AigcDocsMapper;
|
||||
import cn.iocoder.yudao.module.langchat.mapper.AigcDocsSliceMapper;
|
||||
import cn.iocoder.yudao.module.langchat.mapper.AigcKnowledgeMapper;
|
||||
import cn.iocoder.yudao.module.langchat.service.aigc.AigcKnowledgeService;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
|
@ -14,17 +14,17 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.iocoder.yudao.module.langchat.server.service.impl;
|
||||
package cn.iocoder.yudao.module.langchat.service.aigc.impl;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.yudao.module.langchat.server.entity.AigcConversation;
|
||||
import cn.iocoder.yudao.module.langchat.server.entity.AigcMessage;
|
||||
import cn.iocoder.yudao.module.langchat.server.entity.SysUser;
|
||||
import cn.iocoder.yudao.module.langchat.server.mapper.AigcConversationMapper;
|
||||
import cn.iocoder.yudao.module.langchat.server.mapper.AigcMessageMapper;
|
||||
import cn.iocoder.yudao.module.langchat.server.mapper.SysUserMapper;
|
||||
import cn.iocoder.yudao.module.langchat.server.service.AigcMessageService;
|
||||
import cn.iocoder.yudao.module.langchat.server.utils.QueryPage;
|
||||
import cn.iocoder.yudao.module.langchat.entity.AigcConversation;
|
||||
import cn.iocoder.yudao.module.langchat.entity.AigcMessage;
|
||||
import cn.iocoder.yudao.module.langchat.entity.SysUser;
|
||||
import cn.iocoder.yudao.module.langchat.mapper.AigcConversationMapper;
|
||||
import cn.iocoder.yudao.module.langchat.mapper.AigcMessageMapper;
|
||||
import cn.iocoder.yudao.module.langchat.mapper.SysUserMapper;
|
||||
import cn.iocoder.yudao.module.langchat.service.aigc.AigcMessageService;
|
||||
import cn.iocoder.yudao.module.langchat.utils.QueryPage;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
|
@ -60,7 +60,7 @@ public class AigcMessageServiceImpl extends ServiceImpl<AigcMessageMapper, AigcM
|
|||
|
||||
@Override
|
||||
public IPage<AigcConversation> conversationPages(AigcConversation data, QueryPage queryPage) {
|
||||
Page<AigcConversation> page = new Page<>(queryPage.getPage(), queryPage.getLimit());
|
||||
Page<AigcConversation> page = new Page<>(queryPage.getPageNo(), queryPage.getPageSize());
|
||||
Page<AigcConversation> iPage = aigcConversationMapper.selectPage(page, Wrappers.<AigcConversation>lambdaQuery()
|
||||
.like(!StrUtil.isBlank(data.getTitle()), AigcConversation::getTitle, data.getTitle())
|
||||
.orderByDesc(AigcConversation::getCreateTime));
|
|
@ -14,14 +14,14 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.iocoder.yudao.module.langchat.server.service.impl;
|
||||
package cn.iocoder.yudao.module.langchat.service.aigc.impl;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.yudao.module.langchat.server.component.ModelTypeEnum;
|
||||
import cn.iocoder.yudao.module.langchat.server.entity.AigcModel;
|
||||
import cn.iocoder.yudao.module.langchat.server.mapper.AigcModelMapper;
|
||||
import cn.iocoder.yudao.module.langchat.server.service.AigcModelService;
|
||||
import cn.iocoder.yudao.module.langchat.server.utils.QueryPage;
|
||||
import cn.iocoder.yudao.module.langchat.component.ModelTypeEnum;
|
||||
import cn.iocoder.yudao.module.langchat.entity.AigcModel;
|
||||
import cn.iocoder.yudao.module.langchat.mapper.AigcModelMapper;
|
||||
import cn.iocoder.yudao.module.langchat.service.aigc.AigcModelService;
|
||||
import cn.iocoder.yudao.module.langchat.utils.QueryPage;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
|
@ -73,7 +73,7 @@ public class AigcModelServiceImpl extends ServiceImpl<AigcModelMapper, AigcModel
|
|||
|
||||
@Override
|
||||
public Page<AigcModel> page(AigcModel data, QueryPage queryPage) {
|
||||
Page<AigcModel> page = new Page<>(queryPage.getPage(), queryPage.getLimit());
|
||||
Page<AigcModel> page = new Page<>(queryPage.getPageNo(), queryPage.getPageSize());
|
||||
Page<AigcModel> iPage = this.page(page, Wrappers.<AigcModel>lambdaQuery().eq(AigcModel::getProvider, data.getProvider()));
|
||||
iPage.getRecords().forEach(this::hide);
|
||||
return iPage;
|
|
@ -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 cn.iocoder.yudao.module.langchat.service.oss.impl;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.date.DatePattern;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.iocoder.yudao.module.langchat.entity.AigcOss;
|
||||
import cn.iocoder.yudao.module.langchat.mapper.AigcOssMapper;
|
||||
import cn.iocoder.yudao.module.langchat.service.aigc.AigcOssService;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.x.file.storage.core.FileInfo;
|
||||
import org.dromara.x.file.storage.core.FileStorageService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* @author tycoding
|
||||
* @since 2024/1/4
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class AigcOssServiceImpl extends ServiceImpl<AigcOssMapper, AigcOss> implements AigcOssService {
|
||||
|
||||
private final FileStorageService fileStorageService;
|
||||
|
||||
@Override
|
||||
public AigcOss upload(MultipartFile file, String userId) {
|
||||
log.info(">>>>>>>>>>>>>> OSS文件上传开始: {}", file.getOriginalFilename());
|
||||
FileInfo info = fileStorageService.of(file)
|
||||
.setPath(DateUtil.format(new Date(), DatePattern.PURE_DATE_PATTERN))
|
||||
.upload();
|
||||
log.info(">>>>>>>>>>>>>> OSS文件上传结束: {} - {}", info.getFilename(), info.getUrl());
|
||||
AigcOss oss = BeanUtil.copyProperties(info, AigcOss.class);
|
||||
oss.setOssId(info.getId());
|
||||
oss.setUserId(userId);
|
||||
this.save(oss);
|
||||
return oss;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* 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 cn.iocoder.yudao.module.langchat.service.core;
|
||||
|
||||
|
||||
import cn.iocoder.yudao.module.langchat.entity.AigcDocs;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author tycoding
|
||||
* @since 2024/6/6
|
||||
*/
|
||||
public interface EmbeddingService {
|
||||
|
||||
void clearDocSlices(String docsId);
|
||||
|
||||
void embedDocsSlice(AigcDocs data, String url);
|
||||
|
||||
List<Map<String, Object>> search(AigcDocs data);
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* 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 cn.iocoder.yudao.module.langchat.service.core;
|
||||
|
||||
|
||||
import cn.iocoder.yudao.module.langchat.dal.dto.ChatReq;
|
||||
import cn.iocoder.yudao.module.langchat.dal.dto.EmbeddingR;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author tycoding
|
||||
* @since 2024/4/4
|
||||
*/
|
||||
public interface LangEmbeddingService {
|
||||
|
||||
EmbeddingR embeddingText(ChatReq req);
|
||||
|
||||
List<EmbeddingR> embeddingDocs(ChatReq req);
|
||||
}
|
|
@ -0,0 +1,126 @@
|
|||
/*
|
||||
* 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 cn.iocoder.yudao.module.langchat.service.core.impl;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.yudao.module.langchat.dal.dto.ChatReq;
|
||||
import cn.iocoder.yudao.module.langchat.dal.dto.EmbeddingR;
|
||||
import cn.iocoder.yudao.module.langchat.enums.EmbedConst;
|
||||
import cn.iocoder.yudao.module.langchat.entity.AigcDocs;
|
||||
import cn.iocoder.yudao.module.langchat.entity.AigcDocsSlice;
|
||||
import cn.iocoder.yudao.module.langchat.mapper.AigcDocsMapper;
|
||||
import cn.iocoder.yudao.module.langchat.provider.EmbeddingProvider;
|
||||
import cn.iocoder.yudao.module.langchat.service.aigc.AigcKnowledgeService;
|
||||
import cn.iocoder.yudao.module.langchat.service.core.EmbeddingService;
|
||||
import cn.iocoder.yudao.module.langchat.service.core.LangEmbeddingService;
|
||||
import dev.langchain4j.data.embedding.Embedding;
|
||||
import dev.langchain4j.data.segment.TextSegment;
|
||||
import dev.langchain4j.model.embedding.EmbeddingModel;
|
||||
import dev.langchain4j.store.embedding.EmbeddingSearchRequest;
|
||||
import dev.langchain4j.store.embedding.EmbeddingSearchResult;
|
||||
import dev.langchain4j.store.embedding.EmbeddingStore;
|
||||
import dev.langchain4j.store.embedding.filter.Filter;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static dev.langchain4j.store.embedding.filter.MetadataFilterBuilder.metadataKey;
|
||||
|
||||
|
||||
/**
|
||||
* @author tycoding
|
||||
* @since 2024/6/6
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@AllArgsConstructor
|
||||
public class EmbeddingServiceImpl implements EmbeddingService {
|
||||
|
||||
private final EmbeddingProvider embeddingProvider;
|
||||
private final LangEmbeddingService langEmbeddingService;
|
||||
private final AigcKnowledgeService aigcKnowledgeService;
|
||||
private final AigcDocsMapper aigcDocsMapper;
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void clearDocSlices(String docsId) {
|
||||
if (StrUtil.isBlank(docsId)) {
|
||||
return;
|
||||
}
|
||||
// remove from embedding store
|
||||
List<String> vectorIds = aigcKnowledgeService.listSliceVectorIdsOfDoc(docsId);
|
||||
if (vectorIds.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
AigcDocs docs = aigcDocsMapper.selectById(docsId);
|
||||
EmbeddingStore<TextSegment> embeddingStore = embeddingProvider.getEmbeddingStore(docs.getKnowledgeId());
|
||||
embeddingStore.removeAll(vectorIds);
|
||||
// remove from docSlice
|
||||
aigcKnowledgeService.removeSlicesOfDoc(docsId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void embedDocsSlice(AigcDocs data, String url) {
|
||||
List<EmbeddingR> list = langEmbeddingService.embeddingDocs(
|
||||
new ChatReq()
|
||||
.setDocsName(data.getName())
|
||||
.setKnowledgeId(data.getKnowledgeId())
|
||||
.setUrl(url));
|
||||
list.forEach(i -> {
|
||||
aigcKnowledgeService.addDocsSlice(new AigcDocsSlice()
|
||||
.setKnowledgeId(data.getKnowledgeId())
|
||||
.setDocsId(data.getId())
|
||||
.setVectorId(i.getVectorId())
|
||||
.setName(data.getName())
|
||||
.setContent(i.getText())
|
||||
);
|
||||
});
|
||||
|
||||
aigcKnowledgeService.updateDocs(new AigcDocs().setId(data.getId()).setSliceStatus(true).setSliceNum(list.size()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Map<String, Object>> search(AigcDocs data) {
|
||||
if (StrUtil.isBlank(data.getKnowledgeId()) || StrUtil.isBlank(data.getContent())) {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
EmbeddingModel embeddingModel = embeddingProvider.getEmbeddingModel(data.getKnowledgeId());
|
||||
EmbeddingStore<TextSegment> embeddingStore = embeddingProvider.getEmbeddingStore(data.getKnowledgeId());
|
||||
Embedding queryEmbedding = embeddingModel.embed(data.getContent()).content();
|
||||
Filter filter = metadataKey(EmbedConst.KNOWLEDGE).isEqualTo(data.getKnowledgeId());
|
||||
EmbeddingSearchResult<TextSegment> list = embeddingStore.search(EmbeddingSearchRequest
|
||||
.builder()
|
||||
.queryEmbedding(queryEmbedding)
|
||||
.filter(filter)
|
||||
.build());
|
||||
|
||||
List<Map<String, Object>> result = new ArrayList<>();
|
||||
list.matches().forEach(i -> {
|
||||
TextSegment embedded = i.embedded();
|
||||
Map<String, Object> map = embedded.metadata().toMap();
|
||||
map.put("text", embedded.text());
|
||||
result.add(map);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
* 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 cn.iocoder.yudao.module.langchat.service.core.impl;
|
||||
|
||||
import cn.iocoder.yudao.module.langchat.dal.dto.ChatReq;
|
||||
import cn.iocoder.yudao.module.langchat.dal.dto.EmbeddingR;
|
||||
import cn.iocoder.yudao.module.langchat.enums.EmbedConst;
|
||||
import cn.iocoder.yudao.module.langchat.provider.EmbeddingProvider;
|
||||
import cn.iocoder.yudao.module.langchat.service.core.LangEmbeddingService;
|
||||
import dev.langchain4j.data.document.Document;
|
||||
import dev.langchain4j.data.document.DocumentSplitter;
|
||||
import dev.langchain4j.data.document.Metadata;
|
||||
import dev.langchain4j.data.document.loader.UrlDocumentLoader;
|
||||
|
||||
import dev.langchain4j.data.document.parser.apache.tika.ApacheTikaDocumentParser;
|
||||
import dev.langchain4j.data.embedding.Embedding;
|
||||
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.Service;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author tycoding
|
||||
* @since 2024/4/4
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@AllArgsConstructor
|
||||
public class LangEmbeddingServiceImpl implements LangEmbeddingService {
|
||||
|
||||
private final EmbeddingProvider embeddingProvider;
|
||||
|
||||
@Override
|
||||
public EmbeddingR embeddingText(ChatReq req) {
|
||||
log.info(">>>>>>>>>>>>>> Text文本向量解析开始,KnowledgeId={}, DocsName={}", req.getKnowledgeId(), req.getDocsName());
|
||||
TextSegment segment = TextSegment.from(req.getMessage(),
|
||||
Metadata.metadata(EmbedConst.KNOWLEDGE, req.getKnowledgeId()).put(EmbedConst.FILENAME, req.getDocsName()));
|
||||
|
||||
EmbeddingModel embeddingModel = embeddingProvider.getEmbeddingModel(req.getKnowledgeId());
|
||||
EmbeddingStore<TextSegment> embeddingStore = embeddingProvider.getEmbeddingStore(req.getKnowledgeId());
|
||||
Embedding embedding = embeddingModel.embed(segment).content();
|
||||
String id = embeddingStore.add(embedding, segment);
|
||||
|
||||
log.info(">>>>>>>>>>>>>> Text文本向量解析结束,KnowledgeId={}, DocsName={}", req.getKnowledgeId(), req.getDocsName());
|
||||
return new EmbeddingR().setVectorId(id).setText(segment.text());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<EmbeddingR> embeddingDocs(ChatReq req) {
|
||||
log.info(">>>>>>>>>>>>>> Docs文档向量解析开始,KnowledgeId={}, DocsName={}", req.getKnowledgeId(), req.getDocsName());
|
||||
Document document = UrlDocumentLoader.load(req.getUrl(), new ApacheTikaDocumentParser());
|
||||
document.metadata().put(EmbedConst.KNOWLEDGE, req.getKnowledgeId()).put(EmbedConst.FILENAME, req.getDocsName());
|
||||
|
||||
List<EmbeddingR> list = new ArrayList<>();
|
||||
try {
|
||||
DocumentSplitter splitter = EmbeddingProvider.splitter();
|
||||
List<TextSegment> segments = splitter.split(document);
|
||||
|
||||
EmbeddingModel embeddingModel = embeddingProvider.getEmbeddingModel(req.getKnowledgeId());
|
||||
EmbeddingStore<TextSegment> embeddingStore = embeddingProvider.getEmbeddingStore(req.getKnowledgeId());
|
||||
List<Embedding> embeddings = embeddingModel.embedAll(segments).content();
|
||||
List<String> ids = embeddingStore.addAll(embeddings, segments);
|
||||
|
||||
for (int i = 0; i < ids.size(); i++) {
|
||||
list.add(new EmbeddingR().setVectorId(ids.get(i)).setText(segments.get(i).text()));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
log.info(">>>>>>>>>>>>>> Docs文档向量解析结束,KnowledgeId={}, DocsName={}", req.getKnowledgeId(), req.getDocsName());
|
||||
return list;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* 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 cn.iocoder.yudao.module.langchat.task;
|
||||
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.LinkedBlockingDeque;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* @author GB
|
||||
* @since 2024-08-22
|
||||
*/
|
||||
public class AnalysisThreadPool {
|
||||
/**
|
||||
* 根据cpu 数量动态配置核心线程数和最大线程数
|
||||
*/
|
||||
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
|
||||
/**
|
||||
* 核心线程数
|
||||
*/
|
||||
private static final int CORE_PO0L_SIZE = CPU_COUNT + 1;
|
||||
/**
|
||||
* 最大线程数
|
||||
*/
|
||||
private static final int MAX_POOL_SIZE = 2 * CPU_COUNT + 1;
|
||||
/**
|
||||
* 线程队列
|
||||
*/
|
||||
private static final int MAX_LIMIT_JOB_SIZE = 1000;
|
||||
/**
|
||||
* 非核心线程存活时间1s
|
||||
*/
|
||||
private static final int KEEP_ALIVE = 1;
|
||||
volatile private static ThreadPoolExecutor EXECUTOR = null;
|
||||
|
||||
public static ThreadPoolExecutor getThreadPool() {
|
||||
if (null == EXECUTOR) {
|
||||
synchronized (AnalysisThreadPool.class) {
|
||||
if (null == EXECUTOR) {
|
||||
EXECUTOR = new ThreadPoolExecutor(
|
||||
CORE_PO0L_SIZE,
|
||||
MAX_POOL_SIZE,
|
||||
KEEP_ALIVE,
|
||||
TimeUnit.MICROSECONDS,
|
||||
new LinkedBlockingDeque<>(MAX_LIMIT_JOB_SIZE),
|
||||
Executors.defaultThreadFactory(),
|
||||
new ThreadPoolExecutor.AbortPolicy() {
|
||||
@Override
|
||||
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
|
||||
super.rejectedExecution(r, e);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
return EXECUTOR;
|
||||
}
|
||||
|
||||
public static void execute(Runnable runable) {
|
||||
getThreadPool().execute(runable);
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* 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 cn.iocoder.yudao.module.langchat.task;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
|
||||
/**
|
||||
* @author GB
|
||||
* @since 2024-08-22
|
||||
*/
|
||||
public class TaskManager {
|
||||
private static final ConcurrentHashMap<String, List<Future<?>>> TASK_MAP = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* 提交任务
|
||||
*/
|
||||
public static void submitTask(String id, Callable<?> function) {
|
||||
Future<?> future = AnalysisThreadPool.getThreadPool().submit(function);
|
||||
List<Future<?>> orDefault = TASK_MAP.getOrDefault(id, new ArrayList<>());
|
||||
orDefault.add(future);
|
||||
TASK_MAP.put(id, orDefault);
|
||||
}
|
||||
|
||||
/**
|
||||
* 弹出任务
|
||||
*/
|
||||
public void popTaskResult(String id) {
|
||||
TASK_MAP.remove(id);
|
||||
}
|
||||
|
||||
public int getCount(String id) {
|
||||
if (TASK_MAP.containsKey(id)) {
|
||||
Collection<?> collection = TASK_MAP.get(id);
|
||||
return collection != null ? collection.size() : 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.iocoder.yudao.module.langchat.server.utils;
|
||||
package cn.iocoder.yudao.module.langchat.utils;
|
||||
|
||||
import cn.hutool.core.lang.Dict;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
|
@ -33,13 +33,13 @@ public class MybatisUtil {
|
|||
* @return 格式化后的Map对象
|
||||
*/
|
||||
public static Dict getData(IPage<?> page) {
|
||||
return Dict.create().set("rows", page.getRecords()).set("total", (int) page.getTotal());
|
||||
return Dict.create().set("list", page.getRecords()).set("total", (int) page.getTotal());
|
||||
}
|
||||
|
||||
/**
|
||||
* QueryPage对象转换为Page对象
|
||||
*/
|
||||
public static <T> IPage<T> wrap(T t, QueryPage query) {
|
||||
return new Page<T>(query.getPage(), query.getLimit());
|
||||
return new Page<T>(query.getPageNo(), query.getPageSize());
|
||||
}
|
||||
}
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.iocoder.yudao.module.langchat.server.utils;
|
||||
package cn.iocoder.yudao.module.langchat.utils;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
@ -33,10 +33,10 @@ public class QueryPage implements Serializable {
|
|||
/**
|
||||
* 当前页
|
||||
*/
|
||||
private int page = 1;
|
||||
private int pageNo = 1;
|
||||
|
||||
/**
|
||||
* 每页的记录数
|
||||
*/
|
||||
private int limit = 10;
|
||||
private int pageSize = 10;
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
* 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 cn.iocoder.yudao.module.langchat.utils;
|
||||
|
||||
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
|
||||
/**
|
||||
* @author tycoding
|
||||
* @since 2024/1/30
|
||||
*/
|
||||
public class StreamEmitter {
|
||||
|
||||
private final SseEmitter emitter;
|
||||
|
||||
public StreamEmitter() {
|
||||
emitter = new SseEmitter(5 * 60 * 1000L);
|
||||
}
|
||||
|
||||
public SseEmitter get() {
|
||||
return emitter;
|
||||
}
|
||||
|
||||
public SseEmitter streaming(final ExecutorService executor, Runnable func) {
|
||||
// ExecutorService executor = Executors.newSingleThreadExecutor();
|
||||
|
||||
emitter.onCompletion(() -> {
|
||||
System.out.println("SseEmitter 完成");
|
||||
executor.shutdownNow();
|
||||
});
|
||||
|
||||
emitter.onError((e) -> {
|
||||
System.out.println("SseEmitter 出现错误: " + e.getMessage());
|
||||
executor.shutdownNow();
|
||||
});
|
||||
|
||||
emitter.onTimeout(() -> {
|
||||
System.out.println("SseEmitter 超时");
|
||||
emitter.complete();
|
||||
executor.shutdownNow();
|
||||
});
|
||||
executor.execute(() -> {
|
||||
try {
|
||||
func.run();
|
||||
} catch (Exception e) {
|
||||
System.out.println("捕获到异常: " + e.getMessage());
|
||||
emitter.completeWithError(e);
|
||||
Thread.currentThread().interrupt();
|
||||
} finally {
|
||||
if (!executor.isShutdown()) {
|
||||
executor.shutdownNow();
|
||||
}
|
||||
}
|
||||
});
|
||||
return emitter;
|
||||
}
|
||||
|
||||
public void send(Object obj) {
|
||||
try {
|
||||
emitter.send(obj);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void complete() {
|
||||
emitter.complete();
|
||||
}
|
||||
|
||||
public void error(String message) {
|
||||
try {
|
||||
emitter.send("Error: " + message);
|
||||
emitter.complete();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
* 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 cn.iocoder.yudao.module.langchat.wrapper;
|
||||
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import org.dromara.x.file.storage.core.exception.FileStorageRuntimeException;
|
||||
import org.dromara.x.file.storage.core.file.FileWrapper;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* MultipartFile 文件包装类
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
public class MultipartFileWrapper implements FileWrapper {
|
||||
private MultipartFile file;
|
||||
private String name;
|
||||
private String contentType;
|
||||
private InputStream inputStream;
|
||||
private Long size;
|
||||
|
||||
public MultipartFileWrapper(MultipartFile file, String name, String contentType, Long size) {
|
||||
this.file = file;
|
||||
this.name = name;
|
||||
this.contentType = contentType;
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getInputStream() throws IOException {
|
||||
if (inputStream == null) {
|
||||
inputStream = new BufferedInputStream(file.getInputStream());
|
||||
}
|
||||
return inputStream;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transferTo(File dest) {
|
||||
// 在某些 SpringBoot 版本中,例如 2.4.6,此方法会调用失败,
|
||||
// 此时尝试手动将 InputStream 写入指定文件,
|
||||
// 根据文档来看 MultipartFile 最终都会由框架从临时目录中删除
|
||||
try {
|
||||
file.transferTo(dest);
|
||||
IoUtil.close(inputStream);
|
||||
} catch (Exception ignored) {
|
||||
try {
|
||||
FileUtil.writeFromStream(getInputStream(), dest);
|
||||
} catch (Exception e) {
|
||||
throw new FileStorageRuntimeException("文件移动失败", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportTransfer() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void setSize(Long size) {
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public void setContentType(String contentType) {
|
||||
this.contentType = contentType;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* 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 cn.iocoder.yudao.module.langchat.wrapper;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.dromara.x.file.storage.core.file.FileWrapper;
|
||||
import org.dromara.x.file.storage.core.file.FileWrapperAdapter;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
/**
|
||||
* MultipartFile 文件包装适配器
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public class MultipartFileWrapperAdapter implements FileWrapperAdapter {
|
||||
|
||||
@Override
|
||||
public boolean isSupport(Object source) {
|
||||
return source instanceof MultipartFile || source instanceof MultipartFileWrapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileWrapper getFileWrapper(Object source, String name, String contentType, Long size) {
|
||||
if (source instanceof MultipartFileWrapper) {
|
||||
return updateFileWrapper((MultipartFileWrapper) source, name, contentType, size);
|
||||
} else {
|
||||
MultipartFile file = (MultipartFile) source;
|
||||
if (name == null) name = file.getOriginalFilename();
|
||||
if (contentType == null) contentType = file.getContentType();
|
||||
if (size == null) size = file.getSize();
|
||||
return new MultipartFileWrapper(file, name, contentType, size);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,7 +3,7 @@
|
|||
spring:
|
||||
cloud:
|
||||
nacos:
|
||||
server-addr: 192.168.0.17:8848 # Nacos 服务器地址
|
||||
server-addr: 192.168.0.15:8848 # Nacos 服务器地址
|
||||
username: nacos # Nacos 账号
|
||||
password: nacos # Nacos 密码
|
||||
discovery: # 【配置中心】配置项
|
||||
|
|
|
@ -3,16 +3,16 @@
|
|||
spring:
|
||||
cloud:
|
||||
nacos:
|
||||
server-addr: 192.168.0.17:8848 # Nacos 服务器地址
|
||||
server-addr: 192.168.0.15:8848 # Nacos 服务器地址
|
||||
username: nacos # Nacos 账号
|
||||
password: nacos # Nacos 密码
|
||||
discovery: # 【配置中心】配置项
|
||||
namespace: dev # 命名空间。这里使用 dev 开发环境
|
||||
namespace: yudao # 命名空间。这里使用 dev 开发环境
|
||||
group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP
|
||||
metadata:
|
||||
version: 1.0.0 # 服务实例的版本号,可用于灰度发布
|
||||
config: # 【注册中心】配置项
|
||||
namespace: dev # 命名空间。这里使用 dev 开发环境
|
||||
namespace: yudao # 命名空间。这里使用 dev 开发环境
|
||||
group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP
|
||||
|
||||
--- #################### 数据库相关配置 ####################
|
||||
|
@ -77,7 +77,7 @@ spring:
|
|||
|
||||
# Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
|
||||
redis:
|
||||
host: 192.168.0.21 # 地址
|
||||
host: 127.0.0.1 # 地址
|
||||
port: 6379 # 端口
|
||||
database: 0 # 数据库索引
|
||||
password: 123456 # 密码,建议生产环境开启
|
||||
|
@ -211,3 +211,23 @@ justauth:
|
|||
type: REDIS
|
||||
prefix: 'social_auth_state:' # 缓存前缀,目前只对 Redis 缓存生效,默认 JUSTAUTH::STATE::
|
||||
timeout: 24h # 超时时长,目前只对 Redis 缓存生效,默认 3 分钟
|
||||
langchat:
|
||||
oss:
|
||||
default-platform: local
|
||||
qiniu-kodo:
|
||||
- platform: qiniu # 存储平台标识,七牛:qiniu、阿里OSS:aliyun-oss、腾讯OSS:tencent-cos
|
||||
enable-storage: true # 启用存储
|
||||
access-key: <ak>
|
||||
secret-key: <sk>
|
||||
bucket-name:
|
||||
domain: / # 访问域名,注意“/”结尾,例如:http://abc.hn-bkt.clouddn.com/
|
||||
base-path: langchat/ # 基础路径
|
||||
local-plus:
|
||||
- platform: local # 存储平台标识
|
||||
enable-storage: true #启用存储
|
||||
enable-access: true #启用访问(线上请使用 Nginx 配置,效率更高)
|
||||
domain: http://127.0.0.1:8100/ # 访问域名,例如:“http://127.0.0.1:8030/file/”,注意后面要和 path-patterns 保持一致,“/”结尾,本地存储建议使用相对路径,方便后期更换域名
|
||||
base-path: langchat/ # 基础路径
|
||||
path-patterns: /static/** # 访问路径(默认本地target目录),注意:如果使用本地nginx容器,此路径要修改为nginx存储路径
|
||||
storage-path: ./static/ # 存储路径(默认本地target目录),注意:如果使用本地nginx容器,此路径要修改为nginx存储路径
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
spring:
|
||||
cloud:
|
||||
nacos:
|
||||
server-addr: 192.168.0.17:8848 # Nacos 服务器地址
|
||||
server-addr: 192.168.0.15:8848 # Nacos 服务器地址
|
||||
username: nacos # Nacos 账号
|
||||
password: nacos # Nacos 密码
|
||||
discovery: # 【配置中心】配置项
|
||||
|
|
Loading…
Reference in New Issue