扫描
This commit is contained in:
23
src/main/java/com/senmesh/sdk/dify/config/DifyConfig.java
Normal file
23
src/main/java/com/senmesh/sdk/dify/config/DifyConfig.java
Normal file
@@ -0,0 +1,23 @@
|
||||
package com.senmesh.sdk.dify.config;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* Dify配置
|
||||
*
|
||||
* @author sr
|
||||
*/
|
||||
@Data
|
||||
@Configuration
|
||||
@EnableConfigurationProperties(DifyProperties.class)
|
||||
@ConditionalOnProperty(name = DifyConfig.DIFY_ENABLE, havingValue = "true")
|
||||
public class DifyConfig {
|
||||
|
||||
static final String DIFY_ENABLE = "dify.enable";
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.senmesh.sdk.dify.config;
|
||||
|
||||
import java.io.Serializable;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* Dify配置属性
|
||||
*
|
||||
* @author sr
|
||||
*/
|
||||
@ConfigurationProperties(prefix = DifyProperties.PREFIX)
|
||||
@Data
|
||||
@Component
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class DifyProperties implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public static final String PREFIX = "dify";
|
||||
|
||||
/** Dify服务地址 */
|
||||
private String host;
|
||||
|
||||
}
|
||||
|
||||
69
src/main/java/com/senmesh/sdk/dify/dto/DifyChatRequest.java
Normal file
69
src/main/java/com/senmesh/sdk/dify/dto/DifyChatRequest.java
Normal file
@@ -0,0 +1,69 @@
|
||||
package com.senmesh.sdk.dify.dto;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* Dify对话请求
|
||||
*
|
||||
* @author sr
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class DifyChatRequest implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** 输入参数 */
|
||||
private Map<String, Object> inputs;
|
||||
|
||||
/** 查询内容 */
|
||||
private String query;
|
||||
|
||||
/** 响应模式: blocking/streaming */
|
||||
@Builder.Default
|
||||
private String responseMode = "blocking";
|
||||
|
||||
/** 会话ID */
|
||||
private String conversationId;
|
||||
|
||||
/** 用户标识 */
|
||||
private String user;
|
||||
|
||||
/** 文件列表 */
|
||||
private List<DifyFileInfo> files;
|
||||
|
||||
/**
|
||||
* 文件信息
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public static class DifyFileInfo implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** 文件类型: image/document */
|
||||
private String type;
|
||||
|
||||
/** 传输方式: remote_url/local_file */
|
||||
@Builder.Default
|
||||
private String transferMethod = "local_file";
|
||||
|
||||
/** 远程URL */
|
||||
private String url;
|
||||
|
||||
/** 上传文件ID */
|
||||
private String uploadFileId;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
144
src/main/java/com/senmesh/sdk/dify/dto/DifyChatResponse.java
Normal file
144
src/main/java/com/senmesh/sdk/dify/dto/DifyChatResponse.java
Normal file
@@ -0,0 +1,144 @@
|
||||
package com.senmesh.sdk.dify.dto;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* Dify对话响应
|
||||
*
|
||||
* @author sr
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class DifyChatResponse implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** 消息ID */
|
||||
private String messageId;
|
||||
|
||||
/** 会话ID */
|
||||
private String conversationId;
|
||||
|
||||
/** 模式 */
|
||||
private String mode;
|
||||
|
||||
/** 回答内容 */
|
||||
private String answer;
|
||||
|
||||
/** 元数据 */
|
||||
private Metadata metadata;
|
||||
|
||||
/** 创建时间 */
|
||||
private Long createdAt;
|
||||
|
||||
/**
|
||||
* 元数据
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public static class Metadata implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** 使用量 */
|
||||
private Usage usage;
|
||||
|
||||
/** 检索器资源 */
|
||||
private List<RetrieverResource> retrieverResources;
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用量
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public static class Usage implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** 提示词token数 */
|
||||
private Integer promptTokens;
|
||||
|
||||
/** 提示词单位价格 */
|
||||
private String promptUnitPrice;
|
||||
|
||||
/** 提示词价格单位 */
|
||||
private String promptPriceUnit;
|
||||
|
||||
/** 提示词价格 */
|
||||
private String promptPrice;
|
||||
|
||||
/** 完成token数 */
|
||||
private Integer completionTokens;
|
||||
|
||||
/** 完成单位价格 */
|
||||
private String completionUnitPrice;
|
||||
|
||||
/** 完成价格单位 */
|
||||
private String completionPriceUnit;
|
||||
|
||||
/** 完成价格 */
|
||||
private String completionPrice;
|
||||
|
||||
/** 总token数 */
|
||||
private Integer totalTokens;
|
||||
|
||||
/** 总价格 */
|
||||
private String totalPrice;
|
||||
|
||||
/** 货币 */
|
||||
private String currency;
|
||||
|
||||
/** 延迟(毫秒) */
|
||||
private Double latency;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检索器资源
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public static class RetrieverResource implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** 位置 */
|
||||
private Integer position;
|
||||
|
||||
/** 数据集ID */
|
||||
private String datasetId;
|
||||
|
||||
/** 数据集名称 */
|
||||
private String datasetName;
|
||||
|
||||
/** 文档ID */
|
||||
private String documentId;
|
||||
|
||||
/** 文档名称 */
|
||||
private String documentName;
|
||||
|
||||
/** 分段ID */
|
||||
private String segmentId;
|
||||
|
||||
/** 分数 */
|
||||
private Double score;
|
||||
|
||||
/** 内容 */
|
||||
private String content;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.senmesh.sdk.dify.dto;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.Serializable;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* Dify文件上传请求
|
||||
*
|
||||
* @author sr
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class DifyFileUploadRequest implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** 文件输入流 */
|
||||
private InputStream inputStream;
|
||||
|
||||
/** 文件名 */
|
||||
private String fileName;
|
||||
|
||||
/** 文件MIME类型 */
|
||||
private String contentType;
|
||||
|
||||
/** 用户标识 */
|
||||
private String user;
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.senmesh.sdk.dify.dto;
|
||||
|
||||
import java.io.Serializable;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* Dify文件上传响应
|
||||
*
|
||||
* @author sr
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class DifyFileUploadResponse implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** 文件ID */
|
||||
private String id;
|
||||
|
||||
/** 文件名 */
|
||||
private String name;
|
||||
|
||||
/** 文件大小 */
|
||||
private Long size;
|
||||
|
||||
/** 文件扩展名 */
|
||||
private String extension;
|
||||
|
||||
/** MIME类型 */
|
||||
private String mimeType;
|
||||
|
||||
/** 创建者 */
|
||||
private String createdBy;
|
||||
|
||||
/** 创建时间 */
|
||||
private Long createdAt;
|
||||
|
||||
}
|
||||
|
||||
206
src/main/java/com/senmesh/sdk/dify/service/DifyClient.java
Normal file
206
src/main/java/com/senmesh/sdk/dify/service/DifyClient.java
Normal file
@@ -0,0 +1,206 @@
|
||||
package com.senmesh.sdk.dify.service;
|
||||
|
||||
import com.senmesh.common.AlitaResult;
|
||||
import com.senmesh.common.util.JsonUtils;
|
||||
import com.senmesh.sdk.common.constant.SdkConstants;
|
||||
import com.senmesh.sdk.common.util.HttpUtils;
|
||||
import com.senmesh.sdk.dify.config.DifyProperties;
|
||||
import com.senmesh.sdk.dify.dto.DifyChatRequest;
|
||||
import com.senmesh.sdk.dify.dto.DifyChatResponse;
|
||||
import com.senmesh.sdk.dify.dto.DifyFileUploadRequest;
|
||||
import com.senmesh.sdk.dify.dto.DifyFileUploadResponse;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.io.ByteArrayResource;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
/**
|
||||
* Dify客户端
|
||||
*
|
||||
* @author sr
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class DifyClient {
|
||||
|
||||
private static DifyProperties difyProperties;
|
||||
|
||||
private static final String FILE_UPLOAD_URI = "/v1/files/upload";
|
||||
private static final String CHAT_MESSAGES_URI = "/v1/chat-messages";
|
||||
|
||||
/**
|
||||
* 构造方法 - 用于Spring注入,同时设置静态变量
|
||||
*/
|
||||
@Autowired
|
||||
public DifyClient(DifyProperties difyProperties) {
|
||||
DifyClient.difyProperties = difyProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传文件到Dify(静态方法 - 只传apiKey)
|
||||
*
|
||||
* @param apiKey API密钥
|
||||
* @param request 文件上传请求
|
||||
* @return 上传结果
|
||||
*/
|
||||
public static AlitaResult<DifyFileUploadResponse> uploadFile(String apiKey, DifyFileUploadRequest request) {
|
||||
if (difyProperties == null) {
|
||||
return AlitaResult.error(SdkConstants.SDK_INVOKE_NOT_REACH_CODE, "DifyProperties未初始化");
|
||||
}
|
||||
return uploadFile(difyProperties.getHost(), apiKey, request);
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传文件到Dify(静态方法)
|
||||
*
|
||||
* @param host Dify服务地址
|
||||
* @param apiKey API密钥
|
||||
* @param request 文件上传请求
|
||||
* @return 上传结果
|
||||
*/
|
||||
public static AlitaResult<DifyFileUploadResponse> uploadFile(String host, String apiKey, DifyFileUploadRequest request) {
|
||||
try {
|
||||
// 参数校验
|
||||
if (StringUtils.isBlank(host)) {
|
||||
return AlitaResult.error(SdkConstants.SDK_INVOKE_NOT_REACH_CODE, "Dify服务地址不能为空");
|
||||
}
|
||||
if (StringUtils.isBlank(apiKey)) {
|
||||
return AlitaResult.error(SdkConstants.SDK_INVOKE_NOT_REACH_CODE, "API密钥不能为空");
|
||||
}
|
||||
if (request == null || request.getInputStream() == null) {
|
||||
return AlitaResult.error(SdkConstants.SDK_INVOKE_NOT_REACH_CODE, "文件输入流不能为空");
|
||||
}
|
||||
if (StringUtils.isBlank(request.getFileName())) {
|
||||
return AlitaResult.error(SdkConstants.SDK_INVOKE_NOT_REACH_CODE, "文件名不能为空");
|
||||
}
|
||||
if (StringUtils.isBlank(request.getUser())) {
|
||||
return AlitaResult.error(SdkConstants.SDK_INVOKE_NOT_REACH_CODE, "用户标识不能为空");
|
||||
}
|
||||
|
||||
String url = host + FILE_UPLOAD_URI;
|
||||
|
||||
// 构建请求头
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
|
||||
headers.set("Authorization", "Bearer " + apiKey);
|
||||
|
||||
// 构建请求体
|
||||
MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
|
||||
|
||||
// 将InputStream转换为ByteArrayResource
|
||||
byte[] fileBytes = org.springframework.util.StreamUtils.copyToByteArray(request.getInputStream());
|
||||
ByteArrayResource fileResource = new ByteArrayResource(fileBytes) {
|
||||
@Override
|
||||
public String getFilename() {
|
||||
return request.getFileName();
|
||||
}
|
||||
};
|
||||
|
||||
body.add("file", fileResource);
|
||||
body.add("user", request.getUser());
|
||||
|
||||
HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(body, headers);
|
||||
|
||||
log.info("dify uploadFile request url:{}, user:{}, fileName:{}",
|
||||
url, request.getUser(), request.getFileName());
|
||||
|
||||
// 发送请求
|
||||
ResponseEntity<DifyFileUploadResponse> response = HttpUtils.restTemplate.exchange(
|
||||
url,
|
||||
HttpMethod.POST,
|
||||
requestEntity,
|
||||
DifyFileUploadResponse.class
|
||||
);
|
||||
|
||||
DifyFileUploadResponse result = response.getBody();
|
||||
log.info("dify uploadFile response:{}", JsonUtils.toJsonString(result));
|
||||
|
||||
return AlitaResult.success(result);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("dify uploadFile error:", e);
|
||||
return AlitaResult.error(SdkConstants.SDK_HTTP_INVOKE_ERROR_CODE, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送对话消息(静态方法 - 只传apiKey)
|
||||
*
|
||||
* @param apiKey API密钥
|
||||
* @param request 对话请求
|
||||
* @return 对话结果
|
||||
*/
|
||||
public static AlitaResult<DifyChatResponse> chatMessages(String apiKey, DifyChatRequest request) {
|
||||
if (difyProperties == null) {
|
||||
return AlitaResult.error(SdkConstants.SDK_INVOKE_NOT_REACH_CODE, "DifyProperties未初始化");
|
||||
}
|
||||
return chatMessages(difyProperties.getHost(), apiKey, request);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送对话消息(静态方法)
|
||||
*
|
||||
* @param host Dify服务地址
|
||||
* @param apiKey API密钥
|
||||
* @param request 对话请求
|
||||
* @return 对话结果
|
||||
*/
|
||||
public static AlitaResult<DifyChatResponse> chatMessages(String host, String apiKey, DifyChatRequest request) {
|
||||
try {
|
||||
// 参数校验
|
||||
if (StringUtils.isBlank(host)) {
|
||||
return AlitaResult.error(SdkConstants.SDK_INVOKE_NOT_REACH_CODE, "Dify服务地址不能为空");
|
||||
}
|
||||
if (StringUtils.isBlank(apiKey)) {
|
||||
return AlitaResult.error(SdkConstants.SDK_INVOKE_NOT_REACH_CODE, "API密钥不能为空");
|
||||
}
|
||||
if (request == null) {
|
||||
return AlitaResult.error(SdkConstants.SDK_INVOKE_NOT_REACH_CODE, "请求参数不能为空");
|
||||
}
|
||||
if (StringUtils.isBlank(request.getQuery())) {
|
||||
return AlitaResult.error(SdkConstants.SDK_INVOKE_NOT_REACH_CODE, "查询内容不能为空");
|
||||
}
|
||||
if (StringUtils.isBlank(request.getUser())) {
|
||||
return AlitaResult.error(SdkConstants.SDK_INVOKE_NOT_REACH_CODE, "用户标识不能为空");
|
||||
}
|
||||
|
||||
String url = host + CHAT_MESSAGES_URI;
|
||||
|
||||
// 构建请求头
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
headers.set("Authorization", "Bearer " + apiKey);
|
||||
|
||||
HttpEntity<DifyChatRequest> requestEntity = new HttpEntity<>(request, headers);
|
||||
|
||||
log.info("dify chatMessages request url:{}, body:{}", url, JsonUtils.toJsonString(request));
|
||||
|
||||
// 发送请求
|
||||
ResponseEntity<DifyChatResponse> response = HttpUtils.restTemplate.exchange(
|
||||
url,
|
||||
HttpMethod.POST,
|
||||
requestEntity,
|
||||
DifyChatResponse.class
|
||||
);
|
||||
|
||||
DifyChatResponse result = response.getBody();
|
||||
log.info("dify chatMessages response:{}", JsonUtils.toJsonString(result));
|
||||
|
||||
return AlitaResult.success(result);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("dify chatMessages error:", e);
|
||||
return AlitaResult.error(SdkConstants.SDK_HTTP_INVOKE_ERROR_CODE, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user