# -*- coding: utf-8 -*- """Test the RAG reader implementations.""" import os import json from unittest.async_case import IsolatedAsyncioTestCase from agentscope.rag import ( TextReader, PDFReader, WordReader, ExcelReader, PowerPointReader, ) class RAGReaderText(IsolatedAsyncioTestCase): """Test cases for RAG reader implementations.""" async def test_text_reader(self) -> None: """Test the TextReader implementation.""" # Split by char reader = TextReader( chunk_size=10, split_by="char", ) docs = await reader( text="".join(str(i) for i in range(22)), ) self.assertEqual(len(docs), 4) self.assertEqual( docs[0].metadata.content["text"], "0123456789", ) self.assertEqual( docs[1].metadata.content["text"], "1011121314", ) self.assertEqual( docs[2].metadata.content["text"], "1516171819", ) self.assertEqual( docs[3].metadata.content["text"], "2021", ) # Split by sentence reader = TextReader( chunk_size=10, split_by="sentence", ) docs = await reader( text="012345678910111213. 141516171819! 2021? 22", ) self.assertEqual( [_.metadata.content["text"] for _ in docs], ["0123456789", "10111213.", "1415161718", "19!", "2021?", "22"], ) docs = await reader( text="01234. 56789! 10111213? 14151617..", ) self.assertEqual( [_.metadata.content["text"] for _ in docs], ["01234.", "56789!", "10111213?", "14151617.."], ) # Split by paragraph reader = TextReader( chunk_size=5, split_by="paragraph", ) docs = await reader( text="01234\n\n5678910111213.\n\n\n1415", ) self.assertEqual( [_.metadata.content["text"] for _ in docs], ["01234", "56789", "10111", "213.", "1415"], ) async def test_pdf_reader(self) -> None: """Test the PDFReader implementation.""" reader = PDFReader( chunk_size=200, split_by="sentence", ) pdf_path = os.path.join( os.path.abspath(os.path.dirname(__file__)), "../examples/functionality/rag/example.pdf", ) docs = await reader(pdf_path=pdf_path) self.assertEqual(len(docs), 17) self.assertEqual( [_.metadata.content["text"] for _ in docs][:2], [ "1\nThe Great Transformations: From Print to Space\n" "The invention of the printing press in the 15th century " "marked a revolutionary change in \nhuman history.", "Johannes Gutenberg's innovation democratized knowledge and " "made books \naccessible to the common people.", ], ) async def test_word_reader_with_images_and_tables(self) -> None: """Test the WordReader implementation with images and table separation.""" # Test with images and table separation enabled reader = WordReader( chunk_size=200, split_by="sentence", include_image=True, separate_table=True, ) word_path = os.path.join( os.path.abspath(os.path.dirname(__file__)), "../tests/test.docx", ) docs = await reader(word_path=word_path) self.assertListEqual( [_.metadata.content["type"] for _ in docs], ["text"] * 4 + ["image"] * 2 + ["text", "image", "text", "text"], ) self.assertEqual( [_.metadata.content.get("text") for _ in docs], [ "AgentScope\n" "标题2\n" "This is a test file for AgentScope word reader.", "标题3\nTest table:", "| Header1 | Header2 | Header3 | Header4 |\n" "| --- | --- | --- | --- |\n" "| 1 | 2 | 3 | 4 |\n" "| 5 | 6 | 7 | 8 |", "\nTest list:\nAlice\nBob\nCharlie\nDavid\nTest image:", None, # image None, # image "\nText between images", None, # image "\nText between image and table", "| a | b | c |\n| --- | --- | --- |\n| d\ne | f | g |", ], ) self.assertEqual( [ _.metadata.content["source"]["media_type"] for _ in docs if _.metadata.content["type"] == "image" ], ["image/png", "image/png", "image/png"], ) async def test_excel_reader_with_images_and_tables(self) -> None: """Test the ExcelReader implementation with images and table separation.""" # Test with images and table separation enabled reader = ExcelReader( chunk_size=200, split_by="sentence", include_image=True, separate_table=True, include_cell_coordinates=True, table_format="markdown", ) excel_path = os.path.join( os.path.abspath(os.path.dirname(__file__)), "test.xlsx", ) docs = await reader(excel_path=excel_path) # Verify document types match expected sequence # Expected: table blocks from first sheet, then image (row 9), # then table blocks from second sheet # Order is based on row positions: table (row 0-5) → image (row 9) # → table (row 0-4) # Note: with include_cell_coordinates=True, cell coordinates are added # to each cell (e.g., [A1], [B1], etc.), which increases text length # and results in more chunks self.assertListEqual( [_.metadata.content["type"] for _ in docs], ["text"] * 3 + ["image"] * 1 + ["text"] * 5, ) # Verify exact document content doc_texts = [_.metadata.content.get("text") for _ in docs] # Verify sheet headers and table content with cell coordinates # First text block should contain Employee Info sheet header and table # Note: Due to chunk_size=200, the rows are truncated # Order: table (row 0-5) → image (row 9) → table (row 0-4) self.assertEqual( doc_texts[0], "Sheet: Employee Info\n" "| [A1] John Smith | [B1] 25 | [C1] Engineering | " "[D1] 8000 | [E1] 2020-01-15 |\n" "| --- | --- | --- | --- | --- |\n" "| [A2] Jane Doe | [B2] 30 | [C2] Sales | " "[D2] 12000 | [E2] 2019-03-2", ) # Second text block continues the employee table self.assertEqual( doc_texts[1], "0 |\n" "| [A3] Mike \\| Johnson | [B3] 35 | [C3] HR | " "[D3] 9000 | [E3] 2021-06-10 |\n" "| [A4] Sarah Wilson | [B4] 28 | [C4] Finance | " "[D4] 10000 | [E4] 2020-09-05 |\n" "| [A5] David Brown | [B5] 32 | [C5] Marketi", ) # Third text block continues the employee table self.assertEqual( doc_texts[2], "ng | [D5] 11000 | [E5] 2018-12-01 |", ) # Image block (text is None) self.assertIsNone(doc_texts[3]) # Fourth text block should contain Product Info sheet header and # start of table self.assertEqual( doc_texts[4], "Sheet: Product Info\n" "| [A1] Product A | [B1] 100 | [C1] 50 | " "[D1] High-quality Product A, suitable for various scenarios.", ) # Remaining blocks continue the product table self.assertEqual( doc_texts[5], "|\n" "| --- | --- | --- | --- |\n" "| [A2] Product B | [B2] 200 | [C2] 30 | " "[D2] Product B offers excellent performance.", ) self.assertEqual( doc_texts[6], "|\n" "| [A3] Product C | [B3] 300 | [C3] 20 | " "[D3] Product C is a market-leading solution.", ) self.assertEqual( doc_texts[7], "|\n" "| [A4] Product D | [B4] 400 | [C4] 40 | " "[D4] Product D provides comprehensive functionality.", ) # Verify image media types self.assertEqual( [ _.metadata.content["source"]["media_type"] for _ in docs if _.metadata.content["type"] == "image" ], ["image/png"], ) async def test_ppt_reader_with_images_and_tables(self) -> None: """Test the PowerPointReader implementation with images and table separation.""" # Test with images and table separation enabled (using defaults) reader = PowerPointReader( chunk_size=200, split_by="sentence", separate_table=True, ) ppt_path = os.path.join( os.path.abspath(os.path.dirname(__file__)), "../tests/test.pptx", ) docs = await reader(ppt_path=ppt_path) # Verify document types match expected sequence # Expected: text blocks from slides, then table, then image self.assertListEqual( [_.metadata.content["type"] for _ in docs], ["text"] * 5 + ["image"] * 1 + ["text"], ) # Verify exact document content doc_texts = [_.metadata.content.get("text") for _ in docs] # Verify slide content (with slide tags by default) self.assertEqual( doc_texts[0], "\nAgentScope\nText content in slide 1\n", ) self.assertEqual( doc_texts[1], "\nTitle 2\nText content above table", ) # Table should be extracted as a separate block with Markdown format self.assertEqual( doc_texts[2], "| Name | Age | Career |\n" "| --- | --- | --- |\n" "| Alice | 25 | Teacher |\n" "| Bob | 26 | Doctor |", ) self.assertEqual( doc_texts[3], "Text content below table\n", ) self.assertEqual( doc_texts[4], "\nTitle 3\ntext content above image", ) # Image block self.assertIsNone(doc_texts[5]) self.assertEqual( doc_texts[6], "text content below image\n", ) # Verify image media types self.assertEqual( [ _.metadata.content["source"]["media_type"] for _ in docs if _.metadata.content["type"] == "image" ], ["image/png"], ) async def test_ppt_reader_with_json_table_format(self) -> None: """Test the PowerPointReader with JSON table format.""" reader = PowerPointReader( chunk_size=500, split_by="sentence", include_image=False, separate_table=True, table_format="json", ) ppt_path = os.path.join( os.path.abspath(os.path.dirname(__file__)), "../tests/test.pptx", ) docs = await reader(ppt_path=ppt_path) # Find the table block table_texts = [ _.metadata.content.get("text") for _ in docs if _.metadata.content.get("text") and "JSON array" in _.metadata.content.get("text", "") ] # Verify we have a table in JSON format self.assertEqual(len(table_texts), 1) # Extract JSON part (after the system-info tag) table_text = table_texts[0] json_part = table_text.split("\n", 1)[1] # Verify it's valid JSON and matches expected table content parsed = json.loads(json_part) self.assertEqual( parsed, [ ["Name", "Age", "Career"], ["Alice", "25", "Teacher"], ["Bob", "26", "Doctor"], ], ) async def test_ppt_reader_without_image(self) -> None: """Test the PowerPointReader without image extraction.""" reader = PowerPointReader( chunk_size=200, split_by="sentence", include_image=False, separate_table=True, ) ppt_path = os.path.join( os.path.abspath(os.path.dirname(__file__)), "../tests/test.pptx", ) docs = await reader(ppt_path=ppt_path) # Verify no image blocks are present image_blocks = [ _ for _ in docs if _.metadata.content["type"] == "image" ] self.assertEqual(len(image_blocks), 0) # All blocks should be text type self.assertTrue( all(_.metadata.content["type"] == "text" for _ in docs), ) async def test_ppt_reader_merged_table(self) -> None: """Test the PowerPointReader with merged table (separate_table=False).""" reader = PowerPointReader( chunk_size=500, split_by="sentence", include_image=False, separate_table=False, ) ppt_path = os.path.join( os.path.abspath(os.path.dirname(__file__)), "../tests/test.pptx", ) docs = await reader(ppt_path=ppt_path) # When separate_table=False, table should be merged with adjacent text # Find the document that contains the table table_doc = None for doc in docs: text = doc.metadata.content.get("text", "") if "Name" in text and "Age" in text and "Career" in text: table_doc = doc break self.assertIsNotNone(table_doc) # The table should be merged with surrounding text (with slide tags) table_text = table_doc.metadata.content.get("text", "") self.assertEqual( table_text, "\n" "Title 2\n" "Text content above table\n" "| Name | Age | Career |\n" "| --- | --- | --- |\n" "| Alice | 25 | Teacher |\n" "| Bob | 26 | Doctor |\n" "\n" "Text content below table\n" "", ) async def test_ppt_reader_without_slide_tags(self) -> None: """Test the PowerPointReader without slide prefix/suffix XML tags.""" reader = PowerPointReader( chunk_size=500, split_by="sentence", include_image=False, separate_table=False, slide_prefix=None, slide_suffix=None, ) ppt_path = os.path.join( os.path.abspath(os.path.dirname(__file__)), "../tests/test.pptx", ) docs = await reader(ppt_path=ppt_path) # Without slide_prefix/suffix, content should not have XML tags doc_texts = [_.metadata.content.get("text") for _ in docs] # Verify exact content without slide tags self.assertEqual( doc_texts[0], "AgentScope\nText content in slide 1", ) self.assertEqual( doc_texts[1], "Title 2\n" "Text content above table\n" "| Name | Age | Career |\n" "| --- | --- | --- |\n" "| Alice | 25 | Teacher |\n" "| Bob | 26 | Doctor |\n" "\n" "Text content below table", ) self.assertEqual( doc_texts[2], "Title 3\ntext content above image\ntext content below image", )