Compare commits

..

4 Commits

Author SHA1 Message Date
Emanuel Ferreira b75390f6af docs: index multimodal 2025-09-16 18:32:43 -03:00
Peter Rowlands (변기호) bc2f04379b py: bump version to v.0.6.66 (#920) 2025-09-16 19:34:18 +09:00
Peter Rowlands (변기호) f9f951d5d8 parse: expose spreadsheet_force_formula_computation option (#919) 2025-09-16 19:28:03 +09:00
Emmanuel Ferdman 355129fea5 Fix colab broken links (#750)
Signed-off-by: Emmanuel Ferdman <emmanuelferdman@gmail.com>
2025-09-14 23:10:21 +02:00
17 changed files with 853 additions and 309 deletions
@@ -7,7 +7,7 @@
"source": [
"# Extraction and Analysis over a Fidelity Multi-Fund Annual Report\n",
"\n",
"<a href=\"https://colab.research.google.com/github/run-llama/llama_cloud_services-demo/blob/main/examples/extract/asset_manager_fund_analysis.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>\n",
"<a href=\"https://colab.research.google.com/github/run-llama/llama_cloud_services/blob/main/examples/extract/asset_manager_fund_analysis.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>\n",
"\n",
"In this notebook we show you how to create an agentic document workflow over a complex document that contains annual reports for multiple funds - each fund reports financials in a standardized reporting structure, and it's all consolidated in the same document.\n",
"\n",
@@ -7,7 +7,7 @@
"source": [
"# Automotive Equity Research: A Multi-Step Agentic Workflow\n",
"\n",
"<a href=\"https://colab.research.google.com/github/run-llama/llama_cloud_services-demo/blob/main/examples/extract/automotive_sector_analysis.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>\n",
"<a href=\"https://colab.research.google.com/github/run-llama/llama_cloud_services/blob/main/examples/extract/automotive_sector_analysis.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>\n",
"\n",
"This notebook demonstrates an endtoend agentic workflow using LlamaExtract and the LlamaIndex eventdriven workflow framework for automotive sector analysis.\n",
"\n",
+2 -2
View File
@@ -1035,7 +1035,7 @@
],
"metadata": {
"kernelspec": {
"display_name": ".venv",
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
@@ -1052,5 +1052,5 @@
}
},
"nbformat": 4,
"nbformat_minor": 2
"nbformat_minor": 4
}
File diff suppressed because one or more lines are too long
@@ -7,7 +7,7 @@
"source": [
"# Dynamic Section Retrieval with LlamaParse\n",
"\n",
"<a href=\"https://colab.research.google.com/github/run-llama/llama_cloud_services-demo/blob/main/examples/parse/advanced_rag/dynamic_section_retrieval.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>\n",
"<a href=\"https://colab.research.google.com/github/run-llama/llama_cloud_services/blob/main/examples/parse/advanced_rag/dynamic_section_retrieval.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>\n",
"\n",
"This notebook showcases a concept called \"dynamic section retrieval\".\n",
"\n",
+1 -1
View File
@@ -6,7 +6,7 @@
"source": [
"# Advanced RAG with LlamaParse\n",
"\n",
"<a href=\"https://colab.research.google.com/github/run-llama/llama_parse/blob/main/examples/demo_advanced.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>\n",
"<a href=\"https://colab.research.google.com/github/run-llama/llama_parse/blob/main/examples/parse/demo_advanced.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>\n",
"\n",
"This notebook is a complete walkthrough for using LlamaParse with advanced indexing/retrieval techniques in LlamaIndex over the Apple 10K Filing. \n",
"\n",
+1 -1
View File
@@ -6,7 +6,7 @@
"source": [
"# RAG with Excel Spreadsheet using LlamaPrase\n",
"\n",
"<a href=\"https://colab.research.google.com/github/run-llama/llama_cloud_services/blob/main/examples/demo_excel.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>\n",
"<a href=\"https://colab.research.google.com/github/run-llama/llama_cloud_services/blob/main/examples/parse/demo_excel.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>\n",
"\n",
"This notebook shows you using LlamaParse with Excel Spreadsheet.\n",
"\n",
+1 -1
View File
@@ -7,7 +7,7 @@
"source": [
"# Download Charts\n",
"\n",
"<a href=\"https://colab.research.google.com/github/run-llama/llama_cloud_services/blob/main/examples/demo_get_charts.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>\n",
"<a href=\"https://colab.research.google.com/github/run-llama/llama_cloud_services/blob/main/examples/parse/demo_get_charts.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>\n",
"\n",
"This notebook demonstrates how to download charts from a document using the result object.\n",
"\n",
+4 -4
View File
@@ -6,7 +6,7 @@
"source": [
"# LlamaParse - Fast checking Insurance Contract for Coverage\n",
"\n",
"<a href=\"https://colab.research.google.com/github/run-llama/llama_cloud_services/blob/main/examples/demo_insurance.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>\n",
"<a href=\"https://colab.research.google.com/github/run-llama/llama_cloud_services/blob/main/examples/parse/demo_insurance.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>\n",
"\n",
"In this notebook we will look at how LlamaParse can be used to extract structured coverage information from an insurance policy.\n",
"\n",
@@ -36,7 +36,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"## Download an insurance policy fron IRDAI\n",
"## Download an insurance policy from IRDAI\n",
"\n",
"The Insurance Regulatory and Development Authority of India (IRDAI) maintains a great resource: https://policyholder.gov.in/web/guest/non-life-insurance-products where all insurance policies available in India are publicly available for download! Let's download a complex health insurance policy as an example."
]
@@ -228,11 +228,11 @@
" result_type=\"markdown\",\n",
" system_prompt_append=\"\"\"\n",
"This document is an insurance policy.\n",
"When a benefits/coverage/exlusion is describe in the document ammend to it add a text in the follwing benefits string format (where coverage could be an exclusion).\n",
"When a benefits/coverage/exlusion is describe in the document amend to it add a text in the following benefits string format (where coverage could be an exclusion).\n",
"\n",
"For {nameofrisk} and in this condition {whenDoesThecoverageApply} the coverage is {coverageDescription}. \n",
" \n",
"If the document contain a benefits TABLE that describe coverage amounts, do not ouput it as a table, but instead as a list of benefits string.\n",
"If the document contain a benefits TABLE that describe coverage amounts, do not output it as a table, but instead as a list of benefits string.\n",
" \n",
"\"\"\",\n",
").aparse(\"./policy.pdf\")\n",
+1 -1
View File
@@ -7,7 +7,7 @@
"source": [
"# LlamaParse `JobResult` Tour\n",
"\n",
"<a href=\"https://colab.research.google.com/github/run-llama/llama_cloud_services/blob/main/examples/demo_json.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>\n",
"<a href=\"https://colab.research.google.com/github/run-llama/llama_cloud_services/blob/main/examples/parse/demo_json.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>\n",
"\n",
"The `JobResult` object is the main object returned by the LlamaParse API. It contains all the information about the job, including the parsed data, metadata, and any errors.\n",
"\n",
+1 -1
View File
@@ -9,7 +9,7 @@
"\n",
"LlamaParse supports users to specify a `language` parameter before uploading documents, giving users better OCR capabilities over non-English PDFs, parsing images into more accurate representations.\n",
"\n",
"You can specify 80+ different languages: see this file for a full list of supported languages: https://github.com/run-llama/llama_cloud_services/blob/main/llama_parse/base.py.\n",
"You can specify 80+ different languages: see this file for a full list of supported languages: https://github.com/run-llama/llama_cloud_services/blob/main/py/llama_cloud_services/parse/base.py.\n",
"\n",
"This notebook shows a demo of this in action. \n",
"\n",
+1 -1
View File
@@ -4,7 +4,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"<a href=\"https://colab.research.google.com/github/run-llama/llama_cloud_services/blob/main/examples/excel/o1_excel_rag.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
"<a href=\"https://colab.research.google.com/github/run-llama/llama_cloud_services/blob/main/examples/parse/excel/o1_excel_rag.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
]
},
{
@@ -740,7 +740,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"In this example, these pages aren't going to be that different when parsed, but we can verify which pages triggered auto-made by looking at the [JSON output](https://github.com/run-llama/llama_cloud_services/blob/main/examples/demo_json_tour.ipynb) of LlamaParse:"
"In this example, these pages aren't going to be that different when parsed, but we can verify which pages triggered auto-made by looking at the [JSON output](https://github.com/run-llama/llama_cloud_services/blob/main/examples/parse/demo_json_tour.ipynb) of LlamaParse:"
]
},
{
+40 -94
View File
@@ -420,6 +420,10 @@ class LlamaParse(BasePydanticReader):
default=False,
description="If set to true, the parser will extract sub-tables from the spreadsheet when possible (more than one table per sheet).",
)
spreadsheet_force_formula_computation: Optional[bool] = Field(
default=False,
description="If set to true, the parser will re-compute values for all spreadsheet cells containing formulas.",
)
specialized_chart_parsing_agentic: Optional[bool] = Field(
default=False,
description="If set to true, the parser will use a specialized agentic chart parsing model to extract data from charts. This model is able to understand the chart type and extract the data accordingly.",
@@ -965,6 +969,11 @@ class LlamaParse(BasePydanticReader):
if self.spreadsheet_extract_sub_tables:
data["spreadsheet_extract_sub_tables"] = self.spreadsheet_extract_sub_tables
if self.spreadsheet_force_formula_computation:
data[
"spreadsheet_force_formula_computation"
] = self.spreadsheet_force_formula_computation
if self.specialized_chart_parsing_agentic:
data[
"specialized_chart_parsing_agentic"
@@ -1073,11 +1082,7 @@ class LlamaParse(BasePydanticReader):
return current_interval # Default fallback
async def _get_job_result(
self,
job_id: str,
result_type: str,
verbose: bool = False,
raise_job_error: bool = True,
self, job_id: str, result_type: str, verbose: bool = False
) -> Dict[str, Any]:
start = time.time()
tries = 0
@@ -1110,25 +1115,7 @@ class LlamaParse(BasePydanticReader):
print(".", end="", flush=True)
current_interval = self._calculate_backoff(current_interval)
else:
if raise_job_error:
raise JobFailedException.from_result(result_json)
else:
error_code = result_json.get("error_code")
error_message = result_json.get("error_message")
error_parts = [f"Job ID: {job_id} failed with status: {status}"]
if error_code:
error_parts.append(f"Error code: {error_code}")
if error_message:
error_parts.append(f"Error message: {error_message}")
error_str = ", ".join(error_parts)
return {
"pages": [],
"job_metadata": {"job_pages": 0},
"error": error_str,
"status": status,
}
raise JobFailedException.from_result(result_json)
except (
httpx.ConnectError,
httpx.ReadError,
@@ -1159,7 +1146,6 @@ class LlamaParse(BasePydanticReader):
fs: Optional[AbstractFileSystem] = None,
result_type: Optional[str] = None,
num_workers: Optional[int] = None,
raise_job_error: bool = True,
) -> List[Tuple[str, Dict[str, Any]]]:
if self.partition_pages is None:
job_results = [
@@ -1168,7 +1154,6 @@ class LlamaParse(BasePydanticReader):
extra_info=extra_info,
fs=fs,
result_type=result_type,
raise_job_error=raise_job_error,
)
]
else:
@@ -1178,7 +1163,6 @@ class LlamaParse(BasePydanticReader):
fs=fs,
result_type=result_type,
num_workers=num_workers,
raise_job_error=raise_job_error,
)
return job_results
@@ -1188,7 +1172,6 @@ class LlamaParse(BasePydanticReader):
extra_info: Optional[dict] = None,
fs: Optional[AbstractFileSystem] = None,
result_type: Optional[str] = None,
raise_job_error: bool = True,
**create_kwargs: Any,
) -> Tuple[str, Dict[str, Any]]:
"""Create one parse job and wait for the result."""
@@ -1198,10 +1181,7 @@ class LlamaParse(BasePydanticReader):
if self.verbose:
print("Started parsing the file under job_id %s" % job_id)
result = await self._get_job_result(
job_id,
result_type or self.result_type.value,
verbose=self.verbose,
raise_job_error=raise_job_error,
job_id, result_type or self.result_type.value, verbose=self.verbose
)
return job_id, result
@@ -1212,7 +1192,6 @@ class LlamaParse(BasePydanticReader):
fs: Optional[AbstractFileSystem] = None,
result_type: Optional[str] = None,
num_workers: Optional[int] = None,
raise_job_error: bool = True,
) -> List[Tuple[str, Dict[str, Any]]]:
"""Partition a file and run separate parse jobs per partition segment."""
assert self.partition_pages is not None
@@ -1227,7 +1206,6 @@ class LlamaParse(BasePydanticReader):
extra_info=extra_info,
fs=fs,
result_type=result_type,
raise_job_error=raise_job_error,
partition_target_pages=target_pages,
)
for target_pages in partition_pages(
@@ -1255,33 +1233,28 @@ class LlamaParse(BasePydanticReader):
size = self.partition_pages
if not size:
break
# Fetch JSON result type first to get accurate pagination data
# and then fetch the user's desired result type if needed
job_id, json_result = await self._parse_one_unpartitioned(
file_path,
extra_info=extra_info,
fs=fs,
result_type=ResultType.JSON.value,
raise_job_error=raise_job_error,
partition_target_pages=f"{total}-{total + size - 1}",
)
if json_result.get("error"):
if results and "NO_DATA_FOUND_IN_FILE" in json_result.get("error", ""):
return results
results.append((job_id, json_result))
return results
result_type = result_type or self.result_type.value
if result_type == ResultType.JSON.value:
job_result = json_result
else:
job_result = await self._get_job_result(
job_id,
result_type,
verbose=self.verbose,
raise_job_error=raise_job_error,
try:
# Fetch JSON result type first to get accurate pagination data
# and then fetch the user's desired result type if needed
job_id, json_result = await self._parse_one_unpartitioned(
file_path,
extra_info=extra_info,
fs=fs,
result_type=ResultType.JSON.value,
partition_target_pages=f"{total}-{total + size - 1}",
)
result_type = result_type or self.result_type.value
if result_type == ResultType.JSON.value:
job_result = json_result
else:
job_result = await self._get_job_result(
job_id, result_type, verbose=self.verbose
)
except JobFailedException as e:
if results and e.error_code == "NO_DATA_FOUND_IN_FILE":
# Expected when we try to read past the end of the file
return results
raise
results.append((job_id, job_result))
if len(json_result["pages"]) < size:
break
@@ -1395,7 +1368,6 @@ class LlamaParse(BasePydanticReader):
extra_info: Optional[dict] = None,
fs: Optional[AbstractFileSystem] = None,
num_workers: Optional[int] = None,
raise_job_error: bool = True,
) -> List[JobResult]:
job_results = await self._parse_one(
file_path,
@@ -1403,7 +1375,6 @@ class LlamaParse(BasePydanticReader):
fs=fs,
result_type=ResultType.JSON.value,
num_workers=num_workers,
raise_job_error=raise_job_error,
)
return [
JobResult(
@@ -1423,7 +1394,6 @@ class LlamaParse(BasePydanticReader):
file_path: Union[List[FileInput], FileInput],
extra_info: Optional[dict] = None,
fs: Optional[AbstractFileSystem] = None,
raise_job_error: bool = True,
) -> Union[List["JobResult"], "JobResult"]:
"""
Parse the file and return a JobResult object instead of Document objects.
@@ -1435,7 +1405,6 @@ class LlamaParse(BasePydanticReader):
file_path: Path to the file to parse. Can be a string, path, bytes, file-like object, or a list of these.
extra_info: Additional metadata to include in the result.
fs: Optional filesystem to use for reading files.
raise_job_error: If True (default), raises JobFailedException when jobs fail. If False, returns JobResult objects with error information.
Returns:
JobResult object or list of JobResult objects if either multiple files were provided or file(s) were partitioned before parsing.
@@ -1451,11 +1420,7 @@ class LlamaParse(BasePydanticReader):
else:
file_name = str(file_path)
result = await self._aparse_one(
file_path,
file_name,
extra_info=extra_info,
fs=fs,
raise_job_error=raise_job_error,
file_path, file_name, extra_info=extra_info, fs=fs
)
return result[0] if len(result) == 1 else result
@@ -1481,7 +1446,6 @@ class LlamaParse(BasePydanticReader):
extra_info=extra_info,
fs=fs,
num_workers=1,
raise_job_error=raise_job_error,
)
for i, f in enumerate(file_path)
],
@@ -1507,7 +1471,6 @@ class LlamaParse(BasePydanticReader):
file_path: Union[List[FileInput], FileInput],
extra_info: Optional[dict] = None,
fs: Optional[AbstractFileSystem] = None,
raise_job_error: bool = True,
) -> Union[List["JobResult"], "JobResult"]:
"""
Parse the file and return a JobResult object instead of Document objects.
@@ -1519,17 +1482,12 @@ class LlamaParse(BasePydanticReader):
file_path: Path to the file to parse. Can be a string, path, bytes, file-like object, or a list of these.
extra_info: Additional metadata to include in the result.
fs: Optional filesystem to use for reading files.
raise_job_error: If True (default), raises JobFailedException when jobs fail. If False, returns JobResult objects with error information.
Returns:
JobResult object or list of JobResult objects if multiple files were provided
"""
try:
return asyncio_run(
self.aparse(
file_path, extra_info, fs=fs, raise_job_error=raise_job_error
)
)
return asyncio_run(self.aparse(file_path, extra_info, fs=fs))
except RuntimeError as e:
if nest_asyncio_err in str(e):
raise RuntimeError(nest_asyncio_msg)
@@ -1796,7 +1754,7 @@ class LlamaParse(BasePydanticReader):
return sub_docs
async def aget_result(
self, job_id: Union[str, List[str]], raise_job_error: bool = True
self, job_id: Union[str, List[str]]
) -> Union[JobResult, List[JobResult]]:
"""
Return JobResult object for previously parsed job(s).
@@ -1805,17 +1763,13 @@ class LlamaParse(BasePydanticReader):
Args:
job_id: Job ID or list of multiple Job IDs to be retrieved.
raise_job_error: If True (default), raises JobFailedException when jobs fail. If False, returns JobResult objects with error information.
Returns:
JobResult object or list of JobResult objects if multiple job IDs were provided.
"""
if isinstance(job_id, str):
result = await self._get_job_result(
job_id,
ResultType.JSON.value,
verbose=self.verbose,
raise_job_error=raise_job_error,
job_id, ResultType.JSON.value, verbose=self.verbose
)
return JobResult(
job_id=job_id,
@@ -1829,12 +1783,7 @@ class LlamaParse(BasePydanticReader):
elif isinstance(job_id, list):
results = []
jobs = [
self._get_job_result(
id_,
ResultType.JSON.value,
verbose=self.verbose,
raise_job_error=raise_job_error,
)
self._get_job_result(id_, ResultType.JSON.value, verbose=self.verbose)
for id_ in job_id
]
results = await run_jobs(
@@ -1859,7 +1808,7 @@ class LlamaParse(BasePydanticReader):
raise ValueError("The input job_id must be a string or a list of strings.")
def get_result(
self, job_id: Union[str, List[str]], raise_job_error: bool = True
self, job_id: Union[str, List[str]]
) -> Union[JobResult, List[JobResult]]:
"""
Return JobResult object for previously parsed job(s).
@@ -1868,15 +1817,12 @@ class LlamaParse(BasePydanticReader):
Args:
job_id: Job ID or list of multiple Job IDs to be retrieved.
raise_job_error: If True (default), raises JobFailedException when jobs fail. If False, returns JobResult objects with error information.
Returns:
JobResult object or list of JobResult objects if multiple job IDs were provided.
"""
try:
return asyncio_run(
self.aget_result(job_id, raise_job_error=raise_job_error)
)
return asyncio_run(self.aget_result(job_id))
except RuntimeError as e:
if nest_asyncio_err in str(e):
raise RuntimeError(nest_asyncio_msg)
+2 -2
View File
@@ -11,13 +11,13 @@ dev = [
[project]
name = "llama-parse"
version = "0.6.65"
version = "0.6.66"
description = "Parse files into RAG-Optimized formats."
authors = [{name = "Logan Markewich", email = "logan@llamaindex.ai"}]
requires-python = ">=3.9,<4.0"
readme = "README.md"
license = "MIT"
dependencies = ["llama-cloud-services>=0.6.64"]
dependencies = ["llama-cloud-services>=0.6.66"]
[project.scripts]
llama-parse = "llama_parse.cli.main:parse"
+1 -1
View File
@@ -19,7 +19,7 @@ dev = [
[project]
name = "llama-cloud-services"
version = "0.6.65"
version = "0.6.66"
description = "Tailored SDK clients for LlamaCloud services."
authors = [{name = "Logan Markewich", email = "logan@runllama.ai"}]
requires-python = ">=3.9,<4.0"
-196
View File
@@ -2,7 +2,6 @@ import os
import pytest
import shutil
from typing import Optional, cast
from unittest.mock import patch
from fsspec.implementations.local import LocalFileSystem
from httpx import AsyncClient
@@ -205,10 +204,6 @@ async def test_get_result(markdown_parser: LlamaParse) -> None:
assert len(result.pages) == len(expected.pages)
@pytest.mark.skipif(
os.environ.get("LLAMA_CLOUD_API_KEY", "") == "",
reason="LLAMA_CLOUD_API_KEY not set",
)
@pytest.mark.asyncio
async def test_parse_audio() -> None:
parser = LlamaParse()
@@ -216,194 +211,3 @@ async def test_parse_audio() -> None:
result = await parser.aparse(filepath)
assert result.job_id is not None
@pytest.mark.asyncio
async def test_error_handling_with_raise_job_error_false() -> None:
"""Test that failed jobs return JobResult objects with error information when raise_job_error=False."""
parser = LlamaParse(api_key="test_key")
# Mock error result with full error information
mock_error_result = {
"pages": [],
"job_metadata": {"job_pages": 0},
"error": "Job ID: test_job_123 failed with status: ERROR, Error code: INVALID_FILE, Error message: File format not supported",
"status": "ERROR",
}
with patch.object(parser, "_create_job", return_value="test_job_123"), patch.object(
parser, "_get_job_result", return_value=mock_error_result
):
# Test aparse with raise_job_error=False
result = await parser.aparse("test_file.txt", raise_job_error=False)
assert isinstance(result, type(result)) # Check it's a JobResult
assert result.job_id == "test_job_123"
assert result.error is not None
assert "ERROR" in result.error
assert "INVALID_FILE" in result.error
assert "File format not supported" in result.error
assert len(result.pages) == 0
# Test parse (synchronous version) with raise_job_error=False
result_sync = parser.parse("test_file.txt", raise_job_error=False)
assert isinstance(result_sync, type(result_sync))
assert result_sync.job_id == "test_job_123"
assert result_sync.error is not None
assert "INVALID_FILE" in result_sync.error
assert "File format not supported" in result_sync.error
@pytest.mark.asyncio
async def test_error_handling_with_raise_job_error_true() -> None:
"""Test that failed jobs raise JobFailedException when raise_job_error=True (default behavior)."""
parser = LlamaParse(api_key="test_key")
# Mock that _get_job_result will raise JobFailedException when raise_job_error=True
from llama_cloud_services.parse.base import JobFailedException
def mock_get_job_result(job_id, result_type, verbose=False, raise_job_error=True):
if raise_job_error:
raise JobFailedException(
"test_job_123",
"ERROR",
error_code="INVALID_FILE",
error_message="File format not supported",
)
else:
return {
"pages": [],
"job_metadata": {"job_pages": 0},
"error": "Job ID: test_job_123 failed with status: ERROR, Error code: INVALID_FILE, Error message: File format not supported",
"status": "ERROR",
}
with patch.object(parser, "_create_job", return_value="test_job_123"), patch.object(
parser, "_get_job_result", side_effect=mock_get_job_result
):
# Test aparse with raise_job_error=True (default) - should raise exception
with pytest.raises(JobFailedException) as exc_info:
await parser.aparse("test_file.txt")
assert exc_info.value.job_id == "test_job_123"
assert exc_info.value.status == "ERROR"
assert exc_info.value.error_code == "INVALID_FILE"
# Test parse (synchronous version) with raise_job_error=True (default) - should raise exception
with pytest.raises(JobFailedException) as exc_info:
parser.parse("test_file.txt")
assert exc_info.value.job_id == "test_job_123"
assert exc_info.value.status == "ERROR"
@pytest.mark.asyncio
async def test_error_handling_with_minimal_fields() -> None:
"""Test error handling when only status is available (no error_code/error_message) with raise_job_error=False."""
parser = LlamaParse(api_key="test_key")
# Mock error result with minimal fields (only what's guaranteed)
mock_minimal_error_result = {
"pages": [],
"job_metadata": {"job_pages": 0},
"error": "Job ID: test_job_456 failed with status: CANCELED",
"status": "CANCELED",
}
with patch.object(parser, "_create_job", return_value="test_job_456"), patch.object(
parser, "_get_job_result", return_value=mock_minimal_error_result
):
# Test aparse with a minimal error response and raise_job_error=False
result = await parser.aparse("test_file.txt", raise_job_error=False)
assert isinstance(result, type(result))
assert result.job_id == "test_job_456"
assert result.error is not None
assert "CANCELED" in result.error
assert len(result.pages) == 0
@pytest.mark.asyncio
async def test_successful_job_still_works() -> None:
"""Test that successful jobs still work as before after error handling changes."""
parser = LlamaParse(api_key="test_key")
# Mock successful result
mock_success_result = {
"pages": [
{
"page": 0,
"text": "Sample text content",
"md": "# Sample markdown content",
"images": [],
"charts": [],
"tables": [],
"layout": [],
"items": [],
"status": "SUCCESS",
"links": [],
"width": 612.0,
"height": 792.0,
}
],
"job_metadata": {"job_pages": 1},
}
with patch.object(
parser, "_create_job", return_value="success_job_456"
), patch.object(parser, "_get_job_result", return_value=mock_success_result):
# Test aparse with a successful job (both with and without raise_job_error parameter)
result = await parser.aparse("test_file.txt")
assert isinstance(result, type(result))
assert result.job_id == "success_job_456"
assert result.error is None # No error for successful jobs
assert len(result.pages) == 1
assert result.pages[0].text == "Sample text content"
# Test with explicit raise_job_error=False (should work the same for successful jobs)
result2 = await parser.aparse("test_file.txt", raise_job_error=False)
assert isinstance(result2, type(result2))
assert result2.job_id == "success_job_456"
assert result2.error is None
assert len(result2.pages) == 1
assert result2.pages[0].text == "Sample text content"
@pytest.mark.asyncio
async def test_get_result_with_raise_job_error_parameter() -> None:
"""Test that get_result method respects the raise_job_error parameter."""
parser = LlamaParse(api_key="test_key")
# Mock error result
mock_error_result = {
"pages": [],
"job_metadata": {"job_pages": 0},
"error": "Job ID: test_job_789 failed with status: ERROR, Error code: TIMEOUT, Error message: Job timed out",
"status": "ERROR",
}
with patch.object(parser, "_get_job_result", return_value=mock_error_result):
# Test aget_result with raise_job_error=False
result = await parser.aget_result("test_job_789", raise_job_error=False)
assert isinstance(result, type(result))
assert result.job_id == "test_job_789"
assert result.error is not None
assert "TIMEOUT" in result.error
assert len(result.pages) == 0
# Test get_result (synchronous version) with raise_job_error=False
result_sync = parser.get_result("test_job_789", raise_job_error=False)
assert isinstance(result_sync, type(result_sync))
assert result_sync.job_id == "test_job_789"
assert result_sync.error is not None
assert "TIMEOUT" in result_sync.error