From 4a31339ea9d5a387fc9ba6555f842e3532c5c6be Mon Sep 17 00:00:00 2001 From: Khoa Ngo The Date: Tue, 22 Jul 2025 16:50:08 +0700 Subject: [PATCH 01/13] chore: upgrade version --- pyproject.toml | 3 ++- src/ii_agent/cli/app.py | 2 +- src/ii_agent/tools/mcp_tool.py | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index ebf139ed..98f3b5e4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -33,7 +33,7 @@ dependencies = [ "pytest>=8.3.5", "python-dotenv>=1.1.0", "python-pptx>=1.0.2", - "rich==13.7.1", + "rich>=13.7.1", "speechrecognition>=3.14.2", "tavily-python>=0.7.2", "tenacity>=9.1.2", @@ -41,6 +41,7 @@ dependencies = [ "uvicorn[standard]>=0.29.0,<0.30.0", "youtube-transcript-api>=1.0.3", "alembic>=1.16.1", + "fastmcp>=2.2.0", ] [project.optional-dependencies] diff --git a/src/ii_agent/cli/app.py b/src/ii_agent/cli/app.py index 21e4d692..f3e0fe4a 100644 --- a/src/ii_agent/cli/app.py +++ b/src/ii_agent/cli/app.py @@ -9,7 +9,7 @@ import json from pathlib import Path from typing import Optional, Dict, Any -from fastmcp import Client +from fastmcp.client import Client from ii_agent.core.config.ii_agent_config import IIAgentConfig from ii_agent.core.config.agent_config import AgentConfig from ii_agent.core.config.llm_config import LLMConfig diff --git a/src/ii_agent/tools/mcp_tool.py b/src/ii_agent/tools/mcp_tool.py index 27813511..407d25ba 100644 --- a/src/ii_agent/tools/mcp_tool.py +++ b/src/ii_agent/tools/mcp_tool.py @@ -1,5 +1,5 @@ from typing import Any, Optional -from fastmcp import Client +from fastmcp.client import Client from mcp.types import ToolAnnotations from ii_agent.tools.base import BaseTool, ToolResult, TextContent, ImageContent, ToolConfirmationDetails, ToolConfirmationOutcome from ii_agent.core.config.ii_agent_config import IIAgentConfig From f646c3e17ab22e0bdd27810a53622d6381e66040 Mon Sep 17 00:00:00 2001 From: Khoa Ngo The Date: Tue, 22 Jul 2025 17:09:47 +0700 Subject: [PATCH 02/13] feat: add sandbox init code --- src/ii_agent/core/config/runtime_config.py | 38 ++++ src/ii_agent/core/storage/models/settings.py | 9 +- src/ii_agent/db/manager.py | 67 ++++-- src/ii_agent/db/models.py | 11 +- src/ii_agent/runtime/__init__.py | 0 src/ii_agent/runtime/base.py | 59 ++++++ src/ii_agent/runtime/config/runtime_config.py | 24 +++ src/ii_agent/runtime/docker.py | 193 ++++++++++++++++++ src/ii_agent/runtime/e2b.py | 76 +++++++ src/ii_agent/runtime/local.py | 57 ++++++ src/ii_agent/runtime/model/__init__.py | 0 src/ii_agent/runtime/model/constants.py | 7 + src/ii_agent/runtime/model/exception.py | 21 ++ src/ii_agent/runtime/runtime_manager.py | 41 ++++ src/ii_agent/runtime/runtime_registry.py | 43 ++++ src/ii_agent/server/services/agent_service.py | 57 ++++-- 16 files changed, 660 insertions(+), 43 deletions(-) create mode 100644 src/ii_agent/core/config/runtime_config.py create mode 100644 src/ii_agent/runtime/__init__.py create mode 100644 src/ii_agent/runtime/base.py create mode 100644 src/ii_agent/runtime/config/runtime_config.py create mode 100644 src/ii_agent/runtime/docker.py create mode 100644 src/ii_agent/runtime/e2b.py create mode 100644 src/ii_agent/runtime/local.py create mode 100644 src/ii_agent/runtime/model/__init__.py create mode 100644 src/ii_agent/runtime/model/constants.py create mode 100644 src/ii_agent/runtime/model/exception.py create mode 100644 src/ii_agent/runtime/runtime_manager.py create mode 100644 src/ii_agent/runtime/runtime_registry.py diff --git a/src/ii_agent/core/config/runtime_config.py b/src/ii_agent/core/config/runtime_config.py new file mode 100644 index 00000000..b6ecb8fd --- /dev/null +++ b/src/ii_agent/core/config/runtime_config.py @@ -0,0 +1,38 @@ +from pydantic import BaseModel, Field, SecretStr, SerializationInfo, field_serializer +from pydantic.json import pydantic_encoder + +from ii_agent.runtime.model.constants import RuntimeMode + + +class RuntimeConfig(BaseModel): + """Configuration for the runtime.""" + + mode: RuntimeMode = Field(default=RuntimeMode.DOCKER) + template_id: str | None = Field(default=None) + runtime_api_key: SecretStr | None = Field(default=None) + service_port: int = Field(default=17300) + + @field_serializer("runtime_api_key") + def api_key_serializer(self, api_key: SecretStr | None, info: SerializationInfo): + """Custom serializer for API keys. + + To serialize the API key instead of ********, set expose_secrets to True in the serialization context. + """ + if api_key is None: + return None + + context = info.context + if context and context.get("expose_secrets", False): + return api_key.get_secret_value() + + return pydantic_encoder(api_key) + + def update(self, settings: "RuntimeConfig"): + if settings.runtime_api_key and self.runtime_api_key is None: + self.runtime_api_key = settings.runtime_api_key + if settings.service_port and self.service_port is None: + self.service_port = settings.service_port + if settings.mode and self.mode is None: + self.mode = settings.mode + if settings.template_id and self.template_id is None: + self.template_id = settings.template_id diff --git a/src/ii_agent/core/storage/models/settings.py b/src/ii_agent/core/storage/models/settings.py index 97f3efb6..52177dbd 100644 --- a/src/ii_agent/core/storage/models/settings.py +++ b/src/ii_agent/core/storage/models/settings.py @@ -2,7 +2,7 @@ from typing import Dict from pydantic import ( - BaseModel, + BaseModel, Field, ) @@ -10,7 +10,7 @@ from ii_agent.core.config.media_config import MediaConfig from ii_agent.core.config.audio_config import AudioConfig from ii_agent.core.config.llm_config import LLMConfig - +from ii_agent.core.config.runtime_config import RuntimeConfig class Settings(BaseModel): @@ -22,7 +22,8 @@ class Settings(BaseModel): search_config: SearchConfig | None = Field(default=None) media_config: MediaConfig | None = Field(default=None) audio_config: AudioConfig | None = Field(default=None) + runtime_config: RuntimeConfig = Field(default=RuntimeConfig()) model_config = { - 'validate_assignment': True, - } \ No newline at end of file + "validate_assignment": True, + } diff --git a/src/ii_agent/db/manager.py b/src/ii_agent/db/manager.py index ee0c3492..f948928c 100644 --- a/src/ii_agent/db/manager.py +++ b/src/ii_agent/db/manager.py @@ -26,10 +26,16 @@ def run_migrations(): logger.error(f"Error running migrations: {e}") raise + run_migrations() -engine = create_engine(load_ii_agent_config().database_url, connect_args={"check_same_thread": False}) -SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine, expire_on_commit=False) +engine = create_engine( + load_ii_agent_config().database_url, connect_args={"check_same_thread": False} +) +SessionLocal = sessionmaker( + autocommit=False, autoflush=False, bind=engine, expire_on_commit=False +) + @contextmanager def get_db() -> Generator[DBSession, None, None]: @@ -57,6 +63,7 @@ def create_session( session_uuid: uuid.UUID, workspace_path: Path, device_id: Optional[str] = None, + runtime_id: Optional[str] = None, ) -> tuple[uuid.UUID, Path]: """Create a new session with a UUID-based workspace directory. @@ -64,6 +71,7 @@ def create_session( session_uuid: The UUID for the session workspace_path: The path to the workspace directory device_id: Optional device identifier for the session + runtime_id: Optional runtime identifier for the session Returns: A tuple of (session_uuid, workspace_path) @@ -71,7 +79,10 @@ def create_session( # Create session in database with get_db() as db: db_session = Session( - id=session_uuid, workspace_dir=str(workspace_path), device_id=device_id + id=session_uuid, + workspace_dir=str(workspace_path), + device_id=device_id, + runtime_id=runtime_id, ) db.add(db_session) db.flush() # This will populate the id field @@ -89,9 +100,7 @@ def get_session_by_workspace(self, workspace_dir: str) -> Optional[Session]: """ with get_db() as db: return ( - db.query(Session) - .filter(Session.workspace_dir == workspace_dir) - .first() + db.query(Session).filter(Session.workspace_dir == workspace_dir).first() ) def get_session_by_id(self, session_id: uuid.UUID) -> Optional[Session]: @@ -131,9 +140,39 @@ def update_session_name(self, session_id: uuid.UUID, name: str) -> None: db_session.name = name db.flush() + def get_runtime_id_by_session_id(self, session_id: uuid.UUID) -> Optional[str]: + """Get the runtime_id of a session. + + Args: + session_id: The UUID of the session to get the runtime_id for + + Returns: + The runtime_id if found, None otherwise + """ + with get_db() as db: + return ( + db.query(Session) + .filter(Session.id == str(session_id)) + .first() + .runtime_id + ) + + def update_session_runtime_id(self, session_id: uuid.UUID, runtime_id: str) -> None: + """Update the runtime_id of a session. + + Args: + session_id: The UUID of the session to update + runtime_id: The new runtime_id for the session + """ + with get_db() as db: + db_session = db.query(Session).filter(Session.id == str(session_id)).first() + if db_session: + db_session.runtime_id = runtime_id + db.flush() + def get_sessions_by_device_id(self, device_id: str) -> List[dict]: """Get all sessions for a specific device ID, sorted by creation time descending. - + Args: device_id: The device identifier to look up sessions for @@ -148,7 +187,8 @@ def get_sessions_by_device_id(self, device_id: str) -> List[dict]: session.workspace_dir, session.created_at, session.device_id, - session.name + session.name, + session.runtime_id FROM session WHERE session.device_id = :device_id ORDER BY session.created_at DESC @@ -166,6 +206,7 @@ def get_sessions_by_device_id(self, device_id: str) -> List[dict]: "created_at": row.created_at, "device_id": row.device_id, "name": row.name or "", + "runtime_id": row.runtime_id, } sessions.append(session_data) @@ -205,9 +246,7 @@ def get_session_events(self, session_id: uuid.UUID) -> list[Event]: A list of events for the session """ with get_db() as db: - return ( - db.query(Event).filter(Event.session_id == str(session_id)).all() - ) + return db.query(Event).filter(Event.session_id == str(session_id)).all() def delete_session_events(self, session_id: uuid.UUID) -> None: """Delete all events for a session. @@ -221,7 +260,7 @@ def delete_session_events(self, session_id: uuid.UUID) -> None: def delete_events_from_last_to_user_message(self, session_id: uuid.UUID) -> None: """Delete events from the most recent event backwards to the last user message (inclusive). This preserves the conversation history before the last user message. - + Args: session_id: The UUID of the session to delete events for """ @@ -245,9 +284,7 @@ def delete_events_from_last_to_user_message(self, session_id: uuid.UUID) -> None ).delete() else: # If no user message found, delete all events - db.query(Event).filter( - Event.session_id == str(session_id) - ).delete() + db.query(Event).filter(Event.session_id == str(session_id)).delete() def get_session_events_with_details(self, session_id: str) -> List[dict]: """Get all events for a specific session ID with session details, sorted by timestamp ascending. diff --git a/src/ii_agent/db/models.py b/src/ii_agent/db/models.py index 359897b4..68358e0f 100644 --- a/src/ii_agent/db/models.py +++ b/src/ii_agent/db/models.py @@ -19,6 +19,7 @@ class Session(Base): created_at = Column(DateTime, default=datetime.utcnow) device_id = Column(String, nullable=True) # Add device_id column name = Column(String, nullable=True) # Add name column + runtime_id = Column(String, nullable=True) # Add runtime_id column # Relationship with events events = relationship( @@ -26,7 +27,12 @@ class Session(Base): ) def __init__( - self, id: uuid.UUID, workspace_dir: str, device_id: Optional[str] = None, name: Optional[str] = None + self, + id: uuid.UUID, + workspace_dir: str, + device_id: Optional[str] = None, + name: Optional[str] = None, + runtime_id: Optional[str] = None, ): """Initialize a session with a UUID and workspace directory. @@ -35,11 +41,13 @@ def __init__( workspace_dir: The workspace directory path device_id: Optional device identifier name: Optional session name + runtime_id: Optional runtime identifier """ self.id = str(id) # Convert UUID to string for storage self.workspace_dir = workspace_dir self.device_id = device_id self.name = name + self.runtime_id = runtime_id class Event(Base): @@ -70,4 +78,3 @@ def __init__(self, session_id: uuid.UUID, event_type: str, event_payload: dict): self.session_id = str(session_id) # Convert UUID to string for storage self.event_type = event_type self.event_payload = event_payload - diff --git a/src/ii_agent/runtime/__init__.py b/src/ii_agent/runtime/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/ii_agent/runtime/base.py b/src/ii_agent/runtime/base.py new file mode 100644 index 00000000..e98a62ad --- /dev/null +++ b/src/ii_agent/runtime/base.py @@ -0,0 +1,59 @@ +from abc import ABC, abstractmethod +from uuid import UUID + +from ii_agent.core.storage.models.settings import Settings +from ii_agent.runtime.model.exception import RuntimeUninitializedError +from ii_agent.runtime.model.constants import RuntimeMode + + +class BaseRuntime(ABC): + """ + Base runtime class. + """ + + mode: RuntimeMode + session_id: UUID + settings: Settings + runtime_id: str | None = None + host_url: str | None = None + + def __init__(self, session_id: UUID, settings: Settings): + """ + Initializes a runtime instance. + """ + self.session_id = session_id + self.settings = settings + + def get_host_url(self) -> str: + if self.host_url is None: + raise RuntimeUninitializedError("Host URL is not set") + return self.host_url + + def get_runtime_id(self) -> str: + if self.runtime_id is None: + raise RuntimeUninitializedError("Runtime ID is not set") + return self.runtime_id + + @abstractmethod + async def connect(self) -> None: + pass + + @abstractmethod + def expose_port(self, port: int) -> str: + pass + + @abstractmethod + async def create(self) -> None: + pass + + @abstractmethod + async def cleanup(self) -> None: + pass + + @abstractmethod + async def start(self) -> None: + pass + + @abstractmethod + async def stop(self) -> None: + pass diff --git a/src/ii_agent/runtime/config/runtime_config.py b/src/ii_agent/runtime/config/runtime_config.py new file mode 100644 index 00000000..4b942e57 --- /dev/null +++ b/src/ii_agent/runtime/config/runtime_config.py @@ -0,0 +1,24 @@ +import os +from pydantic import BaseModel, Field + + +class RuntimeSettings(BaseModel): + """Configuration for the execution runtime""" + + image: str = Field( + default=f"{os.getenv('COMPOSE_PROJECT_NAME')}-sandbox", description="Base image" + ) # Quick fix for now, should be refactored + system_shell: str = Field(default="system_shell", description="System shell") + work_dir: str = Field( + default="/workspace", description="Container working directory" + ) + memory_limit: str = Field(default="4096mb", description="Memory limit") + cpu_limit: float = Field(default=1.0, description="CPU limit") + timeout: int = Field(default=600, description="Default command timeout (seconds)") + network_enabled: bool = Field( + default=True, description="Whether network access is allowed" + ) + network_name: str = Field( + default=f"{os.getenv('COMPOSE_PROJECT_NAME')}_ii", + description="Name of the Docker network to connect to", + ) diff --git a/src/ii_agent/runtime/docker.py b/src/ii_agent/runtime/docker.py new file mode 100644 index 00000000..6af367ca --- /dev/null +++ b/src/ii_agent/runtime/docker.py @@ -0,0 +1,193 @@ +import asyncio +import os +import uuid +from typing import Dict + +import docker +from ii_agent.core.config.utils import load_ii_agent_config +from ii_agent.core.storage.models.settings import Settings +from ii_agent.runtime.base import BaseRuntime +from ii_agent.runtime.config.runtime_config import RuntimeSettings +from ii_agent.runtime.runtime_registry import RuntimeRegistry +from ii_agent.runtime.model.constants import RuntimeMode + + +@RuntimeRegistry.register(RuntimeMode.DOCKER) +class DockerRuntime(BaseRuntime): + """Docker runtime environment. + + Provides a containerized execution environment with resource limits, + file operations, and command execution capabilities. + + Attributes: + config: Runtime configuration. + volume_bindings: Volume mapping configuration. + client: Docker client. + container: Docker container instance. + """ + + mode: RuntimeMode = RuntimeMode.DOCKER + + def __init__( + self, + session_id: uuid.UUID, + settings: Settings, + ): + """Initializes a docker runtime instance. + + Args: + config: Docker runtime configuration. Default configuration used if None. + volume_bindings: Volume mappings in {host_path: container_path} format. + """ + super().__init__(session_id=session_id, settings=settings) + self.config = RuntimeSettings() + self.volume_bindings = { + load_ii_agent_config().workspace_root + + "/" + + str(self.session_id): self.config.work_dir + } + self.client = docker.from_env() + + async def start(self): + pass + + async def stop(self): + pass + + async def connect(self): + self.host_url = ( + f"http://{self.session_id}:{self.settings.runtime_config.service_port}" + ) + + def expose_port(self, port: int) -> str: + public_url = f"http://{self.session_id}-{port}.{os.getenv('BASE_URL')}" + return public_url + + async def create(self): + """Creates and starts the docker runtime container. + + Returns: + Current runtime instance. + + Raises: + docker.errors.APIError: If Docker API call fails. + RuntimeError: If container creation or startup fails. + """ + os.makedirs(self.config.work_dir, exist_ok=True) + try: + # Prepare container config + host_config = self.client.api.create_host_config( + mem_limit=self.config.memory_limit, + cpu_period=100000, + cpu_quota=int(100000 * self.config.cpu_limit), + network_mode=None + if not self.config.network_enabled + else self.config.network_name, + binds=self._prepare_volume_bindings(), + ) + + # Create container + container = await asyncio.to_thread( + self.client.api.create_container, + image=self.config.image, + hostname="sandbox", + host_config=host_config, + name=str(self.session_id), + labels={ + "com.docker.compose.project": os.getenv( + "COMPOSE_PROJECT_NAME", "ii-agent" + ) + }, + tty=True, + detach=True, + stdin_open=True, # Enable interactive mode + ) + + self.container = self.client.containers.get(container["Id"]) + self.container_id = container["Id"] + self.container.start() + + self.host_url = f"http://{str(self.session_id)}:{self.settings.runtime_config.service_port}" + self.runtime_id = self.container_id + print(f"Container created: {self.container_id}") + except Exception as e: + await self.cleanup() # Ensure resources are cleaned up + raise RuntimeError(f"Failed to create sandbox: {e}") from e + + def _prepare_volume_bindings(self) -> Dict[str, Dict[str, str]]: + """Prepares volume binding configuration. + + Returns: + Volume binding configuration dictionary. + """ + bindings = {} + # Add custom volume bindings + for host_path, container_path in self.volume_bindings.items(): + bindings[host_path] = {"bind": container_path, "mode": "rw"} + + return bindings + + def _safe_resolve_path(self, path: str) -> str: + """Safely resolves container path, preventing path traversal. + + Args: + path: Original path. + + Returns: + Resolved absolute path. + + Raises: + ValueError: If path contains potentially unsafe patterns. + """ + # Check for path traversal attempts + if ".." in path.split("/"): + raise ValueError("Path contains potentially unsafe patterns") + + resolved = ( + os.path.join(self.config.work_dir, path) + if not os.path.isabs(path) + else path + ) + return resolved + + async def cleanup(self) -> None: + """Cleans up sandbox resources.""" + errors = [] + try: + if self.container: + try: + await asyncio.to_thread(self.container.stop, timeout=5) + except Exception as e: + errors.append(f"Container stop error: {e}") + + try: + await asyncio.to_thread(self.container.remove, force=True) + except Exception as e: + errors.append(f"Container remove error: {e}") + finally: + self.container = None + + except Exception as e: + errors.append(f"General cleanup error: {e}") + + if errors: + print(f"Warning: Errors during cleanup: {', '.join(errors)}") + + async def __aenter__(self) -> "DockerRuntime": + await self.create() + return self + + async def __aexit__(self, exc_type, exc_val, exc_tb) -> None: + await self.cleanup() + + +if __name__ == "__main__": + + async def main(): + settings = Settings() + runtime = DockerRuntime(uuid.uuid4(), settings) + await runtime.create() + print("Runtime created") + # await runtime.run_command("ls -la") + + asyncio.run(main()) diff --git a/src/ii_agent/runtime/e2b.py b/src/ii_agent/runtime/e2b.py new file mode 100644 index 00000000..0b40aecb --- /dev/null +++ b/src/ii_agent/runtime/e2b.py @@ -0,0 +1,76 @@ +import logging +import uuid +from e2b_code_interpreter import Sandbox, SandboxListQuery +from ii_agent.core.storage.models.settings import Settings +from ii_agent.runtime.base import BaseRuntime +from ii_agent.runtime.runtime_registry import RuntimeRegistry +from ii_agent.runtime.model.constants import RuntimeMode +from ii_agent.db.manager import Sessions + +logger = logging.getLogger(__name__) + + +@RuntimeRegistry.register(RuntimeMode.E2B) +class E2BSandbox(BaseRuntime): + mode: RuntimeMode = RuntimeMode.E2B + + def __init__(self, session_id: uuid.UUID, settings: Settings): + super().__init__(session_id=session_id, settings=settings) + + async def create(self): + self.sandbox = Sandbox( + self.settings.runtime_config.template_id, + api_key=self._get_api_key(), + timeout=3600, + ) + self.host_url = self.expose_port(self.settings.runtime_config.service_port) + if not self.sandbox.sandbox_id: + raise ValueError("Sandbox ID is not set") + self.runtime_id = str(self.sandbox.sandbox_id) + + Sessions.update_session_runtime_id(self.session_id, self.runtime_id) + + def expose_port(self, port: int) -> str: + return "https://" + self.sandbox.get_host(port) + + async def connect(self): + runtime_id = Sessions.get_runtime_id_by_session_id(self.session_id) + if runtime_id is None: + # Note: Raise error for now, should never happen + raise ValueError(f"Runtime ID not found for session {self.session_id}") + # self.create() + + self.sandbox = Sandbox.connect( + runtime_id, + api_key=self._get_api_key(), + ) + self.host_url = self.expose_port(self.settings.runtime_config.service_port) + self.runtime_id = self.sandbox.sandbox_id + + async def cleanup(self): + pass + + async def start(self): + runtime_id = Sessions.get_runtime_id_by_session_id(self.session_id) + if runtime_id is None: + # Note: Raise error for now, should never happen + raise ValueError(f"Runtime ID not found for session {self.session_id}") + if runtime_id in SandboxListQuery(state=["paused"]): + self.sandbox = Sandbox.resume( + runtime_id, + api_key=self._get_api_key(), + timeout=3600, + ) + self.host_url = self.expose_port(self.settings.runtime_config.service_port) + self.runtime_id = self.sandbox.sandbox_id + + def _get_api_key(self): + if self.settings.runtime_config.runtime_api_key is None: + logger.warning("Runtime API key is not set, using empty string") + return "" + else: + return self.settings.runtime_config.runtime_api_key.get_secret_value() + + async def stop(self): + if self.runtime_id is not None: + self.sandbox.pause() diff --git a/src/ii_agent/runtime/local.py b/src/ii_agent/runtime/local.py new file mode 100644 index 00000000..e8eaf171 --- /dev/null +++ b/src/ii_agent/runtime/local.py @@ -0,0 +1,57 @@ +import asyncio +import os +import uuid +from ii_agent.core.storage.models.settings import Settings +from ii_agent.runtime.base import BaseRuntime +from ii_agent.runtime.runtime_registry import RuntimeRegistry +from ii_agent.runtime.model.constants import RuntimeMode + + +@RuntimeRegistry.register(RuntimeMode.LOCAL) +class LocalRuntime(BaseRuntime): + mode: RuntimeMode = RuntimeMode.LOCAL + + def __init__(self, session_id: uuid.UUID, settings: Settings): + super().__init__(session_id=session_id, settings=settings) + + async def start(self): + pass + + def expose_port(self, port: int) -> str: + return f"http://localhost:{port}" + + async def stop(self): + pass + + async def create(self): + # Start code-server in the background + code_server_cmd = ( + "code-server " + f"--port {os.getenv('CODE_SERVER_PORT', 9000)} " + "--auth none " + f"--bind-addr 0.0.0.0:{os.getenv('CODE_SERVER_PORT', 9000)} " + "--disable-telemetry " + "--disable-update-check " + "--trusted-origins * " + "--disable-workspace-trust " + f"/.ii_agent/workspace/{self.session_id} &" # Quickfix: hard code for now + ) + + try: + process = await asyncio.create_subprocess_shell( + code_server_cmd, + stdout=asyncio.subprocess.PIPE, + stderr=asyncio.subprocess.PIPE, + ) + # Don't wait for the process to complete since it runs in background + print(f"Started code-server with PID: {process.pid}") + except Exception as e: + print(f"Failed to start code-server: {e}") + + self.host_url = f"http://localhost:{self.settings.runtime_config.service_port}" + + async def cleanup(self): + pass + + async def connect(self): + self.host_url = f"http://localhost:{self.settings.runtime_config.service_port}" diff --git a/src/ii_agent/runtime/model/__init__.py b/src/ii_agent/runtime/model/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/ii_agent/runtime/model/constants.py b/src/ii_agent/runtime/model/constants.py new file mode 100644 index 00000000..dafddcaa --- /dev/null +++ b/src/ii_agent/runtime/model/constants.py @@ -0,0 +1,7 @@ +from enum import Enum + + +class RuntimeMode(Enum): + DOCKER = "docker" + LOCAL = "local" + E2B = "e2b" diff --git a/src/ii_agent/runtime/model/exception.py b/src/ii_agent/runtime/model/exception.py new file mode 100644 index 00000000..81a132bc --- /dev/null +++ b/src/ii_agent/runtime/model/exception.py @@ -0,0 +1,21 @@ +"""Exception classes for the runtime system. + +This module defines custom exceptions used throughout the runtime system to +handle various error conditions in a structured way. +""" + + +class RuntimeError(Exception): + """Base exception for runtime-related errors.""" + + +class RuntimeTimeoutError(RuntimeError): + """Exception raised when a runtime operation times out.""" + + +class RuntimeResourceError(RuntimeError): + """Exception raised for resource-related errors.""" + + +class RuntimeUninitializedError(RuntimeError): + """Exception raised when a runtime is not initialized.""" diff --git a/src/ii_agent/runtime/runtime_manager.py b/src/ii_agent/runtime/runtime_manager.py new file mode 100644 index 00000000..821447d9 --- /dev/null +++ b/src/ii_agent/runtime/runtime_manager.py @@ -0,0 +1,41 @@ +import uuid +from ii_agent.core.storage.models.settings import Settings +from ii_agent.runtime.model.exception import RuntimeUninitializedError +from ii_agent.runtime.runtime_registry import RuntimeRegistry + + +class RuntimeManager: + def __init__(self, session_id: uuid.UUID, settings: Settings): + self.session_id = session_id + self.workspace_mode = settings.runtime_config.mode + self.settings = settings + self.runtime = None + + async def start_runtime(self): + self.runtime = RuntimeRegistry.create( + self.workspace_mode, self.session_id, self.settings + ) + await self.runtime.create() + + def expose_port(self, port: int) -> str: + if self.runtime is None: + raise RuntimeUninitializedError("Runtime is not initialized") + return self.runtime.expose_port(port) + + def get_host_url(self) -> str: + if self.runtime is None: + raise RuntimeUninitializedError("Runtime is not initialized") + return self.runtime.get_host_url() + + # WIP + async def connect_runtime(self): + self.runtime = RuntimeRegistry.create( + self.workspace_mode, self.session_id, self.settings + ) + await self.runtime.connect() + + async def stop_runtime(self): + pass + + async def cleanup_runtime(self): + pass diff --git a/src/ii_agent/runtime/runtime_registry.py b/src/ii_agent/runtime/runtime_registry.py new file mode 100644 index 00000000..ff588bb0 --- /dev/null +++ b/src/ii_agent/runtime/runtime_registry.py @@ -0,0 +1,43 @@ +from typing import Dict, Type +import uuid +from ii_agent.core.storage.models.settings import Settings +from ii_agent.runtime.base import BaseRuntime +from ii_agent.runtime.model.constants import RuntimeMode + + +class RuntimeRegistry: + """Registry-based factory with decorator support.""" + + _registry: Dict[str, Type[BaseRuntime]] = {} + + @classmethod + def register(cls, runtime_type: RuntimeMode): + """Decorator to register a runtime class.""" + + def decorator(runtime_class: Type[BaseRuntime]): + cls._registry[runtime_type.value] = runtime_class + return runtime_class + + return decorator + + @classmethod + def create( + cls, + runtime_type: RuntimeMode, + session_id: uuid.UUID, + settings: Settings, + ) -> BaseRuntime: + """Create a runtime instance.""" + runtime_class = cls._registry.get(runtime_type.value) + + if runtime_class is None: + available = ", ".join(cls._registry.keys()) + raise ValueError( + f"Unknown runtime type '{runtime_type.value}'. Available: {available}" + ) + + return runtime_class(session_id=session_id, settings=settings) + + @classmethod + def list_runtime_types(cls) -> list[str]: + return list(cls._registry.keys()) diff --git a/src/ii_agent/server/services/agent_service.py b/src/ii_agent/server/services/agent_service.py index d6a33170..02235ea7 100644 --- a/src/ii_agent/server/services/agent_service.py +++ b/src/ii_agent/server/services/agent_service.py @@ -18,7 +18,10 @@ from ii_agent.llm import get_client from ii_agent.llm.context_manager.llm_summarizing import LLMSummarizingContextManager from ii_agent.llm.token_counter import TokenCounter -from ii_agent.prompts.system_prompt import SYSTEM_PROMPT, SYSTEM_PROMPT_WITH_SEQ_THINKING +from ii_agent.prompts.system_prompt import ( + SYSTEM_PROMPT, + SYSTEM_PROMPT_WITH_SEQ_THINKING, +) from ii_agent.subscribers.websocket_subscriber import WebSocketSubscriber from ii_agent.tools import get_system_tools from ii_agent.utils.workspace_manager import WorkspaceManager @@ -47,7 +50,7 @@ async def create_agent( system_prompt: Optional[str] = None, ) -> Tuple[FunctionCallAgent, AgentController]: """Create a new agent instance following CLI patterns. - + Args: model_name: Name of the LLM model to use session_id: Session UUID @@ -55,7 +58,7 @@ async def create_agent( websocket: WebSocket connection tool_args: Tool configuration arguments system_prompt: Optional custom system prompt - + Returns: Tuple of (FunctionCallAgent, AgentController) """ @@ -63,15 +66,15 @@ async def create_agent( user_id = None # TODO: Support user id settings_store = await FileSettingsStore.get_instance(self.config, user_id) settings = await settings_store.load() - + # Get LLM configuration llm_config = settings.llm_configs.get(model_name) if not llm_config: raise ValueError(f"LLM config not found for model: {model_name}") - + # Create LLM client llm_client = get_client(llm_config) - + # Determine system prompt if system_prompt is None: system_prompt = ( @@ -79,14 +82,14 @@ async def create_agent( if tool_args.get("sequential_thinking", False) else SYSTEM_PROMPT ) - + # Create agent config agent_config = AgentConfig( max_tokens_per_turn=self.config.max_output_tokens_per_turn, system_prompt=system_prompt, temperature=getattr(self.config, "temperature", 0.7), ) - + # Get tools tools = get_system_tools( client=llm_client, @@ -95,21 +98,21 @@ async def create_agent( container_id=self.config.docker_container_id, tool_args=tool_args, ) - + # Create agent agent = FunctionCallAgent( llm=llm_client, config=agent_config, tools=tools, ) - + # Create event stream event_stream = AsyncEventStream(logger=logger) - + # Add WebSocket subscriber websocket_subscriber = WebSocketSubscriber(websocket) event_stream.subscribe(websocket_subscriber.handle_event) - + # Create context manager token_counter = TokenCounter() context_manager = LLMSummarizingContextManager( @@ -117,14 +120,14 @@ async def create_agent( token_counter=token_counter, token_budget=self.config.token_budget, ) - + # Create or restore state state = State() try: state.restore_from_session(str(session_id), self.file_store) except FileNotFoundError: logger.info(f"No history found for session {session_id}") - + # Create controller controller = AgentController( agent=agent, @@ -135,10 +138,10 @@ async def create_agent( context_manager=context_manager, interactive_mode=True, ) - + # Store session ID for tracking controller.session_id = session_id - + return agent, controller async def create_reviewer_agent( @@ -150,19 +153,19 @@ async def create_reviewer_agent( tool_args: Dict[str, Any], ) -> Tuple[FunctionCallAgent, AgentController]: """Create a reviewer agent using FunctionCallAgent with reviewer prompt. - + Args: model_name: Name of the LLM model to use session_id: Session UUID workspace_manager: Workspace manager instance websocket: WebSocket connection tool_args: Tool configuration arguments - + Returns: Tuple of (FunctionCallAgent, AgentController) configured for reviewing """ reviewer_prompt = self.get_reviewer_system_prompt() - + return await self.create_agent( model_name=model_name, session_id=session_id, @@ -185,16 +188,26 @@ def get_reviewer_system_prompt(self) -> str: The user will provide you with the task, result, and workspace directory to review. Conduct a thorough review with emphasis on functionality testing and user experience evaluation.""" - def _ensure_session_exists(self, session_id: uuid.UUID, workspace_manager: WorkspaceManager, device_id: Optional[str] = None): + def _ensure_session_exists( + self, + session_id: uuid.UUID, + workspace_manager: WorkspaceManager, + device_id: Optional[str] = None, + ): """Ensure a database session exists for the given session ID.""" existing_session = Sessions.get_session_by_id(session_id) if existing_session: - logger.info(f"Found existing session {session_id} with workspace at {existing_session.workspace_dir}") + logger.info( + f"Found existing session {session_id} with workspace at {existing_session.workspace_dir}" + ) else: # Create new session if it doesn't exist Sessions.create_session( device_id=device_id, session_uuid=session_id, workspace_path=workspace_manager.root, + runtime_id=str(session_id), + ) + logger.info( + f"Created new session {session_id} with workspace at {workspace_manager.root}" ) - logger.info(f"Created new session {session_id} with workspace at {workspace_manager.root}") \ No newline at end of file From 802ccf78512c8466c90cb535dff6760d3bc8b269 Mon Sep 17 00:00:00 2001 From: Khoa Ngo The Date: Wed, 23 Jul 2025 11:19:00 +0700 Subject: [PATCH 03/13] refactor: mcp new interface --- pyproject.toml | 5 + src/ii_agent/cli/app.py | 16 +- src/ii_agent/core/config/runtime_config.py | 2 +- src/ii_agent/core/config/utils.py | 2 +- .../storage/settings/file_settings_store.py | 10 +- src/ii_agent/runtime/__init__.py | 11 + src/ii_agent/runtime/base.py | 15 +- src/ii_agent/runtime/docker.py | 13 +- src/ii_agent/runtime/e2b.py | 15 +- src/ii_agent/runtime/local.py | 13 +- src/ii_agent/runtime/runtime_manager.py | 8 +- src/ii_agent/runtime/runtime_registry.py | 7 +- src/ii_agent/tools/tool_manager.py | 75 +- uv.lock | 2361 ++++++++++------- 14 files changed, 1571 insertions(+), 982 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 9277443c..cc7adff7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -42,6 +42,11 @@ dependencies = [ "youtube-transcript-api>=1.0.3", "alembic>=1.16.1", "fastmcp>=2.2.0", + "pre-commit>=4.2.0", + "e2b-code-interpreter==1.2.0b5", + "libtmux>=0.46.2", + "ipdb>=0.13.13", + "docker>=7.1.0", ] [project.optional-dependencies] diff --git a/src/ii_agent/cli/app.py b/src/ii_agent/cli/app.py index f641a3fe..7d30b396 100644 --- a/src/ii_agent/cli/app.py +++ b/src/ii_agent/cli/app.py @@ -9,7 +9,7 @@ import json from pathlib import Path from typing import Optional, Dict, Any -from fastmcp.client import Client +from uuid import UUID from ii_agent.core.config.ii_agent_config import IIAgentConfig from ii_agent.core.config.agent_config import AgentConfig from ii_agent.core.config.llm_config import LLMConfig @@ -24,6 +24,7 @@ from ii_agent.llm.context_manager import LLMCompact from ii_agent.core.storage.settings.file_settings_store import FileSettingsStore from ii_agent.llm.token_counter import TokenCounter +from ii_agent.runtime.runtime_manager import RuntimeManager from ii_agent.utils.workspace_manager import WorkspaceManager from ii_agent.tools import get_system_tools from ii_agent.core.logger import logger @@ -36,7 +37,6 @@ ) from ii_agent.tools import AgentToolManager from ii_agent.llm.base import ToolParam -from ii_tool.mcp.server import create_mcp class CLIApp: @@ -179,12 +179,12 @@ async def initialize_agent(self, continue_from_state: bool = False) -> None: interactive_mode=True, ) - # Get system MCP tools - mcp_client = Client( - create_mcp( - workspace_dir=str(self.workspace_manager.root), - session_id=self.config.session_id, - ) + runtime_manager = RuntimeManager( + session_id=UUID(self.config.session_id), settings=settings + ) + await runtime_manager.start_runtime() + mcp_client = await runtime_manager.get_mcp_client( + str(self.workspace_manager.root) ) await tool_manager.register_mcp_tools( mcp_client=mcp_client, diff --git a/src/ii_agent/core/config/runtime_config.py b/src/ii_agent/core/config/runtime_config.py index b6ecb8fd..de56d81a 100644 --- a/src/ii_agent/core/config/runtime_config.py +++ b/src/ii_agent/core/config/runtime_config.py @@ -7,7 +7,7 @@ class RuntimeConfig(BaseModel): """Configuration for the runtime.""" - mode: RuntimeMode = Field(default=RuntimeMode.DOCKER) + mode: RuntimeMode = Field(default=RuntimeMode.LOCAL) template_id: str | None = Field(default=None) runtime_api_key: SecretStr | None = Field(default=None) service_port: int = Field(default=17300) diff --git a/src/ii_agent/core/config/utils.py b/src/ii_agent/core/config/utils.py index b5191675..cd5f4a04 100644 --- a/src/ii_agent/core/config/utils.py +++ b/src/ii_agent/core/config/utils.py @@ -3,4 +3,4 @@ def load_ii_agent_config() -> IIAgentConfig: """Load the IIAgent config from the environment variables.""" - return IIAgentConfig() + return IIAgentConfig(session_id="test") diff --git a/src/ii_agent/core/storage/settings/file_settings_store.py b/src/ii_agent/core/storage/settings/file_settings_store.py index 77498fcc..7ea2553e 100644 --- a/src/ii_agent/core/storage/settings/file_settings_store.py +++ b/src/ii_agent/core/storage/settings/file_settings_store.py @@ -27,19 +27,19 @@ async def call_sync_from_async(fn: Callable, *args, **kwargs): @dataclass class FileSettingsStore(SettingsStore): file_store: FileStore - path: str = 'settings.json' + path: str = "settings.json" - async def load(self) -> Settings | None: + async def load(self) -> Settings: try: json_str = await call_sync_from_async(self.file_store.read, self.path) kwargs = json.loads(json_str) settings = Settings(**kwargs) return settings except FileNotFoundError: - return None + raise FileNotFoundError(f"Settings file not found at {self.path}") async def store(self, settings: Settings) -> None: - json_str = settings.model_dump_json(context={'expose_secrets': True}) + json_str = settings.model_dump_json(context={"expose_secrets": True}) await call_sync_from_async(self.file_store.write, self.path, json_str) @classmethod @@ -48,6 +48,6 @@ async def get_instance( ) -> FileSettingsStore: file_store = get_file_store( config.file_store, - config.file_store_path, + config.file_store_path, ) return FileSettingsStore(file_store) diff --git a/src/ii_agent/runtime/__init__.py b/src/ii_agent/runtime/__init__.py index e69de29b..1b2ae3c7 100644 --- a/src/ii_agent/runtime/__init__.py +++ b/src/ii_agent/runtime/__init__.py @@ -0,0 +1,11 @@ +from ii_agent.runtime.local import LocalRuntime +from ii_agent.runtime.docker import DockerRuntime +from ii_agent.runtime.e2b import E2BRuntime +from ii_agent.runtime.model.constants import RuntimeMode + +__all__ = [ + "LocalRuntime", + "DockerRuntime", + "E2BRuntime", + "RuntimeMode", +] diff --git a/src/ii_agent/runtime/base.py b/src/ii_agent/runtime/base.py index e98a62ad..2ea58627 100644 --- a/src/ii_agent/runtime/base.py +++ b/src/ii_agent/runtime/base.py @@ -1,10 +1,16 @@ +from __future__ import annotations from abc import ABC, abstractmethod +from typing import TYPE_CHECKING from uuid import UUID -from ii_agent.core.storage.models.settings import Settings +from fastmcp import Client + from ii_agent.runtime.model.exception import RuntimeUninitializedError from ii_agent.runtime.model.constants import RuntimeMode +if TYPE_CHECKING: + from ii_agent.core.storage.models.settings import Settings + class BaseRuntime(ABC): """ @@ -24,10 +30,9 @@ def __init__(self, session_id: UUID, settings: Settings): self.session_id = session_id self.settings = settings - def get_host_url(self) -> str: - if self.host_url is None: - raise RuntimeUninitializedError("Host URL is not set") - return self.host_url + @abstractmethod + def get_mcp_client(self, workspace_dir: str) -> Client: + raise NotImplementedError("Subclasses must implement this method") def get_runtime_id(self) -> str: if self.runtime_id is None: diff --git a/src/ii_agent/runtime/docker.py b/src/ii_agent/runtime/docker.py index 6af367ca..b0bc7f35 100644 --- a/src/ii_agent/runtime/docker.py +++ b/src/ii_agent/runtime/docker.py @@ -1,16 +1,20 @@ +from __future__ import annotations import asyncio import os import uuid -from typing import Dict +from typing import Dict, TYPE_CHECKING import docker +from fastmcp.client import Client from ii_agent.core.config.utils import load_ii_agent_config -from ii_agent.core.storage.models.settings import Settings from ii_agent.runtime.base import BaseRuntime from ii_agent.runtime.config.runtime_config import RuntimeSettings from ii_agent.runtime.runtime_registry import RuntimeRegistry from ii_agent.runtime.model.constants import RuntimeMode +if TYPE_CHECKING: + from ii_agent.core.storage.models.settings import Settings + @RuntimeRegistry.register(RuntimeMode.DOCKER) class DockerRuntime(BaseRuntime): @@ -59,6 +63,11 @@ async def connect(self): f"http://{self.session_id}:{self.settings.runtime_config.service_port}" ) + def get_mcp_client(self, workspace_dir: str) -> Client: + if not self.host_url: + raise ValueError("Host URL is not set") + return Client(self.host_url) + def expose_port(self, port: int) -> str: public_url = f"http://{self.session_id}-{port}.{os.getenv('BASE_URL')}" return public_url diff --git a/src/ii_agent/runtime/e2b.py b/src/ii_agent/runtime/e2b.py index 0b40aecb..da6b6598 100644 --- a/src/ii_agent/runtime/e2b.py +++ b/src/ii_agent/runtime/e2b.py @@ -1,17 +1,23 @@ +from __future__ import annotations import logging import uuid +from typing import TYPE_CHECKING + +from fastmcp.client import Client from e2b_code_interpreter import Sandbox, SandboxListQuery -from ii_agent.core.storage.models.settings import Settings from ii_agent.runtime.base import BaseRuntime from ii_agent.runtime.runtime_registry import RuntimeRegistry from ii_agent.runtime.model.constants import RuntimeMode from ii_agent.db.manager import Sessions +if TYPE_CHECKING: + from ii_agent.core.storage.models.settings import Settings + logger = logging.getLogger(__name__) @RuntimeRegistry.register(RuntimeMode.E2B) -class E2BSandbox(BaseRuntime): +class E2BRuntime(BaseRuntime): mode: RuntimeMode = RuntimeMode.E2B def __init__(self, session_id: uuid.UUID, settings: Settings): @@ -33,6 +39,11 @@ async def create(self): def expose_port(self, port: int) -> str: return "https://" + self.sandbox.get_host(port) + def get_mcp_client(self, workspace_dir: str) -> Client: + if not self.host_url: + raise ValueError("Host URL is not set") + return Client(self.host_url) + async def connect(self): runtime_id = Sessions.get_runtime_id_by_session_id(self.session_id) if runtime_id is None: diff --git a/src/ii_agent/runtime/local.py b/src/ii_agent/runtime/local.py index e8eaf171..791d80f5 100644 --- a/src/ii_agent/runtime/local.py +++ b/src/ii_agent/runtime/local.py @@ -1,11 +1,18 @@ +from __future__ import annotations import asyncio import os import uuid -from ii_agent.core.storage.models.settings import Settings +from typing import TYPE_CHECKING + +from fastmcp.client import Client +from ii_tool.mcp.server import create_mcp from ii_agent.runtime.base import BaseRuntime from ii_agent.runtime.runtime_registry import RuntimeRegistry from ii_agent.runtime.model.constants import RuntimeMode +if TYPE_CHECKING: + from ii_agent.core.storage.models.settings import Settings + @RuntimeRegistry.register(RuntimeMode.LOCAL) class LocalRuntime(BaseRuntime): @@ -23,6 +30,10 @@ def expose_port(self, port: int) -> str: async def stop(self): pass + def get_mcp_client(self, workspace_dir: str) -> Client: + mcp_client = create_mcp(workspace_dir, str(self.session_id)) + return Client(mcp_client) + async def create(self): # Start code-server in the background code_server_cmd = ( diff --git a/src/ii_agent/runtime/runtime_manager.py b/src/ii_agent/runtime/runtime_manager.py index 821447d9..c63e1822 100644 --- a/src/ii_agent/runtime/runtime_manager.py +++ b/src/ii_agent/runtime/runtime_manager.py @@ -1,4 +1,6 @@ import uuid + +from fastmcp.client import Client from ii_agent.core.storage.models.settings import Settings from ii_agent.runtime.model.exception import RuntimeUninitializedError from ii_agent.runtime.runtime_registry import RuntimeRegistry @@ -17,15 +19,15 @@ async def start_runtime(self): ) await self.runtime.create() - def expose_port(self, port: int) -> str: + async def expose_port(self, port: int) -> str: if self.runtime is None: raise RuntimeUninitializedError("Runtime is not initialized") return self.runtime.expose_port(port) - def get_host_url(self) -> str: + async def get_mcp_client(self, workspace_dir: str) -> Client: if self.runtime is None: raise RuntimeUninitializedError("Runtime is not initialized") - return self.runtime.get_host_url() + return self.runtime.get_mcp_client(workspace_dir) # WIP async def connect_runtime(self): diff --git a/src/ii_agent/runtime/runtime_registry.py b/src/ii_agent/runtime/runtime_registry.py index ff588bb0..42339b29 100644 --- a/src/ii_agent/runtime/runtime_registry.py +++ b/src/ii_agent/runtime/runtime_registry.py @@ -1,9 +1,12 @@ -from typing import Dict, Type +from __future__ import annotations +from typing import Dict, Type, TYPE_CHECKING import uuid -from ii_agent.core.storage.models.settings import Settings from ii_agent.runtime.base import BaseRuntime from ii_agent.runtime.model.constants import RuntimeMode +if TYPE_CHECKING: + from ii_agent.core.storage.models.settings import Settings + class RuntimeRegistry: """Registry-based factory with decorator support.""" diff --git a/src/ii_agent/tools/tool_manager.py b/src/ii_agent/tools/tool_manager.py index 1ed0aacf..d164ed1a 100644 --- a/src/ii_agent/tools/tool_manager.py +++ b/src/ii_agent/tools/tool_manager.py @@ -1,7 +1,5 @@ import asyncio import logging -from copy import deepcopy -from dataclasses import dataclass from fastmcp import Client from pydantic import BaseModel from typing import Optional, List, Dict, Any @@ -16,6 +14,7 @@ from ii_agent.tools.web_visit_tool import WebVisitTool from ii_agent.tools.web_dev_tool import FullStackInitTool + class ToolCallParameters(BaseModel): tool_call_id: str tool_name: str @@ -58,13 +57,24 @@ class AgentToolManager: search capabilities, and task completion functionality. """ - def __init__(self, tools: List[BaseTool], logger_for_agent_logs: logging.Logger, interactive_mode: bool = True, reviewer_mode: bool = False): - self.tools = deepcopy(tools) + def __init__( + self, + tools: List[BaseTool], + logger_for_agent_logs: logging.Logger, + interactive_mode: bool = True, + reviewer_mode: bool = False, + ): + self.tools = tools async def register_tools(self, tools: List[BaseTool]): self.tools.extend(tools) - async def register_mcp_tools(self, mcp_config: Dict[str, Any] | None = None, mcp_client: Client | None = None, trust: bool = False): + async def register_mcp_tools( + self, + mcp_config: Dict[str, Any] | None = None, + mcp_client: Client | None = None, + trust: bool = False, + ): if not mcp_config and not mcp_client: raise ValueError("Either mcp_config or client must be provided") if mcp_config: @@ -73,7 +83,9 @@ async def register_mcp_tools(self, mcp_config: Dict[str, Any] | None = None, mcp async with mcp_client: mcp_tools = await mcp_client.list_tools() for tool in mcp_tools: - assert tool.description is not None, f"Tool {tool.name} has no description" + assert tool.description is not None, ( + f"Tool {tool.name} has no description" + ) tool_annotations = tool.annotations self.tools.append( MCPTool( @@ -127,9 +139,9 @@ async def run_tool(self, tool_call: ToolCallParameters) -> ToolResult: tool_input = tool_call.tool_input llm_tool = self.get_tool(tool_name) logger.debug(f"Running tool: {tool_name}") - logger.debug(f"Tool input: {tool_input}") + logger.debug(f"Tool input: {tool_input}") tool_result = await llm_tool.execute(tool_input) - + user_display_content = tool_result.user_display_content tool_input_str = "\n".join([f" - {k}: {v}" for k, v in tool_input.items()]) @@ -141,44 +153,50 @@ async def run_tool(self, tool_call: ToolCallParameters) -> ToolResult: return tool_result - async def run_tools_batch(self, tool_calls: List[ToolCallParameters]) -> List[ToolResult]: + async def run_tools_batch( + self, tool_calls: List[ToolCallParameters] + ) -> List[ToolResult]: """ Execute multiple tools either concurrently or serially based on their read-only status. - + Args: tool_calls: List of tool call parameters - + Returns: List of tool results in the same order as input tool_calls """ if not tool_calls: return [] - + if len(tool_calls) == 1: # Single tool - just execute normally result = await self.run_tool(tool_calls[0]) return [result] - + # Determine execution strategy based on read-only status if should_run_concurrently(tool_calls, self): logger.info(f"Running {len(tool_calls)} tools concurrently (all read-only)") return await self._run_tools_concurrently(tool_calls) else: - logger.info(f"Running {len(tool_calls)} tools serially (contains non-read-only tools)") + logger.info( + f"Running {len(tool_calls)} tools serially (contains non-read-only tools)" + ) return await self._run_tools_serially(tool_calls) - - async def _run_tools_concurrently(self, tool_calls: List[ToolCallParameters]) -> List[ToolResult]: + + async def _run_tools_concurrently( + self, tool_calls: List[ToolCallParameters] + ) -> List[ToolResult]: """Execute tools concurrently and return results in order.""" - + # Create tasks for each tool with proper concurrency limits async def run_single_tool(tool_call: ToolCallParameters) -> ToolResult: """Wrapper for single tool execution.""" result = await self.run_tool(tool_call) return result - + # Create tasks for all tools with concurrency limit from ii_agent.utils.concurrent_execution import MAX_TOOL_CONCURRENCY - + if len(tool_calls) <= MAX_TOOL_CONCURRENCY: # All tools can run concurrently tasks = [asyncio.create_task(run_single_tool(tc)) for tc in tool_calls] @@ -186,27 +204,31 @@ async def run_single_tool(tool_call: ToolCallParameters) -> ToolResult: else: # Use semaphore to limit concurrency semaphore = asyncio.Semaphore(MAX_TOOL_CONCURRENCY) - + async def limited_run_tool(tool_call: ToolCallParameters) -> ToolResult: async with semaphore: return await run_single_tool(tool_call) - + tasks = [asyncio.create_task(limited_run_tool(tc)) for tc in tool_calls] results = await asyncio.gather(*tasks, return_exceptions=True) - + # Handle any exceptions and maintain order final_results = [] for i, result in enumerate(results): if isinstance(result, Exception): - error_msg = f"Error executing tool {tool_calls[i].tool_name}: {str(result)}" + error_msg = ( + f"Error executing tool {tool_calls[i].tool_name}: {str(result)}" + ) logger.error(error_msg) final_results.append(error_msg) else: final_results.append(result) - + return final_results - - async def _run_tools_serially(self, tool_calls: List[ToolCallParameters]) -> List[ToolResult]: + + async def _run_tools_serially( + self, tool_calls: List[ToolCallParameters] + ) -> List[ToolResult]: """Execute tools serially and return results in order.""" results = [] for tool_call in tool_calls: @@ -224,4 +246,3 @@ def get_tools(self) -> List[BaseTool]: Returns the list of tools. """ return self.tools - diff --git a/uv.lock b/uv.lock index f3bab875..5546ba58 100644 --- a/uv.lock +++ b/uv.lock @@ -1,14 +1,15 @@ version = 1 -revision = 1 requires-python = ">=3.10" resolution-markers = [ "python_full_version >= '3.13'", - "python_full_version >= '3.12.4' and python_full_version < '3.13'", - "python_full_version >= '3.12' and python_full_version < '3.12.4'", + "python_full_version == '3.12.*'", "python_full_version == '3.11.*'", "python_full_version < '3.11'", ] +[options] +prerelease-mode = "allow" + [[package]] name = "aiohappyeyeballs" version = "2.6.1" @@ -20,7 +21,7 @@ wheels = [ [[package]] name = "aiohttp" -version = "3.12.12" +version = "3.12.14" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiohappyeyeballs" }, @@ -32,93 +33,94 @@ dependencies = [ { name = "propcache" }, { name = "yarl" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f2/84/ea27e6ad14747d8c51afe201fb88a5c8282b6278256d30a6f71f730add88/aiohttp-3.12.12.tar.gz", hash = "sha256:05875595d2483d96cb61fa9f64e75262d7ac6251a7e3c811d8e26f7d721760bd", size = 7818643 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b4/d9/cfde93b9cb75253c716b8b1c773565209e3d4dd0772dd3ce3a2adcaa4639/aiohttp-3.12.12-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6f25e9d274d6abbb15254f76f100c3984d6b9ad6e66263cc60a465dd5c7e48f5", size = 702071 }, - { url = "https://files.pythonhosted.org/packages/ee/b0/46e38b8bc0bc645317deec32612af922ad9bafd85a1df255a67c2f2305f6/aiohttp-3.12.12-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b8ec3c1a1c13d24941b5b913607e57b9364e4c0ea69d5363181467492c4b2ba6", size = 478436 }, - { url = "https://files.pythonhosted.org/packages/8f/47/9c83db7f02ca71eb99a707ee13657fc24ba703b9babc59000c1f58ac1198/aiohttp-3.12.12-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:81ef2f9253c327c211cb7b06ea2edd90e637cf21c347b894d540466b8d304e08", size = 466213 }, - { url = "https://files.pythonhosted.org/packages/31/fe/4690c112e269e06c9182c32eeb43f3a95c4f203fdb095502717327993b80/aiohttp-3.12.12-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28ded835c3663fd41c9ad44685811b11e34e6ac9a7516a30bfce13f6abba4496", size = 1648258 }, - { url = "https://files.pythonhosted.org/packages/c8/1f/dacca6c7bbe69c77d8535d7a672478803e7078cc20fd9993fe09aa5be880/aiohttp-3.12.12-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a4b78ccf254fc10605b263996949a94ca3f50e4f9100e05137d6583e266b711e", size = 1622316 }, - { url = "https://files.pythonhosted.org/packages/ff/65/5ef47708f70524fcdecda735e0aea06e0feb7b8679e976e9bd1e7900f4c0/aiohttp-3.12.12-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4f4a5af90d5232c41bb857568fe7d11ed84408653ec9da1ff999cc30258b9bd1", size = 1694723 }, - { url = "https://files.pythonhosted.org/packages/18/62/ab32bfa59f61292e4096c383316863e10001eec30e5b4b314856ed7156e2/aiohttp-3.12.12-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ffa5205c2f53f1120e93fdf2eca41b0f6344db131bc421246ee82c1e1038a14a", size = 1737037 }, - { url = "https://files.pythonhosted.org/packages/c1/b9/8b8f793081311e4f63aea63003a519064048e406c627c0454d6ed09dbc99/aiohttp-3.12.12-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f68301660f0d7a3eddfb84f959f78a8f9db98c76a49b5235508fa16edaad0f7c", size = 1641701 }, - { url = "https://files.pythonhosted.org/packages/1a/5c/72f510d42d626463b526345dcb8d14b390de89a9ba27a4717b518460bcd4/aiohttp-3.12.12-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:db874d3b0c92fdbb553751af9d2733b378c25cc83cd9dfba87f12fafd2dc9cd5", size = 1581824 }, - { url = "https://files.pythonhosted.org/packages/61/6f/9378c9e1543d1c800ca040e21cd333b8f923ed051ae82b5a49ad96a6ac71/aiohttp-3.12.12-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5e53cf9c201b45838a2d07b1f2d5f7fec9666db7979240002ce64f9b8a1e0cf2", size = 1625674 }, - { url = "https://files.pythonhosted.org/packages/bb/85/4eef9bd52b497a405c88469cc099f4d15d33b149b5746ca4ef8ec6ab6388/aiohttp-3.12.12-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:8687cc5f32b4e328c233acd387d09a1b477007896b2f03c1c823a0fd05f63883", size = 1636460 }, - { url = "https://files.pythonhosted.org/packages/56/59/d8e954830b375fd658843cf7d88d27ca5e38dd5fcbfe62db3d1ba415d0fe/aiohttp-3.12.12-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:5ee537ad29de716a3d8dc46c609908de0c25ffeebf93cd94a03d64cdc07d66d0", size = 1611912 }, - { url = "https://files.pythonhosted.org/packages/c3/5d/d0096a02f0515a38dff67db42d966273a12d17fc895e91466bfb4ab3875e/aiohttp-3.12.12-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:411f821be5af6af11dc5bed6c6c1dc6b6b25b91737d968ec2756f9baa75e5f9b", size = 1691498 }, - { url = "https://files.pythonhosted.org/packages/87/8d/d3a02397a6345c06623ae4648e2aef18fced858510b4a89d7262cfa4c683/aiohttp-3.12.12-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:f90319d94cf5f9786773237f24bd235a7b5959089f1af8ec1154580a3434b503", size = 1714737 }, - { url = "https://files.pythonhosted.org/packages/a9/40/b81000bf07c96db878703ea3dc561393d82441597729910459a8e06acc9a/aiohttp-3.12.12-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:73b148e606f34e9d513c451fd65efe1091772659ca5703338a396a99f60108ff", size = 1643078 }, - { url = "https://files.pythonhosted.org/packages/41/e5/31830642ce2c6d3dba74ed8a94933213df5e1651c1e8b4efc81cc88105ab/aiohttp-3.12.12-cp310-cp310-win32.whl", hash = "sha256:d40e7bfd577fdc8a92b72f35dfbdd3ec90f1bc8a72a42037fefe34d4eca2d4a1", size = 427517 }, - { url = "https://files.pythonhosted.org/packages/55/9d/a4e5379d44679e5f8d7d7ebecb0dae8cafab95176c4e753da6bc4b4aebb5/aiohttp-3.12.12-cp310-cp310-win_amd64.whl", hash = "sha256:65c7804a2343893d6dea9fce69811aea0a9ac47f68312cf2e3ee1668cd9a387f", size = 450725 }, - { url = "https://files.pythonhosted.org/packages/47/1f/b1b66e05dc3066a9ba7862d50e2e95b3871db82ccf9652568845f353eeba/aiohttp-3.12.12-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:38823fe0d8bc059b3eaedb263fe427d887c7032e72b4ef92c472953285f0e658", size = 709385 }, - { url = "https://files.pythonhosted.org/packages/43/e6/3230e42af16438b450b1e193c537fd3d2d31771dafda3c2105a8d11af707/aiohttp-3.12.12-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:10237f2c34711215d04ed21da63852ce023608299554080a45c576215d9df81c", size = 481660 }, - { url = "https://files.pythonhosted.org/packages/06/ba/cfa91fe5cc262535e1175b1522d8fcc09f9d6ad18b85241f4ee3be1d780f/aiohttp-3.12.12-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:563ec477c0dc6d56fc7f943a3475b5acdb399c7686c30f5a98ada24bb7562c7a", size = 469924 }, - { url = "https://files.pythonhosted.org/packages/9a/f0/5c706cfddd4769b55c0cda466aa6034412d39e416f0b30dda81c4a24616f/aiohttp-3.12.12-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3d05c46a61aca7c47df74afff818bc06a251ab95d95ff80b53665edfe1e0bdf", size = 1740116 }, - { url = "https://files.pythonhosted.org/packages/4d/9f/04dba2e1c8bee53c3c623d11a1f947c9e2712500f734dc0dfd06daad32ec/aiohttp-3.12.12-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:277c882916759b4a6b6dc7e2ceb124aad071b3c6456487808d9ab13e1b448d57", size = 1688784 }, - { url = "https://files.pythonhosted.org/packages/df/24/19d6d4c41fbf8304fe7c111fcc701e0aa5a2232ee3ac16272677a11f9cfe/aiohttp-3.12.12-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:216abf74b324b0f4e67041dd4fb2819613909a825904f8a51701fbcd40c09cd7", size = 1787575 }, - { url = "https://files.pythonhosted.org/packages/0c/59/01f4c55a1f91ad3b5255b2498b3a22362a3fe6ee9bc9ba1af3cc668244da/aiohttp-3.12.12-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65d6cefad286459b68e7f867b9586a821fb7f121057b88f02f536ef570992329", size = 1826621 }, - { url = "https://files.pythonhosted.org/packages/55/85/6357166918ff5025602a7cc41332c1ae7a5b57f2fe3da4d755ae30f24bd0/aiohttp-3.12.12-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:feaaaff61966b5f4b4eae0b79fc79427f49484e4cfa5ab7d138ecd933ab540a8", size = 1729082 }, - { url = "https://files.pythonhosted.org/packages/e3/ca/de3b5ccd5a2aa9352f6ec6f446565f6e1601ebb54860c94c686a9ff76660/aiohttp-3.12.12-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a05917780b7cad1755784b16cfaad806bc16029a93d15f063ca60185b7d9ba05", size = 1666159 }, - { url = "https://files.pythonhosted.org/packages/d1/69/a1006021a1d3244c0872ee75cd8da150e0098b3b2ec6945c225754d11a60/aiohttp-3.12.12-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:082c5ec6d262c1b2ee01c63f4fb9152c17f11692bf16f0f100ad94a7a287d456", size = 1714433 }, - { url = "https://files.pythonhosted.org/packages/d2/2a/15aa1179e9fbdd0d17cdf117b4296dedad098abb5a93f8e9c8ab4626f6ea/aiohttp-3.12.12-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:b265a3a8b379b38696ac78bdef943bdc4f4a5d6bed1a3fb5c75c6bab1ecea422", size = 1709590 }, - { url = "https://files.pythonhosted.org/packages/a2/f0/95ed9e21250815f1d1a0cd3e868a3f39400a16010ae59f19ddd4ccc4e787/aiohttp-3.12.12-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:2e0f2e208914ecbc4b2a3b7b4daa759d0c587d9a0b451bb0835ac47fae7fa735", size = 1689776 }, - { url = "https://files.pythonhosted.org/packages/81/4d/370ecc133c648c98a85445f2d331c1272859c89cd52c29a293015bc352c7/aiohttp-3.12.12-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:9923b025845b72f64d167bca221113377c8ffabd0a351dc18fb839d401ee8e22", size = 1783378 }, - { url = "https://files.pythonhosted.org/packages/a8/86/414e3dae7e07caf6b02cd75d7148d0d8673d4c5077f407be3627d6e33fac/aiohttp-3.12.12-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:1ebb213445900527831fecc70e185bf142fdfe5f2a691075f22d63c65ee3c35a", size = 1803841 }, - { url = "https://files.pythonhosted.org/packages/88/df/486f10df681cd1a8c898acc8dc2edbd46ffb088b886757b71ae362bf44d3/aiohttp-3.12.12-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6fc369fb273a8328077d37798b77c1e65676709af5c182cb74bd169ca9defe81", size = 1716896 }, - { url = "https://files.pythonhosted.org/packages/07/1e/1cacaf5d838869432e96ece1580d0b51494ebb66351f0e8118b74b38d2f0/aiohttp-3.12.12-cp311-cp311-win32.whl", hash = "sha256:58ecd10fda6a44c311cd3742cfd2aea8c4c600338e9f27cb37434d9f5ca9ddaa", size = 427030 }, - { url = "https://files.pythonhosted.org/packages/30/dd/e89c1d190da2c84e0ca03c2970b9988a9c56005d18db7f447cf62b3ae6d0/aiohttp-3.12.12-cp311-cp311-win_amd64.whl", hash = "sha256:b0066e88f30be00badffb5ef8f2281532b9a9020863d873ae15f7c147770b6ec", size = 451419 }, - { url = "https://files.pythonhosted.org/packages/df/e6/df14ec151942818ecc5e685fa8a4b07d3d3d8a9e4a7d2701047c89290551/aiohttp-3.12.12-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:98451ce9ce229d092f278a74a7c2a06b3aa72984673c87796126d7ccade893e9", size = 700494 }, - { url = "https://files.pythonhosted.org/packages/4f/dc/7bc6e17adcd7a82b0d0317ad3e792ac22c93fb672077f0eade93e8d70182/aiohttp-3.12.12-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:adbac7286d89245e1aff42e948503fdc6edf6d5d65c8e305a67c40f6a8fb95f4", size = 475095 }, - { url = "https://files.pythonhosted.org/packages/80/fd/c4e8846ad9d9ecdb7d5ba96de65b7bf2c1582f0b2732f2023080c1c05255/aiohttp-3.12.12-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0728882115bfa85cbd8d0f664c8ccc0cfd5bd3789dd837596785450ae52fac31", size = 467929 }, - { url = "https://files.pythonhosted.org/packages/70/40/abebcf5c81f5e65b4379c05929773be2731ce12414264d3e0fe09ee241eb/aiohttp-3.12.12-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6bf3b9d9e767f9d0e09fb1a31516410fc741a62cc08754578c40abc497d09540", size = 1714729 }, - { url = "https://files.pythonhosted.org/packages/8e/67/4c4f96ef6f16405e7c5205ab3c28852c7e904493b6ddc1c744dda1c97a81/aiohttp-3.12.12-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c944860e86b9f77a462321a440ccf6fa10f5719bb9d026f6b0b11307b1c96c7b", size = 1697380 }, - { url = "https://files.pythonhosted.org/packages/e9/a2/dae9ebea4caa8030170c0237e55fa0960df44b3596a849ab9ea621964054/aiohttp-3.12.12-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b1979e1f0c98c06fd0cd940988833b102fa3aa56751f6c40ffe85cabc51f6fd", size = 1752474 }, - { url = "https://files.pythonhosted.org/packages/31/ef/f3d9073565ac7ad5257aaa1490ebfc2f182dfc817d3ccfd38c8ab35b2247/aiohttp-3.12.12-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:120b7dd084e96cfdad85acea2ce1e7708c70a26db913eabb8d7b417c728f5d84", size = 1798631 }, - { url = "https://files.pythonhosted.org/packages/8b/0b/8b1978662274c80c8e4a739d9be1ae9ef25e5ce42b55838d6a9d1a4e3497/aiohttp-3.12.12-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e58f5ae79649ffa247081c2e8c85e31d29623cf2a3137dda985ae05c9478aae", size = 1718071 }, - { url = "https://files.pythonhosted.org/packages/56/aa/35786137db867901b41cb3d2c19c0f4c56dfe581694dba99dec2683d8f8d/aiohttp-3.12.12-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9aa5f049e3e2745b0141f13e5a64e7c48b1a1427ed18bbb7957b348f282fee56", size = 1633871 }, - { url = "https://files.pythonhosted.org/packages/63/1d/34d45497dd04d08d662ecda875c44e91d271bbc5d21f4c9e4cbd3ddf7ae2/aiohttp-3.12.12-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7163cc9cf3722d90f1822f8a38b211e3ae2fc651c63bb55449f03dc1b3ff1d44", size = 1694933 }, - { url = "https://files.pythonhosted.org/packages/29/c7/41e09a4517449eabbb0a7fe6d60f584fe5b21d4bff761197eb0b81e70034/aiohttp-3.12.12-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:ef97c4d035b721de6607f3980fa3e4ef0ec3aca76474b5789b7fac286a8c4e23", size = 1716386 }, - { url = "https://files.pythonhosted.org/packages/3a/32/907bd2010b51b70de5314ad707dfc4e898ea0011ff3d678cdf43d6f8980a/aiohttp-3.12.12-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:1c14448d6a86acadc3f7b2f4cc385d1fb390acb6f37dce27f86fe629410d92e3", size = 1657039 }, - { url = "https://files.pythonhosted.org/packages/60/27/8d87344a33346dcd39273adc33060aeb135e0ef70d1d6e71a3b03894a8e9/aiohttp-3.12.12-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:a1b6df6255cfc493454c79221183d64007dd5080bcda100db29b7ff181b8832c", size = 1736599 }, - { url = "https://files.pythonhosted.org/packages/ca/45/57c7ef1af694a6d0906abab6edde03787c8c6b0cf5d8359b69d1eb0679df/aiohttp-3.12.12-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:60fc7338dfb0626c2927bfbac4785de3ea2e2bbe3d328ba5f3ece123edda4977", size = 1764575 }, - { url = "https://files.pythonhosted.org/packages/2a/cc/b1f918cd702efa9ead9d41f89214e9225cda4e5d013d6eed7f1915c17d0a/aiohttp-3.12.12-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d2afc72207ef4c9d4ca9fcd00689a6a37ef2d625600c3d757b5c2b80c9d0cf9a", size = 1724184 }, - { url = "https://files.pythonhosted.org/packages/47/55/089762ee32c2a2e0f523d9ab38c9da2a344cac0e0cc8d16ecf206517ef7e/aiohttp-3.12.12-cp312-cp312-win32.whl", hash = "sha256:8098a48f93b2cbcdb5778e7c9a0e0375363e40ad692348e6e65c3b70d593b27c", size = 421762 }, - { url = "https://files.pythonhosted.org/packages/ab/47/151f657e429972916f61399bd52b410e9072d5a2cae1b794f890930e5797/aiohttp-3.12.12-cp312-cp312-win_amd64.whl", hash = "sha256:d1c1879b2e0fc337d7a1b63fe950553c2b9e93c071cf95928aeea1902d441403", size = 447863 }, - { url = "https://files.pythonhosted.org/packages/ee/3e/396a7d1c47aa7a74612b186dc716857506c61afac72337a7a96215c2a124/aiohttp-3.12.12-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ea5d604318234427929d486954e3199aded65f41593ac57aa0241ab93dda3d15", size = 694901 }, - { url = "https://files.pythonhosted.org/packages/cc/97/235e48eadf73a1854b4d4da29b88d00049309d897d55a511e1cbe4412603/aiohttp-3.12.12-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e03ff38250b8b572dce6fcd7b6fb6ee398bb8a59e6aa199009c5322d721df4fc", size = 472552 }, - { url = "https://files.pythonhosted.org/packages/6b/73/cd7c9439e8cab4113650541017c6524bd0e675b219dfdbbf945a78305e3f/aiohttp-3.12.12-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:71125b1fc2b6a94bccc63bbece620906a4dead336d2051f8af9cbf04480bc5af", size = 464853 }, - { url = "https://files.pythonhosted.org/packages/d1/33/eea88ee55ed4b3f74732d9fc773e6fcf134a2971a19c7ecc49a291e7e57f/aiohttp-3.12.12-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:784a66f9f853a22c6b8c2bd0ff157f9b879700f468d6d72cfa99167df08c5c9c", size = 1703671 }, - { url = "https://files.pythonhosted.org/packages/2a/e3/a67ecf9c154b13bad9e2a86ea3782a4b73e889343ffde8c1aadcf9099c09/aiohttp-3.12.12-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a5be0b58670b54301404bd1840e4902570a1c3be00358e2700919cb1ea73c438", size = 1684934 }, - { url = "https://files.pythonhosted.org/packages/89/f0/3aaea866531be2f2fcf3a87607e1f55fa72e6ce5acd6b058941a4fc35e15/aiohttp-3.12.12-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce8f13566fc7bf5a728275b434bc3bdea87a7ed3ad5f734102b02ca59d9b510f", size = 1737004 }, - { url = "https://files.pythonhosted.org/packages/a7/7a/15867a4c7d39d8fd9bd02191cf60b1d06415fc407bbd4ff2f9660845f1cb/aiohttp-3.12.12-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d736e57d1901683bc9be648aa308cb73e646252c74b4c639c35dcd401ed385ea", size = 1786378 }, - { url = "https://files.pythonhosted.org/packages/bd/61/82b15f87088b35705e01fce55806241b45a1099b3470bbca0bed8ee98662/aiohttp-3.12.12-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e2007eaa7aae9102f211c519d1ec196bd3cecb1944a095db19eeaf132b798738", size = 1708707 }, - { url = "https://files.pythonhosted.org/packages/28/f2/aed0786d5a1c2ed1f5a13ff2a98baacc27206b81d93812da28fc49d8a5d0/aiohttp-3.12.12-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2a813e61583cab6d5cdbaa34bc28863acdb92f9f46e11de1b3b9251a1e8238f6", size = 1622410 }, - { url = "https://files.pythonhosted.org/packages/17/54/8305f49a960376136ada977be1370fddb584c63d40bd1b9bef59469f28c7/aiohttp-3.12.12-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e408293aa910b0aea48b86a28eace41d497a85ba16c20f619f0c604597ef996c", size = 1675435 }, - { url = "https://files.pythonhosted.org/packages/bb/dc/0a55350025bc297265cfa6c6b1b1f7508f4226ca3238697cbe5e772a7d76/aiohttp-3.12.12-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:f3d31faf290f5a30acba46b388465b67c6dbe8655d183e9efe2f6a1d594e6d9d", size = 1707099 }, - { url = "https://files.pythonhosted.org/packages/d8/70/d949a1612b996e49d540c10ed77a0a1465c482a590e9a59c1c7897746119/aiohttp-3.12.12-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:0b84731697325b023902aa643bd1726d999f5bc7854bc28b17ff410a81151d4b", size = 1649693 }, - { url = "https://files.pythonhosted.org/packages/c1/ea/fb87beb7135e25576a1e6fbe98106c037d9fcf1543f19108f9ceb73c192c/aiohttp-3.12.12-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:a324c6852b6e327811748446e56cc9bb6eaa58710557922183175816e82a4234", size = 1725825 }, - { url = "https://files.pythonhosted.org/packages/f1/1f/adbeb3e440d49b733cef499ace94723ab1fe9fb516425e219379e03b7c9a/aiohttp-3.12.12-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:22fd867fbd72612dcf670c90486dbcbaf702cb807fb0b42bc0b7a142a573574a", size = 1759300 }, - { url = "https://files.pythonhosted.org/packages/f2/c1/2fe007ad930f409d0d7fd9916cd55ec9b78b6a611a237424266ed71da48b/aiohttp-3.12.12-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:3e092f1a970223794a4bf620a26c0e4e4e8e36bccae9b0b5da35e6d8ee598a03", size = 1708189 }, - { url = "https://files.pythonhosted.org/packages/85/5e/ed3ed640fafae3972eae6cd26f66240108cf62452ac8128d59970d538cb1/aiohttp-3.12.12-cp313-cp313-win32.whl", hash = "sha256:7f5f5eb8717ef8ba15ab35fcde5a70ad28bbdc34157595d1cddd888a985f5aae", size = 420783 }, - { url = "https://files.pythonhosted.org/packages/a6/db/57d2bb4af52dd0c6f62c42c7d34b82495b2902e50440134f70bfb7ee0fdd/aiohttp-3.12.12-cp313-cp313-win_amd64.whl", hash = "sha256:ace2499bdd03c329c054dc4b47361f2b19d5aa470f7db5c7e0e989336761b33c", size = 446721 }, +sdist = { url = "https://files.pythonhosted.org/packages/e6/0b/e39ad954107ebf213a2325038a3e7a506be3d98e1435e1f82086eec4cde2/aiohttp-3.12.14.tar.gz", hash = "sha256:6e06e120e34d93100de448fd941522e11dafa78ef1a893c179901b7d66aa29f2", size = 7822921 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/88/f161f429f9de391eee6a5c2cffa54e2ecd5b7122ae99df247f7734dfefcb/aiohttp-3.12.14-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:906d5075b5ba0dd1c66fcaaf60eb09926a9fef3ca92d912d2a0bbdbecf8b1248", size = 702641 }, + { url = "https://files.pythonhosted.org/packages/fe/b5/24fa382a69a25d242e2baa3e56d5ea5227d1b68784521aaf3a1a8b34c9a4/aiohttp-3.12.14-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c875bf6fc2fd1a572aba0e02ef4e7a63694778c5646cdbda346ee24e630d30fb", size = 479005 }, + { url = "https://files.pythonhosted.org/packages/09/67/fda1bc34adbfaa950d98d934a23900918f9d63594928c70e55045838c943/aiohttp-3.12.14-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fbb284d15c6a45fab030740049d03c0ecd60edad9cd23b211d7e11d3be8d56fd", size = 466781 }, + { url = "https://files.pythonhosted.org/packages/36/96/3ce1ea96d3cf6928b87cfb8cdd94650367f5c2f36e686a1f5568f0f13754/aiohttp-3.12.14-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38e360381e02e1a05d36b223ecab7bc4a6e7b5ab15760022dc92589ee1d4238c", size = 1648841 }, + { url = "https://files.pythonhosted.org/packages/be/04/ddea06cb4bc7d8db3745cf95e2c42f310aad485ca075bd685f0e4f0f6b65/aiohttp-3.12.14-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:aaf90137b5e5d84a53632ad95ebee5c9e3e7468f0aab92ba3f608adcb914fa95", size = 1622896 }, + { url = "https://files.pythonhosted.org/packages/73/66/63942f104d33ce6ca7871ac6c1e2ebab48b88f78b2b7680c37de60f5e8cd/aiohttp-3.12.14-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e532a25e4a0a2685fa295a31acf65e027fbe2bea7a4b02cdfbbba8a064577663", size = 1695302 }, + { url = "https://files.pythonhosted.org/packages/20/00/aab615742b953f04b48cb378ee72ada88555b47b860b98c21c458c030a23/aiohttp-3.12.14-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eab9762c4d1b08ae04a6c77474e6136da722e34fdc0e6d6eab5ee93ac29f35d1", size = 1737617 }, + { url = "https://files.pythonhosted.org/packages/d6/4f/ef6d9f77225cf27747368c37b3d69fac1f8d6f9d3d5de2d410d155639524/aiohttp-3.12.14-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:abe53c3812b2899889a7fca763cdfaeee725f5be68ea89905e4275476ffd7e61", size = 1642282 }, + { url = "https://files.pythonhosted.org/packages/37/e1/e98a43c15aa52e9219a842f18c59cbae8bbe2d50c08d298f17e9e8bafa38/aiohttp-3.12.14-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5760909b7080aa2ec1d320baee90d03b21745573780a072b66ce633eb77a8656", size = 1582406 }, + { url = "https://files.pythonhosted.org/packages/71/5c/29c6dfb49323bcdb0239bf3fc97ffcf0eaf86d3a60426a3287ec75d67721/aiohttp-3.12.14-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:02fcd3f69051467bbaa7f84d7ec3267478c7df18d68b2e28279116e29d18d4f3", size = 1626255 }, + { url = "https://files.pythonhosted.org/packages/79/60/ec90782084090c4a6b459790cfd8d17be2c5662c9c4b2d21408b2f2dc36c/aiohttp-3.12.14-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:4dcd1172cd6794884c33e504d3da3c35648b8be9bfa946942d353b939d5f1288", size = 1637041 }, + { url = "https://files.pythonhosted.org/packages/22/89/205d3ad30865c32bc472ac13f94374210745b05bd0f2856996cb34d53396/aiohttp-3.12.14-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:224d0da41355b942b43ad08101b1b41ce633a654128ee07e36d75133443adcda", size = 1612494 }, + { url = "https://files.pythonhosted.org/packages/48/ae/2f66edaa8bd6db2a4cba0386881eb92002cdc70834e2a93d1d5607132c7e/aiohttp-3.12.14-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:e387668724f4d734e865c1776d841ed75b300ee61059aca0b05bce67061dcacc", size = 1692081 }, + { url = "https://files.pythonhosted.org/packages/08/3a/fa73bfc6e21407ea57f7906a816f0dc73663d9549da703be05dbd76d2dc3/aiohttp-3.12.14-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:dec9cde5b5a24171e0b0a4ca064b1414950904053fb77c707efd876a2da525d8", size = 1715318 }, + { url = "https://files.pythonhosted.org/packages/e3/b3/751124b8ceb0831c17960d06ee31a4732cb4a6a006fdbfa1153d07c52226/aiohttp-3.12.14-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:bbad68a2af4877cc103cd94af9160e45676fc6f0c14abb88e6e092b945c2c8e3", size = 1643660 }, + { url = "https://files.pythonhosted.org/packages/81/3c/72477a1d34edb8ab8ce8013086a41526d48b64f77e381c8908d24e1c18f5/aiohttp-3.12.14-cp310-cp310-win32.whl", hash = "sha256:ee580cb7c00bd857b3039ebca03c4448e84700dc1322f860cf7a500a6f62630c", size = 428289 }, + { url = "https://files.pythonhosted.org/packages/a2/c4/8aec4ccf1b822ec78e7982bd5cf971113ecce5f773f04039c76a083116fc/aiohttp-3.12.14-cp310-cp310-win_amd64.whl", hash = "sha256:cf4f05b8cea571e2ccc3ca744e35ead24992d90a72ca2cf7ab7a2efbac6716db", size = 451328 }, + { url = "https://files.pythonhosted.org/packages/53/e1/8029b29316971c5fa89cec170274582619a01b3d82dd1036872acc9bc7e8/aiohttp-3.12.14-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f4552ff7b18bcec18b60a90c6982049cdb9dac1dba48cf00b97934a06ce2e597", size = 709960 }, + { url = "https://files.pythonhosted.org/packages/96/bd/4f204cf1e282041f7b7e8155f846583b19149e0872752711d0da5e9cc023/aiohttp-3.12.14-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8283f42181ff6ccbcf25acaae4e8ab2ff7e92b3ca4a4ced73b2c12d8cd971393", size = 482235 }, + { url = "https://files.pythonhosted.org/packages/d6/0f/2a580fcdd113fe2197a3b9df30230c7e85bb10bf56f7915457c60e9addd9/aiohttp-3.12.14-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:040afa180ea514495aaff7ad34ec3d27826eaa5d19812730fe9e529b04bb2179", size = 470501 }, + { url = "https://files.pythonhosted.org/packages/38/78/2c1089f6adca90c3dd74915bafed6d6d8a87df5e3da74200f6b3a8b8906f/aiohttp-3.12.14-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b413c12f14c1149f0ffd890f4141a7471ba4b41234fe4fd4a0ff82b1dc299dbb", size = 1740696 }, + { url = "https://files.pythonhosted.org/packages/4a/c8/ce6c7a34d9c589f007cfe064da2d943b3dee5aabc64eaecd21faf927ab11/aiohttp-3.12.14-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:1d6f607ce2e1a93315414e3d448b831238f1874b9968e1195b06efaa5c87e245", size = 1689365 }, + { url = "https://files.pythonhosted.org/packages/18/10/431cd3d089de700756a56aa896faf3ea82bee39d22f89db7ddc957580308/aiohttp-3.12.14-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:565e70d03e924333004ed101599902bba09ebb14843c8ea39d657f037115201b", size = 1788157 }, + { url = "https://files.pythonhosted.org/packages/fa/b2/26f4524184e0f7ba46671c512d4b03022633bcf7d32fa0c6f1ef49d55800/aiohttp-3.12.14-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4699979560728b168d5ab63c668a093c9570af2c7a78ea24ca5212c6cdc2b641", size = 1827203 }, + { url = "https://files.pythonhosted.org/packages/e0/30/aadcdf71b510a718e3d98a7bfeaea2396ac847f218b7e8edb241b09bd99a/aiohttp-3.12.14-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad5fdf6af93ec6c99bf800eba3af9a43d8bfd66dce920ac905c817ef4a712afe", size = 1729664 }, + { url = "https://files.pythonhosted.org/packages/67/7f/7ccf11756ae498fdedc3d689a0c36ace8fc82f9d52d3517da24adf6e9a74/aiohttp-3.12.14-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4ac76627c0b7ee0e80e871bde0d376a057916cb008a8f3ffc889570a838f5cc7", size = 1666741 }, + { url = "https://files.pythonhosted.org/packages/6b/4d/35ebc170b1856dd020c92376dbfe4297217625ef4004d56587024dc2289c/aiohttp-3.12.14-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:798204af1180885651b77bf03adc903743a86a39c7392c472891649610844635", size = 1715013 }, + { url = "https://files.pythonhosted.org/packages/7b/24/46dc0380146f33e2e4aa088b92374b598f5bdcde1718c77e8d1a0094f1a4/aiohttp-3.12.14-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:4f1205f97de92c37dd71cf2d5bcfb65fdaed3c255d246172cce729a8d849b4da", size = 1710172 }, + { url = "https://files.pythonhosted.org/packages/2f/0a/46599d7d19b64f4d0fe1b57bdf96a9a40b5c125f0ae0d8899bc22e91fdce/aiohttp-3.12.14-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:76ae6f1dd041f85065d9df77c6bc9c9703da9b5c018479d20262acc3df97d419", size = 1690355 }, + { url = "https://files.pythonhosted.org/packages/08/86/b21b682e33d5ca317ef96bd21294984f72379454e689d7da584df1512a19/aiohttp-3.12.14-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a194ace7bc43ce765338ca2dfb5661489317db216ea7ea700b0332878b392cab", size = 1783958 }, + { url = "https://files.pythonhosted.org/packages/4f/45/f639482530b1396c365f23c5e3b1ae51c9bc02ba2b2248ca0c855a730059/aiohttp-3.12.14-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:16260e8e03744a6fe3fcb05259eeab8e08342c4c33decf96a9dad9f1187275d0", size = 1804423 }, + { url = "https://files.pythonhosted.org/packages/7e/e5/39635a9e06eed1d73671bd4079a3caf9cf09a49df08490686f45a710b80e/aiohttp-3.12.14-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:8c779e5ebbf0e2e15334ea404fcce54009dc069210164a244d2eac8352a44b28", size = 1717479 }, + { url = "https://files.pythonhosted.org/packages/51/e1/7f1c77515d369b7419c5b501196526dad3e72800946c0099594c1f0c20b4/aiohttp-3.12.14-cp311-cp311-win32.whl", hash = "sha256:a289f50bf1bd5be227376c067927f78079a7bdeccf8daa6a9e65c38bae14324b", size = 427907 }, + { url = "https://files.pythonhosted.org/packages/06/24/a6bf915c85b7a5b07beba3d42b3282936b51e4578b64a51e8e875643c276/aiohttp-3.12.14-cp311-cp311-win_amd64.whl", hash = "sha256:0b8a69acaf06b17e9c54151a6c956339cf46db4ff72b3ac28516d0f7068f4ced", size = 452334 }, + { url = "https://files.pythonhosted.org/packages/c3/0d/29026524e9336e33d9767a1e593ae2b24c2b8b09af7c2bd8193762f76b3e/aiohttp-3.12.14-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:a0ecbb32fc3e69bc25efcda7d28d38e987d007096cbbeed04f14a6662d0eee22", size = 701055 }, + { url = "https://files.pythonhosted.org/packages/0a/b8/a5e8e583e6c8c1056f4b012b50a03c77a669c2e9bf012b7cf33d6bc4b141/aiohttp-3.12.14-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:0400f0ca9bb3e0b02f6466421f253797f6384e9845820c8b05e976398ac1d81a", size = 475670 }, + { url = "https://files.pythonhosted.org/packages/29/e8/5202890c9e81a4ec2c2808dd90ffe024952e72c061729e1d49917677952f/aiohttp-3.12.14-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a56809fed4c8a830b5cae18454b7464e1529dbf66f71c4772e3cfa9cbec0a1ff", size = 468513 }, + { url = "https://files.pythonhosted.org/packages/23/e5/d11db8c23d8923d3484a27468a40737d50f05b05eebbb6288bafcb467356/aiohttp-3.12.14-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:27f2e373276e4755691a963e5d11756d093e346119f0627c2d6518208483fb6d", size = 1715309 }, + { url = "https://files.pythonhosted.org/packages/53/44/af6879ca0eff7a16b1b650b7ea4a827301737a350a464239e58aa7c387ef/aiohttp-3.12.14-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:ca39e433630e9a16281125ef57ece6817afd1d54c9f1bf32e901f38f16035869", size = 1697961 }, + { url = "https://files.pythonhosted.org/packages/bb/94/18457f043399e1ec0e59ad8674c0372f925363059c276a45a1459e17f423/aiohttp-3.12.14-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9c748b3f8b14c77720132b2510a7d9907a03c20ba80f469e58d5dfd90c079a1c", size = 1753055 }, + { url = "https://files.pythonhosted.org/packages/26/d9/1d3744dc588fafb50ff8a6226d58f484a2242b5dd93d8038882f55474d41/aiohttp-3.12.14-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f0a568abe1b15ce69d4cc37e23020720423f0728e3cb1f9bcd3f53420ec3bfe7", size = 1799211 }, + { url = "https://files.pythonhosted.org/packages/73/12/2530fb2b08773f717ab2d249ca7a982ac66e32187c62d49e2c86c9bba9b4/aiohttp-3.12.14-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9888e60c2c54eaf56704b17feb558c7ed6b7439bca1e07d4818ab878f2083660", size = 1718649 }, + { url = "https://files.pythonhosted.org/packages/b9/34/8d6015a729f6571341a311061b578e8b8072ea3656b3d72329fa0faa2c7c/aiohttp-3.12.14-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3006a1dc579b9156de01e7916d38c63dc1ea0679b14627a37edf6151bc530088", size = 1634452 }, + { url = "https://files.pythonhosted.org/packages/ff/4b/08b83ea02595a582447aeb0c1986792d0de35fe7a22fb2125d65091cbaf3/aiohttp-3.12.14-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:aa8ec5c15ab80e5501a26719eb48a55f3c567da45c6ea5bb78c52c036b2655c7", size = 1695511 }, + { url = "https://files.pythonhosted.org/packages/b5/66/9c7c31037a063eec13ecf1976185c65d1394ded4a5120dd5965e3473cb21/aiohttp-3.12.14-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:39b94e50959aa07844c7fe2206b9f75d63cc3ad1c648aaa755aa257f6f2498a9", size = 1716967 }, + { url = "https://files.pythonhosted.org/packages/ba/02/84406e0ad1acb0fb61fd617651ab6de760b2d6a31700904bc0b33bd0894d/aiohttp-3.12.14-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:04c11907492f416dad9885d503fbfc5dcb6768d90cad8639a771922d584609d3", size = 1657620 }, + { url = "https://files.pythonhosted.org/packages/07/53/da018f4013a7a179017b9a274b46b9a12cbeb387570f116964f498a6f211/aiohttp-3.12.14-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:88167bd9ab69bb46cee91bd9761db6dfd45b6e76a0438c7e884c3f8160ff21eb", size = 1737179 }, + { url = "https://files.pythonhosted.org/packages/49/e8/ca01c5ccfeaafb026d85fa4f43ceb23eb80ea9c1385688db0ef322c751e9/aiohttp-3.12.14-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:791504763f25e8f9f251e4688195e8b455f8820274320204f7eafc467e609425", size = 1765156 }, + { url = "https://files.pythonhosted.org/packages/22/32/5501ab525a47ba23c20613e568174d6c63aa09e2caa22cded5c6ea8e3ada/aiohttp-3.12.14-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2785b112346e435dd3a1a67f67713a3fe692d288542f1347ad255683f066d8e0", size = 1724766 }, + { url = "https://files.pythonhosted.org/packages/06/af/28e24574801fcf1657945347ee10df3892311c2829b41232be6089e461e7/aiohttp-3.12.14-cp312-cp312-win32.whl", hash = "sha256:15f5f4792c9c999a31d8decf444e79fcfd98497bf98e94284bf390a7bb8c1729", size = 422641 }, + { url = "https://files.pythonhosted.org/packages/98/d5/7ac2464aebd2eecac38dbe96148c9eb487679c512449ba5215d233755582/aiohttp-3.12.14-cp312-cp312-win_amd64.whl", hash = "sha256:3b66e1a182879f579b105a80d5c4bd448b91a57e8933564bf41665064796a338", size = 449316 }, + { url = "https://files.pythonhosted.org/packages/06/48/e0d2fa8ac778008071e7b79b93ab31ef14ab88804d7ba71b5c964a7c844e/aiohttp-3.12.14-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:3143a7893d94dc82bc409f7308bc10d60285a3cd831a68faf1aa0836c5c3c767", size = 695471 }, + { url = "https://files.pythonhosted.org/packages/8d/e7/f73206afa33100804f790b71092888f47df65fd9a4cd0e6800d7c6826441/aiohttp-3.12.14-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:3d62ac3d506cef54b355bd34c2a7c230eb693880001dfcda0bf88b38f5d7af7e", size = 473128 }, + { url = "https://files.pythonhosted.org/packages/df/e2/4dd00180be551a6e7ee979c20fc7c32727f4889ee3fd5b0586e0d47f30e1/aiohttp-3.12.14-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:48e43e075c6a438937c4de48ec30fa8ad8e6dfef122a038847456bfe7b947b63", size = 465426 }, + { url = "https://files.pythonhosted.org/packages/de/dd/525ed198a0bb674a323e93e4d928443a680860802c44fa7922d39436b48b/aiohttp-3.12.14-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:077b4488411a9724cecc436cbc8c133e0d61e694995b8de51aaf351c7578949d", size = 1704252 }, + { url = "https://files.pythonhosted.org/packages/d8/b1/01e542aed560a968f692ab4fc4323286e8bc4daae83348cd63588e4f33e3/aiohttp-3.12.14-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d8c35632575653f297dcbc9546305b2c1133391089ab925a6a3706dfa775ccab", size = 1685514 }, + { url = "https://files.pythonhosted.org/packages/b3/06/93669694dc5fdabdc01338791e70452d60ce21ea0946a878715688d5a191/aiohttp-3.12.14-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6b8ce87963f0035c6834b28f061df90cf525ff7c9b6283a8ac23acee6502afd4", size = 1737586 }, + { url = "https://files.pythonhosted.org/packages/a5/3a/18991048ffc1407ca51efb49ba8bcc1645961f97f563a6c480cdf0286310/aiohttp-3.12.14-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f0a2cf66e32a2563bb0766eb24eae7e9a269ac0dc48db0aae90b575dc9583026", size = 1786958 }, + { url = "https://files.pythonhosted.org/packages/30/a8/81e237f89a32029f9b4a805af6dffc378f8459c7b9942712c809ff9e76e5/aiohttp-3.12.14-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdea089caf6d5cde975084a884c72d901e36ef9c2fd972c9f51efbbc64e96fbd", size = 1709287 }, + { url = "https://files.pythonhosted.org/packages/8c/e3/bd67a11b0fe7fc12c6030473afd9e44223d456f500f7cf526dbaa259ae46/aiohttp-3.12.14-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8a7865f27db67d49e81d463da64a59365ebd6b826e0e4847aa111056dcb9dc88", size = 1622990 }, + { url = "https://files.pythonhosted.org/packages/83/ba/e0cc8e0f0d9ce0904e3cf2d6fa41904e379e718a013c721b781d53dcbcca/aiohttp-3.12.14-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0ab5b38a6a39781d77713ad930cb5e7feea6f253de656a5f9f281a8f5931b086", size = 1676015 }, + { url = "https://files.pythonhosted.org/packages/d8/b3/1e6c960520bda094c48b56de29a3d978254637ace7168dd97ddc273d0d6c/aiohttp-3.12.14-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:9b3b15acee5c17e8848d90a4ebc27853f37077ba6aec4d8cb4dbbea56d156933", size = 1707678 }, + { url = "https://files.pythonhosted.org/packages/0a/19/929a3eb8c35b7f9f076a462eaa9830b32c7f27d3395397665caa5e975614/aiohttp-3.12.14-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:e4c972b0bdaac167c1e53e16a16101b17c6d0ed7eac178e653a07b9f7fad7151", size = 1650274 }, + { url = "https://files.pythonhosted.org/packages/22/e5/81682a6f20dd1b18ce3d747de8eba11cbef9b270f567426ff7880b096b48/aiohttp-3.12.14-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:7442488b0039257a3bdbc55f7209587911f143fca11df9869578db6c26feeeb8", size = 1726408 }, + { url = "https://files.pythonhosted.org/packages/8c/17/884938dffaa4048302985483f77dfce5ac18339aad9b04ad4aaa5e32b028/aiohttp-3.12.14-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:f68d3067eecb64c5e9bab4a26aa11bd676f4c70eea9ef6536b0a4e490639add3", size = 1759879 }, + { url = "https://files.pythonhosted.org/packages/95/78/53b081980f50b5cf874359bde707a6eacd6c4be3f5f5c93937e48c9d0025/aiohttp-3.12.14-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f88d3704c8b3d598a08ad17d06006cb1ca52a1182291f04979e305c8be6c9758", size = 1708770 }, + { url = "https://files.pythonhosted.org/packages/ed/91/228eeddb008ecbe3ffa6c77b440597fdf640307162f0c6488e72c5a2d112/aiohttp-3.12.14-cp313-cp313-win32.whl", hash = "sha256:a3c99ab19c7bf375c4ae3debd91ca5d394b98b6089a03231d4c580ef3c2ae4c5", size = 421688 }, + { url = "https://files.pythonhosted.org/packages/66/5f/8427618903343402fdafe2850738f735fd1d9409d2a8f9bcaae5e630d3ba/aiohttp-3.12.14-cp313-cp313-win_amd64.whl", hash = "sha256:3f8aad695e12edc9d571f878c62bedc91adf30c760c8632f09663e5f564f4baa", size = 448098 }, ] [[package]] name = "aiosignal" -version = "1.3.2" +version = "1.4.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "frozenlist" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ba/b5/6d55e80f6d8a08ce22b982eafa278d823b541c925f11ee774b0b9c43473d/aiosignal-1.3.2.tar.gz", hash = "sha256:a8c255c66fafb1e499c9351d0bf32ff2d8a0321595ebac3b93713656d2436f54", size = 19424 } +sdist = { url = "https://files.pythonhosted.org/packages/61/62/06741b579156360248d1ec624842ad0edf697050bbaf7c3e46394e106ad1/aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7", size = 25007 } wheels = [ - { url = "https://files.pythonhosted.org/packages/ec/6a/bc7e17a3e87a2985d3e8f4da4cd0f481060eb78fb08596c42be62c90a4d9/aiosignal-1.3.2-py2.py3-none-any.whl", hash = "sha256:45cde58e409a301715980c2b01d0c28bdde3770d8290b5eb2173759d9acb31a5", size = 7597 }, + { url = "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", size = 7490 }, ] [[package]] name = "alembic" -version = "1.16.1" +version = "1.16.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mako" }, @@ -126,9 +128,9 @@ dependencies = [ { name = "tomli", marker = "python_full_version < '3.11'" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/20/89/bfb4fe86e3fc3972d35431af7bedbc60fa606e8b17196704a1747f7aa4c3/alembic-1.16.1.tar.gz", hash = "sha256:43d37ba24b3d17bc1eb1024fe0f51cd1dc95aeb5464594a02c6bb9ca9864bfa4", size = 1955006 } +sdist = { url = "https://files.pythonhosted.org/packages/83/52/72e791b75c6b1efa803e491f7cbab78e963695e76d4ada05385252927e76/alembic-1.16.4.tar.gz", hash = "sha256:efab6ada0dd0fae2c92060800e0bf5c1dc26af15a10e02fb4babff164b4725e2", size = 1968161 } wheels = [ - { url = "https://files.pythonhosted.org/packages/31/59/565286efff3692c5716c212202af61466480f6357c4ae3089d4453bff1f3/alembic-1.16.1-py3-none-any.whl", hash = "sha256:0cdd48acada30d93aa1035767d67dff25702f8de74d7c3919f2e8492c8db2e67", size = 242488 }, + { url = "https://files.pythonhosted.org/packages/c2/62/96b5217b742805236614f05904541000f55422a6060a90d7fd4ce26c172d/alembic-1.16.4-py3-none-any.whl", hash = "sha256:b05e51e8e82efc1abd14ba2af6392897e145930c3e0a2faf2b0da2f7f7fd660d", size = 247026 }, ] [[package]] @@ -142,7 +144,7 @@ wheels = [ [[package]] name = "anthropic" -version = "0.54.0" +version = "0.58.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, @@ -153,9 +155,9 @@ dependencies = [ { name = "sniffio" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/89/28/80cb9bb6e7ce77d404145b51da4257455805c17f0a6be528ff3286e3882f/anthropic-0.54.0.tar.gz", hash = "sha256:5e6f997d97ce8e70eac603c3ec2e7f23addeff953fbbb76b19430562bb6ba815", size = 312376 } +sdist = { url = "https://files.pythonhosted.org/packages/95/b9/ab06c586aa5a5e7499017cee5ebab94ee260e75975c45395f32b8592abdd/anthropic-0.58.2.tar.gz", hash = "sha256:86396cc45530a83acea25ae6bca9f86656af81e3d598b4d22a1300e0e4cf8df8", size = 425125 } wheels = [ - { url = "https://files.pythonhosted.org/packages/de/b9/6ffb48e82c5e97b03cecee872d134a6b6666c2767b2d32ed709f3a60a8fe/anthropic-0.54.0-py3-none-any.whl", hash = "sha256:c1062a0a905daeec17ca9c06c401e4b3f24cb0495841d29d752568a1d4018d56", size = 288774 }, + { url = "https://files.pythonhosted.org/packages/d0/f2/68d908ff308c9a65af5749ec31952e01d32f19ea073b0268affc616e6ebc/anthropic-0.58.2-py3-none-any.whl", hash = "sha256:3742181c634c725f337b71096839b6404145e33a8e190c75387c4028b825864d", size = 292896 }, ] [package.optional-dependencies] @@ -190,6 +192,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d0/ae/9a053dd9229c0fde6b1f1f33f609ccff1ee79ddda364c756a924c6d8563b/APScheduler-3.11.0-py3-none-any.whl", hash = "sha256:fc134ca32e50f5eadcc4938e3a4545ab19131435e851abb40b34d63d5141c6da", size = 64004 }, ] +[[package]] +name = "asttokens" +version = "3.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4a/e7/82da0a03e7ba5141f05cce0d302e6eed121ae055e0456ca228bf693984bc/asttokens-3.0.0.tar.gz", hash = "sha256:0dcd8baa8d62b0c1d118b399b2ddba3c4aff271d0d7a9e0d4c1681c79035bbc7", size = 61978 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/25/8a/c46dcc25341b5bce5472c718902eb3d38600a903b14fa6aeecef3f21a46f/asttokens-3.0.0-py3-none-any.whl", hash = "sha256:e3078351a059199dd5138cb1c706e6430c05eff2ff136af5eb4790f9d28932e2", size = 26918 }, +] + [[package]] name = "async-timeout" version = "4.0.3" @@ -263,6 +274,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5d/35/be73b6015511aa0173ec595fc579133b797ad532996f2998fd6b8d1bbe6b/audioop_lts-0.2.1-cp313-cp313t-win_arm64.whl", hash = "sha256:78bfb3703388c780edf900be66e07de5a3d4105ca8e8720c5c4d67927e0b15d0", size = 23918 }, ] +[[package]] +name = "authlib" +version = "1.6.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cryptography" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8e/a1/d8d1c6f8bc922c0b87ae0d933a8ed57be1bef6970894ed79c2852a153cd3/authlib-1.6.1.tar.gz", hash = "sha256:4dffdbb1460ba6ec8c17981a4c67af7d8af131231b5a36a88a1e8c80c111cdfd", size = 159988 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f9/58/cc6a08053f822f98f334d38a27687b69c6655fb05cd74a7a5e70a2aeed95/authlib-1.6.1-py2.py3-none-any.whl", hash = "sha256:e9d2031c34c6309373ab845afc24168fe9e93dc52d252631f52642f21f5ed06e", size = 239299 }, +] + [[package]] name = "backoff" version = "2.2.1" @@ -272,6 +295,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/df/73/b6e24bd22e6720ca8ee9a85a0c4a2971af8497d8f3193fa05390cbd46e09/backoff-2.2.1-py3-none-any.whl", hash = "sha256:63579f9a0628e06278f7e47b7d7d5b6ce20dc65c5e96a6f3ca99a6adca0396e8", size = 15148 }, ] +[[package]] +name = "backports-asyncio-runner" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8e/ff/70dca7d7cb1cbc0edb2c6cc0c38b65cba36cccc491eca64cabd5fe7f8670/backports_asyncio_runner-1.2.0.tar.gz", hash = "sha256:a5aa7b2b7d8f8bfcaa2b57313f70792df84e32a2a746f585213373f900b42162", size = 69893 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/59/76ab57e3fe74484f48a53f8e337171b4a2349e506eabe136d7e01d059086/backports_asyncio_runner-1.2.0-py3-none-any.whl", hash = "sha256:0da0a936a8aeb554eccb426dc55af3ba63bcdc69fa1a600b5bb305413a4477b5", size = 12313 }, +] + [[package]] name = "baml-cli" version = "0.1.0" @@ -362,11 +394,11 @@ wheels = [ [[package]] name = "certifi" -version = "2025.4.26" +version = "2025.7.14" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e8/9e/c05b3920a3b7d20d3d3310465f50348e5b3694f4f88c6daf736eef3024c4/certifi-2025.4.26.tar.gz", hash = "sha256:0a816057ea3cdefcef70270d2c515e4506bbc954f417fa5ade2021213bb8f0c6", size = 160705 } +sdist = { url = "https://files.pythonhosted.org/packages/b3/76/52c535bcebe74590f296d6c77c86dabf761c41980e1347a2422e4aa2ae41/certifi-2025.7.14.tar.gz", hash = "sha256:8ea99dbdfaaf2ba2f9bac77b9249ef62ec5218e7c2b2e903378ed5fccf765995", size = 163981 } wheels = [ - { url = "https://files.pythonhosted.org/packages/4a/7e/3db2bd1b1f9e95f7cddca6d6e75e2f2bd9f51b1246e546d88addca0106bd/certifi-2025.4.26-py3-none-any.whl", hash = "sha256:30350364dfe371162649852c63336a15c70c6510c2ad5015b21c2345311805f3", size = 159618 }, + { url = "https://files.pythonhosted.org/packages/4f/52/34c6cf5bb9285074dc3531c437b3919e825d976fde097a7a73f79e726d03/certifi-2025.7.14-py3-none-any.whl", hash = "sha256:6b31f564a415d79ee77df69d757bb49a5bb53bd9f756cbbe24394ffd6fc1f4b2", size = 162722 }, ] [[package]] @@ -426,6 +458,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/7c/fc/6a8cb64e5f0324877d503c854da15d76c1e50eb722e320b15345c4d0c6de/cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a", size = 182009 }, ] +[[package]] +name = "cfgv" +version = "3.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/11/74/539e56497d9bd1d484fd863dd69cbbfa653cd2aa27abfe35653494d85e94/cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560", size = 7114 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c5/55/51844dd50c4fc7a33b653bfaba4c2456f06955289ca770a5dbd5fd267374/cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9", size = 7249 }, +] + [[package]] name = "chardet" version = "5.2.0" @@ -586,6 +627,22 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ee/58/257350f7db99b4ae12b614a36256d9cc870d71d9e451e79c2dc3b23d7c3c/cssselect-1.3.0-py3-none-any.whl", hash = "sha256:56d1bf3e198080cc1667e137bc51de9cadfca259f03c2d4e09037b3e01e30f0d", size = 18786 }, ] +[[package]] +name = "cyclopts" +version = "3.22.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "docstring-parser", marker = "python_full_version < '4.0'" }, + { name = "rich" }, + { name = "rich-rst" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/cc/2e/8c45ef5b00bd48d7cabbf6f90b7f12df4c232755cd46e6dbc6690f9ac0c5/cyclopts-3.22.2.tar.gz", hash = "sha256:d3495231af6ae86479579777d212ddf77b113200f828badeaf401162ed87227d", size = 74520 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/83/5b/5939e05d87def1612c494429bee705d6b852fad1d21dd2dee1e3ce39997e/cyclopts-3.22.2-py3-none-any.whl", hash = "sha256:6681b0815fa2de2bccc364468fd25b15aa9617cb505c0b16ca62e2b18a57619e", size = 84578 }, +] + [[package]] name = "dataclasses-json" version = "0.6.7" @@ -601,7 +658,7 @@ wheels = [ [[package]] name = "datasets" -version = "3.6.0" +version = "4.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "dill" }, @@ -610,7 +667,7 @@ dependencies = [ { name = "huggingface-hub" }, { name = "multiprocess" }, { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "numpy", version = "2.3.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "packaging" }, { name = "pandas" }, { name = "pyarrow" }, @@ -619,9 +676,18 @@ dependencies = [ { name = "tqdm" }, { name = "xxhash" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/1a/89/d3d6fef58a488f8569c82fd293ab7cbd4250244d67f425dcae64c63800ea/datasets-3.6.0.tar.gz", hash = "sha256:1b2bf43b19776e2787e181cfd329cb0ca1a358ea014780c3581e0f276375e041", size = 569336 } +sdist = { url = "https://files.pythonhosted.org/packages/e3/9d/348ed92110ba5f9b70b51ca1078d4809767a835aa2b7ce7e74ad2b98323d/datasets-4.0.0.tar.gz", hash = "sha256:9657e7140a9050db13443ba21cb5de185af8af944479b00e7ff1e00a61c8dbf1", size = 569566 } wheels = [ - { url = "https://files.pythonhosted.org/packages/20/34/a08b0ee99715eaba118cbe19a71f7b5e2425c2718ef96007c325944a1152/datasets-3.6.0-py3-none-any.whl", hash = "sha256:25000c4a2c0873a710df127d08a202a06eab7bf42441a6bc278b499c2f72cd1b", size = 491546 }, + { url = "https://files.pythonhosted.org/packages/eb/62/eb8157afb21bd229c864521c1ab4fa8e9b4f1b06bafdd8c4668a7a31b5dd/datasets-4.0.0-py3-none-any.whl", hash = "sha256:7ef95e62025fd122882dbce6cb904c8cd3fbc829de6669a5eb939c77d50e203d", size = 494825 }, +] + +[[package]] +name = "decorator" +version = "5.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/43/fa/6d96a0978d19e17b68d634497769987b16c8f4cd0a7a05048bec693caa6b/decorator-5.2.1.tar.gz", hash = "sha256:65f266143752f734b0a7cc83c46f4618af75b8c5911b00ccb61d0ac9b6da0360", size = 56711 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4e/8c/f3147f5c4b73e7550fe5f9352eaa956ae838d5c51eb58e7a25b9f3e2643b/decorator-5.2.1-py3-none-any.whl", hash = "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a", size = 9190 }, ] [[package]] @@ -642,6 +708,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c9/7a/cef76fd8438a42f96db64ddaa85280485a9c395e7df3db8158cfec1eee34/dill-0.3.8-py3-none-any.whl", hash = "sha256:c36ca9ffb54365bdd2f8eb3eff7d2a21237f8452b57ace88b1ac615b7e815bd7", size = 116252 }, ] +[[package]] +name = "distlib" +version = "0.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/96/8e/709914eb2b5749865801041647dc7f4e6d00b549cfe88b65ca192995f07c/distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d", size = 614605 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16", size = 469047 }, +] + [[package]] name = "distro" version = "1.9.0" @@ -660,27 +735,82 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/68/1b/e0a87d256e40e8c888847551b20a017a6b98139178505dc7ffb96f04e954/dnspython-2.7.0-py3-none-any.whl", hash = "sha256:b4c34b7d10b51bcc3a5071e7b8dee77939f1e878477eeecc965e9835f63c6c86", size = 313632 }, ] +[[package]] +name = "docker" +version = "7.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pywin32", marker = "sys_platform == 'win32'" }, + { name = "requests" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/91/9b/4a2ea29aeba62471211598dac5d96825bb49348fa07e906ea930394a83ce/docker-7.1.0.tar.gz", hash = "sha256:ad8c70e6e3f8926cb8a92619b832b4ea5299e2831c14284663184e200546fa6c", size = 117834 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e3/26/57c6fb270950d476074c087527a558ccb6f4436657314bfb6cdf484114c4/docker-7.1.0-py3-none-any.whl", hash = "sha256:c96b93b7f0a746f9e77d325bcfb87422a3d8bd4f03136ae8a85b37f1898d5fc0", size = 147774 }, +] + [[package]] name = "docstring-parser" -version = "0.16" +version = "0.17.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/08/12/9c22a58c0b1e29271051222d8906257616da84135af9ed167c9e28f85cb3/docstring_parser-0.16.tar.gz", hash = "sha256:538beabd0af1e2db0146b6bd3caa526c35a34d61af9fd2887f3a8a27a739aa6e", size = 26565 } +sdist = { url = "https://files.pythonhosted.org/packages/b2/9d/c3b43da9515bd270df0f80548d9944e389870713cc1fe2b8fb35fe2bcefd/docstring_parser-0.17.0.tar.gz", hash = "sha256:583de4a309722b3315439bb31d64ba3eebada841f2e2cee23b99df001434c912", size = 27442 } wheels = [ - { url = "https://files.pythonhosted.org/packages/d5/7c/e9fcff7623954d86bdc17782036cbf715ecab1bec4847c008557affe1ca8/docstring_parser-0.16-py3-none-any.whl", hash = "sha256:bf0a1387354d3691d102edef7ec124f219ef639982d096e26e3b60aeffa90637", size = 36533 }, + { url = "https://files.pythonhosted.org/packages/55/e2/2537ebcff11c1ee1ff17d8d0b6f4db75873e3b0fb32c2d4a2ee31ecb310a/docstring_parser-0.17.0-py3-none-any.whl", hash = "sha256:cf2569abd23dce8099b300f9b4fa8191e9582dda731fd533daf54c4551658708", size = 36896 }, +] + +[[package]] +name = "docutils" +version = "0.22rc5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/41/93/82ea696e7fb15db20d3f41a27633989a9670445129eb5463ea3b51d18cf5/docutils-0.22rc5.tar.gz", hash = "sha256:2f5ccf878d7bfb2bc7feea02b527f0b10e48f44e01f51d9dbb55e7761a308e41", size = 2279312 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/de/9c/0e31e9f650326bfbe483b613071ab9e1b1c2ac48d372e70b465e3241f3ed/docutils-0.22rc5-py3-none-any.whl", hash = "sha256:0285ee30d970430f141dc3d806e1c44decebdf6080364075d071c58c843aeaf5", size = 636361 }, ] [[package]] name = "duckduckgo-search" -version = "8.0.3" +version = "8.1.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, { name = "lxml" }, { name = "primp" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d1/55/7621ee486cf21efac28a62f381417d9ff5c31ec19b34da55ad1bf376596d/duckduckgo_search-8.0.3.tar.gz", hash = "sha256:b56742e1a22b31a692b7cb82ca7ab278f499f5a3817b18078ee37111087bb2eb", size = 21800 } +sdist = { url = "https://files.pythonhosted.org/packages/10/ef/07791a05751e6cc9de1dd49fb12730259ee109b18e6d097e25e6c32d5617/duckduckgo_search-8.1.1.tar.gz", hash = "sha256:9da91c9eb26a17e016ea1da26235d40404b46b0565ea86d75a9f78cc9441f935", size = 22868 } wheels = [ - { url = "https://files.pythonhosted.org/packages/a5/ce/d30315b2c7edc429993c14329f54c040cf12897f38cad79b6fdb00428bd2/duckduckgo_search-8.0.3-py3-none-any.whl", hash = "sha256:e7b70761fa3940032533ffd4735c66884cf0d8426539a3a9272cdfd5dd028b82", size = 18183 }, + { url = "https://files.pythonhosted.org/packages/db/72/c027b3b488b1010cf71670032fcf7e681d44b81829d484bb04e31a949a8d/duckduckgo_search-8.1.1-py3-none-any.whl", hash = "sha256:f48adbb06626ee05918f7e0cef3a45639e9939805c4fc179e68c48a12f1b5062", size = 18932 }, +] + +[[package]] +name = "e2b" +version = "1.2.0b5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "httpcore" }, + { name = "httpx" }, + { name = "packaging" }, + { name = "protobuf" }, + { name = "python-dateutil" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/88/e5/4f185164dd31e2f486ae99266f22c01ea9eeedf0f231ccb7212e1755aa06/e2b-1.2.0b5.tar.gz", hash = "sha256:18b1be73f1ea9b3bfc9588a00db1c1cc193cf9f35b8e73b6abaa81509e538764", size = 52002 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/71/ff/8ffb665d565304fbb6077b04935a5cf8db56592a9abfab79b2237c709394/e2b-1.2.0b5-py3-none-any.whl", hash = "sha256:54f2cd3cf8a630093f39c4e8fcca4e98cd9073af6942eff34a8c5de8cf5f40ee", size = 100140 }, +] + +[[package]] +name = "e2b-code-interpreter" +version = "1.2.0b5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "e2b" }, + { name = "httpx" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/82/ca/a57d3b564cf83b5f9d06e0f9b4654768cfcb32fadc900c1a209e932c1d4d/e2b_code_interpreter-1.2.0b5.tar.gz", hash = "sha256:84d90d57e94dce5e94e522032d8b1b32a6a0d229b23e8b939e82c1894e61d446", size = 9072 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a4/39/5804a3ce4f4e45cb4d8d84382fd8d58038cedceb66d6e6c372d8a99cff90/e2b_code_interpreter-1.2.0b5-py3-none-any.whl", hash = "sha256:085d382546d4a73ffca275ca81bf828e0c6d58958c87b05abce40a31b2ad58ef", size = 12030 }, ] [[package]] @@ -707,25 +837,34 @@ name = "exceptiongroup" version = "1.3.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.11'" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/0b/9f/a65090624ecf468cdca03533906e7c69ed7588582240cfe7cc9e770b50eb/exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88", size = 29749 } wheels = [ { url = "https://files.pythonhosted.org/packages/36/f4/c6e662dade71f56cd2f3735141b265c3c79293c109549c1e6933b0651ffc/exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10", size = 16674 }, ] +[[package]] +name = "executing" +version = "2.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/91/50/a9d80c47ff289c611ff12e63f7c5d13942c65d68125160cefd768c73e6e4/executing-2.2.0.tar.gz", hash = "sha256:5d108c028108fe2551d1a7b2e8b713341e2cb4fc0aa7dcf966fa4327a5226755", size = 978693 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7b/8f/c4d9bafc34ad7ad5d8dc16dd1347ee0e507a52c3adb6bfa8887e1c6a26ba/executing-2.2.0-py2.py3-none-any.whl", hash = "sha256:11387150cad388d62750327a53d3339fad4888b39a6fe233c3afbb54ecffd3aa", size = 26702 }, +] + [[package]] name = "fastapi" -version = "0.115.12" +version = "0.115.14" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pydantic" }, { name = "starlette" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f4/55/ae499352d82338331ca1e28c7f4a63bfd09479b16395dce38cf50a39e2c2/fastapi-0.115.12.tar.gz", hash = "sha256:1e2c2a2646905f9e83d32f04a3f86aff4a286669c6c950ca95b5fd68c2602681", size = 295236 } +sdist = { url = "https://files.pythonhosted.org/packages/ca/53/8c38a874844a8b0fa10dd8adf3836ac154082cf88d3f22b544e9ceea0a15/fastapi-0.115.14.tar.gz", hash = "sha256:b1de15cdc1c499a4da47914db35d0e4ef8f1ce62b624e94e0e5824421df99739", size = 296263 } wheels = [ - { url = "https://files.pythonhosted.org/packages/50/b3/b51f09c2ba432a576fe63758bddc81f78f0c6309d9e5c10d194313bf021e/fastapi-0.115.12-py3-none-any.whl", hash = "sha256:e94613d6c05e27be7ffebdd6ea5f388112e5e430c8f7d6494a9d1d88d43e814d", size = 95164 }, + { url = "https://files.pythonhosted.org/packages/53/50/b1222562c6d270fea83e9c9075b8e8600b8479150a18e4516a6138b980d1/fastapi-0.115.14-py3-none-any.whl", hash = "sha256:6c0c8bf9420bd58f565e585036d971872472b4f7d3f6c73b698e10cffdefb3ca", size = 95514 }, ] [[package]] @@ -743,6 +882,27 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/72/84/df15745ff06c1b44e478b72759d5cf48e4583e221389d4cdea76c472dd1c/fastapi_sso-0.16.0-py3-none-any.whl", hash = "sha256:3a66a942474ef9756d3a9d8b945d55bd9faf99781facdb9b87a40b73d6d6b0c3", size = 23942 }, ] +[[package]] +name = "fastmcp" +version = "2.10.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "authlib" }, + { name = "cyclopts" }, + { name = "exceptiongroup" }, + { name = "httpx" }, + { name = "mcp" }, + { name = "openapi-pydantic" }, + { name = "pydantic", extra = ["email"] }, + { name = "pyperclip" }, + { name = "python-dotenv" }, + { name = "rich" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/00/a0/eceb88277ef9e3a442e099377a9b9c29fb2fa724e234486e03a44ca1c677/fastmcp-2.10.6.tar.gz", hash = "sha256:5a7b3301f9f1b64610430caef743ac70175c4b812e1949f037e4db65b0a42c5a", size = 1640538 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dc/05/4958cccbe862958d862b6a15f2d10d2f5ec3c411268dcb131a433e5e7a0d/fastmcp-2.10.6-py3-none-any.whl", hash = "sha256:9782416a8848cc0f4cfcc578e5c17834da620bef8ecf4d0daabf5dd1272411a2", size = 202613 }, +] + [[package]] name = "filelock" version = "3.18.0" @@ -874,7 +1034,7 @@ wheels = [ [[package]] name = "google-api-core" -version = "2.25.0" +version = "2.25.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "google-auth" }, @@ -883,9 +1043,9 @@ dependencies = [ { name = "protobuf" }, { name = "requests" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/98/a2/8176b416ca08106b2ae30cd4a006c8176945f682c3a5b42f141c9173f505/google_api_core-2.25.0.tar.gz", hash = "sha256:9b548e688702f82a34ed8409fb8a6961166f0b7795032f0be8f48308dff4333a", size = 164914 } +sdist = { url = "https://files.pythonhosted.org/packages/dc/21/e9d043e88222317afdbdb567165fdbc3b0aad90064c7e0c9eb0ad9955ad8/google_api_core-2.25.1.tar.gz", hash = "sha256:d2aaa0b13c78c61cb3f4282c464c046e45fbd75755683c9c525e6e8f7ed0a5e8", size = 165443 } wheels = [ - { url = "https://files.pythonhosted.org/packages/ac/ca/149e41a277bb0855e8ded85fd7579d7747c1223e253d82c5c0f1be236875/google_api_core-2.25.0-py3-none-any.whl", hash = "sha256:1db79d1281dcf9f3d10023283299ba38f3dc9f639ec41085968fd23e5bcf512e", size = 160668 }, + { url = "https://files.pythonhosted.org/packages/14/4b/ead00905132820b623732b175d66354e9d3e69fcf2a5dcdab780664e7896/google_api_core-2.25.1-py3-none-any.whl", hash = "sha256:8a2a56c1fef82987a524371f99f3bd0143702fecc670c72e600c1cda6bf8dbb7", size = 160807 }, ] [package.optional-dependencies] @@ -915,7 +1075,7 @@ requests = [ [[package]] name = "google-cloud-aiplatform" -version = "1.97.0" +version = "1.104.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "docstring-parser" }, @@ -932,14 +1092,14 @@ dependencies = [ { name = "shapely" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9b/ea/38224d2972e16c82ee16c13407e647586e25671bd2f75d4455491c678c92/google_cloud_aiplatform-1.97.0.tar.gz", hash = "sha256:01277ac5648abe7d2af688b123d7d050c1a34922e9f4297e51e44d165cb79b45", size = 9229557 } +sdist = { url = "https://files.pythonhosted.org/packages/94/ec/5e77f14d9b985e894c04e835d08c229647af578034a4864d6f70f78168bd/google_cloud_aiplatform-1.104.0.tar.gz", hash = "sha256:e6a4bdd1dba48610a367c4a8ff0f91504359a217a4944f09f32dfb2c44ec4871", size = 9460064 } wheels = [ - { url = "https://files.pythonhosted.org/packages/9a/b8/f9ca10a648bc2596e904c30270c49e72528e2b3b583d886eeeec5080b27d/google_cloud_aiplatform-1.97.0-py2.py3-none-any.whl", hash = "sha256:4db9455308110b1e8c1b587bd3ff34449fa459fda45c4466b9b2d9ae259a7af6", size = 7687924 }, + { url = "https://files.pythonhosted.org/packages/48/4a/152164523b02ced6a9aadabe515f1734ae1f8d4b64fa18806f61b001128c/google_cloud_aiplatform-1.104.0-py2.py3-none-any.whl", hash = "sha256:066fd56d24a55295c02d48a6b848ff0e2dd62800492704030b615a68334b56a1", size = 7861210 }, ] [[package]] name = "google-cloud-bigquery" -version = "3.34.0" +version = "3.35.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "google-api-core", extra = ["grpc"] }, @@ -950,9 +1110,9 @@ dependencies = [ { name = "python-dateutil" }, { name = "requests" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/24/f9/e9da2d56d7028f05c0e2f5edf6ce43c773220c3172666c3dd925791d763d/google_cloud_bigquery-3.34.0.tar.gz", hash = "sha256:5ee1a78ba5c2ccb9f9a8b2bf3ed76b378ea68f49b6cac0544dc55cc97ff7c1ce", size = 489091 } +sdist = { url = "https://files.pythonhosted.org/packages/15/ee/fc5e651899abd7b7c631afc270fc668c4d757d27403c8ec2c11f0588f226/google_cloud_bigquery-3.35.0.tar.gz", hash = "sha256:b3db627355303ac52e07548d448d6c6cb87e52d80c88e57599cdd64185f40664", size = 496456 } wheels = [ - { url = "https://files.pythonhosted.org/packages/b1/7e/7115c4f67ca0bc678f25bff1eab56cc37d06eb9a3978940b2ebd0705aa0a/google_cloud_bigquery-3.34.0-py3-none-any.whl", hash = "sha256:de20ded0680f8136d92ff5256270b5920dfe4fae479f5d0f73e90e5df30b1cf7", size = 253555 }, + { url = "https://files.pythonhosted.org/packages/95/2c/663be60fe7c4090d84267a17204fceaa4efd541000325d4f9690f6c6fcdc/google_cloud_bigquery-3.35.0-py3-none-any.whl", hash = "sha256:8c98e304d47c82f1fbba77b2f4c1e6c458474842d713ee117d9c58e61b74a70d", size = 256874 }, ] [[package]] @@ -1038,7 +1198,7 @@ wheels = [ [[package]] name = "google-genai" -version = "1.19.0" +version = "1.20.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, @@ -1049,9 +1209,9 @@ dependencies = [ { name = "typing-extensions" }, { name = "websockets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/14/17/8f717f43732ae2b7775f816f0d8f0b39e2a020bbe7ba202f2ddb2f948c3b/google_genai-1.19.0.tar.gz", hash = "sha256:66f5de78075781bfd9e423f1e3592e4240759dfe0ac42ac74a9dcb2c4f662e9d", size = 198000 } +sdist = { url = "https://files.pythonhosted.org/packages/19/12/ad9f08be2ca85122ca50ac69ae70454f18a3c7d840bcc4ed43f517ab47be/google_genai-1.20.0.tar.gz", hash = "sha256:dccca78f765233844b1bd4f1f7a2237d9a76fe6038cf9aa72c0cd991e3c107b5", size = 201550 } wheels = [ - { url = "https://files.pythonhosted.org/packages/c4/ae/64fccdebf5811453ce53b0d5ee23d4f27ef173ef36d3b67dad791a0007aa/google_genai-1.19.0-py3-none-any.whl", hash = "sha256:a2955612e4af8c84f83eb43c1ce4e74e1b714732926d0705e639761938192466", size = 200043 }, + { url = "https://files.pythonhosted.org/packages/b9/b4/08f3ea414060a7e7d4436c08bb22d03dabef74cc05ef13ef8cd846156d5b/google_genai-1.20.0-py3-none-any.whl", hash = "sha256:ccd61d6ebcb14f5c778b817b8010e3955ae4f6ddfeaabf65f42f6d5e3e5a8125", size = 203039 }, ] [[package]] @@ -1150,64 +1310,64 @@ wheels = [ [[package]] name = "grpcio" -version = "1.73.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8e/7b/ca3f561aeecf0c846d15e1b38921a60dffffd5d4113931198fbf455334ee/grpcio-1.73.0.tar.gz", hash = "sha256:3af4c30918a7f0d39de500d11255f8d9da4f30e94a2033e70fe2a720e184bd8e", size = 12786424 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b0/44/5ca479c880b9f56c9a9502873ea500c09d1087dc868217a90724c24d83d0/grpcio-1.73.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:d050197eeed50f858ef6c51ab09514856f957dba7b1f7812698260fc9cc417f6", size = 5365135 }, - { url = "https://files.pythonhosted.org/packages/8d/b7/78ff355cdb602ab01ea437d316846847e0c1f7d109596e5409402cc13156/grpcio-1.73.0-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:ebb8d5f4b0200916fb292a964a4d41210de92aba9007e33d8551d85800ea16cb", size = 10609627 }, - { url = "https://files.pythonhosted.org/packages/8d/92/5111235062b9da0e3010e5fd2bdceb766113fcf60520f9c23eb651089dd7/grpcio-1.73.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:c0811331b469e3f15dda5f90ab71bcd9681189a83944fd6dc908e2c9249041ef", size = 5803418 }, - { url = "https://files.pythonhosted.org/packages/76/fa/dbf3fca0b91fa044f1114b11adc3d4ccc18ab1ac278daa69d450fd9aaef2/grpcio-1.73.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:12787c791c3993d0ea1cc8bf90393647e9a586066b3b322949365d2772ba965b", size = 6444741 }, - { url = "https://files.pythonhosted.org/packages/44/e1/e7c830c1a29abd13f0e7e861c8db57a67db5cb8a1edc6b9d9cd44c26a1e5/grpcio-1.73.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c17771e884fddf152f2a0df12478e8d02853e5b602a10a9a9f1f52fa02b1d32", size = 6040755 }, - { url = "https://files.pythonhosted.org/packages/b4/57/2eaccbfdd8298ab6bb4504600a4283260983a9db7378eb79c922fd559883/grpcio-1.73.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:275e23d4c428c26b51857bbd95fcb8e528783597207ec592571e4372b300a29f", size = 6132216 }, - { url = "https://files.pythonhosted.org/packages/81/a4/1bd2c59d7426ab640b121f42acb820ff7cd5c561d03e9c9164cb8431128e/grpcio-1.73.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:9ffc972b530bf73ef0f948f799482a1bf12d9b6f33406a8e6387c0ca2098a833", size = 6774779 }, - { url = "https://files.pythonhosted.org/packages/c6/64/70ee85055b4107acbe1af6a99ef6885e34db89083e53e5c27b8442e3aa38/grpcio-1.73.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ebd8d269df64aff092b2cec5e015d8ae09c7e90888b5c35c24fdca719a2c9f35", size = 6304223 }, - { url = "https://files.pythonhosted.org/packages/06/02/4b3c373edccf29205205a6d329a267b9337ecbbf658bc70f0a18d63d0a50/grpcio-1.73.0-cp310-cp310-win32.whl", hash = "sha256:072d8154b8f74300ed362c01d54af8b93200c1a9077aeaea79828d48598514f1", size = 3679738 }, - { url = "https://files.pythonhosted.org/packages/30/7a/d6dab939cda2129e39a872ad48f61c9951567dcda8ab419b8de446315a68/grpcio-1.73.0-cp310-cp310-win_amd64.whl", hash = "sha256:ce953d9d2100e1078a76a9dc2b7338d5415924dc59c69a15bf6e734db8a0f1ca", size = 4340441 }, - { url = "https://files.pythonhosted.org/packages/dd/31/9de81fd12f7b27e6af403531b7249d76f743d58e0654e624b3df26a43ce2/grpcio-1.73.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:51036f641f171eebe5fa7aaca5abbd6150f0c338dab3a58f9111354240fe36ec", size = 5363773 }, - { url = "https://files.pythonhosted.org/packages/32/9e/2cb78be357a7f1fc4942b81468ef3c7e5fd3df3ac010540459c10895a57b/grpcio-1.73.0-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:d12bbb88381ea00bdd92c55aff3da3391fd85bc902c41275c8447b86f036ce0f", size = 10621912 }, - { url = "https://files.pythonhosted.org/packages/59/2f/b43954811a2e218a2761c0813800773ac0ca187b94fd2b8494e8ef232dc8/grpcio-1.73.0-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:483c507c2328ed0e01bc1adb13d1eada05cc737ec301d8e5a8f4a90f387f1790", size = 5807985 }, - { url = "https://files.pythonhosted.org/packages/1b/bf/68e9f47e7ee349ffee712dcd907ee66826cf044f0dec7ab517421e56e857/grpcio-1.73.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c201a34aa960c962d0ce23fe5f423f97e9d4b518ad605eae6d0a82171809caaa", size = 6448218 }, - { url = "https://files.pythonhosted.org/packages/af/dd/38ae43dd58480d609350cf1411fdac5c2ebb243e2c770f6f7aa3773d5e29/grpcio-1.73.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:859f70c8e435e8e1fa060e04297c6818ffc81ca9ebd4940e180490958229a45a", size = 6044343 }, - { url = "https://files.pythonhosted.org/packages/93/44/b6770b55071adb86481f36dae87d332fcad883b7f560bba9a940394ba018/grpcio-1.73.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e2459a27c6886e7e687e4e407778425f3c6a971fa17a16420227bda39574d64b", size = 6135858 }, - { url = "https://files.pythonhosted.org/packages/d3/9f/63de49fcef436932fcf0ffb978101a95c83c177058dbfb56dbf30ab81659/grpcio-1.73.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:e0084d4559ee3dbdcce9395e1bc90fdd0262529b32c417a39ecbc18da8074ac7", size = 6775806 }, - { url = "https://files.pythonhosted.org/packages/4d/67/c11f1953469162e958f09690ec3a9be3fdb29dea7f5661362a664f9d609a/grpcio-1.73.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ef5fff73d5f724755693a464d444ee0a448c6cdfd3c1616a9223f736c622617d", size = 6308413 }, - { url = "https://files.pythonhosted.org/packages/ba/6a/9dd04426337db07f28bd51a986b7a038ba56912c81b5bb1083c17dd63404/grpcio-1.73.0-cp311-cp311-win32.whl", hash = "sha256:965a16b71a8eeef91fc4df1dc40dc39c344887249174053814f8a8e18449c4c3", size = 3678972 }, - { url = "https://files.pythonhosted.org/packages/04/8b/8c0a8a4fdc2e7977d325eafc587c9cf468039693ac23ad707153231d3cb2/grpcio-1.73.0-cp311-cp311-win_amd64.whl", hash = "sha256:b71a7b4483d1f753bbc11089ff0f6fa63b49c97a9cc20552cded3fcad466d23b", size = 4342967 }, - { url = "https://files.pythonhosted.org/packages/9d/4d/e938f3a0e51a47f2ce7e55f12f19f316e7074770d56a7c2765e782ec76bc/grpcio-1.73.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:fb9d7c27089d9ba3746f18d2109eb530ef2a37452d2ff50f5a6696cd39167d3b", size = 5334911 }, - { url = "https://files.pythonhosted.org/packages/13/56/f09c72c43aa8d6f15a71f2c63ebdfac9cf9314363dea2598dc501d8370db/grpcio-1.73.0-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:128ba2ebdac41e41554d492b82c34586a90ebd0766f8ebd72160c0e3a57b9155", size = 10601460 }, - { url = "https://files.pythonhosted.org/packages/20/e3/85496edc81e41b3c44ebefffc7bce133bb531120066877df0f910eabfa19/grpcio-1.73.0-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:068ecc415f79408d57a7f146f54cdf9f0acb4b301a52a9e563973dc981e82f3d", size = 5759191 }, - { url = "https://files.pythonhosted.org/packages/88/cc/fef74270a6d29f35ad744bfd8e6c05183f35074ff34c655a2c80f3b422b2/grpcio-1.73.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ddc1cfb2240f84d35d559ade18f69dcd4257dbaa5ba0de1a565d903aaab2968", size = 6409961 }, - { url = "https://files.pythonhosted.org/packages/b0/e6/13cfea15e3b8f79c4ae7b676cb21fab70978b0fde1e1d28bb0e073291290/grpcio-1.73.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e53007f70d9783f53b41b4cf38ed39a8e348011437e4c287eee7dd1d39d54b2f", size = 6003948 }, - { url = "https://files.pythonhosted.org/packages/c2/ed/b1a36dad4cc0dbf1f83f6d7b58825fefd5cc9ff3a5036e46091335649473/grpcio-1.73.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:4dd8d8d092efede7d6f48d695ba2592046acd04ccf421436dd7ed52677a9ad29", size = 6103788 }, - { url = "https://files.pythonhosted.org/packages/e7/c8/d381433d3d46d10f6858126d2d2245ef329e30f3752ce4514c93b95ca6fc/grpcio-1.73.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:70176093d0a95b44d24baa9c034bb67bfe2b6b5f7ebc2836f4093c97010e17fd", size = 6749508 }, - { url = "https://files.pythonhosted.org/packages/87/0a/ff0c31dbd15e63b34320efafac647270aa88c31aa19ff01154a73dc7ce86/grpcio-1.73.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:085ebe876373ca095e24ced95c8f440495ed0b574c491f7f4f714ff794bbcd10", size = 6284342 }, - { url = "https://files.pythonhosted.org/packages/fd/73/f762430c0ba867403b9d6e463afe026bf019bd9206eee753785239719273/grpcio-1.73.0-cp312-cp312-win32.whl", hash = "sha256:cfc556c1d6aef02c727ec7d0016827a73bfe67193e47c546f7cadd3ee6bf1a60", size = 3669319 }, - { url = "https://files.pythonhosted.org/packages/10/8b/3411609376b2830449cf416f457ad9d2aacb7f562e1b90fdd8bdedf26d63/grpcio-1.73.0-cp312-cp312-win_amd64.whl", hash = "sha256:bbf45d59d090bf69f1e4e1594832aaf40aa84b31659af3c5e2c3f6a35202791a", size = 4335596 }, - { url = "https://files.pythonhosted.org/packages/60/da/6f3f7a78e5455c4cbe87c85063cc6da05d65d25264f9d4aed800ece46294/grpcio-1.73.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:da1d677018ef423202aca6d73a8d3b2cb245699eb7f50eb5f74cae15a8e1f724", size = 5335867 }, - { url = "https://files.pythonhosted.org/packages/53/14/7d1f2526b98b9658d7be0bb163fd78d681587de6709d8b0c74b4b481b013/grpcio-1.73.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:36bf93f6a657f37c131d9dd2c391b867abf1426a86727c3575393e9e11dadb0d", size = 10595587 }, - { url = "https://files.pythonhosted.org/packages/02/24/a293c398ae44e741da1ed4b29638edbb002258797b07a783f65506165b4c/grpcio-1.73.0-cp313-cp313-manylinux_2_17_aarch64.whl", hash = "sha256:d84000367508ade791d90c2bafbd905574b5ced8056397027a77a215d601ba15", size = 5765793 }, - { url = "https://files.pythonhosted.org/packages/e1/24/d84dbd0b5bf36fb44922798d525a85cefa2ffee7b7110e61406e9750ed15/grpcio-1.73.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c98ba1d928a178ce33f3425ff823318040a2b7ef875d30a0073565e5ceb058d9", size = 6415494 }, - { url = "https://files.pythonhosted.org/packages/5e/85/c80dc65aed8e9dce3d54688864bac45331d9c7600985541f18bd5cb301d4/grpcio-1.73.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a73c72922dfd30b396a5f25bb3a4590195ee45ecde7ee068acb0892d2900cf07", size = 6007279 }, - { url = "https://files.pythonhosted.org/packages/37/fc/207c00a4c6fa303d26e2cbd62fbdb0582facdfd08f55500fd83bf6b0f8db/grpcio-1.73.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:10e8edc035724aba0346a432060fd192b42bd03675d083c01553cab071a28da5", size = 6105505 }, - { url = "https://files.pythonhosted.org/packages/72/35/8fe69af820667b87ebfcb24214e42a1d53da53cb39edd6b4f84f6b36da86/grpcio-1.73.0-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:f5cdc332b503c33b1643b12ea933582c7b081957c8bc2ea4cc4bc58054a09288", size = 6753792 }, - { url = "https://files.pythonhosted.org/packages/e2/d8/738c77c1e821e350da4a048849f695ff88a02b291f8c69db23908867aea6/grpcio-1.73.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:07ad7c57233c2109e4ac999cb9c2710c3b8e3f491a73b058b0ce431f31ed8145", size = 6287593 }, - { url = "https://files.pythonhosted.org/packages/09/ec/8498eabc018fa39ae8efe5e47e3f4c1bc9ed6281056713871895dc998807/grpcio-1.73.0-cp313-cp313-win32.whl", hash = "sha256:0eb5df4f41ea10bda99a802b2a292d85be28958ede2a50f2beb8c7fc9a738419", size = 3668637 }, - { url = "https://files.pythonhosted.org/packages/d7/35/347db7d2e7674b621afd21b12022e7f48c7b0861b5577134b4e939536141/grpcio-1.73.0-cp313-cp313-win_amd64.whl", hash = "sha256:38cf518cc54cd0c47c9539cefa8888549fcc067db0b0c66a46535ca8032020c4", size = 4335872 }, +version = "1.74.0rc1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4f/70/01dd990e5f7ae82cd2b939fc43e1d25b9dac17fff0d5664df57b2f7eb12e/grpcio-1.74.0rc1.tar.gz", hash = "sha256:f89f7adb5b8289a0bee5a40384fdf5fee7e38ebf260883ecbc7267c2b589cf9e", size = 12762149 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/85/cc/952a8640a90c0c4079ca4e978a00f69a980d8112f75870c547ca52ec3071/grpcio-1.74.0rc1-cp310-cp310-linux_armv7l.whl", hash = "sha256:6b1f23c67e979f7bdac6f890bf768588537466b54840e4e12158f9ff650061b3", size = 5481877 }, + { url = "https://files.pythonhosted.org/packages/b8/62/1b0cc0c4d5471c7590d97831b226ad860159c069fef9a0263c9ff46ed438/grpcio-1.74.0rc1-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:2bb8449a63f273e960be2c62b9589eb8adfad1bb917b8bbd54c6fd48a8e78e59", size = 10987048 }, + { url = "https://files.pythonhosted.org/packages/48/e1/fefc89006f292e8ffc864bb55fe505fa272b9d35b2e90aae705899c8a01d/grpcio-1.74.0rc1-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:55e75e27d1be7003ab9aa839047342ca6b13294e5334b68f186444cb4703b19a", size = 5983706 }, + { url = "https://files.pythonhosted.org/packages/26/0d/fb4acd13f3e5a2ec1fde8b61e9139081aed2b1f2139587dd0687cfe1fab2/grpcio-1.74.0rc1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea110662f7e04fc406061bd9c09332126692c373f3fbf1d49ba12111d520b1b9", size = 6653828 }, + { url = "https://files.pythonhosted.org/packages/89/2a/62bf93566c2bef0876f77b1bbd380f298c55aa301531143cfe3341239d4d/grpcio-1.74.0rc1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ae7562bae889875e28497165d56e70c68dec2e7494f2de6c525f688cdc31f3cc", size = 6215230 }, + { url = "https://files.pythonhosted.org/packages/3f/a3/cabd4aa019386fc317cac6d524c7090287dd4240a96fd0ea9e56658a3eb2/grpcio-1.74.0rc1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b1ef0a0209d9d14f2799779cada7e618d24231467616964db4886ad04ec05115", size = 6329179 }, + { url = "https://files.pythonhosted.org/packages/04/79/c4cbd5b8da3fbff0a3e78b48208c94d145cb6516ed0e882793a9d14771cc/grpcio-1.74.0rc1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:ddbecc398c9bee52bb59ae02e289c641fdcae9c36cb2fadfa3a421b2f7a7de0c", size = 7018694 }, + { url = "https://files.pythonhosted.org/packages/6d/1d/fb9219d6f7ae3e0d62bc922a9817fac48bc1da03c108d27a1ac45ade326a/grpcio-1.74.0rc1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1da336d53880fd6ee15fd01669b6890687bb4c98b2bca533231b00e247ff361c", size = 6509716 }, + { url = "https://files.pythonhosted.org/packages/82/1a/3c8cccc80e92a49d58bcc89819df2adf148aaf09836b9c5e952de099d7fc/grpcio-1.74.0rc1-cp310-cp310-win32.whl", hash = "sha256:198b736bfe3c5e048def5f917d103ab8a593340627aa10ebded4fbf49a2088d2", size = 3817524 }, + { url = "https://files.pythonhosted.org/packages/4d/34/310c944e331b4473446cc94235f72c615a483e64832906d4f265270f78f2/grpcio-1.74.0rc1-cp310-cp310-win_amd64.whl", hash = "sha256:6ada43ca645ce7d6585c9bdda192d128dcfe04802299c422f8c7d396c3ee3897", size = 4494017 }, + { url = "https://files.pythonhosted.org/packages/f9/b0/417a7ab15fb89c682d332d4b25893abc4147db15b3e44e58cb6547facbfe/grpcio-1.74.0rc1-cp311-cp311-linux_armv7l.whl", hash = "sha256:a5ec2119c9242c696e3d3cd4badaabcf738aff018947be5e0b94a32b7bd5f5a6", size = 5487642 }, + { url = "https://files.pythonhosted.org/packages/ce/c2/85dcbe77fea03185b84e7aecc1094f4ca4fd9a3d0607e14e8f5c7b01ca1a/grpcio-1.74.0rc1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:5595d2dcfbeb63a0d49668a81e3841c644f748ae34807d448972236aaf8522ed", size = 10999805 }, + { url = "https://files.pythonhosted.org/packages/aa/a8/f1466a51593662456fe2caae91ca3b6e69fe5711f2d47ba6f8364e7df1cd/grpcio-1.74.0rc1-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:9f5e6642fd79d583b64ce81194d337f86e195c5aba76b743b7069a7948526c57", size = 5987641 }, + { url = "https://files.pythonhosted.org/packages/fb/76/5f93faadc3a30b52bb42355073487dd8a3fb60d89d155c3d1cf0f8338491/grpcio-1.74.0rc1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:34f6c7292a1d97dda9baf097125a94f74cab7b854e616c7ef5854ad43be8753d", size = 6655665 }, + { url = "https://files.pythonhosted.org/packages/79/e2/361c706e4c0a4b3077504221d422743768ac0e1e50b63dea744364f8d2b1/grpcio-1.74.0rc1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3c9916476bbfc33bf5fa8115214a8ea14eced4fd9f564427ababa5fd67d9db94", size = 6219622 }, + { url = "https://files.pythonhosted.org/packages/58/9f/7792bc96805032ae5acc66d9fe7d94a848502ccd3b787e3118f0b774f59f/grpcio-1.74.0rc1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a28a4fc78a546ce761f0024d716478465a9ebf2f92fc2f999adc2d5bec71a6a3", size = 6334946 }, + { url = "https://files.pythonhosted.org/packages/be/f1/13689def59c6d64bf2a8156bd8d1eb02f7592b006de84d32abf887cd6e12/grpcio-1.74.0rc1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:5db65dea791de3b9c9869cdf70d0b2bccf2a35ccbde98b61a1e781ae2cdb9209", size = 7019377 }, + { url = "https://files.pythonhosted.org/packages/ad/e1/2120aa88a441748e316460f3e81d3ebec24472489a1092fecc6ccdb936bb/grpcio-1.74.0rc1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5af63160e40a23f9f4d6a5520114bb1b12790f0953b72650d57ddc0784cc07db", size = 6514261 }, + { url = "https://files.pythonhosted.org/packages/17/93/76782b18200e40c6af363ee359223bca11ec04fbf8f657a20b1dabf83966/grpcio-1.74.0rc1-cp311-cp311-win32.whl", hash = "sha256:c56889f37e7fdcf9ea92d029856b575543e59cd02fbbc25ed3d472b203b7b598", size = 3817255 }, + { url = "https://files.pythonhosted.org/packages/56/3a/81e384f5fa66730b222aab36e9cd14bc7bf7d1f61f7f55fb82f330ce183e/grpcio-1.74.0rc1-cp311-cp311-win_amd64.whl", hash = "sha256:c8ac96dfabe414ad14f9f0bd99d9652de604631c1c7879c5fb17b963c85379b9", size = 4495443 }, + { url = "https://files.pythonhosted.org/packages/72/8b/c634a89d65f3add3fb841bfe4f8c26c7be0fd16bd097af243a0784adee4c/grpcio-1.74.0rc1-cp312-cp312-linux_armv7l.whl", hash = "sha256:374a64facd3bac5702424470d404e732e4e93468e132de62baf05411d14aae9d", size = 5445766 }, + { url = "https://files.pythonhosted.org/packages/30/a7/88e1fce03522f1e4372561b693e19466ecc1ef5d214386186e97e80ec01f/grpcio-1.74.0rc1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:3db22ee80529e230765ba43603562e3865729029896506b40fbbee9562e6f53a", size = 10979806 }, + { url = "https://files.pythonhosted.org/packages/97/b6/7c15eb8d9466ae300ddd6a77d60c3e4fb748bea4a3489f491e1edfc8143c/grpcio-1.74.0rc1-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:4b689555d83a9021a38458c40217d359985e4b93e4b6d396091369513eb04fee", size = 5941997 }, + { url = "https://files.pythonhosted.org/packages/bb/b3/edcbe8f4a87ade093f6a86ce210cf14318c632cb7ba87f2be0f26be53189/grpcio-1.74.0rc1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:938738a98311759ceeda0a49ef1c93000443ebfbbde0ef50c842ae600595ad18", size = 6621837 }, + { url = "https://files.pythonhosted.org/packages/c0/3f/82a8b81b6145e4bd231bcc746d498aa60d810b52f2d839d9fddcc836949d/grpcio-1.74.0rc1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:475b8c386bd7ebb4f29809b7232fabeda0eb40d9d955756de90402e34f19de6c", size = 6180719 }, + { url = "https://files.pythonhosted.org/packages/03/ec/7f2099330d030ee68a25690ab33b1e0f74c7decd6c78f638da85199f53e3/grpcio-1.74.0rc1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d805588bfbd8d7aeeb0c01a278f287a9a555629160995c3ca416d9e0c8909ddd", size = 6301032 }, + { url = "https://files.pythonhosted.org/packages/d8/d4/7432e96c0431c8d5a621534e2f2a9b81d88b2eacaffd1fbcb822be81db70/grpcio-1.74.0rc1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:5b5f4537f4d6d4a8b2cc30b3bc65888fbc7e17e96a19bc4460b857ae4d2c8831", size = 6994178 }, + { url = "https://files.pythonhosted.org/packages/e8/86/a8a38faaee87314851d97d5e3a8fc4f90876864bf694228532e41370c95d/grpcio-1.74.0rc1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:0a5ac4a7432a4ddba936443ca328b914ebabf48267082c50eef1d3506378b123", size = 6489669 }, + { url = "https://files.pythonhosted.org/packages/49/13/2f47f900a948b10c8340a35c4035802f3c9f847f94495a5fe8e77852fd42/grpcio-1.74.0rc1-cp312-cp312-win32.whl", hash = "sha256:988325ca49c047a48ee4542cd10e735b47a169a644bf73611ca05a123d8a2d53", size = 3807127 }, + { url = "https://files.pythonhosted.org/packages/86/2b/74992115930c21904ad4c3113bd636c1e826d4277688a4bcc44604bca7fb/grpcio-1.74.0rc1-cp312-cp312-win_amd64.whl", hash = "sha256:289070f7959cfd13efbebdcbab0343597cff972fb73fd25c45e0440ad4da2b53", size = 4490151 }, + { url = "https://files.pythonhosted.org/packages/a4/7a/ac2810463aedd37fc1c08b810f30848f02e0e75c6d24312bc59681b340d2/grpcio-1.74.0rc1-cp313-cp313-linux_armv7l.whl", hash = "sha256:c4b9630ef976a1e2f1581e22aa73d9f7c1997cdd4f74c406953bbe158cb3ba85", size = 5449670 }, + { url = "https://files.pythonhosted.org/packages/38/59/d85cc8e99e7ff7890573ec0eebc738658d0b8556b6541e4e2f36dc46c99e/grpcio-1.74.0rc1-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:2e97b1cd9333926e58099bda57abf2e54c6a6b972f4a8b06b52d1338900a4d1c", size = 10974019 }, + { url = "https://files.pythonhosted.org/packages/92/61/f9dfcbd15d1c5b04c81565986a3492fc9c16ac1ad800ed667dc189296557/grpcio-1.74.0rc1-cp313-cp313-manylinux_2_17_aarch64.whl", hash = "sha256:6477b8fa0a0b27274bb8c97c4af40f810183a71fa866a7d392b1f6d6c6d82254", size = 5945552 }, + { url = "https://files.pythonhosted.org/packages/83/d3/f9b368bca31e6a1a0a7375704a297431839566ed73b45048b1bb797929ad/grpcio-1.74.0rc1-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6656206cea919798625cecb12ba79aadfd20d31c53c0fbe3d3d38e158895b172", size = 6626152 }, + { url = "https://files.pythonhosted.org/packages/e5/6d/1a1bfecedb34c049f795bc98692641a92ff300f03703b33abcc497d843f0/grpcio-1.74.0rc1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:03f48b2d4eb143c350d4165a9d2deec465ad6c7bc4d16c92411aade35520b8bd", size = 6182642 }, + { url = "https://files.pythonhosted.org/packages/d7/14/5d9714a7790cf13510e4bae5b17bef008f766dd36bfb24e447343300f099/grpcio-1.74.0rc1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7a503425354a400d78ebb0c1b2e6e34c1faff268fd794a36a0cc5aa2ef668111", size = 6303398 }, + { url = "https://files.pythonhosted.org/packages/ec/da/dcbf4dfb0417a162d935cff26305e1882a151180b7f6365c9ae0ce2a509d/grpcio-1.74.0rc1-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:deb9c7dc98d4ed6a5602d090a5e837aa04490807f573945b0caab0fdf23bd3da", size = 6996581 }, + { url = "https://files.pythonhosted.org/packages/48/cd/8b96209bdcbf53b2f354d54cde93c43c6d0b20e34cab046c3be7efa78712/grpcio-1.74.0rc1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:4bfad706b72b088fd5cee53b4db9bd04f14c0af815331063aba7a4d7f0762394", size = 6491564 }, + { url = "https://files.pythonhosted.org/packages/6e/4d/7aec0b29266dca18269649367730612f7c06dc76f9c5cd79a97c5fa0bf0c/grpcio-1.74.0rc1-cp313-cp313-win32.whl", hash = "sha256:37409c8a14ea97e6f3b5787e4667f5eb13dc7ae0214475bc55abcb7bf03f49d0", size = 3805862 }, + { url = "https://files.pythonhosted.org/packages/d5/83/ee163693ecbe574a0eb2befc6efe5a119ce27b7158ad2c718b370ea5d81d/grpcio-1.74.0rc1-cp313-cp313-win_amd64.whl", hash = "sha256:f16d03fc747e2bb2ea5587a03065da0bd747c73866664a83e53b81256965022d", size = 4489249 }, ] [[package]] name = "grpcio-status" -version = "1.73.0" +version = "1.71.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "googleapis-common-protos" }, { name = "grpcio" }, { name = "protobuf" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/6d/07/1c7b5ec7c72b8e2efc32cf82e2fe72497c579c8fa94edb8c3e430874cd42/grpcio_status-1.73.0.tar.gz", hash = "sha256:a2b7f430568217f884fe52a5a0133b6f4c9338beae33fb5370134a8eaf58f974", size = 13670 } +sdist = { url = "https://files.pythonhosted.org/packages/fd/d1/b6e9877fedae3add1afdeae1f89d1927d296da9cf977eca0eb08fb8a460e/grpcio_status-1.71.2.tar.gz", hash = "sha256:c7a97e176df71cdc2c179cd1847d7fc86cca5832ad12e9798d7fed6b7a1aab50", size = 13677 } wheels = [ - { url = "https://files.pythonhosted.org/packages/e2/95/e4b963a8730e04fae0e98cdd12212a9ffb318daf8687ea3220b78b34f8fa/grpcio_status-1.73.0-py3-none-any.whl", hash = "sha256:a3f3a9994b44c364f014e806114ba44cc52e50c426779f958c8b22f14ff0d892", size = 14423 }, + { url = "https://files.pythonhosted.org/packages/67/58/317b0134129b556a93a3b0afe00ee675b5657f0155509e22fcb853bafe2d/grpcio_status-1.71.2-py3-none-any.whl", hash = "sha256:803c98cb6a8b7dc6dbb785b1111aed739f241ab5e9da0bba96888aa74704cfd3", size = 14424 }, ] [[package]] @@ -1233,17 +1393,17 @@ wheels = [ [[package]] name = "hf-xet" -version = "1.1.3" +version = "1.1.6rc2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/75/dc/dc091aeeb671e71cbec30e84963f9c0202c17337b24b0a800e7d205543e8/hf_xet-1.1.3.tar.gz", hash = "sha256:a5f09b1dd24e6ff6bcedb4b0ddab2d81824098bb002cf8b4ffa780545fa348c3", size = 488127 } +sdist = { url = "https://files.pythonhosted.org/packages/68/02/9590927b54d8eca803d5f2c22398c91907bb430fe9082351449ba1b47d21/hf_xet-1.1.6rc2.tar.gz", hash = "sha256:f59e54beb49e3c424d0c8d6a8df74349192e994f7739c18030b32330589d7671", size = 494316 } wheels = [ - { url = "https://files.pythonhosted.org/packages/9b/1f/bc01a4c0894973adebbcd4aa338a06815c76333ebb3921d94dcbd40dae6a/hf_xet-1.1.3-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:c3b508b5f583a75641aebf732853deb058953370ce8184f5dabc49f803b0819b", size = 2256929 }, - { url = "https://files.pythonhosted.org/packages/78/07/6ef50851b5c6b45b77a6e018fa299c69a2db3b8bbd0d5af594c0238b1ceb/hf_xet-1.1.3-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:b788a61977fbe6b5186e66239e2a329a3f0b7e7ff50dad38984c0c74f44aeca1", size = 2153719 }, - { url = "https://files.pythonhosted.org/packages/52/48/e929e6e3db6e4758c2adf0f2ca2c59287f1b76229d8bdc1a4c9cfc05212e/hf_xet-1.1.3-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd2da210856444a34aad8ada2fc12f70dabed7cc20f37e90754d1d9b43bc0534", size = 4820519 }, - { url = "https://files.pythonhosted.org/packages/28/2e/03f89c5014a5aafaa9b150655f811798a317036646623bdaace25f485ae8/hf_xet-1.1.3-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:8203f52827e3df65981984936654a5b390566336956f65765a8aa58c362bb841", size = 4964121 }, - { url = "https://files.pythonhosted.org/packages/47/8b/5cd399a92b47d98086f55fc72d69bc9ea5e5c6f27a9ed3e0cdd6be4e58a3/hf_xet-1.1.3-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:30c575a5306f8e6fda37edb866762140a435037365eba7a17ce7bd0bc0216a8b", size = 5283017 }, - { url = "https://files.pythonhosted.org/packages/53/e3/2fcec58d2fcfd25ff07feb876f466cfa11f8dcf9d3b742c07fe9dd51ee0a/hf_xet-1.1.3-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:7c1a6aa6abed1f696f8099aa9796ca04c9ee778a58728a115607de9cc4638ff1", size = 4970349 }, - { url = "https://files.pythonhosted.org/packages/53/bf/10ca917e335861101017ff46044c90e517b574fbb37219347b83be1952f6/hf_xet-1.1.3-cp37-abi3-win_amd64.whl", hash = "sha256:b578ae5ac9c056296bb0df9d018e597c8dc6390c5266f35b5c44696003cde9f3", size = 2310934 }, + { url = "https://files.pythonhosted.org/packages/21/55/5e30c2c63122301f184cc442c5aed077bd6f5ae2079a0f4b701a373d81fb/hf_xet-1.1.6rc2-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:e93026949c596b2b644730d2a1a007a63d1d1c80f293247962c368c777d00c90", size = 2700641 }, + { url = "https://files.pythonhosted.org/packages/b3/a0/9b493c6373e8a16a67527449a4719b8dbe3177aa17ed591bc2ec91600bc3/hf_xet-1.1.6rc2-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:cf88c08a4fd8dbe62ee468e9dc1c4f0e37abcfa8cac130189fcffc6ca8d53526", size = 2565067 }, + { url = "https://files.pythonhosted.org/packages/56/de/da1c3e840fcb078d20957d69ef150a2492a6b8c8c0272e591770762cffc2/hf_xet-1.1.6rc2-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c37fff1cc2509f861706af6f5e5d27178bade8a02d84eb069c1b942952cc7b92", size = 3115719 }, + { url = "https://files.pythonhosted.org/packages/3a/c9/1719b01de254868cb1e0a6f64dc6f763bfe0e23c56114b392452c07152e4/hf_xet-1.1.6rc2-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:9b1926694628bffe2589d213596703cce9ac7383712ab15dde638a0b778e9e97", size = 3007969 }, + { url = "https://files.pythonhosted.org/packages/66/65/65576be66ecf94f853cdbd5a37babb9693655b8dcf37468f59ed7594f877/hf_xet-1.1.6rc2-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:acd8497a218b5db57594aaa9ba39f3fec86440b80d08f84e2595ab2a654f1559", size = 3170371 }, + { url = "https://files.pythonhosted.org/packages/bb/3a/86750fd0ec6eb7c4de1394e3921f3357f44ef64c033a5d86129b85cf7dac/hf_xet-1.1.6rc2-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:38cd088fc7f187ca735c9f47d1a6b79587334b7ffd111494cf867920574194b9", size = 3280665 }, + { url = "https://files.pythonhosted.org/packages/f7/72/a45ae75ad1065c05ab79a1f431d14c3e8fb1b64abae95da7e0dcb5265ba5/hf_xet-1.1.6rc2-cp37-abi3-win_amd64.whl", hash = "sha256:3676ed0f45b3f0332e135e99f467a34f1aabe8a49172ca6e8d7e011ddad305fa", size = 2751450 }, ] [[package]] @@ -1312,16 +1472,16 @@ wheels = [ [[package]] name = "httpx-sse" -version = "0.4.0" +version = "0.4.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4c/60/8f4281fa9bbf3c8034fd54c0e7412e66edbab6bc74c4996bd616f8d0406e/httpx-sse-0.4.0.tar.gz", hash = "sha256:1e81a3a3070ce322add1d3529ed42eb5f70817f45ed6ec915ab753f961139721", size = 12624 } +sdist = { url = "https://files.pythonhosted.org/packages/6e/fa/66bd985dd0b7c109a3bcb89272ee0bfb7e2b4d06309ad7b38ff866734b2a/httpx_sse-0.4.1.tar.gz", hash = "sha256:8f44d34414bc7b21bf3602713005c5df4917884f76072479b21f68befa4ea26e", size = 12998 } wheels = [ - { url = "https://files.pythonhosted.org/packages/e1/9b/a181f281f65d776426002f330c31849b86b31fc9d848db62e16f03ff739f/httpx_sse-0.4.0-py3-none-any.whl", hash = "sha256:f329af6eae57eaa2bdfd962b42524764af68075ea87370a2de920af5341e318f", size = 7819 }, + { url = "https://files.pythonhosted.org/packages/25/0a/6269e3473b09aed2dab8aa1a600c70f31f00ae1349bee30658f7e358a159/httpx_sse-0.4.1-py3-none-any.whl", hash = "sha256:cba42174344c3a5b06f255ce65b350880f962d99ead85e776f23c6618a377a37", size = 8054 }, ] [[package]] name = "huggingface-hub" -version = "0.32.6" +version = "0.33.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "filelock" }, @@ -1333,9 +1493,18 @@ dependencies = [ { name = "tqdm" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/8f/fb/7fcbafabdf470ffb5457b756cc1f659b4e88a9ff37c108e6c7a5ab5e781e/huggingface_hub-0.32.6.tar.gz", hash = "sha256:8e960f23dc57519c6c2a0bbc7e9bc030eaa14e7f2d61f8e68fd3d025dabed2fa", size = 424961 } +sdist = { url = "https://files.pythonhosted.org/packages/4b/9e/9366b7349fc125dd68b9d384a0fea84d67b7497753fe92c71b67e13f47c4/huggingface_hub-0.33.4.tar.gz", hash = "sha256:6af13478deae120e765bfd92adad0ae1aec1ad8c439b46f23058ad5956cbca0a", size = 426674 } wheels = [ - { url = "https://files.pythonhosted.org/packages/c5/42/dd58e603c5b069c4e4759c7c44e4f5ccdc2ce02185848232775f5d6d5d50/huggingface_hub-0.32.6-py3-none-any.whl", hash = "sha256:32cde9558c965477556edca72352621def7fbc42e167aaf33f4cdb9af65bb28b", size = 512800 }, + { url = "https://files.pythonhosted.org/packages/46/7b/98daa50a2db034cab6cd23a3de04fa2358cb691593d28e9130203eb7a805/huggingface_hub-0.33.4-py3-none-any.whl", hash = "sha256:09f9f4e7ca62547c70f8b82767eefadd2667f4e116acba2e3e62a5a81815a7bb", size = 515339 }, +] + +[[package]] +name = "identify" +version = "2.6.12" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/88/d193a27416618628a5eea64e3223acd800b40749a96ffb322a9b55a49ed1/identify-2.6.12.tar.gz", hash = "sha256:d8de45749f1efb108badef65ee8386f0f7bb19a7f26185f74de6367bffbaf0e6", size = 99254 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7a/cd/18f8da995b658420625f7ef13f037be53ae04ec5ad33f9b718240dcfd48c/identify-2.6.12-py2.py3-none-any.whl", hash = "sha256:ad9672d5a72e0d2ff7c5c8809b62dfa60458626352fb0eb7b55e69bdc45334a2", size = 99145 }, ] [[package]] @@ -1355,12 +1524,17 @@ dependencies = [ { name = "alembic" }, { name = "anthropic", extra = ["vertex"] }, { name = "dataclasses-json" }, + { name = "docker" }, { name = "duckduckgo-search" }, + { name = "e2b-code-interpreter" }, { name = "fastapi" }, + { name = "fastmcp" }, { name = "google-cloud-aiplatform" }, { name = "google-genai" }, { name = "ii-researcher" }, + { name = "ipdb" }, { name = "jsonschema" }, + { name = "libtmux" }, { name = "mammoth" }, { name = "markdownify" }, { name = "openai" }, @@ -1371,8 +1545,10 @@ dependencies = [ { name = "pillow" }, { name = "pip" }, { name = "playwright" }, + { name = "pre-commit" }, { name = "prompt-toolkit" }, - { name = "puremagic" }, + { name = "puremagic", version = "1.30", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "puremagic", version = "2.0.0b4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, { name = "pydub" }, { name = "pymupdf" }, { name = "pytest" }, @@ -1409,13 +1585,18 @@ requires-dist = [ { name = "anthropic", extras = ["vertex"], specifier = ">=0.50.0" }, { name = "dataclasses-json", specifier = ">=0.6.7" }, { name = "datasets", marker = "extra == 'gaia'", specifier = ">=3.6.0" }, + { name = "docker", specifier = ">=7.1.0" }, { name = "duckduckgo-search", specifier = ">=8.0.1" }, + { name = "e2b-code-interpreter", specifier = "==1.2.0b5" }, { name = "fastapi", specifier = ">=0.115.12" }, + { name = "fastmcp", specifier = ">=2.2.0" }, { name = "google-cloud-aiplatform", specifier = ">=1.90.0" }, { name = "google-genai", specifier = ">=1.14.0" }, { name = "huggingface-hub", marker = "extra == 'gaia'", specifier = ">=0.31.1" }, { name = "ii-researcher", specifier = ">=0.1.5" }, + { name = "ipdb", specifier = ">=0.13.13" }, { name = "jsonschema", specifier = ">=4.23.0" }, + { name = "libtmux", specifier = ">=0.46.2" }, { name = "mammoth", specifier = ">=1.9.0" }, { name = "markdownify", specifier = ">=1.1.0" }, { name = "openai", specifier = ">=1.68.2" }, @@ -1426,6 +1607,7 @@ requires-dist = [ { name = "pillow", specifier = ">=11.2.1" }, { name = "pip", specifier = ">=25.1.1" }, { name = "playwright", specifier = ">=1.52.0" }, + { name = "pre-commit", specifier = ">=4.2.0" }, { name = "prompt-toolkit", specifier = ">=3.0.51" }, { name = "puremagic", specifier = ">=1.29" }, { name = "pydub", specifier = ">=0.25.1" }, @@ -1433,7 +1615,7 @@ requires-dist = [ { name = "pytest", specifier = ">=8.3.5" }, { name = "python-dotenv", specifier = ">=1.1.0" }, { name = "python-pptx", specifier = ">=1.0.2" }, - { name = "rich", specifier = "==13.7.1" }, + { name = "rich", specifier = ">=13.7.1" }, { name = "speechrecognition", specifier = ">=3.14.2" }, { name = "sqlalchemy", marker = "extra == 'gaia'", specifier = ">=2.0.0" }, { name = "tavily-python", specifier = ">=0.7.2" }, @@ -1446,7 +1628,6 @@ requires-dist = [ { name = "uvicorn", extras = ["standard"], marker = "extra == 'gaia'", specifier = ">=0.29.0" }, { name = "youtube-transcript-api", specifier = ">=1.0.3" }, ] -provides-extras = ["gaia"] [package.metadata.requires-dev] dev = [{ name = "pytest-asyncio", specifier = ">=1.0.0" }] @@ -1503,6 +1684,97 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050 }, ] +[[package]] +name = "ipdb" +version = "0.13.13" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "decorator" }, + { name = "ipython", version = "8.37.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "ipython", version = "9.4.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3d/1b/7e07e7b752017f7693a0f4d41c13e5ca29ce8cbcfdcc1fd6c4ad8c0a27a0/ipdb-0.13.13.tar.gz", hash = "sha256:e3ac6018ef05126d442af680aad863006ec19d02290561ac88b8b1c0b0cfc726", size = 17042 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/4c/b075da0092003d9a55cf2ecc1cae9384a1ca4f650d51b00fc59875fe76f6/ipdb-0.13.13-py3-none-any.whl", hash = "sha256:45529994741c4ab6d2388bfa5d7b725c2cf7fe9deffabdb8a6113aa5ed449ed4", size = 12130 }, +] + +[[package]] +name = "ipython" +version = "8.37.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.11'", +] +dependencies = [ + { name = "colorama", marker = "python_full_version < '3.11' and sys_platform == 'win32'" }, + { name = "decorator", marker = "python_full_version < '3.11'" }, + { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, + { name = "jedi", marker = "python_full_version < '3.11'" }, + { name = "matplotlib-inline", marker = "python_full_version < '3.11'" }, + { name = "pexpect", marker = "python_full_version < '3.11' and sys_platform != 'emscripten' and sys_platform != 'win32'" }, + { name = "prompt-toolkit", marker = "python_full_version < '3.11'" }, + { name = "pygments", marker = "python_full_version < '3.11'" }, + { name = "stack-data", marker = "python_full_version < '3.11'" }, + { name = "traitlets", marker = "python_full_version < '3.11'" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/85/31/10ac88f3357fc276dc8a64e8880c82e80e7459326ae1d0a211b40abf6665/ipython-8.37.0.tar.gz", hash = "sha256:ca815841e1a41a1e6b73a0b08f3038af9b2252564d01fc405356d34033012216", size = 5606088 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/91/d0/274fbf7b0b12643cbbc001ce13e6a5b1607ac4929d1b11c72460152c9fc3/ipython-8.37.0-py3-none-any.whl", hash = "sha256:ed87326596b878932dbcb171e3e698845434d8c61b8d8cd474bf663041a9dcf2", size = 831864 }, +] + +[[package]] +name = "ipython" +version = "9.4.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.13'", + "python_full_version == '3.12.*'", + "python_full_version == '3.11.*'", +] +dependencies = [ + { name = "colorama", marker = "python_full_version >= '3.11' and sys_platform == 'win32'" }, + { name = "decorator", marker = "python_full_version >= '3.11'" }, + { name = "ipython-pygments-lexers", marker = "python_full_version >= '3.11'" }, + { name = "jedi", marker = "python_full_version >= '3.11'" }, + { name = "matplotlib-inline", marker = "python_full_version >= '3.11'" }, + { name = "pexpect", marker = "python_full_version >= '3.11' and sys_platform != 'emscripten' and sys_platform != 'win32'" }, + { name = "prompt-toolkit", marker = "python_full_version >= '3.11'" }, + { name = "pygments", marker = "python_full_version >= '3.11'" }, + { name = "stack-data", marker = "python_full_version >= '3.11'" }, + { name = "traitlets", marker = "python_full_version >= '3.11'" }, + { name = "typing-extensions", marker = "python_full_version == '3.11.*'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/54/80/406f9e3bde1c1fd9bf5a0be9d090f8ae623e401b7670d8f6fdf2ab679891/ipython-9.4.0.tar.gz", hash = "sha256:c033c6d4e7914c3d9768aabe76bbe87ba1dc66a92a05db6bfa1125d81f2ee270", size = 4385338 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/63/f8/0031ee2b906a15a33d6bfc12dd09c3dfa966b3cb5b284ecfb7549e6ac3c4/ipython-9.4.0-py3-none-any.whl", hash = "sha256:25850f025a446d9b359e8d296ba175a36aedd32e83ca9b5060430fe16801f066", size = 611021 }, +] + +[[package]] +name = "ipython-pygments-lexers" +version = "1.1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pygments", marker = "python_full_version >= '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ef/4c/5dd1d8af08107f88c7f741ead7a40854b8ac24ddf9ae850afbcf698aa552/ipython_pygments_lexers-1.1.1.tar.gz", hash = "sha256:09c0138009e56b6854f9535736f4171d855c8c08a563a0dcd8022f78355c7e81", size = 8393 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d9/33/1f075bf72b0b747cb3288d011319aaf64083cf2efef8354174e3ed4540e2/ipython_pygments_lexers-1.1.1-py3-none-any.whl", hash = "sha256:a9462224a505ade19a605f71f8fa63c2048833ce50abc86768a0d81d876dc81c", size = 8074 }, +] + +[[package]] +name = "jedi" +version = "0.19.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "parso" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/72/3a/79a912fbd4d8dd6fbb02bf69afd3bb72cf0c729bb3063c6f4498603db17a/jedi-0.19.2.tar.gz", hash = "sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0", size = 1231287 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c0/5a/9cac0c82afec3d09ccd97c8b6502d48f165f9124db81b4bcb90b4af974ee/jedi-0.19.2-py2.py3-none-any.whl", hash = "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9", size = 1572278 }, +] + [[package]] name = "jinja2" version = "3.1.6" @@ -1619,7 +1891,7 @@ wheels = [ [[package]] name = "jsonschema" -version = "4.24.0" +version = "4.25.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "attrs" }, @@ -1627,9 +1899,9 @@ dependencies = [ { name = "referencing" }, { name = "rpds-py" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/bf/d3/1cf5326b923a53515d8f3a2cd442e6d7e94fcc444716e879ea70a0ce3177/jsonschema-4.24.0.tar.gz", hash = "sha256:0b4e8069eb12aedfa881333004bccaec24ecef5a8a6a4b6df142b2cc9599d196", size = 353480 } +sdist = { url = "https://files.pythonhosted.org/packages/d5/00/a297a868e9d0784450faa7365c2172a7d6110c763e30ba861867c32ae6a9/jsonschema-4.25.0.tar.gz", hash = "sha256:e63acf5c11762c0e6672ffb61482bdf57f0876684d8d249c0fe2d730d48bc55f", size = 356830 } wheels = [ - { url = "https://files.pythonhosted.org/packages/a2/3d/023389198f69c722d039351050738d6755376c8fd343e91dc493ea485905/jsonschema-4.24.0-py3-none-any.whl", hash = "sha256:a462455f19f5faf404a7902952b6f0e3ce868f3ee09a359b05eca6673bd8412d", size = 88709 }, + { url = "https://files.pythonhosted.org/packages/fe/54/c86cd8e011fe98803d7e382fd67c0df5ceab8d2b7ad8c5a81524f791551c/jsonschema-4.25.0-py3-none-any.whl", hash = "sha256:24c2e8da302de79c8b9382fee3e76b355e44d2a4364bb207159ce10b517bd716", size = 89184 }, ] [[package]] @@ -1646,7 +1918,7 @@ wheels = [ [[package]] name = "langchain" -version = "0.3.25" +version = "0.3.26" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "async-timeout", version = "4.0.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, @@ -1658,14 +1930,14 @@ dependencies = [ { name = "requests" }, { name = "sqlalchemy" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fc/f9/a256609096a9fc7a1b3a6300a97000091efabdf21555a97988f93d4d9258/langchain-0.3.25.tar.gz", hash = "sha256:a1d72aa39546a23db08492d7228464af35c9ee83379945535ceef877340d2a3a", size = 10225045 } +sdist = { url = "https://files.pythonhosted.org/packages/7f/13/a9931800ee42bbe0f8850dd540de14e80dda4945e7ee36e20b5d5964286e/langchain-0.3.26.tar.gz", hash = "sha256:8ff034ee0556d3e45eff1f1e96d0d745ced57858414dba7171c8ebdbeb5580c9", size = 10226808 } wheels = [ - { url = "https://files.pythonhosted.org/packages/ed/5c/5c0be747261e1f8129b875fa3bfea736bc5fe17652f9d5e15ca118571b6f/langchain-0.3.25-py3-none-any.whl", hash = "sha256:931f7d2d1eaf182f9f41c5e3272859cfe7f94fc1f7cef6b3e5a46024b4884c21", size = 1011008 }, + { url = "https://files.pythonhosted.org/packages/f1/f2/c09a2e383283e3af1db669ab037ac05a45814f4b9c472c48dc24c0cef039/langchain-0.3.26-py3-none-any.whl", hash = "sha256:361bb2e61371024a8c473da9f9c55f4ee50f269c5ab43afdb2b1309cb7ac36cf", size = 1012336 }, ] [[package]] name = "langchain-community" -version = "0.3.25" +version = "0.3.27" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiohttp" }, @@ -1675,21 +1947,21 @@ dependencies = [ { name = "langchain-core" }, { name = "langsmith" }, { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "numpy", version = "2.3.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "pydantic-settings" }, { name = "pyyaml" }, { name = "requests" }, { name = "sqlalchemy" }, { name = "tenacity" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c0/9b/332e69933ce7d96153fe5468d5428052ae20b143fa0dba0c78eea8859f94/langchain_community-0.3.25.tar.gz", hash = "sha256:a536888a48b36184dee20df86d266827a01916397fb398af2088ab7c3dfee684", size = 33235586 } +sdist = { url = "https://files.pythonhosted.org/packages/5c/76/200494f6de488217a196c4369e665d26b94c8c3642d46e2fd62f9daf0a3a/langchain_community-0.3.27.tar.gz", hash = "sha256:e1037c3b9da0c6d10bf06e838b034eb741e016515c79ef8f3f16e53ead33d882", size = 33237737 } wheels = [ - { url = "https://files.pythonhosted.org/packages/5c/e1/975bcd11e86de74c10023d291879810d4eaffcfbb5d4c0d8fb6fb41b8247/langchain_community-0.3.25-py3-none-any.whl", hash = "sha256:0d7f673d463019ab1aca4e50e750048214a7772efd2c8e1d59256739b8318f97", size = 2529170 }, + { url = "https://files.pythonhosted.org/packages/c8/bc/f8c7dae8321d37ed39ac9d7896617c4203248240a4835b136e3724b3bb62/langchain_community-0.3.27-py3-none-any.whl", hash = "sha256:581f97b795f9633da738ea95da9cb78f8879b538090c9b7a68c0aed49c828f0d", size = 2530442 }, ] [[package]] name = "langchain-core" -version = "0.3.65" +version = "0.3.70" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "jsonpatch" }, @@ -1700,23 +1972,23 @@ dependencies = [ { name = "tenacity" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/04/8a/d08c83195d1ef26c42728412ab92ab08211893906b283abce65775e21327/langchain_core-0.3.65.tar.gz", hash = "sha256:54b5e0c8d9bb405415c3211da508ef9cfe0acbe5b490d1b4a15664408ee82d9b", size = 558557 } +sdist = { url = "https://files.pythonhosted.org/packages/a6/38/cf4ad0462e39814aecfcbd59dd4b19fb1e9f61999fec9bc1613d593de417/langchain_core-0.3.70.tar.gz", hash = "sha256:58551e5411ff9f92c7c8f4379e07e762ca66800e821cd904e19881fe41f691ee", size = 566031 } wheels = [ - { url = "https://files.pythonhosted.org/packages/54/f0/31db18b7b8213266aed926ce89b5bdd84ccde7ee2edf4cab14e3dd2bfcf1/langchain_core-0.3.65-py3-none-any.whl", hash = "sha256:80e8faf6e9f331f8ef728f3fe793549f1d3fb244fcf9e1bdcecab6a6f4669394", size = 438052 }, + { url = "https://files.pythonhosted.org/packages/25/73/910b809b5f8dfe2738bbb065580364b9751425a404b8ee87479a75b37e95/langchain_core-0.3.70-py3-none-any.whl", hash = "sha256:56f1ce0ab410508e25b3b4b3b87a8ffae38bf16294e5f605ac63a8aff5bc13a4", size = 442252 }, ] [[package]] name = "langchain-openai" -version = "0.3.22" +version = "0.3.28" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "langchain-core" }, { name = "openai" }, { name = "tiktoken" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/32/88/28a78b1f15dbabc32e3b892c3c71bdcb4a48a501e7d604f5d42fb365a191/langchain_openai-0.3.22.tar.gz", hash = "sha256:42802b1c64824edd4a0e5093bdb791e4323cf1d281594cf58194ad714db6cd49", size = 647768 } +sdist = { url = "https://files.pythonhosted.org/packages/6b/1d/90cd764c62d5eb822113d3debc3abe10c8807d2c0af90917bfe09acd6f86/langchain_openai-0.3.28.tar.gz", hash = "sha256:6c669548dbdea325c034ae5ef699710e2abd054c7354fdb3ef7bf909dc739d9e", size = 753951 } wheels = [ - { url = "https://files.pythonhosted.org/packages/ec/dd/effc847fd55b808d04f7e453d1e4bd3dc813708113ee283055e77be6d651/langchain_openai-0.3.22-py3-none-any.whl", hash = "sha256:945d3b18f2293504d0b81971a9017fc1294571cce4204c18aba3cfbfc43d24c6", size = 65295 }, + { url = "https://files.pythonhosted.org/packages/91/56/75f3d84b69b8bdae521a537697375e1241377627c32b78edcae337093502/langchain_openai-0.3.28-py3-none-any.whl", hash = "sha256:4cd6d80a5b2ae471a168017bc01b2e0f01548328d83532400a001623624ede67", size = 70571 }, ] [[package]] @@ -1733,7 +2005,7 @@ wheels = [ [[package]] name = "langsmith" -version = "0.3.45" +version = "0.4.8" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "httpx" }, @@ -1744,14 +2016,23 @@ dependencies = [ { name = "requests-toolbelt" }, { name = "zstandard" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/be/86/b941012013260f95af2e90a3d9415af4a76a003a28412033fc4b09f35731/langsmith-0.3.45.tar.gz", hash = "sha256:1df3c6820c73ed210b2c7bc5cdb7bfa19ddc9126cd03fdf0da54e2e171e6094d", size = 348201 } +sdist = { url = "https://files.pythonhosted.org/packages/46/38/0da897697ce29fb78cdaacae2d0fa3a4bc2a0abf23f84f6ecd1947f79245/langsmith-0.4.8.tar.gz", hash = "sha256:50eccb744473dd6bd3e0fe024786e2196b1f8598f8defffce7ac31113d6c140f", size = 352414 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/19/4f/481324462c44ce21443b833ad73ee51117031d41c16fec06cddbb7495b26/langsmith-0.4.8-py3-none-any.whl", hash = "sha256:ca2f6024ab9d2cd4d091b2e5b58a5d2cb0c354a0c84fe214145a89ad450abae0", size = 367975 }, +] + +[[package]] +name = "libtmux" +version = "0.46.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9c/aa/7e1dcaa097156d6f3a7d8669be4389dced997feeb81744e3ff4681d65ee8/libtmux-0.46.2.tar.gz", hash = "sha256:9a398fec5d714129c8344555d466e1a903dfc0f741ba07aabe75a8ceb25c5dda", size = 346887 } wheels = [ - { url = "https://files.pythonhosted.org/packages/6a/f4/c206c0888f8a506404cb4f16ad89593bdc2f70cf00de26a1a0a7a76ad7a3/langsmith-0.3.45-py3-none-any.whl", hash = "sha256:5b55f0518601fa65f3bb6b1a3100379a96aa7b3ed5e9380581615ba9c65ed8ed", size = 363002 }, + { url = "https://files.pythonhosted.org/packages/d6/2f/9d207039fcfa00d3b30e4d765f062fbcc42c873c7518a8cfebb3eafd00e0/libtmux-0.46.2-py3-none-any.whl", hash = "sha256:6c32dbf22bde8e5e33b2714a4295f6e838dc640f337cd4c085a044f6828c7793", size = 60873 }, ] [[package]] name = "litellm" -version = "1.72.4" +version = "1.63.14" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiohttp" }, @@ -1766,9 +2047,9 @@ dependencies = [ { name = "tiktoken" }, { name = "tokenizers" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/83/80/d73c821a2f65ee5b97b41e61d9b18324ebb9d616e1e21844f4253ac38957/litellm-1.72.4.tar.gz", hash = "sha256:8855de30f78bcb1f37af244519b37a37faaaf579401b1414400b5b5e5b616d57", size = 8132997 } +sdist = { url = "https://files.pythonhosted.org/packages/3e/6a/a1a93c8419b59d0a40f59dc7300d67f45cd33a246367b09365dd7771568b/litellm-1.63.14.tar.gz", hash = "sha256:9cffe19d8140c33a2f777c5b2e8b8175ffe03979aac341b8538d6e6d143bd640", size = 6641535 } wheels = [ - { url = "https://files.pythonhosted.org/packages/6f/0d/0f86db9724b9bd63d057b912aa6aa532a76e6e707f9bb75abbd3b0a0401a/litellm-1.72.4-py3-none-any.whl", hash = "sha256:f98ca994420ed649c466d423655a6e0f2aeecab4564ed372b3378a949e491dc2", size = 8036589 }, + { url = "https://files.pythonhosted.org/packages/8b/0c/149ae51e16d0836d6ddc9aec03a110960227046860dfc68bd3746a23cdf5/litellm-1.63.14-py3-none-any.whl", hash = "sha256:d4c469f5990e142cc23dfa06c3fddd627001928e4df43682001f453af6a1fb51", size = 6964658 }, ] [package.optional-dependencies] @@ -1780,116 +2061,89 @@ proxy = [ { name = "fastapi" }, { name = "fastapi-sso" }, { name = "gunicorn" }, - { name = "litellm-enterprise" }, - { name = "litellm-proxy-extras" }, - { name = "mcp" }, { name = "orjson" }, { name = "pyjwt" }, { name = "pynacl" }, { name = "python-multipart" }, { name = "pyyaml" }, - { name = "rich" }, { name = "rq" }, { name = "uvicorn" }, - { name = "uvloop", marker = "sys_platform != 'win32'" }, + { name = "uvloop" }, { name = "websockets" }, ] -[[package]] -name = "litellm-enterprise" -version = "0.1.7" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/28/a4/d10b32cf325cf2d02a83a489342476987c5573860ff9d1034ce37999d60a/litellm_enterprise-0.1.7.tar.gz", hash = "sha256:1feb5b799bdbd27df1e02a31fa4d18351b945b6f72e337805cf80a64dd2ec870", size = 32369 } - -[[package]] -name = "litellm-proxy-extras" -version = "0.2.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/01/50/8cb93f7bb6d04d4e88189fdc6b6a8d42c7cab4796bbcd214048eb38d57c4/litellm_proxy_extras-0.2.3.tar.gz", hash = "sha256:41fc5a5a68894786f1cbc95f86ef939662136164d8da773236f5a141127e5c0c", size = 14164 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/90/a8/8ac27ff1601ff5c4e399bcfdf5ccba9aaef4e214d31976bd87b3a5f5c7b7/litellm_proxy_extras-0.2.3-py3-none-any.whl", hash = "sha256:00c25d94c01c242aa0aa746eb4d5788dcdfd19e6fc1768a36cd03426cb5ffd39", size = 24363 }, -] - [[package]] name = "lxml" -version = "5.4.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/76/3d/14e82fc7c8fb1b7761f7e748fd47e2ec8276d137b6acfe5a4bb73853e08f/lxml-5.4.0.tar.gz", hash = "sha256:d12832e1dbea4be280b22fd0ea7c9b87f0d8fc51ba06e92dc62d52f804f78ebd", size = 3679479 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f5/1f/a3b6b74a451ceb84b471caa75c934d2430a4d84395d38ef201d539f38cd1/lxml-5.4.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e7bc6df34d42322c5289e37e9971d6ed114e3776b45fa879f734bded9d1fea9c", size = 8076838 }, - { url = "https://files.pythonhosted.org/packages/36/af/a567a55b3e47135b4d1f05a1118c24529104c003f95851374b3748139dc1/lxml-5.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6854f8bd8a1536f8a1d9a3655e6354faa6406621cf857dc27b681b69860645c7", size = 4381827 }, - { url = "https://files.pythonhosted.org/packages/50/ba/4ee47d24c675932b3eb5b6de77d0f623c2db6dc466e7a1f199792c5e3e3a/lxml-5.4.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:696ea9e87442467819ac22394ca36cb3d01848dad1be6fac3fb612d3bd5a12cf", size = 5204098 }, - { url = "https://files.pythonhosted.org/packages/f2/0f/b4db6dfebfefe3abafe360f42a3d471881687fd449a0b86b70f1f2683438/lxml-5.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ef80aeac414f33c24b3815ecd560cee272786c3adfa5f31316d8b349bfade28", size = 4930261 }, - { url = "https://files.pythonhosted.org/packages/0b/1f/0bb1bae1ce056910f8db81c6aba80fec0e46c98d77c0f59298c70cd362a3/lxml-5.4.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b9c2754cef6963f3408ab381ea55f47dabc6f78f4b8ebb0f0b25cf1ac1f7609", size = 5529621 }, - { url = "https://files.pythonhosted.org/packages/21/f5/e7b66a533fc4a1e7fa63dd22a1ab2ec4d10319b909211181e1ab3e539295/lxml-5.4.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7a62cc23d754bb449d63ff35334acc9f5c02e6dae830d78dab4dd12b78a524f4", size = 4983231 }, - { url = "https://files.pythonhosted.org/packages/11/39/a38244b669c2d95a6a101a84d3c85ba921fea827e9e5483e93168bf1ccb2/lxml-5.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f82125bc7203c5ae8633a7d5d20bcfdff0ba33e436e4ab0abc026a53a8960b7", size = 5084279 }, - { url = "https://files.pythonhosted.org/packages/db/64/48cac242347a09a07740d6cee7b7fd4663d5c1abd65f2e3c60420e231b27/lxml-5.4.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:b67319b4aef1a6c56576ff544b67a2a6fbd7eaee485b241cabf53115e8908b8f", size = 4927405 }, - { url = "https://files.pythonhosted.org/packages/98/89/97442835fbb01d80b72374f9594fe44f01817d203fa056e9906128a5d896/lxml-5.4.0-cp310-cp310-manylinux_2_28_ppc64le.whl", hash = "sha256:a8ef956fce64c8551221f395ba21d0724fed6b9b6242ca4f2f7beb4ce2f41997", size = 5550169 }, - { url = "https://files.pythonhosted.org/packages/f1/97/164ca398ee654eb21f29c6b582685c6c6b9d62d5213abc9b8380278e9c0a/lxml-5.4.0-cp310-cp310-manylinux_2_28_s390x.whl", hash = "sha256:0a01ce7d8479dce84fc03324e3b0c9c90b1ece9a9bb6a1b6c9025e7e4520e78c", size = 5062691 }, - { url = "https://files.pythonhosted.org/packages/d0/bc/712b96823d7feb53482d2e4f59c090fb18ec7b0d0b476f353b3085893cda/lxml-5.4.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:91505d3ddebf268bb1588eb0f63821f738d20e1e7f05d3c647a5ca900288760b", size = 5133503 }, - { url = "https://files.pythonhosted.org/packages/d4/55/a62a39e8f9da2a8b6002603475e3c57c870cd9c95fd4b94d4d9ac9036055/lxml-5.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a3bcdde35d82ff385f4ede021df801b5c4a5bcdfb61ea87caabcebfc4945dc1b", size = 4999346 }, - { url = "https://files.pythonhosted.org/packages/ea/47/a393728ae001b92bb1a9e095e570bf71ec7f7fbae7688a4792222e56e5b9/lxml-5.4.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:aea7c06667b987787c7d1f5e1dfcd70419b711cdb47d6b4bb4ad4b76777a0563", size = 5627139 }, - { url = "https://files.pythonhosted.org/packages/5e/5f/9dcaaad037c3e642a7ea64b479aa082968de46dd67a8293c541742b6c9db/lxml-5.4.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:a7fb111eef4d05909b82152721a59c1b14d0f365e2be4c742a473c5d7372f4f5", size = 5465609 }, - { url = "https://files.pythonhosted.org/packages/a7/0a/ebcae89edf27e61c45023005171d0ba95cb414ee41c045ae4caf1b8487fd/lxml-5.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:43d549b876ce64aa18b2328faff70f5877f8c6dede415f80a2f799d31644d776", size = 5192285 }, - { url = "https://files.pythonhosted.org/packages/42/ad/cc8140ca99add7d85c92db8b2354638ed6d5cc0e917b21d36039cb15a238/lxml-5.4.0-cp310-cp310-win32.whl", hash = "sha256:75133890e40d229d6c5837b0312abbe5bac1c342452cf0e12523477cd3aa21e7", size = 3477507 }, - { url = "https://files.pythonhosted.org/packages/e9/39/597ce090da1097d2aabd2f9ef42187a6c9c8546d67c419ce61b88b336c85/lxml-5.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:de5b4e1088523e2b6f730d0509a9a813355b7f5659d70eb4f319c76beea2e250", size = 3805104 }, - { url = "https://files.pythonhosted.org/packages/81/2d/67693cc8a605a12e5975380d7ff83020dcc759351b5a066e1cced04f797b/lxml-5.4.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:98a3912194c079ef37e716ed228ae0dcb960992100461b704aea4e93af6b0bb9", size = 8083240 }, - { url = "https://files.pythonhosted.org/packages/73/53/b5a05ab300a808b72e848efd152fe9c022c0181b0a70b8bca1199f1bed26/lxml-5.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0ea0252b51d296a75f6118ed0d8696888e7403408ad42345d7dfd0d1e93309a7", size = 4387685 }, - { url = "https://files.pythonhosted.org/packages/d8/cb/1a3879c5f512bdcd32995c301886fe082b2edd83c87d41b6d42d89b4ea4d/lxml-5.4.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b92b69441d1bd39f4940f9eadfa417a25862242ca2c396b406f9272ef09cdcaa", size = 4991164 }, - { url = "https://files.pythonhosted.org/packages/f9/94/bbc66e42559f9d04857071e3b3d0c9abd88579367fd2588a4042f641f57e/lxml-5.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20e16c08254b9b6466526bc1828d9370ee6c0d60a4b64836bc3ac2917d1e16df", size = 4746206 }, - { url = "https://files.pythonhosted.org/packages/66/95/34b0679bee435da2d7cae895731700e519a8dfcab499c21662ebe671603e/lxml-5.4.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7605c1c32c3d6e8c990dd28a0970a3cbbf1429d5b92279e37fda05fb0c92190e", size = 5342144 }, - { url = "https://files.pythonhosted.org/packages/e0/5d/abfcc6ab2fa0be72b2ba938abdae1f7cad4c632f8d552683ea295d55adfb/lxml-5.4.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ecf4c4b83f1ab3d5a7ace10bafcb6f11df6156857a3c418244cef41ca9fa3e44", size = 4825124 }, - { url = "https://files.pythonhosted.org/packages/5a/78/6bd33186c8863b36e084f294fc0a5e5eefe77af95f0663ef33809cc1c8aa/lxml-5.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0cef4feae82709eed352cd7e97ae062ef6ae9c7b5dbe3663f104cd2c0e8d94ba", size = 4876520 }, - { url = "https://files.pythonhosted.org/packages/3b/74/4d7ad4839bd0fc64e3d12da74fc9a193febb0fae0ba6ebd5149d4c23176a/lxml-5.4.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:df53330a3bff250f10472ce96a9af28628ff1f4efc51ccba351a8820bca2a8ba", size = 4765016 }, - { url = "https://files.pythonhosted.org/packages/24/0d/0a98ed1f2471911dadfc541003ac6dd6879fc87b15e1143743ca20f3e973/lxml-5.4.0-cp311-cp311-manylinux_2_28_ppc64le.whl", hash = "sha256:aefe1a7cb852fa61150fcb21a8c8fcea7b58c4cb11fbe59c97a0a4b31cae3c8c", size = 5362884 }, - { url = "https://files.pythonhosted.org/packages/48/de/d4f7e4c39740a6610f0f6959052b547478107967362e8424e1163ec37ae8/lxml-5.4.0-cp311-cp311-manylinux_2_28_s390x.whl", hash = "sha256:ef5a7178fcc73b7d8c07229e89f8eb45b2908a9238eb90dcfc46571ccf0383b8", size = 4902690 }, - { url = "https://files.pythonhosted.org/packages/07/8c/61763abd242af84f355ca4ef1ee096d3c1b7514819564cce70fd18c22e9a/lxml-5.4.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:d2ed1b3cb9ff1c10e6e8b00941bb2e5bb568b307bfc6b17dffbbe8be5eecba86", size = 4944418 }, - { url = "https://files.pythonhosted.org/packages/f9/c5/6d7e3b63e7e282619193961a570c0a4c8a57fe820f07ca3fe2f6bd86608a/lxml-5.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:72ac9762a9f8ce74c9eed4a4e74306f2f18613a6b71fa065495a67ac227b3056", size = 4827092 }, - { url = "https://files.pythonhosted.org/packages/71/4a/e60a306df54680b103348545706a98a7514a42c8b4fbfdcaa608567bb065/lxml-5.4.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f5cb182f6396706dc6cc1896dd02b1c889d644c081b0cdec38747573db88a7d7", size = 5418231 }, - { url = "https://files.pythonhosted.org/packages/27/f2/9754aacd6016c930875854f08ac4b192a47fe19565f776a64004aa167521/lxml-5.4.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:3a3178b4873df8ef9457a4875703488eb1622632a9cee6d76464b60e90adbfcd", size = 5261798 }, - { url = "https://files.pythonhosted.org/packages/38/a2/0c49ec6941428b1bd4f280650d7b11a0f91ace9db7de32eb7aa23bcb39ff/lxml-5.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e094ec83694b59d263802ed03a8384594fcce477ce484b0cbcd0008a211ca751", size = 4988195 }, - { url = "https://files.pythonhosted.org/packages/7a/75/87a3963a08eafc46a86c1131c6e28a4de103ba30b5ae903114177352a3d7/lxml-5.4.0-cp311-cp311-win32.whl", hash = "sha256:4329422de653cdb2b72afa39b0aa04252fca9071550044904b2e7036d9d97fe4", size = 3474243 }, - { url = "https://files.pythonhosted.org/packages/fa/f9/1f0964c4f6c2be861c50db380c554fb8befbea98c6404744ce243a3c87ef/lxml-5.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:fd3be6481ef54b8cfd0e1e953323b7aa9d9789b94842d0e5b142ef4bb7999539", size = 3815197 }, - { url = "https://files.pythonhosted.org/packages/f8/4c/d101ace719ca6a4ec043eb516fcfcb1b396a9fccc4fcd9ef593df34ba0d5/lxml-5.4.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:b5aff6f3e818e6bdbbb38e5967520f174b18f539c2b9de867b1e7fde6f8d95a4", size = 8127392 }, - { url = "https://files.pythonhosted.org/packages/11/84/beddae0cec4dd9ddf46abf156f0af451c13019a0fa25d7445b655ba5ccb7/lxml-5.4.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:942a5d73f739ad7c452bf739a62a0f83e2578afd6b8e5406308731f4ce78b16d", size = 4415103 }, - { url = "https://files.pythonhosted.org/packages/d0/25/d0d93a4e763f0462cccd2b8a665bf1e4343dd788c76dcfefa289d46a38a9/lxml-5.4.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:460508a4b07364d6abf53acaa0a90b6d370fafde5693ef37602566613a9b0779", size = 5024224 }, - { url = "https://files.pythonhosted.org/packages/31/ce/1df18fb8f7946e7f3388af378b1f34fcf253b94b9feedb2cec5969da8012/lxml-5.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:529024ab3a505fed78fe3cc5ddc079464e709f6c892733e3f5842007cec8ac6e", size = 4769913 }, - { url = "https://files.pythonhosted.org/packages/4e/62/f4a6c60ae7c40d43657f552f3045df05118636be1165b906d3423790447f/lxml-5.4.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ca56ebc2c474e8f3d5761debfd9283b8b18c76c4fc0967b74aeafba1f5647f9", size = 5290441 }, - { url = "https://files.pythonhosted.org/packages/9e/aa/04f00009e1e3a77838c7fc948f161b5d2d5de1136b2b81c712a263829ea4/lxml-5.4.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a81e1196f0a5b4167a8dafe3a66aa67c4addac1b22dc47947abd5d5c7a3f24b5", size = 4820165 }, - { url = "https://files.pythonhosted.org/packages/c9/1f/e0b2f61fa2404bf0f1fdf1898377e5bd1b74cc9b2cf2c6ba8509b8f27990/lxml-5.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00b8686694423ddae324cf614e1b9659c2edb754de617703c3d29ff568448df5", size = 4932580 }, - { url = "https://files.pythonhosted.org/packages/24/a2/8263f351b4ffe0ed3e32ea7b7830f845c795349034f912f490180d88a877/lxml-5.4.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:c5681160758d3f6ac5b4fea370495c48aac0989d6a0f01bb9a72ad8ef5ab75c4", size = 4759493 }, - { url = "https://files.pythonhosted.org/packages/05/00/41db052f279995c0e35c79d0f0fc9f8122d5b5e9630139c592a0b58c71b4/lxml-5.4.0-cp312-cp312-manylinux_2_28_ppc64le.whl", hash = "sha256:2dc191e60425ad70e75a68c9fd90ab284df64d9cd410ba8d2b641c0c45bc006e", size = 5324679 }, - { url = "https://files.pythonhosted.org/packages/1d/be/ee99e6314cdef4587617d3b3b745f9356d9b7dd12a9663c5f3b5734b64ba/lxml-5.4.0-cp312-cp312-manylinux_2_28_s390x.whl", hash = "sha256:67f779374c6b9753ae0a0195a892a1c234ce8416e4448fe1e9f34746482070a7", size = 4890691 }, - { url = "https://files.pythonhosted.org/packages/ad/36/239820114bf1d71f38f12208b9c58dec033cbcf80101cde006b9bde5cffd/lxml-5.4.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:79d5bfa9c1b455336f52343130b2067164040604e41f6dc4d8313867ed540079", size = 4955075 }, - { url = "https://files.pythonhosted.org/packages/d4/e1/1b795cc0b174efc9e13dbd078a9ff79a58728a033142bc6d70a1ee8fc34d/lxml-5.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3d3c30ba1c9b48c68489dc1829a6eede9873f52edca1dda900066542528d6b20", size = 4838680 }, - { url = "https://files.pythonhosted.org/packages/72/48/3c198455ca108cec5ae3662ae8acd7fd99476812fd712bb17f1b39a0b589/lxml-5.4.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:1af80c6316ae68aded77e91cd9d80648f7dd40406cef73df841aa3c36f6907c8", size = 5391253 }, - { url = "https://files.pythonhosted.org/packages/d6/10/5bf51858971c51ec96cfc13e800a9951f3fd501686f4c18d7d84fe2d6352/lxml-5.4.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:4d885698f5019abe0de3d352caf9466d5de2baded00a06ef3f1216c1a58ae78f", size = 5261651 }, - { url = "https://files.pythonhosted.org/packages/2b/11/06710dd809205377da380546f91d2ac94bad9ff735a72b64ec029f706c85/lxml-5.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:aea53d51859b6c64e7c51d522c03cc2c48b9b5d6172126854cc7f01aa11f52bc", size = 5024315 }, - { url = "https://files.pythonhosted.org/packages/f5/b0/15b6217834b5e3a59ebf7f53125e08e318030e8cc0d7310355e6edac98ef/lxml-5.4.0-cp312-cp312-win32.whl", hash = "sha256:d90b729fd2732df28130c064aac9bb8aff14ba20baa4aee7bd0795ff1187545f", size = 3486149 }, - { url = "https://files.pythonhosted.org/packages/91/1e/05ddcb57ad2f3069101611bd5f5084157d90861a2ef460bf42f45cced944/lxml-5.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:1dc4ca99e89c335a7ed47d38964abcb36c5910790f9bd106f2a8fa2ee0b909d2", size = 3817095 }, - { url = "https://files.pythonhosted.org/packages/87/cb/2ba1e9dd953415f58548506fa5549a7f373ae55e80c61c9041b7fd09a38a/lxml-5.4.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:773e27b62920199c6197130632c18fb7ead3257fce1ffb7d286912e56ddb79e0", size = 8110086 }, - { url = "https://files.pythonhosted.org/packages/b5/3e/6602a4dca3ae344e8609914d6ab22e52ce42e3e1638c10967568c5c1450d/lxml-5.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ce9c671845de9699904b1e9df95acfe8dfc183f2310f163cdaa91a3535af95de", size = 4404613 }, - { url = "https://files.pythonhosted.org/packages/4c/72/bf00988477d3bb452bef9436e45aeea82bb40cdfb4684b83c967c53909c7/lxml-5.4.0-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9454b8d8200ec99a224df8854786262b1bd6461f4280064c807303c642c05e76", size = 5012008 }, - { url = "https://files.pythonhosted.org/packages/92/1f/93e42d93e9e7a44b2d3354c462cd784dbaaf350f7976b5d7c3f85d68d1b1/lxml-5.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cccd007d5c95279e529c146d095f1d39ac05139de26c098166c4beb9374b0f4d", size = 4760915 }, - { url = "https://files.pythonhosted.org/packages/45/0b/363009390d0b461cf9976a499e83b68f792e4c32ecef092f3f9ef9c4ba54/lxml-5.4.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0fce1294a0497edb034cb416ad3e77ecc89b313cff7adbee5334e4dc0d11f422", size = 5283890 }, - { url = "https://files.pythonhosted.org/packages/19/dc/6056c332f9378ab476c88e301e6549a0454dbee8f0ae16847414f0eccb74/lxml-5.4.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:24974f774f3a78ac12b95e3a20ef0931795ff04dbb16db81a90c37f589819551", size = 4812644 }, - { url = "https://files.pythonhosted.org/packages/ee/8a/f8c66bbb23ecb9048a46a5ef9b495fd23f7543df642dabeebcb2eeb66592/lxml-5.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:497cab4d8254c2a90bf988f162ace2ddbfdd806fce3bda3f581b9d24c852e03c", size = 4921817 }, - { url = "https://files.pythonhosted.org/packages/04/57/2e537083c3f381f83d05d9b176f0d838a9e8961f7ed8ddce3f0217179ce3/lxml-5.4.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:e794f698ae4c5084414efea0f5cc9f4ac562ec02d66e1484ff822ef97c2cadff", size = 4753916 }, - { url = "https://files.pythonhosted.org/packages/d8/80/ea8c4072109a350848f1157ce83ccd9439601274035cd045ac31f47f3417/lxml-5.4.0-cp313-cp313-manylinux_2_28_ppc64le.whl", hash = "sha256:2c62891b1ea3094bb12097822b3d44b93fc6c325f2043c4d2736a8ff09e65f60", size = 5289274 }, - { url = "https://files.pythonhosted.org/packages/b3/47/c4be287c48cdc304483457878a3f22999098b9a95f455e3c4bda7ec7fc72/lxml-5.4.0-cp313-cp313-manylinux_2_28_s390x.whl", hash = "sha256:142accb3e4d1edae4b392bd165a9abdee8a3c432a2cca193df995bc3886249c8", size = 4874757 }, - { url = "https://files.pythonhosted.org/packages/2f/04/6ef935dc74e729932e39478e44d8cfe6a83550552eaa072b7c05f6f22488/lxml-5.4.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:1a42b3a19346e5601d1b8296ff6ef3d76038058f311902edd574461e9c036982", size = 4947028 }, - { url = "https://files.pythonhosted.org/packages/cb/f9/c33fc8daa373ef8a7daddb53175289024512b6619bc9de36d77dca3df44b/lxml-5.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4291d3c409a17febf817259cb37bc62cb7eb398bcc95c1356947e2871911ae61", size = 4834487 }, - { url = "https://files.pythonhosted.org/packages/8d/30/fc92bb595bcb878311e01b418b57d13900f84c2b94f6eca9e5073ea756e6/lxml-5.4.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4f5322cf38fe0e21c2d73901abf68e6329dc02a4994e483adbcf92b568a09a54", size = 5381688 }, - { url = "https://files.pythonhosted.org/packages/43/d1/3ba7bd978ce28bba8e3da2c2e9d5ae3f8f521ad3f0ca6ea4788d086ba00d/lxml-5.4.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:0be91891bdb06ebe65122aa6bf3fc94489960cf7e03033c6f83a90863b23c58b", size = 5242043 }, - { url = "https://files.pythonhosted.org/packages/ee/cd/95fa2201041a610c4d08ddaf31d43b98ecc4b1d74b1e7245b1abdab443cb/lxml-5.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:15a665ad90054a3d4f397bc40f73948d48e36e4c09f9bcffc7d90c87410e478a", size = 5021569 }, - { url = "https://files.pythonhosted.org/packages/2d/a6/31da006fead660b9512d08d23d31e93ad3477dd47cc42e3285f143443176/lxml-5.4.0-cp313-cp313-win32.whl", hash = "sha256:d5663bc1b471c79f5c833cffbc9b87d7bf13f87e055a5c86c363ccd2348d7e82", size = 3485270 }, - { url = "https://files.pythonhosted.org/packages/fc/14/c115516c62a7d2499781d2d3d7215218c0731b2c940753bf9f9b7b73924d/lxml-5.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:bcb7a1096b4b6b24ce1ac24d4942ad98f983cd3810f9711bcd0293f43a9d8b9f", size = 3814606 }, - { url = "https://files.pythonhosted.org/packages/c6/b0/e4d1cbb8c078bc4ae44de9c6a79fec4e2b4151b1b4d50af71d799e76b177/lxml-5.4.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:1b717b00a71b901b4667226bba282dd462c42ccf618ade12f9ba3674e1fabc55", size = 3892319 }, - { url = "https://files.pythonhosted.org/packages/5b/aa/e2bdefba40d815059bcb60b371a36fbfcce970a935370e1b367ba1cc8f74/lxml-5.4.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:27a9ded0f0b52098ff89dd4c418325b987feed2ea5cc86e8860b0f844285d740", size = 4211614 }, - { url = "https://files.pythonhosted.org/packages/3c/5f/91ff89d1e092e7cfdd8453a939436ac116db0a665e7f4be0cd8e65c7dc5a/lxml-5.4.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b7ce10634113651d6f383aa712a194179dcd496bd8c41e191cec2099fa09de5", size = 4306273 }, - { url = "https://files.pythonhosted.org/packages/be/7c/8c3f15df2ca534589717bfd19d1e3482167801caedfa4d90a575facf68a6/lxml-5.4.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:53370c26500d22b45182f98847243efb518d268374a9570409d2e2276232fd37", size = 4208552 }, - { url = "https://files.pythonhosted.org/packages/7d/d8/9567afb1665f64d73fc54eb904e418d1138d7f011ed00647121b4dd60b38/lxml-5.4.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c6364038c519dffdbe07e3cf42e6a7f8b90c275d4d1617a69bb59734c1a2d571", size = 4331091 }, - { url = "https://files.pythonhosted.org/packages/f1/ab/fdbbd91d8d82bf1a723ba88ec3e3d76c022b53c391b0c13cad441cdb8f9e/lxml-5.4.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:b12cb6527599808ada9eb2cd6e0e7d3d8f13fe7bbb01c6311255a15ded4c7ab4", size = 3487862 }, +version = "6.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c5/ed/60eb6fa2923602fba988d9ca7c5cdbd7cf25faa795162ed538b527a35411/lxml-6.0.0.tar.gz", hash = "sha256:032e65120339d44cdc3efc326c9f660f5f7205f3a535c1fdbf898b29ea01fb72", size = 4096938 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4b/e9/9c3ca02fbbb7585116c2e274b354a2d92b5c70561687dd733ec7b2018490/lxml-6.0.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:35bc626eec405f745199200ccb5c6b36f202675d204aa29bb52e27ba2b71dea8", size = 8399057 }, + { url = "https://files.pythonhosted.org/packages/86/25/10a6e9001191854bf283515020f3633b1b1f96fd1b39aa30bf8fff7aa666/lxml-6.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:246b40f8a4aec341cbbf52617cad8ab7c888d944bfe12a6abd2b1f6cfb6f6082", size = 4569676 }, + { url = "https://files.pythonhosted.org/packages/f5/a5/378033415ff61d9175c81de23e7ad20a3ffb614df4ffc2ffc86bc6746ffd/lxml-6.0.0-cp310-cp310-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:2793a627e95d119e9f1e19720730472f5543a6d84c50ea33313ce328d870f2dd", size = 5291361 }, + { url = "https://files.pythonhosted.org/packages/5a/a6/19c87c4f3b9362b08dc5452a3c3bce528130ac9105fc8fff97ce895ce62e/lxml-6.0.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:46b9ed911f36bfeb6338e0b482e7fe7c27d362c52fde29f221fddbc9ee2227e7", size = 5008290 }, + { url = "https://files.pythonhosted.org/packages/09/d1/e9b7ad4b4164d359c4d87ed8c49cb69b443225cb495777e75be0478da5d5/lxml-6.0.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2b4790b558bee331a933e08883c423f65bbcd07e278f91b2272489e31ab1e2b4", size = 5163192 }, + { url = "https://files.pythonhosted.org/packages/56/d6/b3eba234dc1584744b0b374a7f6c26ceee5dc2147369a7e7526e25a72332/lxml-6.0.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e2030956cf4886b10be9a0285c6802e078ec2391e1dd7ff3eb509c2c95a69b76", size = 5076973 }, + { url = "https://files.pythonhosted.org/packages/8e/47/897142dd9385dcc1925acec0c4afe14cc16d310ce02c41fcd9010ac5d15d/lxml-6.0.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4d23854ecf381ab1facc8f353dcd9adeddef3652268ee75297c1164c987c11dc", size = 5297795 }, + { url = "https://files.pythonhosted.org/packages/fb/db/551ad84515c6f415cea70193a0ff11d70210174dc0563219f4ce711655c6/lxml-6.0.0-cp310-cp310-manylinux_2_31_armv7l.whl", hash = "sha256:43fe5af2d590bf4691531b1d9a2495d7aab2090547eaacd224a3afec95706d76", size = 4776547 }, + { url = "https://files.pythonhosted.org/packages/e0/14/c4a77ab4f89aaf35037a03c472f1ccc54147191888626079bd05babd6808/lxml-6.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:74e748012f8c19b47f7d6321ac929a9a94ee92ef12bc4298c47e8b7219b26541", size = 5124904 }, + { url = "https://files.pythonhosted.org/packages/70/b4/12ae6a51b8da106adec6a2e9c60f532350a24ce954622367f39269e509b1/lxml-6.0.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:43cfbb7db02b30ad3926e8fceaef260ba2fb7df787e38fa2df890c1ca7966c3b", size = 4805804 }, + { url = "https://files.pythonhosted.org/packages/a9/b6/2e82d34d49f6219cdcb6e3e03837ca5fb8b7f86c2f35106fb8610ac7f5b8/lxml-6.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:34190a1ec4f1e84af256495436b2d196529c3f2094f0af80202947567fdbf2e7", size = 5323477 }, + { url = "https://files.pythonhosted.org/packages/a1/e6/b83ddc903b05cd08a5723fefd528eee84b0edd07bdf87f6c53a1fda841fd/lxml-6.0.0-cp310-cp310-win32.whl", hash = "sha256:5967fe415b1920a3877a4195e9a2b779249630ee49ece22021c690320ff07452", size = 3613840 }, + { url = "https://files.pythonhosted.org/packages/40/af/874fb368dd0c663c030acb92612341005e52e281a102b72a4c96f42942e1/lxml-6.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:f3389924581d9a770c6caa4df4e74b606180869043b9073e2cec324bad6e306e", size = 3993584 }, + { url = "https://files.pythonhosted.org/packages/4a/f4/d296bc22c17d5607653008f6dd7b46afdfda12efd31021705b507df652bb/lxml-6.0.0-cp310-cp310-win_arm64.whl", hash = "sha256:522fe7abb41309e9543b0d9b8b434f2b630c5fdaf6482bee642b34c8c70079c8", size = 3681400 }, + { url = "https://files.pythonhosted.org/packages/7c/23/828d4cc7da96c611ec0ce6147bbcea2fdbde023dc995a165afa512399bbf/lxml-6.0.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4ee56288d0df919e4aac43b539dd0e34bb55d6a12a6562038e8d6f3ed07f9e36", size = 8438217 }, + { url = "https://files.pythonhosted.org/packages/f1/33/5ac521212c5bcb097d573145d54b2b4a3c9766cda88af5a0e91f66037c6e/lxml-6.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b8dd6dd0e9c1992613ccda2bcb74fc9d49159dbe0f0ca4753f37527749885c25", size = 4590317 }, + { url = "https://files.pythonhosted.org/packages/2b/2e/45b7ca8bee304c07f54933c37afe7dd4d39ff61ba2757f519dcc71bc5d44/lxml-6.0.0-cp311-cp311-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:d7ae472f74afcc47320238b5dbfd363aba111a525943c8a34a1b657c6be934c3", size = 5221628 }, + { url = "https://files.pythonhosted.org/packages/32/23/526d19f7eb2b85da1f62cffb2556f647b049ebe2a5aa8d4d41b1fb2c7d36/lxml-6.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5592401cdf3dc682194727c1ddaa8aa0f3ddc57ca64fd03226a430b955eab6f6", size = 4949429 }, + { url = "https://files.pythonhosted.org/packages/ac/cc/f6be27a5c656a43a5344e064d9ae004d4dcb1d3c9d4f323c8189ddfe4d13/lxml-6.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:58ffd35bd5425c3c3b9692d078bf7ab851441434531a7e517c4984d5634cd65b", size = 5087909 }, + { url = "https://files.pythonhosted.org/packages/3b/e6/8ec91b5bfbe6972458bc105aeb42088e50e4b23777170404aab5dfb0c62d/lxml-6.0.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f720a14aa102a38907c6d5030e3d66b3b680c3e6f6bc95473931ea3c00c59967", size = 5031713 }, + { url = "https://files.pythonhosted.org/packages/33/cf/05e78e613840a40e5be3e40d892c48ad3e475804db23d4bad751b8cadb9b/lxml-6.0.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c2a5e8d207311a0170aca0eb6b160af91adc29ec121832e4ac151a57743a1e1e", size = 5232417 }, + { url = "https://files.pythonhosted.org/packages/ac/8c/6b306b3e35c59d5f0b32e3b9b6b3b0739b32c0dc42a295415ba111e76495/lxml-6.0.0-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:2dd1cc3ea7e60bfb31ff32cafe07e24839df573a5e7c2d33304082a5019bcd58", size = 4681443 }, + { url = "https://files.pythonhosted.org/packages/59/43/0bd96bece5f7eea14b7220476835a60d2b27f8e9ca99c175f37c085cb154/lxml-6.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2cfcf84f1defed7e5798ef4f88aa25fcc52d279be731ce904789aa7ccfb7e8d2", size = 5074542 }, + { url = "https://files.pythonhosted.org/packages/e2/3d/32103036287a8ca012d8518071f8852c68f2b3bfe048cef2a0202eb05910/lxml-6.0.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:a52a4704811e2623b0324a18d41ad4b9fabf43ce5ff99b14e40a520e2190c851", size = 4729471 }, + { url = "https://files.pythonhosted.org/packages/ca/a8/7be5d17df12d637d81854bd8648cd329f29640a61e9a72a3f77add4a311b/lxml-6.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c16304bba98f48a28ae10e32a8e75c349dd742c45156f297e16eeb1ba9287a1f", size = 5256285 }, + { url = "https://files.pythonhosted.org/packages/cd/d0/6cb96174c25e0d749932557c8d51d60c6e292c877b46fae616afa23ed31a/lxml-6.0.0-cp311-cp311-win32.whl", hash = "sha256:f8d19565ae3eb956d84da3ef367aa7def14a2735d05bd275cd54c0301f0d0d6c", size = 3612004 }, + { url = "https://files.pythonhosted.org/packages/ca/77/6ad43b165dfc6dead001410adeb45e88597b25185f4479b7ca3b16a5808f/lxml-6.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:b2d71cdefda9424adff9a3607ba5bbfc60ee972d73c21c7e3c19e71037574816", size = 4003470 }, + { url = "https://files.pythonhosted.org/packages/a0/bc/4c50ec0eb14f932a18efc34fc86ee936a66c0eb5f2fe065744a2da8a68b2/lxml-6.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:8a2e76efbf8772add72d002d67a4c3d0958638696f541734304c7f28217a9cab", size = 3682477 }, + { url = "https://files.pythonhosted.org/packages/89/c3/d01d735c298d7e0ddcedf6f028bf556577e5ab4f4da45175ecd909c79378/lxml-6.0.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:78718d8454a6e928470d511bf8ac93f469283a45c354995f7d19e77292f26108", size = 8429515 }, + { url = "https://files.pythonhosted.org/packages/06/37/0e3eae3043d366b73da55a86274a590bae76dc45aa004b7042e6f97803b1/lxml-6.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:84ef591495ffd3f9dcabffd6391db7bb70d7230b5c35ef5148354a134f56f2be", size = 4601387 }, + { url = "https://files.pythonhosted.org/packages/a3/28/e1a9a881e6d6e29dda13d633885d13acb0058f65e95da67841c8dd02b4a8/lxml-6.0.0-cp312-cp312-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:2930aa001a3776c3e2601cb8e0a15d21b8270528d89cc308be4843ade546b9ab", size = 5228928 }, + { url = "https://files.pythonhosted.org/packages/9a/55/2cb24ea48aa30c99f805921c1c7860c1f45c0e811e44ee4e6a155668de06/lxml-6.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:219e0431ea8006e15005767f0351e3f7f9143e793e58519dc97fe9e07fae5563", size = 4952289 }, + { url = "https://files.pythonhosted.org/packages/31/c0/b25d9528df296b9a3306ba21ff982fc5b698c45ab78b94d18c2d6ae71fd9/lxml-6.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:bd5913b4972681ffc9718bc2d4c53cde39ef81415e1671ff93e9aa30b46595e7", size = 5111310 }, + { url = "https://files.pythonhosted.org/packages/e9/af/681a8b3e4f668bea6e6514cbcb297beb6de2b641e70f09d3d78655f4f44c/lxml-6.0.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:390240baeb9f415a82eefc2e13285016f9c8b5ad71ec80574ae8fa9605093cd7", size = 5025457 }, + { url = "https://files.pythonhosted.org/packages/99/b6/3a7971aa05b7be7dfebc7ab57262ec527775c2c3c5b2f43675cac0458cad/lxml-6.0.0-cp312-cp312-manylinux_2_27_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d6e200909a119626744dd81bae409fc44134389e03fbf1d68ed2a55a2fb10991", size = 5657016 }, + { url = "https://files.pythonhosted.org/packages/69/f8/693b1a10a891197143c0673fcce5b75fc69132afa81a36e4568c12c8faba/lxml-6.0.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ca50bd612438258a91b5b3788c6621c1f05c8c478e7951899f492be42defc0da", size = 5257565 }, + { url = "https://files.pythonhosted.org/packages/a8/96/e08ff98f2c6426c98c8964513c5dab8d6eb81dadcd0af6f0c538ada78d33/lxml-6.0.0-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:c24b8efd9c0f62bad0439283c2c795ef916c5a6b75f03c17799775c7ae3c0c9e", size = 4713390 }, + { url = "https://files.pythonhosted.org/packages/a8/83/6184aba6cc94d7413959f6f8f54807dc318fdcd4985c347fe3ea6937f772/lxml-6.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:afd27d8629ae94c5d863e32ab0e1d5590371d296b87dae0a751fb22bf3685741", size = 5066103 }, + { url = "https://files.pythonhosted.org/packages/ee/01/8bf1f4035852d0ff2e36a4d9aacdbcc57e93a6cd35a54e05fa984cdf73ab/lxml-6.0.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:54c4855eabd9fc29707d30141be99e5cd1102e7d2258d2892314cf4c110726c3", size = 4791428 }, + { url = "https://files.pythonhosted.org/packages/29/31/c0267d03b16954a85ed6b065116b621d37f559553d9339c7dcc4943a76f1/lxml-6.0.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c907516d49f77f6cd8ead1322198bdfd902003c3c330c77a1c5f3cc32a0e4d16", size = 5678523 }, + { url = "https://files.pythonhosted.org/packages/5c/f7/5495829a864bc5f8b0798d2b52a807c89966523140f3d6fa3a58ab6720ea/lxml-6.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:36531f81c8214e293097cd2b7873f178997dae33d3667caaae8bdfb9666b76c0", size = 5281290 }, + { url = "https://files.pythonhosted.org/packages/79/56/6b8edb79d9ed294ccc4e881f4db1023af56ba451909b9ce79f2a2cd7c532/lxml-6.0.0-cp312-cp312-win32.whl", hash = "sha256:690b20e3388a7ec98e899fd54c924e50ba6693874aa65ef9cb53de7f7de9d64a", size = 3613495 }, + { url = "https://files.pythonhosted.org/packages/0b/1e/cc32034b40ad6af80b6fd9b66301fc0f180f300002e5c3eb5a6110a93317/lxml-6.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:310b719b695b3dd442cdfbbe64936b2f2e231bb91d998e99e6f0daf991a3eba3", size = 4014711 }, + { url = "https://files.pythonhosted.org/packages/55/10/dc8e5290ae4c94bdc1a4c55865be7e1f31dfd857a88b21cbba68b5fea61b/lxml-6.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:8cb26f51c82d77483cdcd2b4a53cda55bbee29b3c2f3ddeb47182a2a9064e4eb", size = 3674431 }, + { url = "https://files.pythonhosted.org/packages/79/21/6e7c060822a3c954ff085e5e1b94b4a25757c06529eac91e550f3f5cd8b8/lxml-6.0.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6da7cd4f405fd7db56e51e96bff0865b9853ae70df0e6720624049da76bde2da", size = 8414372 }, + { url = "https://files.pythonhosted.org/packages/a4/f6/051b1607a459db670fc3a244fa4f06f101a8adf86cda263d1a56b3a4f9d5/lxml-6.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b34339898bb556a2351a1830f88f751679f343eabf9cf05841c95b165152c9e7", size = 4593940 }, + { url = "https://files.pythonhosted.org/packages/8e/74/dd595d92a40bda3c687d70d4487b2c7eff93fd63b568acd64fedd2ba00fe/lxml-6.0.0-cp313-cp313-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:51a5e4c61a4541bd1cd3ba74766d0c9b6c12d6a1a4964ef60026832aac8e79b3", size = 5214329 }, + { url = "https://files.pythonhosted.org/packages/52/46/3572761efc1bd45fcafb44a63b3b0feeb5b3f0066886821e94b0254f9253/lxml-6.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d18a25b19ca7307045581b18b3ec9ead2b1db5ccd8719c291f0cd0a5cec6cb81", size = 4947559 }, + { url = "https://files.pythonhosted.org/packages/94/8a/5e40de920e67c4f2eef9151097deb9b52d86c95762d8ee238134aff2125d/lxml-6.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d4f0c66df4386b75d2ab1e20a489f30dc7fd9a06a896d64980541506086be1f1", size = 5102143 }, + { url = "https://files.pythonhosted.org/packages/7c/4b/20555bdd75d57945bdabfbc45fdb1a36a1a0ff9eae4653e951b2b79c9209/lxml-6.0.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9f4b481b6cc3a897adb4279216695150bbe7a44c03daba3c894f49d2037e0a24", size = 5021931 }, + { url = "https://files.pythonhosted.org/packages/b6/6e/cf03b412f3763d4ca23b25e70c96a74cfece64cec3addf1c4ec639586b13/lxml-6.0.0-cp313-cp313-manylinux_2_27_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8a78d6c9168f5bcb20971bf3329c2b83078611fbe1f807baadc64afc70523b3a", size = 5645469 }, + { url = "https://files.pythonhosted.org/packages/d4/dd/39c8507c16db6031f8c1ddf70ed95dbb0a6d466a40002a3522c128aba472/lxml-6.0.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2ae06fbab4f1bb7db4f7c8ca9897dc8db4447d1a2b9bee78474ad403437bcc29", size = 5247467 }, + { url = "https://files.pythonhosted.org/packages/4d/56/732d49def0631ad633844cfb2664563c830173a98d5efd9b172e89a4800d/lxml-6.0.0-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:1fa377b827ca2023244a06554c6e7dc6828a10aaf74ca41965c5d8a4925aebb4", size = 4720601 }, + { url = "https://files.pythonhosted.org/packages/8f/7f/6b956fab95fa73462bca25d1ea7fc8274ddf68fb8e60b78d56c03b65278e/lxml-6.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1676b56d48048a62ef77a250428d1f31f610763636e0784ba67a9740823988ca", size = 5060227 }, + { url = "https://files.pythonhosted.org/packages/97/06/e851ac2924447e8b15a294855caf3d543424364a143c001014d22c8ca94c/lxml-6.0.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:0e32698462aacc5c1cf6bdfebc9c781821b7e74c79f13e5ffc8bfe27c42b1abf", size = 4790637 }, + { url = "https://files.pythonhosted.org/packages/06/d4/fd216f3cd6625022c25b336c7570d11f4a43adbaf0a56106d3d496f727a7/lxml-6.0.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4d6036c3a296707357efb375cfc24bb64cd955b9ec731abf11ebb1e40063949f", size = 5662049 }, + { url = "https://files.pythonhosted.org/packages/52/03/0e764ce00b95e008d76b99d432f1807f3574fb2945b496a17807a1645dbd/lxml-6.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7488a43033c958637b1a08cddc9188eb06d3ad36582cebc7d4815980b47e27ef", size = 5272430 }, + { url = "https://files.pythonhosted.org/packages/5f/01/d48cc141bc47bc1644d20fe97bbd5e8afb30415ec94f146f2f76d0d9d098/lxml-6.0.0-cp313-cp313-win32.whl", hash = "sha256:5fcd7d3b1d8ecb91445bd71b9c88bdbeae528fefee4f379895becfc72298d181", size = 3612896 }, + { url = "https://files.pythonhosted.org/packages/f4/87/6456b9541d186ee7d4cb53bf1b9a0d7f3b1068532676940fdd594ac90865/lxml-6.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:2f34687222b78fff795feeb799a7d44eca2477c3d9d3a46ce17d51a4f383e32e", size = 4013132 }, + { url = "https://files.pythonhosted.org/packages/b7/42/85b3aa8f06ca0d24962f8100f001828e1f1f1a38c954c16e71154ed7d53a/lxml-6.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:21db1ec5525780fd07251636eb5f7acb84003e9382c72c18c542a87c416ade03", size = 3672642 }, + { url = "https://files.pythonhosted.org/packages/66/e1/2c22a3cff9e16e1d717014a1e6ec2bf671bf56ea8716bb64466fcf820247/lxml-6.0.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:dbdd7679a6f4f08152818043dbb39491d1af3332128b3752c3ec5cebc0011a72", size = 3898804 }, + { url = "https://files.pythonhosted.org/packages/2b/3a/d68cbcb4393a2a0a867528741fafb7ce92dac5c9f4a1680df98e5e53e8f5/lxml-6.0.0-pp310-pypy310_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:40442e2a4456e9910875ac12951476d36c0870dcb38a68719f8c4686609897c4", size = 4216406 }, + { url = "https://files.pythonhosted.org/packages/15/8f/d9bfb13dff715ee3b2a1ec2f4a021347ea3caf9aba93dea0cfe54c01969b/lxml-6.0.0-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:db0efd6bae1c4730b9c863fc4f5f3c0fa3e8f05cae2c44ae141cb9dfc7d091dc", size = 4326455 }, + { url = "https://files.pythonhosted.org/packages/01/8b/fde194529ee8a27e6f5966d7eef05fa16f0567e4a8e8abc3b855ef6b3400/lxml-6.0.0-pp310-pypy310_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9ab542c91f5a47aaa58abdd8ea84b498e8e49fe4b883d67800017757a3eb78e8", size = 4268788 }, + { url = "https://files.pythonhosted.org/packages/99/a8/3b8e2581b4f8370fc9e8dc343af4abdfadd9b9229970fc71e67bd31c7df1/lxml-6.0.0-pp310-pypy310_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:013090383863b72c62a702d07678b658fa2567aa58d373d963cca245b017e065", size = 4411394 }, + { url = "https://files.pythonhosted.org/packages/e7/a5/899a4719e02ff4383f3f96e5d1878f882f734377f10dfb69e73b5f223e44/lxml-6.0.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c86df1c9af35d903d2b52d22ea3e66db8058d21dc0f59842ca5deb0595921141", size = 3517946 }, ] [package.optional-dependencies] @@ -1935,11 +2189,11 @@ wheels = [ [[package]] name = "markdown" -version = "3.8" +version = "3.8.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2f/15/222b423b0b88689c266d9eac4e61396fe2cc53464459d6a37618ac863b24/markdown-3.8.tar.gz", hash = "sha256:7df81e63f0df5c4b24b7d156eb81e4690595239b7d70937d0409f1b0de319c6f", size = 360906 } +sdist = { url = "https://files.pythonhosted.org/packages/d7/c2/4ab49206c17f75cb08d6311171f2d65798988db4360c4d1485bd0eedd67c/markdown-3.8.2.tar.gz", hash = "sha256:247b9a70dd12e27f67431ce62523e675b866d254f900c4fe75ce3dda62237c45", size = 362071 } wheels = [ - { url = "https://files.pythonhosted.org/packages/51/3f/afe76f8e2246ffbc867440cbcf90525264df0e658f8a5ca1f872b3f6192a/markdown-3.8-py3-none-any.whl", hash = "sha256:794a929b79c5af141ef5ab0f2f642d0f7b1872981250230e72682346f7cc90dc", size = 106210 }, + { url = "https://files.pythonhosted.org/packages/96/2b/34cc11786bc00d0f04d0f5fdc3a2b1ae0b6239eef72d3d345805f9ad92a1/markdown-3.8.2-py3-none-any.whl", hash = "sha256:5c83764dbd4e00bdd94d85a19b8d55ccca20fe35b2e678a1422b380324dd5f24", size = 106827 }, ] [[package]] @@ -2037,23 +2291,38 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/34/75/51952c7b2d3873b44a0028b1bd26a25078c18f92f256608e8d1dc61b39fd/marshmallow-3.26.1-py3-none-any.whl", hash = "sha256:3350409f20a70a7e4e11a27661187b77cdcaeb20abca41c1454fe33636bea09c", size = 50878 }, ] +[[package]] +name = "matplotlib-inline" +version = "0.1.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/99/5b/a36a337438a14116b16480db471ad061c36c3694df7c2084a0da7ba538b7/matplotlib_inline-0.1.7.tar.gz", hash = "sha256:8423b23ec666be3d16e16b60bdd8ac4e86e840ebd1dd11a30b9f117f2fa0ab90", size = 8159 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8f/8e/9ad090d3553c280a8060fbf6e24dc1c0c29704ee7d1c372f0c174aa59285/matplotlib_inline-0.1.7-py3-none-any.whl", hash = "sha256:df192d39a4ff8f21b1895d72e6a13f5fcc5099f00fa84384e0ea28c2cc0653ca", size = 9899 }, +] + [[package]] name = "mcp" -version = "1.5.0" +version = "1.12.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, { name = "httpx" }, { name = "httpx-sse" }, + { name = "jsonschema" }, { name = "pydantic" }, { name = "pydantic-settings" }, + { name = "python-multipart" }, + { name = "pywin32", marker = "sys_platform == 'win32'" }, { name = "sse-starlette" }, { name = "starlette" }, - { name = "uvicorn" }, + { name = "uvicorn", marker = "sys_platform != 'emscripten'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/6d/c9/c55764824e893fdebe777ac7223200986a275c3191dba9169f8eb6d7c978/mcp-1.5.0.tar.gz", hash = "sha256:5b2766c05e68e01a2034875e250139839498c61792163a7b221fc170c12f5aa9", size = 159128 } +sdist = { url = "https://files.pythonhosted.org/packages/45/94/caa0f4754e2437f7033068989f13fee784856f95870c786b0b5c2c0f511e/mcp-1.12.0.tar.gz", hash = "sha256:853f6b17a3f31ea6e2f278c2ec7d3b38457bc80c7c2c675260dd7f04a6fd0e70", size = 424678 } wheels = [ - { url = "https://files.pythonhosted.org/packages/c1/d1/3ff566ecf322077d861f1a68a1ff025cad337417bd66ad22a7c6f7dfcfaf/mcp-1.5.0-py3-none-any.whl", hash = "sha256:51c3f35ce93cb702f7513c12406bbea9665ef75a08db909200b07da9db641527", size = 73734 }, + { url = "https://files.pythonhosted.org/packages/ed/da/c7eaab6a58f1034de115b7902141ad8f81b4f3bbf7dc0cc267594947a4d7/mcp-1.12.0-py3-none-any.whl", hash = "sha256:19a498b2bf273283e463b4dd1ed83f791fbba5c25bfa16b8b34cfd5571673e7f", size = 158470 }, ] [[package]] @@ -2067,99 +2336,104 @@ wheels = [ [[package]] name = "multidict" -version = "6.4.4" +version = "6.6.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/91/2f/a3470242707058fe856fe59241eee5635d79087100b7042a867368863a27/multidict-6.4.4.tar.gz", hash = "sha256:69ee9e6ba214b5245031b76233dd95408a0fd57fdb019ddcc1ead4790932a8e8", size = 90183 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1f/92/0926a5baafa164b5d0ade3cd7932be39310375d7e25c9d7ceca05cb26a45/multidict-6.4.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8adee3ac041145ffe4488ea73fa0a622b464cc25340d98be76924d0cda8545ff", size = 66052 }, - { url = "https://files.pythonhosted.org/packages/b2/54/8a857ae4f8f643ec444d91f419fdd49cc7a90a2ca0e42d86482b604b63bd/multidict-6.4.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b61e98c3e2a861035aaccd207da585bdcacef65fe01d7a0d07478efac005e028", size = 38867 }, - { url = "https://files.pythonhosted.org/packages/9e/5f/63add9069f945c19bc8b217ea6b0f8a1ad9382eab374bb44fae4354b3baf/multidict-6.4.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:75493f28dbadecdbb59130e74fe935288813301a8554dc32f0c631b6bdcdf8b0", size = 38138 }, - { url = "https://files.pythonhosted.org/packages/97/8b/fbd9c0fc13966efdb4a47f5bcffff67a4f2a3189fbeead5766eaa4250b20/multidict-6.4.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ffc3c6a37e048b5395ee235e4a2a0d639c2349dffa32d9367a42fc20d399772", size = 220433 }, - { url = "https://files.pythonhosted.org/packages/a9/c4/5132b2d75b3ea2daedb14d10f91028f09f74f5b4d373b242c1b8eec47571/multidict-6.4.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:87cb72263946b301570b0f63855569a24ee8758aaae2cd182aae7d95fbc92ca7", size = 218059 }, - { url = "https://files.pythonhosted.org/packages/1a/70/f1e818c7a29b908e2d7b4fafb1d7939a41c64868e79de2982eea0a13193f/multidict-6.4.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9bbf7bd39822fd07e3609b6b4467af4c404dd2b88ee314837ad1830a7f4a8299", size = 231120 }, - { url = "https://files.pythonhosted.org/packages/b4/7e/95a194d85f27d5ef9cbe48dff9ded722fc6d12fedf641ec6e1e680890be7/multidict-6.4.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d1f7cbd4f1f44ddf5fd86a8675b7679176eae770f2fc88115d6dddb6cefb59bc", size = 227457 }, - { url = "https://files.pythonhosted.org/packages/25/2b/590ad220968d1babb42f265debe7be5c5c616df6c5688c995a06d8a9b025/multidict-6.4.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb5ac9e5bfce0e6282e7f59ff7b7b9a74aa8e5c60d38186a4637f5aa764046ad", size = 219111 }, - { url = "https://files.pythonhosted.org/packages/e0/f0/b07682b995d3fb5313f339b59d7de02db19ba0c02d1f77c27bdf8212d17c/multidict-6.4.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4efc31dfef8c4eeb95b6b17d799eedad88c4902daba39ce637e23a17ea078915", size = 213012 }, - { url = "https://files.pythonhosted.org/packages/24/56/c77b5f36feef2ec92f1119756e468ac9c3eebc35aa8a4c9e51df664cbbc9/multidict-6.4.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:9fcad2945b1b91c29ef2b4050f590bfcb68d8ac8e0995a74e659aa57e8d78e01", size = 225408 }, - { url = "https://files.pythonhosted.org/packages/cc/b3/e8189b82af9b198b47bc637766208fc917189eea91d674bad417e657bbdf/multidict-6.4.4-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:d877447e7368c7320832acb7159557e49b21ea10ffeb135c1077dbbc0816b598", size = 214396 }, - { url = "https://files.pythonhosted.org/packages/20/e0/200d14c84e35ae13ee99fd65dc106e1a1acb87a301f15e906fc7d5b30c17/multidict-6.4.4-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:33a12ebac9f380714c298cbfd3e5b9c0c4e89c75fe612ae496512ee51028915f", size = 222237 }, - { url = "https://files.pythonhosted.org/packages/13/f3/bb3df40045ca8262694a3245298732ff431dc781414a89a6a364ebac6840/multidict-6.4.4-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:0f14ea68d29b43a9bf37953881b1e3eb75b2739e896ba4a6aa4ad4c5b9ffa145", size = 231425 }, - { url = "https://files.pythonhosted.org/packages/85/3b/538563dc18514384dac169bcba938753ad9ab4d4c8d49b55d6ae49fb2579/multidict-6.4.4-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:0327ad2c747a6600e4797d115d3c38a220fdb28e54983abe8964fd17e95ae83c", size = 226251 }, - { url = "https://files.pythonhosted.org/packages/56/79/77e1a65513f09142358f1beb1d4cbc06898590b34a7de2e47023e3c5a3a2/multidict-6.4.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d1a20707492db9719a05fc62ee215fd2c29b22b47c1b1ba347f9abc831e26683", size = 220363 }, - { url = "https://files.pythonhosted.org/packages/16/57/67b0516c3e348f8daaa79c369b3de4359a19918320ab82e2e586a1c624ef/multidict-6.4.4-cp310-cp310-win32.whl", hash = "sha256:d83f18315b9fca5db2452d1881ef20f79593c4aa824095b62cb280019ef7aa3d", size = 35175 }, - { url = "https://files.pythonhosted.org/packages/86/5a/4ed8fec642d113fa653777cda30ef67aa5c8a38303c091e24c521278a6c6/multidict-6.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:9c17341ee04545fd962ae07330cb5a39977294c883485c8d74634669b1f7fe04", size = 38678 }, - { url = "https://files.pythonhosted.org/packages/19/1b/4c6e638195851524a63972c5773c7737bea7e47b1ba402186a37773acee2/multidict-6.4.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4f5f29794ac0e73d2a06ac03fd18870adc0135a9d384f4a306a951188ed02f95", size = 65515 }, - { url = "https://files.pythonhosted.org/packages/25/d5/10e6bca9a44b8af3c7f920743e5fc0c2bcf8c11bf7a295d4cfe00b08fb46/multidict-6.4.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c04157266344158ebd57b7120d9b0b35812285d26d0e78193e17ef57bfe2979a", size = 38609 }, - { url = "https://files.pythonhosted.org/packages/26/b4/91fead447ccff56247edc7f0535fbf140733ae25187a33621771ee598a18/multidict-6.4.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bb61ffd3ab8310d93427e460f565322c44ef12769f51f77277b4abad7b6f7223", size = 37871 }, - { url = "https://files.pythonhosted.org/packages/3b/37/cbc977cae59277e99d15bbda84cc53b5e0c4929ffd91d958347200a42ad0/multidict-6.4.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e0ba18a9afd495f17c351d08ebbc4284e9c9f7971d715f196b79636a4d0de44", size = 226661 }, - { url = "https://files.pythonhosted.org/packages/15/cd/7e0b57fbd4dc2fc105169c4ecce5be1a63970f23bb4ec8c721b67e11953d/multidict-6.4.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:9faf1b1dcaadf9f900d23a0e6d6c8eadd6a95795a0e57fcca73acce0eb912065", size = 223422 }, - { url = "https://files.pythonhosted.org/packages/f1/01/1de268da121bac9f93242e30cd3286f6a819e5f0b8896511162d6ed4bf8d/multidict-6.4.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a4d1cb1327c6082c4fce4e2a438483390964c02213bc6b8d782cf782c9b1471f", size = 235447 }, - { url = "https://files.pythonhosted.org/packages/d2/8c/8b9a5e4aaaf4f2de14e86181a3a3d7b105077f668b6a06f043ec794f684c/multidict-6.4.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:941f1bec2f5dbd51feeb40aea654c2747f811ab01bdd3422a48a4e4576b7d76a", size = 231455 }, - { url = "https://files.pythonhosted.org/packages/35/db/e1817dcbaa10b319c412769cf999b1016890849245d38905b73e9c286862/multidict-6.4.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e5f8a146184da7ea12910a4cec51ef85e44f6268467fb489c3caf0cd512f29c2", size = 223666 }, - { url = "https://files.pythonhosted.org/packages/4a/e1/66e8579290ade8a00e0126b3d9a93029033ffd84f0e697d457ed1814d0fc/multidict-6.4.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:232b7237e57ec3c09be97206bfb83a0aa1c5d7d377faa019c68a210fa35831f1", size = 217392 }, - { url = "https://files.pythonhosted.org/packages/7b/6f/f8639326069c24a48c7747c2a5485d37847e142a3f741ff3340c88060a9a/multidict-6.4.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:55ae0721c1513e5e3210bca4fc98456b980b0c2c016679d3d723119b6b202c42", size = 228969 }, - { url = "https://files.pythonhosted.org/packages/d2/c3/3d58182f76b960eeade51c89fcdce450f93379340457a328e132e2f8f9ed/multidict-6.4.4-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:51d662c072579f63137919d7bb8fc250655ce79f00c82ecf11cab678f335062e", size = 217433 }, - { url = "https://files.pythonhosted.org/packages/e1/4b/f31a562906f3bd375f3d0e83ce314e4a660c01b16c2923e8229b53fba5d7/multidict-6.4.4-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:0e05c39962baa0bb19a6b210e9b1422c35c093b651d64246b6c2e1a7e242d9fd", size = 225418 }, - { url = "https://files.pythonhosted.org/packages/99/89/78bb95c89c496d64b5798434a3deee21996114d4d2c28dd65850bf3a691e/multidict-6.4.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:d5b1cc3ab8c31d9ebf0faa6e3540fb91257590da330ffe6d2393d4208e638925", size = 235042 }, - { url = "https://files.pythonhosted.org/packages/74/91/8780a6e5885a8770442a8f80db86a0887c4becca0e5a2282ba2cae702bc4/multidict-6.4.4-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:93ec84488a384cd7b8a29c2c7f467137d8a73f6fe38bb810ecf29d1ade011a7c", size = 230280 }, - { url = "https://files.pythonhosted.org/packages/68/c1/fcf69cabd542eb6f4b892469e033567ee6991d361d77abdc55e3a0f48349/multidict-6.4.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b308402608493638763abc95f9dc0030bbd6ac6aff784512e8ac3da73a88af08", size = 223322 }, - { url = "https://files.pythonhosted.org/packages/b8/85/5b80bf4b83d8141bd763e1d99142a9cdfd0db83f0739b4797172a4508014/multidict-6.4.4-cp311-cp311-win32.whl", hash = "sha256:343892a27d1a04d6ae455ecece12904d242d299ada01633d94c4f431d68a8c49", size = 35070 }, - { url = "https://files.pythonhosted.org/packages/09/66/0bed198ffd590ab86e001f7fa46b740d58cf8ff98c2f254e4a36bf8861ad/multidict-6.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:73484a94f55359780c0f458bbd3c39cb9cf9c182552177d2136e828269dee529", size = 38667 }, - { url = "https://files.pythonhosted.org/packages/d2/b5/5675377da23d60875fe7dae6be841787755878e315e2f517235f22f59e18/multidict-6.4.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:dc388f75a1c00000824bf28b7633e40854f4127ede80512b44c3cfeeea1839a2", size = 64293 }, - { url = "https://files.pythonhosted.org/packages/34/a7/be384a482754bb8c95d2bbe91717bf7ccce6dc38c18569997a11f95aa554/multidict-6.4.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:98af87593a666f739d9dba5d0ae86e01b0e1a9cfcd2e30d2d361fbbbd1a9162d", size = 38096 }, - { url = "https://files.pythonhosted.org/packages/66/6d/d59854bb4352306145bdfd1704d210731c1bb2c890bfee31fb7bbc1c4c7f/multidict-6.4.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:aff4cafea2d120327d55eadd6b7f1136a8e5a0ecf6fb3b6863e8aca32cd8e50a", size = 37214 }, - { url = "https://files.pythonhosted.org/packages/99/e0/c29d9d462d7cfc5fc8f9bf24f9c6843b40e953c0b55e04eba2ad2cf54fba/multidict-6.4.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:169c4ba7858176b797fe551d6e99040c531c775d2d57b31bcf4de6d7a669847f", size = 224686 }, - { url = "https://files.pythonhosted.org/packages/dc/4a/da99398d7fd8210d9de068f9a1b5f96dfaf67d51e3f2521f17cba4ee1012/multidict-6.4.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b9eb4c59c54421a32b3273d4239865cb14ead53a606db066d7130ac80cc8ec93", size = 231061 }, - { url = "https://files.pythonhosted.org/packages/21/f5/ac11add39a0f447ac89353e6ca46666847051103649831c08a2800a14455/multidict-6.4.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7cf3bd54c56aa16fdb40028d545eaa8d051402b61533c21e84046e05513d5780", size = 232412 }, - { url = "https://files.pythonhosted.org/packages/d9/11/4b551e2110cded705a3c13a1d4b6a11f73891eb5a1c449f1b2b6259e58a6/multidict-6.4.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f682c42003c7264134bfe886376299db4cc0c6cd06a3295b41b347044bcb5482", size = 231563 }, - { url = "https://files.pythonhosted.org/packages/4c/02/751530c19e78fe73b24c3da66618eda0aa0d7f6e7aa512e46483de6be210/multidict-6.4.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a920f9cf2abdf6e493c519492d892c362007f113c94da4c239ae88429835bad1", size = 223811 }, - { url = "https://files.pythonhosted.org/packages/c7/cb/2be8a214643056289e51ca356026c7b2ce7225373e7a1f8c8715efee8988/multidict-6.4.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:530d86827a2df6504526106b4c104ba19044594f8722d3e87714e847c74a0275", size = 216524 }, - { url = "https://files.pythonhosted.org/packages/19/f3/6d5011ec375c09081f5250af58de85f172bfcaafebff286d8089243c4bd4/multidict-6.4.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ecde56ea2439b96ed8a8d826b50c57364612ddac0438c39e473fafad7ae1c23b", size = 229012 }, - { url = "https://files.pythonhosted.org/packages/67/9c/ca510785df5cf0eaf5b2a8132d7d04c1ce058dcf2c16233e596ce37a7f8e/multidict-6.4.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:dc8c9736d8574b560634775ac0def6bdc1661fc63fa27ffdfc7264c565bcb4f2", size = 226765 }, - { url = "https://files.pythonhosted.org/packages/36/c8/ca86019994e92a0f11e642bda31265854e6ea7b235642f0477e8c2e25c1f/multidict-6.4.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:7f3d3b3c34867579ea47cbd6c1f2ce23fbfd20a273b6f9e3177e256584f1eacc", size = 222888 }, - { url = "https://files.pythonhosted.org/packages/c6/67/bc25a8e8bd522935379066950ec4e2277f9b236162a73548a2576d4b9587/multidict-6.4.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:87a728af265e08f96b6318ebe3c0f68b9335131f461efab2fc64cc84a44aa6ed", size = 234041 }, - { url = "https://files.pythonhosted.org/packages/f1/a0/70c4c2d12857fccbe607b334b7ee28b6b5326c322ca8f73ee54e70d76484/multidict-6.4.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9f193eeda1857f8e8d3079a4abd258f42ef4a4bc87388452ed1e1c4d2b0c8740", size = 231046 }, - { url = "https://files.pythonhosted.org/packages/c1/0f/52954601d02d39742aab01d6b92f53c1dd38b2392248154c50797b4df7f1/multidict-6.4.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:be06e73c06415199200e9a2324a11252a3d62030319919cde5e6950ffeccf72e", size = 227106 }, - { url = "https://files.pythonhosted.org/packages/af/24/679d83ec4379402d28721790dce818e5d6b9f94ce1323a556fb17fa9996c/multidict-6.4.4-cp312-cp312-win32.whl", hash = "sha256:622f26ea6a7e19b7c48dd9228071f571b2fbbd57a8cd71c061e848f281550e6b", size = 35351 }, - { url = "https://files.pythonhosted.org/packages/52/ef/40d98bc5f986f61565f9b345f102409534e29da86a6454eb6b7c00225a13/multidict-6.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:5e2bcda30d5009996ff439e02a9f2b5c3d64a20151d34898c000a6281faa3781", size = 38791 }, - { url = "https://files.pythonhosted.org/packages/df/2a/e166d2ffbf4b10131b2d5b0e458f7cee7d986661caceae0de8753042d4b2/multidict-6.4.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:82ffabefc8d84c2742ad19c37f02cde5ec2a1ee172d19944d380f920a340e4b9", size = 64123 }, - { url = "https://files.pythonhosted.org/packages/8c/96/e200e379ae5b6f95cbae472e0199ea98913f03d8c9a709f42612a432932c/multidict-6.4.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:6a2f58a66fe2c22615ad26156354005391e26a2f3721c3621504cd87c1ea87bf", size = 38049 }, - { url = "https://files.pythonhosted.org/packages/75/fb/47afd17b83f6a8c7fa863c6d23ac5ba6a0e6145ed8a6bcc8da20b2b2c1d2/multidict-6.4.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5883d6ee0fd9d8a48e9174df47540b7545909841ac82354c7ae4cbe9952603bd", size = 37078 }, - { url = "https://files.pythonhosted.org/packages/fa/70/1af3143000eddfb19fd5ca5e78393985ed988ac493bb859800fe0914041f/multidict-6.4.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9abcf56a9511653fa1d052bfc55fbe53dbee8f34e68bd6a5a038731b0ca42d15", size = 224097 }, - { url = "https://files.pythonhosted.org/packages/b1/39/d570c62b53d4fba844e0378ffbcd02ac25ca423d3235047013ba2f6f60f8/multidict-6.4.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6ed5ae5605d4ad5a049fad2a28bb7193400700ce2f4ae484ab702d1e3749c3f9", size = 230768 }, - { url = "https://files.pythonhosted.org/packages/fd/f8/ed88f2c4d06f752b015933055eb291d9bc184936903752c66f68fb3c95a7/multidict-6.4.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bbfcb60396f9bcfa63e017a180c3105b8c123a63e9d1428a36544e7d37ca9e20", size = 231331 }, - { url = "https://files.pythonhosted.org/packages/9c/6f/8e07cffa32f483ab887b0d56bbd8747ac2c1acd00dc0af6fcf265f4a121e/multidict-6.4.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b0f1987787f5f1e2076b59692352ab29a955b09ccc433c1f6b8e8e18666f608b", size = 230169 }, - { url = "https://files.pythonhosted.org/packages/e6/2b/5dcf173be15e42f330110875a2668ddfc208afc4229097312212dc9c1236/multidict-6.4.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1d0121ccce8c812047d8d43d691a1ad7641f72c4f730474878a5aeae1b8ead8c", size = 222947 }, - { url = "https://files.pythonhosted.org/packages/39/75/4ddcbcebe5ebcd6faa770b629260d15840a5fc07ce8ad295a32e14993726/multidict-6.4.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:83ec4967114295b8afd120a8eec579920c882831a3e4c3331d591a8e5bfbbc0f", size = 215761 }, - { url = "https://files.pythonhosted.org/packages/6a/c9/55e998ae45ff15c5608e384206aa71a11e1b7f48b64d166db400b14a3433/multidict-6.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:995f985e2e268deaf17867801b859a282e0448633f1310e3704b30616d269d69", size = 227605 }, - { url = "https://files.pythonhosted.org/packages/04/49/c2404eac74497503c77071bd2e6f88c7e94092b8a07601536b8dbe99be50/multidict-6.4.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:d832c608f94b9f92a0ec8b7e949be7792a642b6e535fcf32f3e28fab69eeb046", size = 226144 }, - { url = "https://files.pythonhosted.org/packages/62/c5/0cd0c3c6f18864c40846aa2252cd69d308699cb163e1c0d989ca301684da/multidict-6.4.4-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d21c1212171cf7da703c5b0b7a0e85be23b720818aef502ad187d627316d5645", size = 221100 }, - { url = "https://files.pythonhosted.org/packages/71/7b/f2f3887bea71739a046d601ef10e689528d4f911d84da873b6be9194ffea/multidict-6.4.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:cbebaa076aaecad3d4bb4c008ecc73b09274c952cf6a1b78ccfd689e51f5a5b0", size = 232731 }, - { url = "https://files.pythonhosted.org/packages/e5/b3/d9de808349df97fa75ec1372758701b5800ebad3c46ae377ad63058fbcc6/multidict-6.4.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:c93a6fb06cc8e5d3628b2b5fda215a5db01e8f08fc15fadd65662d9b857acbe4", size = 229637 }, - { url = "https://files.pythonhosted.org/packages/5e/57/13207c16b615eb4f1745b44806a96026ef8e1b694008a58226c2d8f5f0a5/multidict-6.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8cd8f81f1310182362fb0c7898145ea9c9b08a71081c5963b40ee3e3cac589b1", size = 225594 }, - { url = "https://files.pythonhosted.org/packages/3a/e4/d23bec2f70221604f5565000632c305fc8f25ba953e8ce2d8a18842b9841/multidict-6.4.4-cp313-cp313-win32.whl", hash = "sha256:3e9f1cd61a0ab857154205fb0b1f3d3ace88d27ebd1409ab7af5096e409614cd", size = 35359 }, - { url = "https://files.pythonhosted.org/packages/a7/7a/cfe1a47632be861b627f46f642c1d031704cc1c0f5c0efbde2ad44aa34bd/multidict-6.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:8ffb40b74400e4455785c2fa37eba434269149ec525fc8329858c862e4b35373", size = 38903 }, - { url = "https://files.pythonhosted.org/packages/68/7b/15c259b0ab49938a0a1c8f3188572802704a779ddb294edc1b2a72252e7c/multidict-6.4.4-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:6a602151dbf177be2450ef38966f4be3467d41a86c6a845070d12e17c858a156", size = 68895 }, - { url = "https://files.pythonhosted.org/packages/f1/7d/168b5b822bccd88142e0a3ce985858fea612404edd228698f5af691020c9/multidict-6.4.4-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0d2b9712211b860d123815a80b859075d86a4d54787e247d7fbee9db6832cf1c", size = 40183 }, - { url = "https://files.pythonhosted.org/packages/e0/b7/d4b8d98eb850ef28a4922ba508c31d90715fd9b9da3801a30cea2967130b/multidict-6.4.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:d2fa86af59f8fc1972e121ade052145f6da22758f6996a197d69bb52f8204e7e", size = 39592 }, - { url = "https://files.pythonhosted.org/packages/18/28/a554678898a19583548e742080cf55d169733baf57efc48c2f0273a08583/multidict-6.4.4-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50855d03e9e4d66eab6947ba688ffb714616f985838077bc4b490e769e48da51", size = 226071 }, - { url = "https://files.pythonhosted.org/packages/ee/dc/7ba6c789d05c310e294f85329efac1bf5b450338d2542498db1491a264df/multidict-6.4.4-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:5bce06b83be23225be1905dcdb6b789064fae92499fbc458f59a8c0e68718601", size = 222597 }, - { url = "https://files.pythonhosted.org/packages/24/4f/34eadbbf401b03768dba439be0fb94b0d187facae9142821a3d5599ccb3b/multidict-6.4.4-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:66ed0731f8e5dfd8369a883b6e564aca085fb9289aacabd9decd70568b9a30de", size = 228253 }, - { url = "https://files.pythonhosted.org/packages/c0/e6/493225a3cdb0d8d80d43a94503fc313536a07dae54a3f030d279e629a2bc/multidict-6.4.4-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:329ae97fc2f56f44d91bc47fe0972b1f52d21c4b7a2ac97040da02577e2daca2", size = 226146 }, - { url = "https://files.pythonhosted.org/packages/2f/70/e411a7254dc3bff6f7e6e004303b1b0591358e9f0b7c08639941e0de8bd6/multidict-6.4.4-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c27e5dcf520923d6474d98b96749e6805f7677e93aaaf62656005b8643f907ab", size = 220585 }, - { url = "https://files.pythonhosted.org/packages/08/8f/beb3ae7406a619100d2b1fb0022c3bb55a8225ab53c5663648ba50dfcd56/multidict-6.4.4-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:058cc59b9e9b143cc56715e59e22941a5d868c322242278d28123a5d09cdf6b0", size = 212080 }, - { url = "https://files.pythonhosted.org/packages/9c/ec/355124e9d3d01cf8edb072fd14947220f357e1c5bc79c88dff89297e9342/multidict-6.4.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:69133376bc9a03f8c47343d33f91f74a99c339e8b58cea90433d8e24bb298031", size = 226558 }, - { url = "https://files.pythonhosted.org/packages/fd/22/d2b95cbebbc2ada3be3812ea9287dcc9712d7f1a012fad041770afddb2ad/multidict-6.4.4-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:d6b15c55721b1b115c5ba178c77104123745b1417527ad9641a4c5e2047450f0", size = 212168 }, - { url = "https://files.pythonhosted.org/packages/4d/c5/62bfc0b2f9ce88326dbe7179f9824a939c6c7775b23b95de777267b9725c/multidict-6.4.4-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:a887b77f51d3d41e6e1a63cf3bc7ddf24de5939d9ff69441387dfefa58ac2e26", size = 217970 }, - { url = "https://files.pythonhosted.org/packages/79/74/977cea1aadc43ff1c75d23bd5bc4768a8fac98c14e5878d6ee8d6bab743c/multidict-6.4.4-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:632a3bf8f1787f7ef7d3c2f68a7bde5be2f702906f8b5842ad6da9d974d0aab3", size = 226980 }, - { url = "https://files.pythonhosted.org/packages/48/fc/cc4a1a2049df2eb84006607dc428ff237af38e0fcecfdb8a29ca47b1566c/multidict-6.4.4-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:a145c550900deb7540973c5cdb183b0d24bed6b80bf7bddf33ed8f569082535e", size = 220641 }, - { url = "https://files.pythonhosted.org/packages/3b/6a/a7444d113ab918701988d4abdde373dbdfd2def7bd647207e2bf645c7eac/multidict-6.4.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:cc5d83c6619ca5c9672cb78b39ed8542f1975a803dee2cda114ff73cbb076edd", size = 221728 }, - { url = "https://files.pythonhosted.org/packages/2b/b0/fdf4c73ad1c55e0f4dbbf2aa59dd37037334091f9a4961646d2b7ac91a86/multidict-6.4.4-cp313-cp313t-win32.whl", hash = "sha256:3312f63261b9df49be9d57aaa6abf53a6ad96d93b24f9cc16cf979956355ce6e", size = 41913 }, - { url = "https://files.pythonhosted.org/packages/8e/92/27989ecca97e542c0d01d05a98a5ae12198a243a9ee12563a0313291511f/multidict-6.4.4-cp313-cp313t-win_amd64.whl", hash = "sha256:ba852168d814b2c73333073e1c7116d9395bea69575a01b0b3c89d2d5a87c8fb", size = 46112 }, - { url = "https://files.pythonhosted.org/packages/84/5d/e17845bb0fa76334477d5de38654d27946d5b5d3695443987a094a71b440/multidict-6.4.4-py3-none-any.whl", hash = "sha256:bd4557071b561a8b3b6075c3ce93cf9bfb6182cb241805c3d66ced3b75eff4ac", size = 10481 }, +sdist = { url = "https://files.pythonhosted.org/packages/3d/2c/5dad12e82fbdf7470f29bff2171484bf07cb3b16ada60a6589af8f376440/multidict-6.6.3.tar.gz", hash = "sha256:798a9eb12dab0a6c2e29c1de6f3468af5cb2da6053a20dfa3344907eed0937cc", size = 101006 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0b/67/414933982bce2efce7cbcb3169eaaf901e0f25baec69432b4874dfb1f297/multidict-6.6.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a2be5b7b35271f7fff1397204ba6708365e3d773579fe2a30625e16c4b4ce817", size = 77017 }, + { url = "https://files.pythonhosted.org/packages/8a/fe/d8a3ee1fad37dc2ef4f75488b0d9d4f25bf204aad8306cbab63d97bff64a/multidict-6.6.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:12f4581d2930840295c461764b9a65732ec01250b46c6b2c510d7ee68872b140", size = 44897 }, + { url = "https://files.pythonhosted.org/packages/1f/e0/265d89af8c98240265d82b8cbcf35897f83b76cd59ee3ab3879050fd8c45/multidict-6.6.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dd7793bab517e706c9ed9d7310b06c8672fd0aeee5781bfad612f56b8e0f7d14", size = 44574 }, + { url = "https://files.pythonhosted.org/packages/e6/05/6b759379f7e8e04ccc97cfb2a5dcc5cdbd44a97f072b2272dc51281e6a40/multidict-6.6.3-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:72d8815f2cd3cf3df0f83cac3f3ef801d908b2d90409ae28102e0553af85545a", size = 225729 }, + { url = "https://files.pythonhosted.org/packages/4e/f5/8d5a15488edd9a91fa4aad97228d785df208ed6298580883aa3d9def1959/multidict-6.6.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:531e331a2ee53543ab32b16334e2deb26f4e6b9b28e41f8e0c87e99a6c8e2d69", size = 242515 }, + { url = "https://files.pythonhosted.org/packages/6e/b5/a8f317d47d0ac5bb746d6d8325885c8967c2a8ce0bb57be5399e3642cccb/multidict-6.6.3-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:42ca5aa9329a63be8dc49040f63817d1ac980e02eeddba763a9ae5b4027b9c9c", size = 222224 }, + { url = "https://files.pythonhosted.org/packages/76/88/18b2a0d5e80515fa22716556061189c2853ecf2aa2133081ebbe85ebea38/multidict-6.6.3-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:208b9b9757060b9faa6f11ab4bc52846e4f3c2fb8b14d5680c8aac80af3dc751", size = 253124 }, + { url = "https://files.pythonhosted.org/packages/62/bf/ebfcfd6b55a1b05ef16d0775ae34c0fe15e8dab570d69ca9941073b969e7/multidict-6.6.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:acf6b97bd0884891af6a8b43d0f586ab2fcf8e717cbd47ab4bdddc09e20652d8", size = 251529 }, + { url = "https://files.pythonhosted.org/packages/44/11/780615a98fd3775fc309d0234d563941af69ade2df0bb82c91dda6ddaea1/multidict-6.6.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:68e9e12ed00e2089725669bdc88602b0b6f8d23c0c95e52b95f0bc69f7fe9b55", size = 241627 }, + { url = "https://files.pythonhosted.org/packages/28/3d/35f33045e21034b388686213752cabc3a1b9d03e20969e6fa8f1b1d82db1/multidict-6.6.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:05db2f66c9addb10cfa226e1acb363450fab2ff8a6df73c622fefe2f5af6d4e7", size = 239351 }, + { url = "https://files.pythonhosted.org/packages/6e/cc/ff84c03b95b430015d2166d9aae775a3985d757b94f6635010d0038d9241/multidict-6.6.3-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:0db58da8eafb514db832a1b44f8fa7906fdd102f7d982025f816a93ba45e3dcb", size = 233429 }, + { url = "https://files.pythonhosted.org/packages/2e/f0/8cd49a0b37bdea673a4b793c2093f2f4ba8e7c9d6d7c9bd672fd6d38cd11/multidict-6.6.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:14117a41c8fdb3ee19c743b1c027da0736fdb79584d61a766da53d399b71176c", size = 243094 }, + { url = "https://files.pythonhosted.org/packages/96/19/5d9a0cfdafe65d82b616a45ae950975820289069f885328e8185e64283c2/multidict-6.6.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:877443eaaabcd0b74ff32ebeed6f6176c71850feb7d6a1d2db65945256ea535c", size = 248957 }, + { url = "https://files.pythonhosted.org/packages/e6/dc/c90066151da87d1e489f147b9b4327927241e65f1876702fafec6729c014/multidict-6.6.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:70b72e749a4f6e7ed8fb334fa8d8496384840319512746a5f42fa0aec79f4d61", size = 243590 }, + { url = "https://files.pythonhosted.org/packages/ec/39/458afb0cccbb0ee9164365273be3e039efddcfcb94ef35924b7dbdb05db0/multidict-6.6.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:43571f785b86afd02b3855c5ac8e86ec921b760298d6f82ff2a61daf5a35330b", size = 237487 }, + { url = "https://files.pythonhosted.org/packages/35/38/0016adac3990426610a081787011177e661875546b434f50a26319dc8372/multidict-6.6.3-cp310-cp310-win32.whl", hash = "sha256:20c5a0c3c13a15fd5ea86c42311859f970070e4e24de5a550e99d7c271d76318", size = 41390 }, + { url = "https://files.pythonhosted.org/packages/f3/d2/17897a8f3f2c5363d969b4c635aa40375fe1f09168dc09a7826780bfb2a4/multidict-6.6.3-cp310-cp310-win_amd64.whl", hash = "sha256:ab0a34a007704c625e25a9116c6770b4d3617a071c8a7c30cd338dfbadfe6485", size = 45954 }, + { url = "https://files.pythonhosted.org/packages/2d/5f/d4a717c1e457fe44072e33fa400d2b93eb0f2819c4d669381f925b7cba1f/multidict-6.6.3-cp310-cp310-win_arm64.whl", hash = "sha256:769841d70ca8bdd140a715746199fc6473414bd02efd678d75681d2d6a8986c5", size = 42981 }, + { url = "https://files.pythonhosted.org/packages/08/f0/1a39863ced51f639c81a5463fbfa9eb4df59c20d1a8769ab9ef4ca57ae04/multidict-6.6.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:18f4eba0cbac3546b8ae31e0bbc55b02c801ae3cbaf80c247fcdd89b456ff58c", size = 76445 }, + { url = "https://files.pythonhosted.org/packages/c9/0e/a7cfa451c7b0365cd844e90b41e21fab32edaa1e42fc0c9f68461ce44ed7/multidict-6.6.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ef43b5dd842382329e4797c46f10748d8c2b6e0614f46b4afe4aee9ac33159df", size = 44610 }, + { url = "https://files.pythonhosted.org/packages/c6/bb/a14a4efc5ee748cc1904b0748be278c31b9295ce5f4d2ef66526f410b94d/multidict-6.6.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bf9bd1fd5eec01494e0f2e8e446a74a85d5e49afb63d75a9934e4a5423dba21d", size = 44267 }, + { url = "https://files.pythonhosted.org/packages/c2/f8/410677d563c2d55e063ef74fe578f9d53fe6b0a51649597a5861f83ffa15/multidict-6.6.3-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:5bd8d6f793a787153956cd35e24f60485bf0651c238e207b9a54f7458b16d539", size = 230004 }, + { url = "https://files.pythonhosted.org/packages/fd/df/2b787f80059314a98e1ec6a4cc7576244986df3e56b3c755e6fc7c99e038/multidict-6.6.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1bf99b4daf908c73856bd87ee0a2499c3c9a3d19bb04b9c6025e66af3fd07462", size = 247196 }, + { url = "https://files.pythonhosted.org/packages/05/f2/f9117089151b9a8ab39f9019620d10d9718eec2ac89e7ca9d30f3ec78e96/multidict-6.6.3-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0b9e59946b49dafaf990fd9c17ceafa62976e8471a14952163d10a7a630413a9", size = 225337 }, + { url = "https://files.pythonhosted.org/packages/93/2d/7115300ec5b699faa152c56799b089a53ed69e399c3c2d528251f0aeda1a/multidict-6.6.3-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e2db616467070d0533832d204c54eea6836a5e628f2cb1e6dfd8cd6ba7277cb7", size = 257079 }, + { url = "https://files.pythonhosted.org/packages/15/ea/ff4bab367623e39c20d3b07637225c7688d79e4f3cc1f3b9f89867677f9a/multidict-6.6.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7394888236621f61dcdd25189b2768ae5cc280f041029a5bcf1122ac63df79f9", size = 255461 }, + { url = "https://files.pythonhosted.org/packages/74/07/2c9246cda322dfe08be85f1b8739646f2c4c5113a1422d7a407763422ec4/multidict-6.6.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f114d8478733ca7388e7c7e0ab34b72547476b97009d643644ac33d4d3fe1821", size = 246611 }, + { url = "https://files.pythonhosted.org/packages/a8/62/279c13d584207d5697a752a66ffc9bb19355a95f7659140cb1b3cf82180e/multidict-6.6.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cdf22e4db76d323bcdc733514bf732e9fb349707c98d341d40ebcc6e9318ef3d", size = 243102 }, + { url = "https://files.pythonhosted.org/packages/69/cc/e06636f48c6d51e724a8bc8d9e1db5f136fe1df066d7cafe37ef4000f86a/multidict-6.6.3-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:e995a34c3d44ab511bfc11aa26869b9d66c2d8c799fa0e74b28a473a692532d6", size = 238693 }, + { url = "https://files.pythonhosted.org/packages/89/a4/66c9d8fb9acf3b226cdd468ed009537ac65b520aebdc1703dd6908b19d33/multidict-6.6.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:766a4a5996f54361d8d5a9050140aa5362fe48ce51c755a50c0bc3706460c430", size = 246582 }, + { url = "https://files.pythonhosted.org/packages/cf/01/c69e0317be556e46257826d5449feb4e6aa0d18573e567a48a2c14156f1f/multidict-6.6.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:3893a0d7d28a7fe6ca7a1f760593bc13038d1d35daf52199d431b61d2660602b", size = 253355 }, + { url = "https://files.pythonhosted.org/packages/c0/da/9cc1da0299762d20e626fe0042e71b5694f9f72d7d3f9678397cbaa71b2b/multidict-6.6.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:934796c81ea996e61914ba58064920d6cad5d99140ac3167901eb932150e2e56", size = 247774 }, + { url = "https://files.pythonhosted.org/packages/e6/91/b22756afec99cc31105ddd4a52f95ab32b1a4a58f4d417979c570c4a922e/multidict-6.6.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9ed948328aec2072bc00f05d961ceadfd3e9bfc2966c1319aeaf7b7c21219183", size = 242275 }, + { url = "https://files.pythonhosted.org/packages/be/f1/adcc185b878036a20399d5be5228f3cbe7f823d78985d101d425af35c800/multidict-6.6.3-cp311-cp311-win32.whl", hash = "sha256:9f5b28c074c76afc3e4c610c488e3493976fe0e596dd3db6c8ddfbb0134dcac5", size = 41290 }, + { url = "https://files.pythonhosted.org/packages/e0/d4/27652c1c6526ea6b4f5ddd397e93f4232ff5de42bea71d339bc6a6cc497f/multidict-6.6.3-cp311-cp311-win_amd64.whl", hash = "sha256:bc7f6fbc61b1c16050a389c630da0b32fc6d4a3d191394ab78972bf5edc568c2", size = 45942 }, + { url = "https://files.pythonhosted.org/packages/16/18/23f4932019804e56d3c2413e237f866444b774b0263bcb81df2fdecaf593/multidict-6.6.3-cp311-cp311-win_arm64.whl", hash = "sha256:d4e47d8faffaae822fb5cba20937c048d4f734f43572e7079298a6c39fb172cb", size = 42880 }, + { url = "https://files.pythonhosted.org/packages/0e/a0/6b57988ea102da0623ea814160ed78d45a2645e4bbb499c2896d12833a70/multidict-6.6.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:056bebbeda16b2e38642d75e9e5310c484b7c24e3841dc0fb943206a72ec89d6", size = 76514 }, + { url = "https://files.pythonhosted.org/packages/07/7a/d1e92665b0850c6c0508f101f9cf0410c1afa24973e1115fe9c6a185ebf7/multidict-6.6.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:e5f481cccb3c5c5e5de5d00b5141dc589c1047e60d07e85bbd7dea3d4580d63f", size = 45394 }, + { url = "https://files.pythonhosted.org/packages/52/6f/dd104490e01be6ef8bf9573705d8572f8c2d2c561f06e3826b081d9e6591/multidict-6.6.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:10bea2ee839a759ee368b5a6e47787f399b41e70cf0c20d90dfaf4158dfb4e55", size = 43590 }, + { url = "https://files.pythonhosted.org/packages/44/fe/06e0e01b1b0611e6581b7fd5a85b43dacc08b6cea3034f902f383b0873e5/multidict-6.6.3-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:2334cfb0fa9549d6ce2c21af2bfbcd3ac4ec3646b1b1581c88e3e2b1779ec92b", size = 237292 }, + { url = "https://files.pythonhosted.org/packages/ce/71/4f0e558fb77696b89c233c1ee2d92f3e1d5459070a0e89153c9e9e804186/multidict-6.6.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b8fee016722550a2276ca2cb5bb624480e0ed2bd49125b2b73b7010b9090e888", size = 258385 }, + { url = "https://files.pythonhosted.org/packages/e3/25/cca0e68228addad24903801ed1ab42e21307a1b4b6dd2cf63da5d3ae082a/multidict-6.6.3-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5511cb35f5c50a2db21047c875eb42f308c5583edf96bd8ebf7d770a9d68f6d", size = 242328 }, + { url = "https://files.pythonhosted.org/packages/6e/a3/46f2d420d86bbcb8fe660b26a10a219871a0fbf4d43cb846a4031533f3e0/multidict-6.6.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:712b348f7f449948e0a6c4564a21c7db965af900973a67db432d724619b3c680", size = 268057 }, + { url = "https://files.pythonhosted.org/packages/9e/73/1c743542fe00794a2ec7466abd3f312ccb8fad8dff9f36d42e18fb1ec33e/multidict-6.6.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e4e15d2138ee2694e038e33b7c3da70e6b0ad8868b9f8094a72e1414aeda9c1a", size = 269341 }, + { url = "https://files.pythonhosted.org/packages/a4/11/6ec9dcbe2264b92778eeb85407d1df18812248bf3506a5a1754bc035db0c/multidict-6.6.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8df25594989aebff8a130f7899fa03cbfcc5d2b5f4a461cf2518236fe6f15961", size = 256081 }, + { url = "https://files.pythonhosted.org/packages/9b/2b/631b1e2afeb5f1696846d747d36cda075bfdc0bc7245d6ba5c319278d6c4/multidict-6.6.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:159ca68bfd284a8860f8d8112cf0521113bffd9c17568579e4d13d1f1dc76b65", size = 253581 }, + { url = "https://files.pythonhosted.org/packages/bf/0e/7e3b93f79efeb6111d3bf9a1a69e555ba1d07ad1c11bceb56b7310d0d7ee/multidict-6.6.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:e098c17856a8c9ade81b4810888c5ad1914099657226283cab3062c0540b0643", size = 250750 }, + { url = "https://files.pythonhosted.org/packages/ad/9e/086846c1d6601948e7de556ee464a2d4c85e33883e749f46b9547d7b0704/multidict-6.6.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:67c92ed673049dec52d7ed39f8cf9ebbadf5032c774058b4406d18c8f8fe7063", size = 251548 }, + { url = "https://files.pythonhosted.org/packages/8c/7b/86ec260118e522f1a31550e87b23542294880c97cfbf6fb18cc67b044c66/multidict-6.6.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:bd0578596e3a835ef451784053cfd327d607fc39ea1a14812139339a18a0dbc3", size = 262718 }, + { url = "https://files.pythonhosted.org/packages/8c/bd/22ce8f47abb0be04692c9fc4638508b8340987b18691aa7775d927b73f72/multidict-6.6.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:346055630a2df2115cd23ae271910b4cae40f4e336773550dca4889b12916e75", size = 259603 }, + { url = "https://files.pythonhosted.org/packages/07/9c/91b7ac1691be95cd1f4a26e36a74b97cda6aa9820632d31aab4410f46ebd/multidict-6.6.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:555ff55a359302b79de97e0468e9ee80637b0de1fce77721639f7cd9440b3a10", size = 251351 }, + { url = "https://files.pythonhosted.org/packages/6f/5c/4d7adc739884f7a9fbe00d1eac8c034023ef8bad71f2ebe12823ca2e3649/multidict-6.6.3-cp312-cp312-win32.whl", hash = "sha256:73ab034fb8d58ff85c2bcbadc470efc3fafeea8affcf8722855fb94557f14cc5", size = 41860 }, + { url = "https://files.pythonhosted.org/packages/6a/a3/0fbc7afdf7cb1aa12a086b02959307848eb6bcc8f66fcb66c0cb57e2a2c1/multidict-6.6.3-cp312-cp312-win_amd64.whl", hash = "sha256:04cbcce84f63b9af41bad04a54d4cc4e60e90c35b9e6ccb130be2d75b71f8c17", size = 45982 }, + { url = "https://files.pythonhosted.org/packages/b8/95/8c825bd70ff9b02462dc18d1295dd08d3e9e4eb66856d292ffa62cfe1920/multidict-6.6.3-cp312-cp312-win_arm64.whl", hash = "sha256:0f1130b896ecb52d2a1e615260f3ea2af55fa7dc3d7c3003ba0c3121a759b18b", size = 43210 }, + { url = "https://files.pythonhosted.org/packages/52/1d/0bebcbbb4f000751fbd09957257903d6e002943fc668d841a4cf2fb7f872/multidict-6.6.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:540d3c06d48507357a7d57721e5094b4f7093399a0106c211f33540fdc374d55", size = 75843 }, + { url = "https://files.pythonhosted.org/packages/07/8f/cbe241b0434cfe257f65c2b1bcf9e8d5fb52bc708c5061fb29b0fed22bdf/multidict-6.6.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9c19cea2a690f04247d43f366d03e4eb110a0dc4cd1bbeee4d445435428ed35b", size = 45053 }, + { url = "https://files.pythonhosted.org/packages/32/d2/0b3b23f9dbad5b270b22a3ac3ea73ed0a50ef2d9a390447061178ed6bdb8/multidict-6.6.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7af039820cfd00effec86bda5d8debef711a3e86a1d3772e85bea0f243a4bd65", size = 43273 }, + { url = "https://files.pythonhosted.org/packages/fd/fe/6eb68927e823999e3683bc49678eb20374ba9615097d085298fd5b386564/multidict-6.6.3-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:500b84f51654fdc3944e936f2922114349bf8fdcac77c3092b03449f0e5bc2b3", size = 237124 }, + { url = "https://files.pythonhosted.org/packages/e7/ab/320d8507e7726c460cb77117848b3834ea0d59e769f36fdae495f7669929/multidict-6.6.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f3fc723ab8a5c5ed6c50418e9bfcd8e6dceba6c271cee6728a10a4ed8561520c", size = 256892 }, + { url = "https://files.pythonhosted.org/packages/76/60/38ee422db515ac69834e60142a1a69111ac96026e76e8e9aa347fd2e4591/multidict-6.6.3-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:94c47ea3ade005b5976789baaed66d4de4480d0a0bf31cef6edaa41c1e7b56a6", size = 240547 }, + { url = "https://files.pythonhosted.org/packages/27/fb/905224fde2dff042b030c27ad95a7ae744325cf54b890b443d30a789b80e/multidict-6.6.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:dbc7cf464cc6d67e83e136c9f55726da3a30176f020a36ead246eceed87f1cd8", size = 266223 }, + { url = "https://files.pythonhosted.org/packages/76/35/dc38ab361051beae08d1a53965e3e1a418752fc5be4d3fb983c5582d8784/multidict-6.6.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:900eb9f9da25ada070f8ee4a23f884e0ee66fe4e1a38c3af644256a508ad81ca", size = 267262 }, + { url = "https://files.pythonhosted.org/packages/1f/a3/0a485b7f36e422421b17e2bbb5a81c1af10eac1d4476f2ff92927c730479/multidict-6.6.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7c6df517cf177da5d47ab15407143a89cd1a23f8b335f3a28d57e8b0a3dbb884", size = 254345 }, + { url = "https://files.pythonhosted.org/packages/b4/59/bcdd52c1dab7c0e0d75ff19cac751fbd5f850d1fc39172ce809a74aa9ea4/multidict-6.6.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4ef421045f13879e21c994b36e728d8e7d126c91a64b9185810ab51d474f27e7", size = 252248 }, + { url = "https://files.pythonhosted.org/packages/bb/a4/2d96aaa6eae8067ce108d4acee6f45ced5728beda55c0f02ae1072c730d1/multidict-6.6.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:6c1e61bb4f80895c081790b6b09fa49e13566df8fbff817da3f85b3a8192e36b", size = 250115 }, + { url = "https://files.pythonhosted.org/packages/25/d2/ed9f847fa5c7d0677d4f02ea2c163d5e48573de3f57bacf5670e43a5ffaa/multidict-6.6.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:e5e8523bb12d7623cd8300dbd91b9e439a46a028cd078ca695eb66ba31adee3c", size = 249649 }, + { url = "https://files.pythonhosted.org/packages/1f/af/9155850372563fc550803d3f25373308aa70f59b52cff25854086ecb4a79/multidict-6.6.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:ef58340cc896219e4e653dade08fea5c55c6df41bcc68122e3be3e9d873d9a7b", size = 261203 }, + { url = "https://files.pythonhosted.org/packages/36/2f/c6a728f699896252cf309769089568a33c6439626648843f78743660709d/multidict-6.6.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:fc9dc435ec8699e7b602b94fe0cd4703e69273a01cbc34409af29e7820f777f1", size = 258051 }, + { url = "https://files.pythonhosted.org/packages/d0/60/689880776d6b18fa2b70f6cc74ff87dd6c6b9b47bd9cf74c16fecfaa6ad9/multidict-6.6.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9e864486ef4ab07db5e9cb997bad2b681514158d6954dd1958dfb163b83d53e6", size = 249601 }, + { url = "https://files.pythonhosted.org/packages/75/5e/325b11f2222a549019cf2ef879c1f81f94a0d40ace3ef55cf529915ba6cc/multidict-6.6.3-cp313-cp313-win32.whl", hash = "sha256:5633a82fba8e841bc5c5c06b16e21529573cd654f67fd833650a215520a6210e", size = 41683 }, + { url = "https://files.pythonhosted.org/packages/b1/ad/cf46e73f5d6e3c775cabd2a05976547f3f18b39bee06260369a42501f053/multidict-6.6.3-cp313-cp313-win_amd64.whl", hash = "sha256:e93089c1570a4ad54c3714a12c2cef549dc9d58e97bcded193d928649cab78e9", size = 45811 }, + { url = "https://files.pythonhosted.org/packages/c5/c9/2e3fe950db28fb7c62e1a5f46e1e38759b072e2089209bc033c2798bb5ec/multidict-6.6.3-cp313-cp313-win_arm64.whl", hash = "sha256:c60b401f192e79caec61f166da9c924e9f8bc65548d4246842df91651e83d600", size = 43056 }, + { url = "https://files.pythonhosted.org/packages/3a/58/aaf8114cf34966e084a8cc9517771288adb53465188843d5a19862cb6dc3/multidict-6.6.3-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:02fd8f32d403a6ff13864b0851f1f523d4c988051eea0471d4f1fd8010f11134", size = 82811 }, + { url = "https://files.pythonhosted.org/packages/71/af/5402e7b58a1f5b987a07ad98f2501fdba2a4f4b4c30cf114e3ce8db64c87/multidict-6.6.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:f3aa090106b1543f3f87b2041eef3c156c8da2aed90c63a2fbed62d875c49c37", size = 48304 }, + { url = "https://files.pythonhosted.org/packages/39/65/ab3c8cafe21adb45b24a50266fd747147dec7847425bc2a0f6934b3ae9ce/multidict-6.6.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e924fb978615a5e33ff644cc42e6aa241effcf4f3322c09d4f8cebde95aff5f8", size = 46775 }, + { url = "https://files.pythonhosted.org/packages/49/ba/9fcc1b332f67cc0c0c8079e263bfab6660f87fe4e28a35921771ff3eea0d/multidict-6.6.3-cp313-cp313t-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:b9fe5a0e57c6dbd0e2ce81ca66272282c32cd11d31658ee9553849d91289e1c1", size = 229773 }, + { url = "https://files.pythonhosted.org/packages/a4/14/0145a251f555f7c754ce2dcbcd012939bbd1f34f066fa5d28a50e722a054/multidict-6.6.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b24576f208793ebae00280c59927c3b7c2a3b1655e443a25f753c4611bc1c373", size = 250083 }, + { url = "https://files.pythonhosted.org/packages/9e/d4/d5c0bd2bbb173b586c249a151a26d2fb3ec7d53c96e42091c9fef4e1f10c/multidict-6.6.3-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:135631cb6c58eac37d7ac0df380294fecdc026b28837fa07c02e459c7fb9c54e", size = 228980 }, + { url = "https://files.pythonhosted.org/packages/21/32/c9a2d8444a50ec48c4733ccc67254100c10e1c8ae8e40c7a2d2183b59b97/multidict-6.6.3-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:274d416b0df887aef98f19f21578653982cfb8a05b4e187d4a17103322eeaf8f", size = 257776 }, + { url = "https://files.pythonhosted.org/packages/68/d0/14fa1699f4ef629eae08ad6201c6b476098f5efb051b296f4c26be7a9fdf/multidict-6.6.3-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e252017a817fad7ce05cafbe5711ed40faeb580e63b16755a3a24e66fa1d87c0", size = 256882 }, + { url = "https://files.pythonhosted.org/packages/da/88/84a27570fbe303c65607d517a5f147cd2fc046c2d1da02b84b17b9bdc2aa/multidict-6.6.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2e4cc8d848cd4fe1cdee28c13ea79ab0ed37fc2e89dd77bac86a2e7959a8c3bc", size = 247816 }, + { url = "https://files.pythonhosted.org/packages/1c/60/dca352a0c999ce96a5d8b8ee0b2b9f729dcad2e0b0c195f8286269a2074c/multidict-6.6.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9e236a7094b9c4c1b7585f6b9cca34b9d833cf079f7e4c49e6a4a6ec9bfdc68f", size = 245341 }, + { url = "https://files.pythonhosted.org/packages/50/ef/433fa3ed06028f03946f3993223dada70fb700f763f70c00079533c34578/multidict-6.6.3-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:e0cb0ab69915c55627c933f0b555a943d98ba71b4d1c57bc0d0a66e2567c7471", size = 235854 }, + { url = "https://files.pythonhosted.org/packages/1b/1f/487612ab56fbe35715320905215a57fede20de7db40a261759690dc80471/multidict-6.6.3-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:81ef2f64593aba09c5212a3d0f8c906a0d38d710a011f2f42759704d4557d3f2", size = 243432 }, + { url = "https://files.pythonhosted.org/packages/da/6f/ce8b79de16cd885c6f9052c96a3671373d00c59b3ee635ea93e6e81b8ccf/multidict-6.6.3-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:b9cbc60010de3562545fa198bfc6d3825df430ea96d2cc509c39bd71e2e7d648", size = 252731 }, + { url = "https://files.pythonhosted.org/packages/bb/fe/a2514a6aba78e5abefa1624ca85ae18f542d95ac5cde2e3815a9fbf369aa/multidict-6.6.3-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:70d974eaaa37211390cd02ef93b7e938de564bbffa866f0b08d07e5e65da783d", size = 247086 }, + { url = "https://files.pythonhosted.org/packages/8c/22/b788718d63bb3cce752d107a57c85fcd1a212c6c778628567c9713f9345a/multidict-6.6.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:3713303e4a6663c6d01d648a68f2848701001f3390a030edaaf3fc949c90bf7c", size = 243338 }, + { url = "https://files.pythonhosted.org/packages/22/d6/fdb3d0670819f2228f3f7d9af613d5e652c15d170c83e5f1c94fbc55a25b/multidict-6.6.3-cp313-cp313t-win32.whl", hash = "sha256:639ecc9fe7cd73f2495f62c213e964843826f44505a3e5d82805aa85cac6f89e", size = 47812 }, + { url = "https://files.pythonhosted.org/packages/b6/d6/a9d2c808f2c489ad199723197419207ecbfbc1776f6e155e1ecea9c883aa/multidict-6.6.3-cp313-cp313t-win_amd64.whl", hash = "sha256:9f97e181f344a0ef3881b573d31de8542cc0dbc559ec68c8f8b5ce2c2e91646d", size = 53011 }, + { url = "https://files.pythonhosted.org/packages/f2/40/b68001cba8188dd267590a111f9661b6256debc327137667e832bf5d66e8/multidict-6.6.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ce8b7693da41a3c4fde5871c738a81490cea5496c671d74374c8ab889e1834fb", size = 45254 }, + { url = "https://files.pythonhosted.org/packages/d8/30/9aec301e9772b098c1f5c0ca0279237c9766d94b97802e9888010c64b0ed/multidict-6.6.3-py3-none-any.whl", hash = "sha256:8db10f29c7541fc5da4defd8cd697e1ca429db743fa716325f236079b96f775a", size = 12313 }, ] [[package]] @@ -2189,6 +2463,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963 }, ] +[[package]] +name = "nodeenv" +version = "1.9.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/43/16/fc88b08840de0e0a72a2f9d8c6bae36be573e475a6326ae854bcc549fc45/nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f", size = 47437 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9", size = 22314 }, +] + [[package]] name = "numpy" version = "2.2.6" @@ -2256,80 +2539,79 @@ wheels = [ [[package]] name = "numpy" -version = "2.3.0" +version = "2.3.1" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.13'", - "python_full_version >= '3.12.4' and python_full_version < '3.13'", - "python_full_version >= '3.12' and python_full_version < '3.12.4'", + "python_full_version == '3.12.*'", "python_full_version == '3.11.*'", ] -sdist = { url = "https://files.pythonhosted.org/packages/f3/db/8e12381333aea300890829a0a36bfa738cac95475d88982d538725143fd9/numpy-2.3.0.tar.gz", hash = "sha256:581f87f9e9e9db2cba2141400e160e9dd644ee248788d6f90636eeb8fd9260a6", size = 20382813 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fd/5f/df67435257d827eb3b8af66f585223dc2c3f2eb7ad0b50cb1dae2f35f494/numpy-2.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c3c9fdde0fa18afa1099d6257eb82890ea4f3102847e692193b54e00312a9ae9", size = 21199688 }, - { url = "https://files.pythonhosted.org/packages/e5/ce/aad219575055d6c9ef29c8c540c81e1c38815d3be1fe09cdbe53d48ee838/numpy-2.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:46d16f72c2192da7b83984aa5455baee640e33a9f1e61e656f29adf55e406c2b", size = 14359277 }, - { url = "https://files.pythonhosted.org/packages/29/6b/2d31da8e6d2ec99bed54c185337a87f8fbeccc1cd9804e38217e92f3f5e2/numpy-2.3.0-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:a0be278be9307c4ab06b788f2a077f05e180aea817b3e41cebbd5aaf7bd85ed3", size = 5376069 }, - { url = "https://files.pythonhosted.org/packages/7d/2a/6c59a062397553ec7045c53d5fcdad44e4536e54972faa2ba44153bca984/numpy-2.3.0-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:99224862d1412d2562248d4710126355d3a8db7672170a39d6909ac47687a8a4", size = 6913057 }, - { url = "https://files.pythonhosted.org/packages/d5/5a/8df16f258d28d033e4f359e29d3aeb54663243ac7b71504e89deeb813202/numpy-2.3.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:2393a914db64b0ead0ab80c962e42d09d5f385802006a6c87835acb1f58adb96", size = 14568083 }, - { url = "https://files.pythonhosted.org/packages/0a/92/0528a563dfc2cdccdcb208c0e241a4bb500d7cde218651ffb834e8febc50/numpy-2.3.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:7729c8008d55e80784bd113787ce876ca117185c579c0d626f59b87d433ea779", size = 16929402 }, - { url = "https://files.pythonhosted.org/packages/e4/2f/e7a8c8d4a2212c527568d84f31587012cf5497a7271ea1f23332142f634e/numpy-2.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:06d4fb37a8d383b769281714897420c5cc3545c79dc427df57fc9b852ee0bf58", size = 15879193 }, - { url = "https://files.pythonhosted.org/packages/e2/c3/dada3f005953847fe35f42ac0fe746f6e1ea90b4c6775e4be605dcd7b578/numpy-2.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c39ec392b5db5088259c68250e342612db82dc80ce044cf16496cf14cf6bc6f8", size = 18665318 }, - { url = "https://files.pythonhosted.org/packages/3b/ae/3f448517dedefc8dd64d803f9d51a8904a48df730e00a3c5fb1e75a60620/numpy-2.3.0-cp311-cp311-win32.whl", hash = "sha256:ee9d3ee70d62827bc91f3ea5eee33153212c41f639918550ac0475e3588da59f", size = 6601108 }, - { url = "https://files.pythonhosted.org/packages/8c/4a/556406d2bb2b9874c8cbc840c962683ac28f21efbc9b01177d78f0199ca1/numpy-2.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:43c55b6a860b0eb44d42341438b03513cf3879cb3617afb749ad49307e164edd", size = 13021525 }, - { url = "https://files.pythonhosted.org/packages/ed/ee/bf54278aef30335ffa9a189f869ea09e1a195b3f4b93062164a3b02678a7/numpy-2.3.0-cp311-cp311-win_arm64.whl", hash = "sha256:2e6a1409eee0cb0316cb64640a49a49ca44deb1a537e6b1121dc7c458a1299a8", size = 10170327 }, - { url = "https://files.pythonhosted.org/packages/89/59/9df493df81ac6f76e9f05cdbe013cdb0c9a37b434f6e594f5bd25e278908/numpy-2.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:389b85335838155a9076e9ad7f8fdba0827496ec2d2dc32ce69ce7898bde03ba", size = 20897025 }, - { url = "https://files.pythonhosted.org/packages/2f/86/4ff04335901d6cf3a6bb9c748b0097546ae5af35e455ae9b962ebff4ecd7/numpy-2.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9498f60cd6bb8238d8eaf468a3d5bb031d34cd12556af53510f05fcf581c1b7e", size = 14129882 }, - { url = "https://files.pythonhosted.org/packages/71/8d/a942cd4f959de7f08a79ab0c7e6cecb7431d5403dce78959a726f0f57aa1/numpy-2.3.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:622a65d40d8eb427d8e722fd410ac3ad4958002f109230bc714fa551044ebae2", size = 5110181 }, - { url = "https://files.pythonhosted.org/packages/86/5d/45850982efc7b2c839c5626fb67fbbc520d5b0d7c1ba1ae3651f2f74c296/numpy-2.3.0-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:b9446d9d8505aadadb686d51d838f2b6688c9e85636a0c3abaeb55ed54756459", size = 6647581 }, - { url = "https://files.pythonhosted.org/packages/1a/c0/c871d4a83f93b00373d3eebe4b01525eee8ef10b623a335ec262b58f4dc1/numpy-2.3.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:50080245365d75137a2bf46151e975de63146ae6d79f7e6bd5c0e85c9931d06a", size = 14262317 }, - { url = "https://files.pythonhosted.org/packages/b7/f6/bc47f5fa666d5ff4145254f9e618d56e6a4ef9b874654ca74c19113bb538/numpy-2.3.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:c24bb4113c66936eeaa0dc1e47c74770453d34f46ee07ae4efd853a2ed1ad10a", size = 16633919 }, - { url = "https://files.pythonhosted.org/packages/f5/b4/65f48009ca0c9b76df5f404fccdea5a985a1bb2e34e97f21a17d9ad1a4ba/numpy-2.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4d8d294287fdf685281e671886c6dcdf0291a7c19db3e5cb4178d07ccf6ecc67", size = 15567651 }, - { url = "https://files.pythonhosted.org/packages/f1/62/5367855a2018578e9334ed08252ef67cc302e53edc869666f71641cad40b/numpy-2.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6295f81f093b7f5769d1728a6bd8bf7466de2adfa771ede944ce6711382b89dc", size = 18361723 }, - { url = "https://files.pythonhosted.org/packages/d4/75/5baed8cd867eabee8aad1e74d7197d73971d6a3d40c821f1848b8fab8b84/numpy-2.3.0-cp312-cp312-win32.whl", hash = "sha256:e6648078bdd974ef5d15cecc31b0c410e2e24178a6e10bf511e0557eed0f2570", size = 6318285 }, - { url = "https://files.pythonhosted.org/packages/bc/49/d5781eaa1a15acb3b3a3f49dc9e2ff18d92d0ce5c2976f4ab5c0a7360250/numpy-2.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:0898c67a58cdaaf29994bc0e2c65230fd4de0ac40afaf1584ed0b02cd74c6fdd", size = 12732594 }, - { url = "https://files.pythonhosted.org/packages/c2/1c/6d343e030815c7c97a1f9fbad00211b47717c7fe446834c224bd5311e6f1/numpy-2.3.0-cp312-cp312-win_arm64.whl", hash = "sha256:bd8df082b6c4695753ad6193018c05aac465d634834dca47a3ae06d4bb22d9ea", size = 9891498 }, - { url = "https://files.pythonhosted.org/packages/73/fc/1d67f751fd4dbafc5780244fe699bc4084268bad44b7c5deb0492473127b/numpy-2.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5754ab5595bfa2c2387d241296e0381c21f44a4b90a776c3c1d39eede13a746a", size = 20889633 }, - { url = "https://files.pythonhosted.org/packages/e8/95/73ffdb69e5c3f19ec4530f8924c4386e7ba097efc94b9c0aff607178ad94/numpy-2.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d11fa02f77752d8099573d64e5fe33de3229b6632036ec08f7080f46b6649959", size = 14151683 }, - { url = "https://files.pythonhosted.org/packages/64/d5/06d4bb31bb65a1d9c419eb5676173a2f90fd8da3c59f816cc54c640ce265/numpy-2.3.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:aba48d17e87688a765ab1cd557882052f238e2f36545dfa8e29e6a91aef77afe", size = 5102683 }, - { url = "https://files.pythonhosted.org/packages/12/8b/6c2cef44f8ccdc231f6b56013dff1d71138c48124334aded36b1a1b30c5a/numpy-2.3.0-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:4dc58865623023b63b10d52f18abaac3729346a7a46a778381e0e3af4b7f3beb", size = 6640253 }, - { url = "https://files.pythonhosted.org/packages/62/aa/fca4bf8de3396ddb59544df9b75ffe5b73096174de97a9492d426f5cd4aa/numpy-2.3.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:df470d376f54e052c76517393fa443758fefcdd634645bc9c1f84eafc67087f0", size = 14258658 }, - { url = "https://files.pythonhosted.org/packages/1c/12/734dce1087eed1875f2297f687e671cfe53a091b6f2f55f0c7241aad041b/numpy-2.3.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:87717eb24d4a8a64683b7a4e91ace04e2f5c7c77872f823f02a94feee186168f", size = 16628765 }, - { url = "https://files.pythonhosted.org/packages/48/03/ffa41ade0e825cbcd5606a5669962419528212a16082763fc051a7247d76/numpy-2.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d8fa264d56882b59dcb5ea4d6ab6f31d0c58a57b41aec605848b6eb2ef4a43e8", size = 15564335 }, - { url = "https://files.pythonhosted.org/packages/07/58/869398a11863310aee0ff85a3e13b4c12f20d032b90c4b3ee93c3b728393/numpy-2.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e651756066a0eaf900916497e20e02fe1ae544187cb0fe88de981671ee7f6270", size = 18360608 }, - { url = "https://files.pythonhosted.org/packages/2f/8a/5756935752ad278c17e8a061eb2127c9a3edf4ba2c31779548b336f23c8d/numpy-2.3.0-cp313-cp313-win32.whl", hash = "sha256:e43c3cce3b6ae5f94696669ff2a6eafd9a6b9332008bafa4117af70f4b88be6f", size = 6310005 }, - { url = "https://files.pythonhosted.org/packages/08/60/61d60cf0dfc0bf15381eaef46366ebc0c1a787856d1db0c80b006092af84/numpy-2.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:81ae0bf2564cf475f94be4a27ef7bcf8af0c3e28da46770fc904da9abd5279b5", size = 12729093 }, - { url = "https://files.pythonhosted.org/packages/66/31/2f2f2d2b3e3c32d5753d01437240feaa32220b73258c9eef2e42a0832866/numpy-2.3.0-cp313-cp313-win_arm64.whl", hash = "sha256:c8738baa52505fa6e82778580b23f945e3578412554d937093eac9205e845e6e", size = 9885689 }, - { url = "https://files.pythonhosted.org/packages/f1/89/c7828f23cc50f607ceb912774bb4cff225ccae7131c431398ad8400e2c98/numpy-2.3.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:39b27d8b38942a647f048b675f134dd5a567f95bfff481f9109ec308515c51d8", size = 20986612 }, - { url = "https://files.pythonhosted.org/packages/dd/46/79ecf47da34c4c50eedec7511e53d57ffdfd31c742c00be7dc1d5ffdb917/numpy-2.3.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:0eba4a1ea88f9a6f30f56fdafdeb8da3774349eacddab9581a21234b8535d3d3", size = 14298953 }, - { url = "https://files.pythonhosted.org/packages/59/44/f6caf50713d6ff4480640bccb2a534ce1d8e6e0960c8f864947439f0ee95/numpy-2.3.0-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:b0f1f11d0a1da54927436505a5a7670b154eac27f5672afc389661013dfe3d4f", size = 5225806 }, - { url = "https://files.pythonhosted.org/packages/a6/43/e1fd1aca7c97e234dd05e66de4ab7a5be54548257efcdd1bc33637e72102/numpy-2.3.0-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:690d0a5b60a47e1f9dcec7b77750a4854c0d690e9058b7bef3106e3ae9117808", size = 6735169 }, - { url = "https://files.pythonhosted.org/packages/84/89/f76f93b06a03177c0faa7ca94d0856c4e5c4bcaf3c5f77640c9ed0303e1c/numpy-2.3.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:8b51ead2b258284458e570942137155978583e407babc22e3d0ed7af33ce06f8", size = 14330701 }, - { url = "https://files.pythonhosted.org/packages/aa/f5/4858c3e9ff7a7d64561b20580cf7cc5d085794bd465a19604945d6501f6c/numpy-2.3.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:aaf81c7b82c73bd9b45e79cfb9476cb9c29e937494bfe9092c26aece812818ad", size = 16692983 }, - { url = "https://files.pythonhosted.org/packages/08/17/0e3b4182e691a10e9483bcc62b4bb8693dbf9ea5dc9ba0b77a60435074bb/numpy-2.3.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:f420033a20b4f6a2a11f585f93c843ac40686a7c3fa514060a97d9de93e5e72b", size = 15641435 }, - { url = "https://files.pythonhosted.org/packages/4e/d5/463279fda028d3c1efa74e7e8d507605ae87f33dbd0543cf4c4527c8b882/numpy-2.3.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:d344ca32ab482bcf8735d8f95091ad081f97120546f3d250240868430ce52555", size = 18433798 }, - { url = "https://files.pythonhosted.org/packages/0e/1e/7a9d98c886d4c39a2b4d3a7c026bffcf8fbcaf518782132d12a301cfc47a/numpy-2.3.0-cp313-cp313t-win32.whl", hash = "sha256:48a2e8eaf76364c32a1feaa60d6925eaf32ed7a040183b807e02674305beef61", size = 6438632 }, - { url = "https://files.pythonhosted.org/packages/fe/ab/66fc909931d5eb230107d016861824f335ae2c0533f422e654e5ff556784/numpy-2.3.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ba17f93a94e503551f154de210e4d50c5e3ee20f7e7a1b5f6ce3f22d419b93bb", size = 12868491 }, - { url = "https://files.pythonhosted.org/packages/ee/e8/2c8a1c9e34d6f6d600c83d5ce5b71646c32a13f34ca5c518cc060639841c/numpy-2.3.0-cp313-cp313t-win_arm64.whl", hash = "sha256:f14e016d9409680959691c109be98c436c6249eaf7f118b424679793607b5944", size = 9935345 }, - { url = "https://files.pythonhosted.org/packages/6a/a2/f8c1133f90eaa1c11bbbec1dc28a42054d0ce74bc2c9838c5437ba5d4980/numpy-2.3.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:80b46117c7359de8167cc00a2c7d823bdd505e8c7727ae0871025a86d668283b", size = 21070759 }, - { url = "https://files.pythonhosted.org/packages/6c/e0/4c05fc44ba28463096eee5ae2a12832c8d2759cc5bcec34ae33386d3ff83/numpy-2.3.0-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:5814a0f43e70c061f47abd5857d120179609ddc32a613138cbb6c4e9e2dbdda5", size = 5301054 }, - { url = "https://files.pythonhosted.org/packages/8a/3b/6c06cdebe922bbc2a466fe2105f50f661238ea223972a69c7deb823821e7/numpy-2.3.0-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:ef6c1e88fd6b81ac6d215ed71dc8cd027e54d4bf1d2682d362449097156267a2", size = 6817520 }, - { url = "https://files.pythonhosted.org/packages/9d/a3/1e536797fd10eb3c5dbd2e376671667c9af19e241843548575267242ea02/numpy-2.3.0-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:33a5a12a45bb82d9997e2c0b12adae97507ad7c347546190a18ff14c28bbca12", size = 14398078 }, - { url = "https://files.pythonhosted.org/packages/7c/61/9d574b10d9368ecb1a0c923952aa593510a20df4940aa615b3a71337c8db/numpy-2.3.0-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:54dfc8681c1906d239e95ab1508d0a533c4a9505e52ee2d71a5472b04437ef97", size = 16751324 }, - { url = "https://files.pythonhosted.org/packages/39/de/bcad52ce972dc26232629ca3a99721fd4b22c1d2bda84d5db6541913ef9c/numpy-2.3.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:e017a8a251ff4d18d71f139e28bdc7c31edba7a507f72b1414ed902cbe48c74d", size = 12924237 }, +sdist = { url = "https://files.pythonhosted.org/packages/2e/19/d7c972dfe90a353dbd3efbbe1d14a5951de80c99c9dc1b93cd998d51dc0f/numpy-2.3.1.tar.gz", hash = "sha256:1ec9ae20a4226da374362cca3c62cd753faf2f951440b0e3b98e93c235441d2b", size = 20390372 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b0/c7/87c64d7ab426156530676000c94784ef55676df2f13b2796f97722464124/numpy-2.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6ea9e48336a402551f52cd8f593343699003d2353daa4b72ce8d34f66b722070", size = 21199346 }, + { url = "https://files.pythonhosted.org/packages/58/0e/0966c2f44beeac12af8d836e5b5f826a407cf34c45cb73ddcdfce9f5960b/numpy-2.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5ccb7336eaf0e77c1635b232c141846493a588ec9ea777a7c24d7166bb8533ae", size = 14361143 }, + { url = "https://files.pythonhosted.org/packages/7d/31/6e35a247acb1bfc19226791dfc7d4c30002cd4e620e11e58b0ddf836fe52/numpy-2.3.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:0bb3a4a61e1d327e035275d2a993c96fa786e4913aa089843e6a2d9dd205c66a", size = 5378989 }, + { url = "https://files.pythonhosted.org/packages/b0/25/93b621219bb6f5a2d4e713a824522c69ab1f06a57cd571cda70e2e31af44/numpy-2.3.1-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:e344eb79dab01f1e838ebb67aab09965fb271d6da6b00adda26328ac27d4a66e", size = 6912890 }, + { url = "https://files.pythonhosted.org/packages/ef/60/6b06ed98d11fb32e27fb59468b42383f3877146d3ee639f733776b6ac596/numpy-2.3.1-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:467db865b392168ceb1ef1ffa6f5a86e62468c43e0cfb4ab6da667ede10e58db", size = 14569032 }, + { url = "https://files.pythonhosted.org/packages/75/c9/9bec03675192077467a9c7c2bdd1f2e922bd01d3a69b15c3a0fdcd8548f6/numpy-2.3.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:afed2ce4a84f6b0fc6c1ce734ff368cbf5a5e24e8954a338f3bdffa0718adffb", size = 16930354 }, + { url = "https://files.pythonhosted.org/packages/6a/e2/5756a00cabcf50a3f527a0c968b2b4881c62b1379223931853114fa04cda/numpy-2.3.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0025048b3c1557a20bc80d06fdeb8cc7fc193721484cca82b2cfa072fec71a93", size = 15879605 }, + { url = "https://files.pythonhosted.org/packages/ff/86/a471f65f0a86f1ca62dcc90b9fa46174dd48f50214e5446bc16a775646c5/numpy-2.3.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a5ee121b60aa509679b682819c602579e1df14a5b07fe95671c8849aad8f2115", size = 18666994 }, + { url = "https://files.pythonhosted.org/packages/43/a6/482a53e469b32be6500aaf61cfafd1de7a0b0d484babf679209c3298852e/numpy-2.3.1-cp311-cp311-win32.whl", hash = "sha256:a8b740f5579ae4585831b3cf0e3b0425c667274f82a484866d2adf9570539369", size = 6603672 }, + { url = "https://files.pythonhosted.org/packages/6b/fb/bb613f4122c310a13ec67585c70e14b03bfc7ebabd24f4d5138b97371d7c/numpy-2.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:d4580adadc53311b163444f877e0789f1c8861e2698f6b2a4ca852fda154f3ff", size = 13024015 }, + { url = "https://files.pythonhosted.org/packages/51/58/2d842825af9a0c041aca246dc92eb725e1bc5e1c9ac89712625db0c4e11c/numpy-2.3.1-cp311-cp311-win_arm64.whl", hash = "sha256:ec0bdafa906f95adc9a0c6f26a4871fa753f25caaa0e032578a30457bff0af6a", size = 10456989 }, + { url = "https://files.pythonhosted.org/packages/c6/56/71ad5022e2f63cfe0ca93559403d0edef14aea70a841d640bd13cdba578e/numpy-2.3.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2959d8f268f3d8ee402b04a9ec4bb7604555aeacf78b360dc4ec27f1d508177d", size = 20896664 }, + { url = "https://files.pythonhosted.org/packages/25/65/2db52ba049813670f7f987cc5db6dac9be7cd95e923cc6832b3d32d87cef/numpy-2.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:762e0c0c6b56bdedfef9a8e1d4538556438288c4276901ea008ae44091954e29", size = 14131078 }, + { url = "https://files.pythonhosted.org/packages/57/dd/28fa3c17b0e751047ac928c1e1b6990238faad76e9b147e585b573d9d1bd/numpy-2.3.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:867ef172a0976aaa1f1d1b63cf2090de8b636a7674607d514505fb7276ab08fc", size = 5112554 }, + { url = "https://files.pythonhosted.org/packages/c9/fc/84ea0cba8e760c4644b708b6819d91784c290288c27aca916115e3311d17/numpy-2.3.1-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:4e602e1b8682c2b833af89ba641ad4176053aaa50f5cacda1a27004352dde943", size = 6646560 }, + { url = "https://files.pythonhosted.org/packages/61/b2/512b0c2ddec985ad1e496b0bd853eeb572315c0f07cd6997473ced8f15e2/numpy-2.3.1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:8e333040d069eba1652fb08962ec5b76af7f2c7bce1df7e1418c8055cf776f25", size = 14260638 }, + { url = "https://files.pythonhosted.org/packages/6e/45/c51cb248e679a6c6ab14b7a8e3ead3f4a3fe7425fc7a6f98b3f147bec532/numpy-2.3.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:e7cbf5a5eafd8d230a3ce356d892512185230e4781a361229bd902ff403bc660", size = 16632729 }, + { url = "https://files.pythonhosted.org/packages/e4/ff/feb4be2e5c09a3da161b412019caf47183099cbea1132fd98061808c2df2/numpy-2.3.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5f1b8f26d1086835f442286c1d9b64bb3974b0b1e41bb105358fd07d20872952", size = 15565330 }, + { url = "https://files.pythonhosted.org/packages/bc/6d/ceafe87587101e9ab0d370e4f6e5f3f3a85b9a697f2318738e5e7e176ce3/numpy-2.3.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ee8340cb48c9b7a5899d1149eece41ca535513a9698098edbade2a8e7a84da77", size = 18361734 }, + { url = "https://files.pythonhosted.org/packages/2b/19/0fb49a3ea088be691f040c9bf1817e4669a339d6e98579f91859b902c636/numpy-2.3.1-cp312-cp312-win32.whl", hash = "sha256:e772dda20a6002ef7061713dc1e2585bc1b534e7909b2030b5a46dae8ff077ab", size = 6320411 }, + { url = "https://files.pythonhosted.org/packages/b1/3e/e28f4c1dd9e042eb57a3eb652f200225e311b608632bc727ae378623d4f8/numpy-2.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:cfecc7822543abdea6de08758091da655ea2210b8ffa1faf116b940693d3df76", size = 12734973 }, + { url = "https://files.pythonhosted.org/packages/04/a8/8a5e9079dc722acf53522b8f8842e79541ea81835e9b5483388701421073/numpy-2.3.1-cp312-cp312-win_arm64.whl", hash = "sha256:7be91b2239af2658653c5bb6f1b8bccafaf08226a258caf78ce44710a0160d30", size = 10191491 }, + { url = "https://files.pythonhosted.org/packages/d4/bd/35ad97006d8abff8631293f8ea6adf07b0108ce6fec68da3c3fcca1197f2/numpy-2.3.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:25a1992b0a3fdcdaec9f552ef10d8103186f5397ab45e2d25f8ac51b1a6b97e8", size = 20889381 }, + { url = "https://files.pythonhosted.org/packages/f1/4f/df5923874d8095b6062495b39729178eef4a922119cee32a12ee1bd4664c/numpy-2.3.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7dea630156d39b02a63c18f508f85010230409db5b2927ba59c8ba4ab3e8272e", size = 14152726 }, + { url = "https://files.pythonhosted.org/packages/8c/0f/a1f269b125806212a876f7efb049b06c6f8772cf0121139f97774cd95626/numpy-2.3.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:bada6058dd886061f10ea15f230ccf7dfff40572e99fef440a4a857c8728c9c0", size = 5105145 }, + { url = "https://files.pythonhosted.org/packages/6d/63/a7f7fd5f375b0361682f6ffbf686787e82b7bbd561268e4f30afad2bb3c0/numpy-2.3.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:a894f3816eb17b29e4783e5873f92faf55b710c2519e5c351767c51f79d8526d", size = 6639409 }, + { url = "https://files.pythonhosted.org/packages/bf/0d/1854a4121af895aab383f4aa233748f1df4671ef331d898e32426756a8a6/numpy-2.3.1-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:18703df6c4a4fee55fd3d6e5a253d01c5d33a295409b03fda0c86b3ca2ff41a1", size = 14257630 }, + { url = "https://files.pythonhosted.org/packages/50/30/af1b277b443f2fb08acf1c55ce9d68ee540043f158630d62cef012750f9f/numpy-2.3.1-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:5902660491bd7a48b2ec16c23ccb9124b8abfd9583c5fdfa123fe6b421e03de1", size = 16627546 }, + { url = "https://files.pythonhosted.org/packages/6e/ec/3b68220c277e463095342d254c61be8144c31208db18d3fd8ef02712bcd6/numpy-2.3.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:36890eb9e9d2081137bd78d29050ba63b8dab95dff7912eadf1185e80074b2a0", size = 15562538 }, + { url = "https://files.pythonhosted.org/packages/77/2b/4014f2bcc4404484021c74d4c5ee8eb3de7e3f7ac75f06672f8dcf85140a/numpy-2.3.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a780033466159c2270531e2b8ac063704592a0bc62ec4a1b991c7c40705eb0e8", size = 18360327 }, + { url = "https://files.pythonhosted.org/packages/40/8d/2ddd6c9b30fcf920837b8672f6c65590c7d92e43084c25fc65edc22e93ca/numpy-2.3.1-cp313-cp313-win32.whl", hash = "sha256:39bff12c076812595c3a306f22bfe49919c5513aa1e0e70fac756a0be7c2a2b8", size = 6312330 }, + { url = "https://files.pythonhosted.org/packages/dd/c8/beaba449925988d415efccb45bf977ff8327a02f655090627318f6398c7b/numpy-2.3.1-cp313-cp313-win_amd64.whl", hash = "sha256:8d5ee6eec45f08ce507a6570e06f2f879b374a552087a4179ea7838edbcbfa42", size = 12731565 }, + { url = "https://files.pythonhosted.org/packages/0b/c3/5c0c575d7ec78c1126998071f58facfc124006635da75b090805e642c62e/numpy-2.3.1-cp313-cp313-win_arm64.whl", hash = "sha256:0c4d9e0a8368db90f93bd192bfa771ace63137c3488d198ee21dfb8e7771916e", size = 10190262 }, + { url = "https://files.pythonhosted.org/packages/ea/19/a029cd335cf72f79d2644dcfc22d90f09caa86265cbbde3b5702ccef6890/numpy-2.3.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:b0b5397374f32ec0649dd98c652a1798192042e715df918c20672c62fb52d4b8", size = 20987593 }, + { url = "https://files.pythonhosted.org/packages/25/91/8ea8894406209107d9ce19b66314194675d31761fe2cb3c84fe2eeae2f37/numpy-2.3.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:c5bdf2015ccfcee8253fb8be695516ac4457c743473a43290fd36eba6a1777eb", size = 14300523 }, + { url = "https://files.pythonhosted.org/packages/a6/7f/06187b0066eefc9e7ce77d5f2ddb4e314a55220ad62dd0bfc9f2c44bac14/numpy-2.3.1-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:d70f20df7f08b90a2062c1f07737dd340adccf2068d0f1b9b3d56e2038979fee", size = 5227993 }, + { url = "https://files.pythonhosted.org/packages/e8/ec/a926c293c605fa75e9cfb09f1e4840098ed46d2edaa6e2152ee35dc01ed3/numpy-2.3.1-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:2fb86b7e58f9ac50e1e9dd1290154107e47d1eef23a0ae9145ded06ea606f992", size = 6736652 }, + { url = "https://files.pythonhosted.org/packages/e3/62/d68e52fb6fde5586650d4c0ce0b05ff3a48ad4df4ffd1b8866479d1d671d/numpy-2.3.1-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:23ab05b2d241f76cb883ce8b9a93a680752fbfcbd51c50eff0b88b979e471d8c", size = 14331561 }, + { url = "https://files.pythonhosted.org/packages/fc/ec/b74d3f2430960044bdad6900d9f5edc2dc0fb8bf5a0be0f65287bf2cbe27/numpy-2.3.1-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:ce2ce9e5de4703a673e705183f64fd5da5bf36e7beddcb63a25ee2286e71ca48", size = 16693349 }, + { url = "https://files.pythonhosted.org/packages/0d/15/def96774b9d7eb198ddadfcbd20281b20ebb510580419197e225f5c55c3e/numpy-2.3.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c4913079974eeb5c16ccfd2b1f09354b8fed7e0d6f2cab933104a09a6419b1ee", size = 15642053 }, + { url = "https://files.pythonhosted.org/packages/2b/57/c3203974762a759540c6ae71d0ea2341c1fa41d84e4971a8e76d7141678a/numpy-2.3.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:010ce9b4f00d5c036053ca684c77441f2f2c934fd23bee058b4d6f196efd8280", size = 18434184 }, + { url = "https://files.pythonhosted.org/packages/22/8a/ccdf201457ed8ac6245187850aff4ca56a79edbea4829f4e9f14d46fa9a5/numpy-2.3.1-cp313-cp313t-win32.whl", hash = "sha256:6269b9edfe32912584ec496d91b00b6d34282ca1d07eb10e82dfc780907d6c2e", size = 6440678 }, + { url = "https://files.pythonhosted.org/packages/f1/7e/7f431d8bd8eb7e03d79294aed238b1b0b174b3148570d03a8a8a8f6a0da9/numpy-2.3.1-cp313-cp313t-win_amd64.whl", hash = "sha256:2a809637460e88a113e186e87f228d74ae2852a2e0c44de275263376f17b5bdc", size = 12870697 }, + { url = "https://files.pythonhosted.org/packages/d4/ca/af82bf0fad4c3e573c6930ed743b5308492ff19917c7caaf2f9b6f9e2e98/numpy-2.3.1-cp313-cp313t-win_arm64.whl", hash = "sha256:eccb9a159db9aed60800187bc47a6d3451553f0e1b08b068d8b277ddfbb9b244", size = 10260376 }, + { url = "https://files.pythonhosted.org/packages/e8/34/facc13b9b42ddca30498fc51f7f73c3d0f2be179943a4b4da8686e259740/numpy-2.3.1-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ad506d4b09e684394c42c966ec1527f6ebc25da7f4da4b1b056606ffe446b8a3", size = 21070637 }, + { url = "https://files.pythonhosted.org/packages/65/b6/41b705d9dbae04649b529fc9bd3387664c3281c7cd78b404a4efe73dcc45/numpy-2.3.1-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:ebb8603d45bc86bbd5edb0d63e52c5fd9e7945d3a503b77e486bd88dde67a19b", size = 5304087 }, + { url = "https://files.pythonhosted.org/packages/7a/b4/fe3ac1902bff7a4934a22d49e1c9d71a623204d654d4cc43c6e8fe337fcb/numpy-2.3.1-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:15aa4c392ac396e2ad3d0a2680c0f0dee420f9fed14eef09bdb9450ee6dcb7b7", size = 6817588 }, + { url = "https://files.pythonhosted.org/packages/ae/ee/89bedf69c36ace1ac8f59e97811c1f5031e179a37e4821c3a230bf750142/numpy-2.3.1-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:c6e0bf9d1a2f50d2b65a7cf56db37c095af17b59f6c132396f7c6d5dd76484df", size = 14399010 }, + { url = "https://files.pythonhosted.org/packages/15/08/e00e7070ede29b2b176165eba18d6f9784d5349be3c0c1218338e79c27fd/numpy-2.3.1-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:eabd7e8740d494ce2b4ea0ff05afa1b7b291e978c0ae075487c51e8bd93c0c68", size = 16752042 }, + { url = "https://files.pythonhosted.org/packages/48/6b/1c6b515a83d5564b1698a61efa245727c8feecf308f4091f565988519d20/numpy-2.3.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:e610832418a2bc09d974cc9fecebfa51e9532d6190223bc5ef6a7402ebf3b5cb", size = 12927246 }, ] [[package]] name = "oauthlib" -version = "3.2.2" +version = "3.3.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6d/fa/fbf4001037904031639e6bfbfc02badfc7e12f137a8afa254df6c4c8a670/oauthlib-3.2.2.tar.gz", hash = "sha256:9859c40929662bec5d64f34d01c99e093149682a3f38915dc0655d5a633dd918", size = 177352 } +sdist = { url = "https://files.pythonhosted.org/packages/0b/5f/19930f824ffeb0ad4372da4812c50edbd1434f678c90c2733e1188edfc63/oauthlib-3.3.1.tar.gz", hash = "sha256:0f0f8aa759826a193cf66c12ea1af1637f87b9b4622d46e866952bb022e538c9", size = 185918 } wheels = [ - { url = "https://files.pythonhosted.org/packages/7e/80/cab10959dc1faead58dc8384a781dfbf93cb4d33d50988f7a69f1b7c9bbe/oauthlib-3.2.2-py3-none-any.whl", hash = "sha256:8139f29aac13e25d502680e9e19963e83f16838d48a0d71c287fe40e7067fbca", size = 151688 }, + { url = "https://files.pythonhosted.org/packages/be/9c/92789c596b8df838baa98fa71844d84283302f7604ed565dafe5a6b5041a/oauthlib-3.3.1-py3-none-any.whl", hash = "sha256:88119c938d2b8fb88561af5f6ee0eec8cc8d552b7bb1f712743136eb7523b7a1", size = 160065 }, ] [[package]] name = "openai" -version = "1.86.0" +version = "1.97.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, @@ -2341,142 +2623,163 @@ dependencies = [ { name = "tqdm" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ec/7a/9ad4a61f1502f0e59d8c27fb629e28a63259a44d8d31cd2314e1534a2d9f/openai-1.86.0.tar.gz", hash = "sha256:c64d5b788359a8fdf69bd605ae804ce41c1ce2e78b8dd93e2542e0ee267f1e4b", size = 468272 } +sdist = { url = "https://files.pythonhosted.org/packages/e0/c6/b8d66e4f3b95493a8957065b24533333c927dc23817abe397f13fe589c6e/openai-1.97.0.tar.gz", hash = "sha256:0be349569ccaa4fb54f97bb808423fd29ccaeb1246ee1be762e0c81a47bae0aa", size = 493850 } wheels = [ - { url = "https://files.pythonhosted.org/packages/58/c1/dfb16b3432810fc9758564f9d1a4dbce6b93b7fb763ba57530c7fc48316d/openai-1.86.0-py3-none-any.whl", hash = "sha256:c8889c39410621fe955c230cc4c21bfe36ec887f4e60a957de05f507d7e1f349", size = 730296 }, + { url = "https://files.pythonhosted.org/packages/8a/91/1f1cf577f745e956b276a8b1d3d76fa7a6ee0c2b05db3b001b900f2c71db/openai-1.97.0-py3-none-any.whl", hash = "sha256:a1c24d96f4609f3f7f51c9e1c2606d97cc6e334833438659cfd687e9c972c610", size = 764953 }, +] + +[[package]] +name = "openapi-pydantic" +version = "0.5.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pydantic" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/02/2e/58d83848dd1a79cb92ed8e63f6ba901ca282c5f09d04af9423ec26c56fd7/openapi_pydantic-0.5.1.tar.gz", hash = "sha256:ff6835af6bde7a459fb93eb93bb92b8749b754fc6e51b2f1590a19dc3005ee0d", size = 60892 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/12/cf/03675d8bd8ecbf4445504d8071adab19f5f993676795708e36402ab38263/openapi_pydantic-0.5.1-py3-none-any.whl", hash = "sha256:a3a09ef4586f5bd760a8df7f43028b60cafb6d9f61de2acba9574766255ab146", size = 96381 }, ] [[package]] name = "orjson" -version = "3.10.18" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/81/0b/fea456a3ffe74e70ba30e01ec183a9b26bec4d497f61dcfce1b601059c60/orjson-3.10.18.tar.gz", hash = "sha256:e8da3947d92123eda795b68228cafe2724815621fe35e8e320a9e9593a4bcd53", size = 5422810 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/27/16/2ceb9fb7bc2b11b1e4a3ea27794256e93dee2309ebe297fd131a778cd150/orjson-3.10.18-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:a45e5d68066b408e4bc383b6e4ef05e717c65219a9e1390abc6155a520cac402", size = 248927 }, - { url = "https://files.pythonhosted.org/packages/3d/e1/d3c0a2bba5b9906badd121da449295062b289236c39c3a7801f92c4682b0/orjson-3.10.18-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be3b9b143e8b9db05368b13b04c84d37544ec85bb97237b3a923f076265ec89c", size = 136995 }, - { url = "https://files.pythonhosted.org/packages/d7/51/698dd65e94f153ee5ecb2586c89702c9e9d12f165a63e74eb9ea1299f4e1/orjson-3.10.18-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9b0aa09745e2c9b3bf779b096fa71d1cc2d801a604ef6dd79c8b1bfef52b2f92", size = 132893 }, - { url = "https://files.pythonhosted.org/packages/b3/e5/155ce5a2c43a85e790fcf8b985400138ce5369f24ee6770378ee6b691036/orjson-3.10.18-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:53a245c104d2792e65c8d225158f2b8262749ffe64bc7755b00024757d957a13", size = 137017 }, - { url = "https://files.pythonhosted.org/packages/46/bb/6141ec3beac3125c0b07375aee01b5124989907d61c72c7636136e4bd03e/orjson-3.10.18-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f9495ab2611b7f8a0a8a505bcb0f0cbdb5469caafe17b0e404c3c746f9900469", size = 138290 }, - { url = "https://files.pythonhosted.org/packages/77/36/6961eca0b66b7809d33c4ca58c6bd4c23a1b914fb23aba2fa2883f791434/orjson-3.10.18-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:73be1cbcebadeabdbc468f82b087df435843c809cd079a565fb16f0f3b23238f", size = 142828 }, - { url = "https://files.pythonhosted.org/packages/8b/2f/0c646d5fd689d3be94f4d83fa9435a6c4322c9b8533edbb3cd4bc8c5f69a/orjson-3.10.18-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe8936ee2679e38903df158037a2f1c108129dee218975122e37847fb1d4ac68", size = 132806 }, - { url = "https://files.pythonhosted.org/packages/ea/af/65907b40c74ef4c3674ef2bcfa311c695eb934710459841b3c2da212215c/orjson-3.10.18-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7115fcbc8525c74e4c2b608129bef740198e9a120ae46184dac7683191042056", size = 135005 }, - { url = "https://files.pythonhosted.org/packages/c7/d1/68bd20ac6a32cd1f1b10d23e7cc58ee1e730e80624e3031d77067d7150fc/orjson-3.10.18-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:771474ad34c66bc4d1c01f645f150048030694ea5b2709b87d3bda273ffe505d", size = 413418 }, - { url = "https://files.pythonhosted.org/packages/31/31/c701ec0bcc3e80e5cb6e319c628ef7b768aaa24b0f3b4c599df2eaacfa24/orjson-3.10.18-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:7c14047dbbea52886dd87169f21939af5d55143dad22d10db6a7514f058156a8", size = 153288 }, - { url = "https://files.pythonhosted.org/packages/d9/31/5e1aa99a10893a43cfc58009f9da840990cc8a9ebb75aa452210ba18587e/orjson-3.10.18-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:641481b73baec8db14fdf58f8967e52dc8bda1f2aba3aa5f5c1b07ed6df50b7f", size = 137181 }, - { url = "https://files.pythonhosted.org/packages/bf/8c/daba0ac1b8690011d9242a0f37235f7d17df6d0ad941021048523b76674e/orjson-3.10.18-cp310-cp310-win32.whl", hash = "sha256:607eb3ae0909d47280c1fc657c4284c34b785bae371d007595633f4b1a2bbe06", size = 142694 }, - { url = "https://files.pythonhosted.org/packages/16/62/8b687724143286b63e1d0fab3ad4214d54566d80b0ba9d67c26aaf28a2f8/orjson-3.10.18-cp310-cp310-win_amd64.whl", hash = "sha256:8770432524ce0eca50b7efc2a9a5f486ee0113a5fbb4231526d414e6254eba92", size = 134600 }, - { url = "https://files.pythonhosted.org/packages/97/c7/c54a948ce9a4278794f669a353551ce7db4ffb656c69a6e1f2264d563e50/orjson-3.10.18-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:e0a183ac3b8e40471e8d843105da6fbe7c070faab023be3b08188ee3f85719b8", size = 248929 }, - { url = "https://files.pythonhosted.org/packages/9e/60/a9c674ef1dd8ab22b5b10f9300e7e70444d4e3cda4b8258d6c2488c32143/orjson-3.10.18-cp311-cp311-macosx_15_0_arm64.whl", hash = "sha256:5ef7c164d9174362f85238d0cd4afdeeb89d9e523e4651add6a5d458d6f7d42d", size = 133364 }, - { url = "https://files.pythonhosted.org/packages/c1/4e/f7d1bdd983082216e414e6d7ef897b0c2957f99c545826c06f371d52337e/orjson-3.10.18-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afd14c5d99cdc7bf93f22b12ec3b294931518aa019e2a147e8aa2f31fd3240f7", size = 136995 }, - { url = "https://files.pythonhosted.org/packages/17/89/46b9181ba0ea251c9243b0c8ce29ff7c9796fa943806a9c8b02592fce8ea/orjson-3.10.18-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7b672502323b6cd133c4af6b79e3bea36bad2d16bca6c1f645903fce83909a7a", size = 132894 }, - { url = "https://files.pythonhosted.org/packages/ca/dd/7bce6fcc5b8c21aef59ba3c67f2166f0a1a9b0317dcca4a9d5bd7934ecfd/orjson-3.10.18-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:51f8c63be6e070ec894c629186b1c0fe798662b8687f3d9fdfa5e401c6bd7679", size = 137016 }, - { url = "https://files.pythonhosted.org/packages/1c/4a/b8aea1c83af805dcd31c1f03c95aabb3e19a016b2a4645dd822c5686e94d/orjson-3.10.18-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3f9478ade5313d724e0495d167083c6f3be0dd2f1c9c8a38db9a9e912cdaf947", size = 138290 }, - { url = "https://files.pythonhosted.org/packages/36/d6/7eb05c85d987b688707f45dcf83c91abc2251e0dd9fb4f7be96514f838b1/orjson-3.10.18-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:187aefa562300a9d382b4b4eb9694806e5848b0cedf52037bb5c228c61bb66d4", size = 142829 }, - { url = "https://files.pythonhosted.org/packages/d2/78/ddd3ee7873f2b5f90f016bc04062713d567435c53ecc8783aab3a4d34915/orjson-3.10.18-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9da552683bc9da222379c7a01779bddd0ad39dd699dd6300abaf43eadee38334", size = 132805 }, - { url = "https://files.pythonhosted.org/packages/8c/09/c8e047f73d2c5d21ead9c180203e111cddeffc0848d5f0f974e346e21c8e/orjson-3.10.18-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e450885f7b47a0231979d9c49b567ed1c4e9f69240804621be87c40bc9d3cf17", size = 135008 }, - { url = "https://files.pythonhosted.org/packages/0c/4b/dccbf5055ef8fb6eda542ab271955fc1f9bf0b941a058490293f8811122b/orjson-3.10.18-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:5e3c9cc2ba324187cd06287ca24f65528f16dfc80add48dc99fa6c836bb3137e", size = 413419 }, - { url = "https://files.pythonhosted.org/packages/8a/f3/1eac0c5e2d6d6790bd2025ebfbefcbd37f0d097103d76f9b3f9302af5a17/orjson-3.10.18-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:50ce016233ac4bfd843ac5471e232b865271d7d9d44cf9d33773bcd883ce442b", size = 153292 }, - { url = "https://files.pythonhosted.org/packages/1f/b4/ef0abf64c8f1fabf98791819ab502c2c8c1dc48b786646533a93637d8999/orjson-3.10.18-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b3ceff74a8f7ffde0b2785ca749fc4e80e4315c0fd887561144059fb1c138aa7", size = 137182 }, - { url = "https://files.pythonhosted.org/packages/a9/a3/6ea878e7b4a0dc5c888d0370d7752dcb23f402747d10e2257478d69b5e63/orjson-3.10.18-cp311-cp311-win32.whl", hash = "sha256:fdba703c722bd868c04702cac4cb8c6b8ff137af2623bc0ddb3b3e6a2c8996c1", size = 142695 }, - { url = "https://files.pythonhosted.org/packages/79/2a/4048700a3233d562f0e90d5572a849baa18ae4e5ce4c3ba6247e4ece57b0/orjson-3.10.18-cp311-cp311-win_amd64.whl", hash = "sha256:c28082933c71ff4bc6ccc82a454a2bffcef6e1d7379756ca567c772e4fb3278a", size = 134603 }, - { url = "https://files.pythonhosted.org/packages/03/45/10d934535a4993d27e1c84f1810e79ccf8b1b7418cef12151a22fe9bb1e1/orjson-3.10.18-cp311-cp311-win_arm64.whl", hash = "sha256:a6c7c391beaedd3fa63206e5c2b7b554196f14debf1ec9deb54b5d279b1b46f5", size = 131400 }, - { url = "https://files.pythonhosted.org/packages/21/1a/67236da0916c1a192d5f4ccbe10ec495367a726996ceb7614eaa687112f2/orjson-3.10.18-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:50c15557afb7f6d63bc6d6348e0337a880a04eaa9cd7c9d569bcb4e760a24753", size = 249184 }, - { url = "https://files.pythonhosted.org/packages/b3/bc/c7f1db3b1d094dc0c6c83ed16b161a16c214aaa77f311118a93f647b32dc/orjson-3.10.18-cp312-cp312-macosx_15_0_arm64.whl", hash = "sha256:356b076f1662c9813d5fa56db7d63ccceef4c271b1fb3dd522aca291375fcf17", size = 133279 }, - { url = "https://files.pythonhosted.org/packages/af/84/664657cd14cc11f0d81e80e64766c7ba5c9b7fc1ec304117878cc1b4659c/orjson-3.10.18-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:559eb40a70a7494cd5beab2d73657262a74a2c59aff2068fdba8f0424ec5b39d", size = 136799 }, - { url = "https://files.pythonhosted.org/packages/9a/bb/f50039c5bb05a7ab024ed43ba25d0319e8722a0ac3babb0807e543349978/orjson-3.10.18-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f3c29eb9a81e2fbc6fd7ddcfba3e101ba92eaff455b8d602bf7511088bbc0eae", size = 132791 }, - { url = "https://files.pythonhosted.org/packages/93/8c/ee74709fc072c3ee219784173ddfe46f699598a1723d9d49cbc78d66df65/orjson-3.10.18-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6612787e5b0756a171c7d81ba245ef63a3533a637c335aa7fcb8e665f4a0966f", size = 137059 }, - { url = "https://files.pythonhosted.org/packages/6a/37/e6d3109ee004296c80426b5a62b47bcadd96a3deab7443e56507823588c5/orjson-3.10.18-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ac6bd7be0dcab5b702c9d43d25e70eb456dfd2e119d512447468f6405b4a69c", size = 138359 }, - { url = "https://files.pythonhosted.org/packages/4f/5d/387dafae0e4691857c62bd02839a3bf3fa648eebd26185adfac58d09f207/orjson-3.10.18-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9f72f100cee8dde70100406d5c1abba515a7df926d4ed81e20a9730c062fe9ad", size = 142853 }, - { url = "https://files.pythonhosted.org/packages/27/6f/875e8e282105350b9a5341c0222a13419758545ae32ad6e0fcf5f64d76aa/orjson-3.10.18-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9dca85398d6d093dd41dc0983cbf54ab8e6afd1c547b6b8a311643917fbf4e0c", size = 133131 }, - { url = "https://files.pythonhosted.org/packages/48/b2/73a1f0b4790dcb1e5a45f058f4f5dcadc8a85d90137b50d6bbc6afd0ae50/orjson-3.10.18-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:22748de2a07fcc8781a70edb887abf801bb6142e6236123ff93d12d92db3d406", size = 134834 }, - { url = "https://files.pythonhosted.org/packages/56/f5/7ed133a5525add9c14dbdf17d011dd82206ca6840811d32ac52a35935d19/orjson-3.10.18-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:3a83c9954a4107b9acd10291b7f12a6b29e35e8d43a414799906ea10e75438e6", size = 413368 }, - { url = "https://files.pythonhosted.org/packages/11/7c/439654221ed9c3324bbac7bdf94cf06a971206b7b62327f11a52544e4982/orjson-3.10.18-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:303565c67a6c7b1f194c94632a4a39918e067bd6176a48bec697393865ce4f06", size = 153359 }, - { url = "https://files.pythonhosted.org/packages/48/e7/d58074fa0cc9dd29a8fa2a6c8d5deebdfd82c6cfef72b0e4277c4017563a/orjson-3.10.18-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:86314fdb5053a2f5a5d881f03fca0219bfdf832912aa88d18676a5175c6916b5", size = 137466 }, - { url = "https://files.pythonhosted.org/packages/57/4d/fe17581cf81fb70dfcef44e966aa4003360e4194d15a3f38cbffe873333a/orjson-3.10.18-cp312-cp312-win32.whl", hash = "sha256:187ec33bbec58c76dbd4066340067d9ece6e10067bb0cc074a21ae3300caa84e", size = 142683 }, - { url = "https://files.pythonhosted.org/packages/e6/22/469f62d25ab5f0f3aee256ea732e72dc3aab6d73bac777bd6277955bceef/orjson-3.10.18-cp312-cp312-win_amd64.whl", hash = "sha256:f9f94cf6d3f9cd720d641f8399e390e7411487e493962213390d1ae45c7814fc", size = 134754 }, - { url = "https://files.pythonhosted.org/packages/10/b0/1040c447fac5b91bc1e9c004b69ee50abb0c1ffd0d24406e1350c58a7fcb/orjson-3.10.18-cp312-cp312-win_arm64.whl", hash = "sha256:3d600be83fe4514944500fa8c2a0a77099025ec6482e8087d7659e891f23058a", size = 131218 }, - { url = "https://files.pythonhosted.org/packages/04/f0/8aedb6574b68096f3be8f74c0b56d36fd94bcf47e6c7ed47a7bd1474aaa8/orjson-3.10.18-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:69c34b9441b863175cc6a01f2935de994025e773f814412030f269da4f7be147", size = 249087 }, - { url = "https://files.pythonhosted.org/packages/bc/f7/7118f965541aeac6844fcb18d6988e111ac0d349c9b80cda53583e758908/orjson-3.10.18-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:1ebeda919725f9dbdb269f59bc94f861afbe2a27dce5608cdba2d92772364d1c", size = 133273 }, - { url = "https://files.pythonhosted.org/packages/fb/d9/839637cc06eaf528dd8127b36004247bf56e064501f68df9ee6fd56a88ee/orjson-3.10.18-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5adf5f4eed520a4959d29ea80192fa626ab9a20b2ea13f8f6dc58644f6927103", size = 136779 }, - { url = "https://files.pythonhosted.org/packages/2b/6d/f226ecfef31a1f0e7d6bf9a31a0bbaf384c7cbe3fce49cc9c2acc51f902a/orjson-3.10.18-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7592bb48a214e18cd670974f289520f12b7aed1fa0b2e2616b8ed9e069e08595", size = 132811 }, - { url = "https://files.pythonhosted.org/packages/73/2d/371513d04143c85b681cf8f3bce743656eb5b640cb1f461dad750ac4b4d4/orjson-3.10.18-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f872bef9f042734110642b7a11937440797ace8c87527de25e0c53558b579ccc", size = 137018 }, - { url = "https://files.pythonhosted.org/packages/69/cb/a4d37a30507b7a59bdc484e4a3253c8141bf756d4e13fcc1da760a0b00cb/orjson-3.10.18-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0315317601149c244cb3ecef246ef5861a64824ccbcb8018d32c66a60a84ffbc", size = 138368 }, - { url = "https://files.pythonhosted.org/packages/1e/ae/cd10883c48d912d216d541eb3db8b2433415fde67f620afe6f311f5cd2ca/orjson-3.10.18-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e0da26957e77e9e55a6c2ce2e7182a36a6f6b180ab7189315cb0995ec362e049", size = 142840 }, - { url = "https://files.pythonhosted.org/packages/6d/4c/2bda09855c6b5f2c055034c9eda1529967b042ff8d81a05005115c4e6772/orjson-3.10.18-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb70d489bc79b7519e5803e2cc4c72343c9dc1154258adf2f8925d0b60da7c58", size = 133135 }, - { url = "https://files.pythonhosted.org/packages/13/4a/35971fd809a8896731930a80dfff0b8ff48eeb5d8b57bb4d0d525160017f/orjson-3.10.18-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e9e86a6af31b92299b00736c89caf63816f70a4001e750bda179e15564d7a034", size = 134810 }, - { url = "https://files.pythonhosted.org/packages/99/70/0fa9e6310cda98365629182486ff37a1c6578e34c33992df271a476ea1cd/orjson-3.10.18-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:c382a5c0b5931a5fc5405053d36c1ce3fd561694738626c77ae0b1dfc0242ca1", size = 413491 }, - { url = "https://files.pythonhosted.org/packages/32/cb/990a0e88498babddb74fb97855ae4fbd22a82960e9b06eab5775cac435da/orjson-3.10.18-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:8e4b2ae732431127171b875cb2668f883e1234711d3c147ffd69fe5be51a8012", size = 153277 }, - { url = "https://files.pythonhosted.org/packages/92/44/473248c3305bf782a384ed50dd8bc2d3cde1543d107138fd99b707480ca1/orjson-3.10.18-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2d808e34ddb24fc29a4d4041dcfafbae13e129c93509b847b14432717d94b44f", size = 137367 }, - { url = "https://files.pythonhosted.org/packages/ad/fd/7f1d3edd4ffcd944a6a40e9f88af2197b619c931ac4d3cfba4798d4d3815/orjson-3.10.18-cp313-cp313-win32.whl", hash = "sha256:ad8eacbb5d904d5591f27dee4031e2c1db43d559edb8f91778efd642d70e6bea", size = 142687 }, - { url = "https://files.pythonhosted.org/packages/4b/03/c75c6ad46be41c16f4cfe0352a2d1450546f3c09ad2c9d341110cd87b025/orjson-3.10.18-cp313-cp313-win_amd64.whl", hash = "sha256:aed411bcb68bf62e85588f2a7e03a6082cc42e5a2796e06e72a962d7c6310b52", size = 134794 }, - { url = "https://files.pythonhosted.org/packages/c2/28/f53038a5a72cc4fd0b56c1eafb4ef64aec9685460d5ac34de98ca78b6e29/orjson-3.10.18-cp313-cp313-win_arm64.whl", hash = "sha256:f54c1385a0e6aba2f15a40d703b858bedad36ded0491e55d35d905b2c34a4cc3", size = 131186 }, +version = "3.11.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/29/87/03ababa86d984952304ac8ce9fbd3a317afb4a225b9a81f9b606ac60c873/orjson-3.11.0.tar.gz", hash = "sha256:2e4c129da624f291bcc607016a99e7f04a353f6874f3bd8d9b47b88597d5f700", size = 5318246 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/07/aa/50818f480f0edcb33290c8f35eef6dd3a31e2ff7e1195f8b236ac7419811/orjson-3.11.0-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:b8913baba9751f7400f8fa4ec18a8b618ff01177490842e39e47b66c1b04bc79", size = 240422 }, + { url = "https://files.pythonhosted.org/packages/16/50/5235aff455fa76337493d21e68618e7cf53aa9db011aaeb06cf378f1344c/orjson-3.11.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9d4d86910554de5c9c87bc560b3bdd315cc3988adbdc2acf5dda3797079407ed", size = 132473 }, + { url = "https://files.pythonhosted.org/packages/23/93/bf1c4e77e7affc46cca13fb852842a86dca2dabbee1d91515ed17b1c21c4/orjson-3.11.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:84ae3d329360cf18fb61b67c505c00dedb61b0ee23abfd50f377a58e7d7bed06", size = 127195 }, + { url = "https://files.pythonhosted.org/packages/7e/2d/64b52c6827e43aa3d98def19e188e091a6c574ca13d9ecef5f3f3284fac6/orjson-3.11.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:47a54e660414baacd71ebf41a69bb17ea25abb3c5b69ce9e13e43be7ac20e342", size = 128895 }, + { url = "https://files.pythonhosted.org/packages/ca/5f/9d290bc7a88392f9f7dc2e92ceb2e3efbbebaaf56bbba655b5fe2e3d2ca3/orjson-3.11.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2560b740604751854be146169c1de7e7ee1e6120b00c1788ec3f3a012c6a243f", size = 132016 }, + { url = "https://files.pythonhosted.org/packages/ef/8c/b2bdc34649bbb7b44827d487aef7ad4d6a96c53ebc490ddcc191d47bc3b9/orjson-3.11.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dd7f9cd995da9e46fbac0a371f0ff6e89a21d8ecb7a8a113c0acb147b0a32f73", size = 134251 }, + { url = "https://files.pythonhosted.org/packages/33/be/b763b602976aa27407e6f75331ac581258c719f8abb70f66f2de962f649f/orjson-3.11.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7cf728cb3a013bdf9f4132575404bf885aa773d8bb4205656575e1890fc91990", size = 128078 }, + { url = "https://files.pythonhosted.org/packages/ac/24/1b0fed70392bf179ac8b5abe800f1102ed94f89ac4f889d83916947a2b4e/orjson-3.11.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c27de273320294121200440cd5002b6aeb922d3cb9dab3357087c69f04ca6934", size = 130734 }, + { url = "https://files.pythonhosted.org/packages/05/d2/2d042bb4fe1da067692cb70d8c01a5ce2737e2f56444e6b2d716853ce8c3/orjson-3.11.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:4430ec6ff1a1f4595dd7e0fad991bdb2fed65401ed294984c490ffa025926325", size = 404040 }, + { url = "https://files.pythonhosted.org/packages/b4/c5/54938ab416c0d19c93f0d6977a47bb2b3d121e150305380b783f7d6da185/orjson-3.11.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:325be41a8d7c227d460a9795a181511ba0e731cf3fee088c63eb47e706ea7559", size = 144808 }, + { url = "https://files.pythonhosted.org/packages/6d/be/5ead422f396ee7c8941659ceee3da001e26998971f7d5fe0a38519c48aa5/orjson-3.11.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d9760217b84d1aee393b4436fbe9c639e963ec7bc0f2c074581ce5fb3777e466", size = 132570 }, + { url = "https://files.pythonhosted.org/packages/f6/01/db8352f7d0374d7eec25144e294991800aa85738b2dc7f19cc152ba1b254/orjson-3.11.0-cp310-cp310-win32.whl", hash = "sha256:fe36e5012f886ff91c68b87a499c227fa220e9668cea96335219874c8be5fab5", size = 134763 }, + { url = "https://files.pythonhosted.org/packages/8b/f5/1322b64d5836d92f0b0c119d959853b3c968b8aae23dd1e3c1bfa566823b/orjson-3.11.0-cp310-cp310-win_amd64.whl", hash = "sha256:ebeecd5d5511b3ca9dc4e7db0ab95266afd41baf424cc2fad8c2d3a3cdae650a", size = 129506 }, + { url = "https://files.pythonhosted.org/packages/f9/2c/0b71a763f0f5130aa2631ef79e2cd84d361294665acccbb12b7a9813194e/orjson-3.11.0-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:1785df7ada75c18411ff7e20ac822af904a40161ea9dfe8c55b3f6b66939add6", size = 240007 }, + { url = "https://files.pythonhosted.org/packages/f4/5a/f79ccd63d378b9c7c771d7a54c203d261b4c618fe3034ae95cd30f934f34/orjson-3.11.0-cp311-cp311-macosx_15_0_arm64.whl", hash = "sha256:a57899bebbcea146616a2426d20b51b3562b4bc9f8039a3bd14fae361c23053d", size = 129320 }, + { url = "https://files.pythonhosted.org/packages/7b/8a/63dafc147fa5ba945ad809c374b8f4ee692bb6b18aa6e161c3e6b69b594e/orjson-3.11.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b6fbc2fc825aff1456dd358c11a0ad7912a4cb4537d3db92e5334af7463a967", size = 132254 }, + { url = "https://files.pythonhosted.org/packages/3c/11/4d1eb230483cc689a2f039c531bb2c980029c40ca5a9b5f64dce9786e955/orjson-3.11.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4305a638f4cf9bed3746ca3b7c242f14e05177d5baec2527026e0f9ee6c24fb7", size = 127003 }, + { url = "https://files.pythonhosted.org/packages/4f/39/b6e96072946d908684e0f4b3de1639062fd5b32016b2929c035bd8e5c847/orjson-3.11.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1235fe7bbc37164f69302199d46f29cfb874018738714dccc5a5a44042c79c77", size = 128674 }, + { url = "https://files.pythonhosted.org/packages/1e/dd/c77e3013f35b202ec2cc1f78a95fadf86b8c5a320d56eb1a0bbb965a87bb/orjson-3.11.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a640e3954e7b4fcb160097551e54cafbde9966be3991932155b71071077881aa", size = 131846 }, + { url = "https://files.pythonhosted.org/packages/3f/7d/d83f0f96c2b142f9cdcf12df19052ea3767970989dc757598dc108db208f/orjson-3.11.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6d750b97d22d5566955e50b02c622f3a1d32744d7a578c878b29a873190ccb7a", size = 134016 }, + { url = "https://files.pythonhosted.org/packages/67/4f/d22f79a3c56dde563c4fbc12eebf9224a1b87af5e4ec61beb11f9b3eb499/orjson-3.11.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4bfcfe498484161e011f8190a400591c52b026de96b3b3cbd3f21e8999b9dc0e", size = 127930 }, + { url = "https://files.pythonhosted.org/packages/07/1e/26aede257db2163d974139fd4571f1e80f565216ccbd2c44ee1d43a63dcc/orjson-3.11.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:feaed3ed43a1d2df75c039798eb5ec92c350c7d86be53369bafc4f3700ce7df2", size = 130569 }, + { url = "https://files.pythonhosted.org/packages/b4/bf/2cb57eac8d6054b555cba27203490489a7d3f5dca8c34382f22f2f0f17ba/orjson-3.11.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:aa1120607ec8fc98acf8c54aac6fb0b7b003ba883401fa2d261833111e2fa071", size = 403844 }, + { url = "https://files.pythonhosted.org/packages/76/34/36e859ccfc45464df7b35c438c0ecc7751c930b3ebbefb50db7e3a641eb7/orjson-3.11.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:c4b48d9775b0cf1f0aca734f4c6b272cbfacfac38e6a455e6520662f9434afb7", size = 144613 }, + { url = "https://files.pythonhosted.org/packages/31/c5/5aeb84cdd0b44dc3972668944a1312f7983c2a45fb6b0e5e32b2f9408540/orjson-3.11.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f018ed1986d79434ac712ff19f951cd00b4dfcb767444410fbb834ebec160abf", size = 132419 }, + { url = "https://files.pythonhosted.org/packages/59/0c/95ee1e61a067ad24c4921609156b3beeca8b102f6f36dca62b08e1a7c7a8/orjson-3.11.0-cp311-cp311-win32.whl", hash = "sha256:08e191f8a55ac2c00be48e98a5d10dca004cbe8abe73392c55951bfda60fc123", size = 134620 }, + { url = "https://files.pythonhosted.org/packages/94/3e/afd5e284db9387023803553061ea05c785c36fe7845e4fe25912424b343f/orjson-3.11.0-cp311-cp311-win_amd64.whl", hash = "sha256:b5a4214ea59c8a3b56f8d484b28114af74e9fba0956f9be5c3ce388ae143bf1f", size = 129333 }, + { url = "https://files.pythonhosted.org/packages/8b/a4/d29e9995d73f23f2444b4db299a99477a4f7e6f5bf8923b775ef43a4e660/orjson-3.11.0-cp311-cp311-win_arm64.whl", hash = "sha256:57e8e7198a679ab21241ab3f355a7990c7447559e35940595e628c107ef23736", size = 126656 }, + { url = "https://files.pythonhosted.org/packages/92/c9/241e304fb1e58ea70b720f1a9e5349c6bb7735ffac401ef1b94f422edd6d/orjson-3.11.0-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:b4089f940c638bb1947d54e46c1cd58f4259072fcc97bc833ea9c78903150ac9", size = 240269 }, + { url = "https://files.pythonhosted.org/packages/26/7c/289457cdf40be992b43f1d90ae213ebc03a31a8e2850271ecd79e79a3135/orjson-3.11.0-cp312-cp312-macosx_15_0_arm64.whl", hash = "sha256:8335a0ba1c26359fb5c82d643b4c1abbee2bc62875e0f2b5bde6c8e9e25eb68c", size = 129276 }, + { url = "https://files.pythonhosted.org/packages/66/de/5c0528d46ded965939b6b7f75b1fe93af42b9906b0039096fc92c9001c12/orjson-3.11.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63c1c9772dafc811d16d6a7efa3369a739da15d1720d6e58ebe7562f54d6f4a2", size = 131966 }, + { url = "https://files.pythonhosted.org/packages/ad/74/39822f267b5935fb6fc961ccc443f4968a74d34fc9270b83caa44e37d907/orjson-3.11.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9457ccbd8b241fb4ba516417a4c5b95ba0059df4ac801309bcb4ec3870f45ad9", size = 127028 }, + { url = "https://files.pythonhosted.org/packages/7c/e3/28f6ed7f03db69bddb3ef48621b2b05b394125188f5909ee0a43fcf4820e/orjson-3.11.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0846e13abe79daece94a00b92574f294acad1d362be766c04245b9b4dd0e47e1", size = 129105 }, + { url = "https://files.pythonhosted.org/packages/cb/50/8867fd2fc92c0ab1c3e14673ec5d9d0191202e4ab8ba6256d7a1d6943ad3/orjson-3.11.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5587c85ae02f608a3f377b6af9eb04829606f518257cbffa8f5081c1aacf2e2f", size = 131902 }, + { url = "https://files.pythonhosted.org/packages/13/65/c189deea10342afee08006331082ff67d11b98c2394989998b3ea060354a/orjson-3.11.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c7a1964a71c1567b4570c932a0084ac24ad52c8cf6253d1881400936565ed438", size = 134042 }, + { url = "https://files.pythonhosted.org/packages/2b/e4/cf23c3f4231d2a9a043940ab045f799f84a6df1b4fb6c9b4412cdc3ebf8c/orjson-3.11.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b5a8243e73690cc6e9151c9e1dd046a8f21778d775f7d478fa1eb4daa4897c61", size = 128260 }, + { url = "https://files.pythonhosted.org/packages/de/b9/2cb94d3a67edb918d19bad4a831af99cd96c3657a23daa239611bcf335d7/orjson-3.11.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:51646f6d995df37b6e1b628f092f41c0feccf1d47e3452c6e95e2474b547d842", size = 130282 }, + { url = "https://files.pythonhosted.org/packages/0b/96/df963cc973e689d4c56398647917b4ee95f47e5b6d2779338c09c015b23b/orjson-3.11.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:2fb8ca8f0b4e31b8aaec674c7540649b64ef02809410506a44dc68d31bd5647b", size = 403765 }, + { url = "https://files.pythonhosted.org/packages/fb/92/71429ee1badb69f53281602dbb270fa84fc2e51c83193a814d0208bb63b0/orjson-3.11.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:64a6a3e94a44856c3f6557e6aa56a6686544fed9816ae0afa8df9077f5759791", size = 144779 }, + { url = "https://files.pythonhosted.org/packages/c8/ab/3678b2e5ff0c622a974cb8664ed7cdda5ed26ae2b9d71ba66ec36f32d6cf/orjson-3.11.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d69f95d484938d8fab5963e09131bcf9fbbb81fa4ec132e316eb2fb9adb8ce78", size = 132797 }, + { url = "https://files.pythonhosted.org/packages/9d/8c/74509f715ff189d2aca90ebb0bd5af6658e0f9aa2512abbe6feca4c78208/orjson-3.11.0-cp312-cp312-win32.whl", hash = "sha256:8514f9f9c667ce7d7ef709ab1a73e7fcab78c297270e90b1963df7126d2b0e23", size = 134695 }, + { url = "https://files.pythonhosted.org/packages/82/ba/ef25e3e223f452a01eac6a5b38d05c152d037508dcbf87ad2858cbb7d82e/orjson-3.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:41b38a894520b8cb5344a35ffafdf6ae8042f56d16771b2c5eb107798cee85ee", size = 129446 }, + { url = "https://files.pythonhosted.org/packages/e3/cd/6f4d93867c5d81bb4ab2d4ac870d3d6e9ba34fa580a03b8d04bf1ce1d8ad/orjson-3.11.0-cp312-cp312-win_arm64.whl", hash = "sha256:5579acd235dd134467340b2f8a670c1c36023b5a69c6a3174c4792af7502bd92", size = 126400 }, + { url = "https://files.pythonhosted.org/packages/31/63/82d9b6b48624009d230bc6038e54778af8f84dfd54402f9504f477c5cfd5/orjson-3.11.0-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:4a8ba9698655e16746fdf5266939427da0f9553305152aeb1a1cc14974a19cfb", size = 240125 }, + { url = "https://files.pythonhosted.org/packages/16/3a/d557ed87c63237d4c97a7bac7ac054c347ab8c4b6da09748d162ca287175/orjson-3.11.0-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:67133847f9a35a5ef5acfa3325d4a2f7fe05c11f1505c4117bb086fc06f2a58f", size = 129189 }, + { url = "https://files.pythonhosted.org/packages/69/5e/b2c9e22e2cd10aa7d76a629cee65d661e06a61fbaf4dc226386f5636dd44/orjson-3.11.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f797d57814975b78f5f5423acb003db6f9be5186b72d48bd97a1000e89d331d", size = 131953 }, + { url = "https://files.pythonhosted.org/packages/e2/60/760fcd9b50eb44d1206f2b30c8d310b79714553b9d94a02f9ea3252ebe63/orjson-3.11.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:28acd19822987c5163b9e03a6e60853a52acfee384af2b394d11cb413b889246", size = 126922 }, + { url = "https://files.pythonhosted.org/packages/6a/7a/8c46daa867ccc92da6de9567608be62052774b924a77c78382e30d50b579/orjson-3.11.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e8d38d9e1e2cf9729658e35956cf01e13e89148beb4cb9e794c9c10c5cb252f8", size = 128787 }, + { url = "https://files.pythonhosted.org/packages/f2/14/a2f1b123d85f11a19e8749f7d3f9ed6c9b331c61f7b47cfd3e9a1fedb9bc/orjson-3.11.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:05f094edd2b782650b0761fd78858d9254de1c1286f5af43145b3d08cdacfd51", size = 131895 }, + { url = "https://files.pythonhosted.org/packages/c8/10/362e8192df7528e8086ea712c5cb01355c8d4e52c59a804417ba01e2eb2d/orjson-3.11.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6d09176a4a9e04a5394a4a0edd758f645d53d903b306d02f2691b97d5c736a9e", size = 133868 }, + { url = "https://files.pythonhosted.org/packages/f8/4e/ef43582ef3e3dfd2a39bc3106fa543364fde1ba58489841120219da6e22f/orjson-3.11.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2a585042104e90a61eda2564d11317b6a304eb4e71cd33e839f5af6be56c34d3", size = 128234 }, + { url = "https://files.pythonhosted.org/packages/d7/fa/02dabb2f1d605bee8c4bb1160cfc7467976b1ed359a62cc92e0681b53c45/orjson-3.11.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d2218629dbfdeeb5c9e0573d59f809d42f9d49ae6464d2f479e667aee14c3ef4", size = 130232 }, + { url = "https://files.pythonhosted.org/packages/16/76/951b5619605c8d2ede80cc989f32a66abc954530d86e84030db2250c63a1/orjson-3.11.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:613e54a2b10b51b656305c11235a9c4a5c5491ef5c283f86483d4e9e123ed5e4", size = 403648 }, + { url = "https://files.pythonhosted.org/packages/96/e2/5fa53bb411455a63b3713db90b588e6ca5ed2db59ad49b3fb8a0e94e0dda/orjson-3.11.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:9dac7fbf3b8b05965986c5cfae051eb9a30fced7f15f1d13a5adc608436eb486", size = 144572 }, + { url = "https://files.pythonhosted.org/packages/ad/d0/7d6f91e1e0f034258c3a3358f20b0c9490070e8a7ab8880085547274c7f9/orjson-3.11.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:93b64b254414e2be55ac5257124b5602c5f0b4d06b80bd27d1165efe8f36e836", size = 132766 }, + { url = "https://files.pythonhosted.org/packages/ff/f8/4d46481f1b3fb40dc826d62179f96c808eb470cdcc74b6593fb114d74af3/orjson-3.11.0-cp313-cp313-win32.whl", hash = "sha256:359cbe11bc940c64cb3848cf22000d2aef36aff7bfd09ca2c0b9cb309c387132", size = 134638 }, + { url = "https://files.pythonhosted.org/packages/85/3f/544938dcfb7337d85ee1e43d7685cf8f3bfd452e0b15a32fe70cb4ca5094/orjson-3.11.0-cp313-cp313-win_amd64.whl", hash = "sha256:0759b36428067dc777b202dd286fbdd33d7f261c6455c4238ea4e8474358b1e6", size = 129411 }, + { url = "https://files.pythonhosted.org/packages/43/0c/f75015669d7817d222df1bb207f402277b77d22c4833950c8c8c7cf2d325/orjson-3.11.0-cp313-cp313-win_arm64.whl", hash = "sha256:51cdca2f36e923126d0734efaf72ddbb5d6da01dbd20eab898bdc50de80d7b5a", size = 126349 }, ] [[package]] name = "packaging" -version = "24.2" +version = "25.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d0/63/68dbb6eb2de9cb10ee4c9c14a0148804425e13c4fb20d61cce69f53106da/packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f", size = 163950 } +sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727 } wheels = [ - { url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451 }, + { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469 }, ] [[package]] name = "pandas" -version = "2.3.0" +version = "2.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "numpy", version = "2.3.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "python-dateutil" }, { name = "pytz" }, { name = "tzdata" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/72/51/48f713c4c728d7c55ef7444ba5ea027c26998d96d1a40953b346438602fc/pandas-2.3.0.tar.gz", hash = "sha256:34600ab34ebf1131a7613a260a61dbe8b62c188ec0ea4c296da7c9a06b004133", size = 4484490 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e2/2d/df6b98c736ba51b8eaa71229e8fcd91233a831ec00ab520e1e23090cc072/pandas-2.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:625466edd01d43b75b1883a64d859168e4556261a5035b32f9d743b67ef44634", size = 11527531 }, - { url = "https://files.pythonhosted.org/packages/77/1c/3f8c331d223f86ba1d0ed7d3ed7fcf1501c6f250882489cc820d2567ddbf/pandas-2.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a6872d695c896f00df46b71648eea332279ef4077a409e2fe94220208b6bb675", size = 10774764 }, - { url = "https://files.pythonhosted.org/packages/1b/45/d2599400fad7fe06b849bd40b52c65684bc88fbe5f0a474d0513d057a377/pandas-2.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4dd97c19bd06bc557ad787a15b6489d2614ddaab5d104a0310eb314c724b2d2", size = 11711963 }, - { url = "https://files.pythonhosted.org/packages/66/f8/5508bc45e994e698dbc93607ee6b9b6eb67df978dc10ee2b09df80103d9e/pandas-2.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:034abd6f3db8b9880aaee98f4f5d4dbec7c4829938463ec046517220b2f8574e", size = 12349446 }, - { url = "https://files.pythonhosted.org/packages/f7/fc/17851e1b1ea0c8456ba90a2f514c35134dd56d981cf30ccdc501a0adeac4/pandas-2.3.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:23c2b2dc5213810208ca0b80b8666670eb4660bbfd9d45f58592cc4ddcfd62e1", size = 12920002 }, - { url = "https://files.pythonhosted.org/packages/a1/9b/8743be105989c81fa33f8e2a4e9822ac0ad4aaf812c00fee6bb09fc814f9/pandas-2.3.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:39ff73ec07be5e90330cc6ff5705c651ace83374189dcdcb46e6ff54b4a72cd6", size = 13651218 }, - { url = "https://files.pythonhosted.org/packages/26/fa/8eeb2353f6d40974a6a9fd4081ad1700e2386cf4264a8f28542fd10b3e38/pandas-2.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:40cecc4ea5abd2921682b57532baea5588cc5f80f0231c624056b146887274d2", size = 11082485 }, - { url = "https://files.pythonhosted.org/packages/96/1e/ba313812a699fe37bf62e6194265a4621be11833f5fce46d9eae22acb5d7/pandas-2.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8adff9f138fc614347ff33812046787f7d43b3cef7c0f0171b3340cae333f6ca", size = 11551836 }, - { url = "https://files.pythonhosted.org/packages/1b/cc/0af9c07f8d714ea563b12383a7e5bde9479cf32413ee2f346a9c5a801f22/pandas-2.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e5f08eb9a445d07720776df6e641975665c9ea12c9d8a331e0f6890f2dcd76ef", size = 10807977 }, - { url = "https://files.pythonhosted.org/packages/ee/3e/8c0fb7e2cf4a55198466ced1ca6a9054ae3b7e7630df7757031df10001fd/pandas-2.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fa35c266c8cd1a67d75971a1912b185b492d257092bdd2709bbdebe574ed228d", size = 11788230 }, - { url = "https://files.pythonhosted.org/packages/14/22/b493ec614582307faf3f94989be0f7f0a71932ed6f56c9a80c0bb4a3b51e/pandas-2.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14a0cc77b0f089d2d2ffe3007db58f170dae9b9f54e569b299db871a3ab5bf46", size = 12370423 }, - { url = "https://files.pythonhosted.org/packages/9f/74/b012addb34cda5ce855218a37b258c4e056a0b9b334d116e518d72638737/pandas-2.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c06f6f144ad0a1bf84699aeea7eff6068ca5c63ceb404798198af7eb86082e33", size = 12990594 }, - { url = "https://files.pythonhosted.org/packages/95/81/b310e60d033ab64b08e66c635b94076488f0b6ce6a674379dd5b224fc51c/pandas-2.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ed16339bc354a73e0a609df36d256672c7d296f3f767ac07257801aa064ff73c", size = 13745952 }, - { url = "https://files.pythonhosted.org/packages/25/ac/f6ee5250a8881b55bd3aecde9b8cfddea2f2b43e3588bca68a4e9aaf46c8/pandas-2.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:fa07e138b3f6c04addfeaf56cc7fdb96c3b68a3fe5e5401251f231fce40a0d7a", size = 11094534 }, - { url = "https://files.pythonhosted.org/packages/94/46/24192607058dd607dbfacdd060a2370f6afb19c2ccb617406469b9aeb8e7/pandas-2.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2eb4728a18dcd2908c7fccf74a982e241b467d178724545a48d0caf534b38ebf", size = 11573865 }, - { url = "https://files.pythonhosted.org/packages/9f/cc/ae8ea3b800757a70c9fdccc68b67dc0280a6e814efcf74e4211fd5dea1ca/pandas-2.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b9d8c3187be7479ea5c3d30c32a5d73d62a621166675063b2edd21bc47614027", size = 10702154 }, - { url = "https://files.pythonhosted.org/packages/d8/ba/a7883d7aab3d24c6540a2768f679e7414582cc389876d469b40ec749d78b/pandas-2.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9ff730713d4c4f2f1c860e36c005c7cefc1c7c80c21c0688fd605aa43c9fcf09", size = 11262180 }, - { url = "https://files.pythonhosted.org/packages/01/a5/931fc3ad333d9d87b10107d948d757d67ebcfc33b1988d5faccc39c6845c/pandas-2.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba24af48643b12ffe49b27065d3babd52702d95ab70f50e1b34f71ca703e2c0d", size = 11991493 }, - { url = "https://files.pythonhosted.org/packages/d7/bf/0213986830a92d44d55153c1d69b509431a972eb73f204242988c4e66e86/pandas-2.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:404d681c698e3c8a40a61d0cd9412cc7364ab9a9cc6e144ae2992e11a2e77a20", size = 12470733 }, - { url = "https://files.pythonhosted.org/packages/a4/0e/21eb48a3a34a7d4bac982afc2c4eb5ab09f2d988bdf29d92ba9ae8e90a79/pandas-2.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6021910b086b3ca756755e86ddc64e0ddafd5e58e076c72cb1585162e5ad259b", size = 13212406 }, - { url = "https://files.pythonhosted.org/packages/1f/d9/74017c4eec7a28892d8d6e31ae9de3baef71f5a5286e74e6b7aad7f8c837/pandas-2.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:094e271a15b579650ebf4c5155c05dcd2a14fd4fdd72cf4854b2f7ad31ea30be", size = 10976199 }, - { url = "https://files.pythonhosted.org/packages/d3/57/5cb75a56a4842bbd0511c3d1c79186d8315b82dac802118322b2de1194fe/pandas-2.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2c7e2fc25f89a49a11599ec1e76821322439d90820108309bf42130d2f36c983", size = 11518913 }, - { url = "https://files.pythonhosted.org/packages/05/01/0c8785610e465e4948a01a059562176e4c8088aa257e2e074db868f86d4e/pandas-2.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c6da97aeb6a6d233fb6b17986234cc723b396b50a3c6804776351994f2a658fd", size = 10655249 }, - { url = "https://files.pythonhosted.org/packages/e8/6a/47fd7517cd8abe72a58706aab2b99e9438360d36dcdb052cf917b7bf3bdc/pandas-2.3.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb32dc743b52467d488e7a7c8039b821da2826a9ba4f85b89ea95274f863280f", size = 11328359 }, - { url = "https://files.pythonhosted.org/packages/2a/b3/463bfe819ed60fb7e7ddffb4ae2ee04b887b3444feee6c19437b8f834837/pandas-2.3.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:213cd63c43263dbb522c1f8a7c9d072e25900f6975596f883f4bebd77295d4f3", size = 12024789 }, - { url = "https://files.pythonhosted.org/packages/04/0c/e0704ccdb0ac40aeb3434d1c641c43d05f75c92e67525df39575ace35468/pandas-2.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1d2b33e68d0ce64e26a4acc2e72d747292084f4e8db4c847c6f5f6cbe56ed6d8", size = 12480734 }, - { url = "https://files.pythonhosted.org/packages/e9/df/815d6583967001153bb27f5cf075653d69d51ad887ebbf4cfe1173a1ac58/pandas-2.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:430a63bae10b5086995db1b02694996336e5a8ac9a96b4200572b413dfdfccb9", size = 13223381 }, - { url = "https://files.pythonhosted.org/packages/79/88/ca5973ed07b7f484c493e941dbff990861ca55291ff7ac67c815ce347395/pandas-2.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:4930255e28ff5545e2ca404637bcc56f031893142773b3468dc021c6c32a1390", size = 10970135 }, - { url = "https://files.pythonhosted.org/packages/24/fb/0994c14d1f7909ce83f0b1fb27958135513c4f3f2528bde216180aa73bfc/pandas-2.3.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:f925f1ef673b4bd0271b1809b72b3270384f2b7d9d14a189b12b7fc02574d575", size = 12141356 }, - { url = "https://files.pythonhosted.org/packages/9d/a2/9b903e5962134497ac4f8a96f862ee3081cb2506f69f8e4778ce3d9c9d82/pandas-2.3.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e78ad363ddb873a631e92a3c063ade1ecfb34cae71e9a2be6ad100f875ac1042", size = 11474674 }, - { url = "https://files.pythonhosted.org/packages/81/3a/3806d041bce032f8de44380f866059437fb79e36d6b22c82c187e65f765b/pandas-2.3.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:951805d146922aed8357e4cc5671b8b0b9be1027f0619cea132a9f3f65f2f09c", size = 11439876 }, - { url = "https://files.pythonhosted.org/packages/15/aa/3fc3181d12b95da71f5c2537c3e3b3af6ab3a8c392ab41ebb766e0929bc6/pandas-2.3.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a881bc1309f3fce34696d07b00f13335c41f5f5a8770a33b09ebe23261cfc67", size = 11966182 }, - { url = "https://files.pythonhosted.org/packages/37/e7/e12f2d9b0a2c4a2cc86e2aabff7ccfd24f03e597d770abfa2acd313ee46b/pandas-2.3.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:e1991bbb96f4050b09b5f811253c4f3cf05ee89a589379aa36cd623f21a31d6f", size = 12547686 }, - { url = "https://files.pythonhosted.org/packages/39/c2/646d2e93e0af70f4e5359d870a63584dacbc324b54d73e6b3267920ff117/pandas-2.3.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:bb3be958022198531eb7ec2008cfc78c5b1eed51af8600c6c5d9160d89d8d249", size = 13231847 }, +sdist = { url = "https://files.pythonhosted.org/packages/d1/6f/75aa71f8a14267117adeeed5d21b204770189c0a0025acbdc03c337b28fc/pandas-2.3.1.tar.gz", hash = "sha256:0a95b9ac964fe83ce317827f80304d37388ea77616b1425f0ae41c9d2d0d7bb2", size = 4487493 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c4/ca/aa97b47287221fa37a49634532e520300088e290b20d690b21ce3e448143/pandas-2.3.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:22c2e866f7209ebc3a8f08d75766566aae02bcc91d196935a1d9e59c7b990ac9", size = 11542731 }, + { url = "https://files.pythonhosted.org/packages/80/bf/7938dddc5f01e18e573dcfb0f1b8c9357d9b5fa6ffdee6e605b92efbdff2/pandas-2.3.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3583d348546201aff730c8c47e49bc159833f971c2899d6097bce68b9112a4f1", size = 10790031 }, + { url = "https://files.pythonhosted.org/packages/ee/2f/9af748366763b2a494fed477f88051dbf06f56053d5c00eba652697e3f94/pandas-2.3.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0f951fbb702dacd390561e0ea45cdd8ecfa7fb56935eb3dd78e306c19104b9b0", size = 11724083 }, + { url = "https://files.pythonhosted.org/packages/2c/95/79ab37aa4c25d1e7df953dde407bb9c3e4ae47d154bc0dd1692f3a6dcf8c/pandas-2.3.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cd05b72ec02ebfb993569b4931b2e16fbb4d6ad6ce80224a3ee838387d83a191", size = 12342360 }, + { url = "https://files.pythonhosted.org/packages/75/a7/d65e5d8665c12c3c6ff5edd9709d5836ec9b6f80071b7f4a718c6106e86e/pandas-2.3.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:1b916a627919a247d865aed068eb65eb91a344b13f5b57ab9f610b7716c92de1", size = 13202098 }, + { url = "https://files.pythonhosted.org/packages/65/f3/4c1dbd754dbaa79dbf8b537800cb2fa1a6e534764fef50ab1f7533226c5c/pandas-2.3.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:fe67dc676818c186d5a3d5425250e40f179c2a89145df477dd82945eaea89e97", size = 13837228 }, + { url = "https://files.pythonhosted.org/packages/3f/d6/d7f5777162aa9b48ec3910bca5a58c9b5927cfd9cfde3aa64322f5ba4b9f/pandas-2.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:2eb789ae0274672acbd3c575b0598d213345660120a257b47b5dafdc618aec83", size = 11336561 }, + { url = "https://files.pythonhosted.org/packages/76/1c/ccf70029e927e473a4476c00e0d5b32e623bff27f0402d0a92b7fc29bb9f/pandas-2.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2b0540963d83431f5ce8870ea02a7430adca100cec8a050f0811f8e31035541b", size = 11566608 }, + { url = "https://files.pythonhosted.org/packages/ec/d3/3c37cb724d76a841f14b8f5fe57e5e3645207cc67370e4f84717e8bb7657/pandas-2.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fe7317f578c6a153912bd2292f02e40c1d8f253e93c599e82620c7f69755c74f", size = 10823181 }, + { url = "https://files.pythonhosted.org/packages/8a/4c/367c98854a1251940edf54a4df0826dcacfb987f9068abf3e3064081a382/pandas-2.3.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e6723a27ad7b244c0c79d8e7007092d7c8f0f11305770e2f4cd778b3ad5f9f85", size = 11793570 }, + { url = "https://files.pythonhosted.org/packages/07/5f/63760ff107bcf5146eee41b38b3985f9055e710a72fdd637b791dea3495c/pandas-2.3.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3462c3735fe19f2638f2c3a40bd94ec2dc5ba13abbb032dd2fa1f540a075509d", size = 12378887 }, + { url = "https://files.pythonhosted.org/packages/15/53/f31a9b4dfe73fe4711c3a609bd8e60238022f48eacedc257cd13ae9327a7/pandas-2.3.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:98bcc8b5bf7afed22cc753a28bc4d9e26e078e777066bc53fac7904ddef9a678", size = 13230957 }, + { url = "https://files.pythonhosted.org/packages/e0/94/6fce6bf85b5056d065e0a7933cba2616dcb48596f7ba3c6341ec4bcc529d/pandas-2.3.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4d544806b485ddf29e52d75b1f559142514e60ef58a832f74fb38e48d757b299", size = 13883883 }, + { url = "https://files.pythonhosted.org/packages/c8/7b/bdcb1ed8fccb63d04bdb7635161d0ec26596d92c9d7a6cce964e7876b6c1/pandas-2.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:b3cd4273d3cb3707b6fffd217204c52ed92859533e31dc03b7c5008aa933aaab", size = 11340212 }, + { url = "https://files.pythonhosted.org/packages/46/de/b8445e0f5d217a99fe0eeb2f4988070908979bec3587c0633e5428ab596c/pandas-2.3.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:689968e841136f9e542020698ee1c4fbe9caa2ed2213ae2388dc7b81721510d3", size = 11588172 }, + { url = "https://files.pythonhosted.org/packages/1e/e0/801cdb3564e65a5ac041ab99ea6f1d802a6c325bb6e58c79c06a3f1cd010/pandas-2.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:025e92411c16cbe5bb2a4abc99732a6b132f439b8aab23a59fa593eb00704232", size = 10717365 }, + { url = "https://files.pythonhosted.org/packages/51/a5/c76a8311833c24ae61a376dbf360eb1b1c9247a5d9c1e8b356563b31b80c/pandas-2.3.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b7ff55f31c4fcb3e316e8f7fa194566b286d6ac430afec0d461163312c5841e", size = 11280411 }, + { url = "https://files.pythonhosted.org/packages/da/01/e383018feba0a1ead6cf5fe8728e5d767fee02f06a3d800e82c489e5daaf/pandas-2.3.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7dcb79bf373a47d2a40cf7232928eb7540155abbc460925c2c96d2d30b006eb4", size = 11988013 }, + { url = "https://files.pythonhosted.org/packages/5b/14/cec7760d7c9507f11c97d64f29022e12a6cc4fc03ac694535e89f88ad2ec/pandas-2.3.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:56a342b231e8862c96bdb6ab97170e203ce511f4d0429589c8ede1ee8ece48b8", size = 12767210 }, + { url = "https://files.pythonhosted.org/packages/50/b9/6e2d2c6728ed29fb3d4d4d302504fb66f1a543e37eb2e43f352a86365cdf/pandas-2.3.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ca7ed14832bce68baef331f4d7f294411bed8efd032f8109d690df45e00c4679", size = 13440571 }, + { url = "https://files.pythonhosted.org/packages/80/a5/3a92893e7399a691bad7664d977cb5e7c81cf666c81f89ea76ba2bff483d/pandas-2.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:ac942bfd0aca577bef61f2bc8da8147c4ef6879965ef883d8e8d5d2dc3e744b8", size = 10987601 }, + { url = "https://files.pythonhosted.org/packages/32/ed/ff0a67a2c5505e1854e6715586ac6693dd860fbf52ef9f81edee200266e7/pandas-2.3.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9026bd4a80108fac2239294a15ef9003c4ee191a0f64b90f170b40cfb7cf2d22", size = 11531393 }, + { url = "https://files.pythonhosted.org/packages/c7/db/d8f24a7cc9fb0972adab0cc80b6817e8bef888cfd0024eeb5a21c0bb5c4a/pandas-2.3.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6de8547d4fdb12421e2d047a2c446c623ff4c11f47fddb6b9169eb98ffba485a", size = 10668750 }, + { url = "https://files.pythonhosted.org/packages/0f/b0/80f6ec783313f1e2356b28b4fd8d2148c378370045da918c73145e6aab50/pandas-2.3.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:782647ddc63c83133b2506912cc6b108140a38a37292102aaa19c81c83db2928", size = 11342004 }, + { url = "https://files.pythonhosted.org/packages/e9/e2/20a317688435470872885e7fc8f95109ae9683dec7c50be29b56911515a5/pandas-2.3.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ba6aff74075311fc88504b1db890187a3cd0f887a5b10f5525f8e2ef55bfdb9", size = 12050869 }, + { url = "https://files.pythonhosted.org/packages/55/79/20d746b0a96c67203a5bee5fb4e00ac49c3e8009a39e1f78de264ecc5729/pandas-2.3.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e5635178b387bd2ba4ac040f82bc2ef6e6b500483975c4ebacd34bec945fda12", size = 12750218 }, + { url = "https://files.pythonhosted.org/packages/7c/0f/145c8b41e48dbf03dd18fdd7f24f8ba95b8254a97a3379048378f33e7838/pandas-2.3.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6f3bf5ec947526106399a9e1d26d40ee2b259c66422efdf4de63c848492d91bb", size = 13416763 }, + { url = "https://files.pythonhosted.org/packages/b2/c0/54415af59db5cdd86a3d3bf79863e8cc3fa9ed265f0745254061ac09d5f2/pandas-2.3.1-cp313-cp313-win_amd64.whl", hash = "sha256:1c78cf43c8fde236342a1cb2c34bcff89564a7bfed7e474ed2fffa6aed03a956", size = 10987482 }, + { url = "https://files.pythonhosted.org/packages/48/64/2fd2e400073a1230e13b8cd604c9bc95d9e3b962e5d44088ead2e8f0cfec/pandas-2.3.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:8dfc17328e8da77be3cf9f47509e5637ba8f137148ed0e9b5241e1baf526e20a", size = 12029159 }, + { url = "https://files.pythonhosted.org/packages/d8/0a/d84fd79b0293b7ef88c760d7dca69828d867c89b6d9bc52d6a27e4d87316/pandas-2.3.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:ec6c851509364c59a5344458ab935e6451b31b818be467eb24b0fe89bd05b6b9", size = 11393287 }, + { url = "https://files.pythonhosted.org/packages/50/ae/ff885d2b6e88f3c7520bb74ba319268b42f05d7e583b5dded9837da2723f/pandas-2.3.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:911580460fc4884d9b05254b38a6bfadddfcc6aaef856fb5859e7ca202e45275", size = 11309381 }, + { url = "https://files.pythonhosted.org/packages/85/86/1fa345fc17caf5d7780d2699985c03dbe186c68fee00b526813939062bb0/pandas-2.3.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2f4d6feeba91744872a600e6edbbd5b033005b431d5ae8379abee5bcfa479fab", size = 11883998 }, + { url = "https://files.pythonhosted.org/packages/81/aa/e58541a49b5e6310d89474333e994ee57fea97c8aaa8fc7f00b873059bbf/pandas-2.3.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:fe37e757f462d31a9cd7580236a82f353f5713a80e059a29753cf938c6775d96", size = 12704705 }, + { url = "https://files.pythonhosted.org/packages/d5/f9/07086f5b0f2a19872554abeea7658200824f5835c58a106fa8f2ae96a46c/pandas-2.3.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:5db9637dbc24b631ff3707269ae4559bce4b7fd75c1c4d7e13f40edc42df4444", size = 13189044 }, +] + +[[package]] +name = "parso" +version = "0.8.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/66/94/68e2e17afaa9169cf6412ab0f28623903be73d1b32e208d9e8e541bb086d/parso-0.8.4.tar.gz", hash = "sha256:eb3a7b58240fb99099a345571deecc0f9540ea5f4dd2fe14c2a99d6b281ab92d", size = 400609 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c6/ac/dac4a63f978e4dcb3c6d3a78c4d8e0192a113d288502a1216950c41b1027/parso-0.8.4-py2.py3-none-any.whl", hash = "sha256:a418670a20291dacd2dddc80c377c5c3791378ee1e8d12bffc35420643d43f18", size = 103650 }, ] [[package]] name = "pathvalidate" -version = "3.2.3" +version = "3.3.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/92/87/c7a2f51cc62df0495acb0ed2533a7c74cc895e569a1b020ee5f6e9fa4e21/pathvalidate-3.2.3.tar.gz", hash = "sha256:59b5b9278e30382d6d213497623043ebe63f10e29055be4419a9c04c721739cb", size = 61717 } +sdist = { url = "https://files.pythonhosted.org/packages/fa/2a/52a8da6fe965dea6192eb716b357558e103aea0a1e9a8352ad575a8406ca/pathvalidate-3.3.1.tar.gz", hash = "sha256:b18c07212bfead624345bb8e1d6141cdcf15a39736994ea0b94035ad2b1ba177", size = 63262 } wheels = [ - { url = "https://files.pythonhosted.org/packages/50/14/c5a0e1a947909810fc4c043b84cac472b70e438148d34f5393be1bac663f/pathvalidate-3.2.3-py3-none-any.whl", hash = "sha256:5eaf0562e345d4b6d0c0239d0f690c3bd84d2a9a3c4c73b99ea667401b27bee1", size = 24130 }, + { url = "https://files.pythonhosted.org/packages/9a/70/875f4a23bfc4731703a5835487d0d2fb999031bd415e7d17c0ae615c18b7/pathvalidate-3.3.1-py3-none-any.whl", hash = "sha256:5263baab691f8e1af96092fa5137ee17df5bdfbd6cff1fcac4d6ef4bc2e1735f", size = 24305 }, ] [[package]] @@ -2506,79 +2809,104 @@ wheels = [ [[package]] name = "pillow" -version = "11.2.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/af/cb/bb5c01fcd2a69335b86c22142b2bccfc3464087efb7fd382eee5ffc7fdf7/pillow-11.2.1.tar.gz", hash = "sha256:a64dd61998416367b7ef979b73d3a85853ba9bec4c2925f74e588879a58716b6", size = 47026707 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0d/8b/b158ad57ed44d3cc54db8d68ad7c0a58b8fc0e4c7a3f995f9d62d5b464a1/pillow-11.2.1-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:d57a75d53922fc20c165016a20d9c44f73305e67c351bbc60d1adaf662e74047", size = 3198442 }, - { url = "https://files.pythonhosted.org/packages/b1/f8/bb5d956142f86c2d6cc36704943fa761f2d2e4c48b7436fd0a85c20f1713/pillow-11.2.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:127bf6ac4a5b58b3d32fc8289656f77f80567d65660bc46f72c0d77e6600cc95", size = 3030553 }, - { url = "https://files.pythonhosted.org/packages/22/7f/0e413bb3e2aa797b9ca2c5c38cb2e2e45d88654e5b12da91ad446964cfae/pillow-11.2.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b4ba4be812c7a40280629e55ae0b14a0aafa150dd6451297562e1764808bbe61", size = 4405503 }, - { url = "https://files.pythonhosted.org/packages/f3/b4/cc647f4d13f3eb837d3065824aa58b9bcf10821f029dc79955ee43f793bd/pillow-11.2.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c8bd62331e5032bc396a93609982a9ab6b411c05078a52f5fe3cc59234a3abd1", size = 4490648 }, - { url = "https://files.pythonhosted.org/packages/c2/6f/240b772a3b35cdd7384166461567aa6713799b4e78d180c555bd284844ea/pillow-11.2.1-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:562d11134c97a62fe3af29581f083033179f7ff435f78392565a1ad2d1c2c45c", size = 4508937 }, - { url = "https://files.pythonhosted.org/packages/f3/5e/7ca9c815ade5fdca18853db86d812f2f188212792780208bdb37a0a6aef4/pillow-11.2.1-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:c97209e85b5be259994eb5b69ff50c5d20cca0f458ef9abd835e262d9d88b39d", size = 4599802 }, - { url = "https://files.pythonhosted.org/packages/02/81/c3d9d38ce0c4878a77245d4cf2c46d45a4ad0f93000227910a46caff52f3/pillow-11.2.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0c3e6d0f59171dfa2e25d7116217543310908dfa2770aa64b8f87605f8cacc97", size = 4576717 }, - { url = "https://files.pythonhosted.org/packages/42/49/52b719b89ac7da3185b8d29c94d0e6aec8140059e3d8adcaa46da3751180/pillow-11.2.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cc1c3bc53befb6096b84165956e886b1729634a799e9d6329a0c512ab651e579", size = 4654874 }, - { url = "https://files.pythonhosted.org/packages/5b/0b/ede75063ba6023798267023dc0d0401f13695d228194d2242d5a7ba2f964/pillow-11.2.1-cp310-cp310-win32.whl", hash = "sha256:312c77b7f07ab2139924d2639860e084ec2a13e72af54d4f08ac843a5fc9c79d", size = 2331717 }, - { url = "https://files.pythonhosted.org/packages/ed/3c/9831da3edea527c2ed9a09f31a2c04e77cd705847f13b69ca60269eec370/pillow-11.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:9bc7ae48b8057a611e5fe9f853baa88093b9a76303937449397899385da06fad", size = 2676204 }, - { url = "https://files.pythonhosted.org/packages/01/97/1f66ff8a1503d8cbfc5bae4dc99d54c6ec1e22ad2b946241365320caabc2/pillow-11.2.1-cp310-cp310-win_arm64.whl", hash = "sha256:2728567e249cdd939f6cc3d1f049595c66e4187f3c34078cbc0a7d21c47482d2", size = 2414767 }, - { url = "https://files.pythonhosted.org/packages/68/08/3fbf4b98924c73037a8e8b4c2c774784805e0fb4ebca6c5bb60795c40125/pillow-11.2.1-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:35ca289f712ccfc699508c4658a1d14652e8033e9b69839edf83cbdd0ba39e70", size = 3198450 }, - { url = "https://files.pythonhosted.org/packages/84/92/6505b1af3d2849d5e714fc75ba9e69b7255c05ee42383a35a4d58f576b16/pillow-11.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e0409af9f829f87a2dfb7e259f78f317a5351f2045158be321fd135973fff7bf", size = 3030550 }, - { url = "https://files.pythonhosted.org/packages/3c/8c/ac2f99d2a70ff966bc7eb13dacacfaab57c0549b2ffb351b6537c7840b12/pillow-11.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4e5c5edee874dce4f653dbe59db7c73a600119fbea8d31f53423586ee2aafd7", size = 4415018 }, - { url = "https://files.pythonhosted.org/packages/1f/e3/0a58b5d838687f40891fff9cbaf8669f90c96b64dc8f91f87894413856c6/pillow-11.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b93a07e76d13bff9444f1a029e0af2964e654bfc2e2c2d46bfd080df5ad5f3d8", size = 4498006 }, - { url = "https://files.pythonhosted.org/packages/21/f5/6ba14718135f08fbfa33308efe027dd02b781d3f1d5c471444a395933aac/pillow-11.2.1-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:e6def7eed9e7fa90fde255afaf08060dc4b343bbe524a8f69bdd2a2f0018f600", size = 4517773 }, - { url = "https://files.pythonhosted.org/packages/20/f2/805ad600fc59ebe4f1ba6129cd3a75fb0da126975c8579b8f57abeb61e80/pillow-11.2.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:8f4f3724c068be008c08257207210c138d5f3731af6c155a81c2b09a9eb3a788", size = 4607069 }, - { url = "https://files.pythonhosted.org/packages/71/6b/4ef8a288b4bb2e0180cba13ca0a519fa27aa982875882392b65131401099/pillow-11.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a0a6709b47019dff32e678bc12c63008311b82b9327613f534e496dacaefb71e", size = 4583460 }, - { url = "https://files.pythonhosted.org/packages/62/ae/f29c705a09cbc9e2a456590816e5c234382ae5d32584f451c3eb41a62062/pillow-11.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f6b0c664ccb879109ee3ca702a9272d877f4fcd21e5eb63c26422fd6e415365e", size = 4661304 }, - { url = "https://files.pythonhosted.org/packages/6e/1a/c8217b6f2f73794a5e219fbad087701f412337ae6dbb956db37d69a9bc43/pillow-11.2.1-cp311-cp311-win32.whl", hash = "sha256:cc5d875d56e49f112b6def6813c4e3d3036d269c008bf8aef72cd08d20ca6df6", size = 2331809 }, - { url = "https://files.pythonhosted.org/packages/e2/72/25a8f40170dc262e86e90f37cb72cb3de5e307f75bf4b02535a61afcd519/pillow-11.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:0f5c7eda47bf8e3c8a283762cab94e496ba977a420868cb819159980b6709193", size = 2676338 }, - { url = "https://files.pythonhosted.org/packages/06/9e/76825e39efee61efea258b479391ca77d64dbd9e5804e4ad0fa453b4ba55/pillow-11.2.1-cp311-cp311-win_arm64.whl", hash = "sha256:4d375eb838755f2528ac8cbc926c3e31cc49ca4ad0cf79cff48b20e30634a4a7", size = 2414918 }, - { url = "https://files.pythonhosted.org/packages/c7/40/052610b15a1b8961f52537cc8326ca6a881408bc2bdad0d852edeb6ed33b/pillow-11.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:78afba22027b4accef10dbd5eed84425930ba41b3ea0a86fa8d20baaf19d807f", size = 3190185 }, - { url = "https://files.pythonhosted.org/packages/e5/7e/b86dbd35a5f938632093dc40d1682874c33dcfe832558fc80ca56bfcb774/pillow-11.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:78092232a4ab376a35d68c4e6d5e00dfd73454bd12b230420025fbe178ee3b0b", size = 3030306 }, - { url = "https://files.pythonhosted.org/packages/a4/5c/467a161f9ed53e5eab51a42923c33051bf8d1a2af4626ac04f5166e58e0c/pillow-11.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25a5f306095c6780c52e6bbb6109624b95c5b18e40aab1c3041da3e9e0cd3e2d", size = 4416121 }, - { url = "https://files.pythonhosted.org/packages/62/73/972b7742e38ae0e2ac76ab137ca6005dcf877480da0d9d61d93b613065b4/pillow-11.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c7b29dbd4281923a2bfe562acb734cee96bbb129e96e6972d315ed9f232bef4", size = 4501707 }, - { url = "https://files.pythonhosted.org/packages/e4/3a/427e4cb0b9e177efbc1a84798ed20498c4f233abde003c06d2650a6d60cb/pillow-11.2.1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:3e645b020f3209a0181a418bffe7b4a93171eef6c4ef6cc20980b30bebf17b7d", size = 4522921 }, - { url = "https://files.pythonhosted.org/packages/fe/7c/d8b1330458e4d2f3f45d9508796d7caf0c0d3764c00c823d10f6f1a3b76d/pillow-11.2.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:b2dbea1012ccb784a65349f57bbc93730b96e85b42e9bf7b01ef40443db720b4", size = 4612523 }, - { url = "https://files.pythonhosted.org/packages/b3/2f/65738384e0b1acf451de5a573d8153fe84103772d139e1e0bdf1596be2ea/pillow-11.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:da3104c57bbd72948d75f6a9389e6727d2ab6333c3617f0a89d72d4940aa0443", size = 4587836 }, - { url = "https://files.pythonhosted.org/packages/6a/c5/e795c9f2ddf3debb2dedd0df889f2fe4b053308bb59a3cc02a0cd144d641/pillow-11.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:598174aef4589af795f66f9caab87ba4ff860ce08cd5bb447c6fc553ffee603c", size = 4669390 }, - { url = "https://files.pythonhosted.org/packages/96/ae/ca0099a3995976a9fce2f423166f7bff9b12244afdc7520f6ed38911539a/pillow-11.2.1-cp312-cp312-win32.whl", hash = "sha256:1d535df14716e7f8776b9e7fee118576d65572b4aad3ed639be9e4fa88a1cad3", size = 2332309 }, - { url = "https://files.pythonhosted.org/packages/7c/18/24bff2ad716257fc03da964c5e8f05d9790a779a8895d6566e493ccf0189/pillow-11.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:14e33b28bf17c7a38eede290f77db7c664e4eb01f7869e37fa98a5aa95978941", size = 2676768 }, - { url = "https://files.pythonhosted.org/packages/da/bb/e8d656c9543276517ee40184aaa39dcb41e683bca121022f9323ae11b39d/pillow-11.2.1-cp312-cp312-win_arm64.whl", hash = "sha256:21e1470ac9e5739ff880c211fc3af01e3ae505859392bf65458c224d0bf283eb", size = 2415087 }, - { url = "https://files.pythonhosted.org/packages/36/9c/447528ee3776e7ab8897fe33697a7ff3f0475bb490c5ac1456a03dc57956/pillow-11.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:fdec757fea0b793056419bca3e9932eb2b0ceec90ef4813ea4c1e072c389eb28", size = 3190098 }, - { url = "https://files.pythonhosted.org/packages/b5/09/29d5cd052f7566a63e5b506fac9c60526e9ecc553825551333e1e18a4858/pillow-11.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b0e130705d568e2f43a17bcbe74d90958e8a16263868a12c3e0d9c8162690830", size = 3030166 }, - { url = "https://files.pythonhosted.org/packages/71/5d/446ee132ad35e7600652133f9c2840b4799bbd8e4adba881284860da0a36/pillow-11.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bdb5e09068332578214cadd9c05e3d64d99e0e87591be22a324bdbc18925be0", size = 4408674 }, - { url = "https://files.pythonhosted.org/packages/69/5f/cbe509c0ddf91cc3a03bbacf40e5c2339c4912d16458fcb797bb47bcb269/pillow-11.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d189ba1bebfbc0c0e529159631ec72bb9e9bc041f01ec6d3233d6d82eb823bc1", size = 4496005 }, - { url = "https://files.pythonhosted.org/packages/f9/b3/dd4338d8fb8a5f312021f2977fb8198a1184893f9b00b02b75d565c33b51/pillow-11.2.1-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:191955c55d8a712fab8934a42bfefbf99dd0b5875078240943f913bb66d46d9f", size = 4518707 }, - { url = "https://files.pythonhosted.org/packages/13/eb/2552ecebc0b887f539111c2cd241f538b8ff5891b8903dfe672e997529be/pillow-11.2.1-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:ad275964d52e2243430472fc5d2c2334b4fc3ff9c16cb0a19254e25efa03a155", size = 4610008 }, - { url = "https://files.pythonhosted.org/packages/72/d1/924ce51bea494cb6e7959522d69d7b1c7e74f6821d84c63c3dc430cbbf3b/pillow-11.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:750f96efe0597382660d8b53e90dd1dd44568a8edb51cb7f9d5d918b80d4de14", size = 4585420 }, - { url = "https://files.pythonhosted.org/packages/43/ab/8f81312d255d713b99ca37479a4cb4b0f48195e530cdc1611990eb8fd04b/pillow-11.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fe15238d3798788d00716637b3d4e7bb6bde18b26e5d08335a96e88564a36b6b", size = 4667655 }, - { url = "https://files.pythonhosted.org/packages/94/86/8f2e9d2dc3d308dfd137a07fe1cc478df0a23d42a6c4093b087e738e4827/pillow-11.2.1-cp313-cp313-win32.whl", hash = "sha256:3fe735ced9a607fee4f481423a9c36701a39719252a9bb251679635f99d0f7d2", size = 2332329 }, - { url = "https://files.pythonhosted.org/packages/6d/ec/1179083b8d6067a613e4d595359b5fdea65d0a3b7ad623fee906e1b3c4d2/pillow-11.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:74ee3d7ecb3f3c05459ba95eed5efa28d6092d751ce9bf20e3e253a4e497e691", size = 2676388 }, - { url = "https://files.pythonhosted.org/packages/23/f1/2fc1e1e294de897df39fa8622d829b8828ddad938b0eaea256d65b84dd72/pillow-11.2.1-cp313-cp313-win_arm64.whl", hash = "sha256:5119225c622403afb4b44bad4c1ca6c1f98eed79db8d3bc6e4e160fc6339d66c", size = 2414950 }, - { url = "https://files.pythonhosted.org/packages/c4/3e/c328c48b3f0ead7bab765a84b4977acb29f101d10e4ef57a5e3400447c03/pillow-11.2.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:8ce2e8411c7aaef53e6bb29fe98f28cd4fbd9a1d9be2eeea434331aac0536b22", size = 3192759 }, - { url = "https://files.pythonhosted.org/packages/18/0e/1c68532d833fc8b9f404d3a642991441d9058eccd5606eab31617f29b6d4/pillow-11.2.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:9ee66787e095127116d91dea2143db65c7bb1e232f617aa5957c0d9d2a3f23a7", size = 3033284 }, - { url = "https://files.pythonhosted.org/packages/b7/cb/6faf3fb1e7705fd2db74e070f3bf6f88693601b0ed8e81049a8266de4754/pillow-11.2.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9622e3b6c1d8b551b6e6f21873bdcc55762b4b2126633014cea1803368a9aa16", size = 4445826 }, - { url = "https://files.pythonhosted.org/packages/07/94/8be03d50b70ca47fb434a358919d6a8d6580f282bbb7af7e4aa40103461d/pillow-11.2.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63b5dff3a68f371ea06025a1a6966c9a1e1ee452fc8020c2cd0ea41b83e9037b", size = 4527329 }, - { url = "https://files.pythonhosted.org/packages/fd/a4/bfe78777076dc405e3bd2080bc32da5ab3945b5a25dc5d8acaa9de64a162/pillow-11.2.1-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:31df6e2d3d8fc99f993fd253e97fae451a8db2e7207acf97859732273e108406", size = 4549049 }, - { url = "https://files.pythonhosted.org/packages/65/4d/eaf9068dc687c24979e977ce5677e253624bd8b616b286f543f0c1b91662/pillow-11.2.1-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:062b7a42d672c45a70fa1f8b43d1d38ff76b63421cbbe7f88146b39e8a558d91", size = 4635408 }, - { url = "https://files.pythonhosted.org/packages/1d/26/0fd443365d9c63bc79feb219f97d935cd4b93af28353cba78d8e77b61719/pillow-11.2.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4eb92eca2711ef8be42fd3f67533765d9fd043b8c80db204f16c8ea62ee1a751", size = 4614863 }, - { url = "https://files.pythonhosted.org/packages/49/65/dca4d2506be482c2c6641cacdba5c602bc76d8ceb618fd37de855653a419/pillow-11.2.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f91ebf30830a48c825590aede79376cb40f110b387c17ee9bd59932c961044f9", size = 4692938 }, - { url = "https://files.pythonhosted.org/packages/b3/92/1ca0c3f09233bd7decf8f7105a1c4e3162fb9142128c74adad0fb361b7eb/pillow-11.2.1-cp313-cp313t-win32.whl", hash = "sha256:e0b55f27f584ed623221cfe995c912c61606be8513bfa0e07d2c674b4516d9dd", size = 2335774 }, - { url = "https://files.pythonhosted.org/packages/a5/ac/77525347cb43b83ae905ffe257bbe2cc6fd23acb9796639a1f56aa59d191/pillow-11.2.1-cp313-cp313t-win_amd64.whl", hash = "sha256:36d6b82164c39ce5482f649b437382c0fb2395eabc1e2b1702a6deb8ad647d6e", size = 2681895 }, - { url = "https://files.pythonhosted.org/packages/67/32/32dc030cfa91ca0fc52baebbba2e009bb001122a1daa8b6a79ad830b38d3/pillow-11.2.1-cp313-cp313t-win_arm64.whl", hash = "sha256:225c832a13326e34f212d2072982bb1adb210e0cc0b153e688743018c94a2681", size = 2417234 }, - { url = "https://files.pythonhosted.org/packages/33/49/c8c21e4255b4f4a2c0c68ac18125d7f5460b109acc6dfdef1a24f9b960ef/pillow-11.2.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:9b7b0d4fd2635f54ad82785d56bc0d94f147096493a79985d0ab57aedd563156", size = 3181727 }, - { url = "https://files.pythonhosted.org/packages/6d/f1/f7255c0838f8c1ef6d55b625cfb286835c17e8136ce4351c5577d02c443b/pillow-11.2.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:aa442755e31c64037aa7c1cb186e0b369f8416c567381852c63444dd666fb772", size = 2999833 }, - { url = "https://files.pythonhosted.org/packages/e2/57/9968114457bd131063da98d87790d080366218f64fa2943b65ac6739abb3/pillow-11.2.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f0d3348c95b766f54b76116d53d4cb171b52992a1027e7ca50c81b43b9d9e363", size = 3437472 }, - { url = "https://files.pythonhosted.org/packages/b2/1b/e35d8a158e21372ecc48aac9c453518cfe23907bb82f950d6e1c72811eb0/pillow-11.2.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85d27ea4c889342f7e35f6d56e7e1cb345632ad592e8c51b693d7b7556043ce0", size = 3459976 }, - { url = "https://files.pythonhosted.org/packages/26/da/2c11d03b765efff0ccc473f1c4186dc2770110464f2177efaed9cf6fae01/pillow-11.2.1-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:bf2c33d6791c598142f00c9c4c7d47f6476731c31081331664eb26d6ab583e01", size = 3527133 }, - { url = "https://files.pythonhosted.org/packages/79/1a/4e85bd7cadf78412c2a3069249a09c32ef3323650fd3005c97cca7aa21df/pillow-11.2.1-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:e616e7154c37669fc1dfc14584f11e284e05d1c650e1c0f972f281c4ccc53193", size = 3571555 }, - { url = "https://files.pythonhosted.org/packages/69/03/239939915216de1e95e0ce2334bf17a7870ae185eb390fab6d706aadbfc0/pillow-11.2.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:39ad2e0f424394e3aebc40168845fee52df1394a4673a6ee512d840d14ab3013", size = 2674713 }, - { url = "https://files.pythonhosted.org/packages/a4/ad/2613c04633c7257d9481ab21d6b5364b59fc5d75faafd7cb8693523945a3/pillow-11.2.1-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:80f1df8dbe9572b4b7abdfa17eb5d78dd620b1d55d9e25f834efdbee872d3aed", size = 3181734 }, - { url = "https://files.pythonhosted.org/packages/a4/fd/dcdda4471ed667de57bb5405bb42d751e6cfdd4011a12c248b455c778e03/pillow-11.2.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:ea926cfbc3957090becbcbbb65ad177161a2ff2ad578b5a6ec9bb1e1cd78753c", size = 2999841 }, - { url = "https://files.pythonhosted.org/packages/ac/89/8a2536e95e77432833f0db6fd72a8d310c8e4272a04461fb833eb021bf94/pillow-11.2.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:738db0e0941ca0376804d4de6a782c005245264edaa253ffce24e5a15cbdc7bd", size = 3437470 }, - { url = "https://files.pythonhosted.org/packages/9d/8f/abd47b73c60712f88e9eda32baced7bfc3e9bd6a7619bb64b93acff28c3e/pillow-11.2.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9db98ab6565c69082ec9b0d4e40dd9f6181dab0dd236d26f7a50b8b9bfbd5076", size = 3460013 }, - { url = "https://files.pythonhosted.org/packages/f6/20/5c0a0aa83b213b7a07ec01e71a3d6ea2cf4ad1d2c686cc0168173b6089e7/pillow-11.2.1-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:036e53f4170e270ddb8797d4c590e6dd14d28e15c7da375c18978045f7e6c37b", size = 3527165 }, - { url = "https://files.pythonhosted.org/packages/58/0e/2abab98a72202d91146abc839e10c14f7cf36166f12838ea0c4db3ca6ecb/pillow-11.2.1-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:14f73f7c291279bd65fda51ee87affd7c1e097709f7fdd0188957a16c264601f", size = 3571586 }, - { url = "https://files.pythonhosted.org/packages/21/2c/5e05f58658cf49b6667762cca03d6e7d85cededde2caf2ab37b81f80e574/pillow-11.2.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:208653868d5c9ecc2b327f9b9ef34e0e42a4cdd172c2988fd81d62d2bc9bc044", size = 2674751 }, +version = "11.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f3/0d/d0d6dea55cd152ce3d6767bb38a8fc10e33796ba4ba210cbab9354b6d238/pillow-11.3.0.tar.gz", hash = "sha256:3828ee7586cd0b2091b6209e5ad53e20d0649bbe87164a459d0676e035e8f523", size = 47113069 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4c/5d/45a3553a253ac8763f3561371432a90bdbe6000fbdcf1397ffe502aa206c/pillow-11.3.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:1b9c17fd4ace828b3003dfd1e30bff24863e0eb59b535e8f80194d9cc7ecf860", size = 5316554 }, + { url = "https://files.pythonhosted.org/packages/7c/c8/67c12ab069ef586a25a4a79ced553586748fad100c77c0ce59bb4983ac98/pillow-11.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:65dc69160114cdd0ca0f35cb434633c75e8e7fad4cf855177a05bf38678f73ad", size = 4686548 }, + { url = "https://files.pythonhosted.org/packages/2f/bd/6741ebd56263390b382ae4c5de02979af7f8bd9807346d068700dd6d5cf9/pillow-11.3.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7107195ddc914f656c7fc8e4a5e1c25f32e9236ea3ea860f257b0436011fddd0", size = 5859742 }, + { url = "https://files.pythonhosted.org/packages/ca/0b/c412a9e27e1e6a829e6ab6c2dca52dd563efbedf4c9c6aa453d9a9b77359/pillow-11.3.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cc3e831b563b3114baac7ec2ee86819eb03caa1a2cef0b481a5675b59c4fe23b", size = 7633087 }, + { url = "https://files.pythonhosted.org/packages/59/9d/9b7076aaf30f5dd17e5e5589b2d2f5a5d7e30ff67a171eb686e4eecc2adf/pillow-11.3.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f1f182ebd2303acf8c380a54f615ec883322593320a9b00438eb842c1f37ae50", size = 5963350 }, + { url = "https://files.pythonhosted.org/packages/f0/16/1a6bf01fb622fb9cf5c91683823f073f053005c849b1f52ed613afcf8dae/pillow-11.3.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4445fa62e15936a028672fd48c4c11a66d641d2c05726c7ec1f8ba6a572036ae", size = 6631840 }, + { url = "https://files.pythonhosted.org/packages/7b/e6/6ff7077077eb47fde78739e7d570bdcd7c10495666b6afcd23ab56b19a43/pillow-11.3.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:71f511f6b3b91dd543282477be45a033e4845a40278fa8dcdbfdb07109bf18f9", size = 6074005 }, + { url = "https://files.pythonhosted.org/packages/c3/3a/b13f36832ea6d279a697231658199e0a03cd87ef12048016bdcc84131601/pillow-11.3.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:040a5b691b0713e1f6cbe222e0f4f74cd233421e105850ae3b3c0ceda520f42e", size = 6708372 }, + { url = "https://files.pythonhosted.org/packages/6c/e4/61b2e1a7528740efbc70b3d581f33937e38e98ef3d50b05007267a55bcb2/pillow-11.3.0-cp310-cp310-win32.whl", hash = "sha256:89bd777bc6624fe4115e9fac3352c79ed60f3bb18651420635f26e643e3dd1f6", size = 6277090 }, + { url = "https://files.pythonhosted.org/packages/a9/d3/60c781c83a785d6afbd6a326ed4d759d141de43aa7365725cbcd65ce5e54/pillow-11.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:19d2ff547c75b8e3ff46f4d9ef969a06c30ab2d4263a9e287733aa8b2429ce8f", size = 6985988 }, + { url = "https://files.pythonhosted.org/packages/9f/28/4f4a0203165eefb3763939c6789ba31013a2e90adffb456610f30f613850/pillow-11.3.0-cp310-cp310-win_arm64.whl", hash = "sha256:819931d25e57b513242859ce1876c58c59dc31587847bf74cfe06b2e0cb22d2f", size = 2422899 }, + { url = "https://files.pythonhosted.org/packages/db/26/77f8ed17ca4ffd60e1dcd220a6ec6d71210ba398cfa33a13a1cd614c5613/pillow-11.3.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:1cd110edf822773368b396281a2293aeb91c90a2db00d78ea43e7e861631b722", size = 5316531 }, + { url = "https://files.pythonhosted.org/packages/cb/39/ee475903197ce709322a17a866892efb560f57900d9af2e55f86db51b0a5/pillow-11.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9c412fddd1b77a75aa904615ebaa6001f169b26fd467b4be93aded278266b288", size = 4686560 }, + { url = "https://files.pythonhosted.org/packages/d5/90/442068a160fd179938ba55ec8c97050a612426fae5ec0a764e345839f76d/pillow-11.3.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7d1aa4de119a0ecac0a34a9c8bde33f34022e2e8f99104e47a3ca392fd60e37d", size = 5870978 }, + { url = "https://files.pythonhosted.org/packages/13/92/dcdd147ab02daf405387f0218dcf792dc6dd5b14d2573d40b4caeef01059/pillow-11.3.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:91da1d88226663594e3f6b4b8c3c8d85bd504117d043740a8e0ec449087cc494", size = 7641168 }, + { url = "https://files.pythonhosted.org/packages/6e/db/839d6ba7fd38b51af641aa904e2960e7a5644d60ec754c046b7d2aee00e5/pillow-11.3.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:643f189248837533073c405ec2f0bb250ba54598cf80e8c1e043381a60632f58", size = 5973053 }, + { url = "https://files.pythonhosted.org/packages/f2/2f/d7675ecae6c43e9f12aa8d58b6012683b20b6edfbdac7abcb4e6af7a3784/pillow-11.3.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:106064daa23a745510dabce1d84f29137a37224831d88eb4ce94bb187b1d7e5f", size = 6640273 }, + { url = "https://files.pythonhosted.org/packages/45/ad/931694675ede172e15b2ff03c8144a0ddaea1d87adb72bb07655eaffb654/pillow-11.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cd8ff254faf15591e724dc7c4ddb6bf4793efcbe13802a4ae3e863cd300b493e", size = 6082043 }, + { url = "https://files.pythonhosted.org/packages/3a/04/ba8f2b11fc80d2dd462d7abec16351b45ec99cbbaea4387648a44190351a/pillow-11.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:932c754c2d51ad2b2271fd01c3d121daaa35e27efae2a616f77bf164bc0b3e94", size = 6715516 }, + { url = "https://files.pythonhosted.org/packages/48/59/8cd06d7f3944cc7d892e8533c56b0acb68399f640786313275faec1e3b6f/pillow-11.3.0-cp311-cp311-win32.whl", hash = "sha256:b4b8f3efc8d530a1544e5962bd6b403d5f7fe8b9e08227c6b255f98ad82b4ba0", size = 6274768 }, + { url = "https://files.pythonhosted.org/packages/f1/cc/29c0f5d64ab8eae20f3232da8f8571660aa0ab4b8f1331da5c2f5f9a938e/pillow-11.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:1a992e86b0dd7aeb1f053cd506508c0999d710a8f07b4c791c63843fc6a807ac", size = 6986055 }, + { url = "https://files.pythonhosted.org/packages/c6/df/90bd886fabd544c25addd63e5ca6932c86f2b701d5da6c7839387a076b4a/pillow-11.3.0-cp311-cp311-win_arm64.whl", hash = "sha256:30807c931ff7c095620fe04448e2c2fc673fcbb1ffe2a7da3fb39613489b1ddd", size = 2423079 }, + { url = "https://files.pythonhosted.org/packages/40/fe/1bc9b3ee13f68487a99ac9529968035cca2f0a51ec36892060edcc51d06a/pillow-11.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fdae223722da47b024b867c1ea0be64e0df702c5e0a60e27daad39bf960dd1e4", size = 5278800 }, + { url = "https://files.pythonhosted.org/packages/2c/32/7e2ac19b5713657384cec55f89065fb306b06af008cfd87e572035b27119/pillow-11.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:921bd305b10e82b4d1f5e802b6850677f965d8394203d182f078873851dada69", size = 4686296 }, + { url = "https://files.pythonhosted.org/packages/8e/1e/b9e12bbe6e4c2220effebc09ea0923a07a6da1e1f1bfbc8d7d29a01ce32b/pillow-11.3.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:eb76541cba2f958032d79d143b98a3a6b3ea87f0959bbe256c0b5e416599fd5d", size = 5871726 }, + { url = "https://files.pythonhosted.org/packages/8d/33/e9200d2bd7ba00dc3ddb78df1198a6e80d7669cce6c2bdbeb2530a74ec58/pillow-11.3.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:67172f2944ebba3d4a7b54f2e95c786a3a50c21b88456329314caaa28cda70f6", size = 7644652 }, + { url = "https://files.pythonhosted.org/packages/41/f1/6f2427a26fc683e00d985bc391bdd76d8dd4e92fac33d841127eb8fb2313/pillow-11.3.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97f07ed9f56a3b9b5f49d3661dc9607484e85c67e27f3e8be2c7d28ca032fec7", size = 5977787 }, + { url = "https://files.pythonhosted.org/packages/e4/c9/06dd4a38974e24f932ff5f98ea3c546ce3f8c995d3f0985f8e5ba48bba19/pillow-11.3.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:676b2815362456b5b3216b4fd5bd89d362100dc6f4945154ff172e206a22c024", size = 6645236 }, + { url = "https://files.pythonhosted.org/packages/40/e7/848f69fb79843b3d91241bad658e9c14f39a32f71a301bcd1d139416d1be/pillow-11.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3e184b2f26ff146363dd07bde8b711833d7b0202e27d13540bfe2e35a323a809", size = 6086950 }, + { url = "https://files.pythonhosted.org/packages/0b/1a/7cff92e695a2a29ac1958c2a0fe4c0b2393b60aac13b04a4fe2735cad52d/pillow-11.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6be31e3fc9a621e071bc17bb7de63b85cbe0bfae91bb0363c893cbe67247780d", size = 6723358 }, + { url = "https://files.pythonhosted.org/packages/26/7d/73699ad77895f69edff76b0f332acc3d497f22f5d75e5360f78cbcaff248/pillow-11.3.0-cp312-cp312-win32.whl", hash = "sha256:7b161756381f0918e05e7cb8a371fff367e807770f8fe92ecb20d905d0e1c149", size = 6275079 }, + { url = "https://files.pythonhosted.org/packages/8c/ce/e7dfc873bdd9828f3b6e5c2bbb74e47a98ec23cc5c74fc4e54462f0d9204/pillow-11.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:a6444696fce635783440b7f7a9fc24b3ad10a9ea3f0ab66c5905be1c19ccf17d", size = 6986324 }, + { url = "https://files.pythonhosted.org/packages/16/8f/b13447d1bf0b1f7467ce7d86f6e6edf66c0ad7cf44cf5c87a37f9bed9936/pillow-11.3.0-cp312-cp312-win_arm64.whl", hash = "sha256:2aceea54f957dd4448264f9bf40875da0415c83eb85f55069d89c0ed436e3542", size = 2423067 }, + { url = "https://files.pythonhosted.org/packages/1e/93/0952f2ed8db3a5a4c7a11f91965d6184ebc8cd7cbb7941a260d5f018cd2d/pillow-11.3.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:1c627742b539bba4309df89171356fcb3cc5a9178355b2727d1b74a6cf155fbd", size = 2128328 }, + { url = "https://files.pythonhosted.org/packages/4b/e8/100c3d114b1a0bf4042f27e0f87d2f25e857e838034e98ca98fe7b8c0a9c/pillow-11.3.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:30b7c02f3899d10f13d7a48163c8969e4e653f8b43416d23d13d1bbfdc93b9f8", size = 2170652 }, + { url = "https://files.pythonhosted.org/packages/aa/86/3f758a28a6e381758545f7cdb4942e1cb79abd271bea932998fc0db93cb6/pillow-11.3.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:7859a4cc7c9295f5838015d8cc0a9c215b77e43d07a25e460f35cf516df8626f", size = 2227443 }, + { url = "https://files.pythonhosted.org/packages/01/f4/91d5b3ffa718df2f53b0dc109877993e511f4fd055d7e9508682e8aba092/pillow-11.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ec1ee50470b0d050984394423d96325b744d55c701a439d2bd66089bff963d3c", size = 5278474 }, + { url = "https://files.pythonhosted.org/packages/f9/0e/37d7d3eca6c879fbd9dba21268427dffda1ab00d4eb05b32923d4fbe3b12/pillow-11.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7db51d222548ccfd274e4572fdbf3e810a5e66b00608862f947b163e613b67dd", size = 4686038 }, + { url = "https://files.pythonhosted.org/packages/ff/b0/3426e5c7f6565e752d81221af9d3676fdbb4f352317ceafd42899aaf5d8a/pillow-11.3.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2d6fcc902a24ac74495df63faad1884282239265c6839a0a6416d33faedfae7e", size = 5864407 }, + { url = "https://files.pythonhosted.org/packages/fc/c1/c6c423134229f2a221ee53f838d4be9d82bab86f7e2f8e75e47b6bf6cd77/pillow-11.3.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f0f5d8f4a08090c6d6d578351a2b91acf519a54986c055af27e7a93feae6d3f1", size = 7639094 }, + { url = "https://files.pythonhosted.org/packages/ba/c9/09e6746630fe6372c67c648ff9deae52a2bc20897d51fa293571977ceb5d/pillow-11.3.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c37d8ba9411d6003bba9e518db0db0c58a680ab9fe5179f040b0463644bc9805", size = 5973503 }, + { url = "https://files.pythonhosted.org/packages/d5/1c/a2a29649c0b1983d3ef57ee87a66487fdeb45132df66ab30dd37f7dbe162/pillow-11.3.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:13f87d581e71d9189ab21fe0efb5a23e9f28552d5be6979e84001d3b8505abe8", size = 6642574 }, + { url = "https://files.pythonhosted.org/packages/36/de/d5cc31cc4b055b6c6fd990e3e7f0f8aaf36229a2698501bcb0cdf67c7146/pillow-11.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:023f6d2d11784a465f09fd09a34b150ea4672e85fb3d05931d89f373ab14abb2", size = 6084060 }, + { url = "https://files.pythonhosted.org/packages/d5/ea/502d938cbaeec836ac28a9b730193716f0114c41325db428e6b280513f09/pillow-11.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:45dfc51ac5975b938e9809451c51734124e73b04d0f0ac621649821a63852e7b", size = 6721407 }, + { url = "https://files.pythonhosted.org/packages/45/9c/9c5e2a73f125f6cbc59cc7087c8f2d649a7ae453f83bd0362ff7c9e2aee2/pillow-11.3.0-cp313-cp313-win32.whl", hash = "sha256:a4d336baed65d50d37b88ca5b60c0fa9d81e3a87d4a7930d3880d1624d5b31f3", size = 6273841 }, + { url = "https://files.pythonhosted.org/packages/23/85/397c73524e0cd212067e0c969aa245b01d50183439550d24d9f55781b776/pillow-11.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:0bce5c4fd0921f99d2e858dc4d4d64193407e1b99478bc5cacecba2311abde51", size = 6978450 }, + { url = "https://files.pythonhosted.org/packages/17/d2/622f4547f69cd173955194b78e4d19ca4935a1b0f03a302d655c9f6aae65/pillow-11.3.0-cp313-cp313-win_arm64.whl", hash = "sha256:1904e1264881f682f02b7f8167935cce37bc97db457f8e7849dc3a6a52b99580", size = 2423055 }, + { url = "https://files.pythonhosted.org/packages/dd/80/a8a2ac21dda2e82480852978416cfacd439a4b490a501a288ecf4fe2532d/pillow-11.3.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:4c834a3921375c48ee6b9624061076bc0a32a60b5532b322cc0ea64e639dd50e", size = 5281110 }, + { url = "https://files.pythonhosted.org/packages/44/d6/b79754ca790f315918732e18f82a8146d33bcd7f4494380457ea89eb883d/pillow-11.3.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5e05688ccef30ea69b9317a9ead994b93975104a677a36a8ed8106be9260aa6d", size = 4689547 }, + { url = "https://files.pythonhosted.org/packages/49/20/716b8717d331150cb00f7fdd78169c01e8e0c219732a78b0e59b6bdb2fd6/pillow-11.3.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1019b04af07fc0163e2810167918cb5add8d74674b6267616021ab558dc98ced", size = 5901554 }, + { url = "https://files.pythonhosted.org/packages/74/cf/a9f3a2514a65bb071075063a96f0a5cf949c2f2fce683c15ccc83b1c1cab/pillow-11.3.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f944255db153ebb2b19c51fe85dd99ef0ce494123f21b9db4877ffdfc5590c7c", size = 7669132 }, + { url = "https://files.pythonhosted.org/packages/98/3c/da78805cbdbee9cb43efe8261dd7cc0b4b93f2ac79b676c03159e9db2187/pillow-11.3.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1f85acb69adf2aaee8b7da124efebbdb959a104db34d3a2cb0f3793dbae422a8", size = 6005001 }, + { url = "https://files.pythonhosted.org/packages/6c/fa/ce044b91faecf30e635321351bba32bab5a7e034c60187fe9698191aef4f/pillow-11.3.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:05f6ecbeff5005399bb48d198f098a9b4b6bdf27b8487c7f38ca16eeb070cd59", size = 6668814 }, + { url = "https://files.pythonhosted.org/packages/7b/51/90f9291406d09bf93686434f9183aba27b831c10c87746ff49f127ee80cb/pillow-11.3.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a7bc6e6fd0395bc052f16b1a8670859964dbd7003bd0af2ff08342eb6e442cfe", size = 6113124 }, + { url = "https://files.pythonhosted.org/packages/cd/5a/6fec59b1dfb619234f7636d4157d11fb4e196caeee220232a8d2ec48488d/pillow-11.3.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:83e1b0161c9d148125083a35c1c5a89db5b7054834fd4387499e06552035236c", size = 6747186 }, + { url = "https://files.pythonhosted.org/packages/49/6b/00187a044f98255225f172de653941e61da37104a9ea60e4f6887717e2b5/pillow-11.3.0-cp313-cp313t-win32.whl", hash = "sha256:2a3117c06b8fb646639dce83694f2f9eac405472713fcb1ae887469c0d4f6788", size = 6277546 }, + { url = "https://files.pythonhosted.org/packages/e8/5c/6caaba7e261c0d75bab23be79f1d06b5ad2a2ae49f028ccec801b0e853d6/pillow-11.3.0-cp313-cp313t-win_amd64.whl", hash = "sha256:857844335c95bea93fb39e0fa2726b4d9d758850b34075a7e3ff4f4fa3aa3b31", size = 6985102 }, + { url = "https://files.pythonhosted.org/packages/f3/7e/b623008460c09a0cb38263c93b828c666493caee2eb34ff67f778b87e58c/pillow-11.3.0-cp313-cp313t-win_arm64.whl", hash = "sha256:8797edc41f3e8536ae4b10897ee2f637235c94f27404cac7297f7b607dd0716e", size = 2424803 }, + { url = "https://files.pythonhosted.org/packages/73/f4/04905af42837292ed86cb1b1dabe03dce1edc008ef14c473c5c7e1443c5d/pillow-11.3.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:d9da3df5f9ea2a89b81bb6087177fb1f4d1c7146d583a3fe5c672c0d94e55e12", size = 5278520 }, + { url = "https://files.pythonhosted.org/packages/41/b0/33d79e377a336247df6348a54e6d2a2b85d644ca202555e3faa0cf811ecc/pillow-11.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0b275ff9b04df7b640c59ec5a3cb113eefd3795a8df80bac69646ef699c6981a", size = 4686116 }, + { url = "https://files.pythonhosted.org/packages/49/2d/ed8bc0ab219ae8768f529597d9509d184fe8a6c4741a6864fea334d25f3f/pillow-11.3.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0743841cabd3dba6a83f38a92672cccbd69af56e3e91777b0ee7f4dba4385632", size = 5864597 }, + { url = "https://files.pythonhosted.org/packages/b5/3d/b932bb4225c80b58dfadaca9d42d08d0b7064d2d1791b6a237f87f661834/pillow-11.3.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2465a69cf967b8b49ee1b96d76718cd98c4e925414ead59fdf75cf0fd07df673", size = 7638246 }, + { url = "https://files.pythonhosted.org/packages/09/b5/0487044b7c096f1b48f0d7ad416472c02e0e4bf6919541b111efd3cae690/pillow-11.3.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:41742638139424703b4d01665b807c6468e23e699e8e90cffefe291c5832b027", size = 5973336 }, + { url = "https://files.pythonhosted.org/packages/a8/2d/524f9318f6cbfcc79fbc004801ea6b607ec3f843977652fdee4857a7568b/pillow-11.3.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:93efb0b4de7e340d99057415c749175e24c8864302369e05914682ba642e5d77", size = 6642699 }, + { url = "https://files.pythonhosted.org/packages/6f/d2/a9a4f280c6aefedce1e8f615baaa5474e0701d86dd6f1dede66726462bbd/pillow-11.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7966e38dcd0fa11ca390aed7c6f20454443581d758242023cf36fcb319b1a874", size = 6083789 }, + { url = "https://files.pythonhosted.org/packages/fe/54/86b0cd9dbb683a9d5e960b66c7379e821a19be4ac5810e2e5a715c09a0c0/pillow-11.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:98a9afa7b9007c67ed84c57c9e0ad86a6000da96eaa638e4f8abe5b65ff83f0a", size = 6720386 }, + { url = "https://files.pythonhosted.org/packages/e7/95/88efcaf384c3588e24259c4203b909cbe3e3c2d887af9e938c2022c9dd48/pillow-11.3.0-cp314-cp314-win32.whl", hash = "sha256:02a723e6bf909e7cea0dac1b0e0310be9d7650cd66222a5f1c571455c0a45214", size = 6370911 }, + { url = "https://files.pythonhosted.org/packages/2e/cc/934e5820850ec5eb107e7b1a72dd278140731c669f396110ebc326f2a503/pillow-11.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:a418486160228f64dd9e9efcd132679b7a02a5f22c982c78b6fc7dab3fefb635", size = 7117383 }, + { url = "https://files.pythonhosted.org/packages/d6/e9/9c0a616a71da2a5d163aa37405e8aced9a906d574b4a214bede134e731bc/pillow-11.3.0-cp314-cp314-win_arm64.whl", hash = "sha256:155658efb5e044669c08896c0c44231c5e9abcaadbc5cd3648df2f7c0b96b9a6", size = 2511385 }, + { url = "https://files.pythonhosted.org/packages/1a/33/c88376898aff369658b225262cd4f2659b13e8178e7534df9e6e1fa289f6/pillow-11.3.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:59a03cdf019efbfeeed910bf79c7c93255c3d54bc45898ac2a4140071b02b4ae", size = 5281129 }, + { url = "https://files.pythonhosted.org/packages/1f/70/d376247fb36f1844b42910911c83a02d5544ebd2a8bad9efcc0f707ea774/pillow-11.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f8a5827f84d973d8636e9dc5764af4f0cf2318d26744b3d902931701b0d46653", size = 4689580 }, + { url = "https://files.pythonhosted.org/packages/eb/1c/537e930496149fbac69efd2fc4329035bbe2e5475b4165439e3be9cb183b/pillow-11.3.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ee92f2fd10f4adc4b43d07ec5e779932b4eb3dbfbc34790ada5a6669bc095aa6", size = 5902860 }, + { url = "https://files.pythonhosted.org/packages/bd/57/80f53264954dcefeebcf9dae6e3eb1daea1b488f0be8b8fef12f79a3eb10/pillow-11.3.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c96d333dcf42d01f47b37e0979b6bd73ec91eae18614864622d9b87bbd5bbf36", size = 7670694 }, + { url = "https://files.pythonhosted.org/packages/70/ff/4727d3b71a8578b4587d9c276e90efad2d6fe0335fd76742a6da08132e8c/pillow-11.3.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4c96f993ab8c98460cd0c001447bff6194403e8b1d7e149ade5f00594918128b", size = 6005888 }, + { url = "https://files.pythonhosted.org/packages/05/ae/716592277934f85d3be51d7256f3636672d7b1abfafdc42cf3f8cbd4b4c8/pillow-11.3.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:41342b64afeba938edb034d122b2dda5db2139b9a4af999729ba8818e0056477", size = 6670330 }, + { url = "https://files.pythonhosted.org/packages/e7/bb/7fe6cddcc8827b01b1a9766f5fdeb7418680744f9082035bdbabecf1d57f/pillow-11.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:068d9c39a2d1b358eb9f245ce7ab1b5c3246c7c8c7d9ba58cfa5b43146c06e50", size = 6114089 }, + { url = "https://files.pythonhosted.org/packages/8b/f5/06bfaa444c8e80f1a8e4bff98da9c83b37b5be3b1deaa43d27a0db37ef84/pillow-11.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a1bc6ba083b145187f648b667e05a2534ecc4b9f2784c2cbe3089e44868f2b9b", size = 6748206 }, + { url = "https://files.pythonhosted.org/packages/f0/77/bc6f92a3e8e6e46c0ca78abfffec0037845800ea38c73483760362804c41/pillow-11.3.0-cp314-cp314t-win32.whl", hash = "sha256:118ca10c0d60b06d006be10a501fd6bbdfef559251ed31b794668ed569c87e12", size = 6377370 }, + { url = "https://files.pythonhosted.org/packages/4a/82/3a721f7d69dca802befb8af08b7c79ebcab461007ce1c18bd91a5d5896f9/pillow-11.3.0-cp314-cp314t-win_amd64.whl", hash = "sha256:8924748b688aa210d79883357d102cd64690e56b923a186f35a82cbc10f997db", size = 7121500 }, + { url = "https://files.pythonhosted.org/packages/89/c7/5572fa4a3f45740eaab6ae86fcdf7195b55beac1371ac8c619d880cfe948/pillow-11.3.0-cp314-cp314t-win_arm64.whl", hash = "sha256:79ea0d14d3ebad43ec77ad5272e6ff9bba5b679ef73375ea760261207fa8e0aa", size = 2512835 }, + { url = "https://files.pythonhosted.org/packages/6f/8b/209bd6b62ce8367f47e68a218bffac88888fdf2c9fcf1ecadc6c3ec1ebc7/pillow-11.3.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:3cee80663f29e3843b68199b9d6f4f54bd1d4a6b59bdd91bceefc51238bcb967", size = 5270556 }, + { url = "https://files.pythonhosted.org/packages/2e/e6/231a0b76070c2cfd9e260a7a5b504fb72da0a95279410fa7afd99d9751d6/pillow-11.3.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:b5f56c3f344f2ccaf0dd875d3e180f631dc60a51b314295a3e681fe8cf851fbe", size = 4654625 }, + { url = "https://files.pythonhosted.org/packages/13/f4/10cf94fda33cb12765f2397fc285fa6d8eb9c29de7f3185165b702fc7386/pillow-11.3.0-pp310-pypy310_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e67d793d180c9df62f1f40aee3accca4829d3794c95098887edc18af4b8b780c", size = 4874207 }, + { url = "https://files.pythonhosted.org/packages/72/c9/583821097dc691880c92892e8e2d41fe0a5a3d6021f4963371d2f6d57250/pillow-11.3.0-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d000f46e2917c705e9fb93a3606ee4a819d1e3aa7a9b442f6444f07e77cf5e25", size = 6583939 }, + { url = "https://files.pythonhosted.org/packages/3b/8e/5c9d410f9217b12320efc7c413e72693f48468979a013ad17fd690397b9a/pillow-11.3.0-pp310-pypy310_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:527b37216b6ac3a12d7838dc3bd75208ec57c1c6d11ef01902266a5a0c14fc27", size = 4957166 }, + { url = "https://files.pythonhosted.org/packages/62/bb/78347dbe13219991877ffb3a91bf09da8317fbfcd4b5f9140aeae020ad71/pillow-11.3.0-pp310-pypy310_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:be5463ac478b623b9dd3937afd7fb7ab3d79dd290a28e2b6df292dc75063eb8a", size = 5581482 }, + { url = "https://files.pythonhosted.org/packages/d9/28/1000353d5e61498aaeaaf7f1e4b49ddb05f2c6575f9d4f9f914a3538b6e1/pillow-11.3.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:8dc70ca24c110503e16918a658b869019126ecfe03109b754c402daff12b3d9f", size = 6984596 }, + { url = "https://files.pythonhosted.org/packages/9e/e3/6fa84033758276fb31da12e5fb66ad747ae83b93c67af17f8c6ff4cc8f34/pillow-11.3.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7c8ec7a017ad1bd562f93dbd8505763e688d388cde6e4a010ae1486916e713e6", size = 5270566 }, + { url = "https://files.pythonhosted.org/packages/5b/ee/e8d2e1ab4892970b561e1ba96cbd59c0d28cf66737fc44abb2aec3795a4e/pillow-11.3.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:9ab6ae226de48019caa8074894544af5b53a117ccb9d3b3dcb2871464c829438", size = 4654618 }, + { url = "https://files.pythonhosted.org/packages/f2/6d/17f80f4e1f0761f02160fc433abd4109fa1548dcfdca46cfdadaf9efa565/pillow-11.3.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fe27fb049cdcca11f11a7bfda64043c37b30e6b91f10cb5bab275806c32f6ab3", size = 4874248 }, + { url = "https://files.pythonhosted.org/packages/de/5f/c22340acd61cef960130585bbe2120e2fd8434c214802f07e8c03596b17e/pillow-11.3.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:465b9e8844e3c3519a983d58b80be3f668e2a7a5db97f2784e7079fbc9f9822c", size = 6583963 }, + { url = "https://files.pythonhosted.org/packages/31/5e/03966aedfbfcbb4d5f8aa042452d3361f325b963ebbadddac05b122e47dd/pillow-11.3.0-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5418b53c0d59b3824d05e029669efa023bbef0f3e92e75ec8428f3799487f361", size = 4957170 }, + { url = "https://files.pythonhosted.org/packages/cc/2d/e082982aacc927fc2cab48e1e731bdb1643a1406acace8bed0900a61464e/pillow-11.3.0-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:504b6f59505f08ae014f724b6207ff6222662aab5cc9542577fb084ed0676ac7", size = 5581505 }, + { url = "https://files.pythonhosted.org/packages/34/e7/ae39f538fd6844e982063c3a5e4598b8ced43b9633baa3a85ef33af8c05c/pillow-11.3.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:c84d689db21a1c397d001aa08241044aa2069e7587b398c8cc63020390b1c1b8", size = 6984598 }, ] [[package]] @@ -2590,23 +2918,32 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/29/a2/d40fb2460e883eca5199c62cfc2463fd261f760556ae6290f88488c362c0/pip-25.1.1-py3-none-any.whl", hash = "sha256:2913a38a2abf4ea6b64ab507bd9e967f3b53dc1ede74b01b0931e1ce548751af", size = 1825227 }, ] +[[package]] +name = "platformdirs" +version = "4.3.8" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fe/8b/3c73abc9c759ecd3f1f7ceff6685840859e8070c4d947c93fae71f6a0bf2/platformdirs-4.3.8.tar.gz", hash = "sha256:3d512d96e16bcb959a814c9f348431070822a6496326a4be0911c40b5a74c2bc", size = 21362 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fe/39/979e8e21520d4e47a0bbe349e2713c0aac6f3d853d0e5b34d76206c439aa/platformdirs-4.3.8-py3-none-any.whl", hash = "sha256:ff7059bb7eb1179e2685604f4aaf157cfd9535242bd23742eadc3c13542139b4", size = 18567 }, +] + [[package]] name = "playwright" -version = "1.52.0" +version = "1.53.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "greenlet" }, { name = "pyee" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/62/a20240605485ca99365a8b72ed95e0b4c5739a13fb986353f72d8d3f1d27/playwright-1.52.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:19b2cb9d4794062008a635a99bd135b03ebb782d460f96534a91cb583f549512", size = 39611246 }, - { url = "https://files.pythonhosted.org/packages/dc/23/57ff081663b3061a2a3f0e111713046f705da2595f2f384488a76e4db732/playwright-1.52.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:0797c0479cbdc99607412a3c486a3a2ec9ddc77ac461259fd2878c975bcbb94a", size = 37962977 }, - { url = "https://files.pythonhosted.org/packages/a2/ff/eee8532cff4b3d768768152e8c4f30d3caa80f2969bf3143f4371d377b74/playwright-1.52.0-py3-none-macosx_11_0_universal2.whl", hash = "sha256:7223960b7dd7ddeec1ba378c302d1d09733b8dac438f492e9854c85d3ca7144f", size = 39611247 }, - { url = "https://files.pythonhosted.org/packages/73/c6/8e27af9798f81465b299741ef57064c6ec1a31128ed297406469907dc5a4/playwright-1.52.0-py3-none-manylinux1_x86_64.whl", hash = "sha256:d010124d24a321e0489a8c0d38a3971a7ca7656becea7656c9376bfea7f916d4", size = 45141333 }, - { url = "https://files.pythonhosted.org/packages/4e/e9/0661d343ed55860bcfb8934ce10e9597fc953358773ece507b22b0f35c57/playwright-1.52.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4173e453c43180acc60fd77ffe1ebee8d0efbfd9986c03267007b9c3845415af", size = 44540623 }, - { url = "https://files.pythonhosted.org/packages/7a/81/a850dbc6bc2e1bd6cc87341e59c253269602352de83d34b00ea38cf410ee/playwright-1.52.0-py3-none-win32.whl", hash = "sha256:cd0bdf92df99db6237a99f828e80a6a50db6180ef8d5352fc9495df2c92f9971", size = 34839156 }, - { url = "https://files.pythonhosted.org/packages/51/f3/cca2aa84eb28ea7d5b85d16caa92d62d18b6e83636e3d67957daca1ee4c7/playwright-1.52.0-py3-none-win_amd64.whl", hash = "sha256:dcbf75101eba3066b7521c6519de58721ea44379eb17a0dafa94f9f1b17f59e4", size = 34839164 }, - { url = "https://files.pythonhosted.org/packages/b5/4f/71a8a873e8c3c3e2d3ec03a578e546f6875be8a76214d90219f752f827cd/playwright-1.52.0-py3-none-win_arm64.whl", hash = "sha256:9d0085b8de513de5fb50669f8e6677f0252ef95a9a1d2d23ccee9638e71e65cb", size = 30688972 }, + { url = "https://files.pythonhosted.org/packages/f5/e2/2f107be74419280749723bd1197c99351f4b8a0a25e974b9764affb940b2/playwright-1.53.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:48a1a15ce810f0ffe512b6050de9871ea193b41dd3cc1bbed87b8431012419ba", size = 40392498 }, + { url = "https://files.pythonhosted.org/packages/ac/d5/e8c57a4f6fd46059fb2d51da2d22b47afc886b42400f06b742cd4a9ba131/playwright-1.53.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:a701f9498a5b87e3f929ec01cea3109fbde75821b19c7ba4bba54f6127b94f76", size = 38647035 }, + { url = "https://files.pythonhosted.org/packages/4d/f3/da18cd7c22398531316e58fd131243fd9156fe7765aae239ae542a5d07d2/playwright-1.53.0-py3-none-macosx_11_0_universal2.whl", hash = "sha256:f765498341c4037b4c01e742ae32dd335622f249488ccd77ca32d301d7c82c61", size = 40392502 }, + { url = "https://files.pythonhosted.org/packages/92/32/5d871c3753fbee5113eefc511b9e44c0006a27f2301b4c6bffa4346fbd94/playwright-1.53.0-py3-none-manylinux1_x86_64.whl", hash = "sha256:db19cb5b58f3b15cad3e2419f4910c053e889202fc202461ee183f1530d1db60", size = 45848364 }, + { url = "https://files.pythonhosted.org/packages/dc/6b/9942f86661ff41332f9299db4950623123e60ca71e4fb6e6942fc0212624/playwright-1.53.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9276c9c935fc062f51f4f5107e56420afd6d9a524348dc437793dc2e34c742e3", size = 45235174 }, + { url = "https://files.pythonhosted.org/packages/51/63/28b3f2d36e6a95e88f033d2aa7af06083f6f4aa0d9764759d96033cd053e/playwright-1.53.0-py3-none-win32.whl", hash = "sha256:36eedec101724ff5a000cddab87dd9a72a39f9b3e65a687169c465484e667c06", size = 35415131 }, + { url = "https://files.pythonhosted.org/packages/a9/b5/4ca25974a90d16cfd4a9a953ee5a666cf484a0bdacb4eed484e5cab49e66/playwright-1.53.0-py3-none-win_amd64.whl", hash = "sha256:d68975807a0fd997433537f1dcf2893cda95884a39dc23c6f591b8d5f691e9e8", size = 35415138 }, + { url = "https://files.pythonhosted.org/packages/9a/81/b42ff2116df5d07ccad2dc4eeb20af92c975a1fbc7cd3ed37b678468b813/playwright-1.53.0-py3-none-win_arm64.whl", hash = "sha256:fcfd481f76568d7b011571160e801b47034edd9e2383c43d83a5fb3f35c67885", size = 31188568 }, ] [[package]] @@ -2618,6 +2955,22 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538 }, ] +[[package]] +name = "pre-commit" +version = "4.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cfgv" }, + { name = "identify" }, + { name = "nodeenv" }, + { name = "pyyaml" }, + { name = "virtualenv" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/08/39/679ca9b26c7bb2999ff122d50faa301e49af82ca9c066ec061cfbc0c6784/pre_commit-4.2.0.tar.gz", hash = "sha256:601283b9757afd87d40c4c4a9b2b5de9637a8ea02eaff7adc2d0fb4e04841146", size = 193424 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/74/a88bf1b1efeae488a0c0b7bdf71429c313722d1fc0f377537fbe554e6180/pre_commit-4.2.0-py2.py3-none-any.whl", hash = "sha256:a009ca7205f1eb497d10b845e52c838a98b6cdd2102a6c8e4540e94ee75c58bd", size = 220707 }, +] + [[package]] name = "primp" version = "0.15.0" @@ -2749,16 +3102,16 @@ wheels = [ [[package]] name = "protobuf" -version = "6.31.1" +version = "5.29.5" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/52/f3/b9655a711b32c19720253f6f06326faf90580834e2e83f840472d752bc8b/protobuf-6.31.1.tar.gz", hash = "sha256:d8cac4c982f0b957a4dc73a80e2ea24fab08e679c0de9deb835f4a12d69aca9a", size = 441797 } +sdist = { url = "https://files.pythonhosted.org/packages/43/29/d09e70352e4e88c9c7a198d5645d7277811448d76c23b00345670f7c8a38/protobuf-5.29.5.tar.gz", hash = "sha256:bc1463bafd4b0929216c35f437a8e28731a2b7fe3d98bb77a600efced5a15c84", size = 425226 } wheels = [ - { url = "https://files.pythonhosted.org/packages/f3/6f/6ab8e4bf962fd5570d3deaa2d5c38f0a363f57b4501047b5ebeb83ab1125/protobuf-6.31.1-cp310-abi3-win32.whl", hash = "sha256:7fa17d5a29c2e04b7d90e5e32388b8bfd0e7107cd8e616feef7ed3fa6bdab5c9", size = 423603 }, - { url = "https://files.pythonhosted.org/packages/44/3a/b15c4347dd4bf3a1b0ee882f384623e2063bb5cf9fa9d57990a4f7df2fb6/protobuf-6.31.1-cp310-abi3-win_amd64.whl", hash = "sha256:426f59d2964864a1a366254fa703b8632dcec0790d8862d30034d8245e1cd447", size = 435283 }, - { url = "https://files.pythonhosted.org/packages/6a/c9/b9689a2a250264a84e66c46d8862ba788ee7a641cdca39bccf64f59284b7/protobuf-6.31.1-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:6f1227473dc43d44ed644425268eb7c2e488ae245d51c6866d19fe158e207402", size = 425604 }, - { url = "https://files.pythonhosted.org/packages/76/a1/7a5a94032c83375e4fe7e7f56e3976ea6ac90c5e85fac8576409e25c39c3/protobuf-6.31.1-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:a40fc12b84c154884d7d4c4ebd675d5b3b5283e155f324049ae396b95ddebc39", size = 322115 }, - { url = "https://files.pythonhosted.org/packages/fa/b1/b59d405d64d31999244643d88c45c8241c58f17cc887e73bcb90602327f8/protobuf-6.31.1-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:4ee898bf66f7a8b0bd21bce523814e6fbd8c6add948045ce958b73af7e8878c6", size = 321070 }, - { url = "https://files.pythonhosted.org/packages/f7/af/ab3c51ab7507a7325e98ffe691d9495ee3d3aa5f589afad65ec920d39821/protobuf-6.31.1-py3-none-any.whl", hash = "sha256:720a6c7e6b77288b85063569baae8536671b39f15cc22037ec7045658d80489e", size = 168724 }, + { url = "https://files.pythonhosted.org/packages/5f/11/6e40e9fc5bba02988a214c07cf324595789ca7820160bfd1f8be96e48539/protobuf-5.29.5-cp310-abi3-win32.whl", hash = "sha256:3f1c6468a2cfd102ff4703976138844f78ebd1fb45f49011afc5139e9e283079", size = 422963 }, + { url = "https://files.pythonhosted.org/packages/81/7f/73cefb093e1a2a7c3ffd839e6f9fcafb7a427d300c7f8aef9c64405d8ac6/protobuf-5.29.5-cp310-abi3-win_amd64.whl", hash = "sha256:3f76e3a3675b4a4d867b52e4a5f5b78a2ef9565549d4037e06cf7b0942b1d3fc", size = 434818 }, + { url = "https://files.pythonhosted.org/packages/dd/73/10e1661c21f139f2c6ad9b23040ff36fee624310dc28fba20d33fdae124c/protobuf-5.29.5-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:e38c5add5a311f2a6eb0340716ef9b039c1dfa428b28f25a7838ac329204a671", size = 418091 }, + { url = "https://files.pythonhosted.org/packages/6c/04/98f6f8cf5b07ab1294c13f34b4e69b3722bb609c5b701d6c169828f9f8aa/protobuf-5.29.5-cp38-abi3-manylinux2014_aarch64.whl", hash = "sha256:fa18533a299d7ab6c55a238bf8629311439995f2e7eca5caaff08663606e9015", size = 319824 }, + { url = "https://files.pythonhosted.org/packages/85/e4/07c80521879c2d15f321465ac24c70efe2381378c00bf5e56a0f4fbac8cd/protobuf-5.29.5-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:63848923da3325e1bf7e9003d680ce6e14b07e55d0473253a690c3a8b8fd6e61", size = 319942 }, + { url = "https://files.pythonhosted.org/packages/7e/cc/7e77861000a0691aeea8f4566e5d3aa716f2b1dece4a24439437e41d3d25/protobuf-5.29.5-py3-none-any.whl", hash = "sha256:6cf42630262c59b2d8de33954443d94b746c952b01434fc58a417fdbd2e84bd5", size = 172823 }, ] [[package]] @@ -2770,66 +3123,82 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35", size = 13993 }, ] +[[package]] +name = "pure-eval" +version = "0.2.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cd/05/0a34433a064256a578f1783a10da6df098ceaa4a57bbeaa96a6c0352786b/pure_eval-0.2.3.tar.gz", hash = "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42", size = 19752 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8e/37/efad0257dc6e593a18957422533ff0f87ede7c9c6ea010a2177d738fb82f/pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0", size = 11842 }, +] + +[[package]] +name = "puremagic" +version = "1.30" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version == '3.11.*'", + "python_full_version < '3.11'", +] +sdist = { url = "https://files.pythonhosted.org/packages/dd/7f/9998706bc516bdd664ccf929a1da6c6e5ee06e48f723ce45aae7cf3ff36e/puremagic-1.30.tar.gz", hash = "sha256:f9ff7ac157d54e9cf3bff1addfd97233548e75e685282d84ae11e7ffee1614c9", size = 314785 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/91/ed/1e347d85d05b37a8b9a039ca832e5747e1e5248d0bd66042783ef48b4a37/puremagic-1.30-py3-none-any.whl", hash = "sha256:5eeeb2dd86f335b9cfe8e205346612197af3500c6872dffebf26929f56e9d3c1", size = 43304 }, +] + [[package]] name = "puremagic" -version = "1.29" +version = "2.0.0b4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d6/de/c9dbb741a2e0e657147c6125699e4a2a3b9003840fed62528e17c87c0989/puremagic-1.29.tar.gz", hash = "sha256:67c115db3f63d43b13433860917b11e2b767e5eaec696a491be2fb544f224f7a", size = 314734 } +resolution-markers = [ + "python_full_version >= '3.13'", + "python_full_version == '3.12.*'", +] +sdist = { url = "https://files.pythonhosted.org/packages/71/25/ad2258b828a6fb4addef2bd6cbb17cbd1c480e063dd33e26d43988d41bff/puremagic-2.0.0b4.tar.gz", hash = "sha256:15a10611cad613f8d07697d4e41fefc1d1cd2cd08a8e2c881cb66ec01731cae1", size = 530965 } wheels = [ - { url = "https://files.pythonhosted.org/packages/50/e3/6d0ad0dc83cf0871198a68d527c61e443c10509a93db1e1666be9d1bf9c6/puremagic-1.29-py3-none-any.whl", hash = "sha256:2c3cfcde77f0b1560f1898f627bd388421d2bd64ec94d8d25f400f7742a4f109", size = 43279 }, + { url = "https://files.pythonhosted.org/packages/b5/3e/0998660a13090d0c82d03ebc630098a0d4d45d497888d9cd5ea733fdb0f4/puremagic-2.0.0b4-py3-none-any.whl", hash = "sha256:99b15e6bb598b3b8ad32c565846a9fe16e9b6d9af653fbfebb81fd0c5c4f5541", size = 48215 }, ] [[package]] name = "pyarrow" -version = "20.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a2/ee/a7810cb9f3d6e9238e61d312076a9859bf3668fd21c69744de9532383912/pyarrow-20.0.0.tar.gz", hash = "sha256:febc4a913592573c8d5805091a6c2b5064c8bd6e002131f01061797d91c783c1", size = 1125187 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5b/23/77094eb8ee0dbe88441689cb6afc40ac312a1e15d3a7acc0586999518222/pyarrow-20.0.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:c7dd06fd7d7b410ca5dc839cc9d485d2bc4ae5240851bcd45d85105cc90a47d7", size = 30832591 }, - { url = "https://files.pythonhosted.org/packages/c3/d5/48cc573aff00d62913701d9fac478518f693b30c25f2c157550b0b2565cb/pyarrow-20.0.0-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:d5382de8dc34c943249b01c19110783d0d64b207167c728461add1ecc2db88e4", size = 32273686 }, - { url = "https://files.pythonhosted.org/packages/37/df/4099b69a432b5cb412dd18adc2629975544d656df3d7fda6d73c5dba935d/pyarrow-20.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6415a0d0174487456ddc9beaead703d0ded5966129fa4fd3114d76b5d1c5ceae", size = 41337051 }, - { url = "https://files.pythonhosted.org/packages/4c/27/99922a9ac1c9226f346e3a1e15e63dee6f623ed757ff2893f9d6994a69d3/pyarrow-20.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15aa1b3b2587e74328a730457068dc6c89e6dcbf438d4369f572af9d320a25ee", size = 42404659 }, - { url = "https://files.pythonhosted.org/packages/21/d1/71d91b2791b829c9e98f1e0d85be66ed93aff399f80abb99678511847eaa/pyarrow-20.0.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:5605919fbe67a7948c1f03b9f3727d82846c053cd2ce9303ace791855923fd20", size = 40695446 }, - { url = "https://files.pythonhosted.org/packages/f1/ca/ae10fba419a6e94329707487835ec721f5a95f3ac9168500bcf7aa3813c7/pyarrow-20.0.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a5704f29a74b81673d266e5ec1fe376f060627c2e42c5c7651288ed4b0db29e9", size = 42278528 }, - { url = "https://files.pythonhosted.org/packages/7a/a6/aba40a2bf01b5d00cf9cd16d427a5da1fad0fb69b514ce8c8292ab80e968/pyarrow-20.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:00138f79ee1b5aca81e2bdedb91e3739b987245e11fa3c826f9e57c5d102fb75", size = 42918162 }, - { url = "https://files.pythonhosted.org/packages/93/6b/98b39650cd64f32bf2ec6d627a9bd24fcb3e4e6ea1873c5e1ea8a83b1a18/pyarrow-20.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f2d67ac28f57a362f1a2c1e6fa98bfe2f03230f7e15927aecd067433b1e70ce8", size = 44550319 }, - { url = "https://files.pythonhosted.org/packages/ab/32/340238be1eb5037e7b5de7e640ee22334417239bc347eadefaf8c373936d/pyarrow-20.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:4a8b029a07956b8d7bd742ffca25374dd3f634b35e46cc7a7c3fa4c75b297191", size = 25770759 }, - { url = "https://files.pythonhosted.org/packages/47/a2/b7930824181ceadd0c63c1042d01fa4ef63eee233934826a7a2a9af6e463/pyarrow-20.0.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:24ca380585444cb2a31324c546a9a56abbe87e26069189e14bdba19c86c049f0", size = 30856035 }, - { url = "https://files.pythonhosted.org/packages/9b/18/c765770227d7f5bdfa8a69f64b49194352325c66a5c3bb5e332dfd5867d9/pyarrow-20.0.0-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:95b330059ddfdc591a3225f2d272123be26c8fa76e8c9ee1a77aad507361cfdb", size = 32309552 }, - { url = "https://files.pythonhosted.org/packages/44/fb/dfb2dfdd3e488bb14f822d7335653092dde150cffc2da97de6e7500681f9/pyarrow-20.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f0fb1041267e9968c6d0d2ce3ff92e3928b243e2b6d11eeb84d9ac547308232", size = 41334704 }, - { url = "https://files.pythonhosted.org/packages/58/0d/08a95878d38808051a953e887332d4a76bc06c6ee04351918ee1155407eb/pyarrow-20.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8ff87cc837601532cc8242d2f7e09b4e02404de1b797aee747dd4ba4bd6313f", size = 42399836 }, - { url = "https://files.pythonhosted.org/packages/f3/cd/efa271234dfe38f0271561086eedcad7bc0f2ddd1efba423916ff0883684/pyarrow-20.0.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:7a3a5dcf54286e6141d5114522cf31dd67a9e7c9133d150799f30ee302a7a1ab", size = 40711789 }, - { url = "https://files.pythonhosted.org/packages/46/1f/7f02009bc7fc8955c391defee5348f510e589a020e4b40ca05edcb847854/pyarrow-20.0.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:a6ad3e7758ecf559900261a4df985662df54fb7fdb55e8e3b3aa99b23d526b62", size = 42301124 }, - { url = "https://files.pythonhosted.org/packages/4f/92/692c562be4504c262089e86757a9048739fe1acb4024f92d39615e7bab3f/pyarrow-20.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6bb830757103a6cb300a04610e08d9636f0cd223d32f388418ea893a3e655f1c", size = 42916060 }, - { url = "https://files.pythonhosted.org/packages/a4/ec/9f5c7e7c828d8e0a3c7ef50ee62eca38a7de2fa6eb1b8fa43685c9414fef/pyarrow-20.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:96e37f0766ecb4514a899d9a3554fadda770fb57ddf42b63d80f14bc20aa7db3", size = 44547640 }, - { url = "https://files.pythonhosted.org/packages/54/96/46613131b4727f10fd2ffa6d0d6f02efcc09a0e7374eff3b5771548aa95b/pyarrow-20.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:3346babb516f4b6fd790da99b98bed9708e3f02e734c84971faccb20736848dc", size = 25781491 }, - { url = "https://files.pythonhosted.org/packages/a1/d6/0c10e0d54f6c13eb464ee9b67a68b8c71bcf2f67760ef5b6fbcddd2ab05f/pyarrow-20.0.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:75a51a5b0eef32727a247707d4755322cb970be7e935172b6a3a9f9ae98404ba", size = 30815067 }, - { url = "https://files.pythonhosted.org/packages/7e/e2/04e9874abe4094a06fd8b0cbb0f1312d8dd7d707f144c2ec1e5e8f452ffa/pyarrow-20.0.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:211d5e84cecc640c7a3ab900f930aaff5cd2702177e0d562d426fb7c4f737781", size = 32297128 }, - { url = "https://files.pythonhosted.org/packages/31/fd/c565e5dcc906a3b471a83273039cb75cb79aad4a2d4a12f76cc5ae90a4b8/pyarrow-20.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ba3cf4182828be7a896cbd232aa8dd6a31bd1f9e32776cc3796c012855e1199", size = 41334890 }, - { url = "https://files.pythonhosted.org/packages/af/a9/3bdd799e2c9b20c1ea6dc6fa8e83f29480a97711cf806e823f808c2316ac/pyarrow-20.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c3a01f313ffe27ac4126f4c2e5ea0f36a5fc6ab51f8726cf41fee4b256680bd", size = 42421775 }, - { url = "https://files.pythonhosted.org/packages/10/f7/da98ccd86354c332f593218101ae56568d5dcedb460e342000bd89c49cc1/pyarrow-20.0.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:a2791f69ad72addd33510fec7bb14ee06c2a448e06b649e264c094c5b5f7ce28", size = 40687231 }, - { url = "https://files.pythonhosted.org/packages/bb/1b/2168d6050e52ff1e6cefc61d600723870bf569cbf41d13db939c8cf97a16/pyarrow-20.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:4250e28a22302ce8692d3a0e8ec9d9dde54ec00d237cff4dfa9c1fbf79e472a8", size = 42295639 }, - { url = "https://files.pythonhosted.org/packages/b2/66/2d976c0c7158fd25591c8ca55aee026e6d5745a021915a1835578707feb3/pyarrow-20.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:89e030dc58fc760e4010148e6ff164d2f44441490280ef1e97a542375e41058e", size = 42908549 }, - { url = "https://files.pythonhosted.org/packages/31/a9/dfb999c2fc6911201dcbf348247f9cc382a8990f9ab45c12eabfd7243a38/pyarrow-20.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6102b4864d77102dbbb72965618e204e550135a940c2534711d5ffa787df2a5a", size = 44557216 }, - { url = "https://files.pythonhosted.org/packages/a0/8e/9adee63dfa3911be2382fb4d92e4b2e7d82610f9d9f668493bebaa2af50f/pyarrow-20.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:96d6a0a37d9c98be08f5ed6a10831d88d52cac7b13f5287f1e0f625a0de8062b", size = 25660496 }, - { url = "https://files.pythonhosted.org/packages/9b/aa/daa413b81446d20d4dad2944110dcf4cf4f4179ef7f685dd5a6d7570dc8e/pyarrow-20.0.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:a15532e77b94c61efadde86d10957950392999503b3616b2ffcef7621a002893", size = 30798501 }, - { url = "https://files.pythonhosted.org/packages/ff/75/2303d1caa410925de902d32ac215dc80a7ce7dd8dfe95358c165f2adf107/pyarrow-20.0.0-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:dd43f58037443af715f34f1322c782ec463a3c8a94a85fdb2d987ceb5658e061", size = 32277895 }, - { url = "https://files.pythonhosted.org/packages/92/41/fe18c7c0b38b20811b73d1bdd54b1fccba0dab0e51d2048878042d84afa8/pyarrow-20.0.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa0d288143a8585806e3cc7c39566407aab646fb9ece164609dac1cfff45f6ae", size = 41327322 }, - { url = "https://files.pythonhosted.org/packages/da/ab/7dbf3d11db67c72dbf36ae63dcbc9f30b866c153b3a22ef728523943eee6/pyarrow-20.0.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b6953f0114f8d6f3d905d98e987d0924dabce59c3cda380bdfaa25a6201563b4", size = 42411441 }, - { url = "https://files.pythonhosted.org/packages/90/c3/0c7da7b6dac863af75b64e2f827e4742161128c350bfe7955b426484e226/pyarrow-20.0.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:991f85b48a8a5e839b2128590ce07611fae48a904cae6cab1f089c5955b57eb5", size = 40677027 }, - { url = "https://files.pythonhosted.org/packages/be/27/43a47fa0ff9053ab5203bb3faeec435d43c0d8bfa40179bfd076cdbd4e1c/pyarrow-20.0.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:97c8dc984ed09cb07d618d57d8d4b67a5100a30c3818c2fb0b04599f0da2de7b", size = 42281473 }, - { url = "https://files.pythonhosted.org/packages/bc/0b/d56c63b078876da81bbb9ba695a596eabee9b085555ed12bf6eb3b7cab0e/pyarrow-20.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9b71daf534f4745818f96c214dbc1e6124d7daf059167330b610fc69b6f3d3e3", size = 42893897 }, - { url = "https://files.pythonhosted.org/packages/92/ac/7d4bd020ba9145f354012838692d48300c1b8fe5634bfda886abcada67ed/pyarrow-20.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e8b88758f9303fa5a83d6c90e176714b2fd3852e776fc2d7e42a22dd6c2fb368", size = 44543847 }, - { url = "https://files.pythonhosted.org/packages/9d/07/290f4abf9ca702c5df7b47739c1b2c83588641ddfa2cc75e34a301d42e55/pyarrow-20.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:30b3051b7975801c1e1d387e17c588d8ab05ced9b1e14eec57915f79869b5031", size = 25653219 }, - { url = "https://files.pythonhosted.org/packages/95/df/720bb17704b10bd69dde086e1400b8eefb8f58df3f8ac9cff6c425bf57f1/pyarrow-20.0.0-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:ca151afa4f9b7bc45bcc791eb9a89e90a9eb2772767d0b1e5389609c7d03db63", size = 30853957 }, - { url = "https://files.pythonhosted.org/packages/d9/72/0d5f875efc31baef742ba55a00a25213a19ea64d7176e0fe001c5d8b6e9a/pyarrow-20.0.0-cp313-cp313t-macosx_12_0_x86_64.whl", hash = "sha256:4680f01ecd86e0dd63e39eb5cd59ef9ff24a9d166db328679e36c108dc993d4c", size = 32247972 }, - { url = "https://files.pythonhosted.org/packages/d5/bc/e48b4fa544d2eea72f7844180eb77f83f2030b84c8dad860f199f94307ed/pyarrow-20.0.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7f4c8534e2ff059765647aa69b75d6543f9fef59e2cd4c6d18015192565d2b70", size = 41256434 }, - { url = "https://files.pythonhosted.org/packages/c3/01/974043a29874aa2cf4f87fb07fd108828fc7362300265a2a64a94965e35b/pyarrow-20.0.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3e1f8a47f4b4ae4c69c4d702cfbdfe4d41e18e5c7ef6f1bb1c50918c1e81c57b", size = 42353648 }, - { url = "https://files.pythonhosted.org/packages/68/95/cc0d3634cde9ca69b0e51cbe830d8915ea32dda2157560dda27ff3b3337b/pyarrow-20.0.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:a1f60dc14658efaa927f8214734f6a01a806d7690be4b3232ba526836d216122", size = 40619853 }, - { url = "https://files.pythonhosted.org/packages/29/c2/3ad40e07e96a3e74e7ed7cc8285aadfa84eb848a798c98ec0ad009eb6bcc/pyarrow-20.0.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:204a846dca751428991346976b914d6d2a82ae5b8316a6ed99789ebf976551e6", size = 42241743 }, - { url = "https://files.pythonhosted.org/packages/eb/cb/65fa110b483339add6a9bc7b6373614166b14e20375d4daa73483755f830/pyarrow-20.0.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:f3b117b922af5e4c6b9a9115825726cac7d8b1421c37c2b5e24fbacc8930612c", size = 42839441 }, - { url = "https://files.pythonhosted.org/packages/98/7b/f30b1954589243207d7a0fbc9997401044bf9a033eec78f6cb50da3f304a/pyarrow-20.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:e724a3fd23ae5b9c010e7be857f4405ed5e679db5c93e66204db1a69f733936a", size = 44503279 }, - { url = "https://files.pythonhosted.org/packages/37/40/ad395740cd641869a13bcf60851296c89624662575621968dcfafabaa7f6/pyarrow-20.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:82f1ee5133bd8f49d31be1299dc07f585136679666b502540db854968576faf9", size = 25944982 }, +version = "21.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ef/c2/ea068b8f00905c06329a3dfcd40d0fcc2b7d0f2e355bdb25b65e0a0e4cd4/pyarrow-21.0.0.tar.gz", hash = "sha256:5051f2dccf0e283ff56335760cbc8622cf52264d67e359d5569541ac11b6d5bc", size = 1133487 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/17/d9/110de31880016e2afc52d8580b397dbe47615defbf09ca8cf55f56c62165/pyarrow-21.0.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:e563271e2c5ff4d4a4cbeb2c83d5cf0d4938b891518e676025f7268c6fe5fe26", size = 31196837 }, + { url = "https://files.pythonhosted.org/packages/df/5f/c1c1997613abf24fceb087e79432d24c19bc6f7259cab57c2c8e5e545fab/pyarrow-21.0.0-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:fee33b0ca46f4c85443d6c450357101e47d53e6c3f008d658c27a2d020d44c79", size = 32659470 }, + { url = "https://files.pythonhosted.org/packages/3e/ed/b1589a777816ee33ba123ba1e4f8f02243a844fed0deec97bde9fb21a5cf/pyarrow-21.0.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:7be45519b830f7c24b21d630a31d48bcebfd5d4d7f9d3bdb49da9cdf6d764edb", size = 41055619 }, + { url = "https://files.pythonhosted.org/packages/44/28/b6672962639e85dc0ac36f71ab3a8f5f38e01b51343d7aa372a6b56fa3f3/pyarrow-21.0.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:26bfd95f6bff443ceae63c65dc7e048670b7e98bc892210acba7e4995d3d4b51", size = 42733488 }, + { url = "https://files.pythonhosted.org/packages/f8/cc/de02c3614874b9089c94eac093f90ca5dfa6d5afe45de3ba847fd950fdf1/pyarrow-21.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:bd04ec08f7f8bd113c55868bd3fc442a9db67c27af098c5f814a3091e71cc61a", size = 43329159 }, + { url = "https://files.pythonhosted.org/packages/a6/3e/99473332ac40278f196e105ce30b79ab8affab12f6194802f2593d6b0be2/pyarrow-21.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:9b0b14b49ac10654332a805aedfc0147fb3469cbf8ea951b3d040dab12372594", size = 45050567 }, + { url = "https://files.pythonhosted.org/packages/7b/f5/c372ef60593d713e8bfbb7e0c743501605f0ad00719146dc075faf11172b/pyarrow-21.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:9d9f8bcb4c3be7738add259738abdeddc363de1b80e3310e04067aa1ca596634", size = 26217959 }, + { url = "https://files.pythonhosted.org/packages/94/dc/80564a3071a57c20b7c32575e4a0120e8a330ef487c319b122942d665960/pyarrow-21.0.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:c077f48aab61738c237802836fc3844f85409a46015635198761b0d6a688f87b", size = 31243234 }, + { url = "https://files.pythonhosted.org/packages/ea/cc/3b51cb2db26fe535d14f74cab4c79b191ed9a8cd4cbba45e2379b5ca2746/pyarrow-21.0.0-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:689f448066781856237eca8d1975b98cace19b8dd2ab6145bf49475478bcaa10", size = 32714370 }, + { url = "https://files.pythonhosted.org/packages/24/11/a4431f36d5ad7d83b87146f515c063e4d07ef0b7240876ddb885e6b44f2e/pyarrow-21.0.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:479ee41399fcddc46159a551705b89c05f11e8b8cb8e968f7fec64f62d91985e", size = 41135424 }, + { url = "https://files.pythonhosted.org/packages/74/dc/035d54638fc5d2971cbf1e987ccd45f1091c83bcf747281cf6cc25e72c88/pyarrow-21.0.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:40ebfcb54a4f11bcde86bc586cbd0272bac0d516cfa539c799c2453768477569", size = 42823810 }, + { url = "https://files.pythonhosted.org/packages/2e/3b/89fced102448a9e3e0d4dded1f37fa3ce4700f02cdb8665457fcc8015f5b/pyarrow-21.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8d58d8497814274d3d20214fbb24abcad2f7e351474357d552a8d53bce70c70e", size = 43391538 }, + { url = "https://files.pythonhosted.org/packages/fb/bb/ea7f1bd08978d39debd3b23611c293f64a642557e8141c80635d501e6d53/pyarrow-21.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:585e7224f21124dd57836b1530ac8f2df2afc43c861d7bf3d58a4870c42ae36c", size = 45120056 }, + { url = "https://files.pythonhosted.org/packages/6e/0b/77ea0600009842b30ceebc3337639a7380cd946061b620ac1a2f3cb541e2/pyarrow-21.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:555ca6935b2cbca2c0e932bedd853e9bc523098c39636de9ad4693b5b1df86d6", size = 26220568 }, + { url = "https://files.pythonhosted.org/packages/ca/d4/d4f817b21aacc30195cf6a46ba041dd1be827efa4a623cc8bf39a1c2a0c0/pyarrow-21.0.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:3a302f0e0963db37e0a24a70c56cf91a4faa0bca51c23812279ca2e23481fccd", size = 31160305 }, + { url = "https://files.pythonhosted.org/packages/a2/9c/dcd38ce6e4b4d9a19e1d36914cb8e2b1da4e6003dd075474c4cfcdfe0601/pyarrow-21.0.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:b6b27cf01e243871390474a211a7922bfbe3bda21e39bc9160daf0da3fe48876", size = 32684264 }, + { url = "https://files.pythonhosted.org/packages/4f/74/2a2d9f8d7a59b639523454bec12dba35ae3d0a07d8ab529dc0809f74b23c/pyarrow-21.0.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:e72a8ec6b868e258a2cd2672d91f2860ad532d590ce94cdf7d5e7ec674ccf03d", size = 41108099 }, + { url = "https://files.pythonhosted.org/packages/ad/90/2660332eeb31303c13b653ea566a9918484b6e4d6b9d2d46879a33ab0622/pyarrow-21.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:b7ae0bbdc8c6674259b25bef5d2a1d6af5d39d7200c819cf99e07f7dfef1c51e", size = 42829529 }, + { url = "https://files.pythonhosted.org/packages/33/27/1a93a25c92717f6aa0fca06eb4700860577d016cd3ae51aad0e0488ac899/pyarrow-21.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:58c30a1729f82d201627c173d91bd431db88ea74dcaa3885855bc6203e433b82", size = 43367883 }, + { url = "https://files.pythonhosted.org/packages/05/d9/4d09d919f35d599bc05c6950095e358c3e15148ead26292dfca1fb659b0c/pyarrow-21.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:072116f65604b822a7f22945a7a6e581cfa28e3454fdcc6939d4ff6090126623", size = 45133802 }, + { url = "https://files.pythonhosted.org/packages/71/30/f3795b6e192c3ab881325ffe172e526499eb3780e306a15103a2764916a2/pyarrow-21.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:cf56ec8b0a5c8c9d7021d6fd754e688104f9ebebf1bf4449613c9531f5346a18", size = 26203175 }, + { url = "https://files.pythonhosted.org/packages/16/ca/c7eaa8e62db8fb37ce942b1ea0c6d7abfe3786ca193957afa25e71b81b66/pyarrow-21.0.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:e99310a4ebd4479bcd1964dff9e14af33746300cb014aa4a3781738ac63baf4a", size = 31154306 }, + { url = "https://files.pythonhosted.org/packages/ce/e8/e87d9e3b2489302b3a1aea709aaca4b781c5252fcb812a17ab6275a9a484/pyarrow-21.0.0-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:d2fe8e7f3ce329a71b7ddd7498b3cfac0eeb200c2789bd840234f0dc271a8efe", size = 32680622 }, + { url = "https://files.pythonhosted.org/packages/84/52/79095d73a742aa0aba370c7942b1b655f598069489ab387fe47261a849e1/pyarrow-21.0.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:f522e5709379d72fb3da7785aa489ff0bb87448a9dc5a75f45763a795a089ebd", size = 41104094 }, + { url = "https://files.pythonhosted.org/packages/89/4b/7782438b551dbb0468892a276b8c789b8bbdb25ea5c5eb27faadd753e037/pyarrow-21.0.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:69cbbdf0631396e9925e048cfa5bce4e8c3d3b41562bbd70c685a8eb53a91e61", size = 42825576 }, + { url = "https://files.pythonhosted.org/packages/b3/62/0f29de6e0a1e33518dec92c65be0351d32d7ca351e51ec5f4f837a9aab91/pyarrow-21.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:731c7022587006b755d0bdb27626a1a3bb004bb56b11fb30d98b6c1b4718579d", size = 43368342 }, + { url = "https://files.pythonhosted.org/packages/90/c7/0fa1f3f29cf75f339768cc698c8ad4ddd2481c1742e9741459911c9ac477/pyarrow-21.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:dc56bc708f2d8ac71bd1dcb927e458c93cec10b98eb4120206a4091db7b67b99", size = 45131218 }, + { url = "https://files.pythonhosted.org/packages/01/63/581f2076465e67b23bc5a37d4a2abff8362d389d29d8105832e82c9c811c/pyarrow-21.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:186aa00bca62139f75b7de8420f745f2af12941595bbbfa7ed3870ff63e25636", size = 26087551 }, + { url = "https://files.pythonhosted.org/packages/c9/ab/357d0d9648bb8241ee7348e564f2479d206ebe6e1c47ac5027c2e31ecd39/pyarrow-21.0.0-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:a7a102574faa3f421141a64c10216e078df467ab9576684d5cd696952546e2da", size = 31290064 }, + { url = "https://files.pythonhosted.org/packages/3f/8a/5685d62a990e4cac2043fc76b4661bf38d06efed55cf45a334b455bd2759/pyarrow-21.0.0-cp313-cp313t-macosx_12_0_x86_64.whl", hash = "sha256:1e005378c4a2c6db3ada3ad4c217b381f6c886f0a80d6a316fe586b90f77efd7", size = 32727837 }, + { url = "https://files.pythonhosted.org/packages/fc/de/c0828ee09525c2bafefd3e736a248ebe764d07d0fd762d4f0929dbc516c9/pyarrow-21.0.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:65f8e85f79031449ec8706b74504a316805217b35b6099155dd7e227eef0d4b6", size = 41014158 }, + { url = "https://files.pythonhosted.org/packages/6e/26/a2865c420c50b7a3748320b614f3484bfcde8347b2639b2b903b21ce6a72/pyarrow-21.0.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:3a81486adc665c7eb1a2bde0224cfca6ceaba344a82a971ef059678417880eb8", size = 42667885 }, + { url = "https://files.pythonhosted.org/packages/0a/f9/4ee798dc902533159250fb4321267730bc0a107d8c6889e07c3add4fe3a5/pyarrow-21.0.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:fc0d2f88b81dcf3ccf9a6ae17f89183762c8a94a5bdcfa09e05cfe413acf0503", size = 43276625 }, + { url = "https://files.pythonhosted.org/packages/5a/da/e02544d6997037a4b0d22d8e5f66bc9315c3671371a8b18c79ade1cefe14/pyarrow-21.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6299449adf89df38537837487a4f8d3bd91ec94354fdd2a7d30bc11c48ef6e79", size = 44951890 }, + { url = "https://files.pythonhosted.org/packages/e5/4e/519c1bc1876625fe6b71e9a28287c43ec2f20f73c658b9ae1d485c0c206e/pyarrow-21.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:222c39e2c70113543982c6b34f3077962b44fca38c0bd9e68bb6781534425c10", size = 26371006 }, ] [[package]] @@ -2864,7 +3233,7 @@ wheels = [ [[package]] name = "pydantic" -version = "2.11.5" +version = "2.11.7" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-types" }, @@ -2872,9 +3241,9 @@ dependencies = [ { name = "typing-extensions" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f0/86/8ce9040065e8f924d642c58e4a344e33163a07f6b57f836d0d734e0ad3fb/pydantic-2.11.5.tar.gz", hash = "sha256:7f853db3d0ce78ce8bbb148c401c2cdd6431b3473c0cdff2755c7690952a7b7a", size = 787102 } +sdist = { url = "https://files.pythonhosted.org/packages/00/dd/4325abf92c39ba8623b5af936ddb36ffcfe0beae70405d456ab1fb2f5b8c/pydantic-2.11.7.tar.gz", hash = "sha256:d989c3c6cb79469287b1569f7447a17848c998458d49ebe294e975b9baf0f0db", size = 788350 } wheels = [ - { url = "https://files.pythonhosted.org/packages/b5/69/831ed22b38ff9b4b64b66569f0e5b7b97cf3638346eb95a2147fdb49ad5f/pydantic-2.11.5-py3-none-any.whl", hash = "sha256:f9c26ba06f9747749ca1e5c94d6a85cb84254577553c8785576fd38fa64dc0f7", size = 444229 }, + { url = "https://files.pythonhosted.org/packages/6a/c0/ec2b1c8712ca690e5d61979dee872603e92b8a32f94cc1b72d53beab008a/pydantic-2.11.7-py3-none-any.whl", hash = "sha256:dde5df002701f6de26248661f6835bbe296a47bf73990135c7d07ce741b9623b", size = 444782 }, ] [package.optional-dependencies] @@ -2971,16 +3340,16 @@ wheels = [ [[package]] name = "pydantic-settings" -version = "2.9.1" +version = "2.10.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pydantic" }, { name = "python-dotenv" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/67/1d/42628a2c33e93f8e9acbde0d5d735fa0850f3e6a2f8cb1eb6c40b9a732ac/pydantic_settings-2.9.1.tar.gz", hash = "sha256:c509bf79d27563add44e8446233359004ed85066cd096d8b510f715e6ef5d268", size = 163234 } +sdist = { url = "https://files.pythonhosted.org/packages/68/85/1ea668bbab3c50071ca613c6ab30047fb36ab0da1b92fa8f17bbc38fd36c/pydantic_settings-2.10.1.tar.gz", hash = "sha256:06f0062169818d0f5524420a360d632d5857b83cffd4d42fe29597807a1614ee", size = 172583 } wheels = [ - { url = "https://files.pythonhosted.org/packages/b6/5f/d6d641b490fd3ec2c4c13b4244d68deea3a1b970a97be64f34fb5504ff72/pydantic_settings-2.9.1-py3-none-any.whl", hash = "sha256:59b4f431b1defb26fe620c71a7d3968a710d719f5f4cdbbdb7926edeb770f6ef", size = 44356 }, + { url = "https://files.pythonhosted.org/packages/58/f0/427018098906416f580e3cf1366d3b1abfb408a0652e9f31600c24a1903c/pydantic_settings-2.10.1-py3-none-any.whl", hash = "sha256:a60952460b99cf661dc25c29c0ef171721f98bfcb52ef8d9ea4c943d7c8cc796", size = 45235 }, ] [[package]] @@ -3006,11 +3375,11 @@ wheels = [ [[package]] name = "pygments" -version = "2.19.1" +version = "2.19.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7c/2d/c3338d48ea6cc0feb8446d8e6937e1408088a72a39937982cc6111d17f84/pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f", size = 4968581 } +sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631 } wheels = [ - { url = "https://files.pythonhosted.org/packages/8a/0b/9fcc47d19c48b59121088dd6da2488a49d5f72dacf8262e2790a1d2c7d15/pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c", size = 1225293 }, + { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217 }, ] [[package]] @@ -3024,17 +3393,17 @@ wheels = [ [[package]] name = "pymupdf" -version = "1.26.0" +version = "1.26.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/99/13/d9f3b67b98111e79307e2a3cf6d5f73daaca002144cc6d236c4a0adbc386/pymupdf-1.26.0.tar.gz", hash = "sha256:ffe023f820379c84a0ddae38b0d07ea4016e1de84929491c34415520c629bcce", size = 74590906 } +sdist = { url = "https://files.pythonhosted.org/packages/6d/d4/70a265e4bcd43e97480ae62da69396ef4507c8f9cfd179005ee731c92a04/pymupdf-1.26.3.tar.gz", hash = "sha256:b7d2c3ffa9870e1e4416d18862f5ccd356af5fe337b4511093bbbce2ca73b7e5", size = 75990308 } wheels = [ - { url = "https://files.pythonhosted.org/packages/b0/c8/e43b872d807ade267699b383d04927e4a1cca05268a43aa9ae0382b6f66a/pymupdf-1.26.0-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:86195673a5f2b3fbf0f75c73c40975a893fc42bf3d5c2a7cce9fb160704d9997", size = 23240576 }, - { url = "https://files.pythonhosted.org/packages/ed/bb/2c39c2016b5be35372c5df08b44fa9a4778ccf03f9532b59e9d22f05921c/pymupdf-1.26.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:26c5df128a0ed7c38b80a1e7ddd792dd4d8b264839b6b8b6b6b768b13c0bb60e", size = 22409814 }, - { url = "https://files.pythonhosted.org/packages/90/e1/847d93fecbc975978cb2c6c38dbfbd09dde48c1bc97f9298f2b719880230/pymupdf-1.26.0-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:84a4dc96144a6bdf13f7d78c21500b3d1bef14d658156afd479acb3995f650c3", size = 23447163 }, - { url = "https://files.pythonhosted.org/packages/bb/86/959057f3cbbff4a7a00a8214f9f1787f6f708bd3c63091b47a95f1ae4053/pymupdf-1.26.0-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a3f6a45fcf8177763a2629a2ab2cad326e8950a0d120b174b56369365355a2a7", size = 24052460 }, - { url = "https://files.pythonhosted.org/packages/e1/80/9cca07bf9c5dfa273781d3559266dcce2001ed111f068b80cad950584c01/pymupdf-1.26.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b5d4751696ba888ab1a91c6a0208c5d31724ee0bebe406f7a83067a08214f88b", size = 25300434 }, - { url = "https://files.pythonhosted.org/packages/59/c7/2e3e3d9419415e3c479fd9cb23067d33cc17db89e64c9167551a4acb66f2/pymupdf-1.26.0-cp39-abi3-win32.whl", hash = "sha256:eeb04a439355e2077f7675b8ad9377263d81990fc507748f2254a87d771d682b", size = 16925774 }, - { url = "https://files.pythonhosted.org/packages/e9/d7/a6ce6629b66921529543c899830104f40dde45d98534d945970ea61ca8c9/pymupdf-1.26.0-cp39-abi3-win_amd64.whl", hash = "sha256:e39cc74ff030d773c4e76b9e5c5919cc4683895b73bd63bfd7a349a53ab5e8d7", size = 18525321 }, + { url = "https://files.pythonhosted.org/packages/70/d3/c7af70545cd3097a869fd635bb6222108d3a0fb28c0b8254754a126c4cbb/pymupdf-1.26.3-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ded891963944e5f13b03b88f6d9e982e816a4ec8689fe360876eef000c161f2b", size = 23057205 }, + { url = "https://files.pythonhosted.org/packages/04/3d/ec5b69bfeaa5deefa7141fc0b20d77bb20404507cf17196b4eb59f1f2977/pymupdf-1.26.3-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:436a33c738bb10eadf00395d18a6992b801ffb26521ee1f361ae786dd283327a", size = 22406630 }, + { url = "https://files.pythonhosted.org/packages/fc/20/661d3894bb05ad75ed6ca103ee2c3fa44d88a458b5c8d4a946b9c0f2569b/pymupdf-1.26.3-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:a2d7a3cd442f12f05103cb3bb1415111517f0a97162547a3720f3bbbc5e0b51c", size = 23450287 }, + { url = "https://files.pythonhosted.org/packages/9c/7f/21828f018e65b16a033731d21f7b46d93fa81c6e8257f769ca4a1c2a1cb0/pymupdf-1.26.3-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:454f38c8cf07eb333eb4646dca10517b6e90f57ce2daa2265a78064109d85555", size = 24057319 }, + { url = "https://files.pythonhosted.org/packages/71/5d/e8f88cd5a45b8f5fa6590ce8cef3ce0fad30eac6aac8aea12406f95bee7d/pymupdf-1.26.3-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:759b75d2f710ff4edf8d097d2e98f60e9ecef47632cead6f949b3412facdb9f0", size = 24261350 }, + { url = "https://files.pythonhosted.org/packages/82/22/ecc560e4f281b5dffafbf3a81f023d268b1746d028044f495115b74a2e70/pymupdf-1.26.3-cp39-abi3-win32.whl", hash = "sha256:a839ed44742faa1cd4956bb18068fe5aae435d67ce915e901318646c4e7bbea6", size = 17116371 }, + { url = "https://files.pythonhosted.org/packages/4a/26/8c72973b8833a72785cedc3981eb59b8ac7075942718bbb7b69b352cdde4/pymupdf-1.26.3-cp39-abi3-win_amd64.whl", hash = "sha256:b4cd5124d05737944636cf45fc37ce5824f10e707b0342efe109c7b6bd37a9cc", size = 18735124 }, ] [[package]] @@ -3057,9 +3426,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5e/22/d3db169895faaf3e2eda892f005f433a62db2decbcfbc2f61e6517adfa87/PyNaCl-1.5.0-cp36-abi3-win_amd64.whl", hash = "sha256:20f42270d27e1b6a29f54032090b972d97f0a1b0948cc52392041ef7831fee93", size = 212141 }, ] +[[package]] +name = "pyperclip" +version = "1.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/30/23/2f0a3efc4d6a32f3b63cdff36cd398d9701d26cda58e3ab97ac79fb5e60d/pyperclip-1.9.0.tar.gz", hash = "sha256:b7de0142ddc81bfc5c7507eea19da920b92252b548b96186caf94a5e2527d310", size = 20961 } + [[package]] name = "pytest" -version = "8.4.0" +version = "8.4.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, @@ -3070,21 +3445,22 @@ dependencies = [ { name = "pygments" }, { name = "tomli", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fb/aa/405082ce2749be5398045152251ac69c0f3578c7077efc53431303af97ce/pytest-8.4.0.tar.gz", hash = "sha256:14d920b48472ea0dbf68e45b96cd1ffda4705f33307dcc86c676c1b5104838a6", size = 1515232 } +sdist = { url = "https://files.pythonhosted.org/packages/08/ba/45911d754e8eba3d5a841a5ce61a65a685ff1798421ac054f85aa8747dfb/pytest-8.4.1.tar.gz", hash = "sha256:7c67fd69174877359ed9371ec3af8a3d2b04741818c51e5e99cc1742251fa93c", size = 1517714 } wheels = [ - { url = "https://files.pythonhosted.org/packages/2f/de/afa024cbe022b1b318a3d224125aa24939e99b4ff6f22e0ba639a2eaee47/pytest-8.4.0-py3-none-any.whl", hash = "sha256:f40f825768ad76c0977cbacdf1fd37c6f7a468e460ea6a0636078f8972d4517e", size = 363797 }, + { url = "https://files.pythonhosted.org/packages/29/16/c8a903f4c4dffe7a12843191437d7cd8e32751d5de349d45d3fe69544e87/pytest-8.4.1-py3-none-any.whl", hash = "sha256:539c70ba6fcead8e78eebbf1115e8b589e7565830d7d006a8723f19ac8a0afb7", size = 365474 }, ] [[package]] name = "pytest-asyncio" -version = "1.0.0" +version = "1.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ + { name = "backports-asyncio-runner", marker = "python_full_version < '3.11'" }, { name = "pytest" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d0/d4/14f53324cb1a6381bef29d698987625d80052bb33932d8e7cbf9b337b17c/pytest_asyncio-1.0.0.tar.gz", hash = "sha256:d15463d13f4456e1ead2594520216b225a16f781e144f8fdf6c5bb4667c48b3f", size = 46960 } +sdist = { url = "https://files.pythonhosted.org/packages/4e/51/f8794af39eeb870e87a8c8068642fc07bce0c854d6865d7dd0f2a9d338c2/pytest_asyncio-1.1.0.tar.gz", hash = "sha256:796aa822981e01b68c12e4827b8697108f7205020f24b5793b3c41555dab68ea", size = 46652 } wheels = [ - { url = "https://files.pythonhosted.org/packages/30/05/ce271016e351fddc8399e546f6e23761967ee09c8c568bbfbecb0c150171/pytest_asyncio-1.0.0-py3-none-any.whl", hash = "sha256:4f024da9f1ef945e680dc68610b52550e36590a67fd31bb3b4943979a1f90ef3", size = 15976 }, + { url = "https://files.pythonhosted.org/packages/c7/9d/bf86eddabf8c6c9cb1ea9a869d6873b46f105a5d292d3a6f7071f5b07935/pytest_asyncio-1.1.0-py3-none-any.whl", hash = "sha256:5fe2d69607b0bd75c656d1211f969cadba035030156745ee09e7d71740e58ecf", size = 15157 }, ] [[package]] @@ -3101,11 +3477,11 @@ wheels = [ [[package]] name = "python-dotenv" -version = "1.1.0" +version = "1.1.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/88/2c/7bb1416c5620485aa793f2de31d3df393d3686aa8a8506d11e10e13c5baf/python_dotenv-1.1.0.tar.gz", hash = "sha256:41f90bc6f5f177fb41f53e87666db362025010eb28f60a01c9143bfa33a2b2d5", size = 39920 } +sdist = { url = "https://files.pythonhosted.org/packages/f6/b0/4bc07ccd3572a2f9df7e6782f52b0c6c90dcbb803ac4a167702d7d0dfe1e/python_dotenv-1.1.1.tar.gz", hash = "sha256:a8a6399716257f45be6a007360200409fce5cda2661e3dec71d23dc15f6189ab", size = 41978 } wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/18/98a99ad95133c6a6e2005fe89faedf294a748bd5dc803008059409ac9b1e/python_dotenv-1.1.0-py3-none-any.whl", hash = "sha256:d7c01d9e2293916c18baf562d95698754b0dbbb5e74d457c45d4f6561fb9d55d", size = 20256 }, + { url = "https://files.pythonhosted.org/packages/5f/ed/539768cf28c661b5b068d66d96a2f155c4971a5d55684a514c1a0e0dec2f/python_dotenv-1.1.1-py3-none-any.whl", hash = "sha256:31f23644fe2602f88ff55e1f5c79ba497e01224ee7737937930c448e4d0e24dc", size = 20556 }, ] [[package]] @@ -3141,6 +3517,28 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/81/c4/34e93fe5f5429d7570ec1fa436f1986fb1f00c3e0f43a589fe2bbcd22c3f/pytz-2025.2-py2.py3-none-any.whl", hash = "sha256:5ddf76296dd8c44c26eb8f4b6f35488f3ccbf6fbbd7adee0b7262d43f0ec2f00", size = 509225 }, ] +[[package]] +name = "pywin32" +version = "311" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7b/40/44efbb0dfbd33aca6a6483191dae0716070ed99e2ecb0c53683f400a0b4f/pywin32-311-cp310-cp310-win32.whl", hash = "sha256:d03ff496d2a0cd4a5893504789d4a15399133fe82517455e78bad62efbb7f0a3", size = 8760432 }, + { url = "https://files.pythonhosted.org/packages/5e/bf/360243b1e953bd254a82f12653974be395ba880e7ec23e3731d9f73921cc/pywin32-311-cp310-cp310-win_amd64.whl", hash = "sha256:797c2772017851984b97180b0bebe4b620bb86328e8a884bb626156295a63b3b", size = 9590103 }, + { url = "https://files.pythonhosted.org/packages/57/38/d290720e6f138086fb3d5ffe0b6caa019a791dd57866940c82e4eeaf2012/pywin32-311-cp310-cp310-win_arm64.whl", hash = "sha256:0502d1facf1fed4839a9a51ccbcc63d952cf318f78ffc00a7e78528ac27d7a2b", size = 8778557 }, + { url = "https://files.pythonhosted.org/packages/7c/af/449a6a91e5d6db51420875c54f6aff7c97a86a3b13a0b4f1a5c13b988de3/pywin32-311-cp311-cp311-win32.whl", hash = "sha256:184eb5e436dea364dcd3d2316d577d625c0351bf237c4e9a5fabbcfa5a58b151", size = 8697031 }, + { url = "https://files.pythonhosted.org/packages/51/8f/9bb81dd5bb77d22243d33c8397f09377056d5c687aa6d4042bea7fbf8364/pywin32-311-cp311-cp311-win_amd64.whl", hash = "sha256:3ce80b34b22b17ccbd937a6e78e7225d80c52f5ab9940fe0506a1a16f3dab503", size = 9508308 }, + { url = "https://files.pythonhosted.org/packages/44/7b/9c2ab54f74a138c491aba1b1cd0795ba61f144c711daea84a88b63dc0f6c/pywin32-311-cp311-cp311-win_arm64.whl", hash = "sha256:a733f1388e1a842abb67ffa8e7aad0e70ac519e09b0f6a784e65a136ec7cefd2", size = 8703930 }, + { url = "https://files.pythonhosted.org/packages/e7/ab/01ea1943d4eba0f850c3c61e78e8dd59757ff815ff3ccd0a84de5f541f42/pywin32-311-cp312-cp312-win32.whl", hash = "sha256:750ec6e621af2b948540032557b10a2d43b0cee2ae9758c54154d711cc852d31", size = 8706543 }, + { url = "https://files.pythonhosted.org/packages/d1/a8/a0e8d07d4d051ec7502cd58b291ec98dcc0c3fff027caad0470b72cfcc2f/pywin32-311-cp312-cp312-win_amd64.whl", hash = "sha256:b8c095edad5c211ff31c05223658e71bf7116daa0ecf3ad85f3201ea3190d067", size = 9495040 }, + { url = "https://files.pythonhosted.org/packages/ba/3a/2ae996277b4b50f17d61f0603efd8253cb2d79cc7ae159468007b586396d/pywin32-311-cp312-cp312-win_arm64.whl", hash = "sha256:e286f46a9a39c4a18b319c28f59b61de793654af2f395c102b4f819e584b5852", size = 8710102 }, + { url = "https://files.pythonhosted.org/packages/a5/be/3fd5de0979fcb3994bfee0d65ed8ca9506a8a1260651b86174f6a86f52b3/pywin32-311-cp313-cp313-win32.whl", hash = "sha256:f95ba5a847cba10dd8c4d8fefa9f2a6cf283b8b88ed6178fa8a6c1ab16054d0d", size = 8705700 }, + { url = "https://files.pythonhosted.org/packages/e3/28/e0a1909523c6890208295a29e05c2adb2126364e289826c0a8bc7297bd5c/pywin32-311-cp313-cp313-win_amd64.whl", hash = "sha256:718a38f7e5b058e76aee1c56ddd06908116d35147e133427e59a3983f703a20d", size = 9494700 }, + { url = "https://files.pythonhosted.org/packages/04/bf/90339ac0f55726dce7d794e6d79a18a91265bdf3aa70b6b9ca52f35e022a/pywin32-311-cp313-cp313-win_arm64.whl", hash = "sha256:7b4075d959648406202d92a2310cb990fea19b535c7f4a78d3f5e10b926eeb8a", size = 8709318 }, + { url = "https://files.pythonhosted.org/packages/c9/31/097f2e132c4f16d99a22bfb777e0fd88bd8e1c634304e102f313af69ace5/pywin32-311-cp314-cp314-win32.whl", hash = "sha256:b7a2c10b93f8986666d0c803ee19b5990885872a7de910fc460f9b0c2fbf92ee", size = 8840714 }, + { url = "https://files.pythonhosted.org/packages/90/4b/07c77d8ba0e01349358082713400435347df8426208171ce297da32c313d/pywin32-311-cp314-cp314-win_amd64.whl", hash = "sha256:3aca44c046bd2ed8c90de9cb8427f581c479e594e99b5c0bb19b29c10fd6cb87", size = 9656800 }, + { url = "https://files.pythonhosted.org/packages/c0/d2/21af5c535501a7233e734b8af901574572da66fcc254cb35d0609c9080dd/pywin32-311-cp314-cp314-win_arm64.whl", hash = "sha256:a508e2d9025764a8270f93111a970e1d0fbfc33f4153b388bb649b7eec4f9b42", size = 8932540 }, +] + [[package]] name = "pyyaml" version = "6.0.2" @@ -3325,127 +3723,168 @@ wheels = [ [[package]] name = "rich" -version = "13.7.1" +version = "14.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markdown-it-py" }, { name = "pygments" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b3/01/c954e134dc440ab5f96952fe52b4fdc64225530320a910473c1fe270d9aa/rich-13.7.1.tar.gz", hash = "sha256:9be308cb1fe2f1f57d67ce99e95af38a1e2bc71ad9813b0e247cf7ffbcc3a432", size = 221248 } +sdist = { url = "https://files.pythonhosted.org/packages/a1/53/830aa4c3066a8ab0ae9a9955976fb770fe9c6102117c8ec4ab3ea62d89e8/rich-14.0.0.tar.gz", hash = "sha256:82f1bc23a6a21ebca4ae0c45af9bdbc492ed20231dcb63f297d6d1021a9d5725", size = 224078 } wheels = [ - { url = "https://files.pythonhosted.org/packages/87/67/a37f6214d0e9fe57f6ae54b2956d550ca8365857f42a1ce0392bb21d9410/rich-13.7.1-py3-none-any.whl", hash = "sha256:4edbae314f59eb482f54e9e30bf00d33350aaa94f4bfcd4e9e3110e64d0d7222", size = 240681 }, + { url = "https://files.pythonhosted.org/packages/0d/9b/63f4c7ebc259242c89b3acafdb37b41d1185c07ff0011164674e9076b491/rich-14.0.0-py3-none-any.whl", hash = "sha256:1c9491e1951aac09caffd42f448ee3d04e58923ffe14993f6e83068dc395d7e0", size = 243229 }, ] [[package]] -name = "rpds-py" -version = "0.25.1" +name = "rich-rst" +version = "1.3.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8c/a6/60184b7fc00dd3ca80ac635dd5b8577d444c57e8e8742cecabfacb829921/rpds_py-0.25.1.tar.gz", hash = "sha256:8960b6dac09b62dac26e75d7e2c4a22efb835d827a7278c34f72b2b84fa160e3", size = 27304 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/09/e1158988e50905b7f8306487a576b52d32aa9a87f79f7ab24ee8db8b6c05/rpds_py-0.25.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:f4ad628b5174d5315761b67f212774a32f5bad5e61396d38108bd801c0a8f5d9", size = 373140 }, - { url = "https://files.pythonhosted.org/packages/e0/4b/a284321fb3c45c02fc74187171504702b2934bfe16abab89713eedfe672e/rpds_py-0.25.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8c742af695f7525e559c16f1562cf2323db0e3f0fbdcabdf6865b095256b2d40", size = 358860 }, - { url = "https://files.pythonhosted.org/packages/4e/46/8ac9811150c75edeae9fc6fa0e70376c19bc80f8e1f7716981433905912b/rpds_py-0.25.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:605ffe7769e24b1800b4d024d24034405d9404f0bc2f55b6db3362cd34145a6f", size = 386179 }, - { url = "https://files.pythonhosted.org/packages/f3/ec/87eb42d83e859bce91dcf763eb9f2ab117142a49c9c3d17285440edb5b69/rpds_py-0.25.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ccc6f3ddef93243538be76f8e47045b4aad7a66a212cd3a0f23e34469473d36b", size = 400282 }, - { url = "https://files.pythonhosted.org/packages/68/c8/2a38e0707d7919c8c78e1d582ab15cf1255b380bcb086ca265b73ed6db23/rpds_py-0.25.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f70316f760174ca04492b5ab01be631a8ae30cadab1d1081035136ba12738cfa", size = 521824 }, - { url = "https://files.pythonhosted.org/packages/5e/2c/6a92790243569784dde84d144bfd12bd45102f4a1c897d76375076d730ab/rpds_py-0.25.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e1dafef8df605fdb46edcc0bf1573dea0d6d7b01ba87f85cd04dc855b2b4479e", size = 411644 }, - { url = "https://files.pythonhosted.org/packages/eb/76/66b523ffc84cf47db56efe13ae7cf368dee2bacdec9d89b9baca5e2e6301/rpds_py-0.25.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0701942049095741a8aeb298a31b203e735d1c61f4423511d2b1a41dcd8a16da", size = 386955 }, - { url = "https://files.pythonhosted.org/packages/b6/b9/a362d7522feaa24dc2b79847c6175daa1c642817f4a19dcd5c91d3e2c316/rpds_py-0.25.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e87798852ae0b37c88babb7f7bbbb3e3fecc562a1c340195b44c7e24d403e380", size = 421039 }, - { url = "https://files.pythonhosted.org/packages/0f/c4/b5b6f70b4d719b6584716889fd3413102acf9729540ee76708d56a76fa97/rpds_py-0.25.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3bcce0edc1488906c2d4c75c94c70a0417e83920dd4c88fec1078c94843a6ce9", size = 563290 }, - { url = "https://files.pythonhosted.org/packages/87/a3/2e6e816615c12a8f8662c9d8583a12eb54c52557521ef218cbe3095a8afa/rpds_py-0.25.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e2f6a2347d3440ae789505693a02836383426249d5293541cd712e07e7aecf54", size = 592089 }, - { url = "https://files.pythonhosted.org/packages/c0/08/9b8e1050e36ce266135994e2c7ec06e1841f1c64da739daeb8afe9cb77a4/rpds_py-0.25.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:4fd52d3455a0aa997734f3835cbc4c9f32571345143960e7d7ebfe7b5fbfa3b2", size = 558400 }, - { url = "https://files.pythonhosted.org/packages/f2/df/b40b8215560b8584baccd839ff5c1056f3c57120d79ac41bd26df196da7e/rpds_py-0.25.1-cp310-cp310-win32.whl", hash = "sha256:3f0b1798cae2bbbc9b9db44ee068c556d4737911ad53a4e5093d09d04b3bbc24", size = 219741 }, - { url = "https://files.pythonhosted.org/packages/10/99/e4c58be18cf5d8b40b8acb4122bc895486230b08f978831b16a3916bd24d/rpds_py-0.25.1-cp310-cp310-win_amd64.whl", hash = "sha256:3ebd879ab996537fc510a2be58c59915b5dd63bccb06d1ef514fee787e05984a", size = 231553 }, - { url = "https://files.pythonhosted.org/packages/95/e1/df13fe3ddbbea43567e07437f097863b20c99318ae1f58a0fe389f763738/rpds_py-0.25.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:5f048bbf18b1f9120685c6d6bb70cc1a52c8cc11bdd04e643d28d3be0baf666d", size = 373341 }, - { url = "https://files.pythonhosted.org/packages/7a/58/deef4d30fcbcbfef3b6d82d17c64490d5c94585a2310544ce8e2d3024f83/rpds_py-0.25.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4fbb0dbba559959fcb5d0735a0f87cdbca9e95dac87982e9b95c0f8f7ad10255", size = 359111 }, - { url = "https://files.pythonhosted.org/packages/bb/7e/39f1f4431b03e96ebaf159e29a0f82a77259d8f38b2dd474721eb3a8ac9b/rpds_py-0.25.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4ca54b9cf9d80b4016a67a0193ebe0bcf29f6b0a96f09db942087e294d3d4c2", size = 386112 }, - { url = "https://files.pythonhosted.org/packages/db/e7/847068a48d63aec2ae695a1646089620b3b03f8ccf9f02c122ebaf778f3c/rpds_py-0.25.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1ee3e26eb83d39b886d2cb6e06ea701bba82ef30a0de044d34626ede51ec98b0", size = 400362 }, - { url = "https://files.pythonhosted.org/packages/3b/3d/9441d5db4343d0cee759a7ab4d67420a476cebb032081763de934719727b/rpds_py-0.25.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:89706d0683c73a26f76a5315d893c051324d771196ae8b13e6ffa1ffaf5e574f", size = 522214 }, - { url = "https://files.pythonhosted.org/packages/a2/ec/2cc5b30d95f9f1a432c79c7a2f65d85e52812a8f6cbf8768724571710786/rpds_py-0.25.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c2013ee878c76269c7b557a9a9c042335d732e89d482606990b70a839635feb7", size = 411491 }, - { url = "https://files.pythonhosted.org/packages/dc/6c/44695c1f035077a017dd472b6a3253553780837af2fac9b6ac25f6a5cb4d/rpds_py-0.25.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45e484db65e5380804afbec784522de84fa95e6bb92ef1bd3325d33d13efaebd", size = 386978 }, - { url = "https://files.pythonhosted.org/packages/b1/74/b4357090bb1096db5392157b4e7ed8bb2417dc7799200fcbaee633a032c9/rpds_py-0.25.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:48d64155d02127c249695abb87d39f0faf410733428d499867606be138161d65", size = 420662 }, - { url = "https://files.pythonhosted.org/packages/26/dd/8cadbebf47b96e59dfe8b35868e5c38a42272699324e95ed522da09d3a40/rpds_py-0.25.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:048893e902132fd6548a2e661fb38bf4896a89eea95ac5816cf443524a85556f", size = 563385 }, - { url = "https://files.pythonhosted.org/packages/c3/ea/92960bb7f0e7a57a5ab233662f12152085c7dc0d5468534c65991a3d48c9/rpds_py-0.25.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:0317177b1e8691ab5879f4f33f4b6dc55ad3b344399e23df2e499de7b10a548d", size = 592047 }, - { url = "https://files.pythonhosted.org/packages/61/ad/71aabc93df0d05dabcb4b0c749277881f8e74548582d96aa1bf24379493a/rpds_py-0.25.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bffcf57826d77a4151962bf1701374e0fc87f536e56ec46f1abdd6a903354042", size = 557863 }, - { url = "https://files.pythonhosted.org/packages/93/0f/89df0067c41f122b90b76f3660028a466eb287cbe38efec3ea70e637ca78/rpds_py-0.25.1-cp311-cp311-win32.whl", hash = "sha256:cda776f1967cb304816173b30994faaf2fd5bcb37e73118a47964a02c348e1bc", size = 219627 }, - { url = "https://files.pythonhosted.org/packages/7c/8d/93b1a4c1baa903d0229374d9e7aa3466d751f1d65e268c52e6039c6e338e/rpds_py-0.25.1-cp311-cp311-win_amd64.whl", hash = "sha256:dc3c1ff0abc91444cd20ec643d0f805df9a3661fcacf9c95000329f3ddf268a4", size = 231603 }, - { url = "https://files.pythonhosted.org/packages/cb/11/392605e5247bead2f23e6888e77229fbd714ac241ebbebb39a1e822c8815/rpds_py-0.25.1-cp311-cp311-win_arm64.whl", hash = "sha256:5a3ddb74b0985c4387719fc536faced33cadf2172769540c62e2a94b7b9be1c4", size = 223967 }, - { url = "https://files.pythonhosted.org/packages/7f/81/28ab0408391b1dc57393653b6a0cf2014cc282cc2909e4615e63e58262be/rpds_py-0.25.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:b5ffe453cde61f73fea9430223c81d29e2fbf412a6073951102146c84e19e34c", size = 364647 }, - { url = "https://files.pythonhosted.org/packages/2c/9a/7797f04cad0d5e56310e1238434f71fc6939d0bc517192a18bb99a72a95f/rpds_py-0.25.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:115874ae5e2fdcfc16b2aedc95b5eef4aebe91b28e7e21951eda8a5dc0d3461b", size = 350454 }, - { url = "https://files.pythonhosted.org/packages/69/3c/93d2ef941b04898011e5d6eaa56a1acf46a3b4c9f4b3ad1bbcbafa0bee1f/rpds_py-0.25.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a714bf6e5e81b0e570d01f56e0c89c6375101b8463999ead3a93a5d2a4af91fa", size = 389665 }, - { url = "https://files.pythonhosted.org/packages/c1/57/ad0e31e928751dde8903a11102559628d24173428a0f85e25e187defb2c1/rpds_py-0.25.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:35634369325906bcd01577da4c19e3b9541a15e99f31e91a02d010816b49bfda", size = 403873 }, - { url = "https://files.pythonhosted.org/packages/16/ad/c0c652fa9bba778b4f54980a02962748479dc09632e1fd34e5282cf2556c/rpds_py-0.25.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d4cb2b3ddc16710548801c6fcc0cfcdeeff9dafbc983f77265877793f2660309", size = 525866 }, - { url = "https://files.pythonhosted.org/packages/2a/39/3e1839bc527e6fcf48d5fec4770070f872cdee6c6fbc9b259932f4e88a38/rpds_py-0.25.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9ceca1cf097ed77e1a51f1dbc8d174d10cb5931c188a4505ff9f3e119dfe519b", size = 416886 }, - { url = "https://files.pythonhosted.org/packages/7a/95/dd6b91cd4560da41df9d7030a038298a67d24f8ca38e150562644c829c48/rpds_py-0.25.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c2cd1a4b0c2b8c5e31ffff50d09f39906fe351389ba143c195566056c13a7ea", size = 390666 }, - { url = "https://files.pythonhosted.org/packages/64/48/1be88a820e7494ce0a15c2d390ccb7c52212370badabf128e6a7bb4cb802/rpds_py-0.25.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1de336a4b164c9188cb23f3703adb74a7623ab32d20090d0e9bf499a2203ad65", size = 425109 }, - { url = "https://files.pythonhosted.org/packages/cf/07/3e2a17927ef6d7720b9949ec1b37d1e963b829ad0387f7af18d923d5cfa5/rpds_py-0.25.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9fca84a15333e925dd59ce01da0ffe2ffe0d6e5d29a9eeba2148916d1824948c", size = 567244 }, - { url = "https://files.pythonhosted.org/packages/d2/e5/76cf010998deccc4f95305d827847e2eae9c568099c06b405cf96384762b/rpds_py-0.25.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:88ec04afe0c59fa64e2f6ea0dd9657e04fc83e38de90f6de201954b4d4eb59bd", size = 596023 }, - { url = "https://files.pythonhosted.org/packages/52/9a/df55efd84403736ba37a5a6377b70aad0fd1cb469a9109ee8a1e21299a1c/rpds_py-0.25.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a8bd2f19e312ce3e1d2c635618e8a8d8132892bb746a7cf74780a489f0f6cdcb", size = 561634 }, - { url = "https://files.pythonhosted.org/packages/ab/aa/dc3620dd8db84454aaf9374bd318f1aa02578bba5e567f5bf6b79492aca4/rpds_py-0.25.1-cp312-cp312-win32.whl", hash = "sha256:e5e2f7280d8d0d3ef06f3ec1b4fd598d386cc6f0721e54f09109a8132182fbfe", size = 222713 }, - { url = "https://files.pythonhosted.org/packages/a3/7f/7cef485269a50ed5b4e9bae145f512d2a111ca638ae70cc101f661b4defd/rpds_py-0.25.1-cp312-cp312-win_amd64.whl", hash = "sha256:db58483f71c5db67d643857404da360dce3573031586034b7d59f245144cc192", size = 235280 }, - { url = "https://files.pythonhosted.org/packages/99/f2/c2d64f6564f32af913bf5f3f7ae41c7c263c5ae4c4e8f1a17af8af66cd46/rpds_py-0.25.1-cp312-cp312-win_arm64.whl", hash = "sha256:6d50841c425d16faf3206ddbba44c21aa3310a0cebc3c1cdfc3e3f4f9f6f5728", size = 225399 }, - { url = "https://files.pythonhosted.org/packages/2b/da/323848a2b62abe6a0fec16ebe199dc6889c5d0a332458da8985b2980dffe/rpds_py-0.25.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:659d87430a8c8c704d52d094f5ba6fa72ef13b4d385b7e542a08fc240cb4a559", size = 364498 }, - { url = "https://files.pythonhosted.org/packages/1f/b4/4d3820f731c80fd0cd823b3e95b9963fec681ae45ba35b5281a42382c67d/rpds_py-0.25.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:68f6f060f0bbdfb0245267da014d3a6da9be127fe3e8cc4a68c6f833f8a23bb1", size = 350083 }, - { url = "https://files.pythonhosted.org/packages/d5/b1/3a8ee1c9d480e8493619a437dec685d005f706b69253286f50f498cbdbcf/rpds_py-0.25.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:083a9513a33e0b92cf6e7a6366036c6bb43ea595332c1ab5c8ae329e4bcc0a9c", size = 389023 }, - { url = "https://files.pythonhosted.org/packages/3b/31/17293edcfc934dc62c3bf74a0cb449ecd549531f956b72287203e6880b87/rpds_py-0.25.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:816568614ecb22b18a010c7a12559c19f6fe993526af88e95a76d5a60b8b75fb", size = 403283 }, - { url = "https://files.pythonhosted.org/packages/d1/ca/e0f0bc1a75a8925024f343258c8ecbd8828f8997ea2ac71e02f67b6f5299/rpds_py-0.25.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3c6564c0947a7f52e4792983f8e6cf9bac140438ebf81f527a21d944f2fd0a40", size = 524634 }, - { url = "https://files.pythonhosted.org/packages/3e/03/5d0be919037178fff33a6672ffc0afa04ea1cfcb61afd4119d1b5280ff0f/rpds_py-0.25.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c4a128527fe415d73cf1f70a9a688d06130d5810be69f3b553bf7b45e8acf79", size = 416233 }, - { url = "https://files.pythonhosted.org/packages/05/7c/8abb70f9017a231c6c961a8941403ed6557664c0913e1bf413cbdc039e75/rpds_py-0.25.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a49e1d7a4978ed554f095430b89ecc23f42014a50ac385eb0c4d163ce213c325", size = 390375 }, - { url = "https://files.pythonhosted.org/packages/7a/ac/a87f339f0e066b9535074a9f403b9313fd3892d4a164d5d5f5875ac9f29f/rpds_py-0.25.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d74ec9bc0e2feb81d3f16946b005748119c0f52a153f6db6a29e8cd68636f295", size = 424537 }, - { url = "https://files.pythonhosted.org/packages/1f/8f/8d5c1567eaf8c8afe98a838dd24de5013ce6e8f53a01bd47fe8bb06b5533/rpds_py-0.25.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3af5b4cc10fa41e5bc64e5c198a1b2d2864337f8fcbb9a67e747e34002ce812b", size = 566425 }, - { url = "https://files.pythonhosted.org/packages/95/33/03016a6be5663b389c8ab0bbbcca68d9e96af14faeff0a04affcb587e776/rpds_py-0.25.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:79dc317a5f1c51fd9c6a0c4f48209c6b8526d0524a6904fc1076476e79b00f98", size = 595197 }, - { url = "https://files.pythonhosted.org/packages/33/8d/da9f4d3e208c82fda311bff0cf0a19579afceb77cf456e46c559a1c075ba/rpds_py-0.25.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1521031351865e0181bc585147624d66b3b00a84109b57fcb7a779c3ec3772cd", size = 561244 }, - { url = "https://files.pythonhosted.org/packages/e2/b3/39d5dcf7c5f742ecd6dbc88f6f84ae54184b92f5f387a4053be2107b17f1/rpds_py-0.25.1-cp313-cp313-win32.whl", hash = "sha256:5d473be2b13600b93a5675d78f59e63b51b1ba2d0476893415dfbb5477e65b31", size = 222254 }, - { url = "https://files.pythonhosted.org/packages/5f/19/2d6772c8eeb8302c5f834e6d0dfd83935a884e7c5ce16340c7eaf89ce925/rpds_py-0.25.1-cp313-cp313-win_amd64.whl", hash = "sha256:a7b74e92a3b212390bdce1d93da9f6488c3878c1d434c5e751cbc202c5e09500", size = 234741 }, - { url = "https://files.pythonhosted.org/packages/5b/5a/145ada26cfaf86018d0eb304fe55eafdd4f0b6b84530246bb4a7c4fb5c4b/rpds_py-0.25.1-cp313-cp313-win_arm64.whl", hash = "sha256:dd326a81afe332ede08eb39ab75b301d5676802cdffd3a8f287a5f0b694dc3f5", size = 224830 }, - { url = "https://files.pythonhosted.org/packages/4b/ca/d435844829c384fd2c22754ff65889c5c556a675d2ed9eb0e148435c6690/rpds_py-0.25.1-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:a58d1ed49a94d4183483a3ce0af22f20318d4a1434acee255d683ad90bf78129", size = 359668 }, - { url = "https://files.pythonhosted.org/packages/1f/01/b056f21db3a09f89410d493d2f6614d87bb162499f98b649d1dbd2a81988/rpds_py-0.25.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:f251bf23deb8332823aef1da169d5d89fa84c89f67bdfb566c49dea1fccfd50d", size = 345649 }, - { url = "https://files.pythonhosted.org/packages/e0/0f/e0d00dc991e3d40e03ca36383b44995126c36b3eafa0ccbbd19664709c88/rpds_py-0.25.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8dbd586bfa270c1103ece2109314dd423df1fa3d9719928b5d09e4840cec0d72", size = 384776 }, - { url = "https://files.pythonhosted.org/packages/9f/a2/59374837f105f2ca79bde3c3cd1065b2f8c01678900924949f6392eab66d/rpds_py-0.25.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6d273f136e912aa101a9274c3145dcbddbe4bac560e77e6d5b3c9f6e0ed06d34", size = 395131 }, - { url = "https://files.pythonhosted.org/packages/9c/dc/48e8d84887627a0fe0bac53f0b4631e90976fd5d35fff8be66b8e4f3916b/rpds_py-0.25.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:666fa7b1bd0a3810a7f18f6d3a25ccd8866291fbbc3c9b912b917a6715874bb9", size = 520942 }, - { url = "https://files.pythonhosted.org/packages/7c/f5/ee056966aeae401913d37befeeab57a4a43a4f00099e0a20297f17b8f00c/rpds_py-0.25.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:921954d7fbf3fccc7de8f717799304b14b6d9a45bbeec5a8d7408ccbf531faf5", size = 411330 }, - { url = "https://files.pythonhosted.org/packages/ab/74/b2cffb46a097cefe5d17f94ede7a174184b9d158a0aeb195f39f2c0361e8/rpds_py-0.25.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3d86373ff19ca0441ebeb696ef64cb58b8b5cbacffcda5a0ec2f3911732a194", size = 387339 }, - { url = "https://files.pythonhosted.org/packages/7f/9a/0ff0b375dcb5161c2b7054e7d0b7575f1680127505945f5cabaac890bc07/rpds_py-0.25.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c8980cde3bb8575e7c956a530f2c217c1d6aac453474bf3ea0f9c89868b531b6", size = 418077 }, - { url = "https://files.pythonhosted.org/packages/0d/a1/fda629bf20d6b698ae84c7c840cfb0e9e4200f664fc96e1f456f00e4ad6e/rpds_py-0.25.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:8eb8c84ecea987a2523e057c0d950bcb3f789696c0499290b8d7b3107a719d78", size = 562441 }, - { url = "https://files.pythonhosted.org/packages/20/15/ce4b5257f654132f326f4acd87268e1006cc071e2c59794c5bdf4bebbb51/rpds_py-0.25.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:e43a005671a9ed5a650f3bc39e4dbccd6d4326b24fb5ea8be5f3a43a6f576c72", size = 590750 }, - { url = "https://files.pythonhosted.org/packages/fb/ab/e04bf58a8d375aeedb5268edcc835c6a660ebf79d4384d8e0889439448b0/rpds_py-0.25.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:58f77c60956501a4a627749a6dcb78dac522f249dd96b5c9f1c6af29bfacfb66", size = 558891 }, - { url = "https://files.pythonhosted.org/packages/90/82/cb8c6028a6ef6cd2b7991e2e4ced01c854b6236ecf51e81b64b569c43d73/rpds_py-0.25.1-cp313-cp313t-win32.whl", hash = "sha256:2cb9e5b5e26fc02c8a4345048cd9998c2aca7c2712bd1b36da0c72ee969a3523", size = 218718 }, - { url = "https://files.pythonhosted.org/packages/b6/97/5a4b59697111c89477d20ba8a44df9ca16b41e737fa569d5ae8bff99e650/rpds_py-0.25.1-cp313-cp313t-win_amd64.whl", hash = "sha256:401ca1c4a20cc0510d3435d89c069fe0a9ae2ee6495135ac46bdd49ec0495763", size = 232218 }, - { url = "https://files.pythonhosted.org/packages/78/ff/566ce53529b12b4f10c0a348d316bd766970b7060b4fd50f888be3b3b281/rpds_py-0.25.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b24bf3cd93d5b6ecfbedec73b15f143596c88ee249fa98cefa9a9dc9d92c6f28", size = 373931 }, - { url = "https://files.pythonhosted.org/packages/83/5d/deba18503f7c7878e26aa696e97f051175788e19d5336b3b0e76d3ef9256/rpds_py-0.25.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:0eb90e94f43e5085623932b68840b6f379f26db7b5c2e6bcef3179bd83c9330f", size = 359074 }, - { url = "https://files.pythonhosted.org/packages/0d/74/313415c5627644eb114df49c56a27edba4d40cfd7c92bd90212b3604ca84/rpds_py-0.25.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d50e4864498a9ab639d6d8854b25e80642bd362ff104312d9770b05d66e5fb13", size = 387255 }, - { url = "https://files.pythonhosted.org/packages/8c/c8/c723298ed6338963d94e05c0f12793acc9b91d04ed7c4ba7508e534b7385/rpds_py-0.25.1-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7c9409b47ba0650544b0bb3c188243b83654dfe55dcc173a86832314e1a6a35d", size = 400714 }, - { url = "https://files.pythonhosted.org/packages/33/8a/51f1f6aa653c2e110ed482ef2ae94140d56c910378752a1b483af11019ee/rpds_py-0.25.1-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:796ad874c89127c91970652a4ee8b00d56368b7e00d3477f4415fe78164c8000", size = 523105 }, - { url = "https://files.pythonhosted.org/packages/c7/a4/7873d15c088ad3bff36910b29ceb0f178e4b3232c2adbe9198de68a41e63/rpds_py-0.25.1-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:85608eb70a659bf4c1142b2781083d4b7c0c4e2c90eff11856a9754e965b2540", size = 411499 }, - { url = "https://files.pythonhosted.org/packages/90/f3/0ce1437befe1410766d11d08239333ac1b2d940f8a64234ce48a7714669c/rpds_py-0.25.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4feb9211d15d9160bc85fa72fed46432cdc143eb9cf6d5ca377335a921ac37b", size = 387918 }, - { url = "https://files.pythonhosted.org/packages/94/d4/5551247988b2a3566afb8a9dba3f1d4a3eea47793fd83000276c1a6c726e/rpds_py-0.25.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ccfa689b9246c48947d31dd9d8b16d89a0ecc8e0e26ea5253068efb6c542b76e", size = 421705 }, - { url = "https://files.pythonhosted.org/packages/b0/25/5960f28f847bf736cc7ee3c545a7e1d2f3b5edaf82c96fb616c2f5ed52d0/rpds_py-0.25.1-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:3c5b317ecbd8226887994852e85de562f7177add602514d4ac40f87de3ae45a8", size = 564489 }, - { url = "https://files.pythonhosted.org/packages/02/66/1c99884a0d44e8c2904d3c4ec302f995292d5dde892c3bf7685ac1930146/rpds_py-0.25.1-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:454601988aab2c6e8fd49e7634c65476b2b919647626208e376afcd22019eeb8", size = 592557 }, - { url = "https://files.pythonhosted.org/packages/55/ae/4aeac84ebeffeac14abb05b3bb1d2f728d00adb55d3fb7b51c9fa772e760/rpds_py-0.25.1-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:1c0c434a53714358532d13539272db75a5ed9df75a4a090a753ac7173ec14e11", size = 558691 }, - { url = "https://files.pythonhosted.org/packages/41/b3/728a08ff6f5e06fe3bb9af2e770e9d5fd20141af45cff8dfc62da4b2d0b3/rpds_py-0.25.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:f73ce1512e04fbe2bc97836e89830d6b4314c171587a99688082d090f934d20a", size = 231651 }, - { url = "https://files.pythonhosted.org/packages/49/74/48f3df0715a585cbf5d34919c9c757a4c92c1a9eba059f2d334e72471f70/rpds_py-0.25.1-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:ee86d81551ec68a5c25373c5643d343150cc54672b5e9a0cafc93c1870a53954", size = 374208 }, - { url = "https://files.pythonhosted.org/packages/55/b0/9b01bb11ce01ec03d05e627249cc2c06039d6aa24ea5a22a39c312167c10/rpds_py-0.25.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:89c24300cd4a8e4a51e55c31a8ff3918e6651b241ee8876a42cc2b2a078533ba", size = 359262 }, - { url = "https://files.pythonhosted.org/packages/a9/eb/5395621618f723ebd5116c53282052943a726dba111b49cd2071f785b665/rpds_py-0.25.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:771c16060ff4e79584dc48902a91ba79fd93eade3aa3a12d6d2a4aadaf7d542b", size = 387366 }, - { url = "https://files.pythonhosted.org/packages/68/73/3d51442bdb246db619d75039a50ea1cf8b5b4ee250c3e5cd5c3af5981cd4/rpds_py-0.25.1-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:785ffacd0ee61c3e60bdfde93baa6d7c10d86f15655bd706c89da08068dc5038", size = 400759 }, - { url = "https://files.pythonhosted.org/packages/b7/4c/3a32d5955d7e6cb117314597bc0f2224efc798428318b13073efe306512a/rpds_py-0.25.1-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2a40046a529cc15cef88ac5ab589f83f739e2d332cb4d7399072242400ed68c9", size = 523128 }, - { url = "https://files.pythonhosted.org/packages/be/95/1ffccd3b0bb901ae60b1dd4b1be2ab98bb4eb834cd9b15199888f5702f7b/rpds_py-0.25.1-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:85fc223d9c76cabe5d0bff82214459189720dc135db45f9f66aa7cffbf9ff6c1", size = 411597 }, - { url = "https://files.pythonhosted.org/packages/ef/6d/6e6cd310180689db8b0d2de7f7d1eabf3fb013f239e156ae0d5a1a85c27f/rpds_py-0.25.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b0be9965f93c222fb9b4cc254235b3b2b215796c03ef5ee64f995b1b69af0762", size = 388053 }, - { url = "https://files.pythonhosted.org/packages/4a/87/ec4186b1fe6365ced6fa470960e68fc7804bafbe7c0cf5a36237aa240efa/rpds_py-0.25.1-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8378fa4a940f3fb509c081e06cb7f7f2adae8cf46ef258b0e0ed7519facd573e", size = 421821 }, - { url = "https://files.pythonhosted.org/packages/7a/60/84f821f6bf4e0e710acc5039d91f8f594fae0d93fc368704920d8971680d/rpds_py-0.25.1-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:33358883a4490287e67a2c391dfaea4d9359860281db3292b6886bf0be3d8692", size = 564534 }, - { url = "https://files.pythonhosted.org/packages/41/3a/bc654eb15d3b38f9330fe0f545016ba154d89cdabc6177b0295910cd0ebe/rpds_py-0.25.1-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:1d1fadd539298e70cac2f2cb36f5b8a65f742b9b9f1014dd4ea1f7785e2470bf", size = 592674 }, - { url = "https://files.pythonhosted.org/packages/2e/ba/31239736f29e4dfc7a58a45955c5db852864c306131fd6320aea214d5437/rpds_py-0.25.1-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:9a46c2fb2545e21181445515960006e85d22025bd2fe6db23e76daec6eb689fe", size = 558781 }, +dependencies = [ + { name = "docutils" }, + { name = "rich" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b0/69/5514c3a87b5f10f09a34bb011bc0927bc12c596c8dae5915604e71abc386/rich_rst-1.3.1.tar.gz", hash = "sha256:fad46e3ba42785ea8c1785e2ceaa56e0ffa32dbe5410dec432f37e4107c4f383", size = 13839 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fd/bc/cc4e3dbc5e7992398dcb7a8eda0cbcf4fb792a0cdb93f857b478bf3cf884/rich_rst-1.3.1-py3-none-any.whl", hash = "sha256:498a74e3896507ab04492d326e794c3ef76e7cda078703aa592d1853d91098c1", size = 11621 }, +] + +[[package]] +name = "rpds-py" +version = "0.26.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a5/aa/4456d84bbb54adc6a916fb10c9b374f78ac840337644e4a5eda229c81275/rpds_py-0.26.0.tar.gz", hash = "sha256:20dae58a859b0906f0685642e591056f1e787f3a8b39c8e8749a45dc7d26bdb0", size = 27385 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b9/31/1459645f036c3dfeacef89e8e5825e430c77dde8489f3b99eaafcd4a60f5/rpds_py-0.26.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:4c70c70f9169692b36307a95f3d8c0a9fcd79f7b4a383aad5eaa0e9718b79b37", size = 372466 }, + { url = "https://files.pythonhosted.org/packages/dd/ff/3d0727f35836cc8773d3eeb9a46c40cc405854e36a8d2e951f3a8391c976/rpds_py-0.26.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:777c62479d12395bfb932944e61e915741e364c843afc3196b694db3d669fcd0", size = 357825 }, + { url = "https://files.pythonhosted.org/packages/bf/ce/badc5e06120a54099ae287fa96d82cbb650a5f85cf247ffe19c7b157fd1f/rpds_py-0.26.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec671691e72dff75817386aa02d81e708b5a7ec0dec6669ec05213ff6b77e1bd", size = 381530 }, + { url = "https://files.pythonhosted.org/packages/1e/a5/fa5d96a66c95d06c62d7a30707b6a4cfec696ab8ae280ee7be14e961e118/rpds_py-0.26.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6a1cb5d6ce81379401bbb7f6dbe3d56de537fb8235979843f0d53bc2e9815a79", size = 396933 }, + { url = "https://files.pythonhosted.org/packages/00/a7/7049d66750f18605c591a9db47d4a059e112a0c9ff8de8daf8fa0f446bba/rpds_py-0.26.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4f789e32fa1fb6a7bf890e0124e7b42d1e60d28ebff57fe806719abb75f0e9a3", size = 513973 }, + { url = "https://files.pythonhosted.org/packages/0e/f1/528d02c7d6b29d29fac8fd784b354d3571cc2153f33f842599ef0cf20dd2/rpds_py-0.26.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c55b0a669976cf258afd718de3d9ad1b7d1fe0a91cd1ab36f38b03d4d4aeaaf", size = 402293 }, + { url = "https://files.pythonhosted.org/packages/15/93/fde36cd6e4685df2cd08508f6c45a841e82f5bb98c8d5ecf05649522acb5/rpds_py-0.26.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c70d9ec912802ecfd6cd390dadb34a9578b04f9bcb8e863d0a7598ba5e9e7ccc", size = 383787 }, + { url = "https://files.pythonhosted.org/packages/69/f2/5007553aaba1dcae5d663143683c3dfd03d9395289f495f0aebc93e90f24/rpds_py-0.26.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3021933c2cb7def39d927b9862292e0f4c75a13d7de70eb0ab06efed4c508c19", size = 416312 }, + { url = "https://files.pythonhosted.org/packages/8f/a7/ce52c75c1e624a79e48a69e611f1c08844564e44c85db2b6f711d76d10ce/rpds_py-0.26.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8a7898b6ca3b7d6659e55cdac825a2e58c638cbf335cde41f4619e290dd0ad11", size = 558403 }, + { url = "https://files.pythonhosted.org/packages/79/d5/e119db99341cc75b538bf4cb80504129fa22ce216672fb2c28e4a101f4d9/rpds_py-0.26.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:12bff2ad9447188377f1b2794772f91fe68bb4bbfa5a39d7941fbebdbf8c500f", size = 588323 }, + { url = "https://files.pythonhosted.org/packages/93/94/d28272a0b02f5fe24c78c20e13bbcb95f03dc1451b68e7830ca040c60bd6/rpds_py-0.26.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:191aa858f7d4902e975d4cf2f2d9243816c91e9605070aeb09c0a800d187e323", size = 554541 }, + { url = "https://files.pythonhosted.org/packages/93/e0/8c41166602f1b791da892d976057eba30685486d2e2c061ce234679c922b/rpds_py-0.26.0-cp310-cp310-win32.whl", hash = "sha256:b37a04d9f52cb76b6b78f35109b513f6519efb481d8ca4c321f6a3b9580b3f45", size = 220442 }, + { url = "https://files.pythonhosted.org/packages/87/f0/509736bb752a7ab50fb0270c2a4134d671a7b3038030837e5536c3de0e0b/rpds_py-0.26.0-cp310-cp310-win_amd64.whl", hash = "sha256:38721d4c9edd3eb6670437d8d5e2070063f305bfa2d5aa4278c51cedcd508a84", size = 231314 }, + { url = "https://files.pythonhosted.org/packages/09/4c/4ee8f7e512030ff79fda1df3243c88d70fc874634e2dbe5df13ba4210078/rpds_py-0.26.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:9e8cb77286025bdb21be2941d64ac6ca016130bfdcd228739e8ab137eb4406ed", size = 372610 }, + { url = "https://files.pythonhosted.org/packages/fa/9d/3dc16be00f14fc1f03c71b1d67c8df98263ab2710a2fbd65a6193214a527/rpds_py-0.26.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5e09330b21d98adc8ccb2dbb9fc6cb434e8908d4c119aeaa772cb1caab5440a0", size = 358032 }, + { url = "https://files.pythonhosted.org/packages/e7/5a/7f1bf8f045da2866324a08ae80af63e64e7bfaf83bd31f865a7b91a58601/rpds_py-0.26.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c9c1b92b774b2e68d11193dc39620d62fd8ab33f0a3c77ecdabe19c179cdbc1", size = 381525 }, + { url = "https://files.pythonhosted.org/packages/45/8a/04479398c755a066ace10e3d158866beb600867cacae194c50ffa783abd0/rpds_py-0.26.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:824e6d3503ab990d7090768e4dfd9e840837bae057f212ff9f4f05ec6d1975e7", size = 397089 }, + { url = "https://files.pythonhosted.org/packages/72/88/9203f47268db488a1b6d469d69c12201ede776bb728b9d9f29dbfd7df406/rpds_py-0.26.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8ad7fd2258228bf288f2331f0a6148ad0186b2e3643055ed0db30990e59817a6", size = 514255 }, + { url = "https://files.pythonhosted.org/packages/f5/b4/01ce5d1e853ddf81fbbd4311ab1eff0b3cf162d559288d10fd127e2588b5/rpds_py-0.26.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0dc23bbb3e06ec1ea72d515fb572c1fea59695aefbffb106501138762e1e915e", size = 402283 }, + { url = "https://files.pythonhosted.org/packages/34/a2/004c99936997bfc644d590a9defd9e9c93f8286568f9c16cdaf3e14429a7/rpds_py-0.26.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d80bf832ac7b1920ee29a426cdca335f96a2b5caa839811803e999b41ba9030d", size = 383881 }, + { url = "https://files.pythonhosted.org/packages/05/1b/ef5fba4a8f81ce04c427bfd96223f92f05e6cd72291ce9d7523db3b03a6c/rpds_py-0.26.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0919f38f5542c0a87e7b4afcafab6fd2c15386632d249e9a087498571250abe3", size = 415822 }, + { url = "https://files.pythonhosted.org/packages/16/80/5c54195aec456b292f7bd8aa61741c8232964063fd8a75fdde9c1e982328/rpds_py-0.26.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d422b945683e409000c888e384546dbab9009bb92f7c0b456e217988cf316107", size = 558347 }, + { url = "https://files.pythonhosted.org/packages/f2/1c/1845c1b1fd6d827187c43afe1841d91678d7241cbdb5420a4c6de180a538/rpds_py-0.26.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:77a7711fa562ba2da1aa757e11024ad6d93bad6ad7ede5afb9af144623e5f76a", size = 587956 }, + { url = "https://files.pythonhosted.org/packages/2e/ff/9e979329dd131aa73a438c077252ddabd7df6d1a7ad7b9aacf6261f10faa/rpds_py-0.26.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:238e8c8610cb7c29460e37184f6799547f7e09e6a9bdbdab4e8edb90986a2318", size = 554363 }, + { url = "https://files.pythonhosted.org/packages/00/8b/d78cfe034b71ffbe72873a136e71acc7a831a03e37771cfe59f33f6de8a2/rpds_py-0.26.0-cp311-cp311-win32.whl", hash = "sha256:893b022bfbdf26d7bedb083efeea624e8550ca6eb98bf7fea30211ce95b9201a", size = 220123 }, + { url = "https://files.pythonhosted.org/packages/94/c1/3c8c94c7dd3905dbfde768381ce98778500a80db9924731d87ddcdb117e9/rpds_py-0.26.0-cp311-cp311-win_amd64.whl", hash = "sha256:87a5531de9f71aceb8af041d72fc4cab4943648d91875ed56d2e629bef6d4c03", size = 231732 }, + { url = "https://files.pythonhosted.org/packages/67/93/e936fbed1b734eabf36ccb5d93c6a2e9246fbb13c1da011624b7286fae3e/rpds_py-0.26.0-cp311-cp311-win_arm64.whl", hash = "sha256:de2713f48c1ad57f89ac25b3cb7daed2156d8e822cf0eca9b96a6f990718cc41", size = 221917 }, + { url = "https://files.pythonhosted.org/packages/ea/86/90eb87c6f87085868bd077c7a9938006eb1ce19ed4d06944a90d3560fce2/rpds_py-0.26.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:894514d47e012e794f1350f076c427d2347ebf82f9b958d554d12819849a369d", size = 363933 }, + { url = "https://files.pythonhosted.org/packages/63/78/4469f24d34636242c924626082b9586f064ada0b5dbb1e9d096ee7a8e0c6/rpds_py-0.26.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc921b96fa95a097add244da36a1d9e4f3039160d1d30f1b35837bf108c21136", size = 350447 }, + { url = "https://files.pythonhosted.org/packages/ad/91/c448ed45efdfdade82348d5e7995e15612754826ea640afc20915119734f/rpds_py-0.26.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e1157659470aa42a75448b6e943c895be8c70531c43cb78b9ba990778955582", size = 384711 }, + { url = "https://files.pythonhosted.org/packages/ec/43/e5c86fef4be7f49828bdd4ecc8931f0287b1152c0bb0163049b3218740e7/rpds_py-0.26.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:521ccf56f45bb3a791182dc6b88ae5f8fa079dd705ee42138c76deb1238e554e", size = 400865 }, + { url = "https://files.pythonhosted.org/packages/55/34/e00f726a4d44f22d5c5fe2e5ddd3ac3d7fd3f74a175607781fbdd06fe375/rpds_py-0.26.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9def736773fd56b305c0eef698be5192c77bfa30d55a0e5885f80126c4831a15", size = 517763 }, + { url = "https://files.pythonhosted.org/packages/52/1c/52dc20c31b147af724b16104500fba13e60123ea0334beba7b40e33354b4/rpds_py-0.26.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cdad4ea3b4513b475e027be79e5a0ceac8ee1c113a1a11e5edc3c30c29f964d8", size = 406651 }, + { url = "https://files.pythonhosted.org/packages/2e/77/87d7bfabfc4e821caa35481a2ff6ae0b73e6a391bb6b343db2c91c2b9844/rpds_py-0.26.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82b165b07f416bdccf5c84546a484cc8f15137ca38325403864bfdf2b5b72f6a", size = 386079 }, + { url = "https://files.pythonhosted.org/packages/e3/d4/7f2200c2d3ee145b65b3cddc4310d51f7da6a26634f3ac87125fd789152a/rpds_py-0.26.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d04cab0a54b9dba4d278fe955a1390da3cf71f57feb78ddc7cb67cbe0bd30323", size = 421379 }, + { url = "https://files.pythonhosted.org/packages/ae/13/9fdd428b9c820869924ab62236b8688b122baa22d23efdd1c566938a39ba/rpds_py-0.26.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:79061ba1a11b6a12743a2b0f72a46aa2758613d454aa6ba4f5a265cc48850158", size = 562033 }, + { url = "https://files.pythonhosted.org/packages/f3/e1/b69686c3bcbe775abac3a4c1c30a164a2076d28df7926041f6c0eb5e8d28/rpds_py-0.26.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:f405c93675d8d4c5ac87364bb38d06c988e11028a64b52a47158a355079661f3", size = 591639 }, + { url = "https://files.pythonhosted.org/packages/5c/c9/1e3d8c8863c84a90197ac577bbc3d796a92502124c27092413426f670990/rpds_py-0.26.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dafd4c44b74aa4bed4b250f1aed165b8ef5de743bcca3b88fc9619b6087093d2", size = 557105 }, + { url = "https://files.pythonhosted.org/packages/9f/c5/90c569649057622959f6dcc40f7b516539608a414dfd54b8d77e3b201ac0/rpds_py-0.26.0-cp312-cp312-win32.whl", hash = "sha256:3da5852aad63fa0c6f836f3359647870e21ea96cf433eb393ffa45263a170d44", size = 223272 }, + { url = "https://files.pythonhosted.org/packages/7d/16/19f5d9f2a556cfed454eebe4d354c38d51c20f3db69e7b4ce6cff904905d/rpds_py-0.26.0-cp312-cp312-win_amd64.whl", hash = "sha256:cf47cfdabc2194a669dcf7a8dbba62e37a04c5041d2125fae0233b720da6f05c", size = 234995 }, + { url = "https://files.pythonhosted.org/packages/83/f0/7935e40b529c0e752dfaa7880224771b51175fce08b41ab4a92eb2fbdc7f/rpds_py-0.26.0-cp312-cp312-win_arm64.whl", hash = "sha256:20ab1ae4fa534f73647aad289003f1104092890849e0266271351922ed5574f8", size = 223198 }, + { url = "https://files.pythonhosted.org/packages/6a/67/bb62d0109493b12b1c6ab00de7a5566aa84c0e44217c2d94bee1bd370da9/rpds_py-0.26.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:696764a5be111b036256c0b18cd29783fab22154690fc698062fc1b0084b511d", size = 363917 }, + { url = "https://files.pythonhosted.org/packages/4b/f3/34e6ae1925a5706c0f002a8d2d7f172373b855768149796af87bd65dcdb9/rpds_py-0.26.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1e6c15d2080a63aaed876e228efe4f814bc7889c63b1e112ad46fdc8b368b9e1", size = 350073 }, + { url = "https://files.pythonhosted.org/packages/75/83/1953a9d4f4e4de7fd0533733e041c28135f3c21485faaef56a8aadbd96b5/rpds_py-0.26.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:390e3170babf42462739a93321e657444f0862c6d722a291accc46f9d21ed04e", size = 384214 }, + { url = "https://files.pythonhosted.org/packages/48/0e/983ed1b792b3322ea1d065e67f4b230f3b96025f5ce3878cc40af09b7533/rpds_py-0.26.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7da84c2c74c0f5bc97d853d9e17bb83e2dcafcff0dc48286916001cc114379a1", size = 400113 }, + { url = "https://files.pythonhosted.org/packages/69/7f/36c0925fff6f660a80be259c5b4f5e53a16851f946eb080351d057698528/rpds_py-0.26.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4c5fe114a6dd480a510b6d3661d09d67d1622c4bf20660a474507aaee7eeeee9", size = 515189 }, + { url = "https://files.pythonhosted.org/packages/13/45/cbf07fc03ba7a9b54662c9badb58294ecfb24f828b9732970bd1a431ed5c/rpds_py-0.26.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3100b3090269f3a7ea727b06a6080d4eb7439dca4c0e91a07c5d133bb1727ea7", size = 406998 }, + { url = "https://files.pythonhosted.org/packages/6c/b0/8fa5e36e58657997873fd6a1cf621285ca822ca75b4b3434ead047daa307/rpds_py-0.26.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c03c9b0c64afd0320ae57de4c982801271c0c211aa2d37f3003ff5feb75bb04", size = 385903 }, + { url = "https://files.pythonhosted.org/packages/4b/f7/b25437772f9f57d7a9fbd73ed86d0dcd76b4c7c6998348c070d90f23e315/rpds_py-0.26.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5963b72ccd199ade6ee493723d18a3f21ba7d5b957017607f815788cef50eaf1", size = 419785 }, + { url = "https://files.pythonhosted.org/packages/a7/6b/63ffa55743dfcb4baf2e9e77a0b11f7f97ed96a54558fcb5717a4b2cd732/rpds_py-0.26.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9da4e873860ad5bab3291438525cae80169daecbfafe5657f7f5fb4d6b3f96b9", size = 561329 }, + { url = "https://files.pythonhosted.org/packages/2f/07/1f4f5e2886c480a2346b1e6759c00278b8a69e697ae952d82ae2e6ee5db0/rpds_py-0.26.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:5afaddaa8e8c7f1f7b4c5c725c0070b6eed0228f705b90a1732a48e84350f4e9", size = 590875 }, + { url = "https://files.pythonhosted.org/packages/cc/bc/e6639f1b91c3a55f8c41b47d73e6307051b6e246254a827ede730624c0f8/rpds_py-0.26.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4916dc96489616a6f9667e7526af8fa693c0fdb4f3acb0e5d9f4400eb06a47ba", size = 556636 }, + { url = "https://files.pythonhosted.org/packages/05/4c/b3917c45566f9f9a209d38d9b54a1833f2bb1032a3e04c66f75726f28876/rpds_py-0.26.0-cp313-cp313-win32.whl", hash = "sha256:2a343f91b17097c546b93f7999976fd6c9d5900617aa848c81d794e062ab302b", size = 222663 }, + { url = "https://files.pythonhosted.org/packages/e0/0b/0851bdd6025775aaa2365bb8de0697ee2558184c800bfef8d7aef5ccde58/rpds_py-0.26.0-cp313-cp313-win_amd64.whl", hash = "sha256:0a0b60701f2300c81b2ac88a5fb893ccfa408e1c4a555a77f908a2596eb875a5", size = 234428 }, + { url = "https://files.pythonhosted.org/packages/ed/e8/a47c64ed53149c75fb581e14a237b7b7cd18217e969c30d474d335105622/rpds_py-0.26.0-cp313-cp313-win_arm64.whl", hash = "sha256:257d011919f133a4746958257f2c75238e3ff54255acd5e3e11f3ff41fd14256", size = 222571 }, + { url = "https://files.pythonhosted.org/packages/89/bf/3d970ba2e2bcd17d2912cb42874107390f72873e38e79267224110de5e61/rpds_py-0.26.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:529c8156d7506fba5740e05da8795688f87119cce330c244519cf706a4a3d618", size = 360475 }, + { url = "https://files.pythonhosted.org/packages/82/9f/283e7e2979fc4ec2d8ecee506d5a3675fce5ed9b4b7cb387ea5d37c2f18d/rpds_py-0.26.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:f53ec51f9d24e9638a40cabb95078ade8c99251945dad8d57bf4aabe86ecee35", size = 346692 }, + { url = "https://files.pythonhosted.org/packages/e3/03/7e50423c04d78daf391da3cc4330bdb97042fc192a58b186f2d5deb7befd/rpds_py-0.26.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab504c4d654e4a29558eaa5bb8cea5fdc1703ea60a8099ffd9c758472cf913f", size = 379415 }, + { url = "https://files.pythonhosted.org/packages/57/00/d11ee60d4d3b16808432417951c63df803afb0e0fc672b5e8d07e9edaaae/rpds_py-0.26.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fd0641abca296bc1a00183fe44f7fced8807ed49d501f188faa642d0e4975b83", size = 391783 }, + { url = "https://files.pythonhosted.org/packages/08/b3/1069c394d9c0d6d23c5b522e1f6546b65793a22950f6e0210adcc6f97c3e/rpds_py-0.26.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:69b312fecc1d017b5327afa81d4da1480f51c68810963a7336d92203dbb3d4f1", size = 512844 }, + { url = "https://files.pythonhosted.org/packages/08/3b/c4fbf0926800ed70b2c245ceca99c49f066456755f5d6eb8863c2c51e6d0/rpds_py-0.26.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c741107203954f6fc34d3066d213d0a0c40f7bb5aafd698fb39888af277c70d8", size = 402105 }, + { url = "https://files.pythonhosted.org/packages/1c/b0/db69b52ca07413e568dae9dc674627a22297abb144c4d6022c6d78f1e5cc/rpds_py-0.26.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc3e55a7db08dc9a6ed5fb7103019d2c1a38a349ac41901f9f66d7f95750942f", size = 383440 }, + { url = "https://files.pythonhosted.org/packages/4c/e1/c65255ad5b63903e56b3bb3ff9dcc3f4f5c3badde5d08c741ee03903e951/rpds_py-0.26.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9e851920caab2dbcae311fd28f4313c6953993893eb5c1bb367ec69d9a39e7ed", size = 412759 }, + { url = "https://files.pythonhosted.org/packages/e4/22/bb731077872377a93c6e93b8a9487d0406c70208985831034ccdeed39c8e/rpds_py-0.26.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:dfbf280da5f876d0b00c81f26bedce274e72a678c28845453885a9b3c22ae632", size = 556032 }, + { url = "https://files.pythonhosted.org/packages/e0/8b/393322ce7bac5c4530fb96fc79cc9ea2f83e968ff5f6e873f905c493e1c4/rpds_py-0.26.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:1cc81d14ddfa53d7f3906694d35d54d9d3f850ef8e4e99ee68bc0d1e5fed9a9c", size = 585416 }, + { url = "https://files.pythonhosted.org/packages/49/ae/769dc372211835bf759319a7aae70525c6eb523e3371842c65b7ef41c9c6/rpds_py-0.26.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dca83c498b4650a91efcf7b88d669b170256bf8017a5db6f3e06c2bf031f57e0", size = 554049 }, + { url = "https://files.pythonhosted.org/packages/6b/f9/4c43f9cc203d6ba44ce3146246cdc38619d92c7bd7bad4946a3491bd5b70/rpds_py-0.26.0-cp313-cp313t-win32.whl", hash = "sha256:4d11382bcaf12f80b51d790dee295c56a159633a8e81e6323b16e55d81ae37e9", size = 218428 }, + { url = "https://files.pythonhosted.org/packages/7e/8b/9286b7e822036a4a977f2f1e851c7345c20528dbd56b687bb67ed68a8ede/rpds_py-0.26.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ff110acded3c22c033e637dd8896e411c7d3a11289b2edf041f86663dbc791e9", size = 231524 }, + { url = "https://files.pythonhosted.org/packages/55/07/029b7c45db910c74e182de626dfdae0ad489a949d84a468465cd0ca36355/rpds_py-0.26.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:da619979df60a940cd434084355c514c25cf8eb4cf9a508510682f6c851a4f7a", size = 364292 }, + { url = "https://files.pythonhosted.org/packages/13/d1/9b3d3f986216b4d1f584878dca15ce4797aaf5d372d738974ba737bf68d6/rpds_py-0.26.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ea89a2458a1a75f87caabefe789c87539ea4e43b40f18cff526052e35bbb4fdf", size = 350334 }, + { url = "https://files.pythonhosted.org/packages/18/98/16d5e7bc9ec715fa9668731d0cf97f6b032724e61696e2db3d47aeb89214/rpds_py-0.26.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:feac1045b3327a45944e7dcbeb57530339f6b17baff154df51ef8b0da34c8c12", size = 384875 }, + { url = "https://files.pythonhosted.org/packages/f9/13/aa5e2b1ec5ab0e86a5c464d53514c0467bec6ba2507027d35fc81818358e/rpds_py-0.26.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b818a592bd69bfe437ee8368603d4a2d928c34cffcdf77c2e761a759ffd17d20", size = 399993 }, + { url = "https://files.pythonhosted.org/packages/17/03/8021810b0e97923abdbab6474c8b77c69bcb4b2c58330777df9ff69dc559/rpds_py-0.26.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1a8b0dd8648709b62d9372fc00a57466f5fdeefed666afe3fea5a6c9539a0331", size = 516683 }, + { url = "https://files.pythonhosted.org/packages/dc/b1/da8e61c87c2f3d836954239fdbbfb477bb7b54d74974d8f6fcb34342d166/rpds_py-0.26.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6d3498ad0df07d81112aa6ec6c95a7e7b1ae00929fb73e7ebee0f3faaeabad2f", size = 408825 }, + { url = "https://files.pythonhosted.org/packages/38/bc/1fc173edaaa0e52c94b02a655db20697cb5fa954ad5a8e15a2c784c5cbdd/rpds_py-0.26.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24a4146ccb15be237fdef10f331c568e1b0e505f8c8c9ed5d67759dac58ac246", size = 387292 }, + { url = "https://files.pythonhosted.org/packages/7c/eb/3a9bb4bd90867d21916f253caf4f0d0be7098671b6715ad1cead9fe7bab9/rpds_py-0.26.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a9a63785467b2d73635957d32a4f6e73d5e4df497a16a6392fa066b753e87387", size = 420435 }, + { url = "https://files.pythonhosted.org/packages/cd/16/e066dcdb56f5632713445271a3f8d3d0b426d51ae9c0cca387799df58b02/rpds_py-0.26.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:de4ed93a8c91debfd5a047be327b7cc8b0cc6afe32a716bbbc4aedca9e2a83af", size = 562410 }, + { url = "https://files.pythonhosted.org/packages/60/22/ddbdec7eb82a0dc2e455be44c97c71c232983e21349836ce9f272e8a3c29/rpds_py-0.26.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:caf51943715b12af827696ec395bfa68f090a4c1a1d2509eb4e2cb69abbbdb33", size = 590724 }, + { url = "https://files.pythonhosted.org/packages/2c/b4/95744085e65b7187d83f2fcb0bef70716a1ea0a9e5d8f7f39a86e5d83424/rpds_py-0.26.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:4a59e5bc386de021f56337f757301b337d7ab58baa40174fb150accd480bc953", size = 558285 }, + { url = "https://files.pythonhosted.org/packages/37/37/6309a75e464d1da2559446f9c811aa4d16343cebe3dbb73701e63f760caa/rpds_py-0.26.0-cp314-cp314-win32.whl", hash = "sha256:92c8db839367ef16a662478f0a2fe13e15f2227da3c1430a782ad0f6ee009ec9", size = 223459 }, + { url = "https://files.pythonhosted.org/packages/d9/6f/8e9c11214c46098b1d1391b7e02b70bb689ab963db3b19540cba17315291/rpds_py-0.26.0-cp314-cp314-win_amd64.whl", hash = "sha256:b0afb8cdd034150d4d9f53926226ed27ad15b7f465e93d7468caaf5eafae0d37", size = 236083 }, + { url = "https://files.pythonhosted.org/packages/47/af/9c4638994dd623d51c39892edd9d08e8be8220a4b7e874fa02c2d6e91955/rpds_py-0.26.0-cp314-cp314-win_arm64.whl", hash = "sha256:ca3f059f4ba485d90c8dc75cb5ca897e15325e4e609812ce57f896607c1c0867", size = 223291 }, + { url = "https://files.pythonhosted.org/packages/4d/db/669a241144460474aab03e254326b32c42def83eb23458a10d163cb9b5ce/rpds_py-0.26.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:5afea17ab3a126006dc2f293b14ffc7ef3c85336cf451564a0515ed7648033da", size = 361445 }, + { url = "https://files.pythonhosted.org/packages/3b/2d/133f61cc5807c6c2fd086a46df0eb8f63a23f5df8306ff9f6d0fd168fecc/rpds_py-0.26.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:69f0c0a3df7fd3a7eec50a00396104bb9a843ea6d45fcc31c2d5243446ffd7a7", size = 347206 }, + { url = "https://files.pythonhosted.org/packages/05/bf/0e8fb4c05f70273469eecf82f6ccf37248558526a45321644826555db31b/rpds_py-0.26.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:801a71f70f9813e82d2513c9a96532551fce1e278ec0c64610992c49c04c2dad", size = 380330 }, + { url = "https://files.pythonhosted.org/packages/d4/a8/060d24185d8b24d3923322f8d0ede16df4ade226a74e747b8c7c978e3dd3/rpds_py-0.26.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:df52098cde6d5e02fa75c1f6244f07971773adb4a26625edd5c18fee906fa84d", size = 392254 }, + { url = "https://files.pythonhosted.org/packages/b9/7b/7c2e8a9ee3e6bc0bae26bf29f5219955ca2fbb761dca996a83f5d2f773fe/rpds_py-0.26.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9bc596b30f86dc6f0929499c9e574601679d0341a0108c25b9b358a042f51bca", size = 516094 }, + { url = "https://files.pythonhosted.org/packages/75/d6/f61cafbed8ba1499b9af9f1777a2a199cd888f74a96133d8833ce5eaa9c5/rpds_py-0.26.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9dfbe56b299cf5875b68eb6f0ebaadc9cac520a1989cac0db0765abfb3709c19", size = 402889 }, + { url = "https://files.pythonhosted.org/packages/92/19/c8ac0a8a8df2dd30cdec27f69298a5c13e9029500d6d76718130f5e5be10/rpds_py-0.26.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac64f4b2bdb4ea622175c9ab7cf09444e412e22c0e02e906978b3b488af5fde8", size = 384301 }, + { url = "https://files.pythonhosted.org/packages/41/e1/6b1859898bc292a9ce5776016c7312b672da00e25cec74d7beced1027286/rpds_py-0.26.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:181ef9b6bbf9845a264f9aa45c31836e9f3c1f13be565d0d010e964c661d1e2b", size = 412891 }, + { url = "https://files.pythonhosted.org/packages/ef/b9/ceb39af29913c07966a61367b3c08b4f71fad841e32c6b59a129d5974698/rpds_py-0.26.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:49028aa684c144ea502a8e847d23aed5e4c2ef7cadfa7d5eaafcb40864844b7a", size = 557044 }, + { url = "https://files.pythonhosted.org/packages/2f/27/35637b98380731a521f8ec4f3fd94e477964f04f6b2f8f7af8a2d889a4af/rpds_py-0.26.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:e5d524d68a474a9688336045bbf76cb0def88549c1b2ad9dbfec1fb7cfbe9170", size = 585774 }, + { url = "https://files.pythonhosted.org/packages/52/d9/3f0f105420fecd18551b678c9a6ce60bd23986098b252a56d35781b3e7e9/rpds_py-0.26.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:c1851f429b822831bd2edcbe0cfd12ee9ea77868f8d3daf267b189371671c80e", size = 554886 }, + { url = "https://files.pythonhosted.org/packages/6b/c5/347c056a90dc8dd9bc240a08c527315008e1b5042e7a4cf4ac027be9d38a/rpds_py-0.26.0-cp314-cp314t-win32.whl", hash = "sha256:7bdb17009696214c3b66bb3590c6d62e14ac5935e53e929bcdbc5a495987a84f", size = 219027 }, + { url = "https://files.pythonhosted.org/packages/75/04/5302cea1aa26d886d34cadbf2dc77d90d7737e576c0065f357b96dc7a1a6/rpds_py-0.26.0-cp314-cp314t-win_amd64.whl", hash = "sha256:f14440b9573a6f76b4ee4770c13f0b5921f71dde3b6fcb8dabbefd13b7fe05d7", size = 232821 }, + { url = "https://files.pythonhosted.org/packages/ef/9a/1f033b0b31253d03d785b0cd905bc127e555ab496ea6b4c7c2e1f951f2fd/rpds_py-0.26.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3c0909c5234543ada2515c05dc08595b08d621ba919629e94427e8e03539c958", size = 373226 }, + { url = "https://files.pythonhosted.org/packages/58/29/5f88023fd6aaaa8ca3c4a6357ebb23f6f07da6079093ccf27c99efce87db/rpds_py-0.26.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:c1fb0cda2abcc0ac62f64e2ea4b4e64c57dfd6b885e693095460c61bde7bb18e", size = 359230 }, + { url = "https://files.pythonhosted.org/packages/6c/6c/13eaebd28b439da6964dde22712b52e53fe2824af0223b8e403249d10405/rpds_py-0.26.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:84d142d2d6cf9b31c12aa4878d82ed3b2324226270b89b676ac62ccd7df52d08", size = 382363 }, + { url = "https://files.pythonhosted.org/packages/55/fc/3bb9c486b06da19448646f96147796de23c5811ef77cbfc26f17307b6a9d/rpds_py-0.26.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a547e21c5610b7e9093d870be50682a6a6cf180d6da0f42c47c306073bfdbbf6", size = 397146 }, + { url = "https://files.pythonhosted.org/packages/15/18/9d1b79eb4d18e64ba8bba9e7dec6f9d6920b639f22f07ee9368ca35d4673/rpds_py-0.26.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:35e9a70a0f335371275cdcd08bc5b8051ac494dd58bff3bbfb421038220dc871", size = 514804 }, + { url = "https://files.pythonhosted.org/packages/4f/5a/175ad7191bdbcd28785204621b225ad70e85cdfd1e09cc414cb554633b21/rpds_py-0.26.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0dfa6115c6def37905344d56fb54c03afc49104e2ca473d5dedec0f6606913b4", size = 402820 }, + { url = "https://files.pythonhosted.org/packages/11/45/6a67ecf6d61c4d4aff4bc056e864eec4b2447787e11d1c2c9a0242c6e92a/rpds_py-0.26.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:313cfcd6af1a55a286a3c9a25f64af6d0e46cf60bc5798f1db152d97a216ff6f", size = 384567 }, + { url = "https://files.pythonhosted.org/packages/a1/ba/16589da828732b46454c61858950a78fe4c931ea4bf95f17432ffe64b241/rpds_py-0.26.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f7bf2496fa563c046d05e4d232d7b7fd61346e2402052064b773e5c378bf6f73", size = 416520 }, + { url = "https://files.pythonhosted.org/packages/81/4b/00092999fc7c0c266045e984d56b7314734cc400a6c6dc4d61a35f135a9d/rpds_py-0.26.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:aa81873e2c8c5aa616ab8e017a481a96742fdf9313c40f14338ca7dbf50cb55f", size = 559362 }, + { url = "https://files.pythonhosted.org/packages/96/0c/43737053cde1f93ac4945157f7be1428724ab943e2132a0d235a7e161d4e/rpds_py-0.26.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:68ffcf982715f5b5b7686bdd349ff75d422e8f22551000c24b30eaa1b7f7ae84", size = 588113 }, + { url = "https://files.pythonhosted.org/packages/46/46/8e38f6161466e60a997ed7e9951ae5de131dedc3cf778ad35994b4af823d/rpds_py-0.26.0-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:6188de70e190847bb6db3dc3981cbadff87d27d6fe9b4f0e18726d55795cee9b", size = 555429 }, + { url = "https://files.pythonhosted.org/packages/2c/ac/65da605e9f1dd643ebe615d5bbd11b6efa1d69644fc4bf623ea5ae385a82/rpds_py-0.26.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:1c962145c7473723df9722ba4c058de12eb5ebedcb4e27e7d902920aa3831ee8", size = 231950 }, + { url = "https://files.pythonhosted.org/packages/51/f2/b5c85b758a00c513bb0389f8fc8e61eb5423050c91c958cdd21843faa3e6/rpds_py-0.26.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f61a9326f80ca59214d1cceb0a09bb2ece5b2563d4e0cd37bfd5515c28510674", size = 373505 }, + { url = "https://files.pythonhosted.org/packages/23/e0/25db45e391251118e915e541995bb5f5ac5691a3b98fb233020ba53afc9b/rpds_py-0.26.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:183f857a53bcf4b1b42ef0f57ca553ab56bdd170e49d8091e96c51c3d69ca696", size = 359468 }, + { url = "https://files.pythonhosted.org/packages/0b/73/dd5ee6075bb6491be3a646b301dfd814f9486d924137a5098e61f0487e16/rpds_py-0.26.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:941c1cfdf4799d623cf3aa1d326a6b4fdb7a5799ee2687f3516738216d2262fb", size = 382680 }, + { url = "https://files.pythonhosted.org/packages/2f/10/84b522ff58763a5c443f5bcedc1820240e454ce4e620e88520f04589e2ea/rpds_py-0.26.0-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:72a8d9564a717ee291f554eeb4bfeafe2309d5ec0aa6c475170bdab0f9ee8e88", size = 397035 }, + { url = "https://files.pythonhosted.org/packages/06/ea/8667604229a10a520fcbf78b30ccc278977dcc0627beb7ea2c96b3becef0/rpds_py-0.26.0-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:511d15193cbe013619dd05414c35a7dedf2088fcee93c6bbb7c77859765bd4e8", size = 514922 }, + { url = "https://files.pythonhosted.org/packages/24/e6/9ed5b625c0661c4882fc8cdf302bf8e96c73c40de99c31e0b95ed37d508c/rpds_py-0.26.0-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aea1f9741b603a8d8fedb0ed5502c2bc0accbc51f43e2ad1337fe7259c2b77a5", size = 402822 }, + { url = "https://files.pythonhosted.org/packages/8a/58/212c7b6fd51946047fb45d3733da27e2fa8f7384a13457c874186af691b1/rpds_py-0.26.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4019a9d473c708cf2f16415688ef0b4639e07abaa569d72f74745bbeffafa2c7", size = 384336 }, + { url = "https://files.pythonhosted.org/packages/aa/f5/a40ba78748ae8ebf4934d4b88e77b98497378bc2c24ba55ebe87a4e87057/rpds_py-0.26.0-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:093d63b4b0f52d98ebae33b8c50900d3d67e0666094b1be7a12fffd7f65de74b", size = 416871 }, + { url = "https://files.pythonhosted.org/packages/d5/a6/33b1fc0c9f7dcfcfc4a4353daa6308b3ece22496ceece348b3e7a7559a09/rpds_py-0.26.0-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:2abe21d8ba64cded53a2a677e149ceb76dcf44284202d737178afe7ba540c1eb", size = 559439 }, + { url = "https://files.pythonhosted.org/packages/71/2d/ceb3f9c12f8cfa56d34995097f6cd99da1325642c60d1b6680dd9df03ed8/rpds_py-0.26.0-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:4feb7511c29f8442cbbc28149a92093d32e815a28aa2c50d333826ad2a20fdf0", size = 588380 }, + { url = "https://files.pythonhosted.org/packages/c8/ed/9de62c2150ca8e2e5858acf3f4f4d0d180a38feef9fdab4078bea63d8dba/rpds_py-0.26.0-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:e99685fc95d386da368013e7fb4269dd39c30d99f812a8372d62f244f662709c", size = 555334 }, ] [[package]] name = "rq" -version = "2.3.3" +version = "2.4.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, { name = "redis" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/6a/d8/8dd3c33b1854e1a5247fc9945e5c052dbac61c3a6392b1e2ff50d089c2e7/rq-2.3.3.tar.gz", hash = "sha256:20c41c977b6f27c852a41bd855893717402bae7b8d9607dca21fe9dd55453e22", size = 649348 } +sdist = { url = "https://files.pythonhosted.org/packages/9f/1a/76bd814898c4c574bc0e6100c4626247fc08c0194372d4d3b7bfcf752eae/rq-2.4.1.tar.gz", hash = "sha256:40ba01af3edacc008ab376009a3a547278d2bfe02a77cd4434adc0b01788239f", size = 664540 } wheels = [ - { url = "https://files.pythonhosted.org/packages/66/25/2e17899e70317497cf0fe2d2742ba464becf7e996f65e17b48440de88635/rq-2.3.3-py3-none-any.whl", hash = "sha256:2202c4409c4c527ac4bee409867d6c02515dd110030499eb0de54c7374aee0ce", size = 101012 }, + { url = "https://files.pythonhosted.org/packages/8a/c4/ffd7a6d9a706a50ab91c8bd42ff54cd9b228613d6bb80f7728a5144518b1/rq-2.4.1-py3-none-any.whl", hash = "sha256:a3a0839ba3213a9be013b398670caf71d9360a0c8525f343687cf2c2199e5ec8", size = 108014 }, ] [[package]] @@ -3478,7 +3917,7 @@ version = "2.1.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "numpy", version = "2.3.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/ca/3c/2da625233f4e605155926566c0e7ea8dda361877f48e8b1655e53456f252/shapely-2.1.1.tar.gz", hash = "sha256:500621967f2ffe9642454808009044c21e5b35db89ce69f8a2042c2ffd0e2772", size = 315422 } wheels = [ @@ -3612,14 +4051,28 @@ wheels = [ [[package]] name = "sse-starlette" -version = "2.3.6" +version = "2.4.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/8c/f4/989bc70cb8091eda43a9034ef969b25145291f3601703b82766e5172dfed/sse_starlette-2.3.6.tar.gz", hash = "sha256:0382336f7d4ec30160cf9ca0518962905e1b69b72d6c1c995131e0a703b436e3", size = 18284 } +sdist = { url = "https://files.pythonhosted.org/packages/07/3e/eae74d8d33e3262bae0a7e023bb43d8bdd27980aa3557333f4632611151f/sse_starlette-2.4.1.tar.gz", hash = "sha256:7c8a800a1ca343e9165fc06bbda45c78e4c6166320707ae30b416c42da070926", size = 18635 } wheels = [ - { url = "https://files.pythonhosted.org/packages/81/05/78850ac6e79af5b9508f8841b0f26aa9fd329a1ba00bf65453c2d312bcc8/sse_starlette-2.3.6-py3-none-any.whl", hash = "sha256:d49a8285b182f6e2228e2609c350398b2ca2c36216c2675d875f81e93548f760", size = 10606 }, + { url = "https://files.pythonhosted.org/packages/e4/f1/6c7eaa8187ba789a6dd6d74430307478d2a91c23a5452ab339b6fbe15a08/sse_starlette-2.4.1-py3-none-any.whl", hash = "sha256:08b77ea898ab1a13a428b2b6f73cfe6d0e607a7b4e15b9bb23e4a37b087fd39a", size = 10824 }, +] + +[[package]] +name = "stack-data" +version = "0.6.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "asttokens" }, + { name = "executing" }, + { name = "pure-eval" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/28/e3/55dcc2cfbc3ca9c29519eb6884dd1415ecb53b0e934862d3559ddcb7e20b/stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9", size = 44707 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695", size = 24521 }, ] [[package]] @@ -3658,16 +4111,16 @@ wheels = [ [[package]] name = "tavily-python" -version = "0.7.5" +version = "0.7.10" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "httpx" }, { name = "requests" }, { name = "tiktoken" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/1a/f6/ffbcb3fed5fceffff2ca934d06e2a667023ded6964500aadc732c80dde6f/tavily_python-0.7.5.tar.gz", hash = "sha256:f56aac136fbebeb2c8068fd6261aaa15f31d7da5c68e098ef7d6269ad032f842", size = 17186 } +sdist = { url = "https://files.pythonhosted.org/packages/04/0e/d4aa0f4dec298298b510ee5209f5ff29352bbbba106fd7ea0221ba8840dc/tavily_python-0.7.10.tar.gz", hash = "sha256:c87b4c0549ab2e416cf4ac3da8fe3ce5db106288408b06e197d4b5ba8ec7ead9", size = 19275 } wheels = [ - { url = "https://files.pythonhosted.org/packages/51/9d/1ec73120c3b8fc52a84bcd51d0028ba9dfa729938abb41b41e4fcb58f263/tavily_python-0.7.5-py3-none-any.whl", hash = "sha256:e64660977c1b96df8c3327e8ff8ed626d5ca3178c52ee167ffa543731642f221", size = 15379 }, + { url = "https://files.pythonhosted.org/packages/64/60/4c4678a28b3b5061aa2ab45b215290d3a71810e7996bafdf6b7313e75fb3/tavily_python-0.7.10-py3-none-any.whl", hash = "sha256:a99958e14dd091271611be7fb1e1a8a86f5bff3a9022b9626f4c4f1513338088", size = 15786 }, ] [[package]] @@ -3726,27 +4179,27 @@ wheels = [ [[package]] name = "tokenizers" -version = "0.21.1" +version = "0.21.4.dev0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "huggingface-hub" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/92/76/5ac0c97f1117b91b7eb7323dcd61af80d72f790b4df71249a7850c195f30/tokenizers-0.21.1.tar.gz", hash = "sha256:a1bb04dc5b448985f86ecd4b05407f5a8d97cb2c0532199b2a302a604a0165ab", size = 343256 } +sdist = { url = "https://files.pythonhosted.org/packages/4f/4e/6efc93d07add459f0800b496046e048907030eeebf56a755d4fcbf5d0d54/tokenizers-0.21.4.dev0.tar.gz", hash = "sha256:1dedf85cae0d58c74fc8023d58f28b510996a9c79dfc62136639d5c3ace6bdc0", size = 352282 } wheels = [ - { url = "https://files.pythonhosted.org/packages/a5/1f/328aee25f9115bf04262e8b4e5a2050b7b7cf44b59c74e982db7270c7f30/tokenizers-0.21.1-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:e78e413e9e668ad790a29456e677d9d3aa50a9ad311a40905d6861ba7692cf41", size = 2780767 }, - { url = "https://files.pythonhosted.org/packages/ae/1a/4526797f3719b0287853f12c5ad563a9be09d446c44ac784cdd7c50f76ab/tokenizers-0.21.1-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:cd51cd0a91ecc801633829fcd1fda9cf8682ed3477c6243b9a095539de4aecf3", size = 2650555 }, - { url = "https://files.pythonhosted.org/packages/4d/7a/a209b29f971a9fdc1da86f917fe4524564924db50d13f0724feed37b2a4d/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28da6b72d4fb14ee200a1bd386ff74ade8992d7f725f2bde2c495a9a98cf4d9f", size = 2937541 }, - { url = "https://files.pythonhosted.org/packages/3c/1e/b788b50ffc6191e0b1fc2b0d49df8cff16fe415302e5ceb89f619d12c5bc/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:34d8cfde551c9916cb92014e040806122295a6800914bab5865deb85623931cf", size = 2819058 }, - { url = "https://files.pythonhosted.org/packages/36/aa/3626dfa09a0ecc5b57a8c58eeaeb7dd7ca9a37ad9dd681edab5acd55764c/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aaa852d23e125b73d283c98f007e06d4595732104b65402f46e8ef24b588d9f8", size = 3133278 }, - { url = "https://files.pythonhosted.org/packages/a4/4d/8fbc203838b3d26269f944a89459d94c858f5b3f9a9b6ee9728cdcf69161/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a21a15d5c8e603331b8a59548bbe113564136dc0f5ad8306dd5033459a226da0", size = 3144253 }, - { url = "https://files.pythonhosted.org/packages/d8/1b/2bd062adeb7c7511b847b32e356024980c0ffcf35f28947792c2d8ad2288/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2fdbd4c067c60a0ac7eca14b6bd18a5bebace54eb757c706b47ea93204f7a37c", size = 3398225 }, - { url = "https://files.pythonhosted.org/packages/8a/63/38be071b0c8e06840bc6046991636bcb30c27f6bb1e670f4f4bc87cf49cc/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2dd9a0061e403546f7377df940e866c3e678d7d4e9643d0461ea442b4f89e61a", size = 3038874 }, - { url = "https://files.pythonhosted.org/packages/ec/83/afa94193c09246417c23a3c75a8a0a96bf44ab5630a3015538d0c316dd4b/tokenizers-0.21.1-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:db9484aeb2e200c43b915a1a0150ea885e35f357a5a8fabf7373af333dcc8dbf", size = 9014448 }, - { url = "https://files.pythonhosted.org/packages/ae/b3/0e1a37d4f84c0f014d43701c11eb8072704f6efe8d8fc2dcdb79c47d76de/tokenizers-0.21.1-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:ed248ab5279e601a30a4d67bdb897ecbe955a50f1e7bb62bd99f07dd11c2f5b6", size = 8937877 }, - { url = "https://files.pythonhosted.org/packages/ac/33/ff08f50e6d615eb180a4a328c65907feb6ded0b8f990ec923969759dc379/tokenizers-0.21.1-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:9ac78b12e541d4ce67b4dfd970e44c060a2147b9b2a21f509566d556a509c67d", size = 9186645 }, - { url = "https://files.pythonhosted.org/packages/5f/aa/8ae85f69a9f6012c6f8011c6f4aa1c96154c816e9eea2e1b758601157833/tokenizers-0.21.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:e5a69c1a4496b81a5ee5d2c1f3f7fbdf95e90a0196101b0ee89ed9956b8a168f", size = 9384380 }, - { url = "https://files.pythonhosted.org/packages/e8/5b/a5d98c89f747455e8b7a9504910c865d5e51da55e825a7ae641fb5ff0a58/tokenizers-0.21.1-cp39-abi3-win32.whl", hash = "sha256:1039a3a5734944e09de1d48761ade94e00d0fa760c0e0551151d4dd851ba63e3", size = 2239506 }, - { url = "https://files.pythonhosted.org/packages/e6/b6/072a8e053ae600dcc2ac0da81a23548e3b523301a442a6ca900e92ac35be/tokenizers-0.21.1-cp39-abi3-win_amd64.whl", hash = "sha256:0f0dcbcc9f6e13e675a66d7a5f2f225a736745ce484c1a4e07476a89ccdad382", size = 2435481 }, + { url = "https://files.pythonhosted.org/packages/c9/85/b93e68532f53efdeb16d0a4cd40ce8e0c8c701d0c7c279ce02ec986b7b44/tokenizers-0.21.4.dev0-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:2f11947acd633175c0097184d3616a5708ba16b0e608b729fb9a668ceb84412f", size = 2864738 }, + { url = "https://files.pythonhosted.org/packages/fb/19/01b89568b4788c5efa03bd9abaf6cd4b3a5c987511d108b3a7aba0f035be/tokenizers-0.21.4.dev0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:db4d5148e904006242bc6a83a9e2778389b231c5782f7627f1e768b0b1eff964", size = 2731650 }, + { url = "https://files.pythonhosted.org/packages/e6/eb/7cae36ef668d478138ab2a1b757efc9fc09b913d774d397d25986c9ee945/tokenizers-0.21.4.dev0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8348601d6dda43a8878f48f07ef356070317f24c58984deb946c3789df99563c", size = 3012808 }, + { url = "https://files.pythonhosted.org/packages/ff/63/00ab414f73fc0bf1579622f917e585a5e4229f16a4bf1e6f07833179b745/tokenizers-0.21.4.dev0-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:587ec122c84aa2237d82ec86479e772f441671ee88587a8b673d471b5f69eb71", size = 2938134 }, + { url = "https://files.pythonhosted.org/packages/60/2c/86197c8e9503ce2f29a95af03a4ad31451dab04afa29db89a8fbabc887f7/tokenizers-0.21.4.dev0-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b6ea5b7ac38274e05587929b1d4f86e4c05304c91e2f3ec05510c6d1f424d035", size = 3248144 }, + { url = "https://files.pythonhosted.org/packages/da/a5/afd87899925f02703f99710ed5d2304be84220e5aa9d42a51a5b42f1c1eb/tokenizers-0.21.4.dev0-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1515ee73fb66d77805ea5a226921c790fb7013c3d1b7c89ba329f10c59fcb69e", size = 3428707 }, + { url = "https://files.pythonhosted.org/packages/2d/e8/43d4c0c319d3a90f1e1be6e8b438b4ccb0050d07a00d9b038f832c25b997/tokenizers-0.21.4.dev0-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7847a14ea2508b021797f9099694425df7ef07275b46b4bd5a3ea8bb0d075a7d", size = 3192848 }, + { url = "https://files.pythonhosted.org/packages/a4/f7/f4253f6d82cce668a39fd018c05e28d7786a15394cffbf0caeed1deb2608/tokenizers-0.21.4.dev0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5f1fa2acd44f27b0caaf0f3a07b93b8daa2601486eee526ebd6dd3c696048e79", size = 3115834 }, + { url = "https://files.pythonhosted.org/packages/f5/4f/c21ef22c4d6ea7bad0bcc79be4be3d13e259b4f82ad22bcb7504698c661e/tokenizers-0.21.4.dev0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:21ecf0d02607207d7339a48cb30f215da5f43c495dd5310666aebe8464828f58", size = 9087920 }, + { url = "https://files.pythonhosted.org/packages/59/f6/1a6821e3a5b8dda3be0c7f02c526119a39275c203f26365903a5f05eb29d/tokenizers-0.21.4.dev0-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:485a6bdb4f753289d3068aca8a143614baade79f1441a97225fc97e1763e8bba", size = 9053683 }, + { url = "https://files.pythonhosted.org/packages/d9/db/cf8a05f0d67607633156f1663fe6b6f75d1cb38874a3cf46db2a9763a270/tokenizers-0.21.4.dev0-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:12d5b4decf63a7393cf10494282164797d2a1f9e70e95b0960e8e32a3993cc6d", size = 9297385 }, + { url = "https://files.pythonhosted.org/packages/a5/73/b3b3fe4a989fff4fb66a33261e534205931cb03e74e4fd6c2168153d01af/tokenizers-0.21.4.dev0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:5351ebc4522bf7965129a54706e030a24491526726a244fe7552e9bea0d5fec2", size = 9464148 }, + { url = "https://files.pythonhosted.org/packages/bb/ba/ac9ae487b85450e470b9ff03e5bc9f3c98936d0738299f8ebc2ac3d63d65/tokenizers-0.21.4.dev0-cp39-abi3-win32.whl", hash = "sha256:ceca51b3922325bffd63d06c42f0c8f1ca5667463f78b1fdf5fdb2d4e7cf66f5", size = 2330769 }, + { url = "https://files.pythonhosted.org/packages/5e/86/a1954d11d0a635a5596bd42b9746ea81f02fdbb97831fee29ef73652594c/tokenizers-0.21.4.dev0-cp39-abi3-win_amd64.whl", hash = "sha256:7dc8a83d21035b38ded1e74b29d6d85a686bc5056c30c92ac1cf348de9a1625d", size = 2506854 }, ] [[package]] @@ -3800,13 +4253,22 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", size = 78540 }, ] +[[package]] +name = "traitlets" +version = "5.14.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/eb/79/72064e6a701c2183016abbbfedaba506d81e30e232a68c9f0d6f6fcd1574/traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7", size = 161621 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f", size = 85359 }, +] + [[package]] name = "typing-extensions" -version = "4.14.0" +version = "4.14.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d1/bc/51647cd02527e87d05cb083ccc402f93e441606ff1f01739a62c8ad09ba5/typing_extensions-4.14.0.tar.gz", hash = "sha256:8676b788e32f02ab42d9e7c61324048ae4c6d844a399eebace3d4979d75ceef4", size = 107423 } +sdist = { url = "https://files.pythonhosted.org/packages/98/5a/da40306b885cc8c09109dc2e1abd358d5684b1425678151cdaed4731c822/typing_extensions-4.14.1.tar.gz", hash = "sha256:38b39f4aeeab64884ce9f74c94263ef78f3c22467c8724005483154c26648d36", size = 107673 } wheels = [ - { url = "https://files.pythonhosted.org/packages/69/e0/552843e0d356fbb5256d21449fa957fa4eff3bbc135a74a691ee70c7c5da/typing_extensions-4.14.0-py3-none-any.whl", hash = "sha256:a1514509136dd0b477638fc68d6a91497af5076466ad0fa6c338e44e359944af", size = 43839 }, + { url = "https://files.pythonhosted.org/packages/b5/00/d631e67a838026495268c2f6884f3711a15a9a2a96cd244fdaea53b823fb/typing_extensions-4.14.1-py3-none-any.whl", hash = "sha256:d1e1e3b58374dc93031d6eda2420a48ea44a36c2b4766a4fdeb3710755731d76", size = 43906 }, ] [[package]] @@ -3866,11 +4328,11 @@ wheels = [ [[package]] name = "urllib3" -version = "2.4.0" +version = "2.5.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8a/78/16493d9c386d8e60e442a35feac5e00f0913c0f4b7c217c11e8ec2ff53e0/urllib3-2.4.0.tar.gz", hash = "sha256:414bc6535b787febd7567804cc015fee39daab8ad86268f1310a9250697de466", size = 390672 } +sdist = { url = "https://files.pythonhosted.org/packages/15/22/9ee70a2574a4f4599c47dd506532914ce044817c7752a79b6a51286319bc/urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760", size = 393185 } wheels = [ - { url = "https://files.pythonhosted.org/packages/6b/11/cc635220681e93a0183390e26485430ca2c7b5f9d33b15c74c2861cb8091/urllib3-2.4.0-py3-none-any.whl", hash = "sha256:4e16665048960a0900c702d4a66415956a584919c03361cac9f1df5c5dd7e813", size = 128680 }, + { url = "https://files.pythonhosted.org/packages/a7/c2/fe1e52489ae3122415c51f387e221dd0773709bad6c6cdaa599e8a2c5185/urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc", size = 129795 }, ] [[package]] @@ -3930,69 +4392,118 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/63/9a/0962b05b308494e3202d3f794a6e85abe471fe3cafdbcf95c2e8c713aabd/uvloop-0.21.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a5c39f217ab3c663dc699c04cbd50c13813e31d917642d459fdcec07555cc553", size = 4660018 }, ] +[[package]] +name = "virtualenv" +version = "20.32.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "distlib" }, + { name = "filelock" }, + { name = "platformdirs" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a9/96/0834f30fa08dca3738614e6a9d42752b6420ee94e58971d702118f7cfd30/virtualenv-20.32.0.tar.gz", hash = "sha256:886bf75cadfdc964674e6e33eb74d787dff31ca314ceace03ca5810620f4ecf0", size = 6076970 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5c/c6/f8f28009920a736d0df434b52e9feebfb4d702ba942f15338cb4a83eafc1/virtualenv-20.32.0-py3-none-any.whl", hash = "sha256:2c310aecb62e5aa1b06103ed7c2977b81e042695de2697d01017ff0f1034af56", size = 6057761 }, +] + [[package]] name = "watchfiles" -version = "1.0.5" +version = "1.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/03/e2/8ed598c42057de7aa5d97c472254af4906ff0a59a66699d426fc9ef795d7/watchfiles-1.0.5.tar.gz", hash = "sha256:b7529b5dcc114679d43827d8c35a07c493ad6f083633d573d81c660abc5979e9", size = 94537 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/af/4d/d02e6ea147bb7fff5fd109c694a95109612f419abed46548a930e7f7afa3/watchfiles-1.0.5-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:5c40fe7dd9e5f81e0847b1ea64e1f5dd79dd61afbedb57759df06767ac719b40", size = 405632 }, - { url = "https://files.pythonhosted.org/packages/60/31/9ee50e29129d53a9a92ccf1d3992751dc56fc3c8f6ee721be1c7b9c81763/watchfiles-1.0.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8c0db396e6003d99bb2d7232c957b5f0b5634bbd1b24e381a5afcc880f7373fb", size = 395734 }, - { url = "https://files.pythonhosted.org/packages/ad/8c/759176c97195306f028024f878e7f1c776bda66ccc5c68fa51e699cf8f1d/watchfiles-1.0.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b551d4fb482fc57d852b4541f911ba28957d051c8776e79c3b4a51eb5e2a1b11", size = 455008 }, - { url = "https://files.pythonhosted.org/packages/55/1a/5e977250c795ee79a0229e3b7f5e3a1b664e4e450756a22da84d2f4979fe/watchfiles-1.0.5-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:830aa432ba5c491d52a15b51526c29e4a4b92bf4f92253787f9726fe01519487", size = 459029 }, - { url = "https://files.pythonhosted.org/packages/e6/17/884cf039333605c1d6e296cf5be35fad0836953c3dfd2adb71b72f9dbcd0/watchfiles-1.0.5-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a16512051a822a416b0d477d5f8c0e67b67c1a20d9acecb0aafa3aa4d6e7d256", size = 488916 }, - { url = "https://files.pythonhosted.org/packages/ef/e0/bcb6e64b45837056c0a40f3a2db3ef51c2ced19fda38484fa7508e00632c/watchfiles-1.0.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bfe0cbc787770e52a96c6fda6726ace75be7f840cb327e1b08d7d54eadc3bc85", size = 523763 }, - { url = "https://files.pythonhosted.org/packages/24/e9/f67e9199f3bb35c1837447ecf07e9830ec00ff5d35a61e08c2cd67217949/watchfiles-1.0.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d363152c5e16b29d66cbde8fa614f9e313e6f94a8204eaab268db52231fe5358", size = 502891 }, - { url = "https://files.pythonhosted.org/packages/23/ed/a6cf815f215632f5c8065e9c41fe872025ffea35aa1f80499f86eae922db/watchfiles-1.0.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ee32c9a9bee4d0b7bd7cbeb53cb185cf0b622ac761efaa2eba84006c3b3a614", size = 454921 }, - { url = "https://files.pythonhosted.org/packages/92/4c/e14978599b80cde8486ab5a77a821e8a982ae8e2fcb22af7b0886a033ec8/watchfiles-1.0.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:29c7fd632ccaf5517c16a5188e36f6612d6472ccf55382db6c7fe3fcccb7f59f", size = 631422 }, - { url = "https://files.pythonhosted.org/packages/b2/1a/9263e34c3458f7614b657f974f4ee61fd72f58adce8b436e16450e054efd/watchfiles-1.0.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8e637810586e6fe380c8bc1b3910accd7f1d3a9a7262c8a78d4c8fb3ba6a2b3d", size = 625675 }, - { url = "https://files.pythonhosted.org/packages/96/1f/1803a18bd6ab04a0766386a19bcfe64641381a04939efdaa95f0e3b0eb58/watchfiles-1.0.5-cp310-cp310-win32.whl", hash = "sha256:cd47d063fbeabd4c6cae1d4bcaa38f0902f8dc5ed168072874ea11d0c7afc1ff", size = 277921 }, - { url = "https://files.pythonhosted.org/packages/c2/3b/29a89de074a7d6e8b4dc67c26e03d73313e4ecf0d6e97e942a65fa7c195e/watchfiles-1.0.5-cp310-cp310-win_amd64.whl", hash = "sha256:86c0df05b47a79d80351cd179893f2f9c1b1cae49d96e8b3290c7f4bd0ca0a92", size = 291526 }, - { url = "https://files.pythonhosted.org/packages/39/f4/41b591f59021786ef517e1cdc3b510383551846703e03f204827854a96f8/watchfiles-1.0.5-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:237f9be419e977a0f8f6b2e7b0475ababe78ff1ab06822df95d914a945eac827", size = 405336 }, - { url = "https://files.pythonhosted.org/packages/ae/06/93789c135be4d6d0e4f63e96eea56dc54050b243eacc28439a26482b5235/watchfiles-1.0.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e0da39ff917af8b27a4bdc5a97ac577552a38aac0d260a859c1517ea3dc1a7c4", size = 395977 }, - { url = "https://files.pythonhosted.org/packages/d2/db/1cd89bd83728ca37054512d4d35ab69b5f12b8aa2ac9be3b0276b3bf06cc/watchfiles-1.0.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cfcb3952350e95603f232a7a15f6c5f86c5375e46f0bd4ae70d43e3e063c13d", size = 455232 }, - { url = "https://files.pythonhosted.org/packages/40/90/d8a4d44ffe960517e487c9c04f77b06b8abf05eb680bed71c82b5f2cad62/watchfiles-1.0.5-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:68b2dddba7a4e6151384e252a5632efcaa9bc5d1c4b567f3cb621306b2ca9f63", size = 459151 }, - { url = "https://files.pythonhosted.org/packages/6c/da/267a1546f26465dead1719caaba3ce660657f83c9d9c052ba98fb8856e13/watchfiles-1.0.5-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:95cf944fcfc394c5f9de794ce581914900f82ff1f855326f25ebcf24d5397418", size = 489054 }, - { url = "https://files.pythonhosted.org/packages/b1/31/33850dfd5c6efb6f27d2465cc4c6b27c5a6f5ed53c6fa63b7263cf5f60f6/watchfiles-1.0.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ecf6cd9f83d7c023b1aba15d13f705ca7b7d38675c121f3cc4a6e25bd0857ee9", size = 523955 }, - { url = "https://files.pythonhosted.org/packages/09/84/b7d7b67856efb183a421f1416b44ca975cb2ea6c4544827955dfb01f7dc2/watchfiles-1.0.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:852de68acd6212cd6d33edf21e6f9e56e5d98c6add46f48244bd479d97c967c6", size = 502234 }, - { url = "https://files.pythonhosted.org/packages/71/87/6dc5ec6882a2254cfdd8b0718b684504e737273903b65d7338efaba08b52/watchfiles-1.0.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d5730f3aa35e646103b53389d5bc77edfbf578ab6dab2e005142b5b80a35ef25", size = 454750 }, - { url = "https://files.pythonhosted.org/packages/3d/6c/3786c50213451a0ad15170d091570d4a6554976cf0df19878002fc96075a/watchfiles-1.0.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:18b3bd29954bc4abeeb4e9d9cf0b30227f0f206c86657674f544cb032296acd5", size = 631591 }, - { url = "https://files.pythonhosted.org/packages/1b/b3/1427425ade4e359a0deacce01a47a26024b2ccdb53098f9d64d497f6684c/watchfiles-1.0.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ba5552a1b07c8edbf197055bc9d518b8f0d98a1c6a73a293bc0726dce068ed01", size = 625370 }, - { url = "https://files.pythonhosted.org/packages/15/ba/f60e053b0b5b8145d682672024aa91370a29c5c921a88977eb565de34086/watchfiles-1.0.5-cp311-cp311-win32.whl", hash = "sha256:2f1fefb2e90e89959447bc0420fddd1e76f625784340d64a2f7d5983ef9ad246", size = 277791 }, - { url = "https://files.pythonhosted.org/packages/50/ed/7603c4e164225c12c0d4e8700b64bb00e01a6c4eeea372292a3856be33a4/watchfiles-1.0.5-cp311-cp311-win_amd64.whl", hash = "sha256:b6e76ceb1dd18c8e29c73f47d41866972e891fc4cc7ba014f487def72c1cf096", size = 291622 }, - { url = "https://files.pythonhosted.org/packages/a2/c2/99bb7c96b4450e36877fde33690ded286ff555b5a5c1d925855d556968a1/watchfiles-1.0.5-cp311-cp311-win_arm64.whl", hash = "sha256:266710eb6fddc1f5e51843c70e3bebfb0f5e77cf4f27129278c70554104d19ed", size = 283699 }, - { url = "https://files.pythonhosted.org/packages/2a/8c/4f0b9bdb75a1bfbd9c78fad7d8854369283f74fe7cf03eb16be77054536d/watchfiles-1.0.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:b5eb568c2aa6018e26da9e6c86f3ec3fd958cee7f0311b35c2630fa4217d17f2", size = 401511 }, - { url = "https://files.pythonhosted.org/packages/dc/4e/7e15825def77f8bd359b6d3f379f0c9dac4eb09dd4ddd58fd7d14127179c/watchfiles-1.0.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0a04059f4923ce4e856b4b4e5e783a70f49d9663d22a4c3b3298165996d1377f", size = 392715 }, - { url = "https://files.pythonhosted.org/packages/58/65/b72fb817518728e08de5840d5d38571466c1b4a3f724d190cec909ee6f3f/watchfiles-1.0.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e380c89983ce6e6fe2dd1e1921b9952fb4e6da882931abd1824c092ed495dec", size = 454138 }, - { url = "https://files.pythonhosted.org/packages/3e/a4/86833fd2ea2e50ae28989f5950b5c3f91022d67092bfec08f8300d8b347b/watchfiles-1.0.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fe43139b2c0fdc4a14d4f8d5b5d967f7a2777fd3d38ecf5b1ec669b0d7e43c21", size = 458592 }, - { url = "https://files.pythonhosted.org/packages/38/7e/42cb8df8be9a37e50dd3a818816501cf7a20d635d76d6bd65aae3dbbff68/watchfiles-1.0.5-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee0822ce1b8a14fe5a066f93edd20aada932acfe348bede8aa2149f1a4489512", size = 487532 }, - { url = "https://files.pythonhosted.org/packages/fc/fd/13d26721c85d7f3df6169d8b495fcac8ab0dc8f0945ebea8845de4681dab/watchfiles-1.0.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a0dbcb1c2d8f2ab6e0a81c6699b236932bd264d4cef1ac475858d16c403de74d", size = 522865 }, - { url = "https://files.pythonhosted.org/packages/a1/0d/7f9ae243c04e96c5455d111e21b09087d0eeaf9a1369e13a01c7d3d82478/watchfiles-1.0.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a2014a2b18ad3ca53b1f6c23f8cd94a18ce930c1837bd891262c182640eb40a6", size = 499887 }, - { url = "https://files.pythonhosted.org/packages/8e/0f/a257766998e26aca4b3acf2ae97dff04b57071e991a510857d3799247c67/watchfiles-1.0.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10f6ae86d5cb647bf58f9f655fcf577f713915a5d69057a0371bc257e2553234", size = 454498 }, - { url = "https://files.pythonhosted.org/packages/81/79/8bf142575a03e0af9c3d5f8bcae911ee6683ae93a625d349d4ecf4c8f7df/watchfiles-1.0.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1a7bac2bde1d661fb31f4d4e8e539e178774b76db3c2c17c4bb3e960a5de07a2", size = 630663 }, - { url = "https://files.pythonhosted.org/packages/f1/80/abe2e79f610e45c63a70d271caea90c49bbf93eb00fa947fa9b803a1d51f/watchfiles-1.0.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ab626da2fc1ac277bbf752446470b367f84b50295264d2d313e28dc4405d663", size = 625410 }, - { url = "https://files.pythonhosted.org/packages/91/6f/bc7fbecb84a41a9069c2c6eb6319f7f7df113adf113e358c57fc1aff7ff5/watchfiles-1.0.5-cp312-cp312-win32.whl", hash = "sha256:9f4571a783914feda92018ef3901dab8caf5b029325b5fe4558c074582815249", size = 277965 }, - { url = "https://files.pythonhosted.org/packages/99/a5/bf1c297ea6649ec59e935ab311f63d8af5faa8f0b86993e3282b984263e3/watchfiles-1.0.5-cp312-cp312-win_amd64.whl", hash = "sha256:360a398c3a19672cf93527f7e8d8b60d8275119c5d900f2e184d32483117a705", size = 291693 }, - { url = "https://files.pythonhosted.org/packages/7f/7b/fd01087cc21db5c47e5beae507b87965db341cce8a86f9eb12bf5219d4e0/watchfiles-1.0.5-cp312-cp312-win_arm64.whl", hash = "sha256:1a2902ede862969077b97523987c38db28abbe09fb19866e711485d9fbf0d417", size = 283287 }, - { url = "https://files.pythonhosted.org/packages/c7/62/435766874b704f39b2fecd8395a29042db2b5ec4005bd34523415e9bd2e0/watchfiles-1.0.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:0b289572c33a0deae62daa57e44a25b99b783e5f7aed81b314232b3d3c81a11d", size = 401531 }, - { url = "https://files.pythonhosted.org/packages/6e/a6/e52a02c05411b9cb02823e6797ef9bbba0bfaf1bb627da1634d44d8af833/watchfiles-1.0.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a056c2f692d65bf1e99c41045e3bdcaea3cb9e6b5a53dcaf60a5f3bd95fc9763", size = 392417 }, - { url = "https://files.pythonhosted.org/packages/3f/53/c4af6819770455932144e0109d4854437769672d7ad897e76e8e1673435d/watchfiles-1.0.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9dca99744991fc9850d18015c4f0438865414e50069670f5f7eee08340d8b40", size = 453423 }, - { url = "https://files.pythonhosted.org/packages/cb/d1/8e88df58bbbf819b8bc5cfbacd3c79e01b40261cad0fc84d1e1ebd778a07/watchfiles-1.0.5-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:894342d61d355446d02cd3988a7326af344143eb33a2fd5d38482a92072d9563", size = 458185 }, - { url = "https://files.pythonhosted.org/packages/ff/70/fffaa11962dd5429e47e478a18736d4e42bec42404f5ee3b92ef1b87ad60/watchfiles-1.0.5-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ab44e1580924d1ffd7b3938e02716d5ad190441965138b4aa1d1f31ea0877f04", size = 486696 }, - { url = "https://files.pythonhosted.org/packages/39/db/723c0328e8b3692d53eb273797d9a08be6ffb1d16f1c0ba2bdbdc2a3852c/watchfiles-1.0.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d6f9367b132078b2ceb8d066ff6c93a970a18c3029cea37bfd7b2d3dd2e5db8f", size = 522327 }, - { url = "https://files.pythonhosted.org/packages/cd/05/9fccc43c50c39a76b68343484b9da7b12d42d0859c37c61aec018c967a32/watchfiles-1.0.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f2e55a9b162e06e3f862fb61e399fe9f05d908d019d87bf5b496a04ef18a970a", size = 499741 }, - { url = "https://files.pythonhosted.org/packages/23/14/499e90c37fa518976782b10a18b18db9f55ea73ca14641615056f8194bb3/watchfiles-1.0.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0125f91f70e0732a9f8ee01e49515c35d38ba48db507a50c5bdcad9503af5827", size = 453995 }, - { url = "https://files.pythonhosted.org/packages/61/d9/f75d6840059320df5adecd2c687fbc18960a7f97b55c300d20f207d48aef/watchfiles-1.0.5-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:13bb21f8ba3248386337c9fa51c528868e6c34a707f729ab041c846d52a0c69a", size = 629693 }, - { url = "https://files.pythonhosted.org/packages/fc/17/180ca383f5061b61406477218c55d66ec118e6c0c51f02d8142895fcf0a9/watchfiles-1.0.5-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:839ebd0df4a18c5b3c1b890145b5a3f5f64063c2a0d02b13c76d78fe5de34936", size = 624677 }, - { url = "https://files.pythonhosted.org/packages/bf/15/714d6ef307f803f236d69ee9d421763707899d6298d9f3183e55e366d9af/watchfiles-1.0.5-cp313-cp313-win32.whl", hash = "sha256:4a8ec1e4e16e2d5bafc9ba82f7aaecfeec990ca7cd27e84fb6f191804ed2fcfc", size = 277804 }, - { url = "https://files.pythonhosted.org/packages/a8/b4/c57b99518fadf431f3ef47a610839e46e5f8abf9814f969859d1c65c02c7/watchfiles-1.0.5-cp313-cp313-win_amd64.whl", hash = "sha256:f436601594f15bf406518af922a89dcaab416568edb6f65c4e5bbbad1ea45c11", size = 291087 }, - { url = "https://files.pythonhosted.org/packages/1a/03/81f9fcc3963b3fc415cd4b0b2b39ee8cc136c42fb10a36acf38745e9d283/watchfiles-1.0.5-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f59b870db1f1ae5a9ac28245707d955c8721dd6565e7f411024fa374b5362d1d", size = 405947 }, - { url = "https://files.pythonhosted.org/packages/54/97/8c4213a852feb64807ec1d380f42d4fc8bfaef896bdbd94318f8fd7f3e4e/watchfiles-1.0.5-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:9475b0093767e1475095f2aeb1d219fb9664081d403d1dff81342df8cd707034", size = 397276 }, - { url = "https://files.pythonhosted.org/packages/78/12/d4464d19860cb9672efa45eec1b08f8472c478ed67dcd30647c51ada7aef/watchfiles-1.0.5-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc533aa50664ebd6c628b2f30591956519462f5d27f951ed03d6c82b2dfd9965", size = 455550 }, - { url = "https://files.pythonhosted.org/packages/90/fb/b07bcdf1034d8edeaef4c22f3e9e3157d37c5071b5f9492ffdfa4ad4bed7/watchfiles-1.0.5-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fed1cd825158dcaae36acce7b2db33dcbfd12b30c34317a88b8ed80f0541cc57", size = 455542 }, +sdist = { url = "https://files.pythonhosted.org/packages/2a/9a/d451fcc97d029f5812e898fd30a53fd8c15c7bbd058fd75cfc6beb9bd761/watchfiles-1.1.0.tar.gz", hash = "sha256:693ed7ec72cbfcee399e92c895362b6e66d63dac6b91e2c11ae03d10d503e575", size = 94406 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b9/dd/579d1dc57f0f895426a1211c4ef3b0cb37eb9e642bb04bdcd962b5df206a/watchfiles-1.1.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:27f30e14aa1c1e91cb653f03a63445739919aef84c8d2517997a83155e7a2fcc", size = 405757 }, + { url = "https://files.pythonhosted.org/packages/1c/a0/7a0318cd874393344d48c34d53b3dd419466adf59a29ba5b51c88dd18b86/watchfiles-1.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3366f56c272232860ab45c77c3ca7b74ee819c8e1f6f35a7125556b198bbc6df", size = 397511 }, + { url = "https://files.pythonhosted.org/packages/06/be/503514656d0555ec2195f60d810eca29b938772e9bfb112d5cd5ad6f6a9e/watchfiles-1.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8412eacef34cae2836d891836a7fff7b754d6bcac61f6c12ba5ca9bc7e427b68", size = 450739 }, + { url = "https://files.pythonhosted.org/packages/4e/0d/a05dd9e5f136cdc29751816d0890d084ab99f8c17b86f25697288ca09bc7/watchfiles-1.1.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:df670918eb7dd719642e05979fc84704af913d563fd17ed636f7c4783003fdcc", size = 458106 }, + { url = "https://files.pythonhosted.org/packages/f1/fa/9cd16e4dfdb831072b7ac39e7bea986e52128526251038eb481effe9f48e/watchfiles-1.1.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d7642b9bc4827b5518ebdb3b82698ada8c14c7661ddec5fe719f3e56ccd13c97", size = 484264 }, + { url = "https://files.pythonhosted.org/packages/32/04/1da8a637c7e2b70e750a0308e9c8e662ada0cca46211fa9ef24a23937e0b/watchfiles-1.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:199207b2d3eeaeb80ef4411875a6243d9ad8bc35b07fc42daa6b801cc39cc41c", size = 597612 }, + { url = "https://files.pythonhosted.org/packages/30/01/109f2762e968d3e58c95731a206e5d7d2a7abaed4299dd8a94597250153c/watchfiles-1.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a479466da6db5c1e8754caee6c262cd373e6e6c363172d74394f4bff3d84d7b5", size = 477242 }, + { url = "https://files.pythonhosted.org/packages/b5/b8/46f58cf4969d3b7bc3ca35a98e739fa4085b0657a1540ccc29a1a0bc016f/watchfiles-1.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:935f9edd022ec13e447e5723a7d14456c8af254544cefbc533f6dd276c9aa0d9", size = 453148 }, + { url = "https://files.pythonhosted.org/packages/a5/cd/8267594263b1770f1eb76914940d7b2d03ee55eca212302329608208e061/watchfiles-1.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:8076a5769d6bdf5f673a19d51da05fc79e2bbf25e9fe755c47595785c06a8c72", size = 626574 }, + { url = "https://files.pythonhosted.org/packages/a1/2f/7f2722e85899bed337cba715723e19185e288ef361360718973f891805be/watchfiles-1.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:86b1e28d4c37e89220e924305cd9f82866bb0ace666943a6e4196c5df4d58dcc", size = 624378 }, + { url = "https://files.pythonhosted.org/packages/bf/20/64c88ec43d90a568234d021ab4b2a6f42a5230d772b987c3f9c00cc27b8b/watchfiles-1.1.0-cp310-cp310-win32.whl", hash = "sha256:d1caf40c1c657b27858f9774d5c0e232089bca9cb8ee17ce7478c6e9264d2587", size = 279829 }, + { url = "https://files.pythonhosted.org/packages/39/5c/a9c1ed33de7af80935e4eac09570de679c6e21c07070aa99f74b4431f4d6/watchfiles-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:a89c75a5b9bc329131115a409d0acc16e8da8dfd5867ba59f1dd66ae7ea8fa82", size = 292192 }, + { url = "https://files.pythonhosted.org/packages/8b/78/7401154b78ab484ccaaeef970dc2af0cb88b5ba8a1b415383da444cdd8d3/watchfiles-1.1.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:c9649dfc57cc1f9835551deb17689e8d44666315f2e82d337b9f07bd76ae3aa2", size = 405751 }, + { url = "https://files.pythonhosted.org/packages/76/63/e6c3dbc1f78d001589b75e56a288c47723de28c580ad715eb116639152b5/watchfiles-1.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:406520216186b99374cdb58bc48e34bb74535adec160c8459894884c983a149c", size = 397313 }, + { url = "https://files.pythonhosted.org/packages/6c/a2/8afa359ff52e99af1632f90cbf359da46184207e893a5f179301b0c8d6df/watchfiles-1.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb45350fd1dc75cd68d3d72c47f5b513cb0578da716df5fba02fff31c69d5f2d", size = 450792 }, + { url = "https://files.pythonhosted.org/packages/1d/bf/7446b401667f5c64972a57a0233be1104157fc3abf72c4ef2666c1bd09b2/watchfiles-1.1.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:11ee4444250fcbeb47459a877e5e80ed994ce8e8d20283857fc128be1715dac7", size = 458196 }, + { url = "https://files.pythonhosted.org/packages/58/2f/501ddbdfa3fa874ea5597c77eeea3d413579c29af26c1091b08d0c792280/watchfiles-1.1.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bda8136e6a80bdea23e5e74e09df0362744d24ffb8cd59c4a95a6ce3d142f79c", size = 484788 }, + { url = "https://files.pythonhosted.org/packages/61/1e/9c18eb2eb5c953c96bc0e5f626f0e53cfef4bd19bd50d71d1a049c63a575/watchfiles-1.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b915daeb2d8c1f5cee4b970f2e2c988ce6514aace3c9296e58dd64dc9aa5d575", size = 597879 }, + { url = "https://files.pythonhosted.org/packages/8b/6c/1467402e5185d89388b4486745af1e0325007af0017c3384cc786fff0542/watchfiles-1.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ed8fc66786de8d0376f9f913c09e963c66e90ced9aa11997f93bdb30f7c872a8", size = 477447 }, + { url = "https://files.pythonhosted.org/packages/2b/a1/ec0a606bde4853d6c4a578f9391eeb3684a9aea736a8eb217e3e00aa89a1/watchfiles-1.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe4371595edf78c41ef8ac8df20df3943e13defd0efcb732b2e393b5a8a7a71f", size = 453145 }, + { url = "https://files.pythonhosted.org/packages/90/b9/ef6f0c247a6a35d689fc970dc7f6734f9257451aefb30def5d100d6246a5/watchfiles-1.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b7c5f6fe273291f4d414d55b2c80d33c457b8a42677ad14b4b47ff025d0893e4", size = 626539 }, + { url = "https://files.pythonhosted.org/packages/34/44/6ffda5537085106ff5aaa762b0d130ac6c75a08015dd1621376f708c94de/watchfiles-1.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7738027989881e70e3723c75921f1efa45225084228788fc59ea8c6d732eb30d", size = 624472 }, + { url = "https://files.pythonhosted.org/packages/c3/e3/71170985c48028fa3f0a50946916a14055e741db11c2e7bc2f3b61f4d0e3/watchfiles-1.1.0-cp311-cp311-win32.whl", hash = "sha256:622d6b2c06be19f6e89b1d951485a232e3b59618def88dbeda575ed8f0d8dbf2", size = 279348 }, + { url = "https://files.pythonhosted.org/packages/89/1b/3e39c68b68a7a171070f81fc2561d23ce8d6859659406842a0e4bebf3bba/watchfiles-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:48aa25e5992b61debc908a61ab4d3f216b64f44fdaa71eb082d8b2de846b7d12", size = 292607 }, + { url = "https://files.pythonhosted.org/packages/61/9f/2973b7539f2bdb6ea86d2c87f70f615a71a1fc2dba2911795cea25968aea/watchfiles-1.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:00645eb79a3faa70d9cb15c8d4187bb72970b2470e938670240c7998dad9f13a", size = 285056 }, + { url = "https://files.pythonhosted.org/packages/f6/b8/858957045a38a4079203a33aaa7d23ea9269ca7761c8a074af3524fbb240/watchfiles-1.1.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9dc001c3e10de4725c749d4c2f2bdc6ae24de5a88a339c4bce32300a31ede179", size = 402339 }, + { url = "https://files.pythonhosted.org/packages/80/28/98b222cca751ba68e88521fabd79a4fab64005fc5976ea49b53fa205d1fa/watchfiles-1.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d9ba68ec283153dead62cbe81872d28e053745f12335d037de9cbd14bd1877f5", size = 394409 }, + { url = "https://files.pythonhosted.org/packages/86/50/dee79968566c03190677c26f7f47960aff738d32087087bdf63a5473e7df/watchfiles-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:130fc497b8ee68dce163e4254d9b0356411d1490e868bd8790028bc46c5cc297", size = 450939 }, + { url = "https://files.pythonhosted.org/packages/40/45/a7b56fb129700f3cfe2594a01aa38d033b92a33dddce86c8dfdfc1247b72/watchfiles-1.1.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:50a51a90610d0845a5931a780d8e51d7bd7f309ebc25132ba975aca016b576a0", size = 457270 }, + { url = "https://files.pythonhosted.org/packages/b5/c8/fa5ef9476b1d02dc6b5e258f515fcaaecf559037edf8b6feffcbc097c4b8/watchfiles-1.1.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dc44678a72ac0910bac46fa6a0de6af9ba1355669b3dfaf1ce5f05ca7a74364e", size = 483370 }, + { url = "https://files.pythonhosted.org/packages/98/68/42cfcdd6533ec94f0a7aab83f759ec11280f70b11bfba0b0f885e298f9bd/watchfiles-1.1.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a543492513a93b001975ae283a51f4b67973662a375a403ae82f420d2c7205ee", size = 598654 }, + { url = "https://files.pythonhosted.org/packages/d3/74/b2a1544224118cc28df7e59008a929e711f9c68ce7d554e171b2dc531352/watchfiles-1.1.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ac164e20d17cc285f2b94dc31c384bc3aa3dd5e7490473b3db043dd70fbccfd", size = 478667 }, + { url = "https://files.pythonhosted.org/packages/8c/77/e3362fe308358dc9f8588102481e599c83e1b91c2ae843780a7ded939a35/watchfiles-1.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f7590d5a455321e53857892ab8879dce62d1f4b04748769f5adf2e707afb9d4f", size = 452213 }, + { url = "https://files.pythonhosted.org/packages/6e/17/c8f1a36540c9a1558d4faf08e909399e8133599fa359bf52ec8fcee5be6f/watchfiles-1.1.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:37d3d3f7defb13f62ece99e9be912afe9dd8a0077b7c45ee5a57c74811d581a4", size = 626718 }, + { url = "https://files.pythonhosted.org/packages/26/45/fb599be38b4bd38032643783d7496a26a6f9ae05dea1a42e58229a20ac13/watchfiles-1.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:7080c4bb3efd70a07b1cc2df99a7aa51d98685be56be6038c3169199d0a1c69f", size = 623098 }, + { url = "https://files.pythonhosted.org/packages/a1/e7/fdf40e038475498e160cd167333c946e45d8563ae4dd65caf757e9ffe6b4/watchfiles-1.1.0-cp312-cp312-win32.whl", hash = "sha256:cbcf8630ef4afb05dc30107bfa17f16c0896bb30ee48fc24bf64c1f970f3b1fd", size = 279209 }, + { url = "https://files.pythonhosted.org/packages/3f/d3/3ae9d5124ec75143bdf088d436cba39812122edc47709cd2caafeac3266f/watchfiles-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:cbd949bdd87567b0ad183d7676feb98136cde5bb9025403794a4c0db28ed3a47", size = 292786 }, + { url = "https://files.pythonhosted.org/packages/26/2f/7dd4fc8b5f2b34b545e19629b4a018bfb1de23b3a496766a2c1165ca890d/watchfiles-1.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:0a7d40b77f07be87c6faa93d0951a0fcd8cbca1ddff60a1b65d741bac6f3a9f6", size = 284343 }, + { url = "https://files.pythonhosted.org/packages/d3/42/fae874df96595556a9089ade83be34a2e04f0f11eb53a8dbf8a8a5e562b4/watchfiles-1.1.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:5007f860c7f1f8df471e4e04aaa8c43673429047d63205d1630880f7637bca30", size = 402004 }, + { url = "https://files.pythonhosted.org/packages/fa/55/a77e533e59c3003d9803c09c44c3651224067cbe7fb5d574ddbaa31e11ca/watchfiles-1.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:20ecc8abbd957046f1fe9562757903f5eaf57c3bce70929fda6c7711bb58074a", size = 393671 }, + { url = "https://files.pythonhosted.org/packages/05/68/b0afb3f79c8e832e6571022611adbdc36e35a44e14f129ba09709aa4bb7a/watchfiles-1.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2f0498b7d2a3c072766dba3274fe22a183dbea1f99d188f1c6c72209a1063dc", size = 449772 }, + { url = "https://files.pythonhosted.org/packages/ff/05/46dd1f6879bc40e1e74c6c39a1b9ab9e790bf1f5a2fe6c08b463d9a807f4/watchfiles-1.1.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:239736577e848678e13b201bba14e89718f5c2133dfd6b1f7846fa1b58a8532b", size = 456789 }, + { url = "https://files.pythonhosted.org/packages/8b/ca/0eeb2c06227ca7f12e50a47a3679df0cd1ba487ea19cf844a905920f8e95/watchfiles-1.1.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eff4b8d89f444f7e49136dc695599a591ff769300734446c0a86cba2eb2f9895", size = 482551 }, + { url = "https://files.pythonhosted.org/packages/31/47/2cecbd8694095647406645f822781008cc524320466ea393f55fe70eed3b/watchfiles-1.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:12b0a02a91762c08f7264e2e79542f76870c3040bbc847fb67410ab81474932a", size = 597420 }, + { url = "https://files.pythonhosted.org/packages/d9/7e/82abc4240e0806846548559d70f0b1a6dfdca75c1b4f9fa62b504ae9b083/watchfiles-1.1.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:29e7bc2eee15cbb339c68445959108803dc14ee0c7b4eea556400131a8de462b", size = 477950 }, + { url = "https://files.pythonhosted.org/packages/25/0d/4d564798a49bf5482a4fa9416dea6b6c0733a3b5700cb8a5a503c4b15853/watchfiles-1.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d9481174d3ed982e269c090f780122fb59cee6c3796f74efe74e70f7780ed94c", size = 451706 }, + { url = "https://files.pythonhosted.org/packages/81/b5/5516cf46b033192d544102ea07c65b6f770f10ed1d0a6d388f5d3874f6e4/watchfiles-1.1.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:80f811146831c8c86ab17b640801c25dc0a88c630e855e2bef3568f30434d52b", size = 625814 }, + { url = "https://files.pythonhosted.org/packages/0c/dd/7c1331f902f30669ac3e754680b6edb9a0dd06dea5438e61128111fadd2c/watchfiles-1.1.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:60022527e71d1d1fda67a33150ee42869042bce3d0fcc9cc49be009a9cded3fb", size = 622820 }, + { url = "https://files.pythonhosted.org/packages/1b/14/36d7a8e27cd128d7b1009e7715a7c02f6c131be9d4ce1e5c3b73d0e342d8/watchfiles-1.1.0-cp313-cp313-win32.whl", hash = "sha256:32d6d4e583593cb8576e129879ea0991660b935177c0f93c6681359b3654bfa9", size = 279194 }, + { url = "https://files.pythonhosted.org/packages/25/41/2dd88054b849aa546dbeef5696019c58f8e0774f4d1c42123273304cdb2e/watchfiles-1.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:f21af781a4a6fbad54f03c598ab620e3a77032c5878f3d780448421a6e1818c7", size = 292349 }, + { url = "https://files.pythonhosted.org/packages/c8/cf/421d659de88285eb13941cf11a81f875c176f76a6d99342599be88e08d03/watchfiles-1.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:5366164391873ed76bfdf618818c82084c9db7fac82b64a20c44d335eec9ced5", size = 283836 }, + { url = "https://files.pythonhosted.org/packages/45/10/6faf6858d527e3599cc50ec9fcae73590fbddc1420bd4fdccfebffeedbc6/watchfiles-1.1.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:17ab167cca6339c2b830b744eaf10803d2a5b6683be4d79d8475d88b4a8a4be1", size = 400343 }, + { url = "https://files.pythonhosted.org/packages/03/20/5cb7d3966f5e8c718006d0e97dfe379a82f16fecd3caa7810f634412047a/watchfiles-1.1.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:328dbc9bff7205c215a7807da7c18dce37da7da718e798356212d22696404339", size = 392916 }, + { url = "https://files.pythonhosted.org/packages/8c/07/d8f1176328fa9e9581b6f120b017e286d2a2d22ae3f554efd9515c8e1b49/watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f7208ab6e009c627b7557ce55c465c98967e8caa8b11833531fdf95799372633", size = 449582 }, + { url = "https://files.pythonhosted.org/packages/66/e8/80a14a453cf6038e81d072a86c05276692a1826471fef91df7537dba8b46/watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a8f6f72974a19efead54195bc9bed4d850fc047bb7aa971268fd9a8387c89011", size = 456752 }, + { url = "https://files.pythonhosted.org/packages/5a/25/0853b3fe0e3c2f5af9ea60eb2e781eade939760239a72c2d38fc4cc335f6/watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d181ef50923c29cf0450c3cd47e2f0557b62218c50b2ab8ce2ecaa02bd97e670", size = 481436 }, + { url = "https://files.pythonhosted.org/packages/fe/9e/4af0056c258b861fbb29dcb36258de1e2b857be4a9509e6298abcf31e5c9/watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:adb4167043d3a78280d5d05ce0ba22055c266cf8655ce942f2fb881262ff3cdf", size = 596016 }, + { url = "https://files.pythonhosted.org/packages/c5/fa/95d604b58aa375e781daf350897aaaa089cff59d84147e9ccff2447c8294/watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8c5701dc474b041e2934a26d31d39f90fac8a3dee2322b39f7729867f932b1d4", size = 476727 }, + { url = "https://files.pythonhosted.org/packages/65/95/fe479b2664f19be4cf5ceeb21be05afd491d95f142e72d26a42f41b7c4f8/watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b067915e3c3936966a8607f6fe5487df0c9c4afb85226613b520890049deea20", size = 451864 }, + { url = "https://files.pythonhosted.org/packages/d3/8a/3c4af14b93a15ce55901cd7a92e1a4701910f1768c78fb30f61d2b79785b/watchfiles-1.1.0-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:9c733cda03b6d636b4219625a4acb5c6ffb10803338e437fb614fef9516825ef", size = 625626 }, + { url = "https://files.pythonhosted.org/packages/da/f5/cf6aa047d4d9e128f4b7cde615236a915673775ef171ff85971d698f3c2c/watchfiles-1.1.0-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:cc08ef8b90d78bfac66f0def80240b0197008e4852c9f285907377b2947ffdcb", size = 622744 }, + { url = "https://files.pythonhosted.org/packages/2c/00/70f75c47f05dea6fd30df90f047765f6fc2d6eb8b5a3921379b0b04defa2/watchfiles-1.1.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:9974d2f7dc561cce3bb88dfa8eb309dab64c729de85fba32e98d75cf24b66297", size = 402114 }, + { url = "https://files.pythonhosted.org/packages/53/03/acd69c48db4a1ed1de26b349d94077cca2238ff98fd64393f3e97484cae6/watchfiles-1.1.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c68e9f1fcb4d43798ad8814c4c1b61547b014b667216cb754e606bfade587018", size = 393879 }, + { url = "https://files.pythonhosted.org/packages/2f/c8/a9a2a6f9c8baa4eceae5887fecd421e1b7ce86802bcfc8b6a942e2add834/watchfiles-1.1.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:95ab1594377effac17110e1352989bdd7bdfca9ff0e5eeccd8c69c5389b826d0", size = 450026 }, + { url = "https://files.pythonhosted.org/packages/fe/51/d572260d98388e6e2b967425c985e07d47ee6f62e6455cefb46a6e06eda5/watchfiles-1.1.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fba9b62da882c1be1280a7584ec4515d0a6006a94d6e5819730ec2eab60ffe12", size = 457917 }, + { url = "https://files.pythonhosted.org/packages/c6/2d/4258e52917bf9f12909b6ec314ff9636276f3542f9d3807d143f27309104/watchfiles-1.1.0-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3434e401f3ce0ed6b42569128b3d1e3af773d7ec18751b918b89cd49c14eaafb", size = 483602 }, + { url = "https://files.pythonhosted.org/packages/84/99/bee17a5f341a4345fe7b7972a475809af9e528deba056f8963d61ea49f75/watchfiles-1.1.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fa257a4d0d21fcbca5b5fcba9dca5a78011cb93c0323fb8855c6d2dfbc76eb77", size = 596758 }, + { url = "https://files.pythonhosted.org/packages/40/76/e4bec1d59b25b89d2b0716b41b461ed655a9a53c60dc78ad5771fda5b3e6/watchfiles-1.1.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7fd1b3879a578a8ec2076c7961076df540b9af317123f84569f5a9ddee64ce92", size = 477601 }, + { url = "https://files.pythonhosted.org/packages/1f/fa/a514292956f4a9ce3c567ec0c13cce427c158e9f272062685a8a727d08fc/watchfiles-1.1.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:62cc7a30eeb0e20ecc5f4bd113cd69dcdb745a07c68c0370cea919f373f65d9e", size = 451936 }, + { url = "https://files.pythonhosted.org/packages/32/5d/c3bf927ec3bbeb4566984eba8dd7a8eb69569400f5509904545576741f88/watchfiles-1.1.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:891c69e027748b4a73847335d208e374ce54ca3c335907d381fde4e41661b13b", size = 626243 }, + { url = "https://files.pythonhosted.org/packages/e6/65/6e12c042f1a68c556802a84d54bb06d35577c81e29fba14019562479159c/watchfiles-1.1.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:12fe8eaffaf0faa7906895b4f8bb88264035b3f0243275e0bf24af0436b27259", size = 623073 }, + { url = "https://files.pythonhosted.org/packages/89/ab/7f79d9bf57329e7cbb0a6fd4c7bd7d0cee1e4a8ef0041459f5409da3506c/watchfiles-1.1.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:bfe3c517c283e484843cb2e357dd57ba009cff351edf45fb455b5fbd1f45b15f", size = 400872 }, + { url = "https://files.pythonhosted.org/packages/df/d5/3f7bf9912798e9e6c516094db6b8932df53b223660c781ee37607030b6d3/watchfiles-1.1.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a9ccbf1f129480ed3044f540c0fdbc4ee556f7175e5ab40fe077ff6baf286d4e", size = 392877 }, + { url = "https://files.pythonhosted.org/packages/0d/c5/54ec7601a2798604e01c75294770dbee8150e81c6e471445d7601610b495/watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba0e3255b0396cac3cc7bbace76404dd72b5438bf0d8e7cefa2f79a7f3649caa", size = 449645 }, + { url = "https://files.pythonhosted.org/packages/0a/04/c2f44afc3b2fce21ca0b7802cbd37ed90a29874f96069ed30a36dfe57c2b/watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4281cd9fce9fc0a9dbf0fc1217f39bf9cf2b4d315d9626ef1d4e87b84699e7e8", size = 457424 }, + { url = "https://files.pythonhosted.org/packages/9f/b0/eec32cb6c14d248095261a04f290636da3df3119d4040ef91a4a50b29fa5/watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6d2404af8db1329f9a3c9b79ff63e0ae7131986446901582067d9304ae8aaf7f", size = 481584 }, + { url = "https://files.pythonhosted.org/packages/d1/e2/ca4bb71c68a937d7145aa25709e4f5d68eb7698a25ce266e84b55d591bbd/watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e78b6ed8165996013165eeabd875c5dfc19d41b54f94b40e9fff0eb3193e5e8e", size = 596675 }, + { url = "https://files.pythonhosted.org/packages/a1/dd/b0e4b7fb5acf783816bc950180a6cd7c6c1d2cf7e9372c0ea634e722712b/watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:249590eb75ccc117f488e2fabd1bfa33c580e24b96f00658ad88e38844a040bb", size = 477363 }, + { url = "https://files.pythonhosted.org/packages/69/c4/088825b75489cb5b6a761a4542645718893d395d8c530b38734f19da44d2/watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d05686b5487cfa2e2c28ff1aa370ea3e6c5accfe6435944ddea1e10d93872147", size = 452240 }, + { url = "https://files.pythonhosted.org/packages/10/8c/22b074814970eeef43b7c44df98c3e9667c1f7bf5b83e0ff0201b0bd43f9/watchfiles-1.1.0-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:d0e10e6f8f6dc5762adee7dece33b722282e1f59aa6a55da5d493a97282fedd8", size = 625607 }, + { url = "https://files.pythonhosted.org/packages/32/fa/a4f5c2046385492b2273213ef815bf71a0d4c1943b784fb904e184e30201/watchfiles-1.1.0-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:af06c863f152005c7592df1d6a7009c836a247c9d8adb78fef8575a5a98699db", size = 623315 }, + { url = "https://files.pythonhosted.org/packages/be/7c/a3d7c55cfa377c2f62c4ae3c6502b997186bc5e38156bafcb9b653de9a6d/watchfiles-1.1.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3a6fd40bbb50d24976eb275ccb55cd1951dfb63dbc27cae3066a6ca5f4beabd5", size = 406748 }, + { url = "https://files.pythonhosted.org/packages/38/d0/c46f1b2c0ca47f3667b144de6f0515f6d1c670d72f2ca29861cac78abaa1/watchfiles-1.1.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:9f811079d2f9795b5d48b55a37aa7773680a5659afe34b54cc1d86590a51507d", size = 398801 }, + { url = "https://files.pythonhosted.org/packages/70/9c/9a6a42e97f92eeed77c3485a43ea96723900aefa3ac739a8c73f4bff2cd7/watchfiles-1.1.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a2726d7bfd9f76158c84c10a409b77a320426540df8c35be172444394b17f7ea", size = 451528 }, + { url = "https://files.pythonhosted.org/packages/51/7b/98c7f4f7ce7ff03023cf971cd84a3ee3b790021ae7584ffffa0eb2554b96/watchfiles-1.1.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:df32d59cb9780f66d165a9a7a26f19df2c7d24e3bd58713108b41d0ff4f929c6", size = 454095 }, + { url = "https://files.pythonhosted.org/packages/8c/6b/686dcf5d3525ad17b384fd94708e95193529b460a1b7bf40851f1328ec6e/watchfiles-1.1.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:0ece16b563b17ab26eaa2d52230c9a7ae46cf01759621f4fbbca280e438267b3", size = 406910 }, + { url = "https://files.pythonhosted.org/packages/f3/d3/71c2dcf81dc1edcf8af9f4d8d63b1316fb0a2dd90cbfd427e8d9dd584a90/watchfiles-1.1.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:51b81e55d40c4b4aa8658427a3ee7ea847c591ae9e8b81ef94a90b668999353c", size = 398816 }, + { url = "https://files.pythonhosted.org/packages/b8/fa/12269467b2fc006f8fce4cd6c3acfa77491dd0777d2a747415f28ccc8c60/watchfiles-1.1.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2bcdc54ea267fe72bfc7d83c041e4eb58d7d8dc6f578dfddb52f037ce62f432", size = 451584 }, + { url = "https://files.pythonhosted.org/packages/bd/d3/254cea30f918f489db09d6a8435a7de7047f8cb68584477a515f160541d6/watchfiles-1.1.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:923fec6e5461c42bd7e3fd5ec37492c6f3468be0499bc0707b4bbbc16ac21792", size = 454009 }, ] [[package]] @@ -4065,11 +4576,11 @@ wheels = [ [[package]] name = "xlsxwriter" -version = "3.2.3" +version = "3.2.5" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a7/d1/e026d33dd5d552e5bf3a873dee54dad66b550230df8290d79394f09b2315/xlsxwriter-3.2.3.tar.gz", hash = "sha256:ad6fd41bdcf1b885876b1f6b7087560aecc9ae5a9cc2ba97dcac7ab2e210d3d5", size = 209135 } +sdist = { url = "https://files.pythonhosted.org/packages/a7/47/7704bac42ac6fe1710ae099b70e6a1e68ed173ef14792b647808c357da43/xlsxwriter-3.2.5.tar.gz", hash = "sha256:7e88469d607cdc920151c0ab3ce9cf1a83992d4b7bc730c5ffdd1a12115a7dbe", size = 213306 } wheels = [ - { url = "https://files.pythonhosted.org/packages/37/b1/a252d499f2760b314fcf264d2b36fcc4343a1ecdb25492b210cb0db70a68/XlsxWriter-3.2.3-py3-none-any.whl", hash = "sha256:593f8296e8a91790c6d0378ab08b064f34a642b3feb787cf6738236bd0a4860d", size = 169433 }, + { url = "https://files.pythonhosted.org/packages/fa/34/a22e6664211f0c8879521328000bdcae9bf6dbafa94a923e531f6d5b3f73/xlsxwriter-3.2.5-py3-none-any.whl", hash = "sha256:4f4824234e1eaf9d95df9a8fe974585ff91d0f5e3d3f12ace5b71e443c1c6abd", size = 172347 }, ] [[package]] @@ -4246,24 +4757,24 @@ wheels = [ [[package]] name = "youtube-transcript-api" -version = "1.0.3" +version = "1.2.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "defusedxml" }, { name = "requests" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b0/32/f60d87a99c05a53604c58f20f670c7ea6262b55e0bbeb836ffe4550b248b/youtube_transcript_api-1.0.3.tar.gz", hash = "sha256:902baf90e7840a42e1e148335e09fe5575dbff64c81414957aea7038e8a4db46", size = 2153252 } +sdist = { url = "https://files.pythonhosted.org/packages/b1/ac/092a3527ae4982a63e0cd72be4453340d4f427c30482e46faf1d96338ce5/youtube_transcript_api-1.2.0.tar.gz", hash = "sha256:ad01802f55a56eb33cc06d6b415a6055dd1bc8147bee053848411d9b4700ca2c", size = 468327 } wheels = [ - { url = "https://files.pythonhosted.org/packages/f0/44/40c03bb0f8bddfb9d2beff2ed31641f52d96c287ba881d20e0c074784ac2/youtube_transcript_api-1.0.3-py3-none-any.whl", hash = "sha256:d1874e57de65cf14c9d7d09b2b37c814d6287fa0e770d4922c4cd32a5b3f6c47", size = 2169911 }, + { url = "https://files.pythonhosted.org/packages/c1/cc/9ac0dc0def1888f6ed64c73a20d96dbfcade9f23be416e23028594df0664/youtube_transcript_api-1.2.0-py3-none-any.whl", hash = "sha256:1829455a669645e35e4a970192db37987fb8ca6ddf05aa6051bad00230b77041", size = 483897 }, ] [[package]] name = "yt-dlp" -version = "2025.6.9" +version = "2025.7.21.234438.dev0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b7/fb/588a23e61586960273524d3aa726bd148116d422854f727f4d59c254cb6a/yt_dlp-2025.6.9.tar.gz", hash = "sha256:751f53a3b61353522bf805fa30bbcbd16666126537e39706eab4f8c368f111ac", size = 3028379 } +sdist = { url = "https://files.pythonhosted.org/packages/a6/f6/ffe91816bbe21bbacf53e6349accea39b1480d3afcc7cf89a0835f33e70e/yt_dlp-2025.7.21.234438.dev0.tar.gz", hash = "sha256:0d61ef612c99c914acdb5a0ffed9e855806f150f52997b0c9246afb2c3e83678", size = 3050676 } wheels = [ - { url = "https://files.pythonhosted.org/packages/fd/e1/fa0dd2150b7b1033d917f37067c594160772fa607f311f10d4b881768b36/yt_dlp-2025.6.9-py3-none-any.whl", hash = "sha256:ebdfda9ffa807f6a26aed7c8f906e5557cd06b4c388dc547df1ec2078631fca8", size = 3276143 }, + { url = "https://files.pythonhosted.org/packages/5c/84/3bab53108e9d5aa454a38fd44659a64ba72b2c13add5880954485e22c0fa/yt_dlp-2025.7.21.234438.dev0-py3-none-any.whl", hash = "sha256:9aead785341266d9e4fc18e60200fe22e32431da36422a64abd289ea81a646cf", size = 3288996 }, ] [[package]] From 0b801d48a7d68a7332eec9a3fef92494b61f7778 Mon Sep 17 00:00:00 2001 From: Khoa Ngo The Date: Wed, 23 Jul 2025 11:25:16 +0700 Subject: [PATCH 04/13] feat: upgrade alembic for new schema --- .../b940c8b61543_add_new_column_runtime_id.py | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 src/ii_agent/migrations/versions/b940c8b61543_add_new_column_runtime_id.py diff --git a/src/ii_agent/migrations/versions/b940c8b61543_add_new_column_runtime_id.py b/src/ii_agent/migrations/versions/b940c8b61543_add_new_column_runtime_id.py new file mode 100644 index 00000000..b1280fc3 --- /dev/null +++ b/src/ii_agent/migrations/versions/b940c8b61543_add_new_column_runtime_id.py @@ -0,0 +1,35 @@ +"""Add new column runtime_id + +Revision ID: b940c8b61543 +Revises: a89eabebd4fa +Create Date: 2025-07-23 11:22:31.059770 + +""" + +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision: str = "b940c8b61543" +down_revision: Union[str, None] = "a89eabebd4fa" +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + """Upgrade schema.""" + op.add_column("session", sa.Column("runtime_id", sa.String(), nullable=True)) + + op.execute(""" + UPDATE session + SET runtime_id = id + WHERE runtime_id IS NULL + """) + + +def downgrade() -> None: + """Downgrade schema.""" + op.drop_column("session", "runtime_id") From 59f4f2f4e537301d4b570732382c4896949971d6 Mon Sep 17 00:00:00 2001 From: Khoa Ngo The Date: Wed, 23 Jul 2025 18:36:13 +0700 Subject: [PATCH 05/13] feat: sandbox for mcp server --- .dockerignore | 1 + docker/nginx/Dockerfile | 11 ++ docker/nginx/docker-entrypoint.sh | 15 ++ docker/nginx/nginx.conf.template | 67 +++++++ docker/sandbox/Dockerfile | 56 ++++++ docker/sandbox/start-services.sh | 44 +++++ src/ii_agent/runtime/config/runtime_config.py | 2 +- src/ii_agent/runtime/docker.py | 170 ++++++++++++++---- src/ii_agent/runtime/e2b.py | 12 +- src/ii_agent/runtime/runtime_manager.py | 3 +- src/ii_agent/runtime/utils/docker_utils.py | 73 ++++++++ src/ii_tool/mcp/server.py | 43 ++++- stop.sh | 1 + uv.lock | 48 ++--- 14 files changed, 478 insertions(+), 68 deletions(-) create mode 100644 docker/nginx/Dockerfile create mode 100644 docker/nginx/docker-entrypoint.sh create mode 100644 docker/nginx/nginx.conf.template create mode 100644 docker/sandbox/Dockerfile create mode 100644 docker/sandbox/start-services.sh create mode 100644 src/ii_agent/runtime/utils/docker_utils.py create mode 100644 stop.sh diff --git a/.dockerignore b/.dockerignore index b1beebf2..e17864ee 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,3 +1,4 @@ frontend/node_modules workspace/ .env +.venv diff --git a/docker/nginx/Dockerfile b/docker/nginx/Dockerfile new file mode 100644 index 00000000..9af23900 --- /dev/null +++ b/docker/nginx/Dockerfile @@ -0,0 +1,11 @@ +FROM nginx:alpine + +COPY docker/nginx/nginx.conf.template /etc/nginx/conf.d/default.conf.template +COPY docker/nginx/docker-entrypoint.sh /docker-entrypoint.sh + +# Make entrypoint executable +RUN chmod +x /docker-entrypoint.sh + +EXPOSE 80 + +ENTRYPOINT ["/docker-entrypoint.sh"] diff --git a/docker/nginx/docker-entrypoint.sh b/docker/nginx/docker-entrypoint.sh new file mode 100644 index 00000000..4bf1ca25 --- /dev/null +++ b/docker/nginx/docker-entrypoint.sh @@ -0,0 +1,15 @@ +#!/bin/sh + +# Substitute environment variables in template +echo "Substituting environment variables in template" +echo "PUBLIC_DOMAIN: ${PUBLIC_DOMAIN}" +export ESCAPED_PUBLIC_DOMAIN=$(echo "${PUBLIC_DOMAIN}" | sed 's/\./\\./g') +echo "ESCAPED_PUBLIC_DOMAIN: ${ESCAPED_PUBLIC_DOMAIN}" +envsubst '${PUBLIC_DOMAIN} ${ESCAPED_PUBLIC_DOMAIN} ' /etc/nginx/conf.d/default.conf + +# Show the generated config for debugging +echo "Generated nginx config:" +cat /etc/nginx/conf.d/default.conf + +# Test nginx configuration +exec nginx -g 'daemon off;' diff --git a/docker/nginx/nginx.conf.template b/docker/nginx/nginx.conf.template new file mode 100644 index 00000000..bbae7ca9 --- /dev/null +++ b/docker/nginx/nginx.conf.template @@ -0,0 +1,67 @@ +# Nginx configuration for Docker container subdomain reverse proxy +# Place this in /etc/nginx/sites-available/ and symlink to sites-enabled/ +server { + listen 80; + listen [::]:80; + server_name server_name *.${PUBLIC_DOMAIN}; + + # Configure Docker's internal DNS resolver + resolver 127.0.0.11 valid=30s; + + # Extract containerId and port from subdomain (format: containerId-port.domain.com) + if ($host ~* ^(.+)-([0-9]+)\.${ESCAPED_PUBLIC_DOMAIN}$) { + set $container_id $1; + set $container_port $2; + } + + # Return 404 if subdomain doesn't match the expected format + if ($container_id = "") { + return 404; + } + + location / { + # Use a variable to force runtime DNS resolution + set $upstream http://$container_id:$container_port; + + # Proxy to Docker container at containerId:port + proxy_pass $upstream; + + # Standard proxy headers + proxy_set_header Host $host; + proxy_set_header Origin http://$host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Host $host; + + # WebSocket support - These headers enable WebSocket proxying + proxy_http_version 1.1; # Required for WebSocket + proxy_set_header Upgrade $http_upgrade; # Pass through WebSocket upgrade header + proxy_set_header Connection "upgrade"; # Set connection to upgrade for WebSocket + + # Timeouts + proxy_connect_timeout 600s; + proxy_send_timeout 600s; + proxy_read_timeout 600s; + + client_max_body_size 100M; + + # Buffer settings for better performance + proxy_buffering on; + proxy_buffer_size 4k; + proxy_buffers 8 4k; + + # Handle redirects properly + proxy_redirect off; + + # Handle connection errors gracefully + proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504; + } + + # Health check endpoint (optional) + location /health { + access_log off; + return 200 "healthy\n"; + add_header Content-Type text/plain; + } +} diff --git a/docker/sandbox/Dockerfile b/docker/sandbox/Dockerfile new file mode 100644 index 00000000..9e3e2487 --- /dev/null +++ b/docker/sandbox/Dockerfile @@ -0,0 +1,56 @@ +FROM nikolaik/python-nodejs:python3.10-nodejs20-slim + +# Set environment variables +ENV NODE_OPTIONS="--max-old-space-size=4096" + +RUN mkdir -p /app/ii_agent + +# Install the project into `/app` +WORKDIR /app/ii_agent + +# Enable bytecode compilation +ENV UV_COMPILE_BYTECODE=1 + +# Copy from the cache instead of linking since it's a mounted volume +ENV UV_LINK_MODE=copy + +RUN apt-get update && apt-get install -y \ + build-essential \ + procps \ + lsof \ + git \ + tmux \ + bc \ + net-tools \ + unzip + + +# Copy dependency files first for better layer caching +COPY uv.lock pyproject.toml /app/ii_agent/ + +# Install the project's dependencies using the lockfile and settings +RUN --mount=type=cache,target=/root/.cache/uv \ + --mount=type=bind,source=uv.lock,target=uv.lock \ + --mount=type=bind,source=pyproject.toml,target=pyproject.toml \ + uv sync --locked --prerelease=allow --no-install-project --no-dev + +COPY . /app/ii_agent +RUN --mount=type=cache,target=/root/.cache/uv \ + uv sync --locked --prerelease=allow --no-dev + +ENV PATH="/app/ii_agent/.venv/bin:$PATH" + +RUN curl -fsSL https://bun.sh/install | bash +RUN curl -fsSL https://code-server.dev/install.sh | sh + +RUN npm install -g vercel + +RUN mkdir /workspace + +WORKDIR /workspace + +# Create a startup script to run both services +COPY docker/sandbox/start-services.sh /app/start-services.sh +RUN chmod +x /app/start-services.sh + +CMD ["/app/start-services.sh"] diff --git a/docker/sandbox/start-services.sh b/docker/sandbox/start-services.sh new file mode 100644 index 00000000..2edb46a5 --- /dev/null +++ b/docker/sandbox/start-services.sh @@ -0,0 +1,44 @@ +#!/bin/bash + +# Create workspace directory if it doesn't exist +mkdir -p /workspace + +# Start the sandbox server in the background +echo "Starting sandbox server on port 17300..." +python /app/ii_agent/src/ii_tool/mcp/server.py --port 17300 --workspace_dir /workspace --session_id system_shell & + +# Start code-server in the background +echo "Starting code-server on port 9000..." +code-server \ + --port 9000 \ + --auth none \ + --bind-addr 0.0.0.0:9000 \ + --disable-telemetry \ + --disable-update-check \ + --trusted-origins * \ + --disable-workspace-trust \ + /workspace & + +# Wait for both processes to start +sleep 3 + +# Check if processes are running +echo "Checking if services are running..." +if pgrep -f "server.py" >/dev/null; then + echo "āœ“ Sandbox server is running" +else + echo "āœ— Sandbox server failed to start" +fi + +if pgrep -f "code-server" >/dev/null; then + echo "āœ“ Code-server is running" +else + echo "āœ— Code-server failed to start" +fi + +echo "Services started. Container ready." +echo "Sandbox server available on port 17300" +echo "Code-server available on port 9000" + +# Keep the container running by waiting for all background processes +wait diff --git a/src/ii_agent/runtime/config/runtime_config.py b/src/ii_agent/runtime/config/runtime_config.py index 4b942e57..a8067d97 100644 --- a/src/ii_agent/runtime/config/runtime_config.py +++ b/src/ii_agent/runtime/config/runtime_config.py @@ -19,6 +19,6 @@ class RuntimeSettings(BaseModel): default=True, description="Whether network access is allowed" ) network_name: str = Field( - default=f"{os.getenv('COMPOSE_PROJECT_NAME')}_ii", + default="ii_agent_network", description="Name of the Docker network to connect to", ) diff --git a/src/ii_agent/runtime/docker.py b/src/ii_agent/runtime/docker.py index b0bc7f35..f6dd8faa 100644 --- a/src/ii_agent/runtime/docker.py +++ b/src/ii_agent/runtime/docker.py @@ -11,6 +11,11 @@ from ii_agent.runtime.config.runtime_config import RuntimeSettings from ii_agent.runtime.runtime_registry import RuntimeRegistry from ii_agent.runtime.model.constants import RuntimeMode +from ii_agent.runtime.utils.docker_utils import ( + build_if_not_exists, + get_host_ip, + get_project_root, +) if TYPE_CHECKING: from ii_agent.core.storage.models.settings import Settings @@ -44,6 +49,19 @@ def __init__( volume_bindings: Volume mappings in {host_path: container_path} format. """ super().__init__(session_id=session_id, settings=settings) + self.docker_images = { + "sandbox": { + "path": os.path.join(get_project_root()), + "dockerfile": "docker/sandbox/Dockerfile", + "tag": "ii-agent-sandbox:latest", + }, + "nginx": { + "path": os.path.join(get_project_root()), + "dockerfile": "docker/nginx/Dockerfile", + "tag": "ii-agent-nginx:latest", + "port": 8080, + }, + } self.config = RuntimeSettings() self.volume_bindings = { load_ii_agent_config().workspace_root @@ -52,25 +70,38 @@ def __init__( } self.client = docker.from_env() - async def start(self): - pass - async def stop(self): pass + async def build(self): + try: + for _, config in self.docker_images.items(): + build_if_not_exists( + self.client, config["path"], config["dockerfile"], config["tag"] + ) + except Exception as e: + raise RuntimeError(f"Failed to build runtime: {e}") + async def connect(self): self.host_url = ( - f"http://{self.session_id}:{self.settings.runtime_config.service_port}" + self.expose_port(self.settings.runtime_config.service_port) + "/mcp/" ) + async def start(self): + """Start the runtime by ensuring network exists and nginx container is running.""" + pass + def get_mcp_client(self, workspace_dir: str) -> Client: if not self.host_url: - raise ValueError("Host URL is not set") + raise RuntimeError("Host URL is not set") return Client(self.host_url) def expose_port(self, port: int) -> str: - public_url = f"http://{self.session_id}-{port}.{os.getenv('BASE_URL')}" - return public_url + try: + public_url = f"http://{self.session_id}-{port}.{os.getenv('BASE_URL', get_host_ip())}.nip.io:{self.docker_images['nginx']['port']}" + return public_url + except Exception as e: + raise RuntimeError(f"Failed to expose port: {e}") async def create(self): """Creates and starts the docker runtime container. @@ -82,7 +113,10 @@ async def create(self): docker.errors.APIError: If Docker API call fails. RuntimeError: If container creation or startup fails. """ - os.makedirs(self.config.work_dir, exist_ok=True) + await self.build() + await self._ensure_network_exists() + await self._ensure_nginx_running() + os.makedirs(load_ii_agent_config().workspace_root, exist_ok=True) try: # Prepare container config host_config = self.client.api.create_host_config( @@ -98,7 +132,7 @@ async def create(self): # Create container container = await asyncio.to_thread( self.client.api.create_container, - image=self.config.image, + image=self.docker_images["sandbox"]["tag"], hostname="sandbox", host_config=host_config, name=str(self.session_id), @@ -115,8 +149,11 @@ async def create(self): self.container = self.client.containers.get(container["Id"]) self.container_id = container["Id"] self.container.start() + await asyncio.sleep(3) - self.host_url = f"http://{str(self.session_id)}:{self.settings.runtime_config.service_port}" + self.host_url = ( + self.expose_port(self.settings.runtime_config.service_port) + "/mcp/" + ) self.runtime_id = self.container_id print(f"Container created: {self.container_id}") except Exception as e: @@ -136,29 +173,6 @@ def _prepare_volume_bindings(self) -> Dict[str, Dict[str, str]]: return bindings - def _safe_resolve_path(self, path: str) -> str: - """Safely resolves container path, preventing path traversal. - - Args: - path: Original path. - - Returns: - Resolved absolute path. - - Raises: - ValueError: If path contains potentially unsafe patterns. - """ - # Check for path traversal attempts - if ".." in path.split("/"): - raise ValueError("Path contains potentially unsafe patterns") - - resolved = ( - os.path.join(self.config.work_dir, path) - if not os.path.isabs(path) - else path - ) - return resolved - async def cleanup(self) -> None: """Cleans up sandbox resources.""" errors = [] @@ -189,12 +203,102 @@ async def __aenter__(self) -> "DockerRuntime": async def __aexit__(self, exc_type, exc_val, exc_tb) -> None: await self.cleanup() + async def _ensure_network_exists(self): + """Create the network if it doesn't exist.""" + if not self.config.network_enabled: + return + + try: + # Check if network exists + networks = await asyncio.to_thread( + self.client.networks.list, names=[self.config.network_name] + ) + if not networks: + print(f"Creating network: {self.config.network_name}") + await asyncio.to_thread( + self.client.networks.create, + name=self.config.network_name, + driver="bridge", + ) + print(f"Network '{self.config.network_name}' created successfully") + else: + print(f"Network '{self.config.network_name}' already exists") + except Exception as e: + raise RuntimeError(f"Failed to ensure network exists: {e}") + + async def _ensure_nginx_running(self): + """Create and start nginx container if none is running.""" + try: + # Check if any nginx containers are running + containers = await asyncio.to_thread( + self.client.containers.list, + filters={ + "ancestor": self.docker_images["nginx"]["tag"], + "status": "running", + }, + ) + + if not containers: + print("No running nginx containers found, creating one...") + await self._create_nginx_container() + else: + print(f"Found {len(containers)} running nginx container(s)") + except Exception as e: + raise RuntimeError(f"Failed to ensure nginx is running: {e}") + + async def _create_nginx_container(self): + """Create and start a new nginx container.""" + try: + # Ensure nginx image is built + build_if_not_exists( + self.client, + self.docker_images["nginx"]["path"], + self.docker_images["nginx"]["dockerfile"], + self.docker_images["nginx"]["tag"], + ) + + # Prepare nginx container config + host_config = self.client.api.create_host_config( + port_bindings={"80/tcp": self.docker_images["nginx"]["port"]}, + network_mode=self.config.network_name + if self.config.network_enabled + else None, + ) + + # Create nginx container + nginx_container = await asyncio.to_thread( + self.client.api.create_container, + image=self.docker_images["nginx"]["tag"], + hostname="nginx-proxy", + host_config=host_config, + name=f"nginx-proxy-{uuid.uuid4().hex[:8]}", + labels={ + "com.docker.compose.project": os.getenv( + "COMPOSE_PROJECT_NAME", "ii-agent" + ), + "service": "nginx-proxy", + }, + environment={ + "PUBLIC_DOMAIN": os.getenv("BASE_URL", get_host_ip() + ".nip.io") + }, + detach=True, + ) + + nginx_container_obj = self.client.containers.get(nginx_container["Id"]) + nginx_container_obj.start() + + print(f"Nginx container created and started: {nginx_container['Id']}") + + except Exception as e: + raise RuntimeError(f"Failed to create nginx container: {e}") + if __name__ == "__main__": async def main(): settings = Settings() runtime = DockerRuntime(uuid.uuid4(), settings) + await runtime.start() # This will now ensure network and nginx are ready await runtime.create() print("Runtime created") # await runtime.run_command("ls -la") diff --git a/src/ii_agent/runtime/e2b.py b/src/ii_agent/runtime/e2b.py index da6b6598..99c6f522 100644 --- a/src/ii_agent/runtime/e2b.py +++ b/src/ii_agent/runtime/e2b.py @@ -29,7 +29,9 @@ async def create(self): api_key=self._get_api_key(), timeout=3600, ) - self.host_url = self.expose_port(self.settings.runtime_config.service_port) + self.host_url = ( + self.expose_port(self.settings.runtime_config.service_port) + "/mcp/" + ) if not self.sandbox.sandbox_id: raise ValueError("Sandbox ID is not set") self.runtime_id = str(self.sandbox.sandbox_id) @@ -55,7 +57,9 @@ async def connect(self): runtime_id, api_key=self._get_api_key(), ) - self.host_url = self.expose_port(self.settings.runtime_config.service_port) + self.host_url = ( + self.expose_port(self.settings.runtime_config.service_port) + "/mcp/" + ) self.runtime_id = self.sandbox.sandbox_id async def cleanup(self): @@ -72,7 +76,9 @@ async def start(self): api_key=self._get_api_key(), timeout=3600, ) - self.host_url = self.expose_port(self.settings.runtime_config.service_port) + self.host_url = ( + self.expose_port(self.settings.runtime_config.service_port) + "/mcp/" + ) self.runtime_id = self.sandbox.sandbox_id def _get_api_key(self): diff --git a/src/ii_agent/runtime/runtime_manager.py b/src/ii_agent/runtime/runtime_manager.py index c63e1822..262a832c 100644 --- a/src/ii_agent/runtime/runtime_manager.py +++ b/src/ii_agent/runtime/runtime_manager.py @@ -27,7 +27,8 @@ async def expose_port(self, port: int) -> str: async def get_mcp_client(self, workspace_dir: str) -> Client: if self.runtime is None: raise RuntimeUninitializedError("Runtime is not initialized") - return self.runtime.get_mcp_client(workspace_dir) + client = self.runtime.get_mcp_client(workspace_dir) + return client # WIP async def connect_runtime(self): diff --git a/src/ii_agent/runtime/utils/docker_utils.py b/src/ii_agent/runtime/utils/docker_utils.py new file mode 100644 index 00000000..a678b10a --- /dev/null +++ b/src/ii_agent/runtime/utils/docker_utils.py @@ -0,0 +1,73 @@ +import socket +import importlib.metadata +from pathlib import Path +import subprocess + +import docker + + +def get_project_root() -> Path: + try: + dist = importlib.metadata.distribution("ii-agent") + if dist.files: + package_location = Path(str(dist.locate_file("."))).resolve() + while package_location.parent != package_location: + if (package_location / "pyproject.toml").exists(): + return package_location + package_location = package_location.parent + + return package_location + except Exception as e: + raise Exception("Failed to get project root: " + str(e)) + + +def get_host_ip(): + try: + with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s: + s.connect(("8.8.8.8", 80)) + return s.getsockname()[0] + except Exception as e: + raise Exception("Failed to get host IP: " + str(e)) + + +def image_exists(client: docker.DockerClient, tag): + try: + client.images.get(tag) + return True + except Exception: + return False + + +def build_if_not_exists( + client: docker.DockerClient, path: str, dockerfile: str, tag: str +): + if image_exists(client, tag): + print(f"āœ“ Image {tag} already exists, skipping build") + return client.images.get(tag) + else: + print(f"Building {tag}...") + try: + try: + print(f"Building {tag} with docker client") + subprocess.run( + f"cd {path} && docker build -t {tag} -f {dockerfile} .", shell=True + ) + if image_exists(client, tag): + print(f"āœ“ Successfully built {tag}") + return client.images.get(tag) + else: + raise Exception(f"Failed to build image {tag}") + except Exception: + print( + f"āœ— Build failed for {tag}, trying to build with docker python sdk" + ) + image, _ = client.images.build( + path=path, dockerfile=dockerfile, tag=tag, rm=True + ) + if image_exists(client, tag): + print(f"āœ“ Successfully built {tag}") + return image + else: + raise Exception(f"Failed to build image {tag}") + except Exception as e: + raise Exception(f"Failed to build image {tag}: {e}") diff --git a/src/ii_tool/mcp/server.py b/src/ii_tool/mcp/server.py index da379f6e..8548af42 100644 --- a/src/ii_tool/mcp/server.py +++ b/src/ii_tool/mcp/server.py @@ -1,16 +1,33 @@ from fastmcp import FastMCP +from starlette.middleware import Middleware +from starlette.middleware.cors import CORSMiddleware from argparse import ArgumentParser from ii_tool.core.workspace import WorkspaceManager -from ii_tool.tools.shell import ShellInit, ShellRunCommand, ShellView, ShellKill, ShellStopCommand, ShellList +from ii_tool.tools.shell import ( + ShellInit, + ShellRunCommand, + ShellView, + ShellKill, + ShellStopCommand, + ShellList, +) from ii_tool.tools.shell.terminal_manager import TmuxWindowManager -from ii_tool.tools.file_system import GlobTool, GrepTool, LSTool, FileReadTool, FileWriteTool, FileEditTool, MultiEditTool +from ii_tool.tools.file_system import ( + GlobTool, + GrepTool, + LSTool, + FileReadTool, + FileWriteTool, + FileEditTool, + MultiEditTool, +) from ii_tool.tools.productivity import TodoReadTool, TodoWriteTool def create_mcp(workspace_dir: str, session_id: str): terminal_manager = TmuxWindowManager(chat_session_id=session_id) workspace_manager = WorkspaceManager(workspace_path=workspace_dir) - + shell_tools = [ ShellInit(terminal_manager, workspace_manager), ShellRunCommand(terminal_manager), @@ -38,6 +55,19 @@ def create_mcp(workspace_dir: str, session_id: str): tools = shell_tools + file_system_tools + productivity_tools mcp = FastMCP(name="ii-mcp") + + # Add CORS middleware to allow all hosts + custom_middleware = [ + Middleware( + CORSMiddleware, + allow_origins=["*"], + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], + ) + ] + mcp.http_app(middleware=custom_middleware) + for tool in tools: mcp.tool( tool.run_impl, @@ -47,16 +77,17 @@ def create_mcp(workspace_dir: str, session_id: str): return mcp + if __name__ == "__main__": parser = ArgumentParser() parser.add_argument("--workspace_dir", type=str) parser.add_argument("--session_id", type=str, default=None) parser.add_argument("--port", type=int, default=6060) - + args = parser.parse_args() - + mcp = create_mcp( workspace_dir=args.workspace_dir, session_id=args.session_id, ) - mcp.run(transport="http", host="0.0.0.0", port=args.port) \ No newline at end of file + mcp.run(transport="http", host="0.0.0.0", port=args.port) diff --git a/stop.sh b/stop.sh new file mode 100644 index 00000000..5883e197 --- /dev/null +++ b/stop.sh @@ -0,0 +1 @@ +docker ps --filter "label=com.docker.compose.project=ii-agent" -q | xargs docker stop diff --git a/uv.lock b/uv.lock index 5546ba58..46beea17 100644 --- a/uv.lock +++ b/uv.lock @@ -1961,7 +1961,7 @@ wheels = [ [[package]] name = "langchain-core" -version = "0.3.70" +version = "0.3.71" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "jsonpatch" }, @@ -1972,9 +1972,9 @@ dependencies = [ { name = "tenacity" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a6/38/cf4ad0462e39814aecfcbd59dd4b19fb1e9f61999fec9bc1613d593de417/langchain_core-0.3.70.tar.gz", hash = "sha256:58551e5411ff9f92c7c8f4379e07e762ca66800e821cd904e19881fe41f691ee", size = 566031 } +sdist = { url = "https://files.pythonhosted.org/packages/23/ea/f7089f7557673b2ac71396ab4bd4322ec959fd7d3901232998ba22c8f953/langchain_core-0.3.71.tar.gz", hash = "sha256:03ce06ba86bd1fa202b7b704d81554306f9cf5a3044b80d9a8ea7d93eab08623", size = 567226 } wheels = [ - { url = "https://files.pythonhosted.org/packages/25/73/910b809b5f8dfe2738bbb065580364b9751425a404b8ee87479a75b37e95/langchain_core-0.3.70-py3-none-any.whl", hash = "sha256:56f1ce0ab410508e25b3b4b3b87a8ffae38bf16294e5f605ac63a8aff5bc13a4", size = 442252 }, + { url = "https://files.pythonhosted.org/packages/32/1b/e9af4aac9623d63596c499f619082fa48c4b995696b6d2e8e98e53423809/langchain_core-0.3.71-py3-none-any.whl", hash = "sha256:cce6f3faae57d23bc4f2b41246b9dcf06b8dcdf52caaf6afd62b0849df20ba23", size = 442804 }, ] [[package]] @@ -2305,7 +2305,7 @@ wheels = [ [[package]] name = "mcp" -version = "1.12.0" +version = "1.12.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, @@ -2320,9 +2320,9 @@ dependencies = [ { name = "starlette" }, { name = "uvicorn", marker = "sys_platform != 'emscripten'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/45/94/caa0f4754e2437f7033068989f13fee784856f95870c786b0b5c2c0f511e/mcp-1.12.0.tar.gz", hash = "sha256:853f6b17a3f31ea6e2f278c2ec7d3b38457bc80c7c2c675260dd7f04a6fd0e70", size = 424678 } +sdist = { url = "https://files.pythonhosted.org/packages/5c/5a/16cef13b2e60d5f865fbc96372efb23dc8b0591f102dd55003b4ae62f9b1/mcp-1.12.1.tar.gz", hash = "sha256:d1d0bdeb09e4b17c1a72b356248bf3baf75ab10db7008ef865c4afbeb0eb810e", size = 425768 } wheels = [ - { url = "https://files.pythonhosted.org/packages/ed/da/c7eaab6a58f1034de115b7902141ad8f81b4f3bbf7dc0cc267594947a4d7/mcp-1.12.0-py3-none-any.whl", hash = "sha256:19a498b2bf273283e463b4dd1ed83f791fbba5c25bfa16b8b34cfd5571673e7f", size = 158470 }, + { url = "https://files.pythonhosted.org/packages/b9/04/9a967a575518fc958bda1e34a52eae0c7f6accf3534811914fdaf57b0689/mcp-1.12.1-py3-none-any.whl", hash = "sha256:34147f62891417f8b000c39718add844182ba424c8eb2cea250b4267bda4b08b", size = 158463 }, ] [[package]] @@ -2611,7 +2611,7 @@ wheels = [ [[package]] name = "openai" -version = "1.97.0" +version = "1.97.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, @@ -2623,9 +2623,9 @@ dependencies = [ { name = "tqdm" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e0/c6/b8d66e4f3b95493a8957065b24533333c927dc23817abe397f13fe589c6e/openai-1.97.0.tar.gz", hash = "sha256:0be349569ccaa4fb54f97bb808423fd29ccaeb1246ee1be762e0c81a47bae0aa", size = 493850 } +sdist = { url = "https://files.pythonhosted.org/packages/a6/57/1c471f6b3efb879d26686d31582997615e969f3bb4458111c9705e56332e/openai-1.97.1.tar.gz", hash = "sha256:a744b27ae624e3d4135225da9b1c89c107a2a7e5bc4c93e5b7b5214772ce7a4e", size = 494267 } wheels = [ - { url = "https://files.pythonhosted.org/packages/8a/91/1f1cf577f745e956b276a8b1d3d76fa7a6ee0c2b05db3b001b900f2c71db/openai-1.97.0-py3-none-any.whl", hash = "sha256:a1c24d96f4609f3f7f51c9e1c2606d97cc6e334833438659cfd687e9c972c610", size = 764953 }, + { url = "https://files.pythonhosted.org/packages/ee/35/412a0e9c3f0d37c94ed764b8ac7adae2d834dbd20e69f6aca582118e0f55/openai-1.97.1-py3-none-any.whl", hash = "sha256:4e96bbdf672ec3d44968c9ea39d2c375891db1acc1794668d8149d5fa6000606", size = 764380 }, ] [[package]] @@ -2929,21 +2929,21 @@ wheels = [ [[package]] name = "playwright" -version = "1.53.0" +version = "1.54.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "greenlet" }, { name = "pyee" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/f5/e2/2f107be74419280749723bd1197c99351f4b8a0a25e974b9764affb940b2/playwright-1.53.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:48a1a15ce810f0ffe512b6050de9871ea193b41dd3cc1bbed87b8431012419ba", size = 40392498 }, - { url = "https://files.pythonhosted.org/packages/ac/d5/e8c57a4f6fd46059fb2d51da2d22b47afc886b42400f06b742cd4a9ba131/playwright-1.53.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:a701f9498a5b87e3f929ec01cea3109fbde75821b19c7ba4bba54f6127b94f76", size = 38647035 }, - { url = "https://files.pythonhosted.org/packages/4d/f3/da18cd7c22398531316e58fd131243fd9156fe7765aae239ae542a5d07d2/playwright-1.53.0-py3-none-macosx_11_0_universal2.whl", hash = "sha256:f765498341c4037b4c01e742ae32dd335622f249488ccd77ca32d301d7c82c61", size = 40392502 }, - { url = "https://files.pythonhosted.org/packages/92/32/5d871c3753fbee5113eefc511b9e44c0006a27f2301b4c6bffa4346fbd94/playwright-1.53.0-py3-none-manylinux1_x86_64.whl", hash = "sha256:db19cb5b58f3b15cad3e2419f4910c053e889202fc202461ee183f1530d1db60", size = 45848364 }, - { url = "https://files.pythonhosted.org/packages/dc/6b/9942f86661ff41332f9299db4950623123e60ca71e4fb6e6942fc0212624/playwright-1.53.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9276c9c935fc062f51f4f5107e56420afd6d9a524348dc437793dc2e34c742e3", size = 45235174 }, - { url = "https://files.pythonhosted.org/packages/51/63/28b3f2d36e6a95e88f033d2aa7af06083f6f4aa0d9764759d96033cd053e/playwright-1.53.0-py3-none-win32.whl", hash = "sha256:36eedec101724ff5a000cddab87dd9a72a39f9b3e65a687169c465484e667c06", size = 35415131 }, - { url = "https://files.pythonhosted.org/packages/a9/b5/4ca25974a90d16cfd4a9a953ee5a666cf484a0bdacb4eed484e5cab49e66/playwright-1.53.0-py3-none-win_amd64.whl", hash = "sha256:d68975807a0fd997433537f1dcf2893cda95884a39dc23c6f591b8d5f691e9e8", size = 35415138 }, - { url = "https://files.pythonhosted.org/packages/9a/81/b42ff2116df5d07ccad2dc4eeb20af92c975a1fbc7cd3ed37b678468b813/playwright-1.53.0-py3-none-win_arm64.whl", hash = "sha256:fcfd481f76568d7b011571160e801b47034edd9e2383c43d83a5fb3f35c67885", size = 31188568 }, + { url = "https://files.pythonhosted.org/packages/f3/09/33d5bfe393a582d8dac72165a9e88b274143c9df411b65ece1cc13f42988/playwright-1.54.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:bf3b845af744370f1bd2286c2a9536f474cc8a88dc995b72ea9a5be714c9a77d", size = 40439034 }, + { url = "https://files.pythonhosted.org/packages/e1/7b/51882dc584f7aa59f446f2bb34e33c0e5f015de4e31949e5b7c2c10e54f0/playwright-1.54.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:780928b3ca2077aea90414b37e54edd0c4bbb57d1aafc42f7aa0b3fd2c2fac02", size = 38702308 }, + { url = "https://files.pythonhosted.org/packages/73/a1/7aa8ae175b240c0ec8849fcf000e078f3c693f9aa2ffd992da6550ea0dff/playwright-1.54.0-py3-none-macosx_11_0_universal2.whl", hash = "sha256:81d0b6f28843b27f288cfe438af0a12a4851de57998009a519ea84cee6fbbfb9", size = 40439037 }, + { url = "https://files.pythonhosted.org/packages/34/a9/45084fd23b6206f954198296ce39b0acf50debfdf3ec83a593e4d73c9c8a/playwright-1.54.0-py3-none-manylinux1_x86_64.whl", hash = "sha256:09919f45cc74c64afb5432646d7fef0d19fff50990c862cb8d9b0577093f40cc", size = 45920135 }, + { url = "https://files.pythonhosted.org/packages/02/d4/6a692f4c6db223adc50a6e53af405b45308db39270957a6afebddaa80ea2/playwright-1.54.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:13ae206c55737e8e3eae51fb385d61c0312eeef31535643bb6232741b41b6fdc", size = 45302695 }, + { url = "https://files.pythonhosted.org/packages/72/7a/4ee60a1c3714321db187bebbc40d52cea5b41a856925156325058b5fca5a/playwright-1.54.0-py3-none-win32.whl", hash = "sha256:0b108622ffb6906e28566f3f31721cd57dda637d7e41c430287804ac01911f56", size = 35469309 }, + { url = "https://files.pythonhosted.org/packages/aa/77/8f8fae05a242ef639de963d7ae70a69d0da61d6d72f1207b8bbf74ffd3e7/playwright-1.54.0-py3-none-win_amd64.whl", hash = "sha256:9e5aee9ae5ab1fdd44cd64153313a2045b136fcbcfb2541cc0a3d909132671a2", size = 35469311 }, + { url = "https://files.pythonhosted.org/packages/33/ff/99a6f4292a90504f2927d34032a4baf6adb498dc3f7cf0f3e0e22899e310/playwright-1.54.0-py3-none-win_arm64.whl", hash = "sha256:a975815971f7b8dca505c441a4c56de1aeb56a211290f8cc214eeef5524e8d75", size = 31239119 }, ] [[package]] @@ -4757,24 +4757,24 @@ wheels = [ [[package]] name = "youtube-transcript-api" -version = "1.2.0" +version = "1.2.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "defusedxml" }, { name = "requests" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b1/ac/092a3527ae4982a63e0cd72be4453340d4f427c30482e46faf1d96338ce5/youtube_transcript_api-1.2.0.tar.gz", hash = "sha256:ad01802f55a56eb33cc06d6b415a6055dd1bc8147bee053848411d9b4700ca2c", size = 468327 } +sdist = { url = "https://files.pythonhosted.org/packages/8e/95/9d6ebcbfd4993ff239d9ebc59dc9b9505b88b7f7001f379692d07ee05fba/youtube_transcript_api-1.2.1.tar.gz", hash = "sha256:7c16ba3e981dd7ab4c0f00f42e5a69b19bdb9f13324c60bd6cc8f97701699900", size = 469216 } wheels = [ - { url = "https://files.pythonhosted.org/packages/c1/cc/9ac0dc0def1888f6ed64c73a20d96dbfcade9f23be416e23028594df0664/youtube_transcript_api-1.2.0-py3-none-any.whl", hash = "sha256:1829455a669645e35e4a970192db37987fb8ca6ddf05aa6051bad00230b77041", size = 483897 }, + { url = "https://files.pythonhosted.org/packages/c5/54/9581c511e47582da4286a65d90b82dd36cbb3eab7d452a58c625bf74dc60/youtube_transcript_api-1.2.1-py3-none-any.whl", hash = "sha256:4852356c8459aceab73f8f05e0f7fc4762d5f278e7e34bd6359fce7427df853b", size = 484622 }, ] [[package]] name = "yt-dlp" -version = "2025.7.21.234438.dev0" +version = "2025.7.22.233113.dev0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a6/f6/ffe91816bbe21bbacf53e6349accea39b1480d3afcc7cf89a0835f33e70e/yt_dlp-2025.7.21.234438.dev0.tar.gz", hash = "sha256:0d61ef612c99c914acdb5a0ffed9e855806f150f52997b0c9246afb2c3e83678", size = 3050676 } +sdist = { url = "https://files.pythonhosted.org/packages/b6/5d/0c062de6c43dc883c76150d58bee63a7a7dee76c709ce84f48462a2364b2/yt_dlp-2025.7.22.233113.dev0.tar.gz", hash = "sha256:adb8f507461af1a4c0c5228215e18255356122905965ffc30b45704b570481e6", size = 3050344 } wheels = [ - { url = "https://files.pythonhosted.org/packages/5c/84/3bab53108e9d5aa454a38fd44659a64ba72b2c13add5880954485e22c0fa/yt_dlp-2025.7.21.234438.dev0-py3-none-any.whl", hash = "sha256:9aead785341266d9e4fc18e60200fe22e32431da36422a64abd289ea81a646cf", size = 3288996 }, + { url = "https://files.pythonhosted.org/packages/fc/06/665b8eff05db03cb563ec06e7bab5db72b84d471a4e0a5e295a8748fda7d/yt_dlp-2025.7.22.233113.dev0-py3-none-any.whl", hash = "sha256:a69507fd4adb4339c97098018a2201a6cdf9acf60329e99b50e8967da8b73e57", size = 3288249 }, ] [[package]] From 1b52e0cece55533be2ffed49259f2167360795a8 Mon Sep 17 00:00:00 2001 From: Khoa Ngo The Date: Wed, 23 Jul 2025 20:04:46 +0700 Subject: [PATCH 06/13] feat: cli for sandbox --- src/ii_agent/cli/commands/settings_command.py | 58 ++- src/ii_agent/cli/settings_onboard.py | 485 ++++++++++++------ .../storage/settings/file_settings_store.py | 4 +- 3 files changed, 361 insertions(+), 186 deletions(-) diff --git a/src/ii_agent/cli/commands/settings_command.py b/src/ii_agent/cli/commands/settings_command.py index 93bf7917..d3287325 100644 --- a/src/ii_agent/cli/commands/settings_command.py +++ b/src/ii_agent/cli/commands/settings_command.py @@ -15,57 +15,63 @@ class SettingsCommand(BaseCommand): """Command to manage LLM and application settings.""" - + @property def name(self) -> str: return "settings" - + @property def description(self) -> str: return "Configure LLM settings and application preferences" - + async def execute(self, args: str, context: Dict[str, Any]) -> Optional[str]: """Execute the settings command.""" try: # Get the settings store from the context - config = context.get('config') + config = context.get("config") if not config: self.console.print("[red]Error: Configuration not available[/red]") return None - + # Create settings store - settings_store = await FileSettingsStore.get_instance(config=config, user_id=None) - + settings_store = await FileSettingsStore.get_instance( + config=config, user_id=None + ) + # Show current settings and allow modification - self.console.print(Panel( - "āš™ļø [bold]Settings Configuration[/bold]\n\n" - "This will allow you to configure your LLM settings including:\n" - "• Provider selection (Anthropic, OpenAI, Gemini)\n" - "• Model selection\n" - "• API keys and authentication\n" - "• Vertex AI configuration (for Gemini)\n" - "• Temperature and other parameters", - title="Settings", - style="cyan" - )) - + self.console.print( + Panel( + "āš™ļø [bold]Settings Configuration[/bold]\n\n" + "This will allow you to configure your LLM settings including:\n" + "• Provider selection (Anthropic, OpenAI, Gemini)\n" + "• Model selection\n" + "• API keys and authentication\n" + "• Vertex AI configuration (for Gemini)\n" + "• Temperature and other parameters", + title="Settings", + style="cyan", + ) + ) + # Run the settings modification flow await modify_settings(settings_store) - + self.console.print("\n[green]Settings configuration completed![/green]") - self.console.print("[dim]Note: Changes will take effect for new conversations.[/dim]") - + self.console.print( + "[dim]Note: LLM and Runtime changes take effect for new conversations.[/dim]" + ) + return None - + except Exception as e: self.console.print(f"[red]Error configuring settings: {e}[/red]") return None - + def validate_args(self, args: str) -> bool: """Validate command arguments.""" # Settings command doesn't require any arguments return True - + def get_help_text(self) -> str: """Get detailed help text for the settings command.""" return ( @@ -82,4 +88,4 @@ def get_help_text(self) -> str: "Examples:\n" " /settings - Open settings configuration\n\n" "Note: Settings are saved persistently and will be used for future sessions." - ) \ No newline at end of file + ) diff --git a/src/ii_agent/cli/settings_onboard.py b/src/ii_agent/cli/settings_onboard.py index cba0ade1..c45db1da 100644 --- a/src/ii_agent/cli/settings_onboard.py +++ b/src/ii_agent/cli/settings_onboard.py @@ -15,8 +15,10 @@ from pydantic import SecretStr from ii_agent.core.config.llm_config import LLMConfig, APITypes +from ii_agent.core.config.runtime_config import RuntimeConfig from ii_agent.core.storage.models.settings import Settings from ii_agent.core.storage.settings.file_settings_store import FileSettingsStore +from ii_agent.runtime.model.constants import RuntimeMode from ii_agent.utils.constants import DEFAULT_MODEL @@ -24,7 +26,7 @@ VERIFIED_ANTHROPIC_MODELS = [ "claude-sonnet-4@20250514", "claude-opus-4@20250514", - "claude-3-7-sonnet@20250219" + "claude-3-7-sonnet@20250219", ] VERIFIED_OPENAI_MODELS = [ @@ -33,27 +35,28 @@ ] VERIFIED_GEMINI_MODELS = [ - "gemini-2.5-flash" - "gemini-2.5-pro", + "gemini-2.5-flashgemini-2.5-pro", ] VERIFIED_PROVIDERS = ["anthropic", "openai", "gemini"] + # Color constants for styling COLOR_GREY = "#888888" class UserCancelledError(Exception): """Raised when user cancels the setup process.""" + pass def cli_confirm(question: str, choices: list[str]) -> int: """Simple confirmation dialog.""" - print_formatted_text(HTML(f'{question}')) + print_formatted_text(HTML(f"{question}")) for i, choice in enumerate(choices): - print_formatted_text(HTML(f'{i + 1}. {choice}')) - + print_formatted_text(HTML(f"{i + 1}. {choice}")) + while True: try: response = input("Enter your choice (number): ").strip() @@ -61,9 +64,11 @@ def cli_confirm(question: str, choices: list[str]) -> int: if 0 <= choice_num < len(choices): return choice_num else: - print_formatted_text(HTML('Invalid choice. Please try again.')) + print_formatted_text( + HTML("Invalid choice. Please try again.") + ) except ValueError: - print_formatted_text(HTML('Please enter a valid number.')) + print_formatted_text(HTML("Please enter a valid number.")) except KeyboardInterrupt: raise UserCancelledError("User cancelled setup") @@ -71,41 +76,66 @@ def cli_confirm(question: str, choices: list[str]) -> int: def display_settings(settings: Settings) -> None: """Display current settings in a formatted way.""" if not settings.llm_configs: - print_formatted_text(HTML('No LLM configurations found.')) + print_formatted_text(HTML("No LLM configurations found.")) return - + # Display default LLM configuration - default_llm = settings.llm_configs.get('default') + default_llm = settings.llm_configs.get("default") if not default_llm: - print_formatted_text(HTML('No default LLM configuration found.')) + print_formatted_text(HTML("No default LLM configuration found.")) return - + # Prepare labels and values labels_and_values = [ - (' Provider', default_llm.api_type.value), - (' Model', default_llm.model), - (' API Key', 'Vertex AI Auth' if default_llm.api_key and default_llm.api_key.get_secret_value() == 'vertex-ai-auth' else '********' if default_llm.api_key else 'Not Set'), - (' Base URL', default_llm.base_url or 'Default'), - (' Temperature', str(default_llm.temperature)), + (" Provider", default_llm.api_type.value), + (" Model", default_llm.model), + ( + " API Key", + "Vertex AI Auth" + if default_llm.api_key + and default_llm.api_key.get_secret_value() == "vertex-ai-auth" + else "********" + if default_llm.api_key + else "Not Set", + ), + (" Base URL", default_llm.base_url or "Default"), + (" Temperature", str(default_llm.temperature)), ] - + # Add Vertex AI specific settings if applicable if default_llm.api_type in [APITypes.GEMINI, APITypes.ANTHROPIC]: - labels_and_values.extend([ - (' Vertex Region', default_llm.vertex_region or 'Not Set'), - (' Vertex Project ID', default_llm.vertex_project_id or 'Not Set'), - ]) - + labels_and_values.extend( + [ + (" Vertex Region", default_llm.vertex_region or "Not Set"), + (" Vertex Project ID", default_llm.vertex_project_id or "Not Set"), + ] + ) + + # Add runtime configuration + if settings.runtime_config: + labels_and_values.extend( + [ + (" Runtime Mode", settings.runtime_config.mode.value), + (" Template ID", settings.runtime_config.template_id or "Not Set"), + ( + " Runtime API Key", + "********" + if settings.runtime_config.runtime_api_key + else "Not Set", + ), + ] + ) + # Calculate max width for alignment max_label_width = max(len(label) for label, _ in labels_and_values) - + # Construct the summary text settings_lines = [ - f'{label + ":":<{max_label_width + 1}} {value}' + f"{label + ':':<{max_label_width + 1}} {value}" for label, value in labels_and_values ] - settings_text = '\n'.join(settings_lines) - + settings_text = "\n".join(settings_lines) + container = Frame( TextArea( text=settings_text, @@ -113,10 +143,10 @@ def display_settings(settings: Settings) -> None: style=COLOR_GREY, wrap_lines=True, ), - title='Current Settings', - style=f'fg:{COLOR_GREY}', + title="Current Settings", + style=f"fg:{COLOR_GREY}", ) - + print_container(container) @@ -125,219 +155,265 @@ async def get_validated_input( prompt_text: str, completer=None, validator=None, - error_message: str = 'Input cannot be empty', + error_message: str = "Input cannot be empty", is_password: bool = False, ) -> str: """Get validated input from user.""" session.completer = completer value = None - + while True: try: if is_password: value = await session.prompt_async(prompt_text, is_password=True) else: value = await session.prompt_async(prompt_text) - + if validator: is_valid = validator(value) if not is_valid: - print_formatted_text('') - print_formatted_text(HTML(f'{error_message}: {value}')) - print_formatted_text('') + print_formatted_text("") + print_formatted_text(HTML(f"{error_message}: {value}")) + print_formatted_text("") continue elif not value: - print_formatted_text('') - print_formatted_text(HTML(f'{error_message}')) - print_formatted_text('') + print_formatted_text("") + print_formatted_text(HTML(f"{error_message}")) + print_formatted_text("") continue - + break except KeyboardInterrupt: raise UserCancelledError("User cancelled setup") - + return value def save_settings_confirmation() -> bool: """Ask user if they want to save the settings.""" - return cli_confirm( - '\nSave new settings? (They will take effect immediately)', - ['Yes, save', 'No, discard'] - ) == 0 + return ( + cli_confirm( + "\nSave new settings? (They will take effect immediately)", + ["Yes, save", "No, discard"], + ) + == 0 + ) async def setup_llm_configuration(settings_store: FileSettingsStore) -> None: """Interactive setup for LLM configuration.""" session = PromptSession() - + try: # Step 1: Select provider - print_formatted_text(HTML('\nSetting up LLM Configuration')) - print_formatted_text(HTML('Choose your preferred LLM provider:\n')) - - provider_choice = cli_confirm( - 'Select LLM Provider:', - VERIFIED_PROVIDERS - ) - + print_formatted_text(HTML("\nSetting up LLM Configuration")) + print_formatted_text(HTML("Choose your preferred LLM provider:\n")) + + provider_choice = cli_confirm("Select LLM Provider:", VERIFIED_PROVIDERS) + provider = VERIFIED_PROVIDERS[provider_choice] api_type = APITypes(provider) - + # Step 2: Select model - if provider == 'anthropic': + if provider == "anthropic": available_models = VERIFIED_ANTHROPIC_MODELS - elif provider == 'openai': + elif provider == "openai": available_models = VERIFIED_OPENAI_MODELS - elif provider == 'gemini': + elif provider == "gemini": available_models = VERIFIED_GEMINI_MODELS else: available_models = [DEFAULT_MODEL] - - print_formatted_text(HTML(f'\nDefault model: {available_models[0]}')) - - change_model = cli_confirm( - 'Do you want to use a different model?', - [f'Use {available_models[0]}', 'Select another model'] - ) == 1 - + + print_formatted_text( + HTML(f"\nDefault model: {available_models[0]}") + ) + + change_model = ( + cli_confirm( + "Do you want to use a different model?", + [f"Use {available_models[0]}", "Select another model"], + ) + == 1 + ) + if change_model: model_completer = FuzzyWordCompleter(available_models) - + def model_validator(x): if not x.strip(): return False if x not in available_models: - print_formatted_text(HTML( - f'Warning: {x} is not in the verified list for {provider}. ' - f'Make sure this model name is correct.' - )) + print_formatted_text( + HTML( + f"Warning: {x} is not in the verified list for {provider}. " + f"Make sure this model name is correct." + ) + ) return True - + model = await get_validated_input( session, - 'Enter model name (TAB for options, CTRL-C to cancel): ', + "Enter model name (TAB for options, CTRL-C to cancel): ", completer=model_completer, validator=model_validator, - error_message='Model name cannot be empty' + error_message="Model name cannot be empty", ) else: model = available_models[0] - + # Step 3: API Key (with Vertex AI support) api_key = None - + # For Vertex AI providers, check for existing authentication - if provider in ['anthropic', 'gemini']: - google_app_creds = os.getenv('GOOGLE_APPLICATION_CREDENTIALS') + if provider in ["anthropic", "gemini"]: + google_app_creds = os.getenv("GOOGLE_APPLICATION_CREDENTIALS") if google_app_creds: - print_formatted_text(HTML(f'Found GOOGLE_APPLICATION_CREDENTIALS: {google_app_creds}')) - use_existing_creds = cli_confirm( - 'Use existing Google Application Credentials?', - ['Yes, use existing credentials', 'No, enter API key manually'] - ) == 0 - + print_formatted_text( + HTML( + f"Found GOOGLE_APPLICATION_CREDENTIALS: {google_app_creds}" + ) + ) + use_existing_creds = ( + cli_confirm( + "Use existing Google Application Credentials?", + ["Yes, use existing credentials", "No, enter API key manually"], + ) + == 0 + ) + if use_existing_creds: - api_key = 'vertex-ai-auth' # Placeholder for Vertex AI auth + api_key = "vertex-ai-auth" # Placeholder for Vertex AI auth else: api_key = await get_validated_input( session, - 'Enter API Key (CTRL-C to cancel): ', - error_message='API Key cannot be empty', - is_password=True + "Enter API Key (CTRL-C to cancel): ", + error_message="API Key cannot be empty", + is_password=True, ) else: - print_formatted_text(HTML('No GOOGLE_APPLICATION_CREDENTIALS found.')) + print_formatted_text( + HTML("No GOOGLE_APPLICATION_CREDENTIALS found.") + ) auth_choice = cli_confirm( - 'How would you like to authenticate?', - ['Set up Google Application Credentials', 'Enter API key manually'] + "How would you like to authenticate?", + ["Set up Google Application Credentials", "Enter API key manually"], ) - + if auth_choice == 0: - print_formatted_text(HTML('Please set up Google Application Credentials:')) - print_formatted_text(HTML('1. Create a service account in Google Cloud Console')) - print_formatted_text(HTML('2. Download the JSON key file')) - print_formatted_text(HTML('3. Set GOOGLE_APPLICATION_CREDENTIALS environment variable')) - + print_formatted_text( + HTML( + "Please set up Google Application Credentials:" + ) + ) + print_formatted_text( + HTML( + "1. Create a service account in Google Cloud Console" + ) + ) + print_formatted_text( + HTML("2. Download the JSON key file") + ) + print_formatted_text( + HTML( + "3. Set GOOGLE_APPLICATION_CREDENTIALS environment variable" + ) + ) + creds_path = await get_validated_input( session, - 'Enter path to service account JSON file: ', - error_message='Path cannot be empty' + "Enter path to service account JSON file: ", + error_message="Path cannot be empty", ) - + if os.path.exists(creds_path): - os.environ['GOOGLE_APPLICATION_CREDENTIALS'] = creds_path - print_formatted_text(HTML(f'āœ“ Set GOOGLE_APPLICATION_CREDENTIALS to {creds_path}')) - api_key = 'vertex-ai-auth' + os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = creds_path + print_formatted_text( + HTML( + f"āœ“ Set GOOGLE_APPLICATION_CREDENTIALS to {creds_path}" + ) + ) + api_key = "vertex-ai-auth" else: - print_formatted_text(HTML(f'File not found: {creds_path}')) + print_formatted_text( + HTML(f"File not found: {creds_path}") + ) api_key = await get_validated_input( session, - 'Enter API Key (CTRL-C to cancel): ', - error_message='API Key cannot be empty', - is_password=True + "Enter API Key (CTRL-C to cancel): ", + error_message="API Key cannot be empty", + is_password=True, ) else: api_key = await get_validated_input( session, - 'Enter API Key (CTRL-C to cancel): ', - error_message='API Key cannot be empty', - is_password=True + "Enter API Key (CTRL-C to cancel): ", + error_message="API Key cannot be empty", + is_password=True, ) else: # For non-Vertex AI providers (OpenAI, etc.) api_key = await get_validated_input( session, - 'Enter API Key (CTRL-C to cancel): ', - error_message='API Key cannot be empty', - is_password=True + "Enter API Key (CTRL-C to cancel): ", + error_message="API Key cannot be empty", + is_password=True, ) - + # Step 4: Optional base URL (only for OpenAI) base_url = None - if provider == 'openai': - if cli_confirm('Do you want to set a custom base URL?', ['No', 'Yes']) == 1: + if provider == "openai": + if cli_confirm("Do you want to set a custom base URL?", ["No", "Yes"]) == 1: base_url = await get_validated_input( session, - 'Enter base URL (CTRL-C to cancel): ', - error_message='Base URL cannot be empty' + "Enter base URL (CTRL-C to cancel): ", + error_message="Base URL cannot be empty", ) - + # Step 5: Vertex AI specific settings (for Gemini and Anthropic) vertex_region = None vertex_project_id = None - - if provider in ['gemini', 'anthropic']: - provider_name = 'Gemini' if provider == 'gemini' else 'Anthropic' - print_formatted_text(HTML(f'\nVertex AI Configuration (required for {provider_name}):')) - + + if provider in ["gemini", "anthropic"]: + provider_name = "Gemini" if provider == "gemini" else "Anthropic" + print_formatted_text( + HTML( + f"\nVertex AI Configuration (required for {provider_name}):" + ) + ) + vertex_region = await get_validated_input( session, - 'Enter Vertex AI region (e.g., us-east5): ', - error_message='Vertex region cannot be empty' + "Enter Vertex AI region (e.g., us-east5): ", + error_message="Vertex region cannot be empty", ) - + vertex_project_id = await get_validated_input( session, - 'Enter Vertex AI project ID: ', - error_message='Vertex project ID cannot be empty' + "Enter Vertex AI project ID: ", + error_message="Vertex project ID cannot be empty", ) - + # Step 6: Temperature setting temperature = 0.0 - if cli_confirm('Do you want to set a custom temperature?', ['No (use 0.0)', 'Yes']) == 1: + if ( + cli_confirm( + "Do you want to set a custom temperature?", ["No (use 0.0)", "Yes"] + ) + == 1 + ): temp_input = await get_validated_input( session, - 'Enter temperature (0.0-1.0): ', - validator=lambda x: x.replace('.', '').isdigit() and 0.0 <= float(x) <= 1.0, - error_message='Temperature must be a number between 0.0 and 1.0' + "Enter temperature (0.0-1.0): ", + validator=lambda x: x.replace(".", "").isdigit() + and 0.0 <= float(x) <= 1.0, + error_message="Temperature must be a number between 0.0 and 1.0", ) temperature = float(temp_input) - + # Confirm save if not save_settings_confirmation(): return - + # Create and save configuration llm_config = LLMConfig( model=model, @@ -346,55 +422,148 @@ def model_validator(x): temperature=temperature, vertex_region=vertex_region, vertex_project_id=vertex_project_id, - api_type=api_type + api_type=api_type, ) - + # Load existing settings or create new settings = await settings_store.load() if not settings: settings = Settings() - + # Set as default LLM config - settings.llm_configs['default'] = llm_config - + settings.llm_configs["default"] = llm_config + # Save settings await settings_store.store(settings) - - print_formatted_text(HTML('\nāœ“ Settings saved successfully!')) - + + print_formatted_text(HTML("\nāœ“ Settings saved successfully!")) + except UserCancelledError: - print_formatted_text(HTML('\nSetup cancelled by user.')) + print_formatted_text(HTML("\nSetup cancelled by user.")) except Exception as e: - print_formatted_text(HTML(f'\nError during setup: {e}')) + print_formatted_text(HTML(f"\nError during setup: {e}")) + + +async def setup_runtime_configuration(settings_store: FileSettingsStore) -> None: + """Interactive setup for runtime configuration.""" + session = PromptSession() + + try: + # Step 1: Select runtime mode + print_formatted_text(HTML("\nSetting up Runtime Configuration")) + print_formatted_text(HTML("Choose your preferred runtime mode:\n")) + + runtime_choices = [mode.value for mode in RuntimeMode] + + mode_choice = cli_confirm( + "Select Runtime Mode:", + runtime_choices, + ) + from ipdb import set_trace + + set_trace() + + runtime_mode = RuntimeMode(runtime_choices[mode_choice]) + + # Step 2: E2B specific configuration + template_id = None + runtime_api_key = None + + if runtime_mode == RuntimeMode.E2B: + print_formatted_text( + HTML("\nE2B Configuration (required for E2B runtime):") + ) + + template_id = await get_validated_input( + session, + "Enter E2B template ID: ", + error_message="Template ID cannot be empty", + ) + + runtime_api_key = await get_validated_input( + session, + "Enter E2B API key: ", + error_message="API key cannot be empty", + is_password=True, + ) + + # Confirm save + if not save_settings_confirmation(): + return + + # Create runtime configuration + runtime_config = RuntimeConfig( + mode=runtime_mode, + template_id=template_id, + runtime_api_key=SecretStr(runtime_api_key) if runtime_api_key else None, + ) + + # Load existing settings or create new + settings = await settings_store.load() + if not settings: + settings = Settings() + + # Set runtime config + settings.runtime_config = runtime_config + + # Save settings + await settings_store.store(settings) + + print_formatted_text( + HTML("\nāœ“ Runtime settings saved successfully!") + ) + print_formatted_text( + HTML( + "Note: Runtime changes will take effect on the next startup, not the current session." + ) + ) + + except UserCancelledError: + print_formatted_text( + HTML("\nRuntime setup cancelled by user.") + ) + except Exception as e: + print_formatted_text(HTML(f"\nError during runtime setup: {e}")) async def run_first_time_setup(settings_store: FileSettingsStore) -> bool: """Run the first-time setup flow.""" - print_formatted_text(HTML('\nWelcome to ii-agent!')) - print_formatted_text(HTML('No settings found. Let\'s set up your LLM configuration.\n')) - + print_formatted_text(HTML("\nWelcome to ii-agent!")) + print_formatted_text( + HTML("No settings found. Let's set up your configuration.\n") + ) + try: + # Step 1: Setup LLM configuration await setup_llm_configuration(settings_store) + + # Step 2: Setup runtime configuration + await setup_runtime_configuration(settings_store) + return True except Exception as e: - print_formatted_text(HTML(f'\nSetup failed: {e}')) + print_formatted_text(HTML(f"\nSetup failed: {e}")) return False async def modify_settings(settings_store: FileSettingsStore) -> None: """Modify existing settings.""" settings = await settings_store.load() - + if settings: display_settings(settings) - + modify_choice = cli_confirm( - '\nWhat would you like to modify?', - ['LLM Configuration', 'Go back'] + "\nWhat would you like to modify?", + ["LLM Configuration", "Runtime Configuration", "Go back"], ) - + if modify_choice == 0: await setup_llm_configuration(settings_store) + elif modify_choice == 1: + await setup_runtime_configuration(settings_store) else: - print_formatted_text(HTML('No settings found. Running first-time setup...')) - await run_first_time_setup(settings_store) \ No newline at end of file + print_formatted_text( + HTML("No settings found. Running first-time setup...") + ) + await run_first_time_setup(settings_store) diff --git a/src/ii_agent/core/storage/settings/file_settings_store.py b/src/ii_agent/core/storage/settings/file_settings_store.py index 7ea2553e..1af69ceb 100644 --- a/src/ii_agent/core/storage/settings/file_settings_store.py +++ b/src/ii_agent/core/storage/settings/file_settings_store.py @@ -29,14 +29,14 @@ class FileSettingsStore(SettingsStore): file_store: FileStore path: str = "settings.json" - async def load(self) -> Settings: + async def load(self) -> Settings | None: try: json_str = await call_sync_from_async(self.file_store.read, self.path) kwargs = json.loads(json_str) settings = Settings(**kwargs) return settings except FileNotFoundError: - raise FileNotFoundError(f"Settings file not found at {self.path}") + return None async def store(self, settings: Settings) -> None: json_str = settings.model_dump_json(context={"expose_secrets": True}) From f215541b8a725f1dee1f7e95e28ba06658f5fe9f Mon Sep 17 00:00:00 2001 From: Khoa Ngo The Date: Thu, 24 Jul 2025 02:11:51 +0700 Subject: [PATCH 07/13] feat: cli for session --- src/ii_agent/cli/app.py | 95 +--- src/ii_agent/cli/config.py | 39 +- src/ii_agent/cli/main.py | 139 ++--- src/ii_agent/cli/session_config.py | 39 ++ src/ii_agent/cli/settings_onboard.py | 4 - src/ii_agent/cli/state_persistence.py | 157 +++--- .../cli/subscribers/console_subscriber.py | 491 ++++++++++++------ src/ii_agent/core/config/ii_agent_config.py | 30 +- 8 files changed, 543 insertions(+), 451 deletions(-) create mode 100644 src/ii_agent/cli/session_config.py diff --git a/src/ii_agent/cli/app.py b/src/ii_agent/cli/app.py index 7d30b396..8ad7bc37 100644 --- a/src/ii_agent/cli/app.py +++ b/src/ii_agent/cli/app.py @@ -55,6 +55,7 @@ def __init__( # Create state manager - we'll update it with continue_session later self.state_manager = None self.workspace_path = workspace_path + # Session config will be set up during run_interactive_mode # Create event stream self.event_stream = AsyncEventStream(logger=logger) @@ -64,6 +65,7 @@ def __init__( config=config, confirmation_callback=self._handle_tool_confirmation, ) + self.session_config = None # Subscribe to events self.event_stream.subscribe(self.console_subscriber.handle_event) @@ -115,10 +117,15 @@ async def initialize_agent(self, continue_from_state: bool = False) -> None: if self.agent_controller is not None: return + if not self.session_config: + raise ValueError("Session config not initialized") + # Create state manager now that we know if we're continuing if self.state_manager is None: self.state_manager = StateManager( - Path(self.workspace_path), continue_session=continue_from_state + Path(self.config.file_store_path), + session_name=self.session_config.session_name, + continue_session=continue_from_state, ) settings_store = await FileSettingsStore.get_instance(self.config, None) @@ -180,7 +187,7 @@ async def initialize_agent(self, continue_from_state: bool = False) -> None: ) runtime_manager = RuntimeManager( - session_id=UUID(self.config.session_id), settings=settings + session_id=UUID(self.session_config.session_id), settings=settings ) await runtime_manager.start_runtime() mcp_client = await runtime_manager.get_mcp_client( @@ -244,22 +251,24 @@ async def initialize_agent(self, continue_from_state: bool = False) -> None: self.agent_controller.history ) - async def run_interactive_mode( - self, - session_name: Optional[str] = None, - resume: bool = False, - continue_from_state: bool = False, - ) -> int: + async def run_interactive_mode(self) -> int: """Run interactive chat mode.""" try: + # Set up session configuration with interactive selection + self.session_config = await self.console_subscriber.setup_session_config() + + # Initialize with session continuation check + continue_from_state = ( + await self.console_subscriber.should_continue_from_state( + self.session_config.session_name, self.config.file_store_path + ) + ) await self.initialize_agent(continue_from_state) self.console_subscriber.print_welcome() - self.console_subscriber.print_session_info(session_name) - - # Load session if resuming - if resume and session_name: - self._load_session(session_name) + self.console_subscriber.print_session_info( + self.session_config.session_name if self.session_config else None + ) while True: try: @@ -277,7 +286,9 @@ async def run_interactive_mode( "config": self.config, "agent_controller": self.agent_controller, "workspace_manager": self.workspace_manager, - "session_name": session_name, + "session_name": self.session_config.session_name + if self.session_config + else None, "should_exit": False, } @@ -309,10 +320,6 @@ async def run_interactive_mode( resume=True, # Always resume in interactive mode ) - # Save session if name provided - if session_name: - self._save_session(session_name) - except KeyboardInterrupt: self.console_subscriber.console.print( "\nāš ļø [yellow]Interrupted by user[/yellow]" @@ -367,58 +374,6 @@ def _save_output(self, result: str, output_file: str, output_format: str) -> Non except Exception as e: print(f"Error saving output: {e}") - def _load_session(self, session_name: str) -> None: - """Load session from file.""" - try: - session_dir = self.config.get_session_dir() - if not session_dir: - return - - session_file = session_dir / f"{session_name}.json" - if not session_file.exists(): - print(f"Session '{session_name}' not found") - return - - with open(session_file, "r") as f: - session_data = json.load(f) - - # Restore message history - if self.agent_controller and "history" in session_data: - # You'll need to implement history restoration based on your State class - pass - - print(f"Session '{session_name}' loaded") - - except Exception as e: - print(f"Error loading session: {e}") - - def _save_session(self, session_name: str) -> None: - """Save session to file.""" - try: - session_dir = self.config.get_session_dir() - if not session_dir: - return - - session_dir.mkdir(parents=True, exist_ok=True) - session_file = session_dir / f"{session_name}.json" - - # Save message history - session_data = { - "name": session_name, - "timestamp": str(asyncio.get_event_loop().time()), - "config": self.config.get_llm_config(), - } - - if self.agent_controller: - # You'll need to implement history serialization based on your State class - pass - - with open(session_file, "w") as f: - json.dump(session_data, f, indent=2) - - except Exception as e: - logger.error(f"Error saving session: {e}") - def _save_state_on_exit(self, should_save: bool) -> None: """Save agent state when exiting if requested.""" if not should_save or not self.agent_controller or not self.state_manager: diff --git a/src/ii_agent/cli/config.py b/src/ii_agent/cli/config.py index 218ec2aa..5d973136 100644 --- a/src/ii_agent/cli/config.py +++ b/src/ii_agent/cli/config.py @@ -15,7 +15,6 @@ async def setup_cli_config( - session_id: str, workspace: Optional[str] = None, model: Optional[str] = None, api_key: Optional[str] = None, @@ -24,33 +23,37 @@ async def setup_cli_config( mcp_config: Optional[Dict[str, Any]] = None, ) -> tuple[IIAgentConfig, LLMConfig, str]: """Setup CLI configuration using the standard configuration pattern.""" - + # Create config with defaults - config = IIAgentConfig(session_id=session_id, mcp_config=mcp_config) - + config = IIAgentConfig(mcp_config=mcp_config) + # Load settings from store settings_store = await FileSettingsStore.get_instance(config=config, user_id=None) settings = await settings_store.load() - + # If no settings exist, run first-time setup - if not settings or not settings.llm_configs or not settings.llm_configs.get('default'): + if ( + not settings + or not settings.llm_configs + or not settings.llm_configs.get("default") + ): print("No configuration found. Running first-time setup...") setup_success = await run_first_time_setup(settings_store) if not setup_success: raise RuntimeError("Failed to complete initial setup") - + # Reload settings after setup settings = await settings_store.load() - + # Create LLM config from settings llm_config = LLMConfig() - + if settings and settings.llm_configs: # Get the default LLM config if it exists - default_llm_config = settings.llm_configs.get('default') + default_llm_config = settings.llm_configs.get("default") if default_llm_config: llm_config = default_llm_config - + # Override with passed parameters (CLI arguments take precedence) if model: llm_config.model = model @@ -60,16 +63,16 @@ async def setup_cli_config( llm_config.base_url = base_url if temperature is not None: llm_config.temperature = temperature - + # Handle vertex configuration with fallback to environment variables # Note: vertex args are removed from CLI, so only check environment variables if not llm_config.vertex_region: - llm_config.vertex_region = os.environ.get('VERTEX_REGION') - + llm_config.vertex_region = os.environ.get("VERTEX_REGION") + if not llm_config.vertex_project_id: - llm_config.vertex_project_id = os.environ.get('VERTEX_PROJECT_ID') - + llm_config.vertex_project_id = os.environ.get("VERTEX_PROJECT_ID") + # Determine workspace path workspace_path = workspace or "." - - return config, llm_config, workspace_path \ No newline at end of file + + return config, llm_config, workspace_path diff --git a/src/ii_agent/cli/main.py b/src/ii_agent/cli/main.py index 0a935670..288ce75f 100644 --- a/src/ii_agent/cli/main.py +++ b/src/ii_agent/cli/main.py @@ -4,7 +4,6 @@ This module provides the command-line interface for interacting with the AgentController. """ -import uuid import json import argparse import asyncio @@ -22,116 +21,57 @@ def create_parser() -> argparse.ArgumentParser: description="Intelligent Internet Agent - CLI interface for AI-powered automation", epilog="Use 'ii-agent --help' for command-specific help.", ) - + # Global options parser.add_argument( - "--workspace", - "-w", - type=str, - default=".", - help="Working directory for the agent (default: current directory)" - ) - parser.add_argument( - "--config", - "-c", - type=str, - help="Configuration file path" - ) - parser.add_argument( - "--mcp-config", + "--workspace", + "-w", type=str, - help="MCP config file path" + default=".", + help="Working directory for the agent (default: current directory)", ) - parser.add_argument( - "--minimal", - "-m", - action="store_true", - help="Minimize output" - ) - + parser.add_argument("--config", "-c", type=str, help="Configuration file path") + parser.add_argument("--mcp-config", type=str, help="MCP config file path") + parser.add_argument("--minimal", "-m", action="store_true", help="Minimize output") + # Subcommands subparsers = parser.add_subparsers(dest="command", help="Available commands") - + # Chat command (interactive mode) - chat_parser = subparsers.add_parser( - "chat", - help="Start interactive chat session" - ) - chat_parser.add_argument( - "--session", - "-s", - type=str, - help="Session name to save/restore conversation" - ) - chat_parser.add_argument( - "--resume", - "-r", - action="store_true", - help="Resume from previous session" - ) - chat_parser.add_argument( - "--continue", - "-cont", - action="store_true", - help="Continue from last saved agent state in current directory" - ) - + # chat_parser = subparsers.add_parser("chat", help="Start interactive chat session") + # Config command - config_parser = subparsers.add_parser( - "config", - help="Manage configuration" - ) + config_parser = subparsers.add_parser("config", help="Manage configuration") config_group = config_parser.add_mutually_exclusive_group(required=True) config_group.add_argument( - "--show", - action="store_true", - help="Show current configuration" + "--show", action="store_true", help="Show current configuration" ) config_group.add_argument( - "--set", - nargs=2, - metavar=("KEY", "VALUE"), - help="Set configuration value" + "--set", nargs=2, metavar=("KEY", "VALUE"), help="Set configuration value" ) config_group.add_argument( - "--reset", - action="store_true", - help="Reset configuration to defaults" + "--reset", action="store_true", help="Reset configuration to defaults" ) - + # LLM configuration options parser.add_argument( - "--llm-provider", - choices=["anthropic", "openai", "gemini"], - help="LLM provider to use" - ) - parser.add_argument( - "--llm-model", - type=str, - help="Specific model to use" + "--llm-provider", + choices=["anthropic", "openai", "gemini"], + help="LLM provider to use", ) + parser.add_argument("--llm-model", type=str, help="Specific model to use") + parser.add_argument("--max-tokens", type=int, help="Maximum tokens per turn") parser.add_argument( - "--max-tokens", - type=int, - help="Maximum tokens per turn" + "--temperature", type=float, help="Temperature for LLM responses" ) - parser.add_argument( - "--temperature", - type=float, - help="Temperature for LLM responses" - ) - parser.add_argument( - "--tools", - nargs="*", - help="Specific tools to enable" - ) - + parser.add_argument("--tools", nargs="*", help="Specific tools to enable") + return parser def validate_args(args: argparse.Namespace) -> None: """Validate command-line arguments.""" - + if args.workspace: workspace_path = Path(args.workspace) if not workspace_path.exists(): @@ -140,7 +80,7 @@ def validate_args(args: argparse.Namespace) -> None: if not workspace_path.is_dir(): print(f"Error: Workspace path '{args.workspace}' is not a directory") sys.exit(1) - + if args.config: config_path = Path(args.config) if not config_path.exists(): @@ -152,51 +92,46 @@ async def main_async() -> int: """Main entry point for the CLI.""" parser = create_parser() args = parser.parse_args() - + # Show help if no command is provided if not args.command: parser.print_help() return 0 - + # Validate arguments validate_args(args) - session_id = args.session if args.session else str(uuid.uuid4()) mcp_config = json.loads(open(args.mcp_config).read()) if args.mcp_config else None - + try: # Setup CLI configuration using the new pattern config, llm_config, workspace_path = await setup_cli_config( - session_id=session_id, workspace=args.workspace, model=args.llm_model, temperature=args.temperature, mcp_config=mcp_config, ) - + # Handle config command if args.command == "config": return await handle_config_command(args, config, llm_config) - + # Create and run CLI app app = CLIApp(config, llm_config, workspace_path, minimal=args.minimal) - + if args.command == "chat": - return await app.run_interactive_mode( - session_name=args.session, - resume=args.resume, - continue_from_state=getattr(args, 'continue', False) - ) + return await app.run_interactive_mode() else: print(f"Unknown command: {args.command}") return 1 - + except KeyboardInterrupt: print("\nOperation interrupted by user") return 130 except Exception as e: if args.debug: import traceback + traceback.print_exc() else: print(f"Error: {e}") @@ -220,7 +155,7 @@ async def handle_config_command(args: argparse.Namespace, config, llm_config) -> elif args.reset: print("Reset configuration (feature not yet implemented)") return 0 - + return 1 diff --git a/src/ii_agent/cli/session_config.py b/src/ii_agent/cli/session_config.py new file mode 100644 index 00000000..7265fb4f --- /dev/null +++ b/src/ii_agent/cli/session_config.py @@ -0,0 +1,39 @@ +"""Session configuration for CLI.""" + +from pathlib import Path +from typing import List +from pydantic import BaseModel + + +class SessionConfig(BaseModel): + """Configuration for session management.""" + + session_id: str + session_name: str + + @property + def sessions_dir(self) -> Path: + """Get the sessions directory path.""" + home = Path.home() + return home / ".ii_agent" / "sessions" + + def get_available_sessions(self) -> List[str]: + """Get list of available session names from the sessions directory.""" + sessions_dir = self.sessions_dir + if not sessions_dir.exists(): + return [] + + sessions = [] + for session_file in sessions_dir.glob("*.json"): + sessions.append(session_file.stem) + + return sorted(sessions) + + def session_exists(self, session_name: str) -> bool: + """Check if a session exists.""" + session_file = self.sessions_dir / f"{session_name}.json" + return session_file.exists() + + def ensure_sessions_dir(self) -> None: + """Ensure the sessions directory exists.""" + self.sessions_dir.mkdir(parents=True, exist_ok=True) diff --git a/src/ii_agent/cli/settings_onboard.py b/src/ii_agent/cli/settings_onboard.py index c45db1da..6eb210a3 100644 --- a/src/ii_agent/cli/settings_onboard.py +++ b/src/ii_agent/cli/settings_onboard.py @@ -459,10 +459,6 @@ async def setup_runtime_configuration(settings_store: FileSettingsStore) -> None "Select Runtime Mode:", runtime_choices, ) - from ipdb import set_trace - - set_trace() - runtime_mode = RuntimeMode(runtime_choices[mode_choice]) # Step 2: E2B specific configuration diff --git a/src/ii_agent/cli/state_persistence.py b/src/ii_agent/cli/state_persistence.py index 4ccfb507..3d4918b6 100644 --- a/src/ii_agent/cli/state_persistence.py +++ b/src/ii_agent/cli/state_persistence.py @@ -21,27 +21,37 @@ class StateManager: """Manages saving and loading of agent state for --continue functionality.""" - - def __init__(self, workspace_path: Path, continue_session: bool = False): + + def __init__( + self, workspace_path: Path, session_name: str, continue_session: bool = False + ): self.workspace_path = str(workspace_path) - self.ii_agent_dir = workspace_path / ".ii_agent" + self.session_name = session_name + self.ii_agent_dir = workspace_path / "sessions" / session_name self.ii_agent_dir.mkdir(exist_ok=True) - self.current_state_link = self.ii_agent_dir / "current_state.json" + self.agent_state_link = self.ii_agent_dir / "agent_state.json" + self.metadata_link = self.ii_agent_dir / "metadata.json" # Use the global file store location self.file_store = LocalFileStore("~/.ii_agent") - + # Determine session ID based on whether we're continuing or starting fresh - if continue_session and self.current_state_link.exists(): + if ( + continue_session + and self.agent_state_link.exists() + and self.metadata_link.exists() + ): try: - with open(self.current_state_link, 'r') as f: - current_state_info = json.load(f) - existing_session_id = current_state_info.get("current_session_id") + with open(self.metadata_link, "r") as f: + metadata = json.load(f) + existing_session_id = metadata.get("session_id") if existing_session_id: self.session_id = existing_session_id logger.info(f"Continuing with existing session: {self.session_id}") else: self.session_id = uuid.uuid4().hex - logger.info(f"No valid session found, creating new session: {self.session_id}") + logger.info( + f"No valid session found, creating new session: {self.session_id}" + ) except Exception as e: logger.warning(f"Error reading existing session, creating new one: {e}") self.session_id = uuid.uuid4().hex @@ -49,20 +59,20 @@ def __init__(self, workspace_path: Path, continue_session: bool = False): self.session_id = uuid.uuid4().hex if not continue_session: logger.info(f"Starting new session: {self.session_id}") - + def save_state( - self, + self, agent_state: State, config: IIAgentConfig, llm_config: LLMConfig, workspace_path: str, - session_name: Optional[str] = None + session_name: Optional[str] = None, ) -> None: """Save state and metadata using the new JSON format.""" try: # Save core state using State's save_to_session method agent_state.save_to_session(self.session_id, self.file_store) - + # Save metadata separately metadata = { "version": "2.0", @@ -70,67 +80,68 @@ def save_state( "session_id": self.session_id, "workspace_path": str(workspace_path), "session_name": session_name, - "agent_state": { - "message_count": len(agent_state.message_lists) - }, + "agent_state": {"message_count": len(agent_state.message_lists)}, "config": { "max_output_tokens_per_turn": config.max_output_tokens_per_turn, "max_turns": config.max_turns, - "debug": getattr(config, 'debug', False) + "debug": getattr(config, "debug", False), }, "llm_config": { "model": llm_config.model, "temperature": llm_config.temperature, "max_message_chars": llm_config.max_message_chars, - "api_type": llm_config.api_type.value if llm_config.api_type else 'anthropic', - "max_retries": llm_config.max_retries - } + "api_type": llm_config.api_type.value + if llm_config.api_type + else "anthropic", + "max_retries": llm_config.max_retries, + }, } - + metadata_filename = get_conversation_metadata_filename(self.session_id) - self.file_store.write(metadata_filename, json.dumps(metadata, indent=2, ensure_ascii=False)) - + self.file_store.write( + metadata_filename, json.dumps(metadata, indent=2, ensure_ascii=False) + ) + # Update current state pointer current_state_info = { "current_session_id": self.session_id, "workspace_path": self.workspace_path, - "last_updated": datetime.now().isoformat() + "last_updated": datetime.now().isoformat(), } - with open(self.current_state_link, 'w') as f: + with open(self.agent_state_link, "w") as f: json.dump(current_state_info, f, indent=2) - + logger.info(f"State saved for session {self.session_id}") - + except Exception as e: logger.error(f"Error saving state: {e}") raise - + def load_state(self) -> Optional[Dict[str, Any]]: """Load the saved agent state from current state pointer.""" try: # First, check if current_state.json exists - if not self.current_state_link.exists(): + if not self.agent_state_link.exists(): return None - + # Read the current state pointer - with open(self.current_state_link, 'r') as f: - current_state_info = json.load(f) - - session_id = current_state_info.get("current_session_id") - workspace_path = current_state_info.get("workspace_path") - + with open(self.metadata_link, "r") as f: + metadata = json.load(f) + + session_id = metadata.get("session_id") + if not session_id: return None - + # Create a new State object and restore from session state = State() state.restore_from_session(session_id, self.file_store) - + # Load metadata metadata_filename = get_conversation_metadata_filename(session_id) metadata_json = self.file_store.read(metadata_filename) metadata = json.loads(metadata_json) - + # Combine state and metadata into return format return { "version": metadata.get("version", "2.0"), @@ -140,64 +151,64 @@ def load_state(self) -> Optional[Dict[str, Any]]: "session_id": metadata.get("session_id"), "agent_state": { "message_lists": state.message_lists, - "last_user_prompt_index": state.last_user_prompt_index + "last_user_prompt_index": state.last_user_prompt_index, }, "config": metadata.get("config", {}), - "llm_config": metadata.get("llm_config", {}) + "llm_config": metadata.get("llm_config", {}), } - + except Exception as e: logger.error(f"Error loading state: {e}") return None - - + def clear_state(self) -> None: """Remove the saved state files.""" try: # Remove current state pointer - if self.current_state_link.exists(): - self.current_state_link.unlink() - logger.info(f"Current state pointer {self.current_state_link} removed") + if self.agent_state_link.exists(): + self.agent_state_link.unlink() + logger.info(f"Current state pointer {self.agent_state_link} removed") except Exception as e: logger.error(f"Error clearing state: {e}") - + def has_saved_state(self) -> bool: """Check if there's a saved state file.""" - return self.current_state_link.exists() - + return self.agent_state_link.exists() + def get_state_info(self) -> Optional[Dict[str, Any]]: """Get basic info about the saved state without loading it.""" try: - if not self.current_state_link.exists(): + if not self.agent_state_link.exists(): return None - + # Read current state pointer - with open(self.current_state_link, 'r') as f: + with open(self.agent_state_link, "r") as f: current_state_info = json.load(f) - + session_id = current_state_info.get("current_session_id") - workspace_path = current_state_info.get("workspace_path") - + if not session_id: return None - + # Read metadata file metadata_filename = get_conversation_metadata_filename(session_id) try: metadata_json = self.file_store.read(metadata_filename) metadata = json.loads(metadata_json) - + return { "timestamp": metadata.get("timestamp"), "session_name": metadata.get("session_name"), "workspace_path": metadata.get("workspace_path"), "version": metadata.get("version", "2.0"), "session_id": metadata.get("session_id"), - "message_count": metadata.get("agent_state", {}).get("message_count", 0) + "message_count": metadata.get("agent_state", {}).get( + "message_count", 0 + ), } except FileNotFoundError: return None - + except Exception as e: logger.error(f"Error getting state info: {e}") return None @@ -206,28 +217,32 @@ def get_state_info(self) -> Optional[Dict[str, Any]]: def restore_agent_state(state_data: Dict[str, Any]) -> State: """Restore a State object from saved state data.""" agent_state = State() - + if "agent_state" in state_data: saved_agent_state = state_data["agent_state"] - + # Restore message lists if "message_lists" in saved_agent_state: agent_state.message_lists = saved_agent_state["message_lists"] - + # Restore last user prompt index if "last_user_prompt_index" in saved_agent_state: - agent_state.last_user_prompt_index = saved_agent_state["last_user_prompt_index"] - + agent_state.last_user_prompt_index = saved_agent_state[ + "last_user_prompt_index" + ] + return agent_state -def restore_configs(state_data: Dict[str, Any]) -> tuple[Dict[str, Any], Dict[str, Any]]: +def restore_configs( + state_data: Dict[str, Any], +) -> tuple[Dict[str, Any], Dict[str, Any]]: """Extract configuration data from saved state.""" from ii_agent.core.config.llm_config import APITypes - + config_data = state_data.get("config", {}) llm_config_data = state_data.get("llm_config", {}) - + # Convert api_type string back to enum if present if "api_type" in llm_config_data and isinstance(llm_config_data["api_type"], str): try: @@ -235,5 +250,5 @@ def restore_configs(state_data: Dict[str, Any]) -> tuple[Dict[str, Any], Dict[st except ValueError: # If invalid enum value, default to anthropic llm_config_data["api_type"] = APITypes.ANTHROPIC - - return config_data, llm_config_data \ No newline at end of file + + return config_data, llm_config_data diff --git a/src/ii_agent/cli/subscribers/console_subscriber.py b/src/ii_agent/cli/subscribers/console_subscriber.py index 808a6088..2b7aabe8 100644 --- a/src/ii_agent/cli/subscribers/console_subscriber.py +++ b/src/ii_agent/cli/subscribers/console_subscriber.py @@ -6,6 +6,8 @@ from typing import Optional, Dict, Any, Callable from threading import Lock +import uuid +from pathlib import Path from rich.console import Console from rich.panel import Panel @@ -13,6 +15,7 @@ from rich.progress import Progress from rich.table import Table from rich.syntax import Syntax +from rich.prompt import Prompt, Confirm from ii_agent.core.event import RealtimeEvent, EventType from ii_agent.core.config.llm_config import LLMConfig @@ -21,17 +24,18 @@ from ii_agent.cli.components.spinner import AnimatedSpinner from ii_agent.cli.components.token_usage import TokenUsageDisplay from ii_agent.cli.components.todo_panel import TodoPanel +from ii_agent.cli.session_config import SessionConfig class ConsoleSubscriber: """Subscriber that handles console output for agent events.""" - + def __init__( - self, - minimal: bool = False, + self, + minimal: bool = False, config: Optional[IIAgentConfig] = None, settings: Optional[Settings] = None, - confirmation_callback: Optional[Callable[[str, str, bool, str], None]] = None + confirmation_callback: Optional[Callable[[str, str, bool, str], None]] = None, ): self.minimal = minimal self.config = config @@ -45,10 +49,10 @@ def __init__( self._spinner: Optional[AnimatedSpinner] = None self._token_display = TokenUsageDisplay(self.console) self._todo_panel = TodoPanel(self.console) - + def handle_event(self, event: RealtimeEvent) -> None: """Handle an event by outputting to console.""" - with self._lock: + with self._lock: if event.type == EventType.AGENT_THINKING: self._handle_thinking_event(event) elif event.type == EventType.TOOL_CALL: @@ -65,7 +69,7 @@ def handle_event(self, event: RealtimeEvent) -> None: self._handle_processing_event(event) elif event.type == EventType.TOOL_CONFIRMATION: self._handle_tool_confirmation_event(event) - + def _handle_thinking_event(self, event: RealtimeEvent) -> None: """Handle agent thinking event.""" if not self._thinking_indicator: @@ -76,67 +80,69 @@ def _handle_thinking_event(self, event: RealtimeEvent) -> None: else: self.console.print("šŸ¤” [cyan]Agent is thinking...[/cyan]") self._thinking_indicator = True - + def _handle_tool_call_event(self, event: RealtimeEvent) -> None: """Handle tool call event.""" self._clear_thinking_indicator() - + content = event.content tool_name = content.get("tool_name", "unknown") tool_input = content.get("tool_input", {}) - + self._current_tool_call = content - + if not self.minimal: self._print_tool_call(tool_name, tool_input) else: self.console.print(f"šŸ”§ [blue]Using tool:[/blue] [bold]{tool_name}[/bold]") - + def _handle_tool_result_event(self, event: RealtimeEvent) -> None: """Handle tool result event.""" content = event.content tool_name = content.get("tool_name", "unknown") result = content.get("result", "") - + if not self.minimal: self._print_tool_result(tool_name, result) else: - self.console.print(f"āœ… [green]Tool completed:[/green] [bold]{tool_name}[/bold]") - + self.console.print( + f"āœ… [green]Tool completed:[/green] [bold]{tool_name}[/bold]" + ) + self._current_tool_call = None - + def _handle_agent_response_event(self, event: RealtimeEvent) -> None: """Handle agent response event.""" self._clear_thinking_indicator() - + content = event.content text = content.get("text", "") - + if text.strip(): self._print_response(text) - + def _handle_interrupted_event(self, event: RealtimeEvent) -> None: """Handle interrupted event.""" self._clear_thinking_indicator() - + content = event.content text = content.get("text", "") - + self.console.print(f"āš ļø [yellow]Interrupted:[/yellow] {text}") - + def _handle_error_event(self, event: RealtimeEvent) -> None: """Handle error event.""" self._clear_thinking_indicator() - + content = event.content error_msg = content.get("error", "Unknown error") - + # Simple error format with clean prefix error_text = Text() error_text.append(" āŽæ ", style="red") error_text.append(f"Error: {error_msg}", style="red") self.console.print(error_text) - + def _handle_tool_confirmation_event(self, event: RealtimeEvent) -> None: """Handle tool confirmation event with interactive prompt.""" content = event.content @@ -144,14 +150,14 @@ def _handle_tool_confirmation_event(self, event: RealtimeEvent) -> None: tool_name = content.get("tool_name", "unknown") tool_input = content.get("tool_input", {}) message = content.get("message", "") - + # Show the tool details self.console.print() self.console.print("šŸ”’ [yellow]Tool Confirmation Required[/yellow]") self.console.print(f" Tool: [bold]{tool_name}[/bold]") if message: self.console.print(f" Reason: {message}") - + # Show key parameters if tool_input: self.console.print(" Parameters:") @@ -159,65 +165,75 @@ def _handle_tool_confirmation_event(self, event: RealtimeEvent) -> None: if isinstance(value, str) and len(value) > 100: value = value[:100] + "..." self.console.print(f" {key}: {value}") - + self.console.print() - + # Check if arrow navigation is enabled in CLI config use_arrow_navigation = True # Default to enabled if self.settings and self.settings.cli_config: use_arrow_navigation = self.settings.cli_config.enable_arrow_navigation - + if use_arrow_navigation: # Use the new reliable select menu with arrow navigation try: - from ii_agent.cli.components.select_menu import create_tool_confirmation_menu - + from ii_agent.cli.components.select_menu import ( + create_tool_confirmation_menu, + ) + menu = create_tool_confirmation_menu(self.console) choice_index = menu.select() - + if choice_index is not None: choice = str(choice_index + 1) # Convert 0-based to 1-based else: - choice = '4' # Default to "no" if cancelled - + choice = "4" # Default to "no" if cancelled + except Exception as e: # Fallback to traditional input self.console.print(f"[dim]Arrow navigation unavailable: {e}[/dim]") use_arrow_navigation = False - + if not use_arrow_navigation: # Traditional numbered input self.console.print("Do you want to execute this tool?") self.console.print("[bold green]1.[/bold green] Yes") - self.console.print("[bold blue]2.[/bold blue] Yes, and don't ask again for this tool this session") - self.console.print("[bold cyan]3.[/bold cyan] Yes, approve for all tools in this session") - self.console.print("[bold red]4.[/bold red] No, and tell ii-agent what to do differently") + self.console.print( + "[bold blue]2.[/bold blue] Yes, and don't ask again for this tool this session" + ) + self.console.print( + "[bold cyan]3.[/bold cyan] Yes, approve for all tools in this session" + ) + self.console.print( + "[bold red]4.[/bold red] No, and tell ii-agent what to do differently" + ) self.console.print() choice = self._get_traditional_input() - + # Handle the choice approved = False alternative_instruction = "" - - if choice == '1': + + if choice == "1": self.console.print("āœ… [green]Tool execution approved[/green]") approved = True - - elif choice == '2': - self.console.print(f"āœ… [blue]Tool '{tool_name}' approved for this session[/blue]") + + elif choice == "2": + self.console.print( + f"āœ… [blue]Tool '{tool_name}' approved for this session[/blue]" + ) approved = True # Add tool to allow_tools set if self.config: self.config.allow_tools.add(tool_name) - - elif choice == '3': + + elif choice == "3": self.console.print("āœ… [cyan]All tools approved for this session[/cyan]") approved = True # Set auto_approve_tools to True if self.config: self.config.set_auto_approve_tools(True) - - elif choice == '4': + + elif choice == "4": self.console.print("āŒ [red]Tool execution denied[/red]") approved = False # Get alternative instruction @@ -230,61 +246,63 @@ def _handle_tool_confirmation_event(self, event: RealtimeEvent) -> None: self.console.print("šŸ“ No alternative instruction provided") except (KeyboardInterrupt, EOFError): self.console.print("šŸ“ No alternative instruction provided") - + # Send response back via callback if self.confirmation_callback: - self.confirmation_callback(tool_call_id, tool_name, approved, alternative_instruction) - + self.confirmation_callback( + tool_call_id, tool_name, approved, alternative_instruction + ) + self.console.print() - + def _get_single_key_choice(self) -> str: """Get user choice with single key press (enhanced UX).""" try: import sys import tty import termios - + fd = sys.stdin.fileno() old_settings = termios.tcgetattr(fd) - + try: tty.setraw(fd) while True: key = sys.stdin.read(1) - if key in ['1', '2', '3', '4']: + if key in ["1", "2", "3", "4"]: # Echo the choice print(f"\nSelected: {key}") return key - elif key == '\x03': # Ctrl+C + elif key == "\x03": # Ctrl+C print("\nCancelled") - return '4' - elif key == '\r' or key == '\n': # Enter without selection + return "4" + elif key == "\r" or key == "\n": # Enter without selection continue - + finally: termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) - + except Exception: # Fallback to traditional input return self._get_traditional_input() - + def _get_traditional_input(self) -> str: """Get user choice with traditional input method.""" while True: try: choice = input("Enter your choice (1-4): ").strip() - if choice in ['1', '2', '3', '4']: + if choice in ["1", "2", "3", "4"]: return choice else: self.console.print("[red]Please enter 1, 2, 3, or 4[/red]") except (KeyboardInterrupt, EOFError): - return '4' # Default to no on interrupt - + return "4" # Default to no on interrupt + def _handle_processing_event(self, event: RealtimeEvent) -> None: """Handle processing event.""" content = event.content message = content.get("message", "Processing...") - + if not self.minimal: # Use animated spinner for processing if self._spinner: @@ -293,27 +311,27 @@ def _handle_processing_event(self, event: RealtimeEvent) -> None: self._spinner.start() else: self.console.print(f"ā³ [cyan]{message}[/cyan]") - + def _print_status(self, message: str) -> None: """Print status message.""" self.console.print(message) - + def _print_response(self, text: str) -> None: """Print agent response with clean formatting.""" # Clear any status indicators immediately self._clear_thinking_indicator() - + if not self.minimal: # Use simple prefix instead of heavy frames from rich.text import Text - + # Add spacing before agent response self.console.print() - + # Create clean response with agent icon response_text = Text() response_text.append("šŸ¤– Agent: ", style="green") - + # Check if text contains code blocks if "```" in text: # For code blocks, just add the text and let Rich handle markdown @@ -325,17 +343,17 @@ def _print_response(self, text: str) -> None: else: self.console.print() self.console.print(f"šŸ¤– Agent: [white]{text}[/white]") - + def _print_tool_call(self, tool_name: str, tool_input: Dict[str, Any]) -> None: """Print clean tool call information.""" - + # Special handling for todo tools if tool_name in ["TodoRead", "TodoWrite"]: tool_text = Text() tool_text.append(" šŸ“‹ ", style="bright_blue") tool_text.append(f"Using {tool_name}", style="bright_blue bold") self.console.print(tool_text) - + # For TodoWrite, show task preview with checkboxes if tool_name == "TodoWrite" and "todos" in tool_input: todos = tool_input.get("todos", []) @@ -348,7 +366,7 @@ def _print_tool_call(self, tool_name: str, tool_input: Dict[str, Any]) -> None: task_text.append(" āŽæ ", style="dim blue") else: task_text.append(" ", style="dim") - + # Choose checkbox based on status status = todo.get("status", "pending") if status == "completed": @@ -357,51 +375,61 @@ def _print_tool_call(self, tool_name: str, tool_input: Dict[str, Any]) -> None: task_text.append("☐ ", style="cyan") else: # pending task_text.append("☐ ", style="dim white") - + # Add task content (truncate if too long) content = todo.get("content", "") if len(content) > 60: content = content[:57] + "..." task_text.append(content, style="white") - + self.console.print(task_text) - + # If there are more tasks, show count if len(todos) > max_preview: more_text = Text() more_text.append(" ", style="dim") - more_text.append(f"... and {len(todos) - max_preview} more tasks", style="dim cyan") + more_text.append( + f"... and {len(todos) - max_preview} more tasks", + style="dim cyan", + ) self.console.print(more_text) return - + # Enhanced tool call display for other tools tool_text = Text() tool_text.append(" šŸ”§ ", style="bright_blue") tool_text.append(f"Using {tool_name}", style="bright_blue bold") - + # Show formatted parameters if tool_input: self.console.print(tool_text) self._print_tool_params(tool_input) else: self.console.print(tool_text) - + def _print_tool_params(self, tool_input: Dict[str, Any]) -> None: """Print tool parameters with enhanced formatting.""" for key, value in tool_input.items(): param_text = Text() param_text.append(" ā”œā”€ ", style="dim blue") param_text.append(f"{key}: ", style="cyan") - + # Special handling for file paths - if key.endswith('_path') or key == 'file_path' or (isinstance(value, str) and ('/' in value or '\\' in value)): + if ( + key.endswith("_path") + or key == "file_path" + or (isinstance(value, str) and ("/" in value or "\\" in value)) + ): formatted_path = self._format_file_path(str(value)) param_text.append(formatted_path, style="yellow") # Special handling for long strings elif isinstance(value, str) and len(value) > 80: truncated = value[:80] + "..." # Check if it's likely code/content - if any(marker in value[:100] for marker in ['{', '[', '<', '\\n', 'def ', 'class ']): + if any( + marker in value[:100] + for marker in ["{", "[", "<", "\\n", "def ", "class "] + ): param_text.append(f'"{truncated}"', style="dim green") else: param_text.append(f'"{truncated}"', style="white") @@ -411,14 +439,15 @@ def _print_tool_params(self, tool_input: Dict[str, Any]) -> None: param_text.append(f'"{value}"', style="white") else: param_text.append(str(value), style="magenta") - + self.console.print(param_text) - + def _format_file_path(self, path: str) -> str: """Format file path for better readability.""" # Show relative path from working directory if possible try: import os + if os.path.isabs(path): # Try to make it relative to current working directory rel_path = os.path.relpath(path) @@ -426,57 +455,57 @@ def _format_file_path(self, path: str) -> str: return rel_path except (ValueError, OSError): pass - + # Truncate very long paths intelligently if len(path) > 60: - parts = path.split('/') + parts = path.split("/") if len(parts) > 3: return f"{parts[0]}/.../{'/'.join(parts[-2:])}" else: return f"{path[:30]}...{path[-30:]}" - + return path - + def _print_tool_result(self, tool_name: str, result: str) -> None: """Print tool result with enhanced formatting and visual hierarchy.""" - + if not result.strip(): return - + # Special handling for todo-related tools if tool_name.lower() in ["todoread", "todowrite", "todo_read", "todo_write"]: self._print_todo_result(tool_name, result) return - + # Show result header with success indicator result_header = Text() result_header.append(" āœ“ ", style="bright_green") result_header.append(f"{tool_name} completed", style="green") self.console.print(result_header) - + # Print visual connector connector = Text() connector.append(" │", style="dim green") self.console.print(connector) - + # Format the result content self._format_and_print_result(result) - + def _print_todo_result(self, tool_name: str, result: str) -> None: """Print todo tool result using the TodoPanel component.""" try: import json - + # Parse the result to get todo data todos = [] - + # Try to parse the result as JSON try: # The result might be a string that contains JSON if "todos" in result: # Extract JSON from the result string - start_idx = result.find('[') - end_idx = result.rfind(']') + 1 + start_idx = result.find("[") + end_idx = result.rfind("]") + 1 if start_idx != -1 and end_idx > start_idx: json_str = result[start_idx:end_idx] todos = json.loads(json_str) @@ -485,32 +514,32 @@ def _print_todo_result(self, tool_name: str, result: str) -> None: parsed = json.loads(result) if isinstance(parsed, list): todos = parsed - elif isinstance(parsed, dict) and 'todos' in parsed: - todos = parsed['todos'] + elif isinstance(parsed, dict) and "todos" in parsed: + todos = parsed["todos"] except json.JSONDecodeError: # If JSON parsing fails, show raw result self._format_and_print_result(result) return - + # Use TodoPanel to render the todos if todos: self._todo_panel.render(todos, title=f"šŸ“‹ {tool_name} Result") else: # Empty todos self._todo_panel.render([], title=f"šŸ“‹ {tool_name} Result") - + except Exception as e: # Fallback to regular formatting if something goes wrong self.console.print(f"[dim red]Error rendering todo panel: {e}[/dim red]") self._format_and_print_result(result) - + def _format_and_print_result(self, result: str) -> None: """Format and print tool result with appropriate styling.""" # Truncate very long results original_length = len(result) if original_length > 2000: result = result[:2000] + "\n... (truncated)" - + # Detect result type and format accordingly if self._is_file_operation_result(result): self._print_file_operation_result(result) @@ -520,100 +549,137 @@ def _format_and_print_result(self, result: str) -> None: self._print_structured_result(result) else: self._print_plain_result(result) - + # Show truncation notice if applicable if original_length > 2000: truncation_notice = Text() truncation_notice.append(" └─ ", style="dim green") - truncation_notice.append(f"({original_length - 2000} characters truncated)", style="dim yellow") + truncation_notice.append( + f"({original_length - 2000} characters truncated)", style="dim yellow" + ) self.console.print(truncation_notice) - + def _is_file_operation_result(self, result: str) -> bool: """Check if result is from a file operation.""" file_indicators = [ - "Modified file", "Created file", "Deleted file", "File not found", - "replacement(s)", "added", "removed", "changed" + "Modified file", + "Created file", + "Deleted file", + "File not found", + "replacement(s)", + "added", + "removed", + "changed", ] return any(indicator in result for indicator in file_indicators) - + def _is_code_content(self, result: str) -> bool: """Check if result contains code content.""" code_indicators = [ - "def ", "class ", "function ", "import ", "from ", "const ", "let ", "var ", - "#!/", " bool: """Check if result is structured data (JSON, XML, etc.).""" stripped = result.strip() - return (stripped.startswith('{') and stripped.endswith('}')) or \ - (stripped.startswith('[') and stripped.endswith(']')) or \ - stripped.startswith('<') and stripped.endswith('>') - + return ( + (stripped.startswith("{") and stripped.endswith("}")) + or (stripped.startswith("[") and stripped.endswith("]")) + or stripped.startswith("<") + and stripped.endswith(">") + ) + def _print_file_operation_result(self, result: str) -> None: """Print file operation result with special formatting.""" - lines = result.split('\n') + lines = result.split("\n") for line in lines: if line.strip(): formatted_line = Text() formatted_line.append(" └─ ", style="dim green") - + # Highlight file paths - if '/' in line or '\\' in line: + if "/" in line or "\\" in line: # Try to identify and highlight file paths words = line.split() for word in words: - if '/' in word or '\\' in word: + if "/" in word or "\\" in word: formatted_line.append(word + " ", style="yellow") - elif word.endswith('.py') or word.endswith('.js') or word.endswith('.html'): + elif ( + word.endswith(".py") + or word.endswith(".js") + or word.endswith(".html") + ): formatted_line.append(word + " ", style="yellow") else: formatted_line.append(word + " ", style="dim white") else: formatted_line.append(line, style="dim white") - + self.console.print(formatted_line) - + def _print_code_result(self, result: str) -> None: """Print code content with syntax highlighting.""" try: from rich.panel import Panel + language = self._detect_language(result) - syntax = Syntax(result, language, theme="monokai", line_numbers=False, indent_guides=True) - + syntax = Syntax( + result, + language, + theme="monokai", + line_numbers=False, + indent_guides=True, + ) + # Create a subtle panel for code code_panel = Panel( syntax, border_style="dim green", padding=(0, 1), title="Result", - title_align="left" + title_align="left", ) self.console.print(code_panel, style="dim") except Exception: # Fallback to indented text self._print_plain_result(result) - + def _print_structured_result(self, result: str) -> None: """Print structured data (JSON, XML) with formatting.""" try: import json + # Try to parse and pretty-print JSON - if result.strip().startswith(('{', '[')): + if result.strip().startswith(("{", "[")): parsed = json.loads(result) formatted = json.dumps(parsed, indent=2) self._print_code_result(formatted) return - except: + except Exception: pass - + # Fallback to regular formatting self._print_plain_result(result) - + def _print_plain_result(self, result: str) -> None: """Print plain text result with consistent indentation.""" - lines = result.split('\n') + lines = result.split("\n") for i, line in enumerate(lines): if line.strip(): # Skip empty lines formatted_line = Text() @@ -625,12 +691,11 @@ def _print_plain_result(self, result: str) -> None: self.console.print(formatted_line) elif i > 0 and i < len(lines) - 1: # Keep internal empty lines self.console.print(" ", style="dim") - - + def _print_error(self, message: str) -> None: """Print error message.""" self.console.print(f"[red]{message}[/red]") - + def _clear_thinking_indicator(self) -> None: """Clear the thinking indicator.""" if self._thinking_indicator: @@ -638,7 +703,7 @@ def _clear_thinking_indicator(self) -> None: self._spinner.stop() self._spinner = None self._thinking_indicator = False - + def print_welcome(self) -> None: """Print welcome message.""" if not self.minimal: @@ -651,47 +716,46 @@ def print_welcome(self) -> None: "• Type [bold]/compact[/bold] to truncate context\n" "• Type [bold]/settings[/bold] to configure LLM settings", title="Welcome", - style="cyan" + style="cyan", ) self.console.print(welcome_panel) - + def print_goodbye(self) -> None: """Print goodbye message.""" if not self.minimal: goodbye_panel = Panel( "šŸ‘‹ [bold green]Session ended. Goodbye![/bold green]", title="Farewell", - style="green" + style="green", ) self.console.print(goodbye_panel) - + def print_session_info(self, session_name: Optional[str] = None) -> None: """Print session information.""" if not self.minimal and session_name: session_panel = Panel( f"šŸ“ [bold]Active Session[/bold]\n\nName: [cyan]{session_name}[/cyan]", title="Session Info", - style="yellow" + style="yellow", ) self.console.print(session_panel) - + def print_config_info(self, config: LLMConfig) -> None: """Print configuration information.""" if not self.minimal: table = Table(title="šŸ”§ Agent Configuration", style="blue") table.add_column("Setting", style="cyan") table.add_column("Value", style="white") - + # Extract key config attributes config_items = [] - + # Common LLM config attributes config_items.append(("Model", config.model)) config_items.append(("Provider", config.api_type.value)) config_items.append(("Temperature", str(config.temperature))) config_items.append(("Max Tokens", str(config.max_message_chars))) - - + # Display formatted config items if config_items: for key, value in config_items: @@ -699,19 +763,19 @@ def print_config_info(self, config: LLMConfig) -> None: else: # Fallback to string representation if no known attributes table.add_row("Configuration", str(config)) - + self.console.print(table) - + def print_workspace_info(self, workspace_path: str) -> None: """Print workspace information.""" if not self.minimal: workspace_panel = Panel( f"šŸ“‚ [bold]Workspace[/bold]\n\nPath: [cyan]{workspace_path}[/cyan]", title="Workspace Info", - style="yellow" + style="yellow", ) self.console.print(workspace_panel) - + def _detect_language(self, code: str) -> str: """Detect programming language from code content.""" # Simple language detection based on content @@ -729,54 +793,137 @@ def _detect_language(self, code: str) -> str: return "json" else: return "text" - - def display_token_usage(self, token_count: int, cached_tokens: Optional[int] = None) -> None: + + def display_token_usage( + self, token_count: int, cached_tokens: Optional[int] = None + ) -> None: """Display token usage information.""" self._token_display.display_usage(token_count, cached_tokens) - + def render_conversation_history(self, history) -> None: """Render conversation history using the same formatting as real-time messages.""" - + if not history or len(history.message_lists) == 0: return - + self.console.print() self.console.print("šŸ“œ [bold cyan]Previous Conversation History:[/bold cyan]") self.console.print("─" * 80) - + # Display each turn in the conversation using existing formatting methods for turn in history.message_lists: for message in turn: self._render_message(message) - + self.console.print("─" * 80) self.console.print("šŸ“ [dim]Continuing from here...[/dim]") self.console.print() - + def _render_message(self, message) -> None: """Render a single message using the same formatting as real-time display.""" - - if hasattr(message, 'text'): + + if hasattr(message, "text"): # User message or text result - if hasattr(message, 'type') and message.type == 'text_prompt': + if hasattr(message, "type") and message.type == "text_prompt": # User input - use same formatting as _print_user_input user_text = Text() user_text.append("šŸ‘¤ You: ", style="bold blue") user_text.append(message.text, style="white") self.console.print(user_text) - elif hasattr(message, 'type') and message.type == 'text_result': + elif hasattr(message, "type") and message.type == "text_result": # Agent response - use same formatting as _print_response self._print_response(message.text) - elif hasattr(message, 'tool_name') and hasattr(message, 'tool_input'): + elif hasattr(message, "tool_name") and hasattr(message, "tool_input"): # Tool call - use same formatting as _print_tool_call self._print_tool_call(message.tool_name, message.tool_input) - elif hasattr(message, 'tool_output'): + elif hasattr(message, "tool_output"): # Tool result - use same formatting as _print_tool_result - self._print_tool_result(message.tool_name if hasattr(message, 'tool_name') else "Unknown", message.tool_output) - + self._print_tool_result( + message.tool_name if hasattr(message, "tool_name") else "Unknown", + message.tool_output, + ) + def cleanup(self) -> None: """Clean up resources.""" # Clean up spinner if active if self._spinner: self._spinner.stop() - self._spinner = None \ No newline at end of file + self._spinner = None + + async def setup_session_config(self) -> SessionConfig: + """Set up session configuration with interactive user selection.""" + # Create session config directory if it doesn't exist + sessions_dir = Path.home() / ".ii_agent" / "sessions" + sessions_dir.mkdir(parents=True, exist_ok=True) + + # Get available sessions + available_sessions = [] + for session_dir in sessions_dir.glob("*"): + if session_dir.is_dir(): + json_files = list(session_dir.glob("*.json")) + if json_files: + available_sessions.append(session_dir.name) + + available_sessions.sort() + + if available_sessions: + self.console.print("\n[cyan]Available sessions:[/cyan]") + for i, session in enumerate(available_sessions, 1): + self.console.print(f" {i}. {session}") + + self.console.print("\n[yellow]Options:[/yellow]") + self.console.print(" • Select a number to load an existing session") + self.console.print(" • Press Enter to create a new session") + self.console.print(" • Type a name to create/use a specific session") + + choice = Prompt.ask("\nYour choice", default="") + + if choice.isdigit() and 1 <= int(choice) <= len(available_sessions): + # Load existing session by number + session_name = available_sessions[int(choice) - 1] + session_id = str(uuid.uuid4()) # Generate new ID for continued session + self.console.print(f"[green]Loading session: {session_name}[/green]") + elif choice and choice not in available_sessions: + # Create new named session + session_name = choice + session_id = str(uuid.uuid4()) + self.console.print( + f"[green]Creating new session: {session_name}[/green]" + ) + elif choice and choice in available_sessions: + # Load existing named session + session_name = choice + session_id = str(uuid.uuid4()) # Generate new ID for continued session + self.console.print(f"[green]Loading session: {session_name}[/green]") + else: + # Create new session with generated name + session_name = f"session_{uuid.uuid4().hex[:8]}" + session_id = str(uuid.uuid4()) + self.console.print( + f"[green]Creating new session: {session_name}[/green]" + ) + else: + # No existing sessions, create new one + session_name = f"session_{uuid.uuid4().hex[:8]}" + session_id = str(uuid.uuid4()) + self.console.print(f"[green]Creating new session: {session_name}[/green]") + + session_config = SessionConfig(session_id=session_id, session_name=session_name) + + # Ensure sessions directory exists + session_config.ensure_sessions_dir() + + return session_config + + async def should_continue_from_state(self, session_id: str, root_dir: str) -> bool: + """Ask user if they want to continue from previous state in current directory.""" + # Check if there's a saved state in current directory + workspace_state_file = ( + Path(root_dir) / "sessions" / session_id / "agent_state.json" + ) + if workspace_state_file.exists(): + return Confirm.ask( + "\n[yellow]Found previous state in current directory. Continue from where you left off?[/yellow]", + default=True, + ) + return False diff --git a/src/ii_agent/core/config/ii_agent_config.py b/src/ii_agent/core/config/ii_agent_config.py index 36e6cf07..c885d74b 100644 --- a/src/ii_agent/core/config/ii_agent_config.py +++ b/src/ii_agent/core/config/ii_agent_config.py @@ -21,7 +21,9 @@ class IIAgentConfig(BaseSettings): file_store_path: The path to the file store. """ - model_config = SettingsConfigDict(env_file=".env", env_file_encoding="utf-8", extra="ignore") + model_config = SettingsConfigDict( + env_file=".env", env_file_encoding="utf-8", extra="ignore" + ) file_store: str = Field(default="local") file_store_path: str = Field(default="~/.ii_agent") use_container_workspace: bool = Field(default=False) @@ -33,16 +35,15 @@ class IIAgentConfig(BaseSettings): database_url: Optional[str] = None mcp_config: Optional[Dict[str, Any]] = None - # Per session config - # TODO: move to a separate class - session_id: str - auto_approve_tools: bool = False # Global tool approval setting. If True, all tools will be automatically approved. - allow_tools: set[str] = set() # Tools that are confirmed by the user + auto_approve_tools: bool = False # Global tool approval setting. If True, all tools will be automatically approved. + allow_tools: set[str] = set() # Tools that are confirmed by the user - @model_validator(mode='after') + @model_validator(mode="after") def set_database_url(self) -> "IIAgentConfig": if self.database_url is None: - self.database_url = f"sqlite:///{os.path.expanduser(self.file_store_path)}/ii_agent.db" + self.database_url = ( + f"sqlite:///{os.path.expanduser(self.file_store_path)}/ii_agent.db" + ) return self @@ -55,21 +56,22 @@ def workspace_root(self) -> str: @property def logs_path(self) -> str: return os.path.join(self.file_store_path, "logs") - - @field_validator('file_store_path') + + @field_validator("file_store_path") def expand_path(cls, v): - if v.startswith('~'): + if v.startswith("~"): return os.path.expanduser(v) return v def set_auto_approve_tools(self, value: bool) -> None: """Set the auto_approve_tools field value. - + Args: value: Whether to automatically approve tool executions in CLI mode """ self.auto_approve_tools = value + if __name__ == "__main__": - config = IIAgentConfig(session_id="test") - print(config.workspace_root) \ No newline at end of file + config = IIAgentConfig() + print(config.workspace_root) From 19f3b2f4831f263f20367b5a41a6261f65e9c6f2 Mon Sep 17 00:00:00 2001 From: Khoa Ngo The Date: Thu, 24 Jul 2025 03:09:13 +0700 Subject: [PATCH 08/13] feat: session reload interative --- src/ii_agent/cli/main.py | 2 +- .../cli/subscribers/console_subscriber.py | 342 +++++++++++++++++- 2 files changed, 323 insertions(+), 21 deletions(-) diff --git a/src/ii_agent/cli/main.py b/src/ii_agent/cli/main.py index 288ce75f..b636cb3d 100644 --- a/src/ii_agent/cli/main.py +++ b/src/ii_agent/cli/main.py @@ -38,7 +38,7 @@ def create_parser() -> argparse.ArgumentParser: subparsers = parser.add_subparsers(dest="command", help="Available commands") # Chat command (interactive mode) - # chat_parser = subparsers.add_parser("chat", help="Start interactive chat session") + chat_parser = subparsers.add_parser("chat", help="Start interactive chat session") # noqa: F841 # Config command config_parser = subparsers.add_parser("config", help="Manage configuration") diff --git a/src/ii_agent/cli/subscribers/console_subscriber.py b/src/ii_agent/cli/subscribers/console_subscriber.py index 2b7aabe8..231f0e14 100644 --- a/src/ii_agent/cli/subscribers/console_subscriber.py +++ b/src/ii_agent/cli/subscribers/console_subscriber.py @@ -7,6 +7,10 @@ from typing import Optional, Dict, Any, Callable from threading import Lock import uuid +import json +import sys +import termios +import tty from pathlib import Path from rich.console import Console @@ -850,6 +854,308 @@ def cleanup(self) -> None: self._spinner.stop() self._spinner = None + def _interactive_session_selector( + self, sessions: list + ) -> tuple[Optional[str], bool]: + """Interactive session selector with arrow keys and Ctrl+R expansion.""" + if not sessions: + return None, False + + selected_index = 0 + expanded_sessions = set() + + # Check if terminal supports raw mode + if not sys.stdin.isatty(): + # Fallback to simple selection for non-interactive terminals + self.console.print("\n[bold cyan]šŸ“‹ Available Sessions[/bold cyan]") + for i, session in enumerate(sessions, 1): + self.console.print(f" {i}. [bright_white]{session}[/bright_white]") + + self.console.print( + "\n[dim]Enter session number (1-{}) or name, or 'new' to create a new session[/dim]".format( + len(sessions) + ) + ) + choice = Prompt.ask("Selection", default="") + + if choice.lower() == "new": + new_session_name = Prompt.ask( + "Enter new session name", default=f"session_{uuid.uuid4().hex[:8]}" + ) + return new_session_name, False + elif choice.isdigit() and 1 <= int(choice) <= len(sessions): + return sessions[int(choice) - 1], False + elif choice in sessions: + return choice, False + elif choice: + # Create new session with the given name + return choice, False + else: + return None, False + + def get_session_content(session_name: str) -> str: + """Get session content from agent_state.json.""" + try: + sessions_dir = Path.home() / ".ii_agent" / "sessions" + state_file = sessions_dir / session_name / "agent_state.json" + if state_file.exists(): + with open(state_file, "r") as f: + data = json.load(f) + + # Extract meaningful information + info_lines = [] + + # User Message + if "last_user_prompt_index" in data: + last_user_prompt = data["message_lists"][ + int(data["last_user_prompt_index"]) + ][0]["text"] + if len(last_user_prompt) > 50: + last_user_prompt = "..." + last_user_prompt[-47:] + info_lines.append(f"User: {last_user_prompt}") + + # Message count + if "message_lists" in data and data["message_lists"]: + message_count = len(data["message_lists"]) + info_lines.append(f"šŸ’¬ Messages: {message_count}") + + # Get last few message summaries + recent_messages = [] + for msg_list in data["message_lists"][ + -2: + ]: # Last 2 message lists + messages = msg_list + for msg in messages[-3:]: # Last 3 messages from each list + if msg.get("text"): + content = msg["text"][:100] + if len(msg["text"]) > 100: + content += "..." + recent_messages.append(content) + + if recent_messages: + info_lines.append("šŸ“ Recent messages:") + info_lines.extend(recent_messages[-3:]) # Show only last 3 + + # File size info + file_size = state_file.stat().st_size + size_str = f"{file_size} bytes" + if file_size > 1024: + size_str = f"{file_size / 1024:.1f} KB" + if file_size > 1024 * 1024: + size_str = f"{file_size / (1024 * 1024):.1f} MB" + info_lines.append(f"šŸ“Š File size: {size_str}") + + return ( + "\n".join(info_lines) if info_lines else "No details available" + ) + else: + return "āŒ No state file found" + except Exception as e: + return f"āŒ Error reading state: {str(e)}" + + def render_session_list(): + """Generate session list content without clearing screen.""" + from rich.console import Group + from rich.text import Text + from rich.panel import Panel + from rich.table import Table + + content = [] + + # Create elegant header panel matching the theme + header_content = Text() + header_content.append("šŸ“‹ ", style="cyan") + header_content.append("Session Manager", style="bold cyan") + + header_panel = Panel( + header_content, + title="šŸš€ ii-agent", + title_align="left", + style="cyan", + border_style="bright_blue", + padding=(0, 1), + ) + content.append(header_panel) + content.append(Text()) # Spacer + + # Create instruction panel with consistent styling + instructions = Text() + instructions.append("Navigation: ", style="bold blue") + instructions.append("↑/↓ arrows ", style="bright_white") + instructions.append("• ", style="dim") + instructions.append("Enter ", style="green") + instructions.append("to select ", style="white") + instructions.append("• ", style="dim") + instructions.append("'n' ", style="yellow") + instructions.append("for new session ", style="white") + instructions.append("• ", style="dim") + instructions.append("Ctrl+C ", style="red") + instructions.append("to cancel", style="white") + + content.append(Panel(instructions, border_style="dim blue", padding=(0, 1))) + content.append(Text()) # Spacer + + # Session items with improved visual hierarchy + session_table = Table( + show_header=False, show_edge=False, pad_edge=False, box=None + ) + session_table.add_column("status", width=3, no_wrap=True) + session_table.add_column("name", min_width=20) + session_table.add_column("info", style="dim", no_wrap=True) + + for i, session in enumerate(sessions): + if i == selected_index: + # Selected session with enhanced styling + status_icon = Text("ā–¶", style="bold bright_green") + session_name = Text( + session, style="bold bright_green on bright_black" + ) + info_text = Text("← selected", style="dim green") + else: + # Non-selected sessions with subtle styling + status_icon = Text("•", style="dim blue") + session_name = Text(session, style="bright_white") + # Add session metadata as info + try: + sessions_dir = Path.home() / ".ii_agent" / "sessions" + state_file = sessions_dir / session / "agent_state.json" + if state_file.exists(): + from datetime import datetime + + mtime = datetime.fromtimestamp(state_file.stat().st_mtime) + info_text = Text( + mtime.strftime("%m/%d %H:%M"), style="dim cyan" + ) + else: + info_text = Text("no state", style="dim yellow") + except Exception: + info_text = Text("", style="dim") + + session_table.add_row(status_icon, session_name, info_text) + + # Show expanded content if this session is expanded + if session in expanded_sessions: + session_content = get_session_content(session) + detail_panel = Panel( + session_content, + title="šŸ“Š Session Details", + title_align="left", + subtitle="Ctrl+R to collapse", + subtitle_align="right", + border_style="dim green", + style="dim", + padding=(0, 1), + ) + content.append(detail_panel) + + # Add the session table + sessions_panel = Panel( + session_table, + title=f"šŸ“‚ Sessions ({len(sessions)} available)", + title_align="left", + border_style="dim cyan", + padding=(0, 1), + ) + content.append(sessions_panel) + + # Enhanced footer with current selection info + content.append(Text()) # Spacer + + current_session = sessions[selected_index] + footer_content = Text() + footer_content.append("šŸ“ Current selection: ", style="dim") + footer_content.append(current_session, style="bold cyan") + + if current_session in expanded_sessions: + footer_content.append(" ", style="dim") + footer_content.append("(details shown)", style="dim green") + footer_content.append(" • Press ", style="dim") + footer_content.append("Ctrl+R", style="dim yellow") + footer_content.append(" to collapse", style="dim") + else: + footer_content.append(" • Press ", style="dim") + footer_content.append("Ctrl+R", style="dim yellow") + footer_content.append(" for details", style="dim") + + footer_panel = Panel( + footer_content, border_style="dim", style="dim", padding=(0, 1) + ) + content.append(footer_panel) + + return Group(*content) + + # Print welcome message once at the start + self.console.clear() + self.print_welcome() + + # Create live display for session list + from rich.live import Live + + # Save original terminal settings + old_settings = termios.tcgetattr(sys.stdin) + + with Live( + render_session_list(), + console=self.console, + refresh_per_second=10, + screen=False, + ) as live: + + def update_display(): + live.update(render_session_list()) + + try: + # Set terminal to raw mode + tty.setcbreak(sys.stdin.fileno()) + + while True: + # Read a single character + char = sys.stdin.read(1) + + if char == "\x03": # Ctrl+C + return None, True + elif char == "\r" or char == "\n": # Enter + return sessions[selected_index], False + elif char == "n" or char == "N": # New session + # Restore terminal settings temporarily for input + termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_settings) + try: + live.stop() + self.console.clear() + self.console.print( + "[bold cyan]šŸ“ Create New Session[/bold cyan]" + ) + new_session_name = Prompt.ask( + "Enter session name", + default=f"session_{uuid.uuid4().hex[:8]}", + ) + return new_session_name, False + finally: + # Always restore raw mode + tty.setcbreak(sys.stdin.fileno()) + elif char == "\x12": # Ctrl+R + session_name = sessions[selected_index] + if session_name in expanded_sessions: + expanded_sessions.remove(session_name) + else: + expanded_sessions.add(session_name) + update_display() + elif char == "\x1b": # Escape sequence (arrow keys) + # Read the next two characters for arrow keys + next_chars = sys.stdin.read(2) + if next_chars == "[A": # Up arrow + selected_index = (selected_index - 1) % len(sessions) + update_display() + elif next_chars == "[B": # Down arrow + selected_index = (selected_index + 1) % len(sessions) + update_display() + + except KeyboardInterrupt: + return None, True + finally: + # Restore original terminal settings + termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_settings) + async def setup_session_config(self) -> SessionConfig: """Set up session configuration with interactive user selection.""" # Create session config directory if it doesn't exist @@ -867,34 +1173,30 @@ async def setup_session_config(self) -> SessionConfig: available_sessions.sort() if available_sessions: - self.console.print("\n[cyan]Available sessions:[/cyan]") - for i, session in enumerate(available_sessions, 1): - self.console.print(f" {i}. {session}") - - self.console.print("\n[yellow]Options:[/yellow]") - self.console.print(" • Select a number to load an existing session") - self.console.print(" • Press Enter to create a new session") - self.console.print(" • Type a name to create/use a specific session") - - choice = Prompt.ask("\nYour choice", default="") + # Use interactive selector with arrow keys and Ctrl+R expansion + selected_session, cancelled = self._interactive_session_selector( + available_sessions + ) - if choice.isdigit() and 1 <= int(choice) <= len(available_sessions): - # Load existing session by number - session_name = available_sessions[int(choice) - 1] + if cancelled: + # User cancelled, create new session + session_name = f"session_{uuid.uuid4().hex[:8]}" + session_id = str(uuid.uuid4()) + self.console.print( + f"[green]Creating new session: {session_name}[/green]" + ) + elif selected_session and selected_session in available_sessions: + # Load existing session + session_name = selected_session session_id = str(uuid.uuid4()) # Generate new ID for continued session self.console.print(f"[green]Loading session: {session_name}[/green]") - elif choice and choice not in available_sessions: + elif selected_session: # Create new named session - session_name = choice + session_name = selected_session session_id = str(uuid.uuid4()) self.console.print( f"[green]Creating new session: {session_name}[/green]" ) - elif choice and choice in available_sessions: - # Load existing named session - session_name = choice - session_id = str(uuid.uuid4()) # Generate new ID for continued session - self.console.print(f"[green]Loading session: {session_name}[/green]") else: # Create new session with generated name session_name = f"session_{uuid.uuid4().hex[:8]}" From fc7771584a2739e4612f0024c664b57d30cb6df1 Mon Sep 17 00:00:00 2001 From: Khoa Ngo The Date: Thu, 24 Jul 2025 03:39:55 +0700 Subject: [PATCH 09/13] feat: session reload interative --- .../cli/subscribers/console_subscriber.py | 256 ++++++++++++++---- 1 file changed, 198 insertions(+), 58 deletions(-) diff --git a/src/ii_agent/cli/subscribers/console_subscriber.py b/src/ii_agent/cli/subscribers/console_subscriber.py index 231f0e14..6f41bb63 100644 --- a/src/ii_agent/cli/subscribers/console_subscriber.py +++ b/src/ii_agent/cli/subscribers/console_subscriber.py @@ -11,6 +11,7 @@ import sys import termios import tty +import difflib from pathlib import Path from rich.console import Console @@ -857,12 +858,14 @@ def cleanup(self) -> None: def _interactive_session_selector( self, sessions: list ) -> tuple[Optional[str], bool]: - """Interactive session selector with arrow keys and Ctrl+R expansion.""" + """Interactive session selector with arrow keys - sessions always expand on select.""" if not sessions: return None, False selected_index = 0 - expanded_sessions = set() + search_mode = False + search_query = "" + filtered_sessions = sessions.copy() # Check if terminal supports raw mode if not sys.stdin.isatty(): @@ -893,6 +896,35 @@ def _interactive_session_selector( else: return None, False + def fuzzy_search_sessions(query: str, sessions_list: list) -> list: + """Perform fuzzy search on sessions list.""" + if not query.strip(): + return sessions_list + + # Use difflib for fuzzy matching + matches = difflib.get_close_matches( + query.lower(), + [s.lower() for s in sessions_list], + n=len(sessions_list), + cutoff=0.1, + ) + + # Return original sessions in order of matches + result = [] + for match in matches: + # Find original session name with correct case + for session in sessions_list: + if session.lower() == match: + result.append(session) + break + + # Add sessions that contain the query as substring (not already included) + for session in sessions_list: + if query.lower() in session.lower() and session not in result: + result.append(session) + + return result if result else sessions_list + def get_session_content(session_name: str) -> str: """Get session content from agent_state.json.""" try: @@ -964,33 +996,72 @@ def render_session_list(): # Create elegant header panel matching the theme header_content = Text() - header_content.append("šŸ“‹ ", style="cyan") - header_content.append("Session Manager", style="bold cyan") + if search_mode: + header_content.append("šŸ” ", style="yellow") + header_content.append("Search Sessions", style="bold yellow") + else: + header_content.append("šŸ“‹ ", style="cyan") + header_content.append("Session Manager", style="bold cyan") header_panel = Panel( header_content, title="šŸš€ ii-agent", title_align="left", - style="cyan", - border_style="bright_blue", + style="yellow" if search_mode else "cyan", + border_style="bright_yellow" if search_mode else "bright_blue", padding=(0, 1), ) content.append(header_panel) content.append(Text()) # Spacer + # Show search bar if in search mode + if search_mode: + search_content = Text() + search_content.append("Search: ", style="bold yellow") + search_content.append( + search_query, style="bright_white on bright_black" + ) + search_content.append("ā–ˆ", style="blink bright_white") # Cursor + + search_panel = Panel( + search_content, + title="šŸ” Type to search", + title_align="left", + border_style="yellow", + style="yellow", + padding=(0, 1), + ) + content.append(search_panel) + content.append(Text()) # Spacer + # Create instruction panel with consistent styling instructions = Text() - instructions.append("Navigation: ", style="bold blue") - instructions.append("↑/↓ arrows ", style="bright_white") - instructions.append("• ", style="dim") - instructions.append("Enter ", style="green") - instructions.append("to select ", style="white") - instructions.append("• ", style="dim") - instructions.append("'n' ", style="yellow") - instructions.append("for new session ", style="white") - instructions.append("• ", style="dim") - instructions.append("Ctrl+C ", style="red") - instructions.append("to cancel", style="white") + if search_mode: + instructions.append("Search mode: ", style="bold yellow") + instructions.append("Type to search ", style="bright_white") + instructions.append("• ", style="dim") + instructions.append("↑/↓ arrows ", style="bright_white") + instructions.append("• ", style="dim") + instructions.append("Enter ", style="green") + instructions.append("to select ", style="white") + instructions.append("• ", style="dim") + instructions.append("Esc ", style="red") + instructions.append("to exit search", style="white") + else: + instructions.append("Navigation: ", style="bold blue") + instructions.append("↑/↓ arrows ", style="bright_white") + instructions.append("• ", style="dim") + instructions.append("Enter ", style="green") + instructions.append("to select ", style="white") + instructions.append("• ", style="dim") + instructions.append("'s' ", style="magenta") + instructions.append("to search ", style="white") + instructions.append("• ", style="dim") + instructions.append("'n' ", style="yellow") + instructions.append("for new session ", style="white") + instructions.append("• ", style="dim") + instructions.append("Ctrl+C ", style="red") + instructions.append("to cancel", style="white") content.append(Panel(instructions, border_style="dim blue", padding=(0, 1))) content.append(Text()) # Spacer @@ -1003,7 +1074,7 @@ def render_session_list(): session_table.add_column("name", min_width=20) session_table.add_column("info", style="dim", no_wrap=True) - for i, session in enumerate(sessions): + for i, session in enumerate(filtered_sessions): if i == selected_index: # Selected session with enhanced styling status_icon = Text("ā–¶", style="bold bright_green") @@ -1015,6 +1086,24 @@ def render_session_list(): # Non-selected sessions with subtle styling status_icon = Text("•", style="dim blue") session_name = Text(session, style="bright_white") + # Add search query highlighting if in search mode + if search_mode and search_query.strip(): + # Highlight matching parts + highlighted_name = Text() + session_lower = session.lower() + query_lower = search_query.lower() + if query_lower in session_lower: + start = session_lower.find(query_lower) + end = start + len(query_lower) + highlighted_name.append( + session[:start], style="bright_white" + ) + highlighted_name.append( + session[start:end], style="bold yellow on bright_black" + ) + highlighted_name.append(session[end:], style="bright_white") + session_name = highlighted_name + # Add session metadata as info try: sessions_dir = Path.home() / ".ii_agent" / "sessions" @@ -1033,15 +1122,13 @@ def render_session_list(): session_table.add_row(status_icon, session_name, info_text) - # Show expanded content if this session is expanded - if session in expanded_sessions: + # Show expanded content for selected session + if i == selected_index: session_content = get_session_content(session) detail_panel = Panel( session_content, title="šŸ“Š Session Details", title_align="left", - subtitle="Ctrl+R to collapse", - subtitle_align="right", border_style="dim green", style="dim", padding=(0, 1), @@ -1049,9 +1136,14 @@ def render_session_list(): content.append(detail_panel) # Add the session table + if search_mode and search_query.strip(): + title = f"šŸ“‚ Search Results ({len(filtered_sessions)} of {len(sessions)} sessions)" + else: + title = f"šŸ“‚ Sessions ({len(filtered_sessions)} available)" + sessions_panel = Panel( session_table, - title=f"šŸ“‚ Sessions ({len(sessions)} available)", + title=title, title_align="left", border_style="dim cyan", padding=(0, 1), @@ -1061,26 +1153,32 @@ def render_session_list(): # Enhanced footer with current selection info content.append(Text()) # Spacer - current_session = sessions[selected_index] - footer_content = Text() - footer_content.append("šŸ“ Current selection: ", style="dim") - footer_content.append(current_session, style="bold cyan") - - if current_session in expanded_sessions: + if filtered_sessions: + current_session = filtered_sessions[selected_index] + footer_content = Text() + footer_content.append("šŸ“ Current selection: ", style="dim") + footer_content.append(current_session, style="bold cyan") footer_content.append(" ", style="dim") footer_content.append("(details shown)", style="dim green") - footer_content.append(" • Press ", style="dim") - footer_content.append("Ctrl+R", style="dim yellow") - footer_content.append(" to collapse", style="dim") - else: - footer_content.append(" • Press ", style="dim") - footer_content.append("Ctrl+R", style="dim yellow") - footer_content.append(" for details", style="dim") - footer_panel = Panel( - footer_content, border_style="dim", style="dim", padding=(0, 1) - ) - content.append(footer_panel) + footer_panel = Panel( + footer_content, border_style="dim", style="dim", padding=(0, 1) + ) + content.append(footer_panel) + else: + # No sessions match search + no_results_content = Text() + no_results_content.append("āŒ No sessions match '", style="dim red") + no_results_content.append(search_query, style="red") + no_results_content.append("'", style="dim red") + + footer_panel = Panel( + no_results_content, + border_style="dim red", + style="dim", + padding=(0, 1), + ) + content.append(footer_panel) return Group(*content) @@ -1115,7 +1213,65 @@ def update_display(): if char == "\x03": # Ctrl+C return None, True elif char == "\r" or char == "\n": # Enter - return sessions[selected_index], False + if filtered_sessions: + return filtered_sessions[selected_index], False + else: + continue # No sessions to select + elif char == "s" or char == "S": # Search mode + if not search_mode: + search_mode = True + search_query = "" + filtered_sessions = sessions.copy() + selected_index = 0 + update_display() + elif char == "\x1b": # Escape sequence (arrow keys or ESC) + # Read the next two characters + next_chars = sys.stdin.read(2) + if next_chars == "[A": # Up arrow + if filtered_sessions: + selected_index = (selected_index - 1) % len( + filtered_sessions + ) + update_display() + elif next_chars == "[B": # Down arrow + if filtered_sessions: + selected_index = (selected_index + 1) % len( + filtered_sessions + ) + update_display() + elif search_mode and not next_chars: # ESC key (exit search) + search_mode = False + search_query = "" + filtered_sessions = sessions.copy() + selected_index = 0 + update_display() + elif search_mode: + # Handle search input + if char == "\x7f": # Backspace + if search_query: + search_query = search_query[:-1] + filtered_sessions = fuzzy_search_sessions( + search_query, sessions + ) + if filtered_sessions: + selected_index = min( + selected_index, len(filtered_sessions) - 1 + ) + else: + selected_index = 0 + update_display() + elif char.isprintable(): + search_query += char + filtered_sessions = fuzzy_search_sessions( + search_query, sessions + ) + if filtered_sessions: + selected_index = min( + selected_index, len(filtered_sessions) - 1 + ) + else: + selected_index = 0 + update_display() elif char == "n" or char == "N": # New session # Restore terminal settings temporarily for input termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_settings) @@ -1133,22 +1289,6 @@ def update_display(): finally: # Always restore raw mode tty.setcbreak(sys.stdin.fileno()) - elif char == "\x12": # Ctrl+R - session_name = sessions[selected_index] - if session_name in expanded_sessions: - expanded_sessions.remove(session_name) - else: - expanded_sessions.add(session_name) - update_display() - elif char == "\x1b": # Escape sequence (arrow keys) - # Read the next two characters for arrow keys - next_chars = sys.stdin.read(2) - if next_chars == "[A": # Up arrow - selected_index = (selected_index - 1) % len(sessions) - update_display() - elif next_chars == "[B": # Down arrow - selected_index = (selected_index + 1) % len(sessions) - update_display() except KeyboardInterrupt: return None, True @@ -1173,7 +1313,7 @@ async def setup_session_config(self) -> SessionConfig: available_sessions.sort() if available_sessions: - # Use interactive selector with arrow keys and Ctrl+R expansion + # Use interactive selector with arrow keys selected_session, cancelled = self._interactive_session_selector( available_sessions ) From caaa46f3839c1f1ec744798db51af2dcfb0243e4 Mon Sep 17 00:00:00 2001 From: Khoa Ngo The Date: Thu, 24 Jul 2025 11:49:45 +0700 Subject: [PATCH 10/13] chore: revert ruff --- src/ii_agent/cli/app.py | 66 +-- src/ii_agent/cli/commands/settings_command.py | 58 ++- src/ii_agent/cli/config.py | 34 +- src/ii_agent/cli/main.py | 113 +++-- src/ii_agent/cli/settings_onboard.py | 355 +++++++-------- src/ii_agent/cli/state_persistence.py | 124 +++--- .../cli/subscribers/console_subscriber.py | 407 ++++++++---------- src/ii_agent/core/config/ii_agent_config.py | 21 +- src/ii_agent/core/config/utils.py | 2 +- src/ii_agent/core/storage/models/settings.py | 7 +- .../storage/settings/file_settings_store.py | 8 +- src/ii_agent/server/services/agent_service.py | 55 +-- src/ii_agent/tools/mcp_tool.py | 59 +-- src/ii_agent/tools/tool_manager.py | 74 ++-- src/ii_tool/mcp/server.py | 28 +- 15 files changed, 601 insertions(+), 810 deletions(-) diff --git a/src/ii_agent/cli/app.py b/src/ii_agent/cli/app.py index 8ad7bc37..f50d8a0a 100644 --- a/src/ii_agent/cli/app.py +++ b/src/ii_agent/cli/app.py @@ -55,18 +55,17 @@ def __init__( # Create state manager - we'll update it with continue_session later self.state_manager = None self.workspace_path = workspace_path - # Session config will be set up during run_interactive_mode # Create event stream self.event_stream = AsyncEventStream(logger=logger) - + # Create console subscriber with config and callback self.console_subscriber = ConsoleSubscriber( minimal=minimal, config=config, - confirmation_callback=self._handle_tool_confirmation, + confirmation_callback=self._handle_tool_confirmation ) self.session_config = None - + # Subscribe to events self.event_stream.subscribe(self.console_subscriber.handle_event) @@ -80,38 +79,26 @@ def __init__( # Agent controller will be created when needed self.agent_controller: Optional[AgentController] = None - + # Store for pending tool confirmations self._tool_confirmations: Dict[str, Dict[str, Any]] = {} - - def _handle_tool_confirmation( - self, - tool_call_id: str, - tool_name: str, - approved: bool, - alternative_instruction: str, - ) -> None: + + def _handle_tool_confirmation(self, tool_call_id: str, tool_name: str, approved: bool, alternative_instruction: str) -> None: """Handle tool confirmation response from console subscriber.""" # Store the confirmation response self._tool_confirmations[tool_call_id] = { "tool_name": tool_name, "approved": approved, - "alternative_instruction": alternative_instruction, + "alternative_instruction": alternative_instruction } - + # If there's an agent controller, send the confirmation response to it if self.agent_controller: - self.agent_controller.add_confirmation_response( - tool_call_id, approved, alternative_instruction - ) - logger.debug( - f"Tool confirmation sent to agent controller: {tool_call_id} -> approved={approved}" - ) + self.agent_controller.add_confirmation_response(tool_call_id, approved, alternative_instruction) + logger.debug(f"Tool confirmation sent to agent controller: {tool_call_id} -> approved={approved}") else: - logger.debug( - f"Tool confirmation received but no agent controller: {tool_call_id} -> approved={approved}" - ) - + logger.debug(f"Tool confirmation received but no agent controller: {tool_call_id} -> approved={approved}") + async def initialize_agent(self, continue_from_state: bool = False) -> None: """Initialize the agent controller.""" if self.agent_controller is not None: @@ -130,14 +117,13 @@ async def initialize_agent(self, continue_from_state: bool = False) -> None: settings_store = await FileSettingsStore.get_instance(self.config, None) settings = await settings_store.load() - + # Ensure CLI config exists with defaults if not settings.cli_config: from ii_agent.core.config.cli_config import CliConfig - settings.cli_config = CliConfig() await settings_store.store(settings) - + # Update console subscriber with settings self.console_subscriber.settings = settings @@ -172,7 +158,8 @@ async def initialize_agent(self, continue_from_state: bool = False) -> None: max_tokens_per_turn=self.config.max_output_tokens_per_turn, system_prompt=SYSTEM_PROMPT, ) - + + # Get system local tools tools = get_system_tools( client=llm_client, @@ -195,7 +182,7 @@ async def initialize_agent(self, continue_from_state: bool = False) -> None: ) await tool_manager.register_mcp_tools( mcp_client=mcp_client, - trust=True, # Trust the system MCP tools + trust=True, # Trust the system MCP tools ) if self.config.mcp_config: @@ -203,18 +190,11 @@ async def initialize_agent(self, continue_from_state: bool = False) -> None: await tool_manager.register_mcp_tools(self.config.mcp_config, trust=False) agent = FunctionCallAgent( - llm=llm_client, + llm=llm_client, config=agent_config, - tools=[ - ToolParam( - name=tool.name, - description=tool.description, - input_schema=tool.input_schema, - ) - for tool in tool_manager.get_tools() - ], + tools=[ToolParam(name=tool.name, description=tool.description, input_schema=tool.input_schema) for tool in tool_manager.get_tools()] ) - + # Create context manager token_counter = TokenCounter() context_manager = LLMCompact( @@ -321,9 +301,7 @@ async def run_interactive_mode(self) -> int: ) except KeyboardInterrupt: - self.console_subscriber.console.print( - "\nāš ļø [yellow]Interrupted by user[/yellow]" - ) + self.console_subscriber.console.print("\nāš ļø [yellow]Interrupted by user[/yellow]") if self.agent_controller is not None: self.agent_controller.cancel() continue @@ -416,4 +394,4 @@ def cleanup(self) -> None: # Clean up console subscriber resources if self.console_subscriber: - self.console_subscriber.cleanup() + self.console_subscriber.cleanup() \ No newline at end of file diff --git a/src/ii_agent/cli/commands/settings_command.py b/src/ii_agent/cli/commands/settings_command.py index d3287325..93bf7917 100644 --- a/src/ii_agent/cli/commands/settings_command.py +++ b/src/ii_agent/cli/commands/settings_command.py @@ -15,63 +15,57 @@ class SettingsCommand(BaseCommand): """Command to manage LLM and application settings.""" - + @property def name(self) -> str: return "settings" - + @property def description(self) -> str: return "Configure LLM settings and application preferences" - + async def execute(self, args: str, context: Dict[str, Any]) -> Optional[str]: """Execute the settings command.""" try: # Get the settings store from the context - config = context.get("config") + config = context.get('config') if not config: self.console.print("[red]Error: Configuration not available[/red]") return None - + # Create settings store - settings_store = await FileSettingsStore.get_instance( - config=config, user_id=None - ) - + settings_store = await FileSettingsStore.get_instance(config=config, user_id=None) + # Show current settings and allow modification - self.console.print( - Panel( - "āš™ļø [bold]Settings Configuration[/bold]\n\n" - "This will allow you to configure your LLM settings including:\n" - "• Provider selection (Anthropic, OpenAI, Gemini)\n" - "• Model selection\n" - "• API keys and authentication\n" - "• Vertex AI configuration (for Gemini)\n" - "• Temperature and other parameters", - title="Settings", - style="cyan", - ) - ) - + self.console.print(Panel( + "āš™ļø [bold]Settings Configuration[/bold]\n\n" + "This will allow you to configure your LLM settings including:\n" + "• Provider selection (Anthropic, OpenAI, Gemini)\n" + "• Model selection\n" + "• API keys and authentication\n" + "• Vertex AI configuration (for Gemini)\n" + "• Temperature and other parameters", + title="Settings", + style="cyan" + )) + # Run the settings modification flow await modify_settings(settings_store) - + self.console.print("\n[green]Settings configuration completed![/green]") - self.console.print( - "[dim]Note: LLM and Runtime changes take effect for new conversations.[/dim]" - ) - + self.console.print("[dim]Note: Changes will take effect for new conversations.[/dim]") + return None - + except Exception as e: self.console.print(f"[red]Error configuring settings: {e}[/red]") return None - + def validate_args(self, args: str) -> bool: """Validate command arguments.""" # Settings command doesn't require any arguments return True - + def get_help_text(self) -> str: """Get detailed help text for the settings command.""" return ( @@ -88,4 +82,4 @@ def get_help_text(self) -> str: "Examples:\n" " /settings - Open settings configuration\n\n" "Note: Settings are saved persistently and will be used for future sessions." - ) + ) \ No newline at end of file diff --git a/src/ii_agent/cli/config.py b/src/ii_agent/cli/config.py index 5d973136..1c43bf22 100644 --- a/src/ii_agent/cli/config.py +++ b/src/ii_agent/cli/config.py @@ -23,37 +23,33 @@ async def setup_cli_config( mcp_config: Optional[Dict[str, Any]] = None, ) -> tuple[IIAgentConfig, LLMConfig, str]: """Setup CLI configuration using the standard configuration pattern.""" - + # Create config with defaults config = IIAgentConfig(mcp_config=mcp_config) # Load settings from store settings_store = await FileSettingsStore.get_instance(config=config, user_id=None) settings = await settings_store.load() - + # If no settings exist, run first-time setup - if ( - not settings - or not settings.llm_configs - or not settings.llm_configs.get("default") - ): + if not settings or not settings.llm_configs or not settings.llm_configs.get('default'): print("No configuration found. Running first-time setup...") setup_success = await run_first_time_setup(settings_store) if not setup_success: raise RuntimeError("Failed to complete initial setup") - + # Reload settings after setup settings = await settings_store.load() - + # Create LLM config from settings llm_config = LLMConfig() - + if settings and settings.llm_configs: # Get the default LLM config if it exists - default_llm_config = settings.llm_configs.get("default") + default_llm_config = settings.llm_configs.get('default') if default_llm_config: llm_config = default_llm_config - + # Override with passed parameters (CLI arguments take precedence) if model: llm_config.model = model @@ -63,16 +59,16 @@ async def setup_cli_config( llm_config.base_url = base_url if temperature is not None: llm_config.temperature = temperature - + # Handle vertex configuration with fallback to environment variables # Note: vertex args are removed from CLI, so only check environment variables if not llm_config.vertex_region: - llm_config.vertex_region = os.environ.get("VERTEX_REGION") - + llm_config.vertex_region = os.environ.get('VERTEX_REGION') + if not llm_config.vertex_project_id: - llm_config.vertex_project_id = os.environ.get("VERTEX_PROJECT_ID") - + llm_config.vertex_project_id = os.environ.get('VERTEX_PROJECT_ID') + # Determine workspace path workspace_path = workspace or "." - - return config, llm_config, workspace_path + + return config, llm_config, workspace_path \ No newline at end of file diff --git a/src/ii_agent/cli/main.py b/src/ii_agent/cli/main.py index b636cb3d..79788a74 100644 --- a/src/ii_agent/cli/main.py +++ b/src/ii_agent/cli/main.py @@ -21,57 +21,97 @@ def create_parser() -> argparse.ArgumentParser: description="Intelligent Internet Agent - CLI interface for AI-powered automation", epilog="Use 'ii-agent --help' for command-specific help.", ) - + # Global options parser.add_argument( - "--workspace", - "-w", + "--workspace", + "-w", + type=str, + default=".", + help="Working directory for the agent (default: current directory)" + ) + parser.add_argument( + "--config", + "-c", + type=str, + help="Configuration file path" + ) + parser.add_argument( + "--mcp-config", type=str, - default=".", - help="Working directory for the agent (default: current directory)", + help="MCP config file path" ) - parser.add_argument("--config", "-c", type=str, help="Configuration file path") - parser.add_argument("--mcp-config", type=str, help="MCP config file path") - parser.add_argument("--minimal", "-m", action="store_true", help="Minimize output") - + parser.add_argument( + "--minimal", + "-m", + action="store_true", + help="Minimize output" + ) + # Subcommands subparsers = parser.add_subparsers(dest="command", help="Available commands") - + # Chat command (interactive mode) - chat_parser = subparsers.add_parser("chat", help="Start interactive chat session") # noqa: F841 - + chat_parser = subparsers.add_parser( + "chat", + help="Start interactive chat session" + ) # Config command - config_parser = subparsers.add_parser("config", help="Manage configuration") + config_parser = subparsers.add_parser( + "config", + help="Manage configuration" + ) config_group = config_parser.add_mutually_exclusive_group(required=True) config_group.add_argument( - "--show", action="store_true", help="Show current configuration" + "--show", + action="store_true", + help="Show current configuration" ) config_group.add_argument( - "--set", nargs=2, metavar=("KEY", "VALUE"), help="Set configuration value" + "--set", + nargs=2, + metavar=("KEY", "VALUE"), + help="Set configuration value" ) config_group.add_argument( - "--reset", action="store_true", help="Reset configuration to defaults" + "--reset", + action="store_true", + help="Reset configuration to defaults" ) - + # LLM configuration options parser.add_argument( - "--llm-provider", - choices=["anthropic", "openai", "gemini"], - help="LLM provider to use", + "--llm-provider", + choices=["anthropic", "openai", "gemini"], + help="LLM provider to use" ) - parser.add_argument("--llm-model", type=str, help="Specific model to use") - parser.add_argument("--max-tokens", type=int, help="Maximum tokens per turn") parser.add_argument( - "--temperature", type=float, help="Temperature for LLM responses" + "--llm-model", + type=str, + help="Specific model to use" ) - parser.add_argument("--tools", nargs="*", help="Specific tools to enable") - + parser.add_argument( + "--max-tokens", + type=int, + help="Maximum tokens per turn" + ) + parser.add_argument( + "--temperature", + type=float, + help="Temperature for LLM responses" + ) + parser.add_argument( + "--tools", + nargs="*", + help="Specific tools to enable" + ) + return parser def validate_args(args: argparse.Namespace) -> None: """Validate command-line arguments.""" - + if args.workspace: workspace_path = Path(args.workspace) if not workspace_path.exists(): @@ -80,7 +120,7 @@ def validate_args(args: argparse.Namespace) -> None: if not workspace_path.is_dir(): print(f"Error: Workspace path '{args.workspace}' is not a directory") sys.exit(1) - + if args.config: config_path = Path(args.config) if not config_path.exists(): @@ -92,17 +132,17 @@ async def main_async() -> int: """Main entry point for the CLI.""" parser = create_parser() args = parser.parse_args() - + # Show help if no command is provided if not args.command: parser.print_help() return 0 - + # Validate arguments validate_args(args) mcp_config = json.loads(open(args.mcp_config).read()) if args.mcp_config else None - + try: # Setup CLI configuration using the new pattern config, llm_config, workspace_path = await setup_cli_config( @@ -111,27 +151,26 @@ async def main_async() -> int: temperature=args.temperature, mcp_config=mcp_config, ) - + # Handle config command if args.command == "config": return await handle_config_command(args, config, llm_config) - + # Create and run CLI app app = CLIApp(config, llm_config, workspace_path, minimal=args.minimal) - + if args.command == "chat": return await app.run_interactive_mode() else: print(f"Unknown command: {args.command}") return 1 - + except KeyboardInterrupt: print("\nOperation interrupted by user") return 130 except Exception as e: if args.debug: import traceback - traceback.print_exc() else: print(f"Error: {e}") @@ -155,7 +194,7 @@ async def handle_config_command(args: argparse.Namespace, config, llm_config) -> elif args.reset: print("Reset configuration (feature not yet implemented)") return 0 - + return 1 @@ -170,4 +209,4 @@ def main(): if __name__ == "__main__": - main() + main() \ No newline at end of file diff --git a/src/ii_agent/cli/settings_onboard.py b/src/ii_agent/cli/settings_onboard.py index 6eb210a3..397f4999 100644 --- a/src/ii_agent/cli/settings_onboard.py +++ b/src/ii_agent/cli/settings_onboard.py @@ -26,7 +26,7 @@ VERIFIED_ANTHROPIC_MODELS = [ "claude-sonnet-4@20250514", "claude-opus-4@20250514", - "claude-3-7-sonnet@20250219", + "claude-3-7-sonnet@20250219" ] VERIFIED_OPENAI_MODELS = [ @@ -35,28 +35,27 @@ ] VERIFIED_GEMINI_MODELS = [ - "gemini-2.5-flashgemini-2.5-pro", + "gemini-2.5-flash", + "gemini-2.5-pro", ] VERIFIED_PROVIDERS = ["anthropic", "openai", "gemini"] - # Color constants for styling COLOR_GREY = "#888888" class UserCancelledError(Exception): """Raised when user cancels the setup process.""" - pass def cli_confirm(question: str, choices: list[str]) -> int: """Simple confirmation dialog.""" - print_formatted_text(HTML(f"{question}")) + print_formatted_text(HTML(f'{question}')) for i, choice in enumerate(choices): - print_formatted_text(HTML(f"{i + 1}. {choice}")) - + print_formatted_text(HTML(f'{i + 1}. {choice}')) + while True: try: response = input("Enter your choice (number): ").strip() @@ -64,11 +63,9 @@ def cli_confirm(question: str, choices: list[str]) -> int: if 0 <= choice_num < len(choices): return choice_num else: - print_formatted_text( - HTML("Invalid choice. Please try again.") - ) + print_formatted_text(HTML('Invalid choice. Please try again.')) except ValueError: - print_formatted_text(HTML("Please enter a valid number.")) + print_formatted_text(HTML('Please enter a valid number.')) except KeyboardInterrupt: raise UserCancelledError("User cancelled setup") @@ -76,32 +73,24 @@ def cli_confirm(question: str, choices: list[str]) -> int: def display_settings(settings: Settings) -> None: """Display current settings in a formatted way.""" if not settings.llm_configs: - print_formatted_text(HTML("No LLM configurations found.")) + print_formatted_text(HTML('No LLM configurations found.')) return - + # Display default LLM configuration - default_llm = settings.llm_configs.get("default") + default_llm = settings.llm_configs.get('default') if not default_llm: - print_formatted_text(HTML("No default LLM configuration found.")) + print_formatted_text(HTML('No default LLM configuration found.')) return - + # Prepare labels and values labels_and_values = [ - (" Provider", default_llm.api_type.value), - (" Model", default_llm.model), - ( - " API Key", - "Vertex AI Auth" - if default_llm.api_key - and default_llm.api_key.get_secret_value() == "vertex-ai-auth" - else "********" - if default_llm.api_key - else "Not Set", - ), - (" Base URL", default_llm.base_url or "Default"), - (" Temperature", str(default_llm.temperature)), + (' Provider', default_llm.api_type.value), + (' Model', default_llm.model), + (' API Key', 'Vertex AI Auth' if default_llm.api_key and default_llm.api_key.get_secret_value() == 'vertex-ai-auth' else '********' if default_llm.api_key else 'Not Set'), + (' Base URL', default_llm.base_url or 'Default'), + (' Temperature', str(default_llm.temperature)), ] - + # Add Vertex AI specific settings if applicable if default_llm.api_type in [APITypes.GEMINI, APITypes.ANTHROPIC]: labels_and_values.extend( @@ -128,14 +117,14 @@ def display_settings(settings: Settings) -> None: # Calculate max width for alignment max_label_width = max(len(label) for label, _ in labels_and_values) - + # Construct the summary text settings_lines = [ - f"{label + ':':<{max_label_width + 1}} {value}" + f'{label + ":":<{max_label_width + 1}} {value}' for label, value in labels_and_values ] - settings_text = "\n".join(settings_lines) - + settings_text = '\n'.join(settings_lines) + container = Frame( TextArea( text=settings_text, @@ -143,10 +132,10 @@ def display_settings(settings: Settings) -> None: style=COLOR_GREY, wrap_lines=True, ), - title="Current Settings", - style=f"fg:{COLOR_GREY}", + title='Current Settings', + style=f'fg:{COLOR_GREY}', ) - + print_container(container) @@ -155,265 +144,219 @@ async def get_validated_input( prompt_text: str, completer=None, validator=None, - error_message: str = "Input cannot be empty", + error_message: str = 'Input cannot be empty', is_password: bool = False, ) -> str: """Get validated input from user.""" session.completer = completer value = None - + while True: try: if is_password: value = await session.prompt_async(prompt_text, is_password=True) else: value = await session.prompt_async(prompt_text) - + if validator: is_valid = validator(value) if not is_valid: - print_formatted_text("") - print_formatted_text(HTML(f"{error_message}: {value}")) - print_formatted_text("") + print_formatted_text('') + print_formatted_text(HTML(f'{error_message}: {value}')) + print_formatted_text('') continue elif not value: - print_formatted_text("") - print_formatted_text(HTML(f"{error_message}")) - print_formatted_text("") + print_formatted_text('') + print_formatted_text(HTML(f'{error_message}')) + print_formatted_text('') continue - + break except KeyboardInterrupt: raise UserCancelledError("User cancelled setup") - + return value def save_settings_confirmation() -> bool: """Ask user if they want to save the settings.""" - return ( - cli_confirm( - "\nSave new settings? (They will take effect immediately)", - ["Yes, save", "No, discard"], - ) - == 0 - ) + return cli_confirm( + '\nSave new settings? (They will take effect immediately)', + ['Yes, save', 'No, discard'] + ) == 0 async def setup_llm_configuration(settings_store: FileSettingsStore) -> None: """Interactive setup for LLM configuration.""" session = PromptSession() - + try: # Step 1: Select provider - print_formatted_text(HTML("\nSetting up LLM Configuration")) - print_formatted_text(HTML("Choose your preferred LLM provider:\n")) - - provider_choice = cli_confirm("Select LLM Provider:", VERIFIED_PROVIDERS) - + print_formatted_text(HTML('\nSetting up LLM Configuration')) + print_formatted_text(HTML('Choose your preferred LLM provider:\n')) + + provider_choice = cli_confirm( + 'Select LLM Provider:', + VERIFIED_PROVIDERS + ) + provider = VERIFIED_PROVIDERS[provider_choice] api_type = APITypes(provider) - + # Step 2: Select model - if provider == "anthropic": + if provider == 'anthropic': available_models = VERIFIED_ANTHROPIC_MODELS - elif provider == "openai": + elif provider == 'openai': available_models = VERIFIED_OPENAI_MODELS - elif provider == "gemini": + elif provider == 'gemini': available_models = VERIFIED_GEMINI_MODELS else: available_models = [DEFAULT_MODEL] - - print_formatted_text( - HTML(f"\nDefault model: {available_models[0]}") - ) - - change_model = ( - cli_confirm( - "Do you want to use a different model?", - [f"Use {available_models[0]}", "Select another model"], - ) - == 1 - ) - + + print_formatted_text(HTML(f'\nDefault model: {available_models[0]}')) + + change_model = cli_confirm( + 'Do you want to use a different model?', + [f'Use {available_models[0]}', 'Select another model'] + ) == 1 + if change_model: model_completer = FuzzyWordCompleter(available_models) - + def model_validator(x): if not x.strip(): return False if x not in available_models: - print_formatted_text( - HTML( - f"Warning: {x} is not in the verified list for {provider}. " - f"Make sure this model name is correct." - ) - ) + print_formatted_text(HTML( + f'Warning: {x} is not in the verified list for {provider}. ' + f'Make sure this model name is correct.' + )) return True - + model = await get_validated_input( session, - "Enter model name (TAB for options, CTRL-C to cancel): ", + 'Enter model name (TAB for options, CTRL-C to cancel): ', completer=model_completer, validator=model_validator, - error_message="Model name cannot be empty", + error_message='Model name cannot be empty' ) else: model = available_models[0] - + # Step 3: API Key (with Vertex AI support) api_key = None - + # For Vertex AI providers, check for existing authentication - if provider in ["anthropic", "gemini"]: - google_app_creds = os.getenv("GOOGLE_APPLICATION_CREDENTIALS") + if provider in ['anthropic', 'gemini']: + google_app_creds = os.getenv('GOOGLE_APPLICATION_CREDENTIALS') if google_app_creds: - print_formatted_text( - HTML( - f"Found GOOGLE_APPLICATION_CREDENTIALS: {google_app_creds}" - ) - ) - use_existing_creds = ( - cli_confirm( - "Use existing Google Application Credentials?", - ["Yes, use existing credentials", "No, enter API key manually"], - ) - == 0 - ) - + print_formatted_text(HTML(f'Found GOOGLE_APPLICATION_CREDENTIALS: {google_app_creds}')) + use_existing_creds = cli_confirm( + 'Use existing Google Application Credentials?', + ['Yes, use existing credentials', 'No, enter API key manually'] + ) == 0 + if use_existing_creds: - api_key = "vertex-ai-auth" # Placeholder for Vertex AI auth + api_key = 'vertex-ai-auth' # Placeholder for Vertex AI auth else: api_key = await get_validated_input( session, - "Enter API Key (CTRL-C to cancel): ", - error_message="API Key cannot be empty", - is_password=True, + 'Enter API Key (CTRL-C to cancel): ', + error_message='API Key cannot be empty', + is_password=True ) else: - print_formatted_text( - HTML("No GOOGLE_APPLICATION_CREDENTIALS found.") - ) + print_formatted_text(HTML('No GOOGLE_APPLICATION_CREDENTIALS found.')) auth_choice = cli_confirm( - "How would you like to authenticate?", - ["Set up Google Application Credentials", "Enter API key manually"], + 'How would you like to authenticate?', + ['Set up Google Application Credentials', 'Enter API key manually'] ) - + if auth_choice == 0: - print_formatted_text( - HTML( - "Please set up Google Application Credentials:" - ) - ) - print_formatted_text( - HTML( - "1. Create a service account in Google Cloud Console" - ) - ) - print_formatted_text( - HTML("2. Download the JSON key file") - ) - print_formatted_text( - HTML( - "3. Set GOOGLE_APPLICATION_CREDENTIALS environment variable" - ) - ) - + print_formatted_text(HTML('Please set up Google Application Credentials:')) + print_formatted_text(HTML('1. Create a service account in Google Cloud Console')) + print_formatted_text(HTML('2. Download the JSON key file')) + print_formatted_text(HTML('3. Set GOOGLE_APPLICATION_CREDENTIALS environment variable')) + creds_path = await get_validated_input( session, - "Enter path to service account JSON file: ", - error_message="Path cannot be empty", + 'Enter path to service account JSON file: ', + error_message='Path cannot be empty' ) - + if os.path.exists(creds_path): - os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = creds_path - print_formatted_text( - HTML( - f"āœ“ Set GOOGLE_APPLICATION_CREDENTIALS to {creds_path}" - ) - ) - api_key = "vertex-ai-auth" + os.environ['GOOGLE_APPLICATION_CREDENTIALS'] = creds_path + print_formatted_text(HTML(f'āœ“ Set GOOGLE_APPLICATION_CREDENTIALS to {creds_path}')) + api_key = 'vertex-ai-auth' else: - print_formatted_text( - HTML(f"File not found: {creds_path}") - ) + print_formatted_text(HTML(f'File not found: {creds_path}')) api_key = await get_validated_input( session, - "Enter API Key (CTRL-C to cancel): ", - error_message="API Key cannot be empty", - is_password=True, + 'Enter API Key (CTRL-C to cancel): ', + error_message='API Key cannot be empty', + is_password=True ) else: api_key = await get_validated_input( session, - "Enter API Key (CTRL-C to cancel): ", - error_message="API Key cannot be empty", - is_password=True, + 'Enter API Key (CTRL-C to cancel): ', + error_message='API Key cannot be empty', + is_password=True ) else: # For non-Vertex AI providers (OpenAI, etc.) api_key = await get_validated_input( session, - "Enter API Key (CTRL-C to cancel): ", - error_message="API Key cannot be empty", - is_password=True, + 'Enter API Key (CTRL-C to cancel): ', + error_message='API Key cannot be empty', + is_password=True ) - + # Step 4: Optional base URL (only for OpenAI) base_url = None - if provider == "openai": - if cli_confirm("Do you want to set a custom base URL?", ["No", "Yes"]) == 1: + if provider == 'openai': + if cli_confirm('Do you want to set a custom base URL?', ['No', 'Yes']) == 1: base_url = await get_validated_input( session, - "Enter base URL (CTRL-C to cancel): ", - error_message="Base URL cannot be empty", + 'Enter base URL (CTRL-C to cancel): ', + error_message='Base URL cannot be empty' ) - + # Step 5: Vertex AI specific settings (for Gemini and Anthropic) vertex_region = None vertex_project_id = None - - if provider in ["gemini", "anthropic"]: - provider_name = "Gemini" if provider == "gemini" else "Anthropic" - print_formatted_text( - HTML( - f"\nVertex AI Configuration (required for {provider_name}):" - ) - ) - + + if provider in ['gemini', 'anthropic']: + provider_name = 'Gemini' if provider == 'gemini' else 'Anthropic' + print_formatted_text(HTML(f'\nVertex AI Configuration (required for {provider_name}):')) + vertex_region = await get_validated_input( session, - "Enter Vertex AI region (e.g., us-east5): ", - error_message="Vertex region cannot be empty", + 'Enter Vertex AI region (e.g., us-east5): ', + error_message='Vertex region cannot be empty' ) - + vertex_project_id = await get_validated_input( session, - "Enter Vertex AI project ID: ", - error_message="Vertex project ID cannot be empty", + 'Enter Vertex AI project ID: ', + error_message='Vertex project ID cannot be empty' ) - + # Step 6: Temperature setting temperature = 0.0 - if ( - cli_confirm( - "Do you want to set a custom temperature?", ["No (use 0.0)", "Yes"] - ) - == 1 - ): + if cli_confirm('Do you want to set a custom temperature?', ['No (use 0.0)', 'Yes']) == 1: temp_input = await get_validated_input( session, - "Enter temperature (0.0-1.0): ", - validator=lambda x: x.replace(".", "").isdigit() - and 0.0 <= float(x) <= 1.0, - error_message="Temperature must be a number between 0.0 and 1.0", + 'Enter temperature (0.0-1.0): ', + validator=lambda x: x.replace('.', '').isdigit() and 0.0 <= float(x) <= 1.0, + error_message='Temperature must be a number between 0.0 and 1.0' ) temperature = float(temp_input) - + # Confirm save if not save_settings_confirmation(): return - + # Create and save configuration llm_config = LLMConfig( model=model, @@ -422,17 +365,17 @@ def model_validator(x): temperature=temperature, vertex_region=vertex_region, vertex_project_id=vertex_project_id, - api_type=api_type, + api_type=api_type ) - + # Load existing settings or create new settings = await settings_store.load() if not settings: settings = Settings() - + # Set as default LLM config - settings.llm_configs["default"] = llm_config - + settings.llm_configs['default'] = llm_config + # Save settings await settings_store.store(settings) @@ -524,11 +467,9 @@ async def setup_runtime_configuration(settings_store: FileSettingsStore) -> None async def run_first_time_setup(settings_store: FileSettingsStore) -> bool: """Run the first-time setup flow.""" - print_formatted_text(HTML("\nWelcome to ii-agent!")) - print_formatted_text( - HTML("No settings found. Let's set up your configuration.\n") - ) - + print_formatted_text(HTML('\nWelcome to ii-agent!')) + print_formatted_text(HTML('No settings found. Let\'s set up your LLM configuration.\n')) + try: # Step 1: Setup LLM configuration await setup_llm_configuration(settings_store) @@ -538,28 +479,24 @@ async def run_first_time_setup(settings_store: FileSettingsStore) -> bool: return True except Exception as e: - print_formatted_text(HTML(f"\nSetup failed: {e}")) + print_formatted_text(HTML(f'\nSetup failed: {e}')) return False async def modify_settings(settings_store: FileSettingsStore) -> None: """Modify existing settings.""" settings = await settings_store.load() - + if settings: display_settings(settings) - + modify_choice = cli_confirm( "\nWhat would you like to modify?", ["LLM Configuration", "Runtime Configuration", "Go back"], ) - + if modify_choice == 0: await setup_llm_configuration(settings_store) - elif modify_choice == 1: - await setup_runtime_configuration(settings_store) else: - print_formatted_text( - HTML("No settings found. Running first-time setup...") - ) - await run_first_time_setup(settings_store) + print_formatted_text(HTML('No settings found. Running first-time setup...')) + await run_first_time_setup(settings_store) \ No newline at end of file diff --git a/src/ii_agent/cli/state_persistence.py b/src/ii_agent/cli/state_persistence.py index 3d4918b6..cfe58dc5 100644 --- a/src/ii_agent/cli/state_persistence.py +++ b/src/ii_agent/cli/state_persistence.py @@ -29,15 +29,15 @@ def __init__( self.session_name = session_name self.ii_agent_dir = workspace_path / "sessions" / session_name self.ii_agent_dir.mkdir(exist_ok=True) - self.agent_state_link = self.ii_agent_dir / "agent_state.json" + self.current_state_link = self.ii_agent_dir / "agent_state.json" self.metadata_link = self.ii_agent_dir / "metadata.json" # Use the global file store location self.file_store = LocalFileStore("~/.ii_agent") - + # Determine session ID based on whether we're continuing or starting fresh if ( continue_session - and self.agent_state_link.exists() + and self.current_state_link.exists() and self.metadata_link.exists() ): try: @@ -49,9 +49,7 @@ def __init__( logger.info(f"Continuing with existing session: {self.session_id}") else: self.session_id = uuid.uuid4().hex - logger.info( - f"No valid session found, creating new session: {self.session_id}" - ) + logger.info(f"No valid session found, creating new session: {self.session_id}") except Exception as e: logger.warning(f"Error reading existing session, creating new one: {e}") self.session_id = uuid.uuid4().hex @@ -59,20 +57,20 @@ def __init__( self.session_id = uuid.uuid4().hex if not continue_session: logger.info(f"Starting new session: {self.session_id}") - + def save_state( - self, + self, agent_state: State, config: IIAgentConfig, llm_config: LLMConfig, workspace_path: str, - session_name: Optional[str] = None, + session_name: Optional[str] = None ) -> None: """Save state and metadata using the new JSON format.""" try: # Save core state using State's save_to_session method agent_state.save_to_session(self.session_id, self.file_store) - + # Save metadata separately metadata = { "version": "2.0", @@ -80,50 +78,48 @@ def save_state( "session_id": self.session_id, "workspace_path": str(workspace_path), "session_name": session_name, - "agent_state": {"message_count": len(agent_state.message_lists)}, + "agent_state": { + "message_count": len(agent_state.message_lists) + }, "config": { "max_output_tokens_per_turn": config.max_output_tokens_per_turn, "max_turns": config.max_turns, - "debug": getattr(config, "debug", False), + "debug": getattr(config, 'debug', False) }, "llm_config": { "model": llm_config.model, "temperature": llm_config.temperature, "max_message_chars": llm_config.max_message_chars, - "api_type": llm_config.api_type.value - if llm_config.api_type - else "anthropic", - "max_retries": llm_config.max_retries, - }, + "api_type": llm_config.api_type.value if llm_config.api_type else 'anthropic', + "max_retries": llm_config.max_retries + } } - + metadata_filename = get_conversation_metadata_filename(self.session_id) - self.file_store.write( - metadata_filename, json.dumps(metadata, indent=2, ensure_ascii=False) - ) - + self.file_store.write(metadata_filename, json.dumps(metadata, indent=2, ensure_ascii=False)) + # Update current state pointer current_state_info = { "current_session_id": self.session_id, "workspace_path": self.workspace_path, - "last_updated": datetime.now().isoformat(), + "last_updated": datetime.now().isoformat() } - with open(self.agent_state_link, "w") as f: + with open(self.current_state_link, "w") as f: json.dump(current_state_info, f, indent=2) - + logger.info(f"State saved for session {self.session_id}") - + except Exception as e: logger.error(f"Error saving state: {e}") raise - + def load_state(self) -> Optional[Dict[str, Any]]: """Load the saved agent state from current state pointer.""" try: # First, check if current_state.json exists - if not self.agent_state_link.exists(): + if not self.current_state_link.exists(): return None - + # Read the current state pointer with open(self.metadata_link, "r") as f: metadata = json.load(f) @@ -132,16 +128,16 @@ def load_state(self) -> Optional[Dict[str, Any]]: if not session_id: return None - + # Create a new State object and restore from session state = State() state.restore_from_session(session_id, self.file_store) - + # Load metadata metadata_filename = get_conversation_metadata_filename(session_id) metadata_json = self.file_store.read(metadata_filename) metadata = json.loads(metadata_json) - + # Combine state and metadata into return format return { "version": metadata.get("version", "2.0"), @@ -151,64 +147,64 @@ def load_state(self) -> Optional[Dict[str, Any]]: "session_id": metadata.get("session_id"), "agent_state": { "message_lists": state.message_lists, - "last_user_prompt_index": state.last_user_prompt_index, + "last_user_prompt_index": state.last_user_prompt_index }, "config": metadata.get("config", {}), - "llm_config": metadata.get("llm_config", {}), + "llm_config": metadata.get("llm_config", {}) } - + except Exception as e: logger.error(f"Error loading state: {e}") return None - + + def clear_state(self) -> None: """Remove the saved state files.""" try: # Remove current state pointer - if self.agent_state_link.exists(): - self.agent_state_link.unlink() - logger.info(f"Current state pointer {self.agent_state_link} removed") + if self.current_state_link.exists(): + self.current_state_link.unlink() + logger.info(f"Current state pointer {self.current_state_link} removed") except Exception as e: logger.error(f"Error clearing state: {e}") - + def has_saved_state(self) -> bool: """Check if there's a saved state file.""" - return self.agent_state_link.exists() + return self.current_state_link.exists() def get_state_info(self) -> Optional[Dict[str, Any]]: """Get basic info about the saved state without loading it.""" try: - if not self.agent_state_link.exists(): + if not self.current_state_link.exists(): return None - + # Read current state pointer - with open(self.agent_state_link, "r") as f: + with open(self.current_state_link, "r") as f: current_state_info = json.load(f) - + session_id = current_state_info.get("current_session_id") - + workspace_path = current_state_info.get("workspace_path") + if not session_id: return None - + # Read metadata file metadata_filename = get_conversation_metadata_filename(session_id) try: metadata_json = self.file_store.read(metadata_filename) metadata = json.loads(metadata_json) - + return { "timestamp": metadata.get("timestamp"), "session_name": metadata.get("session_name"), "workspace_path": metadata.get("workspace_path"), "version": metadata.get("version", "2.0"), "session_id": metadata.get("session_id"), - "message_count": metadata.get("agent_state", {}).get( - "message_count", 0 - ), + "message_count": metadata.get("agent_state", {}).get("message_count", 0) } except FileNotFoundError: return None - + except Exception as e: logger.error(f"Error getting state info: {e}") return None @@ -217,32 +213,28 @@ def get_state_info(self) -> Optional[Dict[str, Any]]: def restore_agent_state(state_data: Dict[str, Any]) -> State: """Restore a State object from saved state data.""" agent_state = State() - + if "agent_state" in state_data: saved_agent_state = state_data["agent_state"] - + # Restore message lists if "message_lists" in saved_agent_state: agent_state.message_lists = saved_agent_state["message_lists"] - + # Restore last user prompt index if "last_user_prompt_index" in saved_agent_state: - agent_state.last_user_prompt_index = saved_agent_state[ - "last_user_prompt_index" - ] - + agent_state.last_user_prompt_index = saved_agent_state["last_user_prompt_index"] + return agent_state -def restore_configs( - state_data: Dict[str, Any], -) -> tuple[Dict[str, Any], Dict[str, Any]]: +def restore_configs(state_data: Dict[str, Any]) -> tuple[Dict[str, Any], Dict[str, Any]]: """Extract configuration data from saved state.""" from ii_agent.core.config.llm_config import APITypes - + config_data = state_data.get("config", {}) llm_config_data = state_data.get("llm_config", {}) - + # Convert api_type string back to enum if present if "api_type" in llm_config_data and isinstance(llm_config_data["api_type"], str): try: @@ -250,5 +242,5 @@ def restore_configs( except ValueError: # If invalid enum value, default to anthropic llm_config_data["api_type"] = APITypes.ANTHROPIC - - return config_data, llm_config_data + + return config_data, llm_config_data \ No newline at end of file diff --git a/src/ii_agent/cli/subscribers/console_subscriber.py b/src/ii_agent/cli/subscribers/console_subscriber.py index 6f41bb63..06ef742d 100644 --- a/src/ii_agent/cli/subscribers/console_subscriber.py +++ b/src/ii_agent/cli/subscribers/console_subscriber.py @@ -34,13 +34,13 @@ class ConsoleSubscriber: """Subscriber that handles console output for agent events.""" - + def __init__( - self, - minimal: bool = False, + self, + minimal: bool = False, config: Optional[IIAgentConfig] = None, settings: Optional[Settings] = None, - confirmation_callback: Optional[Callable[[str, str, bool, str], None]] = None, + confirmation_callback: Optional[Callable[[str, str, bool, str], None]] = None ): self.minimal = minimal self.config = config @@ -54,10 +54,10 @@ def __init__( self._spinner: Optional[AnimatedSpinner] = None self._token_display = TokenUsageDisplay(self.console) self._todo_panel = TodoPanel(self.console) - + def handle_event(self, event: RealtimeEvent) -> None: """Handle an event by outputting to console.""" - with self._lock: + with self._lock: if event.type == EventType.AGENT_THINKING: self._handle_thinking_event(event) elif event.type == EventType.TOOL_CALL: @@ -74,7 +74,7 @@ def handle_event(self, event: RealtimeEvent) -> None: self._handle_processing_event(event) elif event.type == EventType.TOOL_CONFIRMATION: self._handle_tool_confirmation_event(event) - + def _handle_thinking_event(self, event: RealtimeEvent) -> None: """Handle agent thinking event.""" if not self._thinking_indicator: @@ -85,69 +85,67 @@ def _handle_thinking_event(self, event: RealtimeEvent) -> None: else: self.console.print("šŸ¤” [cyan]Agent is thinking...[/cyan]") self._thinking_indicator = True - + def _handle_tool_call_event(self, event: RealtimeEvent) -> None: """Handle tool call event.""" self._clear_thinking_indicator() - + content = event.content tool_name = content.get("tool_name", "unknown") tool_input = content.get("tool_input", {}) - + self._current_tool_call = content - + if not self.minimal: self._print_tool_call(tool_name, tool_input) else: self.console.print(f"šŸ”§ [blue]Using tool:[/blue] [bold]{tool_name}[/bold]") - + def _handle_tool_result_event(self, event: RealtimeEvent) -> None: """Handle tool result event.""" content = event.content tool_name = content.get("tool_name", "unknown") result = content.get("result", "") - + if not self.minimal: self._print_tool_result(tool_name, result) else: - self.console.print( - f"āœ… [green]Tool completed:[/green] [bold]{tool_name}[/bold]" - ) - + self.console.print(f"āœ… [green]Tool completed:[/green] [bold]{tool_name}[/bold]") + self._current_tool_call = None - + def _handle_agent_response_event(self, event: RealtimeEvent) -> None: """Handle agent response event.""" self._clear_thinking_indicator() - + content = event.content text = content.get("text", "") - + if text.strip(): self._print_response(text) - + def _handle_interrupted_event(self, event: RealtimeEvent) -> None: """Handle interrupted event.""" self._clear_thinking_indicator() - + content = event.content text = content.get("text", "") - + self.console.print(f"āš ļø [yellow]Interrupted:[/yellow] {text}") - + def _handle_error_event(self, event: RealtimeEvent) -> None: """Handle error event.""" self._clear_thinking_indicator() - + content = event.content error_msg = content.get("error", "Unknown error") - + # Simple error format with clean prefix error_text = Text() error_text.append(" āŽæ ", style="red") error_text.append(f"Error: {error_msg}", style="red") self.console.print(error_text) - + def _handle_tool_confirmation_event(self, event: RealtimeEvent) -> None: """Handle tool confirmation event with interactive prompt.""" content = event.content @@ -155,14 +153,14 @@ def _handle_tool_confirmation_event(self, event: RealtimeEvent) -> None: tool_name = content.get("tool_name", "unknown") tool_input = content.get("tool_input", {}) message = content.get("message", "") - + # Show the tool details self.console.print() self.console.print("šŸ”’ [yellow]Tool Confirmation Required[/yellow]") self.console.print(f" Tool: [bold]{tool_name}[/bold]") if message: self.console.print(f" Reason: {message}") - + # Show key parameters if tool_input: self.console.print(" Parameters:") @@ -170,75 +168,65 @@ def _handle_tool_confirmation_event(self, event: RealtimeEvent) -> None: if isinstance(value, str) and len(value) > 100: value = value[:100] + "..." self.console.print(f" {key}: {value}") - + self.console.print() - + # Check if arrow navigation is enabled in CLI config use_arrow_navigation = True # Default to enabled if self.settings and self.settings.cli_config: use_arrow_navigation = self.settings.cli_config.enable_arrow_navigation - + if use_arrow_navigation: # Use the new reliable select menu with arrow navigation try: - from ii_agent.cli.components.select_menu import ( - create_tool_confirmation_menu, - ) - + from ii_agent.cli.components.select_menu import create_tool_confirmation_menu + menu = create_tool_confirmation_menu(self.console) choice_index = menu.select() - + if choice_index is not None: choice = str(choice_index + 1) # Convert 0-based to 1-based else: - choice = "4" # Default to "no" if cancelled - + choice = '4' # Default to "no" if cancelled + except Exception as e: # Fallback to traditional input self.console.print(f"[dim]Arrow navigation unavailable: {e}[/dim]") use_arrow_navigation = False - + if not use_arrow_navigation: # Traditional numbered input self.console.print("Do you want to execute this tool?") self.console.print("[bold green]1.[/bold green] Yes") - self.console.print( - "[bold blue]2.[/bold blue] Yes, and don't ask again for this tool this session" - ) - self.console.print( - "[bold cyan]3.[/bold cyan] Yes, approve for all tools in this session" - ) - self.console.print( - "[bold red]4.[/bold red] No, and tell ii-agent what to do differently" - ) + self.console.print("[bold blue]2.[/bold blue] Yes, and don't ask again for this tool this session") + self.console.print("[bold cyan]3.[/bold cyan] Yes, approve for all tools in this session") + self.console.print("[bold red]4.[/bold red] No, and tell ii-agent what to do differently") self.console.print() choice = self._get_traditional_input() - + # Handle the choice approved = False alternative_instruction = "" - - if choice == "1": + + if choice == '1': self.console.print("āœ… [green]Tool execution approved[/green]") approved = True - - elif choice == "2": - self.console.print( - f"āœ… [blue]Tool '{tool_name}' approved for this session[/blue]" - ) + + elif choice == '2': + self.console.print(f"āœ… [blue]Tool '{tool_name}' approved for this session[/blue]") approved = True # Add tool to allow_tools set if self.config: self.config.allow_tools.add(tool_name) - - elif choice == "3": + + elif choice == '3': self.console.print("āœ… [cyan]All tools approved for this session[/cyan]") approved = True # Set auto_approve_tools to True if self.config: self.config.set_auto_approve_tools(True) - - elif choice == "4": + + elif choice == '4': self.console.print("āŒ [red]Tool execution denied[/red]") approved = False # Get alternative instruction @@ -251,63 +239,61 @@ def _handle_tool_confirmation_event(self, event: RealtimeEvent) -> None: self.console.print("šŸ“ No alternative instruction provided") except (KeyboardInterrupt, EOFError): self.console.print("šŸ“ No alternative instruction provided") - + # Send response back via callback if self.confirmation_callback: - self.confirmation_callback( - tool_call_id, tool_name, approved, alternative_instruction - ) - + self.confirmation_callback(tool_call_id, tool_name, approved, alternative_instruction) + self.console.print() - + def _get_single_key_choice(self) -> str: """Get user choice with single key press (enhanced UX).""" try: import sys import tty import termios - + fd = sys.stdin.fileno() old_settings = termios.tcgetattr(fd) - + try: tty.setraw(fd) while True: key = sys.stdin.read(1) - if key in ["1", "2", "3", "4"]: + if key in ['1', '2', '3', '4']: # Echo the choice print(f"\nSelected: {key}") return key - elif key == "\x03": # Ctrl+C + elif key == '\x03': # Ctrl+C print("\nCancelled") - return "4" - elif key == "\r" or key == "\n": # Enter without selection + return '4' + elif key == '\r' or key == '\n': # Enter without selection continue - + finally: termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) - + except Exception: # Fallback to traditional input return self._get_traditional_input() - + def _get_traditional_input(self) -> str: """Get user choice with traditional input method.""" while True: try: choice = input("Enter your choice (1-4): ").strip() - if choice in ["1", "2", "3", "4"]: + if choice in ['1', '2', '3', '4']: return choice else: self.console.print("[red]Please enter 1, 2, 3, or 4[/red]") except (KeyboardInterrupt, EOFError): - return "4" # Default to no on interrupt - + return '4' # Default to no on interrupt + def _handle_processing_event(self, event: RealtimeEvent) -> None: """Handle processing event.""" content = event.content message = content.get("message", "Processing...") - + if not self.minimal: # Use animated spinner for processing if self._spinner: @@ -316,27 +302,27 @@ def _handle_processing_event(self, event: RealtimeEvent) -> None: self._spinner.start() else: self.console.print(f"ā³ [cyan]{message}[/cyan]") - + def _print_status(self, message: str) -> None: """Print status message.""" self.console.print(message) - + def _print_response(self, text: str) -> None: """Print agent response with clean formatting.""" # Clear any status indicators immediately self._clear_thinking_indicator() - + if not self.minimal: # Use simple prefix instead of heavy frames from rich.text import Text - + # Add spacing before agent response self.console.print() - + # Create clean response with agent icon response_text = Text() response_text.append("šŸ¤– Agent: ", style="green") - + # Check if text contains code blocks if "```" in text: # For code blocks, just add the text and let Rich handle markdown @@ -348,17 +334,17 @@ def _print_response(self, text: str) -> None: else: self.console.print() self.console.print(f"šŸ¤– Agent: [white]{text}[/white]") - + def _print_tool_call(self, tool_name: str, tool_input: Dict[str, Any]) -> None: """Print clean tool call information.""" - + # Special handling for todo tools if tool_name in ["TodoRead", "TodoWrite"]: tool_text = Text() tool_text.append(" šŸ“‹ ", style="bright_blue") tool_text.append(f"Using {tool_name}", style="bright_blue bold") self.console.print(tool_text) - + # For TodoWrite, show task preview with checkboxes if tool_name == "TodoWrite" and "todos" in tool_input: todos = tool_input.get("todos", []) @@ -371,7 +357,7 @@ def _print_tool_call(self, tool_name: str, tool_input: Dict[str, Any]) -> None: task_text.append(" āŽæ ", style="dim blue") else: task_text.append(" ", style="dim") - + # Choose checkbox based on status status = todo.get("status", "pending") if status == "completed": @@ -380,61 +366,51 @@ def _print_tool_call(self, tool_name: str, tool_input: Dict[str, Any]) -> None: task_text.append("☐ ", style="cyan") else: # pending task_text.append("☐ ", style="dim white") - + # Add task content (truncate if too long) content = todo.get("content", "") if len(content) > 60: content = content[:57] + "..." task_text.append(content, style="white") - + self.console.print(task_text) - + # If there are more tasks, show count if len(todos) > max_preview: more_text = Text() more_text.append(" ", style="dim") - more_text.append( - f"... and {len(todos) - max_preview} more tasks", - style="dim cyan", - ) + more_text.append(f"... and {len(todos) - max_preview} more tasks", style="dim cyan") self.console.print(more_text) return - + # Enhanced tool call display for other tools tool_text = Text() tool_text.append(" šŸ”§ ", style="bright_blue") tool_text.append(f"Using {tool_name}", style="bright_blue bold") - + # Show formatted parameters if tool_input: self.console.print(tool_text) self._print_tool_params(tool_input) else: self.console.print(tool_text) - + def _print_tool_params(self, tool_input: Dict[str, Any]) -> None: """Print tool parameters with enhanced formatting.""" for key, value in tool_input.items(): param_text = Text() param_text.append(" ā”œā”€ ", style="dim blue") param_text.append(f"{key}: ", style="cyan") - + # Special handling for file paths - if ( - key.endswith("_path") - or key == "file_path" - or (isinstance(value, str) and ("/" in value or "\\" in value)) - ): + if key.endswith('_path') or key == 'file_path' or (isinstance(value, str) and ('/' in value or '\\' in value)): formatted_path = self._format_file_path(str(value)) param_text.append(formatted_path, style="yellow") # Special handling for long strings elif isinstance(value, str) and len(value) > 80: truncated = value[:80] + "..." # Check if it's likely code/content - if any( - marker in value[:100] - for marker in ["{", "[", "<", "\\n", "def ", "class "] - ): + if any(marker in value[:100] for marker in ['{', '[', '<', '\\n', 'def ', 'class ']): param_text.append(f'"{truncated}"', style="dim green") else: param_text.append(f'"{truncated}"', style="white") @@ -444,15 +420,14 @@ def _print_tool_params(self, tool_input: Dict[str, Any]) -> None: param_text.append(f'"{value}"', style="white") else: param_text.append(str(value), style="magenta") - + self.console.print(param_text) - + def _format_file_path(self, path: str) -> str: """Format file path for better readability.""" # Show relative path from working directory if possible try: import os - if os.path.isabs(path): # Try to make it relative to current working directory rel_path = os.path.relpath(path) @@ -460,57 +435,57 @@ def _format_file_path(self, path: str) -> str: return rel_path except (ValueError, OSError): pass - + # Truncate very long paths intelligently if len(path) > 60: - parts = path.split("/") + parts = path.split('/') if len(parts) > 3: return f"{parts[0]}/.../{'/'.join(parts[-2:])}" else: return f"{path[:30]}...{path[-30:]}" - + return path - + def _print_tool_result(self, tool_name: str, result: str) -> None: """Print tool result with enhanced formatting and visual hierarchy.""" - + if not result.strip(): return - + # Special handling for todo-related tools if tool_name.lower() in ["todoread", "todowrite", "todo_read", "todo_write"]: self._print_todo_result(tool_name, result) return - + # Show result header with success indicator result_header = Text() result_header.append(" āœ“ ", style="bright_green") result_header.append(f"{tool_name} completed", style="green") self.console.print(result_header) - + # Print visual connector connector = Text() connector.append(" │", style="dim green") self.console.print(connector) - + # Format the result content self._format_and_print_result(result) - + def _print_todo_result(self, tool_name: str, result: str) -> None: """Print todo tool result using the TodoPanel component.""" try: import json - + # Parse the result to get todo data todos = [] - + # Try to parse the result as JSON try: # The result might be a string that contains JSON if "todos" in result: # Extract JSON from the result string - start_idx = result.find("[") - end_idx = result.rfind("]") + 1 + start_idx = result.find('[') + end_idx = result.rfind(']') + 1 if start_idx != -1 and end_idx > start_idx: json_str = result[start_idx:end_idx] todos = json.loads(json_str) @@ -519,32 +494,32 @@ def _print_todo_result(self, tool_name: str, result: str) -> None: parsed = json.loads(result) if isinstance(parsed, list): todos = parsed - elif isinstance(parsed, dict) and "todos" in parsed: - todos = parsed["todos"] + elif isinstance(parsed, dict) and 'todos' in parsed: + todos = parsed['todos'] except json.JSONDecodeError: # If JSON parsing fails, show raw result self._format_and_print_result(result) return - + # Use TodoPanel to render the todos if todos: self._todo_panel.render(todos, title=f"šŸ“‹ {tool_name} Result") else: # Empty todos self._todo_panel.render([], title=f"šŸ“‹ {tool_name} Result") - + except Exception as e: # Fallback to regular formatting if something goes wrong self.console.print(f"[dim red]Error rendering todo panel: {e}[/dim red]") self._format_and_print_result(result) - + def _format_and_print_result(self, result: str) -> None: """Format and print tool result with appropriate styling.""" # Truncate very long results original_length = len(result) if original_length > 2000: result = result[:2000] + "\n... (truncated)" - + # Detect result type and format accordingly if self._is_file_operation_result(result): self._print_file_operation_result(result) @@ -554,137 +529,100 @@ def _format_and_print_result(self, result: str) -> None: self._print_structured_result(result) else: self._print_plain_result(result) - + # Show truncation notice if applicable if original_length > 2000: truncation_notice = Text() truncation_notice.append(" └─ ", style="dim green") - truncation_notice.append( - f"({original_length - 2000} characters truncated)", style="dim yellow" - ) + truncation_notice.append(f"({original_length - 2000} characters truncated)", style="dim yellow") self.console.print(truncation_notice) - + def _is_file_operation_result(self, result: str) -> bool: """Check if result is from a file operation.""" file_indicators = [ - "Modified file", - "Created file", - "Deleted file", - "File not found", - "replacement(s)", - "added", - "removed", - "changed", + "Modified file", "Created file", "Deleted file", "File not found", + "replacement(s)", "added", "removed", "changed" ] return any(indicator in result for indicator in file_indicators) - + def _is_code_content(self, result: str) -> bool: """Check if result contains code content.""" code_indicators = [ - "def ", - "class ", - "function ", - "import ", - "from ", - "const ", - "let ", - "var ", - "#!/", - " bool: """Check if result is structured data (JSON, XML, etc.).""" stripped = result.strip() - return ( - (stripped.startswith("{") and stripped.endswith("}")) - or (stripped.startswith("[") and stripped.endswith("]")) - or stripped.startswith("<") - and stripped.endswith(">") - ) - + return (stripped.startswith('{') and stripped.endswith('}')) or \ + (stripped.startswith('[') and stripped.endswith(']')) or \ + stripped.startswith('<') and stripped.endswith('>') + def _print_file_operation_result(self, result: str) -> None: """Print file operation result with special formatting.""" - lines = result.split("\n") + lines = result.split('\n') for line in lines: if line.strip(): formatted_line = Text() formatted_line.append(" └─ ", style="dim green") - + # Highlight file paths - if "/" in line or "\\" in line: + if '/' in line or '\\' in line: # Try to identify and highlight file paths words = line.split() for word in words: - if "/" in word or "\\" in word: + if '/' in word or '\\' in word: formatted_line.append(word + " ", style="yellow") - elif ( - word.endswith(".py") - or word.endswith(".js") - or word.endswith(".html") - ): + elif word.endswith('.py') or word.endswith('.js') or word.endswith('.html'): formatted_line.append(word + " ", style="yellow") else: formatted_line.append(word + " ", style="dim white") else: formatted_line.append(line, style="dim white") - + self.console.print(formatted_line) - + def _print_code_result(self, result: str) -> None: """Print code content with syntax highlighting.""" try: from rich.panel import Panel - language = self._detect_language(result) - syntax = Syntax( - result, - language, - theme="monokai", - line_numbers=False, - indent_guides=True, - ) - + syntax = Syntax(result, language, theme="monokai", line_numbers=False, indent_guides=True) + # Create a subtle panel for code code_panel = Panel( syntax, border_style="dim green", padding=(0, 1), title="Result", - title_align="left", + title_align="left" ) self.console.print(code_panel, style="dim") except Exception: # Fallback to indented text self._print_plain_result(result) - + def _print_structured_result(self, result: str) -> None: """Print structured data (JSON, XML) with formatting.""" try: import json - # Try to parse and pretty-print JSON - if result.strip().startswith(("{", "[")): + if result.strip().startswith(('{', '[')): parsed = json.loads(result) formatted = json.dumps(parsed, indent=2) self._print_code_result(formatted) return - except Exception: + except: pass - + # Fallback to regular formatting self._print_plain_result(result) - + def _print_plain_result(self, result: str) -> None: """Print plain text result with consistent indentation.""" - lines = result.split("\n") + lines = result.split('\n') for i, line in enumerate(lines): if line.strip(): # Skip empty lines formatted_line = Text() @@ -696,11 +634,12 @@ def _print_plain_result(self, result: str) -> None: self.console.print(formatted_line) elif i > 0 and i < len(lines) - 1: # Keep internal empty lines self.console.print(" ", style="dim") - + + def _print_error(self, message: str) -> None: """Print error message.""" self.console.print(f"[red]{message}[/red]") - + def _clear_thinking_indicator(self) -> None: """Clear the thinking indicator.""" if self._thinking_indicator: @@ -708,7 +647,7 @@ def _clear_thinking_indicator(self) -> None: self._spinner.stop() self._spinner = None self._thinking_indicator = False - + def print_welcome(self) -> None: """Print welcome message.""" if not self.minimal: @@ -721,46 +660,47 @@ def print_welcome(self) -> None: "• Type [bold]/compact[/bold] to truncate context\n" "• Type [bold]/settings[/bold] to configure LLM settings", title="Welcome", - style="cyan", + style="cyan" ) self.console.print(welcome_panel) - + def print_goodbye(self) -> None: """Print goodbye message.""" if not self.minimal: goodbye_panel = Panel( "šŸ‘‹ [bold green]Session ended. Goodbye![/bold green]", title="Farewell", - style="green", + style="green" ) self.console.print(goodbye_panel) - + def print_session_info(self, session_name: Optional[str] = None) -> None: """Print session information.""" if not self.minimal and session_name: session_panel = Panel( f"šŸ“ [bold]Active Session[/bold]\n\nName: [cyan]{session_name}[/cyan]", title="Session Info", - style="yellow", + style="yellow" ) self.console.print(session_panel) - + def print_config_info(self, config: LLMConfig) -> None: """Print configuration information.""" if not self.minimal: table = Table(title="šŸ”§ Agent Configuration", style="blue") table.add_column("Setting", style="cyan") table.add_column("Value", style="white") - + # Extract key config attributes config_items = [] - + # Common LLM config attributes config_items.append(("Model", config.model)) config_items.append(("Provider", config.api_type.value)) config_items.append(("Temperature", str(config.temperature))) config_items.append(("Max Tokens", str(config.max_message_chars))) - + + # Display formatted config items if config_items: for key, value in config_items: @@ -768,19 +708,19 @@ def print_config_info(self, config: LLMConfig) -> None: else: # Fallback to string representation if no known attributes table.add_row("Configuration", str(config)) - + self.console.print(table) - + def print_workspace_info(self, workspace_path: str) -> None: """Print workspace information.""" if not self.minimal: workspace_panel = Panel( f"šŸ“‚ [bold]Workspace[/bold]\n\nPath: [cyan]{workspace_path}[/cyan]", title="Workspace Info", - style="yellow", + style="yellow" ) self.console.print(workspace_panel) - + def _detect_language(self, code: str) -> str: """Detect programming language from code content.""" # Simple language detection based on content @@ -798,56 +738,51 @@ def _detect_language(self, code: str) -> str: return "json" else: return "text" - - def display_token_usage( - self, token_count: int, cached_tokens: Optional[int] = None - ) -> None: + + def display_token_usage(self, token_count: int, cached_tokens: Optional[int] = None) -> None: """Display token usage information.""" self._token_display.display_usage(token_count, cached_tokens) - + def render_conversation_history(self, history) -> None: """Render conversation history using the same formatting as real-time messages.""" - + if not history or len(history.message_lists) == 0: return - + self.console.print() self.console.print("šŸ“œ [bold cyan]Previous Conversation History:[/bold cyan]") self.console.print("─" * 80) - + # Display each turn in the conversation using existing formatting methods for turn in history.message_lists: for message in turn: self._render_message(message) - + self.console.print("─" * 80) self.console.print("šŸ“ [dim]Continuing from here...[/dim]") self.console.print() - + def _render_message(self, message) -> None: """Render a single message using the same formatting as real-time display.""" - - if hasattr(message, "text"): + + if hasattr(message, 'text'): # User message or text result - if hasattr(message, "type") and message.type == "text_prompt": + if hasattr(message, 'type') and message.type == 'text_prompt': # User input - use same formatting as _print_user_input user_text = Text() user_text.append("šŸ‘¤ You: ", style="bold blue") user_text.append(message.text, style="white") self.console.print(user_text) - elif hasattr(message, "type") and message.type == "text_result": + elif hasattr(message, 'type') and message.type == 'text_result': # Agent response - use same formatting as _print_response self._print_response(message.text) - elif hasattr(message, "tool_name") and hasattr(message, "tool_input"): + elif hasattr(message, 'tool_name') and hasattr(message, 'tool_input'): # Tool call - use same formatting as _print_tool_call self._print_tool_call(message.tool_name, message.tool_input) - elif hasattr(message, "tool_output"): + elif hasattr(message, 'tool_output'): # Tool result - use same formatting as _print_tool_result - self._print_tool_result( - message.tool_name if hasattr(message, "tool_name") else "Unknown", - message.tool_output, - ) - + self._print_tool_result(message.tool_name if hasattr(message, 'tool_name') else "Unknown", message.tool_output) + def cleanup(self) -> None: """Clean up resources.""" # Clean up spinner if active diff --git a/src/ii_agent/core/config/ii_agent_config.py b/src/ii_agent/core/config/ii_agent_config.py index c885d74b..7d88bcff 100644 --- a/src/ii_agent/core/config/ii_agent_config.py +++ b/src/ii_agent/core/config/ii_agent_config.py @@ -21,9 +21,7 @@ class IIAgentConfig(BaseSettings): file_store_path: The path to the file store. """ - model_config = SettingsConfigDict( - env_file=".env", env_file_encoding="utf-8", extra="ignore" - ) + model_config = SettingsConfigDict(env_file=".env", env_file_encoding="utf-8", extra="ignore") file_store: str = Field(default="local") file_store_path: str = Field(default="~/.ii_agent") use_container_workspace: bool = Field(default=False) @@ -38,12 +36,10 @@ class IIAgentConfig(BaseSettings): auto_approve_tools: bool = False # Global tool approval setting. If True, all tools will be automatically approved. allow_tools: set[str] = set() # Tools that are confirmed by the user - @model_validator(mode="after") + @model_validator(mode='after') def set_database_url(self) -> "IIAgentConfig": if self.database_url is None: - self.database_url = ( - f"sqlite:///{os.path.expanduser(self.file_store_path)}/ii_agent.db" - ) + self.database_url = f"sqlite:///{os.path.expanduser(self.file_store_path)}/ii_agent.db" return self @@ -56,22 +52,21 @@ def workspace_root(self) -> str: @property def logs_path(self) -> str: return os.path.join(self.file_store_path, "logs") - - @field_validator("file_store_path") + + @field_validator('file_store_path') def expand_path(cls, v): - if v.startswith("~"): + if v.startswith('~'): return os.path.expanduser(v) return v def set_auto_approve_tools(self, value: bool) -> None: """Set the auto_approve_tools field value. - + Args: value: Whether to automatically approve tool executions in CLI mode """ self.auto_approve_tools = value - if __name__ == "__main__": config = IIAgentConfig() - print(config.workspace_root) + print(config.workspace_root) \ No newline at end of file diff --git a/src/ii_agent/core/config/utils.py b/src/ii_agent/core/config/utils.py index cd5f4a04..b5191675 100644 --- a/src/ii_agent/core/config/utils.py +++ b/src/ii_agent/core/config/utils.py @@ -3,4 +3,4 @@ def load_ii_agent_config() -> IIAgentConfig: """Load the IIAgent config from the environment variables.""" - return IIAgentConfig(session_id="test") + return IIAgentConfig() diff --git a/src/ii_agent/core/storage/models/settings.py b/src/ii_agent/core/storage/models/settings.py index a57f9305..199ef10c 100644 --- a/src/ii_agent/core/storage/models/settings.py +++ b/src/ii_agent/core/storage/models/settings.py @@ -2,7 +2,7 @@ from typing import Dict from pydantic import ( - BaseModel, + BaseModel, Field, ) @@ -14,6 +14,7 @@ from ii_agent.core.config.cli_config import CliConfig + class Settings(BaseModel): """ Persisted settings for II_AGENT sessions @@ -27,5 +28,5 @@ class Settings(BaseModel): cli_config: CliConfig | None = Field(default=None) model_config = { - "validate_assignment": True, - } + 'validate_assignment': True, + } \ No newline at end of file diff --git a/src/ii_agent/core/storage/settings/file_settings_store.py b/src/ii_agent/core/storage/settings/file_settings_store.py index 1af69ceb..2d7c45e4 100644 --- a/src/ii_agent/core/storage/settings/file_settings_store.py +++ b/src/ii_agent/core/storage/settings/file_settings_store.py @@ -27,7 +27,7 @@ async def call_sync_from_async(fn: Callable, *args, **kwargs): @dataclass class FileSettingsStore(SettingsStore): file_store: FileStore - path: str = "settings.json" + path: str = 'settings.json' async def load(self) -> Settings | None: try: @@ -39,7 +39,7 @@ async def load(self) -> Settings | None: return None async def store(self, settings: Settings) -> None: - json_str = settings.model_dump_json(context={"expose_secrets": True}) + json_str = settings.model_dump_json(context={'expose_secrets': True}) await call_sync_from_async(self.file_store.write, self.path, json_str) @classmethod @@ -48,6 +48,6 @@ async def get_instance( ) -> FileSettingsStore: file_store = get_file_store( config.file_store, - config.file_store_path, + config.file_store_path, ) - return FileSettingsStore(file_store) + return FileSettingsStore(file_store) \ No newline at end of file diff --git a/src/ii_agent/server/services/agent_service.py b/src/ii_agent/server/services/agent_service.py index 4d66a38e..9441c666 100644 --- a/src/ii_agent/server/services/agent_service.py +++ b/src/ii_agent/server/services/agent_service.py @@ -18,10 +18,7 @@ from ii_agent.llm import get_client from ii_agent.llm.context_manager.llm_summarizing import LLMSummarizingContextManager from ii_agent.llm.token_counter import TokenCounter -from ii_agent.prompts.system_prompt import ( - SYSTEM_PROMPT, - SYSTEM_PROMPT_WITH_SEQ_THINKING, -) +from ii_agent.prompts.system_prompt import SYSTEM_PROMPT, SYSTEM_PROMPT_WITH_SEQ_THINKING from ii_agent.subscribers.websocket_subscriber import WebSocketSubscriber from ii_agent.tools import get_system_tools from ii_agent.utils.workspace_manager import WorkspaceManager @@ -50,7 +47,7 @@ async def create_agent( system_prompt: Optional[str] = None, ) -> Tuple[FunctionCallAgent, AgentController]: """Create a new agent instance following CLI patterns. - + Args: model_name: Name of the LLM model to use session_id: Session UUID @@ -58,7 +55,7 @@ async def create_agent( websocket: WebSocket connection tool_args: Tool configuration arguments system_prompt: Optional custom system prompt - + Returns: Tuple of (FunctionCallAgent, AgentController) """ @@ -66,15 +63,15 @@ async def create_agent( user_id = None # TODO: Support user id settings_store = await FileSettingsStore.get_instance(self.config, user_id) settings = await settings_store.load() - + # Get LLM configuration llm_config = settings.llm_configs.get(model_name) if not llm_config: raise ValueError(f"LLM config not found for model: {model_name}") - + # Create LLM client llm_client = get_client(llm_config) - + # Determine system prompt if system_prompt is None: system_prompt = ( @@ -88,7 +85,7 @@ async def create_agent( system_prompt=system_prompt, temperature=getattr(self.config, "temperature", 0.7), ) - + # Get tools tools = get_system_tools( client=llm_client, @@ -97,21 +94,21 @@ async def create_agent( container_id=self.config.docker_container_id, tool_args=tool_args, ) - + # Create agent agent = FunctionCallAgent( llm=llm_client, config=agent_config, tools=tools, ) - + # Create event stream event_stream = AsyncEventStream(logger=logger) - + # Add WebSocket subscriber websocket_subscriber = WebSocketSubscriber(websocket) event_stream.subscribe(websocket_subscriber.handle_event) - + # Create context manager token_counter = TokenCounter() context_manager = LLMSummarizingContextManager( @@ -119,14 +116,14 @@ async def create_agent( token_counter=token_counter, token_budget=self.config.token_budget, ) - + # Create or restore state state = State() try: state.restore_from_session(str(session_id), self.file_store) except FileNotFoundError: logger.info(f"No history found for session {session_id}") - + # Create controller controller = AgentController( agent=agent, @@ -137,10 +134,10 @@ async def create_agent( context_manager=context_manager, interactive_mode=True, ) - + # Store session ID for tracking controller.session_id = session_id - + return agent, controller async def create_reviewer_agent( @@ -152,19 +149,19 @@ async def create_reviewer_agent( tool_args: Dict[str, Any], ) -> Tuple[FunctionCallAgent, AgentController]: """Create a reviewer agent using FunctionCallAgent with reviewer prompt. - + Args: model_name: Name of the LLM model to use session_id: Session UUID workspace_manager: Workspace manager instance websocket: WebSocket connection tool_args: Tool configuration arguments - + Returns: Tuple of (FunctionCallAgent, AgentController) configured for reviewing """ reviewer_prompt = self.get_reviewer_system_prompt() - + return await self.create_agent( model_name=model_name, session_id=session_id, @@ -187,26 +184,16 @@ def get_reviewer_system_prompt(self) -> str: The user will provide you with the task, result, and workspace directory to review. Conduct a thorough review with emphasis on functionality testing and user experience evaluation.""" - def _ensure_session_exists( - self, - session_id: uuid.UUID, - workspace_manager: WorkspaceManager, - device_id: Optional[str] = None, - ): + def _ensure_session_exists(self, session_id: uuid.UUID, workspace_manager: WorkspaceManager, device_id: Optional[str] = None): """Ensure a database session exists for the given session ID.""" existing_session = Sessions.get_session_by_id(session_id) if existing_session: - logger.info( - f"Found existing session {session_id} with workspace at {existing_session.workspace_dir}" - ) + logger.info(f"Found existing session {session_id} with workspace at {existing_session.workspace_dir}") else: # Create new session if it doesn't exist Sessions.create_session( device_id=device_id, session_uuid=session_id, workspace_path=workspace_manager.root, - runtime_id=str(session_id), - ) - logger.info( - f"Created new session {session_id} with workspace at {workspace_manager.root}" ) + logger.info(f"Created new session {session_id} with workspace at {workspace_manager.root}") \ No newline at end of file diff --git a/src/ii_agent/tools/mcp_tool.py b/src/ii_agent/tools/mcp_tool.py index 02773ae5..5765886e 100644 --- a/src/ii_agent/tools/mcp_tool.py +++ b/src/ii_agent/tools/mcp_tool.py @@ -1,26 +1,13 @@ from typing import Any, Optional -from fastmcp.client import Client -from fastmcp.exceptions import ToolError +from fastmcp import Client +from fastmcp.exceptions import ToolError from mcp.types import ToolAnnotations -from ii_agent.tools.base import ( - BaseTool, - ToolResult, - TextContent, - ImageContent, - ToolConfirmationDetails, -) - +from ii_agent.tools.base import BaseTool, ToolResult, TextContent, ImageContent, ToolConfirmationDetails, ToolConfirmationOutcome +from ii_agent.core.config.ii_agent_config import IIAgentConfig class MCPTool(BaseTool): - def __init__( - self, - mcp_client: Client, - name: str, - description: str, - input_schema: dict[str, Any], - annotations: Optional[ToolAnnotations] = None, - trust: bool = False, - ): + + def __init__(self, mcp_client: Client, name: str, description: str, input_schema: dict[str, Any], annotations: Optional[ToolAnnotations] = None, trust: bool = False): # MCP information self.mcp_client = mcp_client @@ -31,55 +18,43 @@ def __init__( self.annotations = annotations self.trust = trust - + def is_read_only(self) -> bool: if self.annotations is not None: return self.annotations.readOnlyHint or False return False - async def should_confirm_execute( - self, tool_input: dict[str, Any] - ) -> ToolConfirmationDetails | bool: + async def should_confirm_execute(self, tool_input: dict[str, Any]) -> ToolConfirmationDetails | bool: # TODO: implement confirmation if self.trust: return False - + return ToolConfirmationDetails( type="mcp", message=f"Do you want to execute the tool {self.name} with the following input: {tool_input}?", - on_confirm_callback=lambda outcome: None, + on_confirm_callback=lambda outcome: None ) async def execute(self, tool_input: dict[str, Any]) -> ToolResult: try: async with self.mcp_client: mcp_results = await self.mcp_client.call_tool(self.name, tool_input) - + llm_content = [] user_display_content = "" for mcp_result in mcp_results.content: if mcp_result.type == "text": - llm_content.append( - TextContent(type="text", text=mcp_result.text) - ) + llm_content.append(TextContent(type="text", text=mcp_result.text)) user_display_content += mcp_result.text elif mcp_result.type == "image": - llm_content.append( - ImageContent( - type="image", - data=mcp_result.data, - mimeType=mcp_result.mimeType, - ) - ) - user_display_content += "\n[Redacted image]" + llm_content.append(ImageContent(type="image", data=mcp_result.data, mimeType=mcp_result.mimeType)) + user_display_content += f"\n[Redacted image]" else: raise ValueError(f"Unknown result type: {mcp_result.type}") - return ToolResult( - llm_content=llm_content, user_display_content=user_display_content - ) + return ToolResult(llm_content=llm_content, user_display_content=user_display_content) except ToolError as e: return ToolResult( llm_content=f"Error while calling tool {self.name} with input {tool_input}: {str(e)}\n\nPlease analyze the error message to determine if it's due to incorrect input parameters or an internal tool issue. If the error is due to incorrect input, retry with the correct parameters. Otherwise, try an alternative approach and inform the user about the issue.", - user_display_content=f"Error while calling tool {self.name} with input {tool_input}: {str(e)}", - ) + user_display_content=f"Error while calling tool {self.name} with input {tool_input}: {str(e)}" + ) \ No newline at end of file diff --git a/src/ii_agent/tools/tool_manager.py b/src/ii_agent/tools/tool_manager.py index d164ed1a..6698ecf7 100644 --- a/src/ii_agent/tools/tool_manager.py +++ b/src/ii_agent/tools/tool_manager.py @@ -1,5 +1,7 @@ import asyncio import logging +from copy import deepcopy +from dataclasses import dataclass from fastmcp import Client from pydantic import BaseModel from typing import Optional, List, Dict, Any @@ -14,7 +16,6 @@ from ii_agent.tools.web_visit_tool import WebVisitTool from ii_agent.tools.web_dev_tool import FullStackInitTool - class ToolCallParameters(BaseModel): tool_call_id: str tool_name: str @@ -57,24 +58,13 @@ class AgentToolManager: search capabilities, and task completion functionality. """ - def __init__( - self, - tools: List[BaseTool], - logger_for_agent_logs: logging.Logger, - interactive_mode: bool = True, - reviewer_mode: bool = False, - ): + def __init__(self, tools: List[BaseTool], logger_for_agent_logs: logging.Logger, interactive_mode: bool = True, reviewer_mode: bool = False): self.tools = tools async def register_tools(self, tools: List[BaseTool]): self.tools.extend(tools) - async def register_mcp_tools( - self, - mcp_config: Dict[str, Any] | None = None, - mcp_client: Client | None = None, - trust: bool = False, - ): + async def register_mcp_tools(self, mcp_config: Dict[str, Any] | None = None, mcp_client: Client | None = None, trust: bool = False): if not mcp_config and not mcp_client: raise ValueError("Either mcp_config or client must be provided") if mcp_config: @@ -83,9 +73,7 @@ async def register_mcp_tools( async with mcp_client: mcp_tools = await mcp_client.list_tools() for tool in mcp_tools: - assert tool.description is not None, ( - f"Tool {tool.name} has no description" - ) + assert tool.description is not None, f"Tool {tool.name} has no description" tool_annotations = tool.annotations self.tools.append( MCPTool( @@ -139,9 +127,9 @@ async def run_tool(self, tool_call: ToolCallParameters) -> ToolResult: tool_input = tool_call.tool_input llm_tool = self.get_tool(tool_name) logger.debug(f"Running tool: {tool_name}") - logger.debug(f"Tool input: {tool_input}") + logger.debug(f"Tool input: {tool_input}") tool_result = await llm_tool.execute(tool_input) - + user_display_content = tool_result.user_display_content tool_input_str = "\n".join([f" - {k}: {v}" for k, v in tool_input.items()]) @@ -153,50 +141,44 @@ async def run_tool(self, tool_call: ToolCallParameters) -> ToolResult: return tool_result - async def run_tools_batch( - self, tool_calls: List[ToolCallParameters] - ) -> List[ToolResult]: + async def run_tools_batch(self, tool_calls: List[ToolCallParameters]) -> List[ToolResult]: """ Execute multiple tools either concurrently or serially based on their read-only status. - + Args: tool_calls: List of tool call parameters - + Returns: List of tool results in the same order as input tool_calls """ if not tool_calls: return [] - + if len(tool_calls) == 1: # Single tool - just execute normally result = await self.run_tool(tool_calls[0]) return [result] - + # Determine execution strategy based on read-only status if should_run_concurrently(tool_calls, self): logger.info(f"Running {len(tool_calls)} tools concurrently (all read-only)") return await self._run_tools_concurrently(tool_calls) else: - logger.info( - f"Running {len(tool_calls)} tools serially (contains non-read-only tools)" - ) + logger.info(f"Running {len(tool_calls)} tools serially (contains non-read-only tools)") return await self._run_tools_serially(tool_calls) - - async def _run_tools_concurrently( - self, tool_calls: List[ToolCallParameters] - ) -> List[ToolResult]: + + async def _run_tools_concurrently(self, tool_calls: List[ToolCallParameters]) -> List[ToolResult]: """Execute tools concurrently and return results in order.""" - + # Create tasks for each tool with proper concurrency limits async def run_single_tool(tool_call: ToolCallParameters) -> ToolResult: """Wrapper for single tool execution.""" result = await self.run_tool(tool_call) return result - + # Create tasks for all tools with concurrency limit from ii_agent.utils.concurrent_execution import MAX_TOOL_CONCURRENCY - + if len(tool_calls) <= MAX_TOOL_CONCURRENCY: # All tools can run concurrently tasks = [asyncio.create_task(run_single_tool(tc)) for tc in tool_calls] @@ -204,31 +186,27 @@ async def run_single_tool(tool_call: ToolCallParameters) -> ToolResult: else: # Use semaphore to limit concurrency semaphore = asyncio.Semaphore(MAX_TOOL_CONCURRENCY) - + async def limited_run_tool(tool_call: ToolCallParameters) -> ToolResult: async with semaphore: return await run_single_tool(tool_call) - + tasks = [asyncio.create_task(limited_run_tool(tc)) for tc in tool_calls] results = await asyncio.gather(*tasks, return_exceptions=True) - + # Handle any exceptions and maintain order final_results = [] for i, result in enumerate(results): if isinstance(result, Exception): - error_msg = ( - f"Error executing tool {tool_calls[i].tool_name}: {str(result)}" - ) + error_msg = f"Error executing tool {tool_calls[i].tool_name}: {str(result)}" logger.error(error_msg) final_results.append(error_msg) else: final_results.append(result) - + return final_results - - async def _run_tools_serially( - self, tool_calls: List[ToolCallParameters] - ) -> List[ToolResult]: + + async def _run_tools_serially(self, tool_calls: List[ToolCallParameters]) -> List[ToolResult]: """Execute tools serially and return results in order.""" results = [] for tool_call in tool_calls: @@ -245,4 +223,4 @@ def get_tools(self) -> List[BaseTool]: """ Returns the list of tools. """ - return self.tools + return self.tools \ No newline at end of file diff --git a/src/ii_tool/mcp/server.py b/src/ii_tool/mcp/server.py index 8548af42..7607e042 100644 --- a/src/ii_tool/mcp/server.py +++ b/src/ii_tool/mcp/server.py @@ -3,31 +3,16 @@ from starlette.middleware.cors import CORSMiddleware from argparse import ArgumentParser from ii_tool.core.workspace import WorkspaceManager -from ii_tool.tools.shell import ( - ShellInit, - ShellRunCommand, - ShellView, - ShellKill, - ShellStopCommand, - ShellList, -) +from ii_tool.tools.shell import ShellInit, ShellRunCommand, ShellView, ShellKill, ShellStopCommand, ShellList from ii_tool.tools.shell.terminal_manager import TmuxWindowManager -from ii_tool.tools.file_system import ( - GlobTool, - GrepTool, - LSTool, - FileReadTool, - FileWriteTool, - FileEditTool, - MultiEditTool, -) +from ii_tool.tools.file_system import GlobTool, GrepTool, LSTool, FileReadTool, FileWriteTool, FileEditTool, MultiEditTool from ii_tool.tools.productivity import TodoReadTool, TodoWriteTool def create_mcp(workspace_dir: str, session_id: str): terminal_manager = TmuxWindowManager(chat_session_id=session_id) workspace_manager = WorkspaceManager(workspace_path=workspace_dir) - + shell_tools = [ ShellInit(terminal_manager, workspace_manager), ShellRunCommand(terminal_manager), @@ -77,17 +62,16 @@ def create_mcp(workspace_dir: str, session_id: str): return mcp - if __name__ == "__main__": parser = ArgumentParser() parser.add_argument("--workspace_dir", type=str) parser.add_argument("--session_id", type=str, default=None) parser.add_argument("--port", type=int, default=6060) - + args = parser.parse_args() - + mcp = create_mcp( workspace_dir=args.workspace_dir, session_id=args.session_id, ) - mcp.run(transport="http", host="0.0.0.0", port=args.port) + mcp.run(transport="http", host="0.0.0.0", port=args.port) \ No newline at end of file From 7261e1595c172544859f900d82edbeab81d583ea Mon Sep 17 00:00:00 2001 From: Khoa Ngo The Date: Thu, 24 Jul 2025 17:03:51 +0700 Subject: [PATCH 11/13] feat: session reload interative --- src/ii_agent/cli/app.py | 67 ++--- src/ii_agent/cli/session_config.py | 35 +-- src/ii_agent/cli/settings_onboard.py | 2 + src/ii_agent/cli/state_persistence.py | 250 ++++++++++-------- .../cli/subscribers/console_subscriber.py | 174 +++--------- src/ii_agent/runtime/base.py | 5 +- src/ii_agent/runtime/docker.py | 4 +- src/ii_agent/runtime/e2b.py | 10 +- src/ii_agent/runtime/local.py | 3 +- src/ii_agent/runtime/runtime_manager.py | 13 +- src/ii_agent/runtime/runtime_registry.py | 3 +- 11 files changed, 229 insertions(+), 337 deletions(-) diff --git a/src/ii_agent/cli/app.py b/src/ii_agent/cli/app.py index f50d8a0a..4863f105 100644 --- a/src/ii_agent/cli/app.py +++ b/src/ii_agent/cli/app.py @@ -53,7 +53,6 @@ def __init__( self.llm_config = llm_config self.workspace_manager = WorkspaceManager(Path(workspace_path)) # Create state manager - we'll update it with continue_session later - self.state_manager = None self.workspace_path = workspace_path # Create event stream self.event_stream = AsyncEventStream(logger=logger) @@ -99,7 +98,7 @@ def _handle_tool_confirmation(self, tool_call_id: str, tool_name: str, approved: else: logger.debug(f"Tool confirmation received but no agent controller: {tool_call_id} -> approved={approved}") - async def initialize_agent(self, continue_from_state: bool = False) -> None: + async def initialize_agent(self) -> None: """Initialize the agent controller.""" if self.agent_controller is not None: return @@ -107,30 +106,13 @@ async def initialize_agent(self, continue_from_state: bool = False) -> None: if not self.session_config: raise ValueError("Session config not initialized") - # Create state manager now that we know if we're continuing - if self.state_manager is None: - self.state_manager = StateManager( - Path(self.config.file_store_path), - session_name=self.session_config.session_name, - continue_session=continue_from_state, - ) - settings_store = await FileSettingsStore.get_instance(self.config, None) - settings = await settings_store.load() - - # Ensure CLI config exists with defaults - if not settings.cli_config: - from ii_agent.core.config.cli_config import CliConfig - settings.cli_config = CliConfig() - await settings_store.store(settings) - - # Update console subscriber with settings - self.console_subscriber.settings = settings - # Load saved state if --continue flag is used saved_state_data = None - if continue_from_state: - saved_state_data = self.state_manager.load_state() + # Initialize with session continuation check + is_valid_session = self.state_manager.is_valid_session(self.session_config.session_id) + if is_valid_session and await self.console_subscriber.should_continue_from_state(): + saved_state_data = self.state_manager.load_state(self.session_config.session_id) if saved_state_data: self.console_subscriber.console.print( "šŸ”„ [cyan]Continuing from previous state...[/cyan]" @@ -163,7 +145,7 @@ async def initialize_agent(self, continue_from_state: bool = False) -> None: # Get system local tools tools = get_system_tools( client=llm_client, - settings=settings, + settings=self.settings, workspace_manager=self.workspace_manager, ) @@ -174,9 +156,12 @@ async def initialize_agent(self, continue_from_state: bool = False) -> None: ) runtime_manager = RuntimeManager( - session_id=UUID(self.session_config.session_id), settings=settings + session_config=self.session_config, settings=self.settings ) - await runtime_manager.start_runtime() + if not is_valid_session: + await runtime_manager.start_runtime() + else: + await runtime_manager.connect_runtime() mcp_client = await runtime_manager.get_mcp_client( str(self.workspace_manager.root) ) @@ -226,7 +211,7 @@ async def initialize_agent(self, continue_from_state: bool = False) -> None: self.console_subscriber.print_workspace_info(str(self.workspace_manager.root)) # Show previous conversation history if continuing from state - if continue_from_state and saved_state_data: + if is_valid_session and saved_state_data: self.console_subscriber.render_conversation_history( self.agent_controller.history ) @@ -234,16 +219,20 @@ async def initialize_agent(self, continue_from_state: bool = False) -> None: async def run_interactive_mode(self) -> int: """Run interactive chat mode.""" try: - # Set up session configuration with interactive selection - self.session_config = await self.console_subscriber.setup_session_config() - - # Initialize with session continuation check - continue_from_state = ( - await self.console_subscriber.should_continue_from_state( - self.session_config.session_name, self.config.file_store_path - ) - ) - await self.initialize_agent(continue_from_state) + #Initialize settings + settings_store = await FileSettingsStore.get_instance(self.config, None) + self.settings = await settings_store.load() + + # Ensure CLI config exists with defaults + if not self.settings.cli_config: + from ii_agent.core.config.cli_config import CliConfig + self.settings.cli_config = CliConfig() + await settings_store.store(self.settings) + self.console_subscriber.settings = self.settings + self.state_manager = StateManager(self.config, self.settings) + + self.session_config = await self.console_subscriber.select_session_config(self.state_manager) + await self.initialize_agent() self.console_subscriber.print_welcome() self.console_subscriber.print_session_info( @@ -354,7 +343,7 @@ def _save_output(self, result: str, output_file: str, output_format: str) -> Non def _save_state_on_exit(self, should_save: bool) -> None: """Save agent state when exiting if requested.""" - if not should_save or not self.agent_controller or not self.state_manager: + if not should_save or not self.agent_controller or not self.state_manager or not self.session_config: return try: @@ -367,11 +356,11 @@ def _save_state_on_exit(self, should_save: bool) -> None: # Save the complete state self.state_manager.save_state( + session_id=self.session_config.session_id, agent_state=current_state, config=self.config, llm_config=self.llm_config, workspace_path=str(self.workspace_manager.root), - session_name=None, # For --continue, we don't use session names ) self.console_subscriber.console.print( diff --git a/src/ii_agent/cli/session_config.py b/src/ii_agent/cli/session_config.py index 7265fb4f..880f0b0a 100644 --- a/src/ii_agent/cli/session_config.py +++ b/src/ii_agent/cli/session_config.py @@ -1,39 +1,14 @@ """Session configuration for CLI.""" -from pathlib import Path -from typing import List +from typing import Optional from pydantic import BaseModel +from ii_agent.runtime.model.constants import RuntimeMode + class SessionConfig(BaseModel): """Configuration for session management.""" session_id: str - session_name: str - - @property - def sessions_dir(self) -> Path: - """Get the sessions directory path.""" - home = Path.home() - return home / ".ii_agent" / "sessions" - - def get_available_sessions(self) -> List[str]: - """Get list of available session names from the sessions directory.""" - sessions_dir = self.sessions_dir - if not sessions_dir.exists(): - return [] - - sessions = [] - for session_file in sessions_dir.glob("*.json"): - sessions.append(session_file.stem) - - return sorted(sessions) - - def session_exists(self, session_name: str) -> bool: - """Check if a session exists.""" - session_file = self.sessions_dir / f"{session_name}.json" - return session_file.exists() - - def ensure_sessions_dir(self) -> None: - """Ensure the sessions directory exists.""" - self.sessions_dir.mkdir(parents=True, exist_ok=True) + mode: RuntimeMode + session_name: Optional[str] = None diff --git a/src/ii_agent/cli/settings_onboard.py b/src/ii_agent/cli/settings_onboard.py index 397f4999..b285024b 100644 --- a/src/ii_agent/cli/settings_onboard.py +++ b/src/ii_agent/cli/settings_onboard.py @@ -497,6 +497,8 @@ async def modify_settings(settings_store: FileSettingsStore) -> None: if modify_choice == 0: await setup_llm_configuration(settings_store) + elif modify_choice == 1: + await setup_runtime_configuration(settings_store) else: print_formatted_text(HTML('No settings found. Running first-time setup...')) await run_first_time_setup(settings_store) \ No newline at end of file diff --git a/src/ii_agent/cli/state_persistence.py b/src/ii_agent/cli/state_persistence.py index cfe58dc5..3f0311c7 100644 --- a/src/ii_agent/cli/state_persistence.py +++ b/src/ii_agent/cli/state_persistence.py @@ -6,60 +6,91 @@ """ import json -import uuid +import os +import glob from datetime import datetime from pathlib import Path from typing import Optional, Dict, Any +import uuid +from ii_agent.cli.session_config import SessionConfig from ii_agent.core.config.ii_agent_config import IIAgentConfig from ii_agent.core.config.llm_config import LLMConfig from ii_agent.controller.state import State from ii_agent.core.logger import logger from ii_agent.core.storage.local import LocalFileStore -from ii_agent.core.storage.locations import get_conversation_metadata_filename +from ii_agent.core.storage.locations import get_conversation_agent_history_filename, get_conversation_metadata_filename, CONVERSATION_BASE_DIR +from ii_agent.core.storage.models.settings import Settings +from ii_agent.runtime.model.constants import RuntimeMode class StateManager: """Manages saving and loading of agent state for --continue functionality.""" def __init__( - self, workspace_path: Path, session_name: str, continue_session: bool = False + self, config: IIAgentConfig, settings: Settings ): - self.workspace_path = str(workspace_path) - self.session_name = session_name - self.ii_agent_dir = workspace_path / "sessions" / session_name - self.ii_agent_dir.mkdir(exist_ok=True) - self.current_state_link = self.ii_agent_dir / "agent_state.json" - self.metadata_link = self.ii_agent_dir / "metadata.json" - # Use the global file store location - self.file_store = LocalFileStore("~/.ii_agent") - - # Determine session ID based on whether we're continuing or starting fresh - if ( - continue_session - and self.current_state_link.exists() - and self.metadata_link.exists() - ): - try: - with open(self.metadata_link, "r") as f: - metadata = json.load(f) - existing_session_id = metadata.get("session_id") - if existing_session_id: - self.session_id = existing_session_id - logger.info(f"Continuing with existing session: {self.session_id}") - else: - self.session_id = uuid.uuid4().hex - logger.info(f"No valid session found, creating new session: {self.session_id}") - except Exception as e: - logger.warning(f"Error reading existing session, creating new one: {e}") - self.session_id = uuid.uuid4().hex + self.workspace_path = config.file_store_path + self.config = config + self.settings = settings + self.file_store = LocalFileStore(self.workspace_path) + + def get_metadata(self, session_id: str) -> Optional[Dict[str, Any]]: + try: + metadata_filename = get_conversation_metadata_filename(session_id) + metadata = self.file_store.read(metadata_filename) + metadata = json.loads(metadata) + return metadata + except Exception as e: + logger.error(f"Error reading metadata: {e}") + return None + + def get_state_config(self, session_id: Optional[str] = None) -> SessionConfig: + if not session_id or not self.is_valid_session(session_id): + session_id = uuid.uuid4().hex + session_name = None + runtime_mode = self.settings.runtime_config.mode + return SessionConfig(session_id=session_id, session_name=session_name, mode=runtime_mode) else: - self.session_id = uuid.uuid4().hex - if not continue_session: - logger.info(f"Starting new session: {self.session_id}") + metadata = self.get_metadata(session_id) + if "runtime_mode" not in metadata: # type: ignore + runtime_mode = self.settings.runtime_config.mode + return SessionConfig(session_id=session_id, session_name=metadata["session_name"], mode=RuntimeMode(metadata["runtime_mode"])) # type: ignore + + def is_valid_session(self, session_id: str) -> bool: + return self.get_metadata(session_id) is not None and self.get_state(session_id) is not None + + def get_state(self, session_id: str) -> Optional[Dict[str, Any]]: + try: + state_filename = get_conversation_agent_history_filename(session_id) + state = self.file_store.read(state_filename) + state = json.loads(state) + return state + except Exception as e: + logger.error(f"Error reading state: {e}") + return None + + def get_available_sessions(self) -> list[str]: + try: + sessions_dir = Path(os.path.join(self.workspace_path, CONVERSATION_BASE_DIR)) + if not sessions_dir.exists(): + return [] + + sessions = [] + for session_dir in sessions_dir.glob("*"): + if session_dir.is_dir(): + json_files = list(session_dir.glob("*.json")) + if json_files: + sessions.append(session_dir.name) + + return sorted(sessions) + except Exception as e: + logger.error(f"Error getting available sessions: {e}") + return [] def save_state( self, + session_id: str, agent_state: State, config: IIAgentConfig, llm_config: LLMConfig, @@ -69,15 +100,16 @@ def save_state( """Save state and metadata using the new JSON format.""" try: # Save core state using State's save_to_session method - agent_state.save_to_session(self.session_id, self.file_store) + agent_state.save_to_session(session_id, self.file_store) # Save metadata separately metadata = { "version": "2.0", "timestamp": datetime.now().isoformat(), - "session_id": self.session_id, + "session_id": session_id, "workspace_path": str(workspace_path), "session_name": session_name, + "runtime_mode": self.settings.runtime_config.mode.value, "agent_state": { "message_count": len(agent_state.message_lists) }, @@ -95,49 +127,28 @@ def save_state( } } - metadata_filename = get_conversation_metadata_filename(self.session_id) + metadata_filename = get_conversation_metadata_filename(session_id) self.file_store.write(metadata_filename, json.dumps(metadata, indent=2, ensure_ascii=False)) - - # Update current state pointer - current_state_info = { - "current_session_id": self.session_id, - "workspace_path": self.workspace_path, - "last_updated": datetime.now().isoformat() - } - with open(self.current_state_link, "w") as f: - json.dump(current_state_info, f, indent=2) - - logger.info(f"State saved for session {self.session_id}") + logger.info(f"State saved for session {session_id}") except Exception as e: logger.error(f"Error saving state: {e}") raise - def load_state(self) -> Optional[Dict[str, Any]]: + def load_state(self, session_id: str) -> Optional[Dict[str, Any]]: """Load the saved agent state from current state pointer.""" try: # First, check if current_state.json exists - if not self.current_state_link.exists(): - return None - - # Read the current state pointer - with open(self.metadata_link, "r") as f: - metadata = json.load(f) + chat_history = self.get_state(session_id) + metadata = self.get_metadata(session_id) - session_id = metadata.get("session_id") - - if not session_id: + if not chat_history or not metadata: return None # Create a new State object and restore from session state = State() state.restore_from_session(session_id, self.file_store) - # Load metadata - metadata_filename = get_conversation_metadata_filename(session_id) - metadata_json = self.file_store.read(metadata_filename) - metadata = json.loads(metadata_json) - # Combine state and metadata into return format return { "version": metadata.get("version", "2.0"), @@ -157,57 +168,80 @@ def load_state(self) -> Optional[Dict[str, Any]]: logger.error(f"Error loading state: {e}") return None - - def clear_state(self) -> None: - """Remove the saved state files.""" - try: - # Remove current state pointer - if self.current_state_link.exists(): - self.current_state_link.unlink() - logger.info(f"Current state pointer {self.current_state_link} removed") - except Exception as e: - logger.error(f"Error clearing state: {e}") - - def has_saved_state(self) -> bool: - """Check if there's a saved state file.""" - return self.current_state_link.exists() - - def get_state_info(self) -> Optional[Dict[str, Any]]: + def load_state_info(self, session_id: str) -> Optional[Dict[str, Any]]: """Get basic info about the saved state without loading it.""" try: - if not self.current_state_link.exists(): - return None - - # Read current state pointer - with open(self.current_state_link, "r") as f: - current_state_info = json.load(f) - - session_id = current_state_info.get("current_session_id") - workspace_path = current_state_info.get("workspace_path") - - if not session_id: + chat_history = self.get_state(session_id) + metadata = self.get_metadata(session_id) + if not chat_history or not metadata: return None - - # Read metadata file - metadata_filename = get_conversation_metadata_filename(session_id) - try: - metadata_json = self.file_store.read(metadata_filename) - metadata = json.loads(metadata_json) - - return { - "timestamp": metadata.get("timestamp"), - "session_name": metadata.get("session_name"), - "workspace_path": metadata.get("workspace_path"), - "version": metadata.get("version", "2.0"), - "session_id": metadata.get("session_id"), - "message_count": metadata.get("agent_state", {}).get("message_count", 0) - } - except FileNotFoundError: - return None - + return { + "timestamp": metadata.get("timestamp"), + "session_name": metadata.get("session_name"), + "workspace_path": metadata.get("workspace_path"), + "version": metadata.get("version", "2.0"), + "session_id": metadata.get("session_id"), + "message_count": metadata.get("agent_state", {}).get("message_count", 0) + } except Exception as e: logger.error(f"Error getting state info: {e}") return None + + def load_state_summary(self, session_id: str) -> str: + sessions_dir = Path.home() / ".ii_agent" / "sessions" + state_file = sessions_dir / session_id / "agent_state.json" + if state_file.exists(): + with open(state_file, "r") as f: + data = json.load(f) + + # Extract meaningful information + info_lines = [] + + # User Message + if "last_user_prompt_index" in data: + last_user_prompt = data["message_lists"][ + int(data["last_user_prompt_index"]) + ][0]["text"] + if len(last_user_prompt) > 50: + last_user_prompt = "..." + last_user_prompt[-47:] + info_lines.append(f"User: {last_user_prompt}") + + # Message count + if "message_lists" in data and data["message_lists"]: + message_count = len(data["message_lists"]) + info_lines.append(f"šŸ’¬ Messages: {message_count}") + + # Get last few message summaries + recent_messages = [] + for msg_list in data["message_lists"][ + -2: + ]: # Last 2 message lists + messages = msg_list + for msg in messages[-3:]: # Last 3 messages from each list + if msg.get("text"): + content = msg["text"][:100] + if len(msg["text"]) > 100: + content += "..." + recent_messages.append(content) + + if recent_messages: + info_lines.append("šŸ“ Recent messages:") + info_lines.extend(recent_messages[-3:]) # Show only last 3 + + # File size info + file_size = state_file.stat().st_size + size_str = f"{file_size} bytes" + if file_size > 1024: + size_str = f"{file_size / 1024:.1f} KB" + if file_size > 1024 * 1024: + size_str = f"{file_size / (1024 * 1024):.1f} MB" + info_lines.append(f"šŸ“Š File size: {size_str}") + + return ( + "\n".join(info_lines) if info_lines else "No details available" + ) + else: + return "āŒ No state file found" def restore_agent_state(state_data: Dict[str, Any]) -> State: diff --git a/src/ii_agent/cli/subscribers/console_subscriber.py b/src/ii_agent/cli/subscribers/console_subscriber.py index 06ef742d..a5884147 100644 --- a/src/ii_agent/cli/subscribers/console_subscriber.py +++ b/src/ii_agent/cli/subscribers/console_subscriber.py @@ -4,10 +4,8 @@ This module provides real-time console output for agent events. """ -from typing import Optional, Dict, Any, Callable +from typing import Optional, Dict, Any, Callable, Tuple from threading import Lock -import uuid -import json import sys import termios import tty @@ -22,6 +20,7 @@ from rich.syntax import Syntax from rich.prompt import Prompt, Confirm +from ii_agent.cli.state_persistence import StateManager from ii_agent.core.event import RealtimeEvent, EventType from ii_agent.core.config.llm_config import LLMConfig from ii_agent.core.config.ii_agent_config import IIAgentConfig @@ -791,9 +790,10 @@ def cleanup(self) -> None: self._spinner = None def _interactive_session_selector( - self, sessions: list - ) -> tuple[Optional[str], bool]: + self, state_manager: StateManager + ) -> Tuple[Optional[str], bool]: """Interactive session selector with arrow keys - sessions always expand on select.""" + sessions = state_manager.get_available_sessions() if not sessions: return None, False @@ -817,10 +817,10 @@ def _interactive_session_selector( choice = Prompt.ask("Selection", default="") if choice.lower() == "new": - new_session_name = Prompt.ask( - "Enter new session name", default=f"session_{uuid.uuid4().hex[:8]}" + new_session_id = Prompt.ask( + "Enter new session name" ) - return new_session_name, False + return new_session_id, False elif choice.isdigit() and 1 <= int(choice) <= len(sessions): return sessions[int(choice) - 1], False elif choice in sessions: @@ -860,63 +860,10 @@ def fuzzy_search_sessions(query: str, sessions_list: list) -> list: return result if result else sessions_list - def get_session_content(session_name: str) -> str: + def get_session_content(session_id: str) -> str: """Get session content from agent_state.json.""" try: - sessions_dir = Path.home() / ".ii_agent" / "sessions" - state_file = sessions_dir / session_name / "agent_state.json" - if state_file.exists(): - with open(state_file, "r") as f: - data = json.load(f) - - # Extract meaningful information - info_lines = [] - - # User Message - if "last_user_prompt_index" in data: - last_user_prompt = data["message_lists"][ - int(data["last_user_prompt_index"]) - ][0]["text"] - if len(last_user_prompt) > 50: - last_user_prompt = "..." + last_user_prompt[-47:] - info_lines.append(f"User: {last_user_prompt}") - - # Message count - if "message_lists" in data and data["message_lists"]: - message_count = len(data["message_lists"]) - info_lines.append(f"šŸ’¬ Messages: {message_count}") - - # Get last few message summaries - recent_messages = [] - for msg_list in data["message_lists"][ - -2: - ]: # Last 2 message lists - messages = msg_list - for msg in messages[-3:]: # Last 3 messages from each list - if msg.get("text"): - content = msg["text"][:100] - if len(msg["text"]) > 100: - content += "..." - recent_messages.append(content) - - if recent_messages: - info_lines.append("šŸ“ Recent messages:") - info_lines.extend(recent_messages[-3:]) # Show only last 3 - - # File size info - file_size = state_file.stat().st_size - size_str = f"{file_size} bytes" - if file_size > 1024: - size_str = f"{file_size / 1024:.1f} KB" - if file_size > 1024 * 1024: - size_str = f"{file_size / (1024 * 1024):.1f} MB" - info_lines.append(f"šŸ“Š File size: {size_str}") - - return ( - "\n".join(info_lines) if info_lines else "No details available" - ) - else: - return "āŒ No state file found" + return state_manager.load_state_summary(session_id) except Exception as e: return f"āŒ Error reading state: {str(e)}" @@ -1013,14 +960,14 @@ def render_session_list(): if i == selected_index: # Selected session with enhanced styling status_icon = Text("ā–¶", style="bold bright_green") - session_name = Text( + session_id = Text( session, style="bold bright_green on bright_black" ) info_text = Text("← selected", style="dim green") else: # Non-selected sessions with subtle styling status_icon = Text("•", style="dim blue") - session_name = Text(session, style="bright_white") + session_id = Text(session, style="bright_white") # Add search query highlighting if in search mode if search_mode and search_query.strip(): # Highlight matching parts @@ -1037,7 +984,7 @@ def render_session_list(): session[start:end], style="bold yellow on bright_black" ) highlighted_name.append(session[end:], style="bright_white") - session_name = highlighted_name + session_id = highlighted_name # Add session metadata as info try: @@ -1055,7 +1002,7 @@ def render_session_list(): except Exception: info_text = Text("", style="dim") - session_table.add_row(status_icon, session_name, info_text) + session_table.add_row(status_icon, session_id, info_text) # Show expanded content for selected session if i == selected_index: @@ -1216,11 +1163,7 @@ def update_display(): self.console.print( "[bold cyan]šŸ“ Create New Session[/bold cyan]" ) - new_session_name = Prompt.ask( - "Enter session name", - default=f"session_{uuid.uuid4().hex[:8]}", - ) - return new_session_name, False + return None, False finally: # Always restore raw mode tty.setcbreak(sys.stdin.fileno()) @@ -1231,76 +1174,29 @@ def update_display(): # Restore original terminal settings termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_settings) - async def setup_session_config(self) -> SessionConfig: + async def select_session_config(self, state_manager: StateManager) -> SessionConfig: """Set up session configuration with interactive user selection.""" - # Create session config directory if it doesn't exist - sessions_dir = Path.home() / ".ii_agent" / "sessions" - sessions_dir.mkdir(parents=True, exist_ok=True) - - # Get available sessions - available_sessions = [] - for session_dir in sessions_dir.glob("*"): - if session_dir.is_dir(): - json_files = list(session_dir.glob("*.json")) - if json_files: - available_sessions.append(session_dir.name) - - available_sessions.sort() - - if available_sessions: - # Use interactive selector with arrow keys - selected_session, cancelled = self._interactive_session_selector( - available_sessions + session_id, cancelled = self._interactive_session_selector( + state_manager + ) + config = state_manager.get_state_config(session_id=session_id) + if cancelled: + # User cancelled, create new session + self.console.print( + f"[green]Creating new session: {config.session_id}[/green]" ) - - if cancelled: - # User cancelled, create new session - session_name = f"session_{uuid.uuid4().hex[:8]}" - session_id = str(uuid.uuid4()) - self.console.print( - f"[green]Creating new session: {session_name}[/green]" - ) - elif selected_session and selected_session in available_sessions: - # Load existing session - session_name = selected_session - session_id = str(uuid.uuid4()) # Generate new ID for continued session - self.console.print(f"[green]Loading session: {session_name}[/green]") - elif selected_session: - # Create new named session - session_name = selected_session - session_id = str(uuid.uuid4()) - self.console.print( - f"[green]Creating new session: {session_name}[/green]" - ) - else: - # Create new session with generated name - session_name = f"session_{uuid.uuid4().hex[:8]}" - session_id = str(uuid.uuid4()) - self.console.print( - f"[green]Creating new session: {session_name}[/green]" - ) + elif config.session_id and state_manager.is_valid_session(config.session_id): + # Load existing session + self.console.print(f"[green]Loading session: {config.session_id}[/green]") else: - # No existing sessions, create new one - session_name = f"session_{uuid.uuid4().hex[:8]}" - session_id = str(uuid.uuid4()) - self.console.print(f"[green]Creating new session: {session_name}[/green]") - - session_config = SessionConfig(session_id=session_id, session_name=session_name) - - # Ensure sessions directory exists - session_config.ensure_sessions_dir() - - return session_config + self.console.print( + f"[green]Creating new session: {config.session_id}[/green]" + ) + return config - async def should_continue_from_state(self, session_id: str, root_dir: str) -> bool: + async def should_continue_from_state(self) -> bool: """Ask user if they want to continue from previous state in current directory.""" - # Check if there's a saved state in current directory - workspace_state_file = ( - Path(root_dir) / "sessions" / session_id / "agent_state.json" + return Confirm.ask( + "\n[yellow]Found previous state in current directory. Continue from where you left off?[/yellow]", + default=True, ) - if workspace_state_file.exists(): - return Confirm.ask( - "\n[yellow]Found previous state in current directory. Continue from where you left off?[/yellow]", - default=True, - ) - return False diff --git a/src/ii_agent/runtime/base.py b/src/ii_agent/runtime/base.py index 2ea58627..8bd2155f 100644 --- a/src/ii_agent/runtime/base.py +++ b/src/ii_agent/runtime/base.py @@ -1,7 +1,6 @@ from __future__ import annotations from abc import ABC, abstractmethod from typing import TYPE_CHECKING -from uuid import UUID from fastmcp import Client @@ -18,12 +17,12 @@ class BaseRuntime(ABC): """ mode: RuntimeMode - session_id: UUID + session_id: str settings: Settings runtime_id: str | None = None host_url: str | None = None - def __init__(self, session_id: UUID, settings: Settings): + def __init__(self, session_id: str, settings: Settings): """ Initializes a runtime instance. """ diff --git a/src/ii_agent/runtime/docker.py b/src/ii_agent/runtime/docker.py index f6dd8faa..31388bb8 100644 --- a/src/ii_agent/runtime/docker.py +++ b/src/ii_agent/runtime/docker.py @@ -39,7 +39,7 @@ class DockerRuntime(BaseRuntime): def __init__( self, - session_id: uuid.UUID, + session_id: str, settings: Settings, ): """Initializes a docker runtime instance. @@ -297,7 +297,7 @@ async def _create_nginx_container(self): async def main(): settings = Settings() - runtime = DockerRuntime(uuid.uuid4(), settings) + runtime = DockerRuntime(uuid.uuid4().hex, settings) await runtime.start() # This will now ensure network and nginx are ready await runtime.create() print("Runtime created") diff --git a/src/ii_agent/runtime/e2b.py b/src/ii_agent/runtime/e2b.py index 99c6f522..116ab668 100644 --- a/src/ii_agent/runtime/e2b.py +++ b/src/ii_agent/runtime/e2b.py @@ -9,7 +9,7 @@ from ii_agent.runtime.runtime_registry import RuntimeRegistry from ii_agent.runtime.model.constants import RuntimeMode from ii_agent.db.manager import Sessions - +from uuid import UUID if TYPE_CHECKING: from ii_agent.core.storage.models.settings import Settings @@ -20,7 +20,7 @@ class E2BRuntime(BaseRuntime): mode: RuntimeMode = RuntimeMode.E2B - def __init__(self, session_id: uuid.UUID, settings: Settings): + def __init__(self, session_id: str, settings: Settings): super().__init__(session_id=session_id, settings=settings) async def create(self): @@ -36,7 +36,7 @@ async def create(self): raise ValueError("Sandbox ID is not set") self.runtime_id = str(self.sandbox.sandbox_id) - Sessions.update_session_runtime_id(self.session_id, self.runtime_id) + Sessions.update_session_runtime_id(UUID(self.session_id), self.runtime_id) def expose_port(self, port: int) -> str: return "https://" + self.sandbox.get_host(port) @@ -47,7 +47,7 @@ def get_mcp_client(self, workspace_dir: str) -> Client: return Client(self.host_url) async def connect(self): - runtime_id = Sessions.get_runtime_id_by_session_id(self.session_id) + runtime_id = Sessions.get_runtime_id_by_session_id(UUID(self.session_id)) if runtime_id is None: # Note: Raise error for now, should never happen raise ValueError(f"Runtime ID not found for session {self.session_id}") @@ -66,7 +66,7 @@ async def cleanup(self): pass async def start(self): - runtime_id = Sessions.get_runtime_id_by_session_id(self.session_id) + runtime_id = Sessions.get_runtime_id_by_session_id(UUID(self.session_id)) if runtime_id is None: # Note: Raise error for now, should never happen raise ValueError(f"Runtime ID not found for session {self.session_id}") diff --git a/src/ii_agent/runtime/local.py b/src/ii_agent/runtime/local.py index 791d80f5..eb42337d 100644 --- a/src/ii_agent/runtime/local.py +++ b/src/ii_agent/runtime/local.py @@ -1,7 +1,6 @@ from __future__ import annotations import asyncio import os -import uuid from typing import TYPE_CHECKING from fastmcp.client import Client @@ -18,7 +17,7 @@ class LocalRuntime(BaseRuntime): mode: RuntimeMode = RuntimeMode.LOCAL - def __init__(self, session_id: uuid.UUID, settings: Settings): + def __init__(self, session_id: str, settings: Settings): super().__init__(session_id=session_id, settings=settings) async def start(self): diff --git a/src/ii_agent/runtime/runtime_manager.py b/src/ii_agent/runtime/runtime_manager.py index 262a832c..8690d219 100644 --- a/src/ii_agent/runtime/runtime_manager.py +++ b/src/ii_agent/runtime/runtime_manager.py @@ -1,21 +1,20 @@ -import uuid - from fastmcp.client import Client +from ii_agent.cli.session_config import SessionConfig from ii_agent.core.storage.models.settings import Settings from ii_agent.runtime.model.exception import RuntimeUninitializedError from ii_agent.runtime.runtime_registry import RuntimeRegistry class RuntimeManager: - def __init__(self, session_id: uuid.UUID, settings: Settings): - self.session_id = session_id - self.workspace_mode = settings.runtime_config.mode + def __init__(self, session_config: SessionConfig, settings: Settings): + self.session_id = session_config.session_id + self.mode = session_config.mode self.settings = settings self.runtime = None async def start_runtime(self): self.runtime = RuntimeRegistry.create( - self.workspace_mode, self.session_id, self.settings + self.mode, self.session_id, self.settings ) await self.runtime.create() @@ -33,7 +32,7 @@ async def get_mcp_client(self, workspace_dir: str) -> Client: # WIP async def connect_runtime(self): self.runtime = RuntimeRegistry.create( - self.workspace_mode, self.session_id, self.settings + self.mode, self.session_id, self.settings ) await self.runtime.connect() diff --git a/src/ii_agent/runtime/runtime_registry.py b/src/ii_agent/runtime/runtime_registry.py index 42339b29..b8b83a54 100644 --- a/src/ii_agent/runtime/runtime_registry.py +++ b/src/ii_agent/runtime/runtime_registry.py @@ -1,6 +1,5 @@ from __future__ import annotations from typing import Dict, Type, TYPE_CHECKING -import uuid from ii_agent.runtime.base import BaseRuntime from ii_agent.runtime.model.constants import RuntimeMode @@ -27,7 +26,7 @@ def decorator(runtime_class: Type[BaseRuntime]): def create( cls, runtime_type: RuntimeMode, - session_id: uuid.UUID, + session_id: str, settings: Settings, ) -> BaseRuntime: """Create a runtime instance.""" From 0e90c824b6cf05869a23b4fd48866958cd8f2cc2 Mon Sep 17 00:00:00 2001 From: Khoa Ngo The Date: Thu, 24 Jul 2025 17:34:15 +0700 Subject: [PATCH 12/13] feat: interupt and session bug fix --- pyproject.toml | 2 +- src/ii_agent/cli/app.py | 24 ++++++++++++-- src/ii_agent/cli/state_persistence.py | 6 +--- .../cli/subscribers/console_subscriber.py | 25 ++++++++++----- src/ii_agent/runtime/docker.py | 10 +++--- src/ii_agent/runtime/utils/docker_utils.py | 2 +- .../tools/clients/web_search_client.py | 4 +-- uv.lock | 32 +++++++++---------- 8 files changed, 65 insertions(+), 40 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index cc7adff7..34b0a8ca 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,7 +10,6 @@ requires-python = ">=3.10" dependencies = [ "anthropic[vertex]>=0.50.0", "dataclasses-json>=0.6.7", - "duckduckgo-search>=8.0.1", "fastapi>=0.115.12", "google-cloud-aiplatform>=1.90.0", "google-genai>=1.14.0", @@ -47,6 +46,7 @@ dependencies = [ "libtmux>=0.46.2", "ipdb>=0.13.13", "docker>=7.1.0", + "ddgs>=9.4.3", ] [project.optional-dependencies] diff --git a/src/ii_agent/cli/app.py b/src/ii_agent/cli/app.py index 4863f105..7ecce686 100644 --- a/src/ii_agent/cli/app.py +++ b/src/ii_agent/cli/app.py @@ -7,6 +7,7 @@ import asyncio import json +import signal from pathlib import Path from typing import Optional, Dict, Any from uuid import UUID @@ -82,6 +83,9 @@ def __init__( # Store for pending tool confirmations self._tool_confirmations: Dict[str, Dict[str, Any]] = {} + # Setup signal handlers + self._setup_signal_handlers() + def _handle_tool_confirmation(self, tool_call_id: str, tool_name: str, approved: bool, alternative_instruction: str) -> None: """Handle tool confirmation response from console subscriber.""" # Store the confirmation response @@ -111,7 +115,7 @@ async def initialize_agent(self) -> None: saved_state_data = None # Initialize with session continuation check is_valid_session = self.state_manager.is_valid_session(self.session_config.session_id) - if is_valid_session and await self.console_subscriber.should_continue_from_state(): + if is_valid_session: saved_state_data = self.state_manager.load_state(self.session_config.session_id) if saved_state_data: self.console_subscriber.console.print( @@ -158,7 +162,7 @@ async def initialize_agent(self) -> None: runtime_manager = RuntimeManager( session_config=self.session_config, settings=self.settings ) - if not is_valid_session: + if not self.state_manager.is_valid_session(self.session_config.session_id): await runtime_manager.start_runtime() else: await runtime_manager.connect_runtime() @@ -236,7 +240,8 @@ async def run_interactive_mode(self) -> int: self.console_subscriber.print_welcome() self.console_subscriber.print_session_info( - self.session_config.session_name if self.session_config else None + self.session_config.session_id if self.session_config else None, + self.session_config.mode.value if self.session_config else None ) while True: @@ -373,6 +378,19 @@ def _save_state_on_exit(self, should_save: bool) -> None: f"āš ļø [yellow]Failed to save state: {e}[/yellow]" ) + def _setup_signal_handlers(self) -> None: + """Setup signal handlers for graceful shutdown.""" + def signal_handler(signum, frame): + signal_name = "SIGINT" if signum == signal.SIGINT else "SIGTSTP" + self.console_subscriber.console.print(f"\n\nšŸ›‘ [yellow]Received {signal_name}, saving state...[/yellow]") + self._save_state_on_exit(True) + self.console_subscriber.print_goodbye() + exit(0) + + signal.signal(signal.SIGINT, signal_handler) + # Note: SIGTSTP (Ctrl+Z) cannot be caught in the same way as it suspends the process + # We'll handle KeyboardInterrupt in the main loop instead + def cleanup(self) -> None: """Clean up resources.""" if self.event_stream: diff --git a/src/ii_agent/cli/state_persistence.py b/src/ii_agent/cli/state_persistence.py index 3f0311c7..0b372932 100644 --- a/src/ii_agent/cli/state_persistence.py +++ b/src/ii_agent/cli/state_persistence.py @@ -41,8 +41,7 @@ def get_metadata(self, session_id: str) -> Optional[Dict[str, Any]]: metadata = self.file_store.read(metadata_filename) metadata = json.loads(metadata) return metadata - except Exception as e: - logger.error(f"Error reading metadata: {e}") + except Exception: return None def get_state_config(self, session_id: Optional[str] = None) -> SessionConfig: @@ -67,7 +66,6 @@ def get_state(self, session_id: str) -> Optional[Dict[str, Any]]: state = json.loads(state) return state except Exception as e: - logger.error(f"Error reading state: {e}") return None def get_available_sessions(self) -> list[str]: @@ -85,7 +83,6 @@ def get_available_sessions(self) -> list[str]: return sorted(sessions) except Exception as e: - logger.error(f"Error getting available sessions: {e}") return [] def save_state( @@ -132,7 +129,6 @@ def save_state( logger.info(f"State saved for session {session_id}") except Exception as e: - logger.error(f"Error saving state: {e}") raise def load_state(self, session_id: str) -> Optional[Dict[str, Any]]: diff --git a/src/ii_agent/cli/subscribers/console_subscriber.py b/src/ii_agent/cli/subscribers/console_subscriber.py index a5884147..753f08d2 100644 --- a/src/ii_agent/cli/subscribers/console_subscriber.py +++ b/src/ii_agent/cli/subscribers/console_subscriber.py @@ -673,11 +673,18 @@ def print_goodbye(self) -> None: ) self.console.print(goodbye_panel) - def print_session_info(self, session_name: Optional[str] = None) -> None: + def print_session_info(self, session_id: Optional[str] = None, runtime_mode: Optional[str] = None) -> None: """Print session information.""" - if not self.minimal and session_name: + if not self.minimal and session_id: + # Get runtime mode icon + mode_icon = "🐳" if runtime_mode == "docker" else "šŸ’»" if runtime_mode == "local" else "ā˜ļø" if runtime_mode == "e2b" else "āš™ļø" + + session_info = f"šŸ“ [bold]Active Session[/bold]\n\nID: [cyan]{session_id}[/cyan]" + if runtime_mode: + session_info += f"\nRuntime: {mode_icon} [green]{runtime_mode.upper()}[/green]" + session_panel = Panel( - f"šŸ“ [bold]Active Session[/bold]\n\nName: [cyan]{session_name}[/cyan]", + session_info, title="Session Info", style="yellow" ) @@ -1187,11 +1194,13 @@ async def select_session_config(self, state_manager: StateManager) -> SessionCon ) elif config.session_id and state_manager.is_valid_session(config.session_id): # Load existing session - self.console.print(f"[green]Loading session: {config.session_id}[/green]") - else: - self.console.print( - f"[green]Creating new session: {config.session_id}[/green]" - ) + if await self.should_continue_from_state(): + self.console.print(f"[green]Loading session: {config.session_id}[/green]") + else: + config = state_manager.get_state_config() + self.console.print( + f"[green]Creating new session: {config.session_id}[/green]" + ) return config async def should_continue_from_state(self) -> bool: diff --git a/src/ii_agent/runtime/docker.py b/src/ii_agent/runtime/docker.py index 31388bb8..f71b6365 100644 --- a/src/ii_agent/runtime/docker.py +++ b/src/ii_agent/runtime/docker.py @@ -155,7 +155,7 @@ async def create(self): self.expose_port(self.settings.runtime_config.service_port) + "/mcp/" ) self.runtime_id = self.container_id - print(f"Container created: {self.container_id}") + #print(f"Container created: {self.container_id}") except Exception as e: await self.cleanup() # Ensure resources are cleaned up raise RuntimeError(f"Failed to create sandbox: {e}") from e @@ -222,7 +222,8 @@ async def _ensure_network_exists(self): ) print(f"Network '{self.config.network_name}' created successfully") else: - print(f"Network '{self.config.network_name}' already exists") + return + #print(f"Network '{self.config.network_name}' already exists") except Exception as e: raise RuntimeError(f"Failed to ensure network exists: {e}") @@ -242,7 +243,8 @@ async def _ensure_nginx_running(self): print("No running nginx containers found, creating one...") await self._create_nginx_container() else: - print(f"Found {len(containers)} running nginx container(s)") + return + #print(f"Found {len(containers)} running nginx container(s)") except Exception as e: raise RuntimeError(f"Failed to ensure nginx is running: {e}") @@ -287,7 +289,7 @@ async def _create_nginx_container(self): nginx_container_obj = self.client.containers.get(nginx_container["Id"]) nginx_container_obj.start() - print(f"Nginx container created and started: {nginx_container['Id']}") + #print(f"Nginx container created and started: {nginx_container['Id']}") except Exception as e: raise RuntimeError(f"Failed to create nginx container: {e}") diff --git a/src/ii_agent/runtime/utils/docker_utils.py b/src/ii_agent/runtime/utils/docker_utils.py index a678b10a..04b1b44e 100644 --- a/src/ii_agent/runtime/utils/docker_utils.py +++ b/src/ii_agent/runtime/utils/docker_utils.py @@ -42,7 +42,7 @@ def build_if_not_exists( client: docker.DockerClient, path: str, dockerfile: str, tag: str ): if image_exists(client, tag): - print(f"āœ“ Image {tag} already exists, skipping build") + #print(f"āœ“ Image {tag} already exists, skipping build") return client.images.get(tag) else: print(f"Building {tag}...") diff --git a/src/ii_agent/tools/clients/web_search_client.py b/src/ii_agent/tools/clients/web_search_client.py index 6af1af39..e0fde9cf 100644 --- a/src/ii_agent/tools/clients/web_search_client.py +++ b/src/ii_agent/tools/clients/web_search_client.py @@ -147,10 +147,10 @@ class DuckDuckGoSearchClient(BaseSearchClient): def __init__(self, max_results=10, **kwargs): self.max_results = max_results try: - from duckduckgo_search import DDGS + from ddgs import DDGS except ImportError as e: raise ImportError( - "You must install package `duckduckgo-search` to run this tool: for instance run `pip install duckduckgo-search`." + "You must install package `ddgs` to run this tool: for instance run `pip install ddgs`." ) from e self.ddgs = DDGS(**kwargs) diff --git a/uv.lock b/uv.lock index 46beea17..630a8c05 100644 --- a/uv.lock +++ b/uv.lock @@ -681,6 +681,20 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/eb/62/eb8157afb21bd229c864521c1ab4fa8e9b4f1b06bafdd8c4668a7a31b5dd/datasets-4.0.0-py3-none-any.whl", hash = "sha256:7ef95e62025fd122882dbce6cb904c8cd3fbc829de6669a5eb939c77d50e203d", size = 494825 }, ] +[[package]] +name = "ddgs" +version = "9.4.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "lxml" }, + { name = "primp" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a7/36/23cf38e968260fdcc0068306136ac4ba5a9ceb0b4cde2ce7d70b8b90091b/ddgs-9.4.3.tar.gz", hash = "sha256:476646042ae8002c06e52c5be578386efbe8af54d7e86c3eb063fa6110eb5aa8", size = 31227 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b1/56/4805b11d2d8ba44bf381bb0971fe14dc0d081e81702103fb87dcd211bc88/ddgs-9.4.3-py3-none-any.whl", hash = "sha256:bb929f5d891745f5113a3bba9a3b8ec2713ff67afb82d97dcffe4695f61c9cc9", size = 34976 }, +] + [[package]] name = "decorator" version = "5.2.1" @@ -767,20 +781,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/de/9c/0e31e9f650326bfbe483b613071ab9e1b1c2ac48d372e70b465e3241f3ed/docutils-0.22rc5-py3-none-any.whl", hash = "sha256:0285ee30d970430f141dc3d806e1c44decebdf6080364075d071c58c843aeaf5", size = 636361 }, ] -[[package]] -name = "duckduckgo-search" -version = "8.1.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "click" }, - { name = "lxml" }, - { name = "primp" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/10/ef/07791a05751e6cc9de1dd49fb12730259ee109b18e6d097e25e6c32d5617/duckduckgo_search-8.1.1.tar.gz", hash = "sha256:9da91c9eb26a17e016ea1da26235d40404b46b0565ea86d75a9f78cc9441f935", size = 22868 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/db/72/c027b3b488b1010cf71670032fcf7e681d44b81829d484bb04e31a949a8d/duckduckgo_search-8.1.1-py3-none-any.whl", hash = "sha256:f48adbb06626ee05918f7e0cef3a45639e9939805c4fc179e68c48a12f1b5062", size = 18932 }, -] - [[package]] name = "e2b" version = "1.2.0b5" @@ -1524,8 +1524,8 @@ dependencies = [ { name = "alembic" }, { name = "anthropic", extra = ["vertex"] }, { name = "dataclasses-json" }, + { name = "ddgs" }, { name = "docker" }, - { name = "duckduckgo-search" }, { name = "e2b-code-interpreter" }, { name = "fastapi" }, { name = "fastmcp" }, @@ -1585,8 +1585,8 @@ requires-dist = [ { name = "anthropic", extras = ["vertex"], specifier = ">=0.50.0" }, { name = "dataclasses-json", specifier = ">=0.6.7" }, { name = "datasets", marker = "extra == 'gaia'", specifier = ">=3.6.0" }, + { name = "ddgs", specifier = ">=9.4.3" }, { name = "docker", specifier = ">=7.1.0" }, - { name = "duckduckgo-search", specifier = ">=8.0.1" }, { name = "e2b-code-interpreter", specifier = "==1.2.0b5" }, { name = "fastapi", specifier = ">=0.115.12" }, { name = "fastmcp", specifier = ">=2.2.0" }, From 08be0ae3a1e2042120e5660beb6e3fe4dcebd7e0 Mon Sep 17 00:00:00 2001 From: Khoa Ngo The Date: Thu, 24 Jul 2025 18:06:35 +0700 Subject: [PATCH 13/13] fix: setting bug --- .../cli/subscribers/console_subscriber.py | 22 +++++-------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/src/ii_agent/cli/subscribers/console_subscriber.py b/src/ii_agent/cli/subscribers/console_subscriber.py index 753f08d2..c0e06e1a 100644 --- a/src/ii_agent/cli/subscribers/console_subscriber.py +++ b/src/ii_agent/cli/subscribers/console_subscriber.py @@ -933,9 +933,6 @@ def render_session_list(): instructions.append("• ", style="dim") instructions.append("Enter ", style="green") instructions.append("to select ", style="white") - instructions.append("• ", style="dim") - instructions.append("Esc ", style="red") - instructions.append("to exit search", style="white") else: instructions.append("Navigation: ", style="bold blue") instructions.append("↑/↓ arrows ", style="bright_white") @@ -1106,13 +1103,12 @@ def update_display(): return filtered_sessions[selected_index], False else: continue # No sessions to select - elif char == "s" or char == "S": # Search mode - if not search_mode: - search_mode = True - search_query = "" - filtered_sessions = sessions.copy() - selected_index = 0 - update_display() + elif (char == "s" or char == "S") and not search_mode: # Search mode + search_mode = True + search_query = "" + filtered_sessions = sessions.copy() + selected_index = 0 + update_display() elif char == "\x1b": # Escape sequence (arrow keys or ESC) # Read the next two characters next_chars = sys.stdin.read(2) @@ -1128,12 +1124,6 @@ def update_display(): filtered_sessions ) update_display() - elif search_mode and not next_chars: # ESC key (exit search) - search_mode = False - search_query = "" - filtered_sessions = sessions.copy() - selected_index = 0 - update_display() elif search_mode: # Handle search input if char == "\x7f": # Backspace