聊聊Spring AI Alibaba的YuQueDocumentReader

本文主要研究一下Spring AI Alibaba的YuQueDocumentReader

YuQueDocumentReader

community/document-readers/spring-ai-alibaba-starter-document-reader-yuque/src/main/java/com/alibaba/cloud/ai/reader/yuque/YuQueDocumentReader.java

public class YuQueDocumentReader implements DocumentReader {private final DocumentParser parser;private final YuQueResource yuQueResource;public YuQueDocumentReader(YuQueResource yuQueResource, DocumentParser parser) {this.yuQueResource = yuQueResource;this.parser = parser;}@Overridepublic List<Document> get() {try {List<Document> documents = parser.parse(yuQueResource.getInputStream());String source = yuQueResource.getResourcePath();for (Document doc : documents) {doc.getMetadata().put(YuQueResource.SOURCE, source);}return documents;}catch (IOException ioException) {throw new RuntimeException("Failed to load document from yuque: {}", ioException);}}}

YuQueDocumentReader构造器要求输入YuQueResource、DocumentParser,其get方法通过DocumentParser解析,最后在其metadata追加一个SOURCE

YuQueResource

community/document-readers/spring-ai-alibaba-starter-document-reader-yuque/src/main/java/com/alibaba/cloud/ai/reader/yuque/YuQueResource.java

