Compare commits

..

7 Commits

Author SHA1 Message Date
Adrian Lyjak b8f95c6a3d Update pyproject.toml
back out of version bump
2026-01-14 13:00:02 -05:00
Adrian Lyjak 878605b347 Update package.json
back out of version bump
2026-01-14 12:59:43 -05:00
Adrian Lyjak 22130a35d7 Update pyproject.toml
back out of version bump
2026-01-14 12:59:27 -05:00
Adrian Lyjak ed4b55305c Update package.json
back out of version bump
2026-01-14 12:59:00 -05:00
Adrian Lyjak 4b9d48f5e6 Update ninety-goats-look.md
Make it a patch version
2026-01-14 12:58:38 -05:00
Pierre-Loic doulcet ae6d6c276f changeset 2026-01-14 18:57:30 +01:00
Pierre-Loic doulcet 25f9efd5bc remove extension filter 2026-01-14 18:41:23 +01:00
17 changed files with 65 additions and 199 deletions
+6
View File
@@ -0,0 +1,6 @@
---
"llama_parse": patch
"llama-cloud-services-py": patch
---
Remove extension filter
-13
View File
@@ -1,18 +1,5 @@
# llama-cloud-services-py
## 0.6.91
### Patch Changes
- 07ec282: Bump up patch versions for python packages
- 3040951: Use error description in ExtractedData invalid extraction error
## 0.6.90
### Patch Changes
- 19cbb25: Remove extension filter
## 0.6.89
### Patch Changes
@@ -475,49 +475,26 @@ class ExtractedData(BaseModel, Generic[ExtractedT]):
},
)
except ValidationError as e:
# Capture the job-level error from the extraction run if available
job_error = result.error
invalid_item = ExtractedData[Dict[str, Any]].create(
data=result.data or {},
status="error",
field_metadata=field_metadata,
metadata={
"extraction_error": str(e),
**({"job_error": job_error} if job_error else {}),
**(metadata or {}),
},
metadata={"extraction_error": str(e), **(metadata or {})},
file_id=file_id,
file_name=file_name,
file_hash=file_hash,
)
raise InvalidExtractionData(invalid_item, extraction_error=job_error) from e
raise InvalidExtractionData(invalid_item) from e
class InvalidExtractionData(Exception):
"""
Exception raised when the extracted data does not conform to the schema.
Attributes:
invalid_item: The ExtractedData instance containing the invalid data and metadata
extraction_error: The error message from the extraction job, if available
"""
def __init__(
self,
invalid_item: ExtractedData[Dict[str, Any]],
extraction_error: Optional[str] = None,
):
def __init__(self, invalid_item: ExtractedData[Dict[str, Any]]):
self.invalid_item = invalid_item
self.extraction_error = extraction_error
# Build an informative error message
if extraction_error:
message = f"Extraction error: {extraction_error}"
else:
message = "Not able to parse the extracted data, parsed invalid format"
super().__init__(message)
super().__init__("Not able to parse the extracted data, parsed invalid format")
def calculate_overall_confidence(
+6 -4
View File
@@ -3,7 +3,7 @@ from typing import BinaryIO
import os
from pathlib import Path
from llama_cloud.client import AsyncLlamaCloud
from llama_cloud.types import File
from llama_cloud.types import File, FileCreate
from typing import Optional
from llama_cloud_services.utils import SourceText, FileInput
@@ -73,9 +73,11 @@ class FileClient:
presigned_url = await self.client.files.generate_presigned_url(
project_id=self.project_id,
organization_id=self.organization_id,
name=name,
external_file_id=external_file_id,
file_size=file_size,
request=FileCreate(
name=name,
external_file_id=external_file_id,
file_size=file_size,
),
)
httpx_client = self.client._client_wrapper.httpx_client
upload_response = await httpx_client.put(
+10 -19
View File
@@ -21,6 +21,7 @@ from llama_cloud import (
PipelineCreateTransformConfig,
PipelineFileCreateCustomMetadataValue,
PipelineType,
ProjectCreate,
ManagedIngestionStatus,
CloudDocumentCreate,
CloudDocument,
@@ -506,19 +507,14 @@ class LlamaCloudIndex(BaseManagedIndex):
client = get_client(api_key, base_url, app_url, timeout)
if project_id is None:
# get project by name
projects = client.projects.list_projects(
# create project if it doesn't exist
project = client.projects.upsert_project(
organization_id=organization_id,
project_name=project_name,
request=ProjectCreate(name=project_name),
)
if not projects:
raise ValueError(
f"Project '{project_name}' not found. Please create it first in the LlamaCloud UI."
)
project = projects[0]
project_id = project.id
if verbose:
print(f"Found project {project_id} with name {project_name}")
print(f"Created project {project_id} with name {project_name}")
# create pipeline
pipeline_create = PipelineCreate(
@@ -567,20 +563,15 @@ class LlamaCloudIndex(BaseManagedIndex):
app_url = app_url or os.environ.get("LLAMA_CLOUD_APP_URL", DEFAULT_APP_URL)
aclient = get_aclient(api_key, base_url, app_url, timeout)
# get project by name
projects = await aclient.projects.list_projects(
organization_id=organization_id, project_name=project_name
# create project if it doesn't exist
project = await aclient.projects.upsert_project(
organization_id=organization_id, request=ProjectCreate(name=project_name)
)
if not projects:
raise ValueError(
f"Project '{project_name}' not found. Please create it first in the LlamaCloud UI."
)
project = projects[0]
if project.id is None:
raise ValueError(f"Failed to get project {project_name}")
raise ValueError(f"Failed to create/get project {project_name}")
if verbose:
print(f"Found project {project.id} with name {project.name}")
print(f"Created project {project.id} with name {project.name}")
# create pipeline
pipeline_create = PipelineCreate(
-17
View File
@@ -1,22 +1,5 @@
# llama_parse
## 0.6.91
### Patch Changes
- 07ec282: Bump up patch versions for python packages
- Updated dependencies [07ec282]
- Updated dependencies [3040951]
- llama-cloud-services-py@0.6.91
## 0.6.90
### Patch Changes
- 19cbb25: Remove extension filter
- Updated dependencies [19cbb25]
- llama-cloud-services-py@0.6.90
## 0.6.89
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "llama_parse",
"version": "0.6.91",
"version": "0.6.89",
"description": "",
"main": "index.js",
"private": false,
+2 -2
View File
@@ -11,13 +11,13 @@ dev = [
[project]
name = "llama-parse"
version = "0.6.91"
version = "0.6.89"
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.91"]
dependencies = ["llama-cloud-services>=0.6.89"]
[project.scripts]
llama-parse = "llama_parse.cli.main:parse"
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "llama-cloud-services-py",
"version": "0.6.91",
"version": "0.6.89",
"private": false,
"license": "MIT",
"scripts": {},
+2 -2
View File
@@ -23,7 +23,7 @@ dev = [
[project]
name = "llama-cloud-services"
version = "0.6.91"
version = "0.6.89"
description = "Tailored SDK clients for LlamaCloud services."
authors = [{name = "Logan Markewich", email = "logan@runllama.ai"}]
requires-python = ">=3.9,<4.0"
@@ -31,7 +31,7 @@ readme = "README.md"
license = "MIT"
dependencies = [
"llama-index-core>=0.12.0",
"llama-cloud==0.1.46",
"llama-cloud==0.1.45",
"pydantic>=2.8,!=2.10",
"click>=8.1.7,<9",
"python-dotenv>=1.0.1,<2",
+2 -34
View File
@@ -1,43 +1,11 @@
import os
from typing import Any, Dict, List, Optional, Union
from llama_cloud.core.api_error import ApiError
from llama_cloud.types import ExtractConfig
from pydantic import BaseModel
from tenacity import (
retry,
retry_if_exception,
stop_after_attempt,
wait_exponential,
)
from llama_cloud_services.extract import ExtractionAgent, LlamaExtract
from typing import List
from llama_cloud_services.extract import LlamaExtract
# Global storage for agents to cleanup
_TEST_AGENTS_TO_CLEANUP: List[str] = []
def _is_rate_limit_error(exception: BaseException) -> bool:
"""Check if the exception is a rate limit error (429)."""
return isinstance(exception, ApiError) and exception.status_code == 429
@retry(
retry=retry_if_exception(_is_rate_limit_error),
wait=wait_exponential(multiplier=1, min=1, max=30),
stop=stop_after_attempt(5),
reraise=True,
)
def create_agent_with_retry(
extractor: LlamaExtract,
name: str,
data_schema: Union[Dict[str, Any], type[BaseModel]],
config: Optional[ExtractConfig] = None,
) -> ExtractionAgent:
"""Create an extraction agent with retry logic for rate limiting."""
return extractor.create_agent(name=name, data_schema=data_schema, config=config)
def pytest_configure(config):
"""Register custom markers for extract tests."""
config.addinivalue_line("markers", "agent_name: custom agent name for test")
+2 -2
View File
@@ -6,7 +6,7 @@ from pydantic import BaseModel
from llama_cloud_services.extract import LlamaExtract, ExtractionAgent, SourceText
from llama_cloud.types import ExtractConfig, ExtractMode, ExtractRun
from tests.extract.util import load_test_dotenv
from .conftest import register_agent_for_cleanup, create_agent_with_retry
from .conftest import register_agent_for_cleanup
load_test_dotenv()
@@ -87,7 +87,7 @@ def test_agent(llama_extract, test_agent_name, test_schema_dict, request):
except Exception as e:
print(f"Warning: Failed to cleanup existing agent: {e}")
agent = create_agent_with_retry(llama_extract, name=name, data_schema=schema)
agent = llama_extract.create_agent(name=name, data_schema=schema)
# Add agent to cleanup list via conftest helper
register_agent_for_cleanup(agent.id)
+3 -5
View File
@@ -8,7 +8,7 @@ import uuid
from llama_cloud.types import ExtractConfig, ExtractMode
from deepdiff import DeepDiff
from tests.extract.util import json_subset_match_score, load_test_dotenv
from .conftest import register_agent_for_cleanup, create_agent_with_retry
from .conftest import register_agent_for_cleanup
load_test_dotenv()
@@ -117,10 +117,8 @@ def extraction_agent(test_case: ExtractionTestCase, extractor: LlamaExtract):
except Exception as e:
print(f"Warning: Failed to cleanup existing agent: {str(e)}")
# Create new agent with retry logic for rate limiting
agent = create_agent_with_retry(
extractor, name=agent_name, data_schema=schema, config=test_case.config
)
# Create new agent
agent = extractor.create_agent(agent_name, schema, config=test_case.config)
# Register agent for cleanup at the end of the test session
register_agent_for_cleanup(agent.id)
+5 -8
View File
@@ -8,6 +8,7 @@ from llama_cloud import (
AutoTransformConfig,
PipelineCreate,
PipelineFileCreate,
ProjectCreate,
CompositeRetrievalMode,
LlamaParseParameters,
ReRankConfig,
@@ -59,15 +60,11 @@ def local_figures_file() -> str:
def _setup_index_with_file(
client: LlamaCloud, index_name: str, remote_file: Tuple[str, str]
) -> LlamaCloudIndex:
# get project by name
projects = client.projects.list_projects(
organization_id=organization_id, project_name=project_name
# create project if it doesn't exist
project_create = ProjectCreate(name=project_name)
project = client.projects.upsert_project(
organization_id=organization_id, request=project_create
)
if not projects:
raise ValueError(
f"Project '{project_name}' not found. Please create it first in the LlamaCloud UI."
)
project = projects[0]
# create pipeline
pipeline_create = PipelineCreate(
@@ -423,7 +423,6 @@ def create_extract_run(
},
data_schema: Dict[str, Any] = {},
file: File = create_file(),
error: Optional[str] = None,
) -> ExtractRun:
return ExtractRun.parse_obj(
{
@@ -440,7 +439,6 @@ def create_extract_run(
"status": "SUCCESS",
"project_id": str(uuid.uuid4()),
"from_ui": False,
"error": error,
}
)
@@ -546,46 +544,6 @@ def test_extracted_data_from_extraction_result_invalid_data():
assert invalid_data.field_metadata["name"].confidence == 0.9
assert invalid_data.overall_confidence == 0.9
# Verify default error message when no job error present
assert exc_info.value.extraction_error is None
assert "Not able to parse the extracted data" in str(exc_info.value)
def test_extracted_data_from_extraction_result_with_job_error():
"""Test ExtractedData.from_extraction_result with job-level error prominently displayed."""
job_error_message = "Failed to process document: unsupported file format"
# Create ExtractRun with both invalid data AND a job-level error
extract_run = create_extract_run(
data={
"missing_name": "Valid Name",
"age": "not_a_number",
}, # Invalid age, missing name
extraction_metadata={
"name": {"confidence": 0.9},
},
data_schema={},
file=create_file(id="error-file", name="bad_data.pdf"),
error=job_error_message,
)
# Should raise InvalidExtractionData with the job error prominently displayed
with pytest.raises(InvalidExtractionData) as exc_info:
ExtractedData.from_extraction_result(
extract_run, Person, metadata={"test": "metadata"}
)
# Verify the exception message prominently shows the job error
exception = exc_info.value
assert exception.extraction_error == job_error_message
assert f"Extraction error: {job_error_message}" == str(exception)
# Verify the invalid_item contains both errors in metadata
invalid_data = exception.invalid_item
assert invalid_data.metadata.get("job_error") == job_error_message
assert "extraction_error" in invalid_data.metadata # Validation error still present
assert "test" in invalid_data.metadata # Original metadata preserved
class Dimensions(BaseModel):
length: Optional[str] = Field(
+16 -17
View File
@@ -34,10 +34,9 @@ TEST_PIPELINE = Pipeline(
def mock_client() -> MagicMock:
"""Mock client with sensible defaults."""
client = MagicMock()
default_project = Project(
client.projects.upsert_project.return_value = Project(
id="default-proj", name=DEFAULT_PROJECT_NAME, organization_id="default-org"
)
client.projects.list_projects.return_value = [default_project]
client.pipelines.upsert_pipeline.return_value = Pipeline(
id="default-pipe",
name="default",
@@ -101,8 +100,8 @@ def test_from_documents_uses_provided_project_id(mock_client: MagicMock) -> None
project_id=provided_project_id,
)
# Assert - project list not called (project_id provided); pipeline uses provided project_id
mock_client.projects.list_projects.assert_not_called()
# Assert - project upsert not called; pipeline uses provided project_id
mock_client.projects.upsert_project.assert_not_called()
assert mock_client.pipelines.upsert_pipeline.call_count == 1
assert (
mock_client.pipelines.upsert_pipeline.call_args.kwargs["project_id"]
@@ -111,29 +110,29 @@ def test_from_documents_uses_provided_project_id(mock_client: MagicMock) -> None
assert index.project.id == provided_project_id
def test_from_documents_lists_project_when_project_id_missing(
def test_from_documents_upserts_project_when_project_id_missing(
mock_client: MagicMock,
) -> None:
organization_id = "org-xyz"
index_name = "my_new_index"
# Project is found by name when project_id is not provided
found_project = Project(
# Project is created when project_id is not provided
upserted_project = Project(
id="proj-999", name=DEFAULT_PROJECT_NAME, organization_id=organization_id
)
mock_client.projects.list_projects.return_value = [found_project]
mock_client.projects.upsert_project.return_value = upserted_project
test_pipeline = Pipeline(
id="pipe-xyz",
name=index_name,
project_id=found_project.id,
project_id=upserted_project.id,
embedding_config=EMBEDDING_CONFIG,
)
with patch.object(
base,
"resolve_project_and_pipeline",
return_value=(found_project, test_pipeline),
return_value=(upserted_project, test_pipeline),
):
docs = [Document(text="world")]
index = LlamaCloudIndex.from_documents(
@@ -142,15 +141,15 @@ def test_from_documents_lists_project_when_project_id_missing(
organization_id=organization_id,
)
# Assert - project was listed with org id and default project name
mock_client.projects.list_projects.assert_called_once()
kwargs = mock_client.projects.list_projects.call_args.kwargs
# Assert - project was upserted with org id and default project name
mock_client.projects.upsert_project.assert_called_once()
kwargs = mock_client.projects.upsert_project.call_args.kwargs
assert kwargs["organization_id"] == organization_id
assert kwargs["project_name"] == DEFAULT_PROJECT_NAME
assert kwargs["request"].name == DEFAULT_PROJECT_NAME
# Pipeline created under the found project id
# Pipeline created under the upserted project id
assert (
mock_client.pipelines.upsert_pipeline.call_args.kwargs["project_id"]
== found_project.id
== upserted_project.id
)
assert index.project.id == found_project.id
assert index.project.id == upserted_project.id
Generated
+5 -5
View File
@@ -1595,21 +1595,21 @@ wheels = [
[[package]]
name = "llama-cloud"
version = "0.1.46"
version = "0.1.45"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "certifi" },
{ name = "httpx" },
{ name = "pydantic" },
]
sdist = { url = "https://files.pythonhosted.org/packages/40/f3/f4d6520f8d546e6c5a02f6ebeed5c09774a074b8d2c24ad559ace97a56a6/llama_cloud-0.1.46.tar.gz", hash = "sha256:e86f8791c053590d70cc59e0fc13ce72f9b681a8e658bc61df86d0285288d8ee", size = 127752, upload-time = "2026-01-21T18:40:57.103Z" }
sdist = { url = "https://files.pythonhosted.org/packages/e0/b7/3a2a209f1c3fa516de172cb13e03f5a897adea5523f2ee0f544d035e3704/llama_cloud-0.1.45.tar.gz", hash = "sha256:140244008cc5710e31ae97c6043973a3a9969a51b0f38155fa33a8434078e8aa", size = 140968, upload-time = "2025-12-03T02:22:49.484Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/c4/3a/6caaea28c8c804add33c91d356ed7d5a5412d6c9598e1450af95a15e0bcd/llama_cloud-0.1.46-py3-none-any.whl", hash = "sha256:6c6546c09c04a038c86d84d42f00eae8fd3bff49991ad3aab844bd866ecdf352", size = 361989, upload-time = "2026-01-21T18:40:54.863Z" },
{ url = "https://files.pythonhosted.org/packages/62/1d/466b0df69b81ce9410ad6ec7229a1e6601ff69640f02f246e06cfcc7428c/llama_cloud-0.1.45-py3-none-any.whl", hash = "sha256:500299a6d3f25f97bcf6755d6338523023564fa8f376955c2cf299bbc9561cc2", size = 397184, upload-time = "2025-12-03T02:22:48.335Z" },
]
[[package]]
name = "llama-cloud-services"
version = "0.6.90"
version = "0.6.88"
source = { editable = "." }
dependencies = [
{ name = "click", version = "8.1.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
@@ -1649,7 +1649,7 @@ dev = [
requires-dist = [
{ name = "click", specifier = ">=8.1.7,<9" },
{ name = "eval-type-backport", marker = "python_full_version < '3.10'", specifier = ">=0.2.0,<0.3" },
{ name = "llama-cloud", specifier = "==0.1.46" },
{ name = "llama-cloud", specifier = "==0.1.45" },
{ name = "llama-index-core", specifier = ">=0.12.0" },
{ name = "packaging", specifier = ">=23.0" },
{ name = "platformdirs", specifier = ">=4.3.7,<5" },