|
@@ -2,22 +2,27 @@ package cn.iocoder.yudao.module.ai.service.write;
|
|
|
|
|
|
import cn.hutool.core.util.StrUtil;
|
|
|
import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum;
|
|
|
-import cn.iocoder.yudao.framework.ai.core.model.tongyi.QianWenOptions;
|
|
|
import cn.iocoder.yudao.framework.ai.core.model.xinghuo.XingHuoChatModel;
|
|
|
import cn.iocoder.yudao.framework.ai.core.model.xinghuo.XingHuoOptions;
|
|
|
-import cn.iocoder.yudao.framework.ai.core.model.yiyan.YiYanChatOptions;
|
|
|
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
|
|
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
|
|
import cn.iocoder.yudao.module.ai.controller.admin.write.vo.AiWriteGenerateReqVO;
|
|
|
+import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
|
|
|
+import cn.iocoder.yudao.module.ai.dal.dataobject.write.AiWriteDO;
|
|
|
+import cn.iocoder.yudao.module.ai.dal.mysql.write.AiWriteMapper;
|
|
|
import cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants;
|
|
|
import cn.iocoder.yudao.module.ai.service.model.AiApiKeyService;
|
|
|
+import cn.iocoder.yudao.module.ai.service.model.AiChatModelService;
|
|
|
+import com.alibaba.cloud.ai.tongyi.chat.TongYiChatOptions;
|
|
|
import jakarta.annotation.Resource;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
-import org.springframework.ai.chat.ChatResponse;
|
|
|
-import org.springframework.ai.chat.StreamingChatClient;
|
|
|
+import org.springframework.ai.chat.model.ChatResponse;
|
|
|
+import org.springframework.ai.chat.model.StreamingChatModel;
|
|
|
import org.springframework.ai.chat.prompt.ChatOptions;
|
|
|
import org.springframework.ai.chat.prompt.Prompt;
|
|
|
import org.springframework.ai.ollama.api.OllamaOptions;
|
|
|
import org.springframework.ai.openai.OpenAiChatOptions;
|
|
|
+import org.springframework.ai.qianfan.QianFanChatOptions;
|
|
|
import org.springframework.stereotype.Service;
|
|
|
import reactor.core.publisher.Flux;
|
|
|
|
|
@@ -35,16 +40,29 @@ public class AiWriteServiceImpl implements AiWriteService {
|
|
|
|
|
|
@Resource
|
|
|
private AiApiKeyService apiKeyService;
|
|
|
+ @Resource
|
|
|
+ private AiChatModelService chatModalService;
|
|
|
+ @Resource
|
|
|
+ private AiWriteMapper writeMapper;
|
|
|
|
|
|
|
|
|
@Override
|
|
|
- public Flux<CommonResult<String>> generateComposition(AiWriteGenerateReqVO generateReqVO) {
|
|
|
- StreamingChatClient chatClient = apiKeyService.getStreamingChatClient(6L);
|
|
|
- AiPlatformEnum platform = AiPlatformEnum.validatePlatform("QianWen");
|
|
|
- ChatOptions chatOptions = buildChatOptions(platform, "qwen-72b-chat", 1.0, 1000);
|
|
|
+ public Flux<CommonResult<String>> generateWriteContent(AiWriteGenerateReqVO generateReqVO, Long userId) {
|
|
|
+ //TODO 芋艿 写作的模型配置放哪好 先用千问测试
|
|
|
+ // 1.1 校验模型
|
|
|
+ AiChatModelDO model = chatModalService.validateChatModel(14L);
|
|
|
+ StreamingChatModel chatClient = apiKeyService.getStreamingChatClient(model.getKeyId());
|
|
|
+ AiPlatformEnum platform = AiPlatformEnum.validatePlatform(model.getPlatform());
|
|
|
+ ChatOptions chatOptions = buildChatOptions(platform, model.getModel(), model.getTemperature(), model.getMaxTokens());
|
|
|
+
|
|
|
+ //1.2 插入写作信息
|
|
|
+ AiWriteDO writeDO = BeanUtils.toBean(generateReqVO, AiWriteDO.class);
|
|
|
+ writeMapper.insert(writeDO.setUserId(userId).setModel(model.getModel()).setPlatform(platform.getPlatform()));
|
|
|
+
|
|
|
+ //2.1 构建提示词
|
|
|
Prompt prompt = new Prompt(buildWritingPrompt(generateReqVO), chatOptions);
|
|
|
Flux<ChatResponse> streamResponse = chatClient.stream(prompt);
|
|
|
- // 3.3 流式返回
|
|
|
+ // 2.2 流式返回
|
|
|
StringBuffer contentBuffer = new StringBuffer();
|
|
|
return streamResponse.map(chunk -> {
|
|
|
String newContent = chunk.getResult() != null ? chunk.getResult().getOutput().getContent() : null;
|
|
@@ -53,17 +71,17 @@ public class AiWriteServiceImpl implements AiWriteService {
|
|
|
// 响应结果
|
|
|
return success(newContent);
|
|
|
}).doOnComplete(() -> {
|
|
|
- log.info("generateComposition complete, content: {}", contentBuffer);
|
|
|
- }).onErrorResume(error -> {
|
|
|
- log.error("[AI 写作] 发生异常", error);
|
|
|
- return Flux.just(error(ErrorCodeConstants.AI_CHAT_STREAM_ERROR));
|
|
|
- });
|
|
|
+ writeMapper.updateById(new AiWriteDO().setId(writeDO.getId()).setGeneratedContent(contentBuffer.toString()));
|
|
|
+ }).doOnError(throwable -> {
|
|
|
+ log.error("[AI Write][generateReqVO({}) 发生异常]", generateReqVO, throwable);
|
|
|
+ writeMapper.updateById(new AiWriteDO().setId(writeDO.getId()).setErrorMessage(throwable.getMessage()));
|
|
|
+ }).onErrorResume(error -> Flux.just(error(ErrorCodeConstants.WRITE_STREAM_ERROR)));
|
|
|
}
|
|
|
|
|
|
|
|
|
private String buildWritingPrompt(AiWriteGenerateReqVO generateReqVO) {
|
|
|
- String template = "请直接写一篇关于 [{}] 的文章,格式为:{},语气为:{},语言为:{},长度为:{}。请确保涵盖主要内容,不需要任何额外的解释或道歉。";
|
|
|
- String content = generateReqVO.getContent();
|
|
|
+ String template = "请直接写一篇关于 [{}] 的文章,格式为:{},语气为:{},语言为:{},长度为:{}。请确保涵盖主要内容,不需要除了正文内容外的其他回复,如标题、额外的解释或道歉。";
|
|
|
+ String content = generateReqVO.getContentPrompt();
|
|
|
String format = generateReqVO.getFormat();
|
|
|
String tone = generateReqVO.getTone();
|
|
|
String language = generateReqVO.getLanguage();
|
|
@@ -81,14 +99,14 @@ public class AiWriteServiceImpl implements AiWriteService {
|
|
|
case OLLAMA:
|
|
|
return OllamaOptions.create().withModel(model).withTemperature(temperatureF).withNumPredict(maxTokens);
|
|
|
case YI_YAN:
|
|
|
- // TODO @fan:增加一个 model
|
|
|
- return new YiYanChatOptions().setTemperature(temperatureF).setMaxOutputTokens(maxTokens);
|
|
|
+ // TODO 芋艿:貌似 model 只要一设置,就报错
|
|
|
+// return QianFanChatOptions.builder().withModel(model).withTemperature(temperatureF).withMaxTokens(maxTokens).build();
|
|
|
+ return QianFanChatOptions.builder().withTemperature(temperatureF).withMaxTokens(maxTokens).build();
|
|
|
case XING_HUO:
|
|
|
return new XingHuoOptions().setChatModel(XingHuoChatModel.valueOfModel(model)).setTemperature(temperatureF)
|
|
|
.setMaxTokens(maxTokens);
|
|
|
case QIAN_WEN:
|
|
|
- // TODO @fan:增加 model、temperature 参数
|
|
|
- return new QianWenOptions().setMaxTokens(maxTokens);
|
|
|
+ return TongYiChatOptions.builder().withModel(model).withTemperature(temperature).withMaxTokens(maxTokens).build();
|
|
|
default:
|
|
|
throw new IllegalArgumentException(StrUtil.format("未知平台({})", platform));
|
|
|
}
|