public class YuQueResource implements Resource {private static final String BASE_URL = "https://www.yuque.com";private static final String INFO_PATH = "/api/v2/hello";private static final String DOC_DETAIL_PATH = "/api/v2/repos/%s/%s/docs/%s";public static final String SOURCE = "source";public static final String SUPPORT_TYPE = "Doc";private final HttpClient httpClient;private final InputStream inputStream;private final URI uri;private final String resourcePath;private String groupLogin;private String bookSlug;private String id;public YuQueResource(String yuQueToken, String resourcePath) {this.resourcePath = resourcePath;this.httpClient = HttpClient.newBuilder().version(HttpClient.Version.HTTP_2).build();judgePathRule(resourcePath);judgeToken(yuQueToken);URI baseUri = URI.create(BASE_URL + DOC_DETAIL_PATH.formatted(groupLogin, bookSlug, id));HttpRequest httpRequest = HttpRequest.newBuilder().header("X-Auth-Token", yuQueToken).uri(baseUri).GET().build();try {HttpResponse<String> response = this.httpClient.send(httpRequest, HttpResponse.BodyHandlers.ofString());String body = response.body();// Parse the JSON response using JacksonObjectMapper objectMapper = new ObjectMapper();JsonNode jsonObject = objectMapper.readTree(body);JsonNode dataObject = jsonObject.get("data");if (dataObject == null || !dataObject.isObject()) {throw new RuntimeException("Invalid response format: 'data' is not an object");}if (!Objects.equals(dataObject.get("type").asText(), SUPPORT_TYPE)) {throw new RuntimeException("Unsupported resource type, only support " + SUPPORT_TYPE);}inputStream = new ByteArrayInputStream(dataObject.get("body_html").asText().getBytes());uri = URI.create(resourcePath);}catch (Exception e) {throw new RuntimeException(e);}}/*** Judge resource path rule Official online doc* https://www.yuque.com/yuque/developer/openapi* @param resourcePath*/private void judgePathRule(String resourcePath) {// Determine if the path conforms to this format: https://xx.xxx.com/aa/bb/ccString regex = "^https://[a-zA-Z0-9.-]+/([a-zA-Z0-9.-]+)/([a-zA-Z0-9.-]+)/([a-zA-Z0-9.-]+)$";Pattern pattern = Pattern.compile(regex);Matcher matcher = pattern.matcher(resourcePath);Assert.isTrue(matcher.matches(), "Invalid resource path");// Extract the captured groupsthis.groupLogin = matcher.group(1);this.bookSlug = matcher.group(2);this.id = matcher.group(3);Assert.isTrue(StringUtils.hasText(this.groupLogin), "Invalid resource path");Assert.isTrue(StringUtils.hasText(this.bookSlug), "Invalid resource path");Assert.isTrue(StringUtils.hasText(this.id), "Invalid resource path");}/*** judge yuQue token* @param yuQueToken User/Team token*/private void judgeToken(String yuQueToken) {URI uri = URI.create(BASE_URL + INFO_PATH);HttpRequest httpRequest = HttpRequest.newBuilder().header("X-Auth-Token", yuQueToken).uri(uri).GET().build();try {HttpResponse<String> response = this.httpClient.send(httpRequest, HttpResponse.BodyHandlers.ofString());int statusCode = response.statusCode();Assert.isTrue(statusCode == 200, "Failed to auth YuQueToken");}catch (Exception e) {throw new RuntimeException(e);}}//......
}  

YuQueResource的构造器要求输入yuQueToken和resourcePath,它通过httpClient请求https://www.yuque.com/api/v2/repos/{groupLogin}/{bookSlug}/docs/{id},之后解析body_html到inputStream;其中groupLogin、bookSlug、id是judgePathRule通过解析resourcePath提取出来

示例

community/document-readers/spring-ai-alibaba-starter-document-reader-yuque/src/test/java/com/alibaba/cloud/ai/reader/yuque/YuQueDocumentLoaderIT.java

@EnabledIfEnvironmentVariable(named = "YUQUE_TOKEN", matches = ".+")
@EnabledIfEnvironmentVariable(named = "YUQUE_RESOURCE_PATH", matches = ".+")
class YuQueDocumentLoaderIT {private static final String YU_QUE_TOKEN = System.getenv("YUQUE_TOKEN");private static final String RESOURCE_PATH = System.getenv("YUQUE_RESOURCE_PATH");YuQueDocumentReader reader;YuQueResource source;static {if (YU_QUE_TOKEN == null || RESOURCE_PATH == null) {System.out.println("YUQUE_TOKEN or YUQUE_RESOURCE_PATH environment variable is not set. Tests will be skipped.");}}@BeforeEachpublic void beforeEach() {// Skip test if environment variables are not setAssumptions.assumeTrue(YU_QUE_TOKEN != null && !YU_QUE_TOKEN.isEmpty(),"Skipping test because YUQUE_TOKEN is not set");Assumptions.assumeTrue(RESOURCE_PATH != null && !RESOURCE_PATH.isEmpty(),"Skipping test because YUQUE_RESOURCE_PATH is not set");source = YuQueResource.builder().yuQueToken(YU_QUE_TOKEN).resourcePath(RESOURCE_PATH).build();reader = new YuQueDocumentReader(source, new TikaDocumentParser());}@Testpublic void should_load_file() {// Skip test if reader is not initializedAssumptions.assumeTrue(reader != null, "Skipping test because reader is not initialized");List<Document> document = reader.get();String content = document.get(0).getText();System.out.println(content);}}

小结

spring-ai-alibaba-starter-document-reader-yuque提供了YuQueDocumentReader,它通过YuQueResource去请求资源,再通过DocumentParser解析(比如TikaDocumentParser)为Document,最后追加一个SOURCE的metadata。

doc

