From 1bce235ab84afffd9704db50f20363600faf9620 Mon Sep 17 00:00:00 2001 From: akash-vijay-kv Date: Mon, 18 May 2026 10:14:54 +0530 Subject: [PATCH 1/3] [NET-856] feat: Add file handling support in the simulation workflow --- netra/simulation/__init__.py | 4 ++ netra/simulation/api.py | 8 ++- netra/simulation/client.py | 39 ++++++++++++- netra/simulation/models.py | 40 ++++++++++++- netra/simulation/task.py | 39 +++++++++++-- netra/simulation/utils.py | 107 ++++++++++++++++++++++++++++++++++- 6 files changed, 227 insertions(+), 10 deletions(-) diff --git a/netra/simulation/__init__.py b/netra/simulation/__init__.py index 79c7cfd..efcb9d0 100644 --- a/netra/simulation/__init__.py +++ b/netra/simulation/__init__.py @@ -2,6 +2,8 @@ from netra.simulation.models import ( ConversationResponse, ConversationStatus, + FileData, + ProcessedFile, SimulationItem, TaskResult, ) @@ -12,6 +14,8 @@ "BaseTask", "ConversationResponse", "ConversationStatus", + "FileData", + "ProcessedFile", "SimulationItem", "TaskResult", ] diff --git a/netra/simulation/api.py b/netra/simulation/api.py index dfea287..5340875 100644 --- a/netra/simulation/api.py +++ b/netra/simulation/api.py @@ -8,7 +8,7 @@ from netra.config import Config from netra.simulation.client import SimulationHttpClient -from netra.simulation.models import SimulationItem +from netra.simulation.models import FileData, SimulationItem from netra.simulation.task import BaseTask from netra.simulation.utils import ( execute_task, @@ -197,6 +197,7 @@ async def _execute_conversation( run_item_id = run_item.run_item_id message = run_item.message turn_id = run_item.turn_id + raw_files: list[FileData] = run_item.files session_id: Optional[str] = None while True: @@ -208,7 +209,9 @@ async def _execute_conversation( span_context = otel_span.get_span_context() trace_id = format_trace_id(span_context.trace_id) - response_message, task_session_id = await execute_task(task, message, session_id) + response_message, task_session_id = await execute_task( + task, message, session_id, raw_files=raw_files + ) if task_session_id: session_id = task_session_id @@ -243,6 +246,7 @@ async def _execute_conversation( message = response.next_user_message # type:ignore[assignment] turn_id = response.next_turn_id # type:ignore[assignment] + raw_files = response.next_files except Exception as exc: error_msg = str(exc) diff --git a/netra/simulation/client.py b/netra/simulation/client.py index d495185..b9f1322 100644 --- a/netra/simulation/client.py +++ b/netra/simulation/client.py @@ -7,7 +7,7 @@ import httpx from netra.config import Config -from netra.simulation.models import ConversationResponse, SimulationItem +from netra.simulation.models import ConversationResponse, FileData, SimulationItem logger = logging.getLogger(__name__) @@ -148,6 +148,7 @@ def create_run( run_item_id=msg.get("testRunItemId", ""), message=msg.get("userMessage", ""), turn_id=msg.get("turnId", ""), + files=self._parse_files(msg.get("files")), ) for msg in user_messages ] @@ -217,6 +218,7 @@ def trigger_conversation( next_turn_id=next_msg.get("turnId", ""), next_user_message=next_msg.get("userMessage", ""), next_run_item_id=next_msg.get("testRunItemId", ""), + next_files=self._parse_files(next_msg.get("files")), ) except Exception as exc: @@ -277,6 +279,41 @@ def post_run_status(self, run_id: str, status: str) -> Any: logger.error("%s: Failed to post run status for run '%s': %s", _LOG_PREFIX, run_id, error_msg) return {"success": False} + @staticmethod + def _parse_files(raw_files: Any) -> list[FileData]: + """Parse raw file entries from the backend response into FileData objects. + + Args: + raw_files: List of file dictionaries from the JSON response, or None. + + Returns: + List of FileData objects. Malformed entries are skipped. + """ + if not raw_files or not isinstance(raw_files, list): + return [] + + parsed: list[FileData] = [] + for entry in raw_files: + if not isinstance(entry, dict): + continue + file_name = entry.get("fileName", "") + download_url = entry.get("downloadUrl", "") + if not file_name or not download_url: + logger.warning( + "%s: Skipping file entry with missing fileName or downloadUrl", + _LOG_PREFIX, + ) + continue + parsed.append( + FileData( + file_name=file_name, + content_type=entry.get("contentType", ""), + description=entry.get("description"), + download_url=download_url, + ) + ) + return parsed + def _extract_error_message( self, response: Optional[httpx.Response], diff --git a/netra/simulation/models.py b/netra/simulation/models.py index 258f655..570a765 100644 --- a/netra/simulation/models.py +++ b/netra/simulation/models.py @@ -1,6 +1,6 @@ """Data models for the simulation module.""" -from dataclasses import dataclass +from dataclasses import dataclass, field from enum import Enum from typing import Optional @@ -12,6 +12,40 @@ class ConversationStatus(Enum): STOP = "stop" +@dataclass(slots=True, frozen=True) +class FileData: + """Raw file metadata received from the backend. + + Attributes: + file_name: Name of the file. + content_type: MIME type of the file content. + description: Optional description of the file. + download_url: Pre-signed URL to download the file. + """ + + file_name: str + content_type: str + description: Optional[str] + download_url: str + + +@dataclass(slots=True, frozen=True) +class ProcessedFile: + """File after download and base64 encoding, delivered to the user task. + + Attributes: + file_name: Name of the file. + content_type: MIME type of the file content. + description: Optional description of the file. + data: Base64-encoded file content. + """ + + file_name: str + content_type: str + description: Optional[str] + data: str + + @dataclass(slots=True, frozen=True) class SimulationItem: """Represents a single item in a simulation run. @@ -20,11 +54,13 @@ class SimulationItem: run_item_id: Unique identifier for the run item. message: The user message content. turn_id: Identifier for the conversation turn. + files: File metadata attached to this item. """ run_item_id: str message: str turn_id: str + files: list[FileData] = field(default_factory=list) @dataclass(slots=True) @@ -37,6 +73,7 @@ class ConversationResponse: next_turn_id: Identifier for the next turn if continuing. next_user_message: The next user message if continuing. next_run_item_id: Identifier for the next run item if continuing. + next_files: File metadata for the next turn if continuing. """ decision: str @@ -44,6 +81,7 @@ class ConversationResponse: next_turn_id: Optional[str] = None next_user_message: Optional[str] = None next_run_item_id: Optional[str] = None + next_files: list[FileData] = field(default_factory=list) @dataclass(slots=True, frozen=True) diff --git a/netra/simulation/task.py b/netra/simulation/task.py index bdfc39b..126630d 100644 --- a/netra/simulation/task.py +++ b/netra/simulation/task.py @@ -6,7 +6,7 @@ """ from abc import ABC, abstractmethod -from typing import Awaitable, Optional +from typing import Any, Awaitable, Optional from netra.simulation.models import TaskResult @@ -18,8 +18,9 @@ class BaseTask(ABC): Subclasses must: - Implement run(): Executes the task logic and returns a TaskResult. - The run method receives a message and optional session_id, and must return - a TaskResult containing the response message and session_id. + The base contract requires ``message`` and ``session_id``. Subclasses that + need file attachments can add a ``files`` keyword argument — the framework + detects it via introspection and will pass downloaded files automatically. Example: class MyTask(BaseTask): @@ -37,6 +38,24 @@ def run(self, message: str, session_id: Optional[str] = None) -> TaskResult: task=MyTask(), ) + Example with file uploads: + class MyFileTask(BaseTask): + def run( + self, + message: str, + session_id: Optional[str] = None, + files: Optional[list[ProcessedFile]] = None, + ) -> TaskResult: + # Access base64-encoded file data + if files: + for f in files: + print(f.file_name, f.content_type, len(f.data)) + response = my_agent.chat(message, session_id=session_id, files=files) + return TaskResult( + message=response.text, + session_id=response.session_id or session_id or "default", + ) + Async Example: class MyAsyncTask(BaseTask): async def run(self, message: str, session_id: Optional[str] = None) -> TaskResult: @@ -49,17 +68,29 @@ async def run(self, message: str, session_id: Optional[str] = None) -> TaskResul """ @abstractmethod - def run(self, message: str, session_id: Optional[str] = None) -> TaskResult | Awaitable[TaskResult]: + def run( + self, + message: str, + session_id: Optional[str] = None, + **kwargs: Any, + ) -> TaskResult | Awaitable[TaskResult]: """ Execute the task logic. This method can be sync or async. If async, the framework will await the coroutine automatically. + The base signature requires only ``message`` and ``session_id``. + Subclasses that handle file attachments should declare an additional + ``files: Optional[list[ProcessedFile]] = None`` parameter — the + framework will supply it automatically when the dataset item includes + file attachments. + Args: message: The input message from the simulation. session_id: Optional session identifier for conversation continuity. Will be None for the first turn of a conversation. + **kwargs: Reserved for forward-compatible extensions (e.g. ``files``). Returns: TaskResult: The task result containing: diff --git a/netra/simulation/utils.py b/netra/simulation/utils.py index 341cd26..4ce62ca 100644 --- a/netra/simulation/utils.py +++ b/netra/simulation/utils.py @@ -1,17 +1,25 @@ """Utility functions for the simulation module.""" import asyncio +import base64 +import inspect import logging +import os import threading from typing import Awaitable, Optional, Tuple, TypeVar -from netra.simulation.models import TaskResult +import httpx + +from netra.simulation.models import FileData, ProcessedFile, TaskResult from netra.simulation.task import BaseTask logger = logging.getLogger(__name__) T = TypeVar("T") +_LOG_PREFIX = "netra.simulation" +_DEFAULT_FILE_DOWNLOAD_TIMEOUT = 30.0 + def format_trace_id(trace_id: int) -> str: """Format the trace ID as a 32-digit hexadecimal string. @@ -88,17 +96,108 @@ def runner() -> None: return asyncio.run(coro) # type: ignore[arg-type] +def _get_file_download_timeout() -> float: + """Get file download timeout from environment or use default. + + Returns: + The timeout value in seconds. + """ + timeout_str = os.getenv("NETRA_SIMULATION_FILE_DOWNLOAD_TIMEOUT") + if not timeout_str: + return _DEFAULT_FILE_DOWNLOAD_TIMEOUT + try: + return float(timeout_str) + except ValueError: + logger.warning( + "%s: Invalid file download timeout '%s', using default %.1f", + _LOG_PREFIX, + timeout_str, + _DEFAULT_FILE_DOWNLOAD_TIMEOUT, + ) + return _DEFAULT_FILE_DOWNLOAD_TIMEOUT + + +def process_files(files: list[FileData]) -> list[ProcessedFile]: + """Download files from pre-signed URLs and base64-encode their content. + + Each file is downloaded individually. If any file fails to download, the + entire batch is aborted with a ``RuntimeError`` so that file-aware tasks + never receive a partial file list. + + Args: + files: List of FileData objects containing download URLs. + + Returns: + List of ProcessedFile objects with base64-encoded data. + + Raises: + RuntimeError: If a file download or encoding fails. + """ + if not files: + return [] + + timeout = _get_file_download_timeout() + processed: list[ProcessedFile] = [] + + for file_data in files: + try: + response = httpx.get(file_data.download_url, timeout=timeout) + response.raise_for_status() + encoded = base64.b64encode(response.content).decode("ascii") + processed.append( + ProcessedFile( + file_name=file_data.file_name, + content_type=file_data.content_type, + description=file_data.description, + data=encoded, + ) + ) + except Exception as exc: + raise RuntimeError(f"Failed to download file '{file_data.file_name}': {exc}") from exc + + return processed + + +def _task_accepts_files(task: BaseTask) -> bool: + """Check whether the task's run() method accepts a 'files' parameter. + + Used for backward compatibility so that existing BaseTask subclasses that + do not declare the files parameter are not broken. + + Args: + task: The BaseTask instance to inspect. + + Returns: + True if the run() method has a 'files' parameter or **kwargs. + """ + try: + sig = inspect.signature(task.run) + params = sig.parameters + if "files" in params: + return True + return any(p.kind == inspect.Parameter.VAR_KEYWORD for p in params.values()) + except (ValueError, TypeError): + return False + + async def execute_task( task: BaseTask, message: str, session_id: Optional[str], + raw_files: Optional[list[FileData]] = None, ) -> Tuple[str, Optional[str]]: """Execute a task's run method (sync or async) and extract message and session_id. + Files are only downloaded and base64-encoded when the task's run() method + actually accepts a ``files`` parameter, avoiding unnecessary network I/O + for legacy tasks. + Args: task: The BaseTask instance to execute. message: The input message to pass to the task. session_id: The current session identifier. + raw_files: Raw file metadata from the backend. Downloads are deferred + until we confirm the task can accept them. Returns: A tuple of (response_message, session_id). @@ -106,7 +205,11 @@ async def execute_task( Raises: ValueError: If the task returns an unsupported type. """ - result = task.run(message=message, session_id=session_id) + kwargs: dict[str, object] = {"message": message, "session_id": session_id} + if raw_files and _task_accepts_files(task): + kwargs["files"] = process_files(raw_files) + + result = task.run(**kwargs) # type: ignore[arg-type] if asyncio.iscoroutine(result): result = await result From 9f326bd5d5a73c6afa1bc3e5de3668eecffad6ad Mon Sep 17 00:00:00 2001 From: akash-vijay-kv Date: Tue, 19 May 2026 09:30:17 +0530 Subject: [PATCH 2/3] chore: Rename the file response key to attachments --- netra/simulation/client.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/netra/simulation/client.py b/netra/simulation/client.py index b9f1322..77e9b5f 100644 --- a/netra/simulation/client.py +++ b/netra/simulation/client.py @@ -148,7 +148,7 @@ def create_run( run_item_id=msg.get("testRunItemId", ""), message=msg.get("userMessage", ""), turn_id=msg.get("turnId", ""), - files=self._parse_files(msg.get("files")), + files=self._parse_files(msg.get("attachments")), ) for msg in user_messages ] @@ -218,7 +218,7 @@ def trigger_conversation( next_turn_id=next_msg.get("turnId", ""), next_user_message=next_msg.get("userMessage", ""), next_run_item_id=next_msg.get("testRunItemId", ""), - next_files=self._parse_files(next_msg.get("files")), + next_files=self._parse_files(next_msg.get("attachments")), ) except Exception as exc: From 127a08b21c10a3d12d8d6a355489b9691f95c089 Mon Sep 17 00:00:00 2001 From: akash-vijay-kv Date: Wed, 20 May 2026 22:35:41 +0530 Subject: [PATCH 3/3] refactor: Update simulation module to enhance code readability --- netra/__init__.py | 6 + netra/simulation/api.py | 107 ++-- netra/simulation/client.py | 123 ++-- netra/simulation/constants.py | 38 ++ netra/simulation/models.py | 6 +- netra/simulation/task.py | 38 +- netra/simulation/utils.py | 150 +++-- poetry.lock | 1067 +-------------------------------- pyproject.toml | 1 + tests/test_simulation.py | 783 ++++++++++++++++++++++++ 10 files changed, 1064 insertions(+), 1255 deletions(-) create mode 100644 netra/simulation/constants.py create mode 100644 tests/test_simulation.py diff --git a/netra/__init__.py b/netra/__init__.py index 5022d24..cb544fb 100644 --- a/netra/__init__.py +++ b/netra/__init__.py @@ -291,6 +291,12 @@ def shutdown(cls) -> None: meter_provider.shutdown() except Exception: pass + # Close simulation HTTP client + if hasattr(cls, "simulation") and cls.simulation is not None: + try: + cls.simulation.close() + except Exception: + pass @classmethod def get_meter(cls, name: str = "netra", version: Optional[str] = None) -> otel_metrics.Meter: diff --git a/netra/simulation/api.py b/netra/simulation/api.py index 5340875..3092fd4 100644 --- a/netra/simulation/api.py +++ b/netra/simulation/api.py @@ -1,5 +1,3 @@ -"""Public API for running multi-turn conversation simulations.""" - import asyncio import concurrent.futures import logging @@ -8,7 +6,8 @@ from netra.config import Config from netra.simulation.client import SimulationHttpClient -from netra.simulation.models import FileData, SimulationItem +from netra.simulation.constants import DEFAULT_MAX_TURNS, LOG_PREFIX, SPAN_NAME +from netra.simulation.models import ConversationStatus, FileData, SimulationItem from netra.simulation.task import BaseTask from netra.simulation.utils import ( execute_task, @@ -20,9 +19,6 @@ logger = logging.getLogger(__name__) -_LOG_PREFIX = "netra.simulation" -_SPAN_NAME = "Netra.Simulation.TestRun" - class Simulation: """Public API for running multi-turn conversation simulations. @@ -43,6 +39,10 @@ def __init__(self, config: Config) -> None: self._config = config self._client = SimulationHttpClient(config) + def close(self) -> None: + """Release resources held by the simulation client.""" + self._client.close() + def run_simulation( self, name: str, @@ -50,16 +50,18 @@ def run_simulation( task: BaseTask, context: Optional[dict[str, Any]] = None, max_concurrency: int = 5, + max_turns: int = DEFAULT_MAX_TURNS, ) -> Optional[dict[str, Any]]: """Run a multi-turn conversation simulation. Args: name: Name of the simulation run. dataset_id: Identifier of the dataset to simulate. - task: A BaseTask instance whose run() method receives (message, session_id) + task: A BaseTask instance whose run() method receives (message, session_id, files) and returns TaskResult. Can be sync or async. context: Optional context data for the simulation. max_concurrency: Maximum parallel executions (default: 5). + max_turns: Maximum conversation turns per item before aborting (default: 50). Returns: Dictionary with simulation results, or None on failure. @@ -77,51 +79,60 @@ def run_simulation( return None run_id = run_result.get("run_id") - run_items = run_result.get("simulation_items") - if not run_items: - logger.error("%s: No items returned from create_run", _LOG_PREFIX) + simulation_items = run_result.get("simulation_items") + if not simulation_items: + logger.error("%s: No items returned from create_run", LOG_PREFIX) return None - logger.info("%s: Starting simulation with %d items", _LOG_PREFIX, len(run_items)) + logger.info("%s: Starting simulation with %d items", LOG_PREFIX, len(simulation_items)) try: result = run_async_safely( - self._run_simulation_async(run_id, run_items, task, max_concurrency) # type:ignore[arg-type] + self._run_simulation_async( + run_id, simulation_items, task, max_concurrency, max_turns # type:ignore[arg-type] + ) ) elapsed_time = time.time() - start_time - logger.info("%s: Simulation completed in %.2f seconds", _LOG_PREFIX, elapsed_time) + logger.info("%s: Simulation completed in %.2f seconds", LOG_PREFIX, elapsed_time) self._client.post_run_status(run_id, "completed") # type:ignore[arg-type] return result - except BaseException: - logger.error("%s: Run simulation failed", _LOG_PREFIX) + except Exception: + logger.error("%s: Run simulation failed", LOG_PREFIX, exc_info=True) self._client.post_run_status(run_id, "failed") # type:ignore[arg-type] return None async def _run_simulation_async( self, run_id: str, - run_items: list[SimulationItem], + simulation_items: list[SimulationItem], task: BaseTask, max_concurrency: int, + max_turns: int, ) -> dict[str, Any]: - """Async implementation of run_simulation with semaphore-based concurrency. + """Orchestrate concurrent simulation execution. + + Each simulation item is dispatched to a thread via ``run_in_executor``. + Inside each thread, ``run_async_safely`` creates a **new** event loop + so that async user tasks (``BaseTask.run``) work correctly without + nesting into the orchestrator's loop. This two-level design lets us + honour ``max_concurrency`` while supporting both sync and async tasks + transparently. Args: run_id: The simulation run identifier. - run_items: List of simulation items to process. + simulation_items: List of simulation items to process. task: The BaseTask instance to execute (sync or async). max_concurrency: Maximum concurrent executions. + max_turns: Maximum conversation turns per item. Returns: Dictionary with simulation results. """ - - max_workers = min(5, max_concurrency) results: dict[str, Any] = { "success": True, "completed": [], "failed": [], - "total_items": len(run_items), + "total_items": len(simulation_items), } processed_count = 0 lock = asyncio.Lock() @@ -129,8 +140,7 @@ async def _run_simulation_async( loop = asyncio.get_running_loop() def run_item_in_thread(run_item: SimulationItem) -> dict[str, Any]: - """ - Run a single simulation item in a thread. + """Run a single simulation item in a dedicated thread/event-loop. Args: run_item: The simulation item to run. @@ -138,11 +148,10 @@ def run_item_in_thread(run_item: SimulationItem) -> dict[str, Any]: Returns: Dictionary with simulation result. """ - return run_async_safely(self._execute_conversation(run_id, run_item, task)) + return run_async_safely(self._execute_conversation(run_id, run_item, task, max_turns)) async def process_item(run_item: SimulationItem) -> None: - """ - Process a single simulation item and handle its completion. + """Process a single simulation item and record its outcome. Args: run_item: The simulation item to process. @@ -155,14 +164,14 @@ async def process_item(run_item: SimulationItem) -> None: processed_count += 1 logger.info( "%s: %d/%d processed (run_item_id=%s)", - _LOG_PREFIX, + LOG_PREFIX, processed_count, - len(run_items), + len(simulation_items), run_item.run_item_id, ) - with concurrent.futures.ThreadPoolExecutor(max_workers=max_workers) as executor: - tasks = [asyncio.create_task(process_item(run_item)) for run_item in run_items] + with concurrent.futures.ThreadPoolExecutor(max_workers=max_concurrency) as executor: + tasks = [asyncio.create_task(process_item(item)) for item in simulation_items] try: await asyncio.gather(*tasks) except (asyncio.CancelledError, KeyboardInterrupt): @@ -172,7 +181,7 @@ async def process_item(run_item: SimulationItem) -> None: executor.shutdown(wait=False, cancel_futures=True) logger.info( "%s: Completed=%d, Failed=%d", - _LOG_PREFIX, + LOG_PREFIX, len(results["completed"]), len(results["failed"]), ) @@ -183,13 +192,15 @@ async def _execute_conversation( run_id: str, run_item: SimulationItem, task: BaseTask, - ) -> Any: + max_turns: int, + ) -> dict[str, Any]: """Execute a multi-turn conversation for a single simulation item. Args: run_id: The simulation run identifier. run_item: The simulation item to process. task: The BaseTask instance to execute (sync or async). + max_turns: Safety limit on the number of conversation turns. Returns: Dictionary with execution result including success status. @@ -200,9 +211,9 @@ async def _execute_conversation( raw_files: list[FileData] = run_item.files session_id: Optional[str] = None - while True: + for turn_number in range(1, max_turns + 1): try: - with SpanWrapper(_SPAN_NAME, module_name=_LOG_PREFIX) as span: + with SpanWrapper(SPAN_NAME, module_name=LOG_PREFIX) as span: trace_id = "" otel_span = span.get_current_span() if otel_span: @@ -215,12 +226,12 @@ async def _execute_conversation( if task_session_id: session_id = task_session_id - response = self._client.trigger_conversation( - message=response_message, - turn_id=turn_id, - session_id=session_id or "", - trace_id=trace_id, - ) + response = self._client.trigger_conversation( + message=response_message, + turn_id=turn_id, + session_id=session_id or "", + trace_id=trace_id, + ) if response is None: error_msg = "Failed to get conversation response" @@ -231,10 +242,10 @@ async def _execute_conversation( "turn_id": turn_id, } - if response.decision == "stop": + if response.decision == ConversationStatus.STOP: logger.info( "%s: Completed run_item_id=%s reason=%s", - _LOG_PREFIX, + LOG_PREFIX, run_item_id, response.reason, ) @@ -252,7 +263,7 @@ async def _execute_conversation( error_msg = str(exc) logger.error( "%s: Task failed run_item_id=%s, turn_id=%s: %s", - _LOG_PREFIX, + LOG_PREFIX, run_item_id, turn_id, error_msg, @@ -264,3 +275,13 @@ async def _execute_conversation( "error": error_msg, "turn_id": turn_id, } + + error_msg = f"Exceeded maximum turns ({max_turns}) for run_item_id={run_item_id}" + logger.error("%s: %s", LOG_PREFIX, error_msg) + self._client.report_failure(run_id=run_id, run_item_id=run_item_id, error=error_msg) + return { + "run_item_id": run_item_id, + "success": False, + "error": error_msg, + "turn_id": turn_id, + } diff --git a/netra/simulation/client.py b/netra/simulation/client.py index 77e9b5f..fc22acb 100644 --- a/netra/simulation/client.py +++ b/netra/simulation/client.py @@ -1,19 +1,24 @@ -"""HTTP client for simulation API endpoints.""" - import logging -import os from typing import Any, Optional import httpx from netra.config import Config -from netra.simulation.models import ConversationResponse, FileData, SimulationItem +from netra.simulation.constants import ( + DEFAULT_TIMEOUT, + ENV_TIMEOUT, + LOG_PREFIX, + TELEMETRY_SUFFIX, + URL_AGENT_RESPONSE, + URL_CREATE_RUN, + URL_RUN_ITEM_STATUS, + URL_RUN_STATUS, +) +from netra.simulation.models import ConversationResponse, ConversationStatus, FileData, SimulationItem +from netra.simulation.utils import parse_env_float logger = logging.getLogger(__name__) -_DEFAULT_TIMEOUT = 10.0 -_LOG_PREFIX = "netra.simulation" - class SimulationHttpClient: """Internal HTTP client for simulation API endpoints. @@ -32,6 +37,26 @@ def __init__(self, config: Config) -> None: """ self._client: Optional[httpx.Client] = self._create_client(config) + def close(self) -> None: + """Close the underlying HTTP client and release connection resources.""" + if self._client: + try: + self._client.close() + except Exception: + logger.debug("%s: Error closing HTTP client", LOG_PREFIX, exc_info=True) + finally: + self._client = None + + def _ensure_client(self) -> Optional[httpx.Client]: + """Return the underlying client, logging an error if it is not initialized. + + Returns: + The httpx client, or None if not available. + """ + if not self._client: + logger.error("%s: Client not initialized", LOG_PREFIX) + return self._client + def _create_client(self, config: Config) -> Optional[httpx.Client]: """Create and configure the HTTP client. @@ -43,17 +68,17 @@ def _create_client(self, config: Config) -> Optional[httpx.Client]: """ endpoint = (config.otlp_endpoint or "").strip() if not endpoint: - logger.error("%s: NETRA_OTLP_ENDPOINT is required", _LOG_PREFIX) + logger.error("%s: NETRA_OTLP_ENDPOINT is required", LOG_PREFIX) return None base_url = self._resolve_base_url(endpoint) headers = self._build_headers(config) - timeout = self._get_timeout() + timeout = parse_env_float(ENV_TIMEOUT, DEFAULT_TIMEOUT) try: return httpx.Client(base_url=base_url, headers=headers, timeout=timeout) except Exception as exc: - logger.error("%s: Failed to create HTTP client: %s", _LOG_PREFIX, exc) + logger.error("%s: Failed to create HTTP client: %s", LOG_PREFIX, exc) return None def _resolve_base_url(self, endpoint: str) -> str: @@ -66,8 +91,8 @@ def _resolve_base_url(self, endpoint: str) -> str: The cleaned base URL. """ base_url = endpoint.rstrip("/") - if base_url.endswith("/telemetry"): - base_url = base_url[: -len("/telemetry")] + if base_url.endswith(TELEMETRY_SUFFIX): + base_url = base_url[: -len(TELEMETRY_SUFFIX)] return base_url def _build_headers(self, config: Config) -> dict[str, str]: @@ -84,26 +109,6 @@ def _build_headers(self, config: Config) -> dict[str, str]: headers["x-api-key"] = config.api_key return headers - def _get_timeout(self) -> float: - """Get timeout from environment or use default. - - Returns: - The timeout value in seconds. - """ - timeout_str = os.getenv("NETRA_SIMULATION_TIMEOUT") - if not timeout_str: - return _DEFAULT_TIMEOUT - try: - return float(timeout_str) - except ValueError: - logger.warning( - "%s: Invalid timeout '%s', using default %.1f", - _LOG_PREFIX, - timeout_str, - _DEFAULT_TIMEOUT, - ) - return _DEFAULT_TIMEOUT - def create_run( self, name: str, @@ -120,26 +125,25 @@ def create_run( Returns: Dictionary containing run_id and simulation_items, or None on failure. """ - if not self._client: - logger.error("%s: Client not initialized", _LOG_PREFIX) + if not self._ensure_client(): return None response: Optional[httpx.Response] = None try: - url = "/evaluations/test_run/multi-turn" + url = URL_CREATE_RUN payload: dict[str, Any] = { "name": name, "datasetId": dataset_id, "context": context or {}, } - response = self._client.post(url, json=payload, timeout=500) + response = self._client.post(url, json=payload) # type:ignore[union-attr] response.raise_for_status() data = response.json() response_data = data.get("data", {}) user_messages = response_data.get("userMessages", []) if not user_messages: - logger.warning("%s: No user messages returned from create_run", _LOG_PREFIX) + logger.warning("%s: No user messages returned from create_run", LOG_PREFIX) return None run_id = response_data.get("id", "") @@ -159,7 +163,7 @@ def create_run( except Exception as exc: error_msg = self._extract_error_message(response, exc) - logger.error("%s: Failed to create simulation run: %s", _LOG_PREFIX, error_msg) + logger.error("%s: Failed to create simulation run: %s", LOG_PREFIX, error_msg) return None def trigger_conversation( @@ -180,13 +184,12 @@ def trigger_conversation( Returns: ConversationResponse with next turn info, or None on failure. """ - if not self._client: - logger.error("%s: Client not initialized", _LOG_PREFIX) + if not self._ensure_client(): return None response: Optional[httpx.Response] = None try: - url = "/evaluations/turn/agent-response" + url = URL_AGENT_RESPONSE payload: dict[str, Any] = { "turnId": turn_id, "agentResponse": {"message": message}, @@ -194,14 +197,15 @@ def trigger_conversation( "traceId": trace_id, } - response = self._client.post(url, json=payload, timeout=500) + response = self._client.post(url, json=payload) # type:ignore[union-attr] response.raise_for_status() data = response.json() response_data = data.get("data", {}) - decision = response_data.get("decision", "continue") + raw_decision = response_data.get("decision", "continue") + decision = ConversationStatus(raw_decision) - if decision == "stop": + if decision == ConversationStatus.STOP: return ConversationResponse( decision=decision, reason=response_data.get("reason", ""), @@ -209,7 +213,7 @@ def trigger_conversation( user_messages = response_data.get("userMessages", []) if not user_messages: - logger.warning("%s: No user messages in continue response", _LOG_PREFIX) + logger.warning("%s: No user messages in continue response", LOG_PREFIX) return None next_msg = next(iter(user_messages)) @@ -217,13 +221,12 @@ def trigger_conversation( decision=decision, next_turn_id=next_msg.get("turnId", ""), next_user_message=next_msg.get("userMessage", ""), - next_run_item_id=next_msg.get("testRunItemId", ""), next_files=self._parse_files(next_msg.get("attachments")), ) except Exception as exc: error_msg = self._extract_error_message(response, exc) - logger.error("%s: Failed to trigger conversation: %s", _LOG_PREFIX, error_msg) + logger.error("%s: Failed to trigger conversation: %s", LOG_PREFIX, error_msg) raise def report_failure(self, run_id: str, run_item_id: str, error: str) -> None: @@ -234,20 +237,19 @@ def report_failure(self, run_id: str, run_item_id: str, error: str) -> None: run_item_id: Identifier of the run item. error: Error message describing the failure. """ - if not self._client: - logger.error("%s: Client not initialized", _LOG_PREFIX) + if not self._ensure_client(): return response: Optional[httpx.Response] = None try: - url = f"/evaluations/run/{run_id}/item/{run_item_id}/status" + url = URL_RUN_ITEM_STATUS.format(run_id=run_id, run_item_id=run_item_id) payload: dict[str, Any] = {"status": "failed", "failureReason": error} - response = self._client.patch(url, json=payload) + response = self._client.patch(url, json=payload) # type:ignore[union-attr] response.raise_for_status() - logger.info("%s: Reported failure - %s", _LOG_PREFIX, error) + logger.info("%s: Reported failure - %s", LOG_PREFIX, error) except Exception as exc: error_msg = self._extract_error_message(response, exc) - logger.error("%s: Failed to report failure: %s", _LOG_PREFIX, error_msg) + logger.error("%s: Failed to report failure: %s", LOG_PREFIX, error_msg) def post_run_status(self, run_id: str, status: str) -> Any: """Submit the run status. @@ -259,24 +261,23 @@ def post_run_status(self, run_id: str, status: str) -> Any: Returns: Backend JSON response containing confirmation, or error dict. """ - if not self._client: - logger.error("%s: Client not initialized; cannot post run status", _LOG_PREFIX) + if not self._ensure_client(): return {"success": False} response: Optional[httpx.Response] = None try: - url = f"/evaluations/run/{run_id}/status" + url = URL_RUN_STATUS.format(run_id=run_id) payload: dict[str, Any] = {"status": status} - response = self._client.post(url, json=payload) + response = self._client.post(url, json=payload) # type:ignore[union-attr] response.raise_for_status() data = response.json() if isinstance(data, dict) and "data" in data: - logger.info("%s: Test run status %s", _LOG_PREFIX, status) + logger.info("%s: Test run status %s", LOG_PREFIX, status) return data.get("data", {}) return data except Exception as exc: error_msg = self._extract_error_message(response, exc) - logger.error("%s: Failed to post run status for run '%s': %s", _LOG_PREFIX, run_id, error_msg) + logger.error("%s: Failed to post run status for run '%s': %s", LOG_PREFIX, run_id, error_msg) return {"success": False} @staticmethod @@ -301,7 +302,7 @@ def _parse_files(raw_files: Any) -> list[FileData]: if not file_name or not download_url: logger.warning( "%s: Skipping file entry with missing fileName or downloadUrl", - _LOG_PREFIX, + LOG_PREFIX, ) continue parsed.append( @@ -335,5 +336,5 @@ def _extract_error_message( if isinstance(error_data, dict): return error_data.get("message", str(exc)) except Exception: - pass + logger.debug("%s: Could not parse error from response body", LOG_PREFIX, exc_info=True) return str(exc) diff --git a/netra/simulation/constants.py b/netra/simulation/constants.py new file mode 100644 index 0000000..2057947 --- /dev/null +++ b/netra/simulation/constants.py @@ -0,0 +1,38 @@ +"""Shared constants for the simulation module.""" + +# --------------------------------------------------------------------------- +# Logging +# --------------------------------------------------------------------------- +LOG_PREFIX = "netra.simulation" + +# --------------------------------------------------------------------------- +# Span / tracing +# --------------------------------------------------------------------------- +SPAN_NAME = "Netra.Simulation.TestRun" + +# --------------------------------------------------------------------------- +# Conversation limits +# --------------------------------------------------------------------------- +DEFAULT_MAX_TURNS = 50 + +# --------------------------------------------------------------------------- +# API endpoints +# --------------------------------------------------------------------------- +URL_CREATE_RUN = "/evaluations/test_run/multi-turn" +URL_AGENT_RESPONSE = "/evaluations/turn/agent-response" +URL_RUN_ITEM_STATUS = "/evaluations/run/{run_id}/item/{run_item_id}/status" +URL_RUN_STATUS = "/evaluations/run/{run_id}/status" +TELEMETRY_SUFFIX = "/telemetry" + +# --------------------------------------------------------------------------- +# HTTP client timeouts +# --------------------------------------------------------------------------- +DEFAULT_TIMEOUT = 500.0 +ENV_TIMEOUT = "NETRA_SIMULATION_TIMEOUT" + +# --------------------------------------------------------------------------- +# File download +# --------------------------------------------------------------------------- +DEFAULT_FILE_DOWNLOAD_TIMEOUT = 30.0 +ENV_FILE_DOWNLOAD_TIMEOUT = "NETRA_SIMULATION_FILE_DOWNLOAD_TIMEOUT" +MAX_FILE_DOWNLOAD_WORKERS = 8 diff --git a/netra/simulation/models.py b/netra/simulation/models.py index 570a765..26b68e9 100644 --- a/netra/simulation/models.py +++ b/netra/simulation/models.py @@ -68,19 +68,17 @@ class ConversationResponse: """Response from the conversation trigger API. Attributes: - decision: The decision to continue or stop the conversation. + decision: Whether to continue or stop the conversation. reason: Optional reason for stopping the conversation. next_turn_id: Identifier for the next turn if continuing. next_user_message: The next user message if continuing. - next_run_item_id: Identifier for the next run item if continuing. next_files: File metadata for the next turn if continuing. """ - decision: str + decision: ConversationStatus reason: Optional[str] = None next_turn_id: Optional[str] = None next_user_message: Optional[str] = None - next_run_item_id: Optional[str] = None next_files: list[FileData] = field(default_factory=list) diff --git a/netra/simulation/task.py b/netra/simulation/task.py index 126630d..8ecb40f 100644 --- a/netra/simulation/task.py +++ b/netra/simulation/task.py @@ -6,9 +6,9 @@ """ from abc import ABC, abstractmethod -from typing import Any, Awaitable, Optional +from typing import Awaitable, Optional -from netra.simulation.models import TaskResult +from netra.simulation.models import ProcessedFile, TaskResult class BaseTask(ABC): @@ -18,14 +18,18 @@ class BaseTask(ABC): Subclasses must: - Implement run(): Executes the task logic and returns a TaskResult. - The base contract requires ``message`` and ``session_id``. Subclasses that - need file attachments can add a ``files`` keyword argument — the framework - detects it via introspection and will pass downloaded files automatically. + The framework always passes ``message``, ``session_id``, and ``files`` + to ``run()``. Tasks that don't need file attachments can simply ignore + the ``files`` parameter. Example: class MyTask(BaseTask): - def run(self, message: str, session_id: Optional[str] = None) -> TaskResult: - # Call your LLM or agent here + def run( + self, + message: str, + session_id: Optional[str] = None, + files: Optional[list[ProcessedFile]] = None, + ) -> TaskResult: response = my_agent.chat(message, session_id=session_id) return TaskResult( message=response.text, @@ -46,7 +50,6 @@ def run( session_id: Optional[str] = None, files: Optional[list[ProcessedFile]] = None, ) -> TaskResult: - # Access base64-encoded file data if files: for f in files: print(f.file_name, f.content_type, len(f.data)) @@ -58,8 +61,12 @@ def run( Async Example: class MyAsyncTask(BaseTask): - async def run(self, message: str, session_id: Optional[str] = None) -> TaskResult: - # Call your async LLM or agent here + async def run( + self, + message: str, + session_id: Optional[str] = None, + files: Optional[list[ProcessedFile]] = None, + ) -> TaskResult: response = await my_async_agent.chat(message, session_id=session_id) return TaskResult( message=response.text, @@ -72,7 +79,7 @@ def run( self, message: str, session_id: Optional[str] = None, - **kwargs: Any, + files: Optional[list[ProcessedFile]] = None, ) -> TaskResult | Awaitable[TaskResult]: """ Execute the task logic. @@ -80,17 +87,12 @@ def run( This method can be sync or async. If async, the framework will await the coroutine automatically. - The base signature requires only ``message`` and ``session_id``. - Subclasses that handle file attachments should declare an additional - ``files: Optional[list[ProcessedFile]] = None`` parameter — the - framework will supply it automatically when the dataset item includes - file attachments. - Args: message: The input message from the simulation. session_id: Optional session identifier for conversation continuity. Will be None for the first turn of a conversation. - **kwargs: Reserved for forward-compatible extensions (e.g. ``files``). + files: Optional list of base64-encoded file attachments from the + dataset item. Will be None when no files are attached. Returns: TaskResult: The task result containing: diff --git a/netra/simulation/utils.py b/netra/simulation/utils.py index 4ce62ca..24c078b 100644 --- a/netra/simulation/utils.py +++ b/netra/simulation/utils.py @@ -2,23 +2,52 @@ import asyncio import base64 -import inspect +import concurrent.futures import logging import os import threading -from typing import Awaitable, Optional, Tuple, TypeVar +from typing import Awaitable, Optional, TypeVar import httpx +from netra.simulation.constants import ( + DEFAULT_FILE_DOWNLOAD_TIMEOUT, + ENV_FILE_DOWNLOAD_TIMEOUT, + LOG_PREFIX, + MAX_FILE_DOWNLOAD_WORKERS, +) from netra.simulation.models import FileData, ProcessedFile, TaskResult from netra.simulation.task import BaseTask logger = logging.getLogger(__name__) -T = TypeVar("T") +_T = TypeVar("_T") -_LOG_PREFIX = "netra.simulation" -_DEFAULT_FILE_DOWNLOAD_TIMEOUT = 30.0 + +def parse_env_float(env_var: str, default: float) -> float: + """Read an environment variable and parse it as a float. + + Args: + env_var: Name of the environment variable. + default: Value to return when the variable is unset or invalid. + + Returns: + The parsed float, or *default* on failure. + """ + raw = os.getenv(env_var) + if not raw: + return default + try: + return float(raw) + except ValueError: + logger.warning( + "%s: Invalid value '%s' for %s, using default %.1f", + LOG_PREFIX, + raw, + env_var, + default, + ) + return default def format_trace_id(trace_id: int) -> str: @@ -55,11 +84,14 @@ def validate_simulation_inputs( return True -def run_async_safely(coro: Awaitable[T]) -> T: - """Run an async coroutine from sync code. +def run_async_safely(coro: Awaitable[_T]) -> _T: + """Run an async coroutine from synchronous code. - If an event loop is already running, executes in a dedicated thread - to avoid 'asyncio.run() cannot be called from a running event loop'. + When called from a context that already has a running event loop (e.g. a + Jupyter notebook, or an async framework like FastAPI), ``asyncio.run()`` + would raise. In that case we spin up a **new daemon thread** with its own + event loop via ``asyncio.run()`` so the caller's loop is never blocked or + re-entered. Args: coro: The coroutine to execute. @@ -76,7 +108,7 @@ def run_async_safely(coro: Awaitable[T]) -> T: loop = None if loop and loop.is_running(): - result_holder: dict[str, T] = {} + result_holder: dict[str, _T] = {} error_holder: dict[str, BaseException] = {} def runner() -> None: @@ -96,31 +128,37 @@ def runner() -> None: return asyncio.run(coro) # type: ignore[arg-type] -def _get_file_download_timeout() -> float: - """Get file download timeout from environment or use default. +def _download_single_file(file_data: FileData, timeout: float) -> ProcessedFile: + """Download a single file and base64-encode its content. + + Args: + file_data: Metadata for the file to download. + timeout: HTTP request timeout in seconds. Returns: - The timeout value in seconds. + A ProcessedFile with the base64-encoded content. + + Raises: + RuntimeError: If the download or encoding fails. """ - timeout_str = os.getenv("NETRA_SIMULATION_FILE_DOWNLOAD_TIMEOUT") - if not timeout_str: - return _DEFAULT_FILE_DOWNLOAD_TIMEOUT try: - return float(timeout_str) - except ValueError: - logger.warning( - "%s: Invalid file download timeout '%s', using default %.1f", - _LOG_PREFIX, - timeout_str, - _DEFAULT_FILE_DOWNLOAD_TIMEOUT, + response = httpx.get(file_data.download_url, timeout=timeout) + response.raise_for_status() + encoded = base64.b64encode(response.content).decode("ascii") + return ProcessedFile( + file_name=file_data.file_name, + content_type=file_data.content_type, + description=file_data.description, + data=encoded, ) - return _DEFAULT_FILE_DOWNLOAD_TIMEOUT + except Exception as exc: + raise RuntimeError(f"Failed to download file '{file_data.file_name}': {exc}") from exc def process_files(files: list[FileData]) -> list[ProcessedFile]: """Download files from pre-signed URLs and base64-encode their content. - Each file is downloaded individually. If any file fails to download, the + Downloads run concurrently via a thread pool. If any file fails, the entire batch is aborted with a ``RuntimeError`` so that file-aware tasks never receive a partial file list. @@ -136,48 +174,15 @@ def process_files(files: list[FileData]) -> list[ProcessedFile]: if not files: return [] - timeout = _get_file_download_timeout() - processed: list[ProcessedFile] = [] - - for file_data in files: - try: - response = httpx.get(file_data.download_url, timeout=timeout) - response.raise_for_status() - encoded = base64.b64encode(response.content).decode("ascii") - processed.append( - ProcessedFile( - file_name=file_data.file_name, - content_type=file_data.content_type, - description=file_data.description, - data=encoded, - ) - ) - except Exception as exc: - raise RuntimeError(f"Failed to download file '{file_data.file_name}': {exc}") from exc - - return processed + timeout = parse_env_float(ENV_FILE_DOWNLOAD_TIMEOUT, DEFAULT_FILE_DOWNLOAD_TIMEOUT) + if len(files) == 1: + return [_download_single_file(files[0], timeout)] -def _task_accepts_files(task: BaseTask) -> bool: - """Check whether the task's run() method accepts a 'files' parameter. - - Used for backward compatibility so that existing BaseTask subclasses that - do not declare the files parameter are not broken. - - Args: - task: The BaseTask instance to inspect. - - Returns: - True if the run() method has a 'files' parameter or **kwargs. - """ - try: - sig = inspect.signature(task.run) - params = sig.parameters - if "files" in params: - return True - return any(p.kind == inspect.Parameter.VAR_KEYWORD for p in params.values()) - except (ValueError, TypeError): - return False + max_workers = min(MAX_FILE_DOWNLOAD_WORKERS, len(files)) + with concurrent.futures.ThreadPoolExecutor(max_workers=max_workers) as pool: + futures = [pool.submit(_download_single_file, fd, timeout) for fd in files] + return [f.result() for f in futures] async def execute_task( @@ -185,19 +190,14 @@ async def execute_task( message: str, session_id: Optional[str], raw_files: Optional[list[FileData]] = None, -) -> Tuple[str, Optional[str]]: +) -> tuple[str, Optional[str]]: """Execute a task's run method (sync or async) and extract message and session_id. - Files are only downloaded and base64-encoded when the task's run() method - actually accepts a ``files`` parameter, avoiding unnecessary network I/O - for legacy tasks. - Args: task: The BaseTask instance to execute. message: The input message to pass to the task. session_id: The current session identifier. - raw_files: Raw file metadata from the backend. Downloads are deferred - until we confirm the task can accept them. + raw_files: Raw file metadata from the backend. Returns: A tuple of (response_message, session_id). @@ -205,11 +205,9 @@ async def execute_task( Raises: ValueError: If the task returns an unsupported type. """ - kwargs: dict[str, object] = {"message": message, "session_id": session_id} - if raw_files and _task_accepts_files(task): - kwargs["files"] = process_files(raw_files) + processed_files = process_files(raw_files) if raw_files else None - result = task.run(**kwargs) # type: ignore[arg-type] + result = task.run(message=message, session_id=session_id, files=processed_files) if asyncio.iscoroutine(result): result = await result diff --git a/poetry.lock b/poetry.lock index aa76cca..f511334 100644 --- a/poetry.lock +++ b/poetry.lock @@ -137,27 +137,6 @@ files = [ frozenlist = ">=1.1.0" typing-extensions = {version = ">=4.2", markers = "python_version < \"3.13\""} -[[package]] -name = "alembic" -version = "1.18.4" -description = "A database migration tool for SQLAlchemy." -optional = false -python-versions = ">=3.10" -groups = ["main"] -files = [ - {file = "alembic-1.18.4-py3-none-any.whl", hash = "sha256:a5ed4adcf6d8a4cb575f3d759f071b03cd6e5c7618eb796cb52497be25bfe19a"}, - {file = "alembic-1.18.4.tar.gz", hash = "sha256:cb6e1fd84b6174ab8dbb2329f86d631ba9559dd78df550b57804d607672cedbc"}, -] - -[package.dependencies] -Mako = "*" -SQLAlchemy = ">=1.4.23" -tomli = {version = "*", markers = "python_version < \"3.11\""} -typing-extensions = ">=4.12" - -[package.extras] -tz = ["tzdata"] - [[package]] name = "annotated-types" version = "0.7.0" @@ -265,21 +244,6 @@ files = [ {file = "async_timeout-5.0.1.tar.gz", hash = "sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3"}, ] -[[package]] -name = "asyncer" -version = "0.0.8" -description = "Asyncer, async and await, focused on developer experience." -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "asyncer-0.0.8-py3-none-any.whl", hash = "sha256:5920d48fc99c8f8f0f1576e1882f5022885589c5fcbc46ce4224ec3e53776eeb"}, - {file = "asyncer-0.0.8.tar.gz", hash = "sha256:a589d980f57e20efb07ed91d0dbe67f1d2fd343e7142c66d3a099f05c620739c"}, -] - -[package.dependencies] -anyio = ">=3.4.0,<5.0" - [[package]] name = "attrs" version = "25.3.0" @@ -442,18 +406,6 @@ files = [ [package.dependencies] numpy = {version = ">=1.19.0,<3.0.0", markers = "python_version >= \"3.9\""} -[[package]] -name = "cachetools" -version = "7.0.1" -description = "Extensible memoizing collections and decorators" -optional = false -python-versions = ">=3.10" -groups = ["main"] -files = [ - {file = "cachetools-7.0.1-py3-none-any.whl", hash = "sha256:8f086515c254d5664ae2146d14fc7f65c9a4bce75152eb247e5a9c5e6d7b2ecf"}, - {file = "cachetools-7.0.1.tar.gz", hash = "sha256:e31e579d2c5b6e2944177a0397150d312888ddf4e16e12f1016068f0c03b8341"}, -] - [[package]] name = "catalogue" version = "2.0.10" @@ -685,6 +637,7 @@ files = [ {file = "click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b"}, {file = "click-8.2.1.tar.gz", hash = "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202"}, ] +markers = {main = "extra == \"presidio\""} [package.dependencies] colorama = {version = "*", markers = "platform_system == \"Windows\""} @@ -711,18 +664,6 @@ azure = ["azure-storage-blob (>=12)", "azure-storage-file-datalake (>=12)"] gs = ["google-cloud-storage"] s3 = ["boto3 (>=1.34.0)"] -[[package]] -name = "cloudpickle" -version = "3.1.2" -description = "Pickler class to extend the standard pickle.Pickler functionality" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "cloudpickle-3.1.2-py3-none-any.whl", hash = "sha256:9acb47f6afd73f60dc1df93bb801b472f05ff42fa6c84167d25cb206be1fbf4a"}, - {file = "cloudpickle-3.1.2.tar.gz", hash = "sha256:7fda9eb655c9c230dab534f1983763de5835249750e85fbcef43aaa30a9a2414"}, -] - [[package]] name = "colorama" version = "0.4.6" @@ -735,24 +676,6 @@ files = [ {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] -[[package]] -name = "colorlog" -version = "6.10.1" -description = "Add colours to the output of Python's logging module." -optional = false -python-versions = ">=3.6" -groups = ["main"] -files = [ - {file = "colorlog-6.10.1-py3-none-any.whl", hash = "sha256:2d7e8348291948af66122cff006c9f8da6255d224e7cf8e37d8de2df3bad8c9c"}, - {file = "colorlog-6.10.1.tar.gz", hash = "sha256:eb4ae5cb65fe7fec7773c2306061a8e63e02efc2c72eba9d27b0fa23c94f1321"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "sys_platform == \"win32\""} - -[package.extras] -development = ["black", "flake8", "mypy", "pytest", "types-colorama"] - [[package]] name = "commitizen" version = "4.8.3" @@ -934,18 +857,6 @@ wrapt = ">=1.10,<2" [package.extras] dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "setuptools ; python_version >= \"3.12\"", "tox"] -[[package]] -name = "diskcache" -version = "5.6.3" -description = "Disk Cache -- Disk and file backed persistent cache." -optional = false -python-versions = ">=3" -groups = ["main"] -files = [ - {file = "diskcache-5.6.3-py3-none-any.whl", hash = "sha256:5e31b2d5fbad117cc363ebaf6b689474db18a1f6438bc82358b024abd4c2ca19"}, - {file = "diskcache-5.6.3.tar.gz", hash = "sha256:2c3a3fa2743d8535d832ec61c2054a1641f41775aa7c556758a109941e33e4fc"}, -] - [[package]] name = "distlib" version = "0.3.9" @@ -970,50 +881,6 @@ files = [ {file = "distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed"}, ] -[[package]] -name = "dspy" -version = "3.0.3" -description = "DSPy" -optional = false -python-versions = "<3.14,>=3.10" -groups = ["main"] -files = [ - {file = "dspy-3.0.3-py3-none-any.whl", hash = "sha256:d19cc38ab3ec7edcb3db56a3463a606268dd2e83280595062b052bcfe0cfd24f"}, - {file = "dspy-3.0.3.tar.gz", hash = "sha256:4f77c9571a0f5071495b81acedd44ded1dacd4cdcb4e9fe942da144274f7fbf8"}, -] - -[package.dependencies] -anyio = "*" -asyncer = "0.0.8" -backoff = ">=2.2" -cachetools = ">=5.5.0" -cloudpickle = ">=3.0.0" -diskcache = ">=5.6.0" -gepa = {version = "0.0.7", extras = ["dspy"]} -joblib = ">=1.3,<2.0" -json-repair = ">=0.30.0" -litellm = ">=1.64.0" -magicattr = ">=0.1.6" -numpy = ">=1.26.0" -openai = ">=0.28.1" -optuna = ">=3.4.0" -orjson = ">=3.9.0" -pydantic = ">=2.0" -regex = ">=2023.10.3" -requests = ">=2.31.0" -rich = ">=13.7.1" -tenacity = ">=8.2.3" -tqdm = ">=4.66.1" -xxhash = ">=3.5.0" - -[package.extras] -anthropic = ["anthropic (>=0.18.0,<1.0.0)"] -dev = ["build (>=1.0.3)", "datamodel_code_generator (>=0.26.3)", "litellm (>=1.64.0) ; sys_platform == \"win32\"", "litellm[proxy] (>=1.64.0) ; sys_platform != \"win32\"", "pillow (>=10.1.0)", "pre-commit (>=3.7.0)", "pytest (>=6.2.5)", "pytest-asyncio (>=0.26.0)", "pytest-mock (>=3.12.0)", "ruff (>=0.3.0)"] -langchain = ["langchain_core"] -mcp = ["mcp ; python_version >= \"3.10\""] -test-extras = ["datasets (>=2.14.6)", "langchain_core", "mcp ; python_version >= \"3.10\"", "optuna (>=3.4.0)", "pandas (>=2.1.1)"] -weaviate = ["weaviate-client (>=4.5.4,<4.6.0)"] - [[package]] name = "emoji" version = "2.14.1" @@ -1049,94 +916,6 @@ typing-extensions = {version = ">=4.6.0", markers = "python_version < \"3.13\""} [package.extras] test = ["pytest (>=6)"] -[[package]] -name = "fastuuid" -version = "0.14.0" -description = "Python bindings to Rust's UUID library." -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "fastuuid-0.14.0-cp310-cp310-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:6e6243d40f6c793c3e2ee14c13769e341b90be5ef0c23c82fa6515a96145181a"}, - {file = "fastuuid-0.14.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:13ec4f2c3b04271f62be2e1ce7e95ad2dd1cf97e94503a3760db739afbd48f00"}, - {file = "fastuuid-0.14.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b2fdd48b5e4236df145a149d7125badb28e0a383372add3fbaac9a6b7a394470"}, - {file = "fastuuid-0.14.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f74631b8322d2780ebcf2d2d75d58045c3e9378625ec51865fe0b5620800c39d"}, - {file = "fastuuid-0.14.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:83cffc144dc93eb604b87b179837f2ce2af44871a7b323f2bfed40e8acb40ba8"}, - {file = "fastuuid-0.14.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1a771f135ab4523eb786e95493803942a5d1fc1610915f131b363f55af53b219"}, - {file = "fastuuid-0.14.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4edc56b877d960b4eda2c4232f953a61490c3134da94f3c28af129fb9c62a4f6"}, - {file = "fastuuid-0.14.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:bcc96ee819c282e7c09b2eed2b9bd13084e3b749fdb2faf58c318d498df2efbe"}, - {file = "fastuuid-0.14.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7a3c0bca61eacc1843ea97b288d6789fbad7400d16db24e36a66c28c268cfe3d"}, - {file = "fastuuid-0.14.0-cp310-cp310-win32.whl", hash = "sha256:7f2f3efade4937fae4e77efae1af571902263de7b78a0aee1a1653795a093b2a"}, - {file = "fastuuid-0.14.0-cp310-cp310-win_amd64.whl", hash = "sha256:ae64ba730d179f439b0736208b4c279b8bc9c089b102aec23f86512ea458c8a4"}, - {file = "fastuuid-0.14.0-cp311-cp311-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:73946cb950c8caf65127d4e9a325e2b6be0442a224fd51ba3b6ac44e1912ce34"}, - {file = "fastuuid-0.14.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:12ac85024637586a5b69645e7ed986f7535106ed3013640a393a03e461740cb7"}, - {file = "fastuuid-0.14.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:05a8dde1f395e0c9b4be515b7a521403d1e8349443e7641761af07c7ad1624b1"}, - {file = "fastuuid-0.14.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09378a05020e3e4883dfdab438926f31fea15fd17604908f3d39cbeb22a0b4dc"}, - {file = "fastuuid-0.14.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbb0c4b15d66b435d2538f3827f05e44e2baafcc003dd7d8472dc67807ab8fd8"}, - {file = "fastuuid-0.14.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cd5a7f648d4365b41dbf0e38fe8da4884e57bed4e77c83598e076ac0c93995e7"}, - {file = "fastuuid-0.14.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c0a94245afae4d7af8c43b3159d5e3934c53f47140be0be624b96acd672ceb73"}, - {file = "fastuuid-0.14.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:2b29e23c97e77c3a9514d70ce343571e469098ac7f5a269320a0f0b3e193ab36"}, - {file = "fastuuid-0.14.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1e690d48f923c253f28151b3a6b4e335f2b06bf669c68a02665bc150b7839e94"}, - {file = "fastuuid-0.14.0-cp311-cp311-win32.whl", hash = "sha256:a6f46790d59ab38c6aa0e35c681c0484b50dc0acf9e2679c005d61e019313c24"}, - {file = "fastuuid-0.14.0-cp311-cp311-win_amd64.whl", hash = "sha256:e150eab56c95dc9e3fefc234a0eedb342fac433dacc273cd4d150a5b0871e1fa"}, - {file = "fastuuid-0.14.0-cp312-cp312-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:77e94728324b63660ebf8adb27055e92d2e4611645bf12ed9d88d30486471d0a"}, - {file = "fastuuid-0.14.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:caa1f14d2102cb8d353096bc6ef6c13b2c81f347e6ab9d6fbd48b9dea41c153d"}, - {file = "fastuuid-0.14.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d23ef06f9e67163be38cece704170486715b177f6baae338110983f99a72c070"}, - {file = "fastuuid-0.14.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0c9ec605ace243b6dbe3bd27ebdd5d33b00d8d1d3f580b39fdd15cd96fd71796"}, - {file = "fastuuid-0.14.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:808527f2407f58a76c916d6aa15d58692a4a019fdf8d4c32ac7ff303b7d7af09"}, - {file = "fastuuid-0.14.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2fb3c0d7fef6674bbeacdd6dbd386924a7b60b26de849266d1ff6602937675c8"}, - {file = "fastuuid-0.14.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ab3f5d36e4393e628a4df337c2c039069344db5f4b9d2a3c9cea48284f1dd741"}, - {file = "fastuuid-0.14.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:b9a0ca4f03b7e0b01425281ffd44e99d360e15c895f1907ca105854ed85e2057"}, - {file = "fastuuid-0.14.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3acdf655684cc09e60fb7e4cf524e8f42ea760031945aa8086c7eae2eeeabeb8"}, - {file = "fastuuid-0.14.0-cp312-cp312-win32.whl", hash = "sha256:9579618be6280700ae36ac42c3efd157049fe4dd40ca49b021280481c78c3176"}, - {file = "fastuuid-0.14.0-cp312-cp312-win_amd64.whl", hash = "sha256:d9e4332dc4ba054434a9594cbfaf7823b57993d7d8e7267831c3e059857cf397"}, - {file = "fastuuid-0.14.0-cp313-cp313-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:77a09cb7427e7af74c594e409f7731a0cf887221de2f698e1ca0ebf0f3139021"}, - {file = "fastuuid-0.14.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:9bd57289daf7b153bfa3e8013446aa144ce5e8c825e9e366d455155ede5ea2dc"}, - {file = "fastuuid-0.14.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ac60fc860cdf3c3f327374db87ab8e064c86566ca8c49d2e30df15eda1b0c2d5"}, - {file = "fastuuid-0.14.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ab32f74bd56565b186f036e33129da77db8be09178cd2f5206a5d4035fb2a23f"}, - {file = "fastuuid-0.14.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:33e678459cf4addaedd9936bbb038e35b3f6b2061330fd8f2f6a1d80414c0f87"}, - {file = "fastuuid-0.14.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1e3cc56742f76cd25ecb98e4b82a25f978ccffba02e4bdce8aba857b6d85d87b"}, - {file = "fastuuid-0.14.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:cb9a030f609194b679e1660f7e32733b7a0f332d519c5d5a6a0a580991290022"}, - {file = "fastuuid-0.14.0-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:09098762aad4f8da3a888eb9ae01c84430c907a297b97166b8abc07b640f2995"}, - {file = "fastuuid-0.14.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:1383fff584fa249b16329a059c68ad45d030d5a4b70fb7c73a08d98fd53bcdab"}, - {file = "fastuuid-0.14.0-cp313-cp313-win32.whl", hash = "sha256:a0809f8cc5731c066c909047f9a314d5f536c871a7a22e815cc4967c110ac9ad"}, - {file = "fastuuid-0.14.0-cp313-cp313-win_amd64.whl", hash = "sha256:0df14e92e7ad3276327631c9e7cec09e32572ce82089c55cb1bb8df71cf394ed"}, - {file = "fastuuid-0.14.0-cp314-cp314-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:b852a870a61cfc26c884af205d502881a2e59cc07076b60ab4a951cc0c94d1ad"}, - {file = "fastuuid-0.14.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:c7502d6f54cd08024c3ea9b3514e2d6f190feb2f46e6dbcd3747882264bb5f7b"}, - {file = "fastuuid-0.14.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1ca61b592120cf314cfd66e662a5b54a578c5a15b26305e1b8b618a6f22df714"}, - {file = "fastuuid-0.14.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa75b6657ec129d0abded3bec745e6f7ab642e6dba3a5272a68247e85f5f316f"}, - {file = "fastuuid-0.14.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8a0dfea3972200f72d4c7df02c8ac70bad1bb4c58d7e0ec1e6f341679073a7f"}, - {file = "fastuuid-0.14.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1bf539a7a95f35b419f9ad105d5a8a35036df35fdafae48fb2fd2e5f318f0d75"}, - {file = "fastuuid-0.14.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:9a133bf9cc78fdbd1179cb58a59ad0100aa32d8675508150f3658814aeefeaa4"}, - {file = "fastuuid-0.14.0-cp314-cp314-musllinux_1_1_i686.whl", hash = "sha256:f54d5b36c56a2d5e1a31e73b950b28a0d83eb0c37b91d10408875a5a29494bad"}, - {file = "fastuuid-0.14.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:ec27778c6ca3393ef662e2762dba8af13f4ec1aaa32d08d77f71f2a70ae9feb8"}, - {file = "fastuuid-0.14.0-cp314-cp314-win32.whl", hash = "sha256:e23fc6a83f112de4be0cc1990e5b127c27663ae43f866353166f87df58e73d06"}, - {file = "fastuuid-0.14.0-cp314-cp314-win_amd64.whl", hash = "sha256:df61342889d0f5e7a32f7284e55ef95103f2110fee433c2ae7c2c0956d76ac8a"}, - {file = "fastuuid-0.14.0-cp38-cp38-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:47c821f2dfe95909ead0085d4cb18d5149bca704a2b03e03fb3f81a5202d8cea"}, - {file = "fastuuid-0.14.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:3964bab460c528692c70ab6b2e469dd7a7b152fbe8c18616c58d34c93a6cf8d4"}, - {file = "fastuuid-0.14.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c501561e025b7aea3508719c5801c360c711d5218fc4ad5d77bf1c37c1a75779"}, - {file = "fastuuid-0.14.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2dce5d0756f046fa792a40763f36accd7e466525c5710d2195a038f93ff96346"}, - {file = "fastuuid-0.14.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:193ca10ff553cf3cc461572da83b5780fc0e3eea28659c16f89ae5202f3958d4"}, - {file = "fastuuid-0.14.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0737606764b29785566f968bd8005eace73d3666bd0862f33a760796e26d1ede"}, - {file = "fastuuid-0.14.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:e0976c0dff7e222513d206e06341503f07423aceb1db0b83ff6851c008ceee06"}, - {file = "fastuuid-0.14.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:6fbc49a86173e7f074b1a9ec8cf12ca0d54d8070a85a06ebf0e76c309b84f0d0"}, - {file = "fastuuid-0.14.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:de01280eabcd82f7542828ecd67ebf1551d37203ecdfd7ab1f2e534edb78d505"}, - {file = "fastuuid-0.14.0-cp38-cp38-win32.whl", hash = "sha256:af5967c666b7d6a377098849b07f83462c4fedbafcf8eb8bc8ff05dcbe8aa209"}, - {file = "fastuuid-0.14.0-cp38-cp38-win_amd64.whl", hash = "sha256:c3091e63acf42f56a6f74dc65cfdb6f99bfc79b5913c8a9ac498eb7ca09770a8"}, - {file = "fastuuid-0.14.0-cp39-cp39-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:2ec3d94e13712a133137b2805073b65ecef4a47217d5bac15d8ac62376cefdb4"}, - {file = "fastuuid-0.14.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:139d7ff12bb400b4a0c76be64c28cbe2e2edf60b09826cbfd85f33ed3d0bbe8b"}, - {file = "fastuuid-0.14.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d55b7e96531216fc4f071909e33e35e5bfa47962ae67d9e84b00a04d6e8b7173"}, - {file = "fastuuid-0.14.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c0eb25f0fd935e376ac4334927a59e7c823b36062080e2e13acbaf2af15db836"}, - {file = "fastuuid-0.14.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:089c18018fdbdda88a6dafd7d139f8703a1e7c799618e33ea25eb52503d28a11"}, - {file = "fastuuid-0.14.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2fc37479517d4d70c08696960fad85494a8a7a0af4e93e9a00af04d74c59f9e3"}, - {file = "fastuuid-0.14.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:73657c9f778aba530bc96a943d30e1a7c80edb8278df77894fe9457540df4f85"}, - {file = "fastuuid-0.14.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:d31f8c257046b5617fc6af9c69be066d2412bdef1edaa4bdf6a214cf57806105"}, - {file = "fastuuid-0.14.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5816d41f81782b209843e52fdef757a361b448d782452d96abedc53d545da722"}, - {file = "fastuuid-0.14.0-cp39-cp39-win32.whl", hash = "sha256:448aa6833f7a84bfe37dd47e33df83250f404d591eb83527fa2cac8d1e57d7f3"}, - {file = "fastuuid-0.14.0-cp39-cp39-win_amd64.whl", hash = "sha256:84b0779c5abbdec2a9511d5ffbfcd2e53079bf889824b32be170c0d8ef5fc74c"}, - {file = "fastuuid-0.14.0.tar.gz", hash = "sha256:178947fc2f995b38497a74172adee64fdeb8b7ec18f2a5934d037641ba265d26"}, -] - [[package]] name = "filelock" version = "3.18.0" @@ -1325,24 +1104,6 @@ test-downstream = ["aiobotocore (>=2.5.4,<3.0.0)", "dask[dataframe,test]", "moto test-full = ["adlfs", "aiohttp (!=4.0.0a0,!=4.0.0a1)", "cloudpickle", "dask", "distributed", "dropbox", "dropboxdrivefs", "fastparquet", "fusepy", "gcsfs", "jinja2", "kerchunk", "libarchive-c", "lz4", "notebook", "numpy", "ocifs", "pandas", "panel", "paramiko", "pyarrow", "pyarrow (>=1)", "pyftpdlib", "pygit2", "pytest", "pytest-asyncio (!=0.22.0)", "pytest-benchmark", "pytest-cov", "pytest-mock", "pytest-recording", "pytest-rerunfailures", "python-snappy", "requests", "smbprotocol", "tqdm", "urllib3", "zarr", "zstandard"] tqdm = ["tqdm"] -[[package]] -name = "gepa" -version = "0.0.7" -description = "A framework for optimizing textual system components (AI prompts, code snippets, etc.) using LLM-based reflection and Pareto-efficient evolutionary search." -optional = false -python-versions = "<3.14,>=3.10" -groups = ["main"] -files = [ - {file = "gepa-0.0.7-py3-none-any.whl", hash = "sha256:59b8b74f5e384a62d6f590ac6ffe0fa8a0e62fee8d8d6c539f490823d0ffb25c"}, - {file = "gepa-0.0.7.tar.gz", hash = "sha256:3fb98c2908f6e4cbe701a6f0088c4ea599185a801a02b7872b0c624142679cf7"}, -] - -[package.extras] -build = ["build", "packaging", "requests", "semver", "setuptools (>=77.0.1)", "twine", "wheel"] -dev = ["build (>=1.0.3)", "gepa[build]", "gepa[test]", "pre-commit", "ruff (>=0.3.0)"] -full = ["datasets (>=2.14.6)", "litellm (>=1.64.0)", "tqdm (>=4.66.1)", "wandb"] -test = ["gepa[full]", "pytest"] - [[package]] name = "googleapis-common-protos" version = "1.70.0" @@ -1361,74 +1122,6 @@ protobuf = ">=3.20.2,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4 [package.extras] grpc = ["grpcio (>=1.44.0,<2.0.0)"] -[[package]] -name = "greenlet" -version = "3.3.2" -description = "Lightweight in-process concurrent programming" -optional = false -python-versions = ">=3.10" -groups = ["main"] -markers = "platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\"" -files = [ - {file = "greenlet-3.3.2-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:9bc885b89709d901859cf95179ec9f6bb67a3d2bb1f0e88456461bd4b7f8fd0d"}, - {file = "greenlet-3.3.2-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b568183cf65b94919be4438dc28416b234b678c608cafac8874dfeeb2a9bbe13"}, - {file = "greenlet-3.3.2-cp310-cp310-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:527fec58dc9f90efd594b9b700662ed3fb2493c2122067ac9c740d98080a620e"}, - {file = "greenlet-3.3.2-cp310-cp310-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:508c7f01f1791fbc8e011bd508f6794cb95397fdb198a46cb6635eb5b78d85a7"}, - {file = "greenlet-3.3.2-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ad0c8917dd42a819fe77e6bdfcb84e3379c0de956469301d9fd36427a1ca501f"}, - {file = "greenlet-3.3.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:97245cc10e5515dbc8c3104b2928f7f02b6813002770cfaffaf9a6e0fc2b94ef"}, - {file = "greenlet-3.3.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8c1fdd7d1b309ff0da81d60a9688a8bd044ac4e18b250320a96fc68d31c209ca"}, - {file = "greenlet-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:5d0e35379f93a6d0222de929a25ab47b5eb35b5ef4721c2b9cbcc4036129ff1f"}, - {file = "greenlet-3.3.2-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:c56692189a7d1c7606cb794be0a8381470d95c57ce5be03fb3d0ef57c7853b86"}, - {file = "greenlet-3.3.2-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1ebd458fa8285960f382841da585e02201b53a5ec2bac6b156fc623b5ce4499f"}, - {file = "greenlet-3.3.2-cp311-cp311-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a443358b33c4ec7b05b79a7c8b466f5d275025e750298be7340f8fc63dff2a55"}, - {file = "greenlet-3.3.2-cp311-cp311-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4375a58e49522698d3e70cc0b801c19433021b5c37686f7ce9c65b0d5c8677d2"}, - {file = "greenlet-3.3.2-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8e2cd90d413acbf5e77ae41e5d3c9b3ac1d011a756d7284d7f3f2b806bbd6358"}, - {file = "greenlet-3.3.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:442b6057453c8cb29b4fb36a2ac689382fc71112273726e2423f7f17dc73bf99"}, - {file = "greenlet-3.3.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:45abe8eb6339518180d5a7fa47fa01945414d7cca5ecb745346fc6a87d2750be"}, - {file = "greenlet-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:1e692b2dae4cc7077cbb11b47d258533b48c8fde69a33d0d8a82e2fe8d8531d5"}, - {file = "greenlet-3.3.2-cp311-cp311-win_arm64.whl", hash = "sha256:02b0a8682aecd4d3c6c18edf52bc8e51eacdd75c8eac52a790a210b06aa295fd"}, - {file = "greenlet-3.3.2-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:ac8d61d4343b799d1e526db579833d72f23759c71e07181c2d2944e429eb09cd"}, - {file = "greenlet-3.3.2-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3ceec72030dae6ac0c8ed7591b96b70410a8be370b6a477b1dbc072856ad02bd"}, - {file = "greenlet-3.3.2-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a2a5be83a45ce6188c045bcc44b0ee037d6a518978de9a5d97438548b953a1ac"}, - {file = "greenlet-3.3.2-cp312-cp312-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ae9e21c84035c490506c17002f5c8ab25f980205c3e61ddb3a2a2a2e6c411fcb"}, - {file = "greenlet-3.3.2-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:43e99d1749147ac21dde49b99c9abffcbc1e2d55c67501465ef0930d6e78e070"}, - {file = "greenlet-3.3.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4c956a19350e2c37f2c48b336a3afb4bff120b36076d9d7fb68cb44e05d95b79"}, - {file = "greenlet-3.3.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6c6f8ba97d17a1e7d664151284cb3315fc5f8353e75221ed4324f84eb162b395"}, - {file = "greenlet-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:34308836d8370bddadb41f5a7ce96879b72e2fdfb4e87729330c6ab52376409f"}, - {file = "greenlet-3.3.2-cp312-cp312-win_arm64.whl", hash = "sha256:d3a62fa76a32b462a97198e4c9e99afb9ab375115e74e9a83ce180e7a496f643"}, - {file = "greenlet-3.3.2-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:aa6ac98bdfd716a749b84d4034486863fd81c3abde9aa3cf8eff9127981a4ae4"}, - {file = "greenlet-3.3.2-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ab0c7e7901a00bc0a7284907273dc165b32e0d109a6713babd04471327ff7986"}, - {file = "greenlet-3.3.2-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d248d8c23c67d2291ffd47af766e2a3aa9fa1c6703155c099feb11f526c63a92"}, - {file = "greenlet-3.3.2-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ccd21bb86944ca9be6d967cf7691e658e43417782bce90b5d2faeda0ff78a7dd"}, - {file = "greenlet-3.3.2-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b6997d360a4e6a4e936c0f9625b1c20416b8a0ea18a8e19cabbefc712e7397ab"}, - {file = "greenlet-3.3.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:64970c33a50551c7c50491671265d8954046cb6e8e2999aacdd60e439b70418a"}, - {file = "greenlet-3.3.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1a9172f5bf6bd88e6ba5a84e0a68afeac9dc7b6b412b245dd64f52d83c81e55b"}, - {file = "greenlet-3.3.2-cp313-cp313-win_amd64.whl", hash = "sha256:a7945dd0eab63ded0a48e4dcade82939783c172290a7903ebde9e184333ca124"}, - {file = "greenlet-3.3.2-cp313-cp313-win_arm64.whl", hash = "sha256:394ead29063ee3515b4e775216cb756b2e3b4a7e55ae8fd884f17fa579e6b327"}, - {file = "greenlet-3.3.2-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:8d1658d7291f9859beed69a776c10822a0a799bc4bfe1bd4272bb60e62507dab"}, - {file = "greenlet-3.3.2-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:18cb1b7337bca281915b3c5d5ae19f4e76d35e1df80f4ad3c1a7be91fadf1082"}, - {file = "greenlet-3.3.2-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c2e47408e8ce1c6f1ceea0dffcdf6ebb85cc09e55c7af407c99f1112016e45e9"}, - {file = "greenlet-3.3.2-cp314-cp314-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e3cb43ce200f59483eb82949bf1835a99cf43d7571e900d7c8d5c62cdf25d2f9"}, - {file = "greenlet-3.3.2-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:63d10328839d1973e5ba35e98cccbca71b232b14051fd957b6f8b6e8e80d0506"}, - {file = "greenlet-3.3.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:8e4ab3cfb02993c8cc248ea73d7dae6cec0253e9afa311c9b37e603ca9fad2ce"}, - {file = "greenlet-3.3.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:94ad81f0fd3c0c0681a018a976e5c2bd2ca2d9d94895f23e7bb1af4e8af4e2d5"}, - {file = "greenlet-3.3.2-cp314-cp314-win_amd64.whl", hash = "sha256:8c4dd0f3997cf2512f7601563cc90dfb8957c0cff1e3a1b23991d4ea1776c492"}, - {file = "greenlet-3.3.2-cp314-cp314-win_arm64.whl", hash = "sha256:cd6f9e2bbd46321ba3bbb4c8a15794d32960e3b0ae2cc4d49a1a53d314805d71"}, - {file = "greenlet-3.3.2-cp314-cp314t-macosx_11_0_universal2.whl", hash = "sha256:e26e72bec7ab387ac80caa7496e0f908ff954f31065b0ffc1f8ecb1338b11b54"}, - {file = "greenlet-3.3.2-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b466dff7a4ffda6ca975979bab80bdadde979e29fc947ac3be4451428d8b0e4"}, - {file = "greenlet-3.3.2-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b8bddc5b73c9720bea487b3bffdb1840fe4e3656fba3bd40aa1489e9f37877ff"}, - {file = "greenlet-3.3.2-cp314-cp314t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:59b3e2c40f6706b05a9cd299c836c6aa2378cabe25d021acd80f13abf81181cf"}, - {file = "greenlet-3.3.2-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b26b0f4428b871a751968285a1ac9648944cea09807177ac639b030bddebcea4"}, - {file = "greenlet-3.3.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:1fb39a11ee2e4d94be9a76671482be9398560955c9e568550de0224e41104727"}, - {file = "greenlet-3.3.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:20154044d9085151bc309e7689d6f7ba10027f8f5a8c0676ad398b951913d89e"}, - {file = "greenlet-3.3.2-cp314-cp314t-win_amd64.whl", hash = "sha256:c04c5e06ec3e022cbfe2cd4a846e1d4e50087444f875ff6d2c2ad8445495cf1a"}, - {file = "greenlet-3.3.2.tar.gz", hash = "sha256:2eaf067fc6d886931c7962e8c6bede15d2f01965560f3359b27c80bde2d151f2"}, -] - -[package.extras] -docs = ["Sphinx", "furo"] -test = ["objgraph", "psutil", "setuptools"] - [[package]] name = "grpcio" version = "1.73.1" @@ -1812,18 +1505,6 @@ files = [ {file = "jiter-0.10.0.tar.gz", hash = "sha256:07a7142c38aacc85194391108dc91b5b57093c978a9932bd86a36862759d9500"}, ] -[[package]] -name = "joblib" -version = "1.5.3" -description = "Lightweight pipelining with Python functions" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "joblib-1.5.3-py3-none-any.whl", hash = "sha256:5fc3c5039fc5ca8c0276333a188bbd59d6b7ab37fe6632daa76bc7f9ec18e713"}, - {file = "joblib-1.5.3.tar.gz", hash = "sha256:8561a3269e6801106863fd0d6d84bb737be9e7631e33aaed3fb9ce5953688da3"}, -] - [[package]] name = "json-repair" version = "0.44.1" @@ -1836,43 +1517,6 @@ files = [ {file = "json_repair-0.44.1.tar.gz", hash = "sha256:1130eb9733b868dac1340b43cb2effebb519ae6d52dd2d0728c6cca517f1e0b4"}, ] -[[package]] -name = "jsonschema" -version = "4.26.0" -description = "An implementation of JSON Schema validation for Python" -optional = false -python-versions = ">=3.10" -groups = ["main"] -files = [ - {file = "jsonschema-4.26.0-py3-none-any.whl", hash = "sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce"}, - {file = "jsonschema-4.26.0.tar.gz", hash = "sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326"}, -] - -[package.dependencies] -attrs = ">=22.2.0" -jsonschema-specifications = ">=2023.03.6" -referencing = ">=0.28.4" -rpds-py = ">=0.25.0" - -[package.extras] -format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"] -format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "rfc3987-syntax (>=1.1.0)", "uri-template", "webcolors (>=24.6.0)"] - -[[package]] -name = "jsonschema-specifications" -version = "2025.9.1" -description = "The JSON Schema meta-schemas and vocabularies, exposed as a Registry" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe"}, - {file = "jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d"}, -] - -[package.dependencies] -referencing = ">=0.31.0" - [[package]] name = "langcodes" version = "3.5.0" @@ -1913,73 +1557,6 @@ marisa-trie = ">=1.1.0" build = ["build", "twine"] test = ["pytest", "pytest-cov"] -[[package]] -name = "litellm" -version = "1.81.15" -description = "Library to easily interface with LLM API providers" -optional = false -python-versions = "<4.0,>=3.9" -groups = ["main"] -files = [ - {file = "litellm-1.81.15-py3-none-any.whl", hash = "sha256:2fa253658702509ce09fe0e172e5a47baaadf697fb0f784c7fd4ff665ae76ae1"}, - {file = "litellm-1.81.15.tar.gz", hash = "sha256:a8a6277a53280762051c5818ebc76dd5f036368b9426c6f21795ae7f1ac6ebdc"}, -] - -[package.dependencies] -aiohttp = ">=3.10" -click = "*" -fastuuid = ">=0.13.0" -httpx = ">=0.23.0" -importlib-metadata = ">=6.8.0" -jinja2 = ">=3.1.2,<4.0.0" -jsonschema = ">=4.23.0,<5.0.0" -openai = ">=2.8.0" -pydantic = ">=2.5.0,<3.0.0" -python-dotenv = ">=0.2.0" -tiktoken = ">=0.7.0" -tokenizers = "*" - -[package.extras] -caching = ["diskcache (>=5.6.1,<6.0.0)"] -extra-proxy = ["a2a-sdk (>=0.3.22,<0.4.0) ; python_version >= \"3.10\"", "azure-identity (>=1.15.0,<2.0.0) ; python_version >= \"3.9\"", "azure-keyvault-secrets (>=4.8.0,<5.0.0)", "google-cloud-iam (>=2.19.1,<3.0.0)", "google-cloud-kms (>=2.21.3,<3.0.0)", "prisma (==0.11.0)", "redisvl (>=0.4.1,<0.5.0) ; python_version >= \"3.9\" and python_version < \"3.14\"", "resend (>=0.8.0)"] -google = ["google-cloud-aiplatform (>=1.38.0)"] -grpc = ["grpcio (>=1.62.3,<1.68.dev0 || >1.71.0,!=1.71.1,!=1.72.0,!=1.72.1,!=1.73.0) ; python_version < \"3.14\"", "grpcio (>=1.75.0) ; python_version >= \"3.14\""] -mlflow = ["mlflow (>3.1.4) ; python_version >= \"3.10\""] -proxy = ["PyJWT (>=2.10.1,<3.0.0) ; python_version >= \"3.9\"", "apscheduler (>=3.10.4,<4.0.0)", "azure-identity (>=1.15.0,<2.0.0) ; python_version >= \"3.9\"", "azure-storage-blob (>=12.25.1,<13.0.0)", "backoff", "boto3 (==1.40.76)", "cryptography", "fastapi (>=0.120.1)", "fastapi-sso (>=0.16.0,<0.17.0)", "gunicorn (>=23.0.0,<24.0.0)", "litellm-enterprise (==0.1.32)", "litellm-proxy-extras (==0.4.47)", "mcp (>=1.25.0,<2.0.0) ; python_version >= \"3.10\"", "orjson (>=3.9.7,<4.0.0)", "polars (>=1.31.0,<2.0.0) ; python_version >= \"3.10\"", "pynacl (>=1.5.0,<2.0.0)", "pyroscope-io (>=0.8,<0.9) ; sys_platform != \"win32\"", "python-multipart (>=0.0.22,<0.0.23) ; python_version >= \"3.10\"", "pyyaml (>=6.0.1,<7.0.0)", "rich (==13.7.1)", "rq", "soundfile (>=0.12.1,<0.13.0)", "uvicorn (>=0.32.1,<1.0.0)", "uvloop (>=0.21.0,<0.22.0) ; sys_platform != \"win32\"", "websockets (>=15.0.1,<16.0.0)"] -semantic-router = ["semantic-router (>=0.1.12) ; python_version >= \"3.9\" and python_version < \"3.14\""] -utils = ["numpydoc"] - -[[package]] -name = "magicattr" -version = "0.1.6" -description = "A getattr and setattr that works on nested objects, lists, dicts, and any combination thereof without resorting to eval" -optional = false -python-versions = "*" -groups = ["main"] -files = [ - {file = "magicattr-0.1.6-py2.py3-none-any.whl", hash = "sha256:d96b18ee45b5ee83b09c17e15d3459a64de62d538808c2f71182777dd9dbbbdf"}, -] - -[[package]] -name = "mako" -version = "1.3.10" -description = "A super-fast templating language that borrows the best ideas from the existing templating languages." -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "mako-1.3.10-py3-none-any.whl", hash = "sha256:baef24a52fc4fc514a0887ac600f9f1cff3d82c61d4d700a1fa84d597b88db59"}, - {file = "mako-1.3.10.tar.gz", hash = "sha256:99579a6f39583fa7e5630a28c3c1f440e4e97a414b80372649c0ce338da2ea28"}, -] - -[package.dependencies] -MarkupSafe = ">=0.9.2" - -[package.extras] -babel = ["Babel"] -lingua = ["lingua"] -testing = ["pytest"] - [[package]] name = "marisa-trie" version = "1.2.1" @@ -2077,9 +1654,10 @@ test = ["hypothesis", "pytest", "readme-renderer"] name = "markdown-it-py" version = "3.0.0" description = "Python port of markdown-it. Markdown parsing, done right!" -optional = false +optional = true python-versions = ">=3.8" groups = ["main"] +markers = "extra == \"presidio\"" files = [ {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, @@ -2185,9 +1763,10 @@ files = [ name = "mdurl" version = "0.1.2" description = "Markdown URL utilities" -optional = false +optional = true python-versions = ">=3.7" groups = ["main"] +markers = "extra == \"presidio\"" files = [ {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, @@ -2498,9 +2077,10 @@ files = [ name = "numpy" version = "2.2.6" description = "Fundamental package for array computing in Python" -optional = false +optional = true python-versions = ">=3.10" groups = ["main"] +markers = "extra == \"presidio\"" files = [ {file = "numpy-2.2.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b412caa66f72040e6d268491a59f2c43bf03eb6c96dd8f0307829feb7fa2b6fb"}, {file = "numpy-2.2.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e41fd67c52b86603a91c1a505ebaef50b3314de0213461c7a6e99c9a3beff90"}, @@ -2781,34 +2361,6 @@ files = [ {file = "nvidia_nvtx_cu12-12.6.77-py3-none-win_amd64.whl", hash = "sha256:2fb11a4af04a5e6c84073e6404d26588a34afd35379f0855a99797897efa75c0"}, ] -[[package]] -name = "openai" -version = "2.24.0" -description = "The official Python library for the openai API" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "openai-2.24.0-py3-none-any.whl", hash = "sha256:fed30480d7d6c884303287bde864980a4b137b60553ffbcf9ab4a233b7a73d94"}, - {file = "openai-2.24.0.tar.gz", hash = "sha256:1e5769f540dbd01cb33bc4716a23e67b9d695161a734aff9c5f925e2bf99a673"}, -] - -[package.dependencies] -anyio = ">=3.5.0,<5" -distro = ">=1.7.0,<2" -httpx = ">=0.23.0,<1" -jiter = ">=0.10.0,<1" -pydantic = ">=1.9.0,<3" -sniffio = "*" -tqdm = ">4" -typing-extensions = ">=4.11,<5" - -[package.extras] -aiohttp = ["aiohttp", "httpx-aiohttp (>=0.1.9)"] -datalib = ["numpy (>=1)", "pandas (>=1.2.3)", "pandas-stubs (>=1.1.0.11)"] -realtime = ["websockets (>=13,<16)"] -voice-helpers = ["numpy (>=2.0.2)", "sounddevice (>=0.5.1)"] - [[package]] name = "opentelemetry-api" version = "1.34.1" @@ -4471,117 +4023,6 @@ files = [ {file = "opentelemetry_util_http-0.55b1.tar.gz", hash = "sha256:29e119c1f6796cccf5fc2aedb55274435cde5976d0ac3fec3ca20a80118f821e"}, ] -[[package]] -name = "optuna" -version = "4.7.0" -description = "A hyperparameter optimization framework" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "optuna-4.7.0-py3-none-any.whl", hash = "sha256:e41ec84018cecc10eabf28143573b1f0bde0ba56dba8151631a590ecbebc1186"}, - {file = "optuna-4.7.0.tar.gz", hash = "sha256:d91817e2079825557bd2e97de2e8c9ae260bfc99b32712502aef8a5095b2d2c0"}, -] - -[package.dependencies] -alembic = ">=1.5.0" -colorlog = "*" -numpy = "*" -packaging = ">=20.0" -PyYAML = "*" -sqlalchemy = ">=1.4.2" -tqdm = "*" - -[package.extras] -checking = ["mypy", "mypy_boto3_s3", "ruff", "scipy-stubs ; python_version >= \"3.10\"", "types-PyYAML", "types-redis", "types-setuptools", "types-tqdm", "typing_extensions (>=3.10.0.0)"] -document = ["ase", "cmaes (>=0.12.0)", "fvcore", "kaleido (<0.4)", "lightgbm", "matplotlib (!=3.6.0)", "pandas", "pillow", "plotly (>=4.9.0)", "scikit-learn", "sphinx", "sphinx-copybutton", "sphinx-gallery", "sphinx-notfound-page", "sphinx_rtd_theme (>=1.2.0)", "torch", "torchvision"] -optional = ["boto3", "cmaes (>=0.12.0)", "google-cloud-storage", "greenlet", "grpcio", "matplotlib (!=3.6.0)", "pandas", "plotly (>=4.9.0)", "protobuf (>=5.28.1)", "redis", "scikit-learn (>=0.24.2)", "scipy", "torch"] -test = ["fakeredis[lua]", "greenlet", "grpcio", "kaleido (<0.4)", "moto", "protobuf (>=5.28.1)", "pytest", "pytest-xdist", "scipy (>=1.9.2)", "torch"] - -[[package]] -name = "orjson" -version = "3.11.7" -description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy" -optional = false -python-versions = ">=3.10" -groups = ["main"] -files = [ - {file = "orjson-3.11.7-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:a02c833f38f36546ba65a452127633afce4cf0dd7296b753d3bb54e55e5c0174"}, - {file = "orjson-3.11.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b63c6e6738d7c3470ad01601e23376aa511e50e1f3931395b9f9c722406d1a67"}, - {file = "orjson-3.11.7-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:043d3006b7d32c7e233b8cfb1f01c651013ea079e08dcef7189a29abd8befe11"}, - {file = "orjson-3.11.7-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57036b27ac8a25d81112eb0cc9835cd4833c5b16e1467816adc0015f59e870dc"}, - {file = "orjson-3.11.7-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:733ae23ada68b804b222c44affed76b39e30806d38660bf1eb200520d259cc16"}, - {file = "orjson-3.11.7-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5fdfad2093bdd08245f2e204d977facd5f871c88c4a71230d5bcbd0e43bf6222"}, - {file = "orjson-3.11.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cededd6738e1c153530793998e31c05086582b08315db48ab66649768f326baa"}, - {file = "orjson-3.11.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:14f440c7268c8f8633d1b3d443a434bd70cb15686117ea6beff8fdc8f5917a1e"}, - {file = "orjson-3.11.7-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:3a2479753bbb95b0ebcf7969f562cdb9668e6d12416a35b0dda79febf89cdea2"}, - {file = "orjson-3.11.7-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:71924496986275a737f38e3f22b4e0878882b3f7a310d2ff4dc96e812789120c"}, - {file = "orjson-3.11.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b4a9eefdc70bf8bf9857f0290f973dec534ac84c35cd6a7f4083be43e7170a8f"}, - {file = "orjson-3.11.7-cp310-cp310-win32.whl", hash = "sha256:ae9e0b37a834cef7ce8f99de6498f8fad4a2c0bf6bfc3d02abd8ed56aa15b2de"}, - {file = "orjson-3.11.7-cp310-cp310-win_amd64.whl", hash = "sha256:d772afdb22555f0c58cfc741bdae44180122b3616faa1ecadb595cd526e4c993"}, - {file = "orjson-3.11.7-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:9487abc2c2086e7c8eb9a211d2ce8855bae0e92586279d0d27b341d5ad76c85c"}, - {file = "orjson-3.11.7-cp311-cp311-macosx_15_0_arm64.whl", hash = "sha256:79cacb0b52f6004caf92405a7e1f11e6e2de8bdf9019e4f76b44ba045125cd6b"}, - {file = "orjson-3.11.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c2e85fe4698b6a56d5e2ebf7ae87544d668eb6bde1ad1226c13f44663f20ec9e"}, - {file = "orjson-3.11.7-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b8d14b71c0b12963fe8a62aac87119f1afdf4cb88a400f61ca5ae581449efcb5"}, - {file = "orjson-3.11.7-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:91c81ef070c8f3220054115e1ef468b1c9ce8497b4e526cb9f68ab4dc0a7ac62"}, - {file = "orjson-3.11.7-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:411ebaf34d735e25e358a6d9e7978954a9c9d58cfb47bc6683cdc3964cd2f910"}, - {file = "orjson-3.11.7-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a16bcd08ab0bcdfc7e8801d9c4a9cc17e58418e4d48ddc6ded4e9e4b1a94062b"}, - {file = "orjson-3.11.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c0b51672e466fd7e56230ffbae7f1639e18d0ce023351fb75da21b71bc2c960"}, - {file = "orjson-3.11.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:136dcd6a2e796dfd9ffca9fc027d778567b0b7c9968d092842d3c323cef88aa8"}, - {file = "orjson-3.11.7-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:7ba61079379b0ae29e117db13bda5f28d939766e410d321ec1624afc6a0b0504"}, - {file = "orjson-3.11.7-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:0527a4510c300e3b406591b0ba69b5dc50031895b0a93743526a3fc45f59d26e"}, - {file = "orjson-3.11.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a709e881723c9b18acddcfb8ba357322491ad553e277cf467e1e7e20e2d90561"}, - {file = "orjson-3.11.7-cp311-cp311-win32.whl", hash = "sha256:c43b8b5bab288b6b90dac410cca7e986a4fa747a2e8f94615aea407da706980d"}, - {file = "orjson-3.11.7-cp311-cp311-win_amd64.whl", hash = "sha256:6543001328aa857187f905308a028935864aefe9968af3848401b6fe80dbb471"}, - {file = "orjson-3.11.7-cp311-cp311-win_arm64.whl", hash = "sha256:1ee5cc7160a821dfe14f130bc8e63e7611051f964b463d9e2a3a573204446a4d"}, - {file = "orjson-3.11.7-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:bd03ea7606833655048dab1a00734a2875e3e86c276e1d772b2a02556f0d895f"}, - {file = "orjson-3.11.7-cp312-cp312-macosx_15_0_arm64.whl", hash = "sha256:89e440ebc74ce8ab5c7bc4ce6757b4a6b1041becb127df818f6997b5c71aa60b"}, - {file = "orjson-3.11.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ede977b5fe5ac91b1dffc0a517ca4542d2ec8a6a4ff7b2652d94f640796342a"}, - {file = "orjson-3.11.7-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b7b1dae39230a393df353827c855a5f176271c23434cfd2db74e0e424e693e10"}, - {file = "orjson-3.11.7-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ed46f17096e28fb28d2975834836a639af7278aa87c84f68ab08fbe5b8bd75fa"}, - {file = "orjson-3.11.7-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3726be79e36e526e3d9c1aceaadbfb4a04ee80a72ab47b3f3c17fefb9812e7b8"}, - {file = "orjson-3.11.7-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0724e265bc548af1dedebd9cb3d24b4e1c1e685a343be43e87ba922a5c5fff2f"}, - {file = "orjson-3.11.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e7745312efa9e11c17fbd3cb3097262d079da26930ae9ae7ba28fb738367cbad"}, - {file = "orjson-3.11.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f904c24bdeabd4298f7a977ef14ca2a022ca921ed670b92ecd16ab6f3d01f867"}, - {file = "orjson-3.11.7-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:b9fc4d0f81f394689e0814617aadc4f2ea0e8025f38c226cbf22d3b5ddbf025d"}, - {file = "orjson-3.11.7-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:849e38203e5be40b776ed2718e587faf204d184fc9a008ae441f9442320c0cab"}, - {file = "orjson-3.11.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4682d1db3bcebd2b64757e0ddf9e87ae5f00d29d16c5cdf3a62f561d08cc3dd2"}, - {file = "orjson-3.11.7-cp312-cp312-win32.whl", hash = "sha256:f4f7c956b5215d949a1f65334cf9d7612dde38f20a95f2315deef167def91a6f"}, - {file = "orjson-3.11.7-cp312-cp312-win_amd64.whl", hash = "sha256:bf742e149121dc5648ba0a08ea0871e87b660467ef168a3a5e53bc1fbd64bb74"}, - {file = "orjson-3.11.7-cp312-cp312-win_arm64.whl", hash = "sha256:26c3b9132f783b7d7903bf1efb095fed8d4a3a85ec0d334ee8beff3d7a4749d5"}, - {file = "orjson-3.11.7-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:1d98b30cc1313d52d4af17d9c3d307b08389752ec5f2e5febdfada70b0f8c733"}, - {file = "orjson-3.11.7-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:d897e81f8d0cbd2abb82226d1860ad2e1ab3ff16d7b08c96ca00df9d45409ef4"}, - {file = "orjson-3.11.7-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:814be4b49b228cfc0b3c565acf642dd7d13538f966e3ccde61f4f55be3e20785"}, - {file = "orjson-3.11.7-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d06e5c5fed5caedd2e540d62e5b1c25e8c82431b9e577c33537e5fa4aa909539"}, - {file = "orjson-3.11.7-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:31c80ce534ac4ea3739c5ee751270646cbc46e45aea7576a38ffec040b4029a1"}, - {file = "orjson-3.11.7-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f50979824bde13d32b4320eedd513431c921102796d86be3eee0b58e58a3ecd1"}, - {file = "orjson-3.11.7-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9e54f3808e2b6b945078c41aa8d9b5834b28c50843846e97807e5adb75fa9705"}, - {file = "orjson-3.11.7-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a12b80df61aab7b98b490fe9e4879925ba666fccdfcd175252ce4d9035865ace"}, - {file = "orjson-3.11.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:996b65230271f1a97026fd0e6a753f51fbc0c335d2ad0c6201f711b0da32693b"}, - {file = "orjson-3.11.7-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:ab49d4b2a6a1d415ddb9f37a21e02e0d5dbfe10b7870b21bf779fc21e9156157"}, - {file = "orjson-3.11.7-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:390a1dce0c055ddf8adb6aa94a73b45a4a7d7177b5c584b8d1c1947f2ba60fb3"}, - {file = "orjson-3.11.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1eb80451a9c351a71dfaf5b7ccc13ad065405217726b59fdbeadbcc544f9d223"}, - {file = "orjson-3.11.7-cp313-cp313-win32.whl", hash = "sha256:7477aa6a6ec6139c5cb1cc7b214643592169a5494d200397c7fc95d740d5fcf3"}, - {file = "orjson-3.11.7-cp313-cp313-win_amd64.whl", hash = "sha256:b9f95dcdea9d4f805daa9ddf02617a89e484c6985fa03055459f90e87d7a0757"}, - {file = "orjson-3.11.7-cp313-cp313-win_arm64.whl", hash = "sha256:800988273a014a0541483dc81021247d7eacb0c845a9d1a34a422bc718f41539"}, - {file = "orjson-3.11.7-cp314-cp314-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:de0a37f21d0d364954ad5de1970491d7fbd0fb1ef7417d4d56a36dc01ba0c0a0"}, - {file = "orjson-3.11.7-cp314-cp314-macosx_15_0_arm64.whl", hash = "sha256:c2428d358d85e8da9d37cba18b8c4047c55222007a84f97156a5b22028dfbfc0"}, - {file = "orjson-3.11.7-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c4bc6c6ac52cdaa267552544c73e486fecbd710b7ac09bc024d5a78555a22f6"}, - {file = "orjson-3.11.7-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bd0d68edd7dfca1b2eca9361a44ac9f24b078de3481003159929a0573f21a6bf"}, - {file = "orjson-3.11.7-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:623ad1b9548ef63886319c16fa317848e465a21513b31a6ad7b57443c3e0dcf5"}, - {file = "orjson-3.11.7-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6e776b998ac37c0396093d10290e60283f59cfe0fc3fccbd0ccc4bd04dd19892"}, - {file = "orjson-3.11.7-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:652c6c3af76716f4a9c290371ba2e390ede06f6603edb277b481daf37f6f464e"}, - {file = "orjson-3.11.7-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a56df3239294ea5964adf074c54bcc4f0ccd21636049a2cf3ca9cf03b5d03cf1"}, - {file = "orjson-3.11.7-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:bda117c4148e81f746655d5a3239ae9bd00cb7bc3ca178b5fc5a5997e9744183"}, - {file = "orjson-3.11.7-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:23d6c20517a97a9daf1d48b580fcdc6f0516c6f4b5038823426033690b4d2650"}, - {file = "orjson-3.11.7-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:8ff206156006da5b847c9304b6308a01e8cdbc8cce824e2779a5ba71c3def141"}, - {file = "orjson-3.11.7-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:962d046ee1765f74a1da723f4b33e3b228fe3a48bd307acce5021dfefe0e29b2"}, - {file = "orjson-3.11.7-cp314-cp314-win32.whl", hash = "sha256:89e13dd3f89f1c38a9c9eba5fbf7cdc2d1feca82f5f290864b4b7a6aac704576"}, - {file = "orjson-3.11.7-cp314-cp314-win_amd64.whl", hash = "sha256:845c3e0d8ded9c9271cd79596b9b552448b885b97110f628fb687aee2eed11c1"}, - {file = "orjson-3.11.7-cp314-cp314-win_arm64.whl", hash = "sha256:4a2e9c5be347b937a2e0203866f12bba36082e89b402ddb9e927d5822e43088d"}, - {file = "orjson-3.11.7.tar.gz", hash = "sha256:9b1a67243945819ce55d24a30b59d6a168e86220452d2c96f4d1f093e71c0c49"}, -] - [[package]] name = "packaging" version = "25.0" @@ -5142,6 +4583,7 @@ files = [ {file = "pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b"}, {file = "pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887"}, ] +markers = {main = "extra == \"presidio\""} [package.extras] windows-terminal = ["colorama (>=0.4.6)"] @@ -5185,21 +4627,6 @@ files = [ [package.dependencies] six = ">=1.5" -[[package]] -name = "python-dotenv" -version = "1.2.1" -description = "Read key-value pairs from a .env file and set them as environment variables" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "python_dotenv-1.2.1-py3-none-any.whl", hash = "sha256:b81ee9561e9ca4004139c6cbba3a238c32b03e4894671e181b671e8cb8425d61"}, - {file = "python_dotenv-1.2.1.tar.gz", hash = "sha256:42667e897e16ab0d66954af0e60a9caa94f0fd4ecf3aaf6d2d260eec1aa36ad6"}, -] - -[package.extras] -cli = ["click (>=5.0)"] - [[package]] name = "pyyaml" version = "6.0.2" @@ -5278,30 +4705,14 @@ files = [ [package.dependencies] prompt_toolkit = ">=2.0,<4.0" -[[package]] -name = "referencing" -version = "0.37.0" -description = "JSON Referencing + Python" -optional = false -python-versions = ">=3.10" -groups = ["main"] -files = [ - {file = "referencing-0.37.0-py3-none-any.whl", hash = "sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231"}, - {file = "referencing-0.37.0.tar.gz", hash = "sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8"}, -] - -[package.dependencies] -attrs = ">=22.2.0" -rpds-py = ">=0.7.0" -typing-extensions = {version = ">=4.4.0", markers = "python_version < \"3.13\""} - [[package]] name = "regex" version = "2024.11.6" description = "Alternative regular expression module, to replace re." -optional = false +optional = true python-versions = ">=3.8" groups = ["main"] +markers = "extra == \"presidio\"" files = [ {file = "regex-2024.11.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ff590880083d60acc0433f9c3f713c51f7ac6ebb9adf889c79a261ecf541aa91"}, {file = "regex-2024.11.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:658f90550f38270639e83ce492f27d2c8d2cd63805c65a13a14d36ca126753f0"}, @@ -5441,9 +4852,10 @@ requests = ">=1.0.0" name = "rich" version = "14.0.0" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" -optional = false +optional = true python-versions = ">=3.8.0" groups = ["main"] +markers = "extra == \"presidio\"" files = [ {file = "rich-14.0.0-py3-none-any.whl", hash = "sha256:1c9491e1951aac09caffd42f448ee3d04e58923ffe14993f6e83068dc395d7e0"}, {file = "rich-14.0.0.tar.gz", hash = "sha256:82f1bc23a6a21ebca4ae0c45af9bdbc492ed20231dcb63f297d6d1021a9d5725"}, @@ -5457,131 +4869,6 @@ typing-extensions = {version = ">=4.0.0,<5.0", markers = "python_version < \"3.1 [package.extras] jupyter = ["ipywidgets (>=7.5.1,<9)"] -[[package]] -name = "rpds-py" -version = "0.30.0" -description = "Python bindings to Rust's persistent data structures (rpds)" -optional = false -python-versions = ">=3.10" -groups = ["main"] -files = [ - {file = "rpds_py-0.30.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:679ae98e00c0e8d68a7fda324e16b90fd5260945b45d3b824c892cec9eea3288"}, - {file = "rpds_py-0.30.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4cc2206b76b4f576934f0ed374b10d7ca5f457858b157ca52064bdfc26b9fc00"}, - {file = "rpds_py-0.30.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:389a2d49eded1896c3d48b0136ead37c48e221b391c052fba3f4055c367f60a6"}, - {file = "rpds_py-0.30.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:32c8528634e1bf7121f3de08fa85b138f4e0dc47657866630611b03967f041d7"}, - {file = "rpds_py-0.30.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f207f69853edd6f6700b86efb84999651baf3789e78a466431df1331608e5324"}, - {file = "rpds_py-0.30.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:67b02ec25ba7a9e8fa74c63b6ca44cf5707f2fbfadae3ee8e7494297d56aa9df"}, - {file = "rpds_py-0.30.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c0e95f6819a19965ff420f65578bacb0b00f251fefe2c8b23347c37174271f3"}, - {file = "rpds_py-0.30.0-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:a452763cc5198f2f98898eb98f7569649fe5da666c2dc6b5ddb10fde5a574221"}, - {file = "rpds_py-0.30.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e0b65193a413ccc930671c55153a03ee57cecb49e6227204b04fae512eb657a7"}, - {file = "rpds_py-0.30.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:858738e9c32147f78b3ac24dc0edb6610000e56dc0f700fd5f651d0a0f0eb9ff"}, - {file = "rpds_py-0.30.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:da279aa314f00acbb803da1e76fa18666778e8a8f83484fba94526da5de2cba7"}, - {file = "rpds_py-0.30.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7c64d38fb49b6cdeda16ab49e35fe0da2e1e9b34bc38bd78386530f218b37139"}, - {file = "rpds_py-0.30.0-cp310-cp310-win32.whl", hash = "sha256:6de2a32a1665b93233cde140ff8b3467bdb9e2af2b91079f0333a0974d12d464"}, - {file = "rpds_py-0.30.0-cp310-cp310-win_amd64.whl", hash = "sha256:1726859cd0de969f88dc8673bdd954185b9104e05806be64bcd87badbe313169"}, - {file = "rpds_py-0.30.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:a2bffea6a4ca9f01b3f8e548302470306689684e61602aa3d141e34da06cf425"}, - {file = "rpds_py-0.30.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dc4f992dfe1e2bc3ebc7444f6c7051b4bc13cd8e33e43511e8ffd13bf407010d"}, - {file = "rpds_py-0.30.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:422c3cb9856d80b09d30d2eb255d0754b23e090034e1deb4083f8004bd0761e4"}, - {file = "rpds_py-0.30.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:07ae8a593e1c3c6b82ca3292efbe73c30b61332fd612e05abee07c79359f292f"}, - {file = "rpds_py-0.30.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:12f90dd7557b6bd57f40abe7747e81e0c0b119bef015ea7726e69fe550e394a4"}, - {file = "rpds_py-0.30.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:99b47d6ad9a6da00bec6aabe5a6279ecd3c06a329d4aa4771034a21e335c3a97"}, - {file = "rpds_py-0.30.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:33f559f3104504506a44bb666b93a33f5d33133765b0c216a5bf2f1e1503af89"}, - {file = "rpds_py-0.30.0-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:946fe926af6e44f3697abbc305ea168c2c31d3e3ef1058cf68f379bf0335a78d"}, - {file = "rpds_py-0.30.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:495aeca4b93d465efde585977365187149e75383ad2684f81519f504f5c13038"}, - {file = "rpds_py-0.30.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d9a0ca5da0386dee0655b4ccdf46119df60e0f10da268d04fe7cc87886872ba7"}, - {file = "rpds_py-0.30.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8d6d1cc13664ec13c1b84241204ff3b12f9bb82464b8ad6e7a5d3486975c2eed"}, - {file = "rpds_py-0.30.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3896fa1be39912cf0757753826bc8bdc8ca331a28a7c4ae46b7a21280b06bb85"}, - {file = "rpds_py-0.30.0-cp311-cp311-win32.whl", hash = "sha256:55f66022632205940f1827effeff17c4fa7ae1953d2b74a8581baaefb7d16f8c"}, - {file = "rpds_py-0.30.0-cp311-cp311-win_amd64.whl", hash = "sha256:a51033ff701fca756439d641c0ad09a41d9242fa69121c7d8769604a0a629825"}, - {file = "rpds_py-0.30.0-cp311-cp311-win_arm64.whl", hash = "sha256:47b0ef6231c58f506ef0b74d44e330405caa8428e770fec25329ed2cb971a229"}, - {file = "rpds_py-0.30.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a161f20d9a43006833cd7068375a94d035714d73a172b681d8881820600abfad"}, - {file = "rpds_py-0.30.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6abc8880d9d036ecaafe709079969f56e876fcf107f7a8e9920ba6d5a3878d05"}, - {file = "rpds_py-0.30.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca28829ae5f5d569bb62a79512c842a03a12576375d5ece7d2cadf8abe96ec28"}, - {file = "rpds_py-0.30.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a1010ed9524c73b94d15919ca4d41d8780980e1765babf85f9a2f90d247153dd"}, - {file = "rpds_py-0.30.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f8d1736cfb49381ba528cd5baa46f82fdc65c06e843dab24dd70b63d09121b3f"}, - {file = "rpds_py-0.30.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d948b135c4693daff7bc2dcfc4ec57237a29bd37e60c2fabf5aff2bbacf3e2f1"}, - {file = "rpds_py-0.30.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47f236970bccb2233267d89173d3ad2703cd36a0e2a6e92d0560d333871a3d23"}, - {file = "rpds_py-0.30.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:2e6ecb5a5bcacf59c3f912155044479af1d0b6681280048b338b28e364aca1f6"}, - {file = "rpds_py-0.30.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a8fa71a2e078c527c3e9dc9fc5a98c9db40bcc8a92b4e8858e36d329f8684b51"}, - {file = "rpds_py-0.30.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:73c67f2db7bc334e518d097c6d1e6fed021bbc9b7d678d6cc433478365d1d5f5"}, - {file = "rpds_py-0.30.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5ba103fb455be00f3b1c2076c9d4264bfcb037c976167a6047ed82f23153f02e"}, - {file = "rpds_py-0.30.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7cee9c752c0364588353e627da8a7e808a66873672bcb5f52890c33fd965b394"}, - {file = "rpds_py-0.30.0-cp312-cp312-win32.whl", hash = "sha256:1ab5b83dbcf55acc8b08fc62b796ef672c457b17dbd7820a11d6c52c06839bdf"}, - {file = "rpds_py-0.30.0-cp312-cp312-win_amd64.whl", hash = "sha256:a090322ca841abd453d43456ac34db46e8b05fd9b3b4ac0c78bcde8b089f959b"}, - {file = "rpds_py-0.30.0-cp312-cp312-win_arm64.whl", hash = "sha256:669b1805bd639dd2989b281be2cfd951c6121b65e729d9b843e9639ef1fd555e"}, - {file = "rpds_py-0.30.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:f83424d738204d9770830d35290ff3273fbb02b41f919870479fab14b9d303b2"}, - {file = "rpds_py-0.30.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e7536cd91353c5273434b4e003cbda89034d67e7710eab8761fd918ec6c69cf8"}, - {file = "rpds_py-0.30.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2771c6c15973347f50fece41fc447c054b7ac2ae0502388ce3b6738cd366e3d4"}, - {file = "rpds_py-0.30.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0a59119fc6e3f460315fe9d08149f8102aa322299deaa5cab5b40092345c2136"}, - {file = "rpds_py-0.30.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:76fec018282b4ead0364022e3c54b60bf368b9d926877957a8624b58419169b7"}, - {file = "rpds_py-0.30.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:692bef75a5525db97318e8cd061542b5a79812d711ea03dbc1f6f8dbb0c5f0d2"}, - {file = "rpds_py-0.30.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9027da1ce107104c50c81383cae773ef5c24d296dd11c99e2629dbd7967a20c6"}, - {file = "rpds_py-0.30.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:9cf69cdda1f5968a30a359aba2f7f9aa648a9ce4b580d6826437f2b291cfc86e"}, - {file = "rpds_py-0.30.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a4796a717bf12b9da9d3ad002519a86063dcac8988b030e405704ef7d74d2d9d"}, - {file = "rpds_py-0.30.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5d4c2aa7c50ad4728a094ebd5eb46c452e9cb7edbfdb18f9e1221f597a73e1e7"}, - {file = "rpds_py-0.30.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ba81a9203d07805435eb06f536d95a266c21e5b2dfbf6517748ca40c98d19e31"}, - {file = "rpds_py-0.30.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:945dccface01af02675628334f7cf49c2af4c1c904748efc5cf7bbdf0b579f95"}, - {file = "rpds_py-0.30.0-cp313-cp313-win32.whl", hash = "sha256:b40fb160a2db369a194cb27943582b38f79fc4887291417685f3ad693c5a1d5d"}, - {file = "rpds_py-0.30.0-cp313-cp313-win_amd64.whl", hash = "sha256:806f36b1b605e2d6a72716f321f20036b9489d29c51c91f4dd29a3e3afb73b15"}, - {file = "rpds_py-0.30.0-cp313-cp313-win_arm64.whl", hash = "sha256:d96c2086587c7c30d44f31f42eae4eac89b60dabbac18c7669be3700f13c3ce1"}, - {file = "rpds_py-0.30.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:eb0b93f2e5c2189ee831ee43f156ed34e2a89a78a66b98cadad955972548be5a"}, - {file = "rpds_py-0.30.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:922e10f31f303c7c920da8981051ff6d8c1a56207dbdf330d9047f6d30b70e5e"}, - {file = "rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdc62c8286ba9bf7f47befdcea13ea0e26bf294bda99758fd90535cbaf408000"}, - {file = "rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:47f9a91efc418b54fb8190a6b4aa7813a23fb79c51f4bb84e418f5476c38b8db"}, - {file = "rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1f3587eb9b17f3789ad50824084fa6f81921bbf9a795826570bda82cb3ed91f2"}, - {file = "rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:39c02563fc592411c2c61d26b6c5fe1e51eaa44a75aa2c8735ca88b0d9599daa"}, - {file = "rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51a1234d8febafdfd33a42d97da7a43f5dcb120c1060e352a3fbc0c6d36e2083"}, - {file = "rpds_py-0.30.0-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:eb2c4071ab598733724c08221091e8d80e89064cd472819285a9ab0f24bcedb9"}, - {file = "rpds_py-0.30.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6bdfdb946967d816e6adf9a3d8201bfad269c67efe6cefd7093ef959683c8de0"}, - {file = "rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c77afbd5f5250bf27bf516c7c4a016813eb2d3e116139aed0096940c5982da94"}, - {file = "rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:61046904275472a76c8c90c9ccee9013d70a6d0f73eecefd38c1ae7c39045a08"}, - {file = "rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4c5f36a861bc4b7da6516dbdf302c55313afa09b81931e8280361a4f6c9a2d27"}, - {file = "rpds_py-0.30.0-cp313-cp313t-win32.whl", hash = "sha256:3d4a69de7a3e50ffc214ae16d79d8fbb0922972da0356dcf4d0fdca2878559c6"}, - {file = "rpds_py-0.30.0-cp313-cp313t-win_amd64.whl", hash = "sha256:f14fc5df50a716f7ece6a80b6c78bb35ea2ca47c499e422aa4463455dd96d56d"}, - {file = "rpds_py-0.30.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:68f19c879420aa08f61203801423f6cd5ac5f0ac4ac82a2368a9fcd6a9a075e0"}, - {file = "rpds_py-0.30.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ec7c4490c672c1a0389d319b3a9cfcd098dcdc4783991553c332a15acf7249be"}, - {file = "rpds_py-0.30.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f251c812357a3fed308d684a5079ddfb9d933860fc6de89f2b7ab00da481e65f"}, - {file = "rpds_py-0.30.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac98b175585ecf4c0348fd7b29c3864bda53b805c773cbf7bfdaffc8070c976f"}, - {file = "rpds_py-0.30.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3e62880792319dbeb7eb866547f2e35973289e7d5696c6e295476448f5b63c87"}, - {file = "rpds_py-0.30.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4e7fc54e0900ab35d041b0601431b0a0eb495f0851a0639b6ef90f7741b39a18"}, - {file = "rpds_py-0.30.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47e77dc9822d3ad616c3d5759ea5631a75e5809d5a28707744ef79d7a1bcfcad"}, - {file = "rpds_py-0.30.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:b4dc1a6ff022ff85ecafef7979a2c6eb423430e05f1165d6688234e62ba99a07"}, - {file = "rpds_py-0.30.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4559c972db3a360808309e06a74628b95eaccbf961c335c8fe0d590cf587456f"}, - {file = "rpds_py-0.30.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:0ed177ed9bded28f8deb6ab40c183cd1192aa0de40c12f38be4d59cd33cb5c65"}, - {file = "rpds_py-0.30.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ad1fa8db769b76ea911cb4e10f049d80bf518c104f15b3edb2371cc65375c46f"}, - {file = "rpds_py-0.30.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:46e83c697b1f1c72b50e5ee5adb4353eef7406fb3f2043d64c33f20ad1c2fc53"}, - {file = "rpds_py-0.30.0-cp314-cp314-win32.whl", hash = "sha256:ee454b2a007d57363c2dfd5b6ca4a5d7e2c518938f8ed3b706e37e5d470801ed"}, - {file = "rpds_py-0.30.0-cp314-cp314-win_amd64.whl", hash = "sha256:95f0802447ac2d10bcc69f6dc28fe95fdf17940367b21d34e34c737870758950"}, - {file = "rpds_py-0.30.0-cp314-cp314-win_arm64.whl", hash = "sha256:613aa4771c99f03346e54c3f038e4cc574ac09a3ddfb0e8878487335e96dead6"}, - {file = "rpds_py-0.30.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:7e6ecfcb62edfd632e56983964e6884851786443739dbfe3582947e87274f7cb"}, - {file = "rpds_py-0.30.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a1d0bc22a7cdc173fedebb73ef81e07faef93692b8c1ad3733b67e31e1b6e1b8"}, - {file = "rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d08f00679177226c4cb8c5265012eea897c8ca3b93f429e546600c971bcbae7"}, - {file = "rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5965af57d5848192c13534f90f9dd16464f3c37aaf166cc1da1cae1fd5a34898"}, - {file = "rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9a4e86e34e9ab6b667c27f3211ca48f73dba7cd3d90f8d5b11be56e5dbc3fb4e"}, - {file = "rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5d3e6b26f2c785d65cc25ef1e5267ccbe1b069c5c21b8cc724efee290554419"}, - {file = "rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:626a7433c34566535b6e56a1b39a7b17ba961e97ce3b80ec62e6f1312c025551"}, - {file = "rpds_py-0.30.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:acd7eb3f4471577b9b5a41baf02a978e8bdeb08b4b355273994f8b87032000a8"}, - {file = "rpds_py-0.30.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fe5fa731a1fa8a0a56b0977413f8cacac1768dad38d16b3a296712709476fbd5"}, - {file = "rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:74a3243a411126362712ee1524dfc90c650a503502f135d54d1b352bd01f2404"}, - {file = "rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:3e8eeb0544f2eb0d2581774be4c3410356eba189529a6b3e36bbbf9696175856"}, - {file = "rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:dbd936cde57abfee19ab3213cf9c26be06d60750e60a8e4dd85d1ab12c8b1f40"}, - {file = "rpds_py-0.30.0-cp314-cp314t-win32.whl", hash = "sha256:dc824125c72246d924f7f796b4f63c1e9dc810c7d9e2355864b3c3a73d59ade0"}, - {file = "rpds_py-0.30.0-cp314-cp314t-win_amd64.whl", hash = "sha256:27f4b0e92de5bfbc6f86e43959e6edd1425c33b5e69aab0984a72047f2bcf1e3"}, - {file = "rpds_py-0.30.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c2262bdba0ad4fc6fb5545660673925c2d2a5d9e2e0fb603aad545427be0fc58"}, - {file = "rpds_py-0.30.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:ee6af14263f25eedc3bb918a3c04245106a42dfd4f5c2285ea6f997b1fc3f89a"}, - {file = "rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3adbb8179ce342d235c31ab8ec511e66c73faa27a47e076ccc92421add53e2bb"}, - {file = "rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:250fa00e9543ac9b97ac258bd37367ff5256666122c2d0f2bc97577c60a1818c"}, - {file = "rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9854cf4f488b3d57b9aaeb105f06d78e5529d3145b1e4a41750167e8c213c6d3"}, - {file = "rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:993914b8e560023bc0a8bf742c5f303551992dcb85e247b1e5c7f4a7d145bda5"}, - {file = "rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58edca431fb9b29950807e301826586e5bbf24163677732429770a697ffe6738"}, - {file = "rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:dea5b552272a944763b34394d04577cf0f9bd013207bc32323b5a89a53cf9c2f"}, - {file = "rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ba3af48635eb83d03f6c9735dfb21785303e73d22ad03d489e88adae6eab8877"}, - {file = "rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:dff13836529b921e22f15cb099751209a60009731a68519630a24d61f0b1b30a"}, - {file = "rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:1b151685b23929ab7beec71080a8889d4d6d9fa9a983d213f07121205d48e2c4"}, - {file = "rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:ac37f9f516c51e5753f27dfdef11a88330f04de2d564be3991384b2f3535d02e"}, - {file = "rpds_py-0.30.0.tar.gz", hash = "sha256:dd8ff7cf90014af0c0f787eea34794ebf6415242ee1d6fa91eaba725cc441e84"}, -] - [[package]] name = "safetensors" version = "0.5.3" @@ -5828,108 +5115,6 @@ files = [ {file = "spacy_loggers-1.0.5-py3-none-any.whl", hash = "sha256:196284c9c446cc0cdb944005384270d775fdeaf4f494d8e269466cfa497ef645"}, ] -[[package]] -name = "sqlalchemy" -version = "2.0.47" -description = "Database Abstraction Library" -optional = false -python-versions = ">=3.7" -groups = ["main"] -files = [ - {file = "sqlalchemy-2.0.47-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:33a917ede39406ddb93c3e642b5bc480be7c5fd0f3d0d6ae1036d466fb963f1a"}, - {file = "sqlalchemy-2.0.47-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:561d027c829b01e040bdade6b6f5b429249d056ef95d7bdcb9211539ecc82803"}, - {file = "sqlalchemy-2.0.47-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fa5072a37e68c565363c009b7afa5b199b488c87940ec02719860093a08f34ca"}, - {file = "sqlalchemy-2.0.47-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:1e7ed17dd4312a298b6024bfd1baf51654bc49e3f03c798005babf0c7922d6a7"}, - {file = "sqlalchemy-2.0.47-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:6992e353fcb0593eb42d95ad84b3e58fe40b5e37fd332b9ccba28f4b2f36d1fc"}, - {file = "sqlalchemy-2.0.47-cp310-cp310-win32.whl", hash = "sha256:05a6d58ed99ebd01303c92d29a0c9cbf70f637b3ddd155f5172c5a7239940998"}, - {file = "sqlalchemy-2.0.47-cp310-cp310-win_amd64.whl", hash = "sha256:4a7aa4a584cc97e268c11e700dea0b763874eaebb435e75e7d0ffee5d90f5030"}, - {file = "sqlalchemy-2.0.47-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3a1dbf0913879c443617d6b64403cf2801c941651db8c60e96d204ed9388d6b0"}, - {file = "sqlalchemy-2.0.47-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:775effbb97ea3b00c4dd3aeaf3ba8acba6e3e2b4b41d17d67a27e696843dbc95"}, - {file = "sqlalchemy-2.0.47-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:56cc834a3ffac34270cc2a41875e0f40e97aa651f4f3ca1cfbbf421c044cb62b"}, - {file = "sqlalchemy-2.0.47-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:49b5e0c7244262f39e767c018e4fdb5e5dbc23cd54c5ddac8eea8f0ba32ef890"}, - {file = "sqlalchemy-2.0.47-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:15cd822a3f1f6f77b5b841a30c1a07a07f7dee3385f17e638e1722de9ab683be"}, - {file = "sqlalchemy-2.0.47-cp311-cp311-win32.whl", hash = "sha256:9847a19548cd283a65e1ce0afd54016598d55ff72682d6fd3e493af6fc044064"}, - {file = "sqlalchemy-2.0.47-cp311-cp311-win_amd64.whl", hash = "sha256:722abf1c82aeca46a1a0803711244a48a298279eeaec9e02f7bfee9e064182e5"}, - {file = "sqlalchemy-2.0.47-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4fa91b19d6b9821c04cc8f7aa2476429cc8887b9687c762815aa629f5c0edec1"}, - {file = "sqlalchemy-2.0.47-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7c5bbbd14eff577c8c79cbfe39a0771eecd20f430f3678533476f0087138f356"}, - {file = "sqlalchemy-2.0.47-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a5a6c555da8d4280a3c4c78c5b7a3f990cee2b2884e5f934f87a226191682ff7"}, - {file = "sqlalchemy-2.0.47-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ed48a1701d24dff3bb49a5bce94d6bc84cbe33d98af2aa2d3cdcce3dea1709ec"}, - {file = "sqlalchemy-2.0.47-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4f3178c920ad98158f0b6309382194df04b14808fa6052ae07099fdde29d5602"}, - {file = "sqlalchemy-2.0.47-cp312-cp312-win32.whl", hash = "sha256:b9c11ac9934dd59ece9619fe42780a08abe2faab7b0543bb00d5eabea4f421b9"}, - {file = "sqlalchemy-2.0.47-cp312-cp312-win_amd64.whl", hash = "sha256:db43b72cf8274a99e089755c9c1e0b947159b71adbc2c83c3de2e38d5d607acb"}, - {file = "sqlalchemy-2.0.47-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:456a135b790da5d3c6b53d0ef71ac7b7d280b7f41eb0c438986352bf03ca7143"}, - {file = "sqlalchemy-2.0.47-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:09a2f7698e44b3135433387da5d8846cf7cc7c10e5425af7c05fee609df978b6"}, - {file = "sqlalchemy-2.0.47-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a0bbc72e6a177c78d724f9106aaddc0d26a2ada89c6332b5935414eccf04cbd5"}, - {file = "sqlalchemy-2.0.47-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:75460456b043b78b6006e41bdf5b86747ee42eafaf7fffa3b24a6e9a456a2092"}, - {file = "sqlalchemy-2.0.47-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5d9adaa616c3bc7d80f9ded57cd84b51d6617cad6a5456621d858c9f23aaee01"}, - {file = "sqlalchemy-2.0.47-cp313-cp313-win32.whl", hash = "sha256:76e09f974382a496a5ed985db9343628b1cb1ac911f27342e4cc46a8bac10476"}, - {file = "sqlalchemy-2.0.47-cp313-cp313-win_amd64.whl", hash = "sha256:0664089b0bf6724a0bfb49a0cf4d4da24868a0a5c8e937cd7db356d5dcdf2c66"}, - {file = "sqlalchemy-2.0.47-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ed0c967c701ae13da98eb220f9ddab3044ab63504c1ba24ad6a59b26826ad003"}, - {file = "sqlalchemy-2.0.47-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d3537943a61fd25b241e976426a0c6814434b93cf9b09d39e8e78f3c9eb9a487"}, - {file = "sqlalchemy-2.0.47-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:57f7e336a64a0dba686c66392d46b9bc7af2c57d55ce6dc1697b4ef32b043ceb"}, - {file = "sqlalchemy-2.0.47-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dff735a621858680217cb5142b779bad40ef7322ddbb7c12062190db6879772e"}, - {file = "sqlalchemy-2.0.47-cp313-cp313t-win32.whl", hash = "sha256:3893dc096bb3cca9608ea3487372ffcea3ae9b162f40e4d3c51dd49db1d1b2dc"}, - {file = "sqlalchemy-2.0.47-cp313-cp313t-win_amd64.whl", hash = "sha256:b5103427466f4b3e61f04833ae01f9a914b1280a2a8bcde3a9d7ab11f3755b42"}, - {file = "sqlalchemy-2.0.47-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:b03010a5a5dfe71676bc83f2473ebe082478e32d77e6f082c8fe15a31c3b42a6"}, - {file = "sqlalchemy-2.0.47-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f8e3371aa9024520883a415a09cc20c33cfd3eeccf9e0f4f4c367f940b9cbd44"}, - {file = "sqlalchemy-2.0.47-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c9449f747e50d518c6e1b40cc379e48bfc796453c47b15e627ea901c201e48a6"}, - {file = "sqlalchemy-2.0.47-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:21410f60d5cac1d6bfe360e05bd91b179be4fa0aa6eea6be46054971d277608f"}, - {file = "sqlalchemy-2.0.47-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:819841dd5bb4324c284c09e2874cf96fe6338bfb57a64548d9b81a4e39c9871f"}, - {file = "sqlalchemy-2.0.47-cp314-cp314-win32.whl", hash = "sha256:e255ee44821a7ef45649c43064cf94e74f81f61b4df70547304b97a351e9b7db"}, - {file = "sqlalchemy-2.0.47-cp314-cp314-win_amd64.whl", hash = "sha256:209467ff73ea1518fe1a5aaed9ba75bb9e33b2666e2553af9ccd13387bf192cb"}, - {file = "sqlalchemy-2.0.47-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e78fd9186946afaa287f8a1fe147ead06e5d566b08c0afcb601226e9c7322a64"}, - {file = "sqlalchemy-2.0.47-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5740e2f31b5987ed9619d6912ae5b750c03637f2078850da3002934c9532f172"}, - {file = "sqlalchemy-2.0.47-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:fb9ac00d03de93acb210e8ec7243fefe3e012515bf5fd2f0898c8dff38bc77a4"}, - {file = "sqlalchemy-2.0.47-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:c72a0b9eb2672d70d112cb149fbaf172d466bc691014c496aaac594f1988e706"}, - {file = "sqlalchemy-2.0.47-cp314-cp314t-win32.whl", hash = "sha256:c200db1128d72a71dc3c31c24b42eb9fd85b2b3e5a3c9ba1e751c11ac31250ff"}, - {file = "sqlalchemy-2.0.47-cp314-cp314t-win_amd64.whl", hash = "sha256:669837759b84e575407355dcff912835892058aea9b80bd1cb76d6a151cf37f7"}, - {file = "sqlalchemy-2.0.47-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:fe3f8519a52ca5032015780de3fc4e6ab42c6e0bcf9d807143a3d17b3350d546"}, - {file = "sqlalchemy-2.0.47-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d6acfc1f95ed0369e0c4100d98870c9c4bfd56818ddc825357a0a979d5973195"}, - {file = "sqlalchemy-2.0.47-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f530b939eca775f6f77fa359a3e7039209a96958c1aa28c1b796f600e0fee7cd"}, - {file = "sqlalchemy-2.0.47-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:0a6fdf665268dfe0ba52fb2d8d62deee96b297d460e2797bdd52d2d1941dd8cd"}, - {file = "sqlalchemy-2.0.47-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:25b3c189dab94dedb6db9d4e06476ce955182e7f45412b096ae9033519e33ce8"}, - {file = "sqlalchemy-2.0.47-cp38-cp38-win32.whl", hash = "sha256:a8f991cac31b673aff1648cafb8b10022719e5a632bbadaa9c5d41511bd507a5"}, - {file = "sqlalchemy-2.0.47-cp38-cp38-win_amd64.whl", hash = "sha256:52be08b31f70bed2ed05c5c4b8237cf361a8581f32a5e89f9dfc295f795db10f"}, - {file = "sqlalchemy-2.0.47-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d7477190e90852c00bf654b70ae21e5b85b5ac4d09094cf82e84eb3abdb6c5a7"}, - {file = "sqlalchemy-2.0.47-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cb0b3e4946bf05d68673a1857db1a16bd58207c83ebc4ed5732a6e60029bac2d"}, - {file = "sqlalchemy-2.0.47-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9bc111a5b98881b7e1ab108921f2fcc09fa06abbd98f4f0ed6cb2c23e70cdd23"}, - {file = "sqlalchemy-2.0.47-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:97bf49caf4e405c18f3b9f695751c5bf14a9d8899c6e54eaeb49dda0d4fa009d"}, - {file = "sqlalchemy-2.0.47-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:d2fa029051d3db77ad79a55c3ddf005bd7c038a111af0db9b56158857702aef6"}, - {file = "sqlalchemy-2.0.47-cp39-cp39-win32.whl", hash = "sha256:6e547682d508d141de942484b9976cbee91b7a50739d4ee25b3d0a62dd71a954"}, - {file = "sqlalchemy-2.0.47-cp39-cp39-win_amd64.whl", hash = "sha256:bb833131169444c87160aa95fcdd22ae86d0fa4ef174d36b3dfb9be363b4e574"}, - {file = "sqlalchemy-2.0.47-py3-none-any.whl", hash = "sha256:e2647043599297a1ef10e720cf310846b7f31b6c841fee093d2b09d81215eb93"}, - {file = "sqlalchemy-2.0.47.tar.gz", hash = "sha256:e3e7feb57b267fe897e492b9721ae46d5c7de6f9e8dee58aacf105dc4e154f3d"}, -] - -[package.dependencies] -greenlet = {version = ">=1", markers = "platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\""} -typing-extensions = ">=4.6.0" - -[package.extras] -aiomysql = ["aiomysql (>=0.2.0)", "greenlet (>=1)"] -aioodbc = ["aioodbc", "greenlet (>=1)"] -aiosqlite = ["aiosqlite", "greenlet (>=1)", "typing_extensions (!=3.10.0.1)"] -asyncio = ["greenlet (>=1)"] -asyncmy = ["asyncmy (>=0.2.3,!=0.2.4,!=0.2.6)", "greenlet (>=1)"] -mariadb-connector = ["mariadb (>=1.0.1,!=1.1.2,!=1.1.5,!=1.1.10)"] -mssql = ["pyodbc"] -mssql-pymssql = ["pymssql"] -mssql-pyodbc = ["pyodbc"] -mypy = ["mypy (>=0.910)"] -mysql = ["mysqlclient (>=1.4.0)"] -mysql-connector = ["mysql-connector-python"] -oracle = ["cx_oracle (>=8)"] -oracle-oracledb = ["oracledb (>=1.0.1)"] -postgresql = ["psycopg2 (>=2.7)"] -postgresql-asyncpg = ["asyncpg", "greenlet (>=1)"] -postgresql-pg8000 = ["pg8000 (>=1.29.1)"] -postgresql-psycopg = ["psycopg (>=3.0.7)"] -postgresql-psycopg2binary = ["psycopg2-binary"] -postgresql-psycopg2cffi = ["psycopg2cffi"] -postgresql-psycopgbinary = ["psycopg[binary] (>=3.0.7)"] -pymysql = ["pymysql"] -sqlcipher = ["sqlcipher3_binary"] - [[package]] name = "srsly" version = "2.5.1" @@ -6148,80 +5333,6 @@ mxnet = ["mxnet (>=1.5.1,<1.6.0)"] tensorflow = ["tensorflow (>=2.0.0,<2.6.0)"] torch = ["torch (>=1.6.0)"] -[[package]] -name = "tiktoken" -version = "0.12.0" -description = "tiktoken is a fast BPE tokeniser for use with OpenAI's models" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "tiktoken-0.12.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:3de02f5a491cfd179aec916eddb70331814bd6bf764075d39e21d5862e533970"}, - {file = "tiktoken-0.12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b6cfb6d9b7b54d20af21a912bfe63a2727d9cfa8fbda642fd8322c70340aad16"}, - {file = "tiktoken-0.12.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:cde24cdb1b8a08368f709124f15b36ab5524aac5fa830cc3fdce9c03d4fb8030"}, - {file = "tiktoken-0.12.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:6de0da39f605992649b9cfa6f84071e3f9ef2cec458d08c5feb1b6f0ff62e134"}, - {file = "tiktoken-0.12.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:6faa0534e0eefbcafaccb75927a4a380463a2eaa7e26000f0173b920e98b720a"}, - {file = "tiktoken-0.12.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:82991e04fc860afb933efb63957affc7ad54f83e2216fe7d319007dab1ba5892"}, - {file = "tiktoken-0.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:6fb2995b487c2e31acf0a9e17647e3b242235a20832642bb7a9d1a181c0c1bb1"}, - {file = "tiktoken-0.12.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:6e227c7f96925003487c33b1b32265fad2fbcec2b7cf4817afb76d416f40f6bb"}, - {file = "tiktoken-0.12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c06cf0fcc24c2cb2adb5e185c7082a82cba29c17575e828518c2f11a01f445aa"}, - {file = "tiktoken-0.12.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:f18f249b041851954217e9fd8e5c00b024ab2315ffda5ed77665a05fa91f42dc"}, - {file = "tiktoken-0.12.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:47a5bc270b8c3db00bb46ece01ef34ad050e364b51d406b6f9730b64ac28eded"}, - {file = "tiktoken-0.12.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:508fa71810c0efdcd1b898fda574889ee62852989f7c1667414736bcb2b9a4bd"}, - {file = "tiktoken-0.12.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a1af81a6c44f008cba48494089dd98cccb8b313f55e961a52f5b222d1e507967"}, - {file = "tiktoken-0.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:3e68e3e593637b53e56f7237be560f7a394451cb8c11079755e80ae64b9e6def"}, - {file = "tiktoken-0.12.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b97f74aca0d78a1ff21b8cd9e9925714c15a9236d6ceacf5c7327c117e6e21e8"}, - {file = "tiktoken-0.12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2b90f5ad190a4bb7c3eb30c5fa32e1e182ca1ca79f05e49b448438c3e225a49b"}, - {file = "tiktoken-0.12.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:65b26c7a780e2139e73acc193e5c63ac754021f160df919add909c1492c0fb37"}, - {file = "tiktoken-0.12.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:edde1ec917dfd21c1f2f8046b86348b0f54a2c0547f68149d8600859598769ad"}, - {file = "tiktoken-0.12.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:35a2f8ddd3824608b3d650a000c1ef71f730d0c56486845705a8248da00f9fe5"}, - {file = "tiktoken-0.12.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:83d16643edb7fa2c99eff2ab7733508aae1eebb03d5dfc46f5565862810f24e3"}, - {file = "tiktoken-0.12.0-cp312-cp312-win_amd64.whl", hash = "sha256:ffc5288f34a8bc02e1ea7047b8d041104791d2ddbf42d1e5fa07822cbffe16bd"}, - {file = "tiktoken-0.12.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:775c2c55de2310cc1bc9a3ad8826761cbdc87770e586fd7b6da7d4589e13dab3"}, - {file = "tiktoken-0.12.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a01b12f69052fbe4b080a2cfb867c4de12c704b56178edf1d1d7b273561db160"}, - {file = "tiktoken-0.12.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:01d99484dc93b129cd0964f9d34eee953f2737301f18b3c7257bf368d7615baa"}, - {file = "tiktoken-0.12.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:4a1a4fcd021f022bfc81904a911d3df0f6543b9e7627b51411da75ff2fe7a1be"}, - {file = "tiktoken-0.12.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:981a81e39812d57031efdc9ec59fa32b2a5a5524d20d4776574c4b4bd2e9014a"}, - {file = "tiktoken-0.12.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9baf52f84a3f42eef3ff4e754a0db79a13a27921b457ca9832cf944c6be4f8f3"}, - {file = "tiktoken-0.12.0-cp313-cp313-win_amd64.whl", hash = "sha256:b8a0cd0c789a61f31bf44851defbd609e8dd1e2c8589c614cc1060940ef1f697"}, - {file = "tiktoken-0.12.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:d5f89ea5680066b68bcb797ae85219c72916c922ef0fcdd3480c7d2315ffff16"}, - {file = "tiktoken-0.12.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b4e7ed1c6a7a8a60a3230965bdedba8cc58f68926b835e519341413370e0399a"}, - {file = "tiktoken-0.12.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:fc530a28591a2d74bce821d10b418b26a094bf33839e69042a6e86ddb7a7fb27"}, - {file = "tiktoken-0.12.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:06a9f4f49884139013b138920a4c393aa6556b2f8f536345f11819389c703ebb"}, - {file = "tiktoken-0.12.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:04f0e6a985d95913cabc96a741c5ffec525a2c72e9df086ff17ebe35985c800e"}, - {file = "tiktoken-0.12.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:0ee8f9ae00c41770b5f9b0bb1235474768884ae157de3beb5439ca0fd70f3e25"}, - {file = "tiktoken-0.12.0-cp313-cp313t-win_amd64.whl", hash = "sha256:dc2dd125a62cb2b3d858484d6c614d136b5b848976794edfb63688d539b8b93f"}, - {file = "tiktoken-0.12.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:a90388128df3b3abeb2bfd1895b0681412a8d7dc644142519e6f0a97c2111646"}, - {file = "tiktoken-0.12.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:da900aa0ad52247d8794e307d6446bd3cdea8e192769b56276695d34d2c9aa88"}, - {file = "tiktoken-0.12.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:285ba9d73ea0d6171e7f9407039a290ca77efcdb026be7769dccc01d2c8d7fff"}, - {file = "tiktoken-0.12.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:d186a5c60c6a0213f04a7a802264083dea1bbde92a2d4c7069e1a56630aef830"}, - {file = "tiktoken-0.12.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:604831189bd05480f2b885ecd2d1986dc7686f609de48208ebbbddeea071fc0b"}, - {file = "tiktoken-0.12.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:8f317e8530bb3a222547b85a58583238c8f74fd7a7408305f9f63246d1a0958b"}, - {file = "tiktoken-0.12.0-cp314-cp314-win_amd64.whl", hash = "sha256:399c3dd672a6406719d84442299a490420b458c44d3ae65516302a99675888f3"}, - {file = "tiktoken-0.12.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:c2c714c72bc00a38ca969dae79e8266ddec999c7ceccd603cc4f0d04ccd76365"}, - {file = "tiktoken-0.12.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:cbb9a3ba275165a2cb0f9a83f5d7025afe6b9d0ab01a22b50f0e74fee2ad253e"}, - {file = "tiktoken-0.12.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:dfdfaa5ffff8993a3af94d1125870b1d27aed7cb97aa7eb8c1cefdbc87dbee63"}, - {file = "tiktoken-0.12.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:584c3ad3d0c74f5269906eb8a659c8bfc6144a52895d9261cdaf90a0ae5f4de0"}, - {file = "tiktoken-0.12.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:54c891b416a0e36b8e2045b12b33dd66fb34a4fe7965565f1b482da50da3e86a"}, - {file = "tiktoken-0.12.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5edb8743b88d5be814b1a8a8854494719080c28faaa1ccbef02e87354fe71ef0"}, - {file = "tiktoken-0.12.0-cp314-cp314t-win_amd64.whl", hash = "sha256:f61c0aea5565ac82e2ec50a05e02a6c44734e91b51c10510b084ea1b8e633a71"}, - {file = "tiktoken-0.12.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:d51d75a5bffbf26f86554d28e78bfb921eae998edc2675650fd04c7e1f0cdc1e"}, - {file = "tiktoken-0.12.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:09eb4eae62ae7e4c62364d9ec3a57c62eea707ac9a2b2c5d6bd05de6724ea179"}, - {file = "tiktoken-0.12.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:df37684ace87d10895acb44b7f447d4700349b12197a526da0d4a4149fde074c"}, - {file = "tiktoken-0.12.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:4c9614597ac94bb294544345ad8cf30dac2129c05e2db8dc53e082f355857af7"}, - {file = "tiktoken-0.12.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:20cf97135c9a50de0b157879c3c4accbb29116bcf001283d26e073ff3b345946"}, - {file = "tiktoken-0.12.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:15d875454bbaa3728be39880ddd11a5a2a9e548c29418b41e8fd8a767172b5ec"}, - {file = "tiktoken-0.12.0-cp39-cp39-win_amd64.whl", hash = "sha256:2cff3688ba3c639ebe816f8d58ffbbb0aa7433e23e08ab1cade5d175fc973fb3"}, - {file = "tiktoken-0.12.0.tar.gz", hash = "sha256:b18ba7ee2b093863978fcb14f74b3707cdc8d4d4d3836853ce7ec60772139931"}, -] - -[package.dependencies] -regex = ">=2022.1.18" -requests = ">=2.26.0" - -[package.extras] -blobfile = ["blobfile (>=2)"] - [[package]] name = "tldextract" version = "5.3.0" @@ -6285,7 +5396,6 @@ description = "A lil' TOML parser" optional = false python-versions = ">=3.8" groups = ["main", "dev"] -markers = "python_version < \"3.11\"" files = [ {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"}, {file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"}, @@ -6320,6 +5430,7 @@ files = [ {file = "tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc"}, {file = "tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff"}, ] +markers = {main = "extra == \"presidio\" and python_version < \"3.11\"", dev = "python_version < \"3.11\""} [[package]] name = "tomlkit" @@ -6803,156 +5914,6 @@ files = [ {file = "wrapt-1.17.2.tar.gz", hash = "sha256:41388e9d4d1522446fe79d3213196bd9e3b301a336965b9e27ca2788ebd122f3"}, ] -[[package]] -name = "xxhash" -version = "3.6.0" -description = "Python binding for xxHash" -optional = false -python-versions = ">=3.7" -groups = ["main"] -files = [ - {file = "xxhash-3.6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:87ff03d7e35c61435976554477a7f4cd1704c3596a89a8300d5ce7fc83874a71"}, - {file = "xxhash-3.6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f572dfd3d0e2eb1a57511831cf6341242f5a9f8298a45862d085f5b93394a27d"}, - {file = "xxhash-3.6.0-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:89952ea539566b9fed2bbd94e589672794b4286f342254fad28b149f9615fef8"}, - {file = "xxhash-3.6.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:48e6f2ffb07a50b52465a1032c3cf1f4a5683f944acaca8a134a2f23674c2058"}, - {file = "xxhash-3.6.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b5b848ad6c16d308c3ac7ad4ba6bede80ed5df2ba8ed382f8932df63158dd4b2"}, - {file = "xxhash-3.6.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a034590a727b44dd8ac5914236a7b8504144447a9682586c3327e935f33ec8cc"}, - {file = "xxhash-3.6.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8a8f1972e75ebdd161d7896743122834fe87378160c20e97f8b09166213bf8cc"}, - {file = "xxhash-3.6.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ee34327b187f002a596d7b167ebc59a1b729e963ce645964bbc050d2f1b73d07"}, - {file = "xxhash-3.6.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:339f518c3c7a850dd033ab416ea25a692759dc7478a71131fe8869010d2b75e4"}, - {file = "xxhash-3.6.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:bf48889c9630542d4709192578aebbd836177c9f7a4a2778a7d6340107c65f06"}, - {file = "xxhash-3.6.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:5576b002a56207f640636056b4160a378fe36a58db73ae5c27a7ec8db35f71d4"}, - {file = "xxhash-3.6.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:af1f3278bd02814d6dedc5dec397993b549d6f16c19379721e5a1d31e132c49b"}, - {file = "xxhash-3.6.0-cp310-cp310-win32.whl", hash = "sha256:aed058764db109dc9052720da65fafe84873b05eb8b07e5e653597951af57c3b"}, - {file = "xxhash-3.6.0-cp310-cp310-win_amd64.whl", hash = "sha256:e82da5670f2d0d98950317f82a0e4a0197150ff19a6df2ba40399c2a3b9ae5fb"}, - {file = "xxhash-3.6.0-cp310-cp310-win_arm64.whl", hash = "sha256:4a082ffff8c6ac07707fb6b671caf7c6e020c75226c561830b73d862060f281d"}, - {file = "xxhash-3.6.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b47bbd8cf2d72797f3c2772eaaac0ded3d3af26481a26d7d7d41dc2d3c46b04a"}, - {file = "xxhash-3.6.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2b6821e94346f96db75abaa6e255706fb06ebd530899ed76d32cd99f20dc52fa"}, - {file = "xxhash-3.6.0-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:d0a9751f71a1a65ce3584e9cae4467651c7e70c9d31017fa57574583a4540248"}, - {file = "xxhash-3.6.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b29ee68625ab37b04c0b40c3fafdf24d2f75ccd778333cfb698f65f6c463f62"}, - {file = "xxhash-3.6.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6812c25fe0d6c36a46ccb002f40f27ac903bf18af9f6dd8f9669cb4d176ab18f"}, - {file = "xxhash-3.6.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4ccbff013972390b51a18ef1255ef5ac125c92dc9143b2d1909f59abc765540e"}, - {file = "xxhash-3.6.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:297b7fbf86c82c550e12e8fb71968b3f033d27b874276ba3624ea868c11165a8"}, - {file = "xxhash-3.6.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:dea26ae1eb293db089798d3973a5fc928a18fdd97cc8801226fae705b02b14b0"}, - {file = "xxhash-3.6.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:7a0b169aafb98f4284f73635a8e93f0735f9cbde17bd5ec332480484241aaa77"}, - {file = "xxhash-3.6.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:08d45aef063a4531b785cd72de4887766d01dc8f362a515693df349fdb825e0c"}, - {file = "xxhash-3.6.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:929142361a48ee07f09121fe9e96a84950e8d4df3bb298ca5d88061969f34d7b"}, - {file = "xxhash-3.6.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:51312c768403d8540487dbbfb557454cfc55589bbde6424456951f7fcd4facb3"}, - {file = "xxhash-3.6.0-cp311-cp311-win32.whl", hash = "sha256:d1927a69feddc24c987b337ce81ac15c4720955b667fe9b588e02254b80446fd"}, - {file = "xxhash-3.6.0-cp311-cp311-win_amd64.whl", hash = "sha256:26734cdc2d4ffe449b41d186bbeac416f704a482ed835d375a5c0cb02bc63fef"}, - {file = "xxhash-3.6.0-cp311-cp311-win_arm64.whl", hash = "sha256:d72f67ef8bf36e05f5b6c65e8524f265bd61071471cd4cf1d36743ebeeeb06b7"}, - {file = "xxhash-3.6.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:01362c4331775398e7bb34e3ab403bc9ee9f7c497bc7dee6272114055277dd3c"}, - {file = "xxhash-3.6.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b7b2df81a23f8cb99656378e72501b2cb41b1827c0f5a86f87d6b06b69f9f204"}, - {file = "xxhash-3.6.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:dc94790144e66b14f67b10ac8ed75b39ca47536bf8800eb7c24b50271ea0c490"}, - {file = "xxhash-3.6.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:93f107c673bccf0d592cdba077dedaf52fe7f42dcd7676eba1f6d6f0c3efffd2"}, - {file = "xxhash-3.6.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2aa5ee3444c25b69813663c9f8067dcfaa2e126dc55e8dddf40f4d1c25d7effa"}, - {file = "xxhash-3.6.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f7f99123f0e1194fa59cc69ad46dbae2e07becec5df50a0509a808f90a0f03f0"}, - {file = "xxhash-3.6.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:49e03e6fe2cac4a1bc64952dd250cf0dbc5ef4ebb7b8d96bce82e2de163c82a2"}, - {file = "xxhash-3.6.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:bd17fede52a17a4f9a7bc4472a5867cb0b160deeb431795c0e4abe158bc784e9"}, - {file = "xxhash-3.6.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:6fb5f5476bef678f69db04f2bd1efbed3030d2aba305b0fc1773645f187d6a4e"}, - {file = "xxhash-3.6.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:843b52f6d88071f87eba1631b684fcb4b2068cd2180a0224122fe4ef011a9374"}, - {file = "xxhash-3.6.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:7d14a6cfaf03b1b6f5f9790f76880601ccc7896aff7ab9cd8978a939c1eb7e0d"}, - {file = "xxhash-3.6.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:418daf3db71e1413cfe211c2f9a528456936645c17f46b5204705581a45390ae"}, - {file = "xxhash-3.6.0-cp312-cp312-win32.whl", hash = "sha256:50fc255f39428a27299c20e280d6193d8b63b8ef8028995323bf834a026b4fbb"}, - {file = "xxhash-3.6.0-cp312-cp312-win_amd64.whl", hash = "sha256:c0f2ab8c715630565ab8991b536ecded9416d615538be8ecddce43ccf26cbc7c"}, - {file = "xxhash-3.6.0-cp312-cp312-win_arm64.whl", hash = "sha256:eae5c13f3bc455a3bbb68bdc513912dc7356de7e2280363ea235f71f54064829"}, - {file = "xxhash-3.6.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:599e64ba7f67472481ceb6ee80fa3bd828fd61ba59fb11475572cc5ee52b89ec"}, - {file = "xxhash-3.6.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7d8b8aaa30fca4f16f0c84a5c8d7ddee0e25250ec2796c973775373257dde8f1"}, - {file = "xxhash-3.6.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:d597acf8506d6e7101a4a44a5e428977a51c0fadbbfd3c39650cca9253f6e5a6"}, - {file = "xxhash-3.6.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:858dc935963a33bc33490128edc1c12b0c14d9c7ebaa4e387a7869ecc4f3e263"}, - {file = "xxhash-3.6.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ba284920194615cb8edf73bf52236ce2e1664ccd4a38fdb543506413529cc546"}, - {file = "xxhash-3.6.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4b54219177f6c6674d5378bd862c6aedf64725f70dd29c472eaae154df1a2e89"}, - {file = "xxhash-3.6.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:42c36dd7dbad2f5238950c377fcbf6811b1cdb1c444fab447960030cea60504d"}, - {file = "xxhash-3.6.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f22927652cba98c44639ffdc7aaf35828dccf679b10b31c4ad72a5b530a18eb7"}, - {file = "xxhash-3.6.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b45fad44d9c5c119e9c6fbf2e1c656a46dc68e280275007bbfd3d572b21426db"}, - {file = "xxhash-3.6.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:6f2580ffab1a8b68ef2b901cde7e55fa8da5e4be0977c68f78fc80f3c143de42"}, - {file = "xxhash-3.6.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:40c391dd3cd041ebc3ffe6f2c862f402e306eb571422e0aa918d8070ba31da11"}, - {file = "xxhash-3.6.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f205badabde7aafd1a31e8ca2a3e5a763107a71c397c4481d6a804eb5063d8bd"}, - {file = "xxhash-3.6.0-cp313-cp313-win32.whl", hash = "sha256:2577b276e060b73b73a53042ea5bd5203d3e6347ce0d09f98500f418a9fcf799"}, - {file = "xxhash-3.6.0-cp313-cp313-win_amd64.whl", hash = "sha256:757320d45d2fbcce8f30c42a6b2f47862967aea7bf458b9625b4bbe7ee390392"}, - {file = "xxhash-3.6.0-cp313-cp313-win_arm64.whl", hash = "sha256:457b8f85dec5825eed7b69c11ae86834a018b8e3df5e77783c999663da2f96d6"}, - {file = "xxhash-3.6.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a42e633d75cdad6d625434e3468126c73f13f7584545a9cf34e883aa1710e702"}, - {file = "xxhash-3.6.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:568a6d743219e717b07b4e03b0a828ce593833e498c3b64752e0f5df6bfe84db"}, - {file = "xxhash-3.6.0-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:bec91b562d8012dae276af8025a55811b875baace6af510412a5e58e3121bc54"}, - {file = "xxhash-3.6.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:78e7f2f4c521c30ad5e786fdd6bae89d47a32672a80195467b5de0480aa97b1f"}, - {file = "xxhash-3.6.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3ed0df1b11a79856df5ffcab572cbd6b9627034c1c748c5566fa79df9048a7c5"}, - {file = "xxhash-3.6.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0e4edbfc7d420925b0dd5e792478ed393d6e75ff8fc219a6546fb446b6a417b1"}, - {file = "xxhash-3.6.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fba27a198363a7ef87f8c0f6b171ec36b674fe9053742c58dd7e3201c1ab30ee"}, - {file = "xxhash-3.6.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:794fe9145fe60191c6532fa95063765529770edcdd67b3d537793e8004cabbfd"}, - {file = "xxhash-3.6.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:6105ef7e62b5ac73a837778efc331a591d8442f8ef5c7e102376506cb4ae2729"}, - {file = "xxhash-3.6.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:f01375c0e55395b814a679b3eea205db7919ac2af213f4a6682e01220e5fe292"}, - {file = "xxhash-3.6.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:d706dca2d24d834a4661619dcacf51a75c16d65985718d6a7d73c1eeeb903ddf"}, - {file = "xxhash-3.6.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:5f059d9faeacd49c0215d66f4056e1326c80503f51a1532ca336a385edadd033"}, - {file = "xxhash-3.6.0-cp313-cp313t-win32.whl", hash = "sha256:1244460adc3a9be84731d72b8e80625788e5815b68da3da8b83f78115a40a7ec"}, - {file = "xxhash-3.6.0-cp313-cp313t-win_amd64.whl", hash = "sha256:b1e420ef35c503869c4064f4a2f2b08ad6431ab7b229a05cce39d74268bca6b8"}, - {file = "xxhash-3.6.0-cp313-cp313t-win_arm64.whl", hash = "sha256:ec44b73a4220623235f67a996c862049f375df3b1052d9899f40a6382c32d746"}, - {file = "xxhash-3.6.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:a40a3d35b204b7cc7643cbcf8c9976d818cb47befcfac8bbefec8038ac363f3e"}, - {file = "xxhash-3.6.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:a54844be970d3fc22630b32d515e79a90d0a3ddb2644d8d7402e3c4c8da61405"}, - {file = "xxhash-3.6.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:016e9190af8f0a4e3741343777710e3d5717427f175adfdc3e72508f59e2a7f3"}, - {file = "xxhash-3.6.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4f6f72232f849eb9d0141e2ebe2677ece15adfd0fa599bc058aad83c714bb2c6"}, - {file = "xxhash-3.6.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:63275a8aba7865e44b1813d2177e0f5ea7eadad3dd063a21f7cf9afdc7054063"}, - {file = "xxhash-3.6.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3cd01fa2aa00d8b017c97eb46b9a794fbdca53fc14f845f5a328c71254b0abb7"}, - {file = "xxhash-3.6.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0226aa89035b62b6a86d3c68df4d7c1f47a342b8683da2b60cedcddb46c4d95b"}, - {file = "xxhash-3.6.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c6e193e9f56e4ca4923c61238cdaced324f0feac782544eb4c6d55ad5cc99ddd"}, - {file = "xxhash-3.6.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:9176dcaddf4ca963d4deb93866d739a343c01c969231dbe21680e13a5d1a5bf0"}, - {file = "xxhash-3.6.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:c1ce4009c97a752e682b897aa99aef84191077a9433eb237774689f14f8ec152"}, - {file = "xxhash-3.6.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:8cb2f4f679b01513b7adbb9b1b2f0f9cdc31b70007eaf9d59d0878809f385b11"}, - {file = "xxhash-3.6.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:653a91d7c2ab54a92c19ccf43508b6a555440b9be1bc8be553376778be7f20b5"}, - {file = "xxhash-3.6.0-cp314-cp314-win32.whl", hash = "sha256:a756fe893389483ee8c394d06b5ab765d96e68fbbfe6fde7aa17e11f5720559f"}, - {file = "xxhash-3.6.0-cp314-cp314-win_amd64.whl", hash = "sha256:39be8e4e142550ef69629c9cd71b88c90e9a5db703fecbcf265546d9536ca4ad"}, - {file = "xxhash-3.6.0-cp314-cp314-win_arm64.whl", hash = "sha256:25915e6000338999236f1eb68a02a32c3275ac338628a7eaa5a269c401995679"}, - {file = "xxhash-3.6.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:c5294f596a9017ca5a3e3f8884c00b91ab2ad2933cf288f4923c3fd4346cf3d4"}, - {file = "xxhash-3.6.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1cf9dcc4ab9cff01dfbba78544297a3a01dafd60f3bde4e2bfd016cf7e4ddc67"}, - {file = "xxhash-3.6.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:01262da8798422d0685f7cef03b2bd3f4f46511b02830861df548d7def4402ad"}, - {file = "xxhash-3.6.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:51a73fb7cb3a3ead9f7a8b583ffd9b8038e277cdb8cb87cf890e88b3456afa0b"}, - {file = "xxhash-3.6.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b9c6df83594f7df8f7f708ce5ebeacfc69f72c9fbaaababf6cf4758eaada0c9b"}, - {file = "xxhash-3.6.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:627f0af069b0ea56f312fd5189001c24578868643203bca1abbc2c52d3a6f3ca"}, - {file = "xxhash-3.6.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:aa912c62f842dfd013c5f21a642c9c10cd9f4c4e943e0af83618b4a404d9091a"}, - {file = "xxhash-3.6.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:b465afd7909db30168ab62afe40b2fcf79eedc0b89a6c0ab3123515dc0df8b99"}, - {file = "xxhash-3.6.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:a881851cf38b0a70e7c4d3ce81fc7afd86fbc2a024f4cfb2a97cf49ce04b75d3"}, - {file = "xxhash-3.6.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:9b3222c686a919a0f3253cfc12bb118b8b103506612253b5baeaac10d8027cf6"}, - {file = "xxhash-3.6.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:c5aa639bc113e9286137cec8fadc20e9cd732b2cc385c0b7fa673b84fc1f2a93"}, - {file = "xxhash-3.6.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5c1343d49ac102799905e115aee590183c3921d475356cb24b4de29a4bc56518"}, - {file = "xxhash-3.6.0-cp314-cp314t-win32.whl", hash = "sha256:5851f033c3030dd95c086b4a36a2683c2ff4a799b23af60977188b057e467119"}, - {file = "xxhash-3.6.0-cp314-cp314t-win_amd64.whl", hash = "sha256:0444e7967dac37569052d2409b00a8860c2135cff05502df4da80267d384849f"}, - {file = "xxhash-3.6.0-cp314-cp314t-win_arm64.whl", hash = "sha256:bb79b1e63f6fd84ec778a4b1916dfe0a7c3fdb986c06addd5db3a0d413819d95"}, - {file = "xxhash-3.6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7dac94fad14a3d1c92affb661021e1d5cbcf3876be5f5b4d90730775ccb7ac41"}, - {file = "xxhash-3.6.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6965e0e90f1f0e6cb78da568c13d4a348eeb7f40acfd6d43690a666a459458b8"}, - {file = "xxhash-3.6.0-cp38-cp38-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:2ab89a6b80f22214b43d98693c30da66af910c04f9858dd39c8e570749593d7e"}, - {file = "xxhash-3.6.0-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4903530e866b7a9c1eadfd3fa2fbe1b97d3aed4739a80abf506eb9318561c850"}, - {file = "xxhash-3.6.0-cp38-cp38-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4da8168ae52c01ac64c511d6f4a709479da8b7a4a1d7621ed51652f93747dffa"}, - {file = "xxhash-3.6.0-cp38-cp38-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:97460eec202017f719e839a0d3551fbc0b2fcc9c6c6ffaa5af85bbd5de432788"}, - {file = "xxhash-3.6.0-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:45aae0c9df92e7fa46fbb738737324a563c727990755ec1965a6a339ea10a1df"}, - {file = "xxhash-3.6.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:0d50101e57aad86f4344ca9b32d091a2135a9d0a4396f19133426c88025b09f1"}, - {file = "xxhash-3.6.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:9085e798c163ce310d91f8aa6b325dda3c2944c93c6ce1edb314030d4167cc65"}, - {file = "xxhash-3.6.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:a87f271a33fad0e5bf3be282be55d78df3a45ae457950deb5241998790326f87"}, - {file = "xxhash-3.6.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:9e040d3e762f84500961791fa3709ffa4784d4dcd7690afc655c095e02fff05f"}, - {file = "xxhash-3.6.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:b0359391c3dad6de872fefb0cf5b69d55b0655c55ee78b1bb7a568979b2ce96b"}, - {file = "xxhash-3.6.0-cp38-cp38-win32.whl", hash = "sha256:e4ff728a2894e7f436b9e94c667b0f426b9c74b71f900cf37d5468c6b5da0536"}, - {file = "xxhash-3.6.0-cp38-cp38-win_amd64.whl", hash = "sha256:01be0c5b500c5362871fc9cfdf58c69b3e5c4f531a82229ddb9eb1eb14138004"}, - {file = "xxhash-3.6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:cc604dc06027dbeb8281aeac5899c35fcfe7c77b25212833709f0bff4ce74d2a"}, - {file = "xxhash-3.6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:277175a73900ad43a8caeb8b99b9604f21fe8d7c842f2f9061a364a7e220ddb7"}, - {file = "xxhash-3.6.0-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:cfbc5b91397c8c2972fdac13fb3e4ed2f7f8ccac85cd2c644887557780a9b6e2"}, - {file = "xxhash-3.6.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2762bfff264c4e73c0e507274b40634ff465e025f0eaf050897e88ec8367575d"}, - {file = "xxhash-3.6.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2f171a900d59d51511209f7476933c34a0c2c711078d3c80e74e0fe4f38680ec"}, - {file = "xxhash-3.6.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:780b90c313348f030b811efc37b0fa1431163cb8db8064cf88a7936b6ce5f222"}, - {file = "xxhash-3.6.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:18b242455eccdfcd1fa4134c431a30737d2b4f045770f8fe84356b3469d4b919"}, - {file = "xxhash-3.6.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:a75ffc1bd5def584129774c158e108e5d768e10b75813f2b32650bb041066ed6"}, - {file = "xxhash-3.6.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:1fc1ed882d1e8df932a66e2999429ba6cc4d5172914c904ab193381fba825360"}, - {file = "xxhash-3.6.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:44e342e8cc11b4e79dae5c57f2fb6360c3c20cc57d32049af8f567f5b4bcb5f4"}, - {file = "xxhash-3.6.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:c2f9ccd5c4be370939a2e17602fbc49995299203da72a3429db013d44d590e86"}, - {file = "xxhash-3.6.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:02ea4cb627c76f48cd9fb37cf7ab22bd51e57e1b519807234b473faebe526796"}, - {file = "xxhash-3.6.0-cp39-cp39-win32.whl", hash = "sha256:6551880383f0e6971dc23e512c9ccc986147ce7bfa1cd2e4b520b876c53e9f3d"}, - {file = "xxhash-3.6.0-cp39-cp39-win_amd64.whl", hash = "sha256:7c35c4cdc65f2a29f34425c446f2f5cdcd0e3c34158931e1cc927ece925ab802"}, - {file = "xxhash-3.6.0-cp39-cp39-win_arm64.whl", hash = "sha256:ffc578717a347baf25be8397cb10d2528802d24f94cfc005c0e44fef44b5cdd6"}, - {file = "xxhash-3.6.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0f7b7e2ec26c1666ad5fc9dbfa426a6a3367ceaf79db5dd76264659d509d73b0"}, - {file = "xxhash-3.6.0-pp311-pypy311_pp73-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5dc1e14d14fa0f5789ec29a7062004b5933964bb9b02aae6622b8f530dc40296"}, - {file = "xxhash-3.6.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:881b47fc47e051b37d94d13e7455131054b56749b91b508b0907eb07900d1c13"}, - {file = "xxhash-3.6.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c6dc31591899f5e5666f04cc2e529e69b4072827085c1ef15294d91a004bc1bd"}, - {file = "xxhash-3.6.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:15e0dac10eb9309508bfc41f7f9deaa7755c69e35af835db9cb10751adebc35d"}, - {file = "xxhash-3.6.0.tar.gz", hash = "sha256:f0162a78b13a0d7617b2845b90c763339d1f1d82bb04a4b07f4ab535cc5e05d6"}, -] - [[package]] name = "yarl" version = "1.20.1" @@ -7098,4 +6059,4 @@ presidio = ["presidio-analyzer", "presidio-anonymizer", "stanza", "transformers" [metadata] lock-version = "2.1" python-versions = ">=3.10,<3.14" -content-hash = "95b125b556e6179f8ca13ace4435682b741ca66a68ccea928eb7ffe26502bae2" +content-hash = "3819876822aa5a4b582c016b24e24999cbcbafa84a05d1efd21f6e7fef63e8c6" diff --git a/pyproject.toml b/pyproject.toml index 1fd229d..0332429 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -78,6 +78,7 @@ dependencies = [ "opentelemetry-instrumentation-urllib>=0.55b1,<=0.60b1", "opentelemetry-instrumentation-urllib3>=0.55b1,<=0.60b1", "json-repair==0.44.1", + "httpx>=0.27.0,<1.0.0", ] [project.urls] diff --git a/tests/test_simulation.py b/tests/test_simulation.py new file mode 100644 index 0000000..069e907 --- /dev/null +++ b/tests/test_simulation.py @@ -0,0 +1,783 @@ +""" +Unit tests for the netra/simulation/ module. + +Covers models, utils, client, api, and task layers with mocked +HTTP interactions and async helpers. +""" + +import asyncio +import base64 +from typing import Any, Optional +from unittest.mock import MagicMock, patch + +import httpx +import pytest + +from netra.simulation.models import ( + ConversationResponse, + ConversationStatus, + FileData, + ProcessedFile, + SimulationItem, + TaskResult, +) +from netra.simulation.task import BaseTask +from netra.simulation.utils import ( + execute_task, + format_trace_id, + parse_env_float, + process_files, + run_async_safely, + validate_simulation_inputs, +) + +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- + + +class SyncTask(BaseTask): + """Synchronous task that echoes the message.""" + + def run( + self, + message: str, + session_id: Optional[str] = None, + files: Optional[list[ProcessedFile]] = None, + ) -> TaskResult: + return TaskResult(message=f"echo: {message}", session_id=session_id or "sid-1") + + +class AsyncTask(BaseTask): + """Asynchronous task that echoes the message.""" + + async def run( + self, + message: str, + session_id: Optional[str] = None, + files: Optional[list[ProcessedFile]] = None, + ) -> TaskResult: + return TaskResult(message=f"async-echo: {message}", session_id=session_id or "sid-async") + + +class FileAwareTask(BaseTask): + """Task that uses the files parameter.""" + + def run( + self, + message: str, + session_id: Optional[str] = None, + files: Optional[list[ProcessedFile]] = None, + ) -> TaskResult: + count = len(files) if files else 0 + return TaskResult(message=f"files={count}", session_id=session_id or "sid-files") + + +# --------------------------------------------------------------------------- +# Section 1: Models +# --------------------------------------------------------------------------- + + +class TestConversationStatus: + """Tests for ConversationStatus enum.""" + + def test_continue_value(self) -> None: + assert ConversationStatus.CONTINUE.value == "continue" + + def test_stop_value(self) -> None: + assert ConversationStatus.STOP.value == "stop" + + def test_from_string(self) -> None: + assert ConversationStatus("continue") == ConversationStatus.CONTINUE + assert ConversationStatus("stop") == ConversationStatus.STOP + + +class TestFileData: + """Tests for the FileData frozen dataclass.""" + + def test_creation(self) -> None: + fd = FileData(file_name="a.txt", content_type="text/plain", description="desc", download_url="https://x") + assert fd.file_name == "a.txt" + assert fd.content_type == "text/plain" + assert fd.description == "desc" + assert fd.download_url == "https://x" + + def test_frozen(self) -> None: + fd = FileData(file_name="a.txt", content_type="text/plain", description=None, download_url="https://x") + with pytest.raises(AttributeError): + fd.file_name = "b.txt" # type: ignore[misc] + + +class TestProcessedFile: + """Tests for the ProcessedFile frozen dataclass.""" + + def test_creation(self) -> None: + pf = ProcessedFile(file_name="a.txt", content_type="text/plain", description=None, data="AAAA") + assert pf.data == "AAAA" + + def test_frozen(self) -> None: + pf = ProcessedFile(file_name="a.txt", content_type="text/plain", description=None, data="AAAA") + with pytest.raises(AttributeError): + pf.data = "BBBB" # type: ignore[misc] + + +class TestSimulationItem: + """Tests for the SimulationItem frozen dataclass.""" + + def test_defaults(self) -> None: + item = SimulationItem(run_item_id="r1", message="hi", turn_id="t1") + assert item.files == [] + + def test_with_files(self) -> None: + fd = FileData(file_name="a.txt", content_type="text/plain", description=None, download_url="https://x") + item = SimulationItem(run_item_id="r1", message="hi", turn_id="t1", files=[fd]) + assert len(item.files) == 1 + + +class TestConversationResponse: + """Tests for the ConversationResponse dataclass.""" + + def test_stop_decision(self) -> None: + resp = ConversationResponse(decision=ConversationStatus.STOP, reason="done") + assert resp.decision == ConversationStatus.STOP + assert resp.reason == "done" + + def test_continue_decision_defaults(self) -> None: + resp = ConversationResponse(decision=ConversationStatus.CONTINUE) + assert resp.next_turn_id is None + assert resp.next_user_message is None + assert resp.next_files == [] + + +class TestTaskResult: + """Tests for the TaskResult frozen dataclass.""" + + def test_creation(self) -> None: + tr = TaskResult(message="hello", session_id="s1") + assert tr.message == "hello" + assert tr.session_id == "s1" + + def test_frozen(self) -> None: + tr = TaskResult(message="hello", session_id="s1") + with pytest.raises(AttributeError): + tr.message = "bye" # type: ignore[misc] + + +# --------------------------------------------------------------------------- +# Section 2: Utils +# --------------------------------------------------------------------------- + + +class TestParseEnvFloat: + """Tests for parse_env_float.""" + + def test_returns_default_when_unset(self) -> None: + assert parse_env_float("_NETRA_TEST_NONEXISTENT_VAR_", 42.0) == 42.0 + + def test_parses_valid_value(self, monkeypatch: pytest.MonkeyPatch) -> None: + monkeypatch.setenv("_NETRA_TEST_FLOAT_", "3.14") + assert parse_env_float("_NETRA_TEST_FLOAT_", 1.0) == pytest.approx(3.14) + + def test_returns_default_on_invalid(self, monkeypatch: pytest.MonkeyPatch) -> None: + monkeypatch.setenv("_NETRA_TEST_FLOAT_", "not-a-number") + assert parse_env_float("_NETRA_TEST_FLOAT_", 7.0) == 7.0 + + +class TestFormatTraceId: + """Tests for format_trace_id.""" + + def test_zero(self) -> None: + assert format_trace_id(0) == "0" * 32 + + def test_known_value(self) -> None: + result = format_trace_id(255) + assert result == "0" * 30 + "ff" + assert len(result) == 32 + + +class TestValidateSimulationInputs: + """Tests for validate_simulation_inputs.""" + + def test_valid(self) -> None: + assert validate_simulation_inputs("ds-1", SyncTask()) is True + + def test_empty_dataset_id(self) -> None: + assert validate_simulation_inputs("", SyncTask()) is False + + def test_wrong_task_type(self) -> None: + assert validate_simulation_inputs("ds-1", "not a task") is False # type: ignore[arg-type] + + +class TestRunAsyncSafely: + """Tests for run_async_safely.""" + + def test_runs_coroutine(self) -> None: + async def coro() -> int: + return 42 + + assert run_async_safely(coro()) == 42 + + def test_propagates_exception(self) -> None: + async def coro() -> None: + raise ValueError("boom") + + with pytest.raises(ValueError, match="boom"): + run_async_safely(coro()) + + +class TestProcessFiles: + """Tests for process_files.""" + + def test_empty_list(self) -> None: + assert process_files([]) == [] + + @patch("netra.simulation.utils.httpx.get") + def test_downloads_and_encodes(self, mock_get: MagicMock) -> None: + raw_content = b"hello world" + mock_response = MagicMock(spec=httpx.Response) + mock_response.content = raw_content + mock_response.raise_for_status = MagicMock() + mock_get.return_value = mock_response + + fd = FileData(file_name="a.txt", content_type="text/plain", description=None, download_url="https://x/a.txt") + result = process_files([fd]) + + assert len(result) == 1 + assert result[0].file_name == "a.txt" + assert result[0].data == base64.b64encode(raw_content).decode("ascii") + + @patch("netra.simulation.utils.httpx.get") + def test_raises_on_download_failure(self, mock_get: MagicMock) -> None: + mock_get.side_effect = httpx.ConnectError("connection refused") + + fd = FileData(file_name="a.txt", content_type="text/plain", description=None, download_url="https://x/a.txt") + with pytest.raises(RuntimeError, match="Failed to download file 'a.txt'"): + process_files([fd]) + + @patch("netra.simulation.utils.httpx.get") + def test_concurrent_downloads(self, mock_get: MagicMock) -> None: + mock_response = MagicMock(spec=httpx.Response) + mock_response.content = b"data" + mock_response.raise_for_status = MagicMock() + mock_get.return_value = mock_response + + files = [ + FileData(file_name=f"f{i}.txt", content_type="text/plain", description=None, download_url=f"https://x/{i}") + for i in range(3) + ] + result = process_files(files) + assert len(result) == 3 + assert mock_get.call_count == 3 + + +class TestExecuteTaskFiles: + """Tests for file handling in execute_task.""" + + @patch("netra.simulation.utils.process_files", return_value=[]) + def test_files_downloaded_when_raw_files_present(self, mock_pf: MagicMock) -> None: + """Files are always downloaded and passed to the task.""" + fd = FileData(file_name="a.txt", content_type="text/plain", description=None, download_url="https://x/a.txt") + result = asyncio.run(execute_task(FileAwareTask(), "hi", None, raw_files=[fd])) + mock_pf.assert_called_once_with([fd]) + assert result[0] == "files=0" + + def test_no_files_passed_as_none(self) -> None: + """When no raw_files are provided, files=None is passed to the task.""" + msg, sid = asyncio.run(execute_task(SyncTask(), "hi", None, raw_files=None)) + assert msg == "echo: hi" + + @patch("netra.simulation.utils.process_files") + def test_empty_raw_files_skips_download(self, mock_pf: MagicMock) -> None: + """An empty raw_files list should not trigger downloads.""" + asyncio.run(execute_task(SyncTask(), "hi", None, raw_files=[])) + mock_pf.assert_not_called() + + +class TestExecuteTask: + """Tests for execute_task.""" + + def test_sync_task(self) -> None: + msg, sid = asyncio.run(execute_task(SyncTask(), "hello", None)) + assert msg == "echo: hello" + assert sid == "sid-1" + + def test_async_task(self) -> None: + msg, sid = asyncio.run(execute_task(AsyncTask(), "hello", None)) + assert msg == "async-echo: hello" + assert sid == "sid-async" + + def test_raises_on_bad_return_type(self) -> None: + class BadTask(BaseTask): + def run( + self, + message: str, + session_id: Optional[str] = None, + files: Optional[list[ProcessedFile]] = None, + ) -> Any: + return "not a TaskResult" + + with pytest.raises(ValueError, match="Task must return TaskResult"): + asyncio.run(execute_task(BadTask(), "x", None)) + + +# --------------------------------------------------------------------------- +# Section 3: Client +# --------------------------------------------------------------------------- + + +class TestSimulationHttpClient: + """Tests for SimulationHttpClient.""" + + def _make_config(self, endpoint: str = "https://api.getnetra.ai/telemetry", api_key: str = "key-1") -> MagicMock: + """Create a mock Config.""" + cfg = MagicMock() + cfg.otlp_endpoint = endpoint + cfg.api_key = api_key + cfg.headers = {} + return cfg + + def test_create_client_with_valid_config(self) -> None: + from netra.simulation.client import SimulationHttpClient + + client = SimulationHttpClient(self._make_config()) + assert client._client is not None + client.close() + + def test_create_client_strips_telemetry_suffix(self) -> None: + from netra.simulation.client import SimulationHttpClient + + client = SimulationHttpClient(self._make_config(endpoint="https://api.getnetra.ai/telemetry")) + assert client._client is not None + assert "/telemetry" not in str(client._client.base_url) + client.close() + + def test_create_client_returns_none_on_empty_endpoint(self) -> None: + from netra.simulation.client import SimulationHttpClient + + client = SimulationHttpClient(self._make_config(endpoint="")) + assert client._client is None + + def test_close_sets_client_to_none(self) -> None: + from netra.simulation.client import SimulationHttpClient + + client = SimulationHttpClient(self._make_config()) + assert client._client is not None + client.close() + assert client._client is None + + def test_close_idempotent(self) -> None: + from netra.simulation.client import SimulationHttpClient + + client = SimulationHttpClient(self._make_config()) + client.close() + client.close() + assert client._client is None + + def test_ensure_client_returns_none_when_not_initialized(self) -> None: + from netra.simulation.client import SimulationHttpClient + + client = SimulationHttpClient(self._make_config(endpoint="")) + assert client._ensure_client() is None + + def test_create_run_returns_none_without_client(self) -> None: + from netra.simulation.client import SimulationHttpClient + + client = SimulationHttpClient(self._make_config(endpoint="")) + assert client.create_run(name="test", dataset_id="ds-1") is None + + @patch("netra.simulation.client.httpx.Client") + def test_create_run_success(self, mock_client_cls: MagicMock) -> None: + from netra.simulation.client import SimulationHttpClient + + mock_response = MagicMock(spec=httpx.Response) + mock_response.json.return_value = { + "data": { + "id": "run-1", + "userMessages": [ + { + "testRunItemId": "item-1", + "userMessage": "hello", + "turnId": "turn-1", + "attachments": None, + } + ], + } + } + mock_response.raise_for_status = MagicMock() + + mock_instance = MagicMock() + mock_instance.post.return_value = mock_response + mock_client_cls.return_value = mock_instance + + client = SimulationHttpClient(self._make_config()) + result = client.create_run(name="test", dataset_id="ds-1") + + assert result is not None + assert result["run_id"] == "run-1" + assert len(result["simulation_items"]) == 1 + assert result["simulation_items"][0].message == "hello" + + @patch("netra.simulation.client.httpx.Client") + def test_create_run_returns_none_on_http_error(self, mock_client_cls: MagicMock) -> None: + from netra.simulation.client import SimulationHttpClient + + mock_instance = MagicMock() + mock_instance.post.side_effect = httpx.HTTPStatusError( + "Server Error", request=MagicMock(), response=MagicMock() + ) + mock_client_cls.return_value = mock_instance + + client = SimulationHttpClient(self._make_config()) + result = client.create_run(name="test", dataset_id="ds-1") + assert result is None + + @patch("netra.simulation.client.httpx.Client") + def test_trigger_conversation_stop(self, mock_client_cls: MagicMock) -> None: + from netra.simulation.client import SimulationHttpClient + + mock_response = MagicMock(spec=httpx.Response) + mock_response.json.return_value = { + "data": { + "decision": "stop", + "reason": "all done", + } + } + mock_response.raise_for_status = MagicMock() + + mock_instance = MagicMock() + mock_instance.post.return_value = mock_response + mock_client_cls.return_value = mock_instance + + client = SimulationHttpClient(self._make_config()) + resp = client.trigger_conversation(message="hi", turn_id="t1", session_id="s1", trace_id="trace") + + assert resp is not None + assert resp.decision == ConversationStatus.STOP + assert resp.reason == "all done" + + @patch("netra.simulation.client.httpx.Client") + def test_trigger_conversation_continue(self, mock_client_cls: MagicMock) -> None: + from netra.simulation.client import SimulationHttpClient + + mock_response = MagicMock(spec=httpx.Response) + mock_response.json.return_value = { + "data": { + "decision": "continue", + "userMessages": [ + { + "turnId": "turn-2", + "userMessage": "follow-up", + "testRunItemId": "item-2", + "attachments": None, + } + ], + } + } + mock_response.raise_for_status = MagicMock() + + mock_instance = MagicMock() + mock_instance.post.return_value = mock_response + mock_client_cls.return_value = mock_instance + + client = SimulationHttpClient(self._make_config()) + resp = client.trigger_conversation(message="hi", turn_id="t1", session_id="s1", trace_id="trace") + + assert resp is not None + assert resp.decision == ConversationStatus.CONTINUE + assert resp.next_turn_id == "turn-2" + assert resp.next_user_message == "follow-up" + + def test_trigger_conversation_returns_none_without_client(self) -> None: + from netra.simulation.client import SimulationHttpClient + + client = SimulationHttpClient(self._make_config(endpoint="")) + resp = client.trigger_conversation(message="hi", turn_id="t1", session_id="s1", trace_id="trace") + assert resp is None + + @patch("netra.simulation.client.httpx.Client") + def test_report_failure(self, mock_client_cls: MagicMock) -> None: + from netra.simulation.client import SimulationHttpClient + + mock_response = MagicMock(spec=httpx.Response) + mock_response.raise_for_status = MagicMock() + + mock_instance = MagicMock() + mock_instance.patch.return_value = mock_response + mock_client_cls.return_value = mock_instance + + client = SimulationHttpClient(self._make_config()) + client.report_failure(run_id="run-1", run_item_id="item-1", error="boom") + mock_instance.patch.assert_called_once() + + @patch("netra.simulation.client.httpx.Client") + def test_post_run_status_success(self, mock_client_cls: MagicMock) -> None: + from netra.simulation.client import SimulationHttpClient + + mock_response = MagicMock(spec=httpx.Response) + mock_response.json.return_value = {"data": {"status": "completed"}} + mock_response.raise_for_status = MagicMock() + + mock_instance = MagicMock() + mock_instance.post.return_value = mock_response + mock_client_cls.return_value = mock_instance + + client = SimulationHttpClient(self._make_config()) + result = client.post_run_status(run_id="run-1", status="completed") + assert result == {"status": "completed"} + + @patch("netra.simulation.client.httpx.Client") + def test_post_run_status_returns_error_on_failure(self, mock_client_cls: MagicMock) -> None: + from netra.simulation.client import SimulationHttpClient + + mock_instance = MagicMock() + mock_instance.post.side_effect = httpx.ConnectError("timeout") + mock_client_cls.return_value = mock_instance + + client = SimulationHttpClient(self._make_config()) + result = client.post_run_status(run_id="run-1", status="completed") + assert result == {"success": False} + + def test_parse_files_none(self) -> None: + from netra.simulation.client import SimulationHttpClient + + assert SimulationHttpClient._parse_files(None) == [] + + def test_parse_files_valid(self) -> None: + from netra.simulation.client import SimulationHttpClient + + raw = [{"fileName": "a.txt", "downloadUrl": "https://x/a", "contentType": "text/plain"}] + result = SimulationHttpClient._parse_files(raw) + assert len(result) == 1 + assert result[0].file_name == "a.txt" + + def test_parse_files_skips_malformed(self) -> None: + from netra.simulation.client import SimulationHttpClient + + raw = [{"fileName": "", "downloadUrl": "https://x/a"}] + result = SimulationHttpClient._parse_files(raw) + assert result == [] + + def test_extract_error_message_from_response(self) -> None: + from netra.simulation.client import SimulationHttpClient + + cfg = MagicMock() + cfg.otlp_endpoint = "" + cfg.api_key = "" + cfg.headers = {} + client = SimulationHttpClient(cfg) + + mock_response = MagicMock(spec=httpx.Response) + mock_response.json.return_value = {"error": {"message": "custom error"}} + result = client._extract_error_message(mock_response, ValueError("fallback")) + assert result == "custom error" + + def test_extract_error_message_fallback(self) -> None: + from netra.simulation.client import SimulationHttpClient + + cfg = MagicMock() + cfg.otlp_endpoint = "" + cfg.api_key = "" + cfg.headers = {} + client = SimulationHttpClient(cfg) + + result = client._extract_error_message(None, ValueError("fallback")) + assert result == "fallback" + + +# --------------------------------------------------------------------------- +# Section 4: API (Simulation class) +# --------------------------------------------------------------------------- + + +class TestSimulation: + """Tests for the Simulation public API.""" + + def _make_config(self) -> MagicMock: + cfg = MagicMock() + cfg.otlp_endpoint = "https://api.getnetra.ai/telemetry" + cfg.api_key = "key-1" + cfg.headers = {} + return cfg + + @patch("netra.simulation.api.SimulationHttpClient") + def test_run_simulation_returns_none_on_invalid_inputs(self, mock_client_cls: MagicMock) -> None: + from netra.simulation.api import Simulation + + sim = Simulation(self._make_config()) + result = sim.run_simulation(name="test", dataset_id="", task=SyncTask()) + assert result is None + + @patch("netra.simulation.api.SimulationHttpClient") + def test_run_simulation_returns_none_when_create_run_fails(self, mock_client_cls: MagicMock) -> None: + from netra.simulation.api import Simulation + + mock_client_cls.return_value.create_run.return_value = None + sim = Simulation(self._make_config()) + result = sim.run_simulation(name="test", dataset_id="ds-1", task=SyncTask()) + assert result is None + + @patch("netra.simulation.api.SpanWrapper") + @patch("netra.simulation.api.SimulationHttpClient") + def test_run_simulation_success(self, mock_client_cls: MagicMock, mock_span_wrapper: MagicMock) -> None: + from netra.simulation.api import Simulation + + mock_span = MagicMock() + mock_span.__enter__ = MagicMock(return_value=mock_span) + mock_span.__exit__ = MagicMock(return_value=False) + mock_span.get_current_span.return_value = None + mock_span_wrapper.return_value = mock_span + + stop_response = ConversationResponse( + decision=ConversationStatus.STOP, + reason="done", + ) + + mock_client = MagicMock() + mock_client.create_run.return_value = { + "run_id": "run-1", + "simulation_items": [ + SimulationItem(run_item_id="item-1", message="hello", turn_id="turn-1"), + ], + } + mock_client.trigger_conversation.return_value = stop_response + mock_client.post_run_status.return_value = {"status": "completed"} + mock_client_cls.return_value = mock_client + + sim = Simulation(self._make_config()) + result = sim.run_simulation(name="test", dataset_id="ds-1", task=SyncTask()) + + assert result is not None + assert result["total_items"] == 1 + assert len(result["completed"]) == 1 + assert len(result["failed"]) == 0 + + @patch("netra.simulation.api.SpanWrapper") + @patch("netra.simulation.api.SimulationHttpClient") + def test_run_simulation_marks_failed_on_exception( + self, mock_client_cls: MagicMock, mock_span_wrapper: MagicMock + ) -> None: + from netra.simulation.api import Simulation + + mock_span = MagicMock() + mock_span.__enter__ = MagicMock(return_value=mock_span) + mock_span.__exit__ = MagicMock(return_value=False) + mock_span.get_current_span.return_value = None + mock_span_wrapper.return_value = mock_span + + mock_client = MagicMock() + mock_client.create_run.return_value = { + "run_id": "run-1", + "simulation_items": [ + SimulationItem(run_item_id="item-1", message="hello", turn_id="turn-1"), + ], + } + mock_client.trigger_conversation.side_effect = RuntimeError("backend down") + mock_client.post_run_status.return_value = {} + mock_client_cls.return_value = mock_client + + sim = Simulation(self._make_config()) + result = sim.run_simulation(name="test", dataset_id="ds-1", task=SyncTask()) + + assert result is not None + assert len(result["failed"]) == 1 + assert result["failed"][0]["error"] == "backend down" + + @patch("netra.simulation.api.SpanWrapper") + @patch("netra.simulation.api.SimulationHttpClient") + def test_max_turns_guard(self, mock_client_cls: MagicMock, mock_span_wrapper: MagicMock) -> None: + from netra.simulation.api import Simulation + + mock_span = MagicMock() + mock_span.__enter__ = MagicMock(return_value=mock_span) + mock_span.__exit__ = MagicMock(return_value=False) + mock_span.get_current_span.return_value = None + mock_span_wrapper.return_value = mock_span + + continue_response = ConversationResponse( + decision=ConversationStatus.CONTINUE, + next_turn_id="turn-next", + next_user_message="keep going", + ) + + mock_client = MagicMock() + mock_client.create_run.return_value = { + "run_id": "run-1", + "simulation_items": [ + SimulationItem(run_item_id="item-1", message="hello", turn_id="turn-1"), + ], + } + mock_client.trigger_conversation.return_value = continue_response + mock_client.post_run_status.return_value = {} + mock_client_cls.return_value = mock_client + + sim = Simulation(self._make_config()) + result = sim.run_simulation(name="test", dataset_id="ds-1", task=SyncTask(), max_turns=3) + + assert result is not None + assert len(result["failed"]) == 1 + assert "Exceeded maximum turns (3)" in result["failed"][0]["error"] + + @patch("netra.simulation.api.SimulationHttpClient") + def test_close_delegates_to_client(self, mock_client_cls: MagicMock) -> None: + from netra.simulation.api import Simulation + + mock_client = MagicMock() + mock_client_cls.return_value = mock_client + + sim = Simulation(self._make_config()) + sim.close() + mock_client.close.assert_called_once() + + @patch("netra.simulation.api.SpanWrapper") + @patch("netra.simulation.api.SimulationHttpClient") + def test_trigger_conversation_none_response(self, mock_client_cls: MagicMock, mock_span_wrapper: MagicMock) -> None: + from netra.simulation.api import Simulation + + mock_span = MagicMock() + mock_span.__enter__ = MagicMock(return_value=mock_span) + mock_span.__exit__ = MagicMock(return_value=False) + mock_span.get_current_span.return_value = None + mock_span_wrapper.return_value = mock_span + + mock_client = MagicMock() + mock_client.create_run.return_value = { + "run_id": "run-1", + "simulation_items": [ + SimulationItem(run_item_id="item-1", message="hello", turn_id="turn-1"), + ], + } + mock_client.trigger_conversation.return_value = None + mock_client.post_run_status.return_value = {} + mock_client_cls.return_value = mock_client + + sim = Simulation(self._make_config()) + result = sim.run_simulation(name="test", dataset_id="ds-1", task=SyncTask()) + + assert result is not None + assert len(result["failed"]) == 1 + assert "Failed to get conversation response" in result["failed"][0]["error"] + + +# --------------------------------------------------------------------------- +# Section 5: BaseTask +# --------------------------------------------------------------------------- + + +class TestBaseTask: + """Tests for BaseTask abstract class.""" + + def test_cannot_instantiate_directly(self) -> None: + with pytest.raises(TypeError): + BaseTask() # type: ignore[abstract] + + def test_sync_subclass(self) -> None: + task = SyncTask() + result = task.run(message="hi") + assert isinstance(result, TaskResult) + assert result.message == "echo: hi" + + def test_async_subclass(self) -> None: + task = AsyncTask() + result = asyncio.run(task.run(message="hi")) # type: ignore[arg-type] + assert isinstance(result, TaskResult) + assert result.message == "async-echo: hi"