1.添加模型应用及对话内容
This commit is contained in:
parent
e36c60af92
commit
04b6793941
|
@ -18,8 +18,10 @@ package cn.iocoder.yudao.module.langchat;
|
|||
|
||||
import cn.iocoder.yudao.module.langchat.config.EnableFileStorage;
|
||||
import cn.iocoder.yudao.module.langchat.config.SpringFileStorageProperties;
|
||||
import cn.iocoder.yudao.module.langchat.properties.ChatProps;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
|
||||
/**
|
||||
|
@ -30,6 +32,7 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties
|
|||
@SpringBootApplication
|
||||
@EnableConfigurationProperties({
|
||||
SpringFileStorageProperties.class,
|
||||
ChatProps.class
|
||||
})
|
||||
public class LangChatApplication {
|
||||
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* 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.constant;
|
||||
|
||||
/**
|
||||
* @author tycoding
|
||||
* @since 2024/1/15
|
||||
*/
|
||||
public interface CommonConst {
|
||||
|
||||
/**
|
||||
* UTF-8 编码
|
||||
*/
|
||||
String UTF_8 = "utf-8";
|
||||
|
||||
/**
|
||||
* 菜单类型:menu
|
||||
*/
|
||||
String MENU_TYPE_MENU = "menu";
|
||||
|
||||
/**
|
||||
* 菜单类型:button
|
||||
*/
|
||||
String MENU_TYPE_BUTTON = "button";
|
||||
|
||||
/**
|
||||
* 菜单:默认Icon图标
|
||||
*/
|
||||
String MENU_ICON = "alert";
|
||||
|
||||
String LAYOUT = "Layout";
|
||||
}
|
|
@ -0,0 +1,130 @@
|
|||
/*
|
||||
* 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.hutool.core.lang.Dict;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
|
||||
import cn.iocoder.yudao.module.langchat.entity.AigcApp;
|
||||
import cn.iocoder.yudao.module.langchat.entity.AigcAppApi;
|
||||
import cn.iocoder.yudao.module.langchat.entity.AigcKnowledge;
|
||||
import cn.iocoder.yudao.module.langchat.service.aigc.AigcAppApiService;
|
||||
import cn.iocoder.yudao.module.langchat.service.aigc.AigcAppService;
|
||||
import cn.iocoder.yudao.module.langchat.service.aigc.AigcKnowledgeService;
|
||||
import cn.iocoder.yudao.module.langchat.store.AppStore;
|
||||
import cn.iocoder.yudao.module.langchat.utils.MybatisUtil;
|
||||
import cn.iocoder.yudao.module.langchat.utils.QueryPage;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.http.HttpStatus;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
@RequestMapping("/aigc/app")
|
||||
public class AigcAppController {
|
||||
|
||||
private final AigcAppService aigcAppService;
|
||||
private final AigcAppApiService aigcAppApiService;
|
||||
private final AppStore appStore;
|
||||
private final AigcKnowledgeService knowledgeService;
|
||||
|
||||
@GetMapping("/channel/api/{appId}")
|
||||
@TenantIgnore
|
||||
public CommonResult<AigcAppApi> getApiChanel(@PathVariable String appId) {
|
||||
List<AigcAppApi> list = aigcAppApiService.list(Wrappers.<AigcAppApi>lambdaQuery().eq(AigcAppApi::getAppId, appId));
|
||||
return CommonResult.success(list.isEmpty() ? null : list.get(0));
|
||||
}
|
||||
|
||||
@GetMapping("/list")
|
||||
@TenantIgnore
|
||||
public CommonResult<List<AigcApp>> list(AigcApp data) {
|
||||
return CommonResult.success(aigcAppService.list(data));
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@TenantIgnore
|
||||
public CommonResult<Dict> page(AigcApp data, QueryPage queryPage) {
|
||||
return CommonResult.success(MybatisUtil.getData(aigcAppService.page(MybatisUtil.wrap(data, queryPage),
|
||||
Wrappers.<AigcApp>lambdaQuery()
|
||||
.like(StringUtils.isNotEmpty(data.getName()), AigcApp::getName, data.getName())
|
||||
)));
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
@TenantIgnore
|
||||
public CommonResult<AigcApp> findById(@PathVariable String id) {
|
||||
AigcApp app = aigcAppService.getById(id);
|
||||
return CommonResult.success(app);
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
@TenantIgnore
|
||||
@PreAuthorize("@ss.hasPermission('aigc:app:add')")
|
||||
public CommonResult add(@RequestBody AigcApp data) {
|
||||
data.setCreateTime(new Date());
|
||||
data.setSaveTime(new Date());
|
||||
aigcAppService.save(data);
|
||||
appStore.init();
|
||||
return CommonResult.success(null);
|
||||
}
|
||||
|
||||
@PutMapping
|
||||
@TenantIgnore
|
||||
@PreAuthorize("@ss.hasPermission('aigc:app:update')")
|
||||
public CommonResult update(@RequestBody AigcApp data) {
|
||||
// 校验知识库是否是同纬度
|
||||
List<String> knowledgeIds = data.getKnowledgeIds();
|
||||
if (knowledgeIds != null && !knowledgeIds.isEmpty()) {
|
||||
List<AigcKnowledge> list = knowledgeService.list(Wrappers.<AigcKnowledge>lambdaQuery().in(AigcKnowledge::getId, knowledgeIds));
|
||||
Set<String> modelIds = new HashSet<>();
|
||||
Set<String> storeIds = new HashSet<>();
|
||||
list.forEach(know -> {
|
||||
modelIds.add(know.getEmbedModelId());
|
||||
storeIds.add(know.getEmbedStoreId());
|
||||
});
|
||||
if (modelIds.size() > 1) {
|
||||
return CommonResult.error(HttpStatus.SC_INTERNAL_SERVER_ERROR, "请选择相同向量库数据源配置的知识库");
|
||||
}
|
||||
if (storeIds.size() > 1) {
|
||||
return CommonResult.error(HttpStatus.SC_INTERNAL_SERVER_ERROR, "请选择相同向量模型配置的知识库");
|
||||
// throw new ServiceException("请选择相同向量模型配置的知识库");
|
||||
}
|
||||
}
|
||||
|
||||
data.setSaveTime(new Date());
|
||||
aigcAppService.updateById(data);
|
||||
appStore.init();
|
||||
return CommonResult.success(null);
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
@TenantIgnore
|
||||
@PreAuthorize("@ss.hasPermission('aigc:app:delete')")
|
||||
public CommonResult delete(@PathVariable String id) {
|
||||
aigcAppService.removeById(id);
|
||||
appStore.init();
|
||||
return CommonResult.success(null);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* 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.security.core.util.SecurityFrameworkUtils;
|
||||
import cn.iocoder.yudao.module.langchat.entity.AigcOss;
|
||||
import cn.iocoder.yudao.module.langchat.service.aigc.AigcOssService;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author tycoding
|
||||
* @since 2024/1/19
|
||||
*/
|
||||
@RequestMapping("/aigc/oss")
|
||||
@RestController
|
||||
@AllArgsConstructor
|
||||
public class AigcOssController {
|
||||
|
||||
private final AigcOssService aigcOssService;
|
||||
|
||||
@GetMapping("/list")
|
||||
public CommonResult list() {
|
||||
List<AigcOss> list = aigcOssService.list(Wrappers.<AigcOss>lambdaQuery()
|
||||
.eq(AigcOss::getUserId, SecurityFrameworkUtils.getLoginUserId())
|
||||
.orderByDesc(AigcOss::getCreateTime)
|
||||
);
|
||||
return CommonResult.success(null);
|
||||
}
|
||||
|
||||
@PostMapping("/upload")
|
||||
public CommonResult upload(MultipartFile file) {
|
||||
return CommonResult.success(aigcOssService.upload(file, String.valueOf(SecurityFrameworkUtils.getLoginUserId())));
|
||||
}
|
||||
|
||||
@PutMapping
|
||||
public CommonResult update(@RequestBody AigcOss data) {
|
||||
aigcOssService.updateById(data);
|
||||
return CommonResult.success(null);
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
public CommonResult delete(@PathVariable String id) {
|
||||
aigcOssService.removeById(id);
|
||||
return CommonResult.success(null);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,149 @@
|
|||
/*
|
||||
* 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 cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.security.core.LoginUser;
|
||||
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
|
||||
import cn.iocoder.yudao.module.langchat.dal.dto.ChatReq;
|
||||
import cn.iocoder.yudao.module.langchat.dal.dto.ChatRes;
|
||||
import cn.iocoder.yudao.module.langchat.dal.dto.ImageR;
|
||||
import cn.iocoder.yudao.module.langchat.dal.dto.PromptConst;
|
||||
import cn.iocoder.yudao.module.langchat.entity.AigcApp;
|
||||
import cn.iocoder.yudao.module.langchat.entity.AigcMessage;
|
||||
import cn.iocoder.yudao.module.langchat.entity.AigcModel;
|
||||
import cn.iocoder.yudao.module.langchat.enums.RoleEnum;
|
||||
import cn.iocoder.yudao.module.langchat.properties.ChatProps;
|
||||
import cn.iocoder.yudao.module.langchat.service.aigc.AigcAppService;
|
||||
import cn.iocoder.yudao.module.langchat.service.aigc.AigcMessageService;
|
||||
import cn.iocoder.yudao.module.langchat.service.aigc.AigcModelService;
|
||||
import cn.iocoder.yudao.module.langchat.service.core.ChatService;
|
||||
import cn.iocoder.yudao.module.langchat.service.core.impl.PersistentChatMemoryStore;
|
||||
import cn.iocoder.yudao.module.langchat.utils.PromptUtil;
|
||||
import cn.iocoder.yudao.module.langchat.utils.StreamEmitter;
|
||||
import dev.langchain4j.data.message.AiMessage;
|
||||
import dev.langchain4j.data.message.ChatMessage;
|
||||
import dev.langchain4j.data.message.SystemMessage;
|
||||
import dev.langchain4j.data.message.UserMessage;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
/**
|
||||
* @author tycoding
|
||||
* @since 2024/1/30
|
||||
*/
|
||||
@Slf4j
|
||||
@RequestMapping("/aigc")
|
||||
@RestController
|
||||
@AllArgsConstructor
|
||||
public class ChatEndpoint {
|
||||
|
||||
private final ChatService chatService;
|
||||
private final AigcMessageService messageService;
|
||||
private final AigcModelService aigcModelService;
|
||||
private final AigcAppService appService;
|
||||
private final ChatProps chatProps;
|
||||
|
||||
@PostMapping("/chat/completions")
|
||||
public SseEmitter chat(@RequestBody ChatReq req) {
|
||||
StreamEmitter emitter = new StreamEmitter();
|
||||
req.setEmitter(emitter);
|
||||
|
||||
req.setUserId(String.valueOf(SecurityFrameworkUtils.getLoginUserId()));
|
||||
req.setUsername(SecurityFrameworkUtils.getLoginUserNickname());
|
||||
ExecutorService executor = Executors.newSingleThreadExecutor();
|
||||
req.setExecutor(executor);
|
||||
return emitter.streaming(executor, () -> {
|
||||
chatService.chat(req);
|
||||
});
|
||||
}
|
||||
|
||||
@GetMapping("/app/info")
|
||||
public CommonResult<AigcApp> appInfo(@RequestParam String appId, String conversationId) {
|
||||
AigcApp app = appService.getById(appId);
|
||||
if (StrUtil.isBlank(conversationId)) {
|
||||
conversationId = app.getId();
|
||||
}
|
||||
|
||||
if (StrUtil.isNotBlank(app.getPrompt())) {
|
||||
// initialize chat memory
|
||||
SystemMessage message = new SystemMessage(app.getPrompt());
|
||||
PersistentChatMemoryStore.init(conversationId, message);
|
||||
}
|
||||
|
||||
return CommonResult.success(app);
|
||||
}
|
||||
|
||||
@GetMapping("/chat/messages/{conversationId}")
|
||||
public CommonResult messages(@PathVariable String conversationId) {
|
||||
List<AigcMessage> list = messageService.getMessages(conversationId, String.valueOf(SecurityFrameworkUtils.getLoginUserId()));
|
||||
|
||||
// initialize chat memory
|
||||
List<ChatMessage> chatMessages = new ArrayList<>();
|
||||
list.forEach(item -> {
|
||||
if (chatMessages.size() >= chatProps.getMemoryMaxMessage()) {
|
||||
return;
|
||||
}
|
||||
if (item.getRole().equals(RoleEnum.ASSISTANT.getName())) {
|
||||
chatMessages.add(new AiMessage(item.getMessage()));
|
||||
} else {
|
||||
chatMessages.add(new UserMessage(item.getMessage()));
|
||||
}
|
||||
});
|
||||
PersistentChatMemoryStore.init(conversationId, chatMessages);
|
||||
return CommonResult.success(list);
|
||||
}
|
||||
|
||||
@DeleteMapping("/chat/messages/clean/{conversationId}")
|
||||
public CommonResult cleanMessage(@PathVariable String conversationId) {
|
||||
messageService.clearMessage(conversationId);
|
||||
|
||||
// clean chat memory
|
||||
PersistentChatMemoryStore.clean(conversationId);
|
||||
return CommonResult.success(null);
|
||||
}
|
||||
|
||||
@PostMapping("/chat/mindmap")
|
||||
public CommonResult mindmap(@RequestBody ChatReq req) {
|
||||
req.setPrompt(PromptUtil.build(req.getMessage(), PromptConst.MINDMAP));
|
||||
return CommonResult.success(new ChatRes(chatService.text(req)));
|
||||
}
|
||||
|
||||
@PostMapping("/chat/image")
|
||||
public CommonResult image(@RequestBody ImageR req) {
|
||||
req.setPrompt(PromptUtil.build(req.getMessage(), PromptConst.IMAGE));
|
||||
return CommonResult.success(chatService.image(req));
|
||||
}
|
||||
|
||||
@GetMapping("/chat/getImageModels")
|
||||
public CommonResult<List<AigcModel>> getImageModels() {
|
||||
List<AigcModel> list = aigcModelService.getImageModels();
|
||||
list.forEach(i -> {
|
||||
i.setApiKey(null);
|
||||
i.setSecretKey(null);
|
||||
});
|
||||
return CommonResult.success(list);
|
||||
}
|
||||
}
|
|
@ -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.dal.dto;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
* @author tycoding
|
||||
* @since 2024/1/29
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class ChatRes {
|
||||
|
||||
private boolean isDone = false;
|
||||
|
||||
private String message;
|
||||
|
||||
private Integer usedToken;
|
||||
|
||||
private long time;
|
||||
|
||||
public ChatRes(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public ChatRes(Integer usedToken, long startTime) {
|
||||
this.isDone = true;
|
||||
this.usedToken = usedToken;
|
||||
this.time = System.currentTimeMillis() - startTime;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* 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 dev.langchain4j.model.input.Prompt;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
* @author tycoding
|
||||
* @since 2024/1/6
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class ImageR {
|
||||
|
||||
private String modelId;
|
||||
private String modelName;
|
||||
private String modelProvider;
|
||||
|
||||
private Prompt prompt;
|
||||
|
||||
/**
|
||||
* 内容
|
||||
*/
|
||||
private String message;
|
||||
|
||||
/**
|
||||
* 质量
|
||||
*/
|
||||
private String quality;
|
||||
|
||||
/**
|
||||
* 尺寸
|
||||
*/
|
||||
private String size;
|
||||
|
||||
/**
|
||||
* 风格
|
||||
*/
|
||||
private String style;
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* @author tycoding
|
||||
* @since 2024/3/1
|
||||
*/
|
||||
public interface PromptConst {
|
||||
|
||||
String QUESTION = "question";
|
||||
|
||||
String EMPTY = """
|
||||
|
||||
------
|
||||
{{question}}
|
||||
------
|
||||
""";
|
||||
|
||||
String DOCUMENT = "You are good at analyzing documents. Please analyze my questions according to the following documents, question: [{{question}}], [docs]";
|
||||
|
||||
String MINDMAP = """
|
||||
# Role
|
||||
You are a Markdown outline format engineer who focuses on answering user questions. You can quickly and accurately convert user questions into refined Markdown outline titles, and refine the specific details of each title.
|
||||
|
||||
## Skills
|
||||
### Skill 1: Identify user question intent
|
||||
- Accurately understand the specific content and needs of user questions.
|
||||
### Skill 2: Convert to Markdown outline
|
||||
- Simplify user questions into Markdown outline-style titles.
|
||||
### Skill 3: Return to user
|
||||
- Return the optimized outline to the user.
|
||||
|
||||
## Constraints
|
||||
- Only return the organized Markdown format content, without other explanation information
|
||||
- Answer the question in the language used by the user.
|
||||
- Return the answer in Markdown style, keep the main title as concise as possible; and refine the specific step information of each main title in the subtitle.
|
||||
""";
|
||||
|
||||
String WRITE = """
|
||||
# 角色
|
||||
你是一名专业文案撰写师。你擅长运用行业领域相关知识,以专业的视角为用户生成Markdown文档。
|
||||
|
||||
## 技能
|
||||
### 技能 1: 写作
|
||||
- 提取用户输入的主题和关键信息。
|
||||
|
||||
### 技能 2: 专业知识应用
|
||||
- 了解相关行业的相关知识。
|
||||
- 在撰写内容时,运用专业的语言和视角。
|
||||
|
||||
### 技能 3: 遵循Markdown格式
|
||||
- 拆分文档内容,以Markdown大纲格式分段内容,更易于用户阅读
|
||||
|
||||
## 限制
|
||||
- 只讨论写作相关的话题,不要返回其他任何内容和解释。
|
||||
- 始终以用户输入的信息为主题,撰写内容。
|
||||
""";
|
||||
|
||||
String IMAGE = """
|
||||
Please generate the corresponding pictures according to the following requirements.
|
||||
""";
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* 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/2/20
|
||||
*/
|
||||
@Getter
|
||||
public enum RoleEnum {
|
||||
USER("user"),
|
||||
ASSISTANT("assistant"),
|
||||
SYSTEM("system"),
|
||||
;
|
||||
|
||||
private final String name;
|
||||
|
||||
RoleEnum(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* 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.AigcAppApi;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* @author tycoding
|
||||
* @since 2024/7/26
|
||||
*/
|
||||
@Mapper
|
||||
public interface AigcAppApiMapper extends BaseMapper<AigcAppApi> {
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* 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.AigcApp;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* @author tycoding
|
||||
* @since 2024/7/26
|
||||
*/
|
||||
@Mapper
|
||||
public interface AigcAppMapper extends BaseMapper<AigcApp> {
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* 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/8/21
|
||||
*/
|
||||
@Data
|
||||
@ConfigurationProperties("langchat.chat")
|
||||
public class ChatProps {
|
||||
|
||||
/**
|
||||
* 上下文的长度
|
||||
*/
|
||||
private Integer memoryMaxMessage = 20;
|
||||
|
||||
/**
|
||||
* 前端渲染的消息长度(过长会导致页面渲染卡顿)
|
||||
*/
|
||||
private Integer previewMaxMessage = 100;
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* 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 dev.langchain4j.model.chat.ChatLanguageModel;
|
||||
import dev.langchain4j.model.chat.StreamingChatLanguageModel;
|
||||
import dev.langchain4j.model.image.ImageModel;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* @author tycoding
|
||||
* @since 2024/3/8
|
||||
*/
|
||||
@Component
|
||||
@AllArgsConstructor
|
||||
public class ModelProvider {
|
||||
|
||||
private final ModelStoreFactory modelStoreFactory;
|
||||
|
||||
public StreamingChatLanguageModel stream(String modelId) {
|
||||
StreamingChatLanguageModel streamingChatModel = modelStoreFactory.getStreamingChatModel(modelId);
|
||||
if (ObjectUtil.isNotEmpty(streamingChatModel)) {
|
||||
return streamingChatModel;
|
||||
}
|
||||
throw new RuntimeException("没有匹配到模型,请检查模型配置!");
|
||||
}
|
||||
|
||||
public ChatLanguageModel text(String modelId) {
|
||||
ChatLanguageModel chatLanguageModel = modelStoreFactory.getChatLanguageModel(modelId);
|
||||
if (ObjectUtil.isNotEmpty(chatLanguageModel)) {
|
||||
return chatLanguageModel;
|
||||
}
|
||||
throw new RuntimeException("没有匹配到模型,请检查模型配置!");
|
||||
}
|
||||
|
||||
public ImageModel image(String modelId) {
|
||||
ImageModel imageModel = modelStoreFactory.getImageModel(modelId);
|
||||
if (ObjectUtil.isNotEmpty(imageModel)) {
|
||||
return imageModel;
|
||||
}
|
||||
throw new RuntimeException("没有匹配到模型,请检查模型配置!");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* 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.AigcAppApi;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
|
||||
/**
|
||||
* @author tycoding
|
||||
* @since 2024/7/26
|
||||
*/
|
||||
public interface AigcAppApiService extends IService<AigcAppApi> {
|
||||
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* 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.AigcApp;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author tycoding
|
||||
* @since 2024/7/26
|
||||
*/
|
||||
public interface AigcAppService extends IService<AigcApp> {
|
||||
|
||||
List<AigcApp> list(AigcApp data);
|
||||
|
||||
AigcApp getById(String id);
|
||||
}
|
|
@ -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.service.aigc.impl;
|
||||
|
||||
import cn.iocoder.yudao.module.langchat.entity.AigcAppApi;
|
||||
import cn.iocoder.yudao.module.langchat.mapper.AigcAppApiMapper;
|
||||
import cn.iocoder.yudao.module.langchat.service.aigc.AigcAppApiService;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* @author tycoding
|
||||
* @since 2024/7/26
|
||||
*/
|
||||
@Service
|
||||
public class AigcAppApiServiceImpl extends ServiceImpl<AigcAppApiMapper, AigcAppApi> implements AigcAppApiService {
|
||||
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* 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.impl;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.yudao.module.langchat.entity.AigcApp;
|
||||
import cn.iocoder.yudao.module.langchat.entity.AigcKnowledge;
|
||||
import cn.iocoder.yudao.module.langchat.entity.AigcModel;
|
||||
import cn.iocoder.yudao.module.langchat.mapper.AigcAppMapper;
|
||||
import cn.iocoder.yudao.module.langchat.service.aigc.AigcAppService;
|
||||
import cn.iocoder.yudao.module.langchat.service.aigc.AigcKnowledgeService;
|
||||
import cn.iocoder.yudao.module.langchat.service.aigc.AigcModelService;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author tycoding
|
||||
* @since 2024/7/26
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
@Service
|
||||
public class AigcAppServiceImpl extends ServiceImpl<AigcAppMapper, AigcApp> implements AigcAppService {
|
||||
|
||||
private final AigcModelService aigcModelService;
|
||||
private final AigcKnowledgeService aigcKnowledgeService;
|
||||
|
||||
@Override
|
||||
public List<AigcApp> list(AigcApp data) {
|
||||
List<AigcApp> list = baseMapper.selectList(Wrappers.<AigcApp>lambdaQuery()
|
||||
.like(StrUtil.isNotBlank(data.getName()), AigcApp::getName, data.getName()));
|
||||
|
||||
Map<String, List<AigcModel>> modelMap = aigcModelService.list(new AigcModel()).stream().collect(Collectors.groupingBy(AigcModel::getId));
|
||||
Map<String, List<AigcKnowledge>> knowledgeMap = aigcKnowledgeService.list().stream().collect(Collectors.groupingBy(AigcKnowledge::getId));
|
||||
list.forEach(i -> {
|
||||
List<AigcModel> models = modelMap.get(i.getModelId());
|
||||
if (models != null) {
|
||||
i.setModel(models.get(0));
|
||||
}
|
||||
if (i.getKnowledgeIds() != null) {
|
||||
List<AigcKnowledge> knowledges = new ArrayList<>();
|
||||
i.getKnowledgeIds().forEach(k -> {
|
||||
List<AigcKnowledge> items = knowledgeMap.get(k);
|
||||
if (items != null) {
|
||||
knowledges.add(items.get(0));
|
||||
}
|
||||
});
|
||||
i.setKnowledges(knowledges);
|
||||
}
|
||||
});
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AigcApp getById(String id) {
|
||||
AigcApp app = baseMapper.selectById(id);
|
||||
if (app != null) {
|
||||
String modelId = app.getModelId();
|
||||
if (modelId != null) {
|
||||
app.setModel(aigcModelService.selectById(modelId));
|
||||
}
|
||||
List<String> knowledgeIds = app.getKnowledgeIds();
|
||||
if (knowledgeIds != null && !knowledgeIds.isEmpty()) {
|
||||
app.setKnowledges(aigcKnowledgeService.list(Wrappers.<AigcKnowledge>lambdaQuery().in(AigcKnowledge::getId, knowledgeIds)));
|
||||
}
|
||||
}
|
||||
return app;
|
||||
}
|
||||
}
|
|
@ -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.service.core;
|
||||
|
||||
import dev.langchain4j.service.MemoryId;
|
||||
import dev.langchain4j.service.TokenStream;
|
||||
import dev.langchain4j.service.UserMessage;
|
||||
|
||||
/**
|
||||
* @author tycoding
|
||||
* @since 2024/3/8
|
||||
*/
|
||||
public interface Agent {
|
||||
|
||||
TokenStream stream(@MemoryId String id, @UserMessage String message);
|
||||
|
||||
String text(@MemoryId String id, @UserMessage String message);
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright (c) 2024 LangChat. TyCoding All Rights Reserved.
|
||||
*
|
||||
* Licensed under the GNU Affero General Public License, Version 3 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.gnu.org/licenses/agpl-3.0.html
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.iocoder.yudao.module.langchat.service.core;
|
||||
|
||||
|
||||
import cn.iocoder.yudao.module.langchat.dal.dto.ChatReq;
|
||||
import cn.iocoder.yudao.module.langchat.dal.dto.ImageR;
|
||||
import cn.iocoder.yudao.module.langchat.entity.AigcOss;
|
||||
|
||||
/**
|
||||
* @author tycoding
|
||||
* @since 2024/1/4
|
||||
*/
|
||||
public interface ChatService {
|
||||
|
||||
void chat(ChatReq req);
|
||||
|
||||
|
||||
/**
|
||||
* 文本请求
|
||||
*/
|
||||
String text(ChatReq req);
|
||||
|
||||
/**
|
||||
* 文生图
|
||||
*/
|
||||
AigcOss image(ImageR req);
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* 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.ImageR;
|
||||
import dev.langchain4j.data.image.Image;
|
||||
import dev.langchain4j.model.output.Response;
|
||||
import dev.langchain4j.service.TokenStream;
|
||||
|
||||
/**
|
||||
* @author tycoding
|
||||
* @since 2024/3/8
|
||||
*/
|
||||
public interface LangChatService {
|
||||
|
||||
TokenStream chat(ChatReq req);
|
||||
|
||||
TokenStream singleChat(ChatReq req);
|
||||
|
||||
String text(ChatReq req);
|
||||
|
||||
Response<Image> image(ImageR req);
|
||||
}
|
|
@ -0,0 +1,135 @@
|
|||
/*
|
||||
* 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.ChatRes;
|
||||
import cn.iocoder.yudao.module.langchat.dal.dto.ImageR;
|
||||
import cn.iocoder.yudao.module.langchat.entity.AigcApp;
|
||||
import cn.iocoder.yudao.module.langchat.entity.AigcMessage;
|
||||
import cn.iocoder.yudao.module.langchat.entity.AigcOss;
|
||||
import cn.iocoder.yudao.module.langchat.enums.RoleEnum;
|
||||
import cn.iocoder.yudao.module.langchat.service.aigc.AigcMessageService;
|
||||
import cn.iocoder.yudao.module.langchat.service.core.ChatService;
|
||||
import cn.iocoder.yudao.module.langchat.service.core.LangChatService;
|
||||
import cn.iocoder.yudao.module.langchat.store.AppStore;
|
||||
import cn.iocoder.yudao.module.langchat.utils.ServletUtil;
|
||||
import cn.iocoder.yudao.module.langchat.utils.StreamEmitter;
|
||||
import dev.langchain4j.data.image.Image;
|
||||
import dev.langchain4j.model.output.Response;
|
||||
import dev.langchain4j.model.output.TokenUsage;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* @author tycoding
|
||||
* @since 2024/1/4
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@AllArgsConstructor
|
||||
public class ChatServiceImpl implements ChatService {
|
||||
|
||||
private final LangChatService langChatService;
|
||||
private final AigcMessageService aigcMessageService;
|
||||
private final AppStore appStore;
|
||||
|
||||
@Override
|
||||
public void chat(ChatReq req) {
|
||||
StreamEmitter emitter = req.getEmitter();
|
||||
long startTime = System.currentTimeMillis();
|
||||
StringBuilder text = new StringBuilder();
|
||||
|
||||
if (StrUtil.isNotBlank(req.getAppId())) {
|
||||
AigcApp app = appStore.get(req.getAppId());
|
||||
if (app != null) {
|
||||
req.setModelId(app.getModelId());
|
||||
req.setPromptText(app.getPrompt());
|
||||
req.setKnowledgeIds(app.getKnowledgeIds());
|
||||
}
|
||||
}
|
||||
|
||||
// save user message
|
||||
req.setRole(RoleEnum.USER.getName());
|
||||
saveMessage(req, 0, 0);
|
||||
|
||||
try {
|
||||
langChatService
|
||||
.chat(req)
|
||||
.onNext(e -> {
|
||||
text.append(e);
|
||||
emitter.send(new ChatRes(e));
|
||||
})
|
||||
.onComplete((e) -> {
|
||||
TokenUsage tokenUsage = e.tokenUsage();
|
||||
ChatRes res = new ChatRes(tokenUsage.totalTokenCount(), startTime);
|
||||
emitter.send(res);
|
||||
emitter.complete();
|
||||
|
||||
// save assistant message
|
||||
req.setMessage(text.toString());
|
||||
req.setRole(RoleEnum.ASSISTANT.getName());
|
||||
saveMessage(req, tokenUsage.inputTokenCount(), tokenUsage.outputTokenCount());
|
||||
})
|
||||
.onError((e) -> {
|
||||
emitter.error(e.getMessage());
|
||||
throw new RuntimeException(e.getMessage());
|
||||
})
|
||||
.start();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
emitter.error(e.getMessage());
|
||||
throw new RuntimeException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void saveMessage(ChatReq req, Integer inputToken, Integer outputToken) {
|
||||
if (req.getConversationId() != null) {
|
||||
AigcMessage message = new AigcMessage();
|
||||
BeanUtils.copyProperties(req, message);
|
||||
message.setIp(ServletUtil.getIpAddr());
|
||||
message.setPromptTokens(inputToken);
|
||||
message.setTokens(outputToken);
|
||||
aigcMessageService.addMessage(message);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String text(ChatReq req) {
|
||||
String text;
|
||||
try {
|
||||
text = langChatService.text(req);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException(e.getMessage());
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AigcOss image(ImageR req) {
|
||||
Response<Image> res = langChatService.image(req);
|
||||
|
||||
String path = res.content().url().toString();
|
||||
AigcOss oss = new AigcOss();
|
||||
oss.setUrl(path);
|
||||
return oss;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,155 @@
|
|||
/*
|
||||
* 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.IdUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.yudao.framework.common.exception.ServiceException;
|
||||
import cn.iocoder.yudao.module.langchat.dal.dto.ChatReq;
|
||||
import cn.iocoder.yudao.module.langchat.dal.dto.ImageR;
|
||||
import cn.iocoder.yudao.module.langchat.properties.ChatProps;
|
||||
import cn.iocoder.yudao.module.langchat.provider.EmbeddingProvider;
|
||||
import cn.iocoder.yudao.module.langchat.provider.ModelProvider;
|
||||
import cn.iocoder.yudao.module.langchat.service.core.Agent;
|
||||
import cn.iocoder.yudao.module.langchat.service.core.LangChatService;
|
||||
import cn.iocoder.yudao.module.langchat.utils.PromptUtil;
|
||||
import dev.langchain4j.data.image.Image;
|
||||
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
|
||||
import dev.langchain4j.model.chat.ChatLanguageModel;
|
||||
import dev.langchain4j.model.chat.StreamingChatLanguageModel;
|
||||
import dev.langchain4j.model.image.ImageModel;
|
||||
import dev.langchain4j.model.output.Response;
|
||||
import dev.langchain4j.rag.DefaultRetrievalAugmentor;
|
||||
import dev.langchain4j.rag.content.retriever.ContentRetriever;
|
||||
import dev.langchain4j.rag.content.retriever.EmbeddingStoreContentRetriever;
|
||||
import dev.langchain4j.rag.query.Query;
|
||||
import dev.langchain4j.service.AiServices;
|
||||
import dev.langchain4j.service.TokenStream;
|
||||
import dev.langchain4j.store.embedding.filter.Filter;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
import static cn.iocoder.yudao.module.langchat.enums.EmbedConst.KNOWLEDGE;
|
||||
import static dev.langchain4j.store.embedding.filter.MetadataFilterBuilder.metadataKey;
|
||||
|
||||
/**
|
||||
* @author tycoding
|
||||
* @since 2024/3/8
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@AllArgsConstructor
|
||||
public class LangChatServiceImpl implements LangChatService {
|
||||
|
||||
private final ModelProvider provider;
|
||||
private final EmbeddingProvider embeddingProvider;
|
||||
private final ChatProps chatProps;
|
||||
|
||||
private AiServices<Agent> build(StreamingChatLanguageModel streamModel, ChatLanguageModel model, ChatReq req) {
|
||||
AiServices<Agent> aiServices = AiServices.builder(Agent.class)
|
||||
.chatMemoryProvider(memoryId -> MessageWindowChatMemory.builder()
|
||||
.id(req.getConversationId())
|
||||
.chatMemoryStore(new PersistentChatMemoryStore())
|
||||
.maxMessages(chatProps.getMemoryMaxMessage())
|
||||
.build());
|
||||
if (StrUtil.isNotBlank(req.getPromptText())) {
|
||||
aiServices.systemMessageProvider(memoryId -> req.getPromptText());
|
||||
}
|
||||
if (streamModel != null) {
|
||||
aiServices.streamingChatLanguageModel(streamModel);
|
||||
}
|
||||
if (model != null) {
|
||||
aiServices.chatLanguageModel(model);
|
||||
}
|
||||
return aiServices;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TokenStream chat(ChatReq req) {
|
||||
StreamingChatLanguageModel model = provider.stream(req.getModelId());
|
||||
if (StrUtil.isBlank(req.getConversationId())) {
|
||||
req.setConversationId(IdUtil.simpleUUID());
|
||||
}
|
||||
|
||||
AiServices<Agent> aiServices = build(model, null, req);
|
||||
|
||||
if (StrUtil.isNotBlank(req.getKnowledgeId())) {
|
||||
req.getKnowledgeIds().add(req.getKnowledgeId());
|
||||
}
|
||||
|
||||
if (req.getKnowledgeIds() != null && !req.getKnowledgeIds().isEmpty()) {
|
||||
Function<Query, Filter> filter = (query) -> metadataKey(KNOWLEDGE).isIn(req.getKnowledgeIds());
|
||||
ContentRetriever contentRetriever = EmbeddingStoreContentRetriever.builder()
|
||||
.embeddingStore(embeddingProvider.getEmbeddingStore(req.getKnowledgeIds()))
|
||||
.embeddingModel(embeddingProvider.getEmbeddingModel(req.getKnowledgeIds()))
|
||||
.dynamicFilter(filter)
|
||||
.build();
|
||||
aiServices.retrievalAugmentor(DefaultRetrievalAugmentor
|
||||
.builder()
|
||||
.contentRetriever(contentRetriever)
|
||||
.build());
|
||||
}
|
||||
Agent agent = aiServices.build();
|
||||
return agent.stream(req.getConversationId(), req.getMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public TokenStream singleChat(ChatReq req) {
|
||||
StreamingChatLanguageModel model = provider.stream(req.getModelId());
|
||||
if (StrUtil.isBlank(req.getConversationId())) {
|
||||
req.setConversationId(IdUtil.simpleUUID());
|
||||
}
|
||||
|
||||
Agent agent = build(model, null, req).build();
|
||||
if (req.getPrompt() == null) {
|
||||
req.setPrompt(PromptUtil.build(req.getMessage(), req.getPromptText()));
|
||||
}
|
||||
return agent.stream(req.getConversationId(), req.getPrompt().text());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String text(ChatReq req) {
|
||||
if (StrUtil.isBlank(req.getConversationId())) {
|
||||
req.setConversationId(IdUtil.simpleUUID());
|
||||
}
|
||||
|
||||
try {
|
||||
ChatLanguageModel model = provider.text(req.getModelId());
|
||||
Agent agent = build(null, model, req).build();
|
||||
String text = agent.text(req.getConversationId(), req.getMessage());
|
||||
return text;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response<Image> image(ImageR req) {
|
||||
try {
|
||||
ImageModel model = provider.image(req.getModelId());
|
||||
return model.generate(req.getPrompt().text());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw new ServiceException(HttpStatus.INTERNAL_SERVER_ERROR.value(), "图片生成失败");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
* Copyright (c) 2024 LangChat. TyCoding All Rights Reserved.
|
||||
*
|
||||
* Licensed under the GNU Affero General Public License, Version 3 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.gnu.org/licenses/agpl-3.0.html
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.iocoder.yudao.module.langchat.service.core.impl;
|
||||
|
||||
import dev.langchain4j.data.message.ChatMessage;
|
||||
import dev.langchain4j.data.message.SystemMessage;
|
||||
import dev.langchain4j.store.memory.chat.ChatMemoryStore;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author tycoding
|
||||
* @since 2024/8/15
|
||||
*/
|
||||
@Slf4j
|
||||
public class PersistentChatMemoryStore implements ChatMemoryStore {
|
||||
|
||||
private static final Map<Object, List<ChatMessage>> store = new HashMap<>();
|
||||
private static final Map<Object, Boolean> initSystemMessageStore = new HashMap<>();
|
||||
private static final Map<Object, Boolean> initMessageStore = new HashMap<>();
|
||||
|
||||
public static void clean(Object memoryId) {
|
||||
log.info("clean message memory store to: {}", memoryId);
|
||||
store.remove(memoryId);
|
||||
}
|
||||
|
||||
public static void init(Object memoryId, SystemMessage message) {
|
||||
Boolean isInitSystemMessage = initSystemMessageStore.get(memoryId);
|
||||
if (isInitSystemMessage != null && isInitSystemMessage) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<ChatMessage> list = store.get(memoryId);
|
||||
if (list == null) {
|
||||
store.put(memoryId, new ArrayList<>(List.of(message)));
|
||||
} else {
|
||||
list.add(message);
|
||||
}
|
||||
initSystemMessageStore.put(memoryId, true);
|
||||
}
|
||||
|
||||
public static void init(Object memoryId, List<ChatMessage> messages) {
|
||||
log.info("initialize message memory store to: {}", memoryId);
|
||||
|
||||
Boolean isInitMessage = initMessageStore.get(memoryId);
|
||||
if (isInitMessage != null && isInitMessage) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<ChatMessage> list = store.get(memoryId);
|
||||
if (list == null) {
|
||||
store.put(memoryId, messages);
|
||||
} else {
|
||||
list.addAll(messages);
|
||||
}
|
||||
initMessageStore.put(memoryId, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ChatMessage> getMessages(Object memoryId) {
|
||||
List<ChatMessage> list = store.get(memoryId);
|
||||
if (list == null) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateMessages(Object memoryId, List<ChatMessage> messages) {
|
||||
store.put(memoryId, messages);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteMessages(Object memoryId) {
|
||||
store.remove(memoryId);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* 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.store;
|
||||
|
||||
import cn.iocoder.yudao.module.langchat.entity.AigcApp;
|
||||
import cn.iocoder.yudao.module.langchat.service.aigc.AigcAppService;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author tycoding
|
||||
* @since 2024/8/8
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@AllArgsConstructor
|
||||
public class AppStore {
|
||||
|
||||
private static final Map<String, AigcApp> appMap = new HashMap<>();
|
||||
private final AigcAppService aigcAppService;
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
log.info("initialize app config list...");
|
||||
List<AigcApp> list = aigcAppService.list();
|
||||
list.forEach(i -> appMap.put(i.getId(), i));
|
||||
}
|
||||
|
||||
public AigcApp get(String appId) {
|
||||
return appMap.get(appId);
|
||||
}
|
||||
}
|
|
@ -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.utils;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.iocoder.yudao.module.langchat.dal.dto.PromptConst;
|
||||
import dev.langchain4j.model.input.Prompt;
|
||||
import dev.langchain4j.model.input.PromptTemplate;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author tycoding
|
||||
* @since 2024/3/1
|
||||
*/
|
||||
public class PromptUtil {
|
||||
|
||||
public static Prompt build(String message) {
|
||||
return new Prompt(message);
|
||||
}
|
||||
|
||||
public static Prompt build(String message, String promptText) {
|
||||
return new PromptTemplate(promptText + PromptConst.EMPTY).apply(Map.of(PromptConst.QUESTION, message));
|
||||
}
|
||||
|
||||
public static Prompt build(String message, String promptText, Object param) {
|
||||
Map<String, Object> params = BeanUtil.beanToMap(param, false, true);
|
||||
params.put(PromptConst.QUESTION, message);
|
||||
return new PromptTemplate(promptText).apply(params);
|
||||
}
|
||||
|
||||
public static Prompt buildDocs(String message) {
|
||||
return new PromptTemplate(PromptConst.DOCUMENT).apply(Map.of(PromptConst.QUESTION, message));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
* 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 cn.hutool.json.JSONUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.module.langchat.constant.CommonConst;
|
||||
import lombok.SneakyThrows;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* @author tycoding
|
||||
* @since 2024/1/2
|
||||
*/
|
||||
public class ServletUtil {
|
||||
|
||||
@SneakyThrows
|
||||
public static void write(HttpServletResponse response, CommonResult data) {
|
||||
response.setStatus(data.getCode());
|
||||
response.setHeader("Content-type", "application/json;charset=" + CommonConst.UTF_8);
|
||||
response.setCharacterEncoding(CommonConst.UTF_8);
|
||||
response.getWriter().write(JSONUtil.toJsonStr(data));
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public static void write(HttpServletResponse response, int status, CommonResult data) {
|
||||
response.setStatus(status);
|
||||
response.setHeader("Content-type", "application/json;charset=" + CommonConst.UTF_8);
|
||||
response.setCharacterEncoding(CommonConst.UTF_8);
|
||||
response.getWriter().write(JSONUtil.toJsonStr(data));
|
||||
}
|
||||
|
||||
public static HttpServletRequest getRequest() {
|
||||
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
|
||||
if (servletRequestAttributes != null) {
|
||||
return servletRequestAttributes.getRequest();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String getAuthorizationToken() {
|
||||
String token = getRequest().getHeader("Authorization");
|
||||
if (token != null && token.toLowerCase().startsWith("bearer")) {
|
||||
return token.toLowerCase().replace("bearer", "").trim();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String getToken(String token) {
|
||||
if (token != null && token.toLowerCase().startsWith("bearer")) {
|
||||
return token.replace("bearer", "").trim();
|
||||
}
|
||||
return token;
|
||||
}
|
||||
|
||||
public static String getIpAddr() {
|
||||
HttpServletRequest request = getRequest();
|
||||
if (request == null) {
|
||||
return "unknown";
|
||||
} else {
|
||||
String ip = request.getHeader("x-forwarded-for");
|
||||
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
|
||||
ip = request.getHeader("Proxy-Client-IP");
|
||||
}
|
||||
|
||||
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
|
||||
ip = request.getHeader("X-Forwarded-For");
|
||||
}
|
||||
|
||||
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
|
||||
ip = request.getHeader("WL-Proxy-Client-IP");
|
||||
}
|
||||
|
||||
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
|
||||
ip = request.getHeader("X-Real-IP");
|
||||
}
|
||||
|
||||
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
|
||||
ip = request.getRemoteAddr();
|
||||
}
|
||||
|
||||
if ("0:0:0:0:0:0:0:1".equals(ip)) {
|
||||
ip = "127.0.0.1";
|
||||
}
|
||||
|
||||
if (ip.contains(",")) {
|
||||
ip = ip.split(",")[0];
|
||||
}
|
||||
return ip;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue