Commit ef701167b76fd2757da5e0d23b08b02a1b7af057
1 parent
7024c5ab
1、增加 chat service
2、可动态传入 modal,选择模型
Showing
6 changed files
with
154 additions
and
9 deletions
yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/AiClientNameEnum.java
| ... | ... | @@ -25,4 +25,13 @@ public enum AiClientNameEnum { |
| 25 | 25 | private String name; |
| 26 | 26 | |
| 27 | 27 | private String message; |
| 28 | + | |
| 29 | + public static AiClientNameEnum valueOfName(String name) { | |
| 30 | + for (AiClientNameEnum nameEnum : AiClientNameEnum.values()) { | |
| 31 | + if (nameEnum.getName().equals(name)) { | |
| 32 | + return nameEnum; | |
| 33 | + } | |
| 34 | + } | |
| 35 | + throw new IllegalArgumentException("Invalid MessageType value: " + name); | |
| 36 | + } | |
| 28 | 37 | } | ... | ... |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/ChatController.java
| ... | ... | @@ -2,20 +2,20 @@ package cn.iocoder.yudao.module.ai.controller; |
| 2 | 2 | |
| 3 | 3 | import cn.hutool.core.exceptions.ExceptionUtil; |
| 4 | 4 | import cn.iocoder.yudao.framework.ai.chat.ChatResponse; |
| 5 | -import cn.iocoder.yudao.framework.ai.chat.prompt.Prompt; | |
| 6 | 5 | import cn.iocoder.yudao.framework.ai.config.AiClient; |
| 7 | 6 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| 8 | -import cn.iocoder.yudao.module.ai.enums.AiClientNameEnum; | |
| 9 | 7 | import cn.iocoder.yudao.module.ai.service.ChatService; |
| 8 | +import cn.iocoder.yudao.module.ai.vo.ChatReq; | |
| 10 | 9 | import io.swagger.v3.oas.annotations.Operation; |
| 11 | 10 | import io.swagger.v3.oas.annotations.tags.Tag; |
| 12 | 11 | import lombok.AllArgsConstructor; |
| 13 | 12 | import lombok.extern.slf4j.Slf4j; |
| 14 | 13 | import org.springframework.beans.factory.annotation.Autowired; |
| 15 | 14 | import org.springframework.http.MediaType; |
| 15 | +import org.springframework.validation.annotation.Validated; | |
| 16 | 16 | import org.springframework.web.bind.annotation.GetMapping; |
| 17 | +import org.springframework.web.bind.annotation.ModelAttribute; | |
| 17 | 18 | import org.springframework.web.bind.annotation.RequestMapping; |
| 18 | -import org.springframework.web.bind.annotation.RequestParam; | |
| 19 | 19 | import org.springframework.web.bind.annotation.RestController; |
| 20 | 20 | import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; |
| 21 | 21 | import reactor.core.publisher.Flux; |
| ... | ... | @@ -43,17 +43,16 @@ public class ChatController { |
| 43 | 43 | |
| 44 | 44 | @Operation(summary = "聊天-chat", description = "这个一般等待时间比较久,需要全部完成才会返回!") |
| 45 | 45 | @GetMapping("/chat") |
| 46 | - public CommonResult<String> chat(@RequestParam("prompt") String prompt) { | |
| 47 | - ChatResponse callRes = aiClient.call(new Prompt(prompt), AiClientNameEnum.QIAN_WEN.getName()); | |
| 48 | - return CommonResult.success(callRes.getResult().getOutput().getContent()); | |
| 46 | + public CommonResult<String> chat(@Validated @ModelAttribute ChatReq req) { | |
| 47 | + return CommonResult.success(chatService.chat(req)); | |
| 49 | 48 | } |
| 50 | 49 | |
| 51 | 50 | // TODO @芋艿:调用这个方法异常,Unable to handle the Spring Security Exception because the response is already committed. |
| 52 | 51 | @Operation(summary = "聊天-stream", description = "这里跟通义千问一样采用的是 Server-Sent Events (SSE) 通讯模式") |
| 53 | 52 | @GetMapping(value = "/chatStream", produces = MediaType.TEXT_EVENT_STREAM_VALUE) |
| 54 | - public SseEmitter chatStream(@RequestParam("prompt") String prompt) { | |
| 53 | + public SseEmitter chatStream(@Validated @ModelAttribute ChatReq req) { | |
| 55 | 54 | Utf8SseEmitter sseEmitter = new Utf8SseEmitter(); |
| 56 | - Flux<ChatResponse> streamResponse = aiClient.stream(new Prompt(prompt), AiClientNameEnum.QIAN_WEN.getName()); | |
| 55 | + Flux<ChatResponse> streamResponse = chatService.chatStream(req); | |
| 57 | 56 | streamResponse.subscribe( |
| 58 | 57 | new Consumer<ChatResponse>() { |
| 59 | 58 | @Override | ... | ... |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/AiChatRoleController.java renamed to yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/ChatRoleController.java
| ... | ... | @@ -17,7 +17,7 @@ import org.springframework.web.bind.annotation.*; |
| 17 | 17 | @RestController |
| 18 | 18 | @RequestMapping("/chat-role") |
| 19 | 19 | @AllArgsConstructor |
| 20 | -public class AiChatRoleController { | |
| 20 | +public class ChatRoleController { | |
| 21 | 21 | |
| 22 | 22 | private final ChatRoleService chatRoleService; |
| 23 | 23 | ... | ... |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/ChatService.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.ai.service; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.ai.chat.ChatResponse; | |
| 4 | +import cn.iocoder.yudao.module.ai.enums.AiClientNameEnum; | |
| 5 | +import cn.iocoder.yudao.module.ai.vo.ChatReq; | |
| 6 | +import reactor.core.publisher.Flux; | |
| 7 | + | |
| 8 | +/** | |
| 9 | + * 聊天 chat | |
| 10 | + * | |
| 11 | + * @author fansili | |
| 12 | + * @time 2024/4/14 15:55 | |
| 13 | + * @since 1.0 | |
| 14 | + */ | |
| 15 | +public interface ChatService { | |
| 16 | + | |
| 17 | + /** | |
| 18 | + * chat | |
| 19 | + * | |
| 20 | + * @param req | |
| 21 | + * @return | |
| 22 | + */ | |
| 23 | + String chat(ChatReq req); | |
| 24 | + | |
| 25 | + /** | |
| 26 | + * chat stream | |
| 27 | + * | |
| 28 | + * @param req | |
| 29 | + * @param clientNameEnum | |
| 30 | + * @return | |
| 31 | + */ | |
| 32 | + Flux<ChatResponse> chatStream(ChatReq req); | |
| 33 | +} | ... | ... |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/impl/ChatServiceImpl.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.ai.service.impl; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.ai.chat.ChatResponse; | |
| 4 | +import cn.iocoder.yudao.framework.ai.chat.prompt.Prompt; | |
| 5 | +import cn.iocoder.yudao.framework.ai.config.AiClient; | |
| 6 | +import cn.iocoder.yudao.module.ai.enums.AiClientNameEnum; | |
| 7 | +import cn.iocoder.yudao.module.ai.service.ChatService; | |
| 8 | +import cn.iocoder.yudao.module.ai.vo.ChatReq; | |
| 9 | +import lombok.AllArgsConstructor; | |
| 10 | +import lombok.extern.slf4j.Slf4j; | |
| 11 | +import org.springframework.stereotype.Service; | |
| 12 | +import reactor.core.publisher.Flux; | |
| 13 | + | |
| 14 | +/** | |
| 15 | + * 聊天 service | |
| 16 | + * | |
| 17 | + * @author fansili | |
| 18 | + * @time 2024/4/14 15:55 | |
| 19 | + * @since 1.0 | |
| 20 | + */ | |
| 21 | +@Slf4j | |
| 22 | +@Service | |
| 23 | +@AllArgsConstructor | |
| 24 | +public class ChatServiceImpl implements ChatService { | |
| 25 | + | |
| 26 | + private final AiClient aiClient; | |
| 27 | + | |
| 28 | + /** | |
| 29 | + * chat | |
| 30 | + * | |
| 31 | + * @param req | |
| 32 | + * @return | |
| 33 | + */ | |
| 34 | + public String chat(ChatReq req) { | |
| 35 | + AiClientNameEnum clientNameEnum = AiClientNameEnum.valueOfName(req.getModal()); | |
| 36 | + // 创建 chat 需要的 Prompt | |
| 37 | + Prompt prompt = new Prompt(req.getPrompt()); | |
| 38 | + req.setTopK(req.getTopK()); | |
| 39 | + req.setTopP(req.getTopP()); | |
| 40 | + req.setTemperature(req.getTemperature()); | |
| 41 | + // 发送 call 调用 | |
| 42 | + ChatResponse call = aiClient.call(prompt, clientNameEnum.getName()); | |
| 43 | + return call.getResult().getOutput().getContent(); | |
| 44 | + } | |
| 45 | + | |
| 46 | + /** | |
| 47 | + * chat stream | |
| 48 | + * | |
| 49 | + * @param req | |
| 50 | + * @return | |
| 51 | + */ | |
| 52 | + @Override | |
| 53 | + public Flux<ChatResponse> chatStream(ChatReq req) { | |
| 54 | + AiClientNameEnum clientNameEnum = AiClientNameEnum.valueOfName(req.getModal()); | |
| 55 | + // 创建 chat 需要的 Prompt | |
| 56 | + Prompt prompt = new Prompt(req.getPrompt()); | |
| 57 | + req.setTopK(req.getTopK()); | |
| 58 | + req.setTopP(req.getTopP()); | |
| 59 | + req.setTemperature(req.getTemperature()); | |
| 60 | + return aiClient.stream(prompt, clientNameEnum.getName()); | |
| 61 | + } | |
| 62 | +} | ... | ... |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/vo/ChatReq.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.ai.vo; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.module.ai.enums.AiClientNameEnum; | |
| 4 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 5 | +import jakarta.validation.constraints.NotNull; | |
| 6 | +import jakarta.validation.constraints.Size; | |
| 7 | +import lombok.Data; | |
| 8 | +import lombok.experimental.Accessors; | |
| 9 | + | |
| 10 | +/** | |
| 11 | + * chat req | |
| 12 | + * | |
| 13 | + * @author fansili | |
| 14 | + * @time 2024/4/14 16:12 | |
| 15 | + * @since 1.0 | |
| 16 | + */ | |
| 17 | +@Data | |
| 18 | +@Accessors(chain = true) | |
| 19 | +public class ChatReq { | |
| 20 | + | |
| 21 | + | |
| 22 | + @NotNull(message = "提示词不能为空!") | |
| 23 | + @Size(max = 3000, message = "提示词最大3000个字符!") | |
| 24 | + @Schema(description = "填入固定值,1 issues, 2 pr") | |
| 25 | + private String prompt; | |
| 26 | + | |
| 27 | + @Schema(description = "用于控制随机性和多样性的温度参数") | |
| 28 | + private Float temperature; | |
| 29 | + | |
| 30 | + @Schema(description = "生成时,核采样方法的概率阈值。例如,取值为0.8时,仅保留累计概率之和大于等于0.8的概率分布中的token,\n" + | |
| 31 | + " * 作为随机采样的候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的随机性越低。\n" + | |
| 32 | + " * 默认值为0.8。注意,取值不要大于等于1\n") | |
| 33 | + private Float topP; | |
| 34 | + | |
| 35 | + @Schema(description = "在生成消息时采用的Top-K采样大小,表示模型生成回复时考虑的候选项集合的大小") | |
| 36 | + private Integer topK; | |
| 37 | + | |
| 38 | + @Schema(description = "ai模型(查看 AiClientNameEnum)") | |
| 39 | + @NotNull(message = "模型不能为空!") | |
| 40 | + @Size(max = 30, message = "模型字符最大30个字符!") | |
| 41 | + private String modal; | |
| 42 | +} | ... | ... |