diff --git a/.github/workflows/code-format-check.yml b/.github/workflows/code-format-check.yml
index ef7a56e5e..7ded317b6 100644
--- a/.github/workflows/code-format-check.yml
+++ b/.github/workflows/code-format-check.yml
@@ -4,6 +4,9 @@ on:
pull_request:
branches:
- main
+ push:
+ branches:
+ - main
jobs:
build:
@@ -22,4 +25,4 @@ jobs:
- name: Source code formatting check
run: |
- ./mvnw spring-javaformat:validate -pl spring-ai-alibaba-core
+ ./mvnw spring-javaformat:validate
diff --git a/community/document-parsers/document-parser-apache-pdfbox/pom.xml b/community/document-parsers/document-parser-apache-pdfbox/pom.xml
index 7a1a0b9dc..f36fafad1 100644
--- a/community/document-parsers/document-parser-apache-pdfbox/pom.xml
+++ b/community/document-parsers/document-parser-apache-pdfbox/pom.xml
@@ -25,7 +25,6 @@
17
17
UTF-8
- 2.0.32
@@ -36,15 +35,8 @@
- org.apache.pdfbox
- pdfbox
- ${pdfbox.version}
-
-
- commons-logging
- commons-logging
-
-
+ org.springframework.ai
+ spring-ai-pdf-document-reader
diff --git a/community/document-parsers/document-parser-apache-pdfbox/src/main/java/com/alibaba/cloud/ai/parser/apache/pdfbox/ApachePdfBoxDocumentParser.java b/community/document-parsers/document-parser-apache-pdfbox/src/main/java/com/alibaba/cloud/ai/parser/apache/pdfbox/ApachePdfBoxDocumentParser.java
deleted file mode 100644
index b35696d04..000000000
--- a/community/document-parsers/document-parser-apache-pdfbox/src/main/java/com/alibaba/cloud/ai/parser/apache/pdfbox/ApachePdfBoxDocumentParser.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright 2024-2025 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.alibaba.cloud.ai.parser.apache.pdfbox;
-
-import com.alibaba.cloud.ai.document.DocumentParser;
-import org.apache.pdfbox.pdmodel.PDDocument;
-import org.apache.pdfbox.pdmodel.PDDocumentInformation;
-import org.apache.pdfbox.text.PDFTextStripper;
-import org.springframework.ai.document.Document;
-import org.springframework.util.Assert;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * @author HeYQ
- * @since 2024-12-08 22:34
- */
-
-public class ApachePdfBoxDocumentParser implements DocumentParser {
-
- private final boolean includeMetadata;
-
- public ApachePdfBoxDocumentParser() {
- this(false);
- }
-
- public ApachePdfBoxDocumentParser(boolean includeMetadata) {
- this.includeMetadata = includeMetadata;
- }
-
- @Override
- public List parse(InputStream inputStream) {
- try (PDDocument pdfDocument = PDDocument.load(inputStream)) {
- PDFTextStripper stripper = new PDFTextStripper();
- String text = stripper.getText(pdfDocument);
- Assert.notNull(text, "Text cannot be null");
- return includeMetadata ? Collections.singletonList(new Document(text, toMetadata(pdfDocument)))
- : Collections.singletonList(new Document(text));
- }
- catch (IOException e) {
- throw new RuntimeException(e);
- }
- }
-
- private Map toMetadata(PDDocument pdDocument) {
- PDDocumentInformation documentInformation = pdDocument.getDocumentInformation();
- Map metadata = new HashMap<>();
- for (String metadataKey : documentInformation.getMetadataKeys()) {
- String value = documentInformation.getCustomMetadataValue(metadataKey);
- if (value != null) {
- metadata.put(metadataKey, value);
- }
- }
- return metadata;
- }
-
-}
diff --git a/community/document-parsers/document-parser-apache-pdfbox/src/main/java/com/alibaba/cloud/ai/parser/apache/pdfbox/PagePdfDocumentParser.java b/community/document-parsers/document-parser-apache-pdfbox/src/main/java/com/alibaba/cloud/ai/parser/apache/pdfbox/PagePdfDocumentParser.java
new file mode 100644
index 000000000..030a64199
--- /dev/null
+++ b/community/document-parsers/document-parser-apache-pdfbox/src/main/java/com/alibaba/cloud/ai/parser/apache/pdfbox/PagePdfDocumentParser.java
@@ -0,0 +1,137 @@
+package com.alibaba.cloud.ai.parser.apache.pdfbox;
+
+import java.awt.Rectangle;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import com.alibaba.cloud.ai.document.DocumentParser;
+import org.apache.pdfbox.pdfparser.PDFParser;
+import org.apache.pdfbox.pdmodel.PDDocument;
+import org.apache.pdfbox.pdmodel.PDPage;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.springframework.ai.document.Document;
+import org.springframework.ai.reader.pdf.config.PdfDocumentReaderConfig;
+import org.springframework.ai.reader.pdf.layout.PDFLayoutTextStripperByArea;
+import org.springframework.util.CollectionUtils;
+import org.springframework.util.StringUtils;
+
+/**
+ * Groups the parsed PDF pages into {@link Document}s. You can group one or more pages
+ * into a single output document. Use {@link PdfDocumentReaderConfig} for customization
+ * options. The default configuration is: - pagesPerDocument = 1 - pageTopMargin = 0 -
+ * pageBottomMargin = 0
+ *
+ * @author HeYQ
+ */
+public class PagePdfDocumentParser implements DocumentParser {
+
+ public static final String METADATA_START_PAGE_NUMBER = "page_number";
+
+ public static final String METADATA_END_PAGE_NUMBER = "end_page_number";
+
+ private static final String PDF_PAGE_REGION = "pdfPageRegion";
+
+ private final Logger logger = LoggerFactory.getLogger(getClass());
+
+ private final PdfDocumentReaderConfig config;
+
+ public PagePdfDocumentParser() {
+ this(PdfDocumentReaderConfig.defaultConfig());
+ }
+
+ public PagePdfDocumentParser(PdfDocumentReaderConfig config) {
+ this.config = config;
+ }
+
+ @Override
+ public List parse(InputStream inputStream) {
+
+ List readDocuments = new ArrayList<>();
+ try {
+ var pdfTextStripper = new PDFLayoutTextStripperByArea();
+
+ int pageNumber = 0;
+ int pagesPerDocument = 0;
+ int startPageNumber = pageNumber;
+
+ List pageTextGroupList = new ArrayList<>();
+ PDFParser pdfParser = new PDFParser(new org.apache.pdfbox.io.RandomAccessReadBuffer(inputStream));
+ PDDocument document = pdfParser.parse();
+
+ int totalPages = document.getDocumentCatalog().getPages().getCount();
+ // if less than 10
+ int logFrequency = totalPages > 10 ? totalPages / 10 : 1;
+ // pages, print
+ // each iteration
+ int counter = 0;
+
+ PDPage lastPage = document.getDocumentCatalog().getPages().iterator().next();
+ for (PDPage page : document.getDocumentCatalog().getPages()) {
+ lastPage = page;
+ if (counter % logFrequency == 0 && counter / logFrequency < 10) {
+ logger.info("Processing PDF page: {}", (counter + 1));
+ }
+ counter++;
+
+ pagesPerDocument++;
+
+ if (this.config.pagesPerDocument != PdfDocumentReaderConfig.ALL_PAGES
+ && pagesPerDocument >= this.config.pagesPerDocument) {
+ pagesPerDocument = 0;
+
+ var aggregatedPageTextGroup = pageTextGroupList.stream().collect(Collectors.joining());
+ if (StringUtils.hasText(aggregatedPageTextGroup)) {
+ readDocuments.add(toDocument(aggregatedPageTextGroup, startPageNumber, pageNumber));
+ }
+ pageTextGroupList.clear();
+
+ startPageNumber = pageNumber + 1;
+ }
+ int x0 = (int) page.getMediaBox().getLowerLeftX();
+ int xW = (int) page.getMediaBox().getWidth();
+
+ int y0 = (int) page.getMediaBox().getLowerLeftY() + this.config.pageTopMargin;
+ int yW = (int) page.getMediaBox().getHeight()
+ - (this.config.pageTopMargin + this.config.pageBottomMargin);
+
+ pdfTextStripper.addRegion(PDF_PAGE_REGION, new Rectangle(x0, y0, xW, yW));
+ pdfTextStripper.extractRegions(page);
+ var pageText = pdfTextStripper.getTextForRegion(PDF_PAGE_REGION);
+
+ if (StringUtils.hasText(pageText)) {
+
+ pageText = this.config.pageExtractedTextFormatter.format(pageText, pageNumber);
+
+ pageTextGroupList.add(pageText);
+ }
+ pageNumber++;
+ pdfTextStripper.removeRegion(PDF_PAGE_REGION);
+ }
+ if (!CollectionUtils.isEmpty(pageTextGroupList)) {
+ readDocuments.add(toDocument(pageTextGroupList.stream().collect(Collectors.joining()), startPageNumber,
+ pageNumber));
+ }
+ logger.info("Processing {} pages", totalPages);
+ return readDocuments;
+
+ }
+ catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ protected Document toDocument(String docText, int startPageNumber, int endPageNumber) {
+ Document doc = new Document(docText);
+ doc.getMetadata().put(METADATA_START_PAGE_NUMBER, startPageNumber);
+ if (startPageNumber != endPageNumber) {
+ doc.getMetadata().put(METADATA_END_PAGE_NUMBER, endPageNumber);
+ }
+ return doc;
+ }
+
+}
diff --git a/community/document-parsers/document-parser-apache-pdfbox/src/main/java/com/alibaba/cloud/ai/parser/apache/pdfbox/ParagraphPdfDocumentParser.java b/community/document-parsers/document-parser-apache-pdfbox/src/main/java/com/alibaba/cloud/ai/parser/apache/pdfbox/ParagraphPdfDocumentParser.java
new file mode 100644
index 000000000..193cf5c94
--- /dev/null
+++ b/community/document-parsers/document-parser-apache-pdfbox/src/main/java/com/alibaba/cloud/ai/parser/apache/pdfbox/ParagraphPdfDocumentParser.java
@@ -0,0 +1,205 @@
+package com.alibaba.cloud.ai.parser.apache.pdfbox;
+
+import java.awt.Rectangle;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import com.alibaba.cloud.ai.document.DocumentParser;
+import org.apache.pdfbox.pdfparser.PDFParser;
+import org.apache.pdfbox.pdmodel.PDDocument;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.springframework.ai.document.Document;
+import org.springframework.ai.reader.pdf.config.ParagraphManager;
+import org.springframework.ai.reader.pdf.config.ParagraphManager.Paragraph;
+import org.springframework.ai.reader.pdf.config.PdfDocumentReaderConfig;
+import org.springframework.ai.reader.pdf.layout.PDFLayoutTextStripperByArea;
+import org.springframework.util.CollectionUtils;
+import org.springframework.util.StringUtils;
+
+/**
+ * Uses the PDF catalog (e.g. TOC) information to split the input PDF into text paragraphs
+ * and output a single {@link Document} per paragraph. This class provides methods for
+ * reading and processing PDF documents. It uses the Apache PDFBox library for parsing PDF
+ * content and converting it into text paragraphs. The paragraphs are grouped into
+ * {@link Document} objects.
+ *
+ * @author HeYQ
+ */
+public class ParagraphPdfDocumentParser implements DocumentParser {
+
+ // Constants for metadata keys
+ private static final String METADATA_START_PAGE = "page_number";
+
+ private static final String METADATA_END_PAGE = "end_page_number";
+
+ private static final String METADATA_TITLE = "title";
+
+ private static final String METADATA_LEVEL = "level";
+
+ private static final String METADATA_FILE_NAME = "file_name";
+
+ private final Logger logger = LoggerFactory.getLogger(getClass());
+
+ private final PdfDocumentReaderConfig config;
+
+ /**
+ * Constructs a ParagraphPdfDocumentParser using a resource URL and a configuration.
+ */
+ public ParagraphPdfDocumentParser() {
+ this(PdfDocumentReaderConfig.defaultConfig());
+ }
+
+ /**
+ * Constructs a ParagraphPdfDocumentParser using a resource and a configuration.
+ * @param config The configuration for PDF document processing.
+ */
+ public ParagraphPdfDocumentParser(PdfDocumentReaderConfig config) {
+ this.config = config;
+ }
+
+ /**
+ * Reads and processes the PDF document to extract paragraphs.
+ * @return A list of {@link Document} objects representing paragraphs.
+ */
+ @Override
+ public List parse(InputStream inputStream) {
+
+ try {
+ PDFParser pdfParser = new PDFParser(new org.apache.pdfbox.io.RandomAccessReadBuffer(inputStream));
+ PDDocument pdDocument = pdfParser.parse();
+
+ ParagraphManager paragraphTextExtractor = new ParagraphManager(pdDocument);
+
+ var paragraphs = paragraphTextExtractor.flatten();
+
+ List documents = new ArrayList<>(paragraphs.size());
+
+ if (!CollectionUtils.isEmpty(paragraphs)) {
+ logger.info("Start processing paragraphs from PDF");
+ Iterator itr = paragraphs.iterator();
+
+ var current = itr.next();
+
+ if (!itr.hasNext()) {
+ documents.add(toDocument(current, current, pdDocument));
+ }
+ else {
+ while (itr.hasNext()) {
+ var next = itr.next();
+ Document document = toDocument(current, next, pdDocument);
+ if (document != null && StringUtils.hasText(document.getContent())) {
+ documents.add(toDocument(current, next, pdDocument));
+ }
+ current = next;
+ }
+ }
+ }
+ logger.info("End processing paragraphs from PDF");
+ return documents;
+
+ }
+ catch (IllegalArgumentException iae) {
+ throw iae;
+ }
+ catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ protected Document toDocument(Paragraph from, Paragraph to, PDDocument pdDocument) {
+
+ String docText = this.getTextBetweenParagraphs(from, to, pdDocument);
+
+ if (!StringUtils.hasText(docText)) {
+ return null;
+ }
+
+ Document document = new Document(docText);
+ addMetadata(from, to, document);
+
+ return document;
+ }
+
+ protected void addMetadata(Paragraph from, Paragraph to, Document document) {
+ document.getMetadata().put(METADATA_TITLE, from.title());
+ document.getMetadata().put(METADATA_START_PAGE, from.startPageNumber());
+ document.getMetadata().put(METADATA_END_PAGE, to.startPageNumber());
+ document.getMetadata().put(METADATA_LEVEL, from.level());
+ }
+
+ public String getTextBetweenParagraphs(Paragraph fromParagraph, Paragraph toParagraph, PDDocument pdDocument) {
+
+ // Page started from index 0, while PDFBOx getPage return them from index 1.
+ int startPage = fromParagraph.startPageNumber() - 1;
+ int endPage = toParagraph.startPageNumber() - 1;
+
+ try {
+
+ StringBuilder sb = new StringBuilder();
+
+ var pdfTextStripper = new PDFLayoutTextStripperByArea();
+ pdfTextStripper.setSortByPosition(true);
+
+ for (int pageNumber = startPage; pageNumber <= endPage; pageNumber++) {
+
+ var page = pdDocument.getPage(pageNumber);
+
+ int fromPosition = fromParagraph.position();
+ int toPosition = toParagraph.position();
+
+ if (this.config.reversedParagraphPosition) {
+ fromPosition = (int) (page.getMediaBox().getHeight() - fromPosition);
+ toPosition = (int) (page.getMediaBox().getHeight() - toPosition);
+ }
+
+ int x0 = (int) page.getMediaBox().getLowerLeftX();
+ int xW = (int) page.getMediaBox().getWidth();
+
+ int y0 = (int) page.getMediaBox().getLowerLeftY();
+ int yW = (int) page.getMediaBox().getHeight();
+
+ if (pageNumber == startPage) {
+ y0 = fromPosition;
+ yW = (int) page.getMediaBox().getHeight() - y0;
+ }
+ if (pageNumber == endPage) {
+ yW = toPosition - y0;
+ }
+
+ if ((y0 + yW) == (int) page.getMediaBox().getHeight()) {
+ yW = yW - this.config.pageBottomMargin;
+ }
+
+ if (y0 == 0) {
+ y0 = y0 + this.config.pageTopMargin;
+ yW = yW - this.config.pageTopMargin;
+ }
+
+ pdfTextStripper.addRegion("pdfPageRegion", new Rectangle(x0, y0, xW, yW));
+ pdfTextStripper.extractRegions(page);
+ var text = pdfTextStripper.getTextForRegion("pdfPageRegion");
+ if (StringUtils.hasText(text)) {
+ sb.append(text);
+ }
+ pdfTextStripper.removeRegion("pdfPageRegion");
+
+ }
+
+ String text = sb.toString();
+
+ if (StringUtils.hasText(text)) {
+ text = this.config.pageExtractedTextFormatter.format(text, startPage);
+ }
+
+ return text;
+ }
+ catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+}
diff --git a/community/document-parsers/document-parser-apache-pdfbox/src/test/java/com/alibaba/cloud/ai/parser/apache/pdfbox/ApachePdfBoxDocumentParserTest.java b/community/document-parsers/document-parser-apache-pdfbox/src/test/java/com/alibaba/cloud/ai/parser/apache/pdfbox/ApachePdfBoxDocumentParserTest.java
deleted file mode 100644
index 9facd2157..000000000
--- a/community/document-parsers/document-parser-apache-pdfbox/src/test/java/com/alibaba/cloud/ai/parser/apache/pdfbox/ApachePdfBoxDocumentParserTest.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright 2024-2025 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.alibaba.cloud.ai.parser.apache.pdfbox;
-
-import com.alibaba.cloud.ai.document.DocumentParser;
-import org.junit.jupiter.api.Test;
-import org.springframework.ai.document.Document;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-class ApachePdfBoxDocumentParserTest {
-
- @Test
- void should_parse_pdf_file() {
- try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream("test-file.pdf")) {
- DocumentParser parser = new ApachePdfBoxDocumentParser();
- Document document = parser.parse(inputStream).get(0);
-
- assertThat(document.getContent()).isEqualToIgnoringWhitespace("test content");
- assertThat(document.getMetadata()).isEmpty();
- }
- catch (IOException e) {
- throw new RuntimeException(e);
- }
- }
-
- @Test
- void should_parse_pdf_file_include_metadata() {
- try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream("test-file.pdf")) {
- DocumentParser parser = new ApachePdfBoxDocumentParser(true);
- Document document = parser.parse(inputStream).get(0);
-
- assertThat(document.getContent()).isEqualToIgnoringWhitespace("test content");
- assertThat(document.getMetadata()).containsEntry("Author", "ljuba")
- .containsEntry("Creator", "WPS Writer")
- .containsEntry("CreationDate", "D:20230608171011+15'10'")
- .containsEntry("SourceModified", "D:20230608171011+15'10'");
- }
- catch (IOException e) {
- throw new RuntimeException(e);
- }
- }
-
-}
\ No newline at end of file
diff --git a/community/document-parsers/document-parser-apache-pdfbox/src/test/java/com/alibaba/cloud/ai/parser/apache/pdfbox/PagePdfDocumentParserTests.java b/community/document-parsers/document-parser-apache-pdfbox/src/test/java/com/alibaba/cloud/ai/parser/apache/pdfbox/PagePdfDocumentParserTests.java
new file mode 100644
index 000000000..d004b94f2
--- /dev/null
+++ b/community/document-parsers/document-parser-apache-pdfbox/src/test/java/com/alibaba/cloud/ai/parser/apache/pdfbox/PagePdfDocumentParserTests.java
@@ -0,0 +1,58 @@
+package com.alibaba.cloud.ai.parser.apache.pdfbox;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.junit.jupiter.api.Test;
+
+import org.springframework.ai.document.Document;
+import org.springframework.ai.reader.ExtractedTextFormatter;
+import org.springframework.ai.reader.pdf.config.PdfDocumentReaderConfig;
+import org.springframework.core.io.DefaultResourceLoader;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * @author HeYQ
+ * @since 2024-12-24 00:52
+ */
+class PagePdfDocumentParserTests {
+
+ @Test
+ void classpathRead() throws IOException {
+
+ PagePdfDocumentParser parser = new PagePdfDocumentParser(PdfDocumentReaderConfig.builder()
+ .withPageTopMargin(0)
+ .withPageBottomMargin(0)
+ .withPageExtractedTextFormatter(ExtractedTextFormatter.builder()
+ .withNumberOfTopTextLinesToDelete(0)
+ .withNumberOfBottomTextLinesToDelete(3)
+ .withNumberOfTopPagesToSkipBeforeDelete(0)
+ .build())
+ .withPagesPerDocument(1)
+ .build());
+
+ List docs = parser
+ .parse(new DefaultResourceLoader().getResource("classpath:/sample1.pdf").getInputStream());
+
+ assertThat(docs).hasSize(4);
+
+ String allText = docs.stream().map(Document::getContent).collect(Collectors.joining(System.lineSeparator()));
+ System.out.println(allText);
+
+ // assertThat(allText).doesNotContain(
+ // List.of("Page 1 of 4", "Page 2 of 4", "Page 3 of 4", "Page 4 of 4", "PDF
+ // Bookmark Sample"));
+ }
+
+ @Test
+ void testIndexOutOfBound() throws IOException {
+ var documents = new PagePdfDocumentParser(PdfDocumentReaderConfig.builder()
+ .withPageExtractedTextFormatter(ExtractedTextFormatter.builder().build())
+ .withPagesPerDocument(1)
+ .build()).parse(new DefaultResourceLoader().getResource("classpath:/sample2.pdf").getInputStream());
+
+ assertThat(documents).hasSize(64);
+ }
+
+}
diff --git a/community/document-parsers/document-parser-apache-pdfbox/src/test/java/com/alibaba/cloud/ai/parser/apache/pdfbox/ParagraphPdfDocumentParserTests.java b/community/document-parsers/document-parser-apache-pdfbox/src/test/java/com/alibaba/cloud/ai/parser/apache/pdfbox/ParagraphPdfDocumentParserTests.java
new file mode 100644
index 000000000..a45be2e80
--- /dev/null
+++ b/community/document-parsers/document-parser-apache-pdfbox/src/test/java/com/alibaba/cloud/ai/parser/apache/pdfbox/ParagraphPdfDocumentParserTests.java
@@ -0,0 +1,36 @@
+package com.alibaba.cloud.ai.parser.apache.pdfbox;
+
+import org.junit.jupiter.api.Test;
+
+import org.springframework.ai.reader.ExtractedTextFormatter;
+import org.springframework.ai.reader.pdf.config.PdfDocumentReaderConfig;
+import org.springframework.core.io.DefaultResourceLoader;
+
+import java.io.IOException;
+
+/**
+ * @author HeYQ
+ * @since 2024-12-24 00:52
+ */
+
+public class ParagraphPdfDocumentParserTests {
+
+ @Test
+ public void testPdfWithoutToc() throws IOException {
+
+ String content = new ParagraphPdfDocumentParser(PdfDocumentReaderConfig.builder()
+ .withPageTopMargin(0)
+ .withPageBottomMargin(0)
+ .withPageExtractedTextFormatter(ExtractedTextFormatter.builder()
+ .withNumberOfTopTextLinesToDelete(0)
+ .withNumberOfBottomTextLinesToDelete(3)
+ .withNumberOfTopPagesToSkipBeforeDelete(0)
+ .build())
+ .withPagesPerDocument(1)
+ .build()).parse(new DefaultResourceLoader().getResource("classpath:/sample1.pdf").getInputStream())
+ .get(0)
+ .getContent();
+ System.out.println(content);
+ }
+
+}
diff --git a/community/document-parsers/document-parser-apache-pdfbox/src/test/resources/sample1.pdf b/community/document-parsers/document-parser-apache-pdfbox/src/test/resources/sample1.pdf
new file mode 100644
index 000000000..8efd05c3d
Binary files /dev/null and b/community/document-parsers/document-parser-apache-pdfbox/src/test/resources/sample1.pdf differ
diff --git a/community/document-parsers/document-parser-apache-pdfbox/src/test/resources/sample2.pdf b/community/document-parsers/document-parser-apache-pdfbox/src/test/resources/sample2.pdf
new file mode 100644
index 000000000..c6657bf05
Binary files /dev/null and b/community/document-parsers/document-parser-apache-pdfbox/src/test/resources/sample2.pdf differ
diff --git a/community/document-parsers/document-parser-markdown/src/main/java/com/alibaba/cloud/ai/parser/markdown/MarkdownDocumentParser.java b/community/document-parsers/document-parser-markdown/src/main/java/com/alibaba/cloud/ai/parser/markdown/MarkdownDocumentParser.java
index 08af6a553..887182aee 100644
--- a/community/document-parsers/document-parser-markdown/src/main/java/com/alibaba/cloud/ai/parser/markdown/MarkdownDocumentParser.java
+++ b/community/document-parsers/document-parser-markdown/src/main/java/com/alibaba/cloud/ai/parser/markdown/MarkdownDocumentParser.java
@@ -147,14 +147,14 @@ public void visit(BlockQuote blockQuote) {
}
translateLineBreakToSpace();
- this.currentDocumentBuilder.withMetadata("category", "blockquote");
+ this.currentDocumentBuilder.metadata("category", "blockquote");
super.visit(blockQuote);
}
@Override
public void visit(Code code) {
this.currentParagraphs.add(code.getLiteral());
- this.currentDocumentBuilder.withMetadata("category", "code_inline");
+ this.currentDocumentBuilder.metadata("category", "code_inline");
super.visit(code);
}
@@ -166,8 +166,8 @@ public void visit(FencedCodeBlock fencedCodeBlock) {
translateLineBreakToSpace();
this.currentParagraphs.add(fencedCodeBlock.getLiteral());
- this.currentDocumentBuilder.withMetadata("category", "code_block");
- this.currentDocumentBuilder.withMetadata("lang", fencedCodeBlock.getInfo());
+ this.currentDocumentBuilder.metadata("category", "code_block");
+ this.currentDocumentBuilder.metadata("lang", fencedCodeBlock.getInfo());
buildAndFlush();
@@ -177,8 +177,8 @@ public void visit(FencedCodeBlock fencedCodeBlock) {
@Override
public void visit(Text text) {
if (text.getParent() instanceof Heading heading) {
- this.currentDocumentBuilder.withMetadata("category", "header_%d".formatted(heading.getLevel()))
- .withMetadata("title", text.getLiteral());
+ this.currentDocumentBuilder.metadata("category", "header_%d".formatted(heading.getLevel()))
+ .metadata("title", text.getLiteral());
}
else {
this.currentParagraphs.add(text.getLiteral());
@@ -197,9 +197,9 @@ private void buildAndFlush() {
if (!this.currentParagraphs.isEmpty()) {
String content = String.join("", this.currentParagraphs);
- Document.Builder builder = this.currentDocumentBuilder.withContent(content);
+ Document.Builder builder = this.currentDocumentBuilder.text(content);
- this.config.additionalMetadata.forEach(builder::withMetadata);
+ this.config.additionalMetadata.forEach(builder::metadata);
Document document = builder.build();
diff --git a/community/document-readers/feishu-document-reader/src/main/java/com/alibaba/cloud/ai/reader/feishu/config/FeiShuProperties.java b/community/document-readers/feishu-document-reader/src/main/java/com/alibaba/cloud/ai/reader/feishu/config/FeiShuProperties.java
deleted file mode 100644
index a11f68e23..000000000
--- a/community/document-readers/feishu-document-reader/src/main/java/com/alibaba/cloud/ai/reader/feishu/config/FeiShuProperties.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright 2024-2025 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.alibaba.cloud.ai.reader.feishu.config;
-
-import org.springframework.boot.context.properties.ConfigurationProperties;
-
-/**
- * @author wblu214
- * @author wblu214
- */
-@ConfigurationProperties(prefix = FeiShuProperties.FEISHU_PROPERTIES_PREFIX)
-public class FeiShuProperties {
-
- public static final String FEISHU_PROPERTIES_PREFIX = "spring.ai.alibaba.plugin.feishu";
-
- private Boolean enabled;
-
- private String appId;
-
- private String appSecret;
-
- public Boolean getEnabled() {
- return enabled;
- }
-
- public void setEnabled(Boolean enabled) {
- this.enabled = enabled;
- }
-
- public String getAppId() {
- return appId;
- }
-
- public void setAppId(String appId) {
- this.appId = appId;
- }
-
- public String getAppSecret() {
- return appSecret;
- }
-
- public void setAppSecret(String appSecret) {
- this.appSecret = appSecret;
- }
-
- @Override
- public String toString() {
- return "FeiShuProperties{" + "enabled=" + enabled + ", appId='" + appId + '\'' + ", AppSecret='" + appSecret
- + '\'' + '}';
- }
-
-}
diff --git a/community/document-readers/github-document-reader/pom.xml b/community/document-readers/github-document-reader/pom.xml
index ba8a5f1ad..3b9e3fba5 100644
--- a/community/document-readers/github-document-reader/pom.xml
+++ b/community/document-readers/github-document-reader/pom.xml
@@ -11,7 +11,7 @@
github-document-reader
- github-reader
+ github-document-reader
github reader for Spring AI Alibaba
jar
https://github.com/alibaba/spring-ai-alibaba
diff --git a/community/function-calling/README.md b/community/function-calling/README.md
index 5b4c4f0f1..878a333df 100644
--- a/community/function-calling/README.md
+++ b/community/function-calling/README.md
@@ -8,10 +8,11 @@
* Function Impl name 命名为:`${name}Service`,通常是由声明 Bean 注解的方法名确定,如 `baiduSearchService`(建议,请根据插件实际情况确定)
* function bean name 命名为:`${xx}Function`,通常使用 动词+修饰词的方式确定,如`getCurrentLocationUseBaiduMapFunction`(建议,请根据插件实际情况确定)
2. 使用 **org.springframework.context.annotation.Description** `@Description("xxx")` 注解描述插件的功能,应提供对插件功能清晰明确的描述,例如:`@Description("百度搜索插件,用于查询百度上的新闻事件等信息")`
-3. 如插件自身有配置参数,请使用 `@ConfigurationProperties(prefix = "spring.ai.alibaba.functioncalling.${name}")` 注解,例如:
+3. 如果 Function Impl 实现较为复杂,需要使用一些自定义函数,方法命名规范为:`${name}Tool`,例如:`BaiduSearchTool`, 目录层级和实现类保持一致
+4 . 如插件自身有配置参数,请使用 `@ConfigurationProperties(prefix = "spring.ai.alibaba.functioncalling.${name}")` 注解,例如:
```java
@ConfigurationProperties(prefix = "spring.ai.alibaba.functioncalling.baidusearch")
public class BaidusearchProperties {}
```
4. 请在根目录 pom.xml 中添加 module 配置,如 `community/function-calling/spring-ai-alibaba-starter-functioncalling-baidusearch`
-5. 请在插件 pom.xml 文件中只保留必须的传递依赖,插件版本应与 Spring AI Alibaba 统一。
+5. 请在插件 pom.xml 文件中只保留必须的传递依赖,插件版本应与 Spring AI Alibaba 统一,插件依赖规范为:序列化与反序列化依赖统一使用 `com.fasterxml.jackson.core:jackson`,日志依赖统一使用 `org.slf4j`,HTTP 客户端依赖统一使用 `org.springframework.boot::webClient`,其余依赖尽可能少引用或不引用
diff --git a/community/function-calling/spring-ai-alibaba-starter-function-calling-alitranslate/pom.xml b/community/function-calling/spring-ai-alibaba-starter-function-calling-alitranslate/pom.xml
new file mode 100644
index 000000000..f993caf89
--- /dev/null
+++ b/community/function-calling/spring-ai-alibaba-starter-function-calling-alitranslate/pom.xml
@@ -0,0 +1,67 @@
+
+
+
+
+
+ 4.0.0
+
+
+ com.alibaba.cloud.ai
+ spring-ai-alibaba
+ ${revision}
+ ../../../pom.xml
+
+
+ spring-ai-alibaba-starter-function-calling-alitranslate
+ spring-ai-alibaba-starter-function-calling-alitranslate
+ Translate tool for Spring AI Alibaba
+
+
+
+ org.springframework.ai
+ spring-ai-spring-boot-autoconfigure
+
+
+
+ org.springframework.boot
+ spring-boot-configuration-processor
+ true
+
+
+
+ org.springframework.boot
+ spring-boot-starter-webflux
+
+
+
+ com.google.code.gson
+ gson
+
+
+
+ com.aliyun
+ alibabacloud-alimt20181012
+ 1.0.2
+
+
+
+
+
+ spring-ai-alibaba-function-calling-alitranslate
+
+
diff --git a/community/document-readers/feishu-document-reader/src/main/java/com/alibaba/cloud/ai/reader/feishu/config/FeiShuPluginConfiguration.java b/community/function-calling/spring-ai-alibaba-starter-function-calling-alitranslate/src/main/java/com/alibaba/cloud/ai/functioncalling/alitranslate/AliTranslateAutoConfiguration.java
similarity index 53%
rename from community/document-readers/feishu-document-reader/src/main/java/com/alibaba/cloud/ai/reader/feishu/config/FeiShuPluginConfiguration.java
rename to community/function-calling/spring-ai-alibaba-starter-function-calling-alitranslate/src/main/java/com/alibaba/cloud/ai/functioncalling/alitranslate/AliTranslateAutoConfiguration.java
index 388f8a376..4d046164b 100644
--- a/community/document-readers/feishu-document-reader/src/main/java/com/alibaba/cloud/ai/reader/feishu/config/FeiShuPluginConfiguration.java
+++ b/community/function-calling/spring-ai-alibaba-starter-function-calling-alitranslate/src/main/java/com/alibaba/cloud/ai/functioncalling/alitranslate/AliTranslateAutoConfiguration.java
@@ -13,38 +13,31 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.alibaba.cloud.ai.reader.feishu.config;
+package com.alibaba.cloud.ai.functioncalling.alitranslate;
-import com.lark.oapi.Client;
-import com.lark.oapi.core.enums.BaseUrlEnum;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Description;
-import org.springframework.util.Assert;
/**
- * @author wblu214
- * @author wblu214
+ * @author yunlong
*/
@Configuration
-@EnableConfigurationProperties(FeiShuProperties.class)
-public class FeiShuPluginConfiguration {
+@ConditionalOnClass(AliTranslateService.class)
+@EnableConfigurationProperties(AliTranslateProperties.class)
+@ConditionalOnProperty(prefix = "spring.ai.alibaba.functioncalling.alitranslate", name = "enabled",
+ havingValue = "true")
+public class AliTranslateAutoConfiguration {
@Bean
@ConditionalOnMissingBean
- @Description("Build FeiShu Client in Spring AI Alibaba") // description
- @ConditionalOnProperty(prefix = FeiShuProperties.FEISHU_PROPERTIES_PREFIX, name = "enabled", havingValue = "true")
- public Client buildDefaultFeiShuClient(FeiShuProperties feiShuProperties) {
- Assert.notNull(feiShuProperties.getAppId(), "FeiShu AppId must not be empty");
- Assert.notNull(feiShuProperties.getAppSecret(), "FeiShu AppSecret must not be empty");
- return Client.newBuilder(feiShuProperties.getAppId(), feiShuProperties.getAppSecret())
- .openBaseUrl(BaseUrlEnum.FeiShu)
- .logReqAtDebug(true)
- .build();
+ @Description("Implement natural language translation capabilities.")
+ public AliTranslateService aliTranslateFunction(AliTranslateProperties properties) {
+ return new AliTranslateService(properties);
}
- // 商店应用自行扩展
}
diff --git a/community/function-calling/spring-ai-alibaba-starter-function-calling-alitranslate/src/main/java/com/alibaba/cloud/ai/functioncalling/alitranslate/AliTranslateProperties.java b/community/function-calling/spring-ai-alibaba-starter-function-calling-alitranslate/src/main/java/com/alibaba/cloud/ai/functioncalling/alitranslate/AliTranslateProperties.java
new file mode 100644
index 000000000..c410b582c
--- /dev/null
+++ b/community/function-calling/spring-ai-alibaba-starter-function-calling-alitranslate/src/main/java/com/alibaba/cloud/ai/functioncalling/alitranslate/AliTranslateProperties.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2024-2025 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.cloud.ai.functioncalling.alitranslate;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+/**
+ * @author yunlong
+ *
+ * [offlinedoc](https://help.aliyun.com/zh/machine-translation/developer-reference/api-alimt-2018-10-12-translategeneral)
+ */
+@ConfigurationProperties(prefix = "spring.ai.alibaba.functioncalling.alitranslate")
+public class AliTranslateProperties {
+
+ private String region;
+
+ private String accessKeyId;
+
+ private String accessKeySecret;
+
+ public String getAccessKeyId() {
+ return accessKeyId;
+ }
+
+ public void setAccessKeyId(String accessKeyId) {
+ this.accessKeyId = accessKeyId;
+ }
+
+ public String getAccessKeySecret() {
+ return accessKeySecret;
+ }
+
+ public void setAccessKeySecret(String accessKeySecret) {
+ this.accessKeySecret = accessKeySecret;
+ }
+
+ public String getRegion() {
+ return region;
+ }
+
+ public void setRegion(String region) {
+ this.region = region;
+ }
+
+}
diff --git a/community/function-calling/spring-ai-alibaba-starter-function-calling-alitranslate/src/main/java/com/alibaba/cloud/ai/functioncalling/alitranslate/AliTranslateService.java b/community/function-calling/spring-ai-alibaba-starter-function-calling-alitranslate/src/main/java/com/alibaba/cloud/ai/functioncalling/alitranslate/AliTranslateService.java
new file mode 100644
index 000000000..2ccebd2d4
--- /dev/null
+++ b/community/function-calling/spring-ai-alibaba-starter-function-calling-alitranslate/src/main/java/com/alibaba/cloud/ai/functioncalling/alitranslate/AliTranslateService.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2024-2025 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.cloud.ai.functioncalling.alitranslate;
+
+import com.aliyun.auth.credentials.Credential;
+import com.aliyun.auth.credentials.provider.StaticCredentialProvider;
+import com.aliyun.sdk.service.alimt20181012.AsyncClient;
+import com.aliyun.sdk.service.alimt20181012.models.TranslateGeneralRequest;
+import com.aliyun.sdk.service.alimt20181012.models.TranslateGeneralResponse;
+import com.fasterxml.jackson.annotation.JsonClassDescription;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonPropertyDescription;
+import com.google.gson.Gson;
+import com.google.gson.JsonObject;
+import darabonba.core.client.ClientOverrideConfiguration;
+import org.springframework.util.StringUtils;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.function.Function;
+
+/**
+ * @author yunlong
+ */
+public class AliTranslateService implements Function {
+
+ private final AsyncClient client;
+
+ /**
+ * version of the api
+ */
+ public static final String SCENE = "general";
+
+ /**
+ * FormatType text or html
+ */
+ public static final String FORM_TYPE = "text";
+
+ /**
+ * offline doc:
+ * https://help.aliyun.com/zh/machine-translation/support/supported-languages-and-codes?spm=api-workbench.api_explorer.0.0.37a94eecsclZw9
+ */
+ public static final String LANGUAGE_CODE = "zh";
+
+ public AliTranslateService(AliTranslateProperties properties) {
+ assert StringUtils.hasText(properties.getRegion());
+ assert StringUtils.hasText(properties.getAccessKeyId());
+ assert StringUtils.hasText(properties.getAccessKeySecret());
+ StaticCredentialProvider provider = StaticCredentialProvider.create(Credential.builder()
+ .accessKeyId(properties.getAccessKeyId())
+ .accessKeySecret(properties.getAccessKeySecret())
+ .build());
+
+ this.client = AsyncClient.builder()
+ .region(properties.getRegion()) // Region ID
+ .credentialsProvider(provider)
+ .overrideConfiguration(ClientOverrideConfiguration.create().setEndpointOverride("mt.aliyuncs.com"))
+ .build();
+ }
+
+ @Override
+ public Response apply(Request request) {
+ if (request == null || !StringUtils.hasText(request.text) || !StringUtils.hasText(request.targetLanguage)) {
+ return null;
+ }
+
+ TranslateGeneralRequest translateGeneralRequest = TranslateGeneralRequest.builder()
+ .formatType(FORM_TYPE)
+ .sourceLanguage(LANGUAGE_CODE)
+ .targetLanguage(request.targetLanguage)
+ .sourceText(request.text)
+ .scene(SCENE)
+ .build();
+
+ CompletableFuture response = client.translateGeneral(translateGeneralRequest);
+
+ TranslateGeneralResponse resp = null;
+ try {
+ resp = response.get();
+ }
+ catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+
+ // 假设 resp 是一个对象
+ String jsonString = new Gson().toJson(resp);
+ JsonObject jsonObject = new Gson().fromJson(jsonString, JsonObject.class);
+ String result = jsonObject.get("body")
+ .getAsJsonObject()
+ .get("data")
+ .getAsJsonObject()
+ .get("translated")
+ .toString();
+
+ client.close();
+ return new Response(result);
+
+ }
+
+ @JsonClassDescription("Request to alitranslate text to a target language")
+ public record Request(
+ @JsonProperty(required = true,
+ value = "text") @JsonPropertyDescription("Content that needs to be translated") String text,
+ @JsonProperty(required = false,
+ value = "targetLanguage") @JsonPropertyDescription("Target language to alitranslate into") String targetLanguage) {
+
+ public Request(@JsonProperty("text") String text) {
+ this(text, "en"); // 默认目标语言为英语
+ }
+ }
+
+ @JsonClassDescription("Response to alitranslate text to a target language")
+ public record Response(String translatedTexts) {
+ }
+
+}
diff --git a/community/function-calling/spring-ai-alibaba-starter-function-calling-alitranslate/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/community/function-calling/spring-ai-alibaba-starter-function-calling-alitranslate/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
new file mode 100644
index 000000000..64584bf0b
--- /dev/null
+++ b/community/function-calling/spring-ai-alibaba-starter-function-calling-alitranslate/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
@@ -0,0 +1 @@
+com.alibaba.cloud.ai.functioncalling.alitranslate.AliTranslateAutoConfiguration
diff --git a/community/function-calling/spring-ai-alibaba-starter-function-calling-amap/src/main/java/com/alibaba/cloud/ai/functioncalling/amp/AmapProperties.java b/community/function-calling/spring-ai-alibaba-starter-function-calling-amap/src/main/java/com/alibaba/cloud/ai/functioncalling/amp/AmapProperties.java
index bcd7d6cd7..0406ae622 100644
--- a/community/function-calling/spring-ai-alibaba-starter-function-calling-amap/src/main/java/com/alibaba/cloud/ai/functioncalling/amp/AmapProperties.java
+++ b/community/function-calling/spring-ai-alibaba-starter-function-calling-amap/src/main/java/com/alibaba/cloud/ai/functioncalling/amp/AmapProperties.java
@@ -20,7 +20,7 @@
/**
* @author YunLong
*/
-@ConfigurationProperties(prefix = "spring.ai.alibaba.plugin.gaode")
+@ConfigurationProperties(prefix = "spring.ai.alibaba.functioncalling.amap")
public class AmapProperties {
// Official Document Address: https://lbs.amap.com/api/webservice/summary
diff --git a/community/function-calling/spring-ai-alibaba-starter-function-calling-bingsearch/src/main/java/com/alibaba/cloud/ai/functioncalling/bingsearch/BingSearchAutoConfiguration.java b/community/function-calling/spring-ai-alibaba-starter-function-calling-bingsearch/src/main/java/com/alibaba/cloud/ai/functioncalling/bingsearch/BingSearchAutoConfiguration.java
index b7426c3f8..6e7f8a30d 100644
--- a/community/function-calling/spring-ai-alibaba-starter-function-calling-bingsearch/src/main/java/com/alibaba/cloud/ai/functioncalling/bingsearch/BingSearchAutoConfiguration.java
+++ b/community/function-calling/spring-ai-alibaba-starter-function-calling-bingsearch/src/main/java/com/alibaba/cloud/ai/functioncalling/bingsearch/BingSearchAutoConfiguration.java
@@ -33,7 +33,6 @@ public class BingSearchAutoConfiguration {
@Bean
@ConditionalOnMissingBean
@Description("Use bing search engine to query for the latest news.")
- @ConditionalOnProperty(prefix = "spring.ai.alibaba.plugin.bing", name = "enabled", havingValue = "true")
public BingSearchService bingSearchFunction(BingSearchProperties properties) {
return new BingSearchService(properties);
}
diff --git a/community/function-calling/spring-ai-alibaba-starter-function-calling-bingsearch/src/main/java/com/alibaba/cloud/ai/functioncalling/bingsearch/BingSearchProperties.java b/community/function-calling/spring-ai-alibaba-starter-function-calling-bingsearch/src/main/java/com/alibaba/cloud/ai/functioncalling/bingsearch/BingSearchProperties.java
index 69fefff6e..65f480da8 100644
--- a/community/function-calling/spring-ai-alibaba-starter-function-calling-bingsearch/src/main/java/com/alibaba/cloud/ai/functioncalling/bingsearch/BingSearchProperties.java
+++ b/community/function-calling/spring-ai-alibaba-starter-function-calling-bingsearch/src/main/java/com/alibaba/cloud/ai/functioncalling/bingsearch/BingSearchProperties.java
@@ -22,7 +22,7 @@
* @author KrakenZJC
**/
@EnableConfigurationProperties
-@ConfigurationProperties(prefix = "spring.ai.alibaba.plugin.bing")
+@ConfigurationProperties(prefix = "spring.ai.alibaba.functioncalling.bingsearch")
public class BingSearchProperties {
public static final String OCP_APIM_SUBSCRIPTION_KEY = "Ocp-Apim-Subscription-Key";
diff --git a/community/function-calling/spring-ai-alibaba-starter-function-calling-dingtalk/src/main/java/com/alibaba/cloud/ai/functioncalling/dingtalk/DingTalkProperties.java b/community/function-calling/spring-ai-alibaba-starter-function-calling-dingtalk/src/main/java/com/alibaba/cloud/ai/functioncalling/dingtalk/DingTalkProperties.java
index 221639636..8efcf6e83 100644
--- a/community/function-calling/spring-ai-alibaba-starter-function-calling-dingtalk/src/main/java/com/alibaba/cloud/ai/functioncalling/dingtalk/DingTalkProperties.java
+++ b/community/function-calling/spring-ai-alibaba-starter-function-calling-dingtalk/src/main/java/com/alibaba/cloud/ai/functioncalling/dingtalk/DingTalkProperties.java
@@ -20,7 +20,7 @@
/**
* @author YunLong
*/
-@ConfigurationProperties(prefix = "spring.ai.alibaba.plugin.dingtalk")
+@ConfigurationProperties(prefix = "spring.ai.alibaba.functioncalling.dingtalk")
public class DingTalkProperties {
private String customRobotAccessToken;
diff --git a/community/function-calling/spring-ai-alibaba-starter-function-calling-larksuite/src/main/java/com/alibaba/cloud/ai/functioncalling/larksuite/LarkSuiteAutoConfiguration.java b/community/function-calling/spring-ai-alibaba-starter-function-calling-larksuite/src/main/java/com/alibaba/cloud/ai/functioncalling/larksuite/LarkSuiteAutoConfiguration.java
index efe71cf8c..da8397a30 100644
--- a/community/function-calling/spring-ai-alibaba-starter-function-calling-larksuite/src/main/java/com/alibaba/cloud/ai/functioncalling/larksuite/LarkSuiteAutoConfiguration.java
+++ b/community/function-calling/spring-ai-alibaba-starter-function-calling-larksuite/src/main/java/com/alibaba/cloud/ai/functioncalling/larksuite/LarkSuiteAutoConfiguration.java
@@ -27,7 +27,7 @@
*/
@EnableConfigurationProperties({ LarkSuiteProperties.class })
@ConditionalOnClass({ LarkSuiteProperties.class })
-@ConditionalOnProperty(prefix = "spring.ai.alibaba.plugin.larksuite", name = "enabled", havingValue = "true")
+@ConditionalOnProperty(prefix = "spring.ai.alibaba.functioncalling.larksuite", name = "enabled", havingValue = "true")
public class LarkSuiteAutoConfiguration {
@Bean
diff --git a/community/function-calling/spring-ai-alibaba-starter-function-calling-larksuite/src/main/java/com/alibaba/cloud/ai/functioncalling/larksuite/LarkSuiteProperties.java b/community/function-calling/spring-ai-alibaba-starter-function-calling-larksuite/src/main/java/com/alibaba/cloud/ai/functioncalling/larksuite/LarkSuiteProperties.java
index 521b99b26..723e841a6 100644
--- a/community/function-calling/spring-ai-alibaba-starter-function-calling-larksuite/src/main/java/com/alibaba/cloud/ai/functioncalling/larksuite/LarkSuiteProperties.java
+++ b/community/function-calling/spring-ai-alibaba-starter-function-calling-larksuite/src/main/java/com/alibaba/cloud/ai/functioncalling/larksuite/LarkSuiteProperties.java
@@ -21,7 +21,7 @@
* @author 北极星
*/
-@ConfigurationProperties("spring.ai.alibaba.plugin.larksuite")
+@ConfigurationProperties("spring.ai.alibaba.functioncalling.larksuite")
public class LarkSuiteProperties {
/**
diff --git a/community/function-calling/spring-ai-alibaba-starter-function-calling-microsofttranslate/src/main/java/com/alibaba/cloud/ai/functioncalling/microsofttranslate/MicroSoftTranslateProperties.java b/community/function-calling/spring-ai-alibaba-starter-function-calling-microsofttranslate/src/main/java/com/alibaba/cloud/ai/functioncalling/microsofttranslate/MicroSoftTranslateProperties.java
index ecdf3e1cb..3f3d5a03b 100644
--- a/community/function-calling/spring-ai-alibaba-starter-function-calling-microsofttranslate/src/main/java/com/alibaba/cloud/ai/functioncalling/microsofttranslate/MicroSoftTranslateProperties.java
+++ b/community/function-calling/spring-ai-alibaba-starter-function-calling-microsofttranslate/src/main/java/com/alibaba/cloud/ai/functioncalling/microsofttranslate/MicroSoftTranslateProperties.java
@@ -20,7 +20,7 @@
/**
* @author 31445
*/
-@ConfigurationProperties(prefix = "spring.ai.alibaba.plugin.microsofttranslate")
+@ConfigurationProperties(prefix = "spring.ai.alibaba.functioncalling.microsofttranslate")
public class MicroSoftTranslateProperties {
public static final String OCP_APIM_SUBSCRIPTION_KEY = "Ocp-Apim-Subscription-Key";
diff --git a/community/function-calling/spring-ai-alibaba-starter-function-calling-serpapi/src/main/java/com/alibaba/cloud/ai/functioncalling/serpapi/SerpApiProperties.java b/community/function-calling/spring-ai-alibaba-starter-function-calling-serpapi/src/main/java/com/alibaba/cloud/ai/functioncalling/serpapi/SerpApiProperties.java
index fb6a797e1..8fd0076a6 100644
--- a/community/function-calling/spring-ai-alibaba-starter-function-calling-serpapi/src/main/java/com/alibaba/cloud/ai/functioncalling/serpapi/SerpApiProperties.java
+++ b/community/function-calling/spring-ai-alibaba-starter-function-calling-serpapi/src/main/java/com/alibaba/cloud/ai/functioncalling/serpapi/SerpApiProperties.java
@@ -20,7 +20,7 @@
/**
* @author 北极星
*/
-@ConfigurationProperties(prefix = "spring.ai.alibaba.plugin.serpapi")
+@ConfigurationProperties(prefix = "spring.ai.alibaba.functioncalling.serpapi")
public class SerpApiProperties {
public static final String SERP_API_URL = "https://serpapi.com/search.json";
diff --git a/community/function-calling/spring-ai-alibaba-starter-function-calling-sinanews/src/main/java/com/alibaba/cloud/ai/functioncalling/sinanews/SinaNewsAutoConfiguration.java b/community/function-calling/spring-ai-alibaba-starter-function-calling-sinanews/src/main/java/com/alibaba/cloud/ai/functioncalling/sinanews/SinaNewsAutoConfiguration.java
index 5bc1697fb..424186c98 100644
--- a/community/function-calling/spring-ai-alibaba-starter-function-calling-sinanews/src/main/java/com/alibaba/cloud/ai/functioncalling/sinanews/SinaNewsAutoConfiguration.java
+++ b/community/function-calling/spring-ai-alibaba-starter-function-calling-sinanews/src/main/java/com/alibaba/cloud/ai/functioncalling/sinanews/SinaNewsAutoConfiguration.java
@@ -28,7 +28,7 @@
*/
@Configuration
@ConditionalOnClass(SinaNewsService.class)
-@ConditionalOnProperty(prefix = "spring.ai.alibaba.plugin.sinanews", name = "enabled", havingValue = "true")
+@ConditionalOnProperty(prefix = "spring.ai.alibaba.functioncalling.sinanews", name = "enabled", havingValue = "true")
public class SinaNewsAutoConfiguration {
@Bean
diff --git a/community/function-calling/spring-ai-alibaba-starter-function-calling-toutiaonews/src/main/java/com/alibaba/cloud/ai/functioncalling/toutiaonews/TiaotiaoNewsAutoConfiguration.java b/community/function-calling/spring-ai-alibaba-starter-function-calling-toutiaonews/src/main/java/com/alibaba/cloud/ai/functioncalling/toutiaonews/TiaotiaoNewsAutoConfiguration.java
index 03c0ee47c..dc157a9ec 100644
--- a/community/function-calling/spring-ai-alibaba-starter-function-calling-toutiaonews/src/main/java/com/alibaba/cloud/ai/functioncalling/toutiaonews/TiaotiaoNewsAutoConfiguration.java
+++ b/community/function-calling/spring-ai-alibaba-starter-function-calling-toutiaonews/src/main/java/com/alibaba/cloud/ai/functioncalling/toutiaonews/TiaotiaoNewsAutoConfiguration.java
@@ -28,7 +28,7 @@
*/
@Configuration
@ConditionalOnClass(ToutiaoNewsService.class)
-@ConditionalOnProperty(prefix = "spring.ai.alibaba.plugin.toutiaonews", name = "enabled", havingValue = "true")
+@ConditionalOnProperty(prefix = "spring.ai.alibaba.functioncalling.toutiaonews", name = "enabled", havingValue = "true")
public class TiaotiaoNewsAutoConfiguration {
@Bean
diff --git a/community/function-calling/spring-ai-alibaba-starter-function-calling-weather/src/main/java/com/alibaba/cloud/ai/functioncalling/weather/WeatherProperties.java b/community/function-calling/spring-ai-alibaba-starter-function-calling-weather/src/main/java/com/alibaba/cloud/ai/functioncalling/weather/WeatherProperties.java
index 8185589bf..a671ef853 100644
--- a/community/function-calling/spring-ai-alibaba-starter-function-calling-weather/src/main/java/com/alibaba/cloud/ai/functioncalling/weather/WeatherProperties.java
+++ b/community/function-calling/spring-ai-alibaba-starter-function-calling-weather/src/main/java/com/alibaba/cloud/ai/functioncalling/weather/WeatherProperties.java
@@ -20,7 +20,7 @@
/**
* @author 31445
*/
-@ConfigurationProperties(prefix = "spring.ai.alibaba.plugin.weather")
+@ConfigurationProperties(prefix = "spring.ai.alibaba.functioncalling.weather")
public class WeatherProperties {
private String apiKey;
diff --git a/pom.xml b/pom.xml
index 0b20dd467..312be6d0a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -54,6 +54,7 @@
community/function-calling/spring-ai-alibaba-starter-function-calling-toutiaonews
community/function-calling/spring-ai-alibaba-starter-function-calling-yuque
community/function-calling/spring-ai-alibaba-starter-function-calling-googletranslate
+ community/function-calling/spring-ai-alibaba-starter-function-calling-alitranslate
community/document-readers/github-document-reader
community/document-readers/poi-document-reader
@@ -67,7 +68,7 @@
- 1.0.0-M3.3
+ 1.0.0-M5.1-SNAPSHOT
UTF-8
UTF-8
@@ -78,7 +79,7 @@
3.3.3
- 1.0.0-M3
+ 1.0.0-M5
2.15.1
diff --git a/spring-ai-alibaba-autoconfigure/src/main/java/com/alibaba/cloud/ai/autoconfigure/dashscope/DashScopeAutoConfiguration.java b/spring-ai-alibaba-autoconfigure/src/main/java/com/alibaba/cloud/ai/autoconfigure/dashscope/DashScopeAutoConfiguration.java
index ea76c2991..1fe244e61 100644
--- a/spring-ai-alibaba-autoconfigure/src/main/java/com/alibaba/cloud/ai/autoconfigure/dashscope/DashScopeAutoConfiguration.java
+++ b/spring-ai-alibaba-autoconfigure/src/main/java/com/alibaba/cloud/ai/autoconfigure/dashscope/DashScopeAutoConfiguration.java
@@ -30,8 +30,9 @@
import org.springframework.ai.autoconfigure.retry.SpringAiRetryAutoConfiguration;
import org.springframework.ai.chat.observation.ChatModelObservationConvention;
import org.springframework.ai.embedding.observation.EmbeddingModelObservationConvention;
+import org.springframework.ai.model.function.DefaultFunctionCallbackResolver;
import org.springframework.ai.model.function.FunctionCallback;
-import org.springframework.ai.model.function.FunctionCallbackContext;
+import org.springframework.ai.model.function.FunctionCallbackResolver;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
@@ -98,7 +99,7 @@ public Transcription transcription() {
public DashScopeChatModel dashscopeChatModel(DashScopeConnectionProperties commonProperties,
DashScopeChatProperties chatProperties, RestClient.Builder restClientBuilder,
WebClient.Builder webClientBuilder, List toolFunctionCallbacks,
- FunctionCallbackContext functionCallbackContext, RetryTemplate retryTemplate,
+ FunctionCallbackResolver functionCallbackResolver, RetryTemplate retryTemplate,
ResponseErrorHandler responseErrorHandler, ObjectProvider observationRegistry,
ObjectProvider observationConvention) {
@@ -109,7 +110,7 @@ public DashScopeChatModel dashscopeChatModel(DashScopeConnectionProperties commo
var dashscopeApi = dashscopeChatApi(commonProperties, chatProperties, restClientBuilder, webClientBuilder,
responseErrorHandler);
- var dashscopeModel = new DashScopeChatModel(dashscopeApi, chatProperties.getOptions(), functionCallbackContext,
+ var dashscopeModel = new DashScopeChatModel(dashscopeApi, chatProperties.getOptions(), functionCallbackResolver,
retryTemplate, observationRegistry.getIfUnique(() -> ObservationRegistry.NOOP));
observationConvention.ifAvailable(dashscopeModel::setObservationConvention);
@@ -280,9 +281,8 @@ public DashScopeAudioTranscriptionModel dashScopeAudioTranscriptionModel(
@Bean
@ConditionalOnMissingBean
- public FunctionCallbackContext springAiFunctionManager(ApplicationContext context) {
-
- FunctionCallbackContext manager = new FunctionCallbackContext();
+ public FunctionCallbackResolver springAiFunctionManager(ApplicationContext context) {
+ DefaultFunctionCallbackResolver manager = new DefaultFunctionCallbackResolver();
manager.setApplicationContext(context);
return manager;
}
diff --git a/spring-ai-alibaba-core/pom.xml b/spring-ai-alibaba-core/pom.xml
index d39852faf..6274f19d1 100644
--- a/spring-ai-alibaba-core/pom.xml
+++ b/spring-ai-alibaba-core/pom.xml
@@ -131,11 +131,11 @@
test
-
- io.micrometer
- micrometer-observation-test
- test
-
+
+ io.micrometer
+ micrometer-observation-test
+ test
+
-
\ No newline at end of file
+
diff --git a/spring-ai-alibaba-core/src/main/java/com/alibaba/cloud/ai/advisor/DocumentRetrievalAdvisor.java b/spring-ai-alibaba-core/src/main/java/com/alibaba/cloud/ai/advisor/DocumentRetrievalAdvisor.java
index cfc793a3b..bab863566 100644
--- a/spring-ai-alibaba-core/src/main/java/com/alibaba/cloud/ai/advisor/DocumentRetrievalAdvisor.java
+++ b/spring-ai-alibaba-core/src/main/java/com/alibaba/cloud/ai/advisor/DocumentRetrievalAdvisor.java
@@ -19,8 +19,8 @@
import org.springframework.ai.chat.metadata.ChatResponseMetadata;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.document.Document;
-import org.springframework.ai.document.DocumentRetriever;
-import org.springframework.ai.model.Content;
+import org.springframework.ai.rag.Query;
+import org.springframework.ai.rag.retrieval.search.DocumentRetriever;
import org.springframework.util.StringUtils;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@@ -132,39 +132,39 @@ private AdvisedRequest before(AdvisedRequest request) {
var context = new HashMap<>(request.adviseContext());
- List documents = retriever.retrieve(request.userText());
+ List documents = retriever.retrieve(new Query(request.userText()));
context.put(RETRIEVED_DOCUMENTS, documents);
String documentContext = documents.stream()
- .map(Content::getContent)
+ .map(Document::getText)
.collect(Collectors.joining(System.lineSeparator()));
Map advisedUserParams = new HashMap<>(request.userParams());
advisedUserParams.put("question_answer_context", documentContext);
return AdvisedRequest.from(request)
- .withUserText(request.userText() + System.lineSeparator() + this.userTextAdvise)
- .withUserParams(advisedUserParams)
- .withAdviseContext(context)
+ .userText(request.userText() + System.lineSeparator() + this.userTextAdvise)
+ .userParams(advisedUserParams)
+ .adviseContext(context)
.build();
}
private AdvisedResponse after(AdvisedResponse advisedResponse) {
ChatResponseMetadata.Builder metadataBuilder = ChatResponseMetadata.builder();
- metadataBuilder.withKeyValue(RETRIEVED_DOCUMENTS, advisedResponse.adviseContext().get(RETRIEVED_DOCUMENTS));
+ metadataBuilder.keyValue(RETRIEVED_DOCUMENTS, advisedResponse.adviseContext().get(RETRIEVED_DOCUMENTS));
ChatResponseMetadata metadata = advisedResponse.response().getMetadata();
if (metadata != null) {
- metadataBuilder.withId(metadata.getId());
- metadataBuilder.withModel(metadata.getModel());
- metadataBuilder.withUsage(metadata.getUsage());
- metadataBuilder.withPromptMetadata(metadata.getPromptMetadata());
- metadataBuilder.withRateLimit(metadata.getRateLimit());
+ metadataBuilder.id(metadata.getId());
+ metadataBuilder.model(metadata.getModel());
+ metadataBuilder.usage(metadata.getUsage());
+ metadataBuilder.promptMetadata(metadata.getPromptMetadata());
+ metadataBuilder.rateLimit(metadata.getRateLimit());
Set> entries = metadata.entrySet();
for (Map.Entry entry : entries) {
- metadataBuilder.withKeyValue(entry.getKey(), entry.getValue());
+ metadataBuilder.keyValue(entry.getKey(), entry.getValue());
}
}
diff --git a/spring-ai-alibaba-core/src/main/java/com/alibaba/cloud/ai/advisor/RetrievalRerankAdvisor.java b/spring-ai-alibaba-core/src/main/java/com/alibaba/cloud/ai/advisor/RetrievalRerankAdvisor.java
index 3318a2361..f127cd683 100644
--- a/spring-ai-alibaba-core/src/main/java/com/alibaba/cloud/ai/advisor/RetrievalRerankAdvisor.java
+++ b/spring-ai-alibaba-core/src/main/java/com/alibaba/cloud/ai/advisor/RetrievalRerankAdvisor.java
@@ -41,7 +41,6 @@
import org.springframework.ai.chat.metadata.ChatResponseMetadata;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.document.Document;
-import org.springframework.ai.model.Content;
import org.springframework.ai.vectorstore.SearchRequest;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.ai.vectorstore.filter.Filter;
@@ -106,11 +105,11 @@ public class RetrievalRerankAdvisor implements CallAroundAdvisor, StreamAroundAd
public static final String RERANK_SCORE = "rerank_score";
public RetrievalRerankAdvisor(VectorStore vectorStore, RerankModel rerankModel) {
- this(vectorStore, rerankModel, SearchRequest.defaults(), DEFAULT_USER_TEXT_ADVISE, DEFAULT_MIN_SCORE);
+ this(vectorStore, rerankModel, SearchRequest.builder().build(), DEFAULT_USER_TEXT_ADVISE, DEFAULT_MIN_SCORE);
}
public RetrievalRerankAdvisor(VectorStore vectorStore, RerankModel rerankModel, Double score) {
- this(vectorStore, rerankModel, SearchRequest.defaults(), DEFAULT_USER_TEXT_ADVISE, score);
+ this(vectorStore, rerankModel, SearchRequest.builder().build(), DEFAULT_USER_TEXT_ADVISE, score);
}
public RetrievalRerankAdvisor(VectorStore vectorStore, RerankModel rerankModel, SearchRequest searchRequest) {
@@ -224,8 +223,9 @@ private AdvisedRequest before(AdvisedRequest request) {
String advisedUserText = request.userText() + System.lineSeparator() + this.userTextAdvise;
var searchRequestToUse = SearchRequest.from(this.searchRequest)
- .withQuery(request.userText())
- .withFilterExpression(doGetFilterExpression(context));
+ .query(request.userText())
+ .filterExpression(doGetFilterExpression(context))
+ .build();
// 2. Search for similar documents in the vector store.
logger.debug("searchRequestToUse: {}", searchRequestToUse);
@@ -239,7 +239,7 @@ private AdvisedRequest before(AdvisedRequest request) {
// 4. Create the context from the documents.
String documentContext = documents.stream()
- .map(Content::getContent)
+ .map(Document::getText)
.collect(Collectors.joining(System.lineSeparator()));
// 5. Advise the user parameters.
@@ -247,9 +247,9 @@ private AdvisedRequest before(AdvisedRequest request) {
advisedUserParams.put("question_answer_context", documentContext);
return AdvisedRequest.from(request)
- .withUserText(advisedUserText)
- .withUserParams(advisedUserParams)
- .withAdviseContext(context)
+ .userText(advisedUserText)
+ .userParams(advisedUserParams)
+ .adviseContext(context)
.build();
}
@@ -258,19 +258,19 @@ private AdvisedResponse after(AdvisedResponse advisedResponse) {
// model, usage, etc. This will
// be changed once new version of spring ai core is updated.
ChatResponseMetadata.Builder metadataBuilder = ChatResponseMetadata.builder();
- metadataBuilder.withKeyValue(RETRIEVED_DOCUMENTS, advisedResponse.adviseContext().get(RETRIEVED_DOCUMENTS));
+ metadataBuilder.keyValue(RETRIEVED_DOCUMENTS, advisedResponse.adviseContext().get(RETRIEVED_DOCUMENTS));
ChatResponseMetadata metadata = advisedResponse.response().getMetadata();
if (metadata != null) {
- metadataBuilder.withId(metadata.getId());
- metadataBuilder.withModel(metadata.getModel());
- metadataBuilder.withUsage(metadata.getUsage());
- metadataBuilder.withPromptMetadata(metadata.getPromptMetadata());
- metadataBuilder.withRateLimit(metadata.getRateLimit());
+ metadataBuilder.id(metadata.getId());
+ metadataBuilder.model(metadata.getModel());
+ metadataBuilder.usage(metadata.getUsage());
+ metadataBuilder.promptMetadata(metadata.getPromptMetadata());
+ metadataBuilder.rateLimit(metadata.getRateLimit());
Set> entries = metadata.entrySet();
for (Map.Entry entry : entries) {
- metadataBuilder.withKeyValue(entry.getKey(), entry.getValue());
+ metadataBuilder.keyValue(entry.getKey(), entry.getValue());
}
}
diff --git a/spring-ai-alibaba-core/src/main/java/com/alibaba/cloud/ai/dashscope/agent/DashScopeAgent.java b/spring-ai-alibaba-core/src/main/java/com/alibaba/cloud/ai/dashscope/agent/DashScopeAgent.java
index 12fd06c7f..bc852556d 100644
--- a/spring-ai-alibaba-core/src/main/java/com/alibaba/cloud/ai/dashscope/agent/DashScopeAgent.java
+++ b/spring-ai-alibaba-core/src/main/java/com/alibaba/cloud/ai/dashscope/agent/DashScopeAgent.java
@@ -133,7 +133,7 @@ private ChatResponse toChatResponse(DashScopeAgentResponse response) {
metadata.put(OUTPUT, output);
var assistantMessage = new AssistantMessage(text, metadata);
- var generationMetadata = ChatGenerationMetadata.from(output.finishReason(), text);
+ var generationMetadata = ChatGenerationMetadata.builder().finishReason(output.finishReason()).build();
Generation generation = new Generation(assistantMessage, generationMetadata);
return new ChatResponse(List.of(generation));
diff --git a/spring-ai-alibaba-core/src/main/java/com/alibaba/cloud/ai/dashscope/api/DashScopeApi.java b/spring-ai-alibaba-core/src/main/java/com/alibaba/cloud/ai/dashscope/api/DashScopeApi.java
index 65dda7513..5a1c0dca1 100644
--- a/spring-ai-alibaba-core/src/main/java/com/alibaba/cloud/ai/dashscope/api/DashScopeApi.java
+++ b/spring-ai-alibaba-core/src/main/java/com/alibaba/cloud/ai/dashscope/api/DashScopeApi.java
@@ -15,18 +15,6 @@
*/
package com.alibaba.cloud.ai.dashscope.api;
-import java.io.File;
-import java.io.FileInputStream;
-import java.net.URI;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.function.Predicate;
-import java.util.stream.Collectors;
-
import com.alibaba.cloud.ai.dashscope.common.DashScopeException;
import com.alibaba.cloud.ai.dashscope.common.ErrorCodeEnum;
import com.alibaba.cloud.ai.dashscope.rag.DashScopeDocumentRetrieverOptions;
@@ -35,9 +23,6 @@
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
-import reactor.core.publisher.Flux;
-import reactor.core.publisher.Mono;
-
import org.springframework.ai.chat.metadata.Usage;
import org.springframework.ai.document.Document;
import org.springframework.ai.model.ModelOptionsUtils;
@@ -45,17 +30,23 @@
import org.springframework.boot.context.properties.bind.ConstructorBinding;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.core.io.InputStreamResource;
-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.http.*;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.web.client.ResponseErrorHandler;
import org.springframework.web.client.RestClient;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.reactive.function.client.WebClient;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.net.URI;
+import java.util.*;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
import static com.alibaba.cloud.ai.dashscope.common.DashScopeApiConstants.DEFAULT_BASE_URL;
@@ -483,7 +474,7 @@ private ResponseEntity uploadLease(UploadRequest request) {
public ResponseEntity documentSplit(Document document,
DashScopeDocumentTransformerOptions options) {
- DocumentSplitRequest request = new DocumentSplitRequest(document.getContent(), options.getChunkSize(),
+ DocumentSplitRequest request = new DocumentSplitRequest(document.getText(), options.getChunkSize(),
options.getOverlapSize(), options.getFileType(), options.getLanguage(), options.getSeparator());
return this.restClient.post()
.uri("/api/v1/indices/component/configed_transformations/spliter")
diff --git a/spring-ai-alibaba-core/src/main/java/com/alibaba/cloud/ai/dashscope/chat/DashScopeChatModel.java b/spring-ai-alibaba-core/src/main/java/com/alibaba/cloud/ai/dashscope/chat/DashScopeChatModel.java
index 6a700c3ee..e614d2a98 100755
--- a/spring-ai-alibaba-core/src/main/java/com/alibaba/cloud/ai/dashscope/chat/DashScopeChatModel.java
+++ b/spring-ai-alibaba-core/src/main/java/com/alibaba/cloud/ai/dashscope/chat/DashScopeChatModel.java
@@ -15,28 +15,12 @@
*/
package com.alibaba.cloud.ai.dashscope.chat;
-import java.util.ArrayList;
-import java.util.Base64;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-
import com.alibaba.cloud.ai.dashscope.api.DashScopeApi;
-import com.alibaba.cloud.ai.dashscope.api.DashScopeApi.ChatCompletion;
-import com.alibaba.cloud.ai.dashscope.api.DashScopeApi.ChatCompletionChunk;
-import com.alibaba.cloud.ai.dashscope.api.DashScopeApi.ChatCompletionFinishReason;
-import com.alibaba.cloud.ai.dashscope.api.DashScopeApi.ChatCompletionMessage;
+import com.alibaba.cloud.ai.dashscope.api.DashScopeApi.*;
import com.alibaba.cloud.ai.dashscope.api.DashScopeApi.ChatCompletionMessage.ChatCompletionFunction;
import com.alibaba.cloud.ai.dashscope.api.DashScopeApi.ChatCompletionMessage.MediaContent;
import com.alibaba.cloud.ai.dashscope.api.DashScopeApi.ChatCompletionMessage.ToolCall;
-import com.alibaba.cloud.ai.dashscope.api.DashScopeApi.ChatCompletionOutput;
import com.alibaba.cloud.ai.dashscope.api.DashScopeApi.ChatCompletionOutput.Choice;
-import com.alibaba.cloud.ai.dashscope.api.DashScopeApi.ChatCompletionRequest;
-import com.alibaba.cloud.ai.dashscope.api.DashScopeApi.ChatCompletionRequestInput;
-import com.alibaba.cloud.ai.dashscope.api.DashScopeApi.ChatCompletionRequestParameter;
-import com.alibaba.cloud.ai.dashscope.api.DashScopeApi.FunctionTool;
import com.alibaba.cloud.ai.dashscope.chat.observation.DashScopeChatModelObservationConvention;
import com.alibaba.cloud.ai.dashscope.common.DashScopeApiConstants;
import com.alibaba.cloud.ai.dashscope.metadata.DashScopeAiUsage;
@@ -45,33 +29,32 @@
import io.micrometer.observation.contextpropagation.ObservationThreadLocalAccessor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import reactor.core.publisher.Flux;
-import reactor.core.publisher.Mono;
-
import org.springframework.ai.chat.messages.AssistantMessage;
import org.springframework.ai.chat.messages.MessageType;
import org.springframework.ai.chat.messages.ToolResponseMessage;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.metadata.ChatGenerationMetadata;
import org.springframework.ai.chat.metadata.ChatResponseMetadata;
-import org.springframework.ai.chat.model.AbstractToolCallSupport;
import org.springframework.ai.chat.model.ChatModel;
-import org.springframework.ai.chat.model.ChatResponse;
-import org.springframework.ai.chat.model.Generation;
-import org.springframework.ai.chat.model.MessageAggregator;
+import org.springframework.ai.chat.model.*;
import org.springframework.ai.chat.observation.ChatModelObservationContext;
import org.springframework.ai.chat.observation.ChatModelObservationConvention;
import org.springframework.ai.chat.observation.ChatModelObservationDocumentation;
import org.springframework.ai.chat.prompt.ChatOptions;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.model.ModelOptionsUtils;
-import org.springframework.ai.model.function.FunctionCallbackContext;
+import org.springframework.ai.model.function.FunctionCallbackResolver;
import org.springframework.ai.retry.RetryUtils;
import org.springframework.http.ResponseEntity;
import org.springframework.retry.support.RetryTemplate;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.MimeType;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
/**
* {@link ChatModel} implementation for {@literal Alibaba DashScope} backed by
@@ -122,14 +105,14 @@ public DashScopeChatModel(DashScopeApi dashscopeApi, DashScopeChatOptions option
}
public DashScopeChatModel(DashScopeApi dashscopeApi, DashScopeChatOptions options,
- FunctionCallbackContext functionCallbackContext, RetryTemplate retryTemplate) {
- this(dashscopeApi, options, functionCallbackContext, retryTemplate, ObservationRegistry.NOOP);
+ FunctionCallbackResolver functionCallbackResolver, RetryTemplate retryTemplate) {
+ this(dashscopeApi, options, functionCallbackResolver, retryTemplate, ObservationRegistry.NOOP);
}
public DashScopeChatModel(DashScopeApi dashscopeApi, DashScopeChatOptions options,
- FunctionCallbackContext functionCallbackContext, RetryTemplate retryTemplate,
+ FunctionCallbackResolver functionCallbackResolver, RetryTemplate retryTemplate,
ObservationRegistry observationRegistry) {
- super(functionCallbackContext);
+ super(functionCallbackResolver);
Assert.notNull(dashscopeApi, "DashScopeApi must not be null");
Assert.notNull(options, "Options must not be null");
Assert.notNull(retryTemplate, "RetryTemplate must not be null");
@@ -293,7 +276,7 @@ private static Generation buildGeneration(Choice choice, Map met
var assistantMessage = new AssistantMessage(choice.message().content(), metadata, toolCalls);
String finishReason = (choice.finishReason() != null ? choice.finishReason().name() : "");
- var generationMetadata = ChatGenerationMetadata.from(finishReason, null);
+ var generationMetadata = ChatGenerationMetadata.builder().finishReason(finishReason).build();
return new Generation(assistantMessage, generationMetadata);
}
@@ -310,9 +293,9 @@ private ChatCompletion chunkToChatCompletion(ChatCompletionChunk chunk) {
private ChatResponseMetadata from(ChatCompletion result) {
Assert.notNull(result, "DashScopeAi ChatCompletionResult must not be null");
return ChatResponseMetadata.builder()
- .withId(result.requestId())
- .withUsage(DashScopeAiUsage.from(result.usage()))
- .withModel("")
+ .id(result.requestId())
+ .usage(DashScopeAiUsage.from(result.usage()))
+ .model("")
.build();
}
@@ -343,7 +326,7 @@ ChatCompletionRequest createRequest(Prompt prompt, boolean stream) {
List chatCompletionMessages = prompt.getInstructions().stream().map(message -> {
if (message.getMessageType() == MessageType.USER || message.getMessageType() == MessageType.SYSTEM) {
- Object content = message.getContent();
+ Object content = message.getText();
if (message instanceof UserMessage userMessage) {
if (!CollectionUtils.isEmpty(userMessage.getMedia())) {
content = convertMediaContent(userMessage);
diff --git a/spring-ai-alibaba-core/src/main/java/com/alibaba/cloud/ai/dashscope/rag/AnalyticdbVector.java b/spring-ai-alibaba-core/src/main/java/com/alibaba/cloud/ai/dashscope/rag/AnalyticdbVector.java
index e85ab092f..053af6e51 100644
--- a/spring-ai-alibaba-core/src/main/java/com/alibaba/cloud/ai/dashscope/rag/AnalyticdbVector.java
+++ b/spring-ai-alibaba-core/src/main/java/com/alibaba/cloud/ai/dashscope/rag/AnalyticdbVector.java
@@ -15,36 +15,22 @@
*/
package com.alibaba.cloud.ai.dashscope.rag;
+import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.aliyun.gpdb20160503.Client;
-import com.aliyun.gpdb20160503.models.CreateCollectionRequest;
-import com.aliyun.gpdb20160503.models.CreateNamespaceRequest;
-import com.aliyun.gpdb20160503.models.DeleteCollectionDataRequest;
-import com.aliyun.gpdb20160503.models.DeleteCollectionDataResponse;
-import com.aliyun.gpdb20160503.models.DescribeCollectionRequest;
-import com.aliyun.gpdb20160503.models.DescribeNamespaceRequest;
-import com.aliyun.gpdb20160503.models.InitVectorDatabaseRequest;
-import com.aliyun.gpdb20160503.models.InitVectorDatabaseResponse;
-import com.aliyun.gpdb20160503.models.QueryCollectionDataRequest;
-import com.aliyun.gpdb20160503.models.QueryCollectionDataResponse;
-import com.aliyun.gpdb20160503.models.QueryCollectionDataResponseBody;
-import com.aliyun.gpdb20160503.models.UpsertCollectionDataRequest;
-import com.aliyun.teaopenapi.models.Config;
+import com.aliyun.gpdb20160503.models.*;
import com.aliyun.tea.TeaException;
+import com.aliyun.teaopenapi.models.Config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import com.alibaba.fastjson.JSON;
import org.springframework.ai.document.Document;
+import org.springframework.ai.embedding.EmbeddingModel;
import org.springframework.ai.vectorstore.SearchRequest;
import org.springframework.ai.vectorstore.VectorStore;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Optional;
+import java.util.*;
import java.util.stream.Collectors;
+import java.util.stream.IntStream;
/**
* @author HeYQ
@@ -60,12 +46,16 @@ public class AnalyticdbVector implements VectorStore {
private Client client;
- public AnalyticdbVector(String collectionName, AnalyticdbConfig config) throws Exception {
+ private final EmbeddingModel embeddingModel;
+
+ public AnalyticdbVector(String collectionName, AnalyticdbConfig config, EmbeddingModel embeddingModel)
+ throws Exception {
// collection_name must be updated every time
this.collectionName = collectionName.toLowerCase();
this.config = config;
Config clientConfig = Config.build(this.config.toAnalyticdbClientParams());
this.client = new Client(clientConfig);
+ this.embeddingModel = embeddingModel;
initialize();
logger.debug("created AnalyticdbVector client success");
}
@@ -160,15 +150,17 @@ private void createCollectionIfNotExists(Long embeddingDimension) throws Excepti
public void add(List documents) {
List rows = new ArrayList<>(10);
for (Document doc : documents) {
- float[] floatEmbeddings = doc.getEmbedding();
- List embedding = new ArrayList<>(floatEmbeddings.length);
- for (float floatEmbedding : floatEmbeddings) {
- embedding.add((double) floatEmbedding);
- }
+ logger.info("Calling EmbeddingModel for document id = {}", doc.getId());
+ float[] floatEmbeddings = this.embeddingModel.embed(doc);
Map metadata = new HashMap<>();
metadata.put("refDocId", (String) doc.getMetadata().get("docId"));
- metadata.put("content", doc.getContent());
+ metadata.put("content", doc.getText());
metadata.put("metadata", JSON.toJSONString(doc.getMetadata()));
+
+ List embedding = IntStream.range(0, floatEmbeddings.length)
+ .mapToObj(i -> (double) floatEmbeddings[i]) // 将每个 float 转为 Double
+ .toList();
+
rows.add(new UpsertCollectionDataRequest.UpsertCollectionDataRequestRows().setVector(embedding)
.setMetadata(metadata));
}
@@ -214,7 +206,7 @@ public Optional delete(List ids) {
@Override
public List similaritySearch(String query) {
- return similaritySearch(SearchRequest.query(query));
+ return this.similaritySearch(SearchRequest.builder().query(query).build());
}
@@ -233,7 +225,7 @@ public List similaritySearch(SearchRequest searchRequest) {
.setIncludeValues(includeValues)
.setMetrics(this.config.getMetrics())
.setVector(null)
- .setContent(searchRequest.query)
+ .setContent(searchRequest.getQuery())
.setTopK((long) topK)
.setFilter(null);
try {
diff --git a/spring-ai-alibaba-core/src/main/java/com/alibaba/cloud/ai/dashscope/rag/DashScopeCloudStore.java b/spring-ai-alibaba-core/src/main/java/com/alibaba/cloud/ai/dashscope/rag/DashScopeCloudStore.java
index eabfdac8c..7ecb3b790 100644
--- a/spring-ai-alibaba-core/src/main/java/com/alibaba/cloud/ai/dashscope/rag/DashScopeCloudStore.java
+++ b/spring-ai-alibaba-core/src/main/java/com/alibaba/cloud/ai/dashscope/rag/DashScopeCloudStore.java
@@ -15,21 +15,18 @@
*/
package com.alibaba.cloud.ai.dashscope.rag;
-import java.util.List;
-import java.util.Optional;
-import java.util.stream.Collectors;
-
import com.alibaba.cloud.ai.dashscope.api.DashScopeApi;
import com.alibaba.cloud.ai.dashscope.common.DashScopeException;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
import org.springframework.ai.document.Document;
import org.springframework.ai.vectorstore.SearchRequest;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
/**
* @author nuocheng.lxm
* @since 2024/8/6 15:42
@@ -94,7 +91,7 @@ public List similaritySearch(SearchRequest request) {
searchOption = new DashScopeDocumentRetrieverOptions();
}
searchOption.setRerankTopN(request.getTopK());
- return dashScopeApi.retriever(pipelineId, request.query, searchOption);
+ return dashScopeApi.retriever(pipelineId, request.getQuery(), searchOption);
}
}
diff --git a/spring-ai-alibaba-core/src/main/java/com/alibaba/cloud/ai/dashscope/rag/DashScopeDocumentRetrievalAdvisor.java b/spring-ai-alibaba-core/src/main/java/com/alibaba/cloud/ai/dashscope/rag/DashScopeDocumentRetrievalAdvisor.java
index cf0f36868..489001992 100644
--- a/spring-ai-alibaba-core/src/main/java/com/alibaba/cloud/ai/dashscope/rag/DashScopeDocumentRetrievalAdvisor.java
+++ b/spring-ai-alibaba-core/src/main/java/com/alibaba/cloud/ai/dashscope/rag/DashScopeDocumentRetrievalAdvisor.java
@@ -20,7 +20,8 @@
import org.springframework.ai.chat.metadata.ChatResponseMetadata;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.document.Document;
-import org.springframework.ai.document.DocumentRetriever;
+import org.springframework.ai.rag.Query;
+import org.springframework.ai.rag.retrieval.search.DocumentRetriever;
import org.springframework.util.StringUtils;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@@ -156,7 +157,7 @@ private AdvisedRequest before(AdvisedRequest request) {
var context = new HashMap<>(request.adviseContext());
- List documents = retriever.retrieve(request.userText());
+ List documents = retriever.retrieve(new Query(request.userText()));
Map documentMap = new HashMap<>();
StringBuffer documentContext = new StringBuffer();
@@ -168,7 +169,7 @@ private AdvisedRequest before(AdvisedRequest request) {
【标题】%s
【正文】%s
""", indexId, document.getMetadata().get("doc_name"), document.getMetadata().get("title"),
- document.getContent());
+ document.getText());
documentContext.append(docInfo);
documentContext.append(System.lineSeparator());
@@ -183,9 +184,9 @@ private AdvisedRequest before(AdvisedRequest request) {
advisedUserParams.put(RETRIEVED_DOCUMENTS, documentContext);
return AdvisedRequest.from(request)
- .withUserText(request.userText() + System.lineSeparator() + this.userTextAdvise)
- .withUserParams(advisedUserParams)
- .withAdviseContext(context)
+ .userText(request.userText() + System.lineSeparator() + this.userTextAdvise)
+ .userParams(advisedUserParams)
+ .adviseContext(context)
.build();
}
@@ -233,19 +234,19 @@ private AdvisedResponse after(AdvisedResponse advisedResponse) {
// model, usage, etc. This will
// be changed once new version of spring ai core is updated.
ChatResponseMetadata.Builder metadataBuilder = ChatResponseMetadata.builder();
- metadataBuilder.withKeyValue(RETRIEVED_DOCUMENTS, advisedResponse.adviseContext().get(RETRIEVED_DOCUMENTS));
+ metadataBuilder.keyValue(RETRIEVED_DOCUMENTS, advisedResponse.adviseContext().get(RETRIEVED_DOCUMENTS));
ChatResponseMetadata metadata = advisedResponse.response().getMetadata();
if (metadata != null) {
- metadataBuilder.withId(metadata.getId());
- metadataBuilder.withModel(metadata.getModel());
- metadataBuilder.withUsage(metadata.getUsage());
- metadataBuilder.withPromptMetadata(metadata.getPromptMetadata());
- metadataBuilder.withRateLimit(metadata.getRateLimit());
+ metadataBuilder.id(metadata.getId());
+ metadataBuilder.model(metadata.getModel());
+ metadataBuilder.usage(metadata.getUsage());
+ metadataBuilder.promptMetadata(metadata.getPromptMetadata());
+ metadataBuilder.rateLimit(metadata.getRateLimit());
Set> entries = metadata.entrySet();
for (Map.Entry entry : entries) {
- metadataBuilder.withKeyValue(entry.getKey(), entry.getValue());
+ metadataBuilder.keyValue(entry.getKey(), entry.getValue());
}
}
diff --git a/spring-ai-alibaba-core/src/main/java/com/alibaba/cloud/ai/dashscope/rag/DashScopeDocumentRetriever.java b/spring-ai-alibaba-core/src/main/java/com/alibaba/cloud/ai/dashscope/rag/DashScopeDocumentRetriever.java
index 066d36ae1..8e5cf4259 100644
--- a/spring-ai-alibaba-core/src/main/java/com/alibaba/cloud/ai/dashscope/rag/DashScopeDocumentRetriever.java
+++ b/spring-ai-alibaba-core/src/main/java/com/alibaba/cloud/ai/dashscope/rag/DashScopeDocumentRetriever.java
@@ -15,15 +15,15 @@
*/
package com.alibaba.cloud.ai.dashscope.rag;
-import java.util.List;
-
import com.alibaba.cloud.ai.dashscope.api.DashScopeApi;
import com.alibaba.cloud.ai.dashscope.common.DashScopeException;
-
import org.springframework.ai.document.Document;
-import org.springframework.ai.document.DocumentRetriever;
+import org.springframework.ai.rag.Query;
+import org.springframework.ai.rag.retrieval.search.DocumentRetriever;
import org.springframework.util.Assert;
+import java.util.List;
+
/**
* @author nuocheng.lxm
* @since 2024/8/5 14:42
@@ -42,12 +42,12 @@ public DashScopeDocumentRetriever(DashScopeApi dashScopeApi, DashScopeDocumentRe
}
@Override
- public List retrieve(String query) {
+ public List retrieve(Query query) {
String pipelineId = dashScopeApi.getPipelineIdByName(options.getIndexName());
if (pipelineId == null) {
throw new DashScopeException("Index:" + options.getIndexName() + " NotExist");
}
- List documentList = dashScopeApi.retriever(pipelineId, query, options);
+ List documentList = dashScopeApi.retriever(pipelineId, query.text(), options);
return documentList;
}
diff --git a/spring-ai-alibaba-core/src/main/java/com/alibaba/cloud/ai/dashscope/rag/OpenSearchVector.java b/spring-ai-alibaba-core/src/main/java/com/alibaba/cloud/ai/dashscope/rag/OpenSearchVector.java
index 67bb4bd96..414ba7ef0 100644
--- a/spring-ai-alibaba-core/src/main/java/com/alibaba/cloud/ai/dashscope/rag/OpenSearchVector.java
+++ b/spring-ai-alibaba-core/src/main/java/com/alibaba/cloud/ai/dashscope/rag/OpenSearchVector.java
@@ -32,6 +32,8 @@ public class OpenSearchVector implements VectorStore {
private static final String METADATA_FIELD_NAME = "metadata";
+ private static final String EMPTY_TEXT = "";
+
private final String tableName;
private final String pKField;
@@ -93,7 +95,7 @@ public void add(List documents) {
// Insert document content information, key-value pairs matching.
// The field_pk field must be consistent with the pkField configuration.
documentFields.put(ID_FIELD_NAME, document.getId());
- documentFields.put(CONTENT_FIELD_NAME, document.getContent());
+ documentFields.put(CONTENT_FIELD_NAME, document.getText());
// Convert metadata to JSON
documentFields.put(METADATA_FIELD_NAME, JSON.toJSONString(document.getMetadata()));
@@ -127,7 +129,7 @@ public Optional delete(List idList) {
@Override
public List similaritySearch(String query) {
- return this.similaritySearch(SearchRequest.query(query));
+ return this.similaritySearch(SearchRequest.builder().query(query).build());
}
@Override
@@ -434,12 +436,12 @@ private static String extractContent(JSONObject jsonDocument) {
JSONObject fields = jsonDocument.getJSONObject(FIELDS_KEY);
String content = fields.getString(CONTENT_FIELD_NAME);
if (content == null || content.isEmpty()) {
- return Document.EMPTY_TEXT;
+ return EMPTY_TEXT;
}
return content;
}
- return Document.EMPTY_TEXT;
+ return EMPTY_TEXT;
}
/**
@@ -450,7 +452,7 @@ private static String extractContent(JSONObject jsonDocument) {
private static String extractId(JSONObject jsonDocument) {
String id = jsonDocument.getString(ID_FIELD_NAME);
if (id == null || id.isEmpty()) {
- return Document.EMPTY_TEXT;
+ return EMPTY_TEXT;
}
return id;
}
diff --git a/spring-ai-alibaba-core/src/main/java/com/alibaba/cloud/ai/dashscope/rerank/DashScopeRerankModel.java b/spring-ai-alibaba-core/src/main/java/com/alibaba/cloud/ai/dashscope/rerank/DashScopeRerankModel.java
index 64c6c8a73..d9c102e1f 100644
--- a/spring-ai-alibaba-core/src/main/java/com/alibaba/cloud/ai/dashscope/rerank/DashScopeRerankModel.java
+++ b/spring-ai-alibaba-core/src/main/java/com/alibaba/cloud/ai/dashscope/rerank/DashScopeRerankModel.java
@@ -32,14 +32,12 @@
package com.alibaba.cloud.ai.dashscope.rerank;
import com.alibaba.cloud.ai.dashscope.api.DashScopeApi;
-import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatModel;
import com.alibaba.cloud.ai.dashscope.metadata.DashScopeAiUsage;
import com.alibaba.cloud.ai.document.DocumentWithScore;
import com.alibaba.cloud.ai.model.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ai.document.Document;
-import org.springframework.ai.embedding.EmbeddingOptions;
import org.springframework.ai.model.ModelOptionsUtils;
import org.springframework.ai.retry.RetryUtils;
import org.springframework.http.ResponseEntity;
@@ -47,13 +45,9 @@
import org.springframework.retry.support.RetryTemplate;
import org.springframework.util.Assert;
-import java.util.Collection;
import java.util.Collections;
import java.util.List;
-import static java.util.Comparator.comparingInt;
-import static java.util.stream.Collectors.toList;
-
/**
* Title Dashscope rerank model.
* Description Dashscope rerank model.
@@ -126,7 +120,7 @@ public RerankResponse call(RerankRequest request) {
}
private DashScopeApi.RerankRequest createRequest(RerankRequest request, DashScopeRerankOptions requestOptions) {
- List docs = request.getInstructions().stream().map(Document::getContent).toList();
+ List docs = request.getInstructions().stream().map(Document::getText).toList();
DashScopeApi.RerankRequestParameter parameter = new DashScopeApi.RerankRequestParameter(
requestOptions.getTopN(), requestOptions.getReturnDocuments());
diff --git a/spring-ai-alibaba-core/src/main/java/com/alibaba/cloud/ai/evaluation/LaajEvaluator.java b/spring-ai-alibaba-core/src/main/java/com/alibaba/cloud/ai/evaluation/LaajEvaluator.java
index 25a0dc895..162854008 100644
--- a/spring-ai-alibaba-core/src/main/java/com/alibaba/cloud/ai/evaluation/LaajEvaluator.java
+++ b/spring-ai-alibaba-core/src/main/java/com/alibaba/cloud/ai/evaluation/LaajEvaluator.java
@@ -17,9 +17,9 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.ai.chat.client.ChatClient;
+import org.springframework.ai.document.Document;
import org.springframework.ai.evaluation.EvaluationRequest;
import org.springframework.ai.evaluation.Evaluator;
-import org.springframework.ai.model.Content;
import java.util.List;
import java.util.stream.Collectors;
@@ -67,10 +67,10 @@ protected String doGetResponse(EvaluationRequest evaluationRequest) {
}
public String doGetSupportingData(EvaluationRequest evaluationRequest) {
- List data = evaluationRequest.getDataList();
+ List data = evaluationRequest.getDataList();
return data.stream()
- .filter(node -> node != null && node.getContent() != null)
- .map(Content::getContent)
+ .filter(node -> node != null && node.getText() != null)
+ .map(Document::getText)
.collect(Collectors.joining(System.lineSeparator()));
}
diff --git a/spring-ai-alibaba-core/src/test/java/com/alibaba/cloud/ai/agent/Mcp.java b/spring-ai-alibaba-core/src/test/java/com/alibaba/cloud/ai/agent/Mcp.java
deleted file mode 100644
index 5824889bb..000000000
--- a/spring-ai-alibaba-core/src/test/java/com/alibaba/cloud/ai/agent/Mcp.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright 2024-2025 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.alibaba.cloud.ai.agent;
-
-/**
- * @author 北极星
- */
-public class Mcp {
-
- void testWithFC() {
-
- }
-
- void testWithFCToolContext() {
- }
-
-}
diff --git a/spring-ai-alibaba-core/src/test/java/com/alibaba/cloud/ai/autoconfig/dashscope/DashScopeAutoConfiguration.java b/spring-ai-alibaba-core/src/test/java/com/alibaba/cloud/ai/autoconfig/dashscope/DashScopeAutoConfiguration.java
index 4fdc31bbb..c8f9f004c 100644
--- a/spring-ai-alibaba-core/src/test/java/com/alibaba/cloud/ai/autoconfig/dashscope/DashScopeAutoConfiguration.java
+++ b/spring-ai-alibaba-core/src/test/java/com/alibaba/cloud/ai/autoconfig/dashscope/DashScopeAutoConfiguration.java
@@ -27,8 +27,9 @@
import com.alibaba.dashscope.audio.tts.SpeechSynthesizer;
import org.jetbrains.annotations.NotNull;
import org.springframework.ai.autoconfigure.retry.SpringAiRetryAutoConfiguration;
+import org.springframework.ai.model.function.DefaultFunctionCallbackResolver;
import org.springframework.ai.model.function.FunctionCallback;
-import org.springframework.ai.model.function.FunctionCallbackContext;
+import org.springframework.ai.model.function.FunctionCallbackResolver;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
@@ -94,7 +95,7 @@ public Transcription transcription() {
public DashScopeChatModel dashscopeChatModel(DashScopeConnectionProperties commonProperties,
DashScopeChatProperties chatProperties, RestClient.Builder restClientBuilder,
WebClient.Builder webClientBuilder, List toolFunctionCallbacks,
- FunctionCallbackContext functionCallbackContext, RetryTemplate retryTemplate,
+ FunctionCallbackResolver functionCallbackResolver, RetryTemplate retryTemplate,
ResponseErrorHandler responseErrorHandler) {
if (!CollectionUtils.isEmpty(toolFunctionCallbacks)) {
@@ -104,7 +105,7 @@ public DashScopeChatModel dashscopeChatModel(DashScopeConnectionProperties commo
var dashscopeApi = dashscopeChatApi(commonProperties, chatProperties, restClientBuilder, webClientBuilder,
responseErrorHandler);
- return new DashScopeChatModel(dashscopeApi, chatProperties.getOptions(), functionCallbackContext,
+ return new DashScopeChatModel(dashscopeApi, chatProperties.getOptions(), functionCallbackResolver,
retryTemplate);
}
@@ -265,9 +266,8 @@ public DashScopeAudioTranscriptionModel dashScopeAudioTranscriptionModel(
@Bean
@ConditionalOnMissingBean
- public FunctionCallbackContext springAiFunctionManager(ApplicationContext context) {
-
- FunctionCallbackContext manager = new FunctionCallbackContext();
+ public FunctionCallbackResolver springAiFunctionManager(ApplicationContext context) {
+ DefaultFunctionCallbackResolver manager = new DefaultFunctionCallbackResolver();
manager.setApplicationContext(context);
return manager;
}
diff --git a/spring-ai-alibaba-core/src/test/java/com/alibaba/cloud/ai/dashscope/DashscopeAiTestConfiguration.java b/spring-ai-alibaba-core/src/test/java/com/alibaba/cloud/ai/dashscope/DashscopeAiTestConfiguration.java
index ed91350d4..86feb992c 100755
--- a/spring-ai-alibaba-core/src/test/java/com/alibaba/cloud/ai/dashscope/DashscopeAiTestConfiguration.java
+++ b/spring-ai-alibaba-core/src/test/java/com/alibaba/cloud/ai/dashscope/DashscopeAiTestConfiguration.java
@@ -34,7 +34,6 @@
import io.micrometer.observation.tck.TestObservationRegistry;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.embedding.EmbeddingModel;
-import org.springframework.ai.model.function.FunctionCallbackContext;
import org.springframework.ai.retry.RetryUtils;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
diff --git a/spring-ai-alibaba-core/src/test/java/com/alibaba/cloud/ai/dashscope/chat/DashScopeChatModelIT.java b/spring-ai-alibaba-core/src/test/java/com/alibaba/cloud/ai/dashscope/chat/DashScopeChatModelIT.java
index b30d46247..a36b647a8 100644
--- a/spring-ai-alibaba-core/src/test/java/com/alibaba/cloud/ai/dashscope/chat/DashScopeChatModelIT.java
+++ b/spring-ai-alibaba-core/src/test/java/com/alibaba/cloud/ai/dashscope/chat/DashScopeChatModelIT.java
@@ -42,11 +42,11 @@
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.chat.prompt.PromptTemplate;
import org.springframework.ai.chat.prompt.SystemPromptTemplate;
+import org.springframework.ai.converter.BeanOutputConverter;
+import org.springframework.ai.converter.ListOutputConverter;
+import org.springframework.ai.converter.MapOutputConverter;
import org.springframework.ai.model.Media;
import org.springframework.ai.model.function.FunctionCallbackWrapper;
-import org.springframework.ai.parser.BeanOutputParser;
-import org.springframework.ai.parser.ListOutputParser;
-import org.springframework.ai.parser.MapOutputParser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.context.SpringBootTest;
@@ -111,9 +111,9 @@ void roleTest() {
@Test
void outputParser() {
DefaultConversionService conversionService = new DefaultConversionService();
- ListOutputParser outputParser = new ListOutputParser(conversionService);
+ ListOutputConverter outputConverter = new ListOutputConverter(conversionService);
- String format = outputParser.getFormat();
+ String format = outputConverter.getFormat();
String template = """
List five {subject}
{format}
@@ -123,16 +123,16 @@ void outputParser() {
Prompt prompt = new Prompt(promptTemplate.createMessage());
org.springframework.ai.chat.model.Generation generation = this.dashscopeChatModel.call(prompt).getResult();
- List list = outputParser.parse(generation.getOutput().getContent());
+ List list = outputConverter.convert(generation.getOutput().getContent());
assertThat(list).hasSize(5);
}
@Test
void mapOutputParser() {
- MapOutputParser outputParser = new MapOutputParser();
+ MapOutputConverter mapOutputConverter = new MapOutputConverter();
- String format = outputParser.getFormat();
+ String format = mapOutputConverter.getFormat();
String template = """
Provide me a List of {subject}
{format}
@@ -143,7 +143,7 @@ void mapOutputParser() {
org.springframework.ai.chat.model.Generation generation = dashscopeChatModel.call(prompt).getResult();
String generationText = generation.getOutput().getContent().replace("```json", "").replace("```", "");
- Map result = outputParser.parse(generationText);
+ Map result = mapOutputConverter.convert(generationText);
assertThat(result.get("numbers")).isEqualTo(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9));
}
@@ -151,7 +151,7 @@ void mapOutputParser() {
@Test
void beanStreamOutputParserRecords() {
- BeanOutputParser outputParser = new BeanOutputParser<>(ActorsFilmsRecord.class);
+ BeanOutputConverter outputParser = new BeanOutputConverter<>(ActorsFilmsRecord.class);
String format = outputParser.getFormat();
String template = """
@@ -172,7 +172,7 @@ void beanStreamOutputParserRecords() {
.collect(Collectors.joining());
generationTextFromStream = generationTextFromStream.replace("```json", "").replace("```", "");
- ActorsFilmsRecord actorsFilms = outputParser.parse(generationTextFromStream);
+ ActorsFilmsRecord actorsFilms = outputParser.convert(generationTextFromStream);
logger.info("" + actorsFilms);
assertThat(actorsFilms.actor()).isEqualTo("Tom Hanks");
assertThat(actorsFilms.movies()).hasSize(5);
@@ -239,7 +239,7 @@ void functionCallTest() {
@Test
void usageInStream() {
DefaultConversionService conversionService = new DefaultConversionService();
- ListOutputParser outputParser = new ListOutputParser(conversionService);
+ ListOutputConverter outputParser = new ListOutputConverter(conversionService);
String format = outputParser.getFormat();
String template = """
diff --git a/spring-ai-alibaba-core/src/test/java/com/alibaba/cloud/ai/dashscope/chat/client/DashScopeChatClientIT.java b/spring-ai-alibaba-core/src/test/java/com/alibaba/cloud/ai/dashscope/chat/client/DashScopeChatClientIT.java
index 74bb4bb50..e8bfed6bb 100644
--- a/spring-ai-alibaba-core/src/test/java/com/alibaba/cloud/ai/dashscope/chat/client/DashScopeChatClientIT.java
+++ b/spring-ai-alibaba-core/src/test/java/com/alibaba/cloud/ai/dashscope/chat/client/DashScopeChatClientIT.java
@@ -38,8 +38,8 @@
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.document.Document;
import org.springframework.ai.document.DocumentReader;
-import org.springframework.ai.document.DocumentRetriever;
import org.springframework.ai.embedding.EmbeddingModel;
+import org.springframework.ai.rag.retrieval.search.DocumentRetriever;
import org.springframework.ai.reader.JsonReader;
import org.springframework.ai.vectorstore.SimpleVectorStore;
import org.springframework.ai.vectorstore.VectorStore;
@@ -388,7 +388,7 @@ void callRagWithRerank() {
// Step 2 - Create embeddings and save to vector store
logger.info("Creating Embeddings...");
- VectorStore vectorStore = new SimpleVectorStore(dashscopeEmbeddingModel);
+ VectorStore vectorStore = SimpleVectorStore.builder(dashscopeEmbeddingModel).build();
vectorStore.add(documents);
// Step3 - Retrieve and llm generate
diff --git a/spring-ai-alibaba-core/src/test/java/com/alibaba/cloud/ai/dashscope/rag/AnalyticdbVectorTest.java b/spring-ai-alibaba-core/src/test/java/com/alibaba/cloud/ai/dashscope/rag/AnalyticdbVectorTest.java
index e112bdb60..39422c07b 100644
--- a/spring-ai-alibaba-core/src/test/java/com/alibaba/cloud/ai/dashscope/rag/AnalyticdbVectorTest.java
+++ b/spring-ai-alibaba-core/src/test/java/com/alibaba/cloud/ai/dashscope/rag/AnalyticdbVectorTest.java
@@ -27,7 +27,6 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.Random;
@SpringBootTest
@EnabledIfEnvironmentVariable(named = "ANALYTICDB_SECRET_KEY", matches = ".+")
@@ -47,7 +46,9 @@ public void init() throws Exception {
config.setNamespace("llama");
config.setNamespacePassword("llamapassword");
config.setEmbeddingDimension(3L);
- analyticdbVector = new AnalyticdbVector("test_llama", config);
+
+ // TODO 需要修改
+ analyticdbVector = new AnalyticdbVector("test_llama", config, null);
}
@Test
@@ -59,15 +60,15 @@ void testGetInstance() throws Exception {
int length = 1536; // Array length
float min = 0f; // smallest value
float max = 1f; // the largest value
- float[] em = new float[length]; // create float array
- Random random = new Random();
- for (int i = 0; i < length; i++) {
- em[i] = min + (max - min) * random.nextFloat();
- }
- document.setEmbedding(em);
+ // float[] em = new float[length]; // create float array
+ // Random random = new Random();
+ // for (int i = 0; i < length; i++) {
+ // em[i] = min + (max - min) * random.nextFloat();
+ // }
+ // document.setEmbedding(em);
list.add(document);
analyticdbVector.add(list);
- SearchRequest searchRequest = SearchRequest.query("hello");
+ SearchRequest searchRequest = SearchRequest.builder().query("hello").build();
List documents = analyticdbVector.similaritySearch(searchRequest);
System.out.println(documents.get(0).getContent());
diff --git a/spring-ai-alibaba-core/src/test/java/com/alibaba/cloud/ai/dashscope/rerank/DashScopeRerankModelTest.java b/spring-ai-alibaba-core/src/test/java/com/alibaba/cloud/ai/dashscope/rerank/DashScopeRerankModelTest.java
index f5674e4ba..d0935edf6 100644
--- a/spring-ai-alibaba-core/src/test/java/com/alibaba/cloud/ai/dashscope/rerank/DashScopeRerankModelTest.java
+++ b/spring-ai-alibaba-core/src/test/java/com/alibaba/cloud/ai/dashscope/rerank/DashScopeRerankModelTest.java
@@ -70,10 +70,10 @@ public class DashScopeRerankModelTest {
void testRerank() {
String query = "什么是文本排序模型";
List documents = new ArrayList<>();
- documents.add(Document.builder().withContent("文本排序模型广泛用于搜索引擎和推荐系统中,它们根据文本相关性对候选文本进行排序").build());
- documents.add(Document.builder().withContent("量子计算是计算科学的一个前沿领域").build());
- documents.add(Document.builder().withContent("预训练语言模型的发展给文本排序模型带来了新的进展").build());
- documents.add(Document.builder().withContent("文本排序模型能够帮助检索增强生成提升效果").build());
+ documents.add(Document.builder().text("文本排序模型广泛用于搜索引擎和推荐系统中,它们根据文本相关性对候选文本进行排序").build());
+ documents.add(Document.builder().text("量子计算是计算科学的一个前沿领域").build());
+ documents.add(Document.builder().text("预训练语言模型的发展给文本排序模型带来了新的进展").build());
+ documents.add(Document.builder().text("文本排序模型能够帮助检索增强生成提升效果").build());
RerankRequest request = new RerankRequest(query, documents);
RerankResponse response = dashscopeRerankModel.call(request);
diff --git a/spring-ai-alibaba-core/src/test/java/com/alibaba/cloud/ai/evaluation/EvaluationIT.java b/spring-ai-alibaba-core/src/test/java/com/alibaba/cloud/ai/evaluation/EvaluationIT.java
index 33d825097..ea7b326c6 100644
--- a/spring-ai-alibaba-core/src/test/java/com/alibaba/cloud/ai/evaluation/EvaluationIT.java
+++ b/spring-ai-alibaba-core/src/test/java/com/alibaba/cloud/ai/evaluation/EvaluationIT.java
@@ -30,10 +30,9 @@
import org.springframework.ai.chat.messages.AssistantMessage;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.document.Document;
-import org.springframework.ai.document.DocumentRetriever;
import org.springframework.ai.evaluation.EvaluationRequest;
import org.springframework.ai.evaluation.EvaluationResponse;
-import org.springframework.ai.model.Content;
+import org.springframework.ai.rag.retrieval.search.DocumentRetriever;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
@@ -45,7 +44,6 @@
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
-import java.util.stream.Collectors;
/**
* Title React agent test cases.
@@ -97,7 +95,7 @@ void correctnessEvaluateTest() throws IOException {
AnswerCorrectnessEvaluator evaluator = new AnswerCorrectnessEvaluator(ChatClient.builder(dashscopeChatModel),
correctnessResource.getContentAsString(StandardCharsets.UTF_8));
EvaluationRequest evaluationRequest = new EvaluationRequest(userText,
- List.of(new AssistantMessage(expectedResult)), content);
+ List.of(Document.builder().text(expectedResult).build()), content);
EvaluationResponse evaluationResponse = evaluator.evaluate(evaluationRequest);
Assertions.assertTrue(evaluationResponse.isPass());
@@ -123,7 +121,7 @@ void relevanceEvaluateTest() throws IOException {
AnswerRelevancyEvaluator evaluator = new AnswerRelevancyEvaluator(ChatClient.builder(dashscopeChatModel),
relevancyResource.getContentAsString(StandardCharsets.UTF_8), objectMapper);
EvaluationRequest evaluationRequest = new EvaluationRequest(userText,
- List.of(new AssistantMessage(expectedResult)), content);
+ List.of(Document.builder().text(expectedResult).build()), content);
EvaluationResponse evaluationResponse = evaluator.evaluate(evaluationRequest);
Assertions.assertTrue(evaluationResponse.isPass());