初始化
This commit is contained in:
128
easyflow-commons/easyflow-common-ai/pom.xml
Normal file
128
easyflow-commons/easyflow-common-ai/pom.xml
Normal file
@@ -0,0 +1,128 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>tech.easyflow</groupId>
|
||||
<artifactId>easyflow-commons</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
|
||||
<name>easyflow-common-ai</name>
|
||||
<artifactId>easyflow-common-ai</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.commonmark</groupId>
|
||||
<artifactId>commonmark</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.jsoup</groupId>
|
||||
<artifactId>jsoup</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-aop</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-autoconfigure</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.github.jsqlparser</groupId>
|
||||
<artifactId>jsqlparser</artifactId>
|
||||
<version>4.9</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.easyagents</groupId>
|
||||
<artifactId>easy-agents-bom</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>com.squareup.okhttp3</groupId>
|
||||
<artifactId>okhttp</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>com.squareup.okhttp</groupId>
|
||||
<artifactId>okhttp</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>com.squareup.okio</groupId>
|
||||
<artifactId>okio</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-stdlib</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.apache.poi</groupId>
|
||||
<artifactId>ooxml-schemas</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents.client5</groupId>
|
||||
<artifactId>httpclient5</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>javax.xml.bind</groupId>
|
||||
<artifactId>jaxb-api</artifactId>
|
||||
<version>2.3.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.squareup.okhttp3</groupId>
|
||||
<artifactId>okhttp</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>tech.easyflow</groupId>
|
||||
<artifactId>easyflow-common-base</artifactId>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>tech.easyflow</groupId>
|
||||
<artifactId>easyflow-common-options</artifactId>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-http</artifactId>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-json</artifactId>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.idev.excel</groupId>
|
||||
<artifactId>fastexcel</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,55 @@
|
||||
package tech.easyflow.common.ai;
|
||||
|
||||
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* 聊天专用SSE发射器
|
||||
*/
|
||||
public class ChatSseEmitter extends SseEmitter {
|
||||
|
||||
// 默认超时时间:30分钟
|
||||
public static final long DEFAULT_TIMEOUT = 5 * 60 * 1000L;
|
||||
|
||||
public ChatSseEmitter() {
|
||||
super(DEFAULT_TIMEOUT);
|
||||
// 注册基础的超时/异常回调(避免连接泄漏)
|
||||
registerBasicCallbacks();
|
||||
}
|
||||
|
||||
public ChatSseEmitter(long timeout) {
|
||||
super(timeout);
|
||||
registerBasicCallbacks();
|
||||
}
|
||||
|
||||
/**
|
||||
* 基础回调:仅做简单的连接清理
|
||||
*/
|
||||
private void registerBasicCallbacks() {
|
||||
// 超时回调
|
||||
onTimeout(this::complete);
|
||||
// 异常回调
|
||||
onError(e -> complete());
|
||||
}
|
||||
|
||||
public static ChatSseEmitter create() {
|
||||
return new ChatSseEmitter();
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送普通消息(最常用的核心方法)
|
||||
* @param message 消息内容
|
||||
* @throws IOException 发送失败时抛出IO异常
|
||||
*/
|
||||
public void sendMessage(String message) throws IOException {
|
||||
// 封装标准的SSE消息格式
|
||||
send(SseEmitter.event().name("message").data(message));
|
||||
}
|
||||
|
||||
/**
|
||||
* 快速创建发射器
|
||||
*/
|
||||
public ChatSseEmitter createChatSseEmitter() {
|
||||
return new ChatSseEmitter();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package tech.easyflow.common.ai.inteceptor;
|
||||
|
||||
import com.easyagents.core.model.chat.tool.GlobalToolInterceptors;
|
||||
import com.easyagents.core.model.chat.tool.ToolChain;
|
||||
import com.easyagents.core.model.chat.tool.ToolContext;
|
||||
import com.easyagents.core.model.chat.tool.ToolInterceptor;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import java.util.Map;
|
||||
@Component
|
||||
public class ToolLoggingInterceptor implements ToolInterceptor {
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
GlobalToolInterceptors.addInterceptor(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object intercept(ToolContext context, ToolChain chain) throws Exception {
|
||||
String toolName = context.getTool().getName();
|
||||
Map<String, Object> args = context.getArgsMap();
|
||||
|
||||
System.out.println("▶ 调用工具: " + toolName + ", 参数: " + args);
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
try {
|
||||
Object result = chain.proceed(context);
|
||||
System.out.println("✅ 工具返回: " + result);
|
||||
return result;
|
||||
} finally {
|
||||
long duration = System.currentTimeMillis() - start;
|
||||
System.out.println("⏱️ 耗时: " + duration + "ms");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package tech.easyflow.common.ai.plugin;
|
||||
import java.util.*;
|
||||
|
||||
public class NestedParamConverter {
|
||||
|
||||
public static Map<String, Object> convertToNestedParamMap(List<PluginParam> pluginParams) {
|
||||
Map<String, Object> result = new LinkedHashMap<>();
|
||||
if (pluginParams == null || pluginParams.isEmpty()) return result;
|
||||
|
||||
for (PluginParam param : pluginParams) {
|
||||
if (!param.isEnabled()) continue;
|
||||
result.put(param.getName(), buildValue(param));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static Object buildValue(PluginParam param) {
|
||||
if ("String".equalsIgnoreCase(param.getType())) {
|
||||
return param.getDefaultValue();
|
||||
} else if ("Object".equalsIgnoreCase(param.getType())) {
|
||||
Map<String, Object> objMap = new LinkedHashMap<>();
|
||||
if (param.getChildren() != null && !param.getChildren().isEmpty()) {
|
||||
for (PluginParam child : param.getChildren()) {
|
||||
objMap.put(child.getName(), buildValue(child));
|
||||
}
|
||||
}
|
||||
return objMap;
|
||||
} else if ("Array".equalsIgnoreCase(param.getType())) {
|
||||
if (param.getChildren() != null && !param.getChildren().isEmpty()) {
|
||||
PluginParam arrayItemTemplate = param.getChildren().get(0);
|
||||
if ("Array[Object]".equalsIgnoreCase(arrayItemTemplate.getType())) {
|
||||
List<Map<String, Object>> list = new ArrayList<>();
|
||||
|
||||
Map<String, Object> item = new LinkedHashMap<>();
|
||||
for (PluginParam child : arrayItemTemplate.getChildren()) {
|
||||
item.put(child.getName(), buildValue(child));
|
||||
}
|
||||
|
||||
list.add(item); // 示例中只添加一个对象
|
||||
return list;
|
||||
}
|
||||
}
|
||||
return Collections.EMPTY_LIST;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
package tech.easyflow.common.ai.plugin;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.http.*;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
public class PluginHttpClient {
|
||||
|
||||
private static final int TIMEOUT = 10_000;
|
||||
|
||||
public static JSONObject sendRequest(String url, String method,
|
||||
Map<String, Object> headers,
|
||||
List<PluginParam> pluginParams) {
|
||||
// 1. 处理路径参数
|
||||
String processedUrl = replacePathVariables(url, pluginParams);
|
||||
|
||||
// 2. 初始化请求
|
||||
Method httpMethod = Method.valueOf(method.toUpperCase());
|
||||
HttpRequest request = HttpRequest.of(processedUrl)
|
||||
.method(httpMethod)
|
||||
.timeout(TIMEOUT);
|
||||
// 3. 处理请求头(合并默认头和参数头)
|
||||
processHeaders(request, headers, pluginParams);
|
||||
|
||||
// 4. 处理查询参数和请求体
|
||||
processQueryAndBodyParams(request, httpMethod, pluginParams);
|
||||
|
||||
// 5. 执行请求
|
||||
HttpResponse response = request.execute();
|
||||
return JSONUtil.parseObj(response.body());
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理请求头(合并默认头和参数头)
|
||||
*/
|
||||
private static void processHeaders(HttpRequest request,
|
||||
Map<String, Object> defaultHeaders,
|
||||
List<PluginParam> params) {
|
||||
// 添加默认头
|
||||
if (ObjectUtil.isNotEmpty(defaultHeaders)) {
|
||||
defaultHeaders.forEach((k, v) -> request.header(k, v.toString()));
|
||||
}
|
||||
|
||||
// 添加参数中指定的头
|
||||
params.stream()
|
||||
.filter(p -> "header".equalsIgnoreCase(p.getMethod()) && p.isEnabled())
|
||||
.forEach(p -> request.header(p.getName(), p.getDefaultValue().toString()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理查询参数和请求体
|
||||
*/
|
||||
/**
|
||||
* 处理查询参数和请求体(新增文件参数支持)
|
||||
*/
|
||||
private static void processQueryAndBodyParams(HttpRequest request,
|
||||
Method httpMethod,
|
||||
List<PluginParam> params) {
|
||||
Map<String, Object> queryParams = new HashMap<>();
|
||||
Map<String, Object> bodyParams = new HashMap<>();
|
||||
// 标记是否包含文件参数
|
||||
AtomicBoolean hasMultipartFile = new AtomicBoolean(false);
|
||||
|
||||
// 分类参数(同时检测是否有文件)
|
||||
params.stream()
|
||||
.filter(PluginParam::isEnabled)
|
||||
.forEach(p -> {
|
||||
String methodType = p.getMethod().toLowerCase();
|
||||
Object paramValue = buildNestedParamValue(p);
|
||||
|
||||
// 检测是否为文件参数(MultipartFile 类型)
|
||||
if (paramValue instanceof org.springframework.web.multipart.MultipartFile) {
|
||||
hasMultipartFile.set(true);
|
||||
}
|
||||
|
||||
switch (methodType) {
|
||||
case "query":
|
||||
queryParams.put(p.getName(), paramValue);
|
||||
break;
|
||||
case "body":
|
||||
bodyParams.put(p.getName(), paramValue);
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
// 1. 设置查询参数(原有逻辑不变)
|
||||
if (!queryParams.isEmpty()) {
|
||||
request.form(queryParams);
|
||||
}
|
||||
|
||||
// 2. 设置请求体(分两种情况:有文件 vs 无文件)
|
||||
if (!bodyParams.isEmpty() && (httpMethod == Method.POST || httpMethod == Method.PUT)) {
|
||||
if (hasMultipartFile.get()) {
|
||||
// 2.1 包含文件参数 → 用 multipart/form-data 格式
|
||||
processMultipartBody(request, bodyParams);
|
||||
} else {
|
||||
// 2.2 无文件参数 → 保持原有 JSON 格式
|
||||
request.body(JSONUtil.toJsonStr(bodyParams))
|
||||
.header(Header.CONTENT_TYPE, ContentType.JSON.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归构建嵌套参数值
|
||||
* @param param 当前参数
|
||||
* @return 如果是 Object 类型,返回 Map;否则返回 defaultValue
|
||||
*/
|
||||
private static Object buildNestedParamValue(PluginParam param) {
|
||||
// 如果不是 Object 类型,直接返回默认值
|
||||
if (!"Object".equalsIgnoreCase(param.getType())) {
|
||||
return param.getDefaultValue();
|
||||
}
|
||||
|
||||
// 如果是 Object 类型,递归处理子参数
|
||||
Map<String, Object> nestedParams = new HashMap<>();
|
||||
if (param.getChildren() != null) {
|
||||
param.getChildren().stream()
|
||||
.filter(PluginParam::isEnabled)
|
||||
.forEach(child -> {
|
||||
Object childValue = buildNestedParamValue(child); // 递归处理子参数
|
||||
nestedParams.put(child.getName(), childValue);
|
||||
});
|
||||
}
|
||||
return nestedParams;
|
||||
}
|
||||
|
||||
/**
|
||||
* 替换URL中的路径变量 {xxx}
|
||||
*/
|
||||
private static String replacePathVariables(String url, List<PluginParam> params) {
|
||||
String result = url;
|
||||
|
||||
// 收集路径参数
|
||||
Map<String, Object> pathParams = new HashMap<>();
|
||||
params.stream()
|
||||
.filter(p -> "path".equalsIgnoreCase(p.getMethod()) && p.isEnabled())
|
||||
.forEach(p -> pathParams.put(p.getName(), p.getDefaultValue()));
|
||||
|
||||
// 替换变量
|
||||
for (Map.Entry<String, Object> entry : pathParams.entrySet()) {
|
||||
result = result.replaceAll("\\{" + entry.getKey() + "\\}",
|
||||
entry.getValue().toString());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void processMultipartBody(HttpRequest request, Map<String, Object> bodyParams) {
|
||||
// 手动设置 Content-Type 为 multipart/form-data
|
||||
request.header(Header.CONTENT_TYPE, "multipart/form-data");
|
||||
for (Map.Entry<String, Object> entry : bodyParams.entrySet()) {
|
||||
String paramName = entry.getKey();
|
||||
Object paramValue = entry.getValue();
|
||||
|
||||
if (paramValue instanceof MultipartFile) {
|
||||
MultipartFile file = (MultipartFile) paramValue;
|
||||
try {
|
||||
request.form(paramName, file.getBytes(), file.getOriginalFilename());
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(String.format("文件参数处理失败:参数名=%s,文件名=%s",
|
||||
paramName, file.getOriginalFilename()), e);
|
||||
}
|
||||
} else {
|
||||
// 处理普通参数
|
||||
String valueStr;
|
||||
if (paramValue instanceof String || paramValue instanceof Number || paramValue instanceof Boolean) {
|
||||
valueStr = paramValue.toString();
|
||||
} else {
|
||||
valueStr = JSONUtil.toJsonStr(paramValue);
|
||||
}
|
||||
request.form(paramName, valueStr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
package tech.easyflow.common.ai.plugin;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class PluginParam {
|
||||
private String name;
|
||||
private String description;
|
||||
private String type;
|
||||
private String method; // Query / Body / Header / PathVariable 等
|
||||
private Object defaultValue;
|
||||
private boolean required;
|
||||
private boolean enabled;
|
||||
private String key;
|
||||
private List<PluginParam> children;
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getMethod() {
|
||||
return method;
|
||||
}
|
||||
|
||||
public void setMethod(String method) {
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
public Object getDefaultValue() {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
public void setDefaultValue(Object defaultValue) {
|
||||
this.defaultValue = defaultValue;
|
||||
}
|
||||
|
||||
public boolean isRequired() {
|
||||
return required;
|
||||
}
|
||||
|
||||
public void setRequired(boolean required) {
|
||||
this.required = required;
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
public void setKey(String key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public List<PluginParam> getChildren() {
|
||||
return children;
|
||||
}
|
||||
|
||||
public void setChildren(List<PluginParam> children) {
|
||||
this.children = children;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package tech.easyflow.common.ai.plugin;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class PluginParamConverter {
|
||||
|
||||
private static final ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
/**
|
||||
* 将JSON字符串转换为PluginParam对象列表
|
||||
* @param jsonStr 数据库中的JSON字符串
|
||||
* @return PluginParam对象列表
|
||||
*/
|
||||
public static List<PluginParam> convertFromJson(String jsonStr) {
|
||||
try {
|
||||
// 将JSON字符串解析为List<Map>结构
|
||||
List<Map<String, Object>> paramMaps = objectMapper.readValue(
|
||||
jsonStr,
|
||||
new TypeReference<List<Map<String, Object>>>(){}
|
||||
);
|
||||
|
||||
List<PluginParam> result = new ArrayList<>();
|
||||
for (Map<String, Object> paramMap : paramMaps) {
|
||||
result.add(convertMapToPluginParam(paramMap));
|
||||
}
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to convert JSON to PluginParam", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归将Map转换为PluginParam对象
|
||||
*/
|
||||
private static PluginParam convertMapToPluginParam(Map<String, Object> map) {
|
||||
PluginParam param = new PluginParam();
|
||||
param.setKey(getStringValue(map, "key"));
|
||||
param.setName(getStringValue(map, "name"));
|
||||
param.setDescription(getStringValue(map, "description"));
|
||||
param.setType(getStringValue(map, "type"));
|
||||
param.setMethod(getStringValue(map, "method"));
|
||||
param.setDefaultValue(map.get("defaultValue"));
|
||||
param.setRequired(getBooleanValue(map, "required"));
|
||||
param.setEnabled(getBooleanValue(map, "enabled"));
|
||||
|
||||
// 处理子节点
|
||||
if (map.containsKey("children")) {
|
||||
Object childrenObj = map.get("children");
|
||||
if (childrenObj instanceof List) {
|
||||
List<Map<String, Object>> childrenMaps = (List<Map<String, Object>>) childrenObj;
|
||||
List<PluginParam> children = new ArrayList<>();
|
||||
for (Map<String, Object> childMap : childrenMaps) {
|
||||
children.add(convertMapToPluginParam(childMap));
|
||||
}
|
||||
param.setChildren(children);
|
||||
}
|
||||
}
|
||||
|
||||
return param;
|
||||
}
|
||||
|
||||
private static String getStringValue(Map<String, Object> map, String key) {
|
||||
Object value = map.get(key);
|
||||
return value != null ? value.toString() : null;
|
||||
}
|
||||
|
||||
private static boolean getBooleanValue(Map<String, Object> map, String key) {
|
||||
Object value = map.get(key);
|
||||
if (value instanceof Boolean) {
|
||||
return (Boolean) value;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
package tech.easyflow.common.ai.rag;
|
||||
|
||||
import com.easyagents.core.document.Document;
|
||||
import com.easyagents.core.document.DocumentSplitter;
|
||||
import com.easyagents.core.document.id.DocumentIdGenerator;
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.TypeReference;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class ExcelDocumentSplitter implements DocumentSplitter {
|
||||
private int rowsPerChunk;
|
||||
private boolean includeHeader;
|
||||
|
||||
public ExcelDocumentSplitter(int rowsPerChunk) {
|
||||
if (rowsPerChunk <= 0) {
|
||||
throw new IllegalArgumentException("rows must be greater than 0");
|
||||
}
|
||||
this.rowsPerChunk = rowsPerChunk;
|
||||
}
|
||||
|
||||
public ExcelDocumentSplitter(int rowsPerChunk, boolean includeHeader) {
|
||||
if (rowsPerChunk <= 0) {
|
||||
throw new IllegalArgumentException("rows must be greater than 0");
|
||||
}
|
||||
this.rowsPerChunk = rowsPerChunk;
|
||||
this.includeHeader = includeHeader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Document> split(Document document, DocumentIdGenerator idGenerator) {
|
||||
if (document == null || document.getContent() == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
// 解析JSON数据为表格结构
|
||||
List<List<String>> tableData = JSON.parseObject(document.getContent(),
|
||||
new TypeReference<List<List<String>>>() {});
|
||||
|
||||
if (tableData == null || tableData.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<Document> chunks = new ArrayList<>();
|
||||
List<String> headers = includeHeader ? tableData.get(0) : null;
|
||||
int startRow = includeHeader ? 1 : 0;
|
||||
|
||||
// 按照指定行数分割数据
|
||||
for (int i = startRow; i < tableData.size(); i += rowsPerChunk) {
|
||||
int endRow = Math.min(i + rowsPerChunk, tableData.size());
|
||||
|
||||
// 构建当前分块的Markdown表格
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
// 添加表头(如果包含)
|
||||
if (headers != null) {
|
||||
sb.append("| ").append(String.join(" | ", headers)).append(" |\n");
|
||||
sb.append("|");
|
||||
for (int j = 0; j < headers.size(); j++) {
|
||||
sb.append(" --- |");
|
||||
}
|
||||
sb.append("\n");
|
||||
}
|
||||
|
||||
// 添加数据行
|
||||
for (int j = i; j < endRow; j++) {
|
||||
List<String> row = tableData.get(j);
|
||||
sb.append("| ").append(String.join(" | ", row)).append(" |\n");
|
||||
}
|
||||
|
||||
// 创建新文档
|
||||
Document newDocument = new Document();
|
||||
newDocument.addMetadata(document.getMetadataMap());
|
||||
newDocument.setContent(sb.toString());
|
||||
|
||||
if (idGenerator != null) {
|
||||
newDocument.setId(idGenerator.generateId(newDocument));
|
||||
}
|
||||
|
||||
chunks.add(newDocument);
|
||||
}
|
||||
|
||||
return chunks;
|
||||
}
|
||||
|
||||
public int getRowsPerChunk() {
|
||||
return rowsPerChunk;
|
||||
}
|
||||
|
||||
public void setRowsPerChunk(int rowsPerChunk) {
|
||||
this.rowsPerChunk = rowsPerChunk;
|
||||
}
|
||||
|
||||
public boolean isIncludeHeader() {
|
||||
return includeHeader;
|
||||
}
|
||||
|
||||
public void setIncludeHeader(boolean includeHeader) {
|
||||
this.includeHeader = includeHeader;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user