From bec2aafecba94afd42c187b073a5b512fdf42420 Mon Sep 17 00:00:00 2001 From: xiaoxin <718949661@qq.com> Date: Thu, 27 Jun 2024 17:38:31 +0800 Subject: [PATCH 1/6] =?UTF-8?q?=E3=80=90=E5=A2=9E=E5=8A=A0=E3=80=91AI=20Wr?= =?UTF-8?q?ite=20init?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/write/AiWriteController.java | 29 +++++++ .../ai/service/write/AiWriteService.java | 17 ++++ .../ai/service/write/AiWriteServiceImpl.java | 84 +++++++++++++++++++ 3 files changed, 130 insertions(+) create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/write/AiWriteController.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/write/AiWriteService.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/write/AiWriteServiceImpl.java diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/write/AiWriteController.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/write/AiWriteController.java new file mode 100644 index 0000000000..e090aaf2d9 --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/write/AiWriteController.java @@ -0,0 +1,29 @@ +package cn.iocoder.yudao.module.ai.controller.admin.write; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.module.ai.service.write.AiWriteService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.annotation.security.PermitAll; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import reactor.core.publisher.Flux; + +@Tag(name = "管理后台 - AI 写作") +@RestController +@RequestMapping("/ai/write") +public class AiWriteController { + + @Resource + private AiWriteService writeService; + + @PostMapping(value = "/generate-stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE) + @PermitAll + @Operation(summary = "作文生产(流式)", description = "流式返回,响应较快") + public Flux> generateComposition() { + return writeService.generateComposition(); + } +} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/write/AiWriteService.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/write/AiWriteService.java new file mode 100644 index 0000000000..94a92dbb8c --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/write/AiWriteService.java @@ -0,0 +1,17 @@ +package cn.iocoder.yudao.module.ai.service.write; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import reactor.core.publisher.Flux; + +/** + * AI 写作 Service 接口 + * + * @author xiaoxin + */ +public interface AiWriteService { + + + Flux> generateComposition(); + + +} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/write/AiWriteServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/write/AiWriteServiceImpl.java new file mode 100644 index 0000000000..241c177c23 --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/write/AiWriteServiceImpl.java @@ -0,0 +1,84 @@ +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.module.ai.enums.ErrorCodeConstants; +import cn.iocoder.yudao.module.ai.service.model.AiApiKeyService; +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.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.stereotype.Service; +import reactor.core.publisher.Flux; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.error; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +/** + * AI 写作 Service 实现类 + * + * @author xiaoxin + */ +@Service +@Slf4j +public class AiWriteServiceImpl implements AiWriteService { + + @Resource + private AiApiKeyService apiKeyService; + + + @Override + public Flux> generateComposition() { + StreamingChatClient chatClient = apiKeyService.getStreamingChatClient(6L); + AiPlatformEnum platform = AiPlatformEnum.validatePlatform("QianWen"); + ChatOptions chatOptions = buildChatOptions(platform, "qwen-72b-chat", 1.0, 1000); + Prompt prompt = new Prompt("请直接写一篇关于 气候变化 的文章,格式为自动,语气为自动,语言为自动,长度为自动。请确保涵盖主要观点,不需要标题,不需要任何额外的解释或道歉。", chatOptions); + Flux streamResponse = chatClient.stream(prompt); + // 3.3 流式返回 + StringBuffer contentBuffer = new StringBuffer(); + return streamResponse.map(chunk -> { + String newContent = chunk.getResult() != null ? chunk.getResult().getOutput().getContent() : null; + newContent = StrUtil.nullToDefault(newContent, ""); // 避免 null 的 情况 + contentBuffer.append(newContent); + // 响应结果 + return success(newContent); + }).doOnComplete(() -> { + log.info("generateComposition complete, content: {}", contentBuffer); + }).onErrorResume(error -> { + return Flux.just(error(ErrorCodeConstants.AI_CHAT_STREAM_ERROR)); + }); + } + + + private static ChatOptions buildChatOptions(AiPlatformEnum platform, String model, Double temperature, Integer maxTokens) { + Float temperatureF = temperature != null ? temperature.floatValue() : null; + //noinspection EnhancedSwitchMigration + switch (platform) { + case OPENAI: + return OpenAiChatOptions.builder().withModel(model).withTemperature(temperatureF).withMaxTokens(maxTokens).build(); + case OLLAMA: + return OllamaOptions.create().withModel(model).withTemperature(temperatureF).withNumPredict(maxTokens); + case YI_YAN: + // TODO @fan:增加一个 model + return new YiYanChatOptions().setTemperature(temperatureF).setMaxOutputTokens(maxTokens); + 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); + default: + throw new IllegalArgumentException(StrUtil.format("未知平台({})", platform)); + } + } + +} -- Gitee From 7a66a435f936c4c6e2cfc5cd78b94ea0361ee110 Mon Sep 17 00:00:00 2001 From: xiaoxin <718949661@qq.com> Date: Fri, 28 Jun 2024 09:51:56 +0800 Subject: [PATCH 2/6] =?UTF-8?q?=E3=80=90=E5=A2=9E=E5=8A=A0=E3=80=91AI?= =?UTF-8?q?=EF=BC=9A=E5=86=99=E4=BD=9C=E6=A8=A1=E7=89=88=E6=9E=84=E5=BB=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/write/AiWriteController.java | 9 +++-- .../admin/write/vo/AiWriteGenerateReqVO.java | 39 +++++++++++++++++++ .../ai/service/write/AiWriteService.java | 3 +- .../ai/service/write/AiWriteServiceImpl.java | 17 +++++++- 4 files changed, 62 insertions(+), 6 deletions(-) create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/write/vo/AiWriteGenerateReqVO.java diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/write/AiWriteController.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/write/AiWriteController.java index e090aaf2d9..d229ba267d 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/write/AiWriteController.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/write/AiWriteController.java @@ -1,13 +1,16 @@ package cn.iocoder.yudao.module.ai.controller.admin.write; import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.module.ai.controller.admin.write.vo.AiWriteGenerateReqVO; import cn.iocoder.yudao.module.ai.service.write.AiWriteService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; import jakarta.annotation.security.PermitAll; +import jakarta.validation.Valid; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import reactor.core.publisher.Flux; @@ -22,8 +25,8 @@ public class AiWriteController { @PostMapping(value = "/generate-stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE) @PermitAll - @Operation(summary = "作文生产(流式)", description = "流式返回,响应较快") - public Flux> generateComposition() { - return writeService.generateComposition(); + @Operation(summary = "写作生成(流式)", description = "流式返回,响应较快") + public Flux> generateComposition(@RequestBody @Valid AiWriteGenerateReqVO generateReqVO) { + return writeService.generateComposition(generateReqVO); } } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/write/vo/AiWriteGenerateReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/write/vo/AiWriteGenerateReqVO.java new file mode 100644 index 0000000000..396c0b8ca1 --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/write/vo/AiWriteGenerateReqVO.java @@ -0,0 +1,39 @@ +package cn.iocoder.yudao.module.ai.controller.admin.write.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import lombok.Data; + +@Schema(description = "管理后台 - AI 写作生成 Request VO") +@Data +public class AiWriteGenerateReqVO { + + @Schema(description = "写作内容", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "田忌赛马") + private String content; + + @Schema(description = "原文", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "领导我要辞职") + private String originalContent; + + @Schema(description = "回复内容", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "准了") + private String replyContent; + + @Schema(description = "长度", requiredMode = Schema.RequiredMode.REQUIRED, example = "中等") + @NotBlank(message = "长度不能为空") + private String length; + + @Schema(description = "格式", requiredMode = Schema.RequiredMode.REQUIRED, example = "文章") + @NotBlank(message = "格式不能为空") + private String format; + + @Schema(description = "语气", requiredMode = Schema.RequiredMode.REQUIRED, example = "随意") + @NotBlank(message = "语气不能为空") + private String tone; + + @Schema(description = "语言", requiredMode = Schema.RequiredMode.REQUIRED, example = "中文") + @NotBlank(message = "语言不能为空") + private String language; + + + @Schema(description = "写作类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer writeType; +} \ No newline at end of file diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/write/AiWriteService.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/write/AiWriteService.java index 94a92dbb8c..f8fb0634cb 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/write/AiWriteService.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/write/AiWriteService.java @@ -1,6 +1,7 @@ package cn.iocoder.yudao.module.ai.service.write; import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.module.ai.controller.admin.write.vo.AiWriteGenerateReqVO; import reactor.core.publisher.Flux; /** @@ -11,7 +12,7 @@ import reactor.core.publisher.Flux; public interface AiWriteService { - Flux> generateComposition(); + Flux> generateComposition(AiWriteGenerateReqVO generateReqVO); } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/write/AiWriteServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/write/AiWriteServiceImpl.java index 241c177c23..63d2d61929 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/write/AiWriteServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/write/AiWriteServiceImpl.java @@ -7,6 +7,7 @@ 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.module.ai.controller.admin.write.vo.AiWriteGenerateReqVO; import cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants; import cn.iocoder.yudao.module.ai.service.model.AiApiKeyService; import jakarta.annotation.Resource; @@ -37,11 +38,11 @@ public class AiWriteServiceImpl implements AiWriteService { @Override - public Flux> generateComposition() { + public Flux> generateComposition(AiWriteGenerateReqVO generateReqVO) { StreamingChatClient chatClient = apiKeyService.getStreamingChatClient(6L); AiPlatformEnum platform = AiPlatformEnum.validatePlatform("QianWen"); ChatOptions chatOptions = buildChatOptions(platform, "qwen-72b-chat", 1.0, 1000); - Prompt prompt = new Prompt("请直接写一篇关于 气候变化 的文章,格式为自动,语气为自动,语言为自动,长度为自动。请确保涵盖主要观点,不需要标题,不需要任何额外的解释或道歉。", chatOptions); + Prompt prompt = new Prompt(buildWritingPrompt(generateReqVO), chatOptions); Flux streamResponse = chatClient.stream(prompt); // 3.3 流式返回 StringBuffer contentBuffer = new StringBuffer(); @@ -54,11 +55,23 @@ public class AiWriteServiceImpl implements AiWriteService { }).doOnComplete(() -> { log.info("generateComposition complete, content: {}", contentBuffer); }).onErrorResume(error -> { + log.error("[AI 写作] 发生异常", error); return Flux.just(error(ErrorCodeConstants.AI_CHAT_STREAM_ERROR)); }); } + private String buildWritingPrompt(AiWriteGenerateReqVO generateReqVO) { + String template = "请直接写一篇关于 [{}] 的文章,格式为:{},语气为:{},语言为:{},长度为:{}。请确保涵盖主要内容,不需要任何额外的解释或道歉。"; + String content = generateReqVO.getContent(); + String format = generateReqVO.getFormat(); + String tone = generateReqVO.getTone(); + String language = generateReqVO.getLanguage(); + String length = generateReqVO.getLength(); + return StrUtil.format(template, content, format, tone, language, length); + } + + // TODO 芋艿:复用 private static ChatOptions buildChatOptions(AiPlatformEnum platform, String model, Double temperature, Integer maxTokens) { Float temperatureF = temperature != null ? temperature.floatValue() : null; //noinspection EnhancedSwitchMigration -- Gitee From 77ead4859c61cb216f9d26b651390ac96c7baa92 Mon Sep 17 00:00:00 2001 From: xiaoxin <718949661@qq.com> Date: Wed, 3 Jul 2024 10:52:41 +0800 Subject: [PATCH 3/6] =?UTF-8?q?=E3=80=90=E5=A2=9E=E5=8A=A0=E3=80=91AI=20?= =?UTF-8?q?=E5=86=99=E4=BD=9C=EF=BC=9A=E6=94=AF=E6=8C=81=E6=92=B0=E5=86=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/ai/enums/ErrorCodeConstants.java | 5 + .../enums/music/AiMusicGenerateModeEnum.java | 2 +- .../ai/enums/write/AiWriteTypeEnum.java | 37 +++++++ .../admin/write/AiWriteController.java | 4 +- .../admin/write/vo/AiWriteGenerateReqVO.java | 8 +- .../ai/dal/dataobject/write/AiWriteDO.java | 97 +++++++++++++++++++ .../ai/dal/mysql/write/AiWriteMapper.java | 14 +++ .../ai/service/write/AiWriteService.java | 9 +- .../ai/service/write/AiWriteServiceImpl.java | 58 +++++++---- 9 files changed, 207 insertions(+), 27 deletions(-) create mode 100644 yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/write/AiWriteTypeEnum.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/write/AiWriteDO.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/write/AiWriteMapper.java diff --git a/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/ErrorCodeConstants.java b/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/ErrorCodeConstants.java index 5a3e290a37..fd42fe1551 100644 --- a/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/ErrorCodeConstants.java +++ b/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/ErrorCodeConstants.java @@ -42,4 +42,9 @@ public interface ErrorCodeConstants { // ========== API 音乐 1-040-006-000 ========== ErrorCode MUSIC_NOT_EXISTS = new ErrorCode(1_022_006_000, "音乐不存在!"); + + // ========== API 写作 1-022-007-000 ========== + ErrorCode WRITE_NOT_EXISTS = new ErrorCode(1_022_007_000, "作文不存在!"); + ErrorCode WRITE_STREAM_ERROR = new ErrorCode(1_022_07_001, "Stream 对话异常!"); + } diff --git a/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/music/AiMusicGenerateModeEnum.java b/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/music/AiMusicGenerateModeEnum.java index ad4b81b364..651731b60f 100644 --- a/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/music/AiMusicGenerateModeEnum.java +++ b/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/music/AiMusicGenerateModeEnum.java @@ -7,7 +7,7 @@ import lombok.Getter; import java.util.Arrays; /** - * AI 音乐状态的枚举 + * AI 音乐生成模式的枚举 * * @author xiaoxin */ diff --git a/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/write/AiWriteTypeEnum.java b/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/write/AiWriteTypeEnum.java new file mode 100644 index 0000000000..05db29ddae --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/write/AiWriteTypeEnum.java @@ -0,0 +1,37 @@ +package cn.iocoder.yudao.module.ai.enums.write; + +import cn.iocoder.yudao.framework.common.core.IntArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * AI 写作类型的枚举 + * + * @author xiaoxin + */ +@AllArgsConstructor +@Getter +public enum AiWriteTypeEnum implements IntArrayValuable { + + DESCRIPTION(1, "撰写"), + LYRIC(2, "回复"); + + /** + * 类型 + */ + private final Integer type; + /** + * 类型名 + */ + private final String name; + + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(AiWriteTypeEnum::getType).toArray(); + + @Override + public int[] array() { + return ARRAYS; + } + +} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/write/AiWriteController.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/write/AiWriteController.java index d229ba267d..6298d44e37 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/write/AiWriteController.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/write/AiWriteController.java @@ -15,6 +15,8 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import reactor.core.publisher.Flux; +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; + @Tag(name = "管理后台 - AI 写作") @RestController @RequestMapping("/ai/write") @@ -27,6 +29,6 @@ public class AiWriteController { @PermitAll @Operation(summary = "写作生成(流式)", description = "流式返回,响应较快") public Flux> generateComposition(@RequestBody @Valid AiWriteGenerateReqVO generateReqVO) { - return writeService.generateComposition(generateReqVO); + return writeService.generateWriteContent(generateReqVO, getLoginUserId()); } } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/write/vo/AiWriteGenerateReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/write/vo/AiWriteGenerateReqVO.java index 396c0b8ca1..88bcf05683 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/write/vo/AiWriteGenerateReqVO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/write/vo/AiWriteGenerateReqVO.java @@ -8,14 +8,14 @@ import lombok.Data; @Data public class AiWriteGenerateReqVO { - @Schema(description = "写作内容", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "田忌赛马") - private String content; + @Schema(description = "写作内容提示", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "田忌赛马") + private String contentPrompt; @Schema(description = "原文", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "领导我要辞职") private String originalContent; @Schema(description = "回复内容", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "准了") - private String replyContent; + private String replyContentPrompt; @Schema(description = "长度", requiredMode = Schema.RequiredMode.REQUIRED, example = "中等") @NotBlank(message = "长度不能为空") @@ -35,5 +35,5 @@ public class AiWriteGenerateReqVO { @Schema(description = "写作类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Integer writeType; + private Integer writeType; //参见 AiWriteTypeEnum 枚举 } \ No newline at end of file diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/write/AiWriteDO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/write/AiWriteDO.java new file mode 100644 index 0000000000..a569f1096b --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/write/AiWriteDO.java @@ -0,0 +1,97 @@ +package cn.iocoder.yudao.module.ai.dal.dataobject.write; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import cn.iocoder.yudao.module.ai.enums.write.AiWriteTypeEnum; + +/** + * AI 写作 DO + * + * @author xiaoxin + */ +@TableName(value = "ai_write", autoResultMap = true) +@Data +public class AiWriteDO extends BaseDO { + + /** + * 编号 + */ + @TableId(type = IdType.AUTO) + private Long id; + + /** + * 用户编号 + */ + private Long userId; + + /** + * 写作类型 + *

+ * 枚举 {@link AiWriteTypeEnum} + */ + private Integer writeType; + + /** + * 撰写内容提示 + */ + private String contentPrompt; + + /** + * 生成的撰写内容 + */ + private String generatedContent; + + /** + * 原文 + */ + private String originalContent; + + /** + * 回复内容提示 + */ + private String replyContentPrompt; + + /** + * 生成的回复内容 + */ + private String generatedReplyContent; + + /** + * 长度提示词 + */ + private String length; + + /** + * 格式提示词 + */ + private String format; + + /** + * 语气提示词 + */ + private String tone; + + /** + * 语言提示词 + */ + private String language; + + /** + * 模型 + */ + private String model; + + /** + * 平台 + */ + private String platform; + + /** + * 错误信息 + */ + private String errorMessage; + +} \ No newline at end of file diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/write/AiWriteMapper.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/write/AiWriteMapper.java new file mode 100644 index 0000000000..9564466eb2 --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/write/AiWriteMapper.java @@ -0,0 +1,14 @@ +package cn.iocoder.yudao.module.ai.dal.mysql.write; + +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.module.ai.dal.dataobject.write.AiWriteDO; +import org.apache.ibatis.annotations.Mapper; + +/** + * AI 音乐 Mapper + * + * @author xiaoxin + */ +@Mapper +public interface AiWriteMapper extends BaseMapperX { +} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/write/AiWriteService.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/write/AiWriteService.java index f8fb0634cb..0dc349cba0 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/write/AiWriteService.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/write/AiWriteService.java @@ -12,7 +12,14 @@ import reactor.core.publisher.Flux; public interface AiWriteService { - Flux> generateComposition(AiWriteGenerateReqVO generateReqVO); + /** + * 生成写作内容 + * + * @param generateReqVO 作文生成请求参数 + * @param userId 用户编号 + * @return 生成结果 + */ + Flux> generateWriteContent(AiWriteGenerateReqVO generateReqVO, Long userId); } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/write/AiWriteServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/write/AiWriteServiceImpl.java index 63d2d61929..3cc185884b 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/write/AiWriteServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/write/AiWriteServiceImpl.java @@ -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> 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> 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 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)); } -- Gitee From f05f5408658266d3eb349eab76f605aaa9a38f89 Mon Sep 17 00:00:00 2001 From: xiaoxin <718949661@qq.com> Date: Wed, 3 Jul 2024 11:17:16 +0800 Subject: [PATCH 4/6] =?UTF-8?q?=E3=80=90=E5=A2=9E=E5=8A=A0=E3=80=91AI=20?= =?UTF-8?q?=E5=86=99=E4=BD=9C=EF=BC=9A=E6=94=AF=E6=8C=81=E5=9B=9E=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/ai/enums/write/AiWriteTypeEnum.java | 4 ++-- .../admin/write/vo/AiWriteGenerateReqVO.java | 3 --- .../ai/dal/dataobject/write/AiWriteDO.java | 14 ++------------ .../ai/service/write/AiWriteServiceImpl.java | 17 ++++++++++++++--- 4 files changed, 18 insertions(+), 20 deletions(-) diff --git a/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/write/AiWriteTypeEnum.java b/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/write/AiWriteTypeEnum.java index 05db29ddae..3a62e16265 100644 --- a/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/write/AiWriteTypeEnum.java +++ b/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/write/AiWriteTypeEnum.java @@ -15,8 +15,8 @@ import java.util.Arrays; @Getter public enum AiWriteTypeEnum implements IntArrayValuable { - DESCRIPTION(1, "撰写"), - LYRIC(2, "回复"); + WRITING(1, "撰写"), + REPLY(2, "回复"); /** * 类型 diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/write/vo/AiWriteGenerateReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/write/vo/AiWriteGenerateReqVO.java index 88bcf05683..b7f83c077a 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/write/vo/AiWriteGenerateReqVO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/write/vo/AiWriteGenerateReqVO.java @@ -14,9 +14,6 @@ public class AiWriteGenerateReqVO { @Schema(description = "原文", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "领导我要辞职") private String originalContent; - @Schema(description = "回复内容", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "准了") - private String replyContentPrompt; - @Schema(description = "长度", requiredMode = Schema.RequiredMode.REQUIRED, example = "中等") @NotBlank(message = "长度不能为空") private String length; diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/write/AiWriteDO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/write/AiWriteDO.java index a569f1096b..915d56dfd8 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/write/AiWriteDO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/write/AiWriteDO.java @@ -35,12 +35,12 @@ public class AiWriteDO extends BaseDO { private Integer writeType; /** - * 撰写内容提示 + * 生成内容提示 */ private String contentPrompt; /** - * 生成的撰写内容 + * 生成的内容 */ private String generatedContent; @@ -49,16 +49,6 @@ public class AiWriteDO extends BaseDO { */ private String originalContent; - /** - * 回复内容提示 - */ - private String replyContentPrompt; - - /** - * 生成的回复内容 - */ - private String generatedReplyContent; - /** * 长度提示词 */ diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/write/AiWriteServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/write/AiWriteServiceImpl.java index 3cc185884b..cdd6e6ef54 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/write/AiWriteServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/write/AiWriteServiceImpl.java @@ -11,6 +11,7 @@ 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.enums.write.AiWriteTypeEnum; 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; @@ -26,6 +27,8 @@ import org.springframework.ai.qianfan.QianFanChatOptions; import org.springframework.stereotype.Service; import reactor.core.publisher.Flux; +import java.util.Objects; + import static cn.iocoder.yudao.framework.common.pojo.CommonResult.error; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; @@ -80,13 +83,21 @@ public class AiWriteServiceImpl implements AiWriteService { private String buildWritingPrompt(AiWriteGenerateReqVO generateReqVO) { - String template = "请直接写一篇关于 [{}] 的文章,格式为:{},语气为:{},语言为:{},长度为:{}。请确保涵盖主要内容,不需要除了正文内容外的其他回复,如标题、额外的解释或道歉。"; - String content = generateReqVO.getContentPrompt(); + String template; + Integer writeType = generateReqVO.getWriteType(); String format = generateReqVO.getFormat(); String tone = generateReqVO.getTone(); String language = generateReqVO.getLanguage(); String length = generateReqVO.getLength(); - return StrUtil.format(template, content, format, tone, language, length); + if (Objects.equals(writeType, AiWriteTypeEnum.WRITING.getType())) { + template = "请撰写一篇关于 [{}] 的文章。文章的内容格式为:[{}],语气为:[{}],语言为:[{}],长度为:[{}]。请确保涵盖主要内容,不需要除了正文内容外的其他回复,如标题、额外的解释或道歉。"; + return StrUtil.format(template, generateReqVO.getContentPrompt(), format, tone, language, length); + } else if (Objects.equals(writeType, AiWriteTypeEnum.REPLY.getType())) { + template = "请针对如下内容:[{}] 做个回复。回复内容参考:[{}], 回复的内容格式为:[{}],语气为:[{}],语言为:[{}],长度为:[{}]。不需要除了正文内容外的其他回复,如标题、额外的解释或道歉。"; + return StrUtil.format(template, generateReqVO.getOriginalContent(), generateReqVO.getContentPrompt(), format, tone, language, length); + } else { + throw new IllegalArgumentException(StrUtil.format("未知写作类型({})", writeType)); + } } // TODO 芋艿:复用 -- Gitee From b80a76d1157b075ea36a958e6b2fceb7f1e0a5b7 Mon Sep 17 00:00:00 2001 From: xiaoxin <718949661@qq.com> Date: Wed, 3 Jul 2024 11:24:24 +0800 Subject: [PATCH 5/6] =?UTF-8?q?=E3=80=90=E8=A7=A3=E5=86=B3todo=E3=80=91AI?= =?UTF-8?q?=20=E9=9F=B3=E4=B9=90=EF=BC=9A=E6=88=91=E7=9A=84=E9=9F=B3?= =?UTF-8?q?=E4=B9=90=E4=BF=AE=E6=94=B9=E5=8D=95=E7=8B=ACVO?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/music/AiMusicController.java | 2 +- .../admin/music/vo/AiMusicUpdateMyReqVO.java | 18 ++++++++++++++++++ .../admin/music/vo/AiMusicUpdateReqVO.java | 4 ---- .../ai/service/music/AiMusicService.java | 2 +- .../ai/service/music/AiMusicServiceImpl.java | 3 ++- 5 files changed, 22 insertions(+), 7 deletions(-) create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/music/vo/AiMusicUpdateMyReqVO.java diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/music/AiMusicController.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/music/AiMusicController.java index ce88c6ad9e..6c09e4b30b 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/music/AiMusicController.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/music/AiMusicController.java @@ -63,7 +63,7 @@ public class AiMusicController { @PostMapping("/update-my") @Operation(summary = "修改【我的】音乐 目前只支持修改标题") @Parameter(name = "title", required = true, description = "音乐名称", example = "夜空中最亮的星") - public CommonResult updateMy(AiMusicUpdateReqVO updateReqVO) { + public CommonResult updateMy(AiMusicUpdateMyReqVO updateReqVO) { musicService.updateMyMusic(updateReqVO, getLoginUserId()); return success(true); } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/music/vo/AiMusicUpdateMyReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/music/vo/AiMusicUpdateMyReqVO.java new file mode 100644 index 0000000000..4576701156 --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/music/vo/AiMusicUpdateMyReqVO.java @@ -0,0 +1,18 @@ +package cn.iocoder.yudao.module.ai.controller.admin.music.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +@Schema(description = "管理后台 - AI 修改我的音乐 Request VO") +@Data +public class AiMusicUpdateMyReqVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "15583") + @NotNull(message = "编号不能为空") + private Long id; + + @Schema(description = "音乐名称", example = "夜空中最亮的星") + private String title; + +} \ No newline at end of file diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/music/vo/AiMusicUpdateReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/music/vo/AiMusicUpdateReqVO.java index 6a75892e85..447bc9765e 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/music/vo/AiMusicUpdateReqVO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/music/vo/AiMusicUpdateReqVO.java @@ -15,8 +15,4 @@ public class AiMusicUpdateReqVO { @Schema(description = "是否发布", example = "true") private Boolean publicStatus; - // TODO @xin:得单独一个 vo。因为万一。。。模拟请求,就可以改 publicStatus 拉 - @Schema(description = "音乐名称", example = "夜空中最亮的星") - private String title; - } \ No newline at end of file diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/music/AiMusicService.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/music/AiMusicService.java index 7d1541ccbc..49f8332dec 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/music/AiMusicService.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/music/AiMusicService.java @@ -42,7 +42,7 @@ public interface AiMusicService { * * @param updateReqVO 更新信息 */ - void updateMyMusic(@Valid AiMusicUpdateReqVO updateReqVO, Long userId); + void updateMyMusic(@Valid AiMusicUpdateMyReqVO updateReqVO, Long userId); /** * 删除AI 音乐 diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/music/AiMusicServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/music/AiMusicServiceImpl.java index a17fc19b56..591778a553 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/music/AiMusicServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/music/AiMusicServiceImpl.java @@ -9,6 +9,7 @@ import cn.hutool.http.HttpUtil; import cn.iocoder.yudao.framework.ai.core.model.suno.api.SunoApi; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.ai.controller.admin.music.vo.AiMusicPageReqVO; +import cn.iocoder.yudao.module.ai.controller.admin.music.vo.AiMusicUpdateMyReqVO; import cn.iocoder.yudao.module.ai.controller.admin.music.vo.AiMusicUpdateReqVO; import cn.iocoder.yudao.module.ai.controller.admin.music.vo.AiSunoGenerateReqVO; import cn.iocoder.yudao.module.ai.dal.dataobject.music.AiMusicDO; @@ -111,7 +112,7 @@ public class AiMusicServiceImpl implements AiMusicService { } @Override - public void updateMyMusic(AiMusicUpdateReqVO updateReqVO, Long userId) { + public void updateMyMusic(AiMusicUpdateMyReqVO updateReqVO, Long userId) { // 校验音乐是否存在 AiMusicDO musicDO = validateMusicExists(updateReqVO.getId()); if (ObjUtil.notEqual(musicDO.getUserId(), userId)) { -- Gitee From 6dfbfe51679b0e70a0142a177a943d7bbd329a08 Mon Sep 17 00:00:00 2001 From: xiaoxin <718949661@qq.com> Date: Wed, 3 Jul 2024 14:53:08 +0800 Subject: [PATCH 6/6] =?UTF-8?q?=E3=80=90=E4=BC=98=E5=8C=96=E3=80=91AI=20?= =?UTF-8?q?=E5=86=99=E4=BD=9C=EF=BC=9A=E6=A0=BC=E5=BC=8F=E3=80=81=E8=AF=AD?= =?UTF-8?q?=E6=B0=94=E3=80=81=E8=AF=AD=E8=A8=80=E7=AD=89=E6=8A=BD=E6=9E=9A?= =?UTF-8?q?=E4=B8=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/ai/enums/write/AiLanguageEnum.java | 44 +++++++++++++++ .../ai/enums/write/AiWriteFormatEnum.java | 53 +++++++++++++++++++ .../ai/enums/write/AiWriteLengthEnum.java | 47 ++++++++++++++++ .../ai/enums/write/AiWriteToneEnum.java | 46 ++++++++++++++++ .../admin/write/AiWriteController.java | 2 +- .../admin/write/vo/AiWriteGenerateReqVO.java | 32 +++++------ .../ai/dal/dataobject/write/AiWriteDO.java | 12 ++--- .../ai/service/write/AiWriteServiceImpl.java | 16 +++--- 8 files changed, 221 insertions(+), 31 deletions(-) create mode 100644 yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/write/AiLanguageEnum.java create mode 100644 yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/write/AiWriteFormatEnum.java create mode 100644 yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/write/AiWriteLengthEnum.java create mode 100644 yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/write/AiWriteToneEnum.java diff --git a/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/write/AiLanguageEnum.java b/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/write/AiLanguageEnum.java new file mode 100644 index 0000000000..9dfe693026 --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/write/AiLanguageEnum.java @@ -0,0 +1,44 @@ +package cn.iocoder.yudao.module.ai.enums.write; + +import cn.iocoder.yudao.framework.common.core.IntArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +@AllArgsConstructor +@Getter +public enum AiLanguageEnum implements IntArrayValuable { + + AUTO(1, "自动"), + CHINESE(2, "中文"), + ENGLISH(3, "英文"), + KOREAN(4, "韩语"), + JAPANESE(5, "日语"); + + /** + * Language code + */ + private final Integer language; + /** + * Language name + */ + private final String name; + + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(AiLanguageEnum::getLanguage).toArray(); + + @Override + public int[] array() { + return ARRAYS; + } + + public static AiLanguageEnum valueOfLanguage(Integer language) { + for (AiLanguageEnum languageEnum : AiLanguageEnum.values()) { + if (languageEnum.getLanguage().equals(language)) { + return languageEnum; + } + } + throw new IllegalArgumentException("未知语言: " + language); + } + +} \ No newline at end of file diff --git a/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/write/AiWriteFormatEnum.java b/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/write/AiWriteFormatEnum.java new file mode 100644 index 0000000000..d77e08fccc --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/write/AiWriteFormatEnum.java @@ -0,0 +1,53 @@ +package cn.iocoder.yudao.module.ai.enums.write; + +import cn.iocoder.yudao.framework.common.core.IntArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * AI 写作类型的枚举 + * + * @author xiaoxin + */ +@AllArgsConstructor +@Getter +public enum AiWriteFormatEnum implements IntArrayValuable { + + AUTO(1, "自动"), + EMAIL(2, "电子邮件"), + MESSAGE(3, "消息"), + COMMENT(4, "评论"), + PARAGRAPH(5, "段落"), + ARTICLE(6, "文章"), + BLOG_POST(7, "博客文章"), + IDEA(8, "想法"), + OUTLINE(9, "大纲"); + + /** + * 格式 + */ + private final Integer format; + /** + * 格式名 + */ + private final String name; + + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(AiWriteFormatEnum::getFormat).toArray(); + + @Override + public int[] array() { + return ARRAYS; + } + + public static AiWriteFormatEnum valueOfFormat(Integer format) { + for (AiWriteFormatEnum formatEnum : AiWriteFormatEnum.values()) { + if (formatEnum.getFormat().equals(format)) { + return formatEnum; + } + } + throw new IllegalArgumentException("未知格式: " + format); + } + +} diff --git a/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/write/AiWriteLengthEnum.java b/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/write/AiWriteLengthEnum.java new file mode 100644 index 0000000000..2c6a9c5c18 --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/write/AiWriteLengthEnum.java @@ -0,0 +1,47 @@ +package cn.iocoder.yudao.module.ai.enums.write; + +import cn.iocoder.yudao.framework.common.core.IntArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * AI 写作类型的枚举 + * + * @author xiaoxin + */ +@AllArgsConstructor +@Getter +public enum AiWriteLengthEnum implements IntArrayValuable { + + AUTO(1, "自动"), + SHORT(2, "短"), + MEDIUM(3, "中"), + LONG(4, "长"); + + /** + * 长度 + */ + private final Integer length; + /** + * 长度名 + */ + private final String name; + + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(AiWriteLengthEnum::getLength).toArray(); + + @Override + public int[] array() { + return ARRAYS; + } + + public static AiWriteLengthEnum valueOfLength(Integer length) { + for (AiWriteLengthEnum lengthEnum : AiWriteLengthEnum.values()) { + if (lengthEnum.getLength().equals(length)) { + return lengthEnum; + } + } + throw new IllegalArgumentException("未知长度: " + length); + } +} diff --git a/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/write/AiWriteToneEnum.java b/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/write/AiWriteToneEnum.java new file mode 100644 index 0000000000..181682fd93 --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/write/AiWriteToneEnum.java @@ -0,0 +1,46 @@ +package cn.iocoder.yudao.module.ai.enums.write; + +import cn.iocoder.yudao.framework.common.core.IntArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +@AllArgsConstructor +@Getter +public enum AiWriteToneEnum implements IntArrayValuable { + + AUTO(1, "自动"), + FRIENDLY(2, "友善"), + CASUAL(3, "随意"), + KIND(4, "友好"), + PROFESSIONAL(5, "专业"), + HUMOROUS(6, "谈谐"), + INTERESTING(7, "有趣"), + FORMAL(8, "正式"); + + /** + * 语气 + */ + private final Integer tone; + /** + * 语气名 + */ + private final String name; + + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(AiWriteToneEnum::getTone).toArray(); + + @Override + public int[] array() { + return ARRAYS; + } + + public static AiWriteToneEnum valueOfTone(Integer tone) { + for (AiWriteToneEnum toneEnum : AiWriteToneEnum.values()) { + if (toneEnum.getTone().equals(tone)) { + return toneEnum; + } + } + throw new IllegalArgumentException("未知语气: " + tone); + } +} \ No newline at end of file diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/write/AiWriteController.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/write/AiWriteController.java index 6298d44e37..a032998edf 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/write/AiWriteController.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/write/AiWriteController.java @@ -28,7 +28,7 @@ public class AiWriteController { @PostMapping(value = "/generate-stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE) @PermitAll @Operation(summary = "写作生成(流式)", description = "流式返回,响应较快") - public Flux> generateComposition(@RequestBody @Valid AiWriteGenerateReqVO generateReqVO) { + public Flux> generateWriteContent(@RequestBody @Valid AiWriteGenerateReqVO generateReqVO) { return writeService.generateWriteContent(generateReqVO, getLoginUserId()); } } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/write/vo/AiWriteGenerateReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/write/vo/AiWriteGenerateReqVO.java index b7f83c077a..59283c91c0 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/write/vo/AiWriteGenerateReqVO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/write/vo/AiWriteGenerateReqVO.java @@ -1,36 +1,36 @@ package cn.iocoder.yudao.module.ai.controller.admin.write.vo; import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; import lombok.Data; @Schema(description = "管理后台 - AI 写作生成 Request VO") @Data public class AiWriteGenerateReqVO { - @Schema(description = "写作内容提示", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "田忌赛马") - private String contentPrompt; + @Schema(description = "写作内容提示", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "1.撰写:田忌赛马;2.回复:不批") + private String prompt; @Schema(description = "原文", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "领导我要辞职") private String originalContent; - @Schema(description = "长度", requiredMode = Schema.RequiredMode.REQUIRED, example = "中等") - @NotBlank(message = "长度不能为空") - private String length; + @Schema(description = "长度", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "长度不能为空") + private Integer length; - @Schema(description = "格式", requiredMode = Schema.RequiredMode.REQUIRED, example = "文章") - @NotBlank(message = "格式不能为空") - private String format; + @Schema(description = "格式", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "格式不能为空") + private Integer format; - @Schema(description = "语气", requiredMode = Schema.RequiredMode.REQUIRED, example = "随意") - @NotBlank(message = "语气不能为空") - private String tone; + @Schema(description = "语气", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "语气不能为空") + private Integer tone; - @Schema(description = "语言", requiredMode = Schema.RequiredMode.REQUIRED, example = "中文") - @NotBlank(message = "语言不能为空") - private String language; + @Schema(description = "语言", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "语言不能为空") + private Integer language; @Schema(description = "写作类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Integer writeType; //参见 AiWriteTypeEnum 枚举 + private Integer type; //参见 AiWriteTypeEnum 枚举 } \ No newline at end of file diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/write/AiWriteDO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/write/AiWriteDO.java index 915d56dfd8..347c2143df 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/write/AiWriteDO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/write/AiWriteDO.java @@ -32,12 +32,12 @@ public class AiWriteDO extends BaseDO { *

* 枚举 {@link AiWriteTypeEnum} */ - private Integer writeType; + private Integer type; /** * 生成内容提示 */ - private String contentPrompt; + private String prompt; /** * 生成的内容 @@ -52,22 +52,22 @@ public class AiWriteDO extends BaseDO { /** * 长度提示词 */ - private String length; + private Integer length; /** * 格式提示词 */ - private String format; + private Integer format; /** * 语气提示词 */ - private String tone; + private Integer tone; /** * 语言提示词 */ - private String language; + private Integer language; /** * 模型 diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/write/AiWriteServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/write/AiWriteServiceImpl.java index cdd6e6ef54..8a3cdfa9e8 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/write/AiWriteServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/write/AiWriteServiceImpl.java @@ -11,7 +11,7 @@ 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.enums.write.AiWriteTypeEnum; +import cn.iocoder.yudao.module.ai.enums.write.*; 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; @@ -84,17 +84,17 @@ public class AiWriteServiceImpl implements AiWriteService { private String buildWritingPrompt(AiWriteGenerateReqVO generateReqVO) { String template; - Integer writeType = generateReqVO.getWriteType(); - String format = generateReqVO.getFormat(); - String tone = generateReqVO.getTone(); - String language = generateReqVO.getLanguage(); - String length = generateReqVO.getLength(); + Integer writeType = generateReqVO.getType(); + String format = AiWriteFormatEnum.valueOfFormat(generateReqVO.getFormat()).getName(); + String tone = AiWriteToneEnum.valueOfTone(generateReqVO.getTone()).getName(); + String language = AiLanguageEnum.valueOfLanguage(generateReqVO.getLanguage()).getName(); + String length = AiWriteLengthEnum.valueOfLength(generateReqVO.getLength()).getName(); if (Objects.equals(writeType, AiWriteTypeEnum.WRITING.getType())) { template = "请撰写一篇关于 [{}] 的文章。文章的内容格式为:[{}],语气为:[{}],语言为:[{}],长度为:[{}]。请确保涵盖主要内容,不需要除了正文内容外的其他回复,如标题、额外的解释或道歉。"; - return StrUtil.format(template, generateReqVO.getContentPrompt(), format, tone, language, length); + return StrUtil.format(template, generateReqVO.getPrompt(), format, tone, language, length); } else if (Objects.equals(writeType, AiWriteTypeEnum.REPLY.getType())) { template = "请针对如下内容:[{}] 做个回复。回复内容参考:[{}], 回复的内容格式为:[{}],语气为:[{}],语言为:[{}],长度为:[{}]。不需要除了正文内容外的其他回复,如标题、额外的解释或道歉。"; - return StrUtil.format(template, generateReqVO.getOriginalContent(), generateReqVO.getContentPrompt(), format, tone, language, length); + return StrUtil.format(template, generateReqVO.getOriginalContent(), generateReqVO.getPrompt(), format, tone, language, length); } else { throw new IllegalArgumentException(StrUtil.format("未知写作类型({})", writeType)); } -- Gitee