mirror of
https://github.com/run-llama/llama_cloud_services.git
synced 2026-07-21 12:05:23 -04:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 97c4fd1495 | |||
| bafb3f7061 | |||
| 6481c403e0 | |||
| 1509bf910e | |||
| dac0f79e51 | |||
| 32487763d5 | |||
| 06c3c556e6 |
@@ -0,0 +1,6 @@
|
||||
---
|
||||
"llama-cloud-services": patch
|
||||
"llama-cloud-services-py": patch
|
||||
---
|
||||
|
||||
Add tier and version
|
||||
@@ -1,5 +1,11 @@
|
||||
# llama-cloud-services-py
|
||||
|
||||
## 0.6.87
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 06c3c55: Update spreadsheet parsing config
|
||||
|
||||
## 0.6.86
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -2,7 +2,7 @@ import asyncio
|
||||
import io
|
||||
import os
|
||||
import time
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import Any, Dict, TYPE_CHECKING
|
||||
|
||||
import httpx
|
||||
from llama_cloud.client import AsyncLlamaCloud
|
||||
@@ -68,6 +68,8 @@ class LlamaSheets:
|
||||
max_timeout: int = 300,
|
||||
poll_interval: int = 5,
|
||||
max_retries: int = 3,
|
||||
project_id: str | None = None,
|
||||
organization_id: str | None = None,
|
||||
async_httpx_client: httpx.AsyncClient | None = None,
|
||||
) -> None:
|
||||
"""Initialize the LlamaSheets client.
|
||||
@@ -78,6 +80,8 @@ class LlamaSheets:
|
||||
max_timeout: Maximum time to wait for job completion in seconds
|
||||
poll_interval: Interval between status checks in seconds
|
||||
max_retries: Maximum number of retries for failed requests
|
||||
project_id: Project ID for file operations. If not provided, will use LLAMA_CLOUD_PROJECT_ID env var
|
||||
organization_id: Organization ID for file operations. If not provided, will use LLAMA_CLOUD_ORGANIZATION_ID env var
|
||||
async_httpx_client: Optional custom async httpx client
|
||||
"""
|
||||
self.api_key = api_key or os.environ.get("LLAMA_CLOUD_API_KEY")
|
||||
@@ -93,15 +97,32 @@ class LlamaSheets:
|
||||
self.poll_interval = poll_interval
|
||||
self.max_retries = max_retries
|
||||
|
||||
self.project_id = project_id or os.environ.get("LLAMA_CLOUD_PROJECT_ID")
|
||||
self.organization_id = organization_id or os.environ.get(
|
||||
"LLAMA_CLOUD_ORGANIZATION_ID"
|
||||
)
|
||||
|
||||
self._async_client: httpx.AsyncClient | None = async_httpx_client
|
||||
self._files_client = FileClient(
|
||||
AsyncLlamaCloud(
|
||||
token=self.api_key,
|
||||
base_url=self.base_url,
|
||||
httpx_client=async_httpx_client,
|
||||
)
|
||||
),
|
||||
project_id=self.project_id,
|
||||
organization_id=self.organization_id,
|
||||
)
|
||||
|
||||
def _get_default_params(self) -> dict[str, str]:
|
||||
"""Get default query parameters for API requests"""
|
||||
params = {}
|
||||
if self.project_id is not None:
|
||||
params["project_id"] = self.project_id
|
||||
if self.organization_id is not None:
|
||||
params["organization_id"] = self.organization_id
|
||||
|
||||
return params
|
||||
|
||||
def _get_async_client(self) -> httpx.AsyncClient:
|
||||
"""Get or create the async httpx client"""
|
||||
if self._async_client is None:
|
||||
@@ -306,6 +327,8 @@ class LlamaSheets:
|
||||
"config": config.model_dump(mode="json", exclude_none=True),
|
||||
}
|
||||
|
||||
params = self._get_default_params()
|
||||
|
||||
try:
|
||||
async for attempt in AsyncRetrying(
|
||||
stop=stop_after_attempt(self.max_retries),
|
||||
@@ -318,6 +341,7 @@ class LlamaSheets:
|
||||
response = await client.post(
|
||||
f"{self.base_url}/api/v1/beta/sheets/jobs",
|
||||
headers=self._get_headers(),
|
||||
params=params,
|
||||
json=payload,
|
||||
)
|
||||
response.raise_for_status()
|
||||
@@ -347,12 +371,17 @@ class LlamaSheets:
|
||||
):
|
||||
with attempt:
|
||||
client = self._get_async_client()
|
||||
params: Dict[str, Any] = {
|
||||
"include_results": include_results_metadata,
|
||||
**self._get_default_params(),
|
||||
}
|
||||
response = await client.get(
|
||||
f"{self.base_url}/api/v1/beta/sheets/jobs/{job_id}",
|
||||
headers=self._get_headers(),
|
||||
params={"include_results": include_results_metadata},
|
||||
params=params,
|
||||
)
|
||||
response.raise_for_status()
|
||||
|
||||
return SpreadsheetJobResult.model_validate(response.json())
|
||||
except Exception as e:
|
||||
raise SpreadsheetAPIError(f"Failed to get job status: {e}") from e
|
||||
@@ -415,6 +444,8 @@ class LlamaSheets:
|
||||
# Get presigned URL
|
||||
presigned_response = None
|
||||
result_type_str = str(result_type)
|
||||
params = self._get_default_params()
|
||||
|
||||
try:
|
||||
async for attempt in AsyncRetrying(
|
||||
stop=stop_after_attempt(self.max_retries),
|
||||
@@ -427,6 +458,7 @@ class LlamaSheets:
|
||||
response = await client.get(
|
||||
f"{self.base_url}/api/v1/beta/sheets/jobs/{job_id}/regions/{region_id}/result/{result_type_str}",
|
||||
headers=self._get_headers(),
|
||||
params=params,
|
||||
)
|
||||
response.raise_for_status()
|
||||
presigned_response = PresignedUrlResponse.model_validate(
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from datetime import datetime
|
||||
from enum import Enum
|
||||
from typing import Literal
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field, field_validator
|
||||
|
||||
@@ -63,7 +64,7 @@ class SpreadsheetParseResult(BaseModel):
|
||||
class SpreadsheetParsingConfig(BaseModel):
|
||||
"""Configuration for spreadsheet parsing and region extraction"""
|
||||
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
model_config = ConfigDict(extra="ignore")
|
||||
|
||||
sheet_names: list[str] | None = Field(
|
||||
default=None,
|
||||
@@ -86,6 +87,16 @@ class SpreadsheetParsingConfig(BaseModel):
|
||||
description="Enables experimental processing. Accuracy may be impacted.",
|
||||
)
|
||||
|
||||
flatten_hierarchical_tables: bool = Field(
|
||||
default=False,
|
||||
description="Return a flattened dataframe when a detected table is recognized as hierarchical.",
|
||||
)
|
||||
|
||||
table_merge_sensitivity: Literal["strong", "weak"] = Field(
|
||||
default="strong",
|
||||
description="Influences how likely similar-looking regions are merged into a single table. Useful for spreadsheets that either have sparse tables (strong merging) or many distinct tables close together (weak merging).",
|
||||
)
|
||||
|
||||
|
||||
class SpreadsheetJob(BaseModel):
|
||||
"""A spreadsheet parsing job"""
|
||||
|
||||
@@ -654,6 +654,9 @@ class LlamaCloudIndex(BaseManagedIndex):
|
||||
],
|
||||
)
|
||||
|
||||
# Trigger a sync
|
||||
client.pipelines.sync_pipeline(pipeline_id=index.pipeline.id)
|
||||
|
||||
doc_ids = [doc.id for doc in upserted_documents]
|
||||
index.wait_for_completion(
|
||||
doc_ids=doc_ids, verbose=verbose, raise_on_error=raise_on_error
|
||||
@@ -738,6 +741,10 @@ class LlamaCloudIndex(BaseManagedIndex):
|
||||
)
|
||||
],
|
||||
)
|
||||
|
||||
# Trigger a sync
|
||||
self._client.pipelines.sync_pipeline(pipeline_id=self.pipeline.id)
|
||||
|
||||
upserted_document = upserted_documents[0]
|
||||
self.wait_for_completion(
|
||||
doc_ids=[upserted_document.id], verbose=verbose, raise_on_error=True
|
||||
@@ -760,6 +767,9 @@ class LlamaCloudIndex(BaseManagedIndex):
|
||||
)
|
||||
],
|
||||
)
|
||||
# Trigger a sync
|
||||
await self._aclient.pipelines.sync_pipeline(pipeline_id=self.pipeline.id)
|
||||
|
||||
upserted_document = upserted_documents[0]
|
||||
await self.await_for_completion(
|
||||
doc_ids=[upserted_document.id], verbose=verbose, raise_on_error=True
|
||||
@@ -782,6 +792,9 @@ class LlamaCloudIndex(BaseManagedIndex):
|
||||
)
|
||||
],
|
||||
)
|
||||
# Trigger a sync
|
||||
self._client.pipelines.sync_pipeline(pipeline_id=self.pipeline.id)
|
||||
|
||||
upserted_document = upserted_documents[0]
|
||||
self.wait_for_completion(
|
||||
doc_ids=[upserted_document.id], verbose=verbose, raise_on_error=True
|
||||
@@ -804,6 +817,9 @@ class LlamaCloudIndex(BaseManagedIndex):
|
||||
)
|
||||
],
|
||||
)
|
||||
# Trigger a sync
|
||||
await self._aclient.pipelines.sync_pipeline(pipeline_id=self.pipeline.id)
|
||||
|
||||
upserted_document = upserted_documents[0]
|
||||
await self.await_for_completion(
|
||||
doc_ids=[upserted_document.id], verbose=verbose, raise_on_error=True
|
||||
@@ -827,6 +843,9 @@ class LlamaCloudIndex(BaseManagedIndex):
|
||||
for doc in documents
|
||||
],
|
||||
)
|
||||
# Trigger a sync
|
||||
self._client.pipelines.sync_pipeline(pipeline_id=self.pipeline.id)
|
||||
|
||||
doc_ids = [doc.id for doc in upserted_documents]
|
||||
self.wait_for_completion(doc_ids=doc_ids, verbose=True, raise_on_error=True)
|
||||
return [True] * len(doc_ids)
|
||||
@@ -849,6 +868,9 @@ class LlamaCloudIndex(BaseManagedIndex):
|
||||
for doc in documents
|
||||
],
|
||||
)
|
||||
# Trigger a sync
|
||||
await self._aclient.pipelines.sync_pipeline(pipeline_id=self.pipeline.id)
|
||||
|
||||
doc_ids = [doc.id for doc in upserted_documents]
|
||||
await self.await_for_completion(
|
||||
doc_ids=doc_ids, verbose=True, raise_on_error=True
|
||||
|
||||
@@ -568,6 +568,13 @@ class LlamaParse(BasePydanticReader):
|
||||
default=False,
|
||||
description="If set to true, the parser will include line-level bounding boxes in the result.",
|
||||
)
|
||||
tier: Optional[str] = Field(
|
||||
default=None, description="The tier to use for the parsing job."
|
||||
)
|
||||
version: Optional[str] = Field(
|
||||
default=None,
|
||||
description="The version of the parser to use at the specified tier.",
|
||||
)
|
||||
|
||||
# Deprecated
|
||||
bounding_box: Optional[str] = Field(
|
||||
@@ -1125,6 +1132,12 @@ class LlamaParse(BasePydanticReader):
|
||||
if self.line_level_bounding_box is not None:
|
||||
data["line_level_bounding_box"] = self.line_level_bounding_box
|
||||
|
||||
if self.tier is not None:
|
||||
data["tier"] = self.tier
|
||||
|
||||
if self.version is not None:
|
||||
data["version"] = self.version
|
||||
|
||||
# Deprecated
|
||||
if self.bounding_box is not None:
|
||||
data["bounding_box"] = self.bounding_box
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# llama_parse
|
||||
|
||||
## 0.6.87
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [06c3c55]
|
||||
- llama-cloud-services-py@0.6.87
|
||||
|
||||
## 0.6.86
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "llama_parse",
|
||||
"version": "0.6.86",
|
||||
"version": "0.6.87",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"private": false,
|
||||
|
||||
@@ -11,13 +11,13 @@ dev = [
|
||||
|
||||
[project]
|
||||
name = "llama-parse"
|
||||
version = "0.6.86"
|
||||
version = "0.6.87"
|
||||
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.86"]
|
||||
dependencies = ["llama-cloud-services>=0.6.87"]
|
||||
|
||||
[project.scripts]
|
||||
llama-parse = "llama_parse.cli.main:parse"
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "llama-cloud-services-py",
|
||||
"version": "0.6.86",
|
||||
"version": "0.6.87",
|
||||
"private": false,
|
||||
"license": "MIT",
|
||||
"scripts": {},
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ dev = [
|
||||
|
||||
[project]
|
||||
name = "llama-cloud-services"
|
||||
version = "0.6.86"
|
||||
version = "0.6.87"
|
||||
description = "Tailored SDK clients for LlamaCloud services."
|
||||
authors = [{name = "Logan Markewich", email = "logan@runllama.ai"}]
|
||||
requires-python = ">=3.9,<4.0"
|
||||
|
||||
@@ -2,24 +2,58 @@ import os
|
||||
import tempfile
|
||||
import pytest
|
||||
import pandas as pd
|
||||
from pydantic import ValidationError
|
||||
|
||||
from llama_cloud_services.beta.sheets import LlamaSheets
|
||||
from llama_cloud_services.beta.sheets.types import SpreadsheetParsingConfig
|
||||
|
||||
|
||||
class TestSpreadsheetParsingConfig:
|
||||
"""Unit tests for SpreadsheetParsingConfig."""
|
||||
|
||||
def test_default_values(self):
|
||||
"""Test that default values are set correctly."""
|
||||
config = SpreadsheetParsingConfig()
|
||||
assert config.flatten_hierarchical_tables is False
|
||||
assert config.table_merge_sensitivity == "strong"
|
||||
|
||||
def test_custom_values(self):
|
||||
"""Test setting custom values for new fields."""
|
||||
config = SpreadsheetParsingConfig(
|
||||
flatten_hierarchical_tables=True,
|
||||
table_merge_sensitivity="weak",
|
||||
)
|
||||
assert config.flatten_hierarchical_tables is True
|
||||
assert config.table_merge_sensitivity == "weak"
|
||||
|
||||
def test_table_merge_sensitivity_validation(self):
|
||||
"""Test that invalid table_merge_sensitivity values are rejected."""
|
||||
with pytest.raises(ValidationError):
|
||||
SpreadsheetParsingConfig(table_merge_sensitivity="invalid")
|
||||
|
||||
def test_unknown_fields_ignored(self):
|
||||
"""Test that unknown fields are silently ignored."""
|
||||
config = SpreadsheetParsingConfig(
|
||||
unknown_field="test",
|
||||
another_unknown=123,
|
||||
)
|
||||
assert not hasattr(config, "unknown_field")
|
||||
assert not hasattr(config, "another_unknown")
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def sheets_client():
|
||||
"""Create a LlamaSheets client for testing."""
|
||||
api_key = os.getenv(
|
||||
"LLAMA_CLOUD_API_KEY", "llx-3AEorIw5v0lnJPzEOI9xSl0N8yFx3fguw0Zn8QJHzGWmwg5r"
|
||||
)
|
||||
base_url = os.getenv("LLAMA_CLOUD_BASE_URL", "https://api.staging.llamaindex.ai")
|
||||
api_key = os.getenv("LLAMA_CLOUD_API_KEY")
|
||||
base_url = os.getenv("LLAMA_CLOUD_BASE_URL", "https://api.cloud.llamaindex.ai")
|
||||
project_id = os.getenv("LLAMA_CLOUD_PROJECT_ID")
|
||||
|
||||
client = LlamaSheets(
|
||||
api_key=api_key,
|
||||
base_url=base_url,
|
||||
max_timeout=300,
|
||||
poll_interval=2,
|
||||
project_id=project_id,
|
||||
)
|
||||
return client
|
||||
|
||||
@@ -51,10 +85,7 @@ def sample_excel_file():
|
||||
|
||||
|
||||
@pytest.mark.skipif(
|
||||
os.environ.get(
|
||||
"LLAMA_CLOUD_API_KEY", "llx-3AEorIw5v0lnJPzEOI9xSl0N8yFx3fguw0Zn8QJHzGWmwg5r"
|
||||
)
|
||||
== "",
|
||||
os.environ.get("LLAMA_CLOUD_API_KEY", "") == "",
|
||||
reason="LLAMA_CLOUD_API_KEY not set",
|
||||
)
|
||||
@pytest.mark.asyncio
|
||||
@@ -134,10 +165,7 @@ async def test_spreadsheet_extraction_e2e(
|
||||
|
||||
|
||||
@pytest.mark.skipif(
|
||||
os.environ.get(
|
||||
"LLAMA_CLOUD_API_KEY", "llx-3AEorIw5v0lnJPzEOI9xSl0N8yFx3fguw0Zn8QJHzGWmwg5r"
|
||||
)
|
||||
== "",
|
||||
os.environ.get("LLAMA_CLOUD_API_KEY", "") == "",
|
||||
reason="LLAMA_CLOUD_API_KEY not set",
|
||||
)
|
||||
@pytest.mark.asyncio
|
||||
|
||||
@@ -78,7 +78,7 @@ async def test_upload_bytes(
|
||||
uploaded_file = await file_client.upload_bytes(file_bytes, external_file_id)
|
||||
|
||||
assert isinstance(uploaded_file, File)
|
||||
expected_name = external_file_id if use_presigned_url else "upload"
|
||||
expected_name = external_file_id
|
||||
assert uploaded_file.name == expected_name
|
||||
assert uploaded_file.external_file_id == external_file_id
|
||||
|
||||
@@ -100,7 +100,7 @@ async def test_upload_buffer(
|
||||
uploaded_file = await file_client.upload_buffer(buffer, external_file_id, file_size)
|
||||
|
||||
assert isinstance(uploaded_file, File)
|
||||
expected_name = external_file_id if use_presigned_url else "upload"
|
||||
expected_name = external_file_id
|
||||
assert uploaded_file.name == expected_name
|
||||
assert uploaded_file.external_file_id == external_file_id
|
||||
|
||||
|
||||
+9469
-14839
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
import {
|
||||
addFilesToPipelineApiApiV1PipelinesPipelineIdFilesPut,
|
||||
getPipelineFileStatusApiV1PipelinesPipelineIdFilesFileIdStatusGet,
|
||||
listPipelineFilesApiV1PipelinesPipelineIdFilesGet,
|
||||
listPipelineFiles2ApiV1PipelinesPipelineIdFiles2Get,
|
||||
listProjectsApiV1ProjectsGet,
|
||||
readFileContentApiV1FilesIdContentGet,
|
||||
searchPipelinesApiV1PipelinesGet,
|
||||
@@ -97,21 +97,20 @@ export class LLamaCloudFileService {
|
||||
*/
|
||||
public static async getFileUrl(pipelineId: string, filename: string) {
|
||||
initService();
|
||||
const { data: allPipelineFiles } =
|
||||
await listPipelineFilesApiV1PipelinesPipelineIdFilesGet({
|
||||
path: {
|
||||
pipeline_id: pipelineId,
|
||||
},
|
||||
throwOnError: true,
|
||||
});
|
||||
const file = allPipelineFiles.find((file) => file.name === filename);
|
||||
const response = await listPipelineFiles2ApiV1PipelinesPipelineIdFiles2Get({
|
||||
path: {
|
||||
pipeline_id: pipelineId,
|
||||
},
|
||||
throwOnError: true,
|
||||
});
|
||||
const file = response.data.files.find((file) => file.name === filename);
|
||||
if (!file?.file_id) return null;
|
||||
const { data: fileContent } = await readFileContentApiV1FilesIdContentGet({
|
||||
path: {
|
||||
id: file.file_id,
|
||||
},
|
||||
query: {
|
||||
project_id: file.project_id,
|
||||
project_id: file.project_id || null,
|
||||
},
|
||||
throwOnError: true,
|
||||
});
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -186,6 +186,8 @@ export class LlamaParseReader extends FileReader {
|
||||
page_footer_suffix?: string | undefined;
|
||||
merge_tables_across_pages_in_markdown?: boolean | undefined;
|
||||
extract_printed_page_number?: boolean | undefined;
|
||||
tier?: string | undefined;
|
||||
version?: string | undefined;
|
||||
|
||||
constructor(
|
||||
params: Partial<Omit<LlamaParseReader, "language" | "apiKey">> & {
|
||||
@@ -383,6 +385,8 @@ export class LlamaParseReader extends FileReader {
|
||||
merge_tables_across_pages_in_markdown:
|
||||
this.merge_tables_across_pages_in_markdown,
|
||||
extract_printed_page_number: this.extract_printed_page_number,
|
||||
tier: this.tier,
|
||||
version: this.version,
|
||||
} satisfies {
|
||||
[Key in keyof BodyUploadFileApiParsingUploadPost]-?:
|
||||
| BodyUploadFileApiParsingUploadPost[Key]
|
||||
|
||||
Reference in New Issue
Block a user