├── .gitignore ├── README.md ├── pom.xml └── src ├── main ├── java │ └── com │ │ └── pitayafruit │ │ ├── DifyDemoApplication.java │ │ ├── controller │ │ └── TestController.java │ │ ├── req │ │ └── DifyRequestBody.java │ │ ├── resp │ │ ├── BlockResponse.java │ │ └── StreamResponse.java │ │ └── service │ │ └── DifyService.java └── resources │ └── application.yml └── test └── java └── com └── pitayafruit └── DifyDemoApplicationTests.java /.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | !**/src/main/**/build/ 30 | !**/src/test/**/build/ 31 | 32 | ### VS Code ### 33 | .vscode/ 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 项目说明 2 | 基于SpringBoot封装了调用Dify服务的API 3 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 3.3.2 9 | 10 | 11 | com.pitayafruit 12 | dify-demo 13 | 0.0.1-SNAPSHOT 14 | dify-demo 15 | dify-demo 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 17 31 | 32 | 33 | 34 | 35 | org.springframework.boot 36 | spring-boot-starter-web 37 | 38 | 39 | 40 | org.projectlombok 41 | lombok 42 | 43 | 44 | 45 | org.springframework.boot 46 | spring-boot-starter-webflux 47 | 48 | 49 | 50 | com.alibaba.fastjson2 51 | fastjson2 52 | 2.0.52 53 | 54 | 55 | 56 | org.springframework.boot 57 | spring-boot-starter-test 58 | test 59 | 60 | 61 | 62 | 63 | 64 | 65 | org.springframework.boot 66 | spring-boot-maven-plugin 67 | 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /src/main/java/com/pitayafruit/DifyDemoApplication.java: -------------------------------------------------------------------------------- 1 | package com.pitayafruit; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.web.client.RestTemplate; 7 | import org.springframework.web.reactive.function.client.WebClient; 8 | 9 | @SpringBootApplication 10 | public class DifyDemoApplication { 11 | 12 | public static void main(String[] args) { 13 | SpringApplication.run(DifyDemoApplication.class, args); 14 | } 15 | 16 | @Bean 17 | public RestTemplate restTemplate() { 18 | return new RestTemplate(); 19 | } 20 | 21 | @Bean 22 | public WebClient webClient() { 23 | return WebClient.create(); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/pitayafruit/controller/TestController.java: -------------------------------------------------------------------------------- 1 | package com.pitayafruit.controller; 2 | 3 | import com.pitayafruit.resp.BlockResponse; 4 | import com.pitayafruit.resp.StreamResponse; 5 | import com.pitayafruit.service.DifyService; 6 | import lombok.RequiredArgsConstructor; 7 | import org.springframework.beans.factory.annotation.Value; 8 | import org.springframework.web.bind.annotation.GetMapping; 9 | import org.springframework.web.bind.annotation.RequestMapping; 10 | import org.springframework.web.bind.annotation.RestController; 11 | import reactor.core.publisher.Flux; 12 | 13 | @RestController 14 | @RequestMapping("/api/test") 15 | @RequiredArgsConstructor 16 | public class TestController { 17 | 18 | @Value("${dify.key.test}") 19 | private String testKey; 20 | 21 | private final DifyService difyService; 22 | 23 | @GetMapping("/block") 24 | public String test1() { 25 | String query = "鲁迅和周树人什么关系?"; 26 | BlockResponse blockResponse = difyService.blockingMessage(query, 0L, testKey); 27 | return blockResponse.getAnswer(); 28 | } 29 | 30 | @GetMapping("/stream") 31 | public Flux test2() { 32 | String query = "鲁迅和周树人什么关系?"; 33 | return difyService.streamingMessage(query, 0L, testKey); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/com/pitayafruit/req/DifyRequestBody.java: -------------------------------------------------------------------------------- 1 | package com.pitayafruit.req; 2 | 3 | import com.alibaba.fastjson2.annotation.JSONField; 4 | import java.io.Serializable; 5 | import java.util.Map; 6 | import lombok.Data; 7 | 8 | /** 9 | * Dify请求体. 10 | */ 11 | @Data 12 | public class DifyRequestBody implements Serializable { 13 | 14 | /** 15 | * 用户输入/提问内容. 16 | */ 17 | private String query; 18 | 19 | /** 20 | * 允许传入 App 定义的各变量值. 21 | */ 22 | private Map inputs; 23 | 24 | /** 25 | * 响应模式,streaming 流式,blocking 阻塞. 26 | */ 27 | @JSONField(name = "response_mode") 28 | private String responseMode; 29 | 30 | /** 31 | * 用户标识. 32 | */ 33 | private String user; 34 | 35 | /** 36 | * 会话id. 37 | */ 38 | @JSONField(name = "conversation_id") 39 | private String conversationId; 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/pitayafruit/resp/BlockResponse.java: -------------------------------------------------------------------------------- 1 | package com.pitayafruit.resp; 2 | 3 | import java.io.Serializable; 4 | import java.util.Map; 5 | import lombok.Data; 6 | 7 | /** 8 | * Dify阻塞式调用响应. 9 | */ 10 | @Data 11 | public class BlockResponse implements Serializable { 12 | 13 | /** 14 | * 不同模式下的事件类型. 15 | */ 16 | private String event; 17 | 18 | /** 19 | * 消息唯一 ID. 20 | */ 21 | private String messageId; 22 | 23 | /** 24 | * 任务ID. 25 | */ 26 | private String taskId; 27 | 28 | /** 29 | * agent_thought id. 30 | */ 31 | private String id; 32 | 33 | /** 34 | * 会话 ID. 35 | */ 36 | private String conversationId; 37 | 38 | /** 39 | * App 模式,固定为 chat. 40 | */ 41 | private String mode; 42 | 43 | /** 44 | * 完整回复内容. 45 | */ 46 | private String answer; 47 | 48 | /** 49 | * 元数据. 50 | */ 51 | private Map> metadata; 52 | 53 | /** 54 | * 创建时间戳. 55 | */ 56 | private Long createdAt; 57 | 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/com/pitayafruit/resp/StreamResponse.java: -------------------------------------------------------------------------------- 1 | package com.pitayafruit.resp; 2 | 3 | import java.io.Serializable; 4 | import lombok.Data; 5 | 6 | 7 | /** 8 | * Dify流式调用响应. 9 | */ 10 | @Data 11 | public class StreamResponse implements Serializable { 12 | 13 | /** 14 | * 不同模式下的事件类型. 15 | */ 16 | private String event; 17 | 18 | /** 19 | * agent_thought id. 20 | */ 21 | private String id; 22 | 23 | /** 24 | * 任务ID. 25 | */ 26 | private String taskId; 27 | 28 | /** 29 | * 消息唯一ID. 30 | */ 31 | private String messageId; 32 | 33 | /** 34 | * LLM 返回文本块内容. 35 | */ 36 | private String answer; 37 | 38 | /** 39 | * 创建时间戳. 40 | */ 41 | private Long createdAt; 42 | 43 | /** 44 | * 会话 ID. 45 | */ 46 | private String conversationId; 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/com/pitayafruit/service/DifyService.java: -------------------------------------------------------------------------------- 1 | package com.pitayafruit.service; 2 | 3 | import com.alibaba.fastjson2.JSON; 4 | import com.pitayafruit.req.DifyRequestBody; 5 | import com.pitayafruit.resp.BlockResponse; 6 | import com.pitayafruit.resp.StreamResponse; 7 | import java.util.HashMap; 8 | import java.util.List; 9 | import lombok.RequiredArgsConstructor; 10 | import org.springframework.beans.factory.annotation.Value; 11 | import org.springframework.http.HttpEntity; 12 | import org.springframework.http.HttpHeaders; 13 | import org.springframework.http.MediaType; 14 | import org.springframework.http.ResponseEntity; 15 | import org.springframework.stereotype.Service; 16 | import org.springframework.web.client.RestTemplate; 17 | import org.springframework.web.reactive.function.client.WebClient; 18 | import reactor.core.publisher.Flux; 19 | 20 | 21 | @Service 22 | @RequiredArgsConstructor 23 | public class DifyService { 24 | 25 | @Value("${dify.url}") 26 | private String url; 27 | 28 | private final RestTemplate restTemplate; 29 | 30 | private final WebClient webClient; 31 | 32 | /** 33 | * 流式调用dify. 34 | * 35 | * @param query 查询文本 36 | * @param userId 用户id 37 | * @param apiKey apiKey 通过 apiKey 获取权限并区分不同的 dify 应用 38 | * @return Flux 响应流 39 | */ 40 | public Flux streamingMessage(String query, Long userId, String apiKey) { 41 | //1.设置请求体 42 | DifyRequestBody body = new DifyRequestBody(); 43 | body.setInputs(new HashMap<>()); 44 | body.setQuery(query); 45 | body.setResponseMode("streaming"); 46 | body.setConversationId(""); 47 | body.setUser(userId.toString()); 48 | //2.使用webclient发送post请求 49 | return webClient.post() 50 | .uri(url) 51 | .headers(httpHeaders -> { 52 | httpHeaders.setContentType(MediaType.APPLICATION_JSON); 53 | httpHeaders.setBearerAuth(apiKey); 54 | }) 55 | .bodyValue(JSON.toJSONString(body)) 56 | .retrieve() 57 | .bodyToFlux(StreamResponse.class); 58 | } 59 | 60 | 61 | /** 62 | * 阻塞式调用dify. 63 | * 64 | * @param query 查询文本 65 | * @param userId 用户id 66 | * @param apiKey apiKey 通过 apiKey 获取权限并区分不同的 dify 应用 67 | * @return BlockResponse 68 | */ 69 | public BlockResponse blockingMessage(String query, Long userId, String apiKey) { 70 | //1.设置请求体 71 | DifyRequestBody body = new DifyRequestBody(); 72 | body.setInputs(new HashMap<>()); 73 | body.setQuery(query); 74 | body.setResponseMode("blocking"); 75 | body.setConversationId(""); 76 | body.setUser(userId.toString()); 77 | //2.设置请求头 78 | HttpHeaders headers = new HttpHeaders(); 79 | headers.setContentType(MediaType.APPLICATION_JSON); 80 | headers.setAccept(List.of(MediaType.APPLICATION_JSON)); 81 | headers.setBearerAuth(apiKey); 82 | //3.封装请求体和请求头 83 | String jsonString = JSON.toJSONString(body); 84 | HttpEntity entity = new HttpEntity<>(jsonString, headers); 85 | //4.发送post请求,阻塞式 86 | ResponseEntity stringResponseEntity = 87 | restTemplate.postForEntity(url, entity, BlockResponse.class); 88 | //5.返回响应体 89 | return stringResponseEntity.getBody(); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | application: 3 | name: dify-demo 4 | 5 | dify: 6 | url: http://localhost/v1/chat-messages 7 | key: 8 | test: app-y67TVu9T8tZ6r2UUMBJ6wm8R 9 | 10 | -------------------------------------------------------------------------------- /src/test/java/com/pitayafruit/DifyDemoApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.pitayafruit; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class DifyDemoApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | --------------------------------------------------------------------------------