diff --git a/.cursor/rules/python.mdc b/.cursor/rules/python.mdc index 7c340ee3..28bf15c1 100644 --- a/.cursor/rules/python.mdc +++ b/.cursor/rules/python.mdc @@ -14,4 +14,12 @@ docstrings should follow the google style guides for docstrings. integration tests use @pytest.mark.integration -@pytest.mark.asyncio is not needed (its automatic) \ No newline at end of file +@pytest.mark.asyncio is not needed (its automatic) + +--- +description: when running python or tests +globs: +alwaysApply: true +--- + +we use uv on this project, no python -m non-sense please. If you get in trouble with deps just stop and ask, better to have the human resolve things (no sudo brew kind of stuff please) diff --git a/agents-core/pyproject.toml b/agents-core/pyproject.toml index 2113e1a4..b817ceb2 100644 --- a/agents-core/pyproject.toml +++ b/agents-core/pyproject.toml @@ -21,7 +21,7 @@ classifiers = [ requires-python = ">=3.10" dependencies = [ - "getstream[webrtc,telemetry]>=2.5.3", + "getstream[webrtc,telemetry]>=2.5.5", "python-dotenv>=1.1.1", "pillow>=11.3.0", "numpy>=1.24.0", diff --git a/agents-core/vision_agents/core/agents/agents.py b/agents-core/vision_agents/core/agents/agents.py index bdfba57e..ef341234 100644 --- a/agents-core/vision_agents/core/agents/agents.py +++ b/agents-core/vision_agents/core/agents/agents.py @@ -1,6 +1,7 @@ import asyncio import logging import time +import uuid from typing import TYPE_CHECKING, Any, Dict, List, Optional from uuid import uuid4 @@ -16,25 +17,26 @@ from ..edge.events import AudioReceivedEvent, TrackAddedEvent, CallEndedEvent from ..edge.types import Connection, Participant, PcmData, User from ..events.manager import EventManager +from ..llm import events as llm_events from ..llm.events import ( LLMResponseChunkEvent, LLMResponseCompletedEvent, - RealtimePartialTranscriptEvent, - RealtimeTranscriptEvent, + RealtimeUserSpeechTranscriptionEvent, + RealtimeAgentSpeechTranscriptionEvent, ) from ..llm.llm import LLM from ..llm.realtime import Realtime from ..logging_utils import CallContextToken, clear_call_context, set_call_context from ..mcp import MCPBaseServer, MCPManager from ..processors.base_processor import Processor, ProcessorType, filter_processors -from ..stt.events import STTPartialTranscriptEvent, STTTranscriptEvent +from ..stt.events import STTTranscriptEvent from ..stt.stt import STT from ..tts.tts import TTS from ..turn_detection import TurnDetector, TurnStartedEvent, TurnEndedEvent from ..vad import VAD from ..vad.events import VADAudioEvent from . import events -from .conversation import Conversation, Message, StreamHandle +from .conversation import Conversation if TYPE_CHECKING: from vision_agents.plugins.getstream.stream_edge_transport import StreamEdge @@ -50,6 +52,7 @@ def _log_task_exception(task: asyncio.Task): except Exception: logger.exception("Error in background task") + class Agent: """ Agent class makes it easy to build your own video AI. @@ -113,6 +116,7 @@ def __init__( self.events.register_events_from_module(getstream.models, "call.") self.events.register_events_from_module(events) self.events.register_events_from_module(sfu_events) + self.events.register_events_from_module(llm_events) self.llm = llm self.stt = stt @@ -124,15 +128,17 @@ def __init__( self._call_context_token: CallContextToken | None = None # Initialize MCP manager if servers are provided - self.mcp_manager = MCPManager(self.mcp_servers, self.llm, self.logger) if self.mcp_servers else None + self.mcp_manager = ( + MCPManager(self.mcp_servers, self.llm, self.logger) + if self.mcp_servers + else None + ) # we sync the user talking and the agent responses to the conversation # because we want to support streaming responses and can have delta updates for both - # user and agent we keep an handle for both + # user and agent self.conversation: Optional[Conversation] = None - self._user_conversation_handle: Optional[StreamHandle] = None - self._agent_conversation_handle: Optional[StreamHandle] = None - + # Track pending transcripts for turn-based response triggering self._pending_user_transcripts: Dict[str, str] = {} @@ -143,8 +149,6 @@ def __init__( self.events.merge(plugin.events) self.llm._attach_agent(self) - self.llm.events.subscribe(self._handle_after_response) - self.llm.events.subscribe(self._handle_output_text_delta) self.events.subscribe(self._on_vad_audio) self.events.subscribe(self._on_agent_say) @@ -153,7 +157,7 @@ def __init__( self._current_frame = None self._interval_task = None self._callback_executed = False - self._track_tasks : Dict[str, asyncio.Task] = {} + self._track_tasks: Dict[str, asyncio.Task] = {} self._connection: Optional[Connection] = None self._audio_track: Optional[aiortc.AudioStreamTrack] = None self._video_track: Optional[VideoStreamTrack] = None @@ -194,6 +198,135 @@ def subscribe(self, function): """ return self.events.subscribe(function) + async def _setup_llm_events(self): + @self.llm.events.subscribe + async def on_llm_response_send_to_tts(event: LLMResponseCompletedEvent): + # Trigger TTS directly instead of through event system + if self.tts and event.text and event.text.strip(): + await self.tts.send(event.text) + + @self.llm.events.subscribe + async def on_llm_response_sync_conversation(event: LLMResponseCompletedEvent): + self.logger.info(f"🀖 [LLM response]: {event.text} {event.item_id}") + + if self.conversation is None: + return + + # Unified API: handles both streaming and non-streaming + await self.conversation.upsert_message( + message_id=event.item_id, + role="assistant", + user_id=self.agent_user.id or "agent", + content=event.text or "", + completed=True, + replace=True, # Replace any partial content from deltas + ) + + @self.llm.events.subscribe + async def _handle_output_text_delta(event: LLMResponseChunkEvent): + """Handle partial LLM response text deltas.""" + + self.logger.info( + f"🀖 [LLM delta response]: {event.delta} {event.item_id} {event.content_index}" + ) + + if self.conversation is None: + return + + # Unified API: streaming delta + await self.conversation.upsert_message( + message_id=event.item_id, + role="assistant", + user_id=self.agent_user.id or "agent", + content=event.delta or "", + content_index=event.content_index, + completed=False, # Still streaming + ) + + async def _setup_speech_events(self): + @self.events.subscribe + async def on_stt_transcript_event_sync_conversation(event: STTTranscriptEvent): + self.logger.info(f"🎀 [Transcript]: {event.text}") + + if self.conversation is None: + return + + user_id = event.user_id() or "user" + + await self.conversation.upsert_message( + message_id=str(uuid.uuid4()), + role="user", + user_id=user_id, + content=event.text or "", + completed=True, + replace=True, # Replace any partial transcripts + original=event, + ) + + @self.events.subscribe + async def on_realtime_user_speech_transcription( + event: RealtimeUserSpeechTranscriptionEvent, + ): + self.logger.info(f"🎀 [User transcript]: {event.text}") + + if self.conversation is None or not event.text: + return + + await self.conversation.upsert_message( + message_id=str(uuid.uuid4()), + role="user", + user_id=event.user_id() or "", + content=event.text, + completed=True, + replace=True, + original=event, + ) + + @self.events.subscribe + async def on_realtime_agent_speech_transcription( + event: RealtimeAgentSpeechTranscriptionEvent, + ): + self.logger.info(f"🎀 [Agent transcript]: {event.text}") + + if self.conversation is None or not event.text: + return + + await self.conversation.upsert_message( + message_id=str(uuid.uuid4()), + role="assistant", + user_id=self.agent_user.id or "", + content=event.text, + completed=True, + replace=True, + original=event, + ) + + @self.events.subscribe + async def on_stt_transcript_event_create_response(event: STTTranscriptEvent): + if self.realtime_mode or not self.llm: + # when running in realtime mode, there is no need to send the response to the LLM + return + + user_id = event.user_id() or "user" + + # Determine how to handle LLM triggering based on turn detection + if self.turn_detection is not None: + # With turn detection: accumulate transcripts and wait for TurnEndedEvent + # Store/append the transcript for this user + if user_id not in self._pending_user_transcripts: + self._pending_user_transcripts[user_id] = event.text + else: + # Append to existing transcript (user might be speaking in chunks) + self._pending_user_transcripts[user_id] += " " + event.text + + self.logger.debug( + f"📝 Accumulated transcript for {user_id} (waiting for turn end): " + f"{self._pending_user_transcripts[user_id][:100]}..." + ) + else: + # Without turn detection: trigger LLM immediately on transcript completion + # This is the traditional STT -> LLM flow + await self.simple_response(event.text, event.user_metadata) async def join(self, call: Call) -> "AgentSessionContextManager": # TODO: validation. join can only be called once @@ -207,19 +340,20 @@ async def join(self, call: Call) -> "AgentSessionContextManager": # Ensure all subsequent logs include the call context. self._set_call_logging_context(call.id) + # Setup chat and connect it to transcript events (we'll wait at the end) + create_conversation_coro = self.edge.create_conversation( + call, self.agent_user, self.instructions + ) + + await self._setup_llm_events() + await self._setup_speech_events() + try: # Connect to MCP servers if manager is available if self.mcp_manager: with self.tracer.start_as_current_span("mcp_manager.connect_all"): await self.mcp_manager.connect_all() - # Setup chat and connect it to transcript events - self.conversation = await self.edge.create_conversation( - call, self.agent_user, self.instructions - ) - self.events.subscribe(self._on_transcript) - self.events.subscribe(self._on_partial_transcript) - # Ensure Realtime providers are ready before proceeding (they manage their own connection) self.logger.info(f"🀖 Agent joining call: {call.id}") if isinstance(self.llm, Realtime): @@ -266,6 +400,8 @@ async def join(self, call: Call) -> "AgentSessionContextManager": from .agent_session import AgentSessionContextManager + # wait for conversation creation coro at the very end of the join flow + self.conversation = await create_conversation_coro return AgentSessionContextManager(self, self._connection) async def finish(self): @@ -301,8 +437,6 @@ async def close(self): hooks (e.g., WebRTC connections, plugin services). """ self._is_running = False - self._user_conversation_handle = None - self._agent_conversation_handle = None self.clear_call_logging_context() # Disconnect from MCP servers @@ -311,9 +445,9 @@ async def close(self): for processor in self.processors: processor.close() - + # Stop all video forwarders - if hasattr(self, '_video_forwarders'): + if hasattr(self, "_video_forwarders"): for forwarder in self._video_forwarders: try: await forwarder.stop() @@ -393,49 +527,6 @@ async def create_user(self): self.agent_user.id = str(uuid4()) return await self.edge.create_user(self.agent_user) - async def _handle_output_text_delta(self, event: LLMResponseChunkEvent): - """Handle partial LLM response text deltas.""" - - if self.conversation is None: - return - - self.logger.info( - f"received standardized.output_text.delta {self._truncate_for_logging(event)}" - ) - # Create a new streaming message if we don't have one - if self._agent_conversation_handle is None: - self._agent_conversation_handle = self.conversation.start_streaming_message( - role="assistant", - user_id=self.agent_user.id, - initial_content=event.delta or "", - ) - else: - self.conversation.append_to_message( - self._agent_conversation_handle, event.delta or "" - ) - - async def _handle_after_response(self, event: LLMResponseCompletedEvent): - if self.conversation is None: - return - - if event.text is None or not event.text.strip(): - return - - if self._agent_conversation_handle is None: - message = Message( - content=event.text, - role="assistant", - user_id=self.agent_user.id, - ) - self.conversation.add_message(message) - else: - self.conversation.complete_message(self._agent_conversation_handle) - self._agent_conversation_handle = None - - # Trigger TTS directly instead of through event system - if self.tts and event.text and event.text.strip(): - await self.tts.send(event.text) - def _on_vad_audio(self, event: VADAudioEvent): self.logger.info(f"Vad audio event {self._truncate_for_logging(event)}") @@ -499,23 +590,38 @@ async def _on_agent_say(self, event: events.AgentSayEvent): ) self.logger.error(f"Error in agent say: {e}") - async def say(self, text: str, user_id: Optional[str] = None, metadata: Optional[Dict[str, Any]] = None): + async def say( + self, + text: str, + user_id: Optional[str] = None, + metadata: Optional[Dict[str, Any]] = None, + ): """ Make the agent say something using TTS. - + This is a convenience method that sends an AgentSayEvent to trigger TTS synthesis. - + Args: text: The text for the agent to say user_id: Optional user ID for the speech metadata: Optional metadata to include with the speech """ - self.events.send(events.AgentSayEvent( - plugin_name="agent", - text=text, - user_id=user_id or self.agent_user.id, - metadata=metadata - )) + self.events.send( + events.AgentSayEvent( + plugin_name="agent", + text=text, + user_id=user_id or self.agent_user.id, + metadata=metadata, + ) + ) + # Unified API: simple non-streaming message + if self.conversation is not None: + await self.conversation.upsert_message( + role="assistant", + user_id=user_id or self.agent_user.id or "agent", + content=text, + completed=True, + ) def _setup_turn_detection(self): if self.turn_detection: @@ -571,12 +677,13 @@ async def _reply_to_audio( continue await processor.process_audio(audio_bytes, participant.user_id) - # when in Realtime mode call the Realtime directly (non-blocking) if self.realtime_mode and isinstance(self.llm, Realtime): # TODO: this behaviour should be easy to change in the agent class - asyncio.create_task(self.llm.simple_audio_response(pcm_data)) - #task.add_done_callback(lambda t: print(f"Task (send_audio_pcm) error: {t.exception()}")) + asyncio.create_task( + self.llm.simple_audio_response(pcm_data, participant) + ) + # task.add_done_callback(lambda t: print(f"Task (send_audio_pcm) error: {t.exception()}")) # Process audio through STT elif self.stt: self.logger.debug(f"🎵 Processing audio from {participant}") @@ -591,14 +698,12 @@ async def _process_track(self, track_id: str, track_type: int, participant): # subscribe to the video track track = self.edge.add_track_subscriber(track_id) if not track: - self.logger.error( - f"Failed to subscribe to {track_id}" - ) + self.logger.error(f"Failed to subscribe to {track_id}") return # Import VideoForwarder from ..utils.video_forwarder import VideoForwarder - + # Create a SHARED VideoForwarder for the RAW incoming track # This prevents multiple recv() calls competing on the same track raw_forwarder = VideoForwarder( @@ -609,9 +714,9 @@ async def _process_track(self, track_id: str, track_type: int, participant): ) await raw_forwarder.start() self.logger.info("🎥 Created raw VideoForwarder for track %s", track_id) - + # Track forwarders for cleanup - if not hasattr(self, '_video_forwarders'): + if not hasattr(self, "_video_forwarders"): self._video_forwarders = [] self._video_forwarders.append(raw_forwarder) @@ -620,7 +725,9 @@ async def _process_track(self, track_id: str, track_type: int, participant): if self._video_track: # We have a video publisher (e.g., YOLO processor) # Create a separate forwarder for the PROCESSED video track - self.logger.info("🎥 Forwarding PROCESSED video frames to Realtime provider") + self.logger.info( + "🎥 Forwarding PROCESSED video frames to Realtime provider" + ) processed_forwarder = VideoForwarder( self._video_track, # type: ignore[arg-type] max_buffer=30, @@ -629,23 +736,28 @@ async def _process_track(self, track_id: str, track_type: int, participant): ) await processed_forwarder.start() self._video_forwarders.append(processed_forwarder) - + if isinstance(self.llm, Realtime): # Send PROCESSED frames with the processed forwarder - await self.llm._watch_video_track(self._video_track, shared_forwarder=processed_forwarder) + await self.llm._watch_video_track( + self._video_track, shared_forwarder=processed_forwarder + ) else: # No video publisher, send raw frames self.logger.info("🎥 Forwarding RAW video frames to Realtime provider") if isinstance(self.llm, Realtime): - await self.llm._watch_video_track(track, shared_forwarder=raw_forwarder) - + await self.llm._watch_video_track( + track, shared_forwarder=raw_forwarder + ) hasImageProcessers = len(self.image_processors) > 0 # video processors - pass the raw forwarder (they process incoming frames) for processor in self.video_processors: try: - await processor.process_video(track, participant.user_id, shared_forwarder=raw_forwarder) + await processor.process_video( + track, participant.user_id, shared_forwarder=raw_forwarder + ) except Exception as e: self.logger.error( f"Error in video processor {type(processor).__name__}: {e}" @@ -654,13 +766,15 @@ async def _process_track(self, track_id: str, track_type: int, participant): # Use raw forwarder for image processors - only if there are image processors if not hasImageProcessers: # No image processors, just keep the connection alive - self.logger.info("No image processors, video processing handled by video processors only") + self.logger.info( + "No image processors, video processing handled by video processors only" + ) return - + # Initialize error tracking counters timeout_errors = 0 consecutive_errors = 0 - + while True: try: # Use the raw forwarder instead of competing for track.recv() @@ -672,7 +786,6 @@ async def _process_track(self, track_id: str, track_type: int, participant): consecutive_errors = 0 if hasImageProcessers: - img = video_frame.to_image() for processor in self.image_processors: @@ -683,7 +796,6 @@ async def _process_track(self, track_id: str, track_type: int, participant): f"Error in image processor {type(processor).__name__}: {e}" ) - else: self.logger.warning("🎥VDP: Received empty frame") consecutive_errors += 1 @@ -697,15 +809,12 @@ async def _process_track(self, track_id: str, track_type: int, participant): ) await asyncio.sleep(backoff_delay) - # Cleanup and logging - self.logger.info(f"🎥VDP: Video processing loop ended for track {track_id} - timeouts: {timeout_errors}, consecutive_errors: {consecutive_errors}") - async def _on_turn_event(self, event: TurnStartedEvent | TurnEndedEvent) -> None: """Handle turn detection events.""" # In realtime mode, the LLM handles turn detection, interruption, and responses itself if self.realtime_mode: return - + if isinstance(event, TurnStartedEvent): # Interrupt TTS when user starts speaking (barge-in) if event.speaker_id and event.speaker_id != self.agent_user.id: @@ -730,121 +839,35 @@ async def _on_turn_event(self, event: TurnStartedEvent | TurnEndedEvent) -> None self.logger.info( f"👉 Turn ended - participant {event.speaker_id} finished (duration: {event.duration}, confidence: {event.confidence})" ) - + # When turn detection is enabled, trigger LLM response when user's turn ends # This is the signal that the user has finished speaking and expects a response if event.speaker_id and event.speaker_id != self.agent_user.id: # Get the accumulated transcript for this speaker transcript = self._pending_user_transcripts.get(event.speaker_id, "") - + if transcript and transcript.strip(): - self.logger.info(f"🀖 Triggering LLM response after turn ended for {event.speaker_id}") - + self.logger.info( + f"🀖 Triggering LLM response after turn ended for {event.speaker_id}" + ) + # Create participant object if we have metadata participant = None - if hasattr(event, 'custom') and event.custom: + if hasattr(event, "custom") and event.custom: # Try to extract participant info from custom metadata - participant = event.custom.get('participant') - + participant = event.custom.get("participant") + # Trigger LLM response with the complete transcript if self.llm: await self.simple_response(transcript, participant) - + # Clear the pending transcript for this speaker self._pending_user_transcripts[event.speaker_id] = "" - async def _on_partial_transcript( - self, event: STTPartialTranscriptEvent | RealtimePartialTranscriptEvent - ): - self.logger.info(f"🎀 [Partial transcript]: {event.text}") - - if event.text and event.text.strip() and self.conversation: - user_id = "unknown" - if hasattr(event, "user_metadata"): - user_id = getattr(event.user_metadata, "user_id", "unknown") - - # Check if we have an active handle for this user - if self._user_conversation_handle is None: - # Start a new streaming message for this user - self._user_conversation_handle = ( - self.conversation.start_streaming_message( - role="user", user_id=user_id - ) - ) - - # Append the partial transcript to the active message - self.conversation.append_to_message( - self._user_conversation_handle, event.text - ) - - async def _on_transcript(self, event: STTTranscriptEvent | RealtimeTranscriptEvent): - self.logger.info(f"🎀 [STT transcript]: {event.text}") - - if not event.text: - return - - if self.conversation is None: - return - - user_id = "unknown" - if hasattr(event, "user_metadata"): - user_id = getattr(event.user_metadata, "user_id", "unknown") - - if self._user_conversation_handle is None: - # No partial transcripts were received, create a completed message directly - message = Message( - original=event, - content=event.text, - role="user", - user_id=user_id, - ) - self.conversation.add_message(message) - else: - # Replace with final text and complete the message - self.conversation.replace_message( - self._user_conversation_handle, event.text - ) - self.conversation.complete_message(self._user_conversation_handle) - self._user_conversation_handle = None - - # In realtime mode, the LLM handles everything itself (STT, turn detection, responses) - # Skip our manual LLM triggering logic - if self.realtime_mode: - return - - # Determine how to handle LLM triggering based on turn detection - if self.turn_detection is not None: - # With turn detection: accumulate transcripts and wait for TurnEndedEvent - # Store/append the transcript for this user - if user_id not in self._pending_user_transcripts: - self._pending_user_transcripts[user_id] = event.text - else: - # Append to existing transcript (user might be speaking in chunks) - self._pending_user_transcripts[user_id] += " " + event.text - - self.logger.debug( - f"📝 Accumulated transcript for {user_id} (waiting for turn end): " - f"{self._pending_user_transcripts[user_id][:100]}..." - ) - else: - # Without turn detection: trigger LLM immediately on transcript completion - # This is the traditional STT -> LLM flow - if self.llm: - self.logger.info("🀖 Triggering LLM response immediately (no turn detection)") - - # Get participant from event metadata - participant = None - if hasattr(event, "user_metadata"): - participant = event.user_metadata - - await self.simple_response(event.text, participant) - async def _on_stt_error(self, error): """Handle STT service errors.""" self.logger.error(f"❌ STT Error: {error}") - - @property def realtime_mode(self) -> bool: """Check if the agent is in Realtime mode. @@ -869,8 +892,7 @@ def publish_audio(self) -> bool: @property def publish_video(self) -> bool: - """Whether the agent should publish an outbound video track. - """ + """Whether the agent should publish an outbound video track.""" return len(self.video_publishers) > 0 def _needs_audio_or_video_input(self) -> bool: @@ -1000,7 +1022,9 @@ def _prepare_rtc(self): else: framerate = 48000 stereo = True # Default to stereo for WebRTC - self._audio_track = self.edge.create_audio_track(framerate=framerate, stereo=stereo) + self._audio_track = self.edge.create_audio_track( + framerate=framerate, stereo=stereo + ) if self.tts: self.tts.set_output_track(self._audio_track) @@ -1012,7 +1036,6 @@ def _prepare_rtc(self): self._video_track = video_publisher.publish_video_track() self.logger.info("🎥 Video track initialized from video publisher") - def _truncate_for_logging(self, obj, max_length=200): """Truncate object string representation for logging to prevent spam.""" obj_str = str(obj) diff --git a/agents-core/vision_agents/core/agents/conversation.py b/agents-core/vision_agents/core/agents/conversation.py index 3bf3460f..1c93a570 100644 --- a/agents-core/vision_agents/core/agents/conversation.py +++ b/agents-core/vision_agents/core/agents/conversation.py @@ -1,26 +1,17 @@ +import asyncio import datetime import logging import uuid from abc import ABC, abstractmethod -from typing import Optional, List, Any +from typing import Optional, List, Any, Dict from dataclasses import dataclass - logger = logging.getLogger(__name__) + @dataclass class Message: - """A single utterance or assistant message within a conversation. - - Attributes: - content: Text content of the message. - original: Optional provider-native object for this message. - timestamp: Time the message was created (auto-filled on init). - role: Role of the sender (e.g., "user", "assistant"). - user_id: Logical user identifier associated with the message. - id: Unique message identifier (auto-generated if not provided). - """ content: str original: Optional[Any] = None # the original openai, claude or gemini message timestamp: Optional[datetime.datetime] = None @@ -33,237 +24,214 @@ def __post_init__(self): self.timestamp = datetime.datetime.now() -@dataclass -class StreamHandle: - """Handle for managing a streaming message. - - This lightweight object is returned when starting a streaming message - and must be passed to subsequent update operations. It encapsulates - the message ID and user ID, preventing accidental cross-contamination - between concurrent streams. - - Example: - # Start a streaming message - handle = conversation.start_streaming_message(role="assistant") - - # Update the message using the handle - conversation.append_to_message(handle, "Hello") - conversation.append_to_message(handle, " world!") - - # Complete the message - conversation.complete_message(handle) - """ - message_id: str - user_id: str +class ContentBuffer: + """Manages out-of-order fragment buffering for streaming messages.""" + + def __init__(self): + self.fragments: Dict[int, str] = {} + self.last_index = -1 + self.accumulated = "" + + def add_fragment(self, index: int, text: str): + """Add a fragment and apply all sequential pending fragments.""" + self.fragments[index] = text + self._apply_pending() + + def _apply_pending(self): + """Apply all sequential fragments starting from last_index + 1.""" + while (self.last_index + 1) in self.fragments: + self.accumulated += self.fragments.pop(self.last_index + 1) + self.last_index += 1 + + def get_accumulated(self) -> str: + return self.accumulated + + def clear(self): + self.fragments.clear() + self.last_index = -1 + self.accumulated = "" + + +class MessageState: + """Internal state for tracking a message's lifecycle.""" + + def __init__(self, message_id: str): + self.message_id = message_id + self.buffer = ContentBuffer() + self.created_in_backend = False # Has message been sent to Stream/DB? + self.backend_message_ids: List[ + str + ] = [] # For chunking: multiple backend IDs per internal ID class Conversation(ABC): + """Base conversation class with unified message API.""" + def __init__( self, instructions: str, messages: List[Message], ): - """Create a conversation container. - - Args: - instructions: System instructions that guide the assistant. - messages: Initial message history. - """ self.instructions = instructions self.messages = [m for m in messages] + self._message_states: Dict[str, MessageState] = {} + self._lock = asyncio.Lock() # One lock to rule them all + + async def send_message( + self, + role: str, + user_id: str, + content: str, + message_id: Optional[str] = None, + original: Any = None, + ) -> Message: + """Send a simple, complete message (non-streaming). + + This is a convenience method for the common case of sending a complete message. + For streaming messages, use upsert_message() directly. - @abstractmethod - def add_message(self, message: Message, completed: bool = True): - """Add a message to the conversation. - - Args: - message: The Message object to add - completed: If True, mark the message as completed (not generating). - If False, mark as still generating. Defaults to True. - - Returns: - The result of the add operation (implementation-specific) - """ - ... - - @abstractmethod - def update_message(self, message_id: str, input_text: str, user_id: str, replace_content: bool, completed: bool): - """Update an existing message or create a new one if not found. - - Args: - message_id: The ID of the message to update - input_text: The text content to set or append - user_id: The ID of the user who owns the message - replace_content: If True, replace the entire message content. If False, append to existing content. - completed: If True, mark the message as completed (not generating). If False, mark as still generating. - - Returns: - The result of the update operation (implementation-specific) - """ - ... - - # Streaming message convenience methods - def start_streaming_message(self, role: str = "assistant", user_id: Optional[str] = None, - initial_content: str = "") -> StreamHandle: - """Start a new streaming message and return a handle for subsequent operations. - - This method simplifies the management of streaming messages by returning a handle - that encapsulates the message ID and user ID. Use the handle with append_to_message, - replace_message, and complete_message methods. - - Args: - role: The role of the message sender (default: "assistant") - user_id: The ID of the user (default: same as role) - initial_content: Initial content for the message (default: empty string) - - Returns: - StreamHandle: A handle to use for subsequent operations on this message - - Example: - # Simple usage - handle = conversation.start_streaming_message() - conversation.append_to_message(handle, "Processing...") - conversation.replace_message(handle, "Here's the answer: ") - conversation.append_to_message(handle, "42") - conversation.complete_message(handle) - - # Multiple concurrent streams - user_handle = conversation.start_streaming_message(role="user", user_id="user123") - assistant_handle = conversation.start_streaming_message(role="assistant") - - # Update both independently - conversation.append_to_message(user_handle, "Hello") - conversation.append_to_message(assistant_handle, "Hi there!") - - # Complete in any order - conversation.complete_message(user_handle) - conversation.complete_message(assistant_handle) - """ - message = Message( - original=None, - content=initial_content, - role=role, - user_id=user_id or role, - id=None # Will be assigned during add - ) - self.add_message(message, completed=False) - # The message now has an ID assigned by the add_message flow - # Find it in the messages list (it's the last one added) - added_message = self.messages[-1] - # Message IDs and user_ids are always set by add_message - assert added_message.id is not None, "Message ID should be set by add_message" - assert added_message.user_id is not None, "User ID should be set by add_message" - return StreamHandle(message_id=added_message.id, user_id=added_message.user_id) - - def append_to_message(self, handle: StreamHandle, text: str): - """Append text to a streaming message identified by the handle. - - Args: - handle: The StreamHandle returned by start_streaming_message - text: Text to append to the message - """ - self.update_message( - message_id=handle.message_id, - input_text=text, - user_id=handle.user_id, - replace_content=False, - completed=False - ) - - def replace_message(self, handle: StreamHandle, text: str): - """Replace the content of a streaming message identified by the handle. - - Args: - handle: The StreamHandle returned by start_streaming_message - text: Text to replace the message content with - """ - self.update_message( - message_id=handle.message_id, - input_text=text, - user_id=handle.user_id, - replace_content=True, - completed=False - ) - - def complete_message(self, handle: StreamHandle): - """Mark a streaming message as completed. - Args: - handle: The StreamHandle returned by start_streaming_message - """ - # We need to find the message to get its current content - # so we can set completed without changing the content - message = next((msg for msg in self.messages if msg.id == handle.message_id), None) - if message: - # Use replace mode with the current content to avoid space issues - self.update_message( - message_id=handle.message_id, - input_text=message.content, - user_id=handle.user_id, - replace_content=True, - completed=True - ) + role: Message role (user/assistant/system) + user_id: User ID + content: Complete text content + message_id: Optional ID. If None, auto-generates. + original: Original event/object for metadata + Returns: + The Message object -class InMemoryConversation(Conversation): - messages: List[Message] + Examples: + # User message + await conv.send_message("user", "user123", "What's the weather?") - def __init__(self, instructions: str, messages: List[Message]): - """Create an in-memory conversation holder. + # Assistant response + await conv.send_message("assistant", "agent", "It's sunny!") - Stores messages in a local list and performs updates in place. Useful for - tests and local development, or as a base for provider-backed - conversations. + # System message + await conv.send_message("system", "system", "User joined") """ - super().__init__(instructions, messages) + return await self.upsert_message( + role=role, + user_id=user_id, + content=content, + message_id=message_id, + completed=True, + original=original, + ) - def lookup(self, id: str) -> Optional[Message]: - """Find a message by ID. Needed by StreamConversation + async def upsert_message( + self, + role: str, + user_id: str, + content: str = "", + message_id: Optional[str] = None, + content_index: Optional[int] = None, + completed: bool = True, + replace: bool = False, + original: Any = None, + ) -> Message: + """Add or update a message. Handles streaming, non-streaming, everything. Args: - id: Message identifier to lookup. + role: Message role (user/assistant/system) + user_id: User ID + content: Text content (can be partial or complete) + message_id: Optional ID. If None, auto-generates. If provided, updates existing. + content_index: For streaming deltas. If provided, buffers out-of-order fragments. + completed: If True, finalizes the message. If False, keeps it as "generating". + replace: If True, replaces all content. If False, appends/merges with deltas. + original: Original event/object for metadata Returns: - The `Message` if found, otherwise None. - """ - msgs = [m for m in self.messages if m.id == id] - if msgs: - return msgs[0] - return None + The Message object (newly created or updated) - def add_message(self, message: Message, completed: bool = True): - """Append a message to the in-memory list and return None. + Examples: + # Streaming delta + await conv.upsert_message("assistant", "agent", "Hello", msg_id, content_index=0, completed=False) - The `completed` flag is not used for in-memory conversations. - """ - self.messages.append(message) - # In-memory conversation doesn't need to handle completed flag - return None + # Completion (replaces partial content) + await conv.upsert_message("assistant", "agent", "Hello world!", msg_id, completed=True, replace=True) - def update_message(self, message_id: str, input_text: str, user_id: str, replace_content: bool, completed: bool): - """Update or create a message in-memory. + # Simple non-streaming + await conv.upsert_message("user", "user123", "Hi there!") + """ + async with self._lock: + # Generate ID if not provided + if message_id is None: + message_id = str(uuid.uuid4()) + + # Find or create message + message = self._find_message(message_id) + if message is None: + # New message + message = Message( + id=message_id, + role=role, + user_id=user_id, + content="", + original=original, + ) + self.messages.append(message) + state = MessageState(message_id) + self._message_states[message_id] = state + else: + # Existing message - get its state + state_or_none = self._message_states.get(message_id) + if state_or_none is None: + # Message exists but no state - was already completed + # Ignore late updates (deltas arriving after completion) + logger.debug( + f"Message {message_id} already completed, ignoring update. " + f"This happens when deltas arrive after completion." + ) + return message + state = state_or_none + + # Update content + if content_index is not None: + # Streaming: buffer fragments in order + state.buffer.add_fragment(content_index, content) + message.content = state.buffer.get_accumulated() + elif replace: + # Replace all content + state.buffer.clear() + message.content = content + else: + # Append to existing + message.content += content + + # Sync to backend (implementation-specific) + await self._sync_to_backend(message, state, completed) + + # Cleanup state if completed + if completed: + self._message_states.pop(message_id, None) + + return message - If the message is not found, a new one is created with the given id. + @abstractmethod + async def _sync_to_backend( + self, message: Message, state: MessageState, completed: bool + ): + """Sync message to backend storage. Implementation-specific. Args: - message_id: Target message identifier. - input_text: Text to set (replace) or append. - user_id: Owner user id for the message. - replace_content: If True, replace content; otherwise append. - completed: Ignored for in-memory conversations. + message: The message to sync + state: The message's internal state + completed: If True, finalize the message. If False, mark as still generating. """ - # Find the message by id - message = self.lookup(message_id) - - if message is None: - logger.info(f"message {message_id} not found, create one instead") - return self.add_message(Message(user_id=user_id, id=message_id, content=input_text, original=None), completed=completed) + pass - if replace_content: - message.content = input_text - else: - message.content += input_text + def _find_message(self, message_id: str) -> Optional[Message]: + """Find a message by ID.""" + return next((m for m in self.messages if m.id == message_id), None) - # In-memory conversation just updates the message, no external API call - return None +class InMemoryConversation(Conversation): + """In-memory conversation (no external storage).""" + + async def _sync_to_backend( + self, message: Message, state: MessageState, completed: bool + ): + """No-op for in-memory storage - message is already in self.messages.""" + pass diff --git a/agents-core/vision_agents/core/agents/events.py b/agents-core/vision_agents/core/agents/events.py index 004247f1..e271a145 100644 --- a/agents-core/vision_agents/core/agents/events.py +++ b/agents-core/vision_agents/core/agents/events.py @@ -6,9 +6,10 @@ @dataclass class AgentSayEvent(PluginBaseEvent): """Event emitted when the agent wants to say something.""" - type: str = field(default='agent.say', init=False) + + type: str = field(default="agent.say", init=False) text: str = "" - user_id: Optional[str] = None + user_id: Optional[str] = None # type: ignore[assignment] metadata: Optional[Dict[str, Any]] = None def __post_init__(self): @@ -19,18 +20,20 @@ def __post_init__(self): @dataclass class AgentSayStartedEvent(PluginBaseEvent): """Event emitted when agent speech synthesis starts.""" - type: str = field(default='agent.say_started', init=False) + + type: str = field(default="agent.say_started", init=False) text: str = "" - user_id: Optional[str] = None + user_id: Optional[str] = None # type: ignore[assignment] synthesis_id: Optional[str] = None @dataclass class AgentSayCompletedEvent(PluginBaseEvent): """Event emitted when agent speech synthesis completes.""" - type: str = field(default='agent.say_completed', init=False) + + type: str = field(default="agent.say_completed", init=False) text: str = "" - user_id: Optional[str] = None + user_id: Optional[str] = None # type: ignore[assignment] synthesis_id: Optional[str] = None duration_ms: Optional[float] = None @@ -38,9 +41,10 @@ class AgentSayCompletedEvent(PluginBaseEvent): @dataclass class AgentSayErrorEvent(PluginBaseEvent): """Event emitted when agent speech synthesis encounters an error.""" - type: str = field(default='agent.say_error', init=False) + + type: str = field(default="agent.say_error", init=False) text: str = "" - user_id: Optional[str] = None + user_id: Optional[str] = None # type: ignore[assignment] error: Optional[Exception] = None @property diff --git a/agents-core/vision_agents/core/edge/sfu_events.py b/agents-core/vision_agents/core/edge/sfu_events.py index 9556ac76..2b3c180d 100644 --- a/agents-core/vision_agents/core/edge/sfu_events.py +++ b/agents-core/vision_agents/core/edge/sfu_events.py @@ -1,4 +1,5 @@ """Auto-generated SFU event dataclasses. Do not edit manually.""" + # Generated by _generate_sfu_events.py from __future__ import annotations @@ -21,88 +22,106 @@ def _to_dict(message) -> Dict[str, Any]: except Exception: return {} + # ============================================================================== # Message Type Wrappers # These are wrappers for protobuf message types used in events # ============================================================================== + @dataclass class Browser(DataClassJsonMixin): """Wrapper for stream.video.sfu.models.Browser.""" + name: Optional[str] = None version: Optional[str] = None @classmethod - def from_proto(cls, proto_obj) -> 'Browser': + def from_proto(cls, proto_obj) -> "Browser": """Create from protobuf Browser.""" if proto_obj is None: return cls() - return cls( - name=proto_obj.name, - version=proto_obj.version - ) + return cls(name=proto_obj.name, version=proto_obj.version) + @dataclass class CallGrants(DataClassJsonMixin): """Wrapper for stream.video.sfu.models.CallGrants.""" + can_publish_audio: Optional[bool] = None can_publish_video: Optional[bool] = None can_screenshare: Optional[bool] = None @classmethod - def from_proto(cls, proto_obj) -> 'CallGrants': + def from_proto(cls, proto_obj) -> "CallGrants": """Create from protobuf CallGrants.""" if proto_obj is None: return cls() return cls( can_publish_audio=proto_obj.can_publish_audio, can_publish_video=proto_obj.can_publish_video, - can_screenshare=proto_obj.can_screenshare + can_screenshare=proto_obj.can_screenshare, ) + @dataclass class CallState(DataClassJsonMixin): """Wrapper for stream.video.sfu.models.CallState.""" + participants: Optional[List[Participant]] = None started_at: Optional[Any] = None participant_count: Optional[ParticipantCount] = None pins: Optional[List[Pin]] = None @classmethod - def from_proto(cls, proto_obj) -> 'CallState': + def from_proto(cls, proto_obj) -> "CallState": """Create from protobuf CallState.""" if proto_obj is None: return cls() return cls( - participants=[Participant.from_proto(item) for item in proto_obj.participants], - started_at=proto_obj.started_at if proto_obj.HasField('started_at') else None, - participant_count=ParticipantCount.from_proto(proto_obj.participant_count) if proto_obj.HasField('participant_count') else None, - pins=[Pin.from_proto(item) for item in proto_obj.pins] + participants=[ + Participant.from_proto(item) for item in proto_obj.participants + ], + started_at=proto_obj.started_at + if proto_obj.HasField("started_at") + else None, + participant_count=ParticipantCount.from_proto(proto_obj.participant_count) + if proto_obj.HasField("participant_count") + else None, + pins=[Pin.from_proto(item) for item in proto_obj.pins], ) + @dataclass class ClientDetails(DataClassJsonMixin): """Wrapper for stream.video.sfu.models.ClientDetails.""" + sdk: Optional[Sdk] = None os: Optional[OS] = None browser: Optional[Browser] = None device: Optional[Device] = None @classmethod - def from_proto(cls, proto_obj) -> 'ClientDetails': + def from_proto(cls, proto_obj) -> "ClientDetails": """Create from protobuf ClientDetails.""" if proto_obj is None: return cls() return cls( - sdk=Sdk.from_proto(proto_obj.sdk) if proto_obj.HasField('sdk') else None, - os=OS.from_proto(proto_obj.os) if proto_obj.HasField('os') else None, - browser=Browser.from_proto(proto_obj.browser) if proto_obj.HasField('browser') else None, - device=Device.from_proto(proto_obj.device) if proto_obj.HasField('device') else None + sdk=Sdk.from_proto(proto_obj.sdk) if proto_obj.HasField("sdk") else None, + os=OS.from_proto(proto_obj.os) if proto_obj.HasField("os") else None, + browser=Browser.from_proto(proto_obj.browser) + if proto_obj.HasField("browser") + else None, + device=Device.from_proto(proto_obj.device) + if proto_obj.HasField("device") + else None, ) + @dataclass class Codec(DataClassJsonMixin): """Wrapper for stream.video.sfu.models.Codec.""" + payload_type: Optional[int] = None name: Optional[str] = None clock_rate: Optional[int] = None @@ -110,7 +129,7 @@ class Codec(DataClassJsonMixin): fmtp: Optional[str] = None @classmethod - def from_proto(cls, proto_obj) -> 'Codec': + def from_proto(cls, proto_obj) -> "Codec": """Create from protobuf Codec.""" if proto_obj is None: return cls() @@ -119,95 +138,102 @@ def from_proto(cls, proto_obj) -> 'Codec': name=proto_obj.name, clock_rate=proto_obj.clock_rate, encoding_parameters=proto_obj.encoding_parameters, - fmtp=proto_obj.fmtp + fmtp=proto_obj.fmtp, ) + @dataclass class Device(DataClassJsonMixin): """Wrapper for stream.video.sfu.models.Device.""" + name: Optional[str] = None version: Optional[str] = None @classmethod - def from_proto(cls, proto_obj) -> 'Device': + def from_proto(cls, proto_obj) -> "Device": """Create from protobuf Device.""" if proto_obj is None: return cls() - return cls( - name=proto_obj.name, - version=proto_obj.version - ) + return cls(name=proto_obj.name, version=proto_obj.version) + @dataclass class Error(DataClassJsonMixin): """Wrapper for stream.video.sfu.models.Error. - + Enum fields (use values from models_pb2): - code: ErrorCode """ + code: Optional[int] = None message: Optional[str] = None should_retry: Optional[bool] = None @classmethod - def from_proto(cls, proto_obj) -> 'Error': + def from_proto(cls, proto_obj) -> "Error": """Create from protobuf Error.""" if proto_obj is None: return cls() return cls( code=proto_obj.code, message=proto_obj.message, - should_retry=proto_obj.should_retry + should_retry=proto_obj.should_retry, ) + @dataclass class ICETrickle(DataClassJsonMixin): """Wrapper for stream.video.sfu.models.ICETrickle. - + Enum fields (use values from models_pb2): - peer_type: PeerType """ + peer_type: Optional[int] = None ice_candidate: Optional[str] = None session_id: Optional[str] = None @classmethod - def from_proto(cls, proto_obj) -> 'ICETrickle': + def from_proto(cls, proto_obj) -> "ICETrickle": """Create from protobuf ICETrickle.""" if proto_obj is None: return cls() return cls( peer_type=proto_obj.peer_type, ice_candidate=proto_obj.ice_candidate, - session_id=proto_obj.session_id + session_id=proto_obj.session_id, ) + @dataclass class OS(DataClassJsonMixin): """Wrapper for stream.video.sfu.models.OS.""" + name: Optional[str] = None version: Optional[str] = None architecture: Optional[str] = None @classmethod - def from_proto(cls, proto_obj) -> 'OS': + def from_proto(cls, proto_obj) -> "OS": """Create from protobuf OS.""" if proto_obj is None: return cls() return cls( name=proto_obj.name, version=proto_obj.version, - architecture=proto_obj.architecture + architecture=proto_obj.architecture, ) + @dataclass class Participant(DataClassJsonMixin): """Wrapper for stream.video.sfu.models.Participant. - + Enum fields (use values from models_pb2): - published_tracks: TrackType - connection_quality: ConnectionQuality """ + user_id: Optional[str] = None session_id: Optional[str] = None published_tracks: Optional[List[int]] = None @@ -223,7 +249,7 @@ class Participant(DataClassJsonMixin): roles: Optional[List[str]] = None @classmethod - def from_proto(cls, proto_obj) -> 'Participant': + def from_proto(cls, proto_obj) -> "Participant": """Create from protobuf Participant.""" if proto_obj is None: return cls() @@ -231,7 +257,7 @@ def from_proto(cls, proto_obj) -> 'Participant': user_id=proto_obj.user_id, session_id=proto_obj.session_id, published_tracks=list(proto_obj.published_tracks), - joined_at=proto_obj.joined_at if proto_obj.HasField('joined_at') else None, + joined_at=proto_obj.joined_at if proto_obj.HasField("joined_at") else None, track_lookup_prefix=proto_obj.track_lookup_prefix, connection_quality=proto_obj.connection_quality, is_speaking=proto_obj.is_speaking, @@ -239,49 +265,49 @@ def from_proto(cls, proto_obj) -> 'Participant': audio_level=proto_obj.audio_level, name=proto_obj.name, image=proto_obj.image, - custom=proto_obj.custom if proto_obj.HasField('custom') else None, - roles=list(proto_obj.roles) + custom=proto_obj.custom if proto_obj.HasField("custom") else None, + roles=list(proto_obj.roles), ) + @dataclass class ParticipantCount(DataClassJsonMixin): """Wrapper for stream.video.sfu.models.ParticipantCount.""" + total: Optional[int] = None anonymous: Optional[int] = None @classmethod - def from_proto(cls, proto_obj) -> 'ParticipantCount': + def from_proto(cls, proto_obj) -> "ParticipantCount": """Create from protobuf ParticipantCount.""" if proto_obj is None: return cls() - return cls( - total=proto_obj.total, - anonymous=proto_obj.anonymous - ) + return cls(total=proto_obj.total, anonymous=proto_obj.anonymous) + @dataclass class Pin(DataClassJsonMixin): """Wrapper for stream.video.sfu.models.Pin.""" + user_id: Optional[str] = None session_id: Optional[str] = None @classmethod - def from_proto(cls, proto_obj) -> 'Pin': + def from_proto(cls, proto_obj) -> "Pin": """Create from protobuf Pin.""" if proto_obj is None: return cls() - return cls( - user_id=proto_obj.user_id, - session_id=proto_obj.session_id - ) + return cls(user_id=proto_obj.user_id, session_id=proto_obj.session_id) + @dataclass class PublishOption(DataClassJsonMixin): """Wrapper for stream.video.sfu.models.PublishOption. - + Enum fields (use values from models_pb2): - track_type: TrackType """ + track_type: Optional[int] = None codec: Optional[Codec] = None bitrate: Optional[int] = None @@ -293,36 +319,42 @@ class PublishOption(DataClassJsonMixin): use_single_layer: Optional[bool] = None @classmethod - def from_proto(cls, proto_obj) -> 'PublishOption': + def from_proto(cls, proto_obj) -> "PublishOption": """Create from protobuf PublishOption.""" if proto_obj is None: return cls() return cls( track_type=proto_obj.track_type, - codec=Codec.from_proto(proto_obj.codec) if proto_obj.HasField('codec') else None, + codec=Codec.from_proto(proto_obj.codec) + if proto_obj.HasField("codec") + else None, bitrate=proto_obj.bitrate, fps=proto_obj.fps, max_spatial_layers=proto_obj.max_spatial_layers, max_temporal_layers=proto_obj.max_temporal_layers, - video_dimension=VideoDimension.from_proto(proto_obj.video_dimension) if proto_obj.HasField('video_dimension') else None, + video_dimension=VideoDimension.from_proto(proto_obj.video_dimension) + if proto_obj.HasField("video_dimension") + else None, id=proto_obj.id, - use_single_layer=proto_obj.use_single_layer + use_single_layer=proto_obj.use_single_layer, ) + @dataclass class Sdk(DataClassJsonMixin): """Wrapper for stream.video.sfu.models.Sdk. - + Enum fields (use values from models_pb2): - type: SdkType """ + type: Optional[int] = None major: Optional[str] = None minor: Optional[str] = None patch: Optional[str] = None @classmethod - def from_proto(cls, proto_obj) -> 'Sdk': + def from_proto(cls, proto_obj) -> "Sdk": """Create from protobuf Sdk.""" if proto_obj is None: return cls() @@ -330,36 +362,40 @@ def from_proto(cls, proto_obj) -> 'Sdk': type=proto_obj.type, major=proto_obj.major, minor=proto_obj.minor, - patch=proto_obj.patch + patch=proto_obj.patch, ) + @dataclass class SubscribeOption(DataClassJsonMixin): """Wrapper for stream.video.sfu.models.SubscribeOption. - + Enum fields (use values from models_pb2): - track_type: TrackType """ + track_type: Optional[int] = None codecs: Optional[List[Codec]] = None @classmethod - def from_proto(cls, proto_obj) -> 'SubscribeOption': + def from_proto(cls, proto_obj) -> "SubscribeOption": """Create from protobuf SubscribeOption.""" if proto_obj is None: return cls() return cls( track_type=proto_obj.track_type, - codecs=[Codec.from_proto(item) for item in proto_obj.codecs] + codecs=[Codec.from_proto(item) for item in proto_obj.codecs], ) + @dataclass class TrackInfo(DataClassJsonMixin): """Wrapper for stream.video.sfu.models.TrackInfo. - + Enum fields (use values from models_pb2): - track_type: TrackType """ + track_id: Optional[str] = None track_type: Optional[int] = None layers: Optional[List[VideoLayer]] = None @@ -372,7 +408,7 @@ class TrackInfo(DataClassJsonMixin): publish_option_id: Optional[int] = None @classmethod - def from_proto(cls, proto_obj) -> 'TrackInfo': + def from_proto(cls, proto_obj) -> "TrackInfo": """Create from protobuf TrackInfo.""" if proto_obj is None: return cls() @@ -385,33 +421,36 @@ def from_proto(cls, proto_obj) -> 'TrackInfo': stereo=proto_obj.stereo, red=proto_obj.red, muted=proto_obj.muted, - codec=Codec.from_proto(proto_obj.codec) if proto_obj.HasField('codec') else None, - publish_option_id=proto_obj.publish_option_id + codec=Codec.from_proto(proto_obj.codec) + if proto_obj.HasField("codec") + else None, + publish_option_id=proto_obj.publish_option_id, ) + @dataclass class VideoDimension(DataClassJsonMixin): """Wrapper for stream.video.sfu.models.VideoDimension.""" + width: Optional[int] = None height: Optional[int] = None @classmethod - def from_proto(cls, proto_obj) -> 'VideoDimension': + def from_proto(cls, proto_obj) -> "VideoDimension": """Create from protobuf VideoDimension.""" if proto_obj is None: return cls() - return cls( - width=proto_obj.width, - height=proto_obj.height - ) + return cls(width=proto_obj.width, height=proto_obj.height) + @dataclass class VideoLayer(DataClassJsonMixin): """Wrapper for stream.video.sfu.models.VideoLayer. - + Enum fields (use values from models_pb2): - quality: VideoQuality """ + rid: Optional[str] = None video_dimension: Optional[VideoDimension] = None bitrate: Optional[int] = None @@ -419,16 +458,18 @@ class VideoLayer(DataClassJsonMixin): quality: Optional[int] = None @classmethod - def from_proto(cls, proto_obj) -> 'VideoLayer': + def from_proto(cls, proto_obj) -> "VideoLayer": """Create from protobuf VideoLayer.""" if proto_obj is None: return cls() return cls( rid=proto_obj.rid, - video_dimension=VideoDimension.from_proto(proto_obj.video_dimension) if proto_obj.HasField('video_dimension') else None, + video_dimension=VideoDimension.from_proto(proto_obj.video_dimension) + if proto_obj.HasField("video_dimension") + else None, bitrate=proto_obj.bitrate, fps=proto_obj.fps, - quality=proto_obj.quality + quality=proto_obj.quality, ) @@ -436,32 +477,34 @@ def from_proto(cls, proto_obj) -> 'VideoLayer': # Event Classes # ============================================================================== + @dataclass class AudioLevelEvent(BaseEvent): """Dataclass event for video.sfu.event.events_pb2.AudioLevel.""" + type: str = field(default="stream.video.sfu.event.AudioLevel", init=False) payload: Optional[events_pb2.AudioLevel] = field(default=None, repr=False) @property - def user_id(self) -> Optional[str]: + def user_id(self) -> Optional[str]: # type: ignore[override] """Access user_id field from the protobuf payload.""" if self.payload is None: return None - return getattr(self.payload, 'user_id', None) + return getattr(self.payload, "user_id", None) @property def level(self) -> Optional[float]: """Access level field from the protobuf payload.""" if self.payload is None: return None - return getattr(self.payload, 'level', None) + return getattr(self.payload, "level", None) @property def is_speaking(self) -> Optional[bool]: """Access is_speaking field from the protobuf payload.""" if self.payload is None: return None - return getattr(self.payload, 'is_speaking', None) + return getattr(self.payload, "is_speaking", None) @classmethod def from_proto(cls, proto_obj: events_pb2.AudioLevel, **extra): @@ -478,11 +521,15 @@ def __getattr__(self, item: str): """Delegate attribute access to protobuf payload.""" if self.payload is not None: return getattr(self.payload, item) - raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{item}'") + raise AttributeError( + f"'{self.__class__.__name__}' object has no attribute '{item}'" + ) + @dataclass class AudioLevelChangedEvent(BaseEvent): """Dataclass event for video.sfu.event.events_pb2.AudioLevelChanged.""" + type: str = field(default="stream.video.sfu.event.AudioLevelChanged", init=False) payload: Optional[events_pb2.AudioLevelChanged] = field(default=None, repr=False) @@ -491,7 +538,7 @@ def audio_levels(self) -> Optional[List[Any]]: """Access audio_levels field from the protobuf payload.""" if self.payload is None: return None - return getattr(self.payload, 'audio_levels', None) + return getattr(self.payload, "audio_levels", None) @classmethod def from_proto(cls, proto_obj: events_pb2.AudioLevelChanged, **extra): @@ -508,11 +555,15 @@ def __getattr__(self, item: str): """Delegate attribute access to protobuf payload.""" if self.payload is not None: return getattr(self.payload, item) - raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{item}'") + raise AttributeError( + f"'{self.__class__.__name__}' object has no attribute '{item}'" + ) + @dataclass class AudioSenderEvent(BaseEvent): """Dataclass event for video.sfu.event.events_pb2.AudioSender.""" + type: str = field(default="stream.video.sfu.event.AudioSender", init=False) payload: Optional[events_pb2.AudioSender] = field(default=None, repr=False) @@ -521,7 +572,7 @@ def codec(self) -> Optional[Codec]: """Access codec field from the protobuf payload.""" if self.payload is None: return None - proto_val = getattr(self.payload, 'codec', None) + proto_val = getattr(self.payload, "codec", None) return Codec.from_proto(proto_val) if proto_val is not None else None @property @@ -529,14 +580,14 @@ def track_type(self) -> Optional[int]: """Access track_type field from the protobuf payload. Use models_pb2.TrackType enum.""" if self.payload is None: return None - return getattr(self.payload, 'track_type', None) + return getattr(self.payload, "track_type", None) @property def publish_option_id(self) -> Optional[int]: """Access publish_option_id field from the protobuf payload.""" if self.payload is None: return None - return getattr(self.payload, 'publish_option_id', None) + return getattr(self.payload, "publish_option_id", None) @classmethod def from_proto(cls, proto_obj: events_pb2.AudioSender, **extra): @@ -553,11 +604,15 @@ def __getattr__(self, item: str): """Delegate attribute access to protobuf payload.""" if self.payload is not None: return getattr(self.payload, item) - raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{item}'") + raise AttributeError( + f"'{self.__class__.__name__}' object has no attribute '{item}'" + ) + @dataclass class CallEndedEvent(BaseEvent): """Dataclass event for video.sfu.event.events_pb2.CallEnded.""" + type: str = field(default="stream.video.sfu.event.CallEnded", init=False) payload: Optional[events_pb2.CallEnded] = field(default=None, repr=False) @@ -566,7 +621,7 @@ def reason(self) -> Optional[int]: """Access reason field from the protobuf payload. Use models_pb2.CallEndedReason enum.""" if self.payload is None: return None - return getattr(self.payload, 'reason', None) + return getattr(self.payload, "reason", None) @classmethod def from_proto(cls, proto_obj: events_pb2.CallEnded, **extra): @@ -583,11 +638,15 @@ def __getattr__(self, item: str): """Delegate attribute access to protobuf payload.""" if self.payload is not None: return getattr(self.payload, item) - raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{item}'") + raise AttributeError( + f"'{self.__class__.__name__}' object has no attribute '{item}'" + ) + @dataclass class CallGrantsUpdatedEvent(BaseEvent): """Dataclass event for video.sfu.event.events_pb2.CallGrantsUpdated.""" + type: str = field(default="stream.video.sfu.event.CallGrantsUpdated", init=False) payload: Optional[events_pb2.CallGrantsUpdated] = field(default=None, repr=False) @@ -596,7 +655,7 @@ def current_grants(self) -> Optional[CallGrants]: """Access current_grants field from the protobuf payload.""" if self.payload is None: return None - proto_val = getattr(self.payload, 'current_grants', None) + proto_val = getattr(self.payload, "current_grants", None) return CallGrants.from_proto(proto_val) if proto_val is not None else None @property @@ -604,7 +663,7 @@ def message(self) -> Optional[str]: """Access message field from the protobuf payload.""" if self.payload is None: return None - return getattr(self.payload, 'message', None) + return getattr(self.payload, "message", None) @classmethod def from_proto(cls, proto_obj: events_pb2.CallGrantsUpdated, **extra): @@ -621,11 +680,15 @@ def __getattr__(self, item: str): """Delegate attribute access to protobuf payload.""" if self.payload is not None: return getattr(self.payload, item) - raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{item}'") + raise AttributeError( + f"'{self.__class__.__name__}' object has no attribute '{item}'" + ) + @dataclass class ChangePublishOptionsEvent(BaseEvent): """Dataclass event for video.sfu.event.events_pb2.ChangePublishOptions.""" + type: str = field(default="stream.video.sfu.event.ChangePublishOptions", init=False) payload: Optional[events_pb2.ChangePublishOptions] = field(default=None, repr=False) @@ -634,15 +697,19 @@ def publish_options(self) -> Optional[List[PublishOption]]: """Access publish_options field from the protobuf payload.""" if self.payload is None: return None - proto_list = getattr(self.payload, 'publish_options', []) - return [PublishOption.from_proto(item) for item in proto_list] if proto_list else None + proto_list = getattr(self.payload, "publish_options", []) + return ( + [PublishOption.from_proto(item) for item in proto_list] + if proto_list + else None + ) @property def reason(self) -> Optional[str]: """Access reason field from the protobuf payload.""" if self.payload is None: return None - return getattr(self.payload, 'reason', None) + return getattr(self.payload, "reason", None) @classmethod def from_proto(cls, proto_obj: events_pb2.ChangePublishOptions, **extra): @@ -659,13 +726,21 @@ def __getattr__(self, item: str): """Delegate attribute access to protobuf payload.""" if self.payload is not None: return getattr(self.payload, item) - raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{item}'") + raise AttributeError( + f"'{self.__class__.__name__}' object has no attribute '{item}'" + ) + @dataclass class ChangePublishOptionsCompleteEvent(BaseEvent): """Dataclass event for video.sfu.event.events_pb2.ChangePublishOptionsComplete.""" - type: str = field(default="stream.video.sfu.event.ChangePublishOptionsComplete", init=False) - payload: Optional[events_pb2.ChangePublishOptionsComplete] = field(default=None, repr=False) + + type: str = field( + default="stream.video.sfu.event.ChangePublishOptionsComplete", init=False + ) + payload: Optional[events_pb2.ChangePublishOptionsComplete] = field( + default=None, repr=False + ) @classmethod def from_proto(cls, proto_obj: events_pb2.ChangePublishOptionsComplete, **extra): @@ -682,11 +757,15 @@ def __getattr__(self, item: str): """Delegate attribute access to protobuf payload.""" if self.payload is not None: return getattr(self.payload, item) - raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{item}'") + raise AttributeError( + f"'{self.__class__.__name__}' object has no attribute '{item}'" + ) + @dataclass class ChangePublishQualityEvent(BaseEvent): """Dataclass event for video.sfu.event.events_pb2.ChangePublishQuality.""" + type: str = field(default="stream.video.sfu.event.ChangePublishQuality", init=False) payload: Optional[events_pb2.ChangePublishQuality] = field(default=None, repr=False) @@ -695,14 +774,14 @@ def audio_senders(self) -> Optional[List[Any]]: """Access audio_senders field from the protobuf payload.""" if self.payload is None: return None - return getattr(self.payload, 'audio_senders', None) + return getattr(self.payload, "audio_senders", None) @property def video_senders(self) -> Optional[List[Any]]: """Access video_senders field from the protobuf payload.""" if self.payload is None: return None - return getattr(self.payload, 'video_senders', None) + return getattr(self.payload, "video_senders", None) @classmethod def from_proto(cls, proto_obj: events_pb2.ChangePublishQuality, **extra): @@ -719,20 +798,28 @@ def __getattr__(self, item: str): """Delegate attribute access to protobuf payload.""" if self.payload is not None: return getattr(self.payload, item) - raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{item}'") + raise AttributeError( + f"'{self.__class__.__name__}' object has no attribute '{item}'" + ) + @dataclass class ConnectionQualityChangedEvent(BaseEvent): """Dataclass event for video.sfu.event.events_pb2.ConnectionQualityChanged.""" - type: str = field(default="stream.video.sfu.event.ConnectionQualityChanged", init=False) - payload: Optional[events_pb2.ConnectionQualityChanged] = field(default=None, repr=False) + + type: str = field( + default="stream.video.sfu.event.ConnectionQualityChanged", init=False + ) + payload: Optional[events_pb2.ConnectionQualityChanged] = field( + default=None, repr=False + ) @property def connection_quality_updates(self) -> Optional[List[Any]]: """Access connection_quality_updates field from the protobuf payload.""" if self.payload is None: return None - return getattr(self.payload, 'connection_quality_updates', None) + return getattr(self.payload, "connection_quality_updates", None) @classmethod def from_proto(cls, proto_obj: events_pb2.ConnectionQualityChanged, **extra): @@ -749,27 +836,35 @@ def __getattr__(self, item: str): """Delegate attribute access to protobuf payload.""" if self.payload is not None: return getattr(self.payload, item) - raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{item}'") + raise AttributeError( + f"'{self.__class__.__name__}' object has no attribute '{item}'" + ) + @dataclass class ConnectionQualityInfoEvent(BaseEvent): """Dataclass event for video.sfu.event.events_pb2.ConnectionQualityInfo.""" - type: str = field(default="stream.video.sfu.event.ConnectionQualityInfo", init=False) - payload: Optional[events_pb2.ConnectionQualityInfo] = field(default=None, repr=False) + + type: str = field( + default="stream.video.sfu.event.ConnectionQualityInfo", init=False + ) + payload: Optional[events_pb2.ConnectionQualityInfo] = field( + default=None, repr=False + ) @property - def user_id(self) -> Optional[str]: + def user_id(self) -> Optional[str]: # type: ignore[override] """Access user_id field from the protobuf payload.""" if self.payload is None: return None - return getattr(self.payload, 'user_id', None) + return getattr(self.payload, "user_id", None) @property def connection_quality(self) -> Optional[int]: """Access connection_quality field from the protobuf payload. Use models_pb2.ConnectionQuality enum.""" if self.payload is None: return None - return getattr(self.payload, 'connection_quality', None) + return getattr(self.payload, "connection_quality", None) @classmethod def from_proto(cls, proto_obj: events_pb2.ConnectionQualityInfo, **extra): @@ -786,20 +881,28 @@ def __getattr__(self, item: str): """Delegate attribute access to protobuf payload.""" if self.payload is not None: return getattr(self.payload, item) - raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{item}'") + raise AttributeError( + f"'{self.__class__.__name__}' object has no attribute '{item}'" + ) + @dataclass class DominantSpeakerChangedEvent(BaseEvent): """Dataclass event for video.sfu.event.events_pb2.DominantSpeakerChanged.""" - type: str = field(default="stream.video.sfu.event.DominantSpeakerChanged", init=False) - payload: Optional[events_pb2.DominantSpeakerChanged] = field(default=None, repr=False) + + type: str = field( + default="stream.video.sfu.event.DominantSpeakerChanged", init=False + ) + payload: Optional[events_pb2.DominantSpeakerChanged] = field( + default=None, repr=False + ) @property - def user_id(self) -> Optional[str]: + def user_id(self) -> Optional[str]: # type: ignore[override] """Access user_id field from the protobuf payload.""" if self.payload is None: return None - return getattr(self.payload, 'user_id', None) + return getattr(self.payload, "user_id", None) @classmethod def from_proto(cls, proto_obj: events_pb2.DominantSpeakerChanged, **extra): @@ -816,11 +919,15 @@ def __getattr__(self, item: str): """Delegate attribute access to protobuf payload.""" if self.payload is not None: return getattr(self.payload, item) - raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{item}'") + raise AttributeError( + f"'{self.__class__.__name__}' object has no attribute '{item}'" + ) + @dataclass class ErrorEvent(BaseEvent): """Dataclass event for video.sfu.event.events_pb2.Error.""" + type: str = field(default="stream.video.sfu.event.Error", init=False) payload: Optional[events_pb2.Error] = field(default=None, repr=False) @@ -829,7 +936,7 @@ def error(self) -> Optional[Error]: """Access error field from the protobuf payload.""" if self.payload is None: return None - proto_val = getattr(self.payload, 'error', None) + proto_val = getattr(self.payload, "error", None) return Error.from_proto(proto_val) if proto_val is not None else None @property @@ -837,7 +944,7 @@ def reconnect_strategy(self) -> Optional[int]: """Access reconnect_strategy field from the protobuf payload. Use models_pb2.WebsocketReconnectStrategy enum.""" if self.payload is None: return None - return getattr(self.payload, 'reconnect_strategy', None) + return getattr(self.payload, "reconnect_strategy", None) @classmethod def from_proto(cls, proto_obj: events_pb2.Error, **extra): @@ -854,11 +961,15 @@ def __getattr__(self, item: str): """Delegate attribute access to protobuf payload.""" if self.payload is not None: return getattr(self.payload, item) - raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{item}'") + raise AttributeError( + f"'{self.__class__.__name__}' object has no attribute '{item}'" + ) + @dataclass class GoAwayEvent(BaseEvent): """Dataclass event for video.sfu.event.events_pb2.GoAway.""" + type: str = field(default="stream.video.sfu.event.GoAway", init=False) payload: Optional[events_pb2.GoAway] = field(default=None, repr=False) @@ -867,7 +978,7 @@ def reason(self) -> Optional[int]: """Access reason field from the protobuf payload. Use models_pb2.GoAwayReason enum.""" if self.payload is None: return None - return getattr(self.payload, 'reason', None) + return getattr(self.payload, "reason", None) @classmethod def from_proto(cls, proto_obj: events_pb2.GoAway, **extra): @@ -884,11 +995,15 @@ def __getattr__(self, item: str): """Delegate attribute access to protobuf payload.""" if self.payload is not None: return getattr(self.payload, item) - raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{item}'") + raise AttributeError( + f"'{self.__class__.__name__}' object has no attribute '{item}'" + ) + @dataclass class HealthCheckRequestEvent(BaseEvent): """Dataclass event for video.sfu.event.events_pb2.HealthCheckRequest.""" + type: str = field(default="stream.video.sfu.event.HealthCheckRequest", init=False) payload: Optional[events_pb2.HealthCheckRequest] = field(default=None, repr=False) @@ -907,11 +1022,15 @@ def __getattr__(self, item: str): """Delegate attribute access to protobuf payload.""" if self.payload is not None: return getattr(self.payload, item) - raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{item}'") + raise AttributeError( + f"'{self.__class__.__name__}' object has no attribute '{item}'" + ) + @dataclass class HealthCheckResponseEvent(BaseEvent): """Dataclass event for video.sfu.event.events_pb2.HealthCheckResponse.""" + type: str = field(default="stream.video.sfu.event.HealthCheckResponse", init=False) payload: Optional[events_pb2.HealthCheckResponse] = field(default=None, repr=False) @@ -920,7 +1039,7 @@ def participant_count(self) -> Optional[ParticipantCount]: """Access participant_count field from the protobuf payload.""" if self.payload is None: return None - proto_val = getattr(self.payload, 'participant_count', None) + proto_val = getattr(self.payload, "participant_count", None) return ParticipantCount.from_proto(proto_val) if proto_val is not None else None @classmethod @@ -938,11 +1057,15 @@ def __getattr__(self, item: str): """Delegate attribute access to protobuf payload.""" if self.payload is not None: return getattr(self.payload, item) - raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{item}'") + raise AttributeError( + f"'{self.__class__.__name__}' object has no attribute '{item}'" + ) + @dataclass class ICERestartEvent(BaseEvent): """Dataclass event for video.sfu.event.events_pb2.ICERestart.""" + type: str = field(default="stream.video.sfu.event.ICERestart", init=False) payload: Optional[events_pb2.ICERestart] = field(default=None, repr=False) @@ -951,7 +1074,7 @@ def peer_type(self) -> Optional[int]: """Access peer_type field from the protobuf payload. Use models_pb2.PeerType enum.""" if self.payload is None: return None - return getattr(self.payload, 'peer_type', None) + return getattr(self.payload, "peer_type", None) @classmethod def from_proto(cls, proto_obj: events_pb2.ICERestart, **extra): @@ -968,11 +1091,15 @@ def __getattr__(self, item: str): """Delegate attribute access to protobuf payload.""" if self.payload is not None: return getattr(self.payload, item) - raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{item}'") + raise AttributeError( + f"'{self.__class__.__name__}' object has no attribute '{item}'" + ) + @dataclass class ICETrickleEvent(BaseEvent): """Dataclass event for video.sfu.event.events_pb2.ICETrickle.""" + type: str = field(default="stream.video.sfu.event.ICETrickle", init=False) payload: Optional[events_pb2.ICETrickle] = field(default=None, repr=False) @@ -981,14 +1108,14 @@ def peer_type(self) -> Optional[int]: """Access peer_type field from the protobuf payload. Use models_pb2.PeerType enum.""" if self.payload is None: return None - return getattr(self.payload, 'peer_type', None) + return getattr(self.payload, "peer_type", None) @property def ice_candidate(self) -> Optional[str]: """Access ice_candidate field from the protobuf payload.""" if self.payload is None: return None - return getattr(self.payload, 'ice_candidate', None) + return getattr(self.payload, "ice_candidate", None) @classmethod def from_proto(cls, proto_obj: events_pb2.ICETrickle, **extra): @@ -1005,20 +1132,28 @@ def __getattr__(self, item: str): """Delegate attribute access to protobuf payload.""" if self.payload is not None: return getattr(self.payload, item) - raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{item}'") + raise AttributeError( + f"'{self.__class__.__name__}' object has no attribute '{item}'" + ) + @dataclass class InboundStateNotificationEvent(BaseEvent): """Dataclass event for video.sfu.event.events_pb2.InboundStateNotification.""" - type: str = field(default="stream.video.sfu.event.InboundStateNotification", init=False) - payload: Optional[events_pb2.InboundStateNotification] = field(default=None, repr=False) + + type: str = field( + default="stream.video.sfu.event.InboundStateNotification", init=False + ) + payload: Optional[events_pb2.InboundStateNotification] = field( + default=None, repr=False + ) @property def inbound_video_states(self) -> Optional[List[Any]]: """Access inbound_video_states field from the protobuf payload.""" if self.payload is None: return None - return getattr(self.payload, 'inbound_video_states', None) + return getattr(self.payload, "inbound_video_states", None) @classmethod def from_proto(cls, proto_obj: events_pb2.InboundStateNotification, **extra): @@ -1035,34 +1170,38 @@ def __getattr__(self, item: str): """Delegate attribute access to protobuf payload.""" if self.payload is not None: return getattr(self.payload, item) - raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{item}'") + raise AttributeError( + f"'{self.__class__.__name__}' object has no attribute '{item}'" + ) + @dataclass class InboundVideoStateEvent(BaseEvent): """Dataclass event for video.sfu.event.events_pb2.InboundVideoState.""" + type: str = field(default="stream.video.sfu.event.InboundVideoState", init=False) payload: Optional[events_pb2.InboundVideoState] = field(default=None, repr=False) @property - def user_id(self) -> Optional[str]: + def user_id(self) -> Optional[str]: # type: ignore[override] """Access user_id field from the protobuf payload.""" if self.payload is None: return None - return getattr(self.payload, 'user_id', None) + return getattr(self.payload, "user_id", None) @property def track_type(self) -> Optional[int]: """Access track_type field from the protobuf payload. Use models_pb2.TrackType enum.""" if self.payload is None: return None - return getattr(self.payload, 'track_type', None) + return getattr(self.payload, "track_type", None) @property def paused(self) -> Optional[bool]: """Access paused field from the protobuf payload.""" if self.payload is None: return None - return getattr(self.payload, 'paused', None) + return getattr(self.payload, "paused", None) @classmethod def from_proto(cls, proto_obj: events_pb2.InboundVideoState, **extra): @@ -1079,11 +1218,15 @@ def __getattr__(self, item: str): """Delegate attribute access to protobuf payload.""" if self.payload is not None: return getattr(self.payload, item) - raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{item}'") + raise AttributeError( + f"'{self.__class__.__name__}' object has no attribute '{item}'" + ) + @dataclass class JoinRequestEvent(BaseEvent): """Dataclass event for video.sfu.event.events_pb2.JoinRequest.""" + type: str = field(default="stream.video.sfu.event.JoinRequest", init=False) payload: Optional[events_pb2.JoinRequest] = field(default=None, repr=False) @@ -1092,28 +1235,28 @@ def token(self) -> Optional[str]: """Access token field from the protobuf payload.""" if self.payload is None: return None - return getattr(self.payload, 'token', None) + return getattr(self.payload, "token", None) @property def subscriber_sdp(self) -> Optional[str]: """Access subscriber_sdp field from the protobuf payload.""" if self.payload is None: return None - return getattr(self.payload, 'subscriber_sdp', None) + return getattr(self.payload, "subscriber_sdp", None) @property def publisher_sdp(self) -> Optional[str]: """Access publisher_sdp field from the protobuf payload.""" if self.payload is None: return None - return getattr(self.payload, 'publisher_sdp', None) + return getattr(self.payload, "publisher_sdp", None) @property def client_details(self) -> Optional[ClientDetails]: """Access client_details field from the protobuf payload.""" if self.payload is None: return None - proto_val = getattr(self.payload, 'client_details', None) + proto_val = getattr(self.payload, "client_details", None) return ClientDetails.from_proto(proto_val) if proto_val is not None else None @property @@ -1121,44 +1264,52 @@ def migration(self) -> Optional[Any]: """Access migration field from the protobuf payload.""" if self.payload is None: return None - return getattr(self.payload, 'migration', None) + return getattr(self.payload, "migration", None) @property def fast_reconnect(self) -> Optional[bool]: """Access fast_reconnect field from the protobuf payload.""" if self.payload is None: return None - return getattr(self.payload, 'fast_reconnect', None) + return getattr(self.payload, "fast_reconnect", None) @property def reconnect_details(self) -> Optional[Any]: """Access reconnect_details field from the protobuf payload.""" if self.payload is None: return None - return getattr(self.payload, 'reconnect_details', None) + return getattr(self.payload, "reconnect_details", None) @property def preferred_publish_options(self) -> Optional[List[PublishOption]]: """Access preferred_publish_options field from the protobuf payload.""" if self.payload is None: return None - proto_list = getattr(self.payload, 'preferred_publish_options', []) - return [PublishOption.from_proto(item) for item in proto_list] if proto_list else None + proto_list = getattr(self.payload, "preferred_publish_options", []) + return ( + [PublishOption.from_proto(item) for item in proto_list] + if proto_list + else None + ) @property def preferred_subscribe_options(self) -> Optional[List[SubscribeOption]]: """Access preferred_subscribe_options field from the protobuf payload.""" if self.payload is None: return None - proto_list = getattr(self.payload, 'preferred_subscribe_options', []) - return [SubscribeOption.from_proto(item) for item in proto_list] if proto_list else None + proto_list = getattr(self.payload, "preferred_subscribe_options", []) + return ( + [SubscribeOption.from_proto(item) for item in proto_list] + if proto_list + else None + ) @property def capabilities(self) -> Optional[List[int]]: """Access capabilities field from the protobuf payload. Use models_pb2.ClientCapability enum.""" if self.payload is None: return None - return getattr(self.payload, 'capabilities', None) + return getattr(self.payload, "capabilities", None) @classmethod def from_proto(cls, proto_obj: events_pb2.JoinRequest, **extra): @@ -1175,11 +1326,15 @@ def __getattr__(self, item: str): """Delegate attribute access to protobuf payload.""" if self.payload is not None: return getattr(self.payload, item) - raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{item}'") + raise AttributeError( + f"'{self.__class__.__name__}' object has no attribute '{item}'" + ) + @dataclass class JoinResponseEvent(BaseEvent): """Dataclass event for video.sfu.event.events_pb2.JoinResponse.""" + type: str = field(default="stream.video.sfu.event.JoinResponse", init=False) payload: Optional[events_pb2.JoinResponse] = field(default=None, repr=False) @@ -1188,7 +1343,7 @@ def call_state(self) -> Optional[CallState]: """Access call_state field from the protobuf payload.""" if self.payload is None: return None - proto_val = getattr(self.payload, 'call_state', None) + proto_val = getattr(self.payload, "call_state", None) return CallState.from_proto(proto_val) if proto_val is not None else None @property @@ -1196,22 +1351,26 @@ def reconnected(self) -> Optional[bool]: """Access reconnected field from the protobuf payload.""" if self.payload is None: return None - return getattr(self.payload, 'reconnected', None) + return getattr(self.payload, "reconnected", None) @property def fast_reconnect_deadline_seconds(self) -> Optional[int]: """Access fast_reconnect_deadline_seconds field from the protobuf payload.""" if self.payload is None: return None - return getattr(self.payload, 'fast_reconnect_deadline_seconds', None) + return getattr(self.payload, "fast_reconnect_deadline_seconds", None) @property def publish_options(self) -> Optional[List[PublishOption]]: """Access publish_options field from the protobuf payload.""" if self.payload is None: return None - proto_list = getattr(self.payload, 'publish_options', []) - return [PublishOption.from_proto(item) for item in proto_list] if proto_list else None + proto_list = getattr(self.payload, "publish_options", []) + return ( + [PublishOption.from_proto(item) for item in proto_list] + if proto_list + else None + ) @classmethod def from_proto(cls, proto_obj: events_pb2.JoinResponse, **extra): @@ -1228,11 +1387,15 @@ def __getattr__(self, item: str): """Delegate attribute access to protobuf payload.""" if self.payload is not None: return getattr(self.payload, item) - raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{item}'") + raise AttributeError( + f"'{self.__class__.__name__}' object has no attribute '{item}'" + ) + @dataclass class LeaveCallRequestEvent(BaseEvent): """Dataclass event for video.sfu.event.events_pb2.LeaveCallRequest.""" + type: str = field(default="stream.video.sfu.event.LeaveCallRequest", init=False) payload: Optional[events_pb2.LeaveCallRequest] = field(default=None, repr=False) @@ -1241,7 +1404,7 @@ def reason(self) -> Optional[str]: """Access reason field from the protobuf payload.""" if self.payload is None: return None - return getattr(self.payload, 'reason', None) + return getattr(self.payload, "reason", None) @classmethod def from_proto(cls, proto_obj: events_pb2.LeaveCallRequest, **extra): @@ -1258,11 +1421,15 @@ def __getattr__(self, item: str): """Delegate attribute access to protobuf payload.""" if self.payload is not None: return getattr(self.payload, item) - raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{item}'") + raise AttributeError( + f"'{self.__class__.__name__}' object has no attribute '{item}'" + ) + @dataclass class MigrationEvent(BaseEvent): """Dataclass event for video.sfu.event.events_pb2.Migration.""" + type: str = field(default="stream.video.sfu.event.Migration", init=False) payload: Optional[events_pb2.Migration] = field(default=None, repr=False) @@ -1271,22 +1438,24 @@ def from_sfu_id(self) -> Optional[str]: """Access from_sfu_id field from the protobuf payload.""" if self.payload is None: return None - return getattr(self.payload, 'from_sfu_id', None) + return getattr(self.payload, "from_sfu_id", None) @property def announced_tracks(self) -> Optional[List[TrackInfo]]: """Access announced_tracks field from the protobuf payload.""" if self.payload is None: return None - proto_list = getattr(self.payload, 'announced_tracks', []) - return [TrackInfo.from_proto(item) for item in proto_list] if proto_list else None + proto_list = getattr(self.payload, "announced_tracks", []) + return ( + [TrackInfo.from_proto(item) for item in proto_list] if proto_list else None + ) @property def subscriptions(self) -> Optional[List[Any]]: """Access subscriptions field from the protobuf payload.""" if self.payload is None: return None - return getattr(self.payload, 'subscriptions', None) + return getattr(self.payload, "subscriptions", None) @classmethod def from_proto(cls, proto_obj: events_pb2.Migration, **extra): @@ -1303,11 +1472,15 @@ def __getattr__(self, item: str): """Delegate attribute access to protobuf payload.""" if self.payload is not None: return getattr(self.payload, item) - raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{item}'") + raise AttributeError( + f"'{self.__class__.__name__}' object has no attribute '{item}'" + ) + @dataclass class ParticipantJoinedEvent(BaseEvent): """Dataclass event for video.sfu.event.events_pb2.ParticipantJoined.""" + type: str = field(default="stream.video.sfu.event.ParticipantJoined", init=False) payload: Optional[events_pb2.ParticipantJoined] = field(default=None, repr=False) @@ -1316,14 +1489,14 @@ def call_cid(self) -> Optional[str]: """Access call_cid field from the protobuf payload.""" if self.payload is None: return None - return getattr(self.payload, 'call_cid', None) + return getattr(self.payload, "call_cid", None) @property def participant(self) -> Optional[Participant]: """Access participant field from the protobuf payload.""" if self.payload is None: return None - proto_val = getattr(self.payload, 'participant', None) + proto_val = getattr(self.payload, "participant", None) return Participant.from_proto(proto_val) if proto_val is not None else None @classmethod @@ -1341,11 +1514,15 @@ def __getattr__(self, item: str): """Delegate attribute access to protobuf payload.""" if self.payload is not None: return getattr(self.payload, item) - raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{item}'") + raise AttributeError( + f"'{self.__class__.__name__}' object has no attribute '{item}'" + ) + @dataclass class ParticipantLeftEvent(BaseEvent): """Dataclass event for video.sfu.event.events_pb2.ParticipantLeft.""" + type: str = field(default="stream.video.sfu.event.ParticipantLeft", init=False) payload: Optional[events_pb2.ParticipantLeft] = field(default=None, repr=False) @@ -1354,14 +1531,14 @@ def call_cid(self) -> Optional[str]: """Access call_cid field from the protobuf payload.""" if self.payload is None: return None - return getattr(self.payload, 'call_cid', None) + return getattr(self.payload, "call_cid", None) @property def participant(self) -> Optional[Participant]: """Access participant field from the protobuf payload.""" if self.payload is None: return None - proto_val = getattr(self.payload, 'participant', None) + proto_val = getattr(self.payload, "participant", None) return Participant.from_proto(proto_val) if proto_val is not None else None @classmethod @@ -1379,13 +1556,21 @@ def __getattr__(self, item: str): """Delegate attribute access to protobuf payload.""" if self.payload is not None: return getattr(self.payload, item) - raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{item}'") + raise AttributeError( + f"'{self.__class__.__name__}' object has no attribute '{item}'" + ) + @dataclass class ParticipantMigrationCompleteEvent(BaseEvent): """Dataclass event for video.sfu.event.events_pb2.ParticipantMigrationComplete.""" - type: str = field(default="stream.video.sfu.event.ParticipantMigrationComplete", init=False) - payload: Optional[events_pb2.ParticipantMigrationComplete] = field(default=None, repr=False) + + type: str = field( + default="stream.video.sfu.event.ParticipantMigrationComplete", init=False + ) + payload: Optional[events_pb2.ParticipantMigrationComplete] = field( + default=None, repr=False + ) @classmethod def from_proto(cls, proto_obj: events_pb2.ParticipantMigrationComplete, **extra): @@ -1402,11 +1587,15 @@ def __getattr__(self, item: str): """Delegate attribute access to protobuf payload.""" if self.payload is not None: return getattr(self.payload, item) - raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{item}'") + raise AttributeError( + f"'{self.__class__.__name__}' object has no attribute '{item}'" + ) + @dataclass class ParticipantUpdatedEvent(BaseEvent): """Dataclass event for video.sfu.event.events_pb2.ParticipantUpdated.""" + type: str = field(default="stream.video.sfu.event.ParticipantUpdated", init=False) payload: Optional[events_pb2.ParticipantUpdated] = field(default=None, repr=False) @@ -1415,14 +1604,14 @@ def call_cid(self) -> Optional[str]: """Access call_cid field from the protobuf payload.""" if self.payload is None: return None - return getattr(self.payload, 'call_cid', None) + return getattr(self.payload, "call_cid", None) @property def participant(self) -> Optional[Participant]: """Access participant field from the protobuf payload.""" if self.payload is None: return None - proto_val = getattr(self.payload, 'participant', None) + proto_val = getattr(self.payload, "participant", None) return Participant.from_proto(proto_val) if proto_val is not None else None @classmethod @@ -1440,11 +1629,15 @@ def __getattr__(self, item: str): """Delegate attribute access to protobuf payload.""" if self.payload is not None: return getattr(self.payload, item) - raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{item}'") + raise AttributeError( + f"'{self.__class__.__name__}' object has no attribute '{item}'" + ) + @dataclass class PinsChangedEvent(BaseEvent): """Dataclass event for video.sfu.event.events_pb2.PinsChanged.""" + type: str = field(default="stream.video.sfu.event.PinsChanged", init=False) payload: Optional[events_pb2.PinsChanged] = field(default=None, repr=False) @@ -1453,7 +1646,7 @@ def pins(self) -> Optional[List[Pin]]: """Access pins field from the protobuf payload.""" if self.payload is None: return None - proto_list = getattr(self.payload, 'pins', []) + proto_list = getattr(self.payload, "pins", []) return [Pin.from_proto(item) for item in proto_list] if proto_list else None @classmethod @@ -1471,11 +1664,15 @@ def __getattr__(self, item: str): """Delegate attribute access to protobuf payload.""" if self.payload is not None: return getattr(self.payload, item) - raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{item}'") + raise AttributeError( + f"'{self.__class__.__name__}' object has no attribute '{item}'" + ) + @dataclass class PublisherAnswerEvent(BaseEvent): """Dataclass event for video.sfu.event.events_pb2.PublisherAnswer.""" + type: str = field(default="stream.video.sfu.event.PublisherAnswer", init=False) payload: Optional[events_pb2.PublisherAnswer] = field(default=None, repr=False) @@ -1484,7 +1681,7 @@ def sdp(self) -> Optional[str]: """Access sdp field from the protobuf payload.""" if self.payload is None: return None - return getattr(self.payload, 'sdp', None) + return getattr(self.payload, "sdp", None) @classmethod def from_proto(cls, proto_obj: events_pb2.PublisherAnswer, **extra): @@ -1501,11 +1698,15 @@ def __getattr__(self, item: str): """Delegate attribute access to protobuf payload.""" if self.payload is not None: return getattr(self.payload, item) - raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{item}'") + raise AttributeError( + f"'{self.__class__.__name__}' object has no attribute '{item}'" + ) + @dataclass class ReconnectDetailsEvent(BaseEvent): """Dataclass event for video.sfu.event.events_pb2.ReconnectDetails.""" + type: str = field(default="stream.video.sfu.event.ReconnectDetails", init=False) payload: Optional[events_pb2.ReconnectDetails] = field(default=None, repr=False) @@ -1514,50 +1715,52 @@ def strategy(self) -> Optional[int]: """Access strategy field from the protobuf payload. Use models_pb2.WebsocketReconnectStrategy enum.""" if self.payload is None: return None - return getattr(self.payload, 'strategy', None) + return getattr(self.payload, "strategy", None) @property def announced_tracks(self) -> Optional[List[TrackInfo]]: """Access announced_tracks field from the protobuf payload.""" if self.payload is None: return None - proto_list = getattr(self.payload, 'announced_tracks', []) - return [TrackInfo.from_proto(item) for item in proto_list] if proto_list else None + proto_list = getattr(self.payload, "announced_tracks", []) + return ( + [TrackInfo.from_proto(item) for item in proto_list] if proto_list else None + ) @property def subscriptions(self) -> Optional[List[Any]]: """Access subscriptions field from the protobuf payload.""" if self.payload is None: return None - return getattr(self.payload, 'subscriptions', None) + return getattr(self.payload, "subscriptions", None) @property def reconnect_attempt(self) -> Optional[int]: """Access reconnect_attempt field from the protobuf payload.""" if self.payload is None: return None - return getattr(self.payload, 'reconnect_attempt', None) + return getattr(self.payload, "reconnect_attempt", None) @property def from_sfu_id(self) -> Optional[str]: """Access from_sfu_id field from the protobuf payload.""" if self.payload is None: return None - return getattr(self.payload, 'from_sfu_id', None) + return getattr(self.payload, "from_sfu_id", None) @property def previous_session_id(self) -> Optional[str]: """Access previous_session_id field from the protobuf payload.""" if self.payload is None: return None - return getattr(self.payload, 'previous_session_id', None) + return getattr(self.payload, "previous_session_id", None) @property def reason(self) -> Optional[str]: """Access reason field from the protobuf payload.""" if self.payload is None: return None - return getattr(self.payload, 'reason', None) + return getattr(self.payload, "reason", None) @classmethod def from_proto(cls, proto_obj: events_pb2.ReconnectDetails, **extra): @@ -1574,11 +1777,15 @@ def __getattr__(self, item: str): """Delegate attribute access to protobuf payload.""" if self.payload is not None: return getattr(self.payload, item) - raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{item}'") + raise AttributeError( + f"'{self.__class__.__name__}' object has no attribute '{item}'" + ) + @dataclass class SfuEvent(BaseEvent): """Dataclass event for video.sfu.event.events_pb2.SfuEvent.""" + type: str = field(default="stream.video.sfu.event.SfuEvent", init=False) payload: Optional[events_pb2.SfuEvent] = field(default=None, repr=False) @@ -1587,35 +1794,35 @@ def subscriber_offer(self) -> Optional[Any]: """Access subscriber_offer field from the protobuf payload.""" if self.payload is None: return None - return getattr(self.payload, 'subscriber_offer', None) + return getattr(self.payload, "subscriber_offer", None) @property def publisher_answer(self) -> Optional[Any]: """Access publisher_answer field from the protobuf payload.""" if self.payload is None: return None - return getattr(self.payload, 'publisher_answer', None) + return getattr(self.payload, "publisher_answer", None) @property def connection_quality_changed(self) -> Optional[Any]: """Access connection_quality_changed field from the protobuf payload.""" if self.payload is None: return None - return getattr(self.payload, 'connection_quality_changed', None) + return getattr(self.payload, "connection_quality_changed", None) @property def audio_level_changed(self) -> Optional[Any]: """Access audio_level_changed field from the protobuf payload.""" if self.payload is None: return None - return getattr(self.payload, 'audio_level_changed', None) + return getattr(self.payload, "audio_level_changed", None) @property def ice_trickle(self) -> Optional[ICETrickle]: """Access ice_trickle field from the protobuf payload.""" if self.payload is None: return None - proto_val = getattr(self.payload, 'ice_trickle', None) + proto_val = getattr(self.payload, "ice_trickle", None) return ICETrickle.from_proto(proto_val) if proto_val is not None else None @property @@ -1623,126 +1830,126 @@ def change_publish_quality(self) -> Optional[Any]: """Access change_publish_quality field from the protobuf payload.""" if self.payload is None: return None - return getattr(self.payload, 'change_publish_quality', None) + return getattr(self.payload, "change_publish_quality", None) @property def participant_joined(self) -> Optional[Any]: """Access participant_joined field from the protobuf payload.""" if self.payload is None: return None - return getattr(self.payload, 'participant_joined', None) + return getattr(self.payload, "participant_joined", None) @property def participant_left(self) -> Optional[Any]: """Access participant_left field from the protobuf payload.""" if self.payload is None: return None - return getattr(self.payload, 'participant_left', None) + return getattr(self.payload, "participant_left", None) @property def dominant_speaker_changed(self) -> Optional[Any]: """Access dominant_speaker_changed field from the protobuf payload.""" if self.payload is None: return None - return getattr(self.payload, 'dominant_speaker_changed', None) + return getattr(self.payload, "dominant_speaker_changed", None) @property def join_response(self) -> Optional[Any]: """Access join_response field from the protobuf payload.""" if self.payload is None: return None - return getattr(self.payload, 'join_response', None) + return getattr(self.payload, "join_response", None) @property def health_check_response(self) -> Optional[Any]: """Access health_check_response field from the protobuf payload.""" if self.payload is None: return None - return getattr(self.payload, 'health_check_response', None) + return getattr(self.payload, "health_check_response", None) @property def track_published(self) -> Optional[Any]: """Access track_published field from the protobuf payload.""" if self.payload is None: return None - return getattr(self.payload, 'track_published', None) + return getattr(self.payload, "track_published", None) @property def track_unpublished(self) -> Optional[Any]: """Access track_unpublished field from the protobuf payload.""" if self.payload is None: return None - return getattr(self.payload, 'track_unpublished', None) + return getattr(self.payload, "track_unpublished", None) @property def error(self) -> Optional[Any]: """Access error field from the protobuf payload.""" if self.payload is None: return None - return getattr(self.payload, 'error', None) + return getattr(self.payload, "error", None) @property def call_grants_updated(self) -> Optional[Any]: """Access call_grants_updated field from the protobuf payload.""" if self.payload is None: return None - return getattr(self.payload, 'call_grants_updated', None) + return getattr(self.payload, "call_grants_updated", None) @property def go_away(self) -> Optional[Any]: """Access go_away field from the protobuf payload.""" if self.payload is None: return None - return getattr(self.payload, 'go_away', None) + return getattr(self.payload, "go_away", None) @property def ice_restart(self) -> Optional[Any]: """Access ice_restart field from the protobuf payload.""" if self.payload is None: return None - return getattr(self.payload, 'ice_restart', None) + return getattr(self.payload, "ice_restart", None) @property def pins_updated(self) -> Optional[Any]: """Access pins_updated field from the protobuf payload.""" if self.payload is None: return None - return getattr(self.payload, 'pins_updated', None) + return getattr(self.payload, "pins_updated", None) @property def call_ended(self) -> Optional[Any]: """Access call_ended field from the protobuf payload.""" if self.payload is None: return None - return getattr(self.payload, 'call_ended', None) + return getattr(self.payload, "call_ended", None) @property def participant_updated(self) -> Optional[Any]: """Access participant_updated field from the protobuf payload.""" if self.payload is None: return None - return getattr(self.payload, 'participant_updated', None) + return getattr(self.payload, "participant_updated", None) @property def participant_migration_complete(self) -> Optional[Any]: """Access participant_migration_complete field from the protobuf payload.""" if self.payload is None: return None - return getattr(self.payload, 'participant_migration_complete', None) + return getattr(self.payload, "participant_migration_complete", None) @property def change_publish_options(self) -> Optional[Any]: """Access change_publish_options field from the protobuf payload.""" if self.payload is None: return None - return getattr(self.payload, 'change_publish_options', None) + return getattr(self.payload, "change_publish_options", None) @property def inbound_state_notification(self) -> Optional[Any]: """Access inbound_state_notification field from the protobuf payload.""" if self.payload is None: return None - return getattr(self.payload, 'inbound_state_notification', None) + return getattr(self.payload, "inbound_state_notification", None) @classmethod def from_proto(cls, proto_obj: events_pb2.SfuEvent, **extra): @@ -1759,11 +1966,15 @@ def __getattr__(self, item: str): """Delegate attribute access to protobuf payload.""" if self.payload is not None: return getattr(self.payload, item) - raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{item}'") + raise AttributeError( + f"'{self.__class__.__name__}' object has no attribute '{item}'" + ) + @dataclass class SfuRequestEvent(BaseEvent): """Dataclass event for video.sfu.event.events_pb2.SfuRequest.""" + type: str = field(default="stream.video.sfu.event.SfuRequest", init=False) payload: Optional[events_pb2.SfuRequest] = field(default=None, repr=False) @@ -1772,21 +1983,21 @@ def join_request(self) -> Optional[Any]: """Access join_request field from the protobuf payload.""" if self.payload is None: return None - return getattr(self.payload, 'join_request', None) + return getattr(self.payload, "join_request", None) @property def health_check_request(self) -> Optional[Any]: """Access health_check_request field from the protobuf payload.""" if self.payload is None: return None - return getattr(self.payload, 'health_check_request', None) + return getattr(self.payload, "health_check_request", None) @property def leave_call_request(self) -> Optional[Any]: """Access leave_call_request field from the protobuf payload.""" if self.payload is None: return None - return getattr(self.payload, 'leave_call_request', None) + return getattr(self.payload, "leave_call_request", None) @classmethod def from_proto(cls, proto_obj: events_pb2.SfuRequest, **extra): @@ -1803,11 +2014,15 @@ def __getattr__(self, item: str): """Delegate attribute access to protobuf payload.""" if self.payload is not None: return getattr(self.payload, item) - raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{item}'") + raise AttributeError( + f"'{self.__class__.__name__}' object has no attribute '{item}'" + ) + @dataclass class SubscriberOfferEvent(BaseEvent): """Dataclass event for video.sfu.event.events_pb2.SubscriberOffer.""" + type: str = field(default="stream.video.sfu.event.SubscriberOffer", init=False) payload: Optional[events_pb2.SubscriberOffer] = field(default=None, repr=False) @@ -1816,14 +2031,14 @@ def ice_restart(self) -> Optional[bool]: """Access ice_restart field from the protobuf payload.""" if self.payload is None: return None - return getattr(self.payload, 'ice_restart', None) + return getattr(self.payload, "ice_restart", None) @property def sdp(self) -> Optional[str]: """Access sdp field from the protobuf payload.""" if self.payload is None: return None - return getattr(self.payload, 'sdp', None) + return getattr(self.payload, "sdp", None) @classmethod def from_proto(cls, proto_obj: events_pb2.SubscriberOffer, **extra): @@ -1840,27 +2055,31 @@ def __getattr__(self, item: str): """Delegate attribute access to protobuf payload.""" if self.payload is not None: return getattr(self.payload, item) - raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{item}'") + raise AttributeError( + f"'{self.__class__.__name__}' object has no attribute '{item}'" + ) + @dataclass class TrackPublishedEvent(BaseEvent): """Dataclass event for video.sfu.event.events_pb2.TrackPublished.""" + type: str = field(default="stream.video.sfu.event.TrackPublished", init=False) payload: Optional[events_pb2.TrackPublished] = field(default=None, repr=False) @property - def user_id(self) -> Optional[str]: + def user_id(self) -> Optional[str]: # type: ignore[override] """Access user_id field from the protobuf payload.""" if self.payload is None: return None - return getattr(self.payload, 'user_id', None) + return getattr(self.payload, "user_id", None) @property def participant(self) -> Optional[Participant]: """Access participant field from the protobuf payload.""" if self.payload is None: return None - proto_val = getattr(self.payload, 'participant', None) + proto_val = getattr(self.payload, "participant", None) return Participant.from_proto(proto_val) if proto_val is not None else None @classmethod @@ -1878,34 +2097,38 @@ def __getattr__(self, item: str): """Delegate attribute access to protobuf payload.""" if self.payload is not None: return getattr(self.payload, item) - raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{item}'") + raise AttributeError( + f"'{self.__class__.__name__}' object has no attribute '{item}'" + ) + @dataclass class TrackUnpublishedEvent(BaseEvent): """Dataclass event for video.sfu.event.events_pb2.TrackUnpublished.""" + type: str = field(default="stream.video.sfu.event.TrackUnpublished", init=False) payload: Optional[events_pb2.TrackUnpublished] = field(default=None, repr=False) @property - def user_id(self) -> Optional[str]: + def user_id(self) -> Optional[str]: # type: ignore[override] """Access user_id field from the protobuf payload.""" if self.payload is None: return None - return getattr(self.payload, 'user_id', None) + return getattr(self.payload, "user_id", None) @property def cause(self) -> Optional[int]: """Access cause field from the protobuf payload. Use models_pb2.TrackUnpublishReason enum.""" if self.payload is None: return None - return getattr(self.payload, 'cause', None) + return getattr(self.payload, "cause", None) @property def participant(self) -> Optional[Participant]: """Access participant field from the protobuf payload.""" if self.payload is None: return None - proto_val = getattr(self.payload, 'participant', None) + proto_val = getattr(self.payload, "participant", None) return Participant.from_proto(proto_val) if proto_val is not None else None @classmethod @@ -1923,11 +2146,15 @@ def __getattr__(self, item: str): """Delegate attribute access to protobuf payload.""" if self.payload is not None: return getattr(self.payload, item) - raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{item}'") + raise AttributeError( + f"'{self.__class__.__name__}' object has no attribute '{item}'" + ) + @dataclass class VideoLayerSettingEvent(BaseEvent): """Dataclass event for video.sfu.event.events_pb2.VideoLayerSetting.""" + type: str = field(default="stream.video.sfu.event.VideoLayerSetting", init=False) payload: Optional[events_pb2.VideoLayerSetting] = field(default=None, repr=False) @@ -1936,35 +2163,35 @@ def name(self) -> Optional[str]: """Access name field from the protobuf payload.""" if self.payload is None: return None - return getattr(self.payload, 'name', None) + return getattr(self.payload, "name", None) @property def active(self) -> Optional[bool]: """Access active field from the protobuf payload.""" if self.payload is None: return None - return getattr(self.payload, 'active', None) + return getattr(self.payload, "active", None) @property def max_bitrate(self) -> Optional[int]: """Access max_bitrate field from the protobuf payload.""" if self.payload is None: return None - return getattr(self.payload, 'max_bitrate', None) + return getattr(self.payload, "max_bitrate", None) @property def scale_resolution_down_by(self) -> Optional[float]: """Access scale_resolution_down_by field from the protobuf payload.""" if self.payload is None: return None - return getattr(self.payload, 'scale_resolution_down_by', None) + return getattr(self.payload, "scale_resolution_down_by", None) @property def codec(self) -> Optional[Codec]: """Access codec field from the protobuf payload.""" if self.payload is None: return None - proto_val = getattr(self.payload, 'codec', None) + proto_val = getattr(self.payload, "codec", None) return Codec.from_proto(proto_val) if proto_val is not None else None @property @@ -1972,14 +2199,14 @@ def max_framerate(self) -> Optional[int]: """Access max_framerate field from the protobuf payload.""" if self.payload is None: return None - return getattr(self.payload, 'max_framerate', None) + return getattr(self.payload, "max_framerate", None) @property def scalability_mode(self) -> Optional[str]: """Access scalability_mode field from the protobuf payload.""" if self.payload is None: return None - return getattr(self.payload, 'scalability_mode', None) + return getattr(self.payload, "scalability_mode", None) @classmethod def from_proto(cls, proto_obj: events_pb2.VideoLayerSetting, **extra): @@ -1996,11 +2223,15 @@ def __getattr__(self, item: str): """Delegate attribute access to protobuf payload.""" if self.payload is not None: return getattr(self.payload, item) - raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{item}'") + raise AttributeError( + f"'{self.__class__.__name__}' object has no attribute '{item}'" + ) + @dataclass class VideoSenderEvent(BaseEvent): """Dataclass event for video.sfu.event.events_pb2.VideoSender.""" + type: str = field(default="stream.video.sfu.event.VideoSender", init=False) payload: Optional[events_pb2.VideoSender] = field(default=None, repr=False) @@ -2009,7 +2240,7 @@ def codec(self) -> Optional[Codec]: """Access codec field from the protobuf payload.""" if self.payload is None: return None - proto_val = getattr(self.payload, 'codec', None) + proto_val = getattr(self.payload, "codec", None) return Codec.from_proto(proto_val) if proto_val is not None else None @property @@ -2017,21 +2248,21 @@ def layers(self) -> Optional[List[Any]]: """Access layers field from the protobuf payload.""" if self.payload is None: return None - return getattr(self.payload, 'layers', None) + return getattr(self.payload, "layers", None) @property def track_type(self) -> Optional[int]: """Access track_type field from the protobuf payload. Use models_pb2.TrackType enum.""" if self.payload is None: return None - return getattr(self.payload, 'track_type', None) + return getattr(self.payload, "track_type", None) @property def publish_option_id(self) -> Optional[int]: """Access publish_option_id field from the protobuf payload.""" if self.payload is None: return None - return getattr(self.payload, 'publish_option_id', None) + return getattr(self.payload, "publish_option_id", None) @classmethod def from_proto(cls, proto_obj: events_pb2.VideoSender, **extra): @@ -2048,7 +2279,9 @@ def __getattr__(self, item: str): """Delegate attribute access to protobuf payload.""" if self.payload is not None: return getattr(self.payload, item) - raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{item}'") + raise AttributeError( + f"'{self.__class__.__name__}' object has no attribute '{item}'" + ) __all__ = ( diff --git a/agents-core/vision_agents/core/events/base.py b/agents-core/vision_agents/core/events/base.py index 558876c2..3f139445 100644 --- a/agents-core/vision_agents/core/events/base.py +++ b/agents-core/vision_agents/core/events/base.py @@ -9,6 +9,7 @@ from getstream.video.rtc.pb.stream.video.sfu.models.models_pb2 import Participant + class ConnectionState(Enum): """Connection states for streaming plugins.""" @@ -32,12 +33,18 @@ class AudioFormat(Enum): @dataclass class BaseEvent(DataClassJsonMixin): """Base class for all events.""" + type: str event_id: str = field(default_factory=lambda: str(uuid.uuid4())) timestamp: datetime = field(default_factory=lambda: datetime.now(timezone.utc)) session_id: Optional[str] = None user_metadata: Optional[Participant] = None + def user_id(self) -> Optional[str]: + if self.user_metadata is None: + return None + return getattr(self.user_metadata, "user_id") + @dataclass class PluginBaseEvent(BaseEvent): @@ -83,11 +90,12 @@ class PluginErrorEvent(PluginBaseEvent): def error_message(self) -> str: return str(self.error) if self.error else "Unknown error" + @dataclasses.dataclass class ExceptionEvent: exc: Exception handler: FunctionType - type: str = 'base.exception' + type: str = "base.exception" @dataclasses.dataclass @@ -95,24 +103,24 @@ class HealthCheckEvent(DataClassJsonMixin): connection_id: str created_at: int custom: dict - type: str = 'health.check' + type: str = "health.check" @dataclass class ConnectionOkEvent(BaseEvent): """Event emitted when WebSocket connection is established.""" - + type: str = field(default="connection.ok", init=False) connection_id: Optional[str] = None server_time: Optional[str] = None api_key: Optional[str] = None - user_id: Optional[str] = None + user_id: Optional[str] = None # type: ignore[assignment] @dataclass class ConnectionErrorEvent(BaseEvent): """Event emitted when WebSocket connection encounters an error.""" - + type: str = field(default="connection.error", init=False) error_code: Optional[str] = None error_message: Optional[str] = None @@ -122,7 +130,7 @@ class ConnectionErrorEvent(BaseEvent): @dataclass class ConnectionClosedEvent(BaseEvent): """Event emitted when WebSocket connection is closed.""" - + type: str = field(default="connection.closed", init=False) code: Optional[int] = None reason: Optional[str] = None diff --git a/agents-core/vision_agents/core/llm/events.py b/agents-core/vision_agents/core/llm/events.py index e517a479..012f6efb 100644 --- a/agents-core/vision_agents/core/llm/events.py +++ b/agents-core/vision_agents/core/llm/events.py @@ -7,7 +7,8 @@ @dataclass class RealtimeConnectedEvent(PluginBaseEvent): """Event emitted when realtime connection is established.""" - type: str = field(default='plugin.realtime_connected', init=False) + + type: str = field(default="plugin.realtime_connected", init=False) provider: Optional[str] = None session_config: Optional[dict[str, Any]] = None capabilities: Optional[list[str]] = None @@ -15,7 +16,7 @@ class RealtimeConnectedEvent(PluginBaseEvent): @dataclass class RealtimeDisconnectedEvent(PluginBaseEvent): - type: str = field(default='plugin.realtime_disconnected', init=False) + type: str = field(default="plugin.realtime_disconnected", init=False) provider: Optional[str] = None reason: Optional[str] = None was_clean: bool = True @@ -24,7 +25,8 @@ class RealtimeDisconnectedEvent(PluginBaseEvent): @dataclass class RealtimeAudioInputEvent(PluginBaseEvent): """Event emitted when audio input is sent to realtime session.""" - type: str = field(default='plugin.realtime_audio_input', init=False) + + type: str = field(default="plugin.realtime_audio_input", init=False) audio_data: Optional[bytes] = None audio_format: AudioFormat = AudioFormat.PCM_S16 sample_rate: int = 16000 @@ -34,7 +36,8 @@ class RealtimeAudioInputEvent(PluginBaseEvent): @dataclass class RealtimeAudioOutputEvent(PluginBaseEvent): """Event emitted when audio output is received from realtime session.""" - type: str = field(default='plugin.realtime_audio_output', init=False) + + type: str = field(default="plugin.realtime_audio_output", init=False) audio_data: Optional[bytes] = None audio_format: AudioFormat = AudioFormat.PCM_S16 sample_rate: int = 16000 @@ -42,27 +45,11 @@ class RealtimeAudioOutputEvent(PluginBaseEvent): response_id: Optional[str] = None -@dataclass -class RealtimeTranscriptEvent(PluginBaseEvent): - """Event emitted when realtime session provides a transcript.""" - original: Optional[Any] = None - type: str = field(default='plugin.realtime_transcript', init=False) - text: Optional[str] = None - user_metadata: Optional[Any] = None - - -@dataclass -class RealtimePartialTranscriptEvent(PluginBaseEvent): - original: Optional[Any] = None - type: str = field(default='plugin.realtime_partial_transcript', init=False) - text: Optional[str] = None - user_metadata: Optional[Any] = None - - @dataclass class RealtimeResponseEvent(PluginBaseEvent): """Event emitted when realtime session provides a response.""" - type: str = field(default='plugin.realtime_response', init=False) + + type: str = field(default="plugin.realtime_response", init=False) original: Optional[str] = None text: Optional[str] = None response_id: str = field(default_factory=lambda: str(uuid.uuid4())) @@ -73,7 +60,8 @@ class RealtimeResponseEvent(PluginBaseEvent): @dataclass class RealtimeConversationItemEvent(PluginBaseEvent): """Event emitted for conversation item updates in realtime session.""" - type: str = field(default='plugin.realtime_conversation_item', init=False) + + type: str = field(default="plugin.realtime_conversation_item", init=False) item_id: Optional[str] = None item_type: Optional[str] = ( None # "message", "function_call", "function_call_output" @@ -86,7 +74,8 @@ class RealtimeConversationItemEvent(PluginBaseEvent): @dataclass class RealtimeErrorEvent(PluginBaseEvent): """Event emitted when a realtime error occurs.""" - type: str = field(default='plugin.realtime_error', init=False) + + type: str = field(default="plugin.realtime_error", init=False) error: Optional[Exception] = None error_code: Optional[str] = None context: Optional[str] = None @@ -96,9 +85,10 @@ class RealtimeErrorEvent(PluginBaseEvent): def error_message(self) -> str: return str(self.error) if self.error else "Unknown error" + @dataclass class LLMResponseChunkEvent(PluginBaseEvent): - type: str = field(default='plugin.llm_response_chunk', init=False) + type: str = field(default="plugin.llm_response_chunk", init=False) content_index: int | None = None """The index of the content part that the text delta was added to.""" @@ -118,15 +108,18 @@ class LLMResponseChunkEvent(PluginBaseEvent): @dataclass class LLMResponseCompletedEvent(PluginBaseEvent): """Event emitted after an LLM response is processed.""" - type: str = field(default='plugin.llm_response_completed', init=False) + + type: str = field(default="plugin.llm_response_completed", init=False) original: Any = None text: str = "" + item_id: Optional[str] = None @dataclass class ToolStartEvent(PluginBaseEvent): """Event emitted when a tool execution starts.""" - type: str = field(default='plugin.llm.tool.start', init=False) + + type: str = field(default="plugin.llm.tool.start", init=False) tool_name: str = "" arguments: Optional[Dict[str, Any]] = None tool_call_id: Optional[str] = None @@ -135,7 +128,8 @@ class ToolStartEvent(PluginBaseEvent): @dataclass class ToolEndEvent(PluginBaseEvent): """Event emitted when a tool execution ends.""" - type: str = field(default='plugin.llm.tool.end', init=False) + + type: str = field(default="plugin.llm.tool.end", init=False) tool_name: str = "" success: bool = True result: Optional[Any] = None @@ -143,3 +137,20 @@ class ToolEndEvent(PluginBaseEvent): tool_call_id: Optional[str] = None execution_time_ms: Optional[float] = None + +@dataclass +class RealtimeUserSpeechTranscriptionEvent(PluginBaseEvent): + """Event emitted when user speech transcription is available from realtime session.""" + + type: str = field(default="plugin.realtime_user_speech_transcription", init=False) + text: str = "" + original: Optional[Any] = None + + +@dataclass +class RealtimeAgentSpeechTranscriptionEvent(PluginBaseEvent): + """Event emitted when agent speech transcription is available from realtime session.""" + + type: str = field(default="plugin.realtime_agent_speech_transcription", init=False) + text: str = "" + original: Optional[Any] = None diff --git a/agents-core/vision_agents/core/llm/realtime.py b/agents-core/vision_agents/core/llm/realtime.py index b109d3d6..6f33876f 100644 --- a/agents-core/vision_agents/core/llm/realtime.py +++ b/agents-core/vision_agents/core/llm/realtime.py @@ -2,10 +2,11 @@ from typing import ( Any, + Optional, ) from getstream.video.rtc.audio_track import AudioStreamTrack -from vision_agents.core.edge.types import PcmData +from vision_agents.core.edge.types import PcmData, Participant import abc @@ -37,11 +38,12 @@ class Realtime(LLM, abc.ABC): - Transcript outgoing audio """ - fps : int = 1 + + fps: int = 1 def __init__( self, - fps: int = 1, # the number of video frames per second to send (for implementations that support setting fps) + fps: int = 1, # the number of video frames per second to send (for implementations that support setting fps) ): super().__init__() self._is_connected = False @@ -54,6 +56,8 @@ def __init__( self.output_track: AudioStreamTrack = AudioStreamTrack( framerate=48000, stereo=True, format="s16" ) + # Store current participant for user speech transcription events + self._current_participant: Optional[Participant] = None @property def is_connected(self) -> bool: @@ -64,8 +68,9 @@ def is_connected(self) -> bool: async def connect(self): ... @abc.abstractmethod - async def simple_audio_response(self, pcm: PcmData): ... - + async def simple_audio_response( + self, pcm: PcmData, participant: Optional[Participant] = None + ): ... async def _watch_video_track(self, track: Any, **kwargs) -> None: """Optionally overridden by providers that support video input.""" @@ -130,27 +135,6 @@ def _emit_audio_output_event( ) self.events.send(event) - def _emit_partial_transcript_event(self, text: str, user_metadata=None, original=None): - event = events.RealtimeTranscriptEvent( - text=text, - user_metadata=user_metadata, - original=original, - ) - self.events.send(event) - - def _emit_transcript_event( - self, - text: str, - user_metadata=None, - original=None, - ): - event = events.RealtimeTranscriptEvent( - text=text, - user_metadata=user_metadata, - original=original, - ) - self.events.send(event) - def _emit_response_event( self, text, @@ -198,6 +182,27 @@ def _emit_error_event(self, error, context="", user_metadata=None): ) self.events.send(event) + def _emit_user_speech_transcription(self, text: str, original=None): + """Emit a user speech transcription event with participant info.""" + event = events.RealtimeUserSpeechTranscriptionEvent( + session_id=self.session_id, + plugin_name=self.provider_name, + text=text, + original=original, + user_metadata=self._current_participant, + ) + self.events.send(event) + + def _emit_agent_speech_transcription(self, text: str, original=None): + """Emit an agent speech transcription event.""" + event = events.RealtimeAgentSpeechTranscriptionEvent( + session_id=self.session_id, + plugin_name=self.provider_name, + text=text, + original=original, + ) + self.events.send(event) + async def close(self): """Close the Realtime service and release any resources.""" if self._is_connected: @@ -213,4 +218,3 @@ async def close(self): @abc.abstractmethod async def _close_impl(self): ... - diff --git a/examples/01_simple_agent_example/simple_agent_example.py b/examples/01_simple_agent_example/simple_agent_example.py index 3b063e66..1d4abd1b 100644 --- a/examples/01_simple_agent_example/simple_agent_example.py +++ b/examples/01_simple_agent_example/simple_agent_example.py @@ -3,24 +3,29 @@ from dotenv import load_dotenv from vision_agents.core import User, Agent -from vision_agents.plugins import cartesia, deepgram, openai, getstream, smart_turn +from vision_agents.plugins import cartesia, deepgram, getstream, smart_turn, gemini load_dotenv() + async def start_agent() -> None: - llm = openai.LLM(model="gpt-4o-mini") + llm = gemini.LLM("gemini-2.0-flash") # create an agent to run with Stream's edge, openAI llm agent = Agent( edge=getstream.Edge(), # low latency edge. clients for React, iOS, Android, RN, Flutter etc. - agent_user=User(name="My happy AI friend", id="agent"), # the user object for the agent (name, image etc) + agent_user=User( + name="My happy AI friend", id="agent" + ), # the user object for the agent (name, image etc) instructions="You're a voice AI assistant. Keep responses short and conversational. Don't use special characters or formatting. Be friendly and helpful.", processors=[], # processors can fetch extra data, check images/audio data or transform video # llm with tts & stt. if you use a realtime (sts capable) llm the tts, stt and vad aren't needed llm=llm, tts=cartesia.TTS(), stt=deepgram.STT(), - turn_detection=smart_turn.TurnDetection(buffer_duration=2.0, confidence_threshold=0.5), # Enable turn detection with FAL/ Smart turn - #vad=silero.VAD(), + turn_detection=smart_turn.TurnDetection( + buffer_duration=2.0, confidence_threshold=0.5 + ), # Enable turn detection with FAL/ Smart turn + # vad=silero.VAD(), # realtime version (vad, tts and stt not needed) # llm=openai.Realtime() ) @@ -37,17 +42,21 @@ async def start_agent() -> None: # Example 1: standardized simple response # await agent.llm.simple_response("chat with the user about the weather.") # Example 2: use native openAI create response - # await llm.create_response(input=[ - # { - # "role": "user", - # "content": [ - # {"type": "input_text", "text": "Tell me a short poem about this image"}, - # {"type": "input_image", "image_url": f"https://images.unsplash.com/photo-1757495361144-0c2bfba62b9e?q=80&w=2340&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"}, - # ], - # } - # ],) + # await llm.create_response(input=[ + # { + # "role": "user", + # "content": [ + # {"type": "input_text", "text": "Tell me a short poem about this image"}, + # {"type": "input_image", "image_url": f"https://images.unsplash.com/photo-1757495361144-0c2bfba62b9e?q=80&w=2340&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"}, + # ], + # } + # ],) # run till the call ends + # await agent.say("Hello, how are you?") + # await asyncio.sleep(5) + + await agent.simple_response("tell me something interesting in a short sentence") await agent.finish() diff --git a/examples/02_golf_coach_example/golf_coach_example.py b/examples/02_golf_coach_example/golf_coach_example.py index bb74f9e4..2a11538b 100644 --- a/examples/02_golf_coach_example/golf_coach_example.py +++ b/examples/02_golf_coach_example/golf_coach_example.py @@ -11,12 +11,14 @@ async def start_agent() -> None: agent = Agent( - edge=getstream.Edge(), # use stream for edge video transport + edge=getstream.Edge(), # use stream for edge video transport agent_user=User(name="AI golf coach"), - instructions="Read @golf_coach.md", # read the golf coach markdown instructions - llm=gemini.Realtime(fps=10), # Careful with FPS can get expensive + instructions="Read @golf_coach.md", # read the golf coach markdown instructions + llm=gemini.Realtime(fps=3), # Careful with FPS can get expensive # llm=openai.Realtime(fps=10), use this to switch to openai - processors=[ultralytics.YOLOPoseProcessor(model_path="yolo11n-pose.pt")], # realtime pose detection with yolo + processors=[ + ultralytics.YOLOPoseProcessor(model_path="yolo11n-pose.pt") + ], # realtime pose detection with yolo ) await agent.create_user() @@ -28,7 +30,9 @@ async def start_agent() -> None: with await agent.join(call): await agent.edge.open_demo(call) # all LLMs support a simple_response method and a more advanced native method (so you can always use the latest LLM features) - await agent.llm.simple_response(text="Say hi. After the user does their golf swing offer helpful feedback.") + await agent.llm.simple_response( + text="Say hi. After the user does their golf swing offer helpful feedback." + ) # Gemini's native API is available here # agent.llm.send_realtime_input(text="Hello world") await agent.finish() # run till the call ends diff --git a/plugins/cartesia/pyproject.toml b/plugins/cartesia/pyproject.toml index 8ab0b340..8dad50d2 100644 --- a/plugins/cartesia/pyproject.toml +++ b/plugins/cartesia/pyproject.toml @@ -12,7 +12,7 @@ requires-python = ">=3.10" license = "MIT" dependencies = [ "vision-agents", - "cartesia>=2.0.5", + "cartesia>=2.0.9", ] [project.urls] diff --git a/plugins/deepgram/pyproject.toml b/plugins/deepgram/pyproject.toml index d0d3241f..066e1fa3 100644 --- a/plugins/deepgram/pyproject.toml +++ b/plugins/deepgram/pyproject.toml @@ -39,6 +39,6 @@ dev = [ "pytest>=8.4.1", "pytest-asyncio>=1.0.0", "soundfile>=0.13.1", - "torchaudio>=2.7.1", + "torchvision>=0.20.0", "scipy>=1.15.3,<1.16", ] diff --git a/plugins/deepgram/tests/test_stt.py b/plugins/deepgram/tests/test_stt.py index 88cec39b..abdeef80 100644 --- a/plugins/deepgram/tests/test_stt.py +++ b/plugins/deepgram/tests/test_stt.py @@ -15,6 +15,7 @@ ListenV1ResultsMetadata, ) from getstream.video.rtc.track_util import PcmData +from torchvision.io.video import av from vision_agents.core.stt.events import ( STTErrorEvent, STTPartialTranscriptEvent, @@ -142,22 +143,34 @@ def mia_metadata(): @pytest.fixture def audio_data(mia_mp3_path): """Load and prepare the audio data for testing.""" - import numpy as np - import torch - import torchaudio from scipy import signal - # Load the mp3 file - waveform, original_sample_rate = torchaudio.load(mia_mp3_path) - - # Convert to mono if stereo - if waveform.shape[0] > 1: - waveform = torch.mean(waveform, dim=0, keepdim=True) - - # Convert to numpy array - data = waveform.numpy().squeeze() - - # Resample to 16kHz if needed (Deepgram's preferred rate) + # Load the mp3 file using PyAV + container = av.open(mia_mp3_path) + audio_stream = container.streams.audio[0] + original_sample_rate = audio_stream.sample_rate + + # Read all audio frames + samples = [] + for frame in container.decode(audio_stream): + # Convert to numpy array + frame_array = frame.to_ndarray() + if len(frame_array.shape) > 1: + # Convert stereo to mono by averaging channels + frame_array = np.mean(frame_array, axis=0) + samples.append(frame_array) + + # Concatenate all samples + data = np.concatenate(samples) + container.close() + + # Convert to float32 if needed + if data.dtype == np.int16: + data = data.astype(np.float32) / 32768.0 + elif data.dtype == np.int32: + data = data.astype(np.float32) / 2147483648.0 + + # Resample to 48kHz if needed (Deepgram's preferred rate) target_sample_rate = 48000 if original_sample_rate != target_sample_rate: number_of_samples = round( @@ -439,8 +452,11 @@ async def mock_send_control(message: ListenV1ControlMessage): ) assert close_message.type == "CloseStream" + @pytest.mark.asyncio -@patch("vision_agents.plugins.deepgram.stt.AsyncDeepgramClient", MockAsyncDeepgramClient) +@patch( + "vision_agents.plugins.deepgram.stt.AsyncDeepgramClient", MockAsyncDeepgramClient +) async def test_real_time_transcript_emission(): """ Test that transcripts are emitted in real-time without needing a second audio chunk. @@ -504,7 +520,9 @@ async def on_error(event: STTErrorEvent): @pytest.mark.asyncio -@patch("vision_agents.plugins.deepgram.stt.AsyncDeepgramClient", MockAsyncDeepgramClient) +@patch( + "vision_agents.plugins.deepgram.stt.AsyncDeepgramClient", MockAsyncDeepgramClient +) async def test_real_time_partial_transcript_emission(): """ Test that partial transcripts are emitted in real-time. @@ -576,7 +594,9 @@ async def on_partial_transcript(event: STTPartialTranscriptEvent): @pytest.mark.asyncio -@patch("vision_agents.plugins.deepgram.stt.AsyncDeepgramClient", MockAsyncDeepgramClient) +@patch( + "vision_agents.plugins.deepgram.stt.AsyncDeepgramClient", MockAsyncDeepgramClient +) async def test_real_time_error_emission(): """ Test that errors are emitted in real-time. @@ -618,7 +638,9 @@ async def on_error(event: STTErrorEvent): @pytest.mark.asyncio -@patch("vision_agents.plugins.deepgram.stt.AsyncDeepgramClient", MockAsyncDeepgramClient) +@patch( + "vision_agents.plugins.deepgram.stt.AsyncDeepgramClient", MockAsyncDeepgramClient +) async def test_close_cleanup(): """ Test that the STT service is properly closed and cleaned up. @@ -657,7 +679,9 @@ async def on_transcript(event: STTTranscriptEvent): @pytest.mark.asyncio -@patch("vision_agents.plugins.deepgram.stt.AsyncDeepgramClient", MockAsyncDeepgramClient) +@patch( + "vision_agents.plugins.deepgram.stt.AsyncDeepgramClient", MockAsyncDeepgramClient +) async def test_asynchronous_mode_behavior(): """ Test that Deepgram operates in asynchronous mode: @@ -703,7 +727,6 @@ async def on_transcript(event: STTTranscriptEvent): await stt.close() - @pytest.mark.integration @pytest.mark.asyncio async def test_deepgram_with_real_api_keep_alive(): diff --git a/plugins/gemini/tests/test_gemini_llm.py b/plugins/gemini/tests/test_gemini_llm.py index 093c3969..a83540b5 100644 --- a/plugins/gemini/tests/test_gemini_llm.py +++ b/plugins/gemini/tests/test_gemini_llm.py @@ -5,7 +5,11 @@ from vision_agents.core.agents.conversation import InMemoryConversation, Message from vision_agents.plugins.gemini.gemini_llm import GeminiLLM -from vision_agents.core.llm.events import LLMResponseChunkEvent +from vision_agents.core.llm.events import ( + LLMResponseChunkEvent, + LLMResponseCompletedEvent, +) +from vision_agents.plugins.gemini import events load_dotenv() @@ -74,3 +78,129 @@ async def test_native_memory(self, llm: GeminiLLM): message="How many paws are there in the room?" ) assert "8" in response.text or "eight" in response.text + + @pytest.mark.integration + async def test_events(self, llm: GeminiLLM): + """Test that LLM events are properly emitted during streaming responses.""" + # Track events and their content + chunk_events = [] + complete_events = [] + gemini_response_events = [] + error_events = [] + + # Register event handlers BEFORE making the API call + @llm.events.subscribe + async def handle_chunk_event(event: LLMResponseChunkEvent): + chunk_events.append(event) + + @llm.events.subscribe + async def handle_complete_event(event: LLMResponseCompletedEvent): + complete_events.append(event) + + @llm.events.subscribe + async def handle_gemini_response_event(event: events.GeminiResponseEvent): + gemini_response_events.append(event) + + @llm.events.subscribe + async def handle_error_event(event: events.GeminiErrorEvent): + error_events.append(event) + + # Make API call that should generate streaming events + response = await llm.send_message( + message="Create a small story about the weather in the Netherlands. Make it at least 2 paragraphs long." + ) + + # Wait for all events to be processed + await llm.events.wait() + + # Verify response was generated + assert response.text, "Response should have text content" + assert len(response.text) > 50, "Response should be substantial" + + # Verify chunk events were emitted + assert len(chunk_events) > 0, ( + "Should have received chunk events during streaming" + ) + + # Verify completion event was emitted + assert len(complete_events) > 0, "Should have received completion event" + assert len(complete_events) == 1, "Should have exactly one completion event" + + # Verify Gemini response events were emitted + assert len(gemini_response_events) > 0, ( + "Should have received Gemini response events" + ) + + # Verify no error events were emitted + assert len(error_events) == 0, ( + f"Should not have error events, but got: {error_events}" + ) + + # Verify chunk events have proper content and item_id + total_delta_text = "" + chunk_item_ids = set() + content_indices = [] + for chunk_event in chunk_events: + assert chunk_event.delta is not None, ( + "Chunk events should have delta content" + ) + assert isinstance(chunk_event.delta, str), "Delta should be a string" + assert chunk_event.item_id is not None, ( + "Chunk events should have non-null item_id" + ) + assert chunk_event.item_id != "", ( + "Chunk events should have non-empty item_id" + ) + chunk_item_ids.add(chunk_event.item_id) + total_delta_text += chunk_event.delta + + # Validate content_index: should be sequential (0, 1, 2, ...) or None + if chunk_event.content_index is not None: + content_indices.append(chunk_event.content_index) + + # Verify content_index sequencing if any are provided + if content_indices: + # Should be sequential starting from 0 + expected_indices = list(range(len(content_indices))) + assert content_indices == expected_indices, ( + f"content_index should be sequential (0, 1, 2, ...), but got: {content_indices}" + ) + + # Verify completion event has proper content and item_id + complete_event = complete_events[0] + assert complete_event.text == response.text, ( + "Completion event text should match response text" + ) + assert complete_event.original is not None, ( + "Completion event should have original response" + ) + assert complete_event.item_id is not None, ( + "Completion event should have non-null item_id" + ) + assert complete_event.item_id != "", ( + "Completion event should have non-empty item_id" + ) + + # Verify that completion event item_id matches chunk event item_ids + assert complete_event.item_id in chunk_item_ids, ( + f"Completion event item_id '{complete_event.item_id}' should match one of the chunk event item_ids: {chunk_item_ids}" + ) + + # Verify that chunk deltas reconstruct the final text (approximately) + # Note: There might be slight differences due to formatting, so we check for substantial overlap + assert len(total_delta_text) > 0, "Should have accumulated delta text" + assert len(total_delta_text) >= len(response.text) * 0.8, ( + "Delta text should be substantial portion of final text" + ) + + # Verify event ordering: chunks should come before completion + # This is implicit since we're using async/await, but we can verify the structure + assert len(chunk_events) >= 1, ( + "Should have at least one chunk before completion" + ) + + # Verify Gemini response events contain expected content + for gemini_event in gemini_response_events: + assert gemini_event.response_chunk is not None, ( + "Gemini response events should have response_chunk" + ) diff --git a/plugins/gemini/vision_agents/plugins/gemini/gemini_llm.py b/plugins/gemini/vision_agents/plugins/gemini/gemini_llm.py index 2994e3ae..553ae147 100644 --- a/plugins/gemini/vision_agents/plugins/gemini/gemini_llm.py +++ b/plugins/gemini/vision_agents/plugins/gemini/gemini_llm.py @@ -1,3 +1,4 @@ +import uuid from typing import Optional, List, TYPE_CHECKING, Any, Dict from google import genai @@ -100,19 +101,21 @@ async def send_message(self, *args, **kwargs): text_parts : List[str] = [] final_chunk = None pending_calls: List[NormalizedToolCallItem] = [] - - for chunk in iterator: + + # Gemini API does not have an item_id, we create it here and add it to all events + item_id = str(uuid.uuid4()) + + for idx, chunk in enumerate(iterator): response_chunk: GenerateContentResponse = chunk final_chunk = response_chunk - llm_response_optional = self._standardize_and_emit_event(response_chunk, text_parts) + self._standardize_and_emit_event(response_chunk, text_parts, item_id, idx) + # collect function calls as they stream try: chunk_calls = self._extract_tool_calls_from_stream_chunk(chunk) pending_calls.extend(chunk_calls) except Exception: pass # Ignore errors in chunk processing - if llm_response_optional is not None: - llm_response = llm_response_optional # Check if there were function calls in the response if pending_calls: @@ -147,12 +150,11 @@ async def send_message(self, *args, **kwargs): follow_up_last = None next_calls = [] - for chk in follow_up_iter: + for idx, chk in enumerate(follow_up_iter): follow_up_last = chk - llm_response_optional = self._standardize_and_emit_event(chk, follow_up_text_parts) - if llm_response_optional is not None: - llm_response = llm_response_optional - + # TODO: unclear if this is correct (item_id and idx) + self._standardize_and_emit_event(chk, follow_up_text_parts, item_id, idx) + # Check for new function calls try: chunk_calls = self._extract_tool_calls_from_stream_chunk(chk) @@ -172,7 +174,8 @@ async def send_message(self, *args, **kwargs): self.events.send(LLMResponseCompletedEvent( plugin_name="gemini", original=llm_response.original, - text=llm_response.text + text=llm_response.text, + item_id=item_id, )) # Return the LLM response @@ -198,7 +201,7 @@ def _normalize_message(gemini_input) -> List["Message"]: return messages - def _standardize_and_emit_event(self, chunk: GenerateContentResponse, text_parts: List[str]) -> Optional[LLMResponseEvent[Any]]: + def _standardize_and_emit_event(self, chunk: GenerateContentResponse, text_parts: List[str], item_id: str, idx: int) -> Optional[LLMResponseEvent[Any]]: """ Forwards the events and also send out a standardized version (the agent class hooks into that) """ @@ -212,10 +215,8 @@ def _standardize_and_emit_event(self, chunk: GenerateContentResponse, text_parts if hasattr(chunk, 'text') and chunk.text: self.events.send(LLMResponseChunkEvent( plugin_name="gemini", - content_index=0, - item_id="", - output_index=0, - sequence_number=0, + content_index=idx, + item_id=item_id, delta=chunk.text, )) text_parts.append(chunk.text) diff --git a/plugins/gemini/vision_agents/plugins/gemini/gemini_realtime.py b/plugins/gemini/vision_agents/plugins/gemini/gemini_realtime.py index 651a06c4..75130572 100644 --- a/plugins/gemini/vision_agents/plugins/gemini/gemini_realtime.py +++ b/plugins/gemini/vision_agents/plugins/gemini/gemini_realtime.py @@ -6,13 +6,29 @@ from google import genai from google.genai.live import AsyncSession from google.genai.types import SessionResumptionConfigDict -from google.genai.types import LiveConnectConfigDict, Modality, SpeechConfigDict, VoiceConfigDict, \ - PrebuiltVoiceConfigDict, AudioTranscriptionConfigDict, RealtimeInputConfigDict, TurnCoverage, \ - ContextWindowCompressionConfigDict, SlidingWindowDict, HttpOptions, LiveServerMessage, Blob, Part +from google.genai.types import ( + LiveConnectConfigDict, + Modality, + SpeechConfigDict, + VoiceConfigDict, + PrebuiltVoiceConfigDict, + AudioTranscriptionConfigDict, + RealtimeInputConfigDict, + TurnCoverage, + ContextWindowCompressionConfigDict, + SlidingWindowDict, + HttpOptions, + LiveServerMessage, + Blob, + Part, +) from vision_agents.core.edge.types import Participant from vision_agents.core.llm import realtime -from vision_agents.core.llm.events import RealtimeAudioOutputEvent, LLMResponseChunkEvent +from vision_agents.core.llm.events import ( + RealtimeAudioOutputEvent, + LLMResponseChunkEvent, +) from vision_agents.core.llm.llm_types import ToolSchema, NormalizedToolCallItem from vision_agents.core.processors import Processor from vision_agents.core.utils.utils import frame_to_png_bytes @@ -56,18 +72,27 @@ class Realtime(realtime.Realtime): - Audio output always uses a sample rate of 24kHz. - Input audio is natively 16kHz, but the Live API will resample if needed """ - model : str + + model: str session_resumption_id: Optional[str] = None config: LiveConnectConfigDict - connected : bool = False - - def __init__(self, model: str=DEFAULT_MODEL, config: Optional[LiveConnectConfigDict]=None, http_options: Optional[HttpOptions] = None, client: Optional[genai.Client] = None, api_key: Optional[str] = None , **kwargs) -> None: + connected: bool = False + + def __init__( + self, + model: str = DEFAULT_MODEL, + config: Optional[LiveConnectConfigDict] = None, + http_options: Optional[HttpOptions] = None, + client: Optional[genai.Client] = None, + api_key: Optional[str] = None, + **kwargs, + ) -> None: super().__init__(**kwargs) self.model = model if http_options is None: http_options = HttpOptions(api_version="v1alpha") - if client is None: + if client is None: if api_key: client = genai.Client(api_key=api_key, http_options=http_options) else: @@ -85,8 +110,12 @@ def __init__(self, model: str=DEFAULT_MODEL, config: Optional[LiveConnectConfigD self._session: Optional[AsyncSession] = None self._receive_task: Optional[asyncio.Task[Any]] = None - async def simple_response(self, text: str, processors: Optional[List[Processor]] = None, - participant: Optional[Participant] = None): + async def simple_response( + self, + text: str, + processors: Optional[List[Processor]] = None, + participant: Optional[Participant] = None, + ): """ Simple response standardizes how to send a text instruction to this LLM. @@ -98,8 +127,9 @@ async def simple_response(self, text: str, processors: Optional[List[Processor]] self.logger.info("Simple response called with text: %s", text) await self.send_realtime_input(text=text) - - async def simple_audio_response(self, pcm: PcmData): + async def simple_audio_response( + self, pcm: PcmData, participant: Optional[Participant] = None + ): """ Simple audio response standardizes how to send audio to the LLM @@ -108,10 +138,15 @@ async def simple_audio_response(self, pcm: PcmData): llm.simple_response(pcm) For more advanced use cases you can use the native send_realtime_input + + Args: + pcm: PCM audio data to send + participant: Optional participant information for the audio source """ if not self.connected: return + self._current_participant = participant self.logger.debug(f"Sending audio to gemini: {pcm.duration}") # Build blob and send directly audio_bytes = pcm.samples.tobytes() @@ -125,9 +160,7 @@ async def send_realtime_input(self, *args, **kwargs): send_realtime_input wraps the native send_realtime_input """ try: - await self._require_session().send_realtime_input( - *args, **kwargs - ) + await self._require_session().send_realtime_input(*args, **kwargs) except Exception as e: # reconnect here in some cases self.logger.error(e) @@ -141,16 +174,16 @@ async def send_client_content(self, *args, **kwargs): """ Don't use send client content, it can cause bugs when combined with send_realtime_input """ - await self._require_session().send_client_content( - *args, **kwargs - ) + await self._require_session().send_client_content(*args, **kwargs) async def connect(self): """ Connect to Gemini's websocket """ self.logger.info("Connecting to gemini live, config set to %s", self.config) - self._session_context = self.client.aio.live.connect(model=self.model, config=self._get_config_with_resumption()) + self._session_context = self.client.aio.live.connect( + model=self.model, config=self._get_config_with_resumption() + ) self._session = await self._session_context.__aenter__() self.connected = True self.logger.info("Gemini live connected to session %s", self._session) @@ -158,7 +191,6 @@ async def connect(self): # Start the receive loop task self._receive_task = asyncio.create_task(self._receive_loop()) - async def _reconnect(self): await self.connect() @@ -173,24 +205,72 @@ async def _receive_loop(self): async for response in self._require_session().receive(): server_message: LiveServerMessage = response - is_input_transcript = server_message and server_message.server_content and server_message.server_content.input_transcription - is_output_transcript = server_message and server_message.server_content and server_message.server_content.output_transcription - is_response = server_message and server_message.server_content and server_message.server_content.model_turn - is_interrupt = server_message and server_message.server_content and server_message.server_content.interrupted - is_turn_complete = server_message and server_message.server_content and server_message.server_content.turn_complete - is_generation_complete = server_message and server_message.server_content and server_message.server_content.generation_complete + is_input_transcript = ( + server_message + and server_message.server_content + and server_message.server_content.input_transcription + ) + is_output_transcript = ( + server_message + and server_message.server_content + and server_message.server_content.output_transcription + ) + is_response = ( + server_message + and server_message.server_content + and server_message.server_content.model_turn + ) + is_interrupt = ( + server_message + and server_message.server_content + and server_message.server_content.interrupted + ) + is_turn_complete = ( + server_message + and server_message.server_content + and server_message.server_content.turn_complete + ) + is_generation_complete = ( + server_message + and server_message.server_content + and server_message.server_content.generation_complete + ) if is_input_transcript: - # TODO: what to do with this? check with Tommaso - if server_message.server_content and server_message.server_content.input_transcription: - self.logger.info("input: %s", server_message.server_content.input_transcription.text) + if ( + server_message.server_content + and server_message.server_content.input_transcription + ): + text = ( + server_message.server_content.input_transcription.text + ) + self.logger.info("input: %s", text) + if text: + self._emit_user_speech_transcription( + text=text, original=server_message + ) elif is_output_transcript: - # TODO: what to do with this? - if server_message.server_content and server_message.server_content.output_transcription: - self.logger.info("output: %s", server_message.server_content.output_transcription.text) + if ( + server_message.server_content + and server_message.server_content.output_transcription + ): + text = ( + server_message.server_content.output_transcription.text + ) + self.logger.info("output: %s", text) + if text: + self._emit_agent_speech_transcription( + text=text, original=server_message + ) elif is_interrupt: - if server_message.server_content and server_message.server_content.interrupted: - self.logger.info("interrupted: %s", server_message.server_content.interrupted) + if ( + server_message.server_content + and server_message.server_content.interrupted + ): + self.logger.info( + "interrupted: %s", + server_message.server_content.interrupted, + ) elif is_response: # Store the resumption id so we can resume a broken connection if server_message.session_resumption_update: @@ -198,7 +278,10 @@ async def _receive_loop(self): if update.resumable and update.new_handle: self.session_resumption_id = update.new_handle - if server_message.server_content and server_message.server_content.model_turn: + if ( + server_message.server_content + and server_message.server_content.model_turn + ): parts = server_message.server_content.model_turn.parts if parts: @@ -206,41 +289,60 @@ async def _receive_loop(self): typed_part: Part = current_part if typed_part.text: if typed_part.thought: - self.logger.info("Gemini thought %s", typed_part.text) + self.logger.info( + "Gemini thought %s", typed_part.text + ) else: - self.logger.info("output: %s", typed_part.text) + self.logger.info( + "output: %s", typed_part.text + ) event = LLMResponseChunkEvent( delta=typed_part.text ) self.events.send(event) elif typed_part.inline_data: data = typed_part.inline_data.data - + # Emit audio output event audio_event = RealtimeAudioOutputEvent( plugin_name="gemini", audio_data=data, - sample_rate=24000 + sample_rate=24000, ) self.events.send(audio_event) - - await self.output_track.write(data) # original 24khz here - elif hasattr(typed_part, 'function_call') and typed_part.function_call: + + await self.output_track.write( + data + ) # original 24khz here + elif ( + hasattr(typed_part, "function_call") + and typed_part.function_call + ): # Handle function calls from Gemini Live - self.logger.info(f"Received function call: {typed_part.function_call.name}") - await self._handle_function_call(typed_part.function_call) + self.logger.info( + f"Received function call: {typed_part.function_call.name}" + ) + await self._handle_function_call( + typed_part.function_call + ) else: - self.logger.debug("Unrecognized part type: %s", typed_part) + self.logger.debug( + "Unrecognized part type: %s", typed_part + ) elif is_turn_complete: self.logger.info("is_turn_complete complete") elif is_generation_complete: self.logger.info("is_generation_complete complete") elif server_message.tool_call: # Handle tool calls from Gemini Live - self.logger.info(f"Received tool call: {server_message.tool_call}") + self.logger.info( + f"Received tool call: {server_message.tool_call}" + ) await self._handle_tool_call(server_message.tool_call) else: - self.logger.warning("Unrecognized event structure for gemini %s", server_message) + self.logger.warning( + "Unrecognized event structure for gemini %s", server_message + ) except Exception as e: # reconnect here for some errors self.logger.error(f"_receive_loop error: {e}") @@ -264,11 +366,11 @@ def _is_temporary_error(e: Exception): async def _close_impl(self): self.connected = False - if hasattr(self, '_receive_task') and self._receive_task: + if hasattr(self, "_receive_task") and self._receive_task: self._receive_task.cancel() await self._receive_task - if hasattr(self, '_session_context') and self._session_context: + if hasattr(self, "_session_context") and self._session_context: # Properly close the session using the context manager's __aexit__ try: await self._session_context.__aexit__(None, None, None) @@ -277,30 +379,29 @@ async def _close_impl(self): self._session_context = None self._session = None - async def _watch_video_track(self, track: Any, **kwargs) -> None: """ Start sending video frames to Gemini using VideoForwarder. We follow the on_track from Stream. If video is turned on or off this gets forwarded. - + Args: track: Video track to watch shared_forwarder: Optional shared VideoForwarder to use instead of creating a new one """ - shared_forwarder = kwargs.get('shared_forwarder') - + shared_forwarder = kwargs.get("shared_forwarder") + if self._video_forwarder is not None and shared_forwarder is None: self.logger.warning("Video sender already running, stopping previous one") await self._stop_watching_video_track() - + if shared_forwarder is not None: # Use the shared forwarder - just register as a consumer self._video_forwarder = shared_forwarder - self.logger.info(f"🎥 Gemini subscribing to shared VideoForwarder at {self.fps} FPS") + self.logger.info( + f"🎥 Gemini subscribing to shared VideoForwarder at {self.fps} FPS" + ) await self._video_forwarder.start_event_consumer( - self._send_video_frame, - fps=float(self.fps), - consumer_name="gemini" + self._send_video_frame, fps=float(self.fps), consumer_name="gemini" ) else: # Create our own VideoForwarder with the input track (legacy behavior) @@ -310,13 +411,13 @@ async def _watch_video_track(self, track: Any, **kwargs) -> None: fps=float(self.fps), name="gemini_forwarder", ) - + # Start the forwarder await self._video_forwarder.start() - + # Start the callback consumer that sends frames to Gemini await self._video_forwarder.start_event_consumer(self._send_video_frame) - + self.logger.info(f"Started video forwarding with {self.fps} FPS") async def _stop_watching_video_track(self) -> None: @@ -339,7 +440,9 @@ async def _send_video_frame(self, frame: av.VideoFrame) -> None: except Exception as e: self.logger.error(f"Error sending video frame: {e}") - def _create_config(self, config: Optional[LiveConnectConfigDict]=None) -> LiveConnectConfigDict: + def _create_config( + self, config: Optional[LiveConnectConfigDict] = None + ) -> LiveConnectConfigDict: """ _create_config combines the default config with your settings """ @@ -362,10 +465,10 @@ def _create_config(self, config: Optional[LiveConnectConfigDict]=None) -> LiveCo sliding_window=SlidingWindowDict(target_tokens=12800), ), ) - + # Note: Tools will be added later in _get_config_with_resumption() # when functions are actually registered - + if config is not None: for k, v in config.items(): if k in default_config: @@ -379,12 +482,14 @@ def _get_config_with_resumption(self) -> LiveConnectConfigDict: config = self.config.copy() # resume if we have a session resumption id/handle if self.session_resumption_id: - resumption_config: SessionResumptionConfigDict = {"handle": self.session_resumption_id} # type: ignore[typeddict-item] + resumption_config: SessionResumptionConfigDict = { + "handle": self.session_resumption_id + } # type: ignore[typeddict-item] config["session_resumption"] = resumption_config # type: ignore[typeddict-item] # set the instructions # TODO: potentially we can share the markdown as files/parts.. might do better TBD config["system_instruction"] = self._build_enhanced_instructions() - + # Add tools if available - Gemini Live uses similar format to regular Gemini tools_spec = self.get_available_functions() if tools_spec: @@ -395,58 +500,69 @@ def _get_config_with_resumption(self) -> LiveConnectConfigDict: self.logger.info(f"Added {len(tools_spec)} tools to Gemini Live config") else: self.logger.debug("No tools available - function calling will not work") - + return config - def _convert_tools_to_provider_format(self, tools: List[ToolSchema]) -> List[Dict[str, Any]]: + def _convert_tools_to_provider_format( + self, tools: List[ToolSchema] + ) -> List[Dict[str, Any]]: """ Convert ToolSchema objects to Gemini Live format. - + Args: tools: List of ToolSchema objects - + Returns: List of tools in Gemini Live format """ function_declarations = [] for tool in tools: - function_declarations.append({ - "name": tool["name"], - "description": tool.get("description", ""), - "parameters": tool["parameters_schema"] - }) - + function_declarations.append( + { + "name": tool["name"], + "description": tool.get("description", ""), + "parameters": tool["parameters_schema"], + } + ) + # Return as dict with function_declarations (similar to regular Gemini format) return [{"function_declarations": function_declarations}] - def _extract_tool_calls_from_response(self, response: Any) -> List[NormalizedToolCallItem]: + def _extract_tool_calls_from_response( + self, response: Any + ) -> List[NormalizedToolCallItem]: """ Extract tool calls from Gemini Live response. - + Args: response: Gemini Live response object - + Returns: List of normalized tool call items """ calls: List[NormalizedToolCallItem] = [] - + try: # Check for function calls in the response - if hasattr(response, 'server_content') and response.server_content: - if hasattr(response.server_content, 'model_turn') and response.server_content.model_turn: + if hasattr(response, "server_content") and response.server_content: + if ( + hasattr(response.server_content, "model_turn") + and response.server_content.model_turn + ): parts = response.server_content.model_turn.parts for part in parts: - if hasattr(part, 'function_call') and part.function_call: + if hasattr(part, "function_call") and part.function_call: call_item: NormalizedToolCallItem = { "type": "tool_call", "name": getattr(part.function_call, "name", "unknown"), - "arguments_json": getattr(part.function_call, "args", {}) + "arguments_json": getattr( + part.function_call, "args", {} + ), } calls.append(call_item) except Exception as e: self.logger.debug(f"Error extracting tool calls from response: {e}") - + return calls async def _handle_tool_call(self, tool_call: Any) -> None: @@ -454,7 +570,7 @@ async def _handle_tool_call(self, tool_call: Any) -> None: Handle tool calls from Gemini Live. """ try: - if hasattr(tool_call, 'function_calls') and tool_call.function_calls: + if hasattr(tool_call, "function_calls") and tool_call.function_calls: for function_call in tool_call.function_calls: await self._handle_function_call(function_call) except Exception as e: @@ -463,7 +579,7 @@ async def _handle_tool_call(self, tool_call: Any) -> None: async def _handle_function_call(self, function_call: Any) -> None: """ Handle function calls from Gemini Live responses. - + Args: function_call: Function call object from Gemini Live """ @@ -472,14 +588,16 @@ async def _handle_function_call(self, function_call: Any) -> None: tool_call = { "name": getattr(function_call, "name", "unknown"), "arguments_json": getattr(function_call, "args", {}), - "id": getattr(function_call, "id", None) + "id": getattr(function_call, "id", None), } - - self.logger.info(f"Executing function call: {tool_call['name']} with args: {tool_call['arguments_json']}") - + + self.logger.info( + f"Executing function call: {tool_call['name']} with args: {tool_call['arguments_json']}" + ) + # Execute using existing tool execution infrastructure tc, result, error = await self._run_one_tool(tool_call, timeout_s=30) - + # Prepare response data if error: response_data = {"error": str(error)} @@ -490,29 +608,36 @@ async def _handle_function_call(self, function_call: Any) -> None: response_data = {"result": result} else: response_data = result - self.logger.info(f"Function call {tool_call['name']} succeeded: {response_data}") - + self.logger.info( + f"Function call {tool_call['name']} succeeded: {response_data}" + ) + # Send function response back to Gemini Live session call_id_val = tool_call.get("id") await self._send_function_response( - str(tool_call["name"]), - response_data, - str(call_id_val) if call_id_val else None + str(tool_call["name"]), + response_data, + str(call_id_val) if call_id_val else None, ) - + except Exception as e: self.logger.error(f"Error handling function call: {e}") # Send error response back await self._send_function_response( - getattr(function_call, "name", "unknown"), - {"error": str(e)}, - getattr(function_call, "id", None) + getattr(function_call, "name", "unknown"), + {"error": str(e)}, + getattr(function_call, "id", None), ) - async def _send_function_response(self, function_name: str, response_data: Dict[str, Any], call_id: Optional[str] = None) -> None: + async def _send_function_response( + self, + function_name: str, + response_data: Dict[str, Any], + call_id: Optional[str] = None, + ) -> None: """ Send function response back to Gemini Live session. - + Args: function_name: Name of the function that was called response_data: Response data to send back @@ -521,20 +646,26 @@ async def _send_function_response(self, function_name: str, response_data: Dict[ try: # Create function response part from google.genai import types - + function_response = types.FunctionResponse( id=call_id, # Use the call_id if provided name=function_name, - response=response_data + response=response_data, ) - + # Send the function response using the correct method # The Gemini Live API uses send_tool_response for function responses - await self._require_session().send_tool_response(function_responses=[function_response]) - self.logger.debug(f"Sent function response for {function_name}: {response_data}") - + await self._require_session().send_tool_response( + function_responses=[function_response] + ) + self.logger.debug( + f"Sent function response for {function_name}: {response_data}" + ) + except Exception as e: - self.logger.error(f"Error sending function response for {function_name}: {e}") + self.logger.error( + f"Error sending function response for {function_name}: {e}" + ) def _require_session(self) -> AsyncSession: if not self._session: diff --git a/plugins/getstream/tests/test_message_chunking.py b/plugins/getstream/tests/test_message_chunking.py new file mode 100644 index 00000000..e2b184e6 --- /dev/null +++ b/plugins/getstream/tests/test_message_chunking.py @@ -0,0 +1,482 @@ +""" +Unit tests for message chunking logic in StreamConversation. + +Tests the markdown-aware chunking algorithm and chunk management. +""" +import pytest +from unittest.mock import Mock, AsyncMock +from vision_agents.plugins.getstream.stream_conversation import StreamConversation + + +class TestMessageChunking: + """Test suite for message chunking logic.""" + + @pytest.fixture + def conversation(self): + """Create a StreamConversation with small chunk size for testing.""" + mock_channel = Mock() + mock_channel.channel_type = "messaging" + mock_channel.channel_id = "test-channel" + + conversation = StreamConversation( + instructions="Test", + messages=[], + channel=mock_channel, + chunk_size=50, # Small size for easy testing + ) + return conversation + + def test_no_chunking_needed(self, conversation): + """Test that small messages aren't chunked.""" + text = "Hello world" + chunks = conversation._smart_chunk(text, 50) + + assert len(chunks) == 1 + assert chunks[0] == "Hello world" + + def test_simple_chunking(self, conversation): + """Test basic chunking at line boundaries.""" + text = "Line 1 is here\nLine 2 is here\nLine 3 is here\nLine 4 is here\nLine 5 is here" + chunks = conversation._smart_chunk(text, 30) + + # Should split into multiple chunks + assert len(chunks) > 1 + + # Verify all content is preserved + reconstructed = '\n'.join(chunks) + # Remove extra whitespace for comparison + assert reconstructed.replace('\n\n', '\n').strip() == text.strip() + + def test_code_block_not_split(self, conversation): + """Test that code blocks stay together.""" + text = """Here is some code: + +```python +def hello(): + return "world" +``` + +And more text.""" + + chunks = conversation._smart_chunk(text, 100) + + # Code block should stay intact in one chunk + has_complete_code_block = any('```python' in chunk and 'return "world"' in chunk and '```' in chunk for chunk in chunks) + assert has_complete_code_block, "Code block was split incorrectly" + + def test_code_block_in_own_chunk(self, conversation): + """Test that large code blocks get their own chunk.""" + text = """Short intro. + +```python +def function_one(): + return "value" + +def function_two(): + return "another" +``` + +More text after.""" + + chunks = conversation._smart_chunk(text, 60) + + # Should create multiple chunks + assert len(chunks) >= 2 + + # Find chunk with code block + code_chunk = next((c for c in chunks if '```python' in c), None) + assert code_chunk is not None + assert '```' in code_chunk # Should have closing backticks + + def test_very_large_code_block_split(self, conversation): + """Test that code blocks larger than max_size are split at newlines.""" + # Create a code block that's too large + code_lines = ["def function_{}():".format(i) for i in range(20)] + text = "```python\n" + "\n".join(code_lines) + "\n```" + + chunks = conversation._smart_chunk(text, 80) + + # Should be split despite being in code block + assert len(chunks) > 1 + + # All chunks should have content + for chunk in chunks: + assert len(chunk) > 0 + + def test_paragraph_chunking(self, conversation): + """Test chunking at paragraph boundaries.""" + text = "Paragraph one with some text here.\n\nParagraph two with more text.\n\nParagraph three here." + + chunks = conversation._smart_chunk(text, 40) + + # Should split at paragraph boundaries + assert len(chunks) >= 2 + + # Content preserved + reconstructed = '\n'.join(chunk.strip() for chunk in chunks) + # Normalize whitespace for comparison + assert 'Paragraph one' in reconstructed + assert 'Paragraph two' in reconstructed + assert 'Paragraph three' in reconstructed + + def test_empty_text(self, conversation): + """Test chunking empty text.""" + chunks = conversation._smart_chunk("", 50) + + assert len(chunks) == 1 + assert chunks[0] == "" + + def test_exact_boundary(self, conversation): + """Test text that's exactly at the boundary.""" + text = "A" * 50 # Exactly chunk_size + chunks = conversation._smart_chunk(text, 50) + + assert len(chunks) == 1 + assert chunks[0] == text + + def test_one_char_over(self, conversation): + """Test text that's one character over the limit.""" + text = "A" * 51 + chunks = conversation._smart_chunk(text, 50) + + # Should be split (no newlines, so splits anywhere) + assert len(chunks) >= 1 + + def test_multiple_code_blocks(self, conversation): + """Test text with multiple code blocks.""" + text = """First block: + +```python +code1() +``` + +Middle text. + +```javascript +code2() +``` + +End text.""" + + chunks = conversation._smart_chunk(text, 60) + + # Both code blocks should be complete + full_text = '\n'.join(chunks) + assert full_text.count('```python') == full_text.count('```') / 2 or '```python' in full_text + assert 'code1()' in full_text + assert 'code2()' in full_text + + def test_nested_markdown(self, conversation): + """Test text with nested markdown (lists, code, etc).""" + text = """# Header + +- List item 1 +- List item 2 + - Nested item + +Some text. + +```python +code() +```""" + + chunks = conversation._smart_chunk(text, 70) + + # Content should be preserved + full_text = '\n'.join(chunks) + assert '# Header' in full_text or 'Header' in full_text + assert 'List item 1' in full_text + assert 'code()' in full_text + + def test_split_large_block_basic(self, conversation): + """Test _split_large_block with basic text.""" + block = "Line 1\nLine 2\nLine 3\nLine 4\nLine 5\nLine 6" + chunks = conversation._split_large_block(block, 20) + + assert len(chunks) > 1 + + # Verify content preserved + reconstructed = ''.join(chunks) + assert 'Line 1' in reconstructed + assert 'Line 6' in reconstructed + + def test_split_large_block_single_long_line(self, conversation): + """Test _split_large_block with line longer than max_size.""" + block = "A" * 100 # Single line, no newlines + chunks = conversation._split_large_block(block, 20) + + # Should include the line anyway (can't split further) + assert len(chunks) >= 1 + assert "A" * 100 in ''.join(chunks) + + +class TestChunkingIntegration: + """Integration tests for chunking with mocked Stream API.""" + + @pytest.fixture + def mock_channel(self): + """Create a mock channel with tracking.""" + channel = Mock() + channel.channel_type = "messaging" + channel.channel_id = "test-channel" + + # Mock client + channel.client = Mock() + channel.client.update_message_partial = AsyncMock(return_value=Mock()) + channel.client.ephemeral_message_update = AsyncMock(return_value=Mock()) + channel.client.delete_message = AsyncMock(return_value=Mock()) # Add delete_message to client + + # Track messages sent + channel.sent_messages = [] + + async def mock_send_message(request): + mock_response = Mock() + mock_response.data.message.id = f"chunk-{len(channel.sent_messages)}" + mock_response.data.message.type = "regular" + channel.sent_messages.append(request) + return mock_response + + channel.send_message = mock_send_message + + return channel + + @pytest.fixture + def conversation(self, mock_channel): + """Create conversation with small chunk size.""" + return StreamConversation( + instructions="Test", + messages=[], + channel=mock_channel, + chunk_size=50, # Small for testing + ) + + @pytest.mark.asyncio + async def test_large_message_creates_multiple_chunks(self, conversation, mock_channel): + """Test that large messages are automatically chunked.""" + # Create a message that will need 3 chunks + large_text = "A" * 120 # 120 chars = 3 chunks at 50 chars each + + await conversation.send_message( + role="assistant", + user_id="agent", + content=large_text, + ) + + # Should have 1 message in conversation.messages + assert len(conversation.messages) == 1 + assert conversation.messages[0].content == large_text + + # Should have created 3 chunks in Stream + assert len(mock_channel.sent_messages) == 3 + + # Verify chunk metadata + for i, msg_request in enumerate(mock_channel.sent_messages): + assert msg_request.custom["chunk_index"] == i + assert msg_request.custom["total_chunks"] == 3 + assert msg_request.custom["chunk_group"] == conversation.messages[0].id + + # Only last chunk has generating=False + is_last = (i == 2) + assert msg_request.custom["generating"] == (not is_last) or not msg_request.custom["generating"] + + @pytest.mark.asyncio + async def test_streaming_grows_creates_new_chunk(self, conversation, mock_channel): + """Test that streaming content growing beyond chunk_size creates new chunks.""" + msg_id = "test-msg" + + # Start with small content (1 chunk) + await conversation.upsert_message( + role="assistant", user_id="agent", + content="Short", message_id=msg_id, content_index=0, completed=False + ) + + assert len(mock_channel.sent_messages) == 1 + assert conversation.messages[0].content == "Short" + # First chunk created with generating=True (it's the last/only chunk initially) + assert mock_channel.sent_messages[0].custom["generating"] is True + + # Add more content to exceed chunk_size (should create 2nd chunk) + await conversation.upsert_message( + role="assistant", user_id="agent", + content="X" * 60, message_id=msg_id, completed=False, replace=True + ) + + # Should have created a new chunk (2 total) + assert len(mock_channel.sent_messages) == 2 + + # Chunk 1 (last, newly created): should have generating=True + assert mock_channel.sent_messages[1].custom["chunk_index"] == 1 + assert mock_channel.sent_messages[1].custom["generating"] is True + + # First chunk should have been updated via ephemeral to generating=False (it's now intermediate) + assert mock_channel.client.ephemeral_message_update.call_count >= 1 + + # Check first chunk was updated to generating=False + first_chunk_updates = [ + call for call in mock_channel.client.ephemeral_message_update.call_args_list + if call[0][0] == "chunk-0" # First chunk ID + ] + if first_chunk_updates: + # Most recent update should have generating=False + assert first_chunk_updates[-1][1]["set"]["generating"] is False + + @pytest.mark.asyncio + async def test_completion_shrinks_deletes_chunks(self, conversation, mock_channel): + """Test that completion with shorter text deletes extra chunks.""" + msg_id = "test-msg" + + # Start with large content (3 chunks) + large_text = "X" * 120 + await conversation.upsert_message( + role="assistant", user_id="agent", + content=large_text, message_id=msg_id, completed=False + ) + + initial_chunk_count = len(mock_channel.sent_messages) + assert initial_chunk_count >= 2 + + # Complete with shorter text (1 chunk) + short_text = "Done" + await conversation.upsert_message( + role="assistant", user_id="agent", + content=short_text, message_id=msg_id, completed=True, replace=True + ) + + # Should have deleted extra chunks + assert mock_channel.client.delete_message.call_count == initial_chunk_count - 1 + + @pytest.mark.asyncio + async def test_code_block_preserved_in_chunk(self, conversation, mock_channel): + """Test that code blocks are kept intact when possible.""" + text = """Intro text. + +```python +def hello(): + return "world" +``` + +End.""" + + await conversation.send_message( + role="assistant", + user_id="agent", + content=text, + ) + + # Verify code block is complete in at least one chunk + full_text = ''.join(req.text for req in mock_channel.sent_messages) + assert '```python' in full_text + assert 'def hello():' in full_text + assert 'return "world"' in full_text + assert full_text.count('```') >= 2 # Opening and closing + + @pytest.mark.asyncio + async def test_chunk_metadata_correct(self, conversation, mock_channel): + """Test that chunk metadata is set correctly.""" + large_text = "A" * 120 # Will create 3 chunks + + message = await conversation.send_message( + role="assistant", + user_id="agent", + content=large_text, + ) + + chunks_created = len(mock_channel.sent_messages) + + # Verify metadata on all chunks + for i, req in enumerate(mock_channel.sent_messages): + assert req.custom["chunk_group"] == message.id + assert req.custom["chunk_index"] == i + assert req.custom["total_chunks"] == chunks_created + + # Only last chunk has generating (and it's False since completed=True) + is_last = (i == chunks_created - 1) + if is_last: + assert req.custom["generating"] is False + else: + assert req.custom["generating"] is False # Intermediate chunks never generating + + @pytest.mark.asyncio + async def test_streaming_chunk_generating_flag(self, conversation, mock_channel): + """Test that generating flag is only on last chunk during streaming.""" + msg_id = "test-msg" + + # Create large streaming message (3 chunks) + large_text = "X" * 120 + await conversation.upsert_message( + role="assistant", user_id="agent", + content=large_text, message_id=msg_id, completed=False + ) + + # Check the requests sent + chunks_sent = len(mock_channel.sent_messages) + assert chunks_sent >= 2 + + # Check generating flag on each chunk + for i, req in enumerate(mock_channel.sent_messages): + is_last = (i == chunks_sent - 1) + if is_last: + assert req.custom["generating"] is True, f"Last chunk {i} should have generating=True" + else: + assert req.custom["generating"] is False, f"Intermediate chunk {i} should have generating=False" + + +class TestChunkingSentenceBoundaries: + """Test chunking at sentence boundaries.""" + + def test_sentence_chunking(self): + """Test chunking respects sentence boundaries when possible.""" + conversation = StreamConversation( + instructions="Test", + messages=[], + channel=Mock(), + chunk_size=40, + ) + + text = "First sentence here. Second sentence here. Third sentence. Fourth sentence here." + chunks = conversation._smart_chunk(text, 40) + + # Should split at sentence boundaries + assert len(chunks) >= 2 + + # Content preserved + full = '\n'.join(chunks) + assert 'First sentence' in full + assert 'Fourth sentence' in full + + def test_split_large_block_preserves_content(self): + """Test that _split_large_block preserves all content.""" + conversation = StreamConversation( + instructions="Test", + messages=[], + channel=Mock(), + chunk_size=30, + ) + + block = "Line 1\nLine 2\nLine 3\nLine 4\nLine 5" + chunks = conversation._split_large_block(block, 20) + + # Verify all lines present + full = ''.join(chunks) + for i in range(1, 6): + assert f"Line {i}" in full + + def test_split_large_block_single_long_line(self): + """Test splitting when a single line exceeds max_size.""" + conversation = StreamConversation( + instructions="Test", + messages=[], + channel=Mock(), + chunk_size=30, + ) + + # Single line that's way too long + block = "A" * 100 + chunks = conversation._split_large_block(block, 20) + + # Should still create chunk(s) - can't split further + assert len(chunks) >= 1 + + # Content preserved + assert "A" * 100 in ''.join(chunks) + diff --git a/plugins/getstream/tests/test_stream_conversation.py b/plugins/getstream/tests/test_stream_conversation.py new file mode 100644 index 00000000..161a012e --- /dev/null +++ b/plugins/getstream/tests/test_stream_conversation.py @@ -0,0 +1,675 @@ +import logging +from random import shuffle + +import pytest +import uuid +import asyncio +from unittest.mock import Mock, AsyncMock +from dotenv import load_dotenv + +from getstream.models import MessageRequest, ChannelInput, MessagePaginationParams +from getstream import AsyncStream + +from vision_agents.plugins.getstream.stream_conversation import StreamConversation + +logger = logging.getLogger(__name__) + +load_dotenv() + + +class TestStreamConversation: + """Test suite for StreamConversation with unified API.""" + + @pytest.fixture + def mock_channel(self): + """Create a mock Channel.""" + channel = Mock() + channel.channel_type = "messaging" + channel.channel_id = "test-channel-123" + + # Mock the client + channel.client = Mock() + + # Create async mocks for client methods + channel.client.update_message_partial = AsyncMock(return_value=Mock()) + channel.client.ephemeral_message_update = AsyncMock(return_value=Mock()) + + # Mock send_message response + mock_response = Mock() + mock_response.data.message.id = "stream-message-123" + mock_response.data.message.type = "regular" + + # Create async mock for send_message + channel.send_message = AsyncMock(return_value=mock_response) + + return channel + + @pytest.fixture + def stream_conversation(self, mock_channel): + """Create a StreamConversation instance with mocked dependencies.""" + instructions = "You are a helpful assistant." + messages = [] + conversation = StreamConversation( + instructions=instructions, + messages=messages, + channel=mock_channel + ) + return conversation + + @pytest.mark.asyncio + async def test_send_message_simple(self, stream_conversation, mock_channel): + """Test send_message convenience method.""" + await stream_conversation.send_message( + role="user", + user_id="user123", + content="Hello", + ) + + # Verify message was added + assert len(stream_conversation.messages) == 1 + assert stream_conversation.messages[0].content == "Hello" + assert stream_conversation.messages[0].role == "user" + assert stream_conversation.messages[0].user_id == "user123" + + # Verify Stream API was called + mock_channel.send_message.assert_called_once() + call_args = mock_channel.send_message.call_args + request = call_args[0][0] + assert isinstance(request, MessageRequest) + assert request.text == "Hello" + assert request.user_id == "user123" + assert request.custom.get("generating") is False # completed=True by default + + @pytest.mark.asyncio + async def test_upsert_simple_message(self, stream_conversation, mock_channel): + """Test adding a simple non-streaming message with upsert.""" + await stream_conversation.upsert_message( + role="user", + user_id="user123", + content="Hello", + completed=True, + ) + + # Verify message was added + assert len(stream_conversation.messages) == 1 + assert stream_conversation.messages[0].content == "Hello" + assert stream_conversation.messages[0].role == "user" + assert stream_conversation.messages[0].user_id == "user123" + + # Verify Stream API was called + mock_channel.send_message.assert_called_once() + call_args = mock_channel.send_message.call_args + request = call_args[0][0] + assert isinstance(request, MessageRequest) + assert request.text == "Hello" + assert request.user_id == "user123" + + @pytest.mark.asyncio + async def test_upsert_streaming_deltas(self, stream_conversation, mock_channel): + """Test streaming message with deltas.""" + msg_id = str(uuid.uuid4()) + + # Delta 1 + await stream_conversation.upsert_message( + role="assistant", + user_id="agent", + content="Hello", + message_id=msg_id, + content_index=0, + completed=False, + ) + + assert len(stream_conversation.messages) == 1 + assert stream_conversation.messages[0].content == "Hello" + mock_channel.send_message.assert_called_once() + + # Delta 2 + mock_channel.send_message.reset_mock() + await stream_conversation.upsert_message( + role="assistant", + user_id="agent", + content=" world", + message_id=msg_id, + content_index=1, + completed=False, + ) + + assert len(stream_conversation.messages) == 1 + assert stream_conversation.messages[0].content == "Hello world" + # Should call ephemeral update, not send_message + mock_channel.send_message.assert_not_called() + mock_channel.client.ephemeral_message_update.assert_called_once() + + @pytest.mark.asyncio + async def test_upsert_streaming_completion(self, stream_conversation, mock_channel): + """Test streaming message followed by completion.""" + msg_id = str(uuid.uuid4()) + + # Streaming deltas + await stream_conversation.upsert_message( + role="assistant", + user_id="agent", + content="Hello", + message_id=msg_id, + content_index=0, + completed=False, + ) + + await stream_conversation.upsert_message( + role="assistant", + user_id="agent", + content=" world", + message_id=msg_id, + content_index=1, + completed=False, + ) + + # Completion - replace with final text + await stream_conversation.upsert_message( + role="assistant", + user_id="agent", + content="Hello world!", + message_id=msg_id, + completed=True, + replace=True, + ) + + # Should have only 1 message + assert len(stream_conversation.messages) == 1 + assert stream_conversation.messages[0].content == "Hello world!" + + # Should call update_message_partial for completion + mock_channel.client.update_message_partial.assert_called_once() + + @pytest.mark.asyncio + async def test_upsert_out_of_order_deltas(self, stream_conversation, mock_channel): + """Test that out-of-order deltas are buffered correctly.""" + msg_id = str(uuid.uuid4()) + + # Send deltas out of order + await stream_conversation.upsert_message( + role="assistant", user_id="agent", + content=" world", message_id=msg_id, content_index=1, completed=False + ) + + # Message should exist but only have content when index 0 arrives + assert len(stream_conversation.messages) == 1 + assert stream_conversation.messages[0].content == "" # Waiting for index 0 + + # Now send index 0 + await stream_conversation.upsert_message( + role="assistant", user_id="agent", + content="Hello", message_id=msg_id, content_index=0, completed=False + ) + + # Now it should have both + assert stream_conversation.messages[0].content == "Hello world" + + @pytest.mark.asyncio + async def test_upsert_replace_vs_append(self, stream_conversation, mock_channel): + """Test replace vs append behavior.""" + msg_id = str(uuid.uuid4()) + + # Create message + await stream_conversation.upsert_message( + role="assistant", user_id="agent", + content="Hello", message_id=msg_id, completed=False + ) + + # Append + await stream_conversation.upsert_message( + role="assistant", user_id="agent", + content=" world", message_id=msg_id, completed=False, replace=False + ) + + assert stream_conversation.messages[0].content == "Hello world" + + # Replace + await stream_conversation.upsert_message( + role="assistant", user_id="agent", + content="Goodbye", message_id=msg_id, completed=True, replace=True + ) + + assert stream_conversation.messages[0].content == "Goodbye" + + +@pytest.mark.integration +@pytest.mark.asyncio +async def test_streaming_deltas_then_completion_integration(): + """Integration test: streaming deltas followed by completion.""" + channel_id = f"test-channel-{uuid.uuid4()}" + chat_client = AsyncStream().chat + channel = chat_client.channel("messaging", channel_id) + + await channel.get_or_create( + data=ChannelInput(created_by_id="test-user"), + ) + + conversation = StreamConversation( + instructions="Test conversation", + messages=[], + channel=channel + ) + + msg_id = str(uuid.uuid4()) + + # Send streaming deltas + logger.info("Sending deltas...") + await conversation.upsert_message( + role="assistant", user_id="agent", + content="Hello", message_id=msg_id, content_index=0, completed=False + ) + + await conversation.upsert_message( + role="assistant", user_id="agent", + content=" world", message_id=msg_id, content_index=1, completed=False + ) + + await conversation.upsert_message( + role="assistant", user_id="agent", + content="!", message_id=msg_id, content_index=2, completed=False + ) + + # Complete the message + logger.info("Completing message...") + await conversation.upsert_message( + role="assistant", user_id="agent", + content="Hello world!", message_id=msg_id, completed=True, replace=True + ) + + # Verify only 1 message in memory + assert len(conversation.messages) == 1 + assert conversation.messages[0].content == "Hello world!" + + # Verify only 1 message in Stream + response = await channel.get_or_create(state=True, messages=MessagePaginationParams(limit=10)) + assert len(response.data.messages) == 1 + assert response.data.messages[0].text == "Hello world!" + + logger.info("✅ Test passed: Only 1 message created") + + +@pytest.mark.integration +@pytest.mark.asyncio +async def test_completion_before_deltas_integration(): + """Integration test: completion arrives before deltas (race condition).""" + channel_id = f"test-channel-{uuid.uuid4()}" + chat_client = AsyncStream().chat + channel = chat_client.channel("messaging", channel_id) + + await channel.get_or_create( + data=ChannelInput(created_by_id="test-user"), + ) + + conversation = StreamConversation( + instructions="Test conversation", + messages=[], + channel=channel + ) + + msg_id = str(uuid.uuid4()) + full_text = "Hello world!" + + # Completion arrives first + logger.info("Completion arrives first...") + await conversation.upsert_message( + role="assistant", user_id="agent", + content=full_text, message_id=msg_id, completed=True, replace=True + ) + + assert len(conversation.messages) == 1 + assert conversation.messages[0].content == full_text + + # Deltas arrive late (should be no-op since message is completed) + logger.info("Late deltas arrive...") + await conversation.upsert_message( + role="assistant", user_id="agent", + content="Hello", message_id=msg_id, content_index=0, completed=False + ) + + # Should still be only 1 message with full text (deltas ignored after completion) + assert len(conversation.messages) == 1 + assert conversation.messages[0].content == full_text + + # Verify only 1 message in Stream + response = await channel.get_or_create(state=True, messages=MessagePaginationParams(limit=10)) + assert len(response.data.messages) == 1 + assert response.data.messages[0].text == full_text + + logger.info("✅ Test passed: Only 1 message, late deltas ignored") + + +@pytest.mark.integration +@pytest.mark.asyncio +async def test_out_of_order_fragments_integration(): + """Integration test: out-of-order delta fragments.""" + channel_id = f"test-channel-{uuid.uuid4()}" + chat_client = AsyncStream().chat + channel = chat_client.channel("messaging", channel_id) + + await channel.get_or_create( + data=ChannelInput(created_by_id="test-user"), + ) + + conversation = StreamConversation( + instructions="Test conversation", + messages=[], + channel=channel + ) + + msg_id = str(uuid.uuid4()) + + # Send fragments out of order + chunks = [ + (0, "once"), + (1, " upon"), + (2, " a"), + (3, " time"), + (4, " in"), + (5, " a"), + (6, " galaxy"), + (7, " far"), + (8, " far"), + (9, " away"), + ] + + shuffle(chunks) + + for idx, txt in chunks: + await conversation.upsert_message( + role="assistant", user_id="agent", + content=txt, message_id=msg_id, content_index=idx, completed=False + ) + + # Complete the message + await conversation.upsert_message( + role="assistant", user_id="agent", + content="once upon a time in a galaxy far far away", + message_id=msg_id, completed=True, replace=True + ) + + # Verify only 1 message + assert len(conversation.messages) == 1 + + # Verify in Stream + response = await channel.get_or_create(state=True, messages=MessagePaginationParams(limit=10)) + assert len(response.data.messages) == 1 + assert response.data.messages[0].text == "once upon a time in a galaxy far far away" + + +@pytest.mark.integration +@pytest.mark.asyncio +async def test_race_condition_delta_and_completion_concurrent(): + """Test the exact race condition from the bug report. + + Simulates delta and completion events arriving at nearly the same time + (both checking state before either completes). + """ + channel_id = f"test-channel-{uuid.uuid4()}" + chat_client = AsyncStream().chat + channel = chat_client.channel("messaging", channel_id) + + await channel.get_or_create( + data=ChannelInput(created_by_id="test-user"), + ) + + conversation = StreamConversation( + instructions="Test conversation", + messages=[], + channel=channel + ) + + msg_id = str(uuid.uuid4()) + + # Send delta and completion concurrently (race condition) + logger.info("Sending delta and completion concurrently...") + await asyncio.gather( + # Delta arrives + conversation.upsert_message( + role="assistant", user_id="agent", + content="The", message_id=msg_id, content_index=0, completed=False + ), + # Completion arrives at same time + conversation.upsert_message( + role="assistant", user_id="agent", + content="The old lighthouse keeper...", + message_id=msg_id, completed=True, replace=True + ), + ) + + # Should have only 1 message (not 2!) + assert len(conversation.messages) == 1 + assert conversation.messages[0].content == "The old lighthouse keeper..." + + # Verify only 1 message in Stream + response = await channel.get_or_create(state=True, messages=MessagePaginationParams(limit=10)) + assert len(response.data.messages) == 1, f"Expected 1 message, got {len(response.data.messages)}" + assert response.data.messages[0].text == "The old lighthouse keeper..." + + logger.info("✅ Race condition test passed: Only 1 message created") + + +@pytest.mark.integration +@pytest.mark.asyncio +async def test_concurrent_messages(): + """Test multiple concurrent streaming messages.""" + channel_id = f"test-channel-{uuid.uuid4()}" + chat_client = AsyncStream().chat + channel = chat_client.channel("messaging", channel_id) + + await channel.get_or_create( + data=ChannelInput(created_by_id="test-user"), + ) + + conversation = StreamConversation( + instructions="Test conversation", + messages=[], + channel=channel + ) + + msg_id_1 = str(uuid.uuid4()) + msg_id_2 = str(uuid.uuid4()) + + # Stream two messages concurrently + await asyncio.gather( + conversation.upsert_message( + role="user", user_id="user1", + content="Question 1", message_id=msg_id_1, completed=True + ), + conversation.upsert_message( + role="assistant", user_id="agent", + content="Answer 1", message_id=msg_id_2, completed=True + ), + ) + + # Should have 2 messages + assert len(conversation.messages) == 2 + + # Verify in Stream + response = await channel.get_or_create(state=True, messages=MessagePaginationParams(limit=10)) + assert len(response.data.messages) == 2 + + +@pytest.mark.integration +@pytest.mark.asyncio +async def test_large_message_chunking_integration(): + """Integration test: large message automatically chunked in Stream.""" + channel_id = f"test-channel-{uuid.uuid4()}" + chat_client = AsyncStream().chat + channel = chat_client.channel("messaging", channel_id) + + await channel.get_or_create( + data=ChannelInput(created_by_id="test-user"), + ) + + # Small chunk size for testing + conversation = StreamConversation( + instructions="Test conversation", + messages=[], + channel=channel, + chunk_size=200, # Small for testing + ) + + # Create a message that needs chunking + large_text = "A" * 500 # 500 chars = 3 chunks at 200 chars + + logger.info(f"Sending large message ({len(large_text)} chars)...") + await conversation.send_message( + role="assistant", + user_id="agent", + content=large_text, + ) + + # Should have 1 message in conversation.messages + assert len(conversation.messages) == 1 + assert conversation.messages[0].content == large_text + + # Should have 3 chunks in Stream + response = await channel.get_or_create(state=True, messages=MessagePaginationParams(limit=10)) + assert len(response.data.messages) == 3, f"Expected 3 chunks, got {len(response.data.messages)}" + + # Verify chunk metadata + for i, stream_msg in enumerate(response.data.messages): + assert stream_msg.custom.get("chunk_group") == conversation.messages[0].id + assert stream_msg.custom.get("chunk_index") == i + assert stream_msg.custom.get("total_chunks") == 3 + + # Verify all chunks have generating=False (completed=True) + for i, stream_msg in enumerate(response.data.messages): + assert stream_msg.custom.get("generating") is False + + # Verify content is preserved + full_content = ''.join(msg.text for msg in response.data.messages) + assert full_content == large_text + + logger.info("✅ Large message chunking test passed") + + +@pytest.mark.integration +@pytest.mark.asyncio +async def test_streaming_with_chunking_integration(): + """Integration test: streaming message that grows and requires chunking.""" + channel_id = f"test-channel-{uuid.uuid4()}" + chat_client = AsyncStream().chat + channel = chat_client.channel("messaging", channel_id) + + await channel.get_or_create( + data=ChannelInput(created_by_id="test-user"), + ) + + conversation = StreamConversation( + instructions="Test conversation", + messages=[], + channel=channel, + chunk_size=100, # Small for testing + ) + + msg_id = str(uuid.uuid4()) + + # Start with small content (1 chunk) + logger.info("Starting with small content...") + await conversation.upsert_message( + role="assistant", user_id="agent", + content="A" * 50, message_id=msg_id, completed=False + ) + + # Grow to 2 chunks + logger.info("Growing to 2 chunks...") + await conversation.upsert_message( + role="assistant", user_id="agent", + content="A" * 150, message_id=msg_id, completed=False, replace=True + ) + + # Grow to 3 chunks + logger.info("Growing to 3 chunks...") + await conversation.upsert_message( + role="assistant", user_id="agent", + content="A" * 250, message_id=msg_id, completed=False, replace=True + ) + + # Complete with 2 chunks (shrink) + logger.info("Completing with 2 chunks...") + final_text = "A" * 150 + await conversation.upsert_message( + role="assistant", user_id="agent", + content=final_text, message_id=msg_id, completed=True, replace=True + ) + + # Should have 1 message in memory + assert len(conversation.messages) == 1 + assert conversation.messages[0].content == final_text + + # Should have 2 chunks in Stream (3rd deleted) + response = await channel.get_or_create(state=True, messages=MessagePaginationParams(limit=10)) + assert len(response.data.messages) == 2, f"Expected 2 chunks, got {len(response.data.messages)}" + + # All chunks should have generating=False (completed) + for stream_msg in response.data.messages: + assert stream_msg.custom.get("generating") is False + + # Content preserved + full_content = ''.join(msg.text for msg in response.data.messages) + assert full_content == final_text + + logger.info("✅ Streaming with chunking test passed") + + +@pytest.mark.integration +@pytest.mark.asyncio +async def test_markdown_chunking_integration(): + """Integration test: markdown code blocks preserved during chunking.""" + channel_id = f"test-channel-{uuid.uuid4()}" + chat_client = AsyncStream().chat + channel = chat_client.channel("messaging", channel_id) + + await channel.get_or_create( + data=ChannelInput(created_by_id="test-user"), + ) + + conversation = StreamConversation( + instructions="Test conversation", + messages=[], + channel=channel, + chunk_size=150, # Small for testing + ) + + # Message with code block that forces chunking + markdown_text = """Here's some sample code: + +```python +def calculate_sum(a, b): + result = a + b + return result + +def calculate_product(a, b): + result = a * b + return result +``` + +And here's more text after the code block to force multiple chunks.""" + + logger.info("Sending markdown message...") + await conversation.send_message( + role="assistant", + user_id="agent", + content=markdown_text, + ) + + # Should have 1 message in memory + assert len(conversation.messages) == 1 + + # Should have multiple chunks in Stream + response = await channel.get_or_create(state=True, messages=MessagePaginationParams(limit=10)) + chunk_count = len(response.data.messages) + logger.info(f"Created {chunk_count} chunks") + + # Reconstruct full text + full_content = ''.join(msg.text for msg in response.data.messages) + + # Verify code block is intact + assert '```python' in full_content + assert 'def calculate_sum' in full_content + assert 'def calculate_product' in full_content + assert full_content.count('```') >= 2 # Opening and closing + + logger.info("✅ Markdown chunking test passed") diff --git a/plugins/getstream/vision_agents/plugins/getstream/stream_conversation.py b/plugins/getstream/vision_agents/plugins/getstream/stream_conversation.py index ce6a1c8b..b44fb4bc 100644 --- a/plugins/getstream/vision_agents/plugins/getstream/stream_conversation.py +++ b/plugins/getstream/vision_agents/plugins/getstream/stream_conversation.py @@ -1,236 +1,353 @@ import logging -import threading -import queue -import time from typing import List, Dict -from getstream.chat.client import ChatClient -from getstream.models import MessageRequest, ChannelResponse - -from vision_agents.core.agents.conversation import InMemoryConversation, Message +from getstream.models import MessageRequest +from getstream.chat.async_channel import Channel +from getstream.base import StreamAPIException +from vision_agents.core.agents.conversation import ( + Conversation, + Message, + MessageState, +) logger = logging.getLogger(__name__) -class StreamConversation(InMemoryConversation): - """ - Persists the message history to a stream channel & messages - """ - messages: List[Message] +class StreamConversation(Conversation): + """Persists the message history to a Stream channel with automatic chunking.""" - # maps internal ids to stream message ids + # Maps internal message IDs to first Stream message ID (for backward compatibility) internal_ids_to_stream_ids: Dict[str, str] + channel: Channel + chunk_size: int + + def __init__( + self, + instructions: str, + messages: List[Message], + channel: Channel, + chunk_size: int = 1000, + ): + """Initialize StreamConversation with automatic message chunking. - channel: ChannelResponse - chat_client: ChatClient - - def __init__(self, instructions: str, messages: List[Message], channel: ChannelResponse, chat_client: ChatClient): + Args: + instructions: System instructions + messages: Initial messages + channel: Stream channel for persistence + chunk_size: Maximum characters per message chunk (default 1000) + """ super().__init__(instructions, messages) - self.messages = messages self.channel = channel - self.chat_client = chat_client self.internal_ids_to_stream_ids = {} + self.chunk_size = chunk_size + + async def _sync_to_backend( + self, message: Message, state: MessageState, completed: bool + ): + """Sync message to Stream Chat API with automatic chunking. + + Args: + message: The message to sync + state: The message's internal state + completed: If True, finalize the message. If False, mark as still generating. + """ + # Split message into chunks (markdown-aware) + chunks = self._smart_chunk(message.content, self.chunk_size) + + if not state.created_in_backend: + # CREATE: Send all chunks to Stream + await self._create_chunks(message, state, chunks, completed) + state.created_in_backend = True + else: + # UPDATE: Update/create/delete chunks as needed + await self._update_chunks(message, state, chunks, completed) + + async def _create_chunks( + self, message: Message, state: MessageState, chunks: List[str], completed: bool + ): + """Create all chunks for a new message.""" + for i, chunk_text in enumerate(chunks): + is_last_chunk = i == len(chunks) - 1 + + request = MessageRequest( + text=chunk_text, + user_id=message.user_id, + custom={ + # Only the LAST chunk has "generating" flag + "generating": not completed if is_last_chunk else False, + "chunk_group": message.id, + "chunk_index": i, + "total_chunks": len(chunks), + }, + ) - # Initialize the worker thread for API calls - self._api_queue: queue.Queue = queue.Queue() - self._shutdown = False - self._worker_thread = threading.Thread(target=self._api_worker, daemon=True, name="StreamConversation-APIWorker") - self._worker_thread.start() - self._pending_operations = 0 - self._operations_lock = threading.Lock() - logger.info(f"Started API worker thread for channel {channel.id}") - - def _api_worker(self): - """Worker thread that processes Stream API calls.""" - logger.debug("API worker thread started") - while not self._shutdown: try: - # Get operation from queue with timeout to check shutdown periodically - operation = self._api_queue.get(timeout=0.1) + response = await self.channel.send_message(request) - try: - op_type = operation["type"] - logger.debug(f"Processing API operation: {op_type}") - - if op_type == "send_message": - response = self.chat_client.send_message( - operation["channel_type"], - operation["channel_id"], - operation["request"] - ) - # Store the mapping - self.internal_ids_to_stream_ids[operation["internal_id"]] = response.data.message.id - operation["stream_id"] = response.data.message.id - - elif op_type == "update_message_partial": - self.chat_client.update_message_partial( - operation["stream_id"], - user_id=operation["user_id"], - set=operation["set_data"] - ) - - elif op_type == "ephemeral_message_update": - self.chat_client.ephemeral_message_update( - operation["stream_id"], - user_id=operation["user_id"], - set=operation["set_data"] - ) - - logger.debug(f"Successfully processed API operation: {op_type}") + if response.data.message.type == "error": + raise StreamAPIException(response=response.__response) - except Exception as e: - logger.error(f"Error processing API operation {operation.get('type', 'unknown')}: {e}") - # Continue processing other operations even if one fails + chunk_id = response.data.message.id + state.backend_message_ids.append(chunk_id) - finally: - # Decrement pending operations counter - with self._operations_lock: - self._pending_operations -= 1 + logger.debug( + f"Created chunk {i + 1}/{len(chunks)} (Stream ID: {chunk_id}) " + f"for message {message.id}" + ) + except Exception as e: + logger.error( + f"Failed to create chunk {i} for message {message.id}: {e}" + ) + raise + + # Store first chunk ID for backward compatibility + if state.backend_message_ids and message.id: + self.internal_ids_to_stream_ids[message.id] = state.backend_message_ids[0] + + async def _update_chunks( + self, + message: Message, + state: MessageState, + new_chunks: List[str], + completed: bool, + ): + """Update existing chunks, creating or deleting as needed.""" + old_chunk_count = len(state.backend_message_ids) + new_chunk_count = len(new_chunks) + + # Update existing chunks + for i in range(min(old_chunk_count, new_chunk_count)): + chunk_id = state.backend_message_ids[i] + chunk_text = new_chunks[i] + is_last_chunk = i == new_chunk_count - 1 - except queue.Empty: - # Timeout reached, loop back to check shutdown flag - continue + try: + if completed: + # Finalize with update_message_partial + # Only last chunk gets generating=False on completion + await self.channel.client.update_message_partial( + chunk_id, + user_id=message.user_id, + set={ + "text": chunk_text, + "generating": False if is_last_chunk else False, + "chunk_group": message.id, + "chunk_index": i, + "total_chunks": new_chunk_count, + }, + ) + logger.debug( + f"Finalized chunk {i + 1}/{new_chunk_count} (ID: {chunk_id})" + ) + else: + # Update with ephemeral (still generating) + # Only last chunk gets generating=True when streaming + await self.channel.client.ephemeral_message_update( + chunk_id, + user_id=message.user_id, + set={ + "text": chunk_text, + "generating": True if is_last_chunk else False, + "chunk_group": message.id, + "chunk_index": i, + "total_chunks": new_chunk_count, + }, + ) + logger.debug( + f"Updated chunk {i + 1}/{new_chunk_count} (ID: {chunk_id})" + ) except Exception as e: - logger.error(f"Unexpected error in API worker thread: {e}") - time.sleep(0.1) # Brief pause before continuing + logger.error(f"Failed to update chunk {i} (ID: {chunk_id}): {e}") + # Don't re-raise - message is in memory + + # Create new chunks if content grew + if new_chunk_count > old_chunk_count: + for i in range(old_chunk_count, new_chunk_count): + chunk_text = new_chunks[i] + is_last_chunk = i == new_chunk_count - 1 + + request = MessageRequest( + text=chunk_text, + user_id=message.user_id, + custom={ + "generating": not completed if is_last_chunk else False, + "chunk_group": message.id, + "chunk_index": i, + "total_chunks": new_chunk_count, + }, + ) + + try: + response = await self.channel.send_message(request) + chunk_id = response.data.message.id + state.backend_message_ids.append(chunk_id) + logger.debug( + f"Created new chunk {i + 1}/{new_chunk_count} (ID: {chunk_id})" + ) + except Exception as e: + logger.error(f"Failed to create new chunk {i}: {e}") + raise + + # Delete extra chunks if content shrank + elif new_chunk_count < old_chunk_count: + for i in range(new_chunk_count, old_chunk_count): + chunk_id = state.backend_message_ids[i] + try: + # Use client.delete_message, not channel.delete_message + await self.channel.client.delete_message(chunk_id, hard=True) + logger.debug(f"Deleted chunk {i + 1} (ID: {chunk_id})") + except Exception as e: + logger.warning(f"Failed to delete chunk {i} (ID: {chunk_id}): {e}") - logger.debug("API worker thread shutting down") + # Remove from tracking + state.backend_message_ids = state.backend_message_ids[:new_chunk_count] - def wait_for_pending_operations(self, timeout: float = 5.0) -> bool: - """Wait for all pending API operations to complete. + def _smart_chunk(self, text: str, max_size: int) -> List[str]: + """Chunk text intelligently, respecting markdown structures. + + Best-effort approach to avoid breaking: + - Code blocks (```) + - Lists + - Paragraphs Args: - timeout: Maximum time to wait in seconds. + text: Text to chunk + max_size: Maximum characters per chunk Returns: - True if all operations completed, False if timeout reached. + List of text chunks """ - start_time = time.time() - while time.time() - start_time < timeout: - with self._operations_lock: - if self._pending_operations == 0: - return True - time.sleep(0.01) # Small sleep to avoid busy waiting - - with self._operations_lock: - remaining = self._pending_operations - if remaining > 0: - logger.warning(f"Timeout waiting for {remaining} pending operations") - return False - - def shutdown(self): - """Shutdown the worker thread gracefully.""" - logger.info("Shutting down API worker thread") - self._shutdown = True - if self._worker_thread.is_alive(): - self._worker_thread.join(timeout=2.0) - if self._worker_thread.is_alive(): - logger.warning("API worker thread did not shut down cleanly") - - def add_message(self, message: Message, completed: bool = True): - """Add a message to the Stream conversation. + if len(text) <= max_size: + return [text] + + # Special case: text with no newlines (force split at max_size) + if "\n" not in text: + return self._force_split(text, max_size) + + chunks = [] + current_chunk = "" + + # Track markdown state + in_code_block = False + code_block_buffer = "" + + lines = text.split("\n") + + for line in lines: + # Detect code block boundaries + if line.strip().startswith("```"): + if not in_code_block: + # Starting code block + in_code_block = True + code_block_buffer = line + "\n" + else: + # Ending code block + in_code_block = False + code_block_buffer += line + "\n" + + # Try to fit code block in current chunk + if len(current_chunk) + len(code_block_buffer) <= max_size: + current_chunk += code_block_buffer + elif len(code_block_buffer) <= max_size: + # Code block fits alone, start new chunk + if current_chunk: + chunks.append(current_chunk.rstrip()) + current_chunk = code_block_buffer + else: + # Code block too large, must split it + if current_chunk: + chunks.append(current_chunk.rstrip()) + current_chunk = "" + + # Split code block at newlines + cb_chunks = self._split_large_block(code_block_buffer, max_size) + chunks.extend(cb_chunks[:-1]) + current_chunk = cb_chunks[-1] if cb_chunks else "" + + code_block_buffer = "" + continue + + # Inside code block - accumulate + if in_code_block: + code_block_buffer += line + "\n" + continue + + # Regular line - check if we should chunk + line_with_newline = line + "\n" + + # Check if adding this line would exceed limit + if len(current_chunk) + len(line_with_newline) > max_size: + # Try to break at paragraph (double newline) or list boundary + if current_chunk: + chunks.append(current_chunk.rstrip()) + current_chunk = line_with_newline + else: + current_chunk += line_with_newline + + # Handle any remaining buffered content + if in_code_block and code_block_buffer: + # Unclosed code block - include as-is + if len(current_chunk) + len(code_block_buffer) <= max_size: + current_chunk += code_block_buffer + else: + if current_chunk: + chunks.append(current_chunk.rstrip()) + current_chunk = code_block_buffer + + if current_chunk: + chunks.append(current_chunk.rstrip()) + + return chunks if chunks else [text] # Fallback to original text + + def _split_large_block(self, block: str, max_size: int) -> List[str]: + """Split a large block (e.g., huge code block) at newline boundaries. Args: - message: The Message object to add - completed: If True, mark the message as completed using update_message_partial. - If False, mark as still generating using ephemeral_message_update. + block: Large text block to split + max_size: Maximum size per chunk Returns: - None (operations are processed asynchronously) + List of chunks """ - self.messages.append(message) - - # Queue the send_message operation - request = MessageRequest(text=message.content, user_id=message.user_id) - send_op = { - "type": "send_message", - "channel_type": self.channel.type, - "channel_id": self.channel.id, - "request": request, - "internal_id": message.id, - } - - # Increment pending operations counter - with self._operations_lock: - self._pending_operations += 1 - - self._api_queue.put(send_op) - - # Queue the update operation (will use the stream_id once send_message completes) - # We need to wait for the send operation to complete first - # So we'll handle this in a second operation that waits for the stream_id - def queue_update_operation(): - # Wait for the stream_id to be available - max_wait = 5.0 - start_time = time.time() - while time.time() - start_time < max_wait: - stream_id = self.internal_ids_to_stream_ids.get(message.id if message.id else "") - if stream_id: - update_op = { - "type": "update_message_partial" if completed else "ephemeral_message_update", - "stream_id": stream_id, - "user_id": message.user_id, - "set_data": {"text": message.content, "generating": not completed}, - } - with self._operations_lock: - self._pending_operations += 1 - self._api_queue.put(update_op) - return - time.sleep(0.01) - logger.error(f"Timeout waiting for stream_id for message {message.id}") - - # Queue the update in a separate thread to avoid blocking - threading.Thread(target=queue_update_operation, daemon=True).start() - - def update_message(self, message_id: str, input_text: str, user_id: str, replace_content: bool, completed: bool): - """Update a message in the Stream conversation. - - This method updates both the local message content and queues the Stream API sync. - If the message doesn't exist, it creates a new one. + if len(block) <= max_size: + return [block] + + chunks = [] + lines = block.split("\n") + current = "" + + for line in lines: + if len(current) + len(line) + 1 > max_size: + if current: + chunks.append(current) + # If single line exceeds max_size, include it anyway + current = line + "\n" + else: + current += line + "\n" + + if current: + chunks.append(current) + + return chunks + + def _force_split(self, text: str, max_size: int) -> List[str]: + """Force split text at max_size boundaries (no newlines available). + + Used for text without any newlines (e.g., long URLs, continuous text). Args: - message_id: The ID of the message to update - input_text: The text content to set or append - user_id: The ID of the user who owns the message - replace_content: If True, replace the entire message content. If False, append to existing content. - completed: If True, mark the message as completed using update_message_partial. - If False, mark as still generating using ephemeral_message_update. + text: Text to split + max_size: Maximum characters per chunk Returns: - None (operations are processed asynchronously) + List of chunks """ - # First, update the local message using the superclass logic - super().update_message(message_id, input_text, user_id, replace_content, completed) - - # Get the updated message for Stream API sync - message = self.lookup(message_id) - if message is None: - # This shouldn't happen after super().update_message, but handle gracefully - logger.warning(f"message {message_id} not found after update") - return None - - stream_id = self.internal_ids_to_stream_ids.get(message_id) - if stream_id is None: - logger.warning(f"stream_id for message {message_id} not found, skipping Stream API update") - return None - - # Queue the update operation - update_op = { - "type": "update_message_partial" if completed else "ephemeral_message_update", - "stream_id": stream_id, - "user_id": message.user_id, - "set_data": {"text": message.content, "generating": not completed}, - } - - with self._operations_lock: - self._pending_operations += 1 - - return self._api_queue.put(update_op) - - def __del__(self): - """Cleanup when the conversation is destroyed.""" - try: - self.shutdown() - except Exception as e: - logger.error(f"Error during StreamConversation cleanup: {e}") \ No newline at end of file + if len(text) <= max_size: + return [text] + + chunks = [] + for i in range(0, len(text), max_size): + chunks.append(text[i : i + max_size]) + + return chunks diff --git a/plugins/getstream/vision_agents/plugins/getstream/stream_edge_transport.py b/plugins/getstream/vision_agents/plugins/getstream/stream_edge_transport.py index 2d7c2d8e..de87850b 100644 --- a/plugins/getstream/vision_agents/plugins/getstream/stream_edge_transport.py +++ b/plugins/getstream/vision_agents/plugins/getstream/stream_edge_transport.py @@ -11,7 +11,6 @@ from getstream.chat.async_client import ChatClient from getstream.models import ChannelInput, ChannelMember from getstream.video import rtc -from getstream.chat.async_channel import Channel from getstream.video.async_call import Call from getstream.video.rtc import ConnectionManager, audio_track from getstream.video.rtc.pb.stream.video.sfu.models.models_pb2 import ( @@ -59,7 +58,6 @@ def __init__(self, **kwargs): self.events = EventManager() self.events.register_events_from_module(events) self.events.register_events_from_module(sfu_events) - self.channel: Optional[Channel] = None self.conversation: Optional[StreamConversation] = None self.channel_type = "messaging" self.agent_user_id: str | None = None @@ -103,12 +101,10 @@ async def _on_track_published(self, event: sfu_events.TrackPublishedEvent): user_id = event.payload.user_id session_id = event.payload.session_id - if user_id == self.agent_user_id: - return - track_type_int = event.payload.type # TrackType enum int from SFU expected_kind = self._get_webrtc_kind(track_type_int) track_key = (user_id, session_id, track_type_int) + is_agent_track = (user_id == self.agent_user_id) # First check if track already exists in map (e.g., from previous unpublish/republish) if track_key in self._track_map: @@ -153,16 +149,18 @@ async def _on_track_published(self, event: sfu_events.TrackPublishedEvent): f"Trackmap published: {track_type_int} from {user_id}, track_id: {track_id} (waited {elapsed:.2f}s)" ) - # NOW spawn TrackAddedEvent with correct type - self.events.send( - events.TrackAddedEvent( - plugin_name="getstream", - track_id=track_id, - track_type=track_type_int, - user=event.participant, - user_metadata=event.participant, + # Only emit TrackAddedEvent for remote participants, not for agent's own tracks + if not is_agent_track: + # NOW spawn TrackAddedEvent with correct type + self.events.send( + events.TrackAddedEvent( + plugin_name="getstream", + track_id=track_id, + track_type=track_type_int, + user=event.participant, + user_metadata=event.participant, + ) ) - ) else: raise TimeoutError( f"Timeout waiting for pending track: {track_type_int} ({expected_kind}) from user {user_id}, " @@ -225,14 +223,11 @@ async def _on_track_removed( async def create_conversation(self, call: Call, user, instructions): chat_client: ChatClient = call.client.stream.chat - self.channel = await chat_client.get_or_create_channel( - self.channel_type, - call.id, + channel = chat_client.channel(self.channel_type, call.id) + await channel.get_or_create( data=ChannelInput(created_by_id=user.id), ) - self.conversation = StreamConversation( - instructions, [], self.channel.data.channel, chat_client - ) + self.conversation = StreamConversation(instructions, [], channel) return self.conversation async def create_user(self, user: User): diff --git a/plugins/openai/pyproject.toml b/plugins/openai/pyproject.toml index c3806cef..18265405 100644 --- a/plugins/openai/pyproject.toml +++ b/plugins/openai/pyproject.toml @@ -11,7 +11,7 @@ requires-python = ">=3.10" license = "MIT" dependencies = [ "vision-agents", - "openai[realtime]>=2.2.0", + "openai[realtime]>=2.5.0", ] [project.urls] diff --git a/plugins/openai/tests/test_openai_llm.py b/plugins/openai/tests/test_openai_llm.py index fb804b62..40ce91a2 100644 --- a/plugins/openai/tests/test_openai_llm.py +++ b/plugins/openai/tests/test_openai_llm.py @@ -3,7 +3,11 @@ from vision_agents.core.agents.conversation import Message from vision_agents.plugins.openai.openai_llm import OpenAILLM -from vision_agents.core.llm.events import LLMResponseChunkEvent +from vision_agents.core.llm.events import ( + LLMResponseChunkEvent, + LLMResponseCompletedEvent, +) +from vision_agents.plugins.openai import events load_dotenv() @@ -48,31 +52,27 @@ async def test_simple(self, llm: OpenAILLM): @pytest.mark.integration async def test_native_api(self, llm: OpenAILLM): - - response = await llm.create_response( input="say hi", instructions="You are a helpful assistant." ) # Assertions assert response.text - assert hasattr(response.original, 'id') # OpenAI response has id - + assert hasattr(response.original, "id") # OpenAI response has id @pytest.mark.integration async def test_streaming(self, llm: OpenAILLM): - streamingWorks = False - + @llm.events.subscribe async def passed(event: LLMResponseChunkEvent): nonlocal streamingWorks streamingWorks = True - + response = await llm.simple_response( "Explain quantum computing in 1 paragraph", ) - + await llm.events.wait() assert response.text @@ -97,3 +97,132 @@ async def test_native_memory(self, llm: OpenAILLM): input="How many paws are there in the room?", ) assert "8" in response.text or "eight" in response.text + + @pytest.mark.integration + async def test_events(self, llm: OpenAILLM): + """Test that LLM events are properly emitted during streaming responses.""" + # Track events and their content + chunk_events = [] + complete_events = [] + openai_stream_events = [] + error_events = [] + + # Register event handlers BEFORE making the API call + @llm.events.subscribe + async def handle_chunk_event(event: LLMResponseChunkEvent): + chunk_events.append(event) + + @llm.events.subscribe + async def handle_complete_event(event: LLMResponseCompletedEvent): + complete_events.append(event) + + @llm.events.subscribe + async def handle_openai_stream_event(event: events.OpenAIStreamEvent): + openai_stream_events.append(event) + + @llm.events.subscribe + async def handle_error_event(event: events.LLMErrorEvent): + error_events.append(event) + + # Make API call that should generate streaming events + response = await llm.create_response( + input="Create a small story about the weather in the Netherlands. Make it at least 2 paragraphs long.", + ) + + # Wait for all events to be processed + await llm.events.wait() + + # Verify response was generated + assert response.text, "Response should have text content" + assert len(response.text) > 50, "Response should be substantial" + + # Verify chunk events were emitted + assert len(chunk_events) > 0, ( + "Should have received chunk events during streaming" + ) + + # Verify completion event was emitted + assert len(complete_events) > 0, "Should have received completion event" + assert len(complete_events) == 1, "Should have exactly one completion event" + + # Verify OpenAI stream events were emitted + assert len(openai_stream_events) > 0, ( + "Should have received OpenAI stream events" + ) + + # Verify no error events were emitted + assert len(error_events) == 0, ( + f"Should not have error events, but got: {error_events}" + ) + + # Verify chunk events have proper content and item_id + total_delta_text = "" + chunk_item_ids = set() + content_indices = [] + for chunk_event in chunk_events: + assert chunk_event.delta is not None, ( + "Chunk events should have delta content" + ) + assert isinstance(chunk_event.delta, str), "Delta should be a string" + assert chunk_event.item_id is not None, ( + "Chunk events should have non-null item_id" + ) + assert chunk_event.item_id != "", ( + "Chunk events should have non-empty item_id" + ) + chunk_item_ids.add(chunk_event.item_id) + total_delta_text += chunk_event.delta + + # Validate content_index: should be sequential (0, 1, 2, ...) or None + if chunk_event.content_index is not None: + content_indices.append(chunk_event.content_index) + + # Verify content_index sequencing if any are provided + if content_indices: + # Should be sequential starting from 0 + expected_indices = list(range(len(content_indices))) + assert content_indices == expected_indices, ( + f"content_index should be sequential (0, 1, 2, ...), but got: {content_indices}" + ) + + # Verify completion event has proper content and item_id + complete_event = complete_events[0] + assert complete_event.text == response.text, ( + "Completion event text should match response text" + ) + assert complete_event.original is not None, ( + "Completion event should have original response" + ) + assert complete_event.item_id is not None, ( + "Completion event should have non-null item_id" + ) + assert complete_event.item_id != "", ( + "Completion event should have non-empty item_id" + ) + + # Verify that completion event item_id matches chunk event item_ids + assert complete_event.item_id in chunk_item_ids, ( + f"Completion event item_id '{complete_event.item_id}' should match one of the chunk event item_ids: {chunk_item_ids}" + ) + + # Verify that chunk deltas reconstruct the final text (approximately) + # Note: There might be slight differences due to formatting, so we check for substantial overlap + assert len(total_delta_text) > 0, "Should have accumulated delta text" + assert len(total_delta_text) >= len(response.text) * 0.8, ( + "Delta text should be substantial portion of final text" + ) + + # Verify event ordering: chunks should come before completion + # This is implicit since we're using async/await, but we can verify the structure + assert len(chunk_events) >= 1, ( + "Should have at least one chunk before completion" + ) + + # Verify OpenAI stream events contain expected event types + stream_event_types = [event.event_type for event in openai_stream_events] + assert "response.output_text.delta" in stream_event_types, ( + "Should have delta events" + ) + assert "response.completed" in stream_event_types, ( + "Should have completion event" + ) diff --git a/plugins/openai/tests/test_openai_realtime.py b/plugins/openai/tests/test_openai_realtime.py index cfd9ca6e..0556ab33 100644 --- a/plugins/openai/tests/test_openai_realtime.py +++ b/plugins/openai/tests/test_openai_realtime.py @@ -3,7 +3,11 @@ from dotenv import load_dotenv from vision_agents.plugins.openai import Realtime -from vision_agents.core.llm.events import RealtimeAudioOutputEvent +from vision_agents.core.llm.events import ( + RealtimeAudioOutputEvent, + RealtimeUserSpeechTranscriptionEvent, + RealtimeAgentSpeechTranscriptionEvent, +) # Load environment variables load_dotenv() @@ -29,11 +33,11 @@ async def test_simple_response_flow(self, realtime): """Test sending a simple text message and receiving response""" # Send a simple message events = [] - + @realtime.events.subscribe async def on_audio(event: RealtimeAudioOutputEvent): events.append(event) - + await asyncio.sleep(0.01) await realtime.connect() await realtime.simple_response("Hello, can you hear me?") @@ -46,38 +50,36 @@ async def on_audio(event: RealtimeAudioOutputEvent): async def test_audio_sending_flow(self, realtime, mia_audio_16khz): """Test sending real audio data and verify connection remains stable""" events = [] - + @realtime.events.subscribe async def on_audio(event: RealtimeAudioOutputEvent): events.append(event) - + await asyncio.sleep(0.01) await realtime.connect() - + # Wait for connection to be fully established await asyncio.sleep(2.0) - + # Convert 16kHz audio to 48kHz for OpenAI realtime # OpenAI expects 48kHz PCM audio import numpy as np from scipy import signal from vision_agents.core.edge.types import PcmData - + # Resample from 16kHz to 48kHz samples_16k = mia_audio_16khz.samples num_samples_48k = int(len(samples_16k) * 48000 / 16000) samples_48k = signal.resample(samples_16k, num_samples_48k).astype(np.int16) - + # Create new PcmData with 48kHz - audio_48khz = PcmData( - samples=samples_48k, - sample_rate=48000, - format="s16" + audio_48khz = PcmData(samples=samples_48k, sample_rate=48000, format="s16") + + await realtime.simple_response( + "Listen to the following audio and tell me what you hear" ) - - await realtime.simple_response("Listen to the following audio and tell me what you hear") await asyncio.sleep(5.0) - + # Send the resampled audio await realtime.simple_audio_response(audio_48khz) @@ -89,22 +91,281 @@ async def on_audio(event: RealtimeAudioOutputEvent): async def test_video_sending_flow(self, realtime, bunny_video_track): """Test sending real video data and verify connection remains stable""" events = [] - + @realtime.events.subscribe async def on_audio(event: RealtimeAudioOutputEvent): events.append(event) - + await asyncio.sleep(0.01) await realtime.connect() await realtime.simple_response("Describe what you see in this video please") await asyncio.sleep(10.0) # Start video sender with low FPS to avoid overwhelming the connection await realtime._watch_video_track(bunny_video_track) - + # Let it run for a few seconds await asyncio.sleep(10.0) - + # Stop video sender await realtime._stop_watching_video_track() assert len(events) > 0 + async def test_user_speech_transcription_event(self, realtime): + """Test that user speech transcription event is emitted when conversation.item.input_audio_transcription.completed is received""" + user_transcripts = [] + + # Subscribe using decorator pattern like agents.py does + @realtime.events.subscribe + async def on_user_transcript(event: RealtimeUserSpeechTranscriptionEvent): + user_transcripts.append(event) + + # Real OpenAI event payload for user speech transcription (actual payload from OpenAI) + openai_event = { + "content_index": 0, + "event_id": "event_CSLB0tmlLaCQtfefQZW03", + "item_id": "item_CSLAtg5dOSD0bC3yKdelc", + "transcript": "OK, everybody. Do.", + "type": "conversation.item.input_audio_transcription.completed", + "usage": {"seconds": 2, "type": "duration"}, + } + + # Simulate receiving the event from OpenAI + await realtime._handle_openai_event(openai_event) + + # Wait for async event processing + await asyncio.sleep(0.1) + + # Verify the event was emitted + assert len(user_transcripts) == 1 + assert user_transcripts[0].text == "OK, everybody. Do." + assert user_transcripts[0].original == openai_event + + async def test_agent_speech_transcription_event(self, realtime): + """Test that agent speech transcription event is emitted when response.audio_transcript.done is received""" + agent_transcripts = [] + + # Subscribe using decorator pattern like agents.py does + @realtime.events.subscribe + async def on_agent_transcript(event: RealtimeAgentSpeechTranscriptionEvent): + agent_transcripts.append(event) + + # Real OpenAI event payload for agent speech transcription + openai_event = { + "type": "response.output_audio_transcript.done", + "event_id": "event_789", + "response_id": "resp_abc", + "item_id": "item_def", + "output_index": 0, + "content_index": 0, + "transcript": "I'm doing well, thank you for asking!", + } + + # Simulate receiving the event from OpenAI + await realtime._handle_openai_event(openai_event) + + # Wait a moment for async event handling + await asyncio.sleep(0.1) + + # Verify the event was emitted + assert len(agent_transcripts) == 1 + assert agent_transcripts[0].text == "I'm doing well, thank you for asking!" + assert agent_transcripts[0].original == openai_event + + async def test_both_transcription_events(self, realtime): + """Test that both user and agent transcription events are emitted correctly""" + user_transcripts = [] + agent_transcripts = [] + + # Subscribe using decorator pattern like agents.py does + @realtime.events.subscribe + async def on_user_transcript(event: RealtimeUserSpeechTranscriptionEvent): + user_transcripts.append(event) + + @realtime.events.subscribe + async def on_agent_transcript(event: RealtimeAgentSpeechTranscriptionEvent): + agent_transcripts.append(event) + + # Real user speech event + user_event = { + "content_index": 0, + "event_id": "event_user_123", + "item_id": "item_user_456", + "transcript": "Hello, how are you?", + "type": "conversation.item.input_audio_transcription.completed", + "usage": {"seconds": 1, "type": "duration"}, + } + + # Real agent speech event + agent_event = { + "type": "response.output_audio_transcript.done", + "event_id": "event_agent_789", + "response_id": "resp_abc", + "item_id": "item_agent_def", + "output_index": 0, + "content_index": 0, + "transcript": "I'm doing great, thanks!", + } + + # Simulate receiving both events + await realtime._handle_openai_event(user_event) + await realtime._handle_openai_event(agent_event) + + # Wait for async event processing + await asyncio.sleep(0.1) + + # Verify both events were emitted + assert len(user_transcripts) == 1 + assert user_transcripts[0].text == "Hello, how are you?" + assert user_transcripts[0].original == user_event + + assert len(agent_transcripts) == 1 + assert agent_transcripts[0].text == "I'm doing great, thanks!" + assert agent_transcripts[0].original == agent_event + + async def test_user_speech_transcription_tracks_participant(self, realtime): + """Test that user speech transcription correctly tracks participant/user_id""" + user_transcripts = [] + + # Subscribe to events + @realtime.events.subscribe + async def on_user_transcript(event: RealtimeUserSpeechTranscriptionEvent): + user_transcripts.append(event) + + # Create a mock participant with a specific user_id + from vision_agents.core.edge.types import Participant + + test_participant = Participant(original=None, user_id="test_user_123") + + # Simulate sending audio with participant info + from vision_agents.core.edge.types import PcmData + import numpy as np + + pcm = PcmData( + samples=np.zeros(100, dtype=np.int16), sample_rate=48000, format="s16" + ) + await realtime.simple_audio_response(pcm, test_participant) + + # Simulate OpenAI creating the conversation item (this is when we map item_id -> participant) + item_created_event = { + "type": "conversation.item.created", + "event_id": "event_created_123", + "item": { + "id": "item_test_456", + "type": "message", + "role": "user", + "content": [], + }, + } + await realtime._handle_openai_event(item_created_event) + + # Now simulate receiving the transcription event from OpenAI + openai_event = { + "content_index": 0, + "event_id": "event_test_123", + "item_id": "item_test_456", + "transcript": "Test transcription with user ID", + "type": "conversation.item.input_audio_transcription.completed", + "usage": {"seconds": 1, "type": "duration"}, + } + + await realtime._handle_openai_event(openai_event) + await asyncio.sleep(0.1) + + # Verify the event was emitted with correct participant info + assert len(user_transcripts) == 1 + assert user_transcripts[0].text == "Test transcription with user ID" + + # Verify the participant/user_id is correctly tracked + assert user_transcripts[0].user_metadata is not None + assert user_transcripts[0].user_metadata.user_id == "test_user_123" + + # Verify the user_id() helper method works + assert user_transcripts[0].user_id() == "test_user_123" + + async def test_multi_user_participant_tracking(self, realtime): + """Test that participant tracking works correctly when multiple users speak in succession""" + user_transcripts = [] + + @realtime.events.subscribe + async def on_user_transcript(event: RealtimeUserSpeechTranscriptionEvent): + user_transcripts.append(event) + + from vision_agents.core.edge.types import Participant + from vision_agents.core.edge.types import PcmData + import numpy as np + + # User A sends audio + participant_a = Participant(original=None, user_id="user_a") + pcm_a = PcmData( + samples=np.zeros(100, dtype=np.int16), sample_rate=48000, format="s16" + ) + await realtime.simple_audio_response(pcm_a, participant_a) + + # OpenAI creates conversation item for User A + item_created_a = { + "type": "conversation.item.created", + "event_id": "event_created_a", + "item": { + "id": "item_a_123", + "type": "message", + "role": "user", + "content": [], + }, + } + await realtime._handle_openai_event(item_created_a) + + # User B sends audio (before A's transcription arrives) + participant_b = Participant(original=None, user_id="user_b") + pcm_b = PcmData( + samples=np.zeros(100, dtype=np.int16), sample_rate=48000, format="s16" + ) + await realtime.simple_audio_response(pcm_b, participant_b) + + # OpenAI creates conversation item for User B + item_created_b = { + "type": "conversation.item.created", + "event_id": "event_created_b", + "item": { + "id": "item_b_456", + "type": "message", + "role": "user", + "content": [], + }, + } + await realtime._handle_openai_event(item_created_b) + + # Now transcriptions arrive (A's transcription arrives AFTER B started speaking) + transcription_a = { + "content_index": 0, + "event_id": "event_trans_a", + "item_id": "item_a_123", # References User A's item + "transcript": "Hello from User A", + "type": "conversation.item.input_audio_transcription.completed", + "usage": {"seconds": 1, "type": "duration"}, + } + await realtime._handle_openai_event(transcription_a) + + transcription_b = { + "content_index": 0, + "event_id": "event_trans_b", + "item_id": "item_b_456", # References User B's item + "transcript": "Hello from User B", + "type": "conversation.item.input_audio_transcription.completed", + "usage": {"seconds": 1, "type": "duration"}, + } + await realtime._handle_openai_event(transcription_b) + + await asyncio.sleep(0.1) + + # Verify both transcriptions are attributed to the correct users + assert len(user_transcripts) == 2 + + # User A's transcription should be attributed to User A (not B, despite B speaking more recently) + assert user_transcripts[0].text == "Hello from User A" + assert user_transcripts[0].user_metadata is not None + assert user_transcripts[0].user_metadata.user_id == "user_a" + + # User B's transcription should be attributed to User B + assert user_transcripts[1].text == "Hello from User B" + assert user_transcripts[1].user_metadata is not None + assert user_transcripts[1].user_metadata.user_id == "user_b" diff --git a/plugins/openai/vision_agents/plugins/openai/openai_llm.py b/plugins/openai/vision_agents/plugins/openai/openai_llm.py index c16a850c..50f5e4e7 100644 --- a/plugins/openai/vision_agents/plugins/openai/openai_llm.py +++ b/plugins/openai/vision_agents/plugins/openai/openai_llm.py @@ -3,13 +3,20 @@ from openai import AsyncOpenAI from openai.lib.streaming.responses import ResponseStreamEvent -from openai.types.responses import ResponseCompletedEvent, ResponseTextDeltaEvent, Response as OpenAIResponse +from openai.types.responses import ( + ResponseCompletedEvent, + ResponseTextDeltaEvent, + Response as OpenAIResponse, +) from getstream.video.rtc.pb.stream.video.sfu.models.models_pb2 import Participant from vision_agents.core.llm.llm import LLM, LLMResponseEvent from vision_agents.core.llm.llm_types import ToolSchema, NormalizedToolCallItem -from vision_agents.core.llm.events import LLMResponseChunkEvent, LLMResponseCompletedEvent +from vision_agents.core.llm.events import ( + LLMResponseChunkEvent, + LLMResponseCompletedEvent, +) from . import events from vision_agents.core.processors import Processor @@ -42,7 +49,12 @@ class OpenAILLM(LLM): """ - def __init__(self, model: str, api_key: Optional[str] = None, client: Optional[AsyncOpenAI] = None): + def __init__( + self, + model: str, + api_key: Optional[str] = None, + client: Optional[AsyncOpenAI] = None, + ): """ Initialize the OpenAILLM class. @@ -64,8 +76,12 @@ def __init__(self, model: str, api_key: Optional[str] = None, client: Optional[A else: self.client = AsyncOpenAI() - async def simple_response(self, text: str, processors: Optional[List[Processor]] = None, - participant: Participant = None): + async def simple_response( + self, + text: str, + processors: Optional[List[Processor]] = None, + participant: Participant = None, + ): """ simple_response is a standardized way (across openai, claude, gemini etc.) to create a response. @@ -80,7 +96,7 @@ async def simple_response(self, text: str, processors: Optional[List[Processor]] """ # Use enhanced instructions if available (includes markdown file contents) instructions = None - if hasattr(self, 'parsed_instructions') and self.parsed_instructions: + if hasattr(self, "parsed_instructions") and self.parsed_instructions: instructions = self._build_enhanced_instructions() elif self.conversation is not None: instructions = self.conversation.instructions @@ -90,7 +106,9 @@ async def simple_response(self, text: str, processors: Optional[List[Processor]] instructions=instructions, ) - async def create_response(self, *args: Any, **kwargs: Any) -> LLMResponseEvent[OpenAIResponse]: + async def create_response( + self, *args: Any, **kwargs: Any + ) -> LLMResponseEvent[OpenAIResponse]: """ create_response gives you full support/access to the native openAI responses.create method this method wraps the openAI method and ensures we broadcast an event which the agent class hooks into @@ -110,8 +128,7 @@ async def create_response(self, *args: Any, **kwargs: Any) -> LLMResponseEvent[O kwargs["tools"] = self._convert_tools_to_provider_format(tools_spec) # type: ignore[arg-type] # Use parsed instructions if available (includes markdown file contents) - if hasattr(self, 'parsed_instructions') and self.parsed_instructions: - + if hasattr(self, "parsed_instructions") and self.parsed_instructions: # Combine original instructions with markdown file contents enhanced_instructions = self._build_enhanced_instructions() if enhanced_instructions: @@ -122,43 +139,49 @@ async def create_response(self, *args: Any, **kwargs: Any) -> LLMResponseEvent[O # Use the first positional argument as input, or create a default input_content = args[0] if args else "Hello" kwargs["input"] = input_content - + # OpenAI Responses API only accepts keyword arguments response = await self.client.responses.create(**kwargs) - llm_response : Optional[LLMResponseEvent[OpenAIResponse]] = None + llm_response: Optional[LLMResponseEvent[OpenAIResponse]] = None if isinstance(response, OpenAIResponse): # Non-streaming response - llm_response = LLMResponseEvent[OpenAIResponse](response, response.output_text) - + llm_response = LLMResponseEvent[OpenAIResponse]( + response, response.output_text + ) + # Check for tool calls in non-streaming response tool_calls = self._extract_tool_calls_from_response(response) if tool_calls: # Execute tools and get follow-up response llm_response = await self._handle_tool_calls(tool_calls, kwargs) - + elif hasattr(response, "__aiter__"): # async stream # Streaming response stream_response = response pending_tool_calls = [] seen = set() - + # Process streaming events and collect tool calls async for event in stream_response: llm_response_optional = self._standardize_and_emit_event(event) if llm_response_optional is not None: llm_response = llm_response_optional - + # Grab tool calls when the model finalizes the turn if getattr(event, "type", "") == "response.completed": calls = self._extract_tool_calls_from_response(event.response) for c in calls: - key = (c["id"], c["name"], json.dumps(c["arguments_json"], sort_keys=True)) + key = ( + c["id"], + c["name"], + json.dumps(c["arguments_json"], sort_keys=True), + ) if key not in seen: pending_tool_calls.append(c) seen.add(key) - + # If we have tool calls, execute them and get follow-up response if pending_tool_calls: llm_response = await self._handle_tool_calls(pending_tool_calls, kwargs) @@ -171,22 +194,27 @@ async def create_response(self, *args: Any, **kwargs: Any) -> LLMResponseEvent[O # Only emit it here for non-streaming responses to avoid duplication. if llm_response is not None and isinstance(response, OpenAIResponse): # Non-streaming response - emit completion event - self.events.send(LLMResponseCompletedEvent( - original=llm_response.original, - text=llm_response.text - )) + self.events.send( + LLMResponseCompletedEvent( + item_id=llm_response.original.output[0].id, + original=llm_response.original, + text=llm_response.text, + ) + ) return llm_response or LLMResponseEvent[OpenAIResponse](None, "") # type: ignore[arg-type] - async def _handle_tool_calls(self, tool_calls: List[NormalizedToolCallItem], original_kwargs: Dict[str, Any]) -> LLMResponseEvent[OpenAIResponse]: + async def _handle_tool_calls( + self, tool_calls: List[NormalizedToolCallItem], original_kwargs: Dict[str, Any] + ) -> LLMResponseEvent[OpenAIResponse]: """ Handle tool calls by executing them and getting a follow-up response. Supports multi-round tool calling (max 3 rounds). - + Args: tool_calls: List of tool calls to execute original_kwargs: Original kwargs from the request - + Returns: LLM response with tool results """ @@ -195,15 +223,20 @@ async def _handle_tool_calls(self, tool_calls: List[NormalizedToolCallItem], ori current_tool_calls = tool_calls current_kwargs = original_kwargs.copy() seen: set[tuple] = set() - + for round_num in range(max_rounds): # Execute tools (with cross-round deduplication) - triples, seen = await self._dedup_and_execute(current_tool_calls, max_concurrency=8, timeout_s=30, seen=seen) # type: ignore[arg-type] - + triples, seen = await self._dedup_and_execute( + current_tool_calls, # type: ignore[arg-type] + max_concurrency=8, + timeout_s=30, + seen=seen, + ) + # If no tools were executed, break the loop if not triples: break - + # Process all tool calls, including failed ones tool_messages = [] for tc, res, err in triples: @@ -211,74 +244,86 @@ async def _handle_tool_calls(self, tool_calls: List[NormalizedToolCallItem], ori if not cid: # Skip tool calls without ID - they can't be reported back continue - + # Use error result if there was an error, otherwise use the result output = err if err is not None else res - + # Convert to string for OpenAI Responses API with sanitization output_str = self._sanitize_tool_output(output) - tool_messages.append({ - "type": "function_call_output", - "call_id": cid, - "output": output_str, - }) - + tool_messages.append( + { + "type": "function_call_output", + "call_id": cid, + "output": output_str, + } + ) + # Don't send empty tool result inputs if not tool_messages: return llm_response or LLMResponseEvent[OpenAIResponse](None, "") # type: ignore[arg-type] - + # Send follow-up request with tool results if not self.openai_conversation: return llm_response or LLMResponseEvent[OpenAIResponse](None, "") # type: ignore[arg-type] - + follow_up_kwargs = { "model": current_kwargs.get("model", self.model), "conversation": self.openai_conversation.id, "input": tool_messages, "stream": True, } - + # Include tools again for potential follow-up calls tools_spec = self._get_tools_for_provider() if tools_spec: - follow_up_kwargs["tools"] = self._convert_tools_to_provider_format(tools_spec) # type: ignore[arg-type] - + follow_up_kwargs["tools"] = self._convert_tools_to_provider_format( + tools_spec # type: ignore[arg-type] + ) + # Get follow-up response follow_up_response = await self.client.responses.create(**follow_up_kwargs) - + if isinstance(follow_up_response, OpenAIResponse): # Non-streaming response - llm_response = LLMResponseEvent[OpenAIResponse](follow_up_response, follow_up_response.output_text) - + llm_response = LLMResponseEvent[OpenAIResponse]( + follow_up_response, follow_up_response.output_text + ) + # Check for more tool calls - next_tool_calls = self._extract_tool_calls_from_response(follow_up_response) + next_tool_calls = self._extract_tool_calls_from_response( + follow_up_response + ) if next_tool_calls and round_num < max_rounds - 1: current_tool_calls = next_tool_calls current_kwargs = follow_up_kwargs continue else: return llm_response - + elif hasattr(follow_up_response, "__aiter__"): # async stream stream_response = follow_up_response llm_response = None pending_tool_calls = [] # Don't reset seen - keep deduplication across rounds - + async for event in stream_response: llm_response_optional = self._standardize_and_emit_event(event) if llm_response_optional is not None: llm_response = llm_response_optional - + # Check for more tool calls if getattr(event, "type", "") == "response.completed": calls = self._extract_tool_calls_from_response(event.response) for c in calls: - key = (c["id"], c["name"], json.dumps(c["arguments_json"], sort_keys=True)) + key = ( + c["id"], + c["name"], + json.dumps(c["arguments_json"], sort_keys=True), + ) if key not in seen: pending_tool_calls.append(c) seen.add(key) - + # If we have more tool calls and haven't exceeded max rounds, continue if pending_tool_calls and round_num < max_rounds - 1: current_tool_calls = pending_tool_calls @@ -289,7 +334,7 @@ async def _handle_tool_calls(self, tool_calls: List[NormalizedToolCallItem], ori else: # Defensive fallback return LLMResponseEvent[OpenAIResponse](None, "") # type: ignore[arg-type] - + # If we've exhausted all rounds, return the last response return llm_response or LLMResponseEvent[OpenAIResponse](None, "") # type: ignore[arg-type] @@ -302,9 +347,7 @@ def _normalize_message(openai_input) -> List["Message"]: # standardize on input if isinstance(openai_input, str): - openai_input = [ - dict(content=openai_input, role="user", type="message") - ] + openai_input = [dict(content=openai_input, role="user", type="message")] elif not isinstance(openai_input, List): openai_input = [openai_input] @@ -316,15 +359,15 @@ def _normalize_message(openai_input) -> List["Message"]: return messages - - - def _convert_tools_to_provider_format(self, tools: List[ToolSchema]) -> List[Dict[str, Any]]: + def _convert_tools_to_provider_format( + self, tools: List[ToolSchema] + ) -> List[Dict[str, Any]]: """ Convert ToolSchema objects to OpenAI Responses API format. - + Args: tools: List of ToolSchema objects from the function registry - + Returns: List of tools in OpenAI Responses API format """ @@ -339,22 +382,26 @@ def _convert_tools_to_provider_format(self, tools: List[ToolSchema]) -> List[Dic params.setdefault("properties", {}) params.setdefault("additionalProperties", False) - out.append({ - "type": "function", - "name": name, # <-- top-level - "description": description, # <-- top-level - "parameters": params, # <-- top-level - "strict": True, # optional but fine - }) + out.append( + { + "type": "function", + "name": name, # <-- top-level + "description": description, # <-- top-level + "parameters": params, # <-- top-level + "strict": True, # optional but fine + } + ) return out - def _extract_tool_calls_from_response(self, response: Any) -> List[NormalizedToolCallItem]: + def _extract_tool_calls_from_response( + self, response: Any + ) -> List[NormalizedToolCallItem]: """ Extract tool calls from OpenAI response. - + Args: response: OpenAI response object - + Returns: List of normalized tool call items """ @@ -368,22 +415,23 @@ def _extract_tool_calls_from_response(self, response: Any) -> List[NormalizedToo args_obj = {} call_item: NormalizedToolCallItem = { "type": "tool_call", - "id": getattr(item, "call_id", ""), # <-- call_id + "id": getattr(item, "call_id", ""), # <-- call_id "name": getattr(item, "name", "unknown"), "arguments_json": args_obj, } calls.append(call_item) return calls - - def _create_tool_result_message(self, tool_calls: List[NormalizedToolCallItem], results: List[Any]) -> List[Dict[str, Any]]: + def _create_tool_result_message( + self, tool_calls: List[NormalizedToolCallItem], results: List[Any] + ) -> List[Dict[str, Any]]: """ Create tool result messages for OpenAI Responses API. - + Args: tool_calls: List of tool calls that were executed results: List of results from function execution - + Returns: List of tool result messages in Responses API format """ @@ -393,56 +441,69 @@ def _create_tool_result_message(self, tool_calls: List[NormalizedToolCallItem], if not call_id: # skip or wrap into a normal assistant message / log an error continue - + # Send only function_call_output items keyed by call_id # Convert to string for Responses API output_str = res if isinstance(res, str) else json.dumps(res) - msgs.append({ - "type": "function_call_output", - "call_id": call_id, - "output": output_str, - }) + msgs.append( + { + "type": "function_call_output", + "call_id": call_id, + "output": output_str, + } + ) return msgs - def _standardize_and_emit_event(self, event: ResponseStreamEvent) -> Optional[LLMResponseEvent]: + def _standardize_and_emit_event( + self, event: ResponseStreamEvent + ) -> Optional[LLMResponseEvent]: """ Forwards the events and also send out a standardized version (the agent class hooks into that) """ # start by forwarding the native event - self.events.send(events.OpenAIStreamEvent( - plugin_name="openai", - event_type=event.type, - event_data=event - )) + self.events.send( + events.OpenAIStreamEvent( + plugin_name="openai", event_type=event.type, event_data=event + ) + ) if event.type == "response.error": # Handle error events error_message = getattr(event, "error", {}).get("message", "Unknown error") - self.events.send(events.LLMErrorEvent( - plugin_name="openai", - error_message=error_message, - event_data=event - )) + self.events.send( + events.LLMErrorEvent( + plugin_name="openai", error_message=error_message, event_data=event + ) + ) return None elif event.type == "response.output_text.delta": # standardize the delta event delta_event: ResponseTextDeltaEvent = event - self.events.send(LLMResponseChunkEvent( - plugin_name="openai", - content_index=delta_event.content_index, - item_id=delta_event.item_id, - output_index=delta_event.output_index, - sequence_number=delta_event.sequence_number, - delta=delta_event.delta, - )) + self.events.send( + LLMResponseChunkEvent( + plugin_name="openai", + # sadly content_index is always set to 0 + # content_index=delta_event.content_index, + content_index=None, + item_id=delta_event.item_id, + output_index=delta_event.output_index, + sequence_number=delta_event.sequence_number, + delta=delta_event.delta, + ) + ) elif event.type == "response.completed": # standardize the response event and return the llm response completed_event: ResponseCompletedEvent = event - llm_response = LLMResponseEvent[OpenAIResponse](completed_event.response, completed_event.response.output_text) - self.events.send(LLMResponseCompletedEvent( - plugin_name="openai", - original=llm_response.original, - text=llm_response.text - )) + llm_response = LLMResponseEvent[OpenAIResponse]( + completed_event.response, completed_event.response.output_text + ) + self.events.send( + LLMResponseCompletedEvent( + plugin_name="openai", + original=llm_response.original, + text=llm_response.text, + item_id=llm_response.original.output[0].id, + ) + ) return llm_response return None diff --git a/plugins/openai/vision_agents/plugins/openai/openai_realtime.py b/plugins/openai/vision_agents/plugins/openai/openai_realtime.py index 2cec37d6..e2773c93 100644 --- a/plugins/openai/vision_agents/plugins/openai/openai_realtime.py +++ b/plugins/openai/vision_agents/plugins/openai/openai_realtime.py @@ -2,8 +2,12 @@ from typing import Any, Optional, List, Dict from getstream.video.rtc.audio_track import AudioStreamTrack -from openai.types.realtime import RealtimeSessionCreateRequestParam, ResponseAudioTranscriptDoneEvent, \ - InputAudioBufferSpeechStartedEvent +from openai.types.realtime import ( + RealtimeSessionCreateRequestParam, + ResponseAudioTranscriptDoneEvent, + InputAudioBufferSpeechStartedEvent, + ConversationItemInputAudioTranscriptionCompletedEvent, +) from vision_agents.core.llm import realtime from vision_agents.core.llm.llm_types import ToolSchema @@ -22,8 +26,8 @@ """ TODO -- support passing full client options in __init__. at that's not possible. -- review either SessionCreateParams or RealtimeSessionCreateRequestParam +- support passing full client options in __init__. at that's not possible. +- review either SessionCreateParams or RealtimeSessionCreateRequestParam - send video should depend on if the RTC connection with stream is sending video. """ @@ -50,16 +54,20 @@ class Realtime(realtime.Realtime): - MCP integration for external service access. """ - def __init__(self, model: str = "gpt-realtime", voice: str = "marin", *args, **kwargs): + + def __init__( + self, model: str = "gpt-realtime", voice: str = "marin", *args, **kwargs + ): super().__init__(*args, **kwargs) self.model = model self.voice = voice # TODO: send video should depend on if the RTC connection with stream is sending video. self.rtc = RTCManager(self.model, self.voice, True) # audio output track? - self.output_track = AudioStreamTrack( - framerate=48000, stereo=True, format="s16" - ) + self.output_track = AudioStreamTrack(framerate=48000, stereo=True, format="s16") + # Map conversation item_id to participant to handle multi-user scenarios + self._item_to_participant: Dict[str, Participant] = {} + self._pending_participant: Optional[Participant] = None async def connect(self): """Establish the WebRTC connection to OpenAI's Realtime API. @@ -74,23 +82,27 @@ async def connect(self): instructions = self.instructions self.rtc.instructions = instructions - + # Wire callbacks so we can emit audio/events upstream self.rtc.set_event_callback(self._handle_openai_event) self.rtc.set_audio_callback(self._handle_audio_output) await self.rtc.connect() - + # Register tools with OpenAI realtime if available await self._register_tools_with_openai_realtime() - + # Emit connected/ready self._emit_connected_event( session_config={"model": self.model, "voice": self.voice}, capabilities=["text", "audio", "function_calling"], ) - async def simple_response(self, text: str, processors: Optional[List[Processor]] = None, - participant: Optional[Participant] = None): + async def simple_response( + self, + text: str, + processors: Optional[List[Processor]] = None, + participant: Optional[Participant] = None, + ): """Send a simple text input to the OpenAI Realtime session. This is a convenience wrapper that forwards a text prompt upstream via @@ -106,7 +118,9 @@ async def simple_response(self, text: str, processors: Optional[List[Processor]] """ await self.rtc.send_text(text) - async def simple_audio_response(self, audio: PcmData): + async def simple_audio_response( + self, audio: PcmData, participant: Optional[Participant] = None + ): """Send a single PCM audio frame to the OpenAI Realtime session. The audio should be raw PCM matching the realtime session's expected @@ -115,7 +129,10 @@ async def simple_audio_response(self, audio: PcmData): Args: audio: PCM audio frame to forward upstream. + participant: Optional participant information for the audio source. """ + # Track pending participant for the next conversation item + self._pending_participant = participant await self.rtc.send_audio_pcm(audio) async def request_session_info(self) -> None: @@ -141,7 +158,8 @@ async def _handle_openai_event(self, event: dict) -> None: event: Raw event dictionary from OpenAI API. Event Handling: - - response.audio_transcript.done: Emits transcript/response events + - response.audio_transcript.done: Emits agent speech transcription + - conversation.item.input_audio_transcription.completed: Emits user speech transcription - input_audio_buffer.speech_started: Flushes output audio track - response.tool_call: Handles tool calls from OpenAI realtime @@ -149,14 +167,73 @@ async def _handle_openai_event(self, event: dict) -> None: Registered as callback with RTC manager. """ et = event.get("type") - if et == "response.audio_transcript.done": - transcript_event: ResponseAudioTranscriptDoneEvent = ResponseAudioTranscriptDoneEvent.model_validate(event) - self._emit_transcript_event(text=transcript_event.transcript, user_metadata={"role": "assistant", "source": "openai"}) - self._emit_response_event(text=transcript_event.transcript, response_id=transcript_event.response_id, is_complete=True, conversation_item_id=transcript_event.item_id) + logger.debug(f"OpenAI Realtime event: {et}") + + # code here is weird because OpenAI does something strange + # see issue: https://github.com/openai/openai-python/issues/2698 + # as a workaround we copy the event and set type to response.output_audio_transcript.done so that + # ResponseAudioTranscriptDoneEvent.model_validate is happy + if et in [ + "response.audio_transcript.done", + "response.output_audio_transcript.done", + ]: + event_copy = event.copy() + event_copy["type"] = "response.output_audio_transcript.done" + transcript_event: ResponseAudioTranscriptDoneEvent = ( + ResponseAudioTranscriptDoneEvent.model_validate(event_copy) + ) + self._emit_agent_speech_transcription( + text=transcript_event.transcript, original=event + ) + self._emit_response_event( + text=transcript_event.transcript, + response_id=transcript_event.response_id, + is_complete=True, + conversation_item_id=transcript_event.item_id, + ) + elif et == "conversation.item.created": + # When OpenAI creates a conversation item, map it to the participant who sent the audio + item = event.get("item", {}) + if item.get("type") == "message" and item.get("role") == "user": + item_id = item.get("id") + if item_id and self._pending_participant: + self._item_to_participant[item_id] = self._pending_participant + logger.debug( + f"Mapped item {item_id} to participant {self._pending_participant.user_id if self._pending_participant else 'None'}" + ) + elif et == "conversation.item.input_audio_transcription.completed": + # User input audio transcription completed + user_transcript_event: ConversationItemInputAudioTranscriptionCompletedEvent = ConversationItemInputAudioTranscriptionCompletedEvent.model_validate( + event + ) + item_id = user_transcript_event.item_id + + # Look up the correct participant for this transcription + participant = self._item_to_participant.get(item_id) + if participant: + logger.info( + f"User speech transcript from {participant.user_id}: {user_transcript_event.transcript}" + ) + else: + logger.info( + f"User speech transcript (no participant mapping): {user_transcript_event.transcript}" + ) + + # Temporarily set the correct participant for this specific transcription + original_participant = self._current_participant + self._current_participant = participant + self._emit_user_speech_transcription( + text=user_transcript_event.transcript, original=event + ) + self._current_participant = original_participant + + # Clean up the mapping to avoid memory leaks + if item_id: + self._item_to_participant.pop(item_id, None) elif et == "input_audio_buffer.speech_started": # Validate event but don't need to store it InputAudioBufferSpeechStartedEvent.model_validate(event) - await self.output_track.flush() + # await self.output_track.flush() elif et == "response.output_item.added": # Check if this is a function call item = event.get("item", {}) @@ -182,20 +259,22 @@ async def _handle_audio_output(self, audio_bytes: bytes) -> None: audio_data=audio_bytes, sample_rate=48000, # OpenAI Realtime uses 48kHz ) - + # Forward audio to output track for playback await self.output_track.write(audio_bytes) async def _watch_video_track(self, track, **kwargs) -> None: - shared_forwarder = kwargs.get('shared_forwarder') - await self.rtc.start_video_sender(track, self.fps, shared_forwarder=shared_forwarder) + shared_forwarder = kwargs.get("shared_forwarder") + await self.rtc.start_video_sender( + track, self.fps, shared_forwarder=shared_forwarder + ) async def _stop_watching_video_track(self) -> None: await self.rtc.stop_video_sender() async def _handle_tool_call_event(self, event: dict) -> None: """Handle tool call events from OpenAI realtime. - + Args: event: Tool call event from OpenAI realtime API """ @@ -206,24 +285,26 @@ async def _handle_tool_call_event(self, event: dict) -> None: tool_call_data = item else: tool_call_data = event.get("tool_call", {}) - + if not tool_call_data: logger.warning("Received tool call event without tool_call data") return - + # Extract tool call details tool_call = { "type": "tool_call", "id": tool_call_data.get("call_id"), "name": tool_call_data.get("name", "unknown"), - "arguments_json": tool_call_data.get("arguments", {}) + "arguments_json": tool_call_data.get("arguments", {}), } - - logger.info(f"Executing tool call: {tool_call['name']} with args: {tool_call['arguments_json']}") - + + logger.info( + f"Executing tool call: {tool_call['name']} with args: {tool_call['arguments_json']}" + ) + # Execute using existing tool execution infrastructure tc, result, error = await self._run_one_tool(tool_call, timeout_s=30) - + # Prepare response data if error: response_data = {"error": str(error)} @@ -235,10 +316,10 @@ async def _handle_tool_call_event(self, event: dict) -> None: else: response_data = result logger.info(f"Tool call {tool_call['name']} succeeded: {response_data}") - + # Send tool response back to OpenAI realtime session await self._send_tool_response(tool_call["id"], response_data) - + except Exception as e: logger.error(f"Error handling tool call event: {e}") # Send error response back @@ -249,9 +330,11 @@ async def _handle_tool_call_event(self, event: dict) -> None: call_id = event.get("tool_call", {}).get("call_id") await self._send_tool_response(call_id, {"error": str(e)}) - async def _send_tool_response(self, call_id: Optional[str], response_data: Dict[str, Any]) -> None: + async def _send_tool_response( + self, call_id: Optional[str], response_data: Dict[str, Any] + ) -> None: """Send tool response back to OpenAI realtime session. - + Args: call_id: The call ID from the original tool call response_data: The response data to send back @@ -259,44 +342,46 @@ async def _send_tool_response(self, call_id: Optional[str], response_data: Dict[ if not call_id: logger.warning("Cannot send tool response without call_id") return - + try: # Convert response to string for OpenAI realtime response_str = self._sanitize_tool_output(response_data) - + # Send tool response event event = { "type": "conversation.item.create", "item": { "type": "function_call_output", "call_id": call_id, - "output": response_str - } + "output": response_str, + }, } - + await self.rtc._send_event(event) logger.info(f"Sent tool response for call_id {call_id}") - + # Trigger a new response to continue the conversation with audio # This ensures the AI responds with audio after receiving the tool result - await self.rtc._send_event({ - "type": "response.create", - "response": { - "modalities": ["text", "audio"], - "instructions": "Please respond to the user with the tool results in a conversational way." + await self.rtc._send_event( + { + "type": "response.create", + "response": { + "modalities": ["text", "audio"], + "instructions": "Please respond to the user with the tool results in a conversational way.", + }, } - }) - + ) + except Exception as e: logger.error(f"Failed to send tool response: {e}") def _sanitize_tool_output(self, value: Any, max_chars: int = 60_000) -> str: """Sanitize tool output for OpenAI realtime. - + Args: value: The tool output to sanitize max_chars: Maximum characters allowed (not used in realtime mode) - + Returns: Sanitized string output """ @@ -307,12 +392,14 @@ def _sanitize_tool_output(self, value: Any, max_chars: int = 60_000) -> str: else: return str(value) - def _convert_tools_to_openai_realtime_format(self, tools: List[ToolSchema]) -> List[Dict[str, Any]]: + def _convert_tools_to_openai_realtime_format( + self, tools: List[ToolSchema] + ) -> List[Dict[str, Any]]: """Convert ToolSchema objects to OpenAI realtime format. - + Args: tools: List of ToolSchema objects from the function registry - + Returns: List of tools in OpenAI realtime format """ @@ -327,46 +414,50 @@ def _convert_tools_to_openai_realtime_format(self, tools: List[ToolSchema]) -> L params.setdefault("properties", {}) params.setdefault("additionalProperties", False) - out.append({ - "type": "function", - "name": name, - "description": description, - "parameters": params, - }) + out.append( + { + "type": "function", + "name": name, + "description": description, + "parameters": params, + } + ) return out async def _register_tools_with_openai_realtime(self) -> None: """Register available tools with OpenAI realtime session. - + This method registers all available functions and MCP tools with the OpenAI realtime session so they can be called during conversations. """ try: # Get available tools from function registry available_tools = self.get_available_functions() - + if not available_tools: logger.info("No tools available to register with OpenAI realtime") return - + # Convert tools to OpenAI realtime format - tools_for_openai = self._convert_tools_to_openai_realtime_format(available_tools) - + tools_for_openai = self._convert_tools_to_openai_realtime_format( + available_tools + ) + if not tools_for_openai: logger.info("No tools converted for OpenAI realtime") return - + # Send tools configuration to OpenAI realtime tools_event = { "type": "session.update", - "session": { - "tools": tools_for_openai - } + "session": {"tools": tools_for_openai}, } - + await self.rtc._send_event(tools_event) - logger.info(f"Registered {len(tools_for_openai)} tools with OpenAI realtime") - + logger.info( + f"Registered {len(tools_for_openai)} tools with OpenAI realtime" + ) + except Exception as e: logger.error(f"Failed to register tools with OpenAI realtime: {e}") # Don't raise the exception - tool registration failure shouldn't break the connection diff --git a/plugins/openai/vision_agents/plugins/openai/rtc_manager.py b/plugins/openai/vision_agents/plugins/openai/rtc_manager.py index 7f82a32b..e9457b6d 100644 --- a/plugins/openai/vision_agents/plugins/openai/rtc_manager.py +++ b/plugins/openai/vision_agents/plugins/openai/rtc_manager.py @@ -42,7 +42,7 @@ def __init__(self, sample_rate: int = 48000): self._latest_chunk: Optional[bytes] = None self._silence_cache: dict[int, np.ndarray] = {} - def set_input (self, pcm_data: bytes, sample_rate: Optional[int] = None) -> None: + def set_input(self, pcm_data: bytes, sample_rate: Optional[int] = None) -> None: if not pcm_data: return if sample_rate is not None: @@ -93,16 +93,18 @@ class StreamVideoForwardingTrack(VideoStreamTrack): """Track that forwards frames from Stream Video to OpenAI. TODO: why do we have this forwarding track, when there is the video_forwarder """ - + kind = "video" - - def __init__(self, source_track: MediaStreamTrack, fps: int = 1, shared_forwarder=None): + + def __init__( + self, source_track: MediaStreamTrack, fps: int = 1, shared_forwarder=None + ): super().__init__() self._source_track = source_track self._fps = max(1, fps) self._interval = 1.0 / self._fps self._ts = 0 - self._last_frame_time = 0. + self._last_frame_time = 0.0 self._frame_count = 0 self._error_count = 0 self._consecutive_errors = 0 @@ -116,27 +118,30 @@ def __init__(self, source_track: MediaStreamTrack, fps: int = 1, shared_forwarde self._started: bool = False # Rate limiting for inactive track warnings - self._last_inactive_warning = 0. + self._last_inactive_warning = 0.0 self._inactive_warning_interval = 30.0 # Only warn every 30 seconds - + if shared_forwarder: - logger.info(f"🎥 StreamVideoForwardingTrack initialized with SHARED forwarder: fps={fps}, interval={self._interval:.3f}s") + logger.info( + f"🎥 StreamVideoForwardingTrack initialized with SHARED forwarder: fps={fps}, interval={self._interval:.3f}s" + ) else: - logger.info(f"🎥 StreamVideoForwardingTrack initialized: fps={fps}, interval={self._interval:.3f}s (frame limiting DISABLED for performance)") - + logger.info( + f"🎥 StreamVideoForwardingTrack initialized: fps={fps}, interval={self._interval:.3f}s (frame limiting DISABLED for performance)" + ) + async def start(self) -> None: if self._started: + logger.warning("rtc manager already started", stack_info=True) return - - if self._shared_forwarder is not None: - # Use the shared forwarder - self._forwarder = self._shared_forwarder - logger.info(f"🎥 OpenAI using shared VideoForwarder at {self._fps} FPS") - else: - # Create our own VideoForwarder with the input source track (legacy behavior) - self._forwarder = VideoForwarder(self._source_track, max_buffer=5, fps=self._fps) # type: ignore[arg-type] - await self._forwarder.start() - + + if self._shared_forwarder is None: + raise RuntimeError( + "self._shared_forwarder is None, something is very wrong" + ) + + self._forwarder = self._shared_forwarder + logger.info(f"🎥 OpenAI using shared VideoForwarder at {self._fps} FPS") self._started = True async def recv(self): @@ -148,7 +153,9 @@ async def recv(self): # Rate limit warnings to avoid spam now = time.monotonic() if now - self._last_inactive_warning > self._inactive_warning_interval: - logger.warning("🎥 StreamVideoForwardingTrack is no longer active, returning black frame") + logger.warning( + "🎥 StreamVideoForwardingTrack is no longer active, returning black frame" + ) self._last_inactive_warning = now return self._generate_black_frame() @@ -157,8 +164,12 @@ async def recv(self): # Health check: detect if track has been dead for too long if now - self._last_health_check > self._health_check_interval: self._last_health_check = now - if now - self._last_successful_frame_time > 30.0: # No frames for 30 seconds - logger.error("🎥 StreamVideoForwardingTrack health check failed - no frames for 30+ seconds") + if ( + now - self._last_successful_frame_time > 30.0 + ): # No frames for 30 seconds + logger.error( + "🎥 StreamVideoForwardingTrack health check failed - no frames for 30+ seconds" + ) self._is_active = False return self._generate_black_frame() @@ -178,7 +189,9 @@ async def recv(self): try: frame = frame.reformat(format="rgb24") except Exception as e: - logger.warning(f"🎥 Frame format conversion failed: {e}, using original") + logger.warning( + f"🎥 Frame format conversion failed: {e}, using original" + ) # Update timing for WebRTC frame.pts = self._ts @@ -191,18 +204,24 @@ async def recv(self): except asyncio.TimeoutError: self._consecutive_errors += 1 if self._consecutive_errors >= self._max_consecutive_errors: - logger.error(f"🎥 StreamVideoForwardingTrack circuit breaker triggered - {self._consecutive_errors} consecutive timeouts") + logger.error( + f"🎥 StreamVideoForwardingTrack circuit breaker triggered - {self._consecutive_errors} consecutive timeouts" + ) self._is_active = False return self._generate_black_frame() except Exception as e: self._consecutive_errors += 1 self._error_count += 1 - logger.error(f"❌ FRAME ERROR: frame_id={self._frame_count} (error #{self._error_count}, consecutive={self._consecutive_errors}): {e}") + logger.error( + f"❌ FRAME ERROR: frame_id={self._frame_count} (error #{self._error_count}, consecutive={self._consecutive_errors}): {e}" + ) if self._consecutive_errors >= self._max_consecutive_errors: - logger.error(f"🎥 StreamVideoForwardingTrack circuit breaker triggered - {self._consecutive_errors} consecutive errors") + logger.error( + f"🎥 StreamVideoForwardingTrack circuit breaker triggered - {self._consecutive_errors} consecutive errors" + ) self._is_active = False return self._generate_black_frame() - + def _generate_black_frame(self) -> VideoFrame: """Generate a black frame as fallback.""" black_array = np.zeros((480, 640, 3), dtype=np.uint8) @@ -211,10 +230,12 @@ def _generate_black_frame(self) -> VideoFrame: frame.time_base = Fraction(1, self._fps) self._ts += 1 return frame - + def stop(self): """Stop the forwarding track and forwarder.""" - logger.info(f"🎥 StreamVideoForwardingTrack stopped after {self._frame_count} frames, {self._error_count} errors") + logger.info( + f"🎥 StreamVideoForwardingTrack stopped after {self._frame_count} frames, {self._error_count} errors" + ) try: if self._forwarder is not None: asyncio.create_task(self._forwarder.stop()) @@ -281,7 +302,6 @@ async def on_track(track): await self.pc.setRemoteDescription(answer) logger.info("Remote description set; WebRTC established") - async def _get_session_token(self) -> str: url = OPENAI_SESSIONS_URL headers = { @@ -298,7 +318,9 @@ async def _get_session_token(self) -> str: async with AsyncClient() as client: for attempt in range(2): try: - resp = await client.post(url, headers=headers, json=payload, timeout=15) + resp = await client.post( + url, headers=headers, json=payload, timeout=15 + ) resp.raise_for_status() data: dict = resp.json() secret = data.get("client_secret", {}) @@ -319,19 +341,20 @@ async def _add_data_channel(self) -> None: async def on_open(): self._data_channel_open_event.set() - # Immediately switch to semantic VAD so it's active before the user speaks + # Immediately switch to semantic VAD and enable input audio transcription await self._send_event( - { - "type": "session.update", - "session": { - "turn_detection": { - "type": "semantic_vad" - } - }, - } - ) + { + "type": "session.update", + "session": { + "turn_detection": {"type": "semantic_vad"}, + "input_audio_transcription": {"model": "whisper-1"}, + }, + } + ) # Session information will be automatically stored when session.created event is received - logger.info("Requested semantic_vad via session.update") + logger.info( + "Requested semantic_vad and input_audio_transcription via session.update" + ) @self.data_channel.on("message") def on_message(message): @@ -369,7 +392,6 @@ async def recv(self): # Keep a handle to the currently active source (if any) for diagnostics / control self._active_video_source: Optional[MediaStreamTrack] = None - async def send_audio_pcm(self, pcm_data: PcmData) -> None: """Send raw PCM audio data to OpenAI. @@ -394,10 +416,9 @@ async def send_audio_pcm(self, pcm_data: PcmData) -> None: except Exception as e: logger.error(f"Failed to push mic audio: {e}") - async def send_text(self, text: str, role: str = "user"): """Send a text message to OpenAI. - + Args: text: The text message to send. role: Message role. Defaults to "user". @@ -428,13 +449,19 @@ async def _send_event(self, event: dict): # Ensure the data channel is open before sending if not self._data_channel_open_event.is_set(): try: - await asyncio.wait_for(self._data_channel_open_event.wait(), timeout=5.0) + await asyncio.wait_for( + self._data_channel_open_event.wait(), timeout=5.0 + ) except asyncio.TimeoutError: - logger.warning("Data channel not open after timeout; dropping event") + logger.warning( + "Data channel not open after timeout; dropping event" + ) return if self.data_channel.readyState and self.data_channel.readyState != "open": - logger.warning(f"Data channel state is '{self.data_channel.readyState}', cannot send event") + logger.warning( + f"Data channel state is '{self.data_channel.readyState}', cannot send event" + ) message_json = json.dumps(event) self.data_channel.send(message_json) @@ -442,7 +469,9 @@ async def _send_event(self, event: dict): except Exception as e: logger.error(f"Failed to send event: {e}") - async def start_video_sender(self, stream_video_track: MediaStreamTrack, fps: int = 1, shared_forwarder=None) -> None: + async def start_video_sender( + self, stream_video_track: MediaStreamTrack, fps: int = 1, shared_forwarder=None + ) -> None: """Replace dummy video track with the actual Stream Video forwarding track. This creates a forwarding track that reads frames from the Stream Video track @@ -453,15 +482,19 @@ async def start_video_sender(self, stream_video_track: MediaStreamTrack, fps: in fps: Target frames per second. shared_forwarder: Optional shared VideoForwarder to use instead of creating a new one. """ - + try: if not self.send_video: logger.error("❌ Video sending not enabled for this session") raise RuntimeError("Video sending not enabled for this session") if self._video_sender is None: - logger.error("❌ Video sender not available; was video track negotiated?") - raise RuntimeError("Video sender not available; was video track negotiated?") - + logger.error( + "❌ Video sender not available; was video track negotiated?" + ) + raise RuntimeError( + "Video sender not available; was video track negotiated?" + ) + # Stop any existing video sender task if self._video_sender_task is not None: logger.info("🎥 Stopping existing video sender task...") @@ -471,23 +504,29 @@ async def start_video_sender(self, stream_video_track: MediaStreamTrack, fps: in except asyncio.CancelledError: pass logger.info("🎥 Existing video sender task stopped") - + # Create forwarding track and start its forwarder - forwarding_track = StreamVideoForwardingTrack(stream_video_track, fps, shared_forwarder=shared_forwarder) + forwarding_track = StreamVideoForwardingTrack( + stream_video_track, fps, shared_forwarder=shared_forwarder + ) await forwarding_track.start() - + # Replace the dummy track with the forwarding track try: - logger.info("🎥 Replacing OpenAI dummy track with StreamVideoForwardingTrack") + logger.info( + "🎥 Replacing OpenAI dummy track with StreamVideoForwardingTrack" + ) self._video_sender.replaceTrack(forwarding_track) self._forwarding_track = forwarding_track self._active_video_source = stream_video_track - logger.info(f"✅ Successfully replaced OpenAI track with Stream Video forwarding (fps={fps})") + logger.info( + f"✅ Successfully replaced OpenAI track with Stream Video forwarding (fps={fps})" + ) except Exception as replace_error: logger.error(f"❌ Failed to replace video track: {replace_error}") logger.error(f"❌ Replace error type: {type(replace_error).__name__}") raise RuntimeError(f"Track replacement failed: {replace_error}") - + except Exception as e: logger.error(f"❌ Failed to start video sender: {e}") logger.error(f"❌ Error type: {type(e).__name__}") @@ -511,11 +550,11 @@ async def stop_video_sender(self) -> None: except Exception: pass self._forwarding_track = None - + if self._video_sender is None: logger.warning("No video sender available to stop") return - + # Replace track with proper error handling try: if self._video_track is None: @@ -524,13 +563,15 @@ async def stop_video_sender(self) -> None: logger.info("✅ Video sender detached (no base track)") else: self._video_sender.replaceTrack(self._video_track) - logger.info(f"✅ Video sender reverted to dummy track: {type(self._video_track).__name__}") - + logger.info( + f"✅ Video sender reverted to dummy track: {type(self._video_track).__name__}" + ) + self._active_video_source = None except Exception as replace_error: logger.error(f"❌ Failed to revert video track: {replace_error}") raise RuntimeError(f"Track reversion failed: {replace_error}") - + except Exception as e: logger.error(f"❌ Failed to stop video sender: {e}") raise @@ -557,7 +598,9 @@ async def _exchange_sdp(self, local_sdp: str) -> Optional[str]: try: async with AsyncClient() as client: - response = await client.post(url, headers=headers, content=local_sdp, timeout=20) + response = await client.post( + url, headers=headers, content=local_sdp, timeout=20 + ) response.raise_for_status() return response.text if response.text else None except HTTPStatusError as e: @@ -576,7 +619,10 @@ async def _handle_added_track(self, track: MediaStreamTrack) -> None: async def _reader(): while True: try: - frame: AudioFrame = cast(AudioFrame, await asyncio.wait_for(track.recv(), timeout=1.0)) + frame: AudioFrame = cast( + AudioFrame, + await asyncio.wait_for(track.recv(), timeout=1.0), + ) except asyncio.TimeoutError: continue except Exception as e: @@ -597,23 +643,21 @@ async def _reader(): await cb(audio_bytes) except Exception as e: logger.debug(f"Failed to process remote audio frame: {e}") + asyncio.create_task(_reader()) - async def _handle_event(self, event: dict) -> None: """Minimal event handler for data channel messages.""" + print(f"_handle_event {event['type']}") cb = self._event_callback if cb is not None: - try: - await cb(event) - except Exception as e: - logger.debug(f"Event callback error: {e}") + await cb(event) # Store session information when we receive session.created event - # FIXME Typing + # FIXME Typing if event.get("type") == "session.created" and "session" in event: self.session_info = event["session"] - logger.debug(f"Stored session info: {self.session_info}") + logger.error(f"Stored session info: {self.session_info}") async def request_session_info(self) -> None: """Request and log current session information. @@ -625,7 +669,9 @@ async def request_session_info(self) -> None: if self.session_info: logger.info(f"Current session info: {self.session_info}") else: - logger.info("No session information available yet. Waiting for session.created event.") + logger.info( + "No session information available yet. Waiting for session.created event." + ) def set_audio_callback(self, callback: Callable[[bytes], Any]) -> None: """Set callback for receiving audio data from OpenAI. @@ -643,51 +689,78 @@ def set_event_callback(self, callback: Callable[[dict], Any]) -> None: """ self._event_callback = callback - async def _forward_video_frames(self, source_track: MediaStreamTrack, fps: int) -> None: + async def _forward_video_frames( + self, source_track: MediaStreamTrack, fps: int + ) -> None: """Forward video frames from user's track to OpenAI via WebRTC. - + This method reads frames from the user's video track and forwards them through the WebRTC connection to OpenAI for processing. """ interval = max(0.01, 1.0 / max(1, fps)) frame_count = 0 - + try: - logger.info(f"🎥 Starting video frame forwarding loop (fps={fps}, interval={interval:.3f}s)") - logger.info(f"🎥 Source track: {type(source_track).__name__}, kind={getattr(source_track, 'kind', 'unknown')}") - + logger.info( + f"🎥 Starting video frame forwarding loop (fps={fps}, interval={interval:.3f}s)" + ) + logger.info( + f"🎥 Source track: {type(source_track).__name__}, kind={getattr(source_track, 'kind', 'unknown')}" + ) + while True: try: # Read frame from user's video track - logger.debug(f"🎥 Attempting to read frame #{frame_count + 1} from user track...") - frame: VideoFrame = cast(VideoFrame, await asyncio.wait_for(source_track.recv(), timeout=1.0)) + logger.debug( + f"🎥 Attempting to read frame #{frame_count + 1} from user track..." + ) + frame: VideoFrame = cast( + VideoFrame, + await asyncio.wait_for(source_track.recv(), timeout=1.0), + ) frame_count += 1 - + # Log frame details - logger.info(f"🎥 SUCCESS: Read frame #{frame_count} from user track!") - logger.info(f"🎥 Frame details: {frame.width}x{frame.height}, format={frame.format}, pts={frame.pts}") - + logger.info( + f"🎥 SUCCESS: Read frame #{frame_count} from user track!" + ) + logger.info( + f"🎥 Frame details: {frame.width}x{frame.height}, format={frame.format}, pts={frame.pts}" + ) + # The frame is automatically forwarded through the WebRTC connection # since we replaced the track with replaceTrack() - logger.debug(f"🎥 Frame #{frame_count} automatically forwarded via WebRTC") - + logger.debug( + f"🎥 Frame #{frame_count} automatically forwarded via WebRTC" + ) + # Throttle frame rate await asyncio.sleep(interval) - + except asyncio.TimeoutError: - logger.warning(f"🎥 Timeout waiting for frame #{frame_count + 1} from user track") + logger.warning( + f"🎥 Timeout waiting for frame #{frame_count + 1} from user track" + ) continue except Exception as e: - logger.error(f"❌ Error reading video frame #{frame_count + 1}: {e}") + logger.error( + f"❌ Error reading video frame #{frame_count + 1}: {e}" + ) logger.error(f"❌ Exception type: {type(e).__name__}") break - + except asyncio.CancelledError: - logger.info(f"🎥 Video forwarding task cancelled after {frame_count} frames") + logger.info( + f"🎥 Video forwarding task cancelled after {frame_count} frames" + ) except Exception as e: - logger.error(f"❌ Video forwarding task failed after {frame_count} frames: {e}") + logger.error( + f"❌ Video forwarding task failed after {frame_count} frames: {e}" + ) finally: - logger.info(f"🎥 Video forwarding task ended. Total frames processed: {frame_count}") + logger.info( + f"🎥 Video forwarding task ended. Total frames processed: {frame_count}" + ) async def close(self) -> None: """Close the WebRTC connection and clean up resources.""" @@ -699,7 +772,7 @@ async def close(self) -> None: await self._video_sender_task except asyncio.CancelledError: pass - + if self.data_channel is not None: try: self.data_channel.close() diff --git a/plugins/silero/pyproject.toml b/plugins/silero/pyproject.toml index ed9e1dea..f2488ca9 100644 --- a/plugins/silero/pyproject.toml +++ b/plugins/silero/pyproject.toml @@ -36,6 +36,6 @@ dev = [ "pytest>=8.4.1", "pytest-asyncio>=1.0.0", "soundfile>=0.13.1", - "torchaudio>=2.7.1", + "torchvision>=0.20.0", "scipy>=1.15.3,<1.16", ] diff --git a/plugins/silero/tests/test_vad.py b/plugins/silero/tests/test_vad.py index 67d3d167..e2b31043 100644 --- a/plugins/silero/tests/test_vad.py +++ b/plugins/silero/tests/test_vad.py @@ -6,7 +6,7 @@ import numpy as np import pytest import soundfile as sf -import torchaudio +from torchvision.io.video import av from typing import List, Dict, Any, Optional from vision_agents.plugins import silero @@ -44,9 +44,34 @@ def mia_wav_path(mia_mp3_path): fd, wav_path = tempfile.mkstemp(suffix=".wav") os.close(fd) - # Convert mp3 to wav using torchaudio - waveform, sample_rate = torchaudio.load(mia_mp3_path) - torchaudio.save(wav_path, waveform, sample_rate) + # Convert mp3 to wav using PyAV + container = av.open(mia_mp3_path) + audio_stream = container.streams.audio[0] + sample_rate = audio_stream.sample_rate + + # Read all audio frames + samples = [] + for frame in container.decode(audio_stream): + # Convert to numpy array + frame_array = frame.to_ndarray() + if len(frame_array.shape) > 1: + # Convert stereo to mono by averaging channels + frame_array = np.mean(frame_array, axis=0) + samples.append(frame_array) + + # Concatenate all samples + audio_data = np.concatenate(samples) + + # Convert to float32 for soundfile (it expects values between -1.0 and 1.0) + if audio_data.dtype == np.int16: + audio_data = audio_data.astype(np.float32) / 32768.0 + elif audio_data.dtype == np.int32: + audio_data = audio_data.astype(np.float32) / 2147483648.0 + + container.close() + + # Save as wav using soundfile + sf.write(wav_path, audio_data, sample_rate) yield wav_path @@ -446,19 +471,19 @@ async def test_silence_no_turns(): async def on_audio(event: VADAudioEvent): nonlocal audio_event_fired audio_event_fired = True - duration_sec = (event.duration_ms / 1000.0) if event.duration_ms is not None else 0.0 - logger.info( - f"Audio event detected on silence! Duration: {duration_sec:.2f}s" + duration_sec = ( + (event.duration_ms / 1000.0) if event.duration_ms is not None else 0.0 ) + logger.info(f"Audio event detected on silence! Duration: {duration_sec:.2f}s") @vad.events.subscribe async def on_partial(event: VADPartialEvent): nonlocal partial_event_fired partial_event_fired = True - duration_sec = (event.duration_ms / 1000.0) if event.duration_ms is not None else 0.0 - logger.info( - f"Partial event detected on silence! Duration: {duration_sec:.2f}s" + duration_sec = ( + (event.duration_ms / 1000.0) if event.duration_ms is not None else 0.0 ) + logger.info(f"Partial event detected on silence! Duration: {duration_sec:.2f}s") # Process the silence in chunks to simulate streaming chunk_size = 512 diff --git a/tests/test_conversation.py b/tests/test_conversation.py index 2731a2c7..b45d82c8 100644 --- a/tests/test_conversation.py +++ b/tests/test_conversation.py @@ -1,27 +1,43 @@ import datetime -import os -import threading -import time -import uuid -from unittest.mock import Mock - import pytest -from dotenv import load_dotenv -from getstream import Stream -from getstream.chat.client import ChatClient -from getstream.models import MessageRequest, ChannelResponse, ChannelInput from vision_agents.core.agents.conversation import ( Conversation, Message, InMemoryConversation, - # StreamConversation, # Removed from codebase - StreamHandle ) -# Skip entire module - StreamConversation class has been removed from codebase -# TODO: Update tests to use new conversation architecture -pytestmark = pytest.mark.skip(reason="StreamConversation class removed - tests need migration to new architecture") + +class TestMessage: + """Test suite for the Message dataclass.""" + + def test_message_initialization(self): + """Test that Message initializes correctly with default timestamp.""" + message = Message( + original={"role": "user", "content": "Hello"}, + content="Hello", + role="user", + user_id="test-user" + ) + + assert message.content == "Hello" + assert message.role == "user" + assert message.user_id == "test-user" + assert message.timestamp is not None + assert isinstance(message.timestamp, datetime.datetime) + assert message.id is not None # Auto-generated + + def test_message_custom_id(self): + """Test that Message accepts custom ID.""" + message = Message( + content="Hello", + role="user", + user_id="test-user", + id="custom-id-123" + ) + + assert message.id == "custom-id-123" + class TestConversation: """Test suite for the abstract Conversation class.""" @@ -43,27 +59,8 @@ class IncompleteConversation(Conversation): assert "Can't instantiate abstract class" in str(exc_info.value) -class TestMessage: - """Test suite for the Message dataclass.""" - - def test_message_initialization(self): - """Test that Message initializes correctly with default timestamp.""" - message = Message( - original={"role": "user", "content": "Hello"}, - content="Hello", - role="user", - user_id="test-user" - ) - - assert message.content == "Hello" - assert message.role == "user" - assert message.user_id == "test-user" - assert message.timestamp is not None - assert isinstance(message.timestamp, datetime.datetime) - - class TestInMemoryConversation: - """Test suite for InMemoryConversation class.""" + """Test suite for InMemoryConversation with new unified API.""" @pytest.fixture def conversation(self): @@ -73,9 +70,6 @@ def conversation(self): Message(original=None, content="Hello", role="user", user_id="user1"), Message(original=None, content="Hi there!", role="assistant", user_id="assistant") ] - # Set IDs for messages - for i, msg in enumerate(messages): - msg.id = f"msg-{i}" return InMemoryConversation(instructions, messages) def test_initialization(self, conversation): @@ -83,759 +77,199 @@ def test_initialization(self, conversation): assert conversation.instructions == "You are a helpful assistant." assert len(conversation.messages) == 2 - def test_add_message(self, conversation): - """Test adding a single message.""" - new_message = Message( - original=None, - content="New message", + @pytest.mark.asyncio + async def test_send_message(self, conversation): + """Test send_message creates a new message.""" + message = await conversation.send_message( role="user", - user_id="user2" + user_id="user2", + content="How are you?" ) - new_message.id = "new-msg" - conversation.add_message(new_message) assert len(conversation.messages) == 3 - assert conversation.messages[-1] == new_message - - def test_add_message_with_completed(self, conversation): - """Test adding a message with completed parameter.""" - # Test with completed=False - new_message1 = Message( - original=None, - content="Generating message", - role="user", - user_id="user2" + assert conversation.messages[-1].content == "How are you?" + assert conversation.messages[-1].role == "user" + assert conversation.messages[-1].user_id == "user2" + assert message.id is not None + + @pytest.mark.asyncio + async def test_send_message_with_custom_id(self, conversation): + """Test send_message with custom message ID.""" + message = await conversation.send_message( + role="assistant", + user_id="agent", + content="Response", + message_id="custom-msg-id" ) - new_message1.id = "gen-msg" - result = conversation.add_message(new_message1, completed=False) + assert message.id == "custom-msg-id" assert len(conversation.messages) == 3 - assert conversation.messages[-1] == new_message1 - assert result is None # InMemoryConversation returns None + + @pytest.mark.asyncio + async def test_send_message_with_original(self, conversation): + """Test send_message stores original event.""" + original_event = {"type": "stt", "confidence": 0.95} - # Test with completed=True (default) - new_message2 = Message( - original=None, - content="Complete message", + message = await conversation.send_message( role="user", - user_id="user3" - ) - new_message2.id = "comp-msg" - result = conversation.add_message(new_message2, completed=True) - - assert len(conversation.messages) == 4 - assert conversation.messages[-1] == new_message2 - assert result is None - - def test_update_message_existing(self, conversation): - """Test updating an existing message by appending content.""" - # Update existing message by appending (replace_content=False) - result = conversation.update_message( - message_id="msg-0", - input_text=" additional text", user_id="user1", - replace_content=False, - completed=False + content="Transcribed text", + original=original_event ) - # Verify message content was appended (with space handling) - assert conversation.messages[0].content == "Hello additional text" - assert result is None # InMemoryConversation returns None + assert message.original == original_event - def test_update_message_replace(self, conversation): - """Test replacing message content (replace_content=True).""" - result = conversation.update_message( - message_id="msg-0", - input_text="Replaced content", - user_id="user1", - replace_content=True, + @pytest.mark.asyncio + async def test_upsert_message_simple(self, conversation): + """Test upsert_message for simple non-streaming case.""" + message = await conversation.upsert_message( + role="user", + user_id="user3", + content="Question", completed=True ) - # Verify message content was replaced - assert conversation.messages[0].content == "Replaced content" - assert result is None + assert len(conversation.messages) == 3 + assert message.content == "Question" - def test_update_message_not_found(self, conversation): - """Test updating a non-existent message creates a new one.""" - initial_count = len(conversation.messages) + @pytest.mark.asyncio + async def test_upsert_message_streaming_deltas(self, conversation): + """Test upsert_message with streaming deltas.""" + msg_id = "stream-msg" - conversation.update_message( - message_id="non-existent-id", - input_text="New message content", - user_id="user2", - replace_content=True, + # Delta 1 + await conversation.upsert_message( + role="assistant", + user_id="agent", + content="Hello", + message_id=msg_id, + content_index=0, completed=False ) - # Should have added a new message - assert len(conversation.messages) == initial_count + 1 - - # Verify the new message was created correctly - new_msg = conversation.messages[-1] - assert new_msg.id == "non-existent-id" - assert new_msg.content == "New message content" - assert new_msg.user_id == "user2" - - def test_streaming_message_handle(self, conversation): - """Test streaming message with handle API.""" - # Start a streaming message - handle = conversation.start_streaming_message(role="assistant", initial_content="Hello") - - # Verify message was added assert len(conversation.messages) == 3 assert conversation.messages[-1].content == "Hello" - assert conversation.messages[-1].role == "assistant" - assert isinstance(handle, StreamHandle) - assert handle.user_id == "assistant" - - # Append to the message - conversation.append_to_message(handle, " world") - assert conversation.messages[-1].content == "Hello world" - - # Replace the message - conversation.replace_message(handle, "Goodbye") - assert conversation.messages[-1].content == "Goodbye" - - # Complete the message - conversation.complete_message(handle) - # In-memory conversation doesn't track completed state, just verify no error - - def test_multiple_streaming_handles(self, conversation): - """Test multiple concurrent streaming messages.""" - # Start two streaming messages - handle1 = conversation.start_streaming_message(role="user", user_id="user1", initial_content="Question: ") - handle2 = conversation.start_streaming_message(role="assistant", initial_content="Answer: ") - - assert len(conversation.messages) == 4 # 2 initial + 2 new - - # Update them independently - conversation.append_to_message(handle1, "What is 2+2?") - conversation.append_to_message(handle2, "Let me calculate...") - # Find messages by their handles to verify correct updates - msg1 = next(msg for msg in conversation.messages if msg.id == handle1.message_id) - msg2 = next(msg for msg in conversation.messages if msg.id == handle2.message_id) - - assert msg1.content == "Question: What is 2+2?" - assert msg2.content == "Answer: Let me calculate..." - - # Complete them - conversation.complete_message(handle1) - conversation.replace_message(handle2, "Answer: 4") - conversation.complete_message(handle2) - - assert msg2.content == "Answer: 4" # Replaced content, no space issue - - -class TestStreamConversation: - """Test suite for StreamConversation class.""" - - @pytest.fixture - def mock_chat_client(self): - """Create a mock ChatClient.""" - client = Mock(spec=ChatClient) - - # Mock send_message response - mock_response = Mock() - mock_response.data.message.id = "stream-message-123" - client.send_message.return_value = mock_response - - # Mock ephemeral_message_update - client.ephemeral_message_update = Mock(return_value=Mock()) - - # Mock update_message_partial - client.update_message_partial = Mock(return_value=Mock()) - - return client - - @pytest.fixture - def mock_channel(self): - """Create a mock ChannelResponse.""" - channel = Mock(spec=ChannelResponse) - channel.type = "messaging" - channel.id = "test-channel-123" - return channel - - @pytest.fixture - def stream_conversation(self, mock_chat_client, mock_channel): - """Create a StreamConversation instance with mocked dependencies.""" - instructions = "You are a helpful assistant." - messages = [ - Message( - original=None, - content="Hello", - role="user", - user_id="user1", - ) - ] - # Set IDs for messages - for i, msg in enumerate(messages): - msg.id = f"msg-{i}" - - conversation = StreamConversation( # noqa: F821 - instructions=instructions, - messages=messages, - channel=mock_channel, - chat_client=mock_chat_client + # Delta 2 + await conversation.upsert_message( + role="assistant", + user_id="agent", + content=" world", + message_id=msg_id, + content_index=1, + completed=False ) - # Pre-populate some stream IDs for testing - conversation.internal_ids_to_stream_ids = { - "msg-0": "stream-msg-0" - } - - yield conversation + assert len(conversation.messages) == 3 # Same message + assert conversation.messages[-1].content == "Hello world" - # Cleanup after each test - conversation.shutdown() - - def test_initialization(self, stream_conversation, mock_channel, mock_chat_client): - """Test StreamConversation initialization.""" - assert stream_conversation.channel == mock_channel - assert stream_conversation.chat_client == mock_chat_client - assert isinstance(stream_conversation.internal_ids_to_stream_ids, dict) - assert len(stream_conversation.messages) == 1 - - def test_add_message(self, stream_conversation, mock_chat_client): - """Test adding a message to the stream with default completed=True.""" - new_message = Message( - original=None, - content="Test message", - role="user", - user_id="user123" + # Complete + await conversation.upsert_message( + role="assistant", + user_id="agent", + content="Hello world!", + message_id=msg_id, + completed=True, + replace=True ) - new_message.id = "new-msg-id" - - stream_conversation.add_message(new_message) - - # Verify message was added locally immediately - assert len(stream_conversation.messages) == 2 - assert stream_conversation.messages[-1] == new_message - - # Wait for async operations to complete - assert stream_conversation.wait_for_pending_operations(timeout=2.0) - # Verify Stream API was called - mock_chat_client.send_message.assert_called_once() - call_args = mock_chat_client.send_message.call_args - assert call_args[0][0] == "messaging" # channel type - assert call_args[0][1] == "test-channel-123" # channel id - - request = call_args[0][2] - assert isinstance(request, MessageRequest) - assert request.text == "Test message" - assert request.user_id == "user123" - - # Verify ID mapping was stored - assert "new-msg-id" in stream_conversation.internal_ids_to_stream_ids - assert stream_conversation.internal_ids_to_stream_ids["new-msg-id"] == "stream-message-123" - - # Wait a bit more for the update operation to complete - time.sleep(0.1) - - # Verify update_message_partial was called (completed=True is default) - mock_chat_client.update_message_partial.assert_called_once() - update_args = mock_chat_client.update_message_partial.call_args - assert update_args[0][0] == "stream-message-123" - assert update_args[1]["user_id"] == "user123" - assert update_args[1]["set"]["text"] == "Test message" - assert update_args[1]["set"]["generating"] is False # completed=True means not generating + assert len(conversation.messages) == 3 # Still same message + assert conversation.messages[-1].content == "Hello world!" - def test_add_message_with_completed_false(self, stream_conversation, mock_chat_client): - """Test adding a message with completed=False (still generating).""" - # Ensure previous operations are complete - stream_conversation.wait_for_pending_operations(timeout=1.0) + @pytest.mark.asyncio + async def test_upsert_message_out_of_order_deltas(self, conversation): + """Test that out-of-order deltas are buffered correctly.""" + msg_id = "ooo-msg" - # Reset mocks - mock_chat_client.send_message.reset_mock() - mock_chat_client.ephemeral_message_update.reset_mock() - mock_chat_client.update_message_partial.reset_mock() - - new_message = Message( - original=None, - content="Generating message", + # Send delta index 1 first + await conversation.upsert_message( role="assistant", - user_id="assistant" - ) - new_message.id = "gen-msg-id" - - stream_conversation.add_message(new_message, completed=False) - - # Verify message was added locally - assert len(stream_conversation.messages) == 2 - assert stream_conversation.messages[-1] == new_message - - # Wait for async operations to complete - assert stream_conversation.wait_for_pending_operations(timeout=2.0) - - # Verify Stream API was called - mock_chat_client.send_message.assert_called_once() - - # Give a bit more time for the update operation to be queued and processed - time.sleep(0.2) - - # Verify ephemeral_message_update was called (completed=False) - mock_chat_client.ephemeral_message_update.assert_called_once() - mock_chat_client.update_message_partial.assert_not_called() - - update_args = mock_chat_client.ephemeral_message_update.call_args - assert update_args[0][0] == "stream-message-123" - assert update_args[1]["user_id"] == "assistant" - assert update_args[1]["set"]["text"] == "Generating message" - assert update_args[1]["set"]["generating"] is True # completed=False means still generating - - def test_update_message_existing(self, stream_conversation, mock_chat_client): - """Test updating an existing message by appending content.""" - # Update existing message by appending (replace_content=False, completed=False) - stream_conversation.update_message( - message_id="msg-0", - input_text=" additional text", - user_id="user1", - replace_content=False, + user_id="agent", + content=" world", + message_id=msg_id, + content_index=1, completed=False ) - # Verify message content was appended immediately - assert stream_conversation.messages[0].content == "Hello additional text" - - # Wait for async operations to complete - assert stream_conversation.wait_for_pending_operations(timeout=2.0) - - # Verify Stream API was called with ephemeral_message_update (not completed) - mock_chat_client.ephemeral_message_update.assert_called_once() - call_args = mock_chat_client.ephemeral_message_update.call_args - assert call_args[0][0] == "stream-msg-0" # stream message ID - assert call_args[1]["user_id"] == "user1" - assert call_args[1]["set"]["text"] == "Hello additional text" - assert call_args[1]["set"]["generating"] is True # not completed = still generating - - def test_update_message_replace(self, stream_conversation, mock_chat_client): - """Test replacing message content (replace_content=True).""" - # Mock update_message_partial for completed messages - mock_chat_client.update_message_partial = Mock(return_value=Mock()) - - stream_conversation.update_message( - message_id="msg-0", - input_text="Replaced content", - user_id="user1", - replace_content=True, - completed=True - ) - - # Verify message content was replaced - assert stream_conversation.messages[0].content == "Replaced content" - - # Wait for async operations to complete - assert stream_conversation.wait_for_pending_operations(timeout=2.0) + # Content should be empty (waiting for index 0) + assert conversation.messages[-1].content == "" - # Verify Stream API was called with update_message_partial (completed) - mock_chat_client.update_message_partial.assert_called_once() - call_args = mock_chat_client.update_message_partial.call_args - assert call_args[0][0] == "stream-msg-0" # stream message ID - assert call_args[1]["user_id"] == "user1" - assert call_args[1]["set"]["text"] == "Replaced content" - assert call_args[1]["set"]["generating"] is False # completed = not generating - - def test_update_message_not_found(self, stream_conversation, mock_chat_client): - """Test updating a non-existent message creates a new one.""" - # Reset the send_message mock for this test - mock_chat_client.send_message.reset_mock() - - stream_conversation.update_message( - message_id="non-existent-id", - input_text="New message content", - user_id="user2", - replace_content=True, + # Send delta index 0 + await conversation.upsert_message( + role="assistant", + user_id="agent", + content="Hello", + message_id=msg_id, + content_index=0, completed=False ) - # Should have added a new message - assert len(stream_conversation.messages) == 2 - - # Verify the new message was created correctly - new_msg = stream_conversation.messages[-1] - assert new_msg.id == "non-existent-id" - assert new_msg.content == "New message content" - assert new_msg.user_id == "user2" - - # Wait for async operations to complete - assert stream_conversation.wait_for_pending_operations(timeout=2.0) - time.sleep(0.2) # Give extra time for update operation - - # Verify send_message was called (not update) - mock_chat_client.send_message.assert_called_once() + # Now both should be applied + assert conversation.messages[-1].content == "Hello world" - def test_update_message_completed_vs_generating(self, stream_conversation, mock_chat_client): - """Test that completed=True calls update_message_partial and completed=False calls ephemeral_message_update.""" - # Mock update_message_partial for completed messages - mock_chat_client.update_message_partial = Mock(return_value=Mock()) + @pytest.mark.asyncio + async def test_upsert_message_replace_vs_append(self, conversation): + """Test replace vs append behavior.""" + msg_id = "replace-test" - # Test with completed=False (still generating) - stream_conversation.update_message( - message_id="msg-0", - input_text=" in progress", - user_id="user1", - replace_content=False, + # Create initial message + await conversation.upsert_message( + role="assistant", + user_id="agent", + content="Initial", + message_id=msg_id, completed=False ) - # Wait for async operations - assert stream_conversation.wait_for_pending_operations(timeout=2.0) - - # Should call ephemeral_message_update - mock_chat_client.ephemeral_message_update.assert_called() - mock_chat_client.update_message_partial.assert_not_called() - - # Reset mocks - mock_chat_client.ephemeral_message_update.reset_mock() - - # Test with completed=True - stream_conversation.update_message( - message_id="msg-0", - input_text="Final content", - user_id="user1", - replace_content=True, - completed=True + # Append + await conversation.upsert_message( + role="assistant", + user_id="agent", + content=" appended", + message_id=msg_id, + completed=False, + replace=False ) - # Wait for async operations - assert stream_conversation.wait_for_pending_operations(timeout=2.0) + assert conversation.messages[-1].content == "Initial appended" - # Should call update_message_partial - mock_chat_client.update_message_partial.assert_called_once() - mock_chat_client.ephemeral_message_update.assert_not_called() - - def test_update_message_no_stream_id(self, stream_conversation, mock_chat_client): - """Test updating a message without a stream ID mapping.""" - # Add a message without stream ID mapping - new_msg = Message( - original=None, - content="Test", - role="user", - user_id="user3" - ) - new_msg.id = "unmapped-msg" - stream_conversation.messages.append(new_msg) - - # Try to update it by appending - stream_conversation.update_message( - message_id="unmapped-msg", - input_text=" updated", - user_id="user3", - replace_content=False, - completed=False + # Replace + await conversation.upsert_message( + role="assistant", + user_id="agent", + content="Replaced", + message_id=msg_id, + completed=True, + replace=True ) - # Message should still be updated locally (with space handling) - assert stream_conversation.messages[-1].content == "Test updated" - - # Since there's no stream_id mapping, the API call should be skipped - # This is the expected behavior - we don't sync messages without stream IDs - mock_chat_client.ephemeral_message_update.assert_not_called() - - def test_streaming_message_handle(self, stream_conversation, mock_chat_client): - """Test streaming message with handle API.""" - # Reset mocks - mock_chat_client.send_message.reset_mock() - mock_chat_client.ephemeral_message_update.reset_mock() - mock_chat_client.update_message_partial.reset_mock() - - # Start a streaming message - handle = stream_conversation.start_streaming_message(role="assistant", initial_content="Processing") - - # Verify message was added and marked as generating - assert len(stream_conversation.messages) == 2 - assert stream_conversation.messages[-1].content == "Processing" - assert stream_conversation.messages[-1].role == "assistant" - assert isinstance(handle, StreamHandle) - assert handle.user_id == "assistant" - - # Wait for async operations - assert stream_conversation.wait_for_pending_operations(timeout=2.0) - time.sleep(0.2) # Give extra time for update operation - - # Verify send_message was called - mock_chat_client.send_message.assert_called_once() - # Verify ephemeral_message_update was called (completed=False by default) - mock_chat_client.ephemeral_message_update.assert_called_once() - - # Reset for next operations - mock_chat_client.ephemeral_message_update.reset_mock() - - # Append to the message - stream_conversation.append_to_message(handle, "...") - assert stream_conversation.messages[-1].content == "Processing..." - - # Wait for append operation to complete - assert stream_conversation.wait_for_pending_operations(timeout=2.0) - - mock_chat_client.ephemeral_message_update.assert_called_once() - - # Replace the message - stream_conversation.replace_message(handle, "Complete response") - assert stream_conversation.messages[-1].content == "Complete response" - - # Wait for replace operation to complete - assert stream_conversation.wait_for_pending_operations(timeout=2.0) - - assert mock_chat_client.ephemeral_message_update.call_count == 2 - - # Complete the message - mock_chat_client.update_message_partial.reset_mock() - stream_conversation.complete_message(handle) - - # Wait for complete operation - assert stream_conversation.wait_for_pending_operations(timeout=2.0) - - mock_chat_client.update_message_partial.assert_called_once() - - def test_multiple_streaming_handles(self, stream_conversation, mock_chat_client): - """Test multiple concurrent streaming messages with Stream API.""" - # Reset mocks - mock_chat_client.send_message.reset_mock() - mock_chat_client.ephemeral_message_update.reset_mock() - - # Mock different message IDs for each send - mock_response1 = Mock() - mock_response1.data.message.id = "stream-msg-1" - mock_response2 = Mock() - mock_response2.data.message.id = "stream-msg-2" - mock_chat_client.send_message.side_effect = [mock_response1, mock_response2] - - # Start two streaming messages with empty initial content - handle1 = stream_conversation.start_streaming_message(role="user", user_id="user123", initial_content="") - handle2 = stream_conversation.start_streaming_message(role="assistant", initial_content="") - - assert len(stream_conversation.messages) == 3 # 1 initial + 2 new - - # Wait for initial operations to complete - assert stream_conversation.wait_for_pending_operations(timeout=2.0) - time.sleep(0.3) # Give extra time for update operations - - # Update them independently - stream_conversation.append_to_message(handle1, "Hello?") - stream_conversation.append_to_message(handle2, "Hi there!") - - # Wait for append operations to complete - assert stream_conversation.wait_for_pending_operations(timeout=2.0) - - # Find messages by their handles to verify correct updates - msg1 = next(msg for msg in stream_conversation.messages if msg.id == handle1.message_id) - msg2 = next(msg for msg in stream_conversation.messages if msg.id == handle2.message_id) - - assert msg1.content == "Hello?" - assert msg2.content == "Hi there!" - - # Verify ephemeral updates were called for both - assert mock_chat_client.ephemeral_message_update.call_count >= 4 # 2 initial + 2 appends - - # Complete both - stream_conversation.complete_message(handle1) - stream_conversation.complete_message(handle2) - - # Wait for completion operations - assert stream_conversation.wait_for_pending_operations(timeout=2.0) - - # Verify update_message_partial was called for both completions - assert mock_chat_client.update_message_partial.call_count == 2 - - def test_worker_thread_async_operations(self, stream_conversation, mock_chat_client): - """Test that operations are processed asynchronously by the worker thread.""" - # Reset mocks - mock_chat_client.send_message.reset_mock() - mock_chat_client.ephemeral_message_update.reset_mock() - - # Add multiple messages quickly - messages = [] - for i in range(5): - msg = Message( - original=None, - content=f"Message {i}", - role="user", - user_id=f"user{i}" - ) - messages.append(msg) - stream_conversation.add_message(msg, completed=False) - - # Verify messages were added locally immediately - assert len(stream_conversation.messages) == 6 # 1 initial + 5 new - - # Wait for all operations to complete - assert stream_conversation.wait_for_pending_operations(timeout=3.0) - - # Give a bit more time for update operations - time.sleep(0.5) - - # Verify all send_message calls were made - assert mock_chat_client.send_message.call_count == 5 - - # Verify all ephemeral_message_update calls were made - assert mock_chat_client.ephemeral_message_update.call_count >= 5 + assert conversation.messages[-1].content == "Replaced" - def test_wait_for_pending_operations_timeout(self, stream_conversation, mock_chat_client): - """Test that wait_for_pending_operations returns False on timeout.""" - # Make send_message block for a long time - block_event = threading.Event() - - def slow_send_message(*args, **kwargs): - block_event.wait(timeout=5.0) # Block for 5 seconds - mock_response = Mock() - mock_response.data.message.id = "stream-message-slow" - return mock_response - - mock_chat_client.send_message.side_effect = slow_send_message - - # Add a message - msg = Message(original=None, content="Slow message", role="user", user_id="user1") - stream_conversation.add_message(msg) + @pytest.mark.asyncio + async def test_late_deltas_ignored_after_completion(self, conversation): + """Test that deltas arriving after completion are ignored.""" + msg_id = "late-delta-msg" - # Wait should timeout - assert not stream_conversation.wait_for_pending_operations(timeout=0.5) - - # Unblock the operation - block_event.set() - - # Now wait should succeed - assert stream_conversation.wait_for_pending_operations(timeout=2.0) - - def test_shutdown_worker_thread(self, mock_chat_client, mock_channel): - """Test that shutdown properly stops the worker thread.""" - # Create a fresh conversation without using the fixture to avoid double shutdown - conversation = StreamConversation( # noqa: F821 - instructions="Test", - messages=[], - channel=mock_channel, - chat_client=mock_chat_client + # Complete message first + await conversation.upsert_message( + role="assistant", + user_id="agent", + content="Final text", + message_id=msg_id, + completed=True ) - # Verify thread is alive - assert conversation._worker_thread.is_alive() + initial_content = conversation.messages[-1].content - # Shutdown - conversation.shutdown() - - # Verify thread stopped - assert not conversation._worker_thread.is_alive() + # Late delta arrives (should be ignored) + await conversation.upsert_message( + role="assistant", + user_id="agent", + content="Late", + message_id=msg_id, + content_index=0, + completed=False + ) - # Verify shutdown flag is set - assert conversation._shutdown is True - - -@pytest.fixture -def mock_stream_client(): - """Create a mock Stream client for testing.""" - from getstream import Stream - - client = Mock(spec=Stream) - - # Mock user creation - mock_user = Mock() - mock_user.id = "test-agent-user" - mock_user.name = "Test Agent" - client.create_user.return_value = mock_user - - # Mock video.call - mock_call = Mock() - mock_call.id = "test-call-123" - client.video.call.return_value = mock_call - - return client - - -@pytest.mark.integration -def test_stream_conversation_integration(): - """Integration test with real Stream client (requires credentials).""" - - load_dotenv() - - if not os.getenv("STREAM_API_KEY"): - pytest.skip("Stream credentials not available") - - # Create real client - client = Stream.from_env() - - # Create a test channel and user - user = client.create_user(id="test-user") - channel = client.chat.get_or_create_channel("messaging", str(uuid.uuid4()), data=ChannelInput(created_by_id=user.id)).data.channel - - # Create conversation - conversation = StreamConversation( # noqa: F821 - instructions="Test assistant", - messages=[], - channel=channel, - chat_client=client.chat - ) - - # Add a message - message = Message( - original=None, - content="Hello from test", - role="user", - user_id=user.id - ) - conversation.add_message(message) - - # Wait for async operations to complete - assert conversation.wait_for_pending_operations(timeout=5.0) - - # Verify message was sent - assert len(conversation.messages) == 1 - assert message.id in conversation.internal_ids_to_stream_ids - - # update message with replace - conversation.update_message(message_id=message.id, input_text="Replaced content", user_id=user.id, replace_content=True, completed=True) - assert conversation.wait_for_pending_operations(timeout=5.0) - - channel_data = client.chat.get_or_create_channel("messaging", channel.id, state=True).data - assert len(channel_data.messages) == 1 - assert channel_data.messages[0].text == "Replaced content" - # Note: generating flag might not be in custom field depending on Stream API version - - # update message with delta - conversation.update_message(message_id=message.id, input_text=" more stuff", user_id=user.id, - replace_content=False, completed=True) - assert conversation.wait_for_pending_operations(timeout=5.0) - - channel_data = client.chat.get_or_create_channel("messaging", channel.id, state=True).data - assert len(channel_data.messages) == 1 - assert channel_data.messages[0].text == "Replaced content more stuff" - # Note: generating flag might not be in custom field depending on Stream API version - - # Test add_message with completed=False - message2 = Message( - original=None, - content="Still generating...", - role="assistant", - user_id="assistant" - ) - conversation.add_message(message2, completed=False) - assert conversation.wait_for_pending_operations(timeout=5.0) - time.sleep(0.2) # Give extra time for update operation - - channel_data = client.chat.get_or_create_channel("messaging", channel.id, state=True).data - assert len(channel_data.messages) == 2 - assert channel_data.messages[1].text == "Still generating..." - # Note: generating flag might not be in custom field depending on Stream API version - - # Test streaming handle API - handle = conversation.start_streaming_message(role="assistant", initial_content="Thinking") - assert conversation.wait_for_pending_operations(timeout=5.0) - time.sleep(0.2) # Give extra time for update operation - - conversation.append_to_message(handle, "...") - assert conversation.wait_for_pending_operations(timeout=5.0) - - conversation.replace_message(handle, "The answer is 42") - assert conversation.wait_for_pending_operations(timeout=5.0) - - conversation.complete_message(handle) - assert conversation.wait_for_pending_operations(timeout=5.0) - - channel_data = client.chat.get_or_create_channel("messaging", channel.id, state=True).data - assert len(channel_data.messages) == 3 - assert channel_data.messages[2].text == "The answer is 42" - # Note: generating flag might not be in custom field depending on Stream API version - - # Cleanup - conversation.shutdown() + # Content should be unchanged + assert conversation.messages[-1].content == initial_content diff --git a/tests/test_realtime_base.py b/tests/test_realtime_base.py index 990c8958..b2dd0fa4 100644 --- a/tests/test_realtime_base.py +++ b/tests/test_realtime_base.py @@ -17,6 +17,7 @@ import pytest from vision_agents.core.agents import Agent +from vision_agents.core.edge.types import Participant from vision_agents.core.llm import realtime as base_rt from vision_agents.core.llm.llm import LLMResponseEvent from vision_agents.core.llm.events import RealtimeDisconnectedEvent @@ -33,7 +34,7 @@ def partial_update_message(self, text: str, participant: Any = None) -> None: def finish_last_message(self, text: str) -> None: self.finish_calls.append(text) - + def add_message(self, message: dict) -> None: """Add a message to the conversation.""" self.messages.append(message) @@ -53,53 +54,50 @@ def __init__(self) -> None: async def connect(self): return None - async def send_text(self, text: str): - # Emit transcript for user - self._emit_transcript_event(text=text) - # Emit a delta and a final response - self._emit_response_event(text="Hello", is_complete=False) - self._emit_response_event(text="Hello world", is_complete=True) - - async def simple_audio_response(self, pcm): + async def simple_audio_response( + self, pcm, participant: Optional[Participant] = None + ): """Required abstract method implementation.""" return None async def _close_impl(self): return None - + # Additional methods from LLM base class def set_before_response_listener(self, callback): """Set before response callback.""" self.before_response_listener = callback - + def set_after_response_listener(self, callback): """Set after response callback.""" self.after_response_listener = callback - + async def wait_until_ready(self, timeout: float = 5.0) -> bool: """Wait until ready (already ready in fake).""" return True - + async def interrupt_playback(self): """Interrupt playback (no-op for fake).""" pass - + def resume_playback(self): """Resume playback (no-op for fake).""" pass + @pytest.mark.skip(reason="Conversation class has not fully been wired into Agent yet") @pytest.mark.asyncio async def test_agent_conversation_updates_with_realtime(): """Test that Agent wires Realtime events to conversation updates.""" from vision_agents.core.edge import EdgeTransport from vision_agents.core.edge.types import User, Connection - + # =================================================================== # Mock Connection - mimics the structure Agent expects # =================================================================== class MockConnection(Connection): """Mock connection with minimal structure for testing.""" + def __init__(self): super().__init__() # Agent.join() accesses connection._connection._coordinator_ws_client.on_wildcard() @@ -108,86 +106,81 @@ def __init__(self): on_wildcard=lambda *args, **kwargs: None ) ) - + async def close(self): pass - + # =================================================================== # Mock EdgeTransport - provides conversation and connection # =================================================================== class MockEdge(EdgeTransport): """Mock edge transport for testing Agent integration.""" + def __init__(self): super().__init__() self.conversation = None # EdgeTransport doesn't initialize events, but Agent expects it from vision_agents.core.events.manager import EventManager + self.events = EventManager() - + async def create_user(self, user: User): return user - + def create_audio_track(self): return None - + def close(self): pass - + def open_demo(self, *args, **kwargs): pass - + async def join(self, agent, call): """Return a mock connection.""" return MockConnection() - + async def publish_tracks(self, audio_track, video_track): pass - + async def create_conversation(self, call, user, instructions): """Return our fake conversation for testing.""" return self.conversation - + def add_track_subscriber(self, track_id): return None - + # =================================================================== # Fake Conversation - tracks partial and final updates # =================================================================== fake_conv = FakeConversation() - + # =================================================================== # Create Agent with new API # =================================================================== rt = FakeRealtime() mock_edge = MockEdge() mock_edge.conversation = fake_conv # Set before join - + agent_user = User(id="agent-123", name="Test Agent") - + agent = Agent( - edge=mock_edge, - llm=rt, - agent_user=agent_user, - instructions="Test instructions" + edge=mock_edge, llm=rt, agent_user=agent_user, instructions="Test instructions" ) - + # =================================================================== # Mock Call object # =================================================================== call = SimpleNamespace( id="test-call-123", - client=SimpleNamespace( - stream=SimpleNamespace( - chat=SimpleNamespace() - ) - ) + client=SimpleNamespace(stream=SimpleNamespace(chat=SimpleNamespace())), ) - + # =================================================================== # Join call (registers event handlers) # =================================================================== await agent.join(call) - + # =================================================================== # Trigger events through FakeRealtime # =================================================================== @@ -196,22 +189,24 @@ def add_track_subscriber(self, track_id): # 2. RealtimeResponseEvent (partial: "Hello") # 3. RealtimeResponseEvent (complete: "Hello world") await rt.send_text("Hi") - + # Allow async event handlers to run await asyncio.sleep(0.05) - + # Wait for event processing await agent.events.wait(timeout=1.0) - + # =================================================================== # Assertions - verify conversation received updates # =================================================================== - assert ("Hello", None) in fake_conv.partial_calls, \ + assert ("Hello", None) in fake_conv.partial_calls, ( f"Expected partial update 'Hello', got: {fake_conv.partial_calls}" - - assert "Hello world" in fake_conv.finish_calls, \ + ) + + assert "Hello world" in fake_conv.finish_calls, ( f"Expected finish call 'Hello world', got: {fake_conv.finish_calls}" - + ) + # Cleanup await agent.close() @@ -238,17 +233,17 @@ async def send_text(self, text: str): async def simple_response(self, text: str, processors=None, participant=None): """Aggregates streaming responses.""" # Call before listener if set - if hasattr(self, 'before_response_listener') and self.before_response_listener: + if hasattr(self, "before_response_listener") and self.before_response_listener: self.before_response_listener([{"role": "user", "content": text}]) - + await self.send_text(text) # Aggregate all response events ("Hi " + "there" + "!") result = LLMResponseEvent(original=None, text="Hi there!") - + # Call after listener if set - if hasattr(self, 'after_response_listener') and self.after_response_listener: + if hasattr(self, "after_response_listener") and self.after_response_listener: await self.after_response_listener(result) - + return result async def simple_audio_response(self, pcm): @@ -257,12 +252,12 @@ async def simple_audio_response(self, pcm): async def _close_impl(self): return None - + # Additional methods from LLM base class def set_before_response_listener(self, callback): """Set before response callback.""" self.before_response_listener = callback - + def set_after_response_listener(self, callback): """Set after response callback.""" self.after_response_listener = callback @@ -312,10 +307,10 @@ async def _on_disc(event: RealtimeDisconnectedEvent): await asyncio.sleep(0.01) await rt.close() - + # Wait for all events in queue to be processed await rt.events.wait(timeout=1.0) - + assert observed["disconnected"] is True @@ -358,7 +353,7 @@ async def simple_audio_response(self, pcm): async def _close_impl(self): return None - + # Additional method for native_response test async def native_response(self, **kwargs): """Native response aggregates streaming responses.""" diff --git a/uv.lock b/uv.lock index 803c13f5..59b20cb8 100644 --- a/uv.lock +++ b/uv.lock @@ -70,7 +70,7 @@ wheels = [ [[package]] name = "aiohttp" -version = "3.13.0" +version = "3.13.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiohappyeyeballs" }, @@ -82,110 +82,110 @@ dependencies = [ { name = "propcache" }, { name = "yarl" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/62/f1/8515650ac3121a9e55c7b217c60e7fae3e0134b5acfe65691781b5356929/aiohttp-3.13.0.tar.gz", hash = "sha256:378dbc57dd8cf341ce243f13fa1fa5394d68e2e02c15cd5f28eae35a70ec7f67", size = 7832348 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/25/18/a3a9c9b7c8d400f71d1ff93c3e1520a5d53dba170f829ca9c6b2b070677b/aiohttp-3.13.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ca69ec38adf5cadcc21d0b25e2144f6a25b7db7bea7e730bac25075bc305eff0", size = 734428 }, - { url = "https://files.pythonhosted.org/packages/aa/02/f1eac06d78997e015030130ccf1c7cf864a919f97d77ff27e89c82fc3186/aiohttp-3.13.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:240f99f88a9a6beb53ebadac79a2e3417247aa756202ed234b1dbae13d248092", size = 491939 }, - { url = "https://files.pythonhosted.org/packages/e1/db/5d65af7cbe5f302e23b1ea5cfc156cd0c7738a0d2db531a3837d2754de94/aiohttp-3.13.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a4676b978a9711531e7cea499d4cdc0794c617a1c0579310ab46c9fdf5877702", size = 487229 }, - { url = "https://files.pythonhosted.org/packages/d3/d5/56c622ad3bd57ff4adc2b701f298dcc0408735a8af998cec1c66a9ce224e/aiohttp-3.13.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:48fcdd5bc771cbbab8ccc9588b8b6447f6a30f9fe00898b1a5107098e00d6793", size = 1666118 }, - { url = "https://files.pythonhosted.org/packages/44/16/db236671ec3758e3a6be6977009e74016470368012a58fea4b3799546549/aiohttp-3.13.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:eeea0cdd2f687e210c8f605f322d7b0300ba55145014a5dbe98bd4be6fff1f6c", size = 1633983 }, - { url = "https://files.pythonhosted.org/packages/19/ad/d96d7d7023e7f5215b8737cad21a7637f6d9d10fbfbfef0435d0277f71a2/aiohttp-3.13.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:10b3f01d5aeb632adaaf39c5e93f040a550464a768d54c514050c635adcbb9d0", size = 1725922 }, - { url = "https://files.pythonhosted.org/packages/88/d7/e8a5ba2bbd929ed587b2a8ea9390765daede2d8cd28dfae3a0773c6d3fbc/aiohttp-3.13.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a4dc0b83e25267f42ef065ea57653de4365b56d7bc4e4cfc94fabe56998f8ee6", size = 1813770 }, - { url = "https://files.pythonhosted.org/packages/f9/ca/135c21e85ffeff66b80ecd8a647ca104f2e5a91c37dc86649244ddbf87ab/aiohttp-3.13.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:72714919ed9b90f030f761c20670e529c4af96c31bd000917dd0c9afd1afb731", size = 1667322 }, - { url = "https://files.pythonhosted.org/packages/f6/38/348c4343052a400968dbf2051ee3dc222bdefd95af5874cf0f04cc7a8c92/aiohttp-3.13.0-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:564be41e85318403fdb176e9e5b3e852d528392f42f2c1d1efcbeeed481126d7", size = 1553270 }, - { url = "https://files.pythonhosted.org/packages/47/89/71cbda30f0900ab16084769960c467a355d6b1db51668fbb821c4a4ad5ed/aiohttp-3.13.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:84912962071087286333f70569362e10793f73f45c48854e6859df11001eb2d3", size = 1637087 }, - { url = "https://files.pythonhosted.org/packages/bf/b1/5ff5fcaecccdcd5be7ff717cbde6e630760a8130e89167c3aa05b6b57707/aiohttp-3.13.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:90b570f1a146181c3d6ae8f755de66227ded49d30d050479b5ae07710f7894c5", size = 1643443 }, - { url = "https://files.pythonhosted.org/packages/87/e2/1d1f202f43c8be1956f05196159064cc05dc6842a33c1397cbb1b99610af/aiohttp-3.13.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:2d71ca30257ce756e37a6078b1dff2d9475fee13609ad831eac9a6531bea903b", size = 1695571 }, - { url = "https://files.pythonhosted.org/packages/a4/b9/53c1df2991686f947a9651265757ea12c4afc29b351a249b73a0fc81dd3c/aiohttp-3.13.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:cd45eb70eca63f41bb156b7dffbe1a7760153b69892d923bdb79a74099e2ed90", size = 1539975 }, - { url = "https://files.pythonhosted.org/packages/93/24/345166f9c4cd2f5cc1d2173131998ee4adab0db8729126db32a7f91ed400/aiohttp-3.13.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:5ae3a19949a27982c7425a7a5a963c1268fdbabf0be15ab59448cbcf0f992519", size = 1712866 }, - { url = "https://files.pythonhosted.org/packages/09/f1/e8f70462848b74d49b3115050623ecbd697889713c2c93c96616da56b2de/aiohttp-3.13.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ea6df292013c9f050cbf3f93eee9953d6e5acd9e64a0bf4ca16404bfd7aa9bcc", size = 1654058 }, - { url = "https://files.pythonhosted.org/packages/23/ba/47fd065510a8bfab5d5f6e1d97c0de672447c0a941c5021298bd7210afc3/aiohttp-3.13.0-cp310-cp310-win32.whl", hash = "sha256:3b64f22fbb6dcd5663de5ef2d847a5638646ef99112503e6f7704bdecb0d1c4d", size = 430230 }, - { url = "https://files.pythonhosted.org/packages/c4/38/f5385cb79afa1f31bcaa3625a9e8d849b782edaeac09f894f46439e006a1/aiohttp-3.13.0-cp310-cp310-win_amd64.whl", hash = "sha256:f8d877aa60d80715b2afc565f0f1aea66565824c229a2d065b31670e09fed6d7", size = 453013 }, - { url = "https://files.pythonhosted.org/packages/b1/db/df80cacac46cd548a736c5535b13cc18925cf6f9f83cd128cf3839842219/aiohttp-3.13.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:99eb94e97a42367fef5fc11e28cb2362809d3e70837f6e60557816c7106e2e20", size = 741374 }, - { url = "https://files.pythonhosted.org/packages/ae/f9/2d6d93fd57ab4726e18a7cdab083772eda8302d682620fbf2aef48322351/aiohttp-3.13.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4696665b2713021c6eba3e2b882a86013763b442577fe5d2056a42111e732eca", size = 494956 }, - { url = "https://files.pythonhosted.org/packages/89/a6/e1c061b079fed04ffd6777950c82f2e8246fd08b7b3c4f56fdd47f697e5a/aiohttp-3.13.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3e6a38366f7f0d0f6ed7a1198055150c52fda552b107dad4785c0852ad7685d1", size = 491154 }, - { url = "https://files.pythonhosted.org/packages/fe/4d/ee8913c0d2c7da37fdc98673a342b51611eaa0871682b37b8430084e35b5/aiohttp-3.13.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aab715b1a0c37f7f11f9f1f579c6fbaa51ef569e47e3c0a4644fba46077a9409", size = 1745707 }, - { url = "https://files.pythonhosted.org/packages/f9/70/26b2c97e8fa68644aec43d788940984c5f3b53a8d1468d5baaa328f809c9/aiohttp-3.13.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7972c82bed87d7bd8e374b60a6b6e816d75ba4f7c2627c2d14eed216e62738e1", size = 1702404 }, - { url = "https://files.pythonhosted.org/packages/65/1e/c8aa3c293a0e8b18968b1b88e9bd8fb269eb67eb7449f504a4c3e175b159/aiohttp-3.13.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ca8313cb852af788c78d5afdea24c40172cbfff8b35e58b407467732fde20390", size = 1805519 }, - { url = "https://files.pythonhosted.org/packages/51/b6/a3753fe86249eb441768658cfc00f8c4e0913b255c13be00ddb8192775e1/aiohttp-3.13.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6c333a2385d2a6298265f4b3e960590f787311b87f6b5e6e21bb8375914ef504", size = 1893904 }, - { url = "https://files.pythonhosted.org/packages/51/6d/7b1e020fe1d2a2be7cf0ce5e35922f345e3507cf337faa1a6563c42065c1/aiohttp-3.13.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cc6d5fc5edbfb8041d9607f6a417997fa4d02de78284d386bea7ab767b5ea4f3", size = 1745043 }, - { url = "https://files.pythonhosted.org/packages/e6/df/aad5dce268f9d4f29759c3eeb5fb5995c569d76abb267468dc1075218d5b/aiohttp-3.13.0-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7ddedba3d0043349edc79df3dc2da49c72b06d59a45a42c1c8d987e6b8d175b8", size = 1604765 }, - { url = "https://files.pythonhosted.org/packages/1c/19/a84a0e97b2da2224c8b85e1aef5cac834d07b2903c17bff1a6bdbc7041d2/aiohttp-3.13.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:23ca762140159417a6bbc959ca1927f6949711851e56f2181ddfe8d63512b5ad", size = 1721737 }, - { url = "https://files.pythonhosted.org/packages/6c/61/ca6ad390128d964a08554fd63d6df5810fb5fbc7e599cb9e617f1729ae19/aiohttp-3.13.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:bfe824d6707a5dc3c5676685f624bc0c63c40d79dc0239a7fd6c034b98c25ebe", size = 1716052 }, - { url = "https://files.pythonhosted.org/packages/2a/71/769e249e6625372c7d14be79b8b8c3b0592963a09793fb3d36758e60952c/aiohttp-3.13.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:3c11fa5dd2ef773a8a5a6daa40243d83b450915992eab021789498dc87acc114", size = 1783532 }, - { url = "https://files.pythonhosted.org/packages/66/64/b9cd03cdbb629bc492e4a744fbe96550a8340b0cd7a0cc4a9c90cfecd8d3/aiohttp-3.13.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:00fdfe370cffede3163ba9d3f190b32c0cfc8c774f6f67395683d7b0e48cdb8a", size = 1593072 }, - { url = "https://files.pythonhosted.org/packages/24/0e/87922c8cfdbd09f5e2197e9d87714a98c99c423560d44739e3af55400fe3/aiohttp-3.13.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:6475e42ef92717a678bfbf50885a682bb360a6f9c8819fb1a388d98198fdcb80", size = 1798613 }, - { url = "https://files.pythonhosted.org/packages/c5/bb/a3adfe2af76e1ee9e3b5464522004b148b266bc99d7ec424ca7843d64a3c/aiohttp-3.13.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:77da5305a410910218b99f2a963092f4277d8a9c1f429c1ff1b026d1826bd0b6", size = 1737480 }, - { url = "https://files.pythonhosted.org/packages/ad/53/e124dcbd64e6365602f3493fe37a11ca5b7ac0a40822a6e2bc8260cd08e0/aiohttp-3.13.0-cp311-cp311-win32.whl", hash = "sha256:2f9d9ea547618d907f2ee6670c9a951f059c5994e4b6de8dcf7d9747b420c820", size = 429824 }, - { url = "https://files.pythonhosted.org/packages/3e/bd/485d98b372a2cd6998484a93ddd401ec6b6031657661c36846a10e2a1f6e/aiohttp-3.13.0-cp311-cp311-win_amd64.whl", hash = "sha256:0f19f7798996d4458c669bd770504f710014926e9970f4729cf55853ae200469", size = 454137 }, - { url = "https://files.pythonhosted.org/packages/3a/95/7e8bdfa6e79099a086d59d42589492f1fe9d29aae3cefb58b676015ce278/aiohttp-3.13.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1c272a9a18a5ecc48a7101882230046b83023bb2a662050ecb9bfcb28d9ab53a", size = 735585 }, - { url = "https://files.pythonhosted.org/packages/9f/20/2f1d3ee06ee94eafe516810705219bff234d09f135d6951661661d5595ae/aiohttp-3.13.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:97891a23d7fd4e1afe9c2f4473e04595e4acb18e4733b910b6577b74e7e21985", size = 490613 }, - { url = "https://files.pythonhosted.org/packages/74/15/ab8600ef6dc1dcd599009a81acfed2ea407037e654d32e47e344e0b08c34/aiohttp-3.13.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:475bd56492ce5f4cffe32b5533c6533ee0c406d1d0e6924879f83adcf51da0ae", size = 489750 }, - { url = "https://files.pythonhosted.org/packages/33/59/752640c2b86ca987fe5703a01733b00d375e6cd2392bc7574489934e64e5/aiohttp-3.13.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c32ada0abb4bc94c30be2b681c42f058ab104d048da6f0148280a51ce98add8c", size = 1736812 }, - { url = "https://files.pythonhosted.org/packages/3d/c6/dd6b86ddb852a7fdbcdc7a45b6bdc80178aef713c08279afcaee7a5a9f07/aiohttp-3.13.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:4af1f8877ca46ecdd0bc0d4a6b66d4b2bddc84a79e2e8366bc0d5308e76bceb8", size = 1698535 }, - { url = "https://files.pythonhosted.org/packages/33/e2/27c92d205b9e8cee7661670e8e9f187931b71e26d42796b153d2a0ba6949/aiohttp-3.13.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e04ab827ec4f775817736b20cdc8350f40327f9b598dec4e18c9ffdcbea88a93", size = 1766573 }, - { url = "https://files.pythonhosted.org/packages/df/6a/1fc1ad71d130a30f7a207d8d958a41224c29b834463b5185efb2dbff6ad4/aiohttp-3.13.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a6d9487b9471ec36b0faedf52228cd732e89be0a2bbd649af890b5e2ce422353", size = 1865229 }, - { url = "https://files.pythonhosted.org/packages/14/51/d0c1701a79fcb0109cff5304da16226581569b89a282d8e7f1549a7e3ec0/aiohttp-3.13.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2e66c57416352f36bf98f6641ddadd47c93740a22af7150d3e9a1ef6e983f9a8", size = 1750379 }, - { url = "https://files.pythonhosted.org/packages/ae/3d/2ec4b934f85856de1c0c18e90adc8902adadbfac2b3c0b831bfeb7214fc8/aiohttp-3.13.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:469167d5372f5bb3aedff4fc53035d593884fff2617a75317740e885acd48b04", size = 1560798 }, - { url = "https://files.pythonhosted.org/packages/38/56/e23d9c3e13006e599fdce3851517c70279e177871e3e567d22cf3baf5d6c/aiohttp-3.13.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a9f3546b503975a69b547c9fd1582cad10ede1ce6f3e313a2f547c73a3d7814f", size = 1697552 }, - { url = "https://files.pythonhosted.org/packages/56/cb/caa32c2ccaeca0a3dc39129079fd2ad02f9406c3a5f7924340435b87d4cd/aiohttp-3.13.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:6b4174fcec98601f0cfdf308ee29a6ae53c55f14359e848dab4e94009112ee7d", size = 1718609 }, - { url = "https://files.pythonhosted.org/packages/fb/c0/5911856fef9e40fd1ccbb8c54a90116875d5753a92c1cac66ce2059b390d/aiohttp-3.13.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:a533873a7a4ec2270fb362ee5a0d3b98752e4e1dc9042b257cd54545a96bd8ed", size = 1735887 }, - { url = "https://files.pythonhosted.org/packages/0e/48/8d6f4757a24c02f0a454c043556593a00645d10583859f7156db44d8b7d3/aiohttp-3.13.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:ce887c5e54411d607ee0959cac15bb31d506d86a9bcaddf0b7e9d63325a7a802", size = 1553079 }, - { url = "https://files.pythonhosted.org/packages/39/fa/e82c9445e40b50e46770702b5b6ca2f767966d53e1a5eef03583ceac6df6/aiohttp-3.13.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:d871f6a30d43e32fc9252dc7b9febe1a042b3ff3908aa83868d7cf7c9579a59b", size = 1762750 }, - { url = "https://files.pythonhosted.org/packages/3d/e6/9d30554e7f1e700bfeae4ab6b153d5dc7441606a9ec5e929288fa93a1477/aiohttp-3.13.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:222c828243b4789d79a706a876910f656fad4381661691220ba57b2ab4547865", size = 1717461 }, - { url = "https://files.pythonhosted.org/packages/1f/e5/29cca547990a59ea54f0674fc01de98519fc628cfceeab6175711750eca7/aiohttp-3.13.0-cp312-cp312-win32.whl", hash = "sha256:682d2e434ff2f1108314ff7f056ce44e457f12dbed0249b24e106e385cf154b9", size = 424633 }, - { url = "https://files.pythonhosted.org/packages/8b/68/46dd042d7bc62eab30bafdb8569f55ef125c3a88bb174270324224f8df56/aiohttp-3.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:0a2be20eb23888df130214b91c262a90e2de1553d6fb7de9e9010cec994c0ff2", size = 451401 }, - { url = "https://files.pythonhosted.org/packages/86/2c/ac53efdc9c10e41399acc2395af98f835b86d0141d5c3820857eb9f6a14a/aiohttp-3.13.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:00243e51f16f6ec0fb021659d4af92f675f3cf9f9b39efd142aa3ad641d8d1e6", size = 730090 }, - { url = "https://files.pythonhosted.org/packages/13/18/1ac95683e1c1d48ef4503965c96f5401618a04c139edae12e200392daae8/aiohttp-3.13.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:059978d2fddc462e9211362cbc8446747ecd930537fa559d3d25c256f032ff54", size = 488041 }, - { url = "https://files.pythonhosted.org/packages/fd/79/ef0d477c771a642d1a881b92d226314c43d3c74bc674c93e12e679397a97/aiohttp-3.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:564b36512a7da3b386143c611867e3f7cfb249300a1bf60889bd9985da67ab77", size = 486989 }, - { url = "https://files.pythonhosted.org/packages/37/b4/0e440481a0e77a551d6c5dcab5d11f1ff6b2b2ddb8dedc24f54f5caad732/aiohttp-3.13.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4aa995b9156ae499393d949a456a7ab0b994a8241a96db73a3b73c7a090eff6a", size = 1718331 }, - { url = "https://files.pythonhosted.org/packages/e6/59/76c421cc4a75bb1aceadb92f20ee6f05a990aa6960c64b59e8e0d340e3f5/aiohttp-3.13.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:55ca0e95a3905f62f00900255ed807c580775174252999286f283e646d675a49", size = 1686263 }, - { url = "https://files.pythonhosted.org/packages/ec/ac/5095f12a79c7775f402cfc3e83651b6e0a92ade10ddf7f2c78c4fed79f71/aiohttp-3.13.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:49ce7525853a981fc35d380aa2353536a01a9ec1b30979ea4e35966316cace7e", size = 1754265 }, - { url = "https://files.pythonhosted.org/packages/05/d7/a48e4989bd76cc70600c505bbdd0d90ca1ad7f9053eceeb9dbcf9345a9ec/aiohttp-3.13.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2117be9883501eaf95503bd313eb4c7a23d567edd44014ba15835a1e9ec6d852", size = 1856486 }, - { url = "https://files.pythonhosted.org/packages/1e/02/45b388b49e37933f316e1fb39c0de6fb1d77384b0c8f4cf6af5f2cbe3ea6/aiohttp-3.13.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d169c47e40c911f728439da853b6fd06da83761012e6e76f11cb62cddae7282b", size = 1737545 }, - { url = "https://files.pythonhosted.org/packages/6c/a7/4fde058f1605c34a219348a83a99f14724cc64e68a42480fc03cf40f9ea3/aiohttp-3.13.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:703ad3f742fc81e543638a7bebddd35acadaa0004a5e00535e795f4b6f2c25ca", size = 1552958 }, - { url = "https://files.pythonhosted.org/packages/d1/12/0bac4d29231981e3aa234e88d1931f6ba38135ff4c2cf3afbb7895527630/aiohttp-3.13.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5bf635c3476f4119b940cc8d94ad454cbe0c377e61b4527f0192aabeac1e9370", size = 1681166 }, - { url = "https://files.pythonhosted.org/packages/71/95/b829eb5f8ac1ca1d8085bb8df614c8acf3ff32e23ad5ad1173c7c9761daa/aiohttp-3.13.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:cfe6285ef99e7ee51cef20609be2bc1dd0e8446462b71c9db8bb296ba632810a", size = 1710516 }, - { url = "https://files.pythonhosted.org/packages/47/6d/15ccf4ef3c254d899f62580e0c7fc717014f4d14a3ac31771e505d2c736c/aiohttp-3.13.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:34d8af6391c5f2e69749d7f037b614b8c5c42093c251f336bdbfa4b03c57d6c4", size = 1731354 }, - { url = "https://files.pythonhosted.org/packages/46/6a/8acf6c57e03b6fdcc8b4c06392e66abaff3213ea275e41db3edb20738d91/aiohttp-3.13.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:12f5d820fadc5848d4559ea838aef733cf37ed2a1103bba148ac2f5547c14c29", size = 1548040 }, - { url = "https://files.pythonhosted.org/packages/75/7d/fbfd59ab2a83fe2578ce79ac3db49727b81e9f4c3376217ad09c03c6d279/aiohttp-3.13.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:0f1338b61ea66f4757a0544ed8a02ccbf60e38d9cfb3225888888dd4475ebb96", size = 1756031 }, - { url = "https://files.pythonhosted.org/packages/99/e7/cc9f0fdf06cab3ca61e6b62bff9a4b978b8ca736e9d76ddf54365673ab19/aiohttp-3.13.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:582770f82513419512da096e8df21ca44f86a2e56e25dc93c5ab4df0fe065bf0", size = 1714933 }, - { url = "https://files.pythonhosted.org/packages/db/43/7abbe1de94748a58a71881163ee280fd3217db36e8344d109f63638fe16a/aiohttp-3.13.0-cp313-cp313-win32.whl", hash = "sha256:3194b8cab8dbc882f37c13ef1262e0a3d62064fa97533d3aa124771f7bf1ecee", size = 423799 }, - { url = "https://files.pythonhosted.org/packages/c9/58/afab7f2b9e7df88c995995172eb78cae8a3d5a62d5681abaade86b3f0089/aiohttp-3.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:7897298b3eedc790257fef8a6ec582ca04e9dbe568ba4a9a890913b925b8ea21", size = 450138 }, - { url = "https://files.pythonhosted.org/packages/fe/c1/93bb1e35cd0c4665bb422b1ca3d87b588f4bca2656bbe9292b963d5b76a9/aiohttp-3.13.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:c417f8c2e1137775569297c584a8a7144e5d1237789eae56af4faf1894a0b861", size = 733187 }, - { url = "https://files.pythonhosted.org/packages/5e/36/2d50eba91992d3fe7a6452506ccdab45d03685ee8d8acaa5b289384a7d4c/aiohttp-3.13.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:f84b53326abf8e56ebc28a35cebf4a0f396a13a76300f500ab11fe0573bf0b52", size = 488684 }, - { url = "https://files.pythonhosted.org/packages/82/93/fa4b1d5ecdc7805bdf0815ef00257db4632ccf0a8bffd44f9fc4657b1677/aiohttp-3.13.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:990a53b9d6a30b2878789e490758e568b12b4a7fb2527d0c89deb9650b0e5813", size = 489255 }, - { url = "https://files.pythonhosted.org/packages/05/0f/85241f0d158da5e24e8ac9d50c0849ed24f882cafc53dc95749ef85eef09/aiohttp-3.13.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c811612711e01b901e18964b3e5dec0d35525150f5f3f85d0aee2935f059910a", size = 1715914 }, - { url = "https://files.pythonhosted.org/packages/ab/fc/c755590d6f6d2b5d1565c72d6ee658d3c30ec61acb18964d1e9bf991d9b5/aiohttp-3.13.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:ee433e594d7948e760b5c2a78cc06ac219df33b0848793cf9513d486a9f90a52", size = 1665171 }, - { url = "https://files.pythonhosted.org/packages/3a/de/caa61e213ff546b8815aef5e931d7eae1dbe8c840a3f11ec5aa41c5ae462/aiohttp-3.13.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:19bb08e56f57c215e9572cd65cb6f8097804412c54081d933997ddde3e5ac579", size = 1755124 }, - { url = "https://files.pythonhosted.org/packages/fb/b7/40c3219dd2691aa35cf889b4fbb0c00e48a19092928707044bfe92068e01/aiohttp-3.13.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f27b7488144eb5dd9151cf839b195edd1569629d90ace4c5b6b18e4e75d1e63a", size = 1835949 }, - { url = "https://files.pythonhosted.org/packages/57/e8/66e3c32841fc0e26a09539c377aa0f3bbf6deac1957ac5182cf276c5719c/aiohttp-3.13.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d812838c109757a11354a161c95708ae4199c4fd4d82b90959b20914c1d097f6", size = 1714276 }, - { url = "https://files.pythonhosted.org/packages/6b/a5/c68e5b46ff0410fe3abfa508651b09372428f27036138beacf4ff6b7cb8c/aiohttp-3.13.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7c20db99da682f9180fa5195c90b80b159632fb611e8dbccdd99ba0be0970620", size = 1545929 }, - { url = "https://files.pythonhosted.org/packages/7a/a6/4c97dc27f9935c0c0aa6e3e10e5b4548823ab5d056636bde374fcd297256/aiohttp-3.13.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:cf8b0870047900eb1f17f453b4b3953b8ffbf203ef56c2f346780ff930a4d430", size = 1679988 }, - { url = "https://files.pythonhosted.org/packages/8e/1b/11f9c52fd72b786a47e796e6794883417280cdca8eb1032d8d0939928dfa/aiohttp-3.13.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:5b8a5557d5af3f4e3add52a58c4cf2b8e6e59fc56b261768866f5337872d596d", size = 1678031 }, - { url = "https://files.pythonhosted.org/packages/ea/eb/948903d40505f3a25e53e051488d2714ded3afac1f961df135f2936680f9/aiohttp-3.13.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:052bcdd80c1c54b8a18a9ea0cd5e36f473dc8e38d51b804cea34841f677a9971", size = 1726184 }, - { url = "https://files.pythonhosted.org/packages/44/14/c8ced38c7dfe80804dec17a671963ccf3cb282f12700ec70b1f689d8de7d/aiohttp-3.13.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:76484ba17b2832776581b7ab466d094e48eba74cb65a60aea20154dae485e8bd", size = 1542344 }, - { url = "https://files.pythonhosted.org/packages/a4/6e/f2e6bff550a51fd7c45fdab116a1dab7cc502e5d942956f10fc5c626bb15/aiohttp-3.13.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:62d8a0adcdaf62ee56bfb37737153251ac8e4b27845b3ca065862fb01d99e247", size = 1740913 }, - { url = "https://files.pythonhosted.org/packages/da/00/8f057300d9b598a706348abb375b3de9a253195fb615f17c0b2be2a72836/aiohttp-3.13.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5004d727499ecb95f7c9147dd0bfc5b5670f71d355f0bd26d7af2d3af8e07d2f", size = 1695535 }, - { url = "https://files.pythonhosted.org/packages/8a/ab/6919d584d8f053a14b15f0bfa3f315b3f548435c2142145459da2efa8673/aiohttp-3.13.0-cp314-cp314-win32.whl", hash = "sha256:a1c20c26af48aea984f63f96e5d7af7567c32cb527e33b60a0ef0a6313cf8b03", size = 429548 }, - { url = "https://files.pythonhosted.org/packages/c5/59/5d9e78de6132079066f5077d9687bf524f764a2f8207e04d8d68790060c6/aiohttp-3.13.0-cp314-cp314-win_amd64.whl", hash = "sha256:56f7d230ec66e799fbfd8350e9544f8a45a4353f1cf40c1fea74c1780f555b8f", size = 455548 }, - { url = "https://files.pythonhosted.org/packages/7c/ea/7d98da03d1e9798bb99c3ca4963229150d45c9b7a3a16210c5b4a5f89e07/aiohttp-3.13.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:2fd35177dc483ae702f07b86c782f4f4b100a8ce4e7c5778cea016979023d9fd", size = 765319 }, - { url = "https://files.pythonhosted.org/packages/5c/02/37f29beced8213bb467c52ad509a5e3b41e6e967de2f6eaf7f8db63bea54/aiohttp-3.13.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:4df1984c8804ed336089e88ac81a9417b1fd0db7c6f867c50a9264488797e778", size = 502567 }, - { url = "https://files.pythonhosted.org/packages/e7/22/b0afcafcfe3637bc8d7992abf08ee9452018366c0801e4e7d4efda2ed839/aiohttp-3.13.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:e68c0076052dd911a81d3acc4ef2911cc4ef65bf7cadbfbc8ae762da24da858f", size = 507078 }, - { url = "https://files.pythonhosted.org/packages/49/4c/046c847b7a1993b49f3855cc3b97872d5df193d9240de835d0dc6a97b164/aiohttp-3.13.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bc95c49853cd29613e4fe4ff96d73068ff89b89d61e53988442e127e8da8e7ba", size = 1862115 }, - { url = "https://files.pythonhosted.org/packages/1a/25/1449a59e3c6405da5e47b0138ee0855414dc12a8c306685d7fc3dd300e1f/aiohttp-3.13.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3b3bdc89413117b40cc39baae08fd09cbdeb839d421c4e7dce6a34f6b54b3ac1", size = 1717147 }, - { url = "https://files.pythonhosted.org/packages/23/8f/50cc34ad267b38608f21c6a74327015dd08a66f1dd8e7ceac954d0953191/aiohttp-3.13.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3e77a729df23be2116acc4e9de2767d8e92445fbca68886dd991dc912f473755", size = 1841443 }, - { url = "https://files.pythonhosted.org/packages/df/b9/b3ab1278faa0d1b8f434c85f9cf34eeb0a25016ffe1ee6bc361d09fef0ec/aiohttp-3.13.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e88ab34826d6eeb6c67e6e92400b9ec653faf5092a35f07465f44c9f1c429f82", size = 1933652 }, - { url = "https://files.pythonhosted.org/packages/88/e2/86050aaa3bd7021b115cdfc88477b754e8cf93ef0079867840eee22d3c34/aiohttp-3.13.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:019dbef24fe28ce2301419dd63a2b97250d9760ca63ee2976c2da2e3f182f82e", size = 1790682 }, - { url = "https://files.pythonhosted.org/packages/78/8d/9af903324c2ba24a0c4778e9bcc738b773c98dded3a4fcf8041d5211769f/aiohttp-3.13.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:2c4aeaedd20771b7b4bcdf0ae791904445df6d856c02fc51d809d12d17cffdc7", size = 1622011 }, - { url = "https://files.pythonhosted.org/packages/84/97/5174971ba4986d913554ceb248b0401eb5358cb60672ea0166f9f596cd08/aiohttp-3.13.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:b3a8e6a2058a0240cfde542b641d0e78b594311bc1a710cbcb2e1841417d5cb3", size = 1787148 }, - { url = "https://files.pythonhosted.org/packages/dd/ae/8b397e980ac613ef3ddd8e996aa7a40a1828df958257800d4bb325657db3/aiohttp-3.13.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:f8e38d55ca36c15f36d814ea414ecb2401d860de177c49f84a327a25b3ee752b", size = 1774816 }, - { url = "https://files.pythonhosted.org/packages/c7/54/0e8e2111dd92051c787e934b6bbf30c213daaa5e7ee5f51bca8913607492/aiohttp-3.13.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:a921edbe971aade1bf45bcbb3494e30ba6863a5c78f28be992c42de980fd9108", size = 1788610 }, - { url = "https://files.pythonhosted.org/packages/fa/dd/c9283dbfd9325ed6fa6c91f009db6344d8d370a7bcf09f36e7b2fcbfae02/aiohttp-3.13.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:474cade59a447cb4019c0dce9f0434bf835fb558ea932f62c686fe07fe6db6a1", size = 1615498 }, - { url = "https://files.pythonhosted.org/packages/8c/f6/da76230679bd9ef175d876093f89e7fd6d6476c18505e115e3026fe5ef95/aiohttp-3.13.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:99a303ad960747c33b65b1cb65d01a62ac73fa39b72f08a2e1efa832529b01ed", size = 1815187 }, - { url = "https://files.pythonhosted.org/packages/d5/78/394003ac738703822616f4f922705b54e5b3d8e7185831ecc1c97904174d/aiohttp-3.13.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:bb34001fc1f05f6b323e02c278090c07a47645caae3aa77ed7ed8a3ce6abcce9", size = 1760281 }, - { url = "https://files.pythonhosted.org/packages/bd/b0/4bad0a9dd5910bd01c3119f8bd3d71887cd412d4105e4acddcdacf3cfa76/aiohttp-3.13.0-cp314-cp314t-win32.whl", hash = "sha256:dea698b64235d053def7d2f08af9302a69fcd760d1c7bd9988fd5d3b6157e657", size = 462608 }, - { url = "https://files.pythonhosted.org/packages/bd/af/ad12d592f623aae2bd1d3463201dc39c201ea362f9ddee0d03efd9e83720/aiohttp-3.13.0-cp314-cp314t-win_amd64.whl", hash = "sha256:1f164699a060c0b3616459d13c1464a981fddf36f892f0a5027cbd45121fb14b", size = 496010 }, +sdist = { url = "https://files.pythonhosted.org/packages/ba/fa/3ae643cd525cf6844d3dc810481e5748107368eb49563c15a5fb9f680750/aiohttp-3.13.1.tar.gz", hash = "sha256:4b7ee9c355015813a6aa085170b96ec22315dabc3d866fd77d147927000e9464", size = 7835344 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e6/34/5097441cc3047eccc2e0bfed3760ed068489b8392545d3aec0d8fbfab2b5/aiohttp-3.13.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:2349a6b642020bf20116a8a5c83bae8ba071acf1461c7cbe45fc7fafd552e7e2", size = 735069 }, + { url = "https://files.pythonhosted.org/packages/8c/2b/726466b4b4b16271a3db2a8a914d754d6cb9cee7bebde1f3ac6043e4e030/aiohttp-3.13.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2a8434ca31c093a90edb94d7d70e98706ce4d912d7f7a39f56e1af26287f4bb7", size = 492575 }, + { url = "https://files.pythonhosted.org/packages/82/1f/364e64292c95bb6c9e2823b0afa1ad3f06524c573d45df82294be572489d/aiohttp-3.13.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0bd610a7e87431741021a9a6ab775e769ea8c01bf01766d481282bfb17df597f", size = 487862 }, + { url = "https://files.pythonhosted.org/packages/23/b0/c5a774b3125ac854987b8ca45a6d995829987d01ece4525d3fc369a9ca88/aiohttp-3.13.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:777ec887264b629395b528af59b8523bf3164d4c6738cd8989485ff3eda002e2", size = 1666761 }, + { url = "https://files.pythonhosted.org/packages/29/be/32c6c1d3a6c69e594b855bbf4014bea4c42008b0daac8c6e5c9f03207b89/aiohttp-3.13.1-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:ac1892f56e2c445aca5ba28f3bf8e16b26dfc05f3c969867b7ef553b74cb4ebe", size = 1634627 }, + { url = "https://files.pythonhosted.org/packages/73/8d/fde3a8f4801b14e0b9490f5bc86c5106cb7d96bd60ff2aaee53749c72fe1/aiohttp-3.13.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:499a047d1c5e490c31d16c033e2e47d1358f0e15175c7a1329afc6dfeb04bc09", size = 1726564 }, + { url = "https://files.pythonhosted.org/packages/52/b2/8290556f1f6b17b1af976a9abb17f9b54dc7218e11bbf6abbebaa7cc70fb/aiohttp-3.13.1-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:610be925f89501938c770f1e28ca9dd62e9b308592c81bd5d223ce92434c0089", size = 1814413 }, + { url = "https://files.pythonhosted.org/packages/ef/6b/4b657e9fa72479df38117609d4ec8e4b07e8110b872df3872f9c6a96e26b/aiohttp-3.13.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:90eb902c06c6ac85d6b80fa9f2bd681f25b1ebf73433d428b3d182a507242711", size = 1667964 }, + { url = "https://files.pythonhosted.org/packages/ee/ed/563de175d01fa26459a60a7c82dbf69d20e356d459476a7526329091b4c3/aiohttp-3.13.1-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ab8ac3224b2beb46266c094b3869d68d5f96f35dba98e03dea0acbd055eefa03", size = 1553917 }, + { url = "https://files.pythonhosted.org/packages/39/26/48a4b5681eada16eb5b39cae277765aed1644b03610c43eadb8b331ccfea/aiohttp-3.13.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:79ac65b6e2731558aad1e4c1a655d2aa2a77845b62acecf5898b0d4fe8c76618", size = 1637730 }, + { url = "https://files.pythonhosted.org/packages/c1/43/57b137af37344e03c7f6b28ddf38a4af820b53c1fa9ce13f668fe468d2e2/aiohttp-3.13.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:4dadbd858ed8c04d1aa7a2a91ad65f8e1fbd253ae762ef5be8111e763d576c3c", size = 1644088 }, + { url = "https://files.pythonhosted.org/packages/0d/c4/e49bafa4babef09929b10968a6b6efe3707fbaa5c5bb7c8db7f810232269/aiohttp-3.13.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:e0b2ccd331bc77149e88e919aa95c228a011e03e1168fd938e6aeb1a317d7a8a", size = 1696215 }, + { url = "https://files.pythonhosted.org/packages/15/e4/8414be434b3e50f9089ffa7c4d5130ba6ff0d1c6fa9f55cd760b088abbe0/aiohttp-3.13.1-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:fba3c85fb24fe204e73f3c92f09f4f5cfa55fa7e54b34d59d91b7c5a258d0f6a", size = 1540617 }, + { url = "https://files.pythonhosted.org/packages/bd/8b/31cb6725f819b74a9c0b0055c500187294e73aea40708b6a5aa7b328ea4c/aiohttp-3.13.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8d5011e4e741d2635cda18f2997a56e8e1d1b94591dc8732f2ef1d3e1bfc5f45", size = 1713509 }, + { url = "https://files.pythonhosted.org/packages/24/ac/49a79c2711423cfa091e265c46e58617de31258c64502b890f25421cb742/aiohttp-3.13.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c5fe2728a89c82574bd3132d59237c3b5fb83e2e00a320e928d05d74d1ae895f", size = 1654702 }, + { url = "https://files.pythonhosted.org/packages/30/52/1cf23cffeda1f079f20cd9c72174a76e8b0c6595def6803892e37ee35c8a/aiohttp-3.13.1-cp310-cp310-win32.whl", hash = "sha256:add14a5e68cbcfc526c89c1ed8ea963f5ff8b9b4b854985b07820c6fbfdb3c3c", size = 430898 }, + { url = "https://files.pythonhosted.org/packages/0e/13/214a01f2936f4645b1fbd5cba9001331ca5af5c04bbdbe747eed330a8516/aiohttp-3.13.1-cp310-cp310-win_amd64.whl", hash = "sha256:a4cc9d9cfdf75a69ae921c407e02d0c1799ab333b0bc6f7928c175f47c080d6a", size = 453684 }, + { url = "https://files.pythonhosted.org/packages/be/2c/739d03730ffce57d2093e2e611e1541ac9a4b3bb88288c33275058b9ffc2/aiohttp-3.13.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9eefa0a891e85dca56e2d00760945a6325bd76341ec386d3ad4ff72eb97b7e64", size = 742004 }, + { url = "https://files.pythonhosted.org/packages/fc/f8/7f5b7f7184d7c80e421dbaecbd13e0b2a0bb8663fd0406864f9a167a438c/aiohttp-3.13.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6c20eb646371a5a57a97de67e52aac6c47badb1564e719b3601bbb557a2e8fd0", size = 495601 }, + { url = "https://files.pythonhosted.org/packages/3e/af/fb78d028b9642dd33ff127d9a6a151586f33daff631b05250fecd0ab23f8/aiohttp-3.13.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bfc28038cd86fb1deed5cc75c8fda45c6b0f5c51dfd76f8c63d3d22dc1ab3d1b", size = 491790 }, + { url = "https://files.pythonhosted.org/packages/1e/ae/e40e422ee995e4f91f7f087b86304e3dd622d3a5b9ca902a1e94ebf9a117/aiohttp-3.13.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b22eeffca2e522451990c31a36fe0e71079e6112159f39a4391f1c1e259a795", size = 1746350 }, + { url = "https://files.pythonhosted.org/packages/28/a5/fe6022bb869bf2d2633b155ed8348d76358c22d5ff9692a15016b2d1019f/aiohttp-3.13.1-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:65782b2977c05ebd78787e3c834abe499313bf69d6b8be4ff9c340901ee7541f", size = 1703046 }, + { url = "https://files.pythonhosted.org/packages/5a/a5/c4ef3617d7cdc49f2d5af077f19794946f0f2d94b93c631ace79047361a2/aiohttp-3.13.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:dacba54f9be3702eb866b0b9966754b475e1e39996e29e442c3cd7f1117b43a9", size = 1806161 }, + { url = "https://files.pythonhosted.org/packages/ad/45/b87d2430aee7e7d00b24e3dff2c5bd69f21017f6edb19cfd91e514664fc8/aiohttp-3.13.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:aa878da718e8235302c365e376b768035add36b55177706d784a122cb822a6a4", size = 1894546 }, + { url = "https://files.pythonhosted.org/packages/e8/a2/79eb466786a7f11a0292c353a8a9b95e88268c48c389239d7531d66dbb48/aiohttp-3.13.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e4b4e607fbd4964d65945a7b9d1e7f98b0d5545736ea613f77d5a2a37ff1e46", size = 1745683 }, + { url = "https://files.pythonhosted.org/packages/93/1a/153b0ad694f377e94eacc85338efe03ed4776a396c8bb47bd9227135792a/aiohttp-3.13.1-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0c3db2d0e5477ad561bf7ba978c3ae5f8f78afda70daa05020179f759578754f", size = 1605418 }, + { url = "https://files.pythonhosted.org/packages/3f/4e/18605b1bfeb4b00d3396d833647cdb213118e2a96862e5aebee62ad065b4/aiohttp-3.13.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9739d34506fdf59bf2c092560d502aa728b8cdb33f34ba15fb5e2852c35dd829", size = 1722379 }, + { url = "https://files.pythonhosted.org/packages/72/13/0a38ad385d547fb283e0e1fe1ff1dff8899bd4ed0aaceeb13ec14abbf136/aiohttp-3.13.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:b902e30a268a85d50197b4997edc6e78842c14c0703450f632c2d82f17577845", size = 1716693 }, + { url = "https://files.pythonhosted.org/packages/55/65/7029d7573ab9009adde380052c6130d02c8db52195fda112db35e914fe7b/aiohttp-3.13.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:1bbfc04c8de7def6504cce0a97f9885a5c805fd2395a0634bc10f9d6ecb42524", size = 1784174 }, + { url = "https://files.pythonhosted.org/packages/2d/36/fd46e39cb85418e45b0e4a8bfc39651ee0b8f08ea006adf217a221cdb269/aiohttp-3.13.1-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:6941853405a38a5eeb7d9776db77698df373ff7fa8c765cb81ea14a344fccbeb", size = 1593716 }, + { url = "https://files.pythonhosted.org/packages/85/b8/188e0cb1be37b4408373171070fda17c3bf9c67c0d3d4fd5ee5b1fa108e1/aiohttp-3.13.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:7764adcd2dc8bd21c8228a53dda2005428498dc4d165f41b6086f0ac1c65b1c9", size = 1799254 }, + { url = "https://files.pythonhosted.org/packages/67/ff/fdf768764eb427b0cc9ebb2cebddf990f94d98b430679f8383c35aa114be/aiohttp-3.13.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c09e08d38586fa59e5a2f9626505a0326fadb8e9c45550f029feeb92097a0afc", size = 1738122 }, + { url = "https://files.pythonhosted.org/packages/94/84/fce7a4d575943394d7c0e632273838eb6f39de8edf25386017bf5f0de23b/aiohttp-3.13.1-cp311-cp311-win32.whl", hash = "sha256:ce1371675e74f6cf271d0b5530defb44cce713fd0ab733713562b3a2b870815c", size = 430491 }, + { url = "https://files.pythonhosted.org/packages/ac/d2/d21b8ab6315a5d588c550ab285b4f02ae363edf012920e597904c5a56608/aiohttp-3.13.1-cp311-cp311-win_amd64.whl", hash = "sha256:77a2f5cc28cf4704cc157be135c6a6cfb38c9dea478004f1c0fd7449cf445c28", size = 454808 }, + { url = "https://files.pythonhosted.org/packages/1a/72/d463a10bf29871f6e3f63bcf3c91362dc4d72ed5917a8271f96672c415ad/aiohttp-3.13.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0760bd9a28efe188d77b7c3fe666e6ef74320d0f5b105f2e931c7a7e884c8230", size = 736218 }, + { url = "https://files.pythonhosted.org/packages/26/13/f7bccedbe52ea5a6eef1e4ebb686a8d7765319dfd0a5939f4238cb6e79e6/aiohttp-3.13.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7129a424b441c3fe018a414401bf1b9e1d49492445f5676a3aecf4f74f67fcdb", size = 491251 }, + { url = "https://files.pythonhosted.org/packages/0c/7c/7ea51b5aed6cc69c873f62548da8345032aa3416336f2d26869d4d37b4a2/aiohttp-3.13.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e1cb04ae64a594f6ddf5cbb024aba6b4773895ab6ecbc579d60414f8115e9e26", size = 490394 }, + { url = "https://files.pythonhosted.org/packages/31/05/1172cc4af4557f6522efdee6eb2b9f900e1e320a97e25dffd3c5a6af651b/aiohttp-3.13.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:782d656a641e755decd6bd98d61d2a8ea062fd45fd3ff8d4173605dd0d2b56a1", size = 1737455 }, + { url = "https://files.pythonhosted.org/packages/24/3d/ce6e4eca42f797d6b1cd3053cf3b0a22032eef3e4d1e71b9e93c92a3f201/aiohttp-3.13.1-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:f92ad8169767429a6d2237331726c03ccc5f245222f9373aa045510976af2b35", size = 1699176 }, + { url = "https://files.pythonhosted.org/packages/25/04/7127ba55653e04da51477372566b16ae786ef854e06222a1c96b4ba6c8ef/aiohttp-3.13.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0e778f634ca50ec005eefa2253856921c429581422d887be050f2c1c92e5ce12", size = 1767216 }, + { url = "https://files.pythonhosted.org/packages/b8/3b/43bca1e75847e600f40df829a6b2f0f4e1d4c70fb6c4818fdc09a462afd5/aiohttp-3.13.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:9bc36b41cf4aab5d3b34d22934a696ab83516603d1bc1f3e4ff9930fe7d245e5", size = 1865870 }, + { url = "https://files.pythonhosted.org/packages/9e/69/b204e5d43384197a614c88c1717c324319f5b4e7d0a1b5118da583028d40/aiohttp-3.13.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3fd4570ea696aee27204dd524f287127ed0966d14d309dc8cc440f474e3e7dbd", size = 1751021 }, + { url = "https://files.pythonhosted.org/packages/1c/af/845dc6b6fdf378791d720364bf5150f80d22c990f7e3a42331d93b337cc7/aiohttp-3.13.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7bda795f08b8a620836ebfb0926f7973972a4bf8c74fdf9145e489f88c416811", size = 1561448 }, + { url = "https://files.pythonhosted.org/packages/7a/91/d2ab08cd77ed76a49e4106b1cfb60bce2768242dd0c4f9ec0cb01e2cbf94/aiohttp-3.13.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:055a51d90e351aae53dcf324d0eafb2abe5b576d3ea1ec03827d920cf81a1c15", size = 1698196 }, + { url = "https://files.pythonhosted.org/packages/5e/d1/082f0620dc428ecb8f21c08a191a4694915cd50f14791c74a24d9161cc50/aiohttp-3.13.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:d4131df864cbcc09bb16d3612a682af0db52f10736e71312574d90f16406a867", size = 1719252 }, + { url = "https://files.pythonhosted.org/packages/fc/78/2af2f44491be7b08e43945b72d2b4fd76f0a14ba850ba9e41d28a7ce716a/aiohttp-3.13.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:163d3226e043f79bf47c87f8dfc89c496cc7bc9128cb7055ce026e435d551720", size = 1736529 }, + { url = "https://files.pythonhosted.org/packages/b0/34/3e919ecdc93edaea8d140138049a0d9126141072e519535e2efa38eb7a02/aiohttp-3.13.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:a2370986a3b75c1a5f3d6f6d763fc6be4b430226577b0ed16a7c13a75bf43d8f", size = 1553723 }, + { url = "https://files.pythonhosted.org/packages/21/4b/d8003aeda2f67f359b37e70a5a4b53fee336d8e89511ac307ff62aeefcdb/aiohttp-3.13.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:d7c14de0c7c9f1e6e785ce6cbe0ed817282c2af0012e674f45b4e58c6d4ea030", size = 1763394 }, + { url = "https://files.pythonhosted.org/packages/4c/7b/1dbe6a39e33af9baaafc3fc016a280663684af47ba9f0e5d44249c1f72ec/aiohttp-3.13.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:bb611489cf0db10b99beeb7280bd39e0ef72bc3eb6d8c0f0a16d8a56075d1eb7", size = 1718104 }, + { url = "https://files.pythonhosted.org/packages/5c/88/bd1b38687257cce67681b9b0fa0b16437be03383fa1be4d1a45b168bef25/aiohttp-3.13.1-cp312-cp312-win32.whl", hash = "sha256:f90fe0ee75590f7428f7c8b5479389d985d83c949ea10f662ab928a5ed5cf5e6", size = 425303 }, + { url = "https://files.pythonhosted.org/packages/0e/e3/4481f50dd6f27e9e58c19a60cff44029641640237e35d32b04aaee8cf95f/aiohttp-3.13.1-cp312-cp312-win_amd64.whl", hash = "sha256:3461919a9dca272c183055f2aab8e6af0adc810a1b386cce28da11eb00c859d9", size = 452071 }, + { url = "https://files.pythonhosted.org/packages/16/6d/d267b132342e1080f4c1bb7e1b4e96b168b3cbce931ec45780bff693ff95/aiohttp-3.13.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:55785a7f8f13df0c9ca30b5243d9909bd59f48b274262a8fe78cee0828306e5d", size = 730727 }, + { url = "https://files.pythonhosted.org/packages/92/c8/1cf495bac85cf71b80fad5f6d7693e84894f11b9fe876b64b0a1e7cbf32f/aiohttp-3.13.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4bef5b83296cebb8167707b4f8d06c1805db0af632f7a72d7c5288a84667e7c3", size = 488678 }, + { url = "https://files.pythonhosted.org/packages/a8/19/23c6b81cca587ec96943d977a58d11d05a82837022e65cd5502d665a7d11/aiohttp-3.13.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:27af0619c33f9ca52f06069ec05de1a357033449ab101836f431768ecfa63ff5", size = 487637 }, + { url = "https://files.pythonhosted.org/packages/48/58/8f9464afb88b3eed145ad7c665293739b3a6f91589694a2bb7e5778cbc72/aiohttp-3.13.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a47fe43229a8efd3764ef7728a5c1158f31cdf2a12151fe99fde81c9ac87019c", size = 1718975 }, + { url = "https://files.pythonhosted.org/packages/e1/8b/c3da064ca392b2702f53949fd7c403afa38d9ee10bf52c6ad59a42537103/aiohttp-3.13.1-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6e68e126de5b46e8b2bee73cab086b5d791e7dc192056916077aa1e2e2b04437", size = 1686905 }, + { url = "https://files.pythonhosted.org/packages/0a/a4/9c8a3843ecf526daee6010af1a66eb62579be1531d2d5af48ea6f405ad3c/aiohttp-3.13.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e65ef49dd22514329c55970d39079618a8abf856bae7147913bb774a3ab3c02f", size = 1754907 }, + { url = "https://files.pythonhosted.org/packages/a4/80/1f470ed93e06436e3fc2659a9fc329c192fa893fb7ed4e884d399dbfb2a8/aiohttp-3.13.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0e425a7e0511648b3376839dcc9190098671a47f21a36e815b97762eb7d556b0", size = 1857129 }, + { url = "https://files.pythonhosted.org/packages/cc/e6/33d305e6cce0a8daeb79c7d8d6547d6e5f27f4e35fa4883fc9c9eb638596/aiohttp-3.13.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:010dc9b7110f055006acd3648d5d5955bb6473b37c3663ec42a1b4cba7413e6b", size = 1738189 }, + { url = "https://files.pythonhosted.org/packages/ac/42/8df03367e5a64327fe0c39291080697795430c438fc1139c7cc1831aa1df/aiohttp-3.13.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1b5c722d0ca5f57d61066b5dfa96cdb87111e2519156b35c1f8dd17c703bee7a", size = 1553608 }, + { url = "https://files.pythonhosted.org/packages/96/17/6d5c73cd862f1cf29fddcbb54aac147037ff70a043a2829d03a379e95742/aiohttp-3.13.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:93029f0e9b77b714904a281b5aa578cdc8aa8ba018d78c04e51e1c3d8471b8ec", size = 1681809 }, + { url = "https://files.pythonhosted.org/packages/be/31/8926c8ab18533f6076ce28d2c329a203b58c6861681906e2d73b9c397588/aiohttp-3.13.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:d1824c7d08d8ddfc8cb10c847f696942e5aadbd16fd974dfde8bd2c3c08a9fa1", size = 1711161 }, + { url = "https://files.pythonhosted.org/packages/f2/36/2f83e1ca730b1e0a8cf1c8ab9559834c5eec9f5da86e77ac71f0d16b521d/aiohttp-3.13.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:8f47d0ff5b3eb9c1278a2f56ea48fda667da8ebf28bd2cb378b7c453936ce003", size = 1731999 }, + { url = "https://files.pythonhosted.org/packages/b9/ec/1f818cc368dfd4d5ab4e9efc8f2f6f283bfc31e1c06d3e848bcc862d4591/aiohttp-3.13.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:8a396b1da9b51ded79806ac3b57a598f84e0769eaa1ba300655d8b5e17b70c7b", size = 1548684 }, + { url = "https://files.pythonhosted.org/packages/d3/ad/33d36efd16e4fefee91b09a22a3a0e1b830f65471c3567ac5a8041fac812/aiohttp-3.13.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:d9c52a65f54796e066b5d674e33b53178014752d28bca555c479c2c25ffcec5b", size = 1756676 }, + { url = "https://files.pythonhosted.org/packages/3c/c4/4a526d84e77d464437713ca909364988ed2e0cd0cdad2c06cb065ece9e08/aiohttp-3.13.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a89da72d18d6c95a653470b78d8ee5aa3c4b37212004c103403d0776cbea6ff0", size = 1715577 }, + { url = "https://files.pythonhosted.org/packages/a2/21/e39638b7d9c7f1362c4113a91870f89287e60a7ea2d037e258b81e8b37d5/aiohttp-3.13.1-cp313-cp313-win32.whl", hash = "sha256:02e0258b7585ddf5d01c79c716ddd674386bfbf3041fbbfe7bdf9c7c32eb4a9b", size = 424468 }, + { url = "https://files.pythonhosted.org/packages/cc/00/f3a92c592a845ebb2f47d102a67f35f0925cb854c5e7386f1a3a1fdff2ab/aiohttp-3.13.1-cp313-cp313-win_amd64.whl", hash = "sha256:ef56ffe60e8d97baac123272bde1ab889ee07d3419606fae823c80c2b86c403e", size = 450806 }, + { url = "https://files.pythonhosted.org/packages/97/be/0f6c41d2fd0aab0af133c509cabaf5b1d78eab882cb0ceb872e87ceeabf7/aiohttp-3.13.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:77f83b3dc5870a2ea79a0fcfdcc3fc398187ec1675ff61ec2ceccad27ecbd303", size = 733828 }, + { url = "https://files.pythonhosted.org/packages/75/14/24e2ac5efa76ae30e05813e0f50737005fd52da8ddffee474d4a5e7f38a6/aiohttp-3.13.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:9cafd2609ebb755e47323306c7666283fbba6cf82b5f19982ea627db907df23a", size = 489320 }, + { url = "https://files.pythonhosted.org/packages/da/5a/4cbe599358d05ea7db4869aff44707b57d13f01724d48123dc68b3288d5a/aiohttp-3.13.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:9c489309a2ca548d5f11131cfb4092f61d67954f930bba7e413bcdbbb82d7fae", size = 489899 }, + { url = "https://files.pythonhosted.org/packages/67/96/3aec9d9cfc723273d4386328a1e2562cf23629d2f57d137047c49adb2afb/aiohttp-3.13.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:79ac15fe5fdbf3c186aa74b656cd436d9a1e492ba036db8901c75717055a5b1c", size = 1716556 }, + { url = "https://files.pythonhosted.org/packages/b9/99/39a3d250595b5c8172843831221fa5662884f63f8005b00b4034f2a7a836/aiohttp-3.13.1-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:095414be94fce3bc080684b4cd50fb70d439bc4662b2a1984f45f3bf9ede08aa", size = 1665814 }, + { url = "https://files.pythonhosted.org/packages/3b/96/8319e7060a85db14a9c178bc7b3cf17fad458db32ba6d2910de3ca71452d/aiohttp-3.13.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c68172e1a2dca65fa1272c85ca72e802d78b67812b22827df01017a15c5089fa", size = 1755767 }, + { url = "https://files.pythonhosted.org/packages/1c/c6/0a2b3d886b40aa740fa2294cd34ed46d2e8108696748492be722e23082a7/aiohttp-3.13.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3751f9212bcd119944d4ea9de6a3f0fee288c177b8ca55442a2cdff0c8201eb3", size = 1836591 }, + { url = "https://files.pythonhosted.org/packages/fb/34/8ab5904b3331c91a58507234a1e2f662f837e193741609ee5832eb436251/aiohttp-3.13.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8619dca57d98a8353abdc7a1eeb415548952b39d6676def70d9ce76d41a046a9", size = 1714915 }, + { url = "https://files.pythonhosted.org/packages/b5/d3/d36077ca5f447649112189074ac6c192a666bf68165b693e48c23b0d008c/aiohttp-3.13.1-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:97795a0cb0a5f8a843759620e9cbd8889f8079551f5dcf1ccd99ed2f056d9632", size = 1546579 }, + { url = "https://files.pythonhosted.org/packages/a8/14/dbc426a1bb1305c4fc78ce69323498c9e7c699983366ef676aa5d3f949fa/aiohttp-3.13.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:1060e058da8f9f28a7026cdfca9fc886e45e551a658f6a5c631188f72a3736d2", size = 1680633 }, + { url = "https://files.pythonhosted.org/packages/29/83/1e68e519aff9f3ef6d4acb6cdda7b5f592ef5c67c8f095dc0d8e06ce1c3e/aiohttp-3.13.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:f48a2c26333659101ef214907d29a76fe22ad7e912aa1e40aeffdff5e8180977", size = 1678675 }, + { url = "https://files.pythonhosted.org/packages/38/b9/7f3e32a81c08b6d29ea15060c377e1f038ad96cd9923a85f30e817afff22/aiohttp-3.13.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:f1dfad638b9c91ff225162b2824db0e99ae2d1abe0dc7272b5919701f0a1e685", size = 1726829 }, + { url = "https://files.pythonhosted.org/packages/23/ce/610b1f77525a0a46639aea91377b12348e9f9412cc5ddcb17502aa4681c7/aiohttp-3.13.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:8fa09ab6dd567cb105db4e8ac4d60f377a7a94f67cf669cac79982f626360f32", size = 1542985 }, + { url = "https://files.pythonhosted.org/packages/53/39/3ac8dfdad5de38c401846fa071fcd24cb3b88ccfb024854df6cbd9b4a07e/aiohttp-3.13.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:4159fae827f9b5f655538a4f99b7cbc3a2187e5ca2eee82f876ef1da802ccfa9", size = 1741556 }, + { url = "https://files.pythonhosted.org/packages/2a/48/b1948b74fea7930b0f29595d1956842324336de200593d49a51a40607fdc/aiohttp-3.13.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ad671118c19e9cfafe81a7a05c294449fe0ebb0d0c6d5bb445cd2190023f5cef", size = 1696175 }, + { url = "https://files.pythonhosted.org/packages/96/26/063bba38e4b27b640f56cc89fe83cc3546a7ae162c2e30ca345f0ccdc3d1/aiohttp-3.13.1-cp314-cp314-win32.whl", hash = "sha256:c5c970c148c48cf6acb65224ca3c87a47f74436362dde75c27bc44155ccf7dfc", size = 430254 }, + { url = "https://files.pythonhosted.org/packages/88/aa/25fd764384dc4eab714023112d3548a8dd69a058840d61d816ea736097a2/aiohttp-3.13.1-cp314-cp314-win_amd64.whl", hash = "sha256:748a00167b7a88385756fa615417d24081cba7e58c8727d2e28817068b97c18c", size = 456256 }, + { url = "https://files.pythonhosted.org/packages/d4/9f/9ba6059de4bad25c71cd88e3da53f93e9618ea369cf875c9f924b1c167e2/aiohttp-3.13.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:390b73e99d7a1f0f658b3f626ba345b76382f3edc65f49d6385e326e777ed00e", size = 765956 }, + { url = "https://files.pythonhosted.org/packages/1f/30/b86da68b494447d3060f45c7ebb461347535dab4af9162a9267d9d86ca31/aiohttp-3.13.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:27e83abb330e687e019173d8fc1fd6a1cf471769624cf89b1bb49131198a810a", size = 503206 }, + { url = "https://files.pythonhosted.org/packages/c1/21/d27a506552843ff9eeb9fcc2d45f943b09eefdfdf205aab044f4f1f39f6a/aiohttp-3.13.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2b20eed07131adbf3e873e009c2869b16a579b236e9d4b2f211bf174d8bef44a", size = 507719 }, + { url = "https://files.pythonhosted.org/packages/58/23/4042230ec7e4edc7ba43d0342b5a3d2fe0222ca046933c4251a35aaf17f5/aiohttp-3.13.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:58fee9ef8477fd69e823b92cfd1f590ee388521b5ff8f97f3497e62ee0656212", size = 1862758 }, + { url = "https://files.pythonhosted.org/packages/df/88/525c45bea7cbb9f65df42cadb4ff69f6a0dbf95931b0ff7d1fdc40a1cb5f/aiohttp-3.13.1-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:1f62608fcb7b3d034d5e9496bea52d94064b7b62b06edba82cd38191336bbeda", size = 1717790 }, + { url = "https://files.pythonhosted.org/packages/1d/80/21e9b5eb77df352a5788713f37359b570a793f0473f3a72db2e46df379b9/aiohttp-3.13.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:fdc4d81c3dfc999437f23e36d197e8b557a3f779625cd13efe563a9cfc2ce712", size = 1842088 }, + { url = "https://files.pythonhosted.org/packages/d2/bf/d1738f6d63fe8b2a0ad49533911b3347f4953cd001bf3223cb7b61f18dff/aiohttp-3.13.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:601d7ec812f746fd80ff8af38eeb3f196e1bab4a4d39816ccbc94c222d23f1d0", size = 1934292 }, + { url = "https://files.pythonhosted.org/packages/04/e6/26cab509b42610ca49573f2fc2867810f72bd6a2070182256c31b14f2e98/aiohttp-3.13.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:47c3f21c469b840d9609089435c0d9918ae89f41289bf7cc4afe5ff7af5458db", size = 1791328 }, + { url = "https://files.pythonhosted.org/packages/8a/6d/baf7b462852475c9d045bee8418d9cdf280efb687752b553e82d0c58bcc2/aiohttp-3.13.1-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d6c6cdc0750db88520332d4aaa352221732b0cafe89fd0e42feec7cb1b5dc236", size = 1622663 }, + { url = "https://files.pythonhosted.org/packages/c8/48/396a97318af9b5f4ca8b3dc14a67976f71c6400a9609c622f96da341453f/aiohttp-3.13.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:58a12299eeb1fca2414ee2bc345ac69b0f765c20b82c3ab2a75d91310d95a9f6", size = 1787791 }, + { url = "https://files.pythonhosted.org/packages/a8/e2/6925f6784134ce3ff3ce1a8502ab366432a3b5605387618c1a939ce778d9/aiohttp-3.13.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:0989cbfc195a4de1bb48f08454ef1cb47424b937e53ed069d08404b9d3c7aea1", size = 1775459 }, + { url = "https://files.pythonhosted.org/packages/c3/e3/b372047ba739fc39f199b99290c4cc5578ce5fd125f69168c967dac44021/aiohttp-3.13.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:feb5ee664300e2435e0d1bc3443a98925013dfaf2cae9699c1f3606b88544898", size = 1789250 }, + { url = "https://files.pythonhosted.org/packages/02/8c/9f48b93d7d57fc9ef2ad4adace62e4663ea1ce1753806c4872fb36b54c39/aiohttp-3.13.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:58a6f8702da0c3606fb5cf2e669cce0ca681d072fe830968673bb4c69eb89e88", size = 1616139 }, + { url = "https://files.pythonhosted.org/packages/5c/c6/c64e39d61aaa33d7de1be5206c0af3ead4b369bf975dac9fdf907a4291c1/aiohttp-3.13.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:a417ceb433b9d280e2368ffea22d4bc6e3e0d894c4bc7768915124d57d0964b6", size = 1815829 }, + { url = "https://files.pythonhosted.org/packages/22/75/e19e93965ea675f1151753b409af97a14f1d888588a555e53af1e62b83eb/aiohttp-3.13.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:8ac8854f7b0466c5d6a9ea49249b3f6176013859ac8f4bb2522ad8ed6b94ded2", size = 1760923 }, + { url = "https://files.pythonhosted.org/packages/6c/a4/06ed38f1dabd98ea136fd116cba1d02c9b51af5a37d513b6850a9a567d86/aiohttp-3.13.1-cp314-cp314t-win32.whl", hash = "sha256:be697a5aeff42179ed13b332a411e674994bcd406c81642d014ace90bf4bb968", size = 463318 }, + { url = "https://files.pythonhosted.org/packages/04/0f/27e4fdde899e1e90e35eeff56b54ed63826435ad6cdb06b09ed312d1b3fa/aiohttp-3.13.1-cp314-cp314t-win_amd64.whl", hash = "sha256:f1d6aa90546a4e8f20c3500cb68ab14679cd91f927fa52970035fd3207dfb3da", size = 496721 }, ] [[package]] @@ -244,7 +244,7 @@ wheels = [ [[package]] name = "anthropic" -version = "0.69.0" +version = "0.71.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, @@ -256,9 +256,9 @@ dependencies = [ { name = "sniffio" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c8/9d/9ad1778b95f15c5b04e7d328c1b5f558f1e893857b7c33cd288c19c0057a/anthropic-0.69.0.tar.gz", hash = "sha256:c604d287f4d73640f40bd2c0f3265a2eb6ce034217ead0608f6b07a8bc5ae5f2", size = 480622 } +sdist = { url = "https://files.pythonhosted.org/packages/82/4f/70682b068d897841f43223df82d96ec1d617435a8b759c4a2d901a50158b/anthropic-0.71.0.tar.gz", hash = "sha256:eb8e6fa86d049061b3ef26eb4cbae0174ebbff21affa6de7b3098da857d8de6a", size = 489102 } wheels = [ - { url = "https://files.pythonhosted.org/packages/9b/38/75129688de5637eb5b383e5f2b1570a5cc3aecafa4de422da8eea4b90a6c/anthropic-0.69.0-py3-none-any.whl", hash = "sha256:1f73193040f33f11e27c2cd6ec25f24fe7c3f193dc1c5cde6b7a08b18a16bcc5", size = 337265 }, + { url = "https://files.pythonhosted.org/packages/5d/77/073e8ac488f335aec7001952825275582fb8f433737e90f24eeef9d878f6/anthropic-0.71.0-py3-none-any.whl", hash = "sha256:85c5015fcdbdc728390f11b17642a65a4365d03b12b799b18b6cc57e71fdb327", size = 355035 }, ] [[package]] @@ -463,11 +463,11 @@ wheels = [ [[package]] name = "cachetools" -version = "6.2.0" +version = "6.2.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9d/61/e4fad8155db4a04bfb4734c7c8ff0882f078f24294d42798b3568eb63bff/cachetools-6.2.0.tar.gz", hash = "sha256:38b328c0889450f05f5e120f56ab68c8abaf424e1275522b138ffc93253f7e32", size = 30988 } +sdist = { url = "https://files.pythonhosted.org/packages/cc/7e/b975b5814bd36faf009faebe22c1072a1fa1168db34d285ef0ba071ad78c/cachetools-6.2.1.tar.gz", hash = "sha256:3f391e4bd8f8bf0931169baf7456cc822705f4e2a31f840d218f445b9a854201", size = 31325 } wheels = [ - { url = "https://files.pythonhosted.org/packages/6c/56/3124f61d37a7a4e7cc96afc5492c78ba0cb551151e530b54669ddd1436ef/cachetools-6.2.0-py3-none-any.whl", hash = "sha256:1c76a8960c0041fcc21097e357f882197c79da0dbff766e7317890a65d7d8ba6", size = 11276 }, + { url = "https://files.pythonhosted.org/packages/96/c5/1e741d26306c42e2bf6ab740b2202872727e0f606033c9dd713f8b93f5a8/cachetools-6.2.1-py3-none-any.whl", hash = "sha256:09868944b6dde876dfd44e1d47e18484541eaf12f26f29b7af91b26cc892d701", size = 11280 }, ] [[package]] @@ -602,66 +602,91 @@ wheels = [ [[package]] name = "charset-normalizer" -version = "3.4.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/83/2d/5fd176ceb9b2fc619e63405525573493ca23441330fcdaee6bef9460e924/charset_normalizer-3.4.3.tar.gz", hash = "sha256:6fce4b8500244f6fcb71465d4a4930d132ba9ab8e71a7859e6a5d59851068d14", size = 122371 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d6/98/f3b8013223728a99b908c9344da3aa04ee6e3fa235f19409033eda92fb78/charset_normalizer-3.4.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:fb7f67a1bfa6e40b438170ebdc8158b78dc465a5a67b6dde178a46987b244a72", size = 207695 }, - { url = "https://files.pythonhosted.org/packages/21/40/5188be1e3118c82dcb7c2a5ba101b783822cfb413a0268ed3be0468532de/charset_normalizer-3.4.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cc9370a2da1ac13f0153780040f465839e6cccb4a1e44810124b4e22483c93fe", size = 147153 }, - { url = "https://files.pythonhosted.org/packages/37/60/5d0d74bc1e1380f0b72c327948d9c2aca14b46a9efd87604e724260f384c/charset_normalizer-3.4.3-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:07a0eae9e2787b586e129fdcbe1af6997f8d0e5abaa0bc98c0e20e124d67e601", size = 160428 }, - { url = "https://files.pythonhosted.org/packages/85/9a/d891f63722d9158688de58d050c59dc3da560ea7f04f4c53e769de5140f5/charset_normalizer-3.4.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:74d77e25adda8581ffc1c720f1c81ca082921329452eba58b16233ab1842141c", size = 157627 }, - { url = "https://files.pythonhosted.org/packages/65/1a/7425c952944a6521a9cfa7e675343f83fd82085b8af2b1373a2409c683dc/charset_normalizer-3.4.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d0e909868420b7049dafd3a31d45125b31143eec59235311fc4c57ea26a4acd2", size = 152388 }, - { url = "https://files.pythonhosted.org/packages/f0/c9/a2c9c2a355a8594ce2446085e2ec97fd44d323c684ff32042e2a6b718e1d/charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c6f162aabe9a91a309510d74eeb6507fab5fff92337a15acbe77753d88d9dcf0", size = 150077 }, - { url = "https://files.pythonhosted.org/packages/3b/38/20a1f44e4851aa1c9105d6e7110c9d020e093dfa5836d712a5f074a12bf7/charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:4ca4c094de7771a98d7fbd67d9e5dbf1eb73efa4f744a730437d8a3a5cf994f0", size = 161631 }, - { url = "https://files.pythonhosted.org/packages/a4/fa/384d2c0f57edad03d7bec3ebefb462090d8905b4ff5a2d2525f3bb711fac/charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:02425242e96bcf29a49711b0ca9f37e451da7c70562bc10e8ed992a5a7a25cc0", size = 159210 }, - { url = "https://files.pythonhosted.org/packages/33/9e/eca49d35867ca2db336b6ca27617deed4653b97ebf45dfc21311ce473c37/charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:78deba4d8f9590fe4dae384aeff04082510a709957e968753ff3c48399f6f92a", size = 153739 }, - { url = "https://files.pythonhosted.org/packages/2a/91/26c3036e62dfe8de8061182d33be5025e2424002125c9500faff74a6735e/charset_normalizer-3.4.3-cp310-cp310-win32.whl", hash = "sha256:d79c198e27580c8e958906f803e63cddb77653731be08851c7df0b1a14a8fc0f", size = 99825 }, - { url = "https://files.pythonhosted.org/packages/e2/c6/f05db471f81af1fa01839d44ae2a8bfeec8d2a8b4590f16c4e7393afd323/charset_normalizer-3.4.3-cp310-cp310-win_amd64.whl", hash = "sha256:c6e490913a46fa054e03699c70019ab869e990270597018cef1d8562132c2669", size = 107452 }, - { url = "https://files.pythonhosted.org/packages/7f/b5/991245018615474a60965a7c9cd2b4efbaabd16d582a5547c47ee1c7730b/charset_normalizer-3.4.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:b256ee2e749283ef3ddcff51a675ff43798d92d746d1a6e4631bf8c707d22d0b", size = 204483 }, - { url = "https://files.pythonhosted.org/packages/c7/2a/ae245c41c06299ec18262825c1569c5d3298fc920e4ddf56ab011b417efd/charset_normalizer-3.4.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:13faeacfe61784e2559e690fc53fa4c5ae97c6fcedb8eb6fb8d0a15b475d2c64", size = 145520 }, - { url = "https://files.pythonhosted.org/packages/3a/a4/b3b6c76e7a635748c4421d2b92c7b8f90a432f98bda5082049af37ffc8e3/charset_normalizer-3.4.3-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:00237675befef519d9af72169d8604a067d92755e84fe76492fef5441db05b91", size = 158876 }, - { url = "https://files.pythonhosted.org/packages/e2/e6/63bb0e10f90a8243c5def74b5b105b3bbbfb3e7bb753915fe333fb0c11ea/charset_normalizer-3.4.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:585f3b2a80fbd26b048a0be90c5aae8f06605d3c92615911c3a2b03a8a3b796f", size = 156083 }, - { url = "https://files.pythonhosted.org/packages/87/df/b7737ff046c974b183ea9aa111b74185ac8c3a326c6262d413bd5a1b8c69/charset_normalizer-3.4.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e78314bdc32fa80696f72fa16dc61168fda4d6a0c014e0380f9d02f0e5d8a07", size = 150295 }, - { url = "https://files.pythonhosted.org/packages/61/f1/190d9977e0084d3f1dc169acd060d479bbbc71b90bf3e7bf7b9927dec3eb/charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:96b2b3d1a83ad55310de8c7b4a2d04d9277d5591f40761274856635acc5fcb30", size = 148379 }, - { url = "https://files.pythonhosted.org/packages/4c/92/27dbe365d34c68cfe0ca76f1edd70e8705d82b378cb54ebbaeabc2e3029d/charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:939578d9d8fd4299220161fdd76e86c6a251987476f5243e8864a7844476ba14", size = 160018 }, - { url = "https://files.pythonhosted.org/packages/99/04/baae2a1ea1893a01635d475b9261c889a18fd48393634b6270827869fa34/charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:fd10de089bcdcd1be95a2f73dbe6254798ec1bda9f450d5828c96f93e2536b9c", size = 157430 }, - { url = "https://files.pythonhosted.org/packages/2f/36/77da9c6a328c54d17b960c89eccacfab8271fdaaa228305330915b88afa9/charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1e8ac75d72fa3775e0b7cb7e4629cec13b7514d928d15ef8ea06bca03ef01cae", size = 151600 }, - { url = "https://files.pythonhosted.org/packages/64/d4/9eb4ff2c167edbbf08cdd28e19078bf195762e9bd63371689cab5ecd3d0d/charset_normalizer-3.4.3-cp311-cp311-win32.whl", hash = "sha256:6cf8fd4c04756b6b60146d98cd8a77d0cdae0e1ca20329da2ac85eed779b6849", size = 99616 }, - { url = "https://files.pythonhosted.org/packages/f4/9c/996a4a028222e7761a96634d1820de8a744ff4327a00ada9c8942033089b/charset_normalizer-3.4.3-cp311-cp311-win_amd64.whl", hash = "sha256:31a9a6f775f9bcd865d88ee350f0ffb0e25936a7f930ca98995c05abf1faf21c", size = 107108 }, - { url = "https://files.pythonhosted.org/packages/e9/5e/14c94999e418d9b87682734589404a25854d5f5d0408df68bc15b6ff54bb/charset_normalizer-3.4.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e28e334d3ff134e88989d90ba04b47d84382a828c061d0d1027b1b12a62b39b1", size = 205655 }, - { url = "https://files.pythonhosted.org/packages/7d/a8/c6ec5d389672521f644505a257f50544c074cf5fc292d5390331cd6fc9c3/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0cacf8f7297b0c4fcb74227692ca46b4a5852f8f4f24b3c766dd94a1075c4884", size = 146223 }, - { url = "https://files.pythonhosted.org/packages/fc/eb/a2ffb08547f4e1e5415fb69eb7db25932c52a52bed371429648db4d84fb1/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c6fd51128a41297f5409deab284fecbe5305ebd7e5a1f959bee1c054622b7018", size = 159366 }, - { url = "https://files.pythonhosted.org/packages/82/10/0fd19f20c624b278dddaf83b8464dcddc2456cb4b02bb902a6da126b87a1/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3cfb2aad70f2c6debfbcb717f23b7eb55febc0bb23dcffc0f076009da10c6392", size = 157104 }, - { url = "https://files.pythonhosted.org/packages/16/ab/0233c3231af734f5dfcf0844aa9582d5a1466c985bbed6cedab85af9bfe3/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1606f4a55c0fd363d754049cdf400175ee96c992b1f8018b993941f221221c5f", size = 151830 }, - { url = "https://files.pythonhosted.org/packages/ae/02/e29e22b4e02839a0e4a06557b1999d0a47db3567e82989b5bb21f3fbbd9f/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:027b776c26d38b7f15b26a5da1044f376455fb3766df8fc38563b4efbc515154", size = 148854 }, - { url = "https://files.pythonhosted.org/packages/05/6b/e2539a0a4be302b481e8cafb5af8792da8093b486885a1ae4d15d452bcec/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:42e5088973e56e31e4fa58eb6bd709e42fc03799c11c42929592889a2e54c491", size = 160670 }, - { url = "https://files.pythonhosted.org/packages/31/e7/883ee5676a2ef217a40ce0bffcc3d0dfbf9e64cbcfbdf822c52981c3304b/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:cc34f233c9e71701040d772aa7490318673aa7164a0efe3172b2981218c26d93", size = 158501 }, - { url = "https://files.pythonhosted.org/packages/c1/35/6525b21aa0db614cf8b5792d232021dca3df7f90a1944db934efa5d20bb1/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:320e8e66157cc4e247d9ddca8e21f427efc7a04bbd0ac8a9faf56583fa543f9f", size = 153173 }, - { url = "https://files.pythonhosted.org/packages/50/ee/f4704bad8201de513fdc8aac1cabc87e38c5818c93857140e06e772b5892/charset_normalizer-3.4.3-cp312-cp312-win32.whl", hash = "sha256:fb6fecfd65564f208cbf0fba07f107fb661bcd1a7c389edbced3f7a493f70e37", size = 99822 }, - { url = "https://files.pythonhosted.org/packages/39/f5/3b3836ca6064d0992c58c7561c6b6eee1b3892e9665d650c803bd5614522/charset_normalizer-3.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:86df271bf921c2ee3818f0522e9a5b8092ca2ad8b065ece5d7d9d0e9f4849bcc", size = 107543 }, - { url = "https://files.pythonhosted.org/packages/65/ca/2135ac97709b400c7654b4b764daf5c5567c2da45a30cdd20f9eefe2d658/charset_normalizer-3.4.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:14c2a87c65b351109f6abfc424cab3927b3bdece6f706e4d12faaf3d52ee5efe", size = 205326 }, - { url = "https://files.pythonhosted.org/packages/71/11/98a04c3c97dd34e49c7d247083af03645ca3730809a5509443f3c37f7c99/charset_normalizer-3.4.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:41d1fc408ff5fdfb910200ec0e74abc40387bccb3252f3f27c0676731df2b2c8", size = 146008 }, - { url = "https://files.pythonhosted.org/packages/60/f5/4659a4cb3c4ec146bec80c32d8bb16033752574c20b1252ee842a95d1a1e/charset_normalizer-3.4.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1bb60174149316da1c35fa5233681f7c0f9f514509b8e399ab70fea5f17e45c9", size = 159196 }, - { url = "https://files.pythonhosted.org/packages/86/9e/f552f7a00611f168b9a5865a1414179b2c6de8235a4fa40189f6f79a1753/charset_normalizer-3.4.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:30d006f98569de3459c2fc1f2acde170b7b2bd265dc1943e87e1a4efe1b67c31", size = 156819 }, - { url = "https://files.pythonhosted.org/packages/7e/95/42aa2156235cbc8fa61208aded06ef46111c4d3f0de233107b3f38631803/charset_normalizer-3.4.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:416175faf02e4b0810f1f38bcb54682878a4af94059a1cd63b8747244420801f", size = 151350 }, - { url = "https://files.pythonhosted.org/packages/c2/a9/3865b02c56f300a6f94fc631ef54f0a8a29da74fb45a773dfd3dcd380af7/charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6aab0f181c486f973bc7262a97f5aca3ee7e1437011ef0c2ec04b5a11d16c927", size = 148644 }, - { url = "https://files.pythonhosted.org/packages/77/d9/cbcf1a2a5c7d7856f11e7ac2d782aec12bdfea60d104e60e0aa1c97849dc/charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:fdabf8315679312cfa71302f9bd509ded4f2f263fb5b765cf1433b39106c3cc9", size = 160468 }, - { url = "https://files.pythonhosted.org/packages/f6/42/6f45efee8697b89fda4d50580f292b8f7f9306cb2971d4b53f8914e4d890/charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:bd28b817ea8c70215401f657edef3a8aa83c29d447fb0b622c35403780ba11d5", size = 158187 }, - { url = "https://files.pythonhosted.org/packages/70/99/f1c3bdcfaa9c45b3ce96f70b14f070411366fa19549c1d4832c935d8e2c3/charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:18343b2d246dc6761a249ba1fb13f9ee9a2bcd95decc767319506056ea4ad4dc", size = 152699 }, - { url = "https://files.pythonhosted.org/packages/a3/ad/b0081f2f99a4b194bcbb1934ef3b12aa4d9702ced80a37026b7607c72e58/charset_normalizer-3.4.3-cp313-cp313-win32.whl", hash = "sha256:6fb70de56f1859a3f71261cbe41005f56a7842cc348d3aeb26237560bfa5e0ce", size = 99580 }, - { url = "https://files.pythonhosted.org/packages/9a/8f/ae790790c7b64f925e5c953b924aaa42a243fb778fed9e41f147b2a5715a/charset_normalizer-3.4.3-cp313-cp313-win_amd64.whl", hash = "sha256:cf1ebb7d78e1ad8ec2a8c4732c7be2e736f6e5123a4146c5b89c9d1f585f8cef", size = 107366 }, - { url = "https://files.pythonhosted.org/packages/8e/91/b5a06ad970ddc7a0e513112d40113e834638f4ca1120eb727a249fb2715e/charset_normalizer-3.4.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3cd35b7e8aedeb9e34c41385fda4f73ba609e561faedfae0a9e75e44ac558a15", size = 204342 }, - { url = "https://files.pythonhosted.org/packages/ce/ec/1edc30a377f0a02689342f214455c3f6c2fbedd896a1d2f856c002fc3062/charset_normalizer-3.4.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b89bc04de1d83006373429975f8ef9e7932534b8cc9ca582e4db7d20d91816db", size = 145995 }, - { url = "https://files.pythonhosted.org/packages/17/e5/5e67ab85e6d22b04641acb5399c8684f4d37caf7558a53859f0283a650e9/charset_normalizer-3.4.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2001a39612b241dae17b4687898843f254f8748b796a2e16f1051a17078d991d", size = 158640 }, - { url = "https://files.pythonhosted.org/packages/f1/e5/38421987f6c697ee3722981289d554957c4be652f963d71c5e46a262e135/charset_normalizer-3.4.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8dcfc373f888e4fb39a7bc57e93e3b845e7f462dacc008d9749568b1c4ece096", size = 156636 }, - { url = "https://files.pythonhosted.org/packages/a0/e4/5a075de8daa3ec0745a9a3b54467e0c2967daaaf2cec04c845f73493e9a1/charset_normalizer-3.4.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:18b97b8404387b96cdbd30ad660f6407799126d26a39ca65729162fd810a99aa", size = 150939 }, - { url = "https://files.pythonhosted.org/packages/02/f7/3611b32318b30974131db62b4043f335861d4d9b49adc6d57c1149cc49d4/charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ccf600859c183d70eb47e05a44cd80a4ce77394d1ac0f79dbd2dd90a69a3a049", size = 148580 }, - { url = "https://files.pythonhosted.org/packages/7e/61/19b36f4bd67f2793ab6a99b979b4e4f3d8fc754cbdffb805335df4337126/charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:53cd68b185d98dde4ad8990e56a58dea83a4162161b1ea9272e5c9182ce415e0", size = 159870 }, - { url = "https://files.pythonhosted.org/packages/06/57/84722eefdd338c04cf3030ada66889298eaedf3e7a30a624201e0cbe424a/charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:30a96e1e1f865f78b030d65241c1ee850cdf422d869e9028e2fc1d5e4db73b92", size = 157797 }, - { url = "https://files.pythonhosted.org/packages/72/2a/aff5dd112b2f14bcc3462c312dce5445806bfc8ab3a7328555da95330e4b/charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d716a916938e03231e86e43782ca7878fb602a125a91e7acb8b5112e2e96ac16", size = 152224 }, - { url = "https://files.pythonhosted.org/packages/b7/8c/9839225320046ed279c6e839d51f028342eb77c91c89b8ef2549f951f3ec/charset_normalizer-3.4.3-cp314-cp314-win32.whl", hash = "sha256:c6dbd0ccdda3a2ba7c2ecd9d77b37f3b5831687d8dc1b6ca5f56a4880cc7b7ce", size = 100086 }, - { url = "https://files.pythonhosted.org/packages/ee/7a/36fbcf646e41f710ce0a563c1c9a343c6edf9be80786edeb15b6f62e17db/charset_normalizer-3.4.3-cp314-cp314-win_amd64.whl", hash = "sha256:73dc19b562516fc9bcf6e5d6e596df0b4eb98d87e4f79f3ae71840e6ed21361c", size = 107400 }, - { url = "https://files.pythonhosted.org/packages/8a/1f/f041989e93b001bc4e44bb1669ccdcf54d3f00e628229a85b08d330615c5/charset_normalizer-3.4.3-py3-none-any.whl", hash = "sha256:ce571ab16d890d23b5c278547ba694193a45011ff86a9162a71307ed9f86759a", size = 53175 }, +version = "3.4.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/13/69/33ddede1939fdd074bce5434295f38fae7136463422fe4fd3e0e89b98062/charset_normalizer-3.4.4.tar.gz", hash = "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a", size = 129418 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1f/b8/6d51fc1d52cbd52cd4ccedd5b5b2f0f6a11bbf6765c782298b0f3e808541/charset_normalizer-3.4.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e824f1492727fa856dd6eda4f7cee25f8518a12f3c4a56a74e8095695089cf6d", size = 209709 }, + { url = "https://files.pythonhosted.org/packages/5c/af/1f9d7f7faafe2ddfb6f72a2e07a548a629c61ad510fe60f9630309908fef/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4bd5d4137d500351a30687c2d3971758aac9a19208fc110ccb9d7188fbe709e8", size = 148814 }, + { url = "https://files.pythonhosted.org/packages/79/3d/f2e3ac2bbc056ca0c204298ea4e3d9db9b4afe437812638759db2c976b5f/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:027f6de494925c0ab2a55eab46ae5129951638a49a34d87f4c3eda90f696b4ad", size = 144467 }, + { url = "https://files.pythonhosted.org/packages/ec/85/1bf997003815e60d57de7bd972c57dc6950446a3e4ccac43bc3070721856/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f820802628d2694cb7e56db99213f930856014862f3fd943d290ea8438d07ca8", size = 162280 }, + { url = "https://files.pythonhosted.org/packages/3e/8e/6aa1952f56b192f54921c436b87f2aaf7c7a7c3d0d1a765547d64fd83c13/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:798d75d81754988d2565bff1b97ba5a44411867c0cf32b77a7e8f8d84796b10d", size = 159454 }, + { url = "https://files.pythonhosted.org/packages/36/3b/60cbd1f8e93aa25d1c669c649b7a655b0b5fb4c571858910ea9332678558/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d1bb833febdff5c8927f922386db610b49db6e0d4f4ee29601d71e7c2694313", size = 153609 }, + { url = "https://files.pythonhosted.org/packages/64/91/6a13396948b8fd3c4b4fd5bc74d045f5637d78c9675585e8e9fbe5636554/charset_normalizer-3.4.4-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9cd98cdc06614a2f768d2b7286d66805f94c48cde050acdbbb7db2600ab3197e", size = 151849 }, + { url = "https://files.pythonhosted.org/packages/b7/7a/59482e28b9981d105691e968c544cc0df3b7d6133152fb3dcdc8f135da7a/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:077fbb858e903c73f6c9db43374fd213b0b6a778106bc7032446a8e8b5b38b93", size = 151586 }, + { url = "https://files.pythonhosted.org/packages/92/59/f64ef6a1c4bdd2baf892b04cd78792ed8684fbc48d4c2afe467d96b4df57/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:244bfb999c71b35de57821b8ea746b24e863398194a4014e4c76adc2bbdfeff0", size = 145290 }, + { url = "https://files.pythonhosted.org/packages/6b/63/3bf9f279ddfa641ffa1962b0db6a57a9c294361cc2f5fcac997049a00e9c/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:64b55f9dce520635f018f907ff1b0df1fdc31f2795a922fb49dd14fbcdf48c84", size = 163663 }, + { url = "https://files.pythonhosted.org/packages/ed/09/c9e38fc8fa9e0849b172b581fd9803bdf6e694041127933934184e19f8c3/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:faa3a41b2b66b6e50f84ae4a68c64fcd0c44355741c6374813a800cd6695db9e", size = 151964 }, + { url = "https://files.pythonhosted.org/packages/d2/d1/d28b747e512d0da79d8b6a1ac18b7ab2ecfd81b2944c4c710e166d8dd09c/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:6515f3182dbe4ea06ced2d9e8666d97b46ef4c75e326b79bb624110f122551db", size = 161064 }, + { url = "https://files.pythonhosted.org/packages/bb/9a/31d62b611d901c3b9e5500c36aab0ff5eb442043fb3a1c254200d3d397d9/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cc00f04ed596e9dc0da42ed17ac5e596c6ccba999ba6bd92b0e0aef2f170f2d6", size = 155015 }, + { url = "https://files.pythonhosted.org/packages/1f/f3/107e008fa2bff0c8b9319584174418e5e5285fef32f79d8ee6a430d0039c/charset_normalizer-3.4.4-cp310-cp310-win32.whl", hash = "sha256:f34be2938726fc13801220747472850852fe6b1ea75869a048d6f896838c896f", size = 99792 }, + { url = "https://files.pythonhosted.org/packages/eb/66/e396e8a408843337d7315bab30dbf106c38966f1819f123257f5520f8a96/charset_normalizer-3.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:a61900df84c667873b292c3de315a786dd8dac506704dea57bc957bd31e22c7d", size = 107198 }, + { url = "https://files.pythonhosted.org/packages/b5/58/01b4f815bf0312704c267f2ccb6e5d42bcc7752340cd487bc9f8c3710597/charset_normalizer-3.4.4-cp310-cp310-win_arm64.whl", hash = "sha256:cead0978fc57397645f12578bfd2d5ea9138ea0fac82b2f63f7f7c6877986a69", size = 100262 }, + { url = "https://files.pythonhosted.org/packages/ed/27/c6491ff4954e58a10f69ad90aca8a1b6fe9c5d3c6f380907af3c37435b59/charset_normalizer-3.4.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6e1fcf0720908f200cd21aa4e6750a48ff6ce4afe7ff5a79a90d5ed8a08296f8", size = 206988 }, + { url = "https://files.pythonhosted.org/packages/94/59/2e87300fe67ab820b5428580a53cad894272dbb97f38a7a814a2a1ac1011/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f819d5fe9234f9f82d75bdfa9aef3a3d72c4d24a6e57aeaebba32a704553aa0", size = 147324 }, + { url = "https://files.pythonhosted.org/packages/07/fb/0cf61dc84b2b088391830f6274cb57c82e4da8bbc2efeac8c025edb88772/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a59cb51917aa591b1c4e6a43c132f0cdc3c76dbad6155df4e28ee626cc77a0a3", size = 142742 }, + { url = "https://files.pythonhosted.org/packages/62/8b/171935adf2312cd745d290ed93cf16cf0dfe320863ab7cbeeae1dcd6535f/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8ef3c867360f88ac904fd3f5e1f902f13307af9052646963ee08ff4f131adafc", size = 160863 }, + { url = "https://files.pythonhosted.org/packages/09/73/ad875b192bda14f2173bfc1bc9a55e009808484a4b256748d931b6948442/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d9e45d7faa48ee908174d8fe84854479ef838fc6a705c9315372eacbc2f02897", size = 157837 }, + { url = "https://files.pythonhosted.org/packages/6d/fc/de9cce525b2c5b94b47c70a4b4fb19f871b24995c728e957ee68ab1671ea/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:840c25fb618a231545cbab0564a799f101b63b9901f2569faecd6b222ac72381", size = 151550 }, + { url = "https://files.pythonhosted.org/packages/55/c2/43edd615fdfba8c6f2dfbd459b25a6b3b551f24ea21981e23fb768503ce1/charset_normalizer-3.4.4-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ca5862d5b3928c4940729dacc329aa9102900382fea192fc5e52eb69d6093815", size = 149162 }, + { url = "https://files.pythonhosted.org/packages/03/86/bde4ad8b4d0e9429a4e82c1e8f5c659993a9a863ad62c7df05cf7b678d75/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d9c7f57c3d666a53421049053eaacdd14bbd0a528e2186fcb2e672effd053bb0", size = 150019 }, + { url = "https://files.pythonhosted.org/packages/1f/86/a151eb2af293a7e7bac3a739b81072585ce36ccfb4493039f49f1d3cae8c/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:277e970e750505ed74c832b4bf75dac7476262ee2a013f5574dd49075879e161", size = 143310 }, + { url = "https://files.pythonhosted.org/packages/b5/fe/43dae6144a7e07b87478fdfc4dbe9efd5defb0e7ec29f5f58a55aeef7bf7/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:31fd66405eaf47bb62e8cd575dc621c56c668f27d46a61d975a249930dd5e2a4", size = 162022 }, + { url = "https://files.pythonhosted.org/packages/80/e6/7aab83774f5d2bca81f42ac58d04caf44f0cc2b65fc6db2b3b2e8a05f3b3/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:0d3d8f15c07f86e9ff82319b3d9ef6f4bf907608f53fe9d92b28ea9ae3d1fd89", size = 149383 }, + { url = "https://files.pythonhosted.org/packages/4f/e8/b289173b4edae05c0dde07f69f8db476a0b511eac556dfe0d6bda3c43384/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:9f7fcd74d410a36883701fafa2482a6af2ff5ba96b9a620e9e0721e28ead5569", size = 159098 }, + { url = "https://files.pythonhosted.org/packages/d8/df/fe699727754cae3f8478493c7f45f777b17c3ef0600e28abfec8619eb49c/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ebf3e58c7ec8a8bed6d66a75d7fb37b55e5015b03ceae72a8e7c74495551e224", size = 152991 }, + { url = "https://files.pythonhosted.org/packages/1a/86/584869fe4ddb6ffa3bd9f491b87a01568797fb9bd8933f557dba9771beaf/charset_normalizer-3.4.4-cp311-cp311-win32.whl", hash = "sha256:eecbc200c7fd5ddb9a7f16c7decb07b566c29fa2161a16cf67b8d068bd21690a", size = 99456 }, + { url = "https://files.pythonhosted.org/packages/65/f6/62fdd5feb60530f50f7e38b4f6a1d5203f4d16ff4f9f0952962c044e919a/charset_normalizer-3.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:5ae497466c7901d54b639cf42d5b8c1b6a4fead55215500d2f486d34db48d016", size = 106978 }, + { url = "https://files.pythonhosted.org/packages/7a/9d/0710916e6c82948b3be62d9d398cb4fcf4e97b56d6a6aeccd66c4b2f2bd5/charset_normalizer-3.4.4-cp311-cp311-win_arm64.whl", hash = "sha256:65e2befcd84bc6f37095f5961e68a6f077bf44946771354a28ad434c2cce0ae1", size = 99969 }, + { url = "https://files.pythonhosted.org/packages/f3/85/1637cd4af66fa687396e757dec650f28025f2a2f5a5531a3208dc0ec43f2/charset_normalizer-3.4.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0a98e6759f854bd25a58a73fa88833fba3b7c491169f86ce1180c948ab3fd394", size = 208425 }, + { url = "https://files.pythonhosted.org/packages/9d/6a/04130023fef2a0d9c62d0bae2649b69f7b7d8d24ea5536feef50551029df/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b5b290ccc2a263e8d185130284f8501e3e36c5e02750fc6b6bdeb2e9e96f1e25", size = 148162 }, + { url = "https://files.pythonhosted.org/packages/78/29/62328d79aa60da22c9e0b9a66539feae06ca0f5a4171ac4f7dc285b83688/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74bb723680f9f7a6234dcf67aea57e708ec1fbdf5699fb91dfd6f511b0a320ef", size = 144558 }, + { url = "https://files.pythonhosted.org/packages/86/bb/b32194a4bf15b88403537c2e120b817c61cd4ecffa9b6876e941c3ee38fe/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f1e34719c6ed0b92f418c7c780480b26b5d9c50349e9a9af7d76bf757530350d", size = 161497 }, + { url = "https://files.pythonhosted.org/packages/19/89/a54c82b253d5b9b111dc74aca196ba5ccfcca8242d0fb64146d4d3183ff1/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2437418e20515acec67d86e12bf70056a33abdacb5cb1655042f6538d6b085a8", size = 159240 }, + { url = "https://files.pythonhosted.org/packages/c0/10/d20b513afe03acc89ec33948320a5544d31f21b05368436d580dec4e234d/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11d694519d7f29d6cd09f6ac70028dba10f92f6cdd059096db198c283794ac86", size = 153471 }, + { url = "https://files.pythonhosted.org/packages/61/fa/fbf177b55bdd727010f9c0a3c49eefa1d10f960e5f09d1d887bf93c2e698/charset_normalizer-3.4.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ac1c4a689edcc530fc9d9aa11f5774b9e2f33f9a0c6a57864e90908f5208d30a", size = 150864 }, + { url = "https://files.pythonhosted.org/packages/05/12/9fbc6a4d39c0198adeebbde20b619790e9236557ca59fc40e0e3cebe6f40/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:21d142cc6c0ec30d2efee5068ca36c128a30b0f2c53c1c07bd78cb6bc1d3be5f", size = 150647 }, + { url = "https://files.pythonhosted.org/packages/ad/1f/6a9a593d52e3e8c5d2b167daf8c6b968808efb57ef4c210acb907c365bc4/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:5dbe56a36425d26d6cfb40ce79c314a2e4dd6211d51d6d2191c00bed34f354cc", size = 145110 }, + { url = "https://files.pythonhosted.org/packages/30/42/9a52c609e72471b0fc54386dc63c3781a387bb4fe61c20231a4ebcd58bdd/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5bfbb1b9acf3334612667b61bd3002196fe2a1eb4dd74d247e0f2a4d50ec9bbf", size = 162839 }, + { url = "https://files.pythonhosted.org/packages/c4/5b/c0682bbf9f11597073052628ddd38344a3d673fda35a36773f7d19344b23/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:d055ec1e26e441f6187acf818b73564e6e6282709e9bcb5b63f5b23068356a15", size = 150667 }, + { url = "https://files.pythonhosted.org/packages/e4/24/a41afeab6f990cf2daf6cb8c67419b63b48cf518e4f56022230840c9bfb2/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:af2d8c67d8e573d6de5bc30cdb27e9b95e49115cd9baad5ddbd1a6207aaa82a9", size = 160535 }, + { url = "https://files.pythonhosted.org/packages/2a/e5/6a4ce77ed243c4a50a1fecca6aaaab419628c818a49434be428fe24c9957/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:780236ac706e66881f3b7f2f32dfe90507a09e67d1d454c762cf642e6e1586e0", size = 154816 }, + { url = "https://files.pythonhosted.org/packages/a8/ef/89297262b8092b312d29cdb2517cb1237e51db8ecef2e9af5edbe7b683b1/charset_normalizer-3.4.4-cp312-cp312-win32.whl", hash = "sha256:5833d2c39d8896e4e19b689ffc198f08ea58116bee26dea51e362ecc7cd3ed26", size = 99694 }, + { url = "https://files.pythonhosted.org/packages/3d/2d/1e5ed9dd3b3803994c155cd9aacb60c82c331bad84daf75bcb9c91b3295e/charset_normalizer-3.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:a79cfe37875f822425b89a82333404539ae63dbdddf97f84dcbc3d339aae9525", size = 107131 }, + { url = "https://files.pythonhosted.org/packages/d0/d9/0ed4c7098a861482a7b6a95603edce4c0d9db2311af23da1fb2b75ec26fc/charset_normalizer-3.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:376bec83a63b8021bb5c8ea75e21c4ccb86e7e45ca4eb81146091b56599b80c3", size = 100390 }, + { url = "https://files.pythonhosted.org/packages/97/45/4b3a1239bbacd321068ea6e7ac28875b03ab8bc0aa0966452db17cd36714/charset_normalizer-3.4.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794", size = 208091 }, + { url = "https://files.pythonhosted.org/packages/7d/62/73a6d7450829655a35bb88a88fca7d736f9882a27eacdca2c6d505b57e2e/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b39f987ae8ccdf0d2642338faf2abb1862340facc796048b604ef14919e55ed", size = 147936 }, + { url = "https://files.pythonhosted.org/packages/89/c5/adb8c8b3d6625bef6d88b251bbb0d95f8205831b987631ab0c8bb5d937c2/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3162d5d8ce1bb98dd51af660f2121c55d0fa541b46dff7bb9b9f86ea1d87de72", size = 144180 }, + { url = "https://files.pythonhosted.org/packages/91/ed/9706e4070682d1cc219050b6048bfd293ccf67b3d4f5a4f39207453d4b99/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:81d5eb2a312700f4ecaa977a8235b634ce853200e828fbadf3a9c50bab278328", size = 161346 }, + { url = "https://files.pythonhosted.org/packages/d5/0d/031f0d95e4972901a2f6f09ef055751805ff541511dc1252ba3ca1f80cf5/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5bd2293095d766545ec1a8f612559f6b40abc0eb18bb2f5d1171872d34036ede", size = 158874 }, + { url = "https://files.pythonhosted.org/packages/f5/83/6ab5883f57c9c801ce5e5677242328aa45592be8a00644310a008d04f922/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8a8b89589086a25749f471e6a900d3f662d1d3b6e2e59dcecf787b1cc3a1894", size = 153076 }, + { url = "https://files.pythonhosted.org/packages/75/1e/5ff781ddf5260e387d6419959ee89ef13878229732732ee73cdae01800f2/charset_normalizer-3.4.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc7637e2f80d8530ee4a78e878bce464f70087ce73cf7c1caf142416923b98f1", size = 150601 }, + { url = "https://files.pythonhosted.org/packages/d7/57/71be810965493d3510a6ca79b90c19e48696fb1ff964da319334b12677f0/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f8bf04158c6b607d747e93949aa60618b61312fe647a6369f88ce2ff16043490", size = 150376 }, + { url = "https://files.pythonhosted.org/packages/e5/d5/c3d057a78c181d007014feb7e9f2e65905a6c4ef182c0ddf0de2924edd65/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:554af85e960429cf30784dd47447d5125aaa3b99a6f0683589dbd27e2f45da44", size = 144825 }, + { url = "https://files.pythonhosted.org/packages/e6/8c/d0406294828d4976f275ffbe66f00266c4b3136b7506941d87c00cab5272/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:74018750915ee7ad843a774364e13a3db91682f26142baddf775342c3f5b1133", size = 162583 }, + { url = "https://files.pythonhosted.org/packages/d7/24/e2aa1f18c8f15c4c0e932d9287b8609dd30ad56dbe41d926bd846e22fb8d/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c0463276121fdee9c49b98908b3a89c39be45d86d1dbaa22957e38f6321d4ce3", size = 150366 }, + { url = "https://files.pythonhosted.org/packages/e4/5b/1e6160c7739aad1e2df054300cc618b06bf784a7a164b0f238360721ab86/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:362d61fd13843997c1c446760ef36f240cf81d3ebf74ac62652aebaf7838561e", size = 160300 }, + { url = "https://files.pythonhosted.org/packages/7a/10/f882167cd207fbdd743e55534d5d9620e095089d176d55cb22d5322f2afd/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a26f18905b8dd5d685d6d07b0cdf98a79f3c7a918906af7cc143ea2e164c8bc", size = 154465 }, + { url = "https://files.pythonhosted.org/packages/89/66/c7a9e1b7429be72123441bfdbaf2bc13faab3f90b933f664db506dea5915/charset_normalizer-3.4.4-cp313-cp313-win32.whl", hash = "sha256:9b35f4c90079ff2e2edc5b26c0c77925e5d2d255c42c74fdb70fb49b172726ac", size = 99404 }, + { url = "https://files.pythonhosted.org/packages/c4/26/b9924fa27db384bdcd97ab83b4f0a8058d96ad9626ead570674d5e737d90/charset_normalizer-3.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14", size = 107092 }, + { url = "https://files.pythonhosted.org/packages/af/8f/3ed4bfa0c0c72a7ca17f0380cd9e4dd842b09f664e780c13cff1dcf2ef1b/charset_normalizer-3.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:542d2cee80be6f80247095cc36c418f7bddd14f4a6de45af91dfad36d817bba2", size = 100408 }, + { url = "https://files.pythonhosted.org/packages/2a/35/7051599bd493e62411d6ede36fd5af83a38f37c4767b92884df7301db25d/charset_normalizer-3.4.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:da3326d9e65ef63a817ecbcc0df6e94463713b754fe293eaa03da99befb9a5bd", size = 207746 }, + { url = "https://files.pythonhosted.org/packages/10/9a/97c8d48ef10d6cd4fcead2415523221624bf58bcf68a802721a6bc807c8f/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8af65f14dc14a79b924524b1e7fffe304517b2bff5a58bf64f30b98bbc5079eb", size = 147889 }, + { url = "https://files.pythonhosted.org/packages/10/bf/979224a919a1b606c82bd2c5fa49b5c6d5727aa47b4312bb27b1734f53cd/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74664978bb272435107de04e36db5a9735e78232b85b77d45cfb38f758efd33e", size = 143641 }, + { url = "https://files.pythonhosted.org/packages/ba/33/0ad65587441fc730dc7bd90e9716b30b4702dc7b617e6ba4997dc8651495/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:752944c7ffbfdd10c074dc58ec2d5a8a4cd9493b314d367c14d24c17684ddd14", size = 160779 }, + { url = "https://files.pythonhosted.org/packages/67/ed/331d6b249259ee71ddea93f6f2f0a56cfebd46938bde6fcc6f7b9a3d0e09/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1f13550535ad8cff21b8d757a3257963e951d96e20ec82ab44bc64aeb62a191", size = 159035 }, + { url = "https://files.pythonhosted.org/packages/67/ff/f6b948ca32e4f2a4576aa129d8bed61f2e0543bf9f5f2b7fc3758ed005c9/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ecaae4149d99b1c9e7b88bb03e3221956f68fd6d50be2ef061b2381b61d20838", size = 152542 }, + { url = "https://files.pythonhosted.org/packages/16/85/276033dcbcc369eb176594de22728541a925b2632f9716428c851b149e83/charset_normalizer-3.4.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cb6254dc36b47a990e59e1068afacdcd02958bdcce30bb50cc1700a8b9d624a6", size = 149524 }, + { url = "https://files.pythonhosted.org/packages/9e/f2/6a2a1f722b6aba37050e626530a46a68f74e63683947a8acff92569f979a/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c8ae8a0f02f57a6e61203a31428fa1d677cbe50c93622b4149d5c0f319c1d19e", size = 150395 }, + { url = "https://files.pythonhosted.org/packages/60/bb/2186cb2f2bbaea6338cad15ce23a67f9b0672929744381e28b0592676824/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:47cc91b2f4dd2833fddaedd2893006b0106129d4b94fdb6af1f4ce5a9965577c", size = 143680 }, + { url = "https://files.pythonhosted.org/packages/7d/a5/bf6f13b772fbb2a90360eb620d52ed8f796f3c5caee8398c3b2eb7b1c60d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:82004af6c302b5d3ab2cfc4cc5f29db16123b1a8417f2e25f9066f91d4411090", size = 162045 }, + { url = "https://files.pythonhosted.org/packages/df/c5/d1be898bf0dc3ef9030c3825e5d3b83f2c528d207d246cbabe245966808d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b7d8f6c26245217bd2ad053761201e9f9680f8ce52f0fcd8d0755aeae5b2152", size = 149687 }, + { url = "https://files.pythonhosted.org/packages/a5/42/90c1f7b9341eef50c8a1cb3f098ac43b0508413f33affd762855f67a410e/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:799a7a5e4fb2d5898c60b640fd4981d6a25f1c11790935a44ce38c54e985f828", size = 160014 }, + { url = "https://files.pythonhosted.org/packages/76/be/4d3ee471e8145d12795ab655ece37baed0929462a86e72372fd25859047c/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:99ae2cffebb06e6c22bdc25801d7b30f503cc87dbd283479e7b606f70aff57ec", size = 154044 }, + { url = "https://files.pythonhosted.org/packages/b0/6f/8f7af07237c34a1defe7defc565a9bc1807762f672c0fde711a4b22bf9c0/charset_normalizer-3.4.4-cp314-cp314-win32.whl", hash = "sha256:f9d332f8c2a2fcbffe1378594431458ddbef721c1769d78e2cbc06280d8155f9", size = 99940 }, + { url = "https://files.pythonhosted.org/packages/4b/51/8ade005e5ca5b0d80fb4aff72a3775b325bdc3d27408c8113811a7cbe640/charset_normalizer-3.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:8a6562c3700cce886c5be75ade4a5db4214fda19fede41d9792d100288d8f94c", size = 107104 }, + { url = "https://files.pythonhosted.org/packages/da/5f/6b8f83a55bb8278772c5ae54a577f3099025f9ade59d0136ac24a0df4bde/charset_normalizer-3.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:de00632ca48df9daf77a2c65a484531649261ec9f25489917f09e455cb09ddb2", size = 100743 }, + { url = "https://files.pythonhosted.org/packages/0a/4c/925909008ed5a988ccbb72dcc897407e5d6d3bd72410d69e051fc0c14647/charset_normalizer-3.4.4-py3-none-any.whl", hash = "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f", size = 53402 }, ] [[package]] @@ -894,67 +919,67 @@ wheels = [ [[package]] name = "cryptography" -version = "46.0.2" +version = "46.0.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/4a/9b/e301418629f7bfdf72db9e80ad6ed9d1b83c487c471803eaa6464c511a01/cryptography-46.0.2.tar.gz", hash = "sha256:21b6fc8c71a3f9a604f028a329e5560009cc4a3a828bfea5fcba8eb7647d88fe", size = 749293 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e0/98/7a8df8c19a335c8028414738490fc3955c0cecbfdd37fcc1b9c3d04bd561/cryptography-46.0.2-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:f3e32ab7dd1b1ef67b9232c4cf5e2ee4cd517d4316ea910acaaa9c5712a1c663", size = 7261255 }, - { url = "https://files.pythonhosted.org/packages/c6/38/b2adb2aa1baa6706adc3eb746691edd6f90a656a9a65c3509e274d15a2b8/cryptography-46.0.2-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1fd1a69086926b623ef8126b4c33d5399ce9e2f3fac07c9c734c2a4ec38b6d02", size = 4297596 }, - { url = "https://files.pythonhosted.org/packages/e4/27/0f190ada240003119488ae66c897b5e97149292988f556aef4a6a2a57595/cryptography-46.0.2-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:bb7fb9cd44c2582aa5990cf61a4183e6f54eea3172e54963787ba47287edd135", size = 4450899 }, - { url = "https://files.pythonhosted.org/packages/85/d5/e4744105ab02fdf6bb58ba9a816e23b7a633255987310b4187d6745533db/cryptography-46.0.2-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:9066cfd7f146f291869a9898b01df1c9b0e314bfa182cef432043f13fc462c92", size = 4300382 }, - { url = "https://files.pythonhosted.org/packages/33/fb/bf9571065c18c04818cb07de90c43fc042c7977c68e5de6876049559c72f/cryptography-46.0.2-cp311-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:97e83bf4f2f2c084d8dd792d13841d0a9b241643151686010866bbd076b19659", size = 4017347 }, - { url = "https://files.pythonhosted.org/packages/35/72/fc51856b9b16155ca071080e1a3ad0c3a8e86616daf7eb018d9565b99baa/cryptography-46.0.2-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:4a766d2a5d8127364fd936572c6e6757682fc5dfcbdba1632d4554943199f2fa", size = 4983500 }, - { url = "https://files.pythonhosted.org/packages/c1/53/0f51e926799025e31746d454ab2e36f8c3f0d41592bc65cb9840368d3275/cryptography-46.0.2-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:fab8f805e9675e61ed8538f192aad70500fa6afb33a8803932999b1049363a08", size = 4482591 }, - { url = "https://files.pythonhosted.org/packages/86/96/4302af40b23ab8aa360862251fb8fc450b2a06ff24bc5e261c2007f27014/cryptography-46.0.2-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:1e3b6428a3d56043bff0bb85b41c535734204e599c1c0977e1d0f261b02f3ad5", size = 4300019 }, - { url = "https://files.pythonhosted.org/packages/9b/59/0be12c7fcc4c5e34fe2b665a75bc20958473047a30d095a7657c218fa9e8/cryptography-46.0.2-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:1a88634851d9b8de8bb53726f4300ab191d3b2f42595e2581a54b26aba71b7cc", size = 4950006 }, - { url = "https://files.pythonhosted.org/packages/55/1d/42fda47b0111834b49e31590ae14fd020594d5e4dadd639bce89ad790fba/cryptography-46.0.2-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:be939b99d4e091eec9a2bcf41aaf8f351f312cd19ff74b5c83480f08a8a43e0b", size = 4482088 }, - { url = "https://files.pythonhosted.org/packages/17/50/60f583f69aa1602c2bdc7022dae86a0d2b837276182f8c1ec825feb9b874/cryptography-46.0.2-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9f13b040649bc18e7eb37936009b24fd31ca095a5c647be8bb6aaf1761142bd1", size = 4425599 }, - { url = "https://files.pythonhosted.org/packages/d1/57/d8d4134cd27e6e94cf44adb3f3489f935bde85f3a5508e1b5b43095b917d/cryptography-46.0.2-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:9bdc25e4e01b261a8fda4e98618f1c9515febcecebc9566ddf4a70c63967043b", size = 4697458 }, - { url = "https://files.pythonhosted.org/packages/d1/2b/531e37408573e1da33adfb4c58875013ee8ac7d548d1548967d94a0ae5c4/cryptography-46.0.2-cp311-abi3-win32.whl", hash = "sha256:8b9bf67b11ef9e28f4d78ff88b04ed0929fcd0e4f70bb0f704cfc32a5c6311ee", size = 3056077 }, - { url = "https://files.pythonhosted.org/packages/a8/cd/2f83cafd47ed2dc5a3a9c783ff5d764e9e70d3a160e0df9a9dcd639414ce/cryptography-46.0.2-cp311-abi3-win_amd64.whl", hash = "sha256:758cfc7f4c38c5c5274b55a57ef1910107436f4ae842478c4989abbd24bd5acb", size = 3512585 }, - { url = "https://files.pythonhosted.org/packages/00/36/676f94e10bfaa5c5b86c469ff46d3e0663c5dc89542f7afbadac241a3ee4/cryptography-46.0.2-cp311-abi3-win_arm64.whl", hash = "sha256:218abd64a2e72f8472c2102febb596793347a3e65fafbb4ad50519969da44470", size = 2927474 }, - { url = "https://files.pythonhosted.org/packages/6f/cc/47fc6223a341f26d103cb6da2216805e08a37d3b52bee7f3b2aee8066f95/cryptography-46.0.2-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:bda55e8dbe8533937956c996beaa20266a8eca3570402e52ae52ed60de1faca8", size = 7198626 }, - { url = "https://files.pythonhosted.org/packages/93/22/d66a8591207c28bbe4ac7afa25c4656dc19dc0db29a219f9809205639ede/cryptography-46.0.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e7155c0b004e936d381b15425273aee1cebc94f879c0ce82b0d7fecbf755d53a", size = 4287584 }, - { url = "https://files.pythonhosted.org/packages/8c/3e/fac3ab6302b928e0398c269eddab5978e6c1c50b2b77bb5365ffa8633b37/cryptography-46.0.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a61c154cc5488272a6c4b86e8d5beff4639cdb173d75325ce464d723cda0052b", size = 4433796 }, - { url = "https://files.pythonhosted.org/packages/7d/d8/24392e5d3c58e2d83f98fe5a2322ae343360ec5b5b93fe18bc52e47298f5/cryptography-46.0.2-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:9ec3f2e2173f36a9679d3b06d3d01121ab9b57c979de1e6a244b98d51fea1b20", size = 4292126 }, - { url = "https://files.pythonhosted.org/packages/ed/38/3d9f9359b84c16c49a5a336ee8be8d322072a09fac17e737f3bb11f1ce64/cryptography-46.0.2-cp314-cp314t-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2fafb6aa24e702bbf74de4cb23bfa2c3beb7ab7683a299062b69724c92e0fa73", size = 3993056 }, - { url = "https://files.pythonhosted.org/packages/d6/a3/4c44fce0d49a4703cc94bfbe705adebf7ab36efe978053742957bc7ec324/cryptography-46.0.2-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:0c7ffe8c9b1fcbb07a26d7c9fa5e857c2fe80d72d7b9e0353dcf1d2180ae60ee", size = 4967604 }, - { url = "https://files.pythonhosted.org/packages/eb/c2/49d73218747c8cac16bb8318a5513fde3129e06a018af3bc4dc722aa4a98/cryptography-46.0.2-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:5840f05518caa86b09d23f8b9405a7b6d5400085aa14a72a98fdf5cf1568c0d2", size = 4465367 }, - { url = "https://files.pythonhosted.org/packages/1b/64/9afa7d2ee742f55ca6285a54386ed2778556a4ed8871571cb1c1bfd8db9e/cryptography-46.0.2-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:27c53b4f6a682a1b645fbf1cd5058c72cf2f5aeba7d74314c36838c7cbc06e0f", size = 4291678 }, - { url = "https://files.pythonhosted.org/packages/50/48/1696d5ea9623a7b72ace87608f6899ca3c331709ac7ebf80740abb8ac673/cryptography-46.0.2-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:512c0250065e0a6b286b2db4bbcc2e67d810acd53eb81733e71314340366279e", size = 4931366 }, - { url = "https://files.pythonhosted.org/packages/eb/3c/9dfc778401a334db3b24435ee0733dd005aefb74afe036e2d154547cb917/cryptography-46.0.2-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:07c0eb6657c0e9cca5891f4e35081dbf985c8131825e21d99b4f440a8f496f36", size = 4464738 }, - { url = "https://files.pythonhosted.org/packages/dc/b1/abcde62072b8f3fd414e191a6238ce55a0050e9738090dc6cded24c12036/cryptography-46.0.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:48b983089378f50cba258f7f7aa28198c3f6e13e607eaf10472c26320332ca9a", size = 4419305 }, - { url = "https://files.pythonhosted.org/packages/c7/1f/3d2228492f9391395ca34c677e8f2571fb5370fe13dc48c1014f8c509864/cryptography-46.0.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e6f6775eaaa08c0eec73e301f7592f4367ccde5e4e4df8e58320f2ebf161ea2c", size = 4681201 }, - { url = "https://files.pythonhosted.org/packages/de/77/b687745804a93a55054f391528fcfc76c3d6bfd082ce9fb62c12f0d29fc1/cryptography-46.0.2-cp314-cp314t-win32.whl", hash = "sha256:e8633996579961f9b5a3008683344c2558d38420029d3c0bc7ff77c17949a4e1", size = 3022492 }, - { url = "https://files.pythonhosted.org/packages/60/a5/8d498ef2996e583de0bef1dcc5e70186376f00883ae27bf2133f490adf21/cryptography-46.0.2-cp314-cp314t-win_amd64.whl", hash = "sha256:48c01988ecbb32979bb98731f5c2b2f79042a6c58cc9a319c8c2f9987c7f68f9", size = 3496215 }, - { url = "https://files.pythonhosted.org/packages/56/db/ee67aaef459a2706bc302b15889a1a8126ebe66877bab1487ae6ad00f33d/cryptography-46.0.2-cp314-cp314t-win_arm64.whl", hash = "sha256:8e2ad4d1a5899b7caa3a450e33ee2734be7cc0689010964703a7c4bcc8dd4fd0", size = 2919255 }, - { url = "https://files.pythonhosted.org/packages/d5/bb/fa95abcf147a1b0bb94d95f53fbb09da77b24c776c5d87d36f3d94521d2c/cryptography-46.0.2-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:a08e7401a94c002e79dc3bc5231b6558cd4b2280ee525c4673f650a37e2c7685", size = 7248090 }, - { url = "https://files.pythonhosted.org/packages/b7/66/f42071ce0e3ffbfa80a88feadb209c779fda92a23fbc1e14f74ebf72ef6b/cryptography-46.0.2-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d30bc11d35743bf4ddf76674a0a369ec8a21f87aaa09b0661b04c5f6c46e8d7b", size = 4293123 }, - { url = "https://files.pythonhosted.org/packages/a8/5d/1fdbd2e5c1ba822828d250e5a966622ef00185e476d1cd2726b6dd135e53/cryptography-46.0.2-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:bca3f0ce67e5a2a2cf524e86f44697c4323a86e0fd7ba857de1c30d52c11ede1", size = 4439524 }, - { url = "https://files.pythonhosted.org/packages/c8/c1/5e4989a7d102d4306053770d60f978c7b6b1ea2ff8c06e0265e305b23516/cryptography-46.0.2-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:ff798ad7a957a5021dcbab78dfff681f0cf15744d0e6af62bd6746984d9c9e9c", size = 4297264 }, - { url = "https://files.pythonhosted.org/packages/28/78/b56f847d220cb1d6d6aef5a390e116ad603ce13a0945a3386a33abc80385/cryptography-46.0.2-cp38-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:cb5e8daac840e8879407acbe689a174f5ebaf344a062f8918e526824eb5d97af", size = 4011872 }, - { url = "https://files.pythonhosted.org/packages/e1/80/2971f214b066b888944f7b57761bf709ee3f2cf805619a18b18cab9b263c/cryptography-46.0.2-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:3f37aa12b2d91e157827d90ce78f6180f0c02319468a0aea86ab5a9566da644b", size = 4978458 }, - { url = "https://files.pythonhosted.org/packages/a5/84/0cb0a2beaa4f1cbe63ebec4e97cd7e0e9f835d0ba5ee143ed2523a1e0016/cryptography-46.0.2-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:5e38f203160a48b93010b07493c15f2babb4e0f2319bbd001885adb3f3696d21", size = 4472195 }, - { url = "https://files.pythonhosted.org/packages/30/8b/2b542ddbf78835c7cd67b6fa79e95560023481213a060b92352a61a10efe/cryptography-46.0.2-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:d19f5f48883752b5ab34cff9e2f7e4a7f216296f33714e77d1beb03d108632b6", size = 4296791 }, - { url = "https://files.pythonhosted.org/packages/78/12/9065b40201b4f4876e93b9b94d91feb18de9150d60bd842a16a21565007f/cryptography-46.0.2-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:04911b149eae142ccd8c9a68892a70c21613864afb47aba92d8c7ed9cc001023", size = 4939629 }, - { url = "https://files.pythonhosted.org/packages/f6/9e/6507dc048c1b1530d372c483dfd34e7709fc542765015425f0442b08547f/cryptography-46.0.2-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:8b16c1ede6a937c291d41176934268e4ccac2c6521c69d3f5961c5a1e11e039e", size = 4471988 }, - { url = "https://files.pythonhosted.org/packages/b1/86/d025584a5f7d5c5ec8d3633dbcdce83a0cd579f1141ceada7817a4c26934/cryptography-46.0.2-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:747b6f4a4a23d5a215aadd1d0b12233b4119c4313df83ab4137631d43672cc90", size = 4422989 }, - { url = "https://files.pythonhosted.org/packages/4b/39/536370418b38a15a61bbe413006b79dfc3d2b4b0eafceb5581983f973c15/cryptography-46.0.2-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:6b275e398ab3a7905e168c036aad54b5969d63d3d9099a0a66cc147a3cc983be", size = 4685578 }, - { url = "https://files.pythonhosted.org/packages/15/52/ea7e2b1910f547baed566c866fbb86de2402e501a89ecb4871ea7f169a81/cryptography-46.0.2-cp38-abi3-win32.whl", hash = "sha256:0b507c8e033307e37af61cb9f7159b416173bdf5b41d11c4df2e499a1d8e007c", size = 3036711 }, - { url = "https://files.pythonhosted.org/packages/71/9e/171f40f9c70a873e73c2efcdbe91e1d4b1777a03398fa1c4af3c56a2477a/cryptography-46.0.2-cp38-abi3-win_amd64.whl", hash = "sha256:f9b2dc7668418fb6f221e4bf701f716e05e8eadb4f1988a2487b11aedf8abe62", size = 3500007 }, - { url = "https://files.pythonhosted.org/packages/3e/7c/15ad426257615f9be8caf7f97990cf3dcbb5b8dd7ed7e0db581a1c4759dd/cryptography-46.0.2-cp38-abi3-win_arm64.whl", hash = "sha256:91447f2b17e83c9e0c89f133119d83f94ce6e0fb55dd47da0a959316e6e9cfa1", size = 2918153 }, - { url = "https://files.pythonhosted.org/packages/25/b2/067a7db693488f19777ecf73f925bcb6a3efa2eae42355bafaafa37a6588/cryptography-46.0.2-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:f25a41f5b34b371a06dad3f01799706631331adc7d6c05253f5bca22068c7a34", size = 3701860 }, - { url = "https://files.pythonhosted.org/packages/87/12/47c2aab2c285f97c71a791169529dbb89f48fc12e5f62bb6525c3927a1a2/cryptography-46.0.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:e12b61e0b86611e3f4c1756686d9086c1d36e6fd15326f5658112ad1f1cc8807", size = 3429917 }, - { url = "https://files.pythonhosted.org/packages/b7/8c/1aabe338149a7d0f52c3e30f2880b20027ca2a485316756ed6f000462db3/cryptography-46.0.2-pp311-pypy311_pp73-macosx_10_9_x86_64.whl", hash = "sha256:1d3b3edd145953832e09607986f2bd86f85d1dc9c48ced41808b18009d9f30e5", size = 3714495 }, - { url = "https://files.pythonhosted.org/packages/e3/0a/0d10eb970fe3e57da9e9ddcfd9464c76f42baf7b3d0db4a782d6746f788f/cryptography-46.0.2-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:fe245cf4a73c20592f0f48da39748b3513db114465be78f0a36da847221bd1b4", size = 4243379 }, - { url = "https://files.pythonhosted.org/packages/7d/60/e274b4d41a9eb82538b39950a74ef06e9e4d723cb998044635d9deb1b435/cryptography-46.0.2-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:2b9cad9cf71d0c45566624ff76654e9bae5f8a25970c250a26ccfc73f8553e2d", size = 4409533 }, - { url = "https://files.pythonhosted.org/packages/19/9a/fb8548f762b4749aebd13b57b8f865de80258083fe814957f9b0619cfc56/cryptography-46.0.2-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:9bd26f2f75a925fdf5e0a446c0de2714f17819bf560b44b7480e4dd632ad6c46", size = 4243120 }, - { url = "https://files.pythonhosted.org/packages/71/60/883f24147fd4a0c5cab74ac7e36a1ff3094a54ba5c3a6253d2ff4b19255b/cryptography-46.0.2-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:7282d8f092b5be7172d6472f29b0631f39f18512a3642aefe52c3c0e0ccfad5a", size = 4408940 }, - { url = "https://files.pythonhosted.org/packages/d9/b5/c5e179772ec38adb1c072b3aa13937d2860509ba32b2462bf1dda153833b/cryptography-46.0.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:c4b93af7920cdf80f71650769464ccf1fb49a4b56ae0024173c24c48eb6b1612", size = 3438518 }, +sdist = { url = "https://files.pythonhosted.org/packages/9f/33/c00162f49c0e2fe8064a62cb92b93e50c74a72bc370ab92f86112b33ff62/cryptography-46.0.3.tar.gz", hash = "sha256:a8b17438104fed022ce745b362294d9ce35b4c2e45c1d958ad4a4b019285f4a1", size = 749258 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1d/42/9c391dd801d6cf0d561b5890549d4b27bafcc53b39c31a817e69d87c625b/cryptography-46.0.3-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:109d4ddfadf17e8e7779c39f9b18111a09efb969a301a31e987416a0191ed93a", size = 7225004 }, + { url = "https://files.pythonhosted.org/packages/1c/67/38769ca6b65f07461eb200e85fc1639b438bdc667be02cf7f2cd6a64601c/cryptography-46.0.3-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:09859af8466b69bc3c27bdf4f5d84a665e0f7ab5088412e9e2ec49758eca5cbc", size = 4296667 }, + { url = "https://files.pythonhosted.org/packages/5c/49/498c86566a1d80e978b42f0d702795f69887005548c041636df6ae1ca64c/cryptography-46.0.3-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:01ca9ff2885f3acc98c29f1860552e37f6d7c7d013d7334ff2a9de43a449315d", size = 4450807 }, + { url = "https://files.pythonhosted.org/packages/4b/0a/863a3604112174c8624a2ac3c038662d9e59970c7f926acdcfaed8d61142/cryptography-46.0.3-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:6eae65d4c3d33da080cff9c4ab1f711b15c1d9760809dad6ea763f3812d254cb", size = 4299615 }, + { url = "https://files.pythonhosted.org/packages/64/02/b73a533f6b64a69f3cd3872acb6ebc12aef924d8d103133bb3ea750dc703/cryptography-46.0.3-cp311-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5bf0ed4490068a2e72ac03d786693adeb909981cc596425d09032d372bcc849", size = 4016800 }, + { url = "https://files.pythonhosted.org/packages/25/d5/16e41afbfa450cde85a3b7ec599bebefaef16b5c6ba4ec49a3532336ed72/cryptography-46.0.3-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:5ecfccd2329e37e9b7112a888e76d9feca2347f12f37918facbb893d7bb88ee8", size = 4984707 }, + { url = "https://files.pythonhosted.org/packages/c9/56/e7e69b427c3878352c2fb9b450bd0e19ed552753491d39d7d0a2f5226d41/cryptography-46.0.3-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:a2c0cd47381a3229c403062f764160d57d4d175e022c1df84e168c6251a22eec", size = 4482541 }, + { url = "https://files.pythonhosted.org/packages/78/f6/50736d40d97e8483172f1bb6e698895b92a223dba513b0ca6f06b2365339/cryptography-46.0.3-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:549e234ff32571b1f4076ac269fcce7a808d3bf98b76c8dd560e42dbc66d7d91", size = 4299464 }, + { url = "https://files.pythonhosted.org/packages/00/de/d8e26b1a855f19d9994a19c702fa2e93b0456beccbcfe437eda00e0701f2/cryptography-46.0.3-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:c0a7bb1a68a5d3471880e264621346c48665b3bf1c3759d682fc0864c540bd9e", size = 4950838 }, + { url = "https://files.pythonhosted.org/packages/8f/29/798fc4ec461a1c9e9f735f2fc58741b0daae30688f41b2497dcbc9ed1355/cryptography-46.0.3-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:10b01676fc208c3e6feeb25a8b83d81767e8059e1fe86e1dc62d10a3018fa926", size = 4481596 }, + { url = "https://files.pythonhosted.org/packages/15/8d/03cd48b20a573adfff7652b76271078e3045b9f49387920e7f1f631d125e/cryptography-46.0.3-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:0abf1ffd6e57c67e92af68330d05760b7b7efb243aab8377e583284dbab72c71", size = 4426782 }, + { url = "https://files.pythonhosted.org/packages/fa/b1/ebacbfe53317d55cf33165bda24c86523497a6881f339f9aae5c2e13e57b/cryptography-46.0.3-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a04bee9ab6a4da801eb9b51f1b708a1b5b5c9eb48c03f74198464c66f0d344ac", size = 4698381 }, + { url = "https://files.pythonhosted.org/packages/96/92/8a6a9525893325fc057a01f654d7efc2c64b9de90413adcf605a85744ff4/cryptography-46.0.3-cp311-abi3-win32.whl", hash = "sha256:f260d0d41e9b4da1ed1e0f1ce571f97fe370b152ab18778e9e8f67d6af432018", size = 3055988 }, + { url = "https://files.pythonhosted.org/packages/7e/bf/80fbf45253ea585a1e492a6a17efcb93467701fa79e71550a430c5e60df0/cryptography-46.0.3-cp311-abi3-win_amd64.whl", hash = "sha256:a9a3008438615669153eb86b26b61e09993921ebdd75385ddd748702c5adfddb", size = 3514451 }, + { url = "https://files.pythonhosted.org/packages/2e/af/9b302da4c87b0beb9db4e756386a7c6c5b8003cd0e742277888d352ae91d/cryptography-46.0.3-cp311-abi3-win_arm64.whl", hash = "sha256:5d7f93296ee28f68447397bf5198428c9aeeab45705a55d53a6343455dcb2c3c", size = 2928007 }, + { url = "https://files.pythonhosted.org/packages/f5/e2/a510aa736755bffa9d2f75029c229111a1d02f8ecd5de03078f4c18d91a3/cryptography-46.0.3-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:00a5e7e87938e5ff9ff5447ab086a5706a957137e6e433841e9d24f38a065217", size = 7158012 }, + { url = "https://files.pythonhosted.org/packages/73/dc/9aa866fbdbb95b02e7f9d086f1fccfeebf8953509b87e3f28fff927ff8a0/cryptography-46.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c8daeb2d2174beb4575b77482320303f3d39b8e81153da4f0fb08eb5fe86a6c5", size = 4288728 }, + { url = "https://files.pythonhosted.org/packages/c5/fd/bc1daf8230eaa075184cbbf5f8cd00ba9db4fd32d63fb83da4671b72ed8a/cryptography-46.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:39b6755623145ad5eff1dab323f4eae2a32a77a7abef2c5089a04a3d04366715", size = 4435078 }, + { url = "https://files.pythonhosted.org/packages/82/98/d3bd5407ce4c60017f8ff9e63ffee4200ab3e23fe05b765cab805a7db008/cryptography-46.0.3-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:db391fa7c66df6762ee3f00c95a89e6d428f4d60e7abc8328f4fe155b5ac6e54", size = 4293460 }, + { url = "https://files.pythonhosted.org/packages/26/e9/e23e7900983c2b8af7a08098db406cf989d7f09caea7897e347598d4cd5b/cryptography-46.0.3-cp314-cp314t-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:78a97cf6a8839a48c49271cdcbd5cf37ca2c1d6b7fdd86cc864f302b5e9bf459", size = 3995237 }, + { url = "https://files.pythonhosted.org/packages/91/15/af68c509d4a138cfe299d0d7ddb14afba15233223ebd933b4bbdbc7155d3/cryptography-46.0.3-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:dfb781ff7eaa91a6f7fd41776ec37c5853c795d3b358d4896fdbb5df168af422", size = 4967344 }, + { url = "https://files.pythonhosted.org/packages/ca/e3/8643d077c53868b681af077edf6b3cb58288b5423610f21c62aadcbe99f4/cryptography-46.0.3-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:6f61efb26e76c45c4a227835ddeae96d83624fb0d29eb5df5b96e14ed1a0afb7", size = 4466564 }, + { url = "https://files.pythonhosted.org/packages/0e/43/c1e8726fa59c236ff477ff2b5dc071e54b21e5a1e51aa2cee1676f1c986f/cryptography-46.0.3-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:23b1a8f26e43f47ceb6d6a43115f33a5a37d57df4ea0ca295b780ae8546e8044", size = 4292415 }, + { url = "https://files.pythonhosted.org/packages/42/f9/2f8fefdb1aee8a8e3256a0568cffc4e6d517b256a2fe97a029b3f1b9fe7e/cryptography-46.0.3-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:b419ae593c86b87014b9be7396b385491ad7f320bde96826d0dd174459e54665", size = 4931457 }, + { url = "https://files.pythonhosted.org/packages/79/30/9b54127a9a778ccd6d27c3da7563e9f2d341826075ceab89ae3b41bf5be2/cryptography-46.0.3-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:50fc3343ac490c6b08c0cf0d704e881d0d660be923fd3076db3e932007e726e3", size = 4466074 }, + { url = "https://files.pythonhosted.org/packages/ac/68/b4f4a10928e26c941b1b6a179143af9f4d27d88fe84a6a3c53592d2e76bf/cryptography-46.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:22d7e97932f511d6b0b04f2bfd818d73dcd5928db509460aaf48384778eb6d20", size = 4420569 }, + { url = "https://files.pythonhosted.org/packages/a3/49/3746dab4c0d1979888f125226357d3262a6dd40e114ac29e3d2abdf1ec55/cryptography-46.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:d55f3dffadd674514ad19451161118fd010988540cee43d8bc20675e775925de", size = 4681941 }, + { url = "https://files.pythonhosted.org/packages/fd/30/27654c1dbaf7e4a3531fa1fc77986d04aefa4d6d78259a62c9dc13d7ad36/cryptography-46.0.3-cp314-cp314t-win32.whl", hash = "sha256:8a6e050cb6164d3f830453754094c086ff2d0b2f3a897a1d9820f6139a1f0914", size = 3022339 }, + { url = "https://files.pythonhosted.org/packages/f6/30/640f34ccd4d2a1bc88367b54b926b781b5a018d65f404d409aba76a84b1c/cryptography-46.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:760f83faa07f8b64e9c33fc963d790a2edb24efb479e3520c14a45741cd9b2db", size = 3494315 }, + { url = "https://files.pythonhosted.org/packages/ba/8b/88cc7e3bd0a8e7b861f26981f7b820e1f46aa9d26cc482d0feba0ecb4919/cryptography-46.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:516ea134e703e9fe26bcd1277a4b59ad30586ea90c365a87781d7887a646fe21", size = 2919331 }, + { url = "https://files.pythonhosted.org/packages/fd/23/45fe7f376a7df8daf6da3556603b36f53475a99ce4faacb6ba2cf3d82021/cryptography-46.0.3-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:cb3d760a6117f621261d662bccc8ef5bc32ca673e037c83fbe565324f5c46936", size = 7218248 }, + { url = "https://files.pythonhosted.org/packages/27/32/b68d27471372737054cbd34c84981f9edbc24fe67ca225d389799614e27f/cryptography-46.0.3-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4b7387121ac7d15e550f5cb4a43aef2559ed759c35df7336c402bb8275ac9683", size = 4294089 }, + { url = "https://files.pythonhosted.org/packages/26/42/fa8389d4478368743e24e61eea78846a0006caffaf72ea24a15159215a14/cryptography-46.0.3-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:15ab9b093e8f09daab0f2159bb7e47532596075139dd74365da52ecc9cb46c5d", size = 4440029 }, + { url = "https://files.pythonhosted.org/packages/5f/eb/f483db0ec5ac040824f269e93dd2bd8a21ecd1027e77ad7bdf6914f2fd80/cryptography-46.0.3-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:46acf53b40ea38f9c6c229599a4a13f0d46a6c3fa9ef19fc1a124d62e338dfa0", size = 4297222 }, + { url = "https://files.pythonhosted.org/packages/fd/cf/da9502c4e1912cb1da3807ea3618a6829bee8207456fbbeebc361ec38ba3/cryptography-46.0.3-cp38-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:10ca84c4668d066a9878890047f03546f3ae0a6b8b39b697457b7757aaf18dbc", size = 4012280 }, + { url = "https://files.pythonhosted.org/packages/6b/8f/9adb86b93330e0df8b3dcf03eae67c33ba89958fc2e03862ef1ac2b42465/cryptography-46.0.3-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:36e627112085bb3b81b19fed209c05ce2a52ee8b15d161b7c643a7d5a88491f3", size = 4978958 }, + { url = "https://files.pythonhosted.org/packages/d1/a0/5fa77988289c34bdb9f913f5606ecc9ada1adb5ae870bd0d1054a7021cc4/cryptography-46.0.3-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:1000713389b75c449a6e979ffc7dcc8ac90b437048766cef052d4d30b8220971", size = 4473714 }, + { url = "https://files.pythonhosted.org/packages/14/e5/fc82d72a58d41c393697aa18c9abe5ae1214ff6f2a5c18ac470f92777895/cryptography-46.0.3-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:b02cf04496f6576afffef5ddd04a0cb7d49cf6be16a9059d793a30b035f6b6ac", size = 4296970 }, + { url = "https://files.pythonhosted.org/packages/78/06/5663ed35438d0b09056973994f1aec467492b33bd31da36e468b01ec1097/cryptography-46.0.3-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:71e842ec9bc7abf543b47cf86b9a743baa95f4677d22baa4c7d5c69e49e9bc04", size = 4940236 }, + { url = "https://files.pythonhosted.org/packages/fc/59/873633f3f2dcd8a053b8dd1d38f783043b5fce589c0f6988bf55ef57e43e/cryptography-46.0.3-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:402b58fc32614f00980b66d6e56a5b4118e6cb362ae8f3fda141ba4689bd4506", size = 4472642 }, + { url = "https://files.pythonhosted.org/packages/3d/39/8e71f3930e40f6877737d6f69248cf74d4e34b886a3967d32f919cc50d3b/cryptography-46.0.3-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ef639cb3372f69ec44915fafcd6698b6cc78fbe0c2ea41be867f6ed612811963", size = 4423126 }, + { url = "https://files.pythonhosted.org/packages/cd/c7/f65027c2810e14c3e7268353b1681932b87e5a48e65505d8cc17c99e36ae/cryptography-46.0.3-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:3b51b8ca4f1c6453d8829e1eb7299499ca7f313900dd4d89a24b8b87c0a780d4", size = 4686573 }, + { url = "https://files.pythonhosted.org/packages/0a/6e/1c8331ddf91ca4730ab3086a0f1be19c65510a33b5a441cb334e7a2d2560/cryptography-46.0.3-cp38-abi3-win32.whl", hash = "sha256:6276eb85ef938dc035d59b87c8a7dc559a232f954962520137529d77b18ff1df", size = 3036695 }, + { url = "https://files.pythonhosted.org/packages/90/45/b0d691df20633eff80955a0fc7695ff9051ffce8b69741444bd9ed7bd0db/cryptography-46.0.3-cp38-abi3-win_amd64.whl", hash = "sha256:416260257577718c05135c55958b674000baef9a1c7d9e8f306ec60d71db850f", size = 3501720 }, + { url = "https://files.pythonhosted.org/packages/e8/cb/2da4cc83f5edb9c3257d09e1e7ab7b23f049c7962cae8d842bbef0a9cec9/cryptography-46.0.3-cp38-abi3-win_arm64.whl", hash = "sha256:d89c3468de4cdc4f08a57e214384d0471911a3830fcdaf7a8cc587e42a866372", size = 2918740 }, + { url = "https://files.pythonhosted.org/packages/d9/cd/1a8633802d766a0fa46f382a77e096d7e209e0817892929655fe0586ae32/cryptography-46.0.3-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a23582810fedb8c0bc47524558fb6c56aac3fc252cb306072fd2815da2a47c32", size = 3689163 }, + { url = "https://files.pythonhosted.org/packages/4c/59/6b26512964ace6480c3e54681a9859c974172fb141c38df11eadd8416947/cryptography-46.0.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:e7aec276d68421f9574040c26e2a7c3771060bc0cff408bae1dcb19d3ab1e63c", size = 3429474 }, + { url = "https://files.pythonhosted.org/packages/06/8a/e60e46adab4362a682cf142c7dcb5bf79b782ab2199b0dcb81f55970807f/cryptography-46.0.3-pp311-pypy311_pp73-macosx_10_9_x86_64.whl", hash = "sha256:7ce938a99998ed3c8aa7e7272dca1a610401ede816d36d0693907d863b10d9ea", size = 3698132 }, + { url = "https://files.pythonhosted.org/packages/da/38/f59940ec4ee91e93d3311f7532671a5cef5570eb04a144bf203b58552d11/cryptography-46.0.3-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:191bb60a7be5e6f54e30ba16fdfae78ad3a342a0599eb4193ba88e3f3d6e185b", size = 4243992 }, + { url = "https://files.pythonhosted.org/packages/b0/0c/35b3d92ddebfdfda76bb485738306545817253d0a3ded0bfe80ef8e67aa5/cryptography-46.0.3-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c70cc23f12726be8f8bc72e41d5065d77e4515efae3690326764ea1b07845cfb", size = 4409944 }, + { url = "https://files.pythonhosted.org/packages/99/55/181022996c4063fc0e7666a47049a1ca705abb9c8a13830f074edb347495/cryptography-46.0.3-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:9394673a9f4de09e28b5356e7fff97d778f8abad85c9d5ac4a4b7e25a0de7717", size = 4242957 }, + { url = "https://files.pythonhosted.org/packages/ba/af/72cd6ef29f9c5f731251acadaeb821559fe25f10852f44a63374c9ca08c1/cryptography-46.0.3-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:94cd0549accc38d1494e1f8de71eca837d0509d0d44bf11d158524b0e12cebf9", size = 4409447 }, + { url = "https://files.pythonhosted.org/packages/0d/c3/e90f4a4feae6410f914f8ebac129b9ae7a8c92eb60a638012dde42030a9d/cryptography-46.0.3-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:6b5063083824e5509fdba180721d55909ffacccc8adbec85268b48439423d78c", size = 3438528 }, ] [[package]] @@ -1163,7 +1188,7 @@ wheels = [ [[package]] name = "elevenlabs" -version = "2.16.0" +version = "2.18.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "httpx" }, @@ -1173,9 +1198,9 @@ dependencies = [ { name = "typing-extensions" }, { name = "websockets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c3/cf/d44f4d3089adad95089026fe5d8247836daa441a0de174c0c64fa40fb5cb/elevenlabs-2.16.0.tar.gz", hash = "sha256:3a26ea20a3ce4a30ffada125f77b102bd88dc2d3230a912bd144017087d36795", size = 356398 } +sdist = { url = "https://files.pythonhosted.org/packages/f6/b1/e57d32fc038b4fbf98798a98328c73b918fdb6eaf59c44c1f5a6fea3bd1e/elevenlabs-2.18.0.tar.gz", hash = "sha256:f1550c69253fcd642421b1bc3160d6e95dec705f48f10e18e0063f2972d269d9", size = 399576 } wheels = [ - { url = "https://files.pythonhosted.org/packages/45/a9/f98999d95b3b0b0cb6ac7f03d9f2b424a2d5892a361a55079d6801c9cf21/elevenlabs-2.16.0-py3-none-any.whl", hash = "sha256:ba46cb8029c11f3fff5c8e083f5857a90adf0aa463bbd4cf01f5511017940d58", size = 955598 }, + { url = "https://files.pythonhosted.org/packages/41/27/518162d67de571881fc82e34542bd3ace5986dae9d7815eb2ebe007934af/elevenlabs-2.18.0-py3-none-any.whl", hash = "sha256:407571e721fb974a23ccea030312c2db24801bb559c77e8af98c8822a22145d3", size = 1085606 }, ] [[package]] @@ -1227,20 +1252,20 @@ wheels = [ [[package]] name = "fal-client" -version = "0.8.0" +version = "0.8.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "httpx" }, { name = "httpx-sse" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5c/62/6ddf0e206127975490d2f0bdb7c3371d6a338ad755caf700f3ad18215551/fal_client-0.8.0.tar.gz", hash = "sha256:86f521e0dfeff26e5fd6b92bdfaec1503e016f58789ee8e0c0c5298a6006ccf2", size = 14769 } +sdist = { url = "https://files.pythonhosted.org/packages/15/21/6a199b0e5933a93ecb69ca919aa58e62682ff4619e5cdf9e5b17becebeeb/fal_client-0.8.1.tar.gz", hash = "sha256:2c12cb0b0a327f4aa9c24fc7952722acf7aa059a1421a1c53283466f3bc07353", size = 15795 } wheels = [ - { url = "https://files.pythonhosted.org/packages/2f/e7/1cc6e54c105310246f6c8d9e1199c5e7b06a255af164c438449617618e89/fal_client-0.8.0-py3-none-any.whl", hash = "sha256:f2fd9801a7b67b075e388f845065c1f2969f477267233311d34cd07039cab77d", size = 10814 }, + { url = "https://files.pythonhosted.org/packages/45/dc/0012b95c05448264329ba71ad568e440f12b7f5acdf2ddc09fa1aec42e0d/fal_client-0.8.1-py3-none-any.whl", hash = "sha256:ab37063f2b35ca6fad06f75fe45b05b72c774ad7590e0f93d47a8f6ad5e1f9db", size = 10912 }, ] [[package]] name = "fastmcp" -version = "2.12.4" +version = "2.12.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "authlib" }, @@ -1255,9 +1280,9 @@ dependencies = [ { name = "python-dotenv" }, { name = "rich" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a8/b2/57845353a9bc63002995a982e66f3d0be4ec761e7bcb89e7d0638518d42a/fastmcp-2.12.4.tar.gz", hash = "sha256:b55fe89537038f19d0f4476544f9ca5ac171033f61811cc8f12bdeadcbea5016", size = 7167745 } +sdist = { url = "https://files.pythonhosted.org/packages/00/a6/e3b46cd3e228635e0064c2648788b6f66a53bf0d0ddbf5fb44cca951f908/fastmcp-2.12.5.tar.gz", hash = "sha256:2dfd02e255705a4afe43d26caddbc864563036e233dbc6870f389ee523b39a6a", size = 7190263 } wheels = [ - { url = "https://files.pythonhosted.org/packages/e2/c7/562ff39f25de27caec01e4c1e88cbb5fcae5160802ba3d90be33165df24f/fastmcp-2.12.4-py3-none-any.whl", hash = "sha256:56188fbbc1a9df58c537063f25958c57b5c4d715f73e395c41b51550b247d140", size = 329090 }, + { url = "https://files.pythonhosted.org/packages/d8/c1/9fb98c9649e15ea8cc691b4b09558b61dafb3dc0345f7322f8c4a8991ade/fastmcp-2.12.5-py3-none-any.whl", hash = "sha256:b1e542f9b83dbae7cecfdc9c73b062f77074785abda9f2306799116121344133", size = 329099 }, ] [[package]] @@ -1467,7 +1492,7 @@ wheels = [ [[package]] name = "getstream" -version = "2.5.3" +version = "2.5.6" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "dataclasses-json" }, @@ -1481,7 +1506,7 @@ dependencies = [ { name = "python-dateutil" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/d6/25/771d7ec27ee9ce3b6183930ac25879210c288e181d4efe4c7ecd441e6083/getstream-2.5.3-py3-none-any.whl", hash = "sha256:3b67c23f76822341c6fa217d47108896e5884a8eb961b4fe67faabc2c28966cd", size = 231858 }, + { url = "https://files.pythonhosted.org/packages/88/c6/e1226dbfa237f061e6ef729113d81ef1fe23a662a9a8d93504689327618b/getstream-2.5.6-py3-none-any.whl", hash = "sha256:303a0ec7589cc8ae2eddd34b999cd9d97c568de217b982f2d0ea4b978a433ea5", size = 232577 }, ] [package.optional-dependencies] @@ -1558,7 +1583,7 @@ wheels = [ [[package]] name = "google-genai" -version = "1.41.0" +version = "1.45.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, @@ -1570,9 +1595,9 @@ dependencies = [ { name = "typing-extensions" }, { name = "websockets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/72/8b/ee20bcf707769b3b0e1106c3b5c811507736af7e8a60f29a70af1750ba19/google_genai-1.41.0.tar.gz", hash = "sha256:134f861bb0ace4e34af0501ecb75ceee15f7662fd8120698cd185e8cb39f2800", size = 245812 } +sdist = { url = "https://files.pythonhosted.org/packages/91/77/776b92f6f7cf7d7d3bc77b44a323605ae0f94f807cf9a4977c90d296b6b4/google_genai-1.45.0.tar.gz", hash = "sha256:96ec32ae99a30b5a1b54cb874b577ec6e41b5d5b808bf0f10ed4620e867f9386", size = 238198 } wheels = [ - { url = "https://files.pythonhosted.org/packages/15/14/e5e8fbca8863fee718208566c4e927b8e9f45fd46ec5cf89e24759da545b/google_genai-1.41.0-py3-none-any.whl", hash = "sha256:111a3ee64c1a0927d3879faddb368234594432479a40c311e5fe4db338ca8778", size = 245931 }, + { url = "https://files.pythonhosted.org/packages/11/8f/922116dabe3d0312f08903d324db6ac9d406832cf57707550bc61151d91b/google_genai-1.45.0-py3-none-any.whl", hash = "sha256:e755295063e5fd5a4c44acff782a569e37fa8f76a6c75d0ede3375c70d916b7f", size = 238495 }, ] [[package]] @@ -1659,7 +1684,7 @@ wheels = [ [[package]] name = "hatch" -version = "1.14.2" +version = "1.15.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, @@ -1679,9 +1704,9 @@ dependencies = [ { name = "virtualenv" }, { name = "zstandard" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/04/7a/6ad202db77fc02f2368836604942926b57db6c5b76808ac1a2b1874f0bcd/hatch-1.14.2.tar.gz", hash = "sha256:b522c7463198c6e24bd9d9c83252327502e3cc3509844141de0aad7b0aa1967d", size = 5188254 } +sdist = { url = "https://files.pythonhosted.org/packages/f8/30/a7f19d337df93fb15dec6892e9ae678acd4ae10ce03d02722f17c7fe513b/hatch-1.15.1.tar.gz", hash = "sha256:444a78123c9837e8c9f5adfbf2b8b0a72139587eb49d6b368038b0521136fc43", size = 5189156 } wheels = [ - { url = "https://files.pythonhosted.org/packages/75/7e/7b04e626210ff7d3f2c5b5a2e0972e13028d15692f3b880f2930022f51c8/hatch-1.14.2-py3-none-any.whl", hash = "sha256:d2d3c9cfcd60838db3202a2aaa6e48e8119885ffe6590f165a8511f3843c5af3", size = 125833 }, + { url = "https://files.pythonhosted.org/packages/69/01/316ef533114e0de0649e2e925ae2f97dfe26fbe5358f678e84b2a5fa1407/hatch-1.15.1-py3-none-any.whl", hash = "sha256:99dccb26b00226056142f89d6e286be61e2d7b5b5b4e6178ebbe9298c1bc45d9", size = 126295 }, ] [[package]] @@ -1831,11 +1856,11 @@ wheels = [ [[package]] name = "idna" -version = "3.10" +version = "3.11" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490 } +sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582 } wheels = [ - { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 }, + { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008 }, ] [[package]] @@ -1849,77 +1874,93 @@ wheels = [ [[package]] name = "ijson" -version = "3.4.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a3/4f/1cfeada63f5fce87536651268ddf5cca79b8b4bbb457aee4e45777964a0a/ijson-3.4.0.tar.gz", hash = "sha256:5f74dcbad9d592c428d3ca3957f7115a42689ee7ee941458860900236ae9bb13", size = 65782 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/eb/6b/a247ba44004154aaa71f9e6bd9f05ba412f490cc4043618efb29314f035e/ijson-3.4.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e27e50f6dcdee648f704abc5d31b976cd2f90b4642ed447cf03296d138433d09", size = 87609 }, - { url = "https://files.pythonhosted.org/packages/3c/1d/8d2009d74373b7dec2a49b1167e396debb896501396c70a674bb9ccc41ff/ijson-3.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2a753be681ac930740a4af9c93cfb4edc49a167faed48061ea650dc5b0f406f1", size = 59243 }, - { url = "https://files.pythonhosted.org/packages/a7/b2/a85a21ebaba81f64a326c303a94625fb94b84890c52d9efdd8acb38b6312/ijson-3.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a07c47aed534e0ec198e6a2d4360b259d32ac654af59c015afc517ad7973b7fb", size = 59309 }, - { url = "https://files.pythonhosted.org/packages/b1/35/273dfa1f27c38eeaba105496ecb54532199f76c0120177b28315daf5aec3/ijson-3.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c55f48181e11c597cd7146fb31edc8058391201ead69f8f40d2ecbb0b3e4fc6", size = 131213 }, - { url = "https://files.pythonhosted.org/packages/4d/37/9d3bb0e200a103ca9f8e9315c4d96ecaca43a3c1957c1ac069ea9dc9c6ba/ijson-3.4.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abd5669f96f79d8a2dd5ae81cbd06770a4d42c435fd4a75c74ef28d9913b697d", size = 125456 }, - { url = "https://files.pythonhosted.org/packages/00/54/8f015c4df30200fd14435dec9c67bf675dff0fee44a16c084a8ec0f82922/ijson-3.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3e3ddd46d16b8542c63b1b8af7006c758d4e21cc1b86122c15f8530fae773461", size = 130192 }, - { url = "https://files.pythonhosted.org/packages/88/01/46a0540ad3461332edcc689a8874fa13f0a4c00f60f02d155b70e36f5e0b/ijson-3.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:1504cec7fe04be2bb0cc33b50c9dd3f83f98c0540ad4991d4017373b7853cfe6", size = 132217 }, - { url = "https://files.pythonhosted.org/packages/d7/da/8f8df42f3fd7ef279e20eae294738eed62d41ed5b6a4baca5121abc7cf0f/ijson-3.4.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:2f2ff456adeb216603e25d7915f10584c1b958b6eafa60038d76d08fc8a5fb06", size = 127118 }, - { url = "https://files.pythonhosted.org/packages/82/0a/a410d9d3b082cc2ec9738d54935a589974cbe54c0f358e4d17465594d660/ijson-3.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:0ab00d75d61613a125fbbb524551658b1ad6919a52271ca16563ca5bc2737bb1", size = 129808 }, - { url = "https://files.pythonhosted.org/packages/2e/c6/a3e2a446b8bd2cf91cb4ca7439f128d2b379b5a79794d0ea25e379b0f4f3/ijson-3.4.0-cp310-cp310-win32.whl", hash = "sha256:ada421fd59fe2bfa4cfa64ba39aeba3f0753696cdcd4d50396a85f38b1d12b01", size = 51160 }, - { url = "https://files.pythonhosted.org/packages/18/7c/e6620603df42d2ef8a92076eaa5cd2b905366e86e113adf49e7b79970bd3/ijson-3.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:8c75e82cec05d00ed3a4af5f4edf08f59d536ed1a86ac7e84044870872d82a33", size = 53710 }, - { url = "https://files.pythonhosted.org/packages/1a/0d/3e2998f4d7b7d2db2d511e4f0cf9127b6e2140c325c3cb77be46ae46ff1d/ijson-3.4.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9e369bf5a173ca51846c243002ad8025d32032532523b06510881ecc8723ee54", size = 87643 }, - { url = "https://files.pythonhosted.org/packages/e9/7b/afef2b08af2fee5ead65fcd972fadc3e31f9ae2b517fe2c378d50a9bf79b/ijson-3.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:26e7da0a3cd2a56a1fde1b34231867693f21c528b683856f6691e95f9f39caec", size = 59260 }, - { url = "https://files.pythonhosted.org/packages/da/4a/39f583a2a13096f5063028bb767622f09cafc9ec254c193deee6c80af59f/ijson-3.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1c28c7f604729be22aa453e604e9617b665fa0c24cd25f9f47a970e8130c571a", size = 59311 }, - { url = "https://files.pythonhosted.org/packages/3c/58/5b80efd54b093e479c98d14b31d7794267281f6a8729f2c94fbfab661029/ijson-3.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0bed8bcb84d3468940f97869da323ba09ae3e6b950df11dea9b62e2b231ca1e3", size = 136125 }, - { url = "https://files.pythonhosted.org/packages/e5/f5/f37659b1647ecc3992216277cd8a45e2194e84e8818178f77c99e1d18463/ijson-3.4.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:296bc824f4088f2af814aaf973b0435bc887ce3d9f517b1577cc4e7d1afb1cb7", size = 130699 }, - { url = "https://files.pythonhosted.org/packages/ee/2f/4c580ac4bb5eda059b672ad0a05e4bafdae5182a6ec6ab43546763dafa91/ijson-3.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8145f8f40617b6a8aa24e28559d0adc8b889e56a203725226a8a60fa3501073f", size = 134963 }, - { url = "https://files.pythonhosted.org/packages/6d/9e/64ec39718609faab6ed6e1ceb44f9c35d71210ad9c87fff477c03503e8f8/ijson-3.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b674a97bd503ea21bc85103e06b6493b1b2a12da3372950f53e1c664566a33a4", size = 137405 }, - { url = "https://files.pythonhosted.org/packages/71/b2/f0bf0e4a0962845597996de6de59c0078bc03a1f899e03908220039f4cf6/ijson-3.4.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8bc731cf1c3282b021d3407a601a5a327613da9ad3c4cecb1123232623ae1826", size = 131861 }, - { url = "https://files.pythonhosted.org/packages/17/83/4a2e3611e2b4842b413ec84d2e54adea55ab52e4408ea0f1b1b927e19536/ijson-3.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:42ace5e940e0cf58c9de72f688d6829ddd815096d07927ee7e77df2648006365", size = 134297 }, - { url = "https://files.pythonhosted.org/packages/38/75/2d332911ac765b44cd7da0cb2b06143521ad5e31dfcc8d8587e6e6168bc8/ijson-3.4.0-cp311-cp311-win32.whl", hash = "sha256:5be39a0df4cd3f02b304382ea8885391900ac62e95888af47525a287c50005e9", size = 51161 }, - { url = "https://files.pythonhosted.org/packages/7d/ba/4ad571f9f7fcf5906b26e757b130c1713c5f0198a1e59568f05d53a0816c/ijson-3.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:0b1be1781792291e70d2e177acf564ec672a7907ba74f313583bdf39fe81f9b7", size = 53710 }, - { url = "https://files.pythonhosted.org/packages/f8/ec/317ee5b2d13e50448833ead3aa906659a32b376191f6abc2a7c6112d2b27/ijson-3.4.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:956b148f88259a80a9027ffbe2d91705fae0c004fbfba3e5a24028fbe72311a9", size = 87212 }, - { url = "https://files.pythonhosted.org/packages/f8/43/b06c96ced30cacecc5d518f89b0fd1c98c294a30ff88848b70ed7b7f72a1/ijson-3.4.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:06b89960f5c721106394c7fba5760b3f67c515b8eb7d80f612388f5eca2f4621", size = 59175 }, - { url = "https://files.pythonhosted.org/packages/e9/df/b4aeafb7ecde463130840ee9be36130823ec94a00525049bf700883378b8/ijson-3.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9a0bb591cf250dd7e9dfab69d634745a7f3272d31cfe879f9156e0a081fd97ee", size = 59011 }, - { url = "https://files.pythonhosted.org/packages/e3/7c/a80b8e361641609507f62022089626d4b8067f0826f51e1c09e4ba86eba8/ijson-3.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:72e92de999977f4c6b660ffcf2b8d59604ccd531edcbfde05b642baf283e0de8", size = 146094 }, - { url = "https://files.pythonhosted.org/packages/01/44/fa416347b9a802e3646c6ff377fc3278bd7d6106e17beb339514b6a3184e/ijson-3.4.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9e9602157a5b869d44b6896e64f502c712a312fcde044c2e586fccb85d3e316e", size = 137903 }, - { url = "https://files.pythonhosted.org/packages/24/c6/41a9ad4d42df50ff6e70fdce79b034f09b914802737ebbdc141153d8d791/ijson-3.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1e83660edb931a425b7ff662eb49db1f10d30ca6d4d350e5630edbed098bc01", size = 148339 }, - { url = "https://files.pythonhosted.org/packages/5f/6f/7d01efda415b8502dce67e067ed9e8a124f53e763002c02207e542e1a2f1/ijson-3.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:49bf8eac1c7b7913073865a859c215488461f7591b4fa6a33c14b51cb73659d0", size = 149383 }, - { url = "https://files.pythonhosted.org/packages/95/6c/0d67024b9ecb57916c5e5ab0350251c9fe2f86dc9c8ca2b605c194bdad6a/ijson-3.4.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:160b09273cb42019f1811469508b0a057d19f26434d44752bde6f281da6d3f32", size = 141580 }, - { url = "https://files.pythonhosted.org/packages/06/43/e10edcc1c6a3b619294de835e7678bfb3a1b8a75955f3689fd66a1e9e7b4/ijson-3.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2019ff4e6f354aa00c76c8591bd450899111c61f2354ad55cc127e2ce2492c44", size = 150280 }, - { url = "https://files.pythonhosted.org/packages/07/84/1cbeee8e8190a1ebe6926569a92cf1fa80ddb380c129beb6f86559e1bb24/ijson-3.4.0-cp312-cp312-win32.whl", hash = "sha256:931c007bf6bb8330705429989b2deed6838c22b63358a330bf362b6e458ba0bf", size = 51512 }, - { url = "https://files.pythonhosted.org/packages/66/13/530802bc391c95be6fe9f96e9aa427d94067e7c0b7da7a9092344dc44c4b/ijson-3.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:71523f2b64cb856a820223e94d23e88369f193017ecc789bb4de198cc9d349eb", size = 54081 }, - { url = "https://files.pythonhosted.org/packages/77/b3/b1d2eb2745e5204ec7a25365a6deb7868576214feb5e109bce368fb692c9/ijson-3.4.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e8d96f88d75196a61c9d9443de2b72c2d4a7ba9456ff117b57ae3bba23a54256", size = 87216 }, - { url = "https://files.pythonhosted.org/packages/b1/cd/cd6d340087617f8cc9bedbb21d974542fe2f160ed0126b8288d3499a469b/ijson-3.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c45906ce2c1d3b62f15645476fc3a6ca279549127f01662a39ca5ed334a00cf9", size = 59170 }, - { url = "https://files.pythonhosted.org/packages/3e/4d/32d3a9903b488d3306e3c8288f6ee4217d2eea82728261db03a1045eb5d1/ijson-3.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4ab4bc2119b35c4363ea49f29563612237cae9413d2fbe54b223be098b97bc9e", size = 59013 }, - { url = "https://files.pythonhosted.org/packages/d5/c8/db15465ab4b0b477cee5964c8bfc94bf8c45af8e27a23e1ad78d1926e587/ijson-3.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:97b0a9b5a15e61dfb1f14921ea4e0dba39f3a650df6d8f444ddbc2b19b479ff1", size = 146564 }, - { url = "https://files.pythonhosted.org/packages/c4/d8/0755545bc122473a9a434ab90e0f378780e603d75495b1ca3872de757873/ijson-3.4.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e3047bb994dabedf11de11076ed1147a307924b6e5e2df6784fb2599c4ad8c60", size = 137917 }, - { url = "https://files.pythonhosted.org/packages/d0/c6/aeb89c8939ebe3f534af26c8c88000c5e870dbb6ae33644c21a4531f87d2/ijson-3.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:68c83161b052e9f5dc8191acbc862bb1e63f8a35344cb5cd0db1afd3afd487a6", size = 148897 }, - { url = "https://files.pythonhosted.org/packages/be/0e/7ef6e9b372106f2682a4a32b3c65bf86bb471a1670e4dac242faee4a7d3f/ijson-3.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1eebd9b6c20eb1dffde0ae1f0fbb4aeacec2eb7b89adb5c7c0449fc9fd742760", size = 149711 }, - { url = "https://files.pythonhosted.org/packages/d1/5d/9841c3ed75bcdabf19b3202de5f862a9c9c86ce5c7c9d95fa32347fdbf5f/ijson-3.4.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:13fb6d5c35192c541421f3ee81239d91fc15a8d8f26c869250f941f4b346a86c", size = 141691 }, - { url = "https://files.pythonhosted.org/packages/d5/d2/ce74e17218dba292e9be10a44ed0c75439f7958cdd263adb0b5b92d012d5/ijson-3.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:28b7196ff7b37c4897c547a28fa4876919696739fc91c1f347651c9736877c69", size = 150738 }, - { url = "https://files.pythonhosted.org/packages/4e/43/dcc480f94453b1075c9911d4755b823f3ace275761bb37b40139f22109ca/ijson-3.4.0-cp313-cp313-win32.whl", hash = "sha256:3c2691d2da42629522140f77b99587d6f5010440d58d36616f33bc7bdc830cc3", size = 51512 }, - { url = "https://files.pythonhosted.org/packages/35/dd/d8c5f15efd85ba51e6e11451ebe23d779361a9ec0d192064c2a8c3cdfcb8/ijson-3.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:c4554718c275a044c47eb3874f78f2c939f300215d9031e785a6711cc51b83fc", size = 54074 }, - { url = "https://files.pythonhosted.org/packages/79/73/24ad8cd106203419c4d22bed627e02e281d66b83e91bc206a371893d0486/ijson-3.4.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:915a65e3f3c0eee2ea937bc62aaedb6c14cc1e8f0bb9f3f4fb5a9e2bbfa4b480", size = 91694 }, - { url = "https://files.pythonhosted.org/packages/17/2d/f7f680984bcb7324a46a4c2df3bd73cf70faef0acfeb85a3f811abdfd590/ijson-3.4.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:afbe9748707684b6c5adc295c4fdcf27765b300aec4d484e14a13dca4e5c0afa", size = 61390 }, - { url = "https://files.pythonhosted.org/packages/09/a1/f3ca7bab86f95bdb82494739e71d271410dfefce4590785d511669127145/ijson-3.4.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:d823f8f321b4d8d5fa020d0a84f089fec5d52b7c0762430476d9f8bf95bbc1a9", size = 61140 }, - { url = "https://files.pythonhosted.org/packages/51/79/dd340df3d4fc7771c95df29997956b92ed0570fe7b616d1792fea9ad93f2/ijson-3.4.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b8a0a2c54f3becf76881188beefd98b484b1d3bd005769a740d5b433b089fa23", size = 214739 }, - { url = "https://files.pythonhosted.org/packages/59/f0/85380b7f51d1f5fb7065d76a7b623e02feca920cc678d329b2eccc0011e0/ijson-3.4.0-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ced19a83ab09afa16257a0b15bc1aa888dbc555cb754be09d375c7f8d41051f2", size = 198338 }, - { url = "https://files.pythonhosted.org/packages/a5/cd/313264cf2ec42e0f01d198c49deb7b6fadeb793b3685e20e738eb6b3fa13/ijson-3.4.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8100f9885eff1f38d35cef80ef759a1bbf5fc946349afa681bd7d0e681b7f1a0", size = 207515 }, - { url = "https://files.pythonhosted.org/packages/12/94/bf14457aa87ea32641f2db577c9188ef4e4ae373478afef422b31fc7f309/ijson-3.4.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:d7bcc3f7f21b0f703031ecd15209b1284ea51b2a329d66074b5261de3916c1eb", size = 210081 }, - { url = "https://files.pythonhosted.org/packages/7d/b4/eaee39e290e40e52d665db9bd1492cfdce86bd1e47948e0440db209c6023/ijson-3.4.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:2dcb190227b09dd171bdcbfe4720fddd574933c66314818dfb3960c8a6246a77", size = 199253 }, - { url = "https://files.pythonhosted.org/packages/c5/9c/e09c7b9ac720a703ab115b221b819f149ed54c974edfff623c1e925e57da/ijson-3.4.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:eda4cfb1d49c6073a901735aaa62e39cb7ab47f3ad7bb184862562f776f1fa8a", size = 203816 }, - { url = "https://files.pythonhosted.org/packages/7c/14/acd304f412e32d16a2c12182b9d78206bb0ae35354d35664f45db05c1b3b/ijson-3.4.0-cp313-cp313t-win32.whl", hash = "sha256:0772638efa1f3b72b51736833404f1cbd2f5beeb9c1a3d392e7d385b9160cba7", size = 53760 }, - { url = "https://files.pythonhosted.org/packages/2f/24/93dd0a467191590a5ed1fc2b35842bca9d09900d001e00b0b497c0208ef6/ijson-3.4.0-cp313-cp313t-win_amd64.whl", hash = "sha256:3d8a0d67f36e4fb97c61a724456ef0791504b16ce6f74917a31c2e92309bbeb9", size = 56948 }, - { url = "https://files.pythonhosted.org/packages/a7/22/da919f16ca9254f8a9ea0ba482d2c1d012ce6e4c712dcafd8adb16b16c63/ijson-3.4.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:54e989c35dba9cf163d532c14bcf0c260897d5f465643f0cd1fba9c908bed7ef", size = 56480 }, - { url = "https://files.pythonhosted.org/packages/6d/54/c2afd289e034d11c4909f4ea90c9dae55053bed358064f310c3dd5033657/ijson-3.4.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:494eeb8e87afef22fbb969a4cb81ac2c535f30406f334fb6136e9117b0bb5380", size = 55956 }, - { url = "https://files.pythonhosted.org/packages/43/d6/18799b0fca9ecb8a47e22527eedcea3267e95d4567b564ef21d0299e2d12/ijson-3.4.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:81603de95de1688958af65cd2294881a4790edae7de540b70c65c8253c5dc44a", size = 69394 }, - { url = "https://files.pythonhosted.org/packages/c2/d6/c58032c69e9e977bf6d954f22cad0cd52092db89c454ea98926744523665/ijson-3.4.0-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8524be12c1773e1be466034cc49c1ecbe3d5b47bb86217bd2a57f73f970a6c19", size = 70378 }, - { url = "https://files.pythonhosted.org/packages/da/03/07c6840454d5d228bb5b4509c9a7ac5b9c0b8258e2b317a53f97372be1eb/ijson-3.4.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17994696ec895d05e0cfa21b11c68c920c82634b4a3d8b8a1455d6fe9fdee8f7", size = 67770 }, - { url = "https://files.pythonhosted.org/packages/32/c7/da58a9840380308df574dfdb0276c9d802b12f6125f999e92bcef36db552/ijson-3.4.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:0b67727aaee55d43b2e82b6a866c3cbcb2b66a5e9894212190cbd8773d0d9857", size = 53858 }, - { url = "https://files.pythonhosted.org/packages/a3/9b/0bc0594d357600c03c3b5a3a34043d764fc3ad3f0757d2f3aae5b28f6c1c/ijson-3.4.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:cdc8c5ca0eec789ed99db29c68012dda05027af0860bb360afd28d825238d69d", size = 56483 }, - { url = "https://files.pythonhosted.org/packages/00/1f/506cf2574673da1adcc8a794ebb85bf857cabe6294523978637e646814de/ijson-3.4.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:8e6b44b6ec45d5b1a0ee9d97e0e65ab7f62258727004cbbe202bf5f198bc21f7", size = 55957 }, - { url = "https://files.pythonhosted.org/packages/dc/3d/a7cd8d8a6de0f3084fe4d457a8f76176e11b013867d1cad16c67d25e8bec/ijson-3.4.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b51e239e4cb537929796e840d349fc731fdc0d58b1a0683ce5465ad725321e0f", size = 69394 }, - { url = "https://files.pythonhosted.org/packages/32/51/aa30abc02aabfc41c95887acf5f1f88da569642d7197fbe5aa105545226d/ijson-3.4.0-pp311-pypy311_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ed05d43ec02be8ddb1ab59579761f6656b25d241a77fd74f4f0f7ec09074318a", size = 70377 }, - { url = "https://files.pythonhosted.org/packages/c7/37/7773659b8d8d98b34234e1237352f6b446a3c12941619686c7d4a8a5c69c/ijson-3.4.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cfeca1aaa59d93fd0a3718cbe5f7ef0effff85cf837e0bceb71831a47f39cc14", size = 67767 }, - { url = "https://files.pythonhosted.org/packages/cd/1f/dd52a84ed140e31a5d226cd47d98d21aa559aead35ef7bae479eab4c494c/ijson-3.4.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:7ca72ca12e9a1dd4252c97d952be34282907f263f7e28fcdff3a01b83981e837", size = 53864 }, +version = "3.4.0.post0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2d/30/7ab4b9e88e7946f6beef419f74edcc541df3ea562c7882257b4eaa82417d/ijson-3.4.0.post0.tar.gz", hash = "sha256:9aa02dc70bb245670a6ca7fba737b992aeeb4895360980622f7e568dbf23e41e", size = 67216 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b5/15/4f4921ed9ab94032fd0b03ecb211ff9dbd5cc9953463f5b5c4ddeab406fc/ijson-3.4.0.post0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8f904a405b58a04b6ef0425f1babbc5c65feb66b0a4cc7f214d4ad7de106f77d", size = 88244 }, + { url = "https://files.pythonhosted.org/packages/af/d6/b85d4da1752362a789bc3e0fc4b55e812a374a50d2fe1c06cab2e2bcb170/ijson-3.4.0.post0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a07dcc1a8a1ddd76131a7c7528cbd12951c2e34eb3c3d63697b905069a2d65b1", size = 59880 }, + { url = "https://files.pythonhosted.org/packages/c3/96/e1027e6d0efb5b9192bdc9f0af5633c20a56999cce4cf7ad35427f823138/ijson-3.4.0.post0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ab3be841b8c430c1883b8c0775eb551f21b5500c102c7ee828afa35ddd701bdd", size = 59939 }, + { url = "https://files.pythonhosted.org/packages/e3/71/b9ca0a19afb2f36be35c6afa2c4d1c19950dc45f6a50b483b56082b3e165/ijson-3.4.0.post0-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:43059ae0d657b11c5ddb11d149bc400c44f9e514fb8663057e9b2ea4d8d44c1f", size = 125894 }, + { url = "https://files.pythonhosted.org/packages/02/1b/f7356de078d85564829c5e2a2a31473ee0ad1876258ceecf550b582e57b7/ijson-3.4.0.post0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0d3e82963096579d1385c06b2559570d7191e225664b7fa049617da838e1a4a4", size = 132385 }, + { url = "https://files.pythonhosted.org/packages/57/7b/08f86eed5df0849b673260dd2943b6a7367a55b5a4b6e73ddbfbdf4206f1/ijson-3.4.0.post0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:461ce4e87a21a261b60c0a68a2ad17c7dd214f0b90a0bec7e559a66b6ae3bd7e", size = 129567 }, + { url = "https://files.pythonhosted.org/packages/96/e1/69672d95b1a16e7c6bf89cef6c892b228cc84b484945a731786a425700d2/ijson-3.4.0.post0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:890cf6610c9554efcb9765a93e368efeb5bb6135f59ce0828d92eaefff07fde5", size = 132821 }, + { url = "https://files.pythonhosted.org/packages/0b/15/9ed4868e2e92db2454508f7ea1282bec0b039bd344ac0cbac4a2de16786d/ijson-3.4.0.post0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:6793c29a5728e7751a7df01be58ba7da9b9690c12bf79d32094c70a908fa02b9", size = 127757 }, + { url = "https://files.pythonhosted.org/packages/5b/aa/08a308d3aaa6e98511f3100f8a1e4e8ff8c853fa4ec3f18b71094ac36bbe/ijson-3.4.0.post0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:a56b6674d7feec0401c91f86c376f4e3d8ff8129128a8ad21ca43ec0b1242f79", size = 130439 }, + { url = "https://files.pythonhosted.org/packages/56/46/3da05a044f335b97635d59eede016ea158fbf1b59e584149177b6524e1e5/ijson-3.4.0.post0-cp310-cp310-win32.whl", hash = "sha256:01767fcbd75a5fa5a626069787b41f04681216b798510d5f63bcf66884386368", size = 52004 }, + { url = "https://files.pythonhosted.org/packages/60/d7/a126d58f379df16fa9a0c2532ac00ae3debf1d28c090020775bc735032b8/ijson-3.4.0.post0-cp310-cp310-win_amd64.whl", hash = "sha256:09127c06e5dec753feb9e4b8c5f6a23603d1cd672d098159a17e53a73b898eec", size = 54407 }, + { url = "https://files.pythonhosted.org/packages/a7/ac/3d57249d4acba66a33eaef794edb5b2a2222ca449ae08800f8abe9286645/ijson-3.4.0.post0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0b473112e72c0c506da425da3278367b6680f340ecc093084693a1e819d28435", size = 88278 }, + { url = "https://files.pythonhosted.org/packages/12/fb/2d068d23d1a665f500282ceb6f2473952a95fc7107d739fd629b4ab41959/ijson-3.4.0.post0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:043f9b7cf9cc744263a78175e769947733710d2412d25180df44b1086b23ebd5", size = 59898 }, + { url = "https://files.pythonhosted.org/packages/26/3d/8b14589dfb0e5dbb7bcf9063e53d3617c041cf315ff3dfa60945382237ce/ijson-3.4.0.post0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b55e49045f4c8031f3673f56662fd828dc9e8d65bd3b03a9420dda0d370e64ba", size = 59945 }, + { url = "https://files.pythonhosted.org/packages/77/57/086a75094397d4b7584698a540a279689e12905271af78cdfc903bf9eaf8/ijson-3.4.0.post0-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:11f13b73194ea2a5a8b4a2863f25b0b4624311f10db3a75747b510c4958179b0", size = 131318 }, + { url = "https://files.pythonhosted.org/packages/df/35/7f61e9ce4a9ff1306ec581eb851f8a660439126d92ee595c6dc8084aac97/ijson-3.4.0.post0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:659acb2843433e080c271ecedf7d19c71adde1ee5274fc7faa2fec0a793f9f1c", size = 137990 }, + { url = "https://files.pythonhosted.org/packages/59/bf/590bbc3c3566adce5e2f43ba5894520cbaf19a3e7f38c1250926ba67eee4/ijson-3.4.0.post0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:deda4cfcaafa72ca3fa845350045b1d0fef9364ec9f413241bb46988afbe6ee6", size = 134416 }, + { url = "https://files.pythonhosted.org/packages/24/c1/fb719049851979df71f3e039d6f1a565d349c9cb1b29c0f8775d9db141b4/ijson-3.4.0.post0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:47352563e8c594360bacee2e0753e97025f0861234722d02faace62b1b6d2b2a", size = 138034 }, + { url = "https://files.pythonhosted.org/packages/10/ce/ccda891f572876aaf2c43f0b2079e31d5b476c3ae53196187eab1a788eff/ijson-3.4.0.post0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5a48b9486242d1295abe7fd0fbb6308867da5ca3f69b55c77922a93c2b6847aa", size = 132510 }, + { url = "https://files.pythonhosted.org/packages/11/b5/ca8e64ab7cf5252f358e467be767630f085b5bbcd3c04333a3a5f36c3dd3/ijson-3.4.0.post0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9c0886234d1fae15cf4581a430bdba03d79251c1ab3b07e30aa31b13ef28d01c", size = 134907 }, + { url = "https://files.pythonhosted.org/packages/93/14/63a4d5dc548690f29f0c2fc9cabd5ecbb37532547439c05f5b3b9ce73021/ijson-3.4.0.post0-cp311-cp311-win32.whl", hash = "sha256:fecae19b5187d92900c73debb3a979b0b3290a53f85df1f8f3c5ba7d1e9fb9cb", size = 52006 }, + { url = "https://files.pythonhosted.org/packages/fa/bf/932740899e572a97f9be0c6cd64ebda557eae7701ac216fc284aba21786d/ijson-3.4.0.post0-cp311-cp311-win_amd64.whl", hash = "sha256:b39dbf87071f23a23c8077eea2ae7cfeeca9ff9ffec722dfc8b5f352e4dd729c", size = 54410 }, + { url = "https://files.pythonhosted.org/packages/7d/fe/3b6af0025288e769dbfa30485dae1b3bd3f33f00390f3ee532cbb1c33e9b/ijson-3.4.0.post0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:b607a500fca26101be47d2baf7cddb457b819ab60a75ce51ed1092a40da8b2f9", size = 87847 }, + { url = "https://files.pythonhosted.org/packages/6e/a5/95ee2ca82f3b1a57892452f6e5087607d56c620beb8ce625475194568698/ijson-3.4.0.post0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4827d9874a6a81625412c59f7ca979a84d01f7f6bfb3c6d4dc4c46d0382b14e0", size = 59815 }, + { url = "https://files.pythonhosted.org/packages/51/8d/5a704ab3c17c55c21c86423458db8610626ca99cc9086a74dfeb7ee9054c/ijson-3.4.0.post0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d4d4afec780881edb2a0d2dd40b1cdbe246e630022d5192f266172a0307986a7", size = 59648 }, + { url = "https://files.pythonhosted.org/packages/25/56/ca5d6ca145d007f30b44e747f3c163bc08710ce004af0deaad4a2301339b/ijson-3.4.0.post0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:432fb60ffb952926f9438e0539011e2dfcd108f8426ee826ccc6173308c3ff2c", size = 138279 }, + { url = "https://files.pythonhosted.org/packages/c3/d3/22e3cc806fcdda7ad4c8482ed74db7a017d4a1d49b4300c7bc07052fb561/ijson-3.4.0.post0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:54a0e3e05d9a0c95ecba73d9579f146cf6d5c5874116c849dba2d39a5f30380e", size = 149110 }, + { url = "https://files.pythonhosted.org/packages/3e/04/efb30f413648b9267f5a33920ac124d7ebef3bc4063af8f6ffc8ca11ddcb/ijson-3.4.0.post0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:05807edc0bcbd222dc6ea32a2b897f0c81dc7f12c8580148bc82f6d7f5e7ec7b", size = 149026 }, + { url = "https://files.pythonhosted.org/packages/2d/cf/481165f7046ade32488719300a3994a437020bc41cfbb54334356348f513/ijson-3.4.0.post0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a5269af16f715855d9864937f9dd5c348ca1ac49cee6a2c7a1b7091c159e874f", size = 150012 }, + { url = "https://files.pythonhosted.org/packages/0f/24/642e3289917ecf860386e26dfde775f9962d26ab7f6c2e364ed3ca3c25d8/ijson-3.4.0.post0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b200df83c901f5bfa416d069ac71077aa1608f854a4c50df1b84ced560e9c9ec", size = 142193 }, + { url = "https://files.pythonhosted.org/packages/0f/f5/fd2f038abe95e553e1c3ee207cda19db9196eb416e63c7c89699a8cf0db7/ijson-3.4.0.post0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6458bd8e679cdff459a0a5e555b107c3bbacb1f382da3fe0f40e392871eb518d", size = 150904 }, + { url = "https://files.pythonhosted.org/packages/49/35/24259d22519987928164e6cb8fe3486e1df0899b2999ada4b0498639b463/ijson-3.4.0.post0-cp312-cp312-win32.whl", hash = "sha256:55f7f656b5986326c978cbb3a9eea9e33f3ef6ecc4535b38f1d452c731da39ab", size = 52358 }, + { url = "https://files.pythonhosted.org/packages/a1/2b/6f7ade27a8ff5758fc41006dadd2de01730def84fe3e60553b329c59e0d4/ijson-3.4.0.post0-cp312-cp312-win_amd64.whl", hash = "sha256:e15833dcf6f6d188fdc624a31cd0520c3ba21b6855dc304bc7c1a8aeca02d4ac", size = 54789 }, + { url = "https://files.pythonhosted.org/packages/1b/20/aaec6977f9d538bbadd760c7fa0f6a0937742abdcc920ec6478a8576e55f/ijson-3.4.0.post0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:114ed248166ac06377e87a245a158d6b98019d2bdd3bb93995718e0bd996154f", size = 87863 }, + { url = "https://files.pythonhosted.org/packages/5b/29/06bf56a866e2fe21453a1ad8f3a5d7bca3c723f73d96329656dfee969783/ijson-3.4.0.post0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ffb21203736b08fe27cb30df6a4f802fafb9ef7646c5ff7ef79569b63ea76c57", size = 59806 }, + { url = "https://files.pythonhosted.org/packages/ba/ae/e1d0fda91ba7a444b75f0d60cb845fdb1f55d3111351529dcbf4b1c276fe/ijson-3.4.0.post0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:07f20ecd748602ac7f18c617637e53bd73ded7f3b22260bba3abe401a7fc284e", size = 59643 }, + { url = "https://files.pythonhosted.org/packages/4d/24/5a24533be2726396cc1724dc237bada09b19715b5bfb0e7b9400db0901ad/ijson-3.4.0.post0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:27aa193d47ffc6bc4e45453896ad98fb089a367e8283b973f1fe5c0198b60b4e", size = 138082 }, + { url = "https://files.pythonhosted.org/packages/05/60/026c3efcec23c329657e878cbc0a9a25b42e7eb3971e8c2377cb3284e2b7/ijson-3.4.0.post0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ccddb2894eb7af162ba43b9475ac5825d15d568832f82eb8783036e5d2aebd42", size = 149145 }, + { url = "https://files.pythonhosted.org/packages/ed/c2/036499909b7a1bc0bcd85305e4348ad171aeb9df57581287533bdb3497e9/ijson-3.4.0.post0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:61ab0b8c5bf707201dc67e02c116f4b6545c4afd7feb2264b989d242d9c4348a", size = 149046 }, + { url = "https://files.pythonhosted.org/packages/ba/75/e7736073ad96867c129f9e799e3e65086badd89dbf3911f76d9b3bf8a115/ijson-3.4.0.post0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:254cfb8c124af68327a0e7a49b50bbdacafd87c4690a3d62c96eb01020a685ef", size = 150356 }, + { url = "https://files.pythonhosted.org/packages/9d/1b/1c1575d2cda136985561fcf774fe6c54412cd0fa08005342015af0403193/ijson-3.4.0.post0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:04ac9ca54db20f82aeda6379b5f4f6112fdb150d09ebce04affeab98a17b4ed3", size = 142322 }, + { url = "https://files.pythonhosted.org/packages/28/4d/aba9871feb624df8494435d1a9ddc7b6a4f782c6044bfc0d770a4b59f145/ijson-3.4.0.post0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a603d7474bf35e7b3a8e49c8dabfc4751841931301adff3f3318171c4e407f32", size = 151386 }, + { url = "https://files.pythonhosted.org/packages/3f/9a/791baa83895fb6e492bce2c7a0ea6427b6a41fe854349e62a37d0c9deaf0/ijson-3.4.0.post0-cp313-cp313-win32.whl", hash = "sha256:ec5bb1520cb212ebead7dba048bb9b70552c3440584f83b01b0abc96862e2a09", size = 52352 }, + { url = "https://files.pythonhosted.org/packages/a9/0c/061f51493e1da21116d74ee8f6a6b9ae06ca5fa2eb53c3b38b64f9a9a5ae/ijson-3.4.0.post0-cp313-cp313-win_amd64.whl", hash = "sha256:3505dff18bdeb8b171eb28af6df34857e2be80dc01e2e3b624e77215ad58897f", size = 54783 }, + { url = "https://files.pythonhosted.org/packages/c7/89/4344e176f2c5f5ef3251c9bfa4ddd5b4cf3f9601fd6ec3f677a3ba0b9c71/ijson-3.4.0.post0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:45a0b1c833ed2620eaf8da958f06ac8351c59e5e470e078400d23814670ed708", size = 92342 }, + { url = "https://files.pythonhosted.org/packages/d4/b1/85012c586a6645f9fb8bfa3ef62ed2f303c8d73fc7c2f705111582925980/ijson-3.4.0.post0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:7809ec8c8f40228edaaa089f33e811dff4c5b8509702652870d3f286c9682e27", size = 62028 }, + { url = "https://files.pythonhosted.org/packages/65/ea/7b7e2815c101d78b33e74d64ddb70cccc377afccd5dda76e566ed3fcb56f/ijson-3.4.0.post0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:cf4a34c2cfe852aee75c89c05b0a4531c49dc0be27eeed221afd6fbf9c3e149c", size = 61773 }, + { url = "https://files.pythonhosted.org/packages/59/7d/2175e599cb77a64f528629bad3ce95dfdf2aa6171d313c1fc00bbfaf0d22/ijson-3.4.0.post0-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:a39d5d36067604b26b78de70b8951c90e9272450642661fe531a8f7a6936a7fa", size = 198562 }, + { url = "https://files.pythonhosted.org/packages/13/97/82247c501c92405bb2fc44ab5efb497335bcb9cf0f5d3a0b04a800737bd8/ijson-3.4.0.post0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:83fc738d81c9ea686b452996110b8a6678296c481e0546857db24785bff8da92", size = 216212 }, + { url = "https://files.pythonhosted.org/packages/95/ca/b956f507bb02e05ce109fd11ab6a2c054f8b686cc5affe41afe50630984d/ijson-3.4.0.post0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b2a81aee91633868f5b40280e2523f7c5392e920a5082f47c5e991e516b483f6", size = 206618 }, + { url = "https://files.pythonhosted.org/packages/3e/12/e827840ab81d86a9882e499097934df53294f05155f1acfcb9a211ac1142/ijson-3.4.0.post0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:56169e298c5a2e7196aaa55da78ddc2415876a74fe6304f81b1eb0d3273346f7", size = 210689 }, + { url = "https://files.pythonhosted.org/packages/1b/3b/59238d9422c31a4aefa22ebeb8e599e706158a0ab03669ef623be77a499a/ijson-3.4.0.post0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:eeb9540f0b1a575cbb5968166706946458f98c16e7accc6f2fe71efa29864241", size = 199927 }, + { url = "https://files.pythonhosted.org/packages/b6/0f/ec01c36c128c37edb8a5ae8f3de3256009f886338d459210dfe121ee4ba9/ijson-3.4.0.post0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:ba3478ff0bb49d7ba88783f491a99b6e3fa929c930ab062d2bb7837e6a38fe88", size = 204455 }, + { url = "https://files.pythonhosted.org/packages/c8/cf/5560e1db96c6d10a5313be76bf5a1754266cbfb5cc13ff64d107829e07b1/ijson-3.4.0.post0-cp313-cp313t-win32.whl", hash = "sha256:b005ce84e82f28b00bf777a464833465dfe3efa43a0a26c77b5ac40723e1a728", size = 54566 }, + { url = "https://files.pythonhosted.org/packages/22/5a/cbb69144c3b25dd56f5421ff7dc0cf3051355579062024772518e4f4b3c5/ijson-3.4.0.post0-cp313-cp313t-win_amd64.whl", hash = "sha256:fe9c84c9b1c8798afa407be1cea1603401d99bfc7c34497e19f4f5e5ddc9b441", size = 57298 }, + { url = "https://files.pythonhosted.org/packages/af/0b/a4ce8524fd850302bbf5d9f38d07c0fa981fdbe44951d2fcd036935b67dd/ijson-3.4.0.post0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:da6a21b88cbf5ecbc53371283988d22c9643aa71ae2873bbeaefd2dea3b6160b", size = 88361 }, + { url = "https://files.pythonhosted.org/packages/be/90/a5e5f33e46f28174a9c8142d12dcb3d26ce358d9a2230b9b15f5c987b3a5/ijson-3.4.0.post0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:cf24a48a1c3ca9d44a04feb59ccefeb9aa52bb49b9cb70ad30518c25cce74bb7", size = 59960 }, + { url = "https://files.pythonhosted.org/packages/83/e2/551dd7037dda759aa0ce53f0d3d7be03b03c6b05c0b0a5d5ab7a47e6b4b1/ijson-3.4.0.post0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:d14427d366f95f21adcb97d0ed1f6d30f6fdc04d0aa1e4de839152c50c2b8d65", size = 59957 }, + { url = "https://files.pythonhosted.org/packages/ac/b9/3006384f85cc26cf83dbbd542d362cc336f1e1ddd491e32147cfa46ea8ae/ijson-3.4.0.post0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:339d49f6c5d24051c85d9226be96d2d56e633cb8b7d09dd8099de8d8b51a97e2", size = 139967 }, + { url = "https://files.pythonhosted.org/packages/77/3b/b5234add8115cbfe8635b6c152fb527327f45e4c0f0bf2e93844b36b5217/ijson-3.4.0.post0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7206afcb396aaef66c2b066997b4e9d9042c4b7d777f4d994e9cec6d322c2fe6", size = 149196 }, + { url = "https://files.pythonhosted.org/packages/a2/d2/c4ae543e37d7a9fba09740c221976a63705dbad23a9cda9022fc9fa0f3de/ijson-3.4.0.post0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c8dd327da225887194fe8b93f2b3c9c256353e14a6b9eefc940ed17fde38f5b8", size = 148516 }, + { url = "https://files.pythonhosted.org/packages/0d/a1/914b5fb1c26af2474cd04841626e0e95576499a4ca940661fb105ee12dd2/ijson-3.4.0.post0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:4810546e66128af51fd4a0c9a640e84e8508e9c15c4f247d8a3e3253b20e1465", size = 149770 }, + { url = "https://files.pythonhosted.org/packages/7a/c1/51c3584102d0d85d4aa10cc88dbbe431ecb9fe98160a9e2fad62a4456aed/ijson-3.4.0.post0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:103a0838061297d063bca81d724b0958b616f372bd893bbc278320152252c652", size = 143688 }, + { url = "https://files.pythonhosted.org/packages/47/3d/a54f13d766332620bded8ee76bcdd274509ecc53cf99573450f95b3ad910/ijson-3.4.0.post0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:40007c977e230e04118b27322f25a72ae342a3d61464b2057fcd9b21eeb7427a", size = 150688 }, + { url = "https://files.pythonhosted.org/packages/72/49/43d97cccf3266da7c044bd42e5083340ad1fd97fbb16d1bcd6791fd8918f/ijson-3.4.0.post0-cp314-cp314-win32.whl", hash = "sha256:f932969fc1fd4449ca141cf5f47ff357656a154a361f28d9ebca0badc5b02297", size = 52882 }, + { url = "https://files.pythonhosted.org/packages/e9/f0/008f1ed4e0fc6f6dc7a5a82ecf08a59bb212514e158954374d440d700e6c/ijson-3.4.0.post0-cp314-cp314-win_amd64.whl", hash = "sha256:3ed19b1e4349240773a8ce4a4bfa450892d4a57949c02c515cd6be5a46b7696a", size = 55568 }, + { url = "https://files.pythonhosted.org/packages/69/1c/8a199fded709e762aced89bb7086973c837e432dd714bbad78a6ac789c23/ijson-3.4.0.post0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:226447e40ca9340a39ed07d68ea02ee14b52cb4fe649425b256c1f0073531c83", size = 92345 }, + { url = "https://files.pythonhosted.org/packages/be/60/04e97f6a403203bd2eb8849570bdce5719d696b5fb96aa2a62566fe7a1d9/ijson-3.4.0.post0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2c88f0669d45d4b1aa017c9b68d378e7cd15d188dfb6f0209adc78b7f45590a7", size = 62029 }, + { url = "https://files.pythonhosted.org/packages/2a/97/e88295f9456ba939d90d4603af28fcabda3b443ef55e709e9381df3daa58/ijson-3.4.0.post0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:56b3089dc28c12492d92cc4896d2be585a89ecae34e25d08c1df88f21815cb50", size = 61776 }, + { url = "https://files.pythonhosted.org/packages/1b/9f/0e9c236e720c2de887ab0d7cad8a15d2aa55fb449f792437fc99899957a9/ijson-3.4.0.post0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:c117321cfa7b749cc1213f9b4c80dc958f0a206df98ec038ae4bcbbdb8463a15", size = 199808 }, + { url = "https://files.pythonhosted.org/packages/0e/70/c21de30e7013e074924cd82057acfc5760e7b2cc41180f80770621b0ad36/ijson-3.4.0.post0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8311f48db6a33116db5c81682f08b6e2405501a4b4e460193ae69fec3cd1f87a", size = 217152 }, + { url = "https://files.pythonhosted.org/packages/64/78/63a0bcc0707037df4e22bb836451279d850592258c859685a402c27f5d6d/ijson-3.4.0.post0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:91c61a3e63e04da648737e6b4abd537df1b46fb8cdf3219b072e790bb3c1a46b", size = 207663 }, + { url = "https://files.pythonhosted.org/packages/7d/85/834e9838d69893cb7567e1210be044444213c78f7414aaf1cd241df16078/ijson-3.4.0.post0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:1709171023ce82651b2f132575c2e6282e47f64ad67bd3260da476418d0e7895", size = 211157 }, + { url = "https://files.pythonhosted.org/packages/2e/9b/9fda503799ebc30397710552e5dedc1d98d9ea6a694e5717415892623a94/ijson-3.4.0.post0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:5f0a72b1e3c0f78551670c12b2fdc1bf05f2796254d9c2055ba319bec2216020", size = 200231 }, + { url = "https://files.pythonhosted.org/packages/15/f3/6419d1d5795a16591233d3aa3747b084e82c0c1d7184bdad9be638174560/ijson-3.4.0.post0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:b982a3597b0439ce9c8f4cfc929d86c6ed43907908be1e8463a34dc35fe5b258", size = 204825 }, + { url = "https://files.pythonhosted.org/packages/1f/8d/a520e6902129c55fa94428ea0a22e8547540d5e7ca30f18b39594a5feea2/ijson-3.4.0.post0-cp314-cp314t-win32.whl", hash = "sha256:4e39bfdc36b0b460ef15a06550a6a385c64c81f7ac205ccff39bd45147918912", size = 55559 }, + { url = "https://files.pythonhosted.org/packages/20/67/0ac6dd0045957ba1270b7b1860864f7d8cea4062e70b1083134c587e5768/ijson-3.4.0.post0-cp314-cp314t-win_amd64.whl", hash = "sha256:17e45262a5ddef39894013fb1548ee7094e444c8389eb1a97f86708b19bea03e", size = 58238 }, + { url = "https://files.pythonhosted.org/packages/43/66/27cfcea16e85b95e33814eae2052dab187206b8820cdd90aa39d32ffb441/ijson-3.4.0.post0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:add9242f886eae844a7410b84aee2bbb8bdc83c624f227cb1fdb2d0476a96cb1", size = 57029 }, + { url = "https://files.pythonhosted.org/packages/b8/1b/df3f1561c6629241fb2f8bd7ea1da14e3c2dd16fe9d7cbc97120870ed09c/ijson-3.4.0.post0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:69718ed41710dfcaa7564b0af42abc05875d4f7aaa24627c808867ef32634bc7", size = 56523 }, + { url = "https://files.pythonhosted.org/packages/39/0a/6c6a3221ddecf62b696fde0e864415237e05b9a36ab6685a606b8fb3b5a2/ijson-3.4.0.post0-pp311-pypy311_pp73-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:636b6eca96c6c43c04629c6b37fad0181662eaacf9877c71c698485637f752f9", size = 70546 }, + { url = "https://files.pythonhosted.org/packages/42/cb/edf69755e86a3a9f8b418efd60239cb308af46c7c8e12f869423f51c9851/ijson-3.4.0.post0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eb5e73028f6e63d27b3d286069fe350ed80a4ccc493b022b590fea4bb086710d", size = 70532 }, + { url = "https://files.pythonhosted.org/packages/96/7e/c8730ea39b8712622cd5a1bdff676098208400e37bb92052ba52f93e2aa1/ijson-3.4.0.post0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:461acf4320219459dabe5ed90a45cb86c9ba8cc6d6db9dad0d9427d42f57794c", size = 67927 }, + { url = "https://files.pythonhosted.org/packages/ec/f2/53b6e9bdd2a91202066764eaa74b572ba4dede0fe47a5a26f4de34b7541a/ijson-3.4.0.post0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:a0fedf09c0f6ffa2a99e7e7fd9c5f3caf74e655c1ee015a0797383e99382ebc3", size = 54657 }, ] [[package]] @@ -2020,74 +2061,91 @@ wheels = [ [[package]] name = "jiter" -version = "0.11.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9d/c0/a3bb4cc13aced219dd18191ea66e874266bd8aa7b96744e495e1c733aa2d/jiter-0.11.0.tar.gz", hash = "sha256:1d9637eaf8c1d6a63d6562f2a6e5ab3af946c66037eb1b894e8fad75422266e4", size = 167094 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/25/21/7dd1235a19e26979be6098e87e4cced2e061752f3a40a17bbce6dea7fae1/jiter-0.11.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:3893ce831e1c0094a83eeaf56c635a167d6fa8cc14393cc14298fd6fdc2a2449", size = 309875 }, - { url = "https://files.pythonhosted.org/packages/71/f9/462b54708aa85b135733ccba70529dd68a18511bf367a87c5fd28676c841/jiter-0.11.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:25c625b9b61b5a8725267fdf867ef2e51b429687f6a4eef211f4612e95607179", size = 316505 }, - { url = "https://files.pythonhosted.org/packages/bd/40/14e2eeaac6a47bff27d213834795472355fd39769272eb53cb7aa83d5aa8/jiter-0.11.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd4ca85fb6a62cf72e1c7f5e34ddef1b660ce4ed0886ec94a1ef9777d35eaa1f", size = 337613 }, - { url = "https://files.pythonhosted.org/packages/d3/ed/a5f1f8419c92b150a7c7fb5ccba1fb1e192887ad713d780e70874f0ce996/jiter-0.11.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:572208127034725e79c28437b82414028c3562335f2b4f451d98136d0fc5f9cd", size = 361438 }, - { url = "https://files.pythonhosted.org/packages/dd/f5/70682c023dfcdd463a53faf5d30205a7d99c51d70d3e303c932d0936e5a2/jiter-0.11.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:494ba627c7f550ad3dabb21862864b8f2216098dc18ff62f37b37796f2f7c325", size = 486180 }, - { url = "https://files.pythonhosted.org/packages/7c/39/020d08cbab4eab48142ad88b837c41eb08a15c0767fdb7c0d3265128a44b/jiter-0.11.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b8da18a99f58bca3ecc2d2bba99cac000a924e115b6c4f0a2b98f752b6fbf39a", size = 376681 }, - { url = "https://files.pythonhosted.org/packages/52/10/b86733f6e594cf51dd142f37c602d8df87c554c5844958deaab0de30eb5d/jiter-0.11.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4ffd3b0fff3fabbb02cc09910c08144db6bb5697a98d227a074401e01ee63dd", size = 348685 }, - { url = "https://files.pythonhosted.org/packages/fb/ee/8861665e83a9e703aa5f65fddddb6225428e163e6b0baa95a7f9a8fb9aae/jiter-0.11.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8fe6530aa738a4f7d4e4702aa8f9581425d04036a5f9e25af65ebe1f708f23be", size = 385573 }, - { url = "https://files.pythonhosted.org/packages/25/74/05afec03600951f128293813b5a208c9ba1bf587c57a344c05a42a69e1b1/jiter-0.11.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e35d66681c133a03d7e974e7eedae89720fe8ca3bd09f01a4909b86a8adf31f5", size = 516669 }, - { url = "https://files.pythonhosted.org/packages/93/d1/2e5bfe147cfbc2a5eef7f73eb75dc5c6669da4fa10fc7937181d93af9495/jiter-0.11.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c59459beca2fbc9718b6f1acb7bfb59ebc3eb4294fa4d40e9cb679dafdcc6c60", size = 508767 }, - { url = "https://files.pythonhosted.org/packages/87/50/597f71307e10426b5c082fd05d38c615ddbdd08c3348d8502963307f0652/jiter-0.11.0-cp310-cp310-win32.whl", hash = "sha256:b7b0178417b0dcfc5f259edbc6db2b1f5896093ed9035ee7bab0f2be8854726d", size = 205476 }, - { url = "https://files.pythonhosted.org/packages/c7/86/1e5214b3272e311754da26e63edec93a183811d4fc2e0118addec365df8b/jiter-0.11.0-cp310-cp310-win_amd64.whl", hash = "sha256:11df2bf99fb4754abddd7f5d940a48e51f9d11624d6313ca4314145fcad347f0", size = 204708 }, - { url = "https://files.pythonhosted.org/packages/38/55/a69fefeef09c2eaabae44b935a1aa81517e49639c0a0c25d861cb18cd7ac/jiter-0.11.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:cb5d9db02979c3f49071fce51a48f4b4e4cf574175fb2b11c7a535fa4867b222", size = 309503 }, - { url = "https://files.pythonhosted.org/packages/bd/d5/a6aba9e6551f32f9c127184f398208e4eddb96c59ac065c8a92056089d28/jiter-0.11.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1dc6a123f3471c4730db7ca8ba75f1bb3dcb6faeb8d46dd781083e7dee88b32d", size = 317688 }, - { url = "https://files.pythonhosted.org/packages/bb/f3/5e86f57c1883971cdc8535d0429c2787bf734840a231da30a3be12850562/jiter-0.11.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09858f8d230f031c7b8e557429102bf050eea29c77ad9c34c8fe253c5329acb7", size = 337418 }, - { url = "https://files.pythonhosted.org/packages/5e/4f/a71d8a24c2a70664970574a8e0b766663f5ef788f7fe1cc20ee0c016d488/jiter-0.11.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:dbe2196c4a0ce760925a74ab4456bf644748ab0979762139626ad138f6dac72d", size = 361423 }, - { url = "https://files.pythonhosted.org/packages/8f/e5/b09076f4e7fd9471b91e16f9f3dc7330b161b738f3b39b2c37054a36e26a/jiter-0.11.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5beb56d22b63647bafd0b74979216fdee80c580c0c63410be8c11053860ffd09", size = 486367 }, - { url = "https://files.pythonhosted.org/packages/fb/f1/98cb3a36f5e62f80cd860f0179f948d9eab5a316d55d3e1bab98d9767af5/jiter-0.11.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:97025d09ef549795d8dc720a824312cee3253c890ac73c621721ddfc75066789", size = 376335 }, - { url = "https://files.pythonhosted.org/packages/9f/d8/ec74886497ea393c29dbd7651ddecc1899e86404a6b1f84a3ddab0ab59fd/jiter-0.11.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d50880a6da65d8c23a2cf53c412847d9757e74cc9a3b95c5704a1d1a24667347", size = 348981 }, - { url = "https://files.pythonhosted.org/packages/24/93/d22ad7fa3b86ade66c86153ceea73094fc2af8b20c59cb7fceab9fea4704/jiter-0.11.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:452d80a1c86c095a242007bd9fc5d21b8a8442307193378f891cb8727e469648", size = 385797 }, - { url = "https://files.pythonhosted.org/packages/c8/bd/e25ff4a4df226e9b885f7cb01ee4b9dc74e3000e612d6f723860d71a1f34/jiter-0.11.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e84e58198d4894668eec2da660ffff60e0f3e60afa790ecc50cb12b0e02ca1d4", size = 516597 }, - { url = "https://files.pythonhosted.org/packages/be/fb/beda613db7d93ffa2fdd2683f90f2f5dce8daf4bc2d0d2829e7de35308c6/jiter-0.11.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:df64edcfc5dd5279a791eea52aa113d432c933119a025b0b5739f90d2e4e75f1", size = 508853 }, - { url = "https://files.pythonhosted.org/packages/20/64/c5b0d93490634e41e38e2a15de5d54fdbd2c9f64a19abb0f95305b63373c/jiter-0.11.0-cp311-cp311-win32.whl", hash = "sha256:144fc21337d21b1d048f7f44bf70881e1586401d405ed3a98c95a114a9994982", size = 205140 }, - { url = "https://files.pythonhosted.org/packages/a1/e6/c347c0e6f5796e97d4356b7e5ff0ce336498b7f4ef848fae621a56f1ccf3/jiter-0.11.0-cp311-cp311-win_amd64.whl", hash = "sha256:b0f32e644d241293b892b1a6dd8f0b9cc029bfd94c97376b2681c36548aabab7", size = 204311 }, - { url = "https://files.pythonhosted.org/packages/ba/b5/3009b112b8f673e568ef79af9863d8309a15f0a8cdcc06ed6092051f377e/jiter-0.11.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:2fb7b377688cc3850bbe5c192a6bd493562a0bc50cbc8b047316428fbae00ada", size = 305510 }, - { url = "https://files.pythonhosted.org/packages/fe/82/15514244e03b9e71e086bbe2a6de3e4616b48f07d5f834200c873956fb8c/jiter-0.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a1b7cbe3f25bd0d8abb468ba4302a5d45617ee61b2a7a638f63fee1dc086be99", size = 316521 }, - { url = "https://files.pythonhosted.org/packages/92/94/7a2e905f40ad2d6d660e00b68d818f9e29fb87ffe82774f06191e93cbe4a/jiter-0.11.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c0a7f0ec81d5b7588c5cade1eb1925b91436ae6726dc2df2348524aeabad5de6", size = 338214 }, - { url = "https://files.pythonhosted.org/packages/a8/9c/5791ed5bdc76f12110158d3316a7a3ec0b1413d018b41c5ed399549d3ad5/jiter-0.11.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:07630bb46ea2a6b9c6ed986c6e17e35b26148cce2c535454b26ee3f0e8dcaba1", size = 361280 }, - { url = "https://files.pythonhosted.org/packages/d4/7f/b7d82d77ff0d2cb06424141000176b53a9e6b16a1125525bb51ea4990c2e/jiter-0.11.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7764f27d28cd4a9cbc61704dfcd80c903ce3aad106a37902d3270cd6673d17f4", size = 487895 }, - { url = "https://files.pythonhosted.org/packages/42/44/10a1475d46f1fc1fd5cc2e82c58e7bca0ce5852208e0fa5df2f949353321/jiter-0.11.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1d4a6c4a737d486f77f842aeb22807edecb4a9417e6700c7b981e16d34ba7c72", size = 378421 }, - { url = "https://files.pythonhosted.org/packages/9a/5f/0dc34563d8164d31d07bc09d141d3da08157a68dcd1f9b886fa4e917805b/jiter-0.11.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cf408d2a0abd919b60de8c2e7bc5eeab72d4dafd18784152acc7c9adc3291591", size = 347932 }, - { url = "https://files.pythonhosted.org/packages/f7/de/b68f32a4fcb7b4a682b37c73a0e5dae32180140cd1caf11aef6ad40ddbf2/jiter-0.11.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cdef53eda7d18e799625023e1e250dbc18fbc275153039b873ec74d7e8883e09", size = 386959 }, - { url = "https://files.pythonhosted.org/packages/76/0a/c08c92e713b6e28972a846a81ce374883dac2f78ec6f39a0dad9f2339c3a/jiter-0.11.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:53933a38ef7b551dd9c7f1064f9d7bb235bb3168d0fa5f14f0798d1b7ea0d9c5", size = 517187 }, - { url = "https://files.pythonhosted.org/packages/89/b5/4a283bec43b15aad54fcae18d951f06a2ec3f78db5708d3b59a48e9c3fbd/jiter-0.11.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:11840d2324c9ab5162fc1abba23bc922124fedcff0d7b7f85fffa291e2f69206", size = 509461 }, - { url = "https://files.pythonhosted.org/packages/34/a5/f8bad793010534ea73c985caaeef8cc22dfb1fedb15220ecdf15c623c07a/jiter-0.11.0-cp312-cp312-win32.whl", hash = "sha256:4f01a744d24a5f2bb4a11657a1b27b61dc038ae2e674621a74020406e08f749b", size = 206664 }, - { url = "https://files.pythonhosted.org/packages/ed/42/5823ec2b1469395a160b4bf5f14326b4a098f3b6898fbd327366789fa5d3/jiter-0.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:29fff31190ab3a26de026da2f187814f4b9c6695361e20a9ac2123e4d4378a4c", size = 203520 }, - { url = "https://files.pythonhosted.org/packages/97/c4/d530e514d0f4f29b2b68145e7b389cbc7cac7f9c8c23df43b04d3d10fa3e/jiter-0.11.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:4441a91b80a80249f9a6452c14b2c24708f139f64de959943dfeaa6cb915e8eb", size = 305021 }, - { url = "https://files.pythonhosted.org/packages/7a/77/796a19c567c5734cbfc736a6f987affc0d5f240af8e12063c0fb93990ffa/jiter-0.11.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ff85fc6d2a431251ad82dbd1ea953affb5a60376b62e7d6809c5cd058bb39471", size = 314384 }, - { url = "https://files.pythonhosted.org/packages/14/9c/824334de0b037b91b6f3fa9fe5a191c83977c7ec4abe17795d3cb6d174cf/jiter-0.11.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5e86126d64706fd28dfc46f910d496923c6f95b395138c02d0e252947f452bd", size = 337389 }, - { url = "https://files.pythonhosted.org/packages/a2/95/ed4feab69e6cf9b2176ea29d4ef9d01a01db210a3a2c8a31a44ecdc68c38/jiter-0.11.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4ad8bd82165961867a10f52010590ce0b7a8c53da5ddd8bbb62fef68c181b921", size = 360519 }, - { url = "https://files.pythonhosted.org/packages/b5/0c/2ad00f38d3e583caba3909d95b7da1c3a7cd82c0aa81ff4317a8016fb581/jiter-0.11.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b42c2cd74273455ce439fd9528db0c6e84b5623cb74572305bdd9f2f2961d3df", size = 487198 }, - { url = "https://files.pythonhosted.org/packages/ea/8b/919b64cf3499b79bdfba6036da7b0cac5d62d5c75a28fb45bad7819e22f0/jiter-0.11.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f0062dab98172dd0599fcdbf90214d0dcde070b1ff38a00cc1b90e111f071982", size = 377835 }, - { url = "https://files.pythonhosted.org/packages/29/7f/8ebe15b6e0a8026b0d286c083b553779b4dd63db35b43a3f171b544de91d/jiter-0.11.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb948402821bc76d1f6ef0f9e19b816f9b09f8577844ba7140f0b6afe994bc64", size = 347655 }, - { url = "https://files.pythonhosted.org/packages/8e/64/332127cef7e94ac75719dda07b9a472af6158ba819088d87f17f3226a769/jiter-0.11.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:25a5b1110cca7329fd0daf5060faa1234be5c11e988948e4f1a1923b6a457fe1", size = 386135 }, - { url = "https://files.pythonhosted.org/packages/20/c8/557b63527442f84c14774159948262a9d4fabb0d61166f11568f22fc60d2/jiter-0.11.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:bf11807e802a214daf6c485037778843fadd3e2ec29377ae17e0706ec1a25758", size = 516063 }, - { url = "https://files.pythonhosted.org/packages/86/13/4164c819df4a43cdc8047f9a42880f0ceef5afeb22e8b9675c0528ebdccd/jiter-0.11.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:dbb57da40631c267861dd0090461222060960012d70fd6e4c799b0f62d0ba166", size = 508139 }, - { url = "https://files.pythonhosted.org/packages/fa/70/6e06929b401b331d41ddb4afb9f91cd1168218e3371972f0afa51c9f3c31/jiter-0.11.0-cp313-cp313-win32.whl", hash = "sha256:8e36924dad32c48d3c5e188d169e71dc6e84d6cb8dedefea089de5739d1d2f80", size = 206369 }, - { url = "https://files.pythonhosted.org/packages/f4/0d/8185b8e15de6dce24f6afae63380e16377dd75686d56007baa4f29723ea1/jiter-0.11.0-cp313-cp313-win_amd64.whl", hash = "sha256:452d13e4fd59698408087235259cebe67d9d49173b4dacb3e8d35ce4acf385d6", size = 202538 }, - { url = "https://files.pythonhosted.org/packages/13/3a/d61707803260d59520721fa326babfae25e9573a88d8b7b9cb54c5423a59/jiter-0.11.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:089f9df9f69532d1339e83142438668f52c97cd22ee2d1195551c2b1a9e6cf33", size = 313737 }, - { url = "https://files.pythonhosted.org/packages/cd/cc/c9f0eec5d00f2a1da89f6bdfac12b8afdf8d5ad974184863c75060026457/jiter-0.11.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:29ed1fe69a8c69bf0f2a962d8d706c7b89b50f1332cd6b9fbda014f60bd03a03", size = 346183 }, - { url = "https://files.pythonhosted.org/packages/a6/87/fc632776344e7aabbab05a95a0075476f418c5d29ab0f2eec672b7a1f0ac/jiter-0.11.0-cp313-cp313t-win_amd64.whl", hash = "sha256:a4d71d7ea6ea8786291423fe209acf6f8d398a0759d03e7f24094acb8ab686ba", size = 204225 }, - { url = "https://files.pythonhosted.org/packages/ee/3b/e7f45be7d3969bdf2e3cd4b816a7a1d272507cd0edd2d6dc4b07514f2d9a/jiter-0.11.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:9a6dff27eca70930bdbe4cbb7c1a4ba8526e13b63dc808c0670083d2d51a4a72", size = 304414 }, - { url = "https://files.pythonhosted.org/packages/06/32/13e8e0d152631fcc1907ceb4943711471be70496d14888ec6e92034e2caf/jiter-0.11.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:b1ae2a7593a62132c7d4c2abbee80bbbb94fdc6d157e2c6cc966250c564ef774", size = 314223 }, - { url = "https://files.pythonhosted.org/packages/0c/7e/abedd5b5a20ca083f778d96bba0d2366567fcecb0e6e34ff42640d5d7a18/jiter-0.11.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b13a431dba4b059e9e43019d3022346d009baf5066c24dcdea321a303cde9f0", size = 337306 }, - { url = "https://files.pythonhosted.org/packages/ac/e2/30d59bdc1204c86aa975ec72c48c482fee6633120ee9c3ab755e4dfefea8/jiter-0.11.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:af62e84ca3889604ebb645df3b0a3f3bcf6b92babbff642bd214616f57abb93a", size = 360565 }, - { url = "https://files.pythonhosted.org/packages/fe/88/567288e0d2ed9fa8f7a3b425fdaf2cb82b998633c24fe0d98f5417321aa8/jiter-0.11.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c6f3b32bb723246e6b351aecace52aba78adb8eeb4b2391630322dc30ff6c773", size = 486465 }, - { url = "https://files.pythonhosted.org/packages/18/6e/7b72d09273214cadd15970e91dd5ed9634bee605176107db21e1e4205eb1/jiter-0.11.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:adcab442f4a099a358a7f562eaa54ed6456fb866e922c6545a717be51dbed7d7", size = 377581 }, - { url = "https://files.pythonhosted.org/packages/58/52/4db456319f9d14deed325f70102577492e9d7e87cf7097bda9769a1fcacb/jiter-0.11.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9967c2ab338ee2b2c0102fd379ec2693c496abf71ffd47e4d791d1f593b68e2", size = 347102 }, - { url = "https://files.pythonhosted.org/packages/ce/b4/433d5703c38b26083aec7a733eb5be96f9c6085d0e270a87ca6482cbf049/jiter-0.11.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e7d0bed3b187af8b47a981d9742ddfc1d9b252a7235471ad6078e7e4e5fe75c2", size = 386477 }, - { url = "https://files.pythonhosted.org/packages/c8/7a/a60bfd9c55b55b07c5c441c5085f06420b6d493ce9db28d069cc5b45d9f3/jiter-0.11.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:f6fe0283e903ebc55f1a6cc569b8c1f3bf4abd026fed85e3ff8598a9e6f982f0", size = 516004 }, - { url = "https://files.pythonhosted.org/packages/2e/46/f8363e5ecc179b4ed0ca6cb0a6d3bfc266078578c71ff30642ea2ce2f203/jiter-0.11.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:4ee5821e3d66606b29ae5b497230b304f1376f38137d69e35f8d2bd5f310ff73", size = 507855 }, - { url = "https://files.pythonhosted.org/packages/90/33/396083357d51d7ff0f9805852c288af47480d30dd31d8abc74909b020761/jiter-0.11.0-cp314-cp314-win32.whl", hash = "sha256:c2d13ba7567ca8799f17c76ed56b1d49be30df996eb7fa33e46b62800562a5e2", size = 205802 }, - { url = "https://files.pythonhosted.org/packages/e7/ab/eb06ca556b2551d41de7d03bf2ee24285fa3d0c58c5f8d95c64c9c3281b1/jiter-0.11.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:fb4790497369d134a07fc763cc88888c46f734abdd66f9fdf7865038bf3a8f40", size = 313405 }, - { url = "https://files.pythonhosted.org/packages/af/22/7ab7b4ec3a1c1f03aef376af11d23b05abcca3fb31fbca1e7557053b1ba2/jiter-0.11.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e2bbf24f16ba5ad4441a9845e40e4ea0cb9eed00e76ba94050664ef53ef4406", size = 347102 }, +version = "0.11.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a3/68/0357982493a7b20925aece061f7fb7a2678e3b232f8d73a6edb7e5304443/jiter-0.11.1.tar.gz", hash = "sha256:849dcfc76481c0ea0099391235b7ca97d7279e0fa4c86005457ac7c88e8b76dc", size = 168385 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/12/10/d099def5716452c8d5ffa527405373a44ddaf8e3c9d4f6de1e1344cffd90/jiter-0.11.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:ed58841a491bbbf3f7c55a6b68fff568439ab73b2cce27ace0e169057b5851df", size = 310078 }, + { url = "https://files.pythonhosted.org/packages/fe/56/b81d010b0031ffa96dfb590628562ac5f513ce56aa2ab451d29fb3fedeb9/jiter-0.11.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:499beb9b2d7e51d61095a8de39ebcab1d1778f2a74085f8305a969f6cee9f3e4", size = 317138 }, + { url = "https://files.pythonhosted.org/packages/89/12/31ea12af9d79671cc7bd893bf0ccaf3467624c0fc7146a0cbfe7b549bcfa/jiter-0.11.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b87b2821795e28cc990939b68ce7a038edea680a24910bd68a79d54ff3f03c02", size = 348964 }, + { url = "https://files.pythonhosted.org/packages/bc/d2/95cb6dc5ff962410667a29708c7a6c0691cc3c4866a0bfa79d085b56ebd6/jiter-0.11.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:83f6fa494d8bba14ab100417c80e70d32d737e805cb85be2052d771c76fcd1f8", size = 363289 }, + { url = "https://files.pythonhosted.org/packages/b8/3e/37006ad5843a0bc3a3ec3a6c44710d7a154113befaf5f26d2fe190668b63/jiter-0.11.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5fbc6aea1daa2ec6f5ed465f0c5e7b0607175062ceebbea5ca70dd5ddab58083", size = 487243 }, + { url = "https://files.pythonhosted.org/packages/80/5c/d38c8c801a322a0c0de47b9618c16fd766366f087ce37c4e55ae8e3c8b03/jiter-0.11.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:302288e2edc43174bb2db838e94688d724f9aad26c5fb9a74f7a5fb427452a6a", size = 376139 }, + { url = "https://files.pythonhosted.org/packages/b0/cd/442ad2389a5570b0ee673f93e14bbe8cdecd3e08a9ba7756081d84065e4c/jiter-0.11.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85db563fe3b367bb568af5d29dea4d4066d923b8e01f3417d25ebecd958de815", size = 359279 }, + { url = "https://files.pythonhosted.org/packages/9a/35/8f5810d0e7d00bc395889085dbc1ccc36d454b56f28b2a5359dfd1bab48d/jiter-0.11.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f1c1ba2b6b22f775444ef53bc2d5778396d3520abc7b2e1da8eb0c27cb3ffb10", size = 384911 }, + { url = "https://files.pythonhosted.org/packages/3c/bd/8c069ceb0bafcf6b4aa5de0c27f02faf50468df39564a02e1a12389ad6c2/jiter-0.11.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:523be464b14f8fd0cc78da6964b87b5515a056427a2579f9085ce30197a1b54a", size = 517879 }, + { url = "https://files.pythonhosted.org/packages/bc/3c/9163efcf762f79f47433078b4f0a1bddc56096082c02c6cae2f47f07f56f/jiter-0.11.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:25b99b3f04cd2a38fefb22e822e35eb203a2cd37d680dbbc0c0ba966918af336", size = 508739 }, + { url = "https://files.pythonhosted.org/packages/44/07/50690f257935845d3114b95b5dd03749eeaab5e395cbb522f9e957da4551/jiter-0.11.1-cp310-cp310-win32.whl", hash = "sha256:47a79e90545a596bb9104109777894033347b11180d4751a216afef14072dbe7", size = 203948 }, + { url = "https://files.pythonhosted.org/packages/d2/3a/5964a944bf2e98ffd566153fdc2a6a368fcb11b58cc46832ca8c75808dba/jiter-0.11.1-cp310-cp310-win_amd64.whl", hash = "sha256:cace75621ae9bd66878bf69fbd4dfc1a28ef8661e0c2d0eb72d3d6f1268eddf5", size = 207522 }, + { url = "https://files.pythonhosted.org/packages/8b/34/c9e6cfe876f9a24f43ed53fe29f052ce02bd8d5f5a387dbf46ad3764bef0/jiter-0.11.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:9b0088ff3c374ce8ce0168523ec8e97122ebb788f950cf7bb8e39c7dc6a876a2", size = 310160 }, + { url = "https://files.pythonhosted.org/packages/bc/9f/b06ec8181d7165858faf2ac5287c54fe52b2287760b7fe1ba9c06890255f/jiter-0.11.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:74433962dd3c3090655e02e461267095d6c84f0741c7827de11022ef8d7ff661", size = 316573 }, + { url = "https://files.pythonhosted.org/packages/66/49/3179d93090f2ed0c6b091a9c210f266d2d020d82c96f753260af536371d0/jiter-0.11.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6d98030e345e6546df2cc2c08309c502466c66c4747b043f1a0d415fada862b8", size = 348998 }, + { url = "https://files.pythonhosted.org/packages/ae/9d/63db2c8eabda7a9cad65a2e808ca34aaa8689d98d498f5a2357d7a2e2cec/jiter-0.11.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1d6db0b2e788db46bec2cf729a88b6dd36959af2abd9fa2312dfba5acdd96dcb", size = 363413 }, + { url = "https://files.pythonhosted.org/packages/25/ff/3e6b3170c5053053c7baddb8d44e2bf11ff44cd71024a280a8438ae6ba32/jiter-0.11.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:55678fbbda261eafe7289165dd2ddd0e922df5f9a1ae46d7c79a5a15242bd7d1", size = 487144 }, + { url = "https://files.pythonhosted.org/packages/b0/50/b63fcadf699893269b997f4c2e88400bc68f085c6db698c6e5e69d63b2c1/jiter-0.11.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6a6b74fae8e40497653b52ce6ca0f1b13457af769af6fb9c1113efc8b5b4d9be", size = 376215 }, + { url = "https://files.pythonhosted.org/packages/39/8c/57a8a89401134167e87e73471b9cca321cf651c1fd78c45f3a0f16932213/jiter-0.11.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0a55a453f8b035eb4f7852a79a065d616b7971a17f5e37a9296b4b38d3b619e4", size = 359163 }, + { url = "https://files.pythonhosted.org/packages/4b/96/30b0cdbffbb6f753e25339d3dbbe26890c9ef119928314578201c758aace/jiter-0.11.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2638148099022e6bdb3f42904289cd2e403609356fb06eb36ddec2d50958bc29", size = 385344 }, + { url = "https://files.pythonhosted.org/packages/c6/d5/31dae27c1cc9410ad52bb514f11bfa4f286f7d6ef9d287b98b8831e156ec/jiter-0.11.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:252490567a5d990986f83b95a5f1ca1bf205ebd27b3e9e93bb7c2592380e29b9", size = 517972 }, + { url = "https://files.pythonhosted.org/packages/61/1e/5905a7a3aceab80de13ab226fd690471a5e1ee7e554dc1015e55f1a6b896/jiter-0.11.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d431d52b0ca2436eea6195f0f48528202100c7deda354cb7aac0a302167594d5", size = 508408 }, + { url = "https://files.pythonhosted.org/packages/91/12/1c49b97aa49077e136e8591cef7162f0d3e2860ae457a2d35868fd1521ef/jiter-0.11.1-cp311-cp311-win32.whl", hash = "sha256:db6f41e40f8bae20c86cb574b48c4fd9f28ee1c71cb044e9ec12e78ab757ba3a", size = 203937 }, + { url = "https://files.pythonhosted.org/packages/6d/9d/2255f7c17134ee9892c7e013c32d5bcf4bce64eb115402c9fe5e727a67eb/jiter-0.11.1-cp311-cp311-win_amd64.whl", hash = "sha256:0cc407b8e6cdff01b06bb80f61225c8b090c3df108ebade5e0c3c10993735b19", size = 207589 }, + { url = "https://files.pythonhosted.org/packages/3c/28/6307fc8f95afef84cae6caf5429fee58ef16a582c2ff4db317ceb3e352fa/jiter-0.11.1-cp311-cp311-win_arm64.whl", hash = "sha256:fe04ea475392a91896d1936367854d346724a1045a247e5d1c196410473b8869", size = 188391 }, + { url = "https://files.pythonhosted.org/packages/15/8b/318e8af2c904a9d29af91f78c1e18f0592e189bbdb8a462902d31fe20682/jiter-0.11.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:c92148eec91052538ce6823dfca9525f5cfc8b622d7f07e9891a280f61b8c96c", size = 305655 }, + { url = "https://files.pythonhosted.org/packages/f7/29/6c7de6b5d6e511d9e736312c0c9bfcee8f9b6bef68182a08b1d78767e627/jiter-0.11.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ecd4da91b5415f183a6be8f7158d127bdd9e6a3174138293c0d48d6ea2f2009d", size = 315645 }, + { url = "https://files.pythonhosted.org/packages/ac/5f/ef9e5675511ee0eb7f98dd8c90509e1f7743dbb7c350071acae87b0145f3/jiter-0.11.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7e3ac25c00b9275684d47aa42febaa90a9958e19fd1726c4ecf755fbe5e553b", size = 348003 }, + { url = "https://files.pythonhosted.org/packages/56/1b/abe8c4021010b0a320d3c62682769b700fb66f92c6db02d1a1381b3db025/jiter-0.11.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:57d7305c0a841858f866cd459cd9303f73883fb5e097257f3d4a3920722c69d4", size = 365122 }, + { url = "https://files.pythonhosted.org/packages/2a/2d/4a18013939a4f24432f805fbd5a19893e64650b933edb057cd405275a538/jiter-0.11.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e86fa10e117dce22c547f31dd6d2a9a222707d54853d8de4e9a2279d2c97f239", size = 488360 }, + { url = "https://files.pythonhosted.org/packages/f0/77/38124f5d02ac4131f0dfbcfd1a19a0fac305fa2c005bc4f9f0736914a1a4/jiter-0.11.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ae5ef1d48aec7e01ee8420155d901bb1d192998fa811a65ebb82c043ee186711", size = 376884 }, + { url = "https://files.pythonhosted.org/packages/7b/43/59fdc2f6267959b71dd23ce0bd8d4aeaf55566aa435a5d00f53d53c7eb24/jiter-0.11.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb68e7bf65c990531ad8715e57d50195daf7c8e6f1509e617b4e692af1108939", size = 358827 }, + { url = "https://files.pythonhosted.org/packages/7d/d0/b3cc20ff5340775ea3bbaa0d665518eddecd4266ba7244c9cb480c0c82ec/jiter-0.11.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:43b30c8154ded5845fa454ef954ee67bfccce629b2dea7d01f795b42bc2bda54", size = 385171 }, + { url = "https://files.pythonhosted.org/packages/d2/bc/94dd1f3a61f4dc236f787a097360ec061ceeebebf4ea120b924d91391b10/jiter-0.11.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:586cafbd9dd1f3ce6a22b4a085eaa6be578e47ba9b18e198d4333e598a91db2d", size = 518359 }, + { url = "https://files.pythonhosted.org/packages/7e/8c/12ee132bd67e25c75f542c227f5762491b9a316b0dad8e929c95076f773c/jiter-0.11.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:677cc2517d437a83bb30019fd4cf7cad74b465914c56ecac3440d597ac135250", size = 509205 }, + { url = "https://files.pythonhosted.org/packages/39/d5/9de848928ce341d463c7e7273fce90ea6d0ea4343cd761f451860fa16b59/jiter-0.11.1-cp312-cp312-win32.whl", hash = "sha256:fa992af648fcee2b850a3286a35f62bbbaeddbb6dbda19a00d8fbc846a947b6e", size = 205448 }, + { url = "https://files.pythonhosted.org/packages/ee/b0/8002d78637e05009f5e3fb5288f9d57d65715c33b5d6aa20fd57670feef5/jiter-0.11.1-cp312-cp312-win_amd64.whl", hash = "sha256:88b5cae9fa51efeb3d4bd4e52bfd4c85ccc9cac44282e2a9640893a042ba4d87", size = 204285 }, + { url = "https://files.pythonhosted.org/packages/9f/a2/bb24d5587e4dff17ff796716542f663deee337358006a80c8af43ddc11e5/jiter-0.11.1-cp312-cp312-win_arm64.whl", hash = "sha256:9a6cae1ab335551917f882f2c3c1efe7617b71b4c02381e4382a8fc80a02588c", size = 188712 }, + { url = "https://files.pythonhosted.org/packages/7c/4b/e4dd3c76424fad02a601d570f4f2a8438daea47ba081201a721a903d3f4c/jiter-0.11.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:71b6a920a5550f057d49d0e8bcc60945a8da998019e83f01adf110e226267663", size = 305272 }, + { url = "https://files.pythonhosted.org/packages/67/83/2cd3ad5364191130f4de80eacc907f693723beaab11a46c7d155b07a092c/jiter-0.11.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0b3de72e925388453a5171be83379549300db01284f04d2a6f244d1d8de36f94", size = 314038 }, + { url = "https://files.pythonhosted.org/packages/d3/3c/8e67d9ba524e97d2f04c8f406f8769a23205026b13b0938d16646d6e2d3e/jiter-0.11.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc19dd65a2bd3d9c044c5b4ebf657ca1e6003a97c0fc10f555aa4f7fb9821c00", size = 345977 }, + { url = "https://files.pythonhosted.org/packages/8d/a5/489ce64d992c29bccbffabb13961bbb0435e890d7f2d266d1f3df5e917d2/jiter-0.11.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d58faaa936743cd1464540562f60b7ce4fd927e695e8bc31b3da5b914baa9abd", size = 364503 }, + { url = "https://files.pythonhosted.org/packages/d4/c0/e321dd83ee231d05c8fe4b1a12caf1f0e8c7a949bf4724d58397104f10f2/jiter-0.11.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:902640c3103625317291cb73773413b4d71847cdf9383ba65528745ff89f1d14", size = 487092 }, + { url = "https://files.pythonhosted.org/packages/f9/5e/8f24ec49c8d37bd37f34ec0112e0b1a3b4b5a7b456c8efff1df5e189ad43/jiter-0.11.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:30405f726e4c2ed487b176c09f8b877a957f535d60c1bf194abb8dadedb5836f", size = 376328 }, + { url = "https://files.pythonhosted.org/packages/7f/70/ded107620e809327cf7050727e17ccfa79d6385a771b7fe38fb31318ef00/jiter-0.11.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3217f61728b0baadd2551844870f65219ac4a1285d5e1a4abddff3d51fdabe96", size = 356632 }, + { url = "https://files.pythonhosted.org/packages/19/53/c26f7251613f6a9079275ee43c89b8a973a95ff27532c421abc2a87afb04/jiter-0.11.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b1364cc90c03a8196f35f396f84029f12abe925415049204446db86598c8b72c", size = 384358 }, + { url = "https://files.pythonhosted.org/packages/84/16/e0f2cc61e9c4d0b62f6c1bd9b9781d878a427656f88293e2a5335fa8ff07/jiter-0.11.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:53a54bf8e873820ab186b2dca9f6c3303f00d65ae5e7b7d6bda1b95aa472d646", size = 517279 }, + { url = "https://files.pythonhosted.org/packages/60/5c/4cd095eaee68961bca3081acbe7c89e12ae24a5dae5fd5d2a13e01ed2542/jiter-0.11.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:7e29aca023627b0e0c2392d4248f6414d566ff3974fa08ff2ac8dbb96dfee92a", size = 508276 }, + { url = "https://files.pythonhosted.org/packages/4f/25/f459240e69b0e09a7706d96ce203ad615ca36b0fe832308d2b7123abf2d0/jiter-0.11.1-cp313-cp313-win32.whl", hash = "sha256:f153e31d8bca11363751e875c0a70b3d25160ecbaee7b51e457f14498fb39d8b", size = 205593 }, + { url = "https://files.pythonhosted.org/packages/7c/16/461bafe22bae79bab74e217a09c907481a46d520c36b7b9fe71ee8c9e983/jiter-0.11.1-cp313-cp313-win_amd64.whl", hash = "sha256:f773f84080b667c69c4ea0403fc67bb08b07e2b7ce1ef335dea5868451e60fed", size = 203518 }, + { url = "https://files.pythonhosted.org/packages/7b/72/c45de6e320edb4fa165b7b1a414193b3cae302dd82da2169d315dcc78b44/jiter-0.11.1-cp313-cp313-win_arm64.whl", hash = "sha256:635ecd45c04e4c340d2187bcb1cea204c7cc9d32c1364d251564bf42e0e39c2d", size = 188062 }, + { url = "https://files.pythonhosted.org/packages/65/9b/4a57922437ca8753ef823f434c2dec5028b237d84fa320f06a3ba1aec6e8/jiter-0.11.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:d892b184da4d94d94ddb4031296931c74ec8b325513a541ebfd6dfb9ae89904b", size = 313814 }, + { url = "https://files.pythonhosted.org/packages/76/50/62a0683dadca25490a4bedc6a88d59de9af2a3406dd5a576009a73a1d392/jiter-0.11.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa22c223a3041dacb2fcd37c70dfd648b44662b4a48e242592f95bda5ab09d58", size = 344987 }, + { url = "https://files.pythonhosted.org/packages/da/00/2355dbfcbf6cdeaddfdca18287f0f38ae49446bb6378e4a5971e9356fc8a/jiter-0.11.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:330e8e6a11ad4980cd66a0f4a3e0e2e0f646c911ce047014f984841924729789", size = 356399 }, + { url = "https://files.pythonhosted.org/packages/c9/07/c2bd748d578fa933d894a55bff33f983bc27f75fc4e491b354bef7b78012/jiter-0.11.1-cp313-cp313t-win_amd64.whl", hash = "sha256:09e2e386ebf298547ca3a3704b729471f7ec666c2906c5c26c1a915ea24741ec", size = 203289 }, + { url = "https://files.pythonhosted.org/packages/e6/ee/ace64a853a1acbd318eb0ca167bad1cf5ee037207504b83a868a5849747b/jiter-0.11.1-cp313-cp313t-win_arm64.whl", hash = "sha256:fe4a431c291157e11cee7c34627990ea75e8d153894365a3bc84b7a959d23ca8", size = 188284 }, + { url = "https://files.pythonhosted.org/packages/8d/00/d6006d069e7b076e4c66af90656b63da9481954f290d5eca8c715f4bf125/jiter-0.11.1-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:0fa1f70da7a8a9713ff8e5f75ec3f90c0c870be6d526aa95e7c906f6a1c8c676", size = 304624 }, + { url = "https://files.pythonhosted.org/packages/fc/45/4a0e31eb996b9ccfddbae4d3017b46f358a599ccf2e19fbffa5e531bd304/jiter-0.11.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:569ee559e5046a42feb6828c55307cf20fe43308e3ae0d8e9e4f8d8634d99944", size = 315042 }, + { url = "https://files.pythonhosted.org/packages/e7/91/22f5746f5159a28c76acdc0778801f3c1181799aab196dbea2d29e064968/jiter-0.11.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f69955fa1d92e81987f092b233f0be49d4c937da107b7f7dcf56306f1d3fcce9", size = 346357 }, + { url = "https://files.pythonhosted.org/packages/f5/4f/57620857d4e1dc75c8ff4856c90cb6c135e61bff9b4ebfb5dc86814e82d7/jiter-0.11.1-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:090f4c9d4a825e0fcbd0a2647c9a88a0f366b75654d982d95a9590745ff0c48d", size = 365057 }, + { url = "https://files.pythonhosted.org/packages/ce/34/caf7f9cc8ae0a5bb25a5440cc76c7452d264d1b36701b90fdadd28fe08ec/jiter-0.11.1-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bbf3d8cedf9e9d825233e0dcac28ff15c47b7c5512fdfe2e25fd5bbb6e6b0cee", size = 487086 }, + { url = "https://files.pythonhosted.org/packages/50/17/85b5857c329d533d433fedf98804ebec696004a1f88cabad202b2ddc55cf/jiter-0.11.1-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2aa9b1958f9c30d3d1a558b75f0626733c60eb9b7774a86b34d88060be1e67fe", size = 376083 }, + { url = "https://files.pythonhosted.org/packages/85/d3/2d9f973f828226e6faebdef034097a2918077ea776fb4d88489949024787/jiter-0.11.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e42d1ca16590b768c5e7d723055acd2633908baacb3628dd430842e2e035aa90", size = 357825 }, + { url = "https://files.pythonhosted.org/packages/f4/55/848d4dabf2c2c236a05468c315c2cb9dc736c5915e65449ccecdba22fb6f/jiter-0.11.1-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5db4c2486a023820b701a17aec9c5a6173c5ba4393f26662f032f2de9c848b0f", size = 383933 }, + { url = "https://files.pythonhosted.org/packages/0b/6c/204c95a4fbb0e26dfa7776c8ef4a878d0c0b215868011cc904bf44f707e2/jiter-0.11.1-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:4573b78777ccfac954859a6eff45cbd9d281d80c8af049d0f1a3d9fc323d5c3a", size = 517118 }, + { url = "https://files.pythonhosted.org/packages/88/25/09956644ea5a2b1e7a2a0f665cb69a973b28f4621fa61fc0c0f06ff40a31/jiter-0.11.1-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:7593ac6f40831d7961cb67633c39b9fef6689a211d7919e958f45710504f52d3", size = 508194 }, + { url = "https://files.pythonhosted.org/packages/09/49/4d1657355d7f5c9e783083a03a3f07d5858efa6916a7d9634d07db1c23bd/jiter-0.11.1-cp314-cp314-win32.whl", hash = "sha256:87202ec6ff9626ff5f9351507def98fcf0df60e9a146308e8ab221432228f4ea", size = 203961 }, + { url = "https://files.pythonhosted.org/packages/76/bd/f063bd5cc2712e7ca3cf6beda50894418fc0cfeb3f6ff45a12d87af25996/jiter-0.11.1-cp314-cp314-win_amd64.whl", hash = "sha256:a5dd268f6531a182c89d0dd9a3f8848e86e92dfff4201b77a18e6b98aa59798c", size = 202804 }, + { url = "https://files.pythonhosted.org/packages/52/ca/4d84193dfafef1020bf0bedd5e1a8d0e89cb67c54b8519040effc694964b/jiter-0.11.1-cp314-cp314-win_arm64.whl", hash = "sha256:5d761f863f912a44748a21b5c4979c04252588ded8d1d2760976d2e42cd8d991", size = 188001 }, + { url = "https://files.pythonhosted.org/packages/d5/fa/3b05e5c9d32efc770a8510eeb0b071c42ae93a5b576fd91cee9af91689a1/jiter-0.11.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2cc5a3965285ddc33e0cab933e96b640bc9ba5940cea27ebbbf6695e72d6511c", size = 312561 }, + { url = "https://files.pythonhosted.org/packages/50/d3/335822eb216154ddb79a130cbdce88fdf5c3e2b43dc5dba1fd95c485aaf5/jiter-0.11.1-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b572b3636a784c2768b2342f36a23078c8d3aa6d8a30745398b1bab58a6f1a8", size = 344551 }, + { url = "https://files.pythonhosted.org/packages/31/6d/a0bed13676b1398f9b3ba61f32569f20a3ff270291161100956a577b2dd3/jiter-0.11.1-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ad93e3d67a981f96596d65d2298fe8d1aa649deb5374a2fb6a434410ee11915e", size = 363051 }, + { url = "https://files.pythonhosted.org/packages/a4/03/313eda04aa08545a5a04ed5876e52f49ab76a4d98e54578896ca3e16313e/jiter-0.11.1-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a83097ce379e202dcc3fe3fc71a16d523d1ee9192c8e4e854158f96b3efe3f2f", size = 485897 }, + { url = "https://files.pythonhosted.org/packages/5f/13/a1011b9d325e40b53b1b96a17c010b8646013417f3902f97a86325b19299/jiter-0.11.1-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7042c51e7fbeca65631eb0c332f90c0c082eab04334e7ccc28a8588e8e2804d9", size = 375224 }, + { url = "https://files.pythonhosted.org/packages/92/da/1b45026b19dd39b419e917165ff0ea629dbb95f374a3a13d2df95e40a6ac/jiter-0.11.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0a68d679c0e47649a61df591660507608adc2652442de7ec8276538ac46abe08", size = 356606 }, + { url = "https://files.pythonhosted.org/packages/7a/0c/9acb0e54d6a8ba59ce923a180ebe824b4e00e80e56cefde86cc8e0a948be/jiter-0.11.1-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a1b0da75dbf4b6ec0b3c9e604d1ee8beaf15bc046fff7180f7d89e3cdbd3bb51", size = 384003 }, + { url = "https://files.pythonhosted.org/packages/3f/2b/e5a5fe09d6da2145e4eed651e2ce37f3c0cf8016e48b1d302e21fb1628b7/jiter-0.11.1-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:69dd514bf0fa31c62147d6002e5ca2b3e7ef5894f5ac6f0a19752385f4e89437", size = 516946 }, + { url = "https://files.pythonhosted.org/packages/5f/fe/db936e16e0228d48eb81f9934e8327e9fde5185e84f02174fcd22a01be87/jiter-0.11.1-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:bb31ac0b339efa24c0ca606febd8b77ef11c58d09af1b5f2be4c99e907b11111", size = 507614 }, + { url = "https://files.pythonhosted.org/packages/86/db/c4438e8febfb303486d13c6b72f5eb71cf851e300a0c1f0b4140018dd31f/jiter-0.11.1-cp314-cp314t-win32.whl", hash = "sha256:b2ce0d6156a1d3ad41da3eec63b17e03e296b78b0e0da660876fccfada86d2f7", size = 204043 }, + { url = "https://files.pythonhosted.org/packages/36/59/81badb169212f30f47f817dfaabf965bc9b8204fed906fab58104ee541f9/jiter-0.11.1-cp314-cp314t-win_amd64.whl", hash = "sha256:f4db07d127b54c4a2d43b4cf05ff0193e4f73e0dd90c74037e16df0b29f666e1", size = 204046 }, + { url = "https://files.pythonhosted.org/packages/dd/01/43f7b4eb61db3e565574c4c5714685d042fb652f9eef7e5a3de6aafa943a/jiter-0.11.1-cp314-cp314t-win_arm64.whl", hash = "sha256:28e4fdf2d7ebfc935523e50d1efa3970043cfaa161674fe66f9642409d001dfe", size = 188069 }, ] [[package]] @@ -2541,7 +2599,7 @@ wheels = [ [[package]] name = "matplotlib" -version = "3.10.6" +version = "3.10.7" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "contourpy", version = "1.3.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, @@ -2555,62 +2613,62 @@ dependencies = [ { name = "pyparsing" }, { name = "python-dateutil" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a0/59/c3e6453a9676ffba145309a73c462bb407f4400de7de3f2b41af70720a3c/matplotlib-3.10.6.tar.gz", hash = "sha256:ec01b645840dd1996df21ee37f208cd8ba57644779fa20464010638013d3203c", size = 34804264 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/da/dc/ab89f7a5efd0cbaaebf2c3cf1881f4cba20c8925bb43f64511059df76895/matplotlib-3.10.6-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:bc7316c306d97463a9866b89d5cc217824e799fa0de346c8f68f4f3d27c8693d", size = 8247159 }, - { url = "https://files.pythonhosted.org/packages/30/a5/ddaee1a383ab28174093644fff7438eddb87bf8dbd58f7b85f5cdd6b2485/matplotlib-3.10.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d00932b0d160ef03f59f9c0e16d1e3ac89646f7785165ce6ad40c842db16cc2e", size = 8108011 }, - { url = "https://files.pythonhosted.org/packages/75/5b/a53f69bb0522db352b1135bb57cd9fe00fd7252072409392d991d3a755d0/matplotlib-3.10.6-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8fa4c43d6bfdbfec09c733bca8667de11bfa4970e8324c471f3a3632a0301c15", size = 8680518 }, - { url = "https://files.pythonhosted.org/packages/5f/31/e059ddce95f68819b005a2d6820b2d6ed0307827a04598891f00649bed2d/matplotlib-3.10.6-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ea117a9c1627acaa04dbf36265691921b999cbf515a015298e54e1a12c3af837", size = 9514997 }, - { url = "https://files.pythonhosted.org/packages/66/d5/28b408a7c0f07b41577ee27e4454fe329e78ca21fe46ae7a27d279165fb5/matplotlib-3.10.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:08fc803293b4e1694ee325896030de97f74c141ccff0be886bb5915269247676", size = 9566440 }, - { url = "https://files.pythonhosted.org/packages/2d/99/8325b3386b479b1d182ab1a7fd588fd393ff00a99dc04b7cf7d06668cf0f/matplotlib-3.10.6-cp310-cp310-win_amd64.whl", hash = "sha256:2adf92d9b7527fbfb8818e050260f0ebaa460f79d61546374ce73506c9421d09", size = 8108186 }, - { url = "https://files.pythonhosted.org/packages/80/d6/5d3665aa44c49005aaacaa68ddea6fcb27345961cd538a98bb0177934ede/matplotlib-3.10.6-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:905b60d1cb0ee604ce65b297b61cf8be9f4e6cfecf95a3fe1c388b5266bc8f4f", size = 8257527 }, - { url = "https://files.pythonhosted.org/packages/8c/af/30ddefe19ca67eebd70047dabf50f899eaff6f3c5e6a1a7edaecaf63f794/matplotlib-3.10.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7bac38d816637343e53d7185d0c66677ff30ffb131044a81898b5792c956ba76", size = 8119583 }, - { url = "https://files.pythonhosted.org/packages/d3/29/4a8650a3dcae97fa4f375d46efcb25920d67b512186f8a6788b896062a81/matplotlib-3.10.6-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:942a8de2b5bfff1de31d95722f702e2966b8a7e31f4e68f7cd963c7cd8861cf6", size = 8692682 }, - { url = "https://files.pythonhosted.org/packages/aa/d3/b793b9cb061cfd5d42ff0f69d1822f8d5dbc94e004618e48a97a8373179a/matplotlib-3.10.6-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a3276c85370bc0dfca051ec65c5817d1e0f8f5ce1b7787528ec8ed2d524bbc2f", size = 9521065 }, - { url = "https://files.pythonhosted.org/packages/f7/c5/53de5629f223c1c66668d46ac2621961970d21916a4bc3862b174eb2a88f/matplotlib-3.10.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9df5851b219225731f564e4b9e7f2ac1e13c9e6481f941b5631a0f8e2d9387ce", size = 9576888 }, - { url = "https://files.pythonhosted.org/packages/fc/8e/0a18d6d7d2d0a2e66585032a760d13662e5250c784d53ad50434e9560991/matplotlib-3.10.6-cp311-cp311-win_amd64.whl", hash = "sha256:abb5d9478625dd9c9eb51a06d39aae71eda749ae9b3138afb23eb38824026c7e", size = 8115158 }, - { url = "https://files.pythonhosted.org/packages/07/b3/1a5107bb66c261e23b9338070702597a2d374e5aa7004b7adfc754fbed02/matplotlib-3.10.6-cp311-cp311-win_arm64.whl", hash = "sha256:886f989ccfae63659183173bb3fced7fd65e9eb793c3cc21c273add368536951", size = 7992444 }, - { url = "https://files.pythonhosted.org/packages/ea/1a/7042f7430055d567cc3257ac409fcf608599ab27459457f13772c2d9778b/matplotlib-3.10.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:31ca662df6a80bd426f871105fdd69db7543e28e73a9f2afe80de7e531eb2347", size = 8272404 }, - { url = "https://files.pythonhosted.org/packages/a9/5d/1d5f33f5b43f4f9e69e6a5fe1fb9090936ae7bc8e2ff6158e7a76542633b/matplotlib-3.10.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1678bb61d897bb4ac4757b5ecfb02bfb3fddf7f808000fb81e09c510712fda75", size = 8128262 }, - { url = "https://files.pythonhosted.org/packages/67/c3/135fdbbbf84e0979712df58e5e22b4f257b3f5e52a3c4aacf1b8abec0d09/matplotlib-3.10.6-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:56cd2d20842f58c03d2d6e6c1f1cf5548ad6f66b91e1e48f814e4fb5abd1cb95", size = 8697008 }, - { url = "https://files.pythonhosted.org/packages/9c/be/c443ea428fb2488a3ea7608714b1bd85a82738c45da21b447dc49e2f8e5d/matplotlib-3.10.6-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:662df55604a2f9a45435566d6e2660e41efe83cd94f4288dfbf1e6d1eae4b0bb", size = 9530166 }, - { url = "https://files.pythonhosted.org/packages/a9/35/48441422b044d74034aea2a3e0d1a49023f12150ebc58f16600132b9bbaf/matplotlib-3.10.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:08f141d55148cd1fc870c3387d70ca4df16dee10e909b3b038782bd4bda6ea07", size = 9593105 }, - { url = "https://files.pythonhosted.org/packages/45/c3/994ef20eb4154ab84cc08d033834555319e4af970165e6c8894050af0b3c/matplotlib-3.10.6-cp312-cp312-win_amd64.whl", hash = "sha256:590f5925c2d650b5c9d813c5b3b5fc53f2929c3f8ef463e4ecfa7e052044fb2b", size = 8122784 }, - { url = "https://files.pythonhosted.org/packages/57/b8/5c85d9ae0e40f04e71bedb053aada5d6bab1f9b5399a0937afb5d6b02d98/matplotlib-3.10.6-cp312-cp312-win_arm64.whl", hash = "sha256:f44c8d264a71609c79a78d50349e724f5d5fc3684ead7c2a473665ee63d868aa", size = 7992823 }, - { url = "https://files.pythonhosted.org/packages/a0/db/18380e788bb837e724358287b08e223b32bc8dccb3b0c12fa8ca20bc7f3b/matplotlib-3.10.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:819e409653c1106c8deaf62e6de6b8611449c2cd9939acb0d7d4e57a3d95cc7a", size = 8273231 }, - { url = "https://files.pythonhosted.org/packages/d3/0f/38dd49445b297e0d4f12a322c30779df0d43cb5873c7847df8a82e82ec67/matplotlib-3.10.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:59c8ac8382fefb9cb71308dde16a7c487432f5255d8f1fd32473523abecfecdf", size = 8128730 }, - { url = "https://files.pythonhosted.org/packages/e5/b8/9eea6630198cb303d131d95d285a024b3b8645b1763a2916fddb44ca8760/matplotlib-3.10.6-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:84e82d9e0fd70c70bc55739defbd8055c54300750cbacf4740c9673a24d6933a", size = 8698539 }, - { url = "https://files.pythonhosted.org/packages/71/34/44c7b1f075e1ea398f88aeabcc2907c01b9cc99e2afd560c1d49845a1227/matplotlib-3.10.6-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:25f7a3eb42d6c1c56e89eacd495661fc815ffc08d9da750bca766771c0fd9110", size = 9529702 }, - { url = "https://files.pythonhosted.org/packages/b5/7f/e5c2dc9950c7facaf8b461858d1b92c09dd0cf174fe14e21953b3dda06f7/matplotlib-3.10.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f9c862d91ec0b7842920a4cfdaaec29662195301914ea54c33e01f1a28d014b2", size = 9593742 }, - { url = "https://files.pythonhosted.org/packages/ff/1d/70c28528794f6410ee2856cd729fa1f1756498b8d3126443b0a94e1a8695/matplotlib-3.10.6-cp313-cp313-win_amd64.whl", hash = "sha256:1b53bd6337eba483e2e7d29c5ab10eee644bc3a2491ec67cc55f7b44583ffb18", size = 8122753 }, - { url = "https://files.pythonhosted.org/packages/e8/74/0e1670501fc7d02d981564caf7c4df42974464625935424ca9654040077c/matplotlib-3.10.6-cp313-cp313-win_arm64.whl", hash = "sha256:cbd5eb50b7058b2892ce45c2f4e92557f395c9991f5c886d1bb74a1582e70fd6", size = 7992973 }, - { url = "https://files.pythonhosted.org/packages/b1/4e/60780e631d73b6b02bd7239f89c451a72970e5e7ec34f621eda55cd9a445/matplotlib-3.10.6-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:acc86dd6e0e695c095001a7fccff158c49e45e0758fdf5dcdbb0103318b59c9f", size = 8316869 }, - { url = "https://files.pythonhosted.org/packages/f8/15/baa662374a579413210fc2115d40c503b7360a08e9cc254aa0d97d34b0c1/matplotlib-3.10.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e228cd2ffb8f88b7d0b29e37f68ca9aaf83e33821f24a5ccc4f082dd8396bc27", size = 8178240 }, - { url = "https://files.pythonhosted.org/packages/c6/3f/3c38e78d2aafdb8829fcd0857d25aaf9e7dd2dfcf7ec742765b585774931/matplotlib-3.10.6-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:658bc91894adeab669cf4bb4a186d049948262987e80f0857216387d7435d833", size = 8711719 }, - { url = "https://files.pythonhosted.org/packages/96/4b/2ec2bbf8cefaa53207cc56118d1fa8a0f9b80642713ea9390235d331ede4/matplotlib-3.10.6-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8913b7474f6dd83ac444c9459c91f7f0f2859e839f41d642691b104e0af056aa", size = 9541422 }, - { url = "https://files.pythonhosted.org/packages/83/7d/40255e89b3ef11c7871020563b2dd85f6cb1b4eff17c0f62b6eb14c8fa80/matplotlib-3.10.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:091cea22e059b89f6d7d1a18e2c33a7376c26eee60e401d92a4d6726c4e12706", size = 9594068 }, - { url = "https://files.pythonhosted.org/packages/f0/a9/0213748d69dc842537a113493e1c27daf9f96bd7cc316f933dc8ec4de985/matplotlib-3.10.6-cp313-cp313t-win_amd64.whl", hash = "sha256:491e25e02a23d7207629d942c666924a6b61e007a48177fdd231a0097b7f507e", size = 8200100 }, - { url = "https://files.pythonhosted.org/packages/be/15/79f9988066ce40b8a6f1759a934ea0cde8dc4adc2262255ee1bc98de6ad0/matplotlib-3.10.6-cp313-cp313t-win_arm64.whl", hash = "sha256:3d80d60d4e54cda462e2cd9a086d85cd9f20943ead92f575ce86885a43a565d5", size = 8042142 }, - { url = "https://files.pythonhosted.org/packages/7c/58/e7b6d292beae6fb4283ca6fb7fa47d7c944a68062d6238c07b497dd35493/matplotlib-3.10.6-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:70aaf890ce1d0efd482df969b28a5b30ea0b891224bb315810a3940f67182899", size = 8273802 }, - { url = "https://files.pythonhosted.org/packages/9f/f6/7882d05aba16a8cdd594fb9a03a9d3cca751dbb6816adf7b102945522ee9/matplotlib-3.10.6-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1565aae810ab79cb72e402b22facfa6501365e73ebab70a0fdfb98488d2c3c0c", size = 8131365 }, - { url = "https://files.pythonhosted.org/packages/94/bf/ff32f6ed76e78514e98775a53715eca4804b12bdcf35902cdd1cf759d324/matplotlib-3.10.6-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f3b23315a01981689aa4e1a179dbf6ef9fbd17143c3eea77548c2ecfb0499438", size = 9533961 }, - { url = "https://files.pythonhosted.org/packages/fe/c3/6bf88c2fc2da7708a2ff8d2eeb5d68943130f50e636d5d3dcf9d4252e971/matplotlib-3.10.6-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:30fdd37edf41a4e6785f9b37969de57aea770696cb637d9946eb37470c94a453", size = 9804262 }, - { url = "https://files.pythonhosted.org/packages/0f/7a/e05e6d9446d2d577b459427ad060cd2de5742d0e435db3191fea4fcc7e8b/matplotlib-3.10.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:bc31e693da1c08012c764b053e702c1855378e04102238e6a5ee6a7117c53a47", size = 9595508 }, - { url = "https://files.pythonhosted.org/packages/39/fb/af09c463ced80b801629fd73b96f726c9f6124c3603aa2e480a061d6705b/matplotlib-3.10.6-cp314-cp314-win_amd64.whl", hash = "sha256:05be9bdaa8b242bc6ff96330d18c52f1fc59c6fb3a4dd411d953d67e7e1baf98", size = 8252742 }, - { url = "https://files.pythonhosted.org/packages/b1/f9/b682f6db9396d9ab8f050c0a3bfbb5f14fb0f6518f08507c04cc02f8f229/matplotlib-3.10.6-cp314-cp314-win_arm64.whl", hash = "sha256:f56a0d1ab05d34c628592435781d185cd99630bdfd76822cd686fb5a0aecd43a", size = 8124237 }, - { url = "https://files.pythonhosted.org/packages/b5/d2/b69b4a0923a3c05ab90527c60fdec899ee21ca23ede7f0fb818e6620d6f2/matplotlib-3.10.6-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:94f0b4cacb23763b64b5dace50d5b7bfe98710fed5f0cef5c08135a03399d98b", size = 8316956 }, - { url = "https://files.pythonhosted.org/packages/28/e9/dc427b6f16457ffaeecb2fc4abf91e5adb8827861b869c7a7a6d1836fa73/matplotlib-3.10.6-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:cc332891306b9fb39462673d8225d1b824c89783fee82840a709f96714f17a5c", size = 8178260 }, - { url = "https://files.pythonhosted.org/packages/c4/89/1fbd5ad611802c34d1c7ad04607e64a1350b7fb9c567c4ec2c19e066ed35/matplotlib-3.10.6-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee1d607b3fb1590deb04b69f02ea1d53ed0b0bf75b2b1a5745f269afcbd3cdd3", size = 9541422 }, - { url = "https://files.pythonhosted.org/packages/b0/3b/65fec8716025b22c1d72d5a82ea079934c76a547696eaa55be6866bc89b1/matplotlib-3.10.6-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:376a624a218116461696b27b2bbf7a8945053e6d799f6502fc03226d077807bf", size = 9803678 }, - { url = "https://files.pythonhosted.org/packages/c7/b0/40fb2b3a1ab9381bb39a952e8390357c8be3bdadcf6d5055d9c31e1b35ae/matplotlib-3.10.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:83847b47f6524c34b4f2d3ce726bb0541c48c8e7692729865c3df75bfa0f495a", size = 9594077 }, - { url = "https://files.pythonhosted.org/packages/76/34/c4b71b69edf5b06e635eee1ed10bfc73cf8df058b66e63e30e6a55e231d5/matplotlib-3.10.6-cp314-cp314t-win_amd64.whl", hash = "sha256:c7e0518e0d223683532a07f4b512e2e0729b62674f1b3a1a69869f98e6b1c7e3", size = 8342822 }, - { url = "https://files.pythonhosted.org/packages/e8/62/aeabeef1a842b6226a30d49dd13e8a7a1e81e9ec98212c0b5169f0a12d83/matplotlib-3.10.6-cp314-cp314t-win_arm64.whl", hash = "sha256:4dd83e029f5b4801eeb87c64efd80e732452781c16a9cf7415b7b63ec8f374d7", size = 8172588 }, - { url = "https://files.pythonhosted.org/packages/17/6f/2551e45bea2938e0363ccdd54fa08dae7605ce782d4332497d31a7b97672/matplotlib-3.10.6-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:13fcd07ccf17e354398358e0307a1f53f5325dca22982556ddb9c52837b5af41", size = 8241220 }, - { url = "https://files.pythonhosted.org/packages/54/7e/0f4c6e8b98105fdb162a4efde011af204ca47d7c05d735aff480ebfead1b/matplotlib-3.10.6-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:470fc846d59d1406e34fa4c32ba371039cd12c2fe86801159a965956f2575bd1", size = 8104624 }, - { url = "https://files.pythonhosted.org/packages/27/27/c29696702b9317a6ade1ba6f8861e02d7423f18501729203d7a80b686f23/matplotlib-3.10.6-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f7173f8551b88f4ef810a94adae3128c2530e0d07529f7141be7f8d8c365f051", size = 8682271 }, - { url = "https://files.pythonhosted.org/packages/12/bb/02c35a51484aae5f49bd29f091286e7af5f3f677a9736c58a92b3c78baeb/matplotlib-3.10.6-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:f2d684c3204fa62421bbf770ddfebc6b50130f9cad65531eeba19236d73bb488", size = 8252296 }, - { url = "https://files.pythonhosted.org/packages/7d/85/41701e3092005aee9a2445f5ee3904d9dbd4a7df7a45905ffef29b7ef098/matplotlib-3.10.6-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:6f4a69196e663a41d12a728fab8751177215357906436804217d6d9cf0d4d6cf", size = 8116749 }, - { url = "https://files.pythonhosted.org/packages/16/53/8d8fa0ea32a8c8239e04d022f6c059ee5e1b77517769feccd50f1df43d6d/matplotlib-3.10.6-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d6ca6ef03dfd269f4ead566ec6f3fb9becf8dab146fb999022ed85ee9f6b3eb", size = 8693933 }, +sdist = { url = "https://files.pythonhosted.org/packages/ae/e2/d2d5295be2f44c678ebaf3544ba32d20c1f9ef08c49fe47f496180e1db15/matplotlib-3.10.7.tar.gz", hash = "sha256:a06ba7e2a2ef9131c79c49e63dad355d2d878413a0376c1727c8b9335ff731c7", size = 34804865 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6c/87/3932d5778ab4c025db22710b61f49ccaed3956c5cf46ffb2ffa7492b06d9/matplotlib-3.10.7-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:7ac81eee3b7c266dd92cee1cd658407b16c57eed08c7421fa354ed68234de380", size = 8247141 }, + { url = "https://files.pythonhosted.org/packages/45/a8/bfed45339160102bce21a44e38a358a1134a5f84c26166de03fb4a53208f/matplotlib-3.10.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:667ecd5d8d37813a845053d8f5bf110b534c3c9f30e69ebd25d4701385935a6d", size = 8107995 }, + { url = "https://files.pythonhosted.org/packages/e2/3c/5692a2d9a5ba848fda3f48d2b607037df96460b941a59ef236404b39776b/matplotlib-3.10.7-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cc1c51b846aca49a5a8b44fbba6a92d583a35c64590ad9e1e950dc88940a4297", size = 8680503 }, + { url = "https://files.pythonhosted.org/packages/ab/a0/86ace53c48b05d0e6e9c127b2ace097434901f3e7b93f050791c8243201a/matplotlib-3.10.7-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a11c2e9e72e7de09b7b72e62f3df23317c888299c875e2b778abf1eda8c0a42", size = 9514982 }, + { url = "https://files.pythonhosted.org/packages/a6/81/ead71e2824da8f72640a64166d10e62300df4ae4db01a0bac56c5b39fa51/matplotlib-3.10.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f19410b486fdd139885ace124e57f938c1e6a3210ea13dd29cab58f5d4bc12c7", size = 9566429 }, + { url = "https://files.pythonhosted.org/packages/65/7d/954b3067120456f472cce8fdcacaf4a5fcd522478db0c37bb243c7cb59dd/matplotlib-3.10.7-cp310-cp310-win_amd64.whl", hash = "sha256:b498e9e4022f93de2d5a37615200ca01297ceebbb56fe4c833f46862a490f9e3", size = 8108174 }, + { url = "https://files.pythonhosted.org/packages/fc/bc/0fb489005669127ec13f51be0c6adc074d7cf191075dab1da9fe3b7a3cfc/matplotlib-3.10.7-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:53b492410a6cd66c7a471de6c924f6ede976e963c0f3097a3b7abfadddc67d0a", size = 8257507 }, + { url = "https://files.pythonhosted.org/packages/e2/6a/d42588ad895279ff6708924645b5d2ed54a7fb2dc045c8a804e955aeace1/matplotlib-3.10.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d9749313deb729f08207718d29c86246beb2ea3fdba753595b55901dee5d2fd6", size = 8119565 }, + { url = "https://files.pythonhosted.org/packages/10/b7/4aa196155b4d846bd749cf82aa5a4c300cf55a8b5e0dfa5b722a63c0f8a0/matplotlib-3.10.7-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2222c7ba2cbde7fe63032769f6eb7e83ab3227f47d997a8453377709b7fe3a5a", size = 8692668 }, + { url = "https://files.pythonhosted.org/packages/e6/e7/664d2b97016f46683a02d854d730cfcf54ff92c1dafa424beebef50f831d/matplotlib-3.10.7-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e91f61a064c92c307c5a9dc8c05dc9f8a68f0a3be199d9a002a0622e13f874a1", size = 9521051 }, + { url = "https://files.pythonhosted.org/packages/a8/a3/37aef1404efa615f49b5758a5e0261c16dd88f389bc1861e722620e4a754/matplotlib-3.10.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6f1851eab59ca082c95df5a500106bad73672645625e04538b3ad0f69471ffcc", size = 9576878 }, + { url = "https://files.pythonhosted.org/packages/33/cd/b145f9797126f3f809d177ca378de57c45413c5099c5990de2658760594a/matplotlib-3.10.7-cp311-cp311-win_amd64.whl", hash = "sha256:6516ce375109c60ceec579e699524e9d504cd7578506f01150f7a6bc174a775e", size = 8115142 }, + { url = "https://files.pythonhosted.org/packages/2e/39/63bca9d2b78455ed497fcf51a9c71df200a11048f48249038f06447fa947/matplotlib-3.10.7-cp311-cp311-win_arm64.whl", hash = "sha256:b172db79759f5f9bc13ef1c3ef8b9ee7b37b0247f987fbbbdaa15e4f87fd46a9", size = 7992439 }, + { url = "https://files.pythonhosted.org/packages/be/b3/09eb0f7796932826ec20c25b517d568627754f6c6462fca19e12c02f2e12/matplotlib-3.10.7-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7a0edb7209e21840e8361e91ea84ea676658aa93edd5f8762793dec77a4a6748", size = 8272389 }, + { url = "https://files.pythonhosted.org/packages/11/0b/1ae80ddafb8652fd8046cb5c8460ecc8d4afccb89e2c6d6bec61e04e1eaf/matplotlib-3.10.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c380371d3c23e0eadf8ebff114445b9f970aff2010198d498d4ab4c3b41eea4f", size = 8128247 }, + { url = "https://files.pythonhosted.org/packages/7d/18/95ae2e242d4a5c98bd6e90e36e128d71cf1c7e39b0874feaed3ef782e789/matplotlib-3.10.7-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d5f256d49fea31f40f166a5e3131235a5d2f4b7f44520b1cf0baf1ce568ccff0", size = 8696996 }, + { url = "https://files.pythonhosted.org/packages/7e/3d/5b559efc800bd05cb2033aa85f7e13af51958136a48327f7c261801ff90a/matplotlib-3.10.7-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:11ae579ac83cdf3fb72573bb89f70e0534de05266728740d478f0f818983c695", size = 9530153 }, + { url = "https://files.pythonhosted.org/packages/88/57/eab4a719fd110312d3c220595d63a3c85ec2a39723f0f4e7fa7e6e3f74ba/matplotlib-3.10.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4c14b6acd16cddc3569a2d515cfdd81c7a68ac5639b76548cfc1a9e48b20eb65", size = 9593093 }, + { url = "https://files.pythonhosted.org/packages/31/3c/80816f027b3a4a28cd2a0a6ef7f89a2db22310e945cd886ec25bfb399221/matplotlib-3.10.7-cp312-cp312-win_amd64.whl", hash = "sha256:0d8c32b7ea6fb80b1aeff5a2ceb3fb9778e2759e899d9beff75584714afcc5ee", size = 8122771 }, + { url = "https://files.pythonhosted.org/packages/de/77/ef1fc78bfe99999b2675435cc52120887191c566b25017d78beaabef7f2d/matplotlib-3.10.7-cp312-cp312-win_arm64.whl", hash = "sha256:5f3f6d315dcc176ba7ca6e74c7768fb7e4cf566c49cb143f6bc257b62e634ed8", size = 7992812 }, + { url = "https://files.pythonhosted.org/packages/02/9c/207547916a02c78f6bdd83448d9b21afbc42f6379ed887ecf610984f3b4e/matplotlib-3.10.7-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1d9d3713a237970569156cfb4de7533b7c4eacdd61789726f444f96a0d28f57f", size = 8273212 }, + { url = "https://files.pythonhosted.org/packages/bc/d0/b3d3338d467d3fc937f0bb7f256711395cae6f78e22cef0656159950adf0/matplotlib-3.10.7-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:37a1fea41153dd6ee061d21ab69c9cf2cf543160b1b85d89cd3d2e2a7902ca4c", size = 8128713 }, + { url = "https://files.pythonhosted.org/packages/22/ff/6425bf5c20d79aa5b959d1ce9e65f599632345391381c9a104133fe0b171/matplotlib-3.10.7-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b3c4ea4948d93c9c29dc01c0c23eef66f2101bf75158c291b88de6525c55c3d1", size = 8698527 }, + { url = "https://files.pythonhosted.org/packages/d0/7f/ccdca06f4c2e6c7989270ed7829b8679466682f4cfc0f8c9986241c023b6/matplotlib-3.10.7-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:22df30ffaa89f6643206cf13877191c63a50e8f800b038bc39bee9d2d4957632", size = 9529690 }, + { url = "https://files.pythonhosted.org/packages/b8/95/b80fc2c1f269f21ff3d193ca697358e24408c33ce2b106a7438a45407b63/matplotlib-3.10.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b69676845a0a66f9da30e87f48be36734d6748024b525ec4710be40194282c84", size = 9593732 }, + { url = "https://files.pythonhosted.org/packages/e1/b6/23064a96308b9aeceeffa65e96bcde459a2ea4934d311dee20afde7407a0/matplotlib-3.10.7-cp313-cp313-win_amd64.whl", hash = "sha256:744991e0cc863dd669c8dc9136ca4e6e0082be2070b9d793cbd64bec872a6815", size = 8122727 }, + { url = "https://files.pythonhosted.org/packages/b3/a6/2faaf48133b82cf3607759027f82b5c702aa99cdfcefb7f93d6ccf26a424/matplotlib-3.10.7-cp313-cp313-win_arm64.whl", hash = "sha256:fba2974df0bf8ce3c995fa84b79cde38326e0f7b5409e7a3a481c1141340bcf7", size = 7992958 }, + { url = "https://files.pythonhosted.org/packages/4a/f0/b018fed0b599bd48d84c08794cb242227fe3341952da102ee9d9682db574/matplotlib-3.10.7-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:932c55d1fa7af4423422cb6a492a31cbcbdbe68fd1a9a3f545aa5e7a143b5355", size = 8316849 }, + { url = "https://files.pythonhosted.org/packages/b0/b7/bb4f23856197659f275e11a2a164e36e65e9b48ea3e93c4ec25b4f163198/matplotlib-3.10.7-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5e38c2d581d62ee729a6e144c47a71b3f42fb4187508dbbf4fe71d5612c3433b", size = 8178225 }, + { url = "https://files.pythonhosted.org/packages/62/56/0600609893ff277e6f3ab3c0cef4eafa6e61006c058e84286c467223d4d5/matplotlib-3.10.7-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:786656bb13c237bbcebcd402f65f44dd61ead60ee3deb045af429d889c8dbc67", size = 8711708 }, + { url = "https://files.pythonhosted.org/packages/d8/1a/6bfecb0cafe94d6658f2f1af22c43b76cf7a1c2f0dc34ef84cbb6809617e/matplotlib-3.10.7-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:09d7945a70ea43bf9248f4b6582734c2fe726723204a76eca233f24cffc7ef67", size = 9541409 }, + { url = "https://files.pythonhosted.org/packages/08/50/95122a407d7f2e446fd865e2388a232a23f2b81934960ea802f3171518e4/matplotlib-3.10.7-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:d0b181e9fa8daf1d9f2d4c547527b167cb8838fc587deabca7b5c01f97199e84", size = 9594054 }, + { url = "https://files.pythonhosted.org/packages/13/76/75b194a43b81583478a81e78a07da8d9ca6ddf50dd0a2ccabf258059481d/matplotlib-3.10.7-cp313-cp313t-win_amd64.whl", hash = "sha256:31963603041634ce1a96053047b40961f7a29eb8f9a62e80cc2c0427aa1d22a2", size = 8200100 }, + { url = "https://files.pythonhosted.org/packages/f5/9e/6aefebdc9f8235c12bdeeda44cc0383d89c1e41da2c400caf3ee2073a3ce/matplotlib-3.10.7-cp313-cp313t-win_arm64.whl", hash = "sha256:aebed7b50aa6ac698c90f60f854b47e48cd2252b30510e7a1feddaf5a3f72cbf", size = 8042131 }, + { url = "https://files.pythonhosted.org/packages/0d/4b/e5bc2c321b6a7e3a75638d937d19ea267c34bd5a90e12bee76c4d7c7a0d9/matplotlib-3.10.7-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:d883460c43e8c6b173fef244a2341f7f7c0e9725c7fe68306e8e44ed9c8fb100", size = 8273787 }, + { url = "https://files.pythonhosted.org/packages/86/ad/6efae459c56c2fbc404da154e13e3a6039129f3c942b0152624f1c621f05/matplotlib-3.10.7-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:07124afcf7a6504eafcb8ce94091c5898bbdd351519a1beb5c45f7a38c67e77f", size = 8131348 }, + { url = "https://files.pythonhosted.org/packages/a6/5a/a4284d2958dee4116359cc05d7e19c057e64ece1b4ac986ab0f2f4d52d5a/matplotlib-3.10.7-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c17398b709a6cce3d9fdb1595c33e356d91c098cd9486cb2cc21ea2ea418e715", size = 9533949 }, + { url = "https://files.pythonhosted.org/packages/de/ff/f3781b5057fa3786623ad8976fc9f7b0d02b2f28534751fd5a44240de4cf/matplotlib-3.10.7-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7146d64f561498764561e9cd0ed64fcf582e570fc519e6f521e2d0cfd43365e1", size = 9804247 }, + { url = "https://files.pythonhosted.org/packages/47/5a/993a59facb8444efb0e197bf55f545ee449902dcee86a4dfc580c3b61314/matplotlib-3.10.7-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:90ad854c0a435da3104c01e2c6f0028d7e719b690998a2333d7218db80950722", size = 9595497 }, + { url = "https://files.pythonhosted.org/packages/0d/a5/77c95aaa9bb32c345cbb49626ad8eb15550cba2e6d4c88081a6c2ac7b08d/matplotlib-3.10.7-cp314-cp314-win_amd64.whl", hash = "sha256:4645fc5d9d20ffa3a39361fcdbcec731382763b623b72627806bf251b6388866", size = 8252732 }, + { url = "https://files.pythonhosted.org/packages/74/04/45d269b4268d222390d7817dae77b159651909669a34ee9fdee336db5883/matplotlib-3.10.7-cp314-cp314-win_arm64.whl", hash = "sha256:9257be2f2a03415f9105c486d304a321168e61ad450f6153d77c69504ad764bb", size = 8124240 }, + { url = "https://files.pythonhosted.org/packages/4b/c7/ca01c607bb827158b439208c153d6f14ddb9fb640768f06f7ca3488ae67b/matplotlib-3.10.7-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1e4bbad66c177a8fdfa53972e5ef8be72a5f27e6a607cec0d8579abd0f3102b1", size = 8316938 }, + { url = "https://files.pythonhosted.org/packages/84/d2/5539e66e9f56d2fdec94bb8436f5e449683b4e199bcc897c44fbe3c99e28/matplotlib-3.10.7-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:d8eb7194b084b12feb19142262165832fc6ee879b945491d1c3d4660748020c4", size = 8178245 }, + { url = "https://files.pythonhosted.org/packages/77/b5/e6ca22901fd3e4fe433a82e583436dd872f6c966fca7e63cf806b40356f8/matplotlib-3.10.7-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b4d41379b05528091f00e1728004f9a8d7191260f3862178b88e8fd770206318", size = 9541411 }, + { url = "https://files.pythonhosted.org/packages/9e/99/a4524db57cad8fee54b7237239a8f8360bfcfa3170d37c9e71c090c0f409/matplotlib-3.10.7-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4a74f79fafb2e177f240579bc83f0b60f82cc47d2f1d260f422a0627207008ca", size = 9803664 }, + { url = "https://files.pythonhosted.org/packages/e6/a5/85e2edf76ea0ad4288d174926d9454ea85f3ce5390cc4e6fab196cbf250b/matplotlib-3.10.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:702590829c30aada1e8cef0568ddbffa77ca747b4d6e36c6d173f66e301f89cc", size = 9594066 }, + { url = "https://files.pythonhosted.org/packages/39/69/9684368a314f6d83fe5c5ad2a4121a3a8e03723d2e5c8ea17b66c1bad0e7/matplotlib-3.10.7-cp314-cp314t-win_amd64.whl", hash = "sha256:f79d5de970fc90cd5591f60053aecfce1fcd736e0303d9f0bf86be649fa68fb8", size = 8342832 }, + { url = "https://files.pythonhosted.org/packages/04/5f/e22e08da14bc1a0894184640d47819d2338b792732e20d292bf86e5ab785/matplotlib-3.10.7-cp314-cp314t-win_arm64.whl", hash = "sha256:cb783436e47fcf82064baca52ce748af71725d0352e1d31564cbe9c95df92b9c", size = 8172585 }, + { url = "https://files.pythonhosted.org/packages/1e/6c/a9bcf03e9afb2a873e0a5855f79bce476d1023f26f8212969f2b7504756c/matplotlib-3.10.7-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:5c09cf8f2793f81368f49f118b6f9f937456362bee282eac575cca7f84cda537", size = 8241204 }, + { url = "https://files.pythonhosted.org/packages/5b/fd/0e6f5aa762ed689d9fa8750b08f1932628ffa7ed30e76423c399d19407d2/matplotlib-3.10.7-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:de66744b2bb88d5cd27e80dfc2ec9f0517d0a46d204ff98fe9e5f2864eb67657", size = 8104607 }, + { url = "https://files.pythonhosted.org/packages/b9/a9/21c9439d698fac5f0de8fc68b2405b738ed1f00e1279c76f2d9aa5521ead/matplotlib-3.10.7-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:53cc80662dd197ece414dd5b66e07370201515a3eaf52e7c518c68c16814773b", size = 8682257 }, + { url = "https://files.pythonhosted.org/packages/58/8f/76d5dc21ac64a49e5498d7f0472c0781dae442dd266a67458baec38288ec/matplotlib-3.10.7-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:15112bcbaef211bd663fa935ec33313b948e214454d949b723998a43357b17b0", size = 8252283 }, + { url = "https://files.pythonhosted.org/packages/27/0d/9c5d4c2317feb31d819e38c9f947c942f42ebd4eb935fc6fd3518a11eaa7/matplotlib-3.10.7-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d2a959c640cdeecdd2ec3136e8ea0441da59bcaf58d67e9c590740addba2cb68", size = 8116733 }, + { url = "https://files.pythonhosted.org/packages/9a/cc/3fe688ff1355010937713164caacf9ed443675ac48a997bab6ed23b3f7c0/matplotlib-3.10.7-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3886e47f64611046bc1db523a09dd0a0a6bed6081e6f90e13806dd1d1d1b5e91", size = 8693919 }, ] [[package]] @@ -3171,10 +3229,10 @@ wheels = [ [[package]] name = "nvidia-nccl-cu12" -version = "2.27.3" +version = "2.27.5" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5c/5b/4e4fff7bad39adf89f735f2bc87248c81db71205b62bcc0d5ca5b606b3c3/nvidia_nccl_cu12-2.27.3-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:adf27ccf4238253e0b826bce3ff5fa532d65fc42322c8bfdfaf28024c0fbe039", size = 322364134 }, + { url = "https://files.pythonhosted.org/packages/6e/89/f7a07dc961b60645dbbf42e80f2bc85ade7feb9a491b11a1e973aa00071f/nvidia_nccl_cu12-2.27.5-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ad730cf15cb5d25fe849c6e6ca9eb5b76db16a80f13f425ac68d8e2e55624457", size = 322348229 }, ] [[package]] @@ -3185,6 +3243,14 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f6/74/86a07f1d0f42998ca31312f998bd3b9a7eff7f52378f4f270c8679c77fb9/nvidia_nvjitlink_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:81ff63371a7ebd6e6451970684f916be2eab07321b73c9d244dc2b4da7f73b88", size = 39254836 }, ] +[[package]] +name = "nvidia-nvshmem-cu12" +version = "3.3.20" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3b/6c/99acb2f9eb85c29fc6f3a7ac4dccfd992e22666dd08a642b303311326a97/nvidia_nvshmem_cu12-3.3.20-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d00f26d3f9b2e3c3065be895e3059d6479ea5c638a3f38c9fec49b1b9dd7c1e5", size = 124657145 }, +] + [[package]] name = "nvidia-nvtx-cu12" version = "12.8.90" @@ -3245,7 +3311,7 @@ wheels = [ [[package]] name = "openai" -version = "2.2.0" +version = "2.5.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, @@ -3257,9 +3323,9 @@ dependencies = [ { name = "tqdm" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b8/b1/8201e321a7d64a25c6f5a560320272d8be70547add40311fceb916518632/openai-2.2.0.tar.gz", hash = "sha256:bc49d077a8bf0e370eec4d038bc05e232c20855a19df0b58e5b3e5a8da7d33e0", size = 588512 } +sdist = { url = "https://files.pythonhosted.org/packages/72/39/aa3767c920c217ef56f27e89cbe3aaa43dd6eea3269c95f045c5761b9df1/openai-2.5.0.tar.gz", hash = "sha256:f8fa7611f96886a0f31ac6b97e58bc0ada494b255ee2cfd51c8eb502cfcb4814", size = 590333 } wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/92/6aeef1836e66dfec7f7f160a4f06d7041be7f6ccfc47a2f0f5738b332245/openai-2.2.0-py3-none-any.whl", hash = "sha256:d222e63436e33f3134a3d7ce490dc2d2f146fa98036eb65cc225df3ce163916f", size = 998972 }, + { url = "https://files.pythonhosted.org/packages/14/f3/ebbd700d8dc1e6380a7a382969d96bc0cbea8717b52fb38ff0ca2a7653e8/openai-2.5.0-py3-none-any.whl", hash = "sha256:21380e5f52a71666dbadbf322dd518bdf2b9d11ed0bb3f96bea17310302d6280", size = 999851 }, ] [package.optional-dependencies] @@ -3347,45 +3413,45 @@ wheels = [ [[package]] name = "opentelemetry-api" -version = "1.37.0" +version = "1.38.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "importlib-metadata" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/63/04/05040d7ce33a907a2a02257e601992f0cdf11c73b33f13c4492bf6c3d6d5/opentelemetry_api-1.37.0.tar.gz", hash = "sha256:540735b120355bd5112738ea53621f8d5edb35ebcd6fe21ada3ab1c61d1cd9a7", size = 64923 } +sdist = { url = "https://files.pythonhosted.org/packages/08/d8/0f354c375628e048bd0570645b310797299754730079853095bf000fba69/opentelemetry_api-1.38.0.tar.gz", hash = "sha256:f4c193b5e8acb0912b06ac5b16321908dd0843d75049c091487322284a3eea12", size = 65242 } wheels = [ - { url = "https://files.pythonhosted.org/packages/91/48/28ed9e55dcf2f453128df738210a980e09f4e468a456fa3c763dbc8be70a/opentelemetry_api-1.37.0-py3-none-any.whl", hash = "sha256:accf2024d3e89faec14302213bc39550ec0f4095d1cf5ca688e1bfb1c8612f47", size = 65732 }, + { url = "https://files.pythonhosted.org/packages/ae/a2/d86e01c28300bd41bab8f18afd613676e2bd63515417b77636fc1add426f/opentelemetry_api-1.38.0-py3-none-any.whl", hash = "sha256:2891b0197f47124454ab9f0cf58f3be33faca394457ac3e09daba13ff50aa582", size = 65947 }, ] [[package]] name = "opentelemetry-exporter-otlp" -version = "1.37.0" +version = "1.38.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-exporter-otlp-proto-grpc" }, { name = "opentelemetry-exporter-otlp-proto-http" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/64/df/47fde1de15a3d5ad410e98710fac60cd3d509df5dc7ec1359b71d6bf7e70/opentelemetry_exporter_otlp-1.37.0.tar.gz", hash = "sha256:f85b1929dd0d750751cc9159376fb05aa88bb7a08b6cdbf84edb0054d93e9f26", size = 6145 } +sdist = { url = "https://files.pythonhosted.org/packages/c2/2d/16e3487ddde2dee702bd746dd41950a8789b846d22a1c7e64824aac5ebea/opentelemetry_exporter_otlp-1.38.0.tar.gz", hash = "sha256:2f55acdd475e4136117eff20fbf1b9488b1b0b665ab64407516e1ac06f9c3f9d", size = 6147 } wheels = [ - { url = "https://files.pythonhosted.org/packages/f5/23/7e35e41111e3834d918e414eca41555d585e8860c9149507298bb3b9b061/opentelemetry_exporter_otlp-1.37.0-py3-none-any.whl", hash = "sha256:bd44592c6bc7fc3e5c0a9b60f2ee813c84c2800c449e59504ab93f356cc450fc", size = 7019 }, + { url = "https://files.pythonhosted.org/packages/fd/8a/81cd252b16b7d95ec1147982b6af81c7932d23918b4c3b15372531242ddd/opentelemetry_exporter_otlp-1.38.0-py3-none-any.whl", hash = "sha256:bc6562cef229fac8887ed7109fc5abc52315f39d9c03fd487bb8b4ef8fbbc231", size = 7018 }, ] [[package]] name = "opentelemetry-exporter-otlp-proto-common" -version = "1.37.0" +version = "1.38.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-proto" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/dc/6c/10018cbcc1e6fff23aac67d7fd977c3d692dbe5f9ef9bb4db5c1268726cc/opentelemetry_exporter_otlp_proto_common-1.37.0.tar.gz", hash = "sha256:c87a1bdd9f41fdc408d9cc9367bb53f8d2602829659f2b90be9f9d79d0bfe62c", size = 20430 } +sdist = { url = "https://files.pythonhosted.org/packages/19/83/dd4660f2956ff88ed071e9e0e36e830df14b8c5dc06722dbde1841accbe8/opentelemetry_exporter_otlp_proto_common-1.38.0.tar.gz", hash = "sha256:e333278afab4695aa8114eeb7bf4e44e65c6607d54968271a249c180b2cb605c", size = 20431 } wheels = [ - { url = "https://files.pythonhosted.org/packages/08/13/b4ef09837409a777f3c0af2a5b4ba9b7af34872bc43609dda0c209e4060d/opentelemetry_exporter_otlp_proto_common-1.37.0-py3-none-any.whl", hash = "sha256:53038428449c559b0c564b8d718df3314da387109c4d36bd1b94c9a641b0292e", size = 18359 }, + { url = "https://files.pythonhosted.org/packages/a7/9e/55a41c9601191e8cd8eb626b54ee6827b9c9d4a46d736f32abc80d8039fc/opentelemetry_exporter_otlp_proto_common-1.38.0-py3-none-any.whl", hash = "sha256:03cb76ab213300fe4f4c62b7d8f17d97fcfd21b89f0b5ce38ea156327ddda74a", size = 18359 }, ] [[package]] name = "opentelemetry-exporter-otlp-proto-grpc" -version = "1.37.0" +version = "1.38.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "googleapis-common-protos" }, @@ -3396,14 +3462,14 @@ dependencies = [ { name = "opentelemetry-sdk" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d1/11/4ad0979d0bb13ae5a845214e97c8d42da43980034c30d6f72d8e0ebe580e/opentelemetry_exporter_otlp_proto_grpc-1.37.0.tar.gz", hash = "sha256:f55bcb9fc848ce05ad3dd954058bc7b126624d22c4d9e958da24d8537763bec5", size = 24465 } +sdist = { url = "https://files.pythonhosted.org/packages/a2/c0/43222f5b97dc10812bc4f0abc5dc7cd0a2525a91b5151d26c9e2e958f52e/opentelemetry_exporter_otlp_proto_grpc-1.38.0.tar.gz", hash = "sha256:2473935e9eac71f401de6101d37d6f3f0f1831db92b953c7dcc912536158ebd6", size = 24676 } wheels = [ - { url = "https://files.pythonhosted.org/packages/39/17/46630b74751031a658706bef23ac99cdc2953cd3b2d28ec90590a0766b3e/opentelemetry_exporter_otlp_proto_grpc-1.37.0-py3-none-any.whl", hash = "sha256:aee5104835bf7993b7ddaaf380b6467472abaedb1f1dbfcc54a52a7d781a3890", size = 19305 }, + { url = "https://files.pythonhosted.org/packages/28/f0/bd831afbdba74ca2ce3982142a2fad707f8c487e8a3b6fef01f1d5945d1b/opentelemetry_exporter_otlp_proto_grpc-1.38.0-py3-none-any.whl", hash = "sha256:7c49fd9b4bd0dbe9ba13d91f764c2d20b0025649a6e4ac35792fb8d84d764bc7", size = 19695 }, ] [[package]] name = "opentelemetry-exporter-otlp-proto-http" -version = "1.37.0" +version = "1.38.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "googleapis-common-protos" }, @@ -3414,48 +3480,48 @@ dependencies = [ { name = "requests" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5d/e3/6e320aeb24f951449e73867e53c55542bebbaf24faeee7623ef677d66736/opentelemetry_exporter_otlp_proto_http-1.37.0.tar.gz", hash = "sha256:e52e8600f1720d6de298419a802108a8f5afa63c96809ff83becb03f874e44ac", size = 17281 } +sdist = { url = "https://files.pythonhosted.org/packages/81/0a/debcdfb029fbd1ccd1563f7c287b89a6f7bef3b2902ade56797bfd020854/opentelemetry_exporter_otlp_proto_http-1.38.0.tar.gz", hash = "sha256:f16bd44baf15cbe07633c5112ffc68229d0edbeac7b37610be0b2def4e21e90b", size = 17282 } wheels = [ - { url = "https://files.pythonhosted.org/packages/e9/e9/70d74a664d83976556cec395d6bfedd9b85ec1498b778367d5f93e373397/opentelemetry_exporter_otlp_proto_http-1.37.0-py3-none-any.whl", hash = "sha256:54c42b39945a6cc9d9a2a33decb876eabb9547e0dcb49df090122773447f1aef", size = 19576 }, + { url = "https://files.pythonhosted.org/packages/e5/77/154004c99fb9f291f74aa0822a2f5bbf565a72d8126b3a1b63ed8e5f83c7/opentelemetry_exporter_otlp_proto_http-1.38.0-py3-none-any.whl", hash = "sha256:84b937305edfc563f08ec69b9cb2298be8188371217e867c1854d77198d0825b", size = 19579 }, ] [[package]] name = "opentelemetry-proto" -version = "1.37.0" +version = "1.38.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "protobuf" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/dd/ea/a75f36b463a36f3c5a10c0b5292c58b31dbdde74f6f905d3d0ab2313987b/opentelemetry_proto-1.37.0.tar.gz", hash = "sha256:30f5c494faf66f77faeaefa35ed4443c5edb3b0aa46dad073ed7210e1a789538", size = 46151 } +sdist = { url = "https://files.pythonhosted.org/packages/51/14/f0c4f0f6371b9cb7f9fa9ee8918bfd59ac7040c7791f1e6da32a1839780d/opentelemetry_proto-1.38.0.tar.gz", hash = "sha256:88b161e89d9d372ce723da289b7da74c3a8354a8e5359992be813942969ed468", size = 46152 } wheels = [ - { url = "https://files.pythonhosted.org/packages/c4/25/f89ea66c59bd7687e218361826c969443c4fa15dfe89733f3bf1e2a9e971/opentelemetry_proto-1.37.0-py3-none-any.whl", hash = "sha256:8ed8c066ae8828bbf0c39229979bdf583a126981142378a9cbe9d6fd5701c6e2", size = 72534 }, + { url = "https://files.pythonhosted.org/packages/b6/6a/82b68b14efca5150b2632f3692d627afa76b77378c4999f2648979409528/opentelemetry_proto-1.38.0-py3-none-any.whl", hash = "sha256:b6ebe54d3217c42e45462e2a1ae28c3e2bf2ec5a5645236a490f55f45f1a0a18", size = 72535 }, ] [[package]] name = "opentelemetry-sdk" -version = "1.37.0" +version = "1.38.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-api" }, { name = "opentelemetry-semantic-conventions" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f4/62/2e0ca80d7fe94f0b193135375da92c640d15fe81f636658d2acf373086bc/opentelemetry_sdk-1.37.0.tar.gz", hash = "sha256:cc8e089c10953ded765b5ab5669b198bbe0af1b3f89f1007d19acd32dc46dda5", size = 170404 } +sdist = { url = "https://files.pythonhosted.org/packages/85/cb/f0eee1445161faf4c9af3ba7b848cc22a50a3d3e2515051ad8628c35ff80/opentelemetry_sdk-1.38.0.tar.gz", hash = "sha256:93df5d4d871ed09cb4272305be4d996236eedb232253e3ab864c8620f051cebe", size = 171942 } wheels = [ - { url = "https://files.pythonhosted.org/packages/9f/62/9f4ad6a54126fb00f7ed4bb5034964c6e4f00fcd5a905e115bd22707e20d/opentelemetry_sdk-1.37.0-py3-none-any.whl", hash = "sha256:8f3c3c22063e52475c5dbced7209495c2c16723d016d39287dfc215d1771257c", size = 131941 }, + { url = "https://files.pythonhosted.org/packages/2f/2e/e93777a95d7d9c40d270a371392b6d6f1ff170c2a3cb32d6176741b5b723/opentelemetry_sdk-1.38.0-py3-none-any.whl", hash = "sha256:1c66af6564ecc1553d72d811a01df063ff097cdc82ce188da9951f93b8d10f6b", size = 132349 }, ] [[package]] name = "opentelemetry-semantic-conventions" -version = "0.58b0" +version = "0.59b0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-api" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/aa/1b/90701d91e6300d9f2fb352153fb1721ed99ed1f6ea14fa992c756016e63a/opentelemetry_semantic_conventions-0.58b0.tar.gz", hash = "sha256:6bd46f51264279c433755767bb44ad00f1c9e2367e1b42af563372c5a6fa0c25", size = 129867 } +sdist = { url = "https://files.pythonhosted.org/packages/40/bc/8b9ad3802cd8ac6583a4eb7de7e5d7db004e89cb7efe7008f9c8a537ee75/opentelemetry_semantic_conventions-0.59b0.tar.gz", hash = "sha256:7a6db3f30d70202d5bf9fa4b69bc866ca6a30437287de6c510fb594878aed6b0", size = 129861 } wheels = [ - { url = "https://files.pythonhosted.org/packages/07/90/68152b7465f50285d3ce2481b3aec2f82822e3f52e5152eeeaf516bab841/opentelemetry_semantic_conventions-0.58b0-py3-none-any.whl", hash = "sha256:5564905ab1458b96684db1340232729fce3b5375a06e140e8904c78e4f815b28", size = 207954 }, + { url = "https://files.pythonhosted.org/packages/24/7d/c88d7b15ba8fe5c6b8f93be50fc11795e9fc05386c44afaf6b76fe191f9b/opentelemetry_semantic_conventions-0.59b0-py3-none-any.whl", hash = "sha256:35d3b8833ef97d614136e253c1da9342b4c3c083bbaf29ce31d572a1c3825eed", size = 207954 }, ] [[package]] @@ -3524,104 +3590,100 @@ wheels = [ [[package]] name = "pillow" -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 }, +version = "12.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5a/b0/cace85a1b0c9775a9f8f5d5423c8261c858760e2466c79b2dd184638b056/pillow-12.0.0.tar.gz", hash = "sha256:87d4f8125c9988bfbed67af47dd7a953e2fc7b0cc1e7800ec6d2080d490bb353", size = 47008828 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5d/08/26e68b6b5da219c2a2cb7b563af008b53bb8e6b6fcb3fa40715fcdb2523a/pillow-12.0.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:3adfb466bbc544b926d50fe8f4a4e6abd8c6bffd28a26177594e6e9b2b76572b", size = 5289809 }, + { url = "https://files.pythonhosted.org/packages/cb/e9/4e58fb097fb74c7b4758a680aacd558810a417d1edaa7000142976ef9d2f/pillow-12.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1ac11e8ea4f611c3c0147424eae514028b5e9077dd99ab91e1bd7bc33ff145e1", size = 4650606 }, + { url = "https://files.pythonhosted.org/packages/4b/e0/1fa492aa9f77b3bc6d471c468e62bfea1823056bf7e5e4f1914d7ab2565e/pillow-12.0.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d49e2314c373f4c2b39446fb1a45ed333c850e09d0c59ac79b72eb3b95397363", size = 6221023 }, + { url = "https://files.pythonhosted.org/packages/c1/09/4de7cd03e33734ccd0c876f0251401f1314e819cbfd89a0fcb6e77927cc6/pillow-12.0.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c7b2a63fd6d5246349f3d3f37b14430d73ee7e8173154461785e43036ffa96ca", size = 8024937 }, + { url = "https://files.pythonhosted.org/packages/2e/69/0688e7c1390666592876d9d474f5e135abb4acb39dcb583c4dc5490f1aff/pillow-12.0.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d64317d2587c70324b79861babb9c09f71fbb780bad212018874b2c013d8600e", size = 6334139 }, + { url = "https://files.pythonhosted.org/packages/ed/1c/880921e98f525b9b44ce747ad1ea8f73fd7e992bafe3ca5e5644bf433dea/pillow-12.0.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d77153e14b709fd8b8af6f66a3afbb9ed6e9fc5ccf0b6b7e1ced7b036a228782", size = 7026074 }, + { url = "https://files.pythonhosted.org/packages/28/03/96f718331b19b355610ef4ebdbbde3557c726513030665071fd025745671/pillow-12.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:32ed80ea8a90ee3e6fa08c21e2e091bba6eda8eccc83dbc34c95169507a91f10", size = 6448852 }, + { url = "https://files.pythonhosted.org/packages/3a/a0/6a193b3f0cc9437b122978d2c5cbce59510ccf9a5b48825096ed7472da2f/pillow-12.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c828a1ae702fc712978bda0320ba1b9893d99be0badf2647f693cc01cf0f04fa", size = 7117058 }, + { url = "https://files.pythonhosted.org/packages/a7/c4/043192375eaa4463254e8e61f0e2ec9a846b983929a8d0a7122e0a6d6fff/pillow-12.0.0-cp310-cp310-win32.whl", hash = "sha256:bd87e140e45399c818fac4247880b9ce719e4783d767e030a883a970be632275", size = 6295431 }, + { url = "https://files.pythonhosted.org/packages/92/c6/c2f2fc7e56301c21827e689bb8b0b465f1b52878b57471a070678c0c33cd/pillow-12.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:455247ac8a4cfb7b9bc45b7e432d10421aea9fc2e74d285ba4072688a74c2e9d", size = 7000412 }, + { url = "https://files.pythonhosted.org/packages/b2/d2/5f675067ba82da7a1c238a73b32e3fd78d67f9d9f80fbadd33a40b9c0481/pillow-12.0.0-cp310-cp310-win_arm64.whl", hash = "sha256:6ace95230bfb7cd79ef66caa064bbe2f2a1e63d93471c3a2e1f1348d9f22d6b7", size = 2435903 }, + { url = "https://files.pythonhosted.org/packages/0e/5a/a2f6773b64edb921a756eb0729068acad9fc5208a53f4a349396e9436721/pillow-12.0.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:0fd00cac9c03256c8b2ff58f162ebcd2587ad3e1f2e397eab718c47e24d231cc", size = 5289798 }, + { url = "https://files.pythonhosted.org/packages/2e/05/069b1f8a2e4b5a37493da6c5868531c3f77b85e716ad7a590ef87d58730d/pillow-12.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a3475b96f5908b3b16c47533daaa87380c491357d197564e0ba34ae75c0f3257", size = 4650589 }, + { url = "https://files.pythonhosted.org/packages/61/e3/2c820d6e9a36432503ead175ae294f96861b07600a7156154a086ba7111a/pillow-12.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:110486b79f2d112cf6add83b28b627e369219388f64ef2f960fef9ebaf54c642", size = 6230472 }, + { url = "https://files.pythonhosted.org/packages/4f/89/63427f51c64209c5e23d4d52071c8d0f21024d3a8a487737caaf614a5795/pillow-12.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5269cc1caeedb67e6f7269a42014f381f45e2e7cd42d834ede3c703a1d915fe3", size = 8033887 }, + { url = "https://files.pythonhosted.org/packages/f6/1b/c9711318d4901093c15840f268ad649459cd81984c9ec9887756cca049a5/pillow-12.0.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aa5129de4e174daccbc59d0a3b6d20eaf24417d59851c07ebb37aeb02947987c", size = 6343964 }, + { url = "https://files.pythonhosted.org/packages/41/1e/db9470f2d030b4995083044cd8738cdd1bf773106819f6d8ba12597d5352/pillow-12.0.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bee2a6db3a7242ea309aa7ee8e2780726fed67ff4e5b40169f2c940e7eb09227", size = 7034756 }, + { url = "https://files.pythonhosted.org/packages/cc/b0/6177a8bdd5ee4ed87cba2de5a3cc1db55ffbbec6176784ce5bb75aa96798/pillow-12.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:90387104ee8400a7b4598253b4c406f8958f59fcf983a6cea2b50d59f7d63d0b", size = 6458075 }, + { url = "https://files.pythonhosted.org/packages/bc/5e/61537aa6fa977922c6a03253a0e727e6e4a72381a80d63ad8eec350684f2/pillow-12.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bc91a56697869546d1b8f0a3ff35224557ae7f881050e99f615e0119bf934b4e", size = 7125955 }, + { url = "https://files.pythonhosted.org/packages/1f/3d/d5033539344ee3cbd9a4d69e12e63ca3a44a739eb2d4c8da350a3d38edd7/pillow-12.0.0-cp311-cp311-win32.whl", hash = "sha256:27f95b12453d165099c84f8a8bfdfd46b9e4bda9e0e4b65f0635430027f55739", size = 6298440 }, + { url = "https://files.pythonhosted.org/packages/4d/42/aaca386de5cc8bd8a0254516957c1f265e3521c91515b16e286c662854c4/pillow-12.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:b583dc9070312190192631373c6c8ed277254aa6e6084b74bdd0a6d3b221608e", size = 6999256 }, + { url = "https://files.pythonhosted.org/packages/ba/f1/9197c9c2d5708b785f631a6dfbfa8eb3fb9672837cb92ae9af812c13b4ed/pillow-12.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:759de84a33be3b178a64c8ba28ad5c135900359e85fb662bc6e403ad4407791d", size = 2436025 }, + { url = "https://files.pythonhosted.org/packages/2c/90/4fcce2c22caf044e660a198d740e7fbc14395619e3cb1abad12192c0826c/pillow-12.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:53561a4ddc36facb432fae7a9d8afbfaf94795414f5cdc5fc52f28c1dca90371", size = 5249377 }, + { url = "https://files.pythonhosted.org/packages/fd/e0/ed960067543d080691d47d6938ebccbf3976a931c9567ab2fbfab983a5dd/pillow-12.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:71db6b4c1653045dacc1585c1b0d184004f0d7e694c7b34ac165ca70c0838082", size = 4650343 }, + { url = "https://files.pythonhosted.org/packages/e7/a1/f81fdeddcb99c044bf7d6faa47e12850f13cee0849537a7d27eeab5534d4/pillow-12.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2fa5f0b6716fc88f11380b88b31fe591a06c6315e955c096c35715788b339e3f", size = 6232981 }, + { url = "https://files.pythonhosted.org/packages/88/e1/9098d3ce341a8750b55b0e00c03f1630d6178f38ac191c81c97a3b047b44/pillow-12.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:82240051c6ca513c616f7f9da06e871f61bfd7805f566275841af15015b8f98d", size = 8041399 }, + { url = "https://files.pythonhosted.org/packages/a7/62/a22e8d3b602ae8cc01446d0c57a54e982737f44b6f2e1e019a925143771d/pillow-12.0.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:55f818bd74fe2f11d4d7cbc65880a843c4075e0ac7226bc1a23261dbea531953", size = 6347740 }, + { url = "https://files.pythonhosted.org/packages/4f/87/424511bdcd02c8d7acf9f65caa09f291a519b16bd83c3fb3374b3d4ae951/pillow-12.0.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b87843e225e74576437fd5b6a4c2205d422754f84a06942cfaf1dc32243e45a8", size = 7040201 }, + { url = "https://files.pythonhosted.org/packages/dc/4d/435c8ac688c54d11755aedfdd9f29c9eeddf68d150fe42d1d3dbd2365149/pillow-12.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c607c90ba67533e1b2355b821fef6764d1dd2cbe26b8c1005ae84f7aea25ff79", size = 6462334 }, + { url = "https://files.pythonhosted.org/packages/2b/f2/ad34167a8059a59b8ad10bc5c72d4d9b35acc6b7c0877af8ac885b5f2044/pillow-12.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:21f241bdd5080a15bc86d3466a9f6074a9c2c2b314100dd896ac81ee6db2f1ba", size = 7134162 }, + { url = "https://files.pythonhosted.org/packages/0c/b1/a7391df6adacf0a5c2cf6ac1cf1fcc1369e7d439d28f637a847f8803beb3/pillow-12.0.0-cp312-cp312-win32.whl", hash = "sha256:dd333073e0cacdc3089525c7df7d39b211bcdf31fc2824e49d01c6b6187b07d0", size = 6298769 }, + { url = "https://files.pythonhosted.org/packages/a2/0b/d87733741526541c909bbf159e338dcace4f982daac6e5a8d6be225ca32d/pillow-12.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:9fe611163f6303d1619bbcb653540a4d60f9e55e622d60a3108be0d5b441017a", size = 7001107 }, + { url = "https://files.pythonhosted.org/packages/bc/96/aaa61ce33cc98421fb6088af2a03be4157b1e7e0e87087c888e2370a7f45/pillow-12.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:7dfb439562f234f7d57b1ac6bc8fe7f838a4bd49c79230e0f6a1da93e82f1fad", size = 2436012 }, + { url = "https://files.pythonhosted.org/packages/62/f2/de993bb2d21b33a98d031ecf6a978e4b61da207bef02f7b43093774c480d/pillow-12.0.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:0869154a2d0546545cde61d1789a6524319fc1897d9ee31218eae7a60ccc5643", size = 4045493 }, + { url = "https://files.pythonhosted.org/packages/0e/b6/bc8d0c4c9f6f111a783d045310945deb769b806d7574764234ffd50bc5ea/pillow-12.0.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:a7921c5a6d31b3d756ec980f2f47c0cfdbce0fc48c22a39347a895f41f4a6ea4", size = 4120461 }, + { url = "https://files.pythonhosted.org/packages/5d/57/d60d343709366a353dc56adb4ee1e7d8a2cc34e3fbc22905f4167cfec119/pillow-12.0.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:1ee80a59f6ce048ae13cda1abf7fbd2a34ab9ee7d401c46be3ca685d1999a399", size = 3576912 }, + { url = "https://files.pythonhosted.org/packages/a4/a4/a0a31467e3f83b94d37568294b01d22b43ae3c5d85f2811769b9c66389dd/pillow-12.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c50f36a62a22d350c96e49ad02d0da41dbd17ddc2e29750dbdba4323f85eb4a5", size = 5249132 }, + { url = "https://files.pythonhosted.org/packages/83/06/48eab21dd561de2914242711434c0c0eb992ed08ff3f6107a5f44527f5e9/pillow-12.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5193fde9a5f23c331ea26d0cf171fbf67e3f247585f50c08b3e205c7aeb4589b", size = 4650099 }, + { url = "https://files.pythonhosted.org/packages/fc/bd/69ed99fd46a8dba7c1887156d3572fe4484e3f031405fcc5a92e31c04035/pillow-12.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:bde737cff1a975b70652b62d626f7785e0480918dece11e8fef3c0cf057351c3", size = 6230808 }, + { url = "https://files.pythonhosted.org/packages/ea/94/8fad659bcdbf86ed70099cb60ae40be6acca434bbc8c4c0d4ef356d7e0de/pillow-12.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a6597ff2b61d121172f5844b53f21467f7082f5fb385a9a29c01414463f93b07", size = 8037804 }, + { url = "https://files.pythonhosted.org/packages/20/39/c685d05c06deecfd4e2d1950e9a908aa2ca8bc4e6c3b12d93b9cafbd7837/pillow-12.0.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0b817e7035ea7f6b942c13aa03bb554fc44fea70838ea21f8eb31c638326584e", size = 6345553 }, + { url = "https://files.pythonhosted.org/packages/38/57/755dbd06530a27a5ed74f8cb0a7a44a21722ebf318edbe67ddbd7fb28f88/pillow-12.0.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f4f1231b7dec408e8670264ce63e9c71409d9583dd21d32c163e25213ee2a344", size = 7037729 }, + { url = "https://files.pythonhosted.org/packages/ca/b6/7e94f4c41d238615674d06ed677c14883103dce1c52e4af16f000338cfd7/pillow-12.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6e51b71417049ad6ab14c49608b4a24d8fb3fe605e5dfabfe523b58064dc3d27", size = 6459789 }, + { url = "https://files.pythonhosted.org/packages/9c/14/4448bb0b5e0f22dd865290536d20ec8a23b64e2d04280b89139f09a36bb6/pillow-12.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d120c38a42c234dc9a8c5de7ceaaf899cf33561956acb4941653f8bdc657aa79", size = 7130917 }, + { url = "https://files.pythonhosted.org/packages/dd/ca/16c6926cc1c015845745d5c16c9358e24282f1e588237a4c36d2b30f182f/pillow-12.0.0-cp313-cp313-win32.whl", hash = "sha256:4cc6b3b2efff105c6a1656cfe59da4fdde2cda9af1c5e0b58529b24525d0a098", size = 6302391 }, + { url = "https://files.pythonhosted.org/packages/6d/2a/dd43dcfd6dae9b6a49ee28a8eedb98c7d5ff2de94a5d834565164667b97b/pillow-12.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:4cf7fed4b4580601c4345ceb5d4cbf5a980d030fd5ad07c4d2ec589f95f09905", size = 7007477 }, + { url = "https://files.pythonhosted.org/packages/77/f0/72ea067f4b5ae5ead653053212af05ce3705807906ba3f3e8f58ddf617e6/pillow-12.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:9f0b04c6b8584c2c193babcccc908b38ed29524b29dd464bc8801bf10d746a3a", size = 2435918 }, + { url = "https://files.pythonhosted.org/packages/f5/5e/9046b423735c21f0487ea6cb5b10f89ea8f8dfbe32576fe052b5ba9d4e5b/pillow-12.0.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:7fa22993bac7b77b78cae22bad1e2a987ddf0d9015c63358032f84a53f23cdc3", size = 5251406 }, + { url = "https://files.pythonhosted.org/packages/12/66/982ceebcdb13c97270ef7a56c3969635b4ee7cd45227fa707c94719229c5/pillow-12.0.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:f135c702ac42262573fe9714dfe99c944b4ba307af5eb507abef1667e2cbbced", size = 4653218 }, + { url = "https://files.pythonhosted.org/packages/16/b3/81e625524688c31859450119bf12674619429cab3119eec0e30a7a1029cb/pillow-12.0.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c85de1136429c524e55cfa4e033b4a7940ac5c8ee4d9401cc2d1bf48154bbc7b", size = 6266564 }, + { url = "https://files.pythonhosted.org/packages/98/59/dfb38f2a41240d2408096e1a76c671d0a105a4a8471b1871c6902719450c/pillow-12.0.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:38df9b4bfd3db902c9c2bd369bcacaf9d935b2fff73709429d95cc41554f7b3d", size = 8069260 }, + { url = "https://files.pythonhosted.org/packages/dc/3d/378dbea5cd1874b94c312425ca77b0f47776c78e0df2df751b820c8c1d6c/pillow-12.0.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7d87ef5795da03d742bf49439f9ca4d027cde49c82c5371ba52464aee266699a", size = 6379248 }, + { url = "https://files.pythonhosted.org/packages/84/b0/d525ef47d71590f1621510327acec75ae58c721dc071b17d8d652ca494d8/pillow-12.0.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:aff9e4d82d082ff9513bdd6acd4f5bd359f5b2c870907d2b0a9c5e10d40c88fe", size = 7066043 }, + { url = "https://files.pythonhosted.org/packages/61/2c/aced60e9cf9d0cde341d54bf7932c9ffc33ddb4a1595798b3a5150c7ec4e/pillow-12.0.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:8d8ca2b210ada074d57fcee40c30446c9562e542fc46aedc19baf758a93532ee", size = 6490915 }, + { url = "https://files.pythonhosted.org/packages/ef/26/69dcb9b91f4e59f8f34b2332a4a0a951b44f547c4ed39d3e4dcfcff48f89/pillow-12.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:99a7f72fb6249302aa62245680754862a44179b545ded638cf1fef59befb57ef", size = 7157998 }, + { url = "https://files.pythonhosted.org/packages/61/2b/726235842220ca95fa441ddf55dd2382b52ab5b8d9c0596fe6b3f23dafe8/pillow-12.0.0-cp313-cp313t-win32.whl", hash = "sha256:4078242472387600b2ce8d93ade8899c12bf33fa89e55ec89fe126e9d6d5d9e9", size = 6306201 }, + { url = "https://files.pythonhosted.org/packages/c0/3d/2afaf4e840b2df71344ababf2f8edd75a705ce500e5dc1e7227808312ae1/pillow-12.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:2c54c1a783d6d60595d3514f0efe9b37c8808746a66920315bfd34a938d7994b", size = 7013165 }, + { url = "https://files.pythonhosted.org/packages/6f/75/3fa09aa5cf6ed04bee3fa575798ddf1ce0bace8edb47249c798077a81f7f/pillow-12.0.0-cp313-cp313t-win_arm64.whl", hash = "sha256:26d9f7d2b604cd23aba3e9faf795787456ac25634d82cd060556998e39c6fa47", size = 2437834 }, + { url = "https://files.pythonhosted.org/packages/54/2a/9a8c6ba2c2c07b71bec92cf63e03370ca5e5f5c5b119b742bcc0cde3f9c5/pillow-12.0.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:beeae3f27f62308f1ddbcfb0690bf44b10732f2ef43758f169d5e9303165d3f9", size = 4045531 }, + { url = "https://files.pythonhosted.org/packages/84/54/836fdbf1bfb3d66a59f0189ff0b9f5f666cee09c6188309300df04ad71fa/pillow-12.0.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:d4827615da15cd59784ce39d3388275ec093ae3ee8d7f0c089b76fa87af756c2", size = 4120554 }, + { url = "https://files.pythonhosted.org/packages/0d/cd/16aec9f0da4793e98e6b54778a5fbce4f375c6646fe662e80600b8797379/pillow-12.0.0-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:3e42edad50b6909089750e65c91aa09aaf1e0a71310d383f11321b27c224ed8a", size = 3576812 }, + { url = "https://files.pythonhosted.org/packages/f6/b7/13957fda356dc46339298b351cae0d327704986337c3c69bb54628c88155/pillow-12.0.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:e5d8efac84c9afcb40914ab49ba063d94f5dbdf5066db4482c66a992f47a3a3b", size = 5252689 }, + { url = "https://files.pythonhosted.org/packages/fc/f5/eae31a306341d8f331f43edb2e9122c7661b975433de5e447939ae61c5da/pillow-12.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:266cd5f2b63ff316d5a1bba46268e603c9caf5606d44f38c2873c380950576ad", size = 4650186 }, + { url = "https://files.pythonhosted.org/packages/86/62/2a88339aa40c4c77e79108facbd307d6091e2c0eb5b8d3cf4977cfca2fe6/pillow-12.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:58eea5ebe51504057dd95c5b77d21700b77615ab0243d8152793dc00eb4faf01", size = 6230308 }, + { url = "https://files.pythonhosted.org/packages/c7/33/5425a8992bcb32d1cb9fa3dd39a89e613d09a22f2c8083b7bf43c455f760/pillow-12.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f13711b1a5ba512d647a0e4ba79280d3a9a045aaf7e0cc6fbe96b91d4cdf6b0c", size = 8039222 }, + { url = "https://files.pythonhosted.org/packages/d8/61/3f5d3b35c5728f37953d3eec5b5f3e77111949523bd2dd7f31a851e50690/pillow-12.0.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6846bd2d116ff42cba6b646edf5bf61d37e5cbd256425fa089fee4ff5c07a99e", size = 6346657 }, + { url = "https://files.pythonhosted.org/packages/3a/be/ee90a3d79271227e0f0a33c453531efd6ed14b2e708596ba5dd9be948da3/pillow-12.0.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c98fa880d695de164b4135a52fd2e9cd7b7c90a9d8ac5e9e443a24a95ef9248e", size = 7038482 }, + { url = "https://files.pythonhosted.org/packages/44/34/a16b6a4d1ad727de390e9bd9f19f5f669e079e5826ec0f329010ddea492f/pillow-12.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:fa3ed2a29a9e9d2d488b4da81dcb54720ac3104a20bf0bd273f1e4648aff5af9", size = 6461416 }, + { url = "https://files.pythonhosted.org/packages/b6/39/1aa5850d2ade7d7ba9f54e4e4c17077244ff7a2d9e25998c38a29749eb3f/pillow-12.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d034140032870024e6b9892c692fe2968493790dd57208b2c37e3fb35f6df3ab", size = 7131584 }, + { url = "https://files.pythonhosted.org/packages/bf/db/4fae862f8fad0167073a7733973bfa955f47e2cac3dc3e3e6257d10fab4a/pillow-12.0.0-cp314-cp314-win32.whl", hash = "sha256:1b1b133e6e16105f524a8dec491e0586d072948ce15c9b914e41cdadd209052b", size = 6400621 }, + { url = "https://files.pythonhosted.org/packages/2b/24/b350c31543fb0107ab2599464d7e28e6f856027aadda995022e695313d94/pillow-12.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:8dc232e39d409036af549c86f24aed8273a40ffa459981146829a324e0848b4b", size = 7142916 }, + { url = "https://files.pythonhosted.org/packages/0f/9b/0ba5a6fd9351793996ef7487c4fdbde8d3f5f75dbedc093bb598648fddf0/pillow-12.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:d52610d51e265a51518692045e372a4c363056130d922a7351429ac9f27e70b0", size = 2523836 }, + { url = "https://files.pythonhosted.org/packages/f5/7a/ceee0840aebc579af529b523d530840338ecf63992395842e54edc805987/pillow-12.0.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:1979f4566bb96c1e50a62d9831e2ea2d1211761e5662afc545fa766f996632f6", size = 5255092 }, + { url = "https://files.pythonhosted.org/packages/44/76/20776057b4bfd1aef4eeca992ebde0f53a4dce874f3ae693d0ec90a4f79b/pillow-12.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b2e4b27a6e15b04832fe9bf292b94b5ca156016bbc1ea9c2c20098a0320d6cf6", size = 4653158 }, + { url = "https://files.pythonhosted.org/packages/82/3f/d9ff92ace07be8836b4e7e87e6a4c7a8318d47c2f1463ffcf121fc57d9cb/pillow-12.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fb3096c30df99fd01c7bf8e544f392103d0795b9f98ba71a8054bcbf56b255f1", size = 6267882 }, + { url = "https://files.pythonhosted.org/packages/9f/7a/4f7ff87f00d3ad33ba21af78bfcd2f032107710baf8280e3722ceec28cda/pillow-12.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7438839e9e053ef79f7112c881cef684013855016f928b168b81ed5835f3e75e", size = 8071001 }, + { url = "https://files.pythonhosted.org/packages/75/87/fcea108944a52dad8cca0715ae6247e271eb80459364a98518f1e4f480c1/pillow-12.0.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d5c411a8eaa2299322b647cd932586b1427367fd3184ffbb8f7a219ea2041ca", size = 6380146 }, + { url = "https://files.pythonhosted.org/packages/91/52/0d31b5e571ef5fd111d2978b84603fce26aba1b6092f28e941cb46570745/pillow-12.0.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d7e091d464ac59d2c7ad8e7e08105eaf9dafbc3883fd7265ffccc2baad6ac925", size = 7067344 }, + { url = "https://files.pythonhosted.org/packages/7b/f4/2dd3d721f875f928d48e83bb30a434dee75a2531bca839bb996bb0aa5a91/pillow-12.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:792a2c0be4dcc18af9d4a2dfd8a11a17d5e25274a1062b0ec1c2d79c76f3e7f8", size = 6491864 }, + { url = "https://files.pythonhosted.org/packages/30/4b/667dfcf3d61fc309ba5a15b141845cece5915e39b99c1ceab0f34bf1d124/pillow-12.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:afbefa430092f71a9593a99ab6a4e7538bc9eabbf7bf94f91510d3503943edc4", size = 7158911 }, + { url = "https://files.pythonhosted.org/packages/a2/2f/16cabcc6426c32218ace36bf0d55955e813f2958afddbf1d391849fee9d1/pillow-12.0.0-cp314-cp314t-win32.whl", hash = "sha256:3830c769decf88f1289680a59d4f4c46c72573446352e2befec9a8512104fa52", size = 6408045 }, + { url = "https://files.pythonhosted.org/packages/35/73/e29aa0c9c666cf787628d3f0dcf379f4791fba79f4936d02f8b37165bdf8/pillow-12.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:905b0365b210c73afb0ebe9101a32572152dfd1c144c7e28968a331b9217b94a", size = 7148282 }, + { url = "https://files.pythonhosted.org/packages/c1/70/6b41bdcddf541b437bbb9f47f94d2db5d9ddef6c37ccab8c9107743748a4/pillow-12.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:99353a06902c2e43b43e8ff74ee65a7d90307d82370604746738a1e0661ccca7", size = 2525630 }, + { url = "https://files.pythonhosted.org/packages/1d/b3/582327e6c9f86d037b63beebe981425d6811104cb443e8193824ef1a2f27/pillow-12.0.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:b22bd8c974942477156be55a768f7aa37c46904c175be4e158b6a86e3a6b7ca8", size = 5215068 }, + { url = "https://files.pythonhosted.org/packages/fd/d6/67748211d119f3b6540baf90f92fae73ae51d5217b171b0e8b5f7e5d558f/pillow-12.0.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:805ebf596939e48dbb2e4922a1d3852cfc25c38160751ce02da93058b48d252a", size = 4614994 }, + { url = "https://files.pythonhosted.org/packages/2d/e1/f8281e5d844c41872b273b9f2c34a4bf64ca08905668c8ae730eedc7c9fa/pillow-12.0.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cae81479f77420d217def5f54b5b9d279804d17e982e0f2fa19b1d1e14ab5197", size = 5246639 }, + { url = "https://files.pythonhosted.org/packages/94/5a/0d8ab8ffe8a102ff5df60d0de5af309015163bf710c7bb3e8311dd3b3ad0/pillow-12.0.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:aeaefa96c768fc66818730b952a862235d68825c178f1b3ffd4efd7ad2edcb7c", size = 6986839 }, + { url = "https://files.pythonhosted.org/packages/20/2e/3434380e8110b76cd9eb00a363c484b050f949b4bbe84ba770bb8508a02c/pillow-12.0.0-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:09f2d0abef9e4e2f349305a4f8cc784a8a6c2f58a8c4892eea13b10a943bd26e", size = 5313505 }, + { url = "https://files.pythonhosted.org/packages/57/ca/5a9d38900d9d74785141d6580950fe705de68af735ff6e727cb911b64740/pillow-12.0.0-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bdee52571a343d721fb2eb3b090a82d959ff37fc631e3f70422e0c2e029f3e76", size = 5963654 }, + { url = "https://files.pythonhosted.org/packages/95/7e/f896623c3c635a90537ac093c6a618ebe1a90d87206e42309cb5d98a1b9e/pillow-12.0.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:b290fd8aa38422444d4b50d579de197557f182ef1068b75f5aa8558638b8d0a5", size = 6997850 }, ] [[package]] @@ -3744,105 +3806,131 @@ wheels = [ [[package]] name = "propcache" -version = "0.3.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a6/16/43264e4a779dd8588c21a70f0709665ee8f611211bdd2c87d952cfa7c776/propcache-0.3.2.tar.gz", hash = "sha256:20d7d62e4e7ef05f221e0db2856b979540686342e7dd9973b815599c7057e168", size = 44139 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ab/14/510deed325e262afeb8b360043c5d7c960da7d3ecd6d6f9496c9c56dc7f4/propcache-0.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:22d9962a358aedbb7a2e36187ff273adeaab9743373a272976d2e348d08c7770", size = 73178 }, - { url = "https://files.pythonhosted.org/packages/cd/4e/ad52a7925ff01c1325653a730c7ec3175a23f948f08626a534133427dcff/propcache-0.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0d0fda578d1dc3f77b6b5a5dce3b9ad69a8250a891760a548df850a5e8da87f3", size = 43133 }, - { url = "https://files.pythonhosted.org/packages/63/7c/e9399ba5da7780871db4eac178e9c2e204c23dd3e7d32df202092a1ed400/propcache-0.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3def3da3ac3ce41562d85db655d18ebac740cb3fa4367f11a52b3da9d03a5cc3", size = 43039 }, - { url = "https://files.pythonhosted.org/packages/22/e1/58da211eb8fdc6fc854002387d38f415a6ca5f5c67c1315b204a5d3e9d7a/propcache-0.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9bec58347a5a6cebf239daba9bda37dffec5b8d2ce004d9fe4edef3d2815137e", size = 201903 }, - { url = "https://files.pythonhosted.org/packages/c4/0a/550ea0f52aac455cb90111c8bab995208443e46d925e51e2f6ebdf869525/propcache-0.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:55ffda449a507e9fbd4aca1a7d9aa6753b07d6166140e5a18d2ac9bc49eac220", size = 213362 }, - { url = "https://files.pythonhosted.org/packages/5a/af/9893b7d878deda9bb69fcf54600b247fba7317761b7db11fede6e0f28bd0/propcache-0.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:64a67fb39229a8a8491dd42f864e5e263155e729c2e7ff723d6e25f596b1e8cb", size = 210525 }, - { url = "https://files.pythonhosted.org/packages/7c/bb/38fd08b278ca85cde36d848091ad2b45954bc5f15cce494bb300b9285831/propcache-0.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9da1cf97b92b51253d5b68cf5a2b9e0dafca095e36b7f2da335e27dc6172a614", size = 198283 }, - { url = "https://files.pythonhosted.org/packages/78/8c/9fe55bd01d362bafb413dfe508c48753111a1e269737fa143ba85693592c/propcache-0.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5f559e127134b07425134b4065be45b166183fdcb433cb6c24c8e4149056ad50", size = 191872 }, - { url = "https://files.pythonhosted.org/packages/54/14/4701c33852937a22584e08abb531d654c8bcf7948a8f87ad0a4822394147/propcache-0.3.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:aff2e4e06435d61f11a428360a932138d0ec288b0a31dd9bd78d200bd4a2b339", size = 199452 }, - { url = "https://files.pythonhosted.org/packages/16/44/447f2253d859602095356007657ee535e0093215ea0b3d1d6a41d16e5201/propcache-0.3.2-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:4927842833830942a5d0a56e6f4839bc484785b8e1ce8d287359794818633ba0", size = 191567 }, - { url = "https://files.pythonhosted.org/packages/f2/b3/e4756258749bb2d3b46defcff606a2f47410bab82be5824a67e84015b267/propcache-0.3.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:6107ddd08b02654a30fb8ad7a132021759d750a82578b94cd55ee2772b6ebea2", size = 193015 }, - { url = "https://files.pythonhosted.org/packages/1e/df/e6d3c7574233164b6330b9fd697beeac402afd367280e6dc377bb99b43d9/propcache-0.3.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:70bd8b9cd6b519e12859c99f3fc9a93f375ebd22a50296c3a295028bea73b9e7", size = 204660 }, - { url = "https://files.pythonhosted.org/packages/b2/53/e4d31dd5170b4a0e2e6b730f2385a96410633b4833dc25fe5dffd1f73294/propcache-0.3.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2183111651d710d3097338dd1893fcf09c9f54e27ff1a8795495a16a469cc90b", size = 206105 }, - { url = "https://files.pythonhosted.org/packages/7f/fe/74d54cf9fbe2a20ff786e5f7afcfde446588f0cf15fb2daacfbc267b866c/propcache-0.3.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:fb075ad271405dcad8e2a7ffc9a750a3bf70e533bd86e89f0603e607b93aa64c", size = 196980 }, - { url = "https://files.pythonhosted.org/packages/22/ec/c469c9d59dada8a7679625e0440b544fe72e99311a4679c279562051f6fc/propcache-0.3.2-cp310-cp310-win32.whl", hash = "sha256:404d70768080d3d3bdb41d0771037da19d8340d50b08e104ca0e7f9ce55fce70", size = 37679 }, - { url = "https://files.pythonhosted.org/packages/38/35/07a471371ac89d418f8d0b699c75ea6dca2041fbda360823de21f6a9ce0a/propcache-0.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:7435d766f978b4ede777002e6b3b6641dd229cd1da8d3d3106a45770365f9ad9", size = 41459 }, - { url = "https://files.pythonhosted.org/packages/80/8d/e8b436717ab9c2cfc23b116d2c297305aa4cd8339172a456d61ebf5669b8/propcache-0.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0b8d2f607bd8f80ddc04088bc2a037fdd17884a6fcadc47a96e334d72f3717be", size = 74207 }, - { url = "https://files.pythonhosted.org/packages/d6/29/1e34000e9766d112171764b9fa3226fa0153ab565d0c242c70e9945318a7/propcache-0.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:06766d8f34733416e2e34f46fea488ad5d60726bb9481d3cddf89a6fa2d9603f", size = 43648 }, - { url = "https://files.pythonhosted.org/packages/46/92/1ad5af0df781e76988897da39b5f086c2bf0f028b7f9bd1f409bb05b6874/propcache-0.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a2dc1f4a1df4fecf4e6f68013575ff4af84ef6f478fe5344317a65d38a8e6dc9", size = 43496 }, - { url = "https://files.pythonhosted.org/packages/b3/ce/e96392460f9fb68461fabab3e095cb00c8ddf901205be4eae5ce246e5b7e/propcache-0.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be29c4f4810c5789cf10ddf6af80b041c724e629fa51e308a7a0fb19ed1ef7bf", size = 217288 }, - { url = "https://files.pythonhosted.org/packages/c5/2a/866726ea345299f7ceefc861a5e782b045545ae6940851930a6adaf1fca6/propcache-0.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:59d61f6970ecbd8ff2e9360304d5c8876a6abd4530cb752c06586849ac8a9dc9", size = 227456 }, - { url = "https://files.pythonhosted.org/packages/de/03/07d992ccb6d930398689187e1b3c718339a1c06b8b145a8d9650e4726166/propcache-0.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:62180e0b8dbb6b004baec00a7983e4cc52f5ada9cd11f48c3528d8cfa7b96a66", size = 225429 }, - { url = "https://files.pythonhosted.org/packages/5d/e6/116ba39448753b1330f48ab8ba927dcd6cf0baea8a0ccbc512dfb49ba670/propcache-0.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c144ca294a204c470f18cf4c9d78887810d04a3e2fbb30eea903575a779159df", size = 213472 }, - { url = "https://files.pythonhosted.org/packages/a6/85/f01f5d97e54e428885a5497ccf7f54404cbb4f906688a1690cd51bf597dc/propcache-0.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c5c2a784234c28854878d68978265617aa6dc0780e53d44b4d67f3651a17a9a2", size = 204480 }, - { url = "https://files.pythonhosted.org/packages/e3/79/7bf5ab9033b8b8194cc3f7cf1aaa0e9c3256320726f64a3e1f113a812dce/propcache-0.3.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5745bc7acdafa978ca1642891b82c19238eadc78ba2aaa293c6863b304e552d7", size = 214530 }, - { url = "https://files.pythonhosted.org/packages/31/0b/bd3e0c00509b609317df4a18e6b05a450ef2d9a963e1d8bc9c9415d86f30/propcache-0.3.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:c0075bf773d66fa8c9d41f66cc132ecc75e5bb9dd7cce3cfd14adc5ca184cb95", size = 205230 }, - { url = "https://files.pythonhosted.org/packages/7a/23/fae0ff9b54b0de4e819bbe559508da132d5683c32d84d0dc2ccce3563ed4/propcache-0.3.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5f57aa0847730daceff0497f417c9de353c575d8da3579162cc74ac294c5369e", size = 206754 }, - { url = "https://files.pythonhosted.org/packages/b7/7f/ad6a3c22630aaa5f618b4dc3c3598974a72abb4c18e45a50b3cdd091eb2f/propcache-0.3.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:eef914c014bf72d18efb55619447e0aecd5fb7c2e3fa7441e2e5d6099bddff7e", size = 218430 }, - { url = "https://files.pythonhosted.org/packages/5b/2c/ba4f1c0e8a4b4c75910742f0d333759d441f65a1c7f34683b4a74c0ee015/propcache-0.3.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:2a4092e8549031e82facf3decdbc0883755d5bbcc62d3aea9d9e185549936dcf", size = 223884 }, - { url = "https://files.pythonhosted.org/packages/88/e4/ebe30fc399e98572019eee82ad0caf512401661985cbd3da5e3140ffa1b0/propcache-0.3.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:85871b050f174bc0bfb437efbdb68aaf860611953ed12418e4361bc9c392749e", size = 211480 }, - { url = "https://files.pythonhosted.org/packages/96/0a/7d5260b914e01d1d0906f7f38af101f8d8ed0dc47426219eeaf05e8ea7c2/propcache-0.3.2-cp311-cp311-win32.whl", hash = "sha256:36c8d9b673ec57900c3554264e630d45980fd302458e4ac801802a7fd2ef7897", size = 37757 }, - { url = "https://files.pythonhosted.org/packages/e1/2d/89fe4489a884bc0da0c3278c552bd4ffe06a1ace559db5ef02ef24ab446b/propcache-0.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:e53af8cb6a781b02d2ea079b5b853ba9430fcbe18a8e3ce647d5982a3ff69f39", size = 41500 }, - { url = "https://files.pythonhosted.org/packages/a8/42/9ca01b0a6f48e81615dca4765a8f1dd2c057e0540f6116a27dc5ee01dfb6/propcache-0.3.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:8de106b6c84506b31c27168582cd3cb3000a6412c16df14a8628e5871ff83c10", size = 73674 }, - { url = "https://files.pythonhosted.org/packages/af/6e/21293133beb550f9c901bbece755d582bfaf2176bee4774000bd4dd41884/propcache-0.3.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:28710b0d3975117239c76600ea351934ac7b5ff56e60953474342608dbbb6154", size = 43570 }, - { url = "https://files.pythonhosted.org/packages/0c/c8/0393a0a3a2b8760eb3bde3c147f62b20044f0ddac81e9d6ed7318ec0d852/propcache-0.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce26862344bdf836650ed2487c3d724b00fbfec4233a1013f597b78c1cb73615", size = 43094 }, - { url = "https://files.pythonhosted.org/packages/37/2c/489afe311a690399d04a3e03b069225670c1d489eb7b044a566511c1c498/propcache-0.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bca54bd347a253af2cf4544bbec232ab982f4868de0dd684246b67a51bc6b1db", size = 226958 }, - { url = "https://files.pythonhosted.org/packages/9d/ca/63b520d2f3d418c968bf596839ae26cf7f87bead026b6192d4da6a08c467/propcache-0.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:55780d5e9a2ddc59711d727226bb1ba83a22dd32f64ee15594b9392b1f544eb1", size = 234894 }, - { url = "https://files.pythonhosted.org/packages/11/60/1d0ed6fff455a028d678df30cc28dcee7af77fa2b0e6962ce1df95c9a2a9/propcache-0.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:035e631be25d6975ed87ab23153db6a73426a48db688070d925aa27e996fe93c", size = 233672 }, - { url = "https://files.pythonhosted.org/packages/37/7c/54fd5301ef38505ab235d98827207176a5c9b2aa61939b10a460ca53e123/propcache-0.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ee6f22b6eaa39297c751d0e80c0d3a454f112f5c6481214fcf4c092074cecd67", size = 224395 }, - { url = "https://files.pythonhosted.org/packages/ee/1a/89a40e0846f5de05fdc6779883bf46ba980e6df4d2ff8fb02643de126592/propcache-0.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7ca3aee1aa955438c4dba34fc20a9f390e4c79967257d830f137bd5a8a32ed3b", size = 212510 }, - { url = "https://files.pythonhosted.org/packages/5e/33/ca98368586c9566a6b8d5ef66e30484f8da84c0aac3f2d9aec6d31a11bd5/propcache-0.3.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7a4f30862869fa2b68380d677cc1c5fcf1e0f2b9ea0cf665812895c75d0ca3b8", size = 222949 }, - { url = "https://files.pythonhosted.org/packages/ba/11/ace870d0aafe443b33b2f0b7efdb872b7c3abd505bfb4890716ad7865e9d/propcache-0.3.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:b77ec3c257d7816d9f3700013639db7491a434644c906a2578a11daf13176251", size = 217258 }, - { url = "https://files.pythonhosted.org/packages/5b/d2/86fd6f7adffcfc74b42c10a6b7db721d1d9ca1055c45d39a1a8f2a740a21/propcache-0.3.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:cab90ac9d3f14b2d5050928483d3d3b8fb6b4018893fc75710e6aa361ecb2474", size = 213036 }, - { url = "https://files.pythonhosted.org/packages/07/94/2d7d1e328f45ff34a0a284cf5a2847013701e24c2a53117e7c280a4316b3/propcache-0.3.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:0b504d29f3c47cf6b9e936c1852246c83d450e8e063d50562115a6be6d3a2535", size = 227684 }, - { url = "https://files.pythonhosted.org/packages/b7/05/37ae63a0087677e90b1d14710e532ff104d44bc1efa3b3970fff99b891dc/propcache-0.3.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:ce2ac2675a6aa41ddb2a0c9cbff53780a617ac3d43e620f8fd77ba1c84dcfc06", size = 234562 }, - { url = "https://files.pythonhosted.org/packages/a4/7c/3f539fcae630408d0bd8bf3208b9a647ccad10976eda62402a80adf8fc34/propcache-0.3.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:62b4239611205294cc433845b914131b2a1f03500ff3c1ed093ed216b82621e1", size = 222142 }, - { url = "https://files.pythonhosted.org/packages/7c/d2/34b9eac8c35f79f8a962546b3e97e9d4b990c420ee66ac8255d5d9611648/propcache-0.3.2-cp312-cp312-win32.whl", hash = "sha256:df4a81b9b53449ebc90cc4deefb052c1dd934ba85012aa912c7ea7b7e38b60c1", size = 37711 }, - { url = "https://files.pythonhosted.org/packages/19/61/d582be5d226cf79071681d1b46b848d6cb03d7b70af7063e33a2787eaa03/propcache-0.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:7046e79b989d7fe457bb755844019e10f693752d169076138abf17f31380800c", size = 41479 }, - { url = "https://files.pythonhosted.org/packages/dc/d1/8c747fafa558c603c4ca19d8e20b288aa0c7cda74e9402f50f31eb65267e/propcache-0.3.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ca592ed634a73ca002967458187109265e980422116c0a107cf93d81f95af945", size = 71286 }, - { url = "https://files.pythonhosted.org/packages/61/99/d606cb7986b60d89c36de8a85d58764323b3a5ff07770a99d8e993b3fa73/propcache-0.3.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9ecb0aad4020e275652ba3975740f241bd12a61f1a784df044cf7477a02bc252", size = 42425 }, - { url = "https://files.pythonhosted.org/packages/8c/96/ef98f91bbb42b79e9bb82bdd348b255eb9d65f14dbbe3b1594644c4073f7/propcache-0.3.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7f08f1cc28bd2eade7a8a3d2954ccc673bb02062e3e7da09bc75d843386b342f", size = 41846 }, - { url = "https://files.pythonhosted.org/packages/5b/ad/3f0f9a705fb630d175146cd7b1d2bf5555c9beaed54e94132b21aac098a6/propcache-0.3.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1a342c834734edb4be5ecb1e9fb48cb64b1e2320fccbd8c54bf8da8f2a84c33", size = 208871 }, - { url = "https://files.pythonhosted.org/packages/3a/38/2085cda93d2c8b6ec3e92af2c89489a36a5886b712a34ab25de9fbca7992/propcache-0.3.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8a544caaae1ac73f1fecfae70ded3e93728831affebd017d53449e3ac052ac1e", size = 215720 }, - { url = "https://files.pythonhosted.org/packages/61/c1/d72ea2dc83ac7f2c8e182786ab0fc2c7bd123a1ff9b7975bee671866fe5f/propcache-0.3.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:310d11aa44635298397db47a3ebce7db99a4cc4b9bbdfcf6c98a60c8d5261cf1", size = 215203 }, - { url = "https://files.pythonhosted.org/packages/af/81/b324c44ae60c56ef12007105f1460d5c304b0626ab0cc6b07c8f2a9aa0b8/propcache-0.3.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c1396592321ac83157ac03a2023aa6cc4a3cc3cfdecb71090054c09e5a7cce3", size = 206365 }, - { url = "https://files.pythonhosted.org/packages/09/73/88549128bb89e66d2aff242488f62869014ae092db63ccea53c1cc75a81d/propcache-0.3.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8cabf5b5902272565e78197edb682017d21cf3b550ba0460ee473753f28d23c1", size = 196016 }, - { url = "https://files.pythonhosted.org/packages/b9/3f/3bdd14e737d145114a5eb83cb172903afba7242f67c5877f9909a20d948d/propcache-0.3.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0a2f2235ac46a7aa25bdeb03a9e7060f6ecbd213b1f9101c43b3090ffb971ef6", size = 205596 }, - { url = "https://files.pythonhosted.org/packages/0f/ca/2f4aa819c357d3107c3763d7ef42c03980f9ed5c48c82e01e25945d437c1/propcache-0.3.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:92b69e12e34869a6970fd2f3da91669899994b47c98f5d430b781c26f1d9f387", size = 200977 }, - { url = "https://files.pythonhosted.org/packages/cd/4a/e65276c7477533c59085251ae88505caf6831c0e85ff8b2e31ebcbb949b1/propcache-0.3.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:54e02207c79968ebbdffc169591009f4474dde3b4679e16634d34c9363ff56b4", size = 197220 }, - { url = "https://files.pythonhosted.org/packages/7c/54/fc7152e517cf5578278b242396ce4d4b36795423988ef39bb8cd5bf274c8/propcache-0.3.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4adfb44cb588001f68c5466579d3f1157ca07f7504fc91ec87862e2b8e556b88", size = 210642 }, - { url = "https://files.pythonhosted.org/packages/b9/80/abeb4a896d2767bf5f1ea7b92eb7be6a5330645bd7fb844049c0e4045d9d/propcache-0.3.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:fd3e6019dc1261cd0291ee8919dd91fbab7b169bb76aeef6c716833a3f65d206", size = 212789 }, - { url = "https://files.pythonhosted.org/packages/b3/db/ea12a49aa7b2b6d68a5da8293dcf50068d48d088100ac016ad92a6a780e6/propcache-0.3.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4c181cad81158d71c41a2bce88edce078458e2dd5ffee7eddd6b05da85079f43", size = 205880 }, - { url = "https://files.pythonhosted.org/packages/d1/e5/9076a0bbbfb65d1198007059c65639dfd56266cf8e477a9707e4b1999ff4/propcache-0.3.2-cp313-cp313-win32.whl", hash = "sha256:8a08154613f2249519e549de2330cf8e2071c2887309a7b07fb56098f5170a02", size = 37220 }, - { url = "https://files.pythonhosted.org/packages/d3/f5/b369e026b09a26cd77aa88d8fffd69141d2ae00a2abaaf5380d2603f4b7f/propcache-0.3.2-cp313-cp313-win_amd64.whl", hash = "sha256:e41671f1594fc4ab0a6dec1351864713cb3a279910ae8b58f884a88a0a632c05", size = 40678 }, - { url = "https://files.pythonhosted.org/packages/a4/3a/6ece377b55544941a08d03581c7bc400a3c8cd3c2865900a68d5de79e21f/propcache-0.3.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:9a3cf035bbaf035f109987d9d55dc90e4b0e36e04bbbb95af3055ef17194057b", size = 76560 }, - { url = "https://files.pythonhosted.org/packages/0c/da/64a2bb16418740fa634b0e9c3d29edff1db07f56d3546ca2d86ddf0305e1/propcache-0.3.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:156c03d07dc1323d8dacaa221fbe028c5c70d16709cdd63502778e6c3ccca1b0", size = 44676 }, - { url = "https://files.pythonhosted.org/packages/36/7b/f025e06ea51cb72c52fb87e9b395cced02786610b60a3ed51da8af017170/propcache-0.3.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:74413c0ba02ba86f55cf60d18daab219f7e531620c15f1e23d95563f505efe7e", size = 44701 }, - { url = "https://files.pythonhosted.org/packages/a4/00/faa1b1b7c3b74fc277f8642f32a4c72ba1d7b2de36d7cdfb676db7f4303e/propcache-0.3.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f066b437bb3fa39c58ff97ab2ca351db465157d68ed0440abecb21715eb24b28", size = 276934 }, - { url = "https://files.pythonhosted.org/packages/74/ab/935beb6f1756e0476a4d5938ff44bf0d13a055fed880caf93859b4f1baf4/propcache-0.3.2-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f1304b085c83067914721e7e9d9917d41ad87696bf70f0bc7dee450e9c71ad0a", size = 278316 }, - { url = "https://files.pythonhosted.org/packages/f8/9d/994a5c1ce4389610838d1caec74bdf0e98b306c70314d46dbe4fcf21a3e2/propcache-0.3.2-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ab50cef01b372763a13333b4e54021bdcb291fc9a8e2ccb9c2df98be51bcde6c", size = 282619 }, - { url = "https://files.pythonhosted.org/packages/2b/00/a10afce3d1ed0287cef2e09506d3be9822513f2c1e96457ee369adb9a6cd/propcache-0.3.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fad3b2a085ec259ad2c2842666b2a0a49dea8463579c606426128925af1ed725", size = 265896 }, - { url = "https://files.pythonhosted.org/packages/2e/a8/2aa6716ffa566ca57c749edb909ad27884680887d68517e4be41b02299f3/propcache-0.3.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:261fa020c1c14deafd54c76b014956e2f86991af198c51139faf41c4d5e83892", size = 252111 }, - { url = "https://files.pythonhosted.org/packages/36/4f/345ca9183b85ac29c8694b0941f7484bf419c7f0fea2d1e386b4f7893eed/propcache-0.3.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:46d7f8aa79c927e5f987ee3a80205c987717d3659f035c85cf0c3680526bdb44", size = 268334 }, - { url = "https://files.pythonhosted.org/packages/3e/ca/fcd54f78b59e3f97b3b9715501e3147f5340167733d27db423aa321e7148/propcache-0.3.2-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:6d8f3f0eebf73e3c0ff0e7853f68be638b4043c65a70517bb575eff54edd8dbe", size = 255026 }, - { url = "https://files.pythonhosted.org/packages/8b/95/8e6a6bbbd78ac89c30c225210a5c687790e532ba4088afb8c0445b77ef37/propcache-0.3.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:03c89c1b14a5452cf15403e291c0ccd7751d5b9736ecb2c5bab977ad6c5bcd81", size = 250724 }, - { url = "https://files.pythonhosted.org/packages/ee/b0/0dd03616142baba28e8b2d14ce5df6631b4673850a3d4f9c0f9dd714a404/propcache-0.3.2-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:0cc17efde71e12bbaad086d679ce575268d70bc123a5a71ea7ad76f70ba30bba", size = 268868 }, - { url = "https://files.pythonhosted.org/packages/c5/98/2c12407a7e4fbacd94ddd32f3b1e3d5231e77c30ef7162b12a60e2dd5ce3/propcache-0.3.2-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:acdf05d00696bc0447e278bb53cb04ca72354e562cf88ea6f9107df8e7fd9770", size = 271322 }, - { url = "https://files.pythonhosted.org/packages/35/91/9cb56efbb428b006bb85db28591e40b7736847b8331d43fe335acf95f6c8/propcache-0.3.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4445542398bd0b5d32df908031cb1b30d43ac848e20470a878b770ec2dcc6330", size = 265778 }, - { url = "https://files.pythonhosted.org/packages/9a/4c/b0fe775a2bdd01e176b14b574be679d84fc83958335790f7c9a686c1f468/propcache-0.3.2-cp313-cp313t-win32.whl", hash = "sha256:f86e5d7cd03afb3a1db8e9f9f6eff15794e79e791350ac48a8c924e6f439f394", size = 41175 }, - { url = "https://files.pythonhosted.org/packages/a4/ff/47f08595e3d9b5e149c150f88d9714574f1a7cbd89fe2817158a952674bf/propcache-0.3.2-cp313-cp313t-win_amd64.whl", hash = "sha256:9704bedf6e7cbe3c65eca4379a9b53ee6a83749f047808cbb5044d40d7d72198", size = 44857 }, - { url = "https://files.pythonhosted.org/packages/cc/35/cc0aaecf278bb4575b8555f2b137de5ab821595ddae9da9d3cd1da4072c7/propcache-0.3.2-py3-none-any.whl", hash = "sha256:98f1ec44fb675f5052cccc8e609c46ed23a35a1cfd18545ad4e29002d858a43f", size = 12663 }, +version = "0.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9e/da/e9fc233cf63743258bff22b3dfa7ea5baef7b5bc324af47a0ad89b8ffc6f/propcache-0.4.1.tar.gz", hash = "sha256:f48107a8c637e80362555f37ecf49abe20370e557cc4ab374f04ec4423c97c3d", size = 46442 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3c/0e/934b541323035566a9af292dba85a195f7b78179114f2c6ebb24551118a9/propcache-0.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c2d1fa3201efaf55d730400d945b5b3ab6e672e100ba0f9a409d950ab25d7db", size = 79534 }, + { url = "https://files.pythonhosted.org/packages/a1/6b/db0d03d96726d995dc7171286c6ba9d8d14251f37433890f88368951a44e/propcache-0.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1eb2994229cc8ce7fe9b3db88f5465f5fd8651672840b2e426b88cdb1a30aac8", size = 45526 }, + { url = "https://files.pythonhosted.org/packages/e4/c3/82728404aea669e1600f304f2609cde9e665c18df5a11cdd57ed73c1dceb/propcache-0.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:66c1f011f45a3b33d7bcb22daed4b29c0c9e2224758b6be00686731e1b46f925", size = 47263 }, + { url = "https://files.pythonhosted.org/packages/df/1b/39313ddad2bf9187a1432654c38249bab4562ef535ef07f5eb6eb04d0b1b/propcache-0.4.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9a52009f2adffe195d0b605c25ec929d26b36ef986ba85244891dee3b294df21", size = 201012 }, + { url = "https://files.pythonhosted.org/packages/5b/01/f1d0b57d136f294a142acf97f4ed58c8e5b974c21e543000968357115011/propcache-0.4.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5d4e2366a9c7b837555cf02fb9be2e3167d333aff716332ef1b7c3a142ec40c5", size = 209491 }, + { url = "https://files.pythonhosted.org/packages/a1/c8/038d909c61c5bb039070b3fb02ad5cccdb1dde0d714792e251cdb17c9c05/propcache-0.4.1-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:9d2b6caef873b4f09e26ea7e33d65f42b944837563a47a94719cc3544319a0db", size = 215319 }, + { url = "https://files.pythonhosted.org/packages/08/57/8c87e93142b2c1fa2408e45695205a7ba05fb5db458c0bf5c06ba0e09ea6/propcache-0.4.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2b16ec437a8c8a965ecf95739448dd938b5c7f56e67ea009f4300d8df05f32b7", size = 196856 }, + { url = "https://files.pythonhosted.org/packages/42/df/5615fec76aa561987a534759b3686008a288e73107faa49a8ae5795a9f7a/propcache-0.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:296f4c8ed03ca7476813fe666c9ea97869a8d7aec972618671b33a38a5182ef4", size = 193241 }, + { url = "https://files.pythonhosted.org/packages/d5/21/62949eb3a7a54afe8327011c90aca7e03547787a88fb8bd9726806482fea/propcache-0.4.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:1f0978529a418ebd1f49dad413a2b68af33f85d5c5ca5c6ca2a3bed375a7ac60", size = 190552 }, + { url = "https://files.pythonhosted.org/packages/30/ee/ab4d727dd70806e5b4de96a798ae7ac6e4d42516f030ee60522474b6b332/propcache-0.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fd138803047fb4c062b1c1dd95462f5209456bfab55c734458f15d11da288f8f", size = 200113 }, + { url = "https://files.pythonhosted.org/packages/8a/0b/38b46208e6711b016aa8966a3ac793eee0d05c7159d8342aa27fc0bc365e/propcache-0.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8c9b3cbe4584636d72ff556d9036e0c9317fa27b3ac1f0f558e7e84d1c9c5900", size = 200778 }, + { url = "https://files.pythonhosted.org/packages/cf/81/5abec54355ed344476bee711e9f04815d4b00a311ab0535599204eecc257/propcache-0.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f93243fdc5657247533273ac4f86ae106cc6445a0efacb9a1bfe982fcfefd90c", size = 193047 }, + { url = "https://files.pythonhosted.org/packages/ec/b6/1f237c04e32063cb034acd5f6ef34ef3a394f75502e72703545631ab1ef6/propcache-0.4.1-cp310-cp310-win32.whl", hash = "sha256:a0ee98db9c5f80785b266eb805016e36058ac72c51a064040f2bc43b61101cdb", size = 38093 }, + { url = "https://files.pythonhosted.org/packages/a6/67/354aac4e0603a15f76439caf0427781bcd6797f370377f75a642133bc954/propcache-0.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:1cdb7988c4e5ac7f6d175a28a9aa0c94cb6f2ebe52756a3c0cda98d2809a9e37", size = 41638 }, + { url = "https://files.pythonhosted.org/packages/e0/e1/74e55b9fd1a4c209ff1a9a824bf6c8b3d1fc5a1ac3eabe23462637466785/propcache-0.4.1-cp310-cp310-win_arm64.whl", hash = "sha256:d82ad62b19645419fe79dd63b3f9253e15b30e955c0170e5cebc350c1844e581", size = 38229 }, + { url = "https://files.pythonhosted.org/packages/8c/d4/4e2c9aaf7ac2242b9358f98dccd8f90f2605402f5afeff6c578682c2c491/propcache-0.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:60a8fda9644b7dfd5dece8c61d8a85e271cb958075bfc4e01083c148b61a7caf", size = 80208 }, + { url = "https://files.pythonhosted.org/packages/c2/21/d7b68e911f9c8e18e4ae43bdbc1e1e9bbd971f8866eb81608947b6f585ff/propcache-0.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c30b53e7e6bda1d547cabb47c825f3843a0a1a42b0496087bb58d8fedf9f41b5", size = 45777 }, + { url = "https://files.pythonhosted.org/packages/d3/1d/11605e99ac8ea9435651ee71ab4cb4bf03f0949586246476a25aadfec54a/propcache-0.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6918ecbd897443087a3b7cd978d56546a812517dcaaca51b49526720571fa93e", size = 47647 }, + { url = "https://files.pythonhosted.org/packages/58/1a/3c62c127a8466c9c843bccb503d40a273e5cc69838805f322e2826509e0d/propcache-0.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3d902a36df4e5989763425a8ab9e98cd8ad5c52c823b34ee7ef307fd50582566", size = 214929 }, + { url = "https://files.pythonhosted.org/packages/56/b9/8fa98f850960b367c4b8fe0592e7fc341daa7a9462e925228f10a60cf74f/propcache-0.4.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a9695397f85973bb40427dedddf70d8dc4a44b22f1650dd4af9eedf443d45165", size = 221778 }, + { url = "https://files.pythonhosted.org/packages/46/a6/0ab4f660eb59649d14b3d3d65c439421cf2f87fe5dd68591cbe3c1e78a89/propcache-0.4.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2bb07ffd7eaad486576430c89f9b215f9e4be68c4866a96e97db9e97fead85dc", size = 228144 }, + { url = "https://files.pythonhosted.org/packages/52/6a/57f43e054fb3d3a56ac9fc532bc684fc6169a26c75c353e65425b3e56eef/propcache-0.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fd6f30fdcf9ae2a70abd34da54f18da086160e4d7d9251f81f3da0ff84fc5a48", size = 210030 }, + { url = "https://files.pythonhosted.org/packages/40/e2/27e6feebb5f6b8408fa29f5efbb765cd54c153ac77314d27e457a3e993b7/propcache-0.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:fc38cba02d1acba4e2869eef1a57a43dfbd3d49a59bf90dda7444ec2be6a5570", size = 208252 }, + { url = "https://files.pythonhosted.org/packages/9e/f8/91c27b22ccda1dbc7967f921c42825564fa5336a01ecd72eb78a9f4f53c2/propcache-0.4.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:67fad6162281e80e882fb3ec355398cf72864a54069d060321f6cd0ade95fe85", size = 202064 }, + { url = "https://files.pythonhosted.org/packages/f2/26/7f00bd6bd1adba5aafe5f4a66390f243acab58eab24ff1a08bebb2ef9d40/propcache-0.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f10207adf04d08bec185bae14d9606a1444715bc99180f9331c9c02093e1959e", size = 212429 }, + { url = "https://files.pythonhosted.org/packages/84/89/fd108ba7815c1117ddca79c228f3f8a15fc82a73bca8b142eb5de13b2785/propcache-0.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e9b0d8d0845bbc4cfcdcbcdbf5086886bc8157aa963c31c777ceff7846c77757", size = 216727 }, + { url = "https://files.pythonhosted.org/packages/79/37/3ec3f7e3173e73f1d600495d8b545b53802cbf35506e5732dd8578db3724/propcache-0.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:981333cb2f4c1896a12f4ab92a9cc8f09ea664e9b7dbdc4eff74627af3a11c0f", size = 205097 }, + { url = "https://files.pythonhosted.org/packages/61/b0/b2631c19793f869d35f47d5a3a56fb19e9160d3c119f15ac7344fc3ccae7/propcache-0.4.1-cp311-cp311-win32.whl", hash = "sha256:f1d2f90aeec838a52f1c1a32fe9a619fefd5e411721a9117fbf82aea638fe8a1", size = 38084 }, + { url = "https://files.pythonhosted.org/packages/f4/78/6cce448e2098e9f3bfc91bb877f06aa24b6ccace872e39c53b2f707c4648/propcache-0.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:364426a62660f3f699949ac8c621aad6977be7126c5807ce48c0aeb8e7333ea6", size = 41637 }, + { url = "https://files.pythonhosted.org/packages/9c/e9/754f180cccd7f51a39913782c74717c581b9cc8177ad0e949f4d51812383/propcache-0.4.1-cp311-cp311-win_arm64.whl", hash = "sha256:e53f3a38d3510c11953f3e6a33f205c6d1b001129f972805ca9b42fc308bc239", size = 38064 }, + { url = "https://files.pythonhosted.org/packages/a2/0f/f17b1b2b221d5ca28b4b876e8bb046ac40466513960646bda8e1853cdfa2/propcache-0.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e153e9cd40cc8945138822807139367f256f89c6810c2634a4f6902b52d3b4e2", size = 80061 }, + { url = "https://files.pythonhosted.org/packages/76/47/8ccf75935f51448ba9a16a71b783eb7ef6b9ee60f5d14c7f8a8a79fbeed7/propcache-0.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cd547953428f7abb73c5ad82cbb32109566204260d98e41e5dfdc682eb7f8403", size = 46037 }, + { url = "https://files.pythonhosted.org/packages/0a/b6/5c9a0e42df4d00bfb4a3cbbe5cf9f54260300c88a0e9af1f47ca5ce17ac0/propcache-0.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f048da1b4f243fc44f205dfd320933a951b8d89e0afd4c7cacc762a8b9165207", size = 47324 }, + { url = "https://files.pythonhosted.org/packages/9e/d3/6c7ee328b39a81ee877c962469f1e795f9db87f925251efeb0545e0020d0/propcache-0.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ec17c65562a827bba85e3872ead335f95405ea1674860d96483a02f5c698fa72", size = 225505 }, + { url = "https://files.pythonhosted.org/packages/01/5d/1c53f4563490b1d06a684742cc6076ef944bc6457df6051b7d1a877c057b/propcache-0.4.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:405aac25c6394ef275dee4c709be43745d36674b223ba4eb7144bf4d691b7367", size = 230242 }, + { url = "https://files.pythonhosted.org/packages/20/e1/ce4620633b0e2422207c3cb774a0ee61cac13abc6217763a7b9e2e3f4a12/propcache-0.4.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0013cb6f8dde4b2a2f66903b8ba740bdfe378c943c4377a200551ceb27f379e4", size = 238474 }, + { url = "https://files.pythonhosted.org/packages/46/4b/3aae6835b8e5f44ea6a68348ad90f78134047b503765087be2f9912140ea/propcache-0.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:15932ab57837c3368b024473a525e25d316d8353016e7cc0e5ba9eb343fbb1cf", size = 221575 }, + { url = "https://files.pythonhosted.org/packages/6e/a5/8a5e8678bcc9d3a1a15b9a29165640d64762d424a16af543f00629c87338/propcache-0.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:031dce78b9dc099f4c29785d9cf5577a3faf9ebf74ecbd3c856a7b92768c3df3", size = 216736 }, + { url = "https://files.pythonhosted.org/packages/f1/63/b7b215eddeac83ca1c6b934f89d09a625aa9ee4ba158338854c87210cc36/propcache-0.4.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:ab08df6c9a035bee56e31af99be621526bd237bea9f32def431c656b29e41778", size = 213019 }, + { url = "https://files.pythonhosted.org/packages/57/74/f580099a58c8af587cac7ba19ee7cb418506342fbbe2d4a4401661cca886/propcache-0.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4d7af63f9f93fe593afbf104c21b3b15868efb2c21d07d8732c0c4287e66b6a6", size = 220376 }, + { url = "https://files.pythonhosted.org/packages/c4/ee/542f1313aff7eaf19c2bb758c5d0560d2683dac001a1c96d0774af799843/propcache-0.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:cfc27c945f422e8b5071b6e93169679e4eb5bf73bbcbf1ba3ae3a83d2f78ebd9", size = 226988 }, + { url = "https://files.pythonhosted.org/packages/8f/18/9c6b015dd9c6930f6ce2229e1f02fb35298b847f2087ea2b436a5bfa7287/propcache-0.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:35c3277624a080cc6ec6f847cbbbb5b49affa3598c4535a0a4682a697aaa5c75", size = 215615 }, + { url = "https://files.pythonhosted.org/packages/80/9e/e7b85720b98c45a45e1fca6a177024934dc9bc5f4d5dd04207f216fc33ed/propcache-0.4.1-cp312-cp312-win32.whl", hash = "sha256:671538c2262dadb5ba6395e26c1731e1d52534bfe9ae56d0b5573ce539266aa8", size = 38066 }, + { url = "https://files.pythonhosted.org/packages/54/09/d19cff2a5aaac632ec8fc03737b223597b1e347416934c1b3a7df079784c/propcache-0.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:cb2d222e72399fcf5890d1d5cc1060857b9b236adff2792ff48ca2dfd46c81db", size = 41655 }, + { url = "https://files.pythonhosted.org/packages/68/ab/6b5c191bb5de08036a8c697b265d4ca76148efb10fa162f14af14fb5f076/propcache-0.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:204483131fb222bdaaeeea9f9e6c6ed0cac32731f75dfc1d4a567fc1926477c1", size = 37789 }, + { url = "https://files.pythonhosted.org/packages/bf/df/6d9c1b6ac12b003837dde8a10231a7344512186e87b36e855bef32241942/propcache-0.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:43eedf29202c08550aac1d14e0ee619b0430aaef78f85864c1a892294fbc28cf", size = 77750 }, + { url = "https://files.pythonhosted.org/packages/8b/e8/677a0025e8a2acf07d3418a2e7ba529c9c33caf09d3c1f25513023c1db56/propcache-0.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d62cdfcfd89ccb8de04e0eda998535c406bf5e060ffd56be6c586cbcc05b3311", size = 44780 }, + { url = "https://files.pythonhosted.org/packages/89/a4/92380f7ca60f99ebae761936bc48a72a639e8a47b29050615eef757cb2a7/propcache-0.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cae65ad55793da34db5f54e4029b89d3b9b9490d8abe1b4c7ab5d4b8ec7ebf74", size = 46308 }, + { url = "https://files.pythonhosted.org/packages/2d/48/c5ac64dee5262044348d1d78a5f85dd1a57464a60d30daee946699963eb3/propcache-0.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:333ddb9031d2704a301ee3e506dc46b1fe5f294ec198ed6435ad5b6a085facfe", size = 208182 }, + { url = "https://files.pythonhosted.org/packages/c6/0c/cd762dd011a9287389a6a3eb43aa30207bde253610cca06824aeabfe9653/propcache-0.4.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:fd0858c20f078a32cf55f7e81473d96dcf3b93fd2ccdb3d40fdf54b8573df3af", size = 211215 }, + { url = "https://files.pythonhosted.org/packages/30/3e/49861e90233ba36890ae0ca4c660e95df565b2cd15d4a68556ab5865974e/propcache-0.4.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:678ae89ebc632c5c204c794f8dab2837c5f159aeb59e6ed0539500400577298c", size = 218112 }, + { url = "https://files.pythonhosted.org/packages/f1/8b/544bc867e24e1bd48f3118cecd3b05c694e160a168478fa28770f22fd094/propcache-0.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d472aeb4fbf9865e0c6d622d7f4d54a4e101a89715d8904282bb5f9a2f476c3f", size = 204442 }, + { url = "https://files.pythonhosted.org/packages/50/a6/4282772fd016a76d3e5c0df58380a5ea64900afd836cec2c2f662d1b9bb3/propcache-0.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4d3df5fa7e36b3225954fba85589da77a0fe6a53e3976de39caf04a0db4c36f1", size = 199398 }, + { url = "https://files.pythonhosted.org/packages/3e/ec/d8a7cd406ee1ddb705db2139f8a10a8a427100347bd698e7014351c7af09/propcache-0.4.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:ee17f18d2498f2673e432faaa71698032b0127ebf23ae5974eeaf806c279df24", size = 196920 }, + { url = "https://files.pythonhosted.org/packages/f6/6c/f38ab64af3764f431e359f8baf9e0a21013e24329e8b85d2da32e8ed07ca/propcache-0.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:580e97762b950f993ae618e167e7be9256b8353c2dcd8b99ec100eb50f5286aa", size = 203748 }, + { url = "https://files.pythonhosted.org/packages/d6/e3/fa846bd70f6534d647886621388f0a265254d30e3ce47e5c8e6e27dbf153/propcache-0.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:501d20b891688eb8e7aa903021f0b72d5a55db40ffaab27edefd1027caaafa61", size = 205877 }, + { url = "https://files.pythonhosted.org/packages/e2/39/8163fc6f3133fea7b5f2827e8eba2029a0277ab2c5beee6c1db7b10fc23d/propcache-0.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a0bd56e5b100aef69bd8562b74b46254e7c8812918d3baa700c8a8009b0af66", size = 199437 }, + { url = "https://files.pythonhosted.org/packages/93/89/caa9089970ca49c7c01662bd0eeedfe85494e863e8043565aeb6472ce8fe/propcache-0.4.1-cp313-cp313-win32.whl", hash = "sha256:bcc9aaa5d80322bc2fb24bb7accb4a30f81e90ab8d6ba187aec0744bc302ad81", size = 37586 }, + { url = "https://files.pythonhosted.org/packages/f5/ab/f76ec3c3627c883215b5c8080debb4394ef5a7a29be811f786415fc1e6fd/propcache-0.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:381914df18634f5494334d201e98245c0596067504b9372d8cf93f4bb23e025e", size = 40790 }, + { url = "https://files.pythonhosted.org/packages/59/1b/e71ae98235f8e2ba5004d8cb19765a74877abf189bc53fc0c80d799e56c3/propcache-0.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:8873eb4460fd55333ea49b7d189749ecf6e55bf85080f11b1c4530ed3034cba1", size = 37158 }, + { url = "https://files.pythonhosted.org/packages/83/ce/a31bbdfc24ee0dcbba458c8175ed26089cf109a55bbe7b7640ed2470cfe9/propcache-0.4.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:92d1935ee1f8d7442da9c0c4fa7ac20d07e94064184811b685f5c4fada64553b", size = 81451 }, + { url = "https://files.pythonhosted.org/packages/25/9c/442a45a470a68456e710d96cacd3573ef26a1d0a60067e6a7d5e655621ed/propcache-0.4.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:473c61b39e1460d386479b9b2f337da492042447c9b685f28be4f74d3529e566", size = 46374 }, + { url = "https://files.pythonhosted.org/packages/f4/bf/b1d5e21dbc3b2e889ea4327044fb16312a736d97640fb8b6aa3f9c7b3b65/propcache-0.4.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:c0ef0aaafc66fbd87842a3fe3902fd889825646bc21149eafe47be6072725835", size = 48396 }, + { url = "https://files.pythonhosted.org/packages/f4/04/5b4c54a103d480e978d3c8a76073502b18db0c4bc17ab91b3cb5092ad949/propcache-0.4.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f95393b4d66bfae908c3ca8d169d5f79cd65636ae15b5e7a4f6e67af675adb0e", size = 275950 }, + { url = "https://files.pythonhosted.org/packages/b4/c1/86f846827fb969c4b78b0af79bba1d1ea2156492e1b83dea8b8a6ae27395/propcache-0.4.1-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c07fda85708bc48578467e85099645167a955ba093be0a2dcba962195676e859", size = 273856 }, + { url = "https://files.pythonhosted.org/packages/36/1d/fc272a63c8d3bbad6878c336c7a7dea15e8f2d23a544bda43205dfa83ada/propcache-0.4.1-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:af223b406d6d000830c6f65f1e6431783fc3f713ba3e6cc8c024d5ee96170a4b", size = 280420 }, + { url = "https://files.pythonhosted.org/packages/07/0c/01f2219d39f7e53d52e5173bcb09c976609ba30209912a0680adfb8c593a/propcache-0.4.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a78372c932c90ee474559c5ddfffd718238e8673c340dc21fe45c5b8b54559a0", size = 263254 }, + { url = "https://files.pythonhosted.org/packages/2d/18/cd28081658ce597898f0c4d174d4d0f3c5b6d4dc27ffafeef835c95eb359/propcache-0.4.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:564d9f0d4d9509e1a870c920a89b2fec951b44bf5ba7d537a9e7c1ccec2c18af", size = 261205 }, + { url = "https://files.pythonhosted.org/packages/7a/71/1f9e22eb8b8316701c2a19fa1f388c8a3185082607da8e406a803c9b954e/propcache-0.4.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:17612831fda0138059cc5546f4d12a2aacfb9e47068c06af35c400ba58ba7393", size = 247873 }, + { url = "https://files.pythonhosted.org/packages/4a/65/3d4b61f36af2b4eddba9def857959f1016a51066b4f1ce348e0cf7881f58/propcache-0.4.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:41a89040cb10bd345b3c1a873b2bf36413d48da1def52f268a055f7398514874", size = 262739 }, + { url = "https://files.pythonhosted.org/packages/2a/42/26746ab087faa77c1c68079b228810436ccd9a5ce9ac85e2b7307195fd06/propcache-0.4.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:e35b88984e7fa64aacecea39236cee32dd9bd8c55f57ba8a75cf2399553f9bd7", size = 263514 }, + { url = "https://files.pythonhosted.org/packages/94/13/630690fe201f5502d2403dd3cfd451ed8858fe3c738ee88d095ad2ff407b/propcache-0.4.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6f8b465489f927b0df505cbe26ffbeed4d6d8a2bbc61ce90eb074ff129ef0ab1", size = 257781 }, + { url = "https://files.pythonhosted.org/packages/92/f7/1d4ec5841505f423469efbfc381d64b7b467438cd5a4bbcbb063f3b73d27/propcache-0.4.1-cp313-cp313t-win32.whl", hash = "sha256:2ad890caa1d928c7c2965b48f3a3815c853180831d0e5503d35cf00c472f4717", size = 41396 }, + { url = "https://files.pythonhosted.org/packages/48/f0/615c30622316496d2cbbc29f5985f7777d3ada70f23370608c1d3e081c1f/propcache-0.4.1-cp313-cp313t-win_amd64.whl", hash = "sha256:f7ee0e597f495cf415bcbd3da3caa3bd7e816b74d0d52b8145954c5e6fd3ff37", size = 44897 }, + { url = "https://files.pythonhosted.org/packages/fd/ca/6002e46eccbe0e33dcd4069ef32f7f1c9e243736e07adca37ae8c4830ec3/propcache-0.4.1-cp313-cp313t-win_arm64.whl", hash = "sha256:929d7cbe1f01bb7baffb33dc14eb5691c95831450a26354cd210a8155170c93a", size = 39789 }, + { url = "https://files.pythonhosted.org/packages/8e/5c/bca52d654a896f831b8256683457ceddd490ec18d9ec50e97dfd8fc726a8/propcache-0.4.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3f7124c9d820ba5548d431afb4632301acf965db49e666aa21c305cbe8c6de12", size = 78152 }, + { url = "https://files.pythonhosted.org/packages/65/9b/03b04e7d82a5f54fb16113d839f5ea1ede58a61e90edf515f6577c66fa8f/propcache-0.4.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:c0d4b719b7da33599dfe3b22d3db1ef789210a0597bc650b7cee9c77c2be8c5c", size = 44869 }, + { url = "https://files.pythonhosted.org/packages/b2/fa/89a8ef0468d5833a23fff277b143d0573897cf75bd56670a6d28126c7d68/propcache-0.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:9f302f4783709a78240ebc311b793f123328716a60911d667e0c036bc5dcbded", size = 46596 }, + { url = "https://files.pythonhosted.org/packages/86/bd/47816020d337f4a746edc42fe8d53669965138f39ee117414c7d7a340cfe/propcache-0.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c80ee5802e3fb9ea37938e7eecc307fb984837091d5fd262bb37238b1ae97641", size = 206981 }, + { url = "https://files.pythonhosted.org/packages/df/f6/c5fa1357cc9748510ee55f37173eb31bfde6d94e98ccd9e6f033f2fc06e1/propcache-0.4.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ed5a841e8bb29a55fb8159ed526b26adc5bdd7e8bd7bf793ce647cb08656cdf4", size = 211490 }, + { url = "https://files.pythonhosted.org/packages/80/1e/e5889652a7c4a3846683401a48f0f2e5083ce0ec1a8a5221d8058fbd1adf/propcache-0.4.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:55c72fd6ea2da4c318e74ffdf93c4fe4e926051133657459131a95c846d16d44", size = 215371 }, + { url = "https://files.pythonhosted.org/packages/b2/f2/889ad4b2408f72fe1a4f6a19491177b30ea7bf1a0fd5f17050ca08cfc882/propcache-0.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8326e144341460402713f91df60ade3c999d601e7eb5ff8f6f7862d54de0610d", size = 201424 }, + { url = "https://files.pythonhosted.org/packages/27/73/033d63069b57b0812c8bd19f311faebeceb6ba31b8f32b73432d12a0b826/propcache-0.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:060b16ae65bc098da7f6d25bf359f1f31f688384858204fe5d652979e0015e5b", size = 197566 }, + { url = "https://files.pythonhosted.org/packages/dc/89/ce24f3dc182630b4e07aa6d15f0ff4b14ed4b9955fae95a0b54c58d66c05/propcache-0.4.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:89eb3fa9524f7bec9de6e83cf3faed9d79bffa560672c118a96a171a6f55831e", size = 193130 }, + { url = "https://files.pythonhosted.org/packages/a9/24/ef0d5fd1a811fb5c609278d0209c9f10c35f20581fcc16f818da959fc5b4/propcache-0.4.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:dee69d7015dc235f526fe80a9c90d65eb0039103fe565776250881731f06349f", size = 202625 }, + { url = "https://files.pythonhosted.org/packages/f5/02/98ec20ff5546f68d673df2f7a69e8c0d076b5abd05ca882dc7ee3a83653d/propcache-0.4.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:5558992a00dfd54ccbc64a32726a3357ec93825a418a401f5cc67df0ac5d9e49", size = 204209 }, + { url = "https://files.pythonhosted.org/packages/a0/87/492694f76759b15f0467a2a93ab68d32859672b646aa8a04ce4864e7932d/propcache-0.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c9b822a577f560fbd9554812526831712c1436d2c046cedee4c3796d3543b144", size = 197797 }, + { url = "https://files.pythonhosted.org/packages/ee/36/66367de3575db1d2d3f3d177432bd14ee577a39d3f5d1b3d5df8afe3b6e2/propcache-0.4.1-cp314-cp314-win32.whl", hash = "sha256:ab4c29b49d560fe48b696cdcb127dd36e0bc2472548f3bf56cc5cb3da2b2984f", size = 38140 }, + { url = "https://files.pythonhosted.org/packages/0c/2a/a758b47de253636e1b8aef181c0b4f4f204bf0dd964914fb2af90a95b49b/propcache-0.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:5a103c3eb905fcea0ab98be99c3a9a5ab2de60228aa5aceedc614c0281cf6153", size = 41257 }, + { url = "https://files.pythonhosted.org/packages/34/5e/63bd5896c3fec12edcbd6f12508d4890d23c265df28c74b175e1ef9f4f3b/propcache-0.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:74c1fb26515153e482e00177a1ad654721bf9207da8a494a0c05e797ad27b992", size = 38097 }, + { url = "https://files.pythonhosted.org/packages/99/85/9ff785d787ccf9bbb3f3106f79884a130951436f58392000231b4c737c80/propcache-0.4.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:824e908bce90fb2743bd6b59db36eb4f45cd350a39637c9f73b1c1ea66f5b75f", size = 81455 }, + { url = "https://files.pythonhosted.org/packages/90/85/2431c10c8e7ddb1445c1f7c4b54d886e8ad20e3c6307e7218f05922cad67/propcache-0.4.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:c2b5e7db5328427c57c8e8831abda175421b709672f6cfc3d630c3b7e2146393", size = 46372 }, + { url = "https://files.pythonhosted.org/packages/01/20/b0972d902472da9bcb683fa595099911f4d2e86e5683bcc45de60dd05dc3/propcache-0.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6f6ff873ed40292cd4969ef5310179afd5db59fdf055897e282485043fc80ad0", size = 48411 }, + { url = "https://files.pythonhosted.org/packages/e2/e3/7dc89f4f21e8f99bad3d5ddb3a3389afcf9da4ac69e3deb2dcdc96e74169/propcache-0.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:49a2dc67c154db2c1463013594c458881a069fcf98940e61a0569016a583020a", size = 275712 }, + { url = "https://files.pythonhosted.org/packages/20/67/89800c8352489b21a8047c773067644e3897f02ecbbd610f4d46b7f08612/propcache-0.4.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:005f08e6a0529984491e37d8dbc3dd86f84bd78a8ceb5fa9a021f4c48d4984be", size = 273557 }, + { url = "https://files.pythonhosted.org/packages/e2/a1/b52b055c766a54ce6d9c16d9aca0cad8059acd9637cdf8aa0222f4a026ef/propcache-0.4.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5c3310452e0d31390da9035c348633b43d7e7feb2e37be252be6da45abd1abcc", size = 280015 }, + { url = "https://files.pythonhosted.org/packages/48/c8/33cee30bd890672c63743049f3c9e4be087e6780906bfc3ec58528be59c1/propcache-0.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c3c70630930447f9ef1caac7728c8ad1c56bc5015338b20fed0d08ea2480b3a", size = 262880 }, + { url = "https://files.pythonhosted.org/packages/0c/b1/8f08a143b204b418285c88b83d00edbd61afbc2c6415ffafc8905da7038b/propcache-0.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8e57061305815dfc910a3634dcf584f08168a8836e6999983569f51a8544cd89", size = 260938 }, + { url = "https://files.pythonhosted.org/packages/cf/12/96e4664c82ca2f31e1c8dff86afb867348979eb78d3cb8546a680287a1e9/propcache-0.4.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:521a463429ef54143092c11a77e04056dd00636f72e8c45b70aaa3140d639726", size = 247641 }, + { url = "https://files.pythonhosted.org/packages/18/ed/e7a9cfca28133386ba52278136d42209d3125db08d0a6395f0cba0c0285c/propcache-0.4.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:120c964da3fdc75e3731aa392527136d4ad35868cc556fd09bb6d09172d9a367", size = 262510 }, + { url = "https://files.pythonhosted.org/packages/f5/76/16d8bf65e8845dd62b4e2b57444ab81f07f40caa5652b8969b87ddcf2ef6/propcache-0.4.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:d8f353eb14ee3441ee844ade4277d560cdd68288838673273b978e3d6d2c8f36", size = 263161 }, + { url = "https://files.pythonhosted.org/packages/e7/70/c99e9edb5d91d5ad8a49fa3c1e8285ba64f1476782fed10ab251ff413ba1/propcache-0.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ab2943be7c652f09638800905ee1bab2c544e537edb57d527997a24c13dc1455", size = 257393 }, + { url = "https://files.pythonhosted.org/packages/08/02/87b25304249a35c0915d236575bc3574a323f60b47939a2262b77632a3ee/propcache-0.4.1-cp314-cp314t-win32.whl", hash = "sha256:05674a162469f31358c30bcaa8883cb7829fa3110bf9c0991fe27d7896c42d85", size = 42546 }, + { url = "https://files.pythonhosted.org/packages/cb/ef/3c6ecf8b317aa982f309835e8f96987466123c6e596646d4e6a1dfcd080f/propcache-0.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:990f6b3e2a27d683cb7602ed6c86f15ee6b43b1194736f9baaeb93d0016633b1", size = 46259 }, + { url = "https://files.pythonhosted.org/packages/c4/2d/346e946d4951f37eca1e4f55be0f0174c52cd70720f84029b02f296f4a38/propcache-0.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:ecef2343af4cc68e05131e45024ba34f6095821988a9d0a02aa7c73fcc448aa9", size = 40428 }, + { url = "https://files.pythonhosted.org/packages/5b/5a/bc7b4a4ef808fa59a816c17b20c4bef6884daebbdf627ff2a161da67da19/propcache-0.4.1-py3-none-any.whl", hash = "sha256:af2a6052aeb6cf17d3e46ee169099044fd8224cbaf75c76a2ef596e8163e2237", size = 13305 }, ] [[package]] name = "protobuf" -version = "6.32.1" +version = "6.33.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fa/a4/cc17347aa2897568beece2e674674359f911d6fe21b0b8d6268cd42727ac/protobuf-6.32.1.tar.gz", hash = "sha256:ee2469e4a021474ab9baafea6cd070e5bf27c7d29433504ddea1a4ee5850f68d", size = 440635 } +sdist = { url = "https://files.pythonhosted.org/packages/19/ff/64a6c8f420818bb873713988ca5492cba3a7946be57e027ac63495157d97/protobuf-6.33.0.tar.gz", hash = "sha256:140303d5c8d2037730c548f8c7b93b20bb1dc301be280c378b82b8894589c954", size = 443463 } wheels = [ - { url = "https://files.pythonhosted.org/packages/c0/98/645183ea03ab3995d29086b8bf4f7562ebd3d10c9a4b14ee3f20d47cfe50/protobuf-6.32.1-cp310-abi3-win32.whl", hash = "sha256:a8a32a84bc9f2aad712041b8b366190f71dde248926da517bde9e832e4412085", size = 424411 }, - { url = "https://files.pythonhosted.org/packages/8c/f3/6f58f841f6ebafe076cebeae33fc336e900619d34b1c93e4b5c97a81fdfa/protobuf-6.32.1-cp310-abi3-win_amd64.whl", hash = "sha256:b00a7d8c25fa471f16bc8153d0e53d6c9e827f0953f3c09aaa4331c718cae5e1", size = 435738 }, - { url = "https://files.pythonhosted.org/packages/10/56/a8a3f4e7190837139e68c7002ec749190a163af3e330f65d90309145a210/protobuf-6.32.1-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:d8c7e6eb619ffdf105ee4ab76af5a68b60a9d0f66da3ea12d1640e6d8dab7281", size = 426454 }, - { url = "https://files.pythonhosted.org/packages/3f/be/8dd0a927c559b37d7a6c8ab79034fd167dcc1f851595f2e641ad62be8643/protobuf-6.32.1-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:2f5b80a49e1eb7b86d85fcd23fe92df154b9730a725c3b38c4e43b9d77018bf4", size = 322874 }, - { url = "https://files.pythonhosted.org/packages/5c/f6/88d77011b605ef979aace37b7703e4eefad066f7e84d935e5a696515c2dd/protobuf-6.32.1-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:b1864818300c297265c83a4982fd3169f97122c299f56a56e2445c3698d34710", size = 322013 }, - { url = "https://files.pythonhosted.org/packages/97/b7/15cc7d93443d6c6a84626ae3258a91f4c6ac8c0edd5df35ea7658f71b79c/protobuf-6.32.1-py3-none-any.whl", hash = "sha256:2601b779fc7d32a866c6b4404f9d42a3f67c5b9f3f15b4db3cccabe06b95c346", size = 169289 }, + { url = "https://files.pythonhosted.org/packages/7e/ee/52b3fa8feb6db4a833dfea4943e175ce645144532e8a90f72571ad85df4e/protobuf-6.33.0-cp310-abi3-win32.whl", hash = "sha256:d6101ded078042a8f17959eccd9236fb7a9ca20d3b0098bbcb91533a5680d035", size = 425593 }, + { url = "https://files.pythonhosted.org/packages/7b/c6/7a465f1825872c55e0341ff4a80198743f73b69ce5d43ab18043699d1d81/protobuf-6.33.0-cp310-abi3-win_amd64.whl", hash = "sha256:9a031d10f703f03768f2743a1c403af050b6ae1f3480e9c140f39c45f81b13ee", size = 436882 }, + { url = "https://files.pythonhosted.org/packages/e1/a9/b6eee662a6951b9c3640e8e452ab3e09f117d99fc10baa32d1581a0d4099/protobuf-6.33.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:905b07a65f1a4b72412314082c7dbfae91a9e8b68a0cc1577515f8df58ecf455", size = 427521 }, + { url = "https://files.pythonhosted.org/packages/10/35/16d31e0f92c6d2f0e77c2a3ba93185130ea13053dd16200a57434c882f2b/protobuf-6.33.0-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:e0697ece353e6239b90ee43a9231318302ad8353c70e6e45499fa52396debf90", size = 324445 }, + { url = "https://files.pythonhosted.org/packages/e6/eb/2a981a13e35cda8b75b5585aaffae2eb904f8f351bdd3870769692acbd8a/protobuf-6.33.0-cp39-abi3-manylinux2014_s390x.whl", hash = "sha256:e0a1715e4f27355afd9570f3ea369735afc853a6c3951a6afe1f80d8569ad298", size = 339159 }, + { url = "https://files.pythonhosted.org/packages/21/51/0b1cbad62074439b867b4e04cc09b93f6699d78fd191bed2bbb44562e077/protobuf-6.33.0-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:35be49fd3f4fefa4e6e2aacc35e8b837d6703c37a2168a55ac21e9b1bc7559ef", size = 323172 }, + { url = "https://files.pythonhosted.org/packages/07/d1/0a28c21707807c6aacd5dc9c3704b2aa1effbf37adebd8caeaf68b17a636/protobuf-6.33.0-py3-none-any.whl", hash = "sha256:25c9e1963c6734448ea2d308cfa610e692b801304ba0908d7bfa564ac5132995", size = 170477 }, ] [[package]] @@ -3902,7 +3990,7 @@ wheels = [ [[package]] name = "pydantic" -version = "2.12.0" +version = "2.12.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-types" }, @@ -3910,9 +3998,9 @@ dependencies = [ { name = "typing-extensions" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c3/da/b8a7ee04378a53f6fefefc0c5e05570a3ebfdfa0523a878bcd3b475683ee/pydantic-2.12.0.tar.gz", hash = "sha256:c1a077e6270dbfb37bfd8b498b3981e2bb18f68103720e51fa6c306a5a9af563", size = 814760 } +sdist = { url = "https://files.pythonhosted.org/packages/f3/1e/4f0a3233767010308f2fd6bd0814597e3f63f1dc98304a9112b8759df4ff/pydantic-2.12.3.tar.gz", hash = "sha256:1da1c82b0fc140bb0103bc1441ffe062154c8d38491189751ee00fd8ca65ce74", size = 819383 } wheels = [ - { url = "https://files.pythonhosted.org/packages/f4/9d/d5c855424e2e5b6b626fbc6ec514d8e655a600377ce283008b115abb7445/pydantic-2.12.0-py3-none-any.whl", hash = "sha256:f6a1da352d42790537e95e83a8bdfb91c7efbae63ffd0b86fa823899e807116f", size = 459730 }, + { url = "https://files.pythonhosted.org/packages/a1/6b/83661fa77dcefa195ad5f8cd9af3d1a7450fd57cc883ad04d65446ac2029/pydantic-2.12.3-py3-none-any.whl", hash = "sha256:6986454a854bc3bc6e5443e1369e06a3a456af9d339eda45510f517d9ea5c6bf", size = 462431 }, ] [package.optional-dependencies] @@ -3922,104 +4010,108 @@ email = [ [[package]] name = "pydantic-core" -version = "2.41.1" +version = "2.41.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/7d/14/12b4a0d2b0b10d8e1d9a24ad94e7bbb43335eaf29c0c4e57860e8a30734a/pydantic_core-2.41.1.tar.gz", hash = "sha256:1ad375859a6d8c356b7704ec0f547a58e82ee80bb41baa811ad710e124bc8f2f", size = 454870 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b3/2c/a5c4640dc7132540109f67fe83b566fbc7512ccf2a068cfa22a243df70c7/pydantic_core-2.41.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:e63036298322e9aea1c8b7c0a6c1204d615dbf6ec0668ce5b83ff27f07404a61", size = 2113814 }, - { url = "https://files.pythonhosted.org/packages/e3/e7/a8694c3454a57842095d69c7a4ab3cf81c3c7b590f052738eabfdfc2e234/pydantic_core-2.41.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:241299ca91fc77ef64f11ed909d2d9220a01834e8e6f8de61275c4dd16b7c936", size = 1916660 }, - { url = "https://files.pythonhosted.org/packages/9c/58/29f12e65b19c1877a0269eb4f23c5d2267eded6120a7d6762501ab843dc9/pydantic_core-2.41.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1ab7e594a2a5c24ab8013a7dc8cfe5f2260e80e490685814122081705c2cf2b0", size = 1975071 }, - { url = "https://files.pythonhosted.org/packages/98/26/4e677f2b7ec3fbdd10be6b586a82a814c8ebe3e474024c8df2d4260e564e/pydantic_core-2.41.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b054ef1a78519cb934b58e9c90c09e93b837c935dcd907b891f2b265b129eb6e", size = 2067271 }, - { url = "https://files.pythonhosted.org/packages/29/50/50614bd906089904d7ca1be3b9ecf08c00a327143d48f1decfdc21b3c302/pydantic_core-2.41.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f2ab7d10d0ab2ed6da54c757233eb0f48ebfb4f86e9b88ccecb3f92bbd61a538", size = 2253207 }, - { url = "https://files.pythonhosted.org/packages/ea/58/b1e640b4ca559273cca7c28e0fe8891d5d8e9a600f5ab4882670ec107549/pydantic_core-2.41.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2757606b7948bb853a27e4040820306eaa0ccb9e8f9f8a0fa40cb674e170f350", size = 2375052 }, - { url = "https://files.pythonhosted.org/packages/53/25/cd47df3bfb24350e03835f0950288d1054f1cc9a8023401dabe6d4ff2834/pydantic_core-2.41.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cec0e75eb61f606bad0a32f2be87507087514e26e8c73db6cbdb8371ccd27917", size = 2076834 }, - { url = "https://files.pythonhosted.org/packages/ec/b4/71b2c77e5df527fbbc1a03e72c3fd96c44cd10d4241a81befef8c12b9fc4/pydantic_core-2.41.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0234236514f44a5bf552105cfe2543a12f48203397d9d0f866affa569345a5b5", size = 2195374 }, - { url = "https://files.pythonhosted.org/packages/aa/08/4b8a50733005865efde284fec45da75fe16a258f706e16323c5ace4004eb/pydantic_core-2.41.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:1b974e41adfbb4ebb0f65fc4ca951347b17463d60893ba7d5f7b9bb087c83897", size = 2156060 }, - { url = "https://files.pythonhosted.org/packages/83/c3/1037cb603ef2130c210150a51b1710d86825b5c28df54a55750099f91196/pydantic_core-2.41.1-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:248dafb3204136113c383e91a4d815269f51562b6659b756cf3df14eefc7d0bb", size = 2331640 }, - { url = "https://files.pythonhosted.org/packages/56/4c/52d111869610e6b1a46e1f1035abcdc94d0655587e39104433a290e9f377/pydantic_core-2.41.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:678f9d76a91d6bcedd7568bbf6beb77ae8447f85d1aeebaab7e2f0829cfc3a13", size = 2329844 }, - { url = "https://files.pythonhosted.org/packages/32/5d/4b435f0b52ab543967761aca66b84ad3f0026e491e57de47693d15d0a8db/pydantic_core-2.41.1-cp310-cp310-win32.whl", hash = "sha256:dff5bee1d21ee58277900692a641925d2dddfde65182c972569b1a276d2ac8fb", size = 1991289 }, - { url = "https://files.pythonhosted.org/packages/88/52/31b4deafc1d3cb96d0e7c0af70f0dc05454982d135d07f5117e6336153e8/pydantic_core-2.41.1-cp310-cp310-win_amd64.whl", hash = "sha256:5042da12e5d97d215f91567110fdfa2e2595a25f17c19b9ff024f31c34f9b53e", size = 2027747 }, - { url = "https://files.pythonhosted.org/packages/f6/a9/ec440f02e57beabdfd804725ef1e38ac1ba00c49854d298447562e119513/pydantic_core-2.41.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:4f276a6134fe1fc1daa692642a3eaa2b7b858599c49a7610816388f5e37566a1", size = 2111456 }, - { url = "https://files.pythonhosted.org/packages/f0/f9/6bc15bacfd8dcfc073a1820a564516d9c12a435a9a332d4cbbfd48828ddd/pydantic_core-2.41.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:07588570a805296ece009c59d9a679dc08fab72fb337365afb4f3a14cfbfc176", size = 1915012 }, - { url = "https://files.pythonhosted.org/packages/38/8a/d9edcdcdfe80bade17bed424284427c08bea892aaec11438fa52eaeaf79c/pydantic_core-2.41.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28527e4b53400cd60ffbd9812ccb2b5135d042129716d71afd7e45bf42b855c0", size = 1973762 }, - { url = "https://files.pythonhosted.org/packages/d5/b3/ff225c6d49fba4279de04677c1c876fc3dc6562fd0c53e9bfd66f58c51a8/pydantic_core-2.41.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:46a1c935c9228bad738c8a41de06478770927baedf581d172494ab36a6b96575", size = 2065386 }, - { url = "https://files.pythonhosted.org/packages/47/ba/183e8c0be4321314af3fd1ae6bfc7eafdd7a49bdea5da81c56044a207316/pydantic_core-2.41.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:447ddf56e2b7d28d200d3e9eafa936fe40485744b5a824b67039937580b3cb20", size = 2252317 }, - { url = "https://files.pythonhosted.org/packages/57/c5/aab61e94fd02f45c65f1f8c9ec38bb3b33fbf001a1837c74870e97462572/pydantic_core-2.41.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:63892ead40c1160ac860b5debcc95c95c5a0035e543a8b5a4eac70dd22e995f4", size = 2373405 }, - { url = "https://files.pythonhosted.org/packages/e5/4f/3aaa3bd1ea420a15acc42d7d3ccb3b0bbc5444ae2f9dbc1959f8173e16b8/pydantic_core-2.41.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f4a9543ca355e6df8fbe9c83e9faab707701e9103ae857ecb40f1c0cf8b0e94d", size = 2073794 }, - { url = "https://files.pythonhosted.org/packages/58/bd/e3975cdebe03ec080ef881648de316c73f2a6be95c14fc4efb2f7bdd0d41/pydantic_core-2.41.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f2611bdb694116c31e551ed82e20e39a90bea9b7ad9e54aaf2d045ad621aa7a1", size = 2194430 }, - { url = "https://files.pythonhosted.org/packages/2b/b8/6b7e7217f147d3b3105b57fb1caec3c4f667581affdfaab6d1d277e1f749/pydantic_core-2.41.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:fecc130893a9b5f7bfe230be1bb8c61fe66a19db8ab704f808cb25a82aad0bc9", size = 2154611 }, - { url = "https://files.pythonhosted.org/packages/fe/7b/239c2fe76bd8b7eef9ae2140d737368a3c6fea4fd27f8f6b4cde6baa3ce9/pydantic_core-2.41.1-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:1e2df5f8344c99b6ea5219f00fdc8950b8e6f2c422fbc1cc122ec8641fac85a1", size = 2329809 }, - { url = "https://files.pythonhosted.org/packages/bd/2e/77a821a67ff0786f2f14856d6bd1348992f695ee90136a145d7a445c1ff6/pydantic_core-2.41.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:35291331e9d8ed94c257bab6be1cb3a380b5eee570a2784bffc055e18040a2ea", size = 2327907 }, - { url = "https://files.pythonhosted.org/packages/fd/9a/b54512bb9df7f64c586b369328c30481229b70ca6a5fcbb90b715e15facf/pydantic_core-2.41.1-cp311-cp311-win32.whl", hash = "sha256:2876a095292668d753f1a868c4a57c4ac9f6acbd8edda8debe4218d5848cf42f", size = 1989964 }, - { url = "https://files.pythonhosted.org/packages/9d/72/63c9a4f1a5c950e65dd522d7dd67f167681f9d4f6ece3b80085a0329f08f/pydantic_core-2.41.1-cp311-cp311-win_amd64.whl", hash = "sha256:b92d6c628e9a338846a28dfe3fcdc1a3279388624597898b105e078cdfc59298", size = 2025158 }, - { url = "https://files.pythonhosted.org/packages/d8/16/4e2706184209f61b50c231529257c12eb6bd9eb36e99ea1272e4815d2200/pydantic_core-2.41.1-cp311-cp311-win_arm64.whl", hash = "sha256:7d82ae99409eb69d507a89835488fb657faa03ff9968a9379567b0d2e2e56bc5", size = 1972297 }, - { url = "https://files.pythonhosted.org/packages/ee/bc/5f520319ee1c9e25010412fac4154a72e0a40d0a19eb00281b1f200c0947/pydantic_core-2.41.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:db2f82c0ccbce8f021ad304ce35cbe02aa2f95f215cac388eed542b03b4d5eb4", size = 2099300 }, - { url = "https://files.pythonhosted.org/packages/31/14/010cd64c5c3814fb6064786837ec12604be0dd46df3327cf8474e38abbbd/pydantic_core-2.41.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:47694a31c710ced9205d5f1e7e8af3ca57cbb8a503d98cb9e33e27c97a501601", size = 1910179 }, - { url = "https://files.pythonhosted.org/packages/8e/2e/23fc2a8a93efad52df302fdade0a60f471ecc0c7aac889801ac24b4c07d6/pydantic_core-2.41.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:93e9decce94daf47baf9e9d392f5f2557e783085f7c5e522011545d9d6858e00", size = 1957225 }, - { url = "https://files.pythonhosted.org/packages/b9/b6/6db08b2725b2432b9390844852e11d320281e5cea8a859c52c68001975fa/pydantic_core-2.41.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ab0adafdf2b89c8b84f847780a119437a0931eca469f7b44d356f2b426dd9741", size = 2053315 }, - { url = "https://files.pythonhosted.org/packages/61/d9/4de44600f2d4514b44f3f3aeeda2e14931214b6b5bf52479339e801ce748/pydantic_core-2.41.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5da98cc81873f39fd56882e1569c4677940fbc12bce6213fad1ead784192d7c8", size = 2224298 }, - { url = "https://files.pythonhosted.org/packages/7a/ae/dbe51187a7f35fc21b283c5250571a94e36373eb557c1cba9f29a9806dcf/pydantic_core-2.41.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:209910e88afb01fd0fd403947b809ba8dba0e08a095e1f703294fda0a8fdca51", size = 2351797 }, - { url = "https://files.pythonhosted.org/packages/b5/a7/975585147457c2e9fb951c7c8dab56deeb6aa313f3aa72c2fc0df3f74a49/pydantic_core-2.41.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:365109d1165d78d98e33c5bfd815a9b5d7d070f578caefaabcc5771825b4ecb5", size = 2074921 }, - { url = "https://files.pythonhosted.org/packages/62/37/ea94d1d0c01dec1b7d236c7cec9103baab0021f42500975de3d42522104b/pydantic_core-2.41.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:706abf21e60a2857acdb09502bc853ee5bce732955e7b723b10311114f033115", size = 2187767 }, - { url = "https://files.pythonhosted.org/packages/d3/fe/694cf9fdd3a777a618c3afd210dba7b414cb8a72b1bd29b199c2e5765fee/pydantic_core-2.41.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:bf0bd5417acf7f6a7ec3b53f2109f587be176cb35f9cf016da87e6017437a72d", size = 2136062 }, - { url = "https://files.pythonhosted.org/packages/0f/ae/174aeabd89916fbd2988cc37b81a59e1186e952afd2a7ed92018c22f31ca/pydantic_core-2.41.1-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:2e71b1c6ceb9c78424ae9f63a07292fb769fb890a4e7efca5554c47f33a60ea5", size = 2317819 }, - { url = "https://files.pythonhosted.org/packages/65/e8/e9aecafaebf53fc456314f72886068725d6fba66f11b013532dc21259343/pydantic_core-2.41.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:80745b9770b4a38c25015b517451c817799bfb9d6499b0d13d8227ec941cb513", size = 2312267 }, - { url = "https://files.pythonhosted.org/packages/35/2f/1c2e71d2a052f9bb2f2df5a6a05464a0eb800f9e8d9dd800202fe31219e1/pydantic_core-2.41.1-cp312-cp312-win32.whl", hash = "sha256:83b64d70520e7890453f1aa21d66fda44e7b35f1cfea95adf7b4289a51e2b479", size = 1990927 }, - { url = "https://files.pythonhosted.org/packages/b1/78/562998301ff2588b9c6dcc5cb21f52fa919d6e1decc75a35055feb973594/pydantic_core-2.41.1-cp312-cp312-win_amd64.whl", hash = "sha256:377defd66ee2003748ee93c52bcef2d14fde48fe28a0b156f88c3dbf9bc49a50", size = 2034703 }, - { url = "https://files.pythonhosted.org/packages/b2/53/d95699ce5a5cdb44bb470bd818b848b9beadf51459fd4ea06667e8ede862/pydantic_core-2.41.1-cp312-cp312-win_arm64.whl", hash = "sha256:c95caff279d49c1d6cdfe2996e6c2ad712571d3b9caaa209a404426c326c4bde", size = 1972719 }, - { url = "https://files.pythonhosted.org/packages/27/8a/6d54198536a90a37807d31a156642aae7a8e1263ed9fe6fc6245defe9332/pydantic_core-2.41.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:70e790fce5f05204ef4403159857bfcd587779da78627b0babb3654f75361ebf", size = 2105825 }, - { url = "https://files.pythonhosted.org/packages/4f/2e/4784fd7b22ac9c8439db25bf98ffed6853d01e7e560a346e8af821776ccc/pydantic_core-2.41.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9cebf1ca35f10930612d60bd0f78adfacee824c30a880e3534ba02c207cceceb", size = 1910126 }, - { url = "https://files.pythonhosted.org/packages/f3/92/31eb0748059ba5bd0aa708fb4bab9fcb211461ddcf9e90702a6542f22d0d/pydantic_core-2.41.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:170406a37a5bc82c22c3274616bf6f17cc7df9c4a0a0a50449e559cb755db669", size = 1961472 }, - { url = "https://files.pythonhosted.org/packages/ab/91/946527792275b5c4c7dde4cfa3e81241bf6900e9fee74fb1ba43e0c0f1ab/pydantic_core-2.41.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:12d4257fc9187a0ccd41b8b327d6a4e57281ab75e11dda66a9148ef2e1fb712f", size = 2063230 }, - { url = "https://files.pythonhosted.org/packages/31/5d/a35c5d7b414e5c0749f1d9f0d159ee2ef4bab313f499692896b918014ee3/pydantic_core-2.41.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a75a33b4db105dd1c8d57839e17ee12db8d5ad18209e792fa325dbb4baeb00f4", size = 2229469 }, - { url = "https://files.pythonhosted.org/packages/21/4d/8713737c689afa57ecfefe38db78259d4484c97aa494979e6a9d19662584/pydantic_core-2.41.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:08a589f850803a74e0fcb16a72081cafb0d72a3cdda500106942b07e76b7bf62", size = 2347986 }, - { url = "https://files.pythonhosted.org/packages/f6/ec/929f9a3a5ed5cda767081494bacd32f783e707a690ce6eeb5e0730ec4986/pydantic_core-2.41.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a97939d6ea44763c456bd8a617ceada2c9b96bb5b8ab3dfa0d0827df7619014", size = 2072216 }, - { url = "https://files.pythonhosted.org/packages/26/55/a33f459d4f9cc8786d9db42795dbecc84fa724b290d7d71ddc3d7155d46a/pydantic_core-2.41.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d2ae423c65c556f09569524b80ffd11babff61f33055ef9773d7c9fabc11ed8d", size = 2193047 }, - { url = "https://files.pythonhosted.org/packages/77/af/d5c6959f8b089f2185760a2779079e3c2c411bfc70ea6111f58367851629/pydantic_core-2.41.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:4dc703015fbf8764d6a8001c327a87f1823b7328d40b47ce6000c65918ad2b4f", size = 2140613 }, - { url = "https://files.pythonhosted.org/packages/58/e5/2c19bd2a14bffe7fabcf00efbfbd3ac430aaec5271b504a938ff019ac7be/pydantic_core-2.41.1-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:968e4ffdfd35698a5fe659e5e44c508b53664870a8e61c8f9d24d3d145d30257", size = 2327641 }, - { url = "https://files.pythonhosted.org/packages/93/ef/e0870ccda798c54e6b100aff3c4d49df5458fd64217e860cb9c3b0a403f4/pydantic_core-2.41.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:fff2b76c8e172d34771cd4d4f0ade08072385310f214f823b5a6ad4006890d32", size = 2318229 }, - { url = "https://files.pythonhosted.org/packages/b1/4b/c3b991d95f5deb24d0bd52e47bcf716098fa1afe0ce2d4bd3125b38566ba/pydantic_core-2.41.1-cp313-cp313-win32.whl", hash = "sha256:a38a5263185407ceb599f2f035faf4589d57e73c7146d64f10577f6449e8171d", size = 1997911 }, - { url = "https://files.pythonhosted.org/packages/a7/ce/5c316fd62e01f8d6be1b7ee6b54273214e871772997dc2c95e204997a055/pydantic_core-2.41.1-cp313-cp313-win_amd64.whl", hash = "sha256:b42ae7fd6760782c975897e1fdc810f483b021b32245b0105d40f6e7a3803e4b", size = 2034301 }, - { url = "https://files.pythonhosted.org/packages/29/41/902640cfd6a6523194123e2c3373c60f19006447f2fb06f76de4e8466c5b/pydantic_core-2.41.1-cp313-cp313-win_arm64.whl", hash = "sha256:ad4111acc63b7384e205c27a2f15e23ac0ee21a9d77ad6f2e9cb516ec90965fb", size = 1977238 }, - { url = "https://files.pythonhosted.org/packages/04/04/28b040e88c1b89d851278478842f0bdf39c7a05da9e850333c6c8cbe7dfa/pydantic_core-2.41.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:440d0df7415b50084a4ba9d870480c16c5f67c0d1d4d5119e3f70925533a0edc", size = 1875626 }, - { url = "https://files.pythonhosted.org/packages/d6/58/b41dd3087505220bb58bc81be8c3e8cbc037f5710cd3c838f44f90bdd704/pydantic_core-2.41.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:71eaa38d342099405dae6484216dcf1e8e4b0bebd9b44a4e08c9b43db6a2ab67", size = 2045708 }, - { url = "https://files.pythonhosted.org/packages/d7/b8/760f23754e40bf6c65b94a69b22c394c24058a0ef7e2aa471d2e39219c1a/pydantic_core-2.41.1-cp313-cp313t-win_amd64.whl", hash = "sha256:555ecf7e50f1161d3f693bc49f23c82cf6cdeafc71fa37a06120772a09a38795", size = 1997171 }, - { url = "https://files.pythonhosted.org/packages/41/12/cec246429ddfa2778d2d6301eca5362194dc8749ecb19e621f2f65b5090f/pydantic_core-2.41.1-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:05226894a26f6f27e1deb735d7308f74ef5fa3a6de3e0135bb66cdcaee88f64b", size = 2107836 }, - { url = "https://files.pythonhosted.org/packages/20/39/baba47f8d8b87081302498e610aefc37142ce6a1cc98b2ab6b931a162562/pydantic_core-2.41.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:85ff7911c6c3e2fd8d3779c50925f6406d770ea58ea6dde9c230d35b52b16b4a", size = 1904449 }, - { url = "https://files.pythonhosted.org/packages/50/32/9a3d87cae2c75a5178334b10358d631bd094b916a00a5993382222dbfd92/pydantic_core-2.41.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:47f1f642a205687d59b52dc1a9a607f45e588f5a2e9eeae05edd80c7a8c47674", size = 1961750 }, - { url = "https://files.pythonhosted.org/packages/27/42/a96c9d793a04cf2a9773bff98003bb154087b94f5530a2ce6063ecfec583/pydantic_core-2.41.1-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:df11c24e138876ace5ec6043e5cae925e34cf38af1a1b3d63589e8f7b5f5cdc4", size = 2063305 }, - { url = "https://files.pythonhosted.org/packages/3e/8d/028c4b7d157a005b1f52c086e2d4b0067886b213c86220c1153398dbdf8f/pydantic_core-2.41.1-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7f0bf7f5c8f7bf345c527e8a0d72d6b26eda99c1227b0c34e7e59e181260de31", size = 2228959 }, - { url = "https://files.pythonhosted.org/packages/08/f7/ee64cda8fcc9ca3f4716e6357144f9ee71166775df582a1b6b738bf6da57/pydantic_core-2.41.1-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:82b887a711d341c2c47352375d73b029418f55b20bd7815446d175a70effa706", size = 2345421 }, - { url = "https://files.pythonhosted.org/packages/13/c0/e8ec05f0f5ee7a3656973ad9cd3bc73204af99f6512c1a4562f6fb4b3f7d/pydantic_core-2.41.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b5f1d5d6bbba484bdf220c72d8ecd0be460f4bd4c5e534a541bb2cd57589fb8b", size = 2065288 }, - { url = "https://files.pythonhosted.org/packages/0a/25/d77a73ff24e2e4fcea64472f5e39b0402d836da9b08b5361a734d0153023/pydantic_core-2.41.1-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2bf1917385ebe0f968dc5c6ab1375886d56992b93ddfe6bf52bff575d03662be", size = 2189759 }, - { url = "https://files.pythonhosted.org/packages/66/45/4a4ebaaae12a740552278d06fe71418c0f2869537a369a89c0e6723b341d/pydantic_core-2.41.1-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:4f94f3ab188f44b9a73f7295663f3ecb8f2e2dd03a69c8f2ead50d37785ecb04", size = 2140747 }, - { url = "https://files.pythonhosted.org/packages/da/6d/b727ce1022f143194a36593243ff244ed5a1eb3c9122296bf7e716aa37ba/pydantic_core-2.41.1-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:3925446673641d37c30bd84a9d597e49f72eacee8b43322c8999fa17d5ae5bc4", size = 2327416 }, - { url = "https://files.pythonhosted.org/packages/6f/8c/02df9d8506c427787059f87c6c7253435c6895e12472a652d9616ee0fc95/pydantic_core-2.41.1-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:49bd51cc27adb980c7b97357ae036ce9b3c4d0bb406e84fbe16fb2d368b602a8", size = 2318138 }, - { url = "https://files.pythonhosted.org/packages/98/67/0cf429a7d6802536941f430e6e3243f6d4b68f41eeea4b242372f1901794/pydantic_core-2.41.1-cp314-cp314-win32.whl", hash = "sha256:a31ca0cd0e4d12ea0df0077df2d487fc3eb9d7f96bbb13c3c5b88dcc21d05159", size = 1998429 }, - { url = "https://files.pythonhosted.org/packages/38/60/742fef93de5d085022d2302a6317a2b34dbfe15258e9396a535c8a100ae7/pydantic_core-2.41.1-cp314-cp314-win_amd64.whl", hash = "sha256:1b5c4374a152e10a22175d7790e644fbd8ff58418890e07e2073ff9d4414efae", size = 2028870 }, - { url = "https://files.pythonhosted.org/packages/31/38/cdd8ccb8555ef7720bd7715899bd6cfbe3c29198332710e1b61b8f5dd8b8/pydantic_core-2.41.1-cp314-cp314-win_arm64.whl", hash = "sha256:4fee76d757639b493eb600fba668f1e17475af34c17dd61db7a47e824d464ca9", size = 1974275 }, - { url = "https://files.pythonhosted.org/packages/e7/7e/8ac10ccb047dc0221aa2530ec3c7c05ab4656d4d4bd984ee85da7f3d5525/pydantic_core-2.41.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f9b9c968cfe5cd576fdd7361f47f27adeb120517e637d1b189eea1c3ece573f4", size = 1875124 }, - { url = "https://files.pythonhosted.org/packages/c3/e4/7d9791efeb9c7d97e7268f8d20e0da24d03438a7fa7163ab58f1073ba968/pydantic_core-2.41.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1ebc7ab67b856384aba09ed74e3e977dded40e693de18a4f197c67d0d4e6d8e", size = 2043075 }, - { url = "https://files.pythonhosted.org/packages/2d/c3/3f6e6b2342ac11ac8cd5cb56e24c7b14afa27c010e82a765ffa5f771884a/pydantic_core-2.41.1-cp314-cp314t-win_amd64.whl", hash = "sha256:8ae0dc57b62a762985bc7fbf636be3412394acc0ddb4ade07fe104230f1b9762", size = 1995341 }, - { url = "https://files.pythonhosted.org/packages/d4/31/f403d7ca8352e3e4df352ccacd200f5f7f7fe81cef8e458515f015091625/pydantic_core-2.41.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:fabcbdb12de6eada8d6e9a759097adb3c15440fafc675b3e94ae5c9cb8d678a0", size = 2114268 }, - { url = "https://files.pythonhosted.org/packages/6e/b5/334473b6d2810df84db67f03d4f666acacfc538512c2d2a254074fee0889/pydantic_core-2.41.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:80e97ccfaf0aaf67d55de5085b0ed0d994f57747d9d03f2de5cc9847ca737b08", size = 1935786 }, - { url = "https://files.pythonhosted.org/packages/ea/5e/45513e4dc621f47397cfa5fef12ba8fa5e8b1c4c07f2ff2a5fef8ff81b25/pydantic_core-2.41.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:34df1fe8fea5d332484a763702e8b6a54048a9d4fe6ccf41e34a128238e01f52", size = 1971995 }, - { url = "https://files.pythonhosted.org/packages/22/e3/f1797c168e5f52b973bed1c585e99827a22d5e579d1ed57d51bc15b14633/pydantic_core-2.41.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:421b5595f845842fc093f7250e24ee395f54ca62d494fdde96f43ecf9228ae01", size = 2191264 }, - { url = "https://files.pythonhosted.org/packages/bb/e1/24ef4c3b4ab91c21c3a09a966c7d2cffe101058a7bfe5cc8b2c7c7d574e2/pydantic_core-2.41.1-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:dce8b22663c134583aaad24827863306a933f576c79da450be3984924e2031d1", size = 2152430 }, - { url = "https://files.pythonhosted.org/packages/35/74/70c1e225d67f7ef3fdba02c506d9011efaf734020914920b2aa3d1a45e61/pydantic_core-2.41.1-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:300a9c162fea9906cc5c103893ca2602afd84f0ec90d3be36f4cc360125d22e1", size = 2324691 }, - { url = "https://files.pythonhosted.org/packages/c8/bf/dd4d21037c8bef0d8cce90a86a3f2dcb011c30086db2a10113c3eea23eba/pydantic_core-2.41.1-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:e019167628f6e6161ae7ab9fb70f6d076a0bf0d55aa9b20833f86a320c70dd65", size = 2324493 }, - { url = "https://files.pythonhosted.org/packages/7e/78/3093b334e9c9796c8236a4701cd2ddef1c56fb0928fe282a10c797644380/pydantic_core-2.41.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:13ab9cc2de6f9d4ab645a050ae5aee61a2424ac4d3a16ba23d4c2027705e0301", size = 2146156 }, - { url = "https://files.pythonhosted.org/packages/e6/6c/fa3e45c2b054a1e627a89a364917f12cbe3abc3e91b9004edaae16e7b3c5/pydantic_core-2.41.1-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:af2385d3f98243fb733862f806c5bb9122e5fba05b373e3af40e3c82d711cef1", size = 2112094 }, - { url = "https://files.pythonhosted.org/packages/e5/17/7eebc38b4658cc8e6902d0befc26388e4c2a5f2e179c561eeb43e1922c7b/pydantic_core-2.41.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:6550617a0c2115be56f90c31a5370261d8ce9dbf051c3ed53b51172dd34da696", size = 1935300 }, - { url = "https://files.pythonhosted.org/packages/2b/00/9fe640194a1717a464ab861d43595c268830f98cb1e2705aa134b3544b70/pydantic_core-2.41.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc17b6ecf4983d298686014c92ebc955a9f9baf9f57dad4065e7906e7bee6222", size = 1970417 }, - { url = "https://files.pythonhosted.org/packages/b2/ad/f4cdfaf483b78ee65362363e73b6b40c48e067078d7b146e8816d5945ad6/pydantic_core-2.41.1-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:42ae9352cf211f08b04ea110563d6b1e415878eea5b4c70f6bdb17dca3b932d2", size = 2190745 }, - { url = "https://files.pythonhosted.org/packages/cb/c1/18f416d40a10f44e9387497ba449f40fdb1478c61ba05c4b6bdb82300362/pydantic_core-2.41.1-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:e82947de92068b0a21681a13dd2102387197092fbe7defcfb8453e0913866506", size = 2150888 }, - { url = "https://files.pythonhosted.org/packages/42/30/134c8a921630d8a88d6f905a562495a6421e959a23c19b0f49b660801d67/pydantic_core-2.41.1-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:e244c37d5471c9acdcd282890c6c4c83747b77238bfa19429b8473586c907656", size = 2324489 }, - { url = "https://files.pythonhosted.org/packages/9c/48/a9263aeaebdec81e941198525b43edb3b44f27cfa4cb8005b8d3eb8dec72/pydantic_core-2.41.1-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:1e798b4b304a995110d41ec93653e57975620ccb2842ba9420037985e7d7284e", size = 2322763 }, - { url = "https://files.pythonhosted.org/packages/1d/62/755d2bd2593f701c5839fc084e9c2c5e2418f460383ad04e3b5d0befc3ca/pydantic_core-2.41.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:f1fc716c0eb1663c59699b024428ad5ec2bcc6b928527b8fe28de6cb89f47efb", size = 2144046 }, +sdist = { url = "https://files.pythonhosted.org/packages/df/18/d0944e8eaaa3efd0a91b0f1fc537d3be55ad35091b6a87638211ba691964/pydantic_core-2.41.4.tar.gz", hash = "sha256:70e47929a9d4a1905a67e4b687d5946026390568a8e952b92824118063cee4d5", size = 457557 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a7/3d/9b8ca77b0f76fcdbf8bc6b72474e264283f461284ca84ac3fde570c6c49a/pydantic_core-2.41.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2442d9a4d38f3411f22eb9dd0912b7cbf4b7d5b6c92c4173b75d3e1ccd84e36e", size = 2111197 }, + { url = "https://files.pythonhosted.org/packages/59/92/b7b0fe6ed4781642232755cb7e56a86e2041e1292f16d9ae410a0ccee5ac/pydantic_core-2.41.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:30a9876226dda131a741afeab2702e2d127209bde3c65a2b8133f428bc5d006b", size = 1917909 }, + { url = "https://files.pythonhosted.org/packages/52/8c/3eb872009274ffa4fb6a9585114e161aa1a0915af2896e2d441642929fe4/pydantic_core-2.41.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d55bbac04711e2980645af68b97d445cdbcce70e5216de444a6c4b6943ebcccd", size = 1969905 }, + { url = "https://files.pythonhosted.org/packages/f4/21/35adf4a753bcfaea22d925214a0c5b880792e3244731b3f3e6fec0d124f7/pydantic_core-2.41.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e1d778fb7849a42d0ee5927ab0f7453bf9f85eef8887a546ec87db5ddb178945", size = 2051938 }, + { url = "https://files.pythonhosted.org/packages/7d/d0/cdf7d126825e36d6e3f1eccf257da8954452934ede275a8f390eac775e89/pydantic_core-2.41.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1b65077a4693a98b90ec5ad8f203ad65802a1b9b6d4a7e48066925a7e1606706", size = 2250710 }, + { url = "https://files.pythonhosted.org/packages/2e/1c/af1e6fd5ea596327308f9c8d1654e1285cc3d8de0d584a3c9d7705bf8a7c/pydantic_core-2.41.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:62637c769dee16eddb7686bf421be48dfc2fae93832c25e25bc7242e698361ba", size = 2367445 }, + { url = "https://files.pythonhosted.org/packages/d3/81/8cece29a6ef1b3a92f956ea6da6250d5b2d2e7e4d513dd3b4f0c7a83dfea/pydantic_core-2.41.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2dfe3aa529c8f501babf6e502936b9e8d4698502b2cfab41e17a028d91b1ac7b", size = 2072875 }, + { url = "https://files.pythonhosted.org/packages/e3/37/a6a579f5fc2cd4d5521284a0ab6a426cc6463a7b3897aeb95b12f1ba607b/pydantic_core-2.41.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ca2322da745bf2eeb581fc9ea3bbb31147702163ccbcbf12a3bb630e4bf05e1d", size = 2191329 }, + { url = "https://files.pythonhosted.org/packages/ae/03/505020dc5c54ec75ecba9f41119fd1e48f9e41e4629942494c4a8734ded1/pydantic_core-2.41.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e8cd3577c796be7231dcf80badcf2e0835a46665eaafd8ace124d886bab4d700", size = 2151658 }, + { url = "https://files.pythonhosted.org/packages/cb/5d/2c0d09fb53aa03bbd2a214d89ebfa6304be7df9ed86ee3dc7770257f41ee/pydantic_core-2.41.4-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:1cae8851e174c83633f0833e90636832857297900133705ee158cf79d40f03e6", size = 2316777 }, + { url = "https://files.pythonhosted.org/packages/ea/4b/c2c9c8f5e1f9c864b57d08539d9d3db160e00491c9f5ee90e1bfd905e644/pydantic_core-2.41.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a26d950449aae348afe1ac8be5525a00ae4235309b729ad4d3399623125b43c9", size = 2320705 }, + { url = "https://files.pythonhosted.org/packages/28/c3/a74c1c37f49c0a02c89c7340fafc0ba816b29bd495d1a31ce1bdeacc6085/pydantic_core-2.41.4-cp310-cp310-win32.whl", hash = "sha256:0cf2a1f599efe57fa0051312774280ee0f650e11152325e41dfd3018ef2c1b57", size = 1975464 }, + { url = "https://files.pythonhosted.org/packages/d6/23/5dd5c1324ba80303368f7569e2e2e1a721c7d9eb16acb7eb7b7f85cb1be2/pydantic_core-2.41.4-cp310-cp310-win_amd64.whl", hash = "sha256:a8c2e340d7e454dc3340d3d2e8f23558ebe78c98aa8f68851b04dcb7bc37abdc", size = 2024497 }, + { url = "https://files.pythonhosted.org/packages/62/4c/f6cbfa1e8efacd00b846764e8484fe173d25b8dab881e277a619177f3384/pydantic_core-2.41.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:28ff11666443a1a8cf2a044d6a545ebffa8382b5f7973f22c36109205e65dc80", size = 2109062 }, + { url = "https://files.pythonhosted.org/packages/21/f8/40b72d3868896bfcd410e1bd7e516e762d326201c48e5b4a06446f6cf9e8/pydantic_core-2.41.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:61760c3925d4633290292bad462e0f737b840508b4f722247d8729684f6539ae", size = 1916301 }, + { url = "https://files.pythonhosted.org/packages/94/4d/d203dce8bee7faeca791671c88519969d98d3b4e8f225da5b96dad226fc8/pydantic_core-2.41.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eae547b7315d055b0de2ec3965643b0ab82ad0106a7ffd29615ee9f266a02827", size = 1968728 }, + { url = "https://files.pythonhosted.org/packages/65/f5/6a66187775df87c24d526985b3a5d78d861580ca466fbd9d4d0e792fcf6c/pydantic_core-2.41.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ef9ee5471edd58d1fcce1c80ffc8783a650e3e3a193fe90d52e43bb4d87bff1f", size = 2050238 }, + { url = "https://files.pythonhosted.org/packages/5e/b9/78336345de97298cf53236b2f271912ce11f32c1e59de25a374ce12f9cce/pydantic_core-2.41.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:15dd504af121caaf2c95cb90c0ebf71603c53de98305621b94da0f967e572def", size = 2249424 }, + { url = "https://files.pythonhosted.org/packages/99/bb/a4584888b70ee594c3d374a71af5075a68654d6c780369df269118af7402/pydantic_core-2.41.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3a926768ea49a8af4d36abd6a8968b8790f7f76dd7cbd5a4c180db2b4ac9a3a2", size = 2366047 }, + { url = "https://files.pythonhosted.org/packages/5f/8d/17fc5de9d6418e4d2ae8c675f905cdafdc59d3bf3bf9c946b7ab796a992a/pydantic_core-2.41.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6916b9b7d134bff5440098a4deb80e4cb623e68974a87883299de9124126c2a8", size = 2071163 }, + { url = "https://files.pythonhosted.org/packages/54/e7/03d2c5c0b8ed37a4617430db68ec5e7dbba66358b629cd69e11b4d564367/pydantic_core-2.41.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5cf90535979089df02e6f17ffd076f07237efa55b7343d98760bde8743c4b265", size = 2190585 }, + { url = "https://files.pythonhosted.org/packages/be/fc/15d1c9fe5ad9266a5897d9b932b7f53d7e5cfc800573917a2c5d6eea56ec/pydantic_core-2.41.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:7533c76fa647fade2d7ec75ac5cc079ab3f34879626dae5689b27790a6cf5a5c", size = 2150109 }, + { url = "https://files.pythonhosted.org/packages/26/ef/e735dd008808226c83ba56972566138665b71477ad580fa5a21f0851df48/pydantic_core-2.41.4-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:37e516bca9264cbf29612539801ca3cd5d1be465f940417b002905e6ed79d38a", size = 2315078 }, + { url = "https://files.pythonhosted.org/packages/90/00/806efdcf35ff2ac0f938362350cd9827b8afb116cc814b6b75cf23738c7c/pydantic_core-2.41.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0c19cb355224037c83642429b8ce261ae108e1c5fbf5c028bac63c77b0f8646e", size = 2318737 }, + { url = "https://files.pythonhosted.org/packages/41/7e/6ac90673fe6cb36621a2283552897838c020db343fa86e513d3f563b196f/pydantic_core-2.41.4-cp311-cp311-win32.whl", hash = "sha256:09c2a60e55b357284b5f31f5ab275ba9f7f70b7525e18a132ec1f9160b4f1f03", size = 1974160 }, + { url = "https://files.pythonhosted.org/packages/e0/9d/7c5e24ee585c1f8b6356e1d11d40ab807ffde44d2db3b7dfd6d20b09720e/pydantic_core-2.41.4-cp311-cp311-win_amd64.whl", hash = "sha256:711156b6afb5cb1cb7c14a2cc2c4a8b4c717b69046f13c6b332d8a0a8f41ca3e", size = 2021883 }, + { url = "https://files.pythonhosted.org/packages/33/90/5c172357460fc28b2871eb4a0fb3843b136b429c6fa827e4b588877bf115/pydantic_core-2.41.4-cp311-cp311-win_arm64.whl", hash = "sha256:6cb9cf7e761f4f8a8589a45e49ed3c0d92d1d696a45a6feaee8c904b26efc2db", size = 1968026 }, + { url = "https://files.pythonhosted.org/packages/e9/81/d3b3e95929c4369d30b2a66a91db63c8ed0a98381ae55a45da2cd1cc1288/pydantic_core-2.41.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:ab06d77e053d660a6faaf04894446df7b0a7e7aba70c2797465a0a1af00fc887", size = 2099043 }, + { url = "https://files.pythonhosted.org/packages/58/da/46fdac49e6717e3a94fc9201403e08d9d61aa7a770fab6190b8740749047/pydantic_core-2.41.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c53ff33e603a9c1179a9364b0a24694f183717b2e0da2b5ad43c316c956901b2", size = 1910699 }, + { url = "https://files.pythonhosted.org/packages/1e/63/4d948f1b9dd8e991a5a98b77dd66c74641f5f2e5225fee37994b2e07d391/pydantic_core-2.41.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:304c54176af2c143bd181d82e77c15c41cbacea8872a2225dd37e6544dce9999", size = 1952121 }, + { url = "https://files.pythonhosted.org/packages/b2/a7/e5fc60a6f781fc634ecaa9ecc3c20171d238794cef69ae0af79ac11b89d7/pydantic_core-2.41.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:025ba34a4cf4fb32f917d5d188ab5e702223d3ba603be4d8aca2f82bede432a4", size = 2041590 }, + { url = "https://files.pythonhosted.org/packages/70/69/dce747b1d21d59e85af433428978a1893c6f8a7068fa2bb4a927fba7a5ff/pydantic_core-2.41.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b9f5f30c402ed58f90c70e12eff65547d3ab74685ffe8283c719e6bead8ef53f", size = 2219869 }, + { url = "https://files.pythonhosted.org/packages/83/6a/c070e30e295403bf29c4df1cb781317b6a9bac7cd07b8d3acc94d501a63c/pydantic_core-2.41.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dd96e5d15385d301733113bcaa324c8bcf111275b7675a9c6e88bfb19fc05e3b", size = 2345169 }, + { url = "https://files.pythonhosted.org/packages/f0/83/06d001f8043c336baea7fd202a9ac7ad71f87e1c55d8112c50b745c40324/pydantic_core-2.41.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98f348cbb44fae6e9653c1055db7e29de67ea6a9ca03a5fa2c2e11a47cff0e47", size = 2070165 }, + { url = "https://files.pythonhosted.org/packages/14/0a/e567c2883588dd12bcbc110232d892cf385356f7c8a9910311ac997ab715/pydantic_core-2.41.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ec22626a2d14620a83ca583c6f5a4080fa3155282718b6055c2ea48d3ef35970", size = 2189067 }, + { url = "https://files.pythonhosted.org/packages/f4/1d/3d9fca34273ba03c9b1c5289f7618bc4bd09c3ad2289b5420481aa051a99/pydantic_core-2.41.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3a95d4590b1f1a43bf33ca6d647b990a88f4a3824a8c4572c708f0b45a5290ed", size = 2132997 }, + { url = "https://files.pythonhosted.org/packages/52/70/d702ef7a6cd41a8afc61f3554922b3ed8d19dd54c3bd4bdbfe332e610827/pydantic_core-2.41.4-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:f9672ab4d398e1b602feadcffcdd3af44d5f5e6ddc15bc7d15d376d47e8e19f8", size = 2307187 }, + { url = "https://files.pythonhosted.org/packages/68/4c/c06be6e27545d08b802127914156f38d10ca287a9e8489342793de8aae3c/pydantic_core-2.41.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:84d8854db5f55fead3b579f04bda9a36461dab0730c5d570e1526483e7bb8431", size = 2305204 }, + { url = "https://files.pythonhosted.org/packages/b0/e5/35ae4919bcd9f18603419e23c5eaf32750224a89d41a8df1a3704b69f77e/pydantic_core-2.41.4-cp312-cp312-win32.whl", hash = "sha256:9be1c01adb2ecc4e464392c36d17f97e9110fbbc906bcbe1c943b5b87a74aabd", size = 1972536 }, + { url = "https://files.pythonhosted.org/packages/1e/c2/49c5bb6d2a49eb2ee3647a93e3dae7080c6409a8a7558b075027644e879c/pydantic_core-2.41.4-cp312-cp312-win_amd64.whl", hash = "sha256:d682cf1d22bab22a5be08539dca3d1593488a99998f9f412137bc323179067ff", size = 2031132 }, + { url = "https://files.pythonhosted.org/packages/06/23/936343dbcba6eec93f73e95eb346810fc732f71ba27967b287b66f7b7097/pydantic_core-2.41.4-cp312-cp312-win_arm64.whl", hash = "sha256:833eebfd75a26d17470b58768c1834dfc90141b7afc6eb0429c21fc5a21dcfb8", size = 1969483 }, + { url = "https://files.pythonhosted.org/packages/13/d0/c20adabd181a029a970738dfe23710b52a31f1258f591874fcdec7359845/pydantic_core-2.41.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:85e050ad9e5f6fe1004eec65c914332e52f429bc0ae12d6fa2092407a462c746", size = 2105688 }, + { url = "https://files.pythonhosted.org/packages/00/b6/0ce5c03cec5ae94cca220dfecddc453c077d71363b98a4bbdb3c0b22c783/pydantic_core-2.41.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e7393f1d64792763a48924ba31d1e44c2cfbc05e3b1c2c9abb4ceeadd912cced", size = 1910807 }, + { url = "https://files.pythonhosted.org/packages/68/3e/800d3d02c8beb0b5c069c870cbb83799d085debf43499c897bb4b4aaff0d/pydantic_core-2.41.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94dab0940b0d1fb28bcab847adf887c66a27a40291eedf0b473be58761c9799a", size = 1956669 }, + { url = "https://files.pythonhosted.org/packages/60/a4/24271cc71a17f64589be49ab8bd0751f6a0a03046c690df60989f2f95c2c/pydantic_core-2.41.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:de7c42f897e689ee6f9e93c4bec72b99ae3b32a2ade1c7e4798e690ff5246e02", size = 2051629 }, + { url = "https://files.pythonhosted.org/packages/68/de/45af3ca2f175d91b96bfb62e1f2d2f1f9f3b14a734afe0bfeff079f78181/pydantic_core-2.41.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:664b3199193262277b8b3cd1e754fb07f2c6023289c815a1e1e8fb415cb247b1", size = 2224049 }, + { url = "https://files.pythonhosted.org/packages/af/8f/ae4e1ff84672bf869d0a77af24fd78387850e9497753c432875066b5d622/pydantic_core-2.41.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d95b253b88f7d308b1c0b417c4624f44553ba4762816f94e6986819b9c273fb2", size = 2342409 }, + { url = "https://files.pythonhosted.org/packages/18/62/273dd70b0026a085c7b74b000394e1ef95719ea579c76ea2f0cc8893736d/pydantic_core-2.41.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a1351f5bbdbbabc689727cb91649a00cb9ee7203e0a6e54e9f5ba9e22e384b84", size = 2069635 }, + { url = "https://files.pythonhosted.org/packages/30/03/cf485fff699b4cdaea469bc481719d3e49f023241b4abb656f8d422189fc/pydantic_core-2.41.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1affa4798520b148d7182da0615d648e752de4ab1a9566b7471bc803d88a062d", size = 2194284 }, + { url = "https://files.pythonhosted.org/packages/f9/7e/c8e713db32405dfd97211f2fc0a15d6bf8adb7640f3d18544c1f39526619/pydantic_core-2.41.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7b74e18052fea4aa8dea2fb7dbc23d15439695da6cbe6cfc1b694af1115df09d", size = 2137566 }, + { url = "https://files.pythonhosted.org/packages/04/f7/db71fd4cdccc8b75990f79ccafbbd66757e19f6d5ee724a6252414483fb4/pydantic_core-2.41.4-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:285b643d75c0e30abda9dc1077395624f314a37e3c09ca402d4015ef5979f1a2", size = 2316809 }, + { url = "https://files.pythonhosted.org/packages/76/63/a54973ddb945f1bca56742b48b144d85c9fc22f819ddeb9f861c249d5464/pydantic_core-2.41.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:f52679ff4218d713b3b33f88c89ccbf3a5c2c12ba665fb80ccc4192b4608dbab", size = 2311119 }, + { url = "https://files.pythonhosted.org/packages/f8/03/5d12891e93c19218af74843a27e32b94922195ded2386f7b55382f904d2f/pydantic_core-2.41.4-cp313-cp313-win32.whl", hash = "sha256:ecde6dedd6fff127c273c76821bb754d793be1024bc33314a120f83a3c69460c", size = 1981398 }, + { url = "https://files.pythonhosted.org/packages/be/d8/fd0de71f39db91135b7a26996160de71c073d8635edfce8b3c3681be0d6d/pydantic_core-2.41.4-cp313-cp313-win_amd64.whl", hash = "sha256:d081a1f3800f05409ed868ebb2d74ac39dd0c1ff6c035b5162356d76030736d4", size = 2030735 }, + { url = "https://files.pythonhosted.org/packages/72/86/c99921c1cf6650023c08bfab6fe2d7057a5142628ef7ccfa9921f2dda1d5/pydantic_core-2.41.4-cp313-cp313-win_arm64.whl", hash = "sha256:f8e49c9c364a7edcbe2a310f12733aad95b022495ef2a8d653f645e5d20c1564", size = 1973209 }, + { url = "https://files.pythonhosted.org/packages/36/0d/b5706cacb70a8414396efdda3d72ae0542e050b591119e458e2490baf035/pydantic_core-2.41.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:ed97fd56a561f5eb5706cebe94f1ad7c13b84d98312a05546f2ad036bafe87f4", size = 1877324 }, + { url = "https://files.pythonhosted.org/packages/de/2d/cba1fa02cfdea72dfb3a9babb067c83b9dff0bbcb198368e000a6b756ea7/pydantic_core-2.41.4-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a870c307bf1ee91fc58a9a61338ff780d01bfae45922624816878dce784095d2", size = 1884515 }, + { url = "https://files.pythonhosted.org/packages/07/ea/3df927c4384ed9b503c9cc2d076cf983b4f2adb0c754578dfb1245c51e46/pydantic_core-2.41.4-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d25e97bc1f5f8f7985bdc2335ef9e73843bb561eb1fa6831fdfc295c1c2061cf", size = 2042819 }, + { url = "https://files.pythonhosted.org/packages/6a/ee/df8e871f07074250270a3b1b82aad4cd0026b588acd5d7d3eb2fcb1471a3/pydantic_core-2.41.4-cp313-cp313t-win_amd64.whl", hash = "sha256:d405d14bea042f166512add3091c1af40437c2e7f86988f3915fabd27b1e9cd2", size = 1995866 }, + { url = "https://files.pythonhosted.org/packages/fc/de/b20f4ab954d6d399499c33ec4fafc46d9551e11dc1858fb7f5dca0748ceb/pydantic_core-2.41.4-cp313-cp313t-win_arm64.whl", hash = "sha256:19f3684868309db5263a11bace3c45d93f6f24afa2ffe75a647583df22a2ff89", size = 1970034 }, + { url = "https://files.pythonhosted.org/packages/54/28/d3325da57d413b9819365546eb9a6e8b7cbd9373d9380efd5f74326143e6/pydantic_core-2.41.4-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:e9205d97ed08a82ebb9a307e92914bb30e18cdf6f6b12ca4bedadb1588a0bfe1", size = 2102022 }, + { url = "https://files.pythonhosted.org/packages/9e/24/b58a1bc0d834bf1acc4361e61233ee217169a42efbdc15a60296e13ce438/pydantic_core-2.41.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:82df1f432b37d832709fbcc0e24394bba04a01b6ecf1ee87578145c19cde12ac", size = 1905495 }, + { url = "https://files.pythonhosted.org/packages/fb/a4/71f759cc41b7043e8ecdaab81b985a9b6cad7cec077e0b92cff8b71ecf6b/pydantic_core-2.41.4-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc3b4cc4539e055cfa39a3763c939f9d409eb40e85813257dcd761985a108554", size = 1956131 }, + { url = "https://files.pythonhosted.org/packages/b0/64/1e79ac7aa51f1eec7c4cda8cbe456d5d09f05fdd68b32776d72168d54275/pydantic_core-2.41.4-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b1eb1754fce47c63d2ff57fdb88c351a6c0150995890088b33767a10218eaa4e", size = 2052236 }, + { url = "https://files.pythonhosted.org/packages/e9/e3/a3ffc363bd4287b80f1d43dc1c28ba64831f8dfc237d6fec8f2661138d48/pydantic_core-2.41.4-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e6ab5ab30ef325b443f379ddb575a34969c333004fca5a1daa0133a6ffaad616", size = 2223573 }, + { url = "https://files.pythonhosted.org/packages/28/27/78814089b4d2e684a9088ede3790763c64693c3d1408ddc0a248bc789126/pydantic_core-2.41.4-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:31a41030b1d9ca497634092b46481b937ff9397a86f9f51bd41c4767b6fc04af", size = 2342467 }, + { url = "https://files.pythonhosted.org/packages/92/97/4de0e2a1159cb85ad737e03306717637842c88c7fd6d97973172fb183149/pydantic_core-2.41.4-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a44ac1738591472c3d020f61c6df1e4015180d6262ebd39bf2aeb52571b60f12", size = 2063754 }, + { url = "https://files.pythonhosted.org/packages/0f/50/8cb90ce4b9efcf7ae78130afeb99fd1c86125ccdf9906ef64b9d42f37c25/pydantic_core-2.41.4-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d72f2b5e6e82ab8f94ea7d0d42f83c487dc159c5240d8f83beae684472864e2d", size = 2196754 }, + { url = "https://files.pythonhosted.org/packages/34/3b/ccdc77af9cd5082723574a1cc1bcae7a6acacc829d7c0a06201f7886a109/pydantic_core-2.41.4-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:c4d1e854aaf044487d31143f541f7aafe7b482ae72a022c664b2de2e466ed0ad", size = 2137115 }, + { url = "https://files.pythonhosted.org/packages/ca/ba/e7c7a02651a8f7c52dc2cff2b64a30c313e3b57c7d93703cecea76c09b71/pydantic_core-2.41.4-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:b568af94267729d76e6ee5ececda4e283d07bbb28e8148bb17adad93d025d25a", size = 2317400 }, + { url = "https://files.pythonhosted.org/packages/2c/ba/6c533a4ee8aec6b812c643c49bb3bd88d3f01e3cebe451bb85512d37f00f/pydantic_core-2.41.4-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:6d55fb8b1e8929b341cc313a81a26e0d48aa3b519c1dbaadec3a6a2b4fcad025", size = 2312070 }, + { url = "https://files.pythonhosted.org/packages/22/ae/f10524fcc0ab8d7f96cf9a74c880243576fd3e72bd8ce4f81e43d22bcab7/pydantic_core-2.41.4-cp314-cp314-win32.whl", hash = "sha256:5b66584e549e2e32a1398df11da2e0a7eff45d5c2d9db9d5667c5e6ac764d77e", size = 1982277 }, + { url = "https://files.pythonhosted.org/packages/b4/dc/e5aa27aea1ad4638f0c3fb41132f7eb583bd7420ee63204e2d4333a3bbf9/pydantic_core-2.41.4-cp314-cp314-win_amd64.whl", hash = "sha256:557a0aab88664cc552285316809cab897716a372afaf8efdbef756f8b890e894", size = 2024608 }, + { url = "https://files.pythonhosted.org/packages/3e/61/51d89cc2612bd147198e120a13f150afbf0bcb4615cddb049ab10b81b79e/pydantic_core-2.41.4-cp314-cp314-win_arm64.whl", hash = "sha256:3f1ea6f48a045745d0d9f325989d8abd3f1eaf47dd00485912d1a3a63c623a8d", size = 1967614 }, + { url = "https://files.pythonhosted.org/packages/0d/c2/472f2e31b95eff099961fa050c376ab7156a81da194f9edb9f710f68787b/pydantic_core-2.41.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6c1fe4c5404c448b13188dd8bd2ebc2bdd7e6727fa61ff481bcc2cca894018da", size = 1876904 }, + { url = "https://files.pythonhosted.org/packages/4a/07/ea8eeb91173807ecdae4f4a5f4b150a520085b35454350fc219ba79e66a3/pydantic_core-2.41.4-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:523e7da4d43b113bf8e7b49fa4ec0c35bf4fe66b2230bfc5c13cc498f12c6c3e", size = 1882538 }, + { url = "https://files.pythonhosted.org/packages/1e/29/b53a9ca6cd366bfc928823679c6a76c7a4c69f8201c0ba7903ad18ebae2f/pydantic_core-2.41.4-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5729225de81fb65b70fdb1907fcf08c75d498f4a6f15af005aabb1fdadc19dfa", size = 2041183 }, + { url = "https://files.pythonhosted.org/packages/c7/3d/f8c1a371ceebcaf94d6dd2d77c6cf4b1c078e13a5837aee83f760b4f7cfd/pydantic_core-2.41.4-cp314-cp314t-win_amd64.whl", hash = "sha256:de2cfbb09e88f0f795fd90cf955858fc2c691df65b1f21f0aa00b99f3fbc661d", size = 1993542 }, + { url = "https://files.pythonhosted.org/packages/8a/ac/9fc61b4f9d079482a290afe8d206b8f490e9fd32d4fc03ed4fc698214e01/pydantic_core-2.41.4-cp314-cp314t-win_arm64.whl", hash = "sha256:d34f950ae05a83e0ede899c595f312ca976023ea1db100cd5aa188f7005e3ab0", size = 1973897 }, + { url = "https://files.pythonhosted.org/packages/5d/d4/912e976a2dd0b49f31c98a060ca90b353f3b73ee3ea2fd0030412f6ac5ec/pydantic_core-2.41.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:1e5ab4fc177dd41536b3c32b2ea11380dd3d4619a385860621478ac2d25ceb00", size = 2106739 }, + { url = "https://files.pythonhosted.org/packages/71/f0/66ec5a626c81eba326072d6ee2b127f8c139543f1bf609b4842978d37833/pydantic_core-2.41.4-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:3d88d0054d3fa11ce936184896bed3c1c5441d6fa483b498fac6a5d0dd6f64a9", size = 1932549 }, + { url = "https://files.pythonhosted.org/packages/c4/af/625626278ca801ea0a658c2dcf290dc9f21bb383098e99e7c6a029fccfc0/pydantic_core-2.41.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b2a054a8725f05b4b6503357e0ac1c4e8234ad3b0c2ac130d6ffc66f0e170e2", size = 2135093 }, + { url = "https://files.pythonhosted.org/packages/20/f6/2fba049f54e0f4975fef66be654c597a1d005320fa141863699180c7697d/pydantic_core-2.41.4-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b0d9db5a161c99375a0c68c058e227bee1d89303300802601d76a3d01f74e258", size = 2187971 }, + { url = "https://files.pythonhosted.org/packages/0e/80/65ab839a2dfcd3b949202f9d920c34f9de5a537c3646662bdf2f7d999680/pydantic_core-2.41.4-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:6273ea2c8ffdac7b7fda2653c49682db815aebf4a89243a6feccf5e36c18c347", size = 2147939 }, + { url = "https://files.pythonhosted.org/packages/44/58/627565d3d182ce6dfda18b8e1c841eede3629d59c9d7cbc1e12a03aeb328/pydantic_core-2.41.4-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:4c973add636efc61de22530b2ef83a65f39b6d6f656df97f678720e20de26caa", size = 2311400 }, + { url = "https://files.pythonhosted.org/packages/24/06/8a84711162ad5a5f19a88cead37cca81b4b1f294f46260ef7334ae4f24d3/pydantic_core-2.41.4-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:b69d1973354758007f46cf2d44a4f3d0933f10b6dc9bf15cf1356e037f6f731a", size = 2316840 }, + { url = "https://files.pythonhosted.org/packages/aa/8b/b7bb512a4682a2f7fbfae152a755d37351743900226d29bd953aaf870eaa/pydantic_core-2.41.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:3619320641fd212aaf5997b6ca505e97540b7e16418f4a241f44cdf108ffb50d", size = 2149135 }, + { url = "https://files.pythonhosted.org/packages/7e/7d/138e902ed6399b866f7cfe4435d22445e16fff888a1c00560d9dc79a780f/pydantic_core-2.41.4-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:491535d45cd7ad7e4a2af4a5169b0d07bebf1adfd164b0368da8aa41e19907a5", size = 2104721 }, + { url = "https://files.pythonhosted.org/packages/47/13/0525623cf94627f7b53b4c2034c81edc8491cbfc7c28d5447fa318791479/pydantic_core-2.41.4-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:54d86c0cada6aba4ec4c047d0e348cbad7063b87ae0f005d9f8c9ad04d4a92a2", size = 1931608 }, + { url = "https://files.pythonhosted.org/packages/d6/f9/744bc98137d6ef0a233f808bfc9b18cf94624bf30836a18d3b05d08bf418/pydantic_core-2.41.4-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eca1124aced216b2500dc2609eade086d718e8249cb9696660ab447d50a758bd", size = 2132986 }, + { url = "https://files.pythonhosted.org/packages/17/c8/629e88920171173f6049386cc71f893dff03209a9ef32b4d2f7e7c264bcf/pydantic_core-2.41.4-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6c9024169becccf0cb470ada03ee578d7348c119a0d42af3dcf9eda96e3a247c", size = 2187516 }, + { url = "https://files.pythonhosted.org/packages/2e/0f/4f2734688d98488782218ca61bcc118329bf5de05bb7fe3adc7dd79b0b86/pydantic_core-2.41.4-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:26895a4268ae5a2849269f4991cdc97236e4b9c010e51137becf25182daac405", size = 2146146 }, + { url = "https://files.pythonhosted.org/packages/ed/f2/ab385dbd94a052c62224b99cf99002eee99dbec40e10006c78575aead256/pydantic_core-2.41.4-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:ca4df25762cf71308c446e33c9b1fdca2923a3f13de616e2a949f38bf21ff5a8", size = 2311296 }, + { url = "https://files.pythonhosted.org/packages/fc/8e/e4f12afe1beeb9823bba5375f8f258df0cc61b056b0195fb1cf9f62a1a58/pydantic_core-2.41.4-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:5a28fcedd762349519276c36634e71853b4541079cab4acaaac60c4421827308", size = 2315386 }, + { url = "https://files.pythonhosted.org/packages/48/f7/925f65d930802e3ea2eb4d5afa4cb8730c8dc0d2cb89a59dc4ed2fcb2d74/pydantic_core-2.41.4-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:c173ddcd86afd2535e2b695217e82191580663a1d1928239f877f5a1649ef39f", size = 2147775 }, ] [[package]] @@ -4077,23 +4169,24 @@ wheels = [ [[package]] name = "pylibsrtp" -version = "0.12.0" +version = "1.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cffi" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/54/c8/a59e61f5dd655f5f21033bd643dd31fe980a537ed6f373cdfb49d3a3bd32/pylibsrtp-0.12.0.tar.gz", hash = "sha256:f5c3c0fb6954e7bb74dc7e6398352740ca67327e6759a199fe852dbc7b84b8ac", size = 10878 } +sdist = { url = "https://files.pythonhosted.org/packages/0d/a6/6e532bec974aaecbf9fe4e12538489fb1c28456e65088a50f305aeab9f89/pylibsrtp-1.0.0.tar.gz", hash = "sha256:b39dff075b263a8ded5377f2490c60d2af452c9f06c4d061c7a2b640612b34d4", size = 10858 } wheels = [ - { url = "https://files.pythonhosted.org/packages/65/f0/b818395c4cae2d5cc5a0c78fc47d694eae78e6a0d678baeb52a381a26327/pylibsrtp-0.12.0-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:5adde3cf9a5feef561d0eb7ed99dedb30b9bf1ce9a0c1770b2bf19fd0b98bc9a", size = 1727918 }, - { url = "https://files.pythonhosted.org/packages/05/1a/ee553abe4431b7bd9bab18f078c0ad2298b94ea55e664da6ecb8700b1052/pylibsrtp-0.12.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:d2c81d152606721331ece87c80ed17159ba6da55c7c61a6b750cff67ab7f63a5", size = 2057900 }, - { url = "https://files.pythonhosted.org/packages/7f/a2/2dd0188be58d3cba48c5eb4b3c787e5743c111cd0c9289de4b6f2798382a/pylibsrtp-0.12.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:242fa3d44219846bf1734d5df595563a2c8fbb0fb00ccc79ab0f569fc0af2c1b", size = 2567047 }, - { url = "https://files.pythonhosted.org/packages/6c/3a/4bdab9fc1d78f2efa02c8a8f3e9c187bfa278e89481b5123f07c8dd69310/pylibsrtp-0.12.0-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b74aaf8fac1b119a3c762f54751c3d20e77227b84c26d85aae57c2c43129b49c", size = 2168775 }, - { url = "https://files.pythonhosted.org/packages/d0/fc/0b1e1bfed420d79427d50aff84c370dcd78d81af9500c1e86fbcc5bf95e1/pylibsrtp-0.12.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:33e3e223102989b71f07e1deeb804170ed53fb4e1b283762eb031bd45bb425d4", size = 2225033 }, - { url = "https://files.pythonhosted.org/packages/39/7b/e1021d27900315c2c077ec7d45f50274cedbdde067ff679d44df06f01a8a/pylibsrtp-0.12.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:36d07de64dbc82dbbb99fd77f36c8e23d6730bdbcccf09701945690a9a9a422a", size = 2606093 }, - { url = "https://files.pythonhosted.org/packages/eb/c2/0fae6687a06fcde210a778148ec808af49e431c36fe9908503a695c35479/pylibsrtp-0.12.0-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:ef03b4578577690f716fd023daed8914eee6de9a764fa128eda19a0e645cc032", size = 2193213 }, - { url = "https://files.pythonhosted.org/packages/67/c2/2ed7a4a5c38b999fd34298f76b93d29f5ba8c06f85cfad3efd9468343715/pylibsrtp-0.12.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:0a8421e9fe4d20ce48d439430e55149f12b1bca1b0436741972c362c49948c0a", size = 2256774 }, - { url = "https://files.pythonhosted.org/packages/48/d7/f13fedce3b21d24f6f154d1dee7287464a34728dcb3b0c50f687dbad5765/pylibsrtp-0.12.0-cp39-abi3-win32.whl", hash = "sha256:cbc9bfbfb2597e993a1aa16b832ba16a9dd4647f70815421bb78484f8b50b924", size = 1156186 }, - { url = "https://files.pythonhosted.org/packages/9b/26/3a20b638a3a3995368f856eeb10701dd6c0e9ace9fb6665eeb1b95ccce19/pylibsrtp-0.12.0-cp39-abi3-win_amd64.whl", hash = "sha256:061ef1dbb5f08079ac6d7515b7e67ca48a3163e16e5b820beea6b01cb31d7e54", size = 1485072 }, + { url = "https://files.pythonhosted.org/packages/aa/af/89e61a62fa3567f1b7883feb4d19e19564066c2fcd41c37e08d317b51881/pylibsrtp-1.0.0-cp310-abi3-macosx_10_9_x86_64.whl", hash = "sha256:822c30ea9e759b333dc1f56ceac778707c51546e97eb874de98d7d378c000122", size = 1865017 }, + { url = "https://files.pythonhosted.org/packages/8d/0e/8d215484a9877adcf2459a8b28165fc89668b034565277fd55d666edd247/pylibsrtp-1.0.0-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:aaad74e5c8cbc1c32056c3767fea494c1e62b3aea2c908eda2a1051389fdad76", size = 2182739 }, + { url = "https://files.pythonhosted.org/packages/57/3f/76a841978877ae13eac0d4af412c13bbd5d83b3df2c1f5f2175f2e0f68e5/pylibsrtp-1.0.0-cp310-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9209b86e662ebbd17c8a9e8549ba57eca92a3e87fb5ba8c0e27b8c43cd08a767", size = 2732922 }, + { url = "https://files.pythonhosted.org/packages/0e/14/cf5d2a98a66fdfe258f6b036cda570f704a644fa861d7883a34bc359501e/pylibsrtp-1.0.0-cp310-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:293c9f2ac21a2bd689c477603a1aa235d85cf252160e6715f0101e42a43cbedc", size = 2434534 }, + { url = "https://files.pythonhosted.org/packages/bd/08/a3f6e86c04562f7dce6717cd2206a0f84ca85c5e38121d998e0e330194c3/pylibsrtp-1.0.0-cp310-abi3-manylinux_2_28_i686.whl", hash = "sha256:81fb8879c2e522021a7cbd3f4bda1b37c192e1af939dfda3ff95b4723b329663", size = 2345818 }, + { url = "https://files.pythonhosted.org/packages/8e/d5/130c2b5b4b51df5631684069c6f0a6761c59d096a33d21503ac207cf0e47/pylibsrtp-1.0.0-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:4ddb562e443cf2e557ea2dfaeef0d7e6b90e96dd38eb079b4ab2c8e34a79f50b", size = 2774490 }, + { url = "https://files.pythonhosted.org/packages/91/e3/715a453bfee3bea92a243888ad359094a7727cc6d393f21281320fe7798c/pylibsrtp-1.0.0-cp310-abi3-musllinux_1_2_i686.whl", hash = "sha256:f02e616c9dfab2b03b32d8cc7b748f9d91814c0211086f987629a60f05f6e2cc", size = 2372603 }, + { url = "https://files.pythonhosted.org/packages/e3/56/52fa74294254e1f53a4ff170ee2006e57886cf4bb3db46a02b4f09e1d99f/pylibsrtp-1.0.0-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:c134fa09e7b80a5b7fed626230c5bc257fd771bd6978e754343e7a61d96bc7e6", size = 2451269 }, + { url = "https://files.pythonhosted.org/packages/1e/51/2e9b34f484cbdd3bac999bf1f48b696d7389433e900639089e8fc4e0da0d/pylibsrtp-1.0.0-cp310-abi3-win32.whl", hash = "sha256:bae377c3b402b17b9bbfbfe2534c2edba17aa13bea4c64ce440caacbe0858b55", size = 1247503 }, + { url = "https://files.pythonhosted.org/packages/c3/70/43db21af194580aba2d9a6d4c7bd8c1a6e887fa52cd810b88f89096ecad2/pylibsrtp-1.0.0-cp310-abi3-win_amd64.whl", hash = "sha256:8d6527c4a78a39a8d397f8862a8b7cdad4701ee866faf9de4ab8c70be61fd34d", size = 1601659 }, + { url = "https://files.pythonhosted.org/packages/8e/ec/6e02b2561d056ea5b33046e3cad21238e6a9097b97d6ccc0fbe52b50c858/pylibsrtp-1.0.0-cp310-abi3-win_arm64.whl", hash = "sha256:2696bdb2180d53ac55d0eb7b58048a2aa30cd4836dd2ca683669889137a94d2a", size = 1159246 }, ] [[package]] @@ -4503,28 +4596,28 @@ wheels = [ [[package]] name = "rich" -version = "14.1.0" +version = "14.2.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markdown-it-py" }, { name = "pygments" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fe/75/af448d8e52bf1d8fa6a9d089ca6c07ff4453d86c65c145d0a300bb073b9b/rich-14.1.0.tar.gz", hash = "sha256:e497a48b844b0320d45007cdebfeaeed8db2a4f4bcf49f15e455cfc4af11eaa8", size = 224441 } +sdist = { url = "https://files.pythonhosted.org/packages/fb/d2/8920e102050a0de7bfabeb4c4614a49248cf8d5d7a8d01885fbb24dc767a/rich-14.2.0.tar.gz", hash = "sha256:73ff50c7c0c1c77c8243079283f4edb376f0f6442433aecb8ce7e6d0b92d1fe4", size = 219990 } wheels = [ - { url = "https://files.pythonhosted.org/packages/e3/30/3c4d035596d3cf444529e0b2953ad0466f6049528a879d27534700580395/rich-14.1.0-py3-none-any.whl", hash = "sha256:536f5f1785986d6dbdea3c75205c473f970777b4a0d6c6dd1b696aa05a3fa04f", size = 243368 }, + { url = "https://files.pythonhosted.org/packages/25/7a/b0178788f8dc6cafce37a212c99565fa1fe7872c70c6c9c1e1a372d9d88f/rich-14.2.0-py3-none-any.whl", hash = "sha256:76bc51fe2e57d2b1be1f96c524b890b816e334ab4c1e45888799bfaab0021edd", size = 243393 }, ] [[package]] name = "rich-rst" -version = "1.3.1" +version = "1.3.2" source = { registry = "https://pypi.org/simple" } 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 } +sdist = { url = "https://files.pythonhosted.org/packages/bc/6d/a506aaa4a9eaa945ed8ab2b7347859f53593864289853c5d6d62b77246e0/rich_rst-1.3.2.tar.gz", hash = "sha256:a1196fdddf1e364b02ec68a05e8ff8f6914fee10fbca2e6b6735f166bb0da8d4", size = 14936 } wheels = [ - { url = "https://files.pythonhosted.org/packages/fd/bc/cc4e3dbc5e7992398dcb7a8eda0cbcf4fb792a0cdb93f857b478bf3cf884/rich_rst-1.3.1-py3-none-any.whl", hash = "sha256:498a74e3896507ab04492d326e794c3ef76e7cda078703aa592d1853d91098c1", size = 11621 }, + { url = "https://files.pythonhosted.org/packages/13/2f/b4530fbf948867702d0a3f27de4a6aab1d156f406d72852ab902c4d04de9/rich_rst-1.3.2-py3-none-any.whl", hash = "sha256:a99b4907cbe118cf9d18b0b44de272efa61f15117c61e39ebdc431baf5df722a", size = 12567 }, ] [[package]] @@ -4676,28 +4769,28 @@ wheels = [ [[package]] name = "ruff" -version = "0.14.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/41/b9/9bd84453ed6dd04688de9b3f3a4146a1698e8faae2ceeccce4e14c67ae17/ruff-0.14.0.tar.gz", hash = "sha256:62ec8969b7510f77945df916de15da55311fade8d6050995ff7f680afe582c57", size = 5452071 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3a/4e/79d463a5f80654e93fa653ebfb98e0becc3f0e7cf6219c9ddedf1e197072/ruff-0.14.0-py3-none-linux_armv6l.whl", hash = "sha256:58e15bffa7054299becf4bab8a1187062c6f8cafbe9f6e39e0d5aface455d6b3", size = 12494532 }, - { url = "https://files.pythonhosted.org/packages/ee/40/e2392f445ed8e02aa6105d49db4bfff01957379064c30f4811c3bf38aece/ruff-0.14.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:838d1b065f4df676b7c9957992f2304e41ead7a50a568185efd404297d5701e8", size = 13160768 }, - { url = "https://files.pythonhosted.org/packages/75/da/2a656ea7c6b9bd14c7209918268dd40e1e6cea65f4bb9880eaaa43b055cd/ruff-0.14.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:703799d059ba50f745605b04638fa7e9682cc3da084b2092feee63500ff3d9b8", size = 12363376 }, - { url = "https://files.pythonhosted.org/packages/42/e2/1ffef5a1875add82416ff388fcb7ea8b22a53be67a638487937aea81af27/ruff-0.14.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ba9a8925e90f861502f7d974cc60e18ca29c72bb0ee8bfeabb6ade35a3abde7", size = 12608055 }, - { url = "https://files.pythonhosted.org/packages/4a/32/986725199d7cee510d9f1dfdf95bf1efc5fa9dd714d0d85c1fb1f6be3bc3/ruff-0.14.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e41f785498bd200ffc276eb9e1570c019c1d907b07cfb081092c8ad51975bbe7", size = 12318544 }, - { url = "https://files.pythonhosted.org/packages/9a/ed/4969cefd53315164c94eaf4da7cfba1f267dc275b0abdd593d11c90829a3/ruff-0.14.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:30a58c087aef4584c193aebf2700f0fbcfc1e77b89c7385e3139956fa90434e2", size = 14001280 }, - { url = "https://files.pythonhosted.org/packages/ab/ad/96c1fc9f8854c37681c9613d825925c7f24ca1acfc62a4eb3896b50bacd2/ruff-0.14.0-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:f8d07350bc7af0a5ce8812b7d5c1a7293cf02476752f23fdfc500d24b79b783c", size = 15027286 }, - { url = "https://files.pythonhosted.org/packages/b3/00/1426978f97df4fe331074baf69615f579dc4e7c37bb4c6f57c2aad80c87f/ruff-0.14.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eec3bbbf3a7d5482b5c1f42d5fc972774d71d107d447919fca620b0be3e3b75e", size = 14451506 }, - { url = "https://files.pythonhosted.org/packages/58/d5/9c1cea6e493c0cf0647674cca26b579ea9d2a213b74b5c195fbeb9678e15/ruff-0.14.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:16b68e183a0e28e5c176d51004aaa40559e8f90065a10a559176713fcf435206", size = 13437384 }, - { url = "https://files.pythonhosted.org/packages/29/b4/4cd6a4331e999fc05d9d77729c95503f99eae3ba1160469f2b64866964e3/ruff-0.14.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb732d17db2e945cfcbbc52af0143eda1da36ca8ae25083dd4f66f1542fdf82e", size = 13447976 }, - { url = "https://files.pythonhosted.org/packages/3b/c0/ac42f546d07e4f49f62332576cb845d45c67cf5610d1851254e341d563b6/ruff-0.14.0-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:c958f66ab884b7873e72df38dcabee03d556a8f2ee1b8538ee1c2bbd619883dd", size = 13682850 }, - { url = "https://files.pythonhosted.org/packages/5f/c4/4b0c9bcadd45b4c29fe1af9c5d1dc0ca87b4021665dfbe1c4688d407aa20/ruff-0.14.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:7eb0499a2e01f6e0c285afc5bac43ab380cbfc17cd43a2e1dd10ec97d6f2c42d", size = 12449825 }, - { url = "https://files.pythonhosted.org/packages/4b/a8/e2e76288e6c16540fa820d148d83e55f15e994d852485f221b9524514730/ruff-0.14.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:4c63b2d99fafa05efca0ab198fd48fa6030d57e4423df3f18e03aa62518c565f", size = 12272599 }, - { url = "https://files.pythonhosted.org/packages/18/14/e2815d8eff847391af632b22422b8207704222ff575dec8d044f9ab779b2/ruff-0.14.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:668fce701b7a222f3f5327f86909db2bbe99c30877c8001ff934c5413812ac02", size = 13193828 }, - { url = "https://files.pythonhosted.org/packages/44/c6/61ccc2987cf0aecc588ff8f3212dea64840770e60d78f5606cd7dc34de32/ruff-0.14.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:a86bf575e05cb68dcb34e4c7dfe1064d44d3f0c04bbc0491949092192b515296", size = 13628617 }, - { url = "https://files.pythonhosted.org/packages/73/e6/03b882225a1b0627e75339b420883dc3c90707a8917d2284abef7a58d317/ruff-0.14.0-py3-none-win32.whl", hash = "sha256:7450a243d7125d1c032cb4b93d9625dea46c8c42b4f06c6b709baac168e10543", size = 12367872 }, - { url = "https://files.pythonhosted.org/packages/41/77/56cf9cf01ea0bfcc662de72540812e5ba8e9563f33ef3d37ab2174892c47/ruff-0.14.0-py3-none-win_amd64.whl", hash = "sha256:ea95da28cd874c4d9c922b39381cbd69cb7e7b49c21b8152b014bd4f52acddc2", size = 13464628 }, - { url = "https://files.pythonhosted.org/packages/c6/2a/65880dfd0e13f7f13a775998f34703674a4554906167dce02daf7865b954/ruff-0.14.0-py3-none-win_arm64.whl", hash = "sha256:f42c9495f5c13ff841b1da4cb3c2a42075409592825dada7c5885c2c844ac730", size = 12565142 }, +version = "0.14.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9e/58/6ca66896635352812de66f71cdf9ff86b3a4f79071ca5730088c0cd0fc8d/ruff-0.14.1.tar.gz", hash = "sha256:1dd86253060c4772867c61791588627320abcb6ed1577a90ef432ee319729b69", size = 5513429 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8d/39/9cc5ab181478d7a18adc1c1e051a84ee02bec94eb9bdfd35643d7c74ca31/ruff-0.14.1-py3-none-linux_armv6l.whl", hash = "sha256:083bfc1f30f4a391ae09c6f4f99d83074416b471775b59288956f5bc18e82f8b", size = 12445415 }, + { url = "https://files.pythonhosted.org/packages/ef/2e/1226961855ccd697255988f5a2474890ac7c5863b080b15bd038df820818/ruff-0.14.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:f6fa757cd717f791009f7669fefb09121cc5f7d9bd0ef211371fad68c2b8b224", size = 12784267 }, + { url = "https://files.pythonhosted.org/packages/c1/ea/fd9e95863124ed159cd0667ec98449ae461de94acda7101f1acb6066da00/ruff-0.14.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:d6191903d39ac156921398e9c86b7354d15e3c93772e7dbf26c9fcae59ceccd5", size = 11781872 }, + { url = "https://files.pythonhosted.org/packages/1e/5a/e890f7338ff537dba4589a5e02c51baa63020acfb7c8cbbaea4831562c96/ruff-0.14.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed04f0e04f7a4587244e5c9d7df50e6b5bf2705d75059f409a6421c593a35896", size = 12226558 }, + { url = "https://files.pythonhosted.org/packages/a6/7a/8ab5c3377f5bf31e167b73651841217542bcc7aa1c19e83030835cc25204/ruff-0.14.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5c9e6cf6cd4acae0febbce29497accd3632fe2025c0c583c8b87e8dbdeae5f61", size = 12187898 }, + { url = "https://files.pythonhosted.org/packages/48/8d/ba7c33aa55406955fc124e62c8259791c3d42e3075a71710fdff9375134f/ruff-0.14.1-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a6fa2458527794ecdfbe45f654e42c61f2503a230545a91af839653a0a93dbc6", size = 12939168 }, + { url = "https://files.pythonhosted.org/packages/b4/c2/70783f612b50f66d083380e68cbd1696739d88e9b4f6164230375532c637/ruff-0.14.1-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:39f1c392244e338b21d42ab29b8a6392a722c5090032eb49bb4d6defcdb34345", size = 14386942 }, + { url = "https://files.pythonhosted.org/packages/48/44/cd7abb9c776b66d332119d67f96acf15830d120f5b884598a36d9d3f4d83/ruff-0.14.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7382fa12a26cce1f95070ce450946bec357727aaa428983036362579eadcc5cf", size = 13990622 }, + { url = "https://files.pythonhosted.org/packages/eb/56/4259b696db12ac152fe472764b4f78bbdd9b477afd9bc3a6d53c01300b37/ruff-0.14.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dd0bf2be3ae8521e1093a487c4aa3b455882f139787770698530d28ed3fbb37c", size = 13431143 }, + { url = "https://files.pythonhosted.org/packages/e0/35/266a80d0eb97bd224b3265b9437bd89dde0dcf4faf299db1212e81824e7e/ruff-0.14.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cabcaa9ccf8089fb4fdb78d17cc0e28241520f50f4c2e88cb6261ed083d85151", size = 13132844 }, + { url = "https://files.pythonhosted.org/packages/65/6e/d31ce218acc11a8d91ef208e002a31acf315061a85132f94f3df7a252b18/ruff-0.14.1-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:747d583400f6125ec11a4c14d1c8474bf75d8b419ad22a111a537ec1a952d192", size = 13401241 }, + { url = "https://files.pythonhosted.org/packages/9f/b5/dbc4221bf0b03774b3b2f0d47f39e848d30664157c15b965a14d890637d2/ruff-0.14.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:5a6e74c0efd78515a1d13acbfe6c90f0f5bd822aa56b4a6d43a9ffb2ae6e56cd", size = 12132476 }, + { url = "https://files.pythonhosted.org/packages/98/4b/ac99194e790ccd092d6a8b5f341f34b6e597d698e3077c032c502d75ea84/ruff-0.14.1-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:0ea6a864d2fb41a4b6d5b456ed164302a0d96f4daac630aeba829abfb059d020", size = 12139749 }, + { url = "https://files.pythonhosted.org/packages/47/26/7df917462c3bb5004e6fdfcc505a49e90bcd8a34c54a051953118c00b53a/ruff-0.14.1-py3-none-musllinux_1_2_i686.whl", hash = "sha256:0826b8764f94229604fa255918d1cc45e583e38c21c203248b0bfc9a0e930be5", size = 12544758 }, + { url = "https://files.pythonhosted.org/packages/64/d0/81e7f0648e9764ad9b51dd4be5e5dac3fcfff9602428ccbae288a39c2c22/ruff-0.14.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:cbc52160465913a1a3f424c81c62ac8096b6a491468e7d872cb9444a860bc33d", size = 13221811 }, + { url = "https://files.pythonhosted.org/packages/c3/07/3c45562c67933cc35f6d5df4ca77dabbcd88fddaca0d6b8371693d29fd56/ruff-0.14.1-py3-none-win32.whl", hash = "sha256:e037ea374aaaff4103240ae79168c0945ae3d5ae8db190603de3b4012bd1def6", size = 12319467 }, + { url = "https://files.pythonhosted.org/packages/02/88/0ee4ca507d4aa05f67e292d2e5eb0b3e358fbcfe527554a2eda9ac422d6b/ruff-0.14.1-py3-none-win_amd64.whl", hash = "sha256:59d599cdff9c7f925a017f6f2c256c908b094e55967f93f2821b1439928746a1", size = 13401123 }, + { url = "https://files.pythonhosted.org/packages/b8/81/4b6387be7014858d924b843530e1b2a8e531846807516e9bea2ee0936bf7/ruff-0.14.1-py3-none-win_arm64.whl", hash = "sha256:e3b443c4c9f16ae850906b8d0a707b2a4c16f8d2f0a7fe65c475c5886665ce44", size = 12436636 }, ] [[package]] @@ -4847,16 +4940,16 @@ wheels = [ [[package]] name = "setuptools-scm" -version = "9.2.0" +version = "9.2.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "packaging" }, { name = "setuptools" }, { name = "tomli", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/8f/8d/ffdcace33d0480d591057a30285b7c33f8dc431fed3fff7dbadf5f9f128f/setuptools_scm-9.2.0.tar.gz", hash = "sha256:6662c9b9497b6c9bf13bead9d7a9084756f68238302c5ed089fb4dbd29d102d7", size = 201229 } +sdist = { url = "https://files.pythonhosted.org/packages/d0/32/7d8f405986239e829556ea369b7451ba885c3dc8472ae07b9437b375f6e0/setuptools_scm-9.2.1.tar.gz", hash = "sha256:2f5b21e45205e47ed3976e58bc29c38f49bb9b6f07c625c7fd2823871694b244", size = 202821 } wheels = [ - { url = "https://files.pythonhosted.org/packages/f7/14/dd3a6053325e882fe191fb4b42289bbdfabf5f44307c302903a8a3236a0a/setuptools_scm-9.2.0-py3-none-any.whl", hash = "sha256:c551ef54e2270727ee17067881c9687ca2aedf179fa5b8f3fab9e8d73bdc421f", size = 62099 }, + { url = "https://files.pythonhosted.org/packages/11/cc/5fda7ed3aab0814a86e428846b6b386f55093f4621d86e3cd081ae126c27/setuptools_scm-9.2.1-py3-none-any.whl", hash = "sha256:84c9b3f39ffb77ba00dd92a06e025e4f66dc69ab8df1c2a225a0678e76311f8d", size = 62878 }, ] [[package]] @@ -5191,41 +5284,51 @@ wheels = [ [[package]] name = "tomli" -version = "2.2.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/18/87/302344fed471e44a87289cf4967697d07e532f2421fdaf868a303cbae4ff/tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff", size = 17175 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/43/ca/75707e6efa2b37c77dadb324ae7d9571cb424e61ea73fad7c56c2d14527f/tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249", size = 131077 }, - { url = "https://files.pythonhosted.org/packages/c7/16/51ae563a8615d472fdbffc43a3f3d46588c264ac4f024f63f01283becfbb/tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6", size = 123429 }, - { url = "https://files.pythonhosted.org/packages/f1/dd/4f6cd1e7b160041db83c694abc78e100473c15d54620083dbd5aae7b990e/tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a", size = 226067 }, - { url = "https://files.pythonhosted.org/packages/a9/6b/c54ede5dc70d648cc6361eaf429304b02f2871a345bbdd51e993d6cdf550/tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee", size = 236030 }, - { url = "https://files.pythonhosted.org/packages/1f/47/999514fa49cfaf7a92c805a86c3c43f4215621855d151b61c602abb38091/tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e", size = 240898 }, - { url = "https://files.pythonhosted.org/packages/73/41/0a01279a7ae09ee1573b423318e7934674ce06eb33f50936655071d81a24/tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4", size = 229894 }, - { url = "https://files.pythonhosted.org/packages/55/18/5d8bc5b0a0362311ce4d18830a5d28943667599a60d20118074ea1b01bb7/tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106", size = 245319 }, - { url = "https://files.pythonhosted.org/packages/92/a3/7ade0576d17f3cdf5ff44d61390d4b3febb8a9fc2b480c75c47ea048c646/tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8", size = 238273 }, - { url = "https://files.pythonhosted.org/packages/72/6f/fa64ef058ac1446a1e51110c375339b3ec6be245af9d14c87c4a6412dd32/tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff", size = 98310 }, - { url = "https://files.pythonhosted.org/packages/6a/1c/4a2dcde4a51b81be3530565e92eda625d94dafb46dbeb15069df4caffc34/tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b", size = 108309 }, - { url = "https://files.pythonhosted.org/packages/52/e1/f8af4c2fcde17500422858155aeb0d7e93477a0d59a98e56cbfe75070fd0/tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea", size = 132762 }, - { url = "https://files.pythonhosted.org/packages/03/b8/152c68bb84fc00396b83e7bbddd5ec0bd3dd409db4195e2a9b3e398ad2e3/tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8", size = 123453 }, - { url = "https://files.pythonhosted.org/packages/c8/d6/fc9267af9166f79ac528ff7e8c55c8181ded34eb4b0e93daa767b8841573/tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192", size = 233486 }, - { url = "https://files.pythonhosted.org/packages/5c/51/51c3f2884d7bab89af25f678447ea7d297b53b5a3b5730a7cb2ef6069f07/tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222", size = 242349 }, - { url = "https://files.pythonhosted.org/packages/ab/df/bfa89627d13a5cc22402e441e8a931ef2108403db390ff3345c05253935e/tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77", size = 252159 }, - { url = "https://files.pythonhosted.org/packages/9e/6e/fa2b916dced65763a5168c6ccb91066f7639bdc88b48adda990db10c8c0b/tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6", size = 237243 }, - { url = "https://files.pythonhosted.org/packages/b4/04/885d3b1f650e1153cbb93a6a9782c58a972b94ea4483ae4ac5cedd5e4a09/tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd", size = 259645 }, - { url = "https://files.pythonhosted.org/packages/9c/de/6b432d66e986e501586da298e28ebeefd3edc2c780f3ad73d22566034239/tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e", size = 244584 }, - { url = "https://files.pythonhosted.org/packages/1c/9a/47c0449b98e6e7d1be6cbac02f93dd79003234ddc4aaab6ba07a9a7482e2/tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98", size = 98875 }, - { url = "https://files.pythonhosted.org/packages/ef/60/9b9638f081c6f1261e2688bd487625cd1e660d0a85bd469e91d8db969734/tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4", size = 109418 }, - { url = "https://files.pythonhosted.org/packages/04/90/2ee5f2e0362cb8a0b6499dc44f4d7d48f8fff06d28ba46e6f1eaa61a1388/tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7", size = 132708 }, - { url = "https://files.pythonhosted.org/packages/c0/ec/46b4108816de6b385141f082ba99e315501ccd0a2ea23db4a100dd3990ea/tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c", size = 123582 }, - { url = "https://files.pythonhosted.org/packages/a0/bd/b470466d0137b37b68d24556c38a0cc819e8febe392d5b199dcd7f578365/tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13", size = 232543 }, - { url = "https://files.pythonhosted.org/packages/d9/e5/82e80ff3b751373f7cead2815bcbe2d51c895b3c990686741a8e56ec42ab/tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281", size = 241691 }, - { url = "https://files.pythonhosted.org/packages/05/7e/2a110bc2713557d6a1bfb06af23dd01e7dde52b6ee7dadc589868f9abfac/tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272", size = 251170 }, - { url = "https://files.pythonhosted.org/packages/64/7b/22d713946efe00e0adbcdfd6d1aa119ae03fd0b60ebed51ebb3fa9f5a2e5/tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140", size = 236530 }, - { url = "https://files.pythonhosted.org/packages/38/31/3a76f67da4b0cf37b742ca76beaf819dca0ebef26d78fc794a576e08accf/tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2", size = 258666 }, - { url = "https://files.pythonhosted.org/packages/07/10/5af1293da642aded87e8a988753945d0cf7e00a9452d3911dd3bb354c9e2/tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744", size = 243954 }, - { url = "https://files.pythonhosted.org/packages/5b/b9/1ed31d167be802da0fc95020d04cd27b7d7065cc6fbefdd2f9186f60d7bd/tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec", size = 98724 }, - { url = "https://files.pythonhosted.org/packages/c7/32/b0963458706accd9afcfeb867c0f9175a741bf7b19cd424230714d722198/tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69", size = 109383 }, - { url = "https://files.pythonhosted.org/packages/6e/c2/61d3e0f47e2b74ef40a68b9e6ad5984f6241a942f7cd3bbfbdbd03861ea9/tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc", size = 14257 }, +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/52/ed/3f73f72945444548f33eba9a87fc7a6e969915e7b1acc8260b30e1f76a2f/tomli-2.3.0.tar.gz", hash = "sha256:64be704a875d2a59753d80ee8a533c3fe183e3f06807ff7dc2232938ccb01549", size = 17392 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/2e/299f62b401438d5fe1624119c723f5d877acc86a4c2492da405626665f12/tomli-2.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:88bd15eb972f3664f5ed4b57c1634a97153b4bac4479dcb6a495f41921eb7f45", size = 153236 }, + { url = "https://files.pythonhosted.org/packages/86/7f/d8fffe6a7aefdb61bced88fcb5e280cfd71e08939da5894161bd71bea022/tomli-2.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:883b1c0d6398a6a9d29b508c331fa56adbcdff647f6ace4dfca0f50e90dfd0ba", size = 148084 }, + { url = "https://files.pythonhosted.org/packages/47/5c/24935fb6a2ee63e86d80e4d3b58b222dafaf438c416752c8b58537c8b89a/tomli-2.3.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d1381caf13ab9f300e30dd8feadb3de072aeb86f1d34a8569453ff32a7dea4bf", size = 234832 }, + { url = "https://files.pythonhosted.org/packages/89/da/75dfd804fc11e6612846758a23f13271b76d577e299592b4371a4ca4cd09/tomli-2.3.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a0e285d2649b78c0d9027570d4da3425bdb49830a6156121360b3f8511ea3441", size = 242052 }, + { url = "https://files.pythonhosted.org/packages/70/8c/f48ac899f7b3ca7eb13af73bacbc93aec37f9c954df3c08ad96991c8c373/tomli-2.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0a154a9ae14bfcf5d8917a59b51ffd5a3ac1fd149b71b47a3a104ca4edcfa845", size = 239555 }, + { url = "https://files.pythonhosted.org/packages/ba/28/72f8afd73f1d0e7829bfc093f4cb98ce0a40ffc0cc997009ee1ed94ba705/tomli-2.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:74bf8464ff93e413514fefd2be591c3b0b23231a77f901db1eb30d6f712fc42c", size = 245128 }, + { url = "https://files.pythonhosted.org/packages/b6/eb/a7679c8ac85208706d27436e8d421dfa39d4c914dcf5fa8083a9305f58d9/tomli-2.3.0-cp311-cp311-win32.whl", hash = "sha256:00b5f5d95bbfc7d12f91ad8c593a1659b6387b43f054104cda404be6bda62456", size = 96445 }, + { url = "https://files.pythonhosted.org/packages/0a/fe/3d3420c4cb1ad9cb462fb52967080575f15898da97e21cb6f1361d505383/tomli-2.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:4dc4ce8483a5d429ab602f111a93a6ab1ed425eae3122032db7e9acf449451be", size = 107165 }, + { url = "https://files.pythonhosted.org/packages/ff/b7/40f36368fcabc518bb11c8f06379a0fd631985046c038aca08c6d6a43c6e/tomli-2.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d7d86942e56ded512a594786a5ba0a5e521d02529b3826e7761a05138341a2ac", size = 154891 }, + { url = "https://files.pythonhosted.org/packages/f9/3f/d9dd692199e3b3aab2e4e4dd948abd0f790d9ded8cd10cbaae276a898434/tomli-2.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:73ee0b47d4dad1c5e996e3cd33b8a76a50167ae5f96a2607cbe8cc773506ab22", size = 148796 }, + { url = "https://files.pythonhosted.org/packages/60/83/59bff4996c2cf9f9387a0f5a3394629c7efa5ef16142076a23a90f1955fa/tomli-2.3.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:792262b94d5d0a466afb5bc63c7daa9d75520110971ee269152083270998316f", size = 242121 }, + { url = "https://files.pythonhosted.org/packages/45/e5/7c5119ff39de8693d6baab6c0b6dcb556d192c165596e9fc231ea1052041/tomli-2.3.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4f195fe57ecceac95a66a75ac24d9d5fbc98ef0962e09b2eddec5d39375aae52", size = 250070 }, + { url = "https://files.pythonhosted.org/packages/45/12/ad5126d3a278f27e6701abde51d342aa78d06e27ce2bb596a01f7709a5a2/tomli-2.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e31d432427dcbf4d86958c184b9bfd1e96b5b71f8eb17e6d02531f434fd335b8", size = 245859 }, + { url = "https://files.pythonhosted.org/packages/fb/a1/4d6865da6a71c603cfe6ad0e6556c73c76548557a8d658f9e3b142df245f/tomli-2.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7b0882799624980785240ab732537fcfc372601015c00f7fc367c55308c186f6", size = 250296 }, + { url = "https://files.pythonhosted.org/packages/a0/b7/a7a7042715d55c9ba6e8b196d65d2cb662578b4d8cd17d882d45322b0d78/tomli-2.3.0-cp312-cp312-win32.whl", hash = "sha256:ff72b71b5d10d22ecb084d345fc26f42b5143c5533db5e2eaba7d2d335358876", size = 97124 }, + { url = "https://files.pythonhosted.org/packages/06/1e/f22f100db15a68b520664eb3328fb0ae4e90530887928558112c8d1f4515/tomli-2.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:1cb4ed918939151a03f33d4242ccd0aa5f11b3547d0cf30f7c74a408a5b99878", size = 107698 }, + { url = "https://files.pythonhosted.org/packages/89/48/06ee6eabe4fdd9ecd48bf488f4ac783844fd777f547b8d1b61c11939974e/tomli-2.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5192f562738228945d7b13d4930baffda67b69425a7f0da96d360b0a3888136b", size = 154819 }, + { url = "https://files.pythonhosted.org/packages/f1/01/88793757d54d8937015c75dcdfb673c65471945f6be98e6a0410fba167ed/tomli-2.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:be71c93a63d738597996be9528f4abe628d1adf5e6eb11607bc8fe1a510b5dae", size = 148766 }, + { url = "https://files.pythonhosted.org/packages/42/17/5e2c956f0144b812e7e107f94f1cc54af734eb17b5191c0bbfb72de5e93e/tomli-2.3.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c4665508bcbac83a31ff8ab08f424b665200c0e1e645d2bd9ab3d3e557b6185b", size = 240771 }, + { url = "https://files.pythonhosted.org/packages/d5/f4/0fbd014909748706c01d16824eadb0307115f9562a15cbb012cd9b3512c5/tomli-2.3.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4021923f97266babc6ccab9f5068642a0095faa0a51a246a6a02fccbb3514eaf", size = 248586 }, + { url = "https://files.pythonhosted.org/packages/30/77/fed85e114bde5e81ecf9bc5da0cc69f2914b38f4708c80ae67d0c10180c5/tomli-2.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4ea38c40145a357d513bffad0ed869f13c1773716cf71ccaa83b0fa0cc4e42f", size = 244792 }, + { url = "https://files.pythonhosted.org/packages/55/92/afed3d497f7c186dc71e6ee6d4fcb0acfa5f7d0a1a2878f8beae379ae0cc/tomli-2.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ad805ea85eda330dbad64c7ea7a4556259665bdf9d2672f5dccc740eb9d3ca05", size = 248909 }, + { url = "https://files.pythonhosted.org/packages/f8/84/ef50c51b5a9472e7265ce1ffc7f24cd4023d289e109f669bdb1553f6a7c2/tomli-2.3.0-cp313-cp313-win32.whl", hash = "sha256:97d5eec30149fd3294270e889b4234023f2c69747e555a27bd708828353ab606", size = 96946 }, + { url = "https://files.pythonhosted.org/packages/b2/b7/718cd1da0884f281f95ccfa3a6cc572d30053cba64603f79d431d3c9b61b/tomli-2.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:0c95ca56fbe89e065c6ead5b593ee64b84a26fca063b5d71a1122bf26e533999", size = 107705 }, + { url = "https://files.pythonhosted.org/packages/19/94/aeafa14a52e16163008060506fcb6aa1949d13548d13752171a755c65611/tomli-2.3.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:cebc6fe843e0733ee827a282aca4999b596241195f43b4cc371d64fc6639da9e", size = 154244 }, + { url = "https://files.pythonhosted.org/packages/db/e4/1e58409aa78eefa47ccd19779fc6f36787edbe7d4cd330eeeedb33a4515b/tomli-2.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4c2ef0244c75aba9355561272009d934953817c49f47d768070c3c94355c2aa3", size = 148637 }, + { url = "https://files.pythonhosted.org/packages/26/b6/d1eccb62f665e44359226811064596dd6a366ea1f985839c566cd61525ae/tomli-2.3.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c22a8bf253bacc0cf11f35ad9808b6cb75ada2631c2d97c971122583b129afbc", size = 241925 }, + { url = "https://files.pythonhosted.org/packages/70/91/7cdab9a03e6d3d2bb11beae108da5bdc1c34bdeb06e21163482544ddcc90/tomli-2.3.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0eea8cc5c5e9f89c9b90c4896a8deefc74f518db5927d0e0e8d4a80953d774d0", size = 249045 }, + { url = "https://files.pythonhosted.org/packages/15/1b/8c26874ed1f6e4f1fcfeb868db8a794cbe9f227299402db58cfcc858766c/tomli-2.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b74a0e59ec5d15127acdabd75ea17726ac4c5178ae51b85bfe39c4f8a278e879", size = 245835 }, + { url = "https://files.pythonhosted.org/packages/fd/42/8e3c6a9a4b1a1360c1a2a39f0b972cef2cc9ebd56025168c4137192a9321/tomli-2.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b5870b50c9db823c595983571d1296a6ff3e1b88f734a4c8f6fc6188397de005", size = 253109 }, + { url = "https://files.pythonhosted.org/packages/22/0c/b4da635000a71b5f80130937eeac12e686eefb376b8dee113b4a582bba42/tomli-2.3.0-cp314-cp314-win32.whl", hash = "sha256:feb0dacc61170ed7ab602d3d972a58f14ee3ee60494292d384649a3dc38ef463", size = 97930 }, + { url = "https://files.pythonhosted.org/packages/b9/74/cb1abc870a418ae99cd5c9547d6bce30701a954e0e721821df483ef7223c/tomli-2.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:b273fcbd7fc64dc3600c098e39136522650c49bca95df2d11cf3b626422392c8", size = 107964 }, + { url = "https://files.pythonhosted.org/packages/54/78/5c46fff6432a712af9f792944f4fcd7067d8823157949f4e40c56b8b3c83/tomli-2.3.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:940d56ee0410fa17ee1f12b817b37a4d4e4dc4d27340863cc67236c74f582e77", size = 163065 }, + { url = "https://files.pythonhosted.org/packages/39/67/f85d9bd23182f45eca8939cd2bc7050e1f90c41f4a2ecbbd5963a1d1c486/tomli-2.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f85209946d1fe94416debbb88d00eb92ce9cd5266775424ff81bc959e001acaf", size = 159088 }, + { url = "https://files.pythonhosted.org/packages/26/5a/4b546a0405b9cc0659b399f12b6adb750757baf04250b148d3c5059fc4eb/tomli-2.3.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a56212bdcce682e56b0aaf79e869ba5d15a6163f88d5451cbde388d48b13f530", size = 268193 }, + { url = "https://files.pythonhosted.org/packages/42/4f/2c12a72ae22cf7b59a7fe75b3465b7aba40ea9145d026ba41cb382075b0e/tomli-2.3.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c5f3ffd1e098dfc032d4d3af5c0ac64f6d286d98bc148698356847b80fa4de1b", size = 275488 }, + { url = "https://files.pythonhosted.org/packages/92/04/a038d65dbe160c3aa5a624e93ad98111090f6804027d474ba9c37c8ae186/tomli-2.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5e01decd096b1530d97d5d85cb4dff4af2d8347bd35686654a004f8dea20fc67", size = 272669 }, + { url = "https://files.pythonhosted.org/packages/be/2f/8b7c60a9d1612a7cbc39ffcca4f21a73bf368a80fc25bccf8253e2563267/tomli-2.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:8a35dd0e643bb2610f156cca8db95d213a90015c11fee76c946aa62b7ae7e02f", size = 279709 }, + { url = "https://files.pythonhosted.org/packages/7e/46/cc36c679f09f27ded940281c38607716c86cf8ba4a518d524e349c8b4874/tomli-2.3.0-cp314-cp314t-win32.whl", hash = "sha256:a1f7f282fe248311650081faafa5f4732bdbfef5d45fe3f2e702fbc6f2d496e0", size = 107563 }, + { url = "https://files.pythonhosted.org/packages/84/ff/426ca8683cf7b753614480484f6437f568fd2fda2edbdf57a2d3d8b27a0b/tomli-2.3.0-cp314-cp314t-win_amd64.whl", hash = "sha256:70a251f8d4ba2d9ac2542eecf008b3c8a9fc5c3f9f02c56a9d7952612be2fdba", size = 119756 }, + { url = "https://files.pythonhosted.org/packages/77/b8/0135fadc89e73be292b473cb820b4f5a08197779206b33191e801feeae40/tomli-2.3.0-py3-none-any.whl", hash = "sha256:e95b1af3c5b07d9e643909b5abbec77cd9f1217e6d0bca72b0234736b9fb1f1b", size = 14408 }, ] [[package]] @@ -5248,7 +5351,7 @@ wheels = [ [[package]] name = "torch" -version = "2.8.0" +version = "2.9.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "filelock" }, @@ -5269,6 +5372,7 @@ dependencies = [ { name = "nvidia-cusparselt-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, { name = "nvidia-nccl-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, { name = "nvidia-nvjitlink-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-nvshmem-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, { name = "nvidia-nvtx-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, { name = "setuptools", marker = "python_full_version >= '3.12'" }, { name = "sympy" }, @@ -5276,61 +5380,77 @@ dependencies = [ { name = "typing-extensions" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/63/28/110f7274254f1b8476c561dada127173f994afa2b1ffc044efb773c15650/torch-2.8.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:0be92c08b44009d4131d1ff7a8060d10bafdb7ddcb7359ef8d8c5169007ea905", size = 102052793 }, - { url = "https://files.pythonhosted.org/packages/70/1c/58da560016f81c339ae14ab16c98153d51c941544ae568da3cb5b1ceb572/torch-2.8.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:89aa9ee820bb39d4d72b794345cccef106b574508dd17dbec457949678c76011", size = 888025420 }, - { url = "https://files.pythonhosted.org/packages/70/87/f69752d0dd4ba8218c390f0438130c166fa264a33b7025adb5014b92192c/torch-2.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:e8e5bf982e87e2b59d932769938b698858c64cc53753894be25629bdf5cf2f46", size = 241363614 }, - { url = "https://files.pythonhosted.org/packages/ef/d6/e6d4c57e61c2b2175d3aafbfb779926a2cfd7c32eeda7c543925dceec923/torch-2.8.0-cp310-none-macosx_11_0_arm64.whl", hash = "sha256:a3f16a58a9a800f589b26d47ee15aca3acf065546137fc2af039876135f4c760", size = 73611154 }, - { url = "https://files.pythonhosted.org/packages/8f/c4/3e7a3887eba14e815e614db70b3b529112d1513d9dae6f4d43e373360b7f/torch-2.8.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:220a06fd7af8b653c35d359dfe1aaf32f65aa85befa342629f716acb134b9710", size = 102073391 }, - { url = "https://files.pythonhosted.org/packages/5a/63/4fdc45a0304536e75a5e1b1bbfb1b56dd0e2743c48ee83ca729f7ce44162/torch-2.8.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:c12fa219f51a933d5f80eeb3a7a5d0cbe9168c0a14bbb4055f1979431660879b", size = 888063640 }, - { url = "https://files.pythonhosted.org/packages/84/57/2f64161769610cf6b1c5ed782bd8a780e18a3c9d48931319f2887fa9d0b1/torch-2.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:8c7ef765e27551b2fbfc0f41bcf270e1292d9bf79f8e0724848b1682be6e80aa", size = 241366752 }, - { url = "https://files.pythonhosted.org/packages/a4/5e/05a5c46085d9b97e928f3f037081d3d2b87fb4b4195030fc099aaec5effc/torch-2.8.0-cp311-none-macosx_11_0_arm64.whl", hash = "sha256:5ae0524688fb6707c57a530c2325e13bb0090b745ba7b4a2cd6a3ce262572916", size = 73621174 }, - { url = "https://files.pythonhosted.org/packages/49/0c/2fd4df0d83a495bb5e54dca4474c4ec5f9c62db185421563deeb5dabf609/torch-2.8.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:e2fab4153768d433f8ed9279c8133a114a034a61e77a3a104dcdf54388838705", size = 101906089 }, - { url = "https://files.pythonhosted.org/packages/99/a8/6acf48d48838fb8fe480597d98a0668c2beb02ee4755cc136de92a0a956f/torch-2.8.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:b2aca0939fb7e4d842561febbd4ffda67a8e958ff725c1c27e244e85e982173c", size = 887913624 }, - { url = "https://files.pythonhosted.org/packages/af/8a/5c87f08e3abd825c7dfecef5a0f1d9aa5df5dd0e3fd1fa2f490a8e512402/torch-2.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:2f4ac52f0130275d7517b03a33d2493bab3693c83dcfadf4f81688ea82147d2e", size = 241326087 }, - { url = "https://files.pythonhosted.org/packages/be/66/5c9a321b325aaecb92d4d1855421e3a055abd77903b7dab6575ca07796db/torch-2.8.0-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:619c2869db3ada2c0105487ba21b5008defcc472d23f8b80ed91ac4a380283b0", size = 73630478 }, - { url = "https://files.pythonhosted.org/packages/10/4e/469ced5a0603245d6a19a556e9053300033f9c5baccf43a3d25ba73e189e/torch-2.8.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:2b2f96814e0345f5a5aed9bf9734efa913678ed19caf6dc2cddb7930672d6128", size = 101936856 }, - { url = "https://files.pythonhosted.org/packages/16/82/3948e54c01b2109238357c6f86242e6ecbf0c63a1af46906772902f82057/torch-2.8.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:65616ca8ec6f43245e1f5f296603e33923f4c30f93d65e103d9e50c25b35150b", size = 887922844 }, - { url = "https://files.pythonhosted.org/packages/e3/54/941ea0a860f2717d86a811adf0c2cd01b3983bdd460d0803053c4e0b8649/torch-2.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:659df54119ae03e83a800addc125856effda88b016dfc54d9f65215c3975be16", size = 241330968 }, - { url = "https://files.pythonhosted.org/packages/de/69/8b7b13bba430f5e21d77708b616f767683629fc4f8037564a177d20f90ed/torch-2.8.0-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:1a62a1ec4b0498930e2543535cf70b1bef8c777713de7ceb84cd79115f553767", size = 73915128 }, - { url = "https://files.pythonhosted.org/packages/15/0e/8a800e093b7f7430dbaefa80075aee9158ec22e4c4fc3c1a66e4fb96cb4f/torch-2.8.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:83c13411a26fac3d101fe8035a6b0476ae606deb8688e904e796a3534c197def", size = 102020139 }, - { url = "https://files.pythonhosted.org/packages/4a/15/5e488ca0bc6162c86a33b58642bc577c84ded17c7b72d97e49b5833e2d73/torch-2.8.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:8f0a9d617a66509ded240add3754e462430a6c1fc5589f86c17b433dd808f97a", size = 887990692 }, - { url = "https://files.pythonhosted.org/packages/b4/a8/6a04e4b54472fc5dba7ca2341ab219e529f3c07b6941059fbf18dccac31f/torch-2.8.0-cp313-cp313t-win_amd64.whl", hash = "sha256:a7242b86f42be98ac674b88a4988643b9bc6145437ec8f048fea23f72feb5eca", size = 241603453 }, - { url = "https://files.pythonhosted.org/packages/04/6e/650bb7f28f771af0cb791b02348db8b7f5f64f40f6829ee82aa6ce99aabe/torch-2.8.0-cp313-none-macosx_11_0_arm64.whl", hash = "sha256:7b677e17f5a3e69fdef7eb3b9da72622f8d322692930297e4ccb52fefc6c8211", size = 73632395 }, + { url = "https://files.pythonhosted.org/packages/bb/86/245c240d2138c17ed572c943c289056c2721abab70810d772c6bf5495b28/torch-2.9.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:030bbfe367379ae6a4ae4042b6c44da25383343b8b3c68abaa9c7231efbaf2dd", size = 104213554 }, + { url = "https://files.pythonhosted.org/packages/58/1d/fd1e88ae0948825efcab7dd66d12bec23f05d4d38ed81573c8d453c14c06/torch-2.9.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:51cb63902182a78e90886e8068befd8ea102af4b00e420263591a3d70c7d3c6c", size = 899795167 }, + { url = "https://files.pythonhosted.org/packages/63/5a/496197b45c14982bef4e079b24c61dc108e3ab0d0cc9718dba9f54f45a46/torch-2.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:3f6aad4d2f0ee2248bac25339d74858ff846c3969b27d14ac235821f055af83d", size = 109310314 }, + { url = "https://files.pythonhosted.org/packages/58/b0/2b4e647b0fc706e88eb6c253d05511865578f5f67b55fad639bf3272a4a1/torch-2.9.0-cp310-none-macosx_11_0_arm64.whl", hash = "sha256:413e1654c9203733138858780e184d9fc59442f0b3b209e16f39354eb893db9b", size = 74452019 }, + { url = "https://files.pythonhosted.org/packages/58/fe/334225e6330e672b36aef23d77451fa906ea12881570c08638a91331a212/torch-2.9.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:c596708b5105d0b199215acf0c9be7c1db5f1680d88eddadf4b75a299259a677", size = 104230578 }, + { url = "https://files.pythonhosted.org/packages/05/cc/49566caaa218872ec9a2912456f470ff92649894a4bc2e5274aa9ef87c4a/torch-2.9.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:51de31219c97c51cf4bf2be94d622e3deb5dcc526c6dc00e97c17eaec0fc1d67", size = 899815990 }, + { url = "https://files.pythonhosted.org/packages/74/25/e9ab21d5925b642d008f139d4a3c9664fc9ee1faafca22913c080cc4c0a5/torch-2.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:dd515c70059afd95f48b8192733764c08ca37a1d19803af6401b5ecad7c8676e", size = 109313698 }, + { url = "https://files.pythonhosted.org/packages/b3/b7/205ef3e94de636feffd64b28bb59a0dfac0771221201b9871acf9236f5ca/torch-2.9.0-cp311-none-macosx_11_0_arm64.whl", hash = "sha256:614a185e4986326d526a91210c8fc1397e76e8cfafa78baf6296a790e53a9eec", size = 74463678 }, + { url = "https://files.pythonhosted.org/packages/d1/d3/3985739f3b8e88675127bf70f82b3a48ae083e39cda56305dbd90398fec0/torch-2.9.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:e5f7af1dc4c0a7c4a260c2534f41ddaf209714f7c89145e644c44712fbd6b642", size = 104107898 }, + { url = "https://files.pythonhosted.org/packages/a5/4b/f4bb2e6c25d0272f798cd6d7a04ed315da76cec68c602d87040c7847287f/torch-2.9.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:01cff95ecd9a212ea2f141db28acccdceb6a4c54f64e6c51091146f5e2a772c6", size = 899738273 }, + { url = "https://files.pythonhosted.org/packages/66/11/c1c5ba6691cda6279087c35bd626536e4fd29521fe740abf5008377a9a02/torch-2.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:4582b162f541651f0cb184d3e291c05c2f556c7117c64a9873e2ee158d40062b", size = 109280887 }, + { url = "https://files.pythonhosted.org/packages/dd/5f/b85bd8c05312d71de9402bf5868d217c38827cfd09d8f8514e5be128a52b/torch-2.9.0-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:33f58e9a102a91259af289d50525c30323b5c9ae1d31322b6447c0814da68695", size = 74478983 }, + { url = "https://files.pythonhosted.org/packages/c2/1c/90eb13833cdf4969ea9707586d7b57095c3b6e2b223a7256bf111689bcb8/torch-2.9.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:c30a17fc83eeab346913e237c64b15b5ba6407fff812f6c541e322e19bc9ea0e", size = 104111330 }, + { url = "https://files.pythonhosted.org/packages/0e/21/2254c54b8d523592c25ef4434769aa23e29b1e6bf5f4c0ad9e27bf442927/torch-2.9.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:8f25033b8667b57857dfd01458fbf2a9e6a6df1f8def23aef0dc46292f6aa642", size = 899750243 }, + { url = "https://files.pythonhosted.org/packages/b7/a5/5cb94fa4fd1e78223455c23c200f30f6dc10c6d4a2bcc8f6e7f2a2588370/torch-2.9.0-cp313-cp313-win_amd64.whl", hash = "sha256:d037f1b4ffd25013be4a7bf3651a0a910c68554956c7b2c92ebe87c76475dece", size = 109284513 }, + { url = "https://files.pythonhosted.org/packages/66/e8/fc414d8656250ee46120b44836ffbb3266343db424b3e18ca79ebbf69d4f/torch-2.9.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e4e5b5cba837a2a8d1a497ba9a58dae46fa392593eaa13b871c42f71847503a5", size = 74830362 }, + { url = "https://files.pythonhosted.org/packages/ed/5f/9474c98fc5ae0cd04b9466035428cd360e6611a86b8352a0fc2fa504acdc/torch-2.9.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:64693568f5dc4dbd5f880a478b1cea0201cc6b510d91d1bc54fea86ac5d1a637", size = 104144940 }, + { url = "https://files.pythonhosted.org/packages/2d/5a/8e0c1cf57830172c109d4bd6be2708cabeaf550983eee7029291322447a0/torch-2.9.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:f8ed31ddd7d10bfb3fbe0b9fe01b1243577f13d75e6f4a0839a283915ce3791e", size = 899744054 }, + { url = "https://files.pythonhosted.org/packages/6d/28/82c28b30fcb4b7c9cdd995763d18bbb830d6521356712faebbad92ffa61d/torch-2.9.0-cp313-cp313t-win_amd64.whl", hash = "sha256:eff527d4e4846e6f70d2afd8058b73825761203d66576a7e04ea2ecfebcb4ab8", size = 109517546 }, + { url = "https://files.pythonhosted.org/packages/ff/c3/a91f96ec74347fa5fd24453fa514bc61c61ecc79196fa760b012a1873d96/torch-2.9.0-cp313-none-macosx_11_0_arm64.whl", hash = "sha256:f8877779cf56d1ce431a7636703bdb13307f5960bb1af49716d8b179225e0e6a", size = 74480732 }, + { url = "https://files.pythonhosted.org/packages/5c/73/9f70af34b334a7e0ef496ceec96b7ec767bd778ea35385ce6f77557534d1/torch-2.9.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:7e614fae699838038d888729f82b687c03413c5989ce2a9481f9a7e7a396e0bb", size = 74433037 }, + { url = "https://files.pythonhosted.org/packages/b7/84/37cf88625901934c97109e583ecc21777d21c6f54cda97a7e5bbad1ee2f2/torch-2.9.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:dfb5b8cd310ba3436c7e14e8b7833ef658cf3045e50d2bdaed23c8fc517065eb", size = 104116482 }, + { url = "https://files.pythonhosted.org/packages/56/8e/ca8b17866943a8d4f4664d402ea84210aa274588b4c5d89918f5caa24eec/torch-2.9.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:b3d29524993a478e46f5d598b249cd824b7ed98d7fba538bd9c4cde6c803948f", size = 899746916 }, + { url = "https://files.pythonhosted.org/packages/43/65/3b17c0fbbdab6501c5b320a52a648628d0d44e7379f64e27d9eef701b6bf/torch-2.9.0-cp314-cp314-win_amd64.whl", hash = "sha256:71c7578984f5ec0eb645eb4816ac8435fcf3e3e2ae1901bcd2f519a9cafb5125", size = 109275151 }, + { url = "https://files.pythonhosted.org/packages/83/36/74f8c051f785500396e42f93542422422dfd874a174f21f8d955d36e5d64/torch-2.9.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:71d9309aee457bbe0b164bce2111cd911c4ed4e847e65d5077dbbcd3aba6befc", size = 74823353 }, + { url = "https://files.pythonhosted.org/packages/62/51/dc3b4e2f9ba98ae27238f0153ca098bf9340b2dafcc67fde645d496dfc2a/torch-2.9.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:c08fb654d783899e204a32cca758a7ce8a45b2d78eeb89517cc937088316f78e", size = 104140340 }, + { url = "https://files.pythonhosted.org/packages/c0/8d/b00657f8141ac16af7bb6cda2e67de18499a3263b78d516b9a93fcbc98e3/torch-2.9.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:ec8feb0099b2daa5728fbc7abb0b05730fd97e0f359ff8bda09865aaa7bd7d4b", size = 899731750 }, + { url = "https://files.pythonhosted.org/packages/fc/29/bd361e0cbb2c79ce6450f42643aaf6919956f89923a50571b0ebfe92d142/torch-2.9.0-cp314-cp314t-win_amd64.whl", hash = "sha256:695ba920f234ad4170c9c50e28d56c848432f8f530e6bc7f88fcb15ddf338e75", size = 109503850 }, ] [[package]] name = "torchaudio" -version = "2.8.0" +version = "2.9.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "torch" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/30/81/92d34ff136b17ddda872f6d8149f2ca927ad53a37ae26d02cb5f66435772/torchaudio-2.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c2f44cf279f673cfcdd8f576c349eee8bedf8caab351a5dd78b32970cc34a212", size = 1852315 }, - { url = "https://files.pythonhosted.org/packages/95/c8/e46c22a3c059844bb0f1b670317c9e538b51728558326dcd9e5fffbf2ec2/torchaudio-2.8.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:d3c1b85b26a09832d139f6d6da6b66caeb51d2e16e08f8587665c44a9e1aa8f9", size = 1685620 }, - { url = "https://files.pythonhosted.org/packages/d8/f5/69db76b564263f22c1788cc298ab1c4e2391a79fa8ba7b4a3e76d945292a/torchaudio-2.8.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:58f912bf2d289c709b42a55475b2b483becec79d9affb7684b606bb1f896b434", size = 4001714 }, - { url = "https://files.pythonhosted.org/packages/f1/18/63adf60a9f0592c6dcea2b37735990881edbbe671e3af3ae79f2da816a50/torchaudio-2.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:4e2b4712ad6d7547ce82d84567c8c29d5e2966ff1d31d94e1644024fb4b2649f", size = 2500313 }, - { url = "https://files.pythonhosted.org/packages/dd/bf/6b01ef3defb8d0a772c863588711e9b2b011c27d6b37c1b9d15a359c8442/torchaudio-2.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c9276857d241c6de257af765c0f51fc011af38cb725401495121b280913007cf", size = 1859094 }, - { url = "https://files.pythonhosted.org/packages/75/ca/da5d0a3bb7d114a8b590ecce14859ea0a05102bb4de68cdd1ed7a90634d6/torchaudio-2.8.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:4573c6042950c20278e3608a9a38050ba0bc72e0049e1bbfd249caf859a8029b", size = 1692033 }, - { url = "https://files.pythonhosted.org/packages/b6/ef/62ac736d8f906cc414181050e08a495a637dab985186c34bd76ea37efbc0/torchaudio-2.8.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:776c0b4ba84b9e3ddf6304b9c47cd63549d7896a6f3d5184ece074cc3d76ed6b", size = 4011716 }, - { url = "https://files.pythonhosted.org/packages/14/86/015337c8434abc604b8680371df783f66c421a7f211cbe40a374b0540b6d/torchaudio-2.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:078105bf80f725c0215a0bebac8cb2fb1b3993ab32bdc3fcd50145a5b4127001", size = 2505194 }, - { url = "https://files.pythonhosted.org/packages/ac/cc/c2e2a3eb6ee956f73c68541e439916f8146170ea9cc61e72adea5c995312/torchaudio-2.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ddef94bf181e6447cbb05f38beaca8f6c5bb8d2b9ddced1aa3452025b9fc70d3", size = 1856736 }, - { url = "https://files.pythonhosted.org/packages/c7/0d/24dad878784f1edd62862f27173781669f0c71eb46368636787d1e364188/torchaudio-2.8.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:862e2e40bf09d865e5df080a84c1a39bbcef40e43140f4b1737eb3a389d3b38f", size = 1692930 }, - { url = "https://files.pythonhosted.org/packages/c2/a6/84d80f34472503e9eb82245d7df501c59602d75d7360e717fb9b84f91c5e/torchaudio-2.8.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:93a8583f280fe83ba021aa713319381ea71362cc87b67ee38e97a43cb2254aee", size = 4014607 }, - { url = "https://files.pythonhosted.org/packages/43/ab/96ad33afa320738a7cfb4b51ba97e2f3cfb1e04ae3115d5057655103ba4f/torchaudio-2.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:4b82cacd1b8ccd543b1149d8cab257a40dfda8119023d2e3a96c66349c84bffb", size = 2499890 }, - { url = "https://files.pythonhosted.org/packages/3b/ea/2a68259c4dbb5fe44ebfdcfa40b115010d8c677221a7ef0f5577f3c4f5f1/torchaudio-2.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f851d32e94ca05e470f0c60e25726ec1e0eb71cb2ca5a0206b7fd03272ccc3c8", size = 1857045 }, - { url = "https://files.pythonhosted.org/packages/0d/a3/1c79a8ef29fe403b83bdfc033db852bc2a888b80c406325e5c6fb37a7f2d/torchaudio-2.8.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:09535a9b727c0793cd07c1ace99f3f353626281bcc3e30c2f2314e3ebc9d3f96", size = 1692755 }, - { url = "https://files.pythonhosted.org/packages/49/df/61941198e9ac6bcebfdd57e1836e4f3c23409308e3d8d7458f0198a6a366/torchaudio-2.8.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:d2a85b124494736241884372fe1c6dd8c15e9bc1931bd325838c5c00238c7378", size = 4013897 }, - { url = "https://files.pythonhosted.org/packages/c3/ab/7175d35a4bbc4a465a9f1388571842f16eb6dec5069d7ea9c8c2d7b5b401/torchaudio-2.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:c1b5139c840367a7855a062a06688a416619f6fd2ca46d9b9299b49a7d133dfd", size = 2500085 }, - { url = "https://files.pythonhosted.org/packages/34/1a/69b9f8349d9d57953d5e7e445075cbf74000173fb5f5d5d9e9d59415fc63/torchaudio-2.8.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:68df9c9068984edff8065c2b6656725e6114fe89281b0cf122c7505305fc98a4", size = 1935600 }, - { url = "https://files.pythonhosted.org/packages/71/76/40fec21b65bccfdc5c8cdb9d511033ab07a7ad4b05f0a5b07f85c68279fc/torchaudio-2.8.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:1951f10ed092f2dda57634f6a3950ef21c9d9352551aa84a9fccd51bbda18095", size = 1704199 }, - { url = "https://files.pythonhosted.org/packages/8e/53/95c3363413c2f2009f805144160b093a385f641224465fbcd717449c71fb/torchaudio-2.8.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:4f7d97494698d98854129349b12061e8c3398d33bd84c929fa9aed5fd1389f73", size = 4020596 }, - { url = "https://files.pythonhosted.org/packages/52/27/7fc2d7435af044ffbe0b9b8e98d99eac096d43f128a5cde23c04825d5dcf/torchaudio-2.8.0-cp313-cp313t-win_amd64.whl", hash = "sha256:d4a715d09ac28c920d031ee1e60ecbc91e8a5079ad8c61c0277e658436c821a6", size = 2549553 }, + { url = "https://files.pythonhosted.org/packages/78/aa/7fce684dc0e21f8ea3ecf4a9f37253f8fa0b51aa0973202b58f33b9dc031/torchaudio-2.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:214d2e8bec2b204ac3f552f3dceae51550e06a91c5863d5dc341d81691ef655e", size = 806922 }, + { url = "https://files.pythonhosted.org/packages/0b/c2/212181b1df762487462b3a092f6a9ae6ba87df02df71bb2121c100b13b8d/torchaudio-2.9.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:1e84e45f74bf5b208b5ce59b36f26ec1e5f63596542c3ebee6edeadf85e73563", size = 473802 }, + { url = "https://files.pythonhosted.org/packages/39/27/75184741da9aa1e94ec136319781e1275a560d1c311a293cc22aba747863/torchaudio-2.9.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:905f2c916e392b6dde375c002abe98f6fc64705fdf1192c90a6df2de235305f3", size = 2055464 }, + { url = "https://files.pythonhosted.org/packages/43/af/f12349d7cb325b9b36452192953eb8c4ca9a6c28c8335c2d2f5e576be7f3/torchaudio-2.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:4ed556da9de16f69ccbe804df510ae8fefdf995cbdc2fcf26ea7532d25463326", size = 663878 }, + { url = "https://files.pythonhosted.org/packages/d5/a2/7696b9579ad0c40b78ce2774fb24875c43257f3d0d24540e1cfa946c13b4/torchaudio-2.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:662eb49ab25e1a2b7367bb072a8ad05c8a4b650ebbe7090a5af1a1eb1d40767c", size = 808368 }, + { url = "https://files.pythonhosted.org/packages/55/1a/48d528cae6050b9a5f07c1c942b547143237e9f080f4a2ccb80ba88486df/torchaudio-2.9.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:914f1408142bdeda1ca9f834dd04967625fccc75893bd1504a018a13a04f1b66", size = 475720 }, + { url = "https://files.pythonhosted.org/packages/f0/41/7aba77bc89d06df993c1519b66b7e0b09661d297d0eb8c044ab2c5af665f/torchaudio-2.9.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:86b15ce1d74814d5ca14bfac0d3b33f325c8cac4a6f09dcc5b82748133a96792", size = 2058688 }, + { url = "https://files.pythonhosted.org/packages/96/64/93944c24d7ec76dff3315f9aaf382e86d09fa2c865942c3d6b48666e5b1d/torchaudio-2.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:840487d748128ded45bd65b213b55db701ad047544e77ae3c57ea48f55623a77", size = 664692 }, + { url = "https://files.pythonhosted.org/packages/b7/63/3c0ede3aa3d19a8a6698ddd107fa88660549360b51bf8ce2717cd498d800/torchaudio-2.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ab4cbcccfd873b0fb41fcb39c9869e59ef84bb95b093f6f58e2d05172a7500d2", size = 809116 }, + { url = "https://files.pythonhosted.org/packages/be/d5/25e58745defe9d05893d3cba5c0e1a76aeaac503ac5ec4d9f83c871df71c/torchaudio-2.9.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:7f93388b6e536c14d6015b6f75277a8b45efc532f61b35adc1ed06c98a86003e", size = 476020 }, + { url = "https://files.pythonhosted.org/packages/f0/9c/58b8b49dfba2ae85e41ca86b0c52de45bbbea01987490de219c99c523a58/torchaudio-2.9.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:508318a2130b40ad51378f90caf8727a4bd3ac2b296f2b90c900b44e6068a940", size = 2059901 }, + { url = "https://files.pythonhosted.org/packages/d7/eb/58b05f75d12f69ccc460893a20c999da082e063082120ed06e05cca3a053/torchaudio-2.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:82117e3a605f2959dc09b4cd8a11178d6e92727d5f85e5d4f9fe47502f84ee96", size = 665350 }, + { url = "https://files.pythonhosted.org/packages/6c/66/974371d4e4042d186931b72365817d9d3a509f2bc570888a48612448c060/torchaudio-2.9.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:5549c25db4c2da306e179e9aa99980e7f5b1826a8d2d7de08125f3943a5620b2", size = 809149 }, + { url = "https://files.pythonhosted.org/packages/09/61/8f7b875a2d879666f2f121e458817703e5499988a86105d2a25afecb9987/torchaudio-2.9.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:1eb0d1dac8cefbc4a54afb21aac72a1c25a91f73e9c3bd85f6684930a4a1be5d", size = 475699 }, + { url = "https://files.pythonhosted.org/packages/26/db/10ba200f90b76f7b859f46b5ba30cdded69f71bcb0fe3c59bb215532cd2b/torchaudio-2.9.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:266d304dd4ed738a10148b020e3d066e81272ee851f6f92193fe549df96af868", size = 2060349 }, + { url = "https://files.pythonhosted.org/packages/be/53/5f9adbea55e48f91532ee4f041283900939ee5cb6bc1395587214e67a629/torchaudio-2.9.0-cp313-cp313-win_amd64.whl", hash = "sha256:7d3926129389d934aa048bd6c6f68fbf3ef26828ebbbbeac99794ea00e90dc1c", size = 665310 }, + { url = "https://files.pythonhosted.org/packages/e3/41/88b989aab1e11134d858350196fcf3afd4c2a6821d74efb3c1b9ab23b8cf/torchaudio-2.9.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:967d664477fb91dffad82ef64ea3695801c0cc35304baec71be875b569440872", size = 813491 }, + { url = "https://files.pythonhosted.org/packages/1a/c1/8d0481fc921cb72d6cadbacd338fa71db0052e8fdb1bf33127c694bbf257/torchaudio-2.9.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:276871d6f5fed5268a87c5da303a13ca2e06b9d29a4c44663b960f0a2e2f46d7", size = 477749 }, + { url = "https://files.pythonhosted.org/packages/cf/d3/d085cd76413b9f3f792e61933235d982caf5cdbdf60f0e4fdae71879becc/torchaudio-2.9.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:3d5657d929d6ca07b08cfa005988f2ea8caacf9af42f20bc7eff10f88812ce30", size = 2062165 }, + { url = "https://files.pythonhosted.org/packages/f2/41/d9876f5b19b4b2f98a6131d1a98ee6d5d8f707c01311bbba7cc3bb02f4bf/torchaudio-2.9.0-cp313-cp313t-win_amd64.whl", hash = "sha256:3fe9cac0c2ee713e07f8c88d09528d55e0fa74987b0122e27911dfb720f39054", size = 669260 }, + { url = "https://files.pythonhosted.org/packages/97/ad/db50c49d73d1904152bbaaaa281e03a41ec519dd6a9df48cc69ea5cd48b9/torchaudio-2.9.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:3fa41447a21103fcde930b4ad2bd2634565a0becff1a5425535b4f0116c0d5df", size = 810532 }, + { url = "https://files.pythonhosted.org/packages/a8/00/aa8ed83a169a87af72d6cdc17e0350f418b3cba3bd7397b0cca873274789/torchaudio-2.9.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:69f46f21bd67e90ade33a7d0f0cf98270cd61b98f5f8249d3893be0a16b3e31f", size = 475864 }, + { url = "https://files.pythonhosted.org/packages/4b/bb/7ca64ed0556afa08d3a7a47c887ee9b1c4f3eebd193baf47505b6fac479c/torchaudio-2.9.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:631b0f43564a25e27e615b217454c334f52162679f39ae10b9fa7562ed587dfc", size = 2060360 }, + { url = "https://files.pythonhosted.org/packages/63/13/4407b79ddedc9ea95d88fa54c3758df21f0117683fceba4bacd98ceaa772/torchaudio-2.9.0-cp314-cp314-win_amd64.whl", hash = "sha256:ed6df9f14431e13498b984dc87df1aabb2156b9ce0ce7268ce4a61650197310a", size = 665048 }, + { url = "https://files.pythonhosted.org/packages/7d/1a/d3cd6b67b5c68ff4211be923978d1d7c10ea2f44f826d4cd15b775f52c11/torchaudio-2.9.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:93358d8f2f24969ba3f368f4eec33295df830af54836c7fd3336740228f9af16", size = 813499 }, + { url = "https://files.pythonhosted.org/packages/ab/65/a35a182519b40dcd2cedaf5fdcac6f724ae2451c534dfcece6ff5f85f983/torchaudio-2.9.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:742143d9d62769bc4b9a2977ca4f4720e0a5e922bdc5df585c155e0a1f545461", size = 477752 }, + { url = "https://files.pythonhosted.org/packages/6f/1c/30272b71ae08817eaca00bb856ebef25dd44041329579903c1915b57f0c9/torchaudio-2.9.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:0a234634e1142fb2652c49e935a98b4d9656fd0af9e4aa14b1b05a80c3cf8e78", size = 2062173 }, + { url = "https://files.pythonhosted.org/packages/b9/d6/d007f6bc55a16a86e64e9bba295b90485011cc6a113d8f56b503b4f34a7d/torchaudio-2.9.0-cp314-cp314t-win_amd64.whl", hash = "sha256:cbf5d6da8fd2ed545c78218b39fd6aacaa4dd5e265c5f85b248a2fac223f0bd6", size = 669272 }, ] [[package]] name = "torchvision" -version = "0.23.0" +version = "0.24.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy" }, @@ -5338,26 +5458,34 @@ dependencies = [ { name = "torch" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/4d/49/5ad5c3ff4920be0adee9eb4339b4fb3b023a0fc55b9ed8dbc73df92946b8/torchvision-0.23.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7266871daca00ad46d1c073e55d972179d12a58fa5c9adec9a3db9bbed71284a", size = 1856885 }, - { url = "https://files.pythonhosted.org/packages/25/44/ddd56d1637bac42a8c5da2c8c440d8a28c431f996dd9790f32dd9a96ca6e/torchvision-0.23.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:31c583ba27426a3a04eca8c05450524105c1564db41be6632f7536ef405a6de2", size = 2394251 }, - { url = "https://files.pythonhosted.org/packages/93/f3/3cdf55bbf0f737304d997561c34ab0176222e0496b6743b0feab5995182c/torchvision-0.23.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:3932bf67256f2d095ce90a9f826f6033694c818856f4bb26794cf2ce64253e53", size = 8627497 }, - { url = "https://files.pythonhosted.org/packages/97/90/02afe57c3ef4284c5cf89d3b7ae203829b3a981f72b93a7dd2a3fd2c83c1/torchvision-0.23.0-cp310-cp310-win_amd64.whl", hash = "sha256:83ee5bf827d61a8af14620c0a61d8608558638ac9c3bac8adb7b27138e2147d1", size = 1600760 }, - { url = "https://files.pythonhosted.org/packages/f0/d7/15d3d7bd8d0239211b21673d1bac7bc345a4ad904a8e25bb3fd8a9cf1fbc/torchvision-0.23.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:49aa20e21f0c2bd458c71d7b449776cbd5f16693dd5807195a820612b8a229b7", size = 1856884 }, - { url = "https://files.pythonhosted.org/packages/dd/14/7b44fe766b7d11e064c539d92a172fa9689a53b69029e24f2f1f51e7dc56/torchvision-0.23.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:01dc33ee24c79148aee7cdbcf34ae8a3c9da1674a591e781577b716d233b1fa6", size = 2395543 }, - { url = "https://files.pythonhosted.org/packages/79/9c/fcb09aff941c8147d9e6aa6c8f67412a05622b0c750bcf796be4c85a58d4/torchvision-0.23.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:35c27941831b653f5101edfe62c03d196c13f32139310519e8228f35eae0e96a", size = 8628388 }, - { url = "https://files.pythonhosted.org/packages/93/40/3415d890eb357b25a8e0a215d32365a88ecc75a283f75c4e919024b22d97/torchvision-0.23.0-cp311-cp311-win_amd64.whl", hash = "sha256:09bfde260e7963a15b80c9e442faa9f021c7e7f877ac0a36ca6561b367185013", size = 1600741 }, - { url = "https://files.pythonhosted.org/packages/df/1d/0ea0b34bde92a86d42620f29baa6dcbb5c2fc85990316df5cb8f7abb8ea2/torchvision-0.23.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e0e2c04a91403e8dd3af9756c6a024a1d9c0ed9c0d592a8314ded8f4fe30d440", size = 1856885 }, - { url = "https://files.pythonhosted.org/packages/e2/00/2f6454decc0cd67158c7890364e446aad4b91797087a57a78e72e1a8f8bc/torchvision-0.23.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:6dd7c4d329a0e03157803031bc856220c6155ef08c26d4f5bbac938acecf0948", size = 2396614 }, - { url = "https://files.pythonhosted.org/packages/e4/b5/3e580dcbc16f39a324f3dd71b90edbf02a42548ad44d2b4893cc92b1194b/torchvision-0.23.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:4e7d31c43bc7cbecbb1a5652ac0106b436aa66e26437585fc2c4b2cf04d6014c", size = 8627108 }, - { url = "https://files.pythonhosted.org/packages/82/c1/c2fe6d61e110a8d0de2f94276899a2324a8f1e6aee559eb6b4629ab27466/torchvision-0.23.0-cp312-cp312-win_amd64.whl", hash = "sha256:a2e45272abe7b8bf0d06c405e78521b5757be1bd0ed7e5cd78120f7fdd4cbf35", size = 1600723 }, - { url = "https://files.pythonhosted.org/packages/91/37/45a5b9407a7900f71d61b2b2f62db4b7c632debca397f205fdcacb502780/torchvision-0.23.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1c37e325e09a184b730c3ef51424f383ec5745378dc0eca244520aca29722600", size = 1856886 }, - { url = "https://files.pythonhosted.org/packages/ac/da/a06c60fc84fc849377cf035d3b3e9a1c896d52dbad493b963c0f1cdd74d0/torchvision-0.23.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:2f7fd6c15f3697e80627b77934f77705f3bc0e98278b989b2655de01f6903e1d", size = 2353112 }, - { url = "https://files.pythonhosted.org/packages/a0/27/5ce65ba5c9d3b7d2ccdd79892ab86a2f87ac2ca6638f04bb0280321f1a9c/torchvision-0.23.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:a76fafe113b2977be3a21bf78f115438c1f88631d7a87203acb3dd6ae55889e6", size = 8627658 }, - { url = "https://files.pythonhosted.org/packages/1f/e4/028a27b60aa578a2fa99d9d7334ff1871bb17008693ea055a2fdee96da0d/torchvision-0.23.0-cp313-cp313-win_amd64.whl", hash = "sha256:07d069cb29691ff566e3b7f11f20d91044f079e1dbdc9d72e0655899a9b06938", size = 1600749 }, - { url = "https://files.pythonhosted.org/packages/05/35/72f91ad9ac7c19a849dedf083d347dc1123f0adeb401f53974f84f1d04c8/torchvision-0.23.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:2df618e1143805a7673aaf82cb5720dd9112d4e771983156aaf2ffff692eebf9", size = 2047192 }, - { url = "https://files.pythonhosted.org/packages/1d/9d/406cea60a9eb9882145bcd62a184ee61e823e8e1d550cdc3c3ea866a9445/torchvision-0.23.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:2a3299d2b1d5a7aed2d3b6ffb69c672ca8830671967eb1cee1497bacd82fe47b", size = 2359295 }, - { url = "https://files.pythonhosted.org/packages/2b/f4/34662f71a70fa1e59de99772142f22257ca750de05ccb400b8d2e3809c1d/torchvision-0.23.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:76bc4c0b63d5114aa81281390f8472a12a6a35ce9906e67ea6044e5af4cab60c", size = 8800474 }, - { url = "https://files.pythonhosted.org/packages/6e/f5/b5a2d841a8d228b5dbda6d524704408e19e7ca6b7bb0f24490e081da1fa1/torchvision-0.23.0-cp313-cp313t-win_amd64.whl", hash = "sha256:b9e2dabf0da9c8aa9ea241afb63a8f3e98489e706b22ac3f30416a1be377153b", size = 1527667 }, + { url = "https://files.pythonhosted.org/packages/63/5b/1404eeab00819df71a30e916c2081654366741f7838fcc4fff86b7bd9e7e/torchvision-0.24.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5e8d5e667deff87bd66d26df6d225f46224bb0782d4f3f8f5d2f3068b5fd4492", size = 1891723 }, + { url = "https://files.pythonhosted.org/packages/88/e3/1b003ecd52bd721f8304aeb66691edfbc2002747ec83d36188ad6abab506/torchvision-0.24.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:a110a51c75e89807a8382b0d8034f5e180fb9319570be3389ffd3d4ac4fd57a9", size = 2418988 }, + { url = "https://files.pythonhosted.org/packages/56/2e/3c19a35e62da0f606baf8f6e2ceeab1eb66aaa2f84c6528538b06b416d54/torchvision-0.24.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:81d5b12a6df1bb2cc8bdbad837b637d6ea446f2866e6d94f1b5d478856331be3", size = 8046769 }, + { url = "https://files.pythonhosted.org/packages/e0/1d/e7ab614a1ace820a2366eab1532679fbe81bd9501ffd6a1b7be14936366d/torchvision-0.24.0-cp310-cp310-win_amd64.whl", hash = "sha256:0839dbb305d34671f5a64f558782095134b04bbeff8b90f11eb80515d7d50092", size = 3686529 }, + { url = "https://files.pythonhosted.org/packages/a3/17/54ed2ec6944ea972b461a86424c8c7f98835982c90cbc45bf59bd962863a/torchvision-0.24.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f771cf918351ad509a28488be475f3e9cc71a750d6b1467842bfb64863a5e986", size = 1891719 }, + { url = "https://files.pythonhosted.org/packages/f8/07/0cd6776eee784742ad3cb2bfd3295383d84cb2f9e87386119333d1587f0f/torchvision-0.24.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:bbd63bf4ebff84c48c50123eba90526cc9f794fe45bc9f5dd07cec19e8c62bce", size = 2420513 }, + { url = "https://files.pythonhosted.org/packages/1a/f4/6026c08011ddcefcbc14161c5aa9dce55c35c6b045e04ef0952e88bf4594/torchvision-0.24.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:78fe414b3bb6dbf7e6f6da6f733ba96881f6b29a9b997228de7c5f603e5ed940", size = 8048018 }, + { url = "https://files.pythonhosted.org/packages/2f/b4/362b4e67ed87cee0fb4f8f0363a852eaeef527968bf62c07ed56f764d729/torchvision-0.24.0-cp311-cp311-win_amd64.whl", hash = "sha256:629584b94e52f32a6278f2a35d85eeaae95fcc38730fcb765064f26c3c96df5d", size = 4027686 }, + { url = "https://files.pythonhosted.org/packages/47/ef/81e4e69e02e2c4650b30e8c11c8974f946682a30e0ab7e9803a831beff76/torchvision-0.24.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c61d40bcd2e2451e932902a702ad495ba1ec6f279e90b1e15cef2bb55dc911e2", size = 1891726 }, + { url = "https://files.pythonhosted.org/packages/00/7b/e3809b3302caea9a12c13f3adebe4fef127188438e719fd6c8dc93db1da6/torchvision-0.24.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:b0531d1483fc322d7da0d83be52f0df860a75114ab87dbeeb9de765feaeda843", size = 2419495 }, + { url = "https://files.pythonhosted.org/packages/7e/e6/7324ead6793075a8c75c56abeed1236d1750de16a5613cfe2ddad164a92a/torchvision-0.24.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:26b9dd9c083f8e5f7ac827de6d5b88c615d9c582dc87666770fbdf16887e4c25", size = 8050480 }, + { url = "https://files.pythonhosted.org/packages/3e/ad/3c56fcd2a0d6e8afa80e115b5ade4302232ec99655220a51d05709819523/torchvision-0.24.0-cp312-cp312-win_amd64.whl", hash = "sha256:060b7c50ed4b3fb0316b08e2e31bfd874ec2f63ef5ae02f81e54341ca4e88703", size = 4292225 }, + { url = "https://files.pythonhosted.org/packages/4f/b5/b2008e4b77a8d6aada828dd0f6a438d8f94befa23fdd2d62fa0ac6e60113/torchvision-0.24.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:84d79cfc6457310107ce4d712de7a3d388b24484bc9aeded4a76d8f8e3a2813d", size = 1891722 }, + { url = "https://files.pythonhosted.org/packages/8f/02/e2f6b0ff93ca4db5751ac9c5be43f13d5e53d9e9412324f464dca1775027/torchvision-0.24.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:fec12a269cf80f6b0b71471c8d498cd3bdd9d8e892c425bf39fecb604852c3b0", size = 2371478 }, + { url = "https://files.pythonhosted.org/packages/77/85/42e5fc4f716ec7b73cf1f32eeb5c77961be4d4054b26cd6a5ff97f20c966/torchvision-0.24.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:7323a9be5e3da695605753f501cdc87824888c5655d27735cdeaa9986b45884c", size = 8050200 }, + { url = "https://files.pythonhosted.org/packages/93/c2/48cb0b6b26276d2120b1e0dbc877579a748eae02b4091a7522ce54f6d5e1/torchvision-0.24.0-cp313-cp313-win_amd64.whl", hash = "sha256:08cad8b204196e945f0b2d73adee952d433db1c03645851d52b22a45f1015b13", size = 4309939 }, + { url = "https://files.pythonhosted.org/packages/7d/d7/3dd10830b047eeb46ae6b465474258d7b4fbb7d8872dca69bd42449f5c82/torchvision-0.24.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:6ab956a6e588623353e0f20d4b03eb1656cb4a3c75ca4dd8b4e32e01bc43271a", size = 2028355 }, + { url = "https://files.pythonhosted.org/packages/f7/cf/2d7e43409089ce7070f5336161f9216d58653ee1cb26bcb5d6c84cc2de36/torchvision-0.24.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:b1b3db80609c32a088554e8e94b4fc31f1033fe5bb4ac0673ec49c3eb03fb4da", size = 2374466 }, + { url = "https://files.pythonhosted.org/packages/e9/30/8f7c328fd7e0a9665da4b6b56b1c627665c18470bfe62f3729ad3eda9aec/torchvision-0.24.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:e6635f100d455c80b43f297df4b8585a76c6a2e114802f6567ddd28d7b5479b0", size = 8217068 }, + { url = "https://files.pythonhosted.org/packages/55/a2/b6f9e40e2904574c80b3bb872c66af20bbd642053e7c8e1b9e99ab396535/torchvision-0.24.0-cp313-cp313t-win_amd64.whl", hash = "sha256:4ce158bbdc3a9086034bced0b5212888bd5b251fee6d08a9eff151d30b4b228a", size = 4273912 }, + { url = "https://files.pythonhosted.org/packages/1b/24/790a39645cc8c71bf442d54a76da9bda5caeb2a44c5f7e02498649cd99d4/torchvision-0.24.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4bdfc85a5ed706421555f32cdc5e3ddb6d40bf65ef03a274ce3c176393e2904b", size = 2028335 }, + { url = "https://files.pythonhosted.org/packages/b0/d7/69479a066ea773653e88eda99031e38681e9094046f87cb957af5036db0e/torchvision-0.24.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:73576a9c4a593223fbae85a64e8bbd77049abd1101893ecf3c5e981284fd58b4", size = 2371609 }, + { url = "https://files.pythonhosted.org/packages/46/64/3c7fdb3771ec992b9445a1f7a969466b23ce2cdb14e09303b3db351a0655/torchvision-0.24.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:dd565b1b06666ff399d0801d4d1824fa570c0167a179ca700a5be232527b3c62", size = 8214918 }, + { url = "https://files.pythonhosted.org/packages/58/51/abc416bc34d574ad479af738e413d9ebf93027ee92d0f4ae38f966b818f7/torchvision-0.24.0-cp314-cp314-win_amd64.whl", hash = "sha256:eb45d12ac48d757738788fd3fb8e88e647d6b2ab2424134ca87556efc72d81b5", size = 4257776 }, + { url = "https://files.pythonhosted.org/packages/08/f7/261d1353c611820541ecd43046b89da3f1ae998dc786e4288b890a009883/torchvision-0.24.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:68120e7e03c31900e499a10bb7fdd63cfd67f0054c9fa108e7e27f9cd372f315", size = 2028359 }, + { url = "https://files.pythonhosted.org/packages/a2/fd/615d8a86db1578345de7fa1edaf476fbcf4f057bf7e4fd898306b620c487/torchvision-0.24.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:64e54494043eecf9f57a9881c6fdea49c62282782e737c002ae8b1639e6ea80e", size = 2374469 }, + { url = "https://files.pythonhosted.org/packages/04/98/bac11e8fdbf00d6c398246ff2781370aa72c99f2ac685c01ce79354c9a32/torchvision-0.24.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:75ef9546323b321a451239d886f0cb528f7e98bb294da47a3200effd4e572064", size = 8217060 }, + { url = "https://files.pythonhosted.org/packages/47/6f/9fba8abc468c904570699eceeb51588f9622172b8fffa4ab11bcf15598c2/torchvision-0.24.0-cp314-cp314t-win_amd64.whl", hash = "sha256:2efb617667950814fc8bb9437e5893861b3616e214285be33cbc364a3f42c599", size = 4358490 }, ] [[package]] @@ -5374,7 +5502,7 @@ wheels = [ [[package]] name = "transformers" -version = "4.57.0" +version = "4.57.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "filelock" }, @@ -5388,24 +5516,23 @@ dependencies = [ { name = "tokenizers" }, { name = "tqdm" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f3/5c/a22c39dac2687f3fe2a6b97e2c1ae516e91cd4d3976a7a2b7c24ff2fae48/transformers-4.57.0.tar.gz", hash = "sha256:d045753f3d93f9216e693cdb168698dfd2e9d3aad1bb72579a5d60ebf1545a8b", size = 10142956 } +sdist = { url = "https://files.pythonhosted.org/packages/d6/68/a39307bcc4116a30b2106f2e689130a48de8bd8a1e635b5e1030e46fcd9e/transformers-4.57.1.tar.gz", hash = "sha256:f06c837959196c75039809636cd964b959f6604b75b8eeec6fdfc0440b89cc55", size = 10142511 } wheels = [ - { url = "https://files.pythonhosted.org/packages/e5/2b/4d2708ac1ff5cd708b6548f4c5812d0ae40d1c28591c4c1c762b6dbdef2d/transformers-4.57.0-py3-none-any.whl", hash = "sha256:9d7c6d098c026e40d897e017ed1f481ab803cbac041021dbc6ae6100e4949b55", size = 11990588 }, + { url = "https://files.pythonhosted.org/packages/71/d3/c16c3b3cf7655a67db1144da94b021c200ac1303f82428f2beef6c2e72bb/transformers-4.57.1-py3-none-any.whl", hash = "sha256:b10d05da8fa67dc41644dbbf9bc45a44cb86ae33da6f9295f5fbf5b7890bd267", size = 11990925 }, ] [[package]] name = "triton" -version = "3.4.0" +version = "3.5.0" source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "setuptools", marker = "sys_platform != 'win32'" }, -] wheels = [ - { url = "https://files.pythonhosted.org/packages/62/ee/0ee5f64a87eeda19bbad9bc54ae5ca5b98186ed00055281fd40fb4beb10e/triton-3.4.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7ff2785de9bc02f500e085420273bb5cc9c9bb767584a4aa28d6e360cec70128", size = 155430069 }, - { url = "https://files.pythonhosted.org/packages/7d/39/43325b3b651d50187e591eefa22e236b2981afcebaefd4f2fc0ea99df191/triton-3.4.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7b70f5e6a41e52e48cfc087436c8a28c17ff98db369447bcaff3b887a3ab4467", size = 155531138 }, - { url = "https://files.pythonhosted.org/packages/d0/66/b1eb52839f563623d185f0927eb3530ee4d5ffe9d377cdaf5346b306689e/triton-3.4.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:31c1d84a5c0ec2c0f8e8a072d7fd150cab84a9c239eaddc6706c081bfae4eb04", size = 155560068 }, - { url = "https://files.pythonhosted.org/packages/30/7b/0a685684ed5322d2af0bddefed7906674f67974aa88b0fae6e82e3b766f6/triton-3.4.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00be2964616f4c619193cb0d1b29a99bd4b001d7dc333816073f92cf2a8ccdeb", size = 155569223 }, - { url = "https://files.pythonhosted.org/packages/20/63/8cb444ad5cdb25d999b7d647abac25af0ee37d292afc009940c05b82dda0/triton-3.4.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7936b18a3499ed62059414d7df563e6c163c5e16c3773678a3ee3d417865035d", size = 155659780 }, + { url = "https://files.pythonhosted.org/packages/0b/eb/09e31d107a5d00eb281aa7e6635ca463e9bca86515944e399480eadb71f8/triton-3.5.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d5d3b3d480debf24eaa739623c9a42446b0b77f95593d30eb1f64cd2278cc1f0", size = 170333110 }, + { url = "https://files.pythonhosted.org/packages/3d/78/949a04391c21956c816523678f0e5fa308eb5b1e7622d88c4e4ef5fceca0/triton-3.5.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f34bfa21c5b3a203c0f0eab28dcc1e49bd1f67d22724e77fb6665a659200a4ec", size = 170433488 }, + { url = "https://files.pythonhosted.org/packages/f5/3a/e991574f3102147b642e49637e0281e9bb7c4ba254edb2bab78247c85e01/triton-3.5.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c9e71db82261c4ffa3921cd050cd5faa18322d2d405c30eb56084afaff3b0833", size = 170476535 }, + { url = "https://files.pythonhosted.org/packages/6c/29/10728de8a6e932e517c10773486b8e99f85d1b1d9dd87d9a9616e1fef4a1/triton-3.5.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e6bb9aa5519c084a333acdba443789e50012a4b851cd486c54f0b8dc2a8d3a12", size = 170487289 }, + { url = "https://files.pythonhosted.org/packages/5c/38/db80e48b9220c9bce872b0f616ad0446cdf554a40b85c7865cbca99ab3c2/triton-3.5.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c83f2343e1a220a716c7b3ab9fccfcbe3ad4020d189549200e2d2e8d5868bed9", size = 170577179 }, + { url = "https://files.pythonhosted.org/packages/ff/60/1810655d1d856c9a4fcc90ee8966d85f552d98c53a6589f95ab2cbe27bb8/triton-3.5.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:da0fa67ccd76c3dcfb0bffe1b1c57c685136a6bd33d141c24d9655d4185b1289", size = 170487949 }, + { url = "https://files.pythonhosted.org/packages/fb/b7/1dec8433ac604c061173d0589d99217fe7bf90a70bdc375e745d044b8aad/triton-3.5.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:317fe477ea8fd4524a6a8c499fb0a36984a56d0b75bf9c9cb6133a1c56d5a6e7", size = 170580176 }, ] [[package]] @@ -5502,7 +5629,7 @@ wheels = [ [[package]] name = "ultralytics" -version = "8.3.207" +version = "8.3.217" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "matplotlib" }, @@ -5518,9 +5645,9 @@ dependencies = [ { name = "torchvision" }, { name = "ultralytics-thop" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/49/17/ec67eb1a27ea83165c02f181bf97163184eae745aac52ec11af424c20cd3/ultralytics-8.3.207.tar.gz", hash = "sha256:d474eacb32d1ae18d5d7f21bfa53700b9fec1f548bf2b36172dced062354c73a", size = 919998 } +sdist = { url = "https://files.pythonhosted.org/packages/71/81/e36576a1e12496326c89c3e3489f52d1d75f19cf53109324699259ccb956/ultralytics-8.3.217.tar.gz", hash = "sha256:c1005c106cbe31e19b22fd90b9e9098cfa83454eaada8e90c6be64269e524718", size = 922137 } wheels = [ - { url = "https://files.pythonhosted.org/packages/71/41/f4e72cfc5881c0fec93328e1e7c151770f9bec5fb31f014fa9cf613003c2/ultralytics-8.3.207-py3-none-any.whl", hash = "sha256:5443550114890fa3f54025ce92b27b4c7ac639ac5e878cc64adaec03ec233184", size = 1072928 }, + { url = "https://files.pythonhosted.org/packages/cc/df/cadc2f74e09aadc1704913dac9c0ef8d9d325fa9a67cca038536fb089b47/ultralytics-8.3.217-py3-none-any.whl", hash = "sha256:8c1c9059f6a9fb90787e65c8dcc6c154b3f4a3695b65dfd1b0ea21f860397faa", size = 1075192 }, ] [[package]] @@ -5568,28 +5695,28 @@ wheels = [ [[package]] name = "uv" -version = "0.9.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fc/05/98147931b168b32273b20da39192856650b4b04198ffadd046340c3b2af1/uv-0.9.0.tar.gz", hash = "sha256:9f6bbcec6d45f921a81a3ab7cd384738b842d2a5e49686fdf5a0affcd4875d47", size = 3681408 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/38/3f/812eb731f5abfe0a9a3ecb15614e011b5b5d9a684cc9ddfed5095af43b4b/uv-0.9.0-py3-none-linux_armv6l.whl", hash = "sha256:c447a5f49f3d75d65c91d3ab286d52f3bcc054256c31725c6a60ab7b2b180797", size = 20530647 }, - { url = "https://files.pythonhosted.org/packages/c2/1e/efb46c17b7220fab41b470e6f269f4e7997d274fb5fb46512e1579ff3b07/uv-0.9.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ecc841797888d5983dcbac0918d0392f8078265083c2bc676a76e36a1a84b3ad", size = 19617751 }, - { url = "https://files.pythonhosted.org/packages/8e/06/f5e38314e318bfaa20ccce966f6d0a69b093854648d31085b2d8b2097aab/uv-0.9.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:b900e84d992a657e16371426dbb030ab031c0322a604b632dada34401ebe7145", size = 18207114 }, - { url = "https://files.pythonhosted.org/packages/83/2e/2a0787ee496bef5e1312a733aab6d06b28b093b87a196ec6d45497a1cc4c/uv-0.9.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:ac6ec2b69dbf61ddd725d10626db2d9401c2687b1c71077217d097214bb9665d", size = 19962531 }, - { url = "https://files.pythonhosted.org/packages/42/a7/d0bf7573ea4956c6ea6ea8375a0ec06a11e4a7240f1c21592ae160cb9c36/uv-0.9.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:431856f10a8fea6e10f6194a0cc1410d9e0d007e484c2259fea50524e904e1ae", size = 20147793 }, - { url = "https://files.pythonhosted.org/packages/c1/09/8e09ae31a421464cf9e42d578e632342a2c7542575bfcef2b78d2b89bb7b/uv-0.9.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57ea8d38cbc44bb0a19facb2a953015c1e7f8445c7c007913c6de615ff976270", size = 21091691 }, - { url = "https://files.pythonhosted.org/packages/ce/a4/2669f39ed5fb9f66de9971f301841a6596b5d16b63c4d033d65ef38d8aa0/uv-0.9.0-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:dc7ae35d33174aa5861182ca44ddc3a68146eeae11fbda10f306c1924b4057a1", size = 22531644 }, - { url = "https://files.pythonhosted.org/packages/02/f3/553278547813ad866cdf29500913d9811b8ce896c9c798495de9b20e112d/uv-0.9.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8d9654d8953303402ec88e95474da1533ea309c3086f3f8375b7a56492273530", size = 22213399 }, - { url = "https://files.pythonhosted.org/packages/69/e3/d9c2d52a3487d66c3a99a521f2ca5a1f3f7a975a3fb61f446c093646e970/uv-0.9.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c68fa7c8cd172d11bcb2fa0adac2ddb53bd51264be3f52e02840c00feb0f0572", size = 21386341 }, - { url = "https://files.pythonhosted.org/packages/6f/92/7d491ede63c70e237168d7c6a53aa6353420414fa29e5ee06e0c130cfa35/uv-0.9.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8fcd2f0d52ab16faa055ca2969d1d94ae1b1397ba5a0962332c500dd81b0f893", size = 21216943 }, - { url = "https://files.pythonhosted.org/packages/4a/52/25a5dcfccb7a905f9fde93acd0a6c6bb6d1a14333d2f696e64d33ec108df/uv-0.9.0-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:5ff5954dc403b408d30470d499a40286bc549e2f9623dc05c93d5bb05af80340", size = 20105222 }, - { url = "https://files.pythonhosted.org/packages/8c/fb/f96df677030a29e7af381e9fdc014e4a15bd28bb239719f540dd780eefed/uv-0.9.0-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:d9234efcd64d09be3cdd5482a5fc5ca824715533f8ad064d9c14d4ae844584f7", size = 21227746 }, - { url = "https://files.pythonhosted.org/packages/cf/b1/87451ceae6ac012e21e893899fcb5d4748c718c6a0e574b00d2777ec3785/uv-0.9.0-py3-none-musllinux_1_1_armv7l.whl", hash = "sha256:54e14d2b5dce68930e76dc1b61a7d9920cc5f08dee68074e02e828b515cbe565", size = 20096479 }, - { url = "https://files.pythonhosted.org/packages/66/33/afd79936553b3d901ccdaa0e4a3f482b5a16c5d263c032cc927800c3de2a/uv-0.9.0-py3-none-musllinux_1_1_i686.whl", hash = "sha256:ab1c25094cd298ad82d331abb80ab56ad0e49a3c18c6c1c25413389be7432158", size = 20528329 }, - { url = "https://files.pythonhosted.org/packages/9b/cd/9f47b7aeb907c7c9c8e87a02796c386bc102f142d4dbe29f57eb68802d62/uv-0.9.0-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:4e0b226ed5d061ff9001547f59ca4a76af427d5f28d1d50ae245c6916ea026d7", size = 21415050 }, - { url = "https://files.pythonhosted.org/packages/fa/b1/d1973ac4c34c5e8608760a81147e51e5f6d4670a1bf6e6c56fc1067ff5f5/uv-0.9.0-py3-none-win32.whl", hash = "sha256:943ff96a34c5cf364eaf43cfc8cf181261ed94ab0c7e199686deb1dfa0cd9723", size = 19380428 }, - { url = "https://files.pythonhosted.org/packages/60/cd/98242bb85c92a06156ebfead3c3c9126e8dbb7872808b11a76428b9aaf4a/uv-0.9.0-py3-none-win_amd64.whl", hash = "sha256:ee5969cef61e8eef251ee14e573fa22b42bbe90a2d194b6de6d794baee176a9b", size = 21387424 }, - { url = "https://files.pythonhosted.org/packages/1a/7e/d9b4df434081e883eefb0e975e4dd40de210596c28cf8a88ec5783ae593b/uv-0.9.0-py3-none-win_arm64.whl", hash = "sha256:4a77a33c86d478fd1fb27db29035044261f086e7287325bcbe695c9b31808f4c", size = 19853856 }, +version = "0.9.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0d/dc/4a0e01bcb38c756130c8118a8561d4bf0a0bb685b70ad11e8f40a0cbfa10/uv-0.9.3.tar.gz", hash = "sha256:a290a1a8783bf04ca2d4a63d5d72191b255dfa4cc3426a9c9b5af4da49a7b5af", size = 3699151 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c3/ad/194e550062e4b3b9a74cb06401dc0afd83490af8e2ec0f414737868d0262/uv-0.9.3-py3-none-linux_armv6l.whl", hash = "sha256:7b1b79dd435ade1de97c6f0b8b90811a6ccf1bd0bdd70f4d034a93696cf0d0a3", size = 20584531 }, + { url = "https://files.pythonhosted.org/packages/d0/1a/8e68d0020c29f6f329a265773c23b0c01e002794ea884b8bdbd594c7ea97/uv-0.9.3-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:596a982c5a061d58412824a2ebe2960b52db23f1b1658083ba9c0e7ae390308a", size = 19577639 }, + { url = "https://files.pythonhosted.org/packages/16/25/6df8be6cd549200e80d19374579689fda39b18735afde841345284fb113d/uv-0.9.3-py3-none-macosx_11_0_arm64.whl", hash = "sha256:741e80c4230e1b9a5d0869aca2fb082b3832b251ef61537bc9278364b8e74df2", size = 18210073 }, + { url = "https://files.pythonhosted.org/packages/07/19/bb8aa38b4441e03c742e71a31779f91b42d9db255ede66f80cdfdb672618/uv-0.9.3-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:406ab1a8b313b4b3cf67ad747fb8713a0c0cf3d3daf11942b5a4e49f60882339", size = 20022427 }, + { url = "https://files.pythonhosted.org/packages/40/15/f190004dd855b443cfc1cc36edb1765e6cd0b6b340a50bb8015531dfff2e/uv-0.9.3-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:73dbd91581a82e53bb4352243d7bc491cf78ac3ebb951d95bb8b7964e5ee0659", size = 20150307 }, + { url = "https://files.pythonhosted.org/packages/dd/55/553e90bc2b881f168de9cd57f9e0b0464304a12aee289e71b54c42559e1a/uv-0.9.3-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:970ac8428678b92eddb990dc132d75e893234bb1b809e87b90a4acd96bb054e4", size = 21152942 }, + { url = "https://files.pythonhosted.org/packages/30/fb/768647a31622c2c1da7a9394eaab937e2e7ca0e8c983ca3d1918ec623620/uv-0.9.3-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:32694e64d6e4ea44b647866c4240659f3964b0317e98f539b73915dbcca7d973", size = 22632018 }, + { url = "https://files.pythonhosted.org/packages/98/92/66d660414aed123686bf9a2a3ea167967b847b97c08cacd13d6b2b6d1267/uv-0.9.3-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:36df7eb562b103e3263a03df1b04cee91ee52af88d005d07ee494137c7a5782a", size = 22241856 }, + { url = "https://files.pythonhosted.org/packages/0d/99/af8b0cd2c958e8cb9c20e6e2d417de9476338a2b155643492a8ee2baf077/uv-0.9.3-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:117c5921bcfdac04b88211ee830c6c7e412eaf93a34aa3ad4bb3230bc61646aa", size = 21391699 }, + { url = "https://files.pythonhosted.org/packages/82/45/488417c6c0127c00bcdfac3556ae2ea0597df8245fe5f9bcfda35ebdbe85/uv-0.9.3-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:73ae4bbc7d555ba1738da08c64b55f21ab0ea0ff85636708cebaf460d98a440d", size = 21318117 }, + { url = "https://files.pythonhosted.org/packages/1d/62/508c20f8dbdd2342cc4821ab6f41e29a9b36e2a469dfb5cbbd042e15218c/uv-0.9.3-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:2e75ce14c9375e7e99422d5383fb415e8f0eab9ebdcdfba45756749dee0c42b2", size = 20132999 }, + { url = "https://files.pythonhosted.org/packages/2d/fc/ea673d1c68915ea53f1ab7e134b330a2351c543f06e9d0009b4f27cc3057/uv-0.9.3-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:71faefa9805ccf3f2db645ae27c9e719e47aaa8781e43dfa3760d993aadecb8c", size = 21223810 }, + { url = "https://files.pythonhosted.org/packages/97/1f/af8ced7f6c8f6af887c52369088058ecae92ff21819e385531023f9ec923/uv-0.9.3-py3-none-musllinux_1_1_armv7l.whl", hash = "sha256:8844103e0b4074821fb2814abf30af59d66f33b6ca1bb2276dd37d4e5997c292", size = 20156823 }, + { url = "https://files.pythonhosted.org/packages/05/2d/e1d8f74ec9d95daf57f3c53083c98a2145ee895a4f8502c61c9013c9bf5a/uv-0.9.3-py3-none-musllinux_1_1_i686.whl", hash = "sha256:214bb2fb4d87a55e2ba2bc038a8b646a24ec66980528d2ed1e6e7d0612d246e1", size = 20564971 }, + { url = "https://files.pythonhosted.org/packages/bc/04/4aaf90e031f0735795407a208c9528f85b0b27b63409abe4ee3bee0d4527/uv-0.9.3-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:ccf4cd2e1907fb011764f6f4bc0e514c500e8d300288f04a4680400d5aa205ec", size = 21506573 }, + { url = "https://files.pythonhosted.org/packages/4e/7e/548f5c58b7130b62ef75ffc120919b45698179bc0efae0fb6bd32be2d634/uv-0.9.3-py3-none-win32.whl", hash = "sha256:5092d8ac9d0ff7d158ac0d834b27b39b7f23fdbf71865ac8b8bdb55db6f3f5c5", size = 19328674 }, + { url = "https://files.pythonhosted.org/packages/87/cd/667f6249a9a3a8d2d7ba1aa72db6b1fc6cdaf7b0d7aeda43478702e2a13e/uv-0.9.3-py3-none-win_amd64.whl", hash = "sha256:55516bf85c44a00b948472ddda80d7c5cd9990e9b5c085dc5005da93f40266a7", size = 21346807 }, + { url = "https://files.pythonhosted.org/packages/67/e7/485f57f3415593e4e96b312aa168b9ce6b66d72e89c8b1cb59b683522672/uv-0.9.3-py3-none-win_arm64.whl", hash = "sha256:7a63c2514833f80a006feef9b8adce1380e06a7945ceb80928d43ac8693b4dd6", size = 19824358 }, ] [[package]] @@ -5608,7 +5735,7 @@ wheels = [ [[package]] name = "virtualenv" -version = "20.34.0" +version = "20.35.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "distlib" }, @@ -5616,9 +5743,9 @@ dependencies = [ { name = "platformdirs" }, { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/1c/14/37fcdba2808a6c615681cd216fecae00413c9dab44fb2e57805ecf3eaee3/virtualenv-20.34.0.tar.gz", hash = "sha256:44815b2c9dee7ed86e387b842a84f20b93f7f417f95886ca1996a72a4138eb1a", size = 6003808 } +sdist = { url = "https://files.pythonhosted.org/packages/a4/d5/b0ccd381d55c8f45d46f77df6ae59fbc23d19e901e2d523395598e5f4c93/virtualenv-20.35.3.tar.gz", hash = "sha256:4f1a845d131133bdff10590489610c98c168ff99dc75d6c96853801f7f67af44", size = 6002907 } wheels = [ - { url = "https://files.pythonhosted.org/packages/76/06/04c8e804f813cf972e3262f3f8584c232de64f0cde9f703b46cf53a45090/virtualenv-20.34.0-py3-none-any.whl", hash = "sha256:341f5afa7eee943e4984a9207c025feedd768baff6753cd660c857ceb3e36026", size = 5983279 }, + { url = "https://files.pythonhosted.org/packages/27/73/d9a94da0e9d470a543c1b9d3ccbceb0f59455983088e727b8a1824ed90fb/virtualenv-20.35.3-py3-none-any.whl", hash = "sha256:63d106565078d8c8d0b206d48080f938a8b25361e19432d2c9db40d2899c810a", size = 5981061 }, ] [[package]] @@ -5705,7 +5832,7 @@ xai = [ [package.metadata] requires-dist = [ { name = "click", marker = "extra == 'dev'" }, - { name = "getstream", extras = ["telemetry", "webrtc"], specifier = ">=2.5.3" }, + { name = "getstream", extras = ["telemetry", "webrtc"], specifier = ">=2.5.5" }, { name = "mcp", specifier = ">=1.16.0" }, { name = "mypy", marker = "extra == 'dev'" }, { name = "numpy", specifier = ">=1.24.0" }, @@ -5788,7 +5915,7 @@ dev = [ [package.metadata] requires-dist = [ - { name = "cartesia", specifier = ">=2.0.5" }, + { name = "cartesia", specifier = ">=2.0.9" }, { name = "vision-agents", editable = "agents-core" }, ] @@ -5813,7 +5940,7 @@ dev = [ { name = "pytest-asyncio" }, { name = "scipy" }, { name = "soundfile" }, - { name = "torchaudio" }, + { name = "torchvision" }, ] [package.metadata] @@ -5829,7 +5956,7 @@ dev = [ { name = "pytest-asyncio", specifier = ">=1.0.0" }, { name = "scipy", specifier = ">=1.15.3,<1.16" }, { name = "soundfile", specifier = ">=0.13.1" }, - { name = "torchaudio", specifier = ">=2.7.1" }, + { name = "torchvision", specifier = ">=0.20.0" }, ] [[package]] @@ -6016,7 +6143,7 @@ dev = [ [package.metadata] requires-dist = [ - { name = "openai", extras = ["realtime"], specifier = ">=2.2.0" }, + { name = "openai", extras = ["realtime"], specifier = ">=2.5.0" }, { name = "vision-agents", editable = "agents-core" }, ] @@ -6042,7 +6169,7 @@ dev = [ { name = "pytest-asyncio" }, { name = "scipy" }, { name = "soundfile" }, - { name = "torchaudio" }, + { name = "torchvision" }, ] [package.metadata] @@ -6059,7 +6186,7 @@ dev = [ { name = "pytest-asyncio", specifier = ">=1.0.0" }, { name = "scipy", specifier = ">=1.15.3,<1.16" }, { name = "soundfile", specifier = ">=0.13.1" }, - { name = "torchaudio", specifier = ">=2.7.1" }, + { name = "torchvision", specifier = ">=0.20.0" }, ] [[package]] @@ -6360,7 +6487,7 @@ wheels = [ [[package]] name = "xai-sdk" -version = "1.2.0" +version = "1.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiohttp" }, @@ -6371,9 +6498,9 @@ dependencies = [ { name = "pydantic" }, { name = "requests" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9f/e0/99e5cd35e330a607f948e07e1dedb513c880828dd1ca8edcd27dc47224a3/xai_sdk-1.2.0.tar.gz", hash = "sha256:9041ea4ef2cfb7e91252cf178ee597b32dfe7342251e73b08be5c5537c32311e", size = 273148 } +sdist = { url = "https://files.pythonhosted.org/packages/73/d3/c2bd8914a31828437b8f1a52d69572813e1bef687977ad5eaef81072e283/xai_sdk-1.3.1.tar.gz", hash = "sha256:db88824bd799b70d8ae4d7c7c26f2179909ee18493144b2fe90f258473540590", size = 279782 } wheels = [ - { url = "https://files.pythonhosted.org/packages/42/23/ca65a84d855c6723aa17591f85fffd6379a682a5ffc90a55aa0e606230f3/xai_sdk-1.2.0-py3-none-any.whl", hash = "sha256:3e12c1e31b5c1d9e2128f03bcfc021bfd8f967f7f2d2ad7635dc84f1b5ffd4f3", size = 156585 }, + { url = "https://files.pythonhosted.org/packages/95/00/7085afc3ee79eed65b6ebecca01b3657e1afa2e77030b4094eafa8a2de6f/xai_sdk-1.3.1-py3-none-any.whl", hash = "sha256:5cf0abaa0f731e08d235fc6b60d490d94564f85916819e20fa1e82b29d57c3f8", size = 161143 }, ] [[package]]