  • java2ai

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/80666.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

OCR定制识别:解锁文字识别的无限可能

OCR 定制识别是什么&#xff1f; OCR&#xff0c;即光学字符识别&#xff08;Optical Character Recognition&#xff09; &#xff0c;它就像是一个神奇的 “文字翻译器”&#xff0c;能把图片里的文字转化成计算机可编辑的文本。比如&#xff0c;你扫描一份纸质文档成图片&am…

麒麟系统(基于Ubuntu)上使用Qt编译时遇到“type_traits文件未找到”的错误

在麒麟系统&#xff08;基于Ubuntu&#xff09;上使用Qt编译时遇到“type_traits文件未找到”的错误&#xff0c;通常是由于C标准库头文件缺失或项目配置问题导致的。以下是逐步解决方案&#xff1a; 1. 安装C标准库和开发工具 确保系统已安装完整的开发工具链和标准库&#…

服务器上安装node

1.安装 下载安装包 https://nodejs.org/en/download 解压安装包 将安装包上传到/opt/software目录下 cd /opt/software tar -xzvf node-v16.14.2-linux-x64.tar.gz 将解压的文件夹移动到安装目录(/opt/nodejs)下 mv /opt/software/node-v16.14.2-linux-x64 /opt/nodejs …

Vue3 + Vite + TS,使用 ExcelJS导出excel文档,生成水印,添加背景水印,dom转图片,插入图片,全部代码

Vue3 Vite TS,使用 ExcelJS导出excel文档&#xff0c;生成水印&#xff0c;添加背景水印&#xff0c;dom转图片&#xff0c;插入图片&#xff0c;全部代码 ExcelJS生成文档并导出导出表头其他函数 生成水印设置文档的背景水印dom 转图片插入图片全部代码 ExcelJS 读取&#…

devops自动化容器化部署

devops 一、简单案例体验gitlabrunner部署静态文件二、devops企业级部署方案1、流程图2、依赖工具3、流程图4、主机规划5、安装工具软件1、安装git2、安装gitlab3、安装jenkins-server4、安装harbor5、安装web-server&#xff0c;也就是部署服务的机子&#xff0c;需要安装dock…

高级 SQL 技巧:提升数据处理能力的实用方法

在数据驱动的时代,SQL 作为操作和管理关系型数据库的标准语言,其重要性不言而喻。基础的 SQL 语句能满足日常的数据查询需求,但在处理复杂业务逻辑、进行数据分析和优化数据库性能时,就需要掌握一些高级 SQL 技巧。这些技巧不仅能提高查询效率,还能实现复杂的数据处理任务…

21.disql命令登录达梦数据库,查询并操作数据库

目录 1.连接达梦数据库 1.1 windows或linux系统 步骤&#xff08;1&#xff09;&#xff1a;打开终端窗口 步骤&#xff08;2&#xff09;&#xff1a;进入梦数据库安装目录下的 bin 文件夹 步骤&#xff08;3&#xff09;&#xff1a;用disql命令进行登录 1.2 docker部署…

N8N MACOS本地部署流程避坑指南

最近n8n很火&#xff0c;就想在本地部署一个&#xff0c;尝尝鲜&#xff0c;看说明n8n是开源软件&#xff0c;可以在本地部署&#xff0c;于是就尝试部署了下&#xff0c;大概用了1个多小时&#xff0c;把相关的过程记录一下&#xff1a; 1、基础软件包 abcXu-MacBook-m2-Air…

qt之开发大恒usb3.0相机一

1.在大恒相机给的sample里没有看见qt开发的demo. 第一步先运行c sdk中中的demo&#xff0c;看了下代码&#xff0c;大恒使用的UI框架是MFC.然后 vs2022编译。运行结果 第一步&#xff0c;先用qt进行坐下页面布局&#xff0c;如下图&#xff08;保存图片的地方做了些更改&#…

leetcode-枚举

枚举 3200. 三角形的最大高度 题目 给你两个整数 red 和 blue&#xff0c;分别表示红色球和蓝色球的数量。你需要使用这些球来组成一个三角形&#xff0c;满足第 1 行有 1 个球&#xff0c;第 2 行有 2 个球&#xff0c;第 3 行有 3 个球&#xff0c;依此类推。 每一行的球必…

DeepSeek智能时空数据分析(三):专业级地理数据可视化赏析-《杭州市国土空间总体规划(2021-2035年)》

序言&#xff1a;时空数据分析很有用&#xff0c;但是GIS/时空数据库技术门槛太高 时空数据分析在优化业务运营中至关重要&#xff0c;然而&#xff0c;三大挑战仍制约其发展&#xff1a;技术门槛高&#xff0c;需融合GIS理论、SQL开发与时空数据库等多领域知识&#xff1b;空…

如何用WordPress AI插件自动生成SEO文章,提升网站流量?

1. 为什么你需要一个WordPress AI文章生成插件&#xff1f; 每天手动写文章太耗时&#xff1f;SEO优化总是不达标&#xff1f;WordPress AI插件能帮你24小时自动生成原创内容&#xff0c;从关键词挖掘到智能排版&#xff0c;全程无需人工干预。 痛点&#xff1a;手动写作效率低…

鼠标指定范围内随机点击

鼠标指定范围内随机点击 点赞神器 将鼠标移动到相应位置后按F5 F6键&#xff0c;设置点击范围&#xff0c; F8开始&#xff0c;ESC中止。 有些直播有点赞限制&#xff0c;例如某音&#xff0c;每小时限制3千次&#xff0c;可以设置1200毫秒&#xff0c;3000次。 软件截图&#…

数据库设置外键的作用

数据库外键&#xff08;Foreign Key&#xff09;是关系型数据库中用于建立表与表之间关联关系的重要约束&#xff0c;其核心作用是确保数据的一致性、完整性和关联性。以下是外键的主要作用及相关说明&#xff1a; 1. 建立表间关联关系 外键通过引用另一张表的主键&#xff0…

发币流程是什么,需要多少成本?

这是一个专注于Web3相关开发的账号&#xff0c;具体会讲解步骤以及开发方案 偶尔会有科普&#xff0c;有兴趣的可以点右上角关注一下 发币&#xff08;发行数字货币&#xff09;的流程通常涉及技术实现、法律合规、经济模型设计等多个环节&#xff0c;以下是关键步骤的简要说明…

测试常用的Linux系统指令详解

为什么测试工程师需要掌握Linux命令&#xff1f; 在现代软件测试领域&#xff0c;约75%的服务端应用运行在Linux环境中&#xff0c;能够熟练使用Linux命令的测试工程师&#xff0c;其工作效率比仅依赖GUI工具的测试人员高出40%以上。本文将系统介绍测试工作中最实用的Linux命令…

Java学习手册:Web 安全基础

一、常见 Web 安全威胁 在 Web 开发中&#xff0c;安全问题至关重要。以下是一些常见的 Web 安全威胁&#xff1a; 1. SQL 注入 SQL 注入是一种攻击方式&#xff0c;攻击者通过在输入字段中插入恶意的 SQL 代码&#xff0c;从而操纵数据库。例如&#xff0c;假设有一个登录表…

游戏引擎学习第246天:将 Worker 上下文移到主线程创建

回顾并为今天的工作做准备 关于GPU驱动bug的问题&#xff0c;目前本地机器上没有复现。如果有问题&#xff0c;昨天的测试就应该已经暴露出来了。当前演示的是游戏的过场动画&#xff0c;运行正常&#xff0c;使用的是硬件渲染。 之前使用软件渲染时没有遇到太多问题&#xff…

2025.4.26总结

今天把马良老师的《职场十二法则》看完后&#xff0c;感触极大&#xff0c;这们课程就是一场职场启蒙课。 虽然看过不少关于职场的书籍&#xff0c;但大多数是关于职场进阶&#xff0c;方法方面的。并没有解答“面对未来二三十年的职场生涯&#xff0c;我该怎么去看待自己的工…

路由器转发规则设置方法步骤,内网服务器端口怎么让异地连接访问的实现

在路由器上设置端口转发&#xff08;Port Forwarding&#xff09;可以将外部网络流量引导到特定的局域网设备&#xff0c;这对于需要远程访问服务器、摄像头、游戏主机等设备非常有用。 登录路由器管理界面&#xff0c;添加端口转发规则让外网访问内网的实现教程分享。以下是设…