mirror of
https://github.com/run-llama/llama_cloud_services.git
synced 2026-07-22 12:35:50 -04:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b66b47a708 | |||
| fe485ff62e |
@@ -39,7 +39,7 @@ SchemaInput = Union[JSONObjectType, Type[BaseModel]]
|
||||
|
||||
DEFAULT_EXTRACT_CONFIG = ExtractConfig(
|
||||
extraction_target=ExtractTarget.PER_DOC,
|
||||
extraction_mode=ExtractMode.ACCURATE,
|
||||
extraction_mode=ExtractMode.BALANCED,
|
||||
)
|
||||
|
||||
|
||||
@@ -564,6 +564,14 @@ class LlamaExtract(BaseComponent):
|
||||
Returns:
|
||||
ExtractionAgent: The created extraction agent
|
||||
"""
|
||||
if config is not None:
|
||||
if config.extraction_mode == ExtractMode.ACCURATE:
|
||||
warnings.warn(
|
||||
"ACCURATE extraction mode is deprecated. Using BALANCED instead."
|
||||
)
|
||||
config.extraction_mode = ExtractMode.BALANCED
|
||||
else:
|
||||
config = DEFAULT_EXTRACT_CONFIG
|
||||
|
||||
if isinstance(data_schema, dict):
|
||||
data_schema = data_schema
|
||||
@@ -581,7 +589,7 @@ class LlamaExtract(BaseComponent):
|
||||
request=ExtractAgentCreate(
|
||||
name=name,
|
||||
data_schema=data_schema,
|
||||
config=config or DEFAULT_EXTRACT_CONFIG,
|
||||
config=config,
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
@@ -4,6 +4,7 @@ import os
|
||||
import time
|
||||
from contextlib import asynccontextmanager
|
||||
from copy import deepcopy
|
||||
from enum import Enum
|
||||
from io import BufferedIOBase
|
||||
from pathlib import Path, PurePath, PurePosixPath
|
||||
from typing import Any, AsyncGenerator, Dict, List, Optional, Union
|
||||
@@ -52,6 +53,14 @@ def build_url(
|
||||
return base_url
|
||||
|
||||
|
||||
class BackoffPattern(str, Enum):
|
||||
"""Backoff pattern for polling."""
|
||||
|
||||
CONSTANT = "constant"
|
||||
LINEAR = "linear"
|
||||
EXPONENTIAL = "exponential"
|
||||
|
||||
|
||||
class LlamaParse(BasePydanticReader):
|
||||
"""A smart-parser for files."""
|
||||
|
||||
@@ -78,6 +87,15 @@ class LlamaParse(BasePydanticReader):
|
||||
description="The interval in seconds to check if the parsing is done.",
|
||||
)
|
||||
|
||||
backoff_pattern: BackoffPattern = Field(
|
||||
default=BackoffPattern.LINEAR,
|
||||
description="Controls the backoff pattern when retrying failed requests: 'constant', 'linear', or 'exponential'.",
|
||||
)
|
||||
max_check_interval: int = Field(
|
||||
default=5,
|
||||
description="Maximum interval in seconds between polling attempts when checking job status.",
|
||||
)
|
||||
|
||||
custom_client: Optional[httpx.AsyncClient] = Field(
|
||||
default=None, description="A custom HTTPX client to use for sending requests."
|
||||
)
|
||||
@@ -794,54 +812,87 @@ class LlamaParse(BasePydanticReader):
|
||||
if file_handle is not None:
|
||||
file_handle.close()
|
||||
|
||||
def _calculate_backoff(self, current_interval: float) -> float:
|
||||
"""Calculate the next backoff interval based on the backoff pattern.
|
||||
|
||||
Args:
|
||||
current_interval: The current interval in seconds
|
||||
|
||||
Returns:
|
||||
The next interval in seconds
|
||||
"""
|
||||
if self.backoff_pattern == BackoffPattern.CONSTANT:
|
||||
return current_interval
|
||||
elif self.backoff_pattern == BackoffPattern.LINEAR:
|
||||
return min(current_interval + 1, float(self.max_check_interval))
|
||||
elif self.backoff_pattern == BackoffPattern.EXPONENTIAL:
|
||||
return min(current_interval * 2, float(self.max_check_interval))
|
||||
return current_interval # Default fallback
|
||||
|
||||
async def _get_job_result(
|
||||
self, job_id: str, result_type: str, verbose: bool = False
|
||||
) -> Dict[str, Any]:
|
||||
start = time.time()
|
||||
tries = 0
|
||||
error_count = 0
|
||||
current_interval: float = float(self.check_interval)
|
||||
|
||||
# so we're not re-setting the headers & stuff on each
|
||||
# usage... assume that there is not some other
|
||||
# coro also modifying base_url and the other client related configs.
|
||||
client = self.aclient
|
||||
while True:
|
||||
await asyncio.sleep(self.check_interval)
|
||||
tries += 1
|
||||
result = await client.get(JOB_STATUS_ROUTE.format(job_id=job_id))
|
||||
if result.status_code != 200:
|
||||
try:
|
||||
await asyncio.sleep(current_interval)
|
||||
tries += 1
|
||||
result = await client.get(JOB_STATUS_ROUTE.format(job_id=job_id))
|
||||
result.raise_for_status() # this raises if status is not 2xx
|
||||
# Allowed values "PENDING", "SUCCESS", "ERROR", "CANCELED"
|
||||
result_json = result.json()
|
||||
status = result_json["status"]
|
||||
if status == "SUCCESS":
|
||||
parsed_result = await client.get(
|
||||
JOB_RESULT_URL.format(job_id=job_id, result_type=result_type),
|
||||
)
|
||||
return parsed_result.json()
|
||||
elif status == "PENDING":
|
||||
end = time.time()
|
||||
if end - start > self.max_timeout:
|
||||
raise Exception(f"Timeout while parsing the file: {job_id}")
|
||||
if verbose and tries % 10 == 0:
|
||||
print(".", end="", flush=True)
|
||||
current_interval = self._calculate_backoff(current_interval)
|
||||
else:
|
||||
error_code = result_json.get("error_code", "No error code found")
|
||||
error_message = result_json.get(
|
||||
"error_message", "No error message found"
|
||||
)
|
||||
exception_str = (
|
||||
f"Job ID: {job_id} failed with status: {status}, "
|
||||
f"Error code: {error_code}, Error message: {error_message}"
|
||||
)
|
||||
raise Exception(exception_str)
|
||||
except (
|
||||
httpx.ConnectError,
|
||||
httpx.ReadError,
|
||||
httpx.WriteError,
|
||||
httpx.ConnectTimeout,
|
||||
httpx.ReadTimeout,
|
||||
httpx.WriteTimeout,
|
||||
httpx.HTTPStatusError,
|
||||
) as err:
|
||||
error_count += 1
|
||||
end = time.time()
|
||||
if end - start > self.max_timeout:
|
||||
raise Exception(f"Timeout while parsing the file: {job_id}")
|
||||
raise Exception(
|
||||
f"Timeout while parsing the file: {job_id}"
|
||||
) from err
|
||||
if verbose and tries % 10 == 0:
|
||||
print(".", end="", flush=True)
|
||||
await asyncio.sleep(self.check_interval)
|
||||
continue
|
||||
|
||||
# Allowed values "PENDING", "SUCCESS", "ERROR", "CANCELED"
|
||||
result_json = result.json()
|
||||
status = result_json["status"]
|
||||
if status == "SUCCESS":
|
||||
parsed_result = await client.get(
|
||||
JOB_RESULT_URL.format(job_id=job_id, result_type=result_type),
|
||||
)
|
||||
return parsed_result.json()
|
||||
|
||||
elif status == "PENDING":
|
||||
end = time.time()
|
||||
if end - start > self.max_timeout:
|
||||
raise Exception(f"Timeout while parsing the file: {job_id}")
|
||||
if verbose and tries % 10 == 0:
|
||||
print(".", end="", flush=True)
|
||||
await asyncio.sleep(self.check_interval)
|
||||
|
||||
else:
|
||||
error_code = result_json.get("error_code", "No error code found")
|
||||
error_message = result_json.get(
|
||||
"error_message", "No error message found"
|
||||
)
|
||||
|
||||
exception_str = f"Job ID: {job_id} failed with status: {status}, Error code: {error_code}, Error message: {error_message}"
|
||||
raise Exception(exception_str)
|
||||
print(
|
||||
f"HTTP error: {err}...",
|
||||
flush=True,
|
||||
)
|
||||
current_interval = self._calculate_backoff(current_interval)
|
||||
|
||||
async def _aload_data(
|
||||
self,
|
||||
|
||||
@@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"
|
||||
|
||||
[tool.poetry]
|
||||
name = "llama-parse"
|
||||
version = "0.6.8"
|
||||
version = "0.6.9"
|
||||
description = "Parse files into RAG-Optimized formats."
|
||||
authors = ["Logan Markewich <logan@llamaindex.ai>"]
|
||||
license = "MIT"
|
||||
@@ -13,7 +13,7 @@ packages = [{include = "llama_parse"}]
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = ">=3.9,<4.0"
|
||||
llama-cloud-services = ">=0.6.7"
|
||||
llama-cloud-services = ">=0.6.9"
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
pytest = "^8.0.0"
|
||||
|
||||
Generated
+457
-395
File diff suppressed because it is too large
Load Diff
+3
-2
@@ -8,7 +8,7 @@ python_version = "3.10"
|
||||
|
||||
[tool.poetry]
|
||||
name = "llama-cloud-services"
|
||||
version = "0.6.8"
|
||||
version = "0.6.9"
|
||||
description = "Tailored SDK clients for LlamaCloud services."
|
||||
authors = ["Logan Markewich <logan@runllama.ai>"]
|
||||
license = "MIT"
|
||||
@@ -18,11 +18,12 @@ packages = [{include = "llama_cloud_services"}]
|
||||
[tool.poetry.dependencies]
|
||||
python = ">=3.9,<4.0"
|
||||
llama-index-core = ">=0.11.0"
|
||||
llama-cloud = "^0.1.16"
|
||||
llama-cloud = "^0.1.17"
|
||||
pydantic = "!=2.10"
|
||||
click = "^8.1.7"
|
||||
python-dotenv = "^1.0.1"
|
||||
eval-type-backport = {python = "<3.10", version = "^0.2.0"}
|
||||
platformdirs = "^4.3.7"
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
pytest = "^8.0.0"
|
||||
|
||||
@@ -60,7 +60,7 @@ def get_test_cases():
|
||||
|
||||
settings = [
|
||||
ExtractConfig(extraction_mode=ExtractMode.FAST),
|
||||
ExtractConfig(extraction_mode=ExtractMode.ACCURATE),
|
||||
ExtractConfig(extraction_mode=ExtractMode.BALANCED),
|
||||
]
|
||||
|
||||
for input_file in sorted(input_files):
|
||||
|
||||
@@ -55,7 +55,7 @@ def get_test_cases():
|
||||
|
||||
settings = [
|
||||
ExtractConfig(extraction_mode=ExtractMode.FAST),
|
||||
ExtractConfig(extraction_mode=ExtractMode.ACCURATE),
|
||||
ExtractConfig(extraction_mode=ExtractMode.BALANCED),
|
||||
]
|
||||
|
||||
for input_file in sorted(input_files):
|
||||
|
||||
Reference in New Issue
Block a user