Merge remote-tracking branch 'origin/master' into master-wangwei

# Conflicts:
#	yudao-module-llm/yudao-module-llm-biz/src/main/java/cn/iocoder/yudao/module/llm/framework/backend/config/LLMBackendProperties.java
This commit is contained in:
Edward_89 2025-01-13 13:34:26 +08:00
commit e9e908b78c
14 changed files with 245 additions and 16 deletions

View File

@ -12,6 +12,9 @@ import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URI;
import java.nio.charset.Charset;
import java.util.Map;
@ -142,6 +145,14 @@ public class HttpUtils {
return response.body();
}
}
public static InputStream postStream(String url, Map<String, String> headers, String requestBody) {
try (HttpResponse response = HttpRequest.post(url)
.addHeaders(headers)
.body(requestBody)
.execute()) {
return response.bodyStream();
}
}
public static String postForm(String url, Map<String, String> headers, Map<String, Object> form) {
try (HttpResponse response = HttpRequest.post(url)

View File

@ -24,4 +24,6 @@ public class ChatReqVO {
private String prompt;
@Schema(description = "对话ID", requiredMode = Schema.RequiredMode.REQUIRED)
private String uuid;
@Schema(description = "系统提示语")
private String systemPrompt;
}

View File

@ -118,4 +118,11 @@ public class LearningResourcesController {
return success(aLong);
}
@PutMapping("/videoViewsUpdate")
@Operation(summary = "视频查看量更新")
// @PreAuthorize("@ss.hasPermission('llm:learning-resources:update')")
public CommonResult<Boolean> videoViewsUpdate(@Valid @RequestBody LearningResourcesSaveReqVO updateReqVO) {
learningResourcesService.videoViewsUpdate(updateReqVO);
return success(true);
}
}

View File

@ -8,6 +8,7 @@ import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.module.llm.dal.dataobject.learningresources.LearningResourcesDO;
import org.apache.ibatis.annotations.Mapper;
import cn.iocoder.yudao.module.llm.controller.admin.learningresources.vo.*;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Update;
/**
@ -30,4 +31,5 @@ public interface LearningResourcesMapper extends BaseMapperX<LearningResourcesDO
}
@Update("update llm_learning_resources set down_count = down_count + 1 where id = #{id}")
void updateDownCount(LearningResourcesDO learningResourcesDO);
}
}

View File

@ -92,4 +92,6 @@ public class LLMBackendProperties {
private String modelFileDownload;
private String optimizePrompt;
private String aigcModelCompletions;
}

View File

@ -77,11 +77,13 @@ public class ApplicationServiceImpl implements ApplicationService {
//prompt使用量+1
Long promptId = application.getPromptId();
PromptTemplatesRespVO promptTemplates = promptTemplatesService.getPromptTemplates(promptId);
PromptTemplatesDO promptTemplatesDO = new PromptTemplatesDO();
promptTemplatesDO.setUseCount(promptTemplates.getUseCount() == null?1:promptTemplates.getUseCount() + 1);
promptTemplatesDO.setId(promptTemplates.getId());
promptTemplatesService.updatePromptTemplatesById(promptTemplatesDO);
if(promptId != null){
PromptTemplatesRespVO promptTemplates = promptTemplatesService.getPromptTemplates(promptId);
PromptTemplatesDO promptTemplatesDO = new PromptTemplatesDO();
promptTemplatesDO.setUseCount(promptTemplates.getUseCount() == null?1:promptTemplates.getUseCount() + 1);
promptTemplatesDO.setId(promptTemplates.getId());
promptTemplatesService.updatePromptTemplatesById(promptTemplatesDO);
}
// 返回
return application.getId();
}

View File

@ -3,6 +3,8 @@ package cn.iocoder.yudao.module.llm.service.conversation;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.module.llm.controller.admin.datarefluxdata.vo.DataRefluxDataSaveReqVO;
import cn.iocoder.yudao.module.llm.dal.dataobject.basemodel.BaseModelDO;
import cn.iocoder.yudao.module.llm.dal.dataobject.modelservice.ModelServiceDO;
import cn.iocoder.yudao.module.llm.dal.mysql.modelservice.ModelServiceMapper;
import cn.iocoder.yudao.module.llm.service.basemodel.BaseModelService;
import cn.iocoder.yudao.module.llm.service.datarefluxdata.DataRefluxDataService;
import cn.iocoder.yudao.module.llm.service.http.ModelService;
@ -28,6 +30,7 @@ import cn.iocoder.yudao.module.llm.dal.mysql.conversation.ConversationMapper;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.llm.enums.ErrorCodeConstants.*;
import static cn.iocoder.yudao.module.llm.service.http.ModelService.PRIVATE_MODEL_ID;
/**
* 大模型对话记录 Service 实现类
@ -48,6 +51,8 @@ public class ConversationServiceImpl implements ConversationService {
private BaseModelService baseModelService;
@Resource
private DataRefluxDataService dataRefluxDataService;
@Resource
private ModelServiceMapper modelServiceMapper;
// 聊天会话历史记录缓存Key
private final static String CHAT_HIStORY_REDIS_KEY = "llm:chat:history";
@ -98,6 +103,20 @@ public class ConversationServiceImpl implements ConversationService {
@Override
public ChatRespVO chat(ChatReqVO chatReqVO) {
if (Objects.equals(1, chatReqVO.getModelType())){
return publicModelChat(chatReqVO);
}else {
return privateModelChat(chatReqVO);
}
}
/**
* 公共模型聊天
* @param chatReqVO
* @return
*/
public ChatRespVO publicModelChat(ChatReqVO chatReqVO) {
if (StringUtils.isBlank(chatReqVO.getUuid())) {
// 如果没有uuid就生成一个
chatReqVO.setUuid(UUID.randomUUID().toString());
@ -143,4 +162,70 @@ public class ConversationServiceImpl implements ConversationService {
dataRefluxDataService.saveDataRefluxData(dataRefluxDataSaveReqVO);
return chatRespVO;
}
/**
* 私有模型聊天
* @param chatReqVO
* @return
*/
private ChatRespVO privateModelChat(ChatReqVO chatReqVO) {
if (StringUtils.isBlank(chatReqVO.getUuid())) {
// 如果没有uuid就生成一个
chatReqVO.setUuid(UUID.randomUUID().toString());
}
String model = null;
if (Objects.equals(0, chatReqVO.getModelType())) {
// 模型管理
ModelServiceDO modelServiceDO = modelServiceMapper.selectById(chatReqVO.getModelId());
if (modelServiceDO == null) {
throw exception(MODEL_SERVICE_NOT_EXISTS);
}
model = modelServiceDO.getServiceName();
}
ModelCompletionsReqVO.ModelCompletionsMessage message = new ModelCompletionsReqVO.ModelCompletionsMessage();
message.setRole("user");
message.setContent(chatReqVO.getPrompt());
List<String> messageHistoryList = stringRedisTemplate.opsForList().range(CHAT_HIStORY_REDIS_KEY + ":" + chatReqVO.getUuid(), 0, -1);
List<ModelCompletionsReqVO.ModelCompletionsMessage> messages = new ArrayList<>();
if (messageHistoryList != null && !messageHistoryList.isEmpty()) {
for (String messageHistory : messageHistoryList) {
ModelCompletionsReqVO.ModelCompletionsMessage modelCompletionsMessage = JsonUtils.parseObject(messageHistory, ModelCompletionsReqVO.ModelCompletionsMessage.class);
if (modelCompletionsMessage.getRole().equals("system")){
modelCompletionsMessage.setContent(StringUtils.isNotBlank(chatReqVO.getSystemPrompt())? chatReqVO.getSystemPrompt():"");
stringRedisTemplate.opsForList().set(CHAT_HIStORY_REDIS_KEY+ ":" + chatReqVO.getUuid(), 0, JsonUtils.toJsonString(modelCompletionsMessage));
}
messages.add(modelCompletionsMessage);
}
}else {
ModelCompletionsReqVO.ModelCompletionsMessage systemMessage = new ModelCompletionsReqVO.ModelCompletionsMessage();
systemMessage.setRole("system");
systemMessage.setContent(StringUtils.isNotBlank(chatReqVO.getSystemPrompt())? chatReqVO.getSystemPrompt():"");
stringRedisTemplate.opsForList().rightPush(CHAT_HIStORY_REDIS_KEY + ":" + chatReqVO.getUuid(), JsonUtils.toJsonString(systemMessage));
messages.add(systemMessage);
}
messages.add(message);
ModelCompletionsReqVO modelCompletionsReqVO = new ModelCompletionsReqVO();
modelCompletionsReqVO.setMessages(messages);
// TODO 先传固定的内容 后期和后端调通直接修改成 model
modelCompletionsReqVO.setModel(PRIVATE_MODEL_ID);
ModelCompletionsRespVO modelCompletionsRespVO = modelService.modelPrivateCompletions(new HashMap<>(),modelCompletionsReqVO);
if (modelCompletionsRespVO == null) {
throw exception(MODEL_COMPLETIONS_ERROR);
}
ChatRespVO chatRespVO = BeanUtils.toBean(chatReqVO, ChatRespVO.class);
chatRespVO.setResponse(modelCompletionsRespVO.getAnswer());
stringRedisTemplate.opsForList().rightPush(CHAT_HIStORY_REDIS_KEY + ":" + chatReqVO.getUuid(), JsonUtils.toJsonString(message));
ModelCompletionsReqVO.ModelCompletionsMessage responseMessage = new ModelCompletionsReqVO.ModelCompletionsMessage();
responseMessage.setRole("assistant");
responseMessage.setContent(modelCompletionsRespVO.getAnswer());
stringRedisTemplate.opsForList().rightPush(CHAT_HIStORY_REDIS_KEY + ":" + chatReqVO.getUuid(), JsonUtils.toJsonString(responseMessage));
DataRefluxDataSaveReqVO dataRefluxDataSaveReqVO = new DataRefluxDataSaveReqVO();
dataRefluxDataSaveReqVO.setModelServiceId(chatReqVO.getModelId());
dataRefluxDataSaveReqVO.setModelType(chatReqVO.getModelType());
dataRefluxDataSaveReqVO.setPrompt(chatReqVO.getPrompt());
dataRefluxDataSaveReqVO.setResponse(modelCompletionsRespVO.getAnswer());
dataRefluxDataSaveReqVO.setSystem(modelCompletionsRespVO.getSystem());
dataRefluxDataService.saveDataRefluxData(dataRefluxDataSaveReqVO);
return chatRespVO;
}
}

View File

@ -6,11 +6,17 @@ import cn.iocoder.yudao.module.llm.framework.backend.config.LLMBackendProperties
import cn.iocoder.yudao.module.llm.service.http.vo.*;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.List;
import java.util.Map;
@ -19,9 +25,12 @@ import java.util.Map;
public class ModelService {
public final static String DEFAULT_MODEL_ID = "qwen2.5-instruct";
public final static String PRIVATE_MODEL_ID = "llama3.1";
@Resource
private LLMBackendProperties llmBackendProperties;
@Resource
private TrainHttpService trainHttpService;
/**
* 获取模型列表
@ -87,4 +96,42 @@ public class ModelService {
return JSONArray.parseArray(res, String.class);
}
public ModelCompletionsRespVO modelPrivateCompletions(Map<String, String> headers,ModelCompletionsReqVO req) {
trainHttpService.login(headers);
if (StringUtils.isBlank(req.getModel())) {
req.setModel(PRIVATE_MODEL_ID);
}
log.info("url: {}", llmBackendProperties.getAigcModelCompletions());
log.info("request: {}", req);
/* String result = HttpUtils.postStream(llmBackendProperties.getAigcModelCompletions(), headers, JSON.toJSONString(req));
log.info("response: {}", result);
if (StringUtils.isBlank(result)) {
return null;
}*/
try {
StringBuilder fullContent = new StringBuilder();
try (InputStream inputStream = HttpUtils.postStream(llmBackendProperties.getAigcModelCompletions(), headers, JSON.toJSONString(req));
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
String line;
while ((line = reader.readLine()) != null) {
JSONObject jsonObject = JSON.parseObject(line);
if (jsonObject.getBoolean("success")) {
AigcRespChatVO aigcRespVO = jsonObject.toJavaObject(AigcRespChatVO.class);
if (aigcRespVO.getData().size() != 0) {
AigcChatCompletion chatCompletion = aigcRespVO.getData().get(aigcRespVO.getData().size() - 1);
fullContent.append(chatCompletion.getContent());
}
}
}
}
ModelCompletionsRespVO respVO = new ModelCompletionsRespVO();
respVO.setSystem("助手");
respVO.setQuestion(req.getMessages().get(req.getMessages().size() - 1).getContent());
respVO.setAnswer(fullContent.toString());
return respVO;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}

View File

@ -0,0 +1,24 @@
package cn.iocoder.yudao.module.llm.service.http.vo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class AigcChatCompletion {
/**
* 聊天返回内容
*/
private String fullContent;
private String content;
private String createAt;
private String finishReason;
private String contentType;
private String messageId;
private String model;
private Integer topP;
private Integer temperature;
private Integer maxTokens;
}

View File

@ -0,0 +1,19 @@
package cn.iocoder.yudao.module.llm.service.http.vo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class AigcRespChatVO {
private boolean success;
private int code;
private List<AigcChatCompletion> data;
private String message;
private String traceId;
}

View File

@ -54,4 +54,11 @@ public interface LearningResourcesService {
PageResult<LearningResourcesDO> getLearningResourcesPage(LearningResourcesPageReqVO pageReqVO);
String downLearningResources(Long id);
}
/**
* 视频查看量更新
* @param updateReqVO 更新信息
*/
void videoViewsUpdate (@Valid LearningResourcesSaveReqVO updateReqVO);
}

View File

@ -1,30 +1,24 @@
package cn.iocoder.yudao.module.llm.service.learningresources;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil;
import cn.iocoder.yudao.module.llm.dal.dataobject.dataset.DatasetDO;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import org.springframework.validation.annotation.Validated;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
import cn.iocoder.yudao.module.llm.controller.admin.learningresources.vo.*;
import cn.iocoder.yudao.module.llm.dal.dataobject.learningresources.LearningResourcesDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.llm.dal.mysql.learningresources.LearningResourcesMapper;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.llm.enums.ErrorCodeConstants.*;
import static jdk.nashorn.internal.runtime.regexp.joni.Config.log;
/**
* 学习资源 Service 实现类
@ -101,4 +95,27 @@ public class LearningResourcesServiceImpl implements LearningResourcesService {
learningResourcesMapper.updateDownCount(learningResourcesDO);
return learningResourcesDO.getFileUrl();
}
}
/**
* 视频查看量更新
*
* @param updateReqVO 更新信息
*/
@Override
public void videoViewsUpdate(LearningResourcesSaveReqVO updateReqVO) {
Long id = updateReqVO.getId();
if (id == null) {
throw exception(LEARNING_RESOURCES_NOT_EXISTS);
}
// 校验存在
validateLearningResourcesExists(id);
// 更新
learningResourcesMapper.update(new UpdateWrapper<LearningResourcesDO>()
.setSql("down_count = down_count + 1")
.eq("id", id));
}
}

View File

@ -259,6 +259,8 @@ llm:
model_completions: http://api.xhllm.xinnuojinzhi.com/model/v1/chat/completions
# aigc表数据查询接口
table_data_query: http://36.133.1.230:5123/table/%s
# aigc模型推理
aigc_model_completions: http://36.133.1.230:8080/api/channels/chat/completions
application_api: http://localhost:48100/admin-app/llm/application/api/apiKey/chat

View File

@ -301,6 +301,8 @@ llm:
model_completions: http://api.xhllm.xinnuojinzhi.com/model/v1/chat/completions
# aigc表数据查询接口
table_data_query: http://36.133.1.230:5123/table/%s
# aigc模型推理
aigc_model_completions: http://36.133.1.230:8080/api/channels/chat/completions
application_api: http://localhost:48080/admin-app/llm/application/api/apiKey/chat