Commit bec2aafecba94afd42c187b073a5b512fdf42420

Authored by xiaoxin
1 parent d4cfcd0a

【增加】AI Write init

yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/write/AiWriteController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.controller.admin.write;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  4 +import cn.iocoder.yudao.module.ai.service.write.AiWriteService;
  5 +import io.swagger.v3.oas.annotations.Operation;
  6 +import io.swagger.v3.oas.annotations.tags.Tag;
  7 +import jakarta.annotation.Resource;
  8 +import jakarta.annotation.security.PermitAll;
  9 +import org.springframework.http.MediaType;
  10 +import org.springframework.web.bind.annotation.PostMapping;
  11 +import org.springframework.web.bind.annotation.RequestMapping;
  12 +import org.springframework.web.bind.annotation.RestController;
  13 +import reactor.core.publisher.Flux;
  14 +
  15 +@Tag(name = "管理后台 - AI 写作")
  16 +@RestController
  17 +@RequestMapping("/ai/write")
  18 +public class AiWriteController {
  19 +
  20 + @Resource
  21 + private AiWriteService writeService;
  22 +
  23 + @PostMapping(value = "/generate-stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
  24 + @PermitAll
  25 + @Operation(summary = "作文生产(流式)", description = "流式返回,响应较快")
  26 + public Flux<CommonResult<String>> generateComposition() {
  27 + return writeService.generateComposition();
  28 + }
  29 +}
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/write/AiWriteService.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.service.write;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  4 +import reactor.core.publisher.Flux;
  5 +
  6 +/**
  7 + * AI 写作 Service 接口
  8 + *
  9 + * @author xiaoxin
  10 + */
  11 +public interface AiWriteService {
  12 +
  13 +
  14 + Flux<CommonResult<String>> generateComposition();
  15 +
  16 +
  17 +}
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/write/AiWriteServiceImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.service.write;
  2 +
  3 +import cn.hutool.core.util.StrUtil;
  4 +import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum;
  5 +import cn.iocoder.yudao.framework.ai.core.model.tongyi.QianWenOptions;
  6 +import cn.iocoder.yudao.framework.ai.core.model.xinghuo.XingHuoChatModel;
  7 +import cn.iocoder.yudao.framework.ai.core.model.xinghuo.XingHuoOptions;
  8 +import cn.iocoder.yudao.framework.ai.core.model.yiyan.YiYanChatOptions;
  9 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  10 +import cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants;
  11 +import cn.iocoder.yudao.module.ai.service.model.AiApiKeyService;
  12 +import jakarta.annotation.Resource;
  13 +import lombok.extern.slf4j.Slf4j;
  14 +import org.springframework.ai.chat.ChatResponse;
  15 +import org.springframework.ai.chat.StreamingChatClient;
  16 +import org.springframework.ai.chat.prompt.ChatOptions;
  17 +import org.springframework.ai.chat.prompt.Prompt;
  18 +import org.springframework.ai.ollama.api.OllamaOptions;
  19 +import org.springframework.ai.openai.OpenAiChatOptions;
  20 +import org.springframework.stereotype.Service;
  21 +import reactor.core.publisher.Flux;
  22 +
  23 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.error;
  24 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  25 +
  26 +/**
  27 + * AI 写作 Service 实现类
  28 + *
  29 + * @author xiaoxin
  30 + */
  31 +@Service
  32 +@Slf4j
  33 +public class AiWriteServiceImpl implements AiWriteService {
  34 +
  35 + @Resource
  36 + private AiApiKeyService apiKeyService;
  37 +
  38 +
  39 + @Override
  40 + public Flux<CommonResult<String>> generateComposition() {
  41 + StreamingChatClient chatClient = apiKeyService.getStreamingChatClient(6L);
  42 + AiPlatformEnum platform = AiPlatformEnum.validatePlatform("QianWen");
  43 + ChatOptions chatOptions = buildChatOptions(platform, "qwen-72b-chat", 1.0, 1000);
  44 + Prompt prompt = new Prompt("请直接写一篇关于 气候变化 的文章,格式为自动,语气为自动,语言为自动,长度为自动。请确保涵盖主要观点,不需要标题,不需要任何额外的解释或道歉。", chatOptions);
  45 + Flux<ChatResponse> streamResponse = chatClient.stream(prompt);
  46 + // 3.3 流式返回
  47 + StringBuffer contentBuffer = new StringBuffer();
  48 + return streamResponse.map(chunk -> {
  49 + String newContent = chunk.getResult() != null ? chunk.getResult().getOutput().getContent() : null;
  50 + newContent = StrUtil.nullToDefault(newContent, ""); // 避免 null 的 情况
  51 + contentBuffer.append(newContent);
  52 + // 响应结果
  53 + return success(newContent);
  54 + }).doOnComplete(() -> {
  55 + log.info("generateComposition complete, content: {}", contentBuffer);
  56 + }).onErrorResume(error -> {
  57 + return Flux.just(error(ErrorCodeConstants.AI_CHAT_STREAM_ERROR));
  58 + });
  59 + }
  60 +
  61 +
  62 + private static ChatOptions buildChatOptions(AiPlatformEnum platform, String model, Double temperature, Integer maxTokens) {
  63 + Float temperatureF = temperature != null ? temperature.floatValue() : null;
  64 + //noinspection EnhancedSwitchMigration
  65 + switch (platform) {
  66 + case OPENAI:
  67 + return OpenAiChatOptions.builder().withModel(model).withTemperature(temperatureF).withMaxTokens(maxTokens).build();
  68 + case OLLAMA:
  69 + return OllamaOptions.create().withModel(model).withTemperature(temperatureF).withNumPredict(maxTokens);
  70 + case YI_YAN:
  71 + // TODO @fan:增加一个 model
  72 + return new YiYanChatOptions().setTemperature(temperatureF).setMaxOutputTokens(maxTokens);
  73 + case XING_HUO:
  74 + return new XingHuoOptions().setChatModel(XingHuoChatModel.valueOfModel(model)).setTemperature(temperatureF)
  75 + .setMaxTokens(maxTokens);
  76 + case QIAN_WEN:
  77 + // TODO @fan:增加 model、temperature 参数
  78 + return new QianWenOptions().setMaxTokens(maxTokens);
  79 + default:
  80 + throw new IllegalArgumentException(StrUtil.format("未知平台({})", platform));
  81 + }
  82 + }
  83 +
  84 +}