Commit ef701167b76fd2757da5e0d23b08b02a1b7af057

Authored by cherishsince
1 parent 7024c5ab

1、增加 chat service

2、可动态传入 modal,选择模型
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,4 +25,13 @@ public enum AiClientNameEnum {
25 private String name; 25 private String name;
26 26
27 private String message; 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,20 +2,20 @@ package cn.iocoder.yudao.module.ai.controller;
2 2
3 import cn.hutool.core.exceptions.ExceptionUtil; 3 import cn.hutool.core.exceptions.ExceptionUtil;
4 import cn.iocoder.yudao.framework.ai.chat.ChatResponse; 4 import cn.iocoder.yudao.framework.ai.chat.ChatResponse;
5 -import cn.iocoder.yudao.framework.ai.chat.prompt.Prompt;  
6 import cn.iocoder.yudao.framework.ai.config.AiClient; 5 import cn.iocoder.yudao.framework.ai.config.AiClient;
7 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 6 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
8 -import cn.iocoder.yudao.module.ai.enums.AiClientNameEnum;  
9 import cn.iocoder.yudao.module.ai.service.ChatService; 7 import cn.iocoder.yudao.module.ai.service.ChatService;
  8 +import cn.iocoder.yudao.module.ai.vo.ChatReq;
10 import io.swagger.v3.oas.annotations.Operation; 9 import io.swagger.v3.oas.annotations.Operation;
11 import io.swagger.v3.oas.annotations.tags.Tag; 10 import io.swagger.v3.oas.annotations.tags.Tag;
12 import lombok.AllArgsConstructor; 11 import lombok.AllArgsConstructor;
13 import lombok.extern.slf4j.Slf4j; 12 import lombok.extern.slf4j.Slf4j;
14 import org.springframework.beans.factory.annotation.Autowired; 13 import org.springframework.beans.factory.annotation.Autowired;
15 import org.springframework.http.MediaType; 14 import org.springframework.http.MediaType;
  15 +import org.springframework.validation.annotation.Validated;
16 import org.springframework.web.bind.annotation.GetMapping; 16 import org.springframework.web.bind.annotation.GetMapping;
  17 +import org.springframework.web.bind.annotation.ModelAttribute;
17 import org.springframework.web.bind.annotation.RequestMapping; 18 import org.springframework.web.bind.annotation.RequestMapping;
18 -import org.springframework.web.bind.annotation.RequestParam;  
19 import org.springframework.web.bind.annotation.RestController; 19 import org.springframework.web.bind.annotation.RestController;
20 import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; 20 import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
21 import reactor.core.publisher.Flux; 21 import reactor.core.publisher.Flux;
@@ -43,17 +43,16 @@ public class ChatController { @@ -43,17 +43,16 @@ public class ChatController {
43 43
44 @Operation(summary = "聊天-chat", description = "这个一般等待时间比较久,需要全部完成才会返回!") 44 @Operation(summary = "聊天-chat", description = "这个一般等待时间比较久,需要全部完成才会返回!")
45 @GetMapping("/chat") 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 // TODO @芋艿:调用这个方法异常,Unable to handle the Spring Security Exception because the response is already committed. 50 // TODO @芋艿:调用这个方法异常,Unable to handle the Spring Security Exception because the response is already committed.
52 @Operation(summary = "聊天-stream", description = "这里跟通义千问一样采用的是 Server-Sent Events (SSE) 通讯模式") 51 @Operation(summary = "聊天-stream", description = "这里跟通义千问一样采用的是 Server-Sent Events (SSE) 通讯模式")
53 @GetMapping(value = "/chatStream", produces = MediaType.TEXT_EVENT_STREAM_VALUE) 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 Utf8SseEmitter sseEmitter = new Utf8SseEmitter(); 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 streamResponse.subscribe( 56 streamResponse.subscribe(
58 new Consumer<ChatResponse>() { 57 new Consumer<ChatResponse>() {
59 @Override 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,7 +17,7 @@ import org.springframework.web.bind.annotation.*;
17 @RestController 17 @RestController
18 @RequestMapping("/chat-role") 18 @RequestMapping("/chat-role")
19 @AllArgsConstructor 19 @AllArgsConstructor
20 -public class AiChatRoleController { 20 +public class ChatRoleController {
21 21
22 private final ChatRoleService chatRoleService; 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 +}