diff --git a/pyproject.toml b/pyproject.toml index 115c7c4eb..38c6660c0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -37,7 +37,7 @@ Repository = "https://github.com/openai/openai-agents-python" [project.optional-dependencies] voice = ["numpy>=2.2.0, <3; python_version>='3.10'", "websockets>=15.0, <16"] viz = ["graphviz>=0.17"] -litellm = ["litellm>=1.67.4.post1, <2"] +litellm = ["litellm>=1.80.8, <2"] realtime = ["websockets>=15.0, <16"] sqlalchemy = ["SQLAlchemy>=2.0", "asyncpg>=0.29.0"] encrypt = ["cryptography>=45.0, <46"] diff --git a/src/agents/extensions/models/litellm_model.py b/src/agents/extensions/models/litellm_model.py index 439146c6c..498bfd25d 100644 --- a/src/agents/extensions/models/litellm_model.py +++ b/src/agents/extensions/models/litellm_model.py @@ -62,6 +62,15 @@ class InternalChatCompletionMessage(ChatCompletionMessage): thinking_blocks: list[dict[str, Any]] | None = None +class InternalToolCall(ChatCompletionMessageFunctionToolCall): + """ + An internal subclass to carry provider-specific metadata (e.g., Gemini thought signatures) + without modifying the original model. + """ + + extra_content: dict[str, Any] | None = None + + class LitellmModel(Model): """This class enables using any model via LiteLLM. LiteLLM allows you to acess OpenAPI, Anthropic, Gemini, Mistral, and many other models. @@ -168,9 +177,15 @@ async def get_response( "output_tokens": usage.output_tokens, } + # Build provider_data for provider specific fields + provider_data: dict[str, Any] = {"model": self.model} + if message is not None and hasattr(response, "id"): + provider_data["response_id"] = response.id + items = ( Converter.message_to_output_items( - LitellmConverter.convert_message_to_openai(message) + LitellmConverter.convert_message_to_openai(message, model=self.model), + provider_data=provider_data, ) if message is not None else [] @@ -215,7 +230,9 @@ async def stream_response( ) final_response: Response | None = None - async for chunk in ChatCmplStreamHandler.handle_stream(response, stream): + async for chunk in ChatCmplStreamHandler.handle_stream( + response, stream, model=self.model + ): yield chunk if chunk.type == "response.completed": @@ -280,13 +297,19 @@ async def _fetch_response( ) converted_messages = Converter.items_to_messages( - input, preserve_thinking_blocks=preserve_thinking_blocks + input, model=self.model, preserve_thinking_blocks=preserve_thinking_blocks ) # Fix for interleaved thinking bug: reorder messages to ensure tool_use comes before tool_result # noqa: E501 if "anthropic" in self.model.lower() or "claude" in self.model.lower(): converted_messages = self._fix_tool_message_ordering(converted_messages) + # Convert Google's extra_content to litellm's provider_specific_fields format + if "gemini" in self.model.lower(): + converted_messages = self._convert_gemini_extra_content_to_provider_specific_fields( + converted_messages + ) + if system_instructions: converted_messages.insert( 0, @@ -436,6 +459,65 @@ async def _fetch_response( ) return response, ret + def _convert_gemini_extra_content_to_provider_specific_fields( + self, messages: list[ChatCompletionMessageParam] + ) -> list[ChatCompletionMessageParam]: + """ + Convert Gemini model's extra_content format to provider_specific_fields format for litellm. + + Transforms tool calls from internal format: + extra_content={"google": {"thought_signature": "..."}} + To litellm format: + provider_specific_fields={"thought_signature": "..."} + + Only processes tool_calls that appear after the last user message. + See: https://ai.google.dev/gemini-api/docs/thought-signatures + """ + + # Find the index of the last user message + last_user_index = -1 + for i in range(len(messages) - 1, -1, -1): + if isinstance(messages[i], dict) and messages[i].get("role") == "user": + last_user_index = i + break + + for i, message in enumerate(messages): + if not isinstance(message, dict): + continue + + # Only process assistant messages that come after the last user message + # If no user message found (last_user_index == -1), process all messages + if last_user_index != -1 and i <= last_user_index: + continue + + # Check if this is an assistant message with tool calls + if message.get("role") == "assistant" and message.get("tool_calls"): + tool_calls = message.get("tool_calls", []) + + for tool_call in tool_calls: # type: ignore[attr-defined] + if not isinstance(tool_call, dict): + continue + + # Default to skip validator, overridden if valid thought signature exists + tool_call["provider_specific_fields"] = { + "thought_signature": "skip_thought_signature_validator" + } + + # Override with actual thought signature if extra_content exists + if "extra_content" in tool_call: + extra_content = tool_call.pop("extra_content") + if isinstance(extra_content, dict): + # Extract google-specific fields + google_fields = extra_content.get("google") + if google_fields and isinstance(google_fields, dict): + thought_sig = google_fields.get("thought_signature") + if thought_sig: + tool_call["provider_specific_fields"] = { + "thought_signature": thought_sig + } + + return messages + def _fix_tool_message_ordering( self, messages: list[ChatCompletionMessageParam] ) -> list[ChatCompletionMessageParam]: @@ -563,15 +645,26 @@ def _merge_headers(self, model_settings: ModelSettings): class LitellmConverter: @classmethod def convert_message_to_openai( - cls, message: litellm.types.utils.Message + cls, message: litellm.types.utils.Message, model: str | None = None ) -> ChatCompletionMessage: + """ + Convert a LiteLLM message to OpenAI ChatCompletionMessage format. + + Args: + message: The LiteLLM message to convert + model: The target model to convert to. Used to handle provider-specific + transformations. + """ if message.role != "assistant": raise ModelBehaviorError(f"Unsupported role: {message.role}") tool_calls: ( list[ChatCompletionMessageFunctionToolCall | ChatCompletionMessageCustomToolCall] | None ) = ( - [LitellmConverter.convert_tool_call_to_openai(tool) for tool in message.tool_calls] + [ + LitellmConverter.convert_tool_call_to_openai(tool, model=model) + for tool in message.tool_calls + ] if message.tool_calls else None ) @@ -641,13 +734,43 @@ def convert_annotations_to_openai( @classmethod def convert_tool_call_to_openai( - cls, tool_call: litellm.types.utils.ChatCompletionMessageToolCall + cls, tool_call: litellm.types.utils.ChatCompletionMessageToolCall, model: str | None = None ) -> ChatCompletionMessageFunctionToolCall: - return ChatCompletionMessageFunctionToolCall( - id=tool_call.id, + # Clean up litellm's addition of __thought__ suffix to tool_call.id for + # Gemini models. See: https://github.com/BerriAI/litellm/pull/16895 + # This suffix is redundant since we can get thought_signature from + # provider_specific_fields, and this hack causes validation errors when + # cross-model passing to other models. + tool_call_id = tool_call.id + if model and "gemini" in model.lower() and "__thought__" in tool_call_id: + tool_call_id = tool_call_id.split("__thought__")[0] + + # Convert litellm's tool call format to chat completion message format + base_tool_call = ChatCompletionMessageFunctionToolCall( + id=tool_call_id, type="function", function=Function( name=tool_call.function.name or "", arguments=tool_call.function.arguments, ), ) + + # Preserve provider-specific fields if present (e.g., Gemini thought signatures) + if hasattr(tool_call, "provider_specific_fields") and tool_call.provider_specific_fields: + # Convert to nested extra_content structure + extra_content: dict[str, Any] = {} + provider_fields = tool_call.provider_specific_fields + + # Check for thought_signature (Gemini specific) + if model and "gemini" in model.lower(): + if "thought_signature" in provider_fields: + extra_content["google"] = { + "thought_signature": provider_fields["thought_signature"] + } + + return InternalToolCall( + **base_tool_call.model_dump(), + extra_content=extra_content if extra_content else None, + ) + + return base_tool_call diff --git a/src/agents/handoffs/history.py b/src/agents/handoffs/history.py index dc59547fb..e2623c471 100644 --- a/src/agents/handoffs/history.py +++ b/src/agents/handoffs/history.py @@ -144,7 +144,7 @@ def _format_transcript_item(item: TResponseInputItem) -> str: return f"{prefix}: {content_str}" if content_str else prefix item_type = item.get("type", "item") - rest = {k: v for k, v in item.items() if k != "type"} + rest = {k: v for k, v in item.items() if k not in ("type", "provider_data")} try: serialized = json.dumps(rest, ensure_ascii=False, default=str) except TypeError: diff --git a/src/agents/models/chatcmpl_converter.py b/src/agents/models/chatcmpl_converter.py index bc0304be0..b81dc2160 100644 --- a/src/agents/models/chatcmpl_converter.py +++ b/src/agents/models/chatcmpl_converter.py @@ -94,18 +94,37 @@ def convert_response_format( } @classmethod - def message_to_output_items(cls, message: ChatCompletionMessage) -> list[TResponseOutputItem]: + def message_to_output_items( + cls, + message: ChatCompletionMessage, + provider_data: dict[str, Any] | None = None, + ) -> list[TResponseOutputItem]: + """ + Convert a ChatCompletionMessage to a list of response output items. + + Args: + message: The chat completion message to convert + provider_data: Metadata indicating the source model that generated this message. + Contains provider-specific information like model name and response_id, + which is attached to output items. + """ items: list[TResponseOutputItem] = [] # Check if message is agents.extentions.models.litellm_model.InternalChatCompletionMessage # We can't actually import it here because litellm is an optional dependency # So we use hasattr to check for reasoning_content and thinking_blocks if hasattr(message, "reasoning_content") and message.reasoning_content: - reasoning_item = ResponseReasoningItem( - id=FAKE_RESPONSES_ID, - summary=[Summary(text=message.reasoning_content, type="summary_text")], - type="reasoning", - ) + reasoning_kwargs: dict[str, Any] = { + "id": FAKE_RESPONSES_ID, + "summary": [Summary(text=message.reasoning_content, type="summary_text")], + "type": "reasoning", + } + + # Add provider_data if available + if provider_data: + reasoning_kwargs["provider_data"] = provider_data + + reasoning_item = ResponseReasoningItem(**reasoning_kwargs) # Store thinking blocks for Anthropic compatibility if hasattr(message, "thinking_blocks") and message.thinking_blocks: @@ -129,13 +148,19 @@ def message_to_output_items(cls, message: ChatCompletionMessage) -> list[TRespon items.append(reasoning_item) - message_item = ResponseOutputMessage( - id=FAKE_RESPONSES_ID, - content=[], - role="assistant", - type="message", - status="completed", - ) + message_kwargs: dict[str, Any] = { + "id": FAKE_RESPONSES_ID, + "content": [], + "role": "assistant", + "type": "message", + "status": "completed", + } + + # Add provider_data if available + if provider_data: + message_kwargs["provider_data"] = provider_data + + message_item = ResponseOutputMessage(**message_kwargs) if message.content: message_item.content.append( ResponseOutputText( @@ -155,15 +180,35 @@ def message_to_output_items(cls, message: ChatCompletionMessage) -> list[TRespon if message.tool_calls: for tool_call in message.tool_calls: if tool_call.type == "function": - items.append( - ResponseFunctionToolCall( - id=FAKE_RESPONSES_ID, - call_id=tool_call.id, - arguments=tool_call.function.arguments, - name=tool_call.function.name, - type="function_call", - ) - ) + # Create base function call item + func_call_kwargs: dict[str, Any] = { + "id": FAKE_RESPONSES_ID, + "call_id": tool_call.id, + "arguments": tool_call.function.arguments, + "name": tool_call.function.name, + "type": "function_call", + } + + # Build provider_data for function call + func_provider_data: dict[str, Any] = {} + + # Start with provider_data (if provided) + if provider_data: + func_provider_data.update(provider_data) + + # Convert Google's extra_content field data to item's provider_data field + if hasattr(tool_call, "extra_content") and tool_call.extra_content: + google_fields = tool_call.extra_content.get("google") + if google_fields and isinstance(google_fields, dict): + thought_sig = google_fields.get("thought_signature") + if thought_sig: + func_provider_data["thought_signature"] = thought_sig + + # Add provider_data if we have any + if func_provider_data: + func_call_kwargs["provider_data"] = func_provider_data + + items.append(ResponseFunctionToolCall(**func_call_kwargs)) elif tool_call.type == "custom": pass @@ -339,6 +384,7 @@ def extract_all_content( def items_to_messages( cls, items: str | Iterable[TResponseInputItem], + model: str | None = None, preserve_thinking_blocks: bool = False, ) -> list[ChatCompletionMessageParam]: """ @@ -346,6 +392,9 @@ def items_to_messages( Args: items: A string or iterable of response input items to convert + model: The target model to convert to. Used to restore provider-specific data + (e.g., Gemini thought signatures, Claude thinking blocks) when converting + items back to chat completion messages for the target model. preserve_thinking_blocks: Whether to preserve thinking blocks in tool calls for reasoning models like Claude 4 Sonnet/Opus which support interleaved thinking. When True, thinking blocks are reconstructed and included in @@ -533,6 +582,20 @@ def ensure_assistant_message() -> ChatCompletionAssistantMessageParam: "arguments": arguments, }, ) + + # Restore provider_data back to chat completion message for non-OpenAI models + if "provider_data" in func_call: + provider_fields = func_call["provider_data"] # type: ignore[typeddict-item] + if isinstance(provider_fields, dict): + # Restore thought_signature for Gemini in Google's extra_content format + if model and "gemini" in model.lower(): + thought_sig = provider_fields.get("thought_signature") + + if thought_sig: + new_tool_call["extra_content"] = { # type: ignore[typeddict-unknown-key] + "google": {"thought_signature": thought_sig} + } + tool_calls.append(new_tool_call) asst["tool_calls"] = tool_calls # 5) function call output => tool message @@ -559,9 +622,21 @@ def ensure_assistant_message() -> ChatCompletionAssistantMessageParam: # Reconstruct thinking blocks from content (text) and encrypted_content (signature) content_items = reasoning_item.get("content", []) encrypted_content = reasoning_item.get("encrypted_content") - signatures = encrypted_content.split("\n") if encrypted_content else [] - if content_items and preserve_thinking_blocks: + item_provider_data: dict[str, Any] = reasoning_item.get("provider_data", {}) # type: ignore[assignment] + item_model = item_provider_data.get("model", "") + + if ( + model + and ("claude" in model.lower() or "anthropic" in model.lower()) + and content_items + and preserve_thinking_blocks + # Items may not all originate from Claude, so we need to check for model match. + # For backward compatibility, if provider_data is missing, we ignore the check. + and (model == item_model or item_provider_data == {}) + ): + signatures = encrypted_content.split("\n") if encrypted_content else [] + # Reconstruct thinking blocks from content and signature reconstructed_thinking_blocks = [] for content_item in content_items: diff --git a/src/agents/models/chatcmpl_stream_handler.py b/src/agents/models/chatcmpl_stream_handler.py index f1c504977..7b8f5ea31 100644 --- a/src/agents/models/chatcmpl_stream_handler.py +++ b/src/agents/models/chatcmpl_stream_handler.py @@ -2,6 +2,7 @@ from collections.abc import AsyncIterator from dataclasses import dataclass, field +from typing import Any from openai import AsyncStream from openai.types.chat import ChatCompletionChunk @@ -65,6 +66,8 @@ class StreamingState: # Store accumulated thinking text and signature for Anthropic compatibility thinking_text: str = "" thinking_signature: str | None = None + # Store provider data for all output items + provider_data: dict[str, Any] = field(default_factory=dict) class SequenceNumber: @@ -83,7 +86,17 @@ async def handle_stream( cls, response: Response, stream: AsyncStream[ChatCompletionChunk], + model: str | None = None, ) -> AsyncIterator[TResponseStreamEvent]: + """ + Handle a streaming chat completion response and yield response events. + + Args: + response: The initial Response object to populate with streamed data + stream: The async stream of chat completion chunks from the model + model: The source model that is generating this stream. Used to handle + provider-specific stream processing. + """ usage: CompletionUsage | None = None state = StreamingState() sequence_number = SequenceNumber() @@ -104,6 +117,15 @@ async def handle_stream( if not chunk.choices or not chunk.choices[0].delta: continue + # Build provider_data for non-OpenAI Responses API endpoints format + if model: + state.provider_data["model"] = model + elif hasattr(chunk, "model") and chunk.model: + state.provider_data["model"] = chunk.model + + if hasattr(chunk, "id") and chunk.id: + state.provider_data["response_id"] = chunk.id + delta = chunk.choices[0].delta # Handle thinking blocks from Anthropic (for preserving signatures) @@ -123,20 +145,16 @@ async def handle_stream( if hasattr(delta, "reasoning_content"): reasoning_content = delta.reasoning_content if reasoning_content and not state.reasoning_content_index_and_output: - state.reasoning_content_index_and_output = ( - 0, - ResponseReasoningItem( - id=FAKE_RESPONSES_ID, - summary=[Summary(text="", type="summary_text")], - type="reasoning", - ), + reasoning_item = ResponseReasoningItem( + id=FAKE_RESPONSES_ID, + summary=[Summary(text="", type="summary_text")], + type="reasoning", ) + if state.provider_data: + reasoning_item.provider_data = state.provider_data.copy() # type: ignore[attr-defined] + state.reasoning_content_index_and_output = (0, reasoning_item) yield ResponseOutputItemAddedEvent( - item=ResponseReasoningItem( - id=FAKE_RESPONSES_ID, - summary=[Summary(text="", type="summary_text")], - type="reasoning", - ), + item=reasoning_item, output_index=0, type="response.output_item.added", sequence_number=sequence_number.get_and_increment(), @@ -177,22 +195,17 @@ async def handle_stream( if hasattr(delta, "reasoning"): reasoning_text = delta.reasoning if reasoning_text and not state.reasoning_content_index_and_output: - state.reasoning_content_index_and_output = ( - 0, - ResponseReasoningItem( - id=FAKE_RESPONSES_ID, - summary=[], - content=[Content(text="", type="reasoning_text")], - type="reasoning", - ), + reasoning_item = ResponseReasoningItem( + id=FAKE_RESPONSES_ID, + summary=[], + content=[Content(text="", type="reasoning_text")], + type="reasoning", ) + if state.provider_data: + reasoning_item.provider_data = state.provider_data.copy() # type: ignore[attr-defined] + state.reasoning_content_index_and_output = (0, reasoning_item) yield ResponseOutputItemAddedEvent( - item=ResponseReasoningItem( - id=FAKE_RESPONSES_ID, - summary=[], - content=[Content(text="", type="reasoning_text")], - type="reasoning", - ), + item=reasoning_item, output_index=0, type="response.output_item.added", sequence_number=sequence_number.get_and_increment(), @@ -244,6 +257,8 @@ async def handle_stream( type="message", status="in_progress", ) + if state.provider_data: + assistant_item.provider_data = state.provider_data.copy() # type: ignore[attr-defined] # Notify consumers of the start of a new output message + first content part yield ResponseOutputItemAddedEvent( item=assistant_item, @@ -302,6 +317,8 @@ async def handle_stream( type="message", status="in_progress", ) + if state.provider_data: + assistant_item.provider_data = state.provider_data.copy() # type: ignore[attr-defined] # Notify downstream that assistant message + first content part are starting yield ResponseOutputItemAddedEvent( item=assistant_item, @@ -359,7 +376,60 @@ async def handle_stream( state.function_calls[tc_delta.index].name = tc_function.name if tc_delta.id: - state.function_calls[tc_delta.index].call_id = tc_delta.id + # Clean up litellm's addition of __thought__ suffix to tool_call.id for + # Gemini models. See: https://github.com/BerriAI/litellm/pull/16895 + # This suffix is redundant since we can get thought_signature from + # provider_specific_fields, and this hack causes validation errors when + # cross-model passing to other models. + tool_call_id = tc_delta.id + if model and "gemini" in model.lower() and "__thought__" in tool_call_id: + tool_call_id = tool_call_id.split("__thought__")[0] + + state.function_calls[tc_delta.index].call_id = tool_call_id + + # Initialize provider_data for this function call from state.provider_data + if not hasattr(state.function_calls[tc_delta.index], "provider_data"): + if state.provider_data: + state.function_calls[ + tc_delta.index + ].provider_data = state.provider_data.copy() # type: ignore[attr-defined] + + # Capture provider_specific_fields data from LiteLLM + if ( + hasattr(tc_delta, "provider_specific_fields") + and tc_delta.provider_specific_fields + ): + # Handle Gemini thought_signatures + if model and "gemini" in model.lower(): + provider_specific_fields = tc_delta.provider_specific_fields + if isinstance(provider_specific_fields, dict): + thought_sig = provider_specific_fields.get("thought_signature") + if thought_sig: + # Start with state.provider_data, then add thought_signature + func_provider_data = ( + state.provider_data.copy() if state.provider_data else {} + ) + func_provider_data["thought_signature"] = thought_sig + state.function_calls[ + tc_delta.index + ].provider_data = func_provider_data # type: ignore[attr-defined] + + # Capture extra_content data from Google's chatcmpl endpoint + if hasattr(tc_delta, "extra_content") and tc_delta.extra_content: + extra_content = tc_delta.extra_content + if isinstance(extra_content, dict): + google_fields = extra_content.get("google") + if google_fields and isinstance(google_fields, dict): + thought_sig = google_fields.get("thought_signature") + if thought_sig: + # Start with state.provider_data, then add thought_signature + func_provider_data = ( + state.provider_data.copy() if state.provider_data else {} + ) + func_provider_data["thought_signature"] = thought_sig + state.function_calls[ + tc_delta.index + ].provider_data = func_provider_data # type: ignore[attr-defined] function_call = state.function_calls[tc_delta.index] @@ -390,14 +460,28 @@ async def handle_stream( ) # Send initial function call added event + func_call_item = ResponseFunctionToolCall( + id=FAKE_RESPONSES_ID, + call_id=function_call.call_id, + arguments="", # Start with empty arguments + name=function_call.name, + type="function_call", + ) + # Merge provider_data from state and function_call (e.g. thought_signature) + if state.provider_data or ( + hasattr(function_call, "provider_data") and function_call.provider_data + ): + merged_provider_data = ( + state.provider_data.copy() if state.provider_data else {} + ) + if ( + hasattr(function_call, "provider_data") + and function_call.provider_data + ): + merged_provider_data.update(function_call.provider_data) + func_call_item.provider_data = merged_provider_data # type: ignore[attr-defined] yield ResponseOutputItemAddedEvent( - item=ResponseFunctionToolCall( - id=FAKE_RESPONSES_ID, - call_id=function_call.call_id, - arguments="", # Start with empty arguments - name=function_call.name, - type="function_call", - ), + item=func_call_item, output_index=function_call_starting_index, type="response.output_item.added", sequence_number=sequence_number.get_and_increment(), @@ -485,14 +569,27 @@ async def handle_stream( if state.function_call_streaming.get(index, False): # Function call was streamed, just send the completion event output_index = state.function_call_output_idx[index] + + # Build function call kwargs, include provider_data if present + func_call_kwargs: dict[str, Any] = { + "id": FAKE_RESPONSES_ID, + "call_id": function_call.call_id, + "arguments": function_call.arguments, + "name": function_call.name, + "type": "function_call", + } + + # Merge provider_data from state and function_call (e.g. thought_signature) + if state.provider_data or ( + hasattr(function_call, "provider_data") and function_call.provider_data + ): + merged_provider_data = state.provider_data.copy() if state.provider_data else {} + if hasattr(function_call, "provider_data") and function_call.provider_data: + merged_provider_data.update(function_call.provider_data) + func_call_kwargs["provider_data"] = merged_provider_data + yield ResponseOutputItemDoneEvent( - item=ResponseFunctionToolCall( - id=FAKE_RESPONSES_ID, - call_id=function_call.call_id, - arguments=function_call.arguments, - name=function_call.name, - type="function_call", - ), + item=ResponseFunctionToolCall(**func_call_kwargs), output_index=output_index, type="response.output_item.done", sequence_number=sequence_number.get_and_increment(), @@ -513,15 +610,27 @@ async def handle_stream( 1 for streaming in state.function_call_streaming.values() if streaming ) + # Build function call kwargs, include provider_data if present + fallback_func_call_kwargs: dict[str, Any] = { + "id": FAKE_RESPONSES_ID, + "call_id": function_call.call_id, + "arguments": function_call.arguments, + "name": function_call.name, + "type": "function_call", + } + + # Merge provider_data from state and function_call (e.g. thought_signature) + if state.provider_data or ( + hasattr(function_call, "provider_data") and function_call.provider_data + ): + merged_provider_data = state.provider_data.copy() if state.provider_data else {} + if hasattr(function_call, "provider_data") and function_call.provider_data: + merged_provider_data.update(function_call.provider_data) + fallback_func_call_kwargs["provider_data"] = merged_provider_data + # Send all events at once (backward compatibility) yield ResponseOutputItemAddedEvent( - item=ResponseFunctionToolCall( - id=FAKE_RESPONSES_ID, - call_id=function_call.call_id, - arguments=function_call.arguments, - name=function_call.name, - type="function_call", - ), + item=ResponseFunctionToolCall(**fallback_func_call_kwargs), output_index=fallback_starting_index, type="response.output_item.added", sequence_number=sequence_number.get_and_increment(), @@ -534,13 +643,7 @@ async def handle_stream( sequence_number=sequence_number.get_and_increment(), ) yield ResponseOutputItemDoneEvent( - item=ResponseFunctionToolCall( - id=FAKE_RESPONSES_ID, - call_id=function_call.call_id, - arguments=function_call.arguments, - name=function_call.name, - type="function_call", - ), + item=ResponseFunctionToolCall(**fallback_func_call_kwargs), output_index=fallback_starting_index, type="response.output_item.done", sequence_number=sequence_number.get_and_increment(), @@ -574,6 +677,8 @@ async def handle_stream( type="message", status="completed", ) + if state.provider_data: + assistant_msg.provider_data = state.provider_data.copy() # type: ignore[attr-defined] if state.text_content_index_and_output: assistant_msg.content.append(state.text_content_index_and_output[1]) if state.refusal_content_index_and_output: @@ -594,6 +699,7 @@ async def handle_stream( final_response = response.model_copy() final_response.output = outputs + final_response.usage = ( ResponseUsage( input_tokens=usage.prompt_tokens or 0, diff --git a/src/agents/models/openai_chatcompletions.py b/src/agents/models/openai_chatcompletions.py index 76f36d86b..22f370c74 100644 --- a/src/agents/models/openai_chatcompletions.py +++ b/src/agents/models/openai_chatcompletions.py @@ -117,7 +117,16 @@ async def get_response( "output_tokens": usage.output_tokens, } - items = Converter.message_to_output_items(message) if message is not None else [] + # Build provider_data for provider_specific_fields + provider_data = {"model": self.model} + if message is not None and hasattr(response, "id"): + provider_data["response_id"] = response.id + + items = ( + Converter.message_to_output_items(message, provider_data=provider_data) + if message is not None + else [] + ) return ModelResponse( output=items, @@ -160,7 +169,9 @@ async def stream_response( ) final_response: Response | None = None - async for chunk in ChatCmplStreamHandler.handle_stream(response, stream): + async for chunk in ChatCmplStreamHandler.handle_stream( + response, stream, model=self.model + ): yield chunk if chunk.type == "response.completed": @@ -218,7 +229,7 @@ async def _fetch_response( stream: bool = False, prompt: ResponsePromptParam | None = None, ) -> ChatCompletion | tuple[Response, AsyncStream[ChatCompletionChunk]]: - converted_messages = Converter.items_to_messages(input) + converted_messages = Converter.items_to_messages(input, model=self.model) if system_instructions: converted_messages.insert( diff --git a/src/agents/models/openai_responses.py b/src/agents/models/openai_responses.py index a8695c89c..45bf73fc6 100644 --- a/src/agents/models/openai_responses.py +++ b/src/agents/models/openai_responses.py @@ -43,6 +43,7 @@ from ..usage import Usage from ..util._json import _to_dump_compatible from ..version import __version__ +from .fake_id import FAKE_RESPONSES_ID from .interface import Model, ModelTracing if TYPE_CHECKING: @@ -253,6 +254,7 @@ async def _fetch_response( ) -> Response | AsyncStream[ResponseStreamEvent]: list_input = ItemHelpers.input_to_new_input_list(input) list_input = _to_dump_compatible(list_input) + list_input = self._remove_openai_responses_api_incompatible_fields(list_input) if model_settings.parallel_tool_calls and tools: parallel_tool_calls: bool | Omit = True @@ -342,6 +344,49 @@ async def _fetch_response( ) return cast(Union[Response, AsyncStream[ResponseStreamEvent]], response) + def _remove_openai_responses_api_incompatible_fields(self, list_input: list[Any]) -> list[Any]: + """ + Remove or transform input items that are incompatible with the OpenAI Responses API. + + Only items with truthy provider_data are processed. + This function handles the following incompatibilities: + - provider_data: Removes fields specific to other providers (e.g., Gemini, Claude). + - Fake IDs: Removes temporary IDs (FAKE_RESPONSES_ID) that should not be sent to OpenAI. + - Reasoning items: Filters out provider-specific reasoning items entirely. + """ + # Early return optimization: if no item has provider_data, return unchanged. + has_provider_data = any( + isinstance(item, dict) and item.get("provider_data") for item in list_input + ) + if not has_provider_data: + return list_input + + result = [] + for item in list_input: + cleaned = self._clean_item_for_openai(item) + if cleaned is not None: + result.append(cleaned) + return result + + def _clean_item_for_openai(self, item: Any) -> Any | None: + # Only process dict items + if not isinstance(item, dict): + return item + + # Filter out reasoning items with provider_data (provider-specific reasoning). + if item.get("type") == "reasoning" and item.get("provider_data"): + return None + + # Remove fake response ID. + if item.get("id") == FAKE_RESPONSES_ID: + del item["id"] + + # Remove provider_data field. + if "provider_data" in item: + del item["provider_data"] + + return item + def _get_client(self) -> AsyncOpenAI: if self._client is None: self._client = AsyncOpenAI() diff --git a/tests/test_anthropic_thinking_blocks.py b/tests/test_anthropic_thinking_blocks.py index 8fbc59833..63b89ba6d 100644 --- a/tests/test_anthropic_thinking_blocks.py +++ b/tests/test_anthropic_thinking_blocks.py @@ -183,7 +183,11 @@ def test_anthropic_thinking_blocks_with_tool_calls(): else: items_as_dicts.append(cast(dict[str, Any], item)) - messages = Converter.items_to_messages(items_as_dicts, preserve_thinking_blocks=True) # type: ignore[arg-type] + messages = Converter.items_to_messages( + items_as_dicts, # type: ignore[arg-type] + model="anthropic/claude-4-opus", + preserve_thinking_blocks=True, + ) # Find the assistant message with tool calls assistant_messages = [ diff --git a/tests/test_gemini_thought_signatures.py b/tests/test_gemini_thought_signatures.py new file mode 100644 index 000000000..42975414e --- /dev/null +++ b/tests/test_gemini_thought_signatures.py @@ -0,0 +1,126 @@ +""" +Test for Gemini thought signatures in function calling. + +Validates that thought signatures are preserved through the bidirectional roundtrip: +- Gemini chatcmpl message → response item → back to message +""" + +from __future__ import annotations + +from typing import Any + +from openai.types.chat.chat_completion_message_tool_call import Function + +from agents.extensions.models.litellm_model import InternalChatCompletionMessage, InternalToolCall +from agents.models.chatcmpl_converter import Converter + + +def test_gemini_thought_signature_roundtrip(): + """Test that thought signatures are preserved from Gemini responses to messages.""" + + # Create mock Gemini response with thought signature in new extra_content structure + class MockToolCall(InternalToolCall): + def __init__(self): + super().__init__( + id="call_123", + type="function", + function=Function(name="get_weather", arguments='{"city": "Paris"}'), + extra_content={"google": {"thought_signature": "test_signature_abc"}}, + ) + + message = InternalChatCompletionMessage( + role="assistant", + content="I'll check the weather.", + reasoning_content="", + tool_calls=[MockToolCall()], + ) + + # Step 1: Convert to items + provider_data = {"model": "gemini/gemini-3-pro", "response_id": "gemini-response-id-123"} + + items = Converter.message_to_output_items(message, provider_data=provider_data) + + func_calls = [item for item in items if hasattr(item, "type") and item.type == "function_call"] + assert len(func_calls) == 1 + + # Verify thought_signature is stored in items with our provider_data structure + func_call_dict = func_calls[0].model_dump() + + assert func_call_dict["provider_data"]["model"] == "gemini/gemini-3-pro" + assert func_call_dict["provider_data"]["response_id"] == "gemini-response-id-123" + assert func_call_dict["provider_data"]["thought_signature"] == "test_signature_abc" + + # Step 2: Convert back to messages + items_as_dicts = [item.model_dump() for item in items] + messages = Converter.items_to_messages( + [{"role": "user", "content": "test"}] + items_as_dicts, + model="gemini/gemini-3-pro", + ) + + # Verify thought_signature is restored in extra_content format + assistant_msg = [msg for msg in messages if msg.get("role") == "assistant"][0] + tool_call = assistant_msg["tool_calls"][0] # type: ignore[index, typeddict-item] + assert tool_call["extra_content"]["google"]["thought_signature"] == "test_signature_abc" + + +def test_gemini_multiple_tool_calls_with_thought_signatures(): + """Test multiple tool calls each preserve their own thought signatures.""" + tool_call_1 = InternalToolCall( + id="call_1", + type="function", + function=Function(name="func_a", arguments='{"x": 1}'), + extra_content={"google": {"thought_signature": "sig_aaa"}}, + ) + tool_call_2 = InternalToolCall( + id="call_2", + type="function", + function=Function(name="func_b", arguments='{"y": 2}'), + extra_content={"google": {"thought_signature": "sig_bbb"}}, + ) + + message = InternalChatCompletionMessage( + role="assistant", + content="Calling two functions.", + reasoning_content="", + tool_calls=[tool_call_1, tool_call_2], + ) + + provider_data = {"model": "gemini/gemini-3-pro"} + items = Converter.message_to_output_items(message, provider_data=provider_data) + + func_calls = [i for i in items if hasattr(i, "type") and i.type == "function_call"] + assert len(func_calls) == 2 + + assert func_calls[0].model_dump()["provider_data"]["thought_signature"] == "sig_aaa" + assert func_calls[1].model_dump()["provider_data"]["thought_signature"] == "sig_bbb" + + +def test_gemini_thought_signature_items_to_messages(): + """Test that items_to_messages restores extra_content from provider_data for Gemini.""" + + # Create a function call item with provider_data containing thought_signature + func_call_item = { + "id": "fake-id", + "call_id": "call_restore", + "name": "restore_func", + "arguments": '{"test": true}', + "type": "function_call", + "provider_data": { + "model": "gemini/gemini-3-pro", + "response_id": "gemini-response-id-123", + "thought_signature": "restored_sig_xyz", + }, + } + + items = [{"role": "user", "content": "test"}, func_call_item] + messages = Converter.items_to_messages(items, model="gemini/gemini-3-pro") # type: ignore[arg-type] + + # Find the assistant message with tool_calls + assistant_msgs = [m for m in messages if m.get("role") == "assistant"] + assert len(assistant_msgs) == 1 + + tool_calls: list[dict[str, Any]] = assistant_msgs[0].get("tool_calls", []) # type: ignore[assignment] + assert len(tool_calls) == 1 + + # Verify extra_content is restored in Google format + assert tool_calls[0]["extra_content"]["google"]["thought_signature"] == "restored_sig_xyz" diff --git a/tests/test_gemini_thought_signatures_stream.py b/tests/test_gemini_thought_signatures_stream.py new file mode 100644 index 000000000..22b7763a5 --- /dev/null +++ b/tests/test_gemini_thought_signatures_stream.py @@ -0,0 +1,210 @@ +""" +Test for Gemini thought signatures in streaming function calls. + +Validates that thought signatures are captured from streaming chunks +and included in the final function call events. +""" + +from __future__ import annotations + +from collections.abc import AsyncIterator +from typing import Any, cast + +import pytest +from openai.types.chat import ChatCompletionChunk +from openai.types.chat.chat_completion_chunk import ( + Choice, + ChoiceDelta, + ChoiceDeltaToolCall, + ChoiceDeltaToolCallFunction, +) +from openai.types.responses import Response + +from agents.models.chatcmpl_stream_handler import ChatCmplStreamHandler + +# ========== Helper Functions ========== + + +def create_tool_call_delta( + index: int, + tool_call_id: str | None = None, + function_name: str | None = None, + arguments: str | None = None, + provider_specific_fields: dict[str, Any] | None = None, + extra_content: dict[str, Any] | None = None, +) -> ChoiceDeltaToolCall: + """Create a tool call delta for streaming.""" + function = ChoiceDeltaToolCallFunction( + name=function_name, + arguments=arguments, + ) + + delta = ChoiceDeltaToolCall( + index=index, + id=tool_call_id, + type="function" if tool_call_id else None, + function=function, + ) + + # Add provider_specific_fields (litellm format) + if provider_specific_fields: + delta_any = cast(Any, delta) + delta_any.provider_specific_fields = provider_specific_fields + + # Add extra_content (Google chatcmpl format) + if extra_content: + delta_any = cast(Any, delta) + delta_any.extra_content = extra_content + + return delta + + +def create_chunk( + tool_calls: list[ChoiceDeltaToolCall] | None = None, + content: str | None = None, + include_usage: bool = False, +) -> ChatCompletionChunk: + """Create a ChatCompletionChunk for testing.""" + delta = ChoiceDelta( + content=content, + role="assistant" if content or tool_calls else None, + tool_calls=tool_calls, + ) + + chunk = ChatCompletionChunk( + id="chunk-id-123", + created=1, + model="gemini/gemini-3-pro", + object="chat.completion.chunk", + choices=[Choice(index=0, delta=delta, finish_reason=None)], + ) + + if include_usage: + from openai.types.completion_usage import CompletionUsage + + chunk.usage = CompletionUsage( + completion_tokens=10, + prompt_tokens=5, + total_tokens=15, + ) + + return chunk + + +def create_final_chunk() -> ChatCompletionChunk: + """Create a final chunk with finish_reason='tool_calls'.""" + return ChatCompletionChunk( + id="chunk-id-456", + created=1, + model="gemini/gemini-3-pro", + object="chat.completion.chunk", + choices=[Choice(index=0, delta=ChoiceDelta(), finish_reason="tool_calls")], + ) + + +async def create_fake_stream( + chunks: list[ChatCompletionChunk], +) -> AsyncIterator[ChatCompletionChunk]: + """Create an async iterator from chunks.""" + for chunk in chunks: + yield chunk + + +def create_mock_response() -> Response: + """Create a mock Response object.""" + return Response( + id="resp-id", + created_at=0, + model="gemini/gemini-3-pro", + object="response", + output=[], + tool_choice="auto", + tools=[], + parallel_tool_calls=False, + ) + + +# ========== Tests ========== + + +@pytest.mark.asyncio +async def test_stream_captures_litellmprovider_specific_fields_thought_signature(): + """Test streaming captures thought_signature from litellm's provider_specific_fields.""" + chunks = [ + create_chunk( + tool_calls=[ + create_tool_call_delta( + index=0, + tool_call_id="call_stream_1", + function_name="get_weather", + provider_specific_fields={"thought_signature": "litellm_sig_123"}, + ) + ] + ), + create_chunk(tool_calls=[create_tool_call_delta(index=0, arguments='{"city": "Tokyo"}')]), + create_final_chunk(), + ] + + response = create_mock_response() + stream = create_fake_stream(chunks) + + events = [] + async for event in ChatCmplStreamHandler.handle_stream( + response, + stream, # type: ignore[arg-type] + model="gemini/gemini-3-pro", + ): + events.append(event) + + # Find function call done event + done_events = [e for e in events if e.type == "response.output_item.done"] + func_done = [ + e for e in done_events if hasattr(e.item, "type") and e.item.type == "function_call" + ] + assert len(func_done) == 1 + + provider_data = func_done[0].item.model_dump().get("provider_data", {}) + assert provider_data.get("thought_signature") == "litellm_sig_123" + assert provider_data["model"] == "gemini/gemini-3-pro" + assert provider_data["response_id"] == "chunk-id-123" + + +@pytest.mark.asyncio +async def test_stream_captures_google_extra_content_thought_signature(): + """Test streaming captures thought_signature from Google's extra_content format.""" + chunks = [ + create_chunk( + tool_calls=[ + create_tool_call_delta( + index=0, + tool_call_id="call_stream_2", + function_name="search", + extra_content={"google": {"thought_signature": "google_sig_456"}}, + ) + ] + ), + create_chunk(tool_calls=[create_tool_call_delta(index=0, arguments='{"query": "test"}')]), + create_final_chunk(), + ] + + response = create_mock_response() + stream = create_fake_stream(chunks) + + events = [] + async for event in ChatCmplStreamHandler.handle_stream( + response, + stream, # type: ignore[arg-type] + model="gemini/gemini-3-pro", + ): + events.append(event) + + done_events = [e for e in events if e.type == "response.output_item.done"] + func_done = [ + e for e in done_events if hasattr(e.item, "type") and e.item.type == "function_call" + ] + assert len(func_done) == 1 + + provider_data = func_done[0].item.model_dump().get("provider_data", {}) + assert provider_data.get("thought_signature") == "google_sig_456" + assert provider_data["model"] == "gemini/gemini-3-pro" + assert provider_data["response_id"] == "chunk-id-123" diff --git a/tests/test_model_payload_iterators.py b/tests/test_model_payload_iterators.py index 3d7b9edc6..5c16db07c 100644 --- a/tests/test_model_payload_iterators.py +++ b/tests/test_model_payload_iterators.py @@ -60,7 +60,7 @@ async def test_chat_completions_materializes_iterator_payload( monkeypatch.setattr( chat_converter, "items_to_messages", - classmethod(lambda _cls, _input: [{"role": "user", "content": message_iter}]), + classmethod(lambda _cls, _input, **kwargs: [{"role": "user", "content": message_iter}]), ) monkeypatch.setattr( chat_converter, diff --git a/tests/test_remove_openai_responses_api_incompatible_fields.py b/tests/test_remove_openai_responses_api_incompatible_fields.py new file mode 100644 index 000000000..87c91196b --- /dev/null +++ b/tests/test_remove_openai_responses_api_incompatible_fields.py @@ -0,0 +1,162 @@ +from __future__ import annotations + +from typing import Any +from unittest.mock import MagicMock + +import pytest + +from agents.models.fake_id import FAKE_RESPONSES_ID +from agents.models.openai_responses import OpenAIResponsesModel + + +@pytest.fixture +def model() -> OpenAIResponsesModel: + """Create a model instance for testing.""" + mock_client = MagicMock() + return OpenAIResponsesModel(model="gpt-5", openai_client=mock_client) + + +class TestRemoveOpenAIResponsesAPIIncompatibleFields: + """Tests for _remove_openai_responses_api_incompatible_fields method.""" + + def test_returns_unchanged_when_no_provider_data(self, model: OpenAIResponsesModel): + """When no items have provider_data, the input should be returned unchanged.""" + list_input = [ + {"type": "message", "content": "hello"}, + {"type": "function_call", "call_id": "call_123", "name": "test"}, + ] + + result = model._remove_openai_responses_api_incompatible_fields(list_input) + + assert result is list_input # Same object reference. + + def test_removes_reasoning_items_with_provider_data(self, model: OpenAIResponsesModel): + """Reasoning items with provider_data should be completely removed.""" + list_input = [ + {"type": "message", "content": "hello"}, + {"type": "reasoning", "provider_data": {"model": "gemini/gemini-3"}}, + {"type": "function_call", "call_id": "call_123"}, + ] + + result = model._remove_openai_responses_api_incompatible_fields(list_input) + + assert len(result) == 2 + assert result[0] == {"type": "message", "content": "hello"} + assert result[1] == {"type": "function_call", "call_id": "call_123"} + + def test_keeps_reasoning_items_without_provider_data(self, model: OpenAIResponsesModel): + """Reasoning items without provider_data should be kept.""" + list_input = [ + {"type": "reasoning", "summary": []}, + {"type": "message", "content": "hello", "provider_data": {"foo": "bar"}}, + ] + + result = model._remove_openai_responses_api_incompatible_fields(list_input) + + assert len(result) == 2 + assert result[0] == {"type": "reasoning", "summary": []} + assert result[1] == {"type": "message", "content": "hello"} + + def test_removes_provider_data_from_all_items(self, model: OpenAIResponsesModel): + """provider_data field should be removed from all dict items.""" + list_input = [ + {"type": "message", "content": "hello", "provider_data": {"model": "gemini/gemini-3"}}, + { + "type": "function_call", + "call_id": "call_123", + "provider_data": {"model": "gemini/gemini-3"}, + }, + ] + + result = model._remove_openai_responses_api_incompatible_fields(list_input) + + assert len(result) == 2 + assert "provider_data" not in result[0] + assert "provider_data" not in result[1] + + def test_removes_fake_responses_id(self, model: OpenAIResponsesModel): + """Items with id equal to FAKE_RESPONSES_ID should have their id removed.""" + list_input = [ + { + "type": "message", + "id": FAKE_RESPONSES_ID, + "content": "hello", + "provider_data": {"model": "gemini/gemini-3"}, + }, + ] + + result = model._remove_openai_responses_api_incompatible_fields(list_input) + + assert len(result) == 1 + assert "id" not in result[0] + assert result[0]["content"] == "hello" + + def test_preserves_real_ids(self, model: OpenAIResponsesModel): + """Real IDs (not FAKE_RESPONSES_ID) should be preserved.""" + list_input = [ + { + "type": "message", + "id": "msg_real123", + "content": "hello", + "provider_data": {}, + }, + ] + + result = model._remove_openai_responses_api_incompatible_fields(list_input) + + assert result[0]["id"] == "msg_real123" + + def test_handles_empty_list(self, model: OpenAIResponsesModel): + """Empty list should be returned unchanged.""" + list_input: list[dict[str, Any]] = [] + + result = model._remove_openai_responses_api_incompatible_fields(list_input) + + assert result == [] + + def test_combined_scenario(self, model: OpenAIResponsesModel): + """Test a realistic scenario with multiple items needing different processing.""" + list_input = [ + {"type": "message", "content": "user input"}, + {"type": "reasoning", "summary": [], "provider_data": {"model": "gemini/gemini-3"}}, + { + "type": "function_call", + "call_id": "call_abc_123", + "name": "get_weather", + "provider_data": {"model": "gemini/gemini-3"}, + }, + { + "type": "function_call_output", + "call_id": "call_abc_123", + "output": '{"temp": 72}', + }, + { + "type": "message", + "id": FAKE_RESPONSES_ID, + "content": "The weather is 72F", + "provider_data": {"model": "gemini/gemini-3"}, + }, + ] + + result = model._remove_openai_responses_api_incompatible_fields(list_input) + + # Should have 4 items (reasoning with provider_data removed). + assert len(result) == 4 + + # First item unchanged (no provider_data). + assert result[0] == {"type": "message", "content": "user input"} + + # Function call: __thought__ suffix removed, provider_data removed. + assert result[1]["type"] == "function_call" + assert result[1]["call_id"] == "call_abc_123" + assert "provider_data" not in result[1] + + # Function call output: __thought__ suffix removed, provider_data removed. + assert result[2]["type"] == "function_call_output" + assert result[2]["call_id"] == "call_abc_123" + + # Last message: fake id removed, provider_data removed. + assert result[3]["type"] == "message" + assert result[3]["content"] == "The weather is 72F" + assert "id" not in result[3] + assert "provider_data" not in result[3] diff --git a/uv.lock b/uv.lock index 128a6451c..269ca2afa 100644 --- a/uv.lock +++ b/uv.lock @@ -719,6 +719,80 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e5/47/d63c60f59a59467fda0f93f46335c9d18526d7071f025cb5b89d5353ea42/fastapi-0.116.1-py3-none-any.whl", hash = "sha256:c46ac7c312df840f0c9e220f7964bada936781bc4e2e6eb71f1c4d7553786565", size = 95631, upload-time = "2025-07-11T16:22:30.485Z" }, ] +[[package]] +name = "fastuuid" +version = "0.14.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c3/7d/d9daedf0f2ebcacd20d599928f8913e9d2aea1d56d2d355a93bfa2b611d7/fastuuid-0.14.0.tar.gz", hash = "sha256:178947fc2f995b38497a74172adee64fdeb8b7ec18f2a5934d037641ba265d26", size = 18232, upload-time = "2025-10-19T22:19:22.402Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ad/b2/731a6696e37cd20eed353f69a09f37a984a43c9713764ee3f7ad5f57f7f9/fastuuid-0.14.0-cp310-cp310-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:6e6243d40f6c793c3e2ee14c13769e341b90be5ef0c23c82fa6515a96145181a", size = 516760, upload-time = "2025-10-19T22:25:21.509Z" }, + { url = "https://files.pythonhosted.org/packages/c5/79/c73c47be2a3b8734d16e628982653517f80bbe0570e27185d91af6096507/fastuuid-0.14.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:13ec4f2c3b04271f62be2e1ce7e95ad2dd1cf97e94503a3760db739afbd48f00", size = 264748, upload-time = "2025-10-19T22:41:52.873Z" }, + { url = "https://files.pythonhosted.org/packages/24/c5/84c1eea05977c8ba5173555b0133e3558dc628bcf868d6bf1689ff14aedc/fastuuid-0.14.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b2fdd48b5e4236df145a149d7125badb28e0a383372add3fbaac9a6b7a394470", size = 254537, upload-time = "2025-10-19T22:33:55.603Z" }, + { url = "https://files.pythonhosted.org/packages/0e/23/4e362367b7fa17dbed646922f216b9921efb486e7abe02147e4b917359f8/fastuuid-0.14.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f74631b8322d2780ebcf2d2d75d58045c3e9378625ec51865fe0b5620800c39d", size = 278994, upload-time = "2025-10-19T22:26:17.631Z" }, + { url = "https://files.pythonhosted.org/packages/b2/72/3985be633b5a428e9eaec4287ed4b873b7c4c53a9639a8b416637223c4cd/fastuuid-0.14.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:83cffc144dc93eb604b87b179837f2ce2af44871a7b323f2bfed40e8acb40ba8", size = 280003, upload-time = "2025-10-19T22:23:45.415Z" }, + { url = "https://files.pythonhosted.org/packages/b3/6d/6ef192a6df34e2266d5c9deb39cd3eea986df650cbcfeaf171aa52a059c3/fastuuid-0.14.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1a771f135ab4523eb786e95493803942a5d1fc1610915f131b363f55af53b219", size = 303583, upload-time = "2025-10-19T22:26:00.756Z" }, + { url = "https://files.pythonhosted.org/packages/9d/11/8a2ea753c68d4fece29d5d7c6f3f903948cc6e82d1823bc9f7f7c0355db3/fastuuid-0.14.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4edc56b877d960b4eda2c4232f953a61490c3134da94f3c28af129fb9c62a4f6", size = 460955, upload-time = "2025-10-19T22:36:25.196Z" }, + { url = "https://files.pythonhosted.org/packages/23/42/7a32c93b6ce12642d9a152ee4753a078f372c9ebb893bc489d838dd4afd5/fastuuid-0.14.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:bcc96ee819c282e7c09b2eed2b9bd13084e3b749fdb2faf58c318d498df2efbe", size = 480763, upload-time = "2025-10-19T22:24:28.451Z" }, + { url = "https://files.pythonhosted.org/packages/b9/e9/a5f6f686b46e3ed4ed3b93770111c233baac87dd6586a411b4988018ef1d/fastuuid-0.14.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7a3c0bca61eacc1843ea97b288d6789fbad7400d16db24e36a66c28c268cfe3d", size = 452613, upload-time = "2025-10-19T22:25:06.827Z" }, + { url = "https://files.pythonhosted.org/packages/b4/c9/18abc73c9c5b7fc0e476c1733b678783b2e8a35b0be9babd423571d44e98/fastuuid-0.14.0-cp310-cp310-win32.whl", hash = "sha256:7f2f3efade4937fae4e77efae1af571902263de7b78a0aee1a1653795a093b2a", size = 155045, upload-time = "2025-10-19T22:28:32.732Z" }, + { url = "https://files.pythonhosted.org/packages/5e/8a/d9e33f4eb4d4f6d9f2c5c7d7e96b5cdbb535c93f3b1ad6acce97ee9d4bf8/fastuuid-0.14.0-cp310-cp310-win_amd64.whl", hash = "sha256:ae64ba730d179f439b0736208b4c279b8bc9c089b102aec23f86512ea458c8a4", size = 156122, upload-time = "2025-10-19T22:23:15.59Z" }, + { url = "https://files.pythonhosted.org/packages/98/f3/12481bda4e5b6d3e698fbf525df4443cc7dce746f246b86b6fcb2fba1844/fastuuid-0.14.0-cp311-cp311-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:73946cb950c8caf65127d4e9a325e2b6be0442a224fd51ba3b6ac44e1912ce34", size = 516386, upload-time = "2025-10-19T22:42:40.176Z" }, + { url = "https://files.pythonhosted.org/packages/59/19/2fc58a1446e4d72b655648eb0879b04e88ed6fa70d474efcf550f640f6ec/fastuuid-0.14.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:12ac85024637586a5b69645e7ed986f7535106ed3013640a393a03e461740cb7", size = 264569, upload-time = "2025-10-19T22:25:50.977Z" }, + { url = "https://files.pythonhosted.org/packages/78/29/3c74756e5b02c40cfcc8b1d8b5bac4edbd532b55917a6bcc9113550e99d1/fastuuid-0.14.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:05a8dde1f395e0c9b4be515b7a521403d1e8349443e7641761af07c7ad1624b1", size = 254366, upload-time = "2025-10-19T22:29:49.166Z" }, + { url = "https://files.pythonhosted.org/packages/52/96/d761da3fccfa84f0f353ce6e3eb8b7f76b3aa21fd25e1b00a19f9c80a063/fastuuid-0.14.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09378a05020e3e4883dfdab438926f31fea15fd17604908f3d39cbeb22a0b4dc", size = 278978, upload-time = "2025-10-19T22:35:41.306Z" }, + { url = "https://files.pythonhosted.org/packages/fc/c2/f84c90167cc7765cb82b3ff7808057608b21c14a38531845d933a4637307/fastuuid-0.14.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbb0c4b15d66b435d2538f3827f05e44e2baafcc003dd7d8472dc67807ab8fd8", size = 279692, upload-time = "2025-10-19T22:25:36.997Z" }, + { url = "https://files.pythonhosted.org/packages/af/7b/4bacd03897b88c12348e7bd77943bac32ccf80ff98100598fcff74f75f2e/fastuuid-0.14.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cd5a7f648d4365b41dbf0e38fe8da4884e57bed4e77c83598e076ac0c93995e7", size = 303384, upload-time = "2025-10-19T22:29:46.578Z" }, + { url = "https://files.pythonhosted.org/packages/c0/a2/584f2c29641df8bd810d00c1f21d408c12e9ad0c0dafdb8b7b29e5ddf787/fastuuid-0.14.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c0a94245afae4d7af8c43b3159d5e3934c53f47140be0be624b96acd672ceb73", size = 460921, upload-time = "2025-10-19T22:36:42.006Z" }, + { url = "https://files.pythonhosted.org/packages/24/68/c6b77443bb7764c760e211002c8638c0c7cce11cb584927e723215ba1398/fastuuid-0.14.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:2b29e23c97e77c3a9514d70ce343571e469098ac7f5a269320a0f0b3e193ab36", size = 480575, upload-time = "2025-10-19T22:28:18.975Z" }, + { url = "https://files.pythonhosted.org/packages/5a/87/93f553111b33f9bb83145be12868c3c475bf8ea87c107063d01377cc0e8e/fastuuid-0.14.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1e690d48f923c253f28151b3a6b4e335f2b06bf669c68a02665bc150b7839e94", size = 452317, upload-time = "2025-10-19T22:25:32.75Z" }, + { url = "https://files.pythonhosted.org/packages/9e/8c/a04d486ca55b5abb7eaa65b39df8d891b7b1635b22db2163734dc273579a/fastuuid-0.14.0-cp311-cp311-win32.whl", hash = "sha256:a6f46790d59ab38c6aa0e35c681c0484b50dc0acf9e2679c005d61e019313c24", size = 154804, upload-time = "2025-10-19T22:24:15.615Z" }, + { url = "https://files.pythonhosted.org/packages/9c/b2/2d40bf00820de94b9280366a122cbaa60090c8cf59e89ac3938cf5d75895/fastuuid-0.14.0-cp311-cp311-win_amd64.whl", hash = "sha256:e150eab56c95dc9e3fefc234a0eedb342fac433dacc273cd4d150a5b0871e1fa", size = 156099, upload-time = "2025-10-19T22:24:31.646Z" }, + { url = "https://files.pythonhosted.org/packages/02/a2/e78fcc5df65467f0d207661b7ef86c5b7ac62eea337c0c0fcedbeee6fb13/fastuuid-0.14.0-cp312-cp312-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:77e94728324b63660ebf8adb27055e92d2e4611645bf12ed9d88d30486471d0a", size = 510164, upload-time = "2025-10-19T22:31:45.635Z" }, + { url = "https://files.pythonhosted.org/packages/2b/b3/c846f933f22f581f558ee63f81f29fa924acd971ce903dab1a9b6701816e/fastuuid-0.14.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:caa1f14d2102cb8d353096bc6ef6c13b2c81f347e6ab9d6fbd48b9dea41c153d", size = 261837, upload-time = "2025-10-19T22:38:38.53Z" }, + { url = "https://files.pythonhosted.org/packages/54/ea/682551030f8c4fa9a769d9825570ad28c0c71e30cf34020b85c1f7ee7382/fastuuid-0.14.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d23ef06f9e67163be38cece704170486715b177f6baae338110983f99a72c070", size = 251370, upload-time = "2025-10-19T22:40:26.07Z" }, + { url = "https://files.pythonhosted.org/packages/14/dd/5927f0a523d8e6a76b70968e6004966ee7df30322f5fc9b6cdfb0276646a/fastuuid-0.14.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0c9ec605ace243b6dbe3bd27ebdd5d33b00d8d1d3f580b39fdd15cd96fd71796", size = 277766, upload-time = "2025-10-19T22:37:23.779Z" }, + { url = "https://files.pythonhosted.org/packages/16/6e/c0fb547eef61293153348f12e0f75a06abb322664b34a1573a7760501336/fastuuid-0.14.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:808527f2407f58a76c916d6aa15d58692a4a019fdf8d4c32ac7ff303b7d7af09", size = 278105, upload-time = "2025-10-19T22:26:56.821Z" }, + { url = "https://files.pythonhosted.org/packages/2d/b1/b9c75e03b768f61cf2e84ee193dc18601aeaf89a4684b20f2f0e9f52b62c/fastuuid-0.14.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2fb3c0d7fef6674bbeacdd6dbd386924a7b60b26de849266d1ff6602937675c8", size = 301564, upload-time = "2025-10-19T22:30:31.604Z" }, + { url = "https://files.pythonhosted.org/packages/fc/fa/f7395fdac07c7a54f18f801744573707321ca0cee082e638e36452355a9d/fastuuid-0.14.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ab3f5d36e4393e628a4df337c2c039069344db5f4b9d2a3c9cea48284f1dd741", size = 459659, upload-time = "2025-10-19T22:31:32.341Z" }, + { url = "https://files.pythonhosted.org/packages/66/49/c9fd06a4a0b1f0f048aacb6599e7d96e5d6bc6fa680ed0d46bf111929d1b/fastuuid-0.14.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:b9a0ca4f03b7e0b01425281ffd44e99d360e15c895f1907ca105854ed85e2057", size = 478430, upload-time = "2025-10-19T22:26:22.962Z" }, + { url = "https://files.pythonhosted.org/packages/be/9c/909e8c95b494e8e140e8be6165d5fc3f61fdc46198c1554df7b3e1764471/fastuuid-0.14.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3acdf655684cc09e60fb7e4cf524e8f42ea760031945aa8086c7eae2eeeabeb8", size = 450894, upload-time = "2025-10-19T22:27:01.647Z" }, + { url = "https://files.pythonhosted.org/packages/90/eb/d29d17521976e673c55ef7f210d4cdd72091a9ec6755d0fd4710d9b3c871/fastuuid-0.14.0-cp312-cp312-win32.whl", hash = "sha256:9579618be6280700ae36ac42c3efd157049fe4dd40ca49b021280481c78c3176", size = 154374, upload-time = "2025-10-19T22:29:19.879Z" }, + { url = "https://files.pythonhosted.org/packages/cc/fc/f5c799a6ea6d877faec0472d0b27c079b47c86b1cdc577720a5386483b36/fastuuid-0.14.0-cp312-cp312-win_amd64.whl", hash = "sha256:d9e4332dc4ba054434a9594cbfaf7823b57993d7d8e7267831c3e059857cf397", size = 156550, upload-time = "2025-10-19T22:27:49.658Z" }, + { url = "https://files.pythonhosted.org/packages/a5/83/ae12dd39b9a39b55d7f90abb8971f1a5f3c321fd72d5aa83f90dc67fe9ed/fastuuid-0.14.0-cp313-cp313-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:77a09cb7427e7af74c594e409f7731a0cf887221de2f698e1ca0ebf0f3139021", size = 510720, upload-time = "2025-10-19T22:42:34.633Z" }, + { url = "https://files.pythonhosted.org/packages/53/b0/a4b03ff5d00f563cc7546b933c28cb3f2a07344b2aec5834e874f7d44143/fastuuid-0.14.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:9bd57289daf7b153bfa3e8013446aa144ce5e8c825e9e366d455155ede5ea2dc", size = 262024, upload-time = "2025-10-19T22:30:25.482Z" }, + { url = "https://files.pythonhosted.org/packages/9c/6d/64aee0a0f6a58eeabadd582e55d0d7d70258ffdd01d093b30c53d668303b/fastuuid-0.14.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ac60fc860cdf3c3f327374db87ab8e064c86566ca8c49d2e30df15eda1b0c2d5", size = 251679, upload-time = "2025-10-19T22:36:14.096Z" }, + { url = "https://files.pythonhosted.org/packages/60/f5/a7e9cda8369e4f7919d36552db9b2ae21db7915083bc6336f1b0082c8b2e/fastuuid-0.14.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ab32f74bd56565b186f036e33129da77db8be09178cd2f5206a5d4035fb2a23f", size = 277862, upload-time = "2025-10-19T22:36:23.302Z" }, + { url = "https://files.pythonhosted.org/packages/f0/d3/8ce11827c783affffd5bd4d6378b28eb6cc6d2ddf41474006b8d62e7448e/fastuuid-0.14.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:33e678459cf4addaedd9936bbb038e35b3f6b2061330fd8f2f6a1d80414c0f87", size = 278278, upload-time = "2025-10-19T22:29:43.809Z" }, + { url = "https://files.pythonhosted.org/packages/a2/51/680fb6352d0bbade04036da46264a8001f74b7484e2fd1f4da9e3db1c666/fastuuid-0.14.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1e3cc56742f76cd25ecb98e4b82a25f978ccffba02e4bdce8aba857b6d85d87b", size = 301788, upload-time = "2025-10-19T22:36:06.825Z" }, + { url = "https://files.pythonhosted.org/packages/fa/7c/2014b5785bd8ebdab04ec857635ebd84d5ee4950186a577db9eff0fb8ff6/fastuuid-0.14.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:cb9a030f609194b679e1660f7e32733b7a0f332d519c5d5a6a0a580991290022", size = 459819, upload-time = "2025-10-19T22:35:31.623Z" }, + { url = "https://files.pythonhosted.org/packages/01/d2/524d4ceeba9160e7a9bc2ea3e8f4ccf1ad78f3bde34090ca0c51f09a5e91/fastuuid-0.14.0-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:09098762aad4f8da3a888eb9ae01c84430c907a297b97166b8abc07b640f2995", size = 478546, upload-time = "2025-10-19T22:26:03.023Z" }, + { url = "https://files.pythonhosted.org/packages/bc/17/354d04951ce114bf4afc78e27a18cfbd6ee319ab1829c2d5fb5e94063ac6/fastuuid-0.14.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:1383fff584fa249b16329a059c68ad45d030d5a4b70fb7c73a08d98fd53bcdab", size = 450921, upload-time = "2025-10-19T22:31:02.151Z" }, + { url = "https://files.pythonhosted.org/packages/fb/be/d7be8670151d16d88f15bb121c5b66cdb5ea6a0c2a362d0dcf30276ade53/fastuuid-0.14.0-cp313-cp313-win32.whl", hash = "sha256:a0809f8cc5731c066c909047f9a314d5f536c871a7a22e815cc4967c110ac9ad", size = 154559, upload-time = "2025-10-19T22:36:36.011Z" }, + { url = "https://files.pythonhosted.org/packages/22/1d/5573ef3624ceb7abf4a46073d3554e37191c868abc3aecd5289a72f9810a/fastuuid-0.14.0-cp313-cp313-win_amd64.whl", hash = "sha256:0df14e92e7ad3276327631c9e7cec09e32572ce82089c55cb1bb8df71cf394ed", size = 156539, upload-time = "2025-10-19T22:33:35.898Z" }, + { url = "https://files.pythonhosted.org/packages/16/c9/8c7660d1fe3862e3f8acabd9be7fc9ad71eb270f1c65cce9a2b7a31329ab/fastuuid-0.14.0-cp314-cp314-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:b852a870a61cfc26c884af205d502881a2e59cc07076b60ab4a951cc0c94d1ad", size = 510600, upload-time = "2025-10-19T22:43:44.17Z" }, + { url = "https://files.pythonhosted.org/packages/4c/f4/a989c82f9a90d0ad995aa957b3e572ebef163c5299823b4027986f133dfb/fastuuid-0.14.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:c7502d6f54cd08024c3ea9b3514e2d6f190feb2f46e6dbcd3747882264bb5f7b", size = 262069, upload-time = "2025-10-19T22:43:38.38Z" }, + { url = "https://files.pythonhosted.org/packages/da/6c/a1a24f73574ac995482b1326cf7ab41301af0fabaa3e37eeb6b3df00e6e2/fastuuid-0.14.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1ca61b592120cf314cfd66e662a5b54a578c5a15b26305e1b8b618a6f22df714", size = 251543, upload-time = "2025-10-19T22:32:22.537Z" }, + { url = "https://files.pythonhosted.org/packages/1a/20/2a9b59185ba7a6c7b37808431477c2d739fcbdabbf63e00243e37bd6bf49/fastuuid-0.14.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa75b6657ec129d0abded3bec745e6f7ab642e6dba3a5272a68247e85f5f316f", size = 277798, upload-time = "2025-10-19T22:33:53.821Z" }, + { url = "https://files.pythonhosted.org/packages/ef/33/4105ca574f6ded0af6a797d39add041bcfb468a1255fbbe82fcb6f592da2/fastuuid-0.14.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8a0dfea3972200f72d4c7df02c8ac70bad1bb4c58d7e0ec1e6f341679073a7f", size = 278283, upload-time = "2025-10-19T22:29:02.812Z" }, + { url = "https://files.pythonhosted.org/packages/fe/8c/fca59f8e21c4deb013f574eae05723737ddb1d2937ce87cb2a5d20992dc3/fastuuid-0.14.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1bf539a7a95f35b419f9ad105d5a8a35036df35fdafae48fb2fd2e5f318f0d75", size = 301627, upload-time = "2025-10-19T22:35:54.985Z" }, + { url = "https://files.pythonhosted.org/packages/cb/e2/f78c271b909c034d429218f2798ca4e89eeda7983f4257d7865976ddbb6c/fastuuid-0.14.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:9a133bf9cc78fdbd1179cb58a59ad0100aa32d8675508150f3658814aeefeaa4", size = 459778, upload-time = "2025-10-19T22:28:00.999Z" }, + { url = "https://files.pythonhosted.org/packages/1e/f0/5ff209d865897667a2ff3e7a572267a9ced8f7313919f6d6043aed8b1caa/fastuuid-0.14.0-cp314-cp314-musllinux_1_1_i686.whl", hash = "sha256:f54d5b36c56a2d5e1a31e73b950b28a0d83eb0c37b91d10408875a5a29494bad", size = 478605, upload-time = "2025-10-19T22:36:21.764Z" }, + { url = "https://files.pythonhosted.org/packages/e0/c8/2ce1c78f983a2c4987ea865d9516dbdfb141a120fd3abb977ae6f02ba7ca/fastuuid-0.14.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:ec27778c6ca3393ef662e2762dba8af13f4ec1aaa32d08d77f71f2a70ae9feb8", size = 450837, upload-time = "2025-10-19T22:34:37.178Z" }, + { url = "https://files.pythonhosted.org/packages/df/60/dad662ec9a33b4a5fe44f60699258da64172c39bd041da2994422cdc40fe/fastuuid-0.14.0-cp314-cp314-win32.whl", hash = "sha256:e23fc6a83f112de4be0cc1990e5b127c27663ae43f866353166f87df58e73d06", size = 154532, upload-time = "2025-10-19T22:35:18.217Z" }, + { url = "https://files.pythonhosted.org/packages/1f/f6/da4db31001e854025ffd26bc9ba0740a9cbba2c3259695f7c5834908b336/fastuuid-0.14.0-cp314-cp314-win_amd64.whl", hash = "sha256:df61342889d0f5e7a32f7284e55ef95103f2110fee433c2ae7c2c0956d76ac8a", size = 156457, upload-time = "2025-10-19T22:33:44.579Z" }, + { url = "https://files.pythonhosted.org/packages/47/fe/d611ad94b497592851ac290a481ad158c7d2f529687b1b24fe127f3207b1/fastuuid-0.14.0-cp39-cp39-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:2ec3d94e13712a133137b2805073b65ecef4a47217d5bac15d8ac62376cefdb4", size = 520953, upload-time = "2025-10-19T22:21:25.674Z" }, + { url = "https://files.pythonhosted.org/packages/ed/c5/27ef7b7fee0f5b64b8ad37ede747f3dfe83d51f900630857f3816bef6718/fastuuid-0.14.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:139d7ff12bb400b4a0c76be64c28cbe2e2edf60b09826cbfd85f33ed3d0bbe8b", size = 266936, upload-time = "2025-10-19T22:23:44.964Z" }, + { url = "https://files.pythonhosted.org/packages/f2/9e/758e0d751ed53099e2fe4c98a1a26c537dca0cbcdb35a67b2ccfe2619e76/fastuuid-0.14.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d55b7e96531216fc4f071909e33e35e5bfa47962ae67d9e84b00a04d6e8b7173", size = 256429, upload-time = "2025-10-19T22:28:10.254Z" }, + { url = "https://files.pythonhosted.org/packages/f5/98/c756318f410625998645f7bcfbb71ad77f87dfccdf14184d87be572e4fbe/fastuuid-0.14.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c0eb25f0fd935e376ac4334927a59e7c823b36062080e2e13acbaf2af15db836", size = 281295, upload-time = "2025-10-19T22:24:19.608Z" }, + { url = "https://files.pythonhosted.org/packages/ac/f6/5834139f5f2a3a53345a8a25c059e4dc2f9d93d23c5bb143b63bae78361b/fastuuid-0.14.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:089c18018fdbdda88a6dafd7d139f8703a1e7c799618e33ea25eb52503d28a11", size = 281725, upload-time = "2025-10-19T22:20:45.253Z" }, + { url = "https://files.pythonhosted.org/packages/53/5f/3576924624e74cf2471498aeac291f4979201a57ff191554628c8a04b315/fastuuid-0.14.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2fc37479517d4d70c08696960fad85494a8a7a0af4e93e9a00af04d74c59f9e3", size = 305706, upload-time = "2025-10-19T22:24:26.585Z" }, + { url = "https://files.pythonhosted.org/packages/c5/9a/476218621a9aa2e6aec0c5352c5f76d2c8b431e4e6c621b9b585c7e49605/fastuuid-0.14.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:73657c9f778aba530bc96a943d30e1a7c80edb8278df77894fe9457540df4f85", size = 462622, upload-time = "2025-10-19T22:31:32.377Z" }, + { url = "https://files.pythonhosted.org/packages/d7/39/0696748e8b72bd784ac124b49b9e2a78581772f101576e2d48894b3b2b76/fastuuid-0.14.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:d31f8c257046b5617fc6af9c69be066d2412bdef1edaa4bdf6a214cf57806105", size = 482829, upload-time = "2025-10-19T22:22:24.219Z" }, + { url = "https://files.pythonhosted.org/packages/ab/2a/378385aaacf410fd06d6689f1f1ad9ad124e7e6d4879dac40306e229fe70/fastuuid-0.14.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5816d41f81782b209843e52fdef757a361b448d782452d96abedc53d545da722", size = 454153, upload-time = "2025-10-19T22:21:43.206Z" }, + { url = "https://files.pythonhosted.org/packages/ab/e0/750ce4b46ca4a24dd8cd9129276f86f50a0397943e401a3b609a41a69101/fastuuid-0.14.0-cp39-cp39-win32.whl", hash = "sha256:448aa6833f7a84bfe37dd47e33df83250f404d591eb83527fa2cac8d1e57d7f3", size = 157059, upload-time = "2025-10-19T22:22:21.084Z" }, + { url = "https://files.pythonhosted.org/packages/3b/39/488bc15a941342484d2a7dc077a026de6242d59589a16bce35fb59d3611f/fastuuid-0.14.0-cp39-cp39-win_amd64.whl", hash = "sha256:84b0779c5abbdec2a9511d5ffbfcd2e53079bf889824b32be170c0d8ef5fc74c", size = 158082, upload-time = "2025-10-19T22:21:34.817Z" }, +] + [[package]] name = "filelock" version = "3.18.0" @@ -968,87 +1042,69 @@ wheels = [ [[package]] name = "grpcio" -version = "1.75.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/9d/f7/8963848164c7604efb3a3e6ee457fdb3a469653e19002bd24742473254f8/grpcio-1.75.1.tar.gz", hash = "sha256:3e81d89ece99b9ace23a6916880baca613c03a799925afb2857887efa8b1b3d2", size = 12731327, upload-time = "2025-09-26T09:03:36.887Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/51/57/89fd829fb00a6d0bee3fbcb2c8a7aa0252d908949b6ab58bfae99d39d77e/grpcio-1.75.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:1712b5890b22547dd29f3215c5788d8fc759ce6dd0b85a6ba6e2731f2d04c088", size = 5705534, upload-time = "2025-09-26T09:00:52.225Z" }, - { url = "https://files.pythonhosted.org/packages/76/dd/2f8536e092551cf804e96bcda79ecfbc51560b214a0f5b7ebc253f0d4664/grpcio-1.75.1-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:8d04e101bba4b55cea9954e4aa71c24153ba6182481b487ff376da28d4ba46cf", size = 11484103, upload-time = "2025-09-26T09:00:59.457Z" }, - { url = "https://files.pythonhosted.org/packages/9a/3d/affe2fb897804c98d56361138e73786af8f4dd876b9d9851cfe6342b53c8/grpcio-1.75.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:683cfc70be0c1383449097cba637317e4737a357cfc185d887fd984206380403", size = 6289953, upload-time = "2025-09-26T09:01:03.699Z" }, - { url = "https://files.pythonhosted.org/packages/87/aa/0f40b7f47a0ff10d7e482bc3af22dac767c7ff27205915f08962d5ca87a2/grpcio-1.75.1-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:491444c081a54dcd5e6ada57314321ae526377f498d4aa09d975c3241c5b9e1c", size = 6949785, upload-time = "2025-09-26T09:01:07.504Z" }, - { url = "https://files.pythonhosted.org/packages/a5/45/b04407e44050781821c84f26df71b3f7bc469923f92f9f8bc27f1406dbcc/grpcio-1.75.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ce08d4e112d0d38487c2b631ec8723deac9bc404e9c7b1011426af50a79999e4", size = 6465708, upload-time = "2025-09-26T09:01:11.028Z" }, - { url = "https://files.pythonhosted.org/packages/09/3e/4ae3ec0a4d20dcaafbb6e597defcde06399ccdc5b342f607323f3b47f0a3/grpcio-1.75.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5a2acda37fc926ccc4547977ac3e56b1df48fe200de968e8c8421f6e3093df6c", size = 7100912, upload-time = "2025-09-26T09:01:14.393Z" }, - { url = "https://files.pythonhosted.org/packages/34/3f/a9085dab5c313bb0cb853f222d095e2477b9b8490a03634cdd8d19daa5c3/grpcio-1.75.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:745c5fe6bf05df6a04bf2d11552c7d867a2690759e7ab6b05c318a772739bd75", size = 8042497, upload-time = "2025-09-26T09:01:17.759Z" }, - { url = "https://files.pythonhosted.org/packages/c3/87/ea54eba931ab9ed3f999ba95f5d8d01a20221b664725bab2fe93e3dee848/grpcio-1.75.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:259526a7159d39e2db40d566fe3e8f8e034d0fb2db5bf9c00e09aace655a4c2b", size = 7493284, upload-time = "2025-09-26T09:01:20.896Z" }, - { url = "https://files.pythonhosted.org/packages/b7/5e/287f1bf1a998f4ac46ef45d518de3b5da08b4e86c7cb5e1108cee30b0282/grpcio-1.75.1-cp310-cp310-win32.whl", hash = "sha256:f4b29b9aabe33fed5df0a85e5f13b09ff25e2c05bd5946d25270a8bd5682dac9", size = 3950809, upload-time = "2025-09-26T09:01:23.695Z" }, - { url = "https://files.pythonhosted.org/packages/a4/a2/3cbfc06a4ec160dc77403b29ecb5cf76ae329eb63204fea6a7c715f1dfdb/grpcio-1.75.1-cp310-cp310-win_amd64.whl", hash = "sha256:cf2e760978dcce7ff7d465cbc7e276c3157eedc4c27aa6de7b594c7a295d3d61", size = 4644704, upload-time = "2025-09-26T09:01:25.763Z" }, - { url = "https://files.pythonhosted.org/packages/0c/3c/35ca9747473a306bfad0cee04504953f7098527cd112a4ab55c55af9e7bd/grpcio-1.75.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:573855ca2e58e35032aff30bfbd1ee103fbcf4472e4b28d4010757700918e326", size = 5709761, upload-time = "2025-09-26T09:01:28.528Z" }, - { url = "https://files.pythonhosted.org/packages/c9/2c/ecbcb4241e4edbe85ac2663f885726fea0e947767401288b50d8fdcb9200/grpcio-1.75.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:6a4996a2c8accc37976dc142d5991adf60733e223e5c9a2219e157dc6a8fd3a2", size = 11496691, upload-time = "2025-09-26T09:01:31.214Z" }, - { url = "https://files.pythonhosted.org/packages/81/40/bc07aee2911f0d426fa53fe636216100c31a8ea65a400894f280274cb023/grpcio-1.75.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b1ea1bbe77ecbc1be00af2769f4ae4a88ce93be57a4f3eebd91087898ed749f9", size = 6296084, upload-time = "2025-09-26T09:01:34.596Z" }, - { url = "https://files.pythonhosted.org/packages/b8/d1/10c067f6c67396cbf46448b80f27583b5e8c4b46cdfbe18a2a02c2c2f290/grpcio-1.75.1-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:e5b425aee54cc5e3e3c58f00731e8a33f5567965d478d516d35ef99fd648ab68", size = 6950403, upload-time = "2025-09-26T09:01:36.736Z" }, - { url = "https://files.pythonhosted.org/packages/3f/42/5f628abe360b84dfe8dd8f32be6b0606dc31dc04d3358eef27db791ea4d5/grpcio-1.75.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0049a7bf547dafaeeb1db17079ce79596c298bfe308fc084d023c8907a845b9a", size = 6470166, upload-time = "2025-09-26T09:01:39.474Z" }, - { url = "https://files.pythonhosted.org/packages/c3/93/a24035080251324019882ee2265cfde642d6476c0cf8eb207fc693fcebdc/grpcio-1.75.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5b8ea230c7f77c0a1a3208a04a1eda164633fb0767b4cefd65a01079b65e5b1f", size = 7107828, upload-time = "2025-09-26T09:01:41.782Z" }, - { url = "https://files.pythonhosted.org/packages/e4/f8/d18b984c1c9ba0318e3628dbbeb6af77a5007f02abc378c845070f2d3edd/grpcio-1.75.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:36990d629c3c9fb41e546414e5af52d0a7af37ce7113d9682c46d7e2919e4cca", size = 8045421, upload-time = "2025-09-26T09:01:45.835Z" }, - { url = "https://files.pythonhosted.org/packages/7e/b6/4bf9aacff45deca5eac5562547ed212556b831064da77971a4e632917da3/grpcio-1.75.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b10ad908118d38c2453ade7ff790e5bce36580c3742919007a2a78e3a1e521ca", size = 7503290, upload-time = "2025-09-26T09:01:49.28Z" }, - { url = "https://files.pythonhosted.org/packages/3b/15/d8d69d10223cb54c887a2180bd29fe5fa2aec1d4995c8821f7aa6eaf72e4/grpcio-1.75.1-cp311-cp311-win32.whl", hash = "sha256:d6be2b5ee7bea656c954dcf6aa8093c6f0e6a3ef9945c99d99fcbfc88c5c0bfe", size = 3950631, upload-time = "2025-09-26T09:01:51.23Z" }, - { url = "https://files.pythonhosted.org/packages/8a/40/7b8642d45fff6f83300c24eaac0380a840e5e7fe0e8d80afd31b99d7134e/grpcio-1.75.1-cp311-cp311-win_amd64.whl", hash = "sha256:61c692fb05956b17dd6d1ab480f7f10ad0536dba3bc8fd4e3c7263dc244ed772", size = 4646131, upload-time = "2025-09-26T09:01:53.266Z" }, - { url = "https://files.pythonhosted.org/packages/3a/81/42be79e73a50aaa20af66731c2defeb0e8c9008d9935a64dd8ea8e8c44eb/grpcio-1.75.1-cp312-cp312-linux_armv7l.whl", hash = "sha256:7b888b33cd14085d86176b1628ad2fcbff94cfbbe7809465097aa0132e58b018", size = 5668314, upload-time = "2025-09-26T09:01:55.424Z" }, - { url = "https://files.pythonhosted.org/packages/c5/a7/3686ed15822fedc58c22f82b3a7403d9faf38d7c33de46d4de6f06e49426/grpcio-1.75.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:8775036efe4ad2085975531d221535329f5dac99b6c2a854a995456098f99546", size = 11476125, upload-time = "2025-09-26T09:01:57.927Z" }, - { url = "https://files.pythonhosted.org/packages/14/85/21c71d674f03345ab183c634ecd889d3330177e27baea8d5d247a89b6442/grpcio-1.75.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:bb658f703468d7fbb5dcc4037c65391b7dc34f808ac46ed9136c24fc5eeb041d", size = 6246335, upload-time = "2025-09-26T09:02:00.76Z" }, - { url = "https://files.pythonhosted.org/packages/fd/db/3beb661bc56a385ae4fa6b0e70f6b91ac99d47afb726fe76aaff87ebb116/grpcio-1.75.1-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:4b7177a1cdb3c51b02b0c0a256b0a72fdab719600a693e0e9037949efffb200b", size = 6916309, upload-time = "2025-09-26T09:02:02.894Z" }, - { url = "https://files.pythonhosted.org/packages/1e/9c/eda9fe57f2b84343d44c1b66cf3831c973ba29b078b16a27d4587a1fdd47/grpcio-1.75.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7d4fa6ccc3ec2e68a04f7b883d354d7fea22a34c44ce535a2f0c0049cf626ddf", size = 6435419, upload-time = "2025-09-26T09:02:05.055Z" }, - { url = "https://files.pythonhosted.org/packages/c3/b8/090c98983e0a9d602e3f919a6e2d4e470a8b489452905f9a0fa472cac059/grpcio-1.75.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3d86880ecaeb5b2f0a8afa63824de93adb8ebe4e49d0e51442532f4e08add7d6", size = 7064893, upload-time = "2025-09-26T09:02:07.275Z" }, - { url = "https://files.pythonhosted.org/packages/ec/c0/6d53d4dbbd00f8bd81571f5478d8a95528b716e0eddb4217cc7cb45aae5f/grpcio-1.75.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a8041d2f9e8a742aeae96f4b047ee44e73619f4f9d24565e84d5446c623673b6", size = 8011922, upload-time = "2025-09-26T09:02:09.527Z" }, - { url = "https://files.pythonhosted.org/packages/f2/7c/48455b2d0c5949678d6982c3e31ea4d89df4e16131b03f7d5c590811cbe9/grpcio-1.75.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3652516048bf4c314ce12be37423c79829f46efffb390ad64149a10c6071e8de", size = 7466181, upload-time = "2025-09-26T09:02:12.279Z" }, - { url = "https://files.pythonhosted.org/packages/fd/12/04a0e79081e3170b6124f8cba9b6275871276be06c156ef981033f691880/grpcio-1.75.1-cp312-cp312-win32.whl", hash = "sha256:44b62345d8403975513af88da2f3d5cc76f73ca538ba46596f92a127c2aea945", size = 3938543, upload-time = "2025-09-26T09:02:14.77Z" }, - { url = "https://files.pythonhosted.org/packages/5f/d7/11350d9d7fb5adc73d2b0ebf6ac1cc70135577701e607407fe6739a90021/grpcio-1.75.1-cp312-cp312-win_amd64.whl", hash = "sha256:b1e191c5c465fa777d4cafbaacf0c01e0d5278022082c0abbd2ee1d6454ed94d", size = 4641938, upload-time = "2025-09-26T09:02:16.927Z" }, - { url = "https://files.pythonhosted.org/packages/46/74/bac4ab9f7722164afdf263ae31ba97b8174c667153510322a5eba4194c32/grpcio-1.75.1-cp313-cp313-linux_armv7l.whl", hash = "sha256:3bed22e750d91d53d9e31e0af35a7b0b51367e974e14a4ff229db5b207647884", size = 5672779, upload-time = "2025-09-26T09:02:19.11Z" }, - { url = "https://files.pythonhosted.org/packages/a6/52/d0483cfa667cddaa294e3ab88fd2c2a6e9dc1a1928c0e5911e2e54bd5b50/grpcio-1.75.1-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:5b8f381eadcd6ecaa143a21e9e80a26424c76a0a9b3d546febe6648f3a36a5ac", size = 11470623, upload-time = "2025-09-26T09:02:22.117Z" }, - { url = "https://files.pythonhosted.org/packages/cf/e4/d1954dce2972e32384db6a30273275e8c8ea5a44b80347f9055589333b3f/grpcio-1.75.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5bf4001d3293e3414d0cf99ff9b1139106e57c3a66dfff0c5f60b2a6286ec133", size = 6248838, upload-time = "2025-09-26T09:02:26.426Z" }, - { url = "https://files.pythonhosted.org/packages/06/43/073363bf63826ba8077c335d797a8d026f129dc0912b69c42feaf8f0cd26/grpcio-1.75.1-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:9f82ff474103e26351dacfe8d50214e7c9322960d8d07ba7fa1d05ff981c8b2d", size = 6922663, upload-time = "2025-09-26T09:02:28.724Z" }, - { url = "https://files.pythonhosted.org/packages/c2/6f/076ac0df6c359117676cacfa8a377e2abcecec6a6599a15a672d331f6680/grpcio-1.75.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0ee119f4f88d9f75414217823d21d75bfe0e6ed40135b0cbbfc6376bc9f7757d", size = 6436149, upload-time = "2025-09-26T09:02:30.971Z" }, - { url = "https://files.pythonhosted.org/packages/6b/27/1d08824f1d573fcb1fa35ede40d6020e68a04391709939e1c6f4193b445f/grpcio-1.75.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:664eecc3abe6d916fa6cf8dd6b778e62fb264a70f3430a3180995bf2da935446", size = 7067989, upload-time = "2025-09-26T09:02:33.233Z" }, - { url = "https://files.pythonhosted.org/packages/c6/98/98594cf97b8713feb06a8cb04eeef60b4757e3e2fb91aa0d9161da769843/grpcio-1.75.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:c32193fa08b2fbebf08fe08e84f8a0aad32d87c3ad42999c65e9449871b1c66e", size = 8010717, upload-time = "2025-09-26T09:02:36.011Z" }, - { url = "https://files.pythonhosted.org/packages/8c/7e/bb80b1bba03c12158f9254762cdf5cced4a9bc2e8ed51ed335915a5a06ef/grpcio-1.75.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5cebe13088b9254f6e615bcf1da9131d46cfa4e88039454aca9cb65f639bd3bc", size = 7463822, upload-time = "2025-09-26T09:02:38.26Z" }, - { url = "https://files.pythonhosted.org/packages/23/1c/1ea57fdc06927eb5640f6750c697f596f26183573069189eeaf6ef86ba2d/grpcio-1.75.1-cp313-cp313-win32.whl", hash = "sha256:4b4c678e7ed50f8ae8b8dbad15a865ee73ce12668b6aaf411bf3258b5bc3f970", size = 3938490, upload-time = "2025-09-26T09:02:40.268Z" }, - { url = "https://files.pythonhosted.org/packages/4b/24/fbb8ff1ccadfbf78ad2401c41aceaf02b0d782c084530d8871ddd69a2d49/grpcio-1.75.1-cp313-cp313-win_amd64.whl", hash = "sha256:5573f51e3f296a1bcf71e7a690c092845fb223072120f4bdb7a5b48e111def66", size = 4642538, upload-time = "2025-09-26T09:02:42.519Z" }, - { url = "https://files.pythonhosted.org/packages/f2/1b/9a0a5cecd24302b9fdbcd55d15ed6267e5f3d5b898ff9ac8cbe17ee76129/grpcio-1.75.1-cp314-cp314-linux_armv7l.whl", hash = "sha256:c05da79068dd96723793bffc8d0e64c45f316248417515f28d22204d9dae51c7", size = 5673319, upload-time = "2025-09-26T09:02:44.742Z" }, - { url = "https://files.pythonhosted.org/packages/c6/ec/9d6959429a83fbf5df8549c591a8a52bb313976f6646b79852c4884e3225/grpcio-1.75.1-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:06373a94fd16ec287116a825161dca179a0402d0c60674ceeec8c9fba344fe66", size = 11480347, upload-time = "2025-09-26T09:02:47.539Z" }, - { url = "https://files.pythonhosted.org/packages/09/7a/26da709e42c4565c3d7bf999a9569da96243ce34a8271a968dee810a7cf1/grpcio-1.75.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4484f4b7287bdaa7a5b3980f3c7224c3c622669405d20f69549f5fb956ad0421", size = 6254706, upload-time = "2025-09-26T09:02:50.4Z" }, - { url = "https://files.pythonhosted.org/packages/f1/08/dcb26a319d3725f199c97e671d904d84ee5680de57d74c566a991cfab632/grpcio-1.75.1-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:2720c239c1180eee69f7883c1d4c83fc1a495a2535b5fa322887c70bf02b16e8", size = 6922501, upload-time = "2025-09-26T09:02:52.711Z" }, - { url = "https://files.pythonhosted.org/packages/78/66/044d412c98408a5e23cb348845979a2d17a2e2b6c3c34c1ec91b920f49d0/grpcio-1.75.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:07a554fa31c668cf0e7a188678ceeca3cb8fead29bbe455352e712ec33ca701c", size = 6437492, upload-time = "2025-09-26T09:02:55.542Z" }, - { url = "https://files.pythonhosted.org/packages/4e/9d/5e3e362815152aa1afd8b26ea613effa005962f9da0eec6e0e4527e7a7d1/grpcio-1.75.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:3e71a2105210366bfc398eef7f57a664df99194f3520edb88b9c3a7e46ee0d64", size = 7081061, upload-time = "2025-09-26T09:02:58.261Z" }, - { url = "https://files.pythonhosted.org/packages/1e/1a/46615682a19e100f46e31ddba9ebc297c5a5ab9ddb47b35443ffadb8776c/grpcio-1.75.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:8679aa8a5b67976776d3c6b0521e99d1c34db8a312a12bcfd78a7085cb9b604e", size = 8010849, upload-time = "2025-09-26T09:03:00.548Z" }, - { url = "https://files.pythonhosted.org/packages/67/8e/3204b94ac30b0f675ab1c06540ab5578660dc8b690db71854d3116f20d00/grpcio-1.75.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:aad1c774f4ebf0696a7f148a56d39a3432550612597331792528895258966dc0", size = 7464478, upload-time = "2025-09-26T09:03:03.096Z" }, - { url = "https://files.pythonhosted.org/packages/b7/97/2d90652b213863b2cf466d9c1260ca7e7b67a16780431b3eb1d0420e3d5b/grpcio-1.75.1-cp314-cp314-win32.whl", hash = "sha256:62ce42d9994446b307649cb2a23335fa8e927f7ab2cbf5fcb844d6acb4d85f9c", size = 4012672, upload-time = "2025-09-26T09:03:05.477Z" }, - { url = "https://files.pythonhosted.org/packages/f9/df/e2e6e9fc1c985cd1a59e6996a05647c720fe8a03b92f5ec2d60d366c531e/grpcio-1.75.1-cp314-cp314-win_amd64.whl", hash = "sha256:f86e92275710bea3000cb79feca1762dc0ad3b27830dd1a74e82ab321d4ee464", size = 4772475, upload-time = "2025-09-26T09:03:07.661Z" }, - { url = "https://files.pythonhosted.org/packages/8f/e2/33efd823a879dc7b60c10192df1900ee5c200f8e782663a41a3b2aecd143/grpcio-1.75.1-cp39-cp39-linux_armv7l.whl", hash = "sha256:c09fba33327c3ac11b5c33dbdd8218eef8990d78f83b1656d628831812a8c0fb", size = 5706679, upload-time = "2025-09-26T09:03:10.218Z" }, - { url = "https://files.pythonhosted.org/packages/5f/13/17e39ee4897f1cd12dd463e863b830e64643b13e9a4af5062b4a6f0790be/grpcio-1.75.1-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:7e21400b037be29545704889e72e586c238e346dcb2d08d8a7288d16c883a9ec", size = 11490271, upload-time = "2025-09-26T09:03:12.778Z" }, - { url = "https://files.pythonhosted.org/packages/77/90/b80e75f8cce758425b2772742eed4e9db765a965d902ba4b7f239b2513de/grpcio-1.75.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c12121e509b9f8b0914d10054d24120237d19e870b1cd82acbb8a9b9ddd198a3", size = 6291926, upload-time = "2025-09-26T09:03:16.282Z" }, - { url = "https://files.pythonhosted.org/packages/40/5f/e6033d8f99063350e20873a46225468b73045b9ef2c8cba73d66a87c3fd5/grpcio-1.75.1-cp39-cp39-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:73577a93e692b3474b1bfe84285d098de36705dbd838bb4d6a056d326e4dc880", size = 6950040, upload-time = "2025-09-26T09:03:18.874Z" }, - { url = "https://files.pythonhosted.org/packages/01/12/34076c079b45af5aed40f037fffe388d7fbe90dd539ed01e4744c926d227/grpcio-1.75.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e19e7dfa0d7ca7dea22be464339e18ac608fd75d88c56770c646cdabe54bc724", size = 6465780, upload-time = "2025-09-26T09:03:21.219Z" }, - { url = "https://files.pythonhosted.org/packages/e4/c5/ee6fd69a9f6e7288d04da010ad7480a0566d2aac81097ff4dafbc5ffa9b6/grpcio-1.75.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:4e1c28f51c1cf67eccdfc1065e8e866c9ed622f09773ca60947089c117f848a1", size = 7098308, upload-time = "2025-09-26T09:03:23.875Z" }, - { url = "https://files.pythonhosted.org/packages/78/32/f2be13f13035361768923159fe20470a7d22db2c7c692b952e21284f56e5/grpcio-1.75.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:030a6164bc2ca726052778c0cf8e3249617a34e368354f9e6107c27ad4af8c28", size = 8042268, upload-time = "2025-09-26T09:03:26.268Z" }, - { url = "https://files.pythonhosted.org/packages/e7/2d/1bb0572f0a2eaab100b4635c6c2cd0d37e3cda5554037e3f90b1bc428d56/grpcio-1.75.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:67697efef5a98d46d5db7b1720fa4043536f8b8e5072a5d61cfca762f287e939", size = 7491470, upload-time = "2025-09-26T09:03:28.906Z" }, - { url = "https://files.pythonhosted.org/packages/aa/e0/1e962dcb64019bbd87eedcfacdedb83af0f66da01f2f6e03d69b0aa1b7f0/grpcio-1.75.1-cp39-cp39-win32.whl", hash = "sha256:52015cf73eb5d76f6404e0ce0505a69b51fd1f35810b3a01233b34b10baafb41", size = 3951697, upload-time = "2025-09-26T09:03:31.535Z" }, - { url = "https://files.pythonhosted.org/packages/87/bc/47fb3aaa77e7d657999937ec1026beba9e37f3199599fe510f762d31da97/grpcio-1.75.1-cp39-cp39-win_amd64.whl", hash = "sha256:9fe51e4a1f896ea84ac750900eae34d9e9b896b5b1e4a30b02dc31ad29f36383", size = 4645764, upload-time = "2025-09-26T09:03:34.071Z" }, +version = "1.67.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/20/53/d9282a66a5db45981499190b77790570617a604a38f3d103d0400974aeb5/grpcio-1.67.1.tar.gz", hash = "sha256:3dc2ed4cabea4dc14d5e708c2b426205956077cc5de419b4d4079315017e9732", size = 12580022, upload-time = "2024-10-29T06:30:07.787Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4e/cd/f6ca5c49aa0ae7bc6d0757f7dae6f789569e9490a635eaabe02bc02de7dc/grpcio-1.67.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:8b0341d66a57f8a3119b77ab32207072be60c9bf79760fa609c5609f2deb1f3f", size = 5112450, upload-time = "2024-10-29T06:23:38.202Z" }, + { url = "https://files.pythonhosted.org/packages/d4/f0/d9bbb4a83cbee22f738ee7a74aa41e09ccfb2dcea2cc30ebe8dab5b21771/grpcio-1.67.1-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:f5a27dddefe0e2357d3e617b9079b4bfdc91341a91565111a21ed6ebbc51b22d", size = 10937518, upload-time = "2024-10-29T06:23:43.535Z" }, + { url = "https://files.pythonhosted.org/packages/5b/17/0c5dbae3af548eb76669887642b5f24b232b021afe77eb42e22bc8951d9c/grpcio-1.67.1-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:43112046864317498a33bdc4797ae6a268c36345a910de9b9c17159d8346602f", size = 5633610, upload-time = "2024-10-29T06:23:47.168Z" }, + { url = "https://files.pythonhosted.org/packages/17/48/e000614e00153d7b2760dcd9526b95d72f5cfe473b988e78f0ff3b472f6c/grpcio-1.67.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c9b929f13677b10f63124c1a410994a401cdd85214ad83ab67cc077fc7e480f0", size = 6240678, upload-time = "2024-10-29T06:23:49.352Z" }, + { url = "https://files.pythonhosted.org/packages/64/19/a16762a70eeb8ddfe43283ce434d1499c1c409ceec0c646f783883084478/grpcio-1.67.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e7d1797a8a3845437d327145959a2c0c47c05947c9eef5ff1a4c80e499dcc6fa", size = 5884528, upload-time = "2024-10-29T06:23:52.345Z" }, + { url = "https://files.pythonhosted.org/packages/6b/dc/bd016aa3684914acd2c0c7fa4953b2a11583c2b844f3d7bae91fa9b98fbb/grpcio-1.67.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:0489063974d1452436139501bf6b180f63d4977223ee87488fe36858c5725292", size = 6583680, upload-time = "2024-10-29T06:23:55.074Z" }, + { url = "https://files.pythonhosted.org/packages/1a/93/1441cb14c874f11aa798a816d582f9da82194b6677f0f134ea53d2d5dbeb/grpcio-1.67.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:9fd042de4a82e3e7aca44008ee2fb5da01b3e5adb316348c21980f7f58adc311", size = 6162967, upload-time = "2024-10-29T06:23:57.286Z" }, + { url = "https://files.pythonhosted.org/packages/29/e9/9295090380fb4339b7e935b9d005fa9936dd573a22d147c9e5bb2df1b8d4/grpcio-1.67.1-cp310-cp310-win32.whl", hash = "sha256:638354e698fd0c6c76b04540a850bf1db27b4d2515a19fcd5cf645c48d3eb1ed", size = 3616336, upload-time = "2024-10-29T06:23:59.69Z" }, + { url = "https://files.pythonhosted.org/packages/ce/de/7c783b8cb8f02c667ca075c49680c4aeb8b054bc69784bcb3e7c1bbf4985/grpcio-1.67.1-cp310-cp310-win_amd64.whl", hash = "sha256:608d87d1bdabf9e2868b12338cd38a79969eaf920c89d698ead08f48de9c0f9e", size = 4352071, upload-time = "2024-10-29T06:24:02.477Z" }, + { url = "https://files.pythonhosted.org/packages/59/2c/b60d6ea1f63a20a8d09c6db95c4f9a16497913fb3048ce0990ed81aeeca0/grpcio-1.67.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:7818c0454027ae3384235a65210bbf5464bd715450e30a3d40385453a85a70cb", size = 5119075, upload-time = "2024-10-29T06:24:04.696Z" }, + { url = "https://files.pythonhosted.org/packages/b3/9a/e1956f7ca582a22dd1f17b9e26fcb8229051b0ce6d33b47227824772feec/grpcio-1.67.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ea33986b70f83844cd00814cee4451055cd8cab36f00ac64a31f5bb09b31919e", size = 11009159, upload-time = "2024-10-29T06:24:07.781Z" }, + { url = "https://files.pythonhosted.org/packages/43/a8/35fbbba580c4adb1d40d12e244cf9f7c74a379073c0a0ca9d1b5338675a1/grpcio-1.67.1-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:c7a01337407dd89005527623a4a72c5c8e2894d22bead0895306b23c6695698f", size = 5629476, upload-time = "2024-10-29T06:24:11.444Z" }, + { url = "https://files.pythonhosted.org/packages/77/c9/864d336e167263d14dfccb4dbfa7fce634d45775609895287189a03f1fc3/grpcio-1.67.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:80b866f73224b0634f4312a4674c1be21b2b4afa73cb20953cbbb73a6b36c3cc", size = 6239901, upload-time = "2024-10-29T06:24:14.2Z" }, + { url = "https://files.pythonhosted.org/packages/f7/1e/0011408ebabf9bd69f4f87cc1515cbfe2094e5a32316f8714a75fd8ddfcb/grpcio-1.67.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9fff78ba10d4250bfc07a01bd6254a6d87dc67f9627adece85c0b2ed754fa96", size = 5881010, upload-time = "2024-10-29T06:24:17.451Z" }, + { url = "https://files.pythonhosted.org/packages/b4/7d/fbca85ee9123fb296d4eff8df566f458d738186d0067dec6f0aa2fd79d71/grpcio-1.67.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:8a23cbcc5bb11ea7dc6163078be36c065db68d915c24f5faa4f872c573bb400f", size = 6580706, upload-time = "2024-10-29T06:24:20.038Z" }, + { url = "https://files.pythonhosted.org/packages/75/7a/766149dcfa2dfa81835bf7df623944c1f636a15fcb9b6138ebe29baf0bc6/grpcio-1.67.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1a65b503d008f066e994f34f456e0647e5ceb34cfcec5ad180b1b44020ad4970", size = 6161799, upload-time = "2024-10-29T06:24:22.604Z" }, + { url = "https://files.pythonhosted.org/packages/09/13/5b75ae88810aaea19e846f5380611837de411181df51fd7a7d10cb178dcb/grpcio-1.67.1-cp311-cp311-win32.whl", hash = "sha256:e29ca27bec8e163dca0c98084040edec3bc49afd10f18b412f483cc68c712744", size = 3616330, upload-time = "2024-10-29T06:24:25.775Z" }, + { url = "https://files.pythonhosted.org/packages/aa/39/38117259613f68f072778c9638a61579c0cfa5678c2558706b10dd1d11d3/grpcio-1.67.1-cp311-cp311-win_amd64.whl", hash = "sha256:786a5b18544622bfb1e25cc08402bd44ea83edfb04b93798d85dca4d1a0b5be5", size = 4354535, upload-time = "2024-10-29T06:24:28.614Z" }, + { url = "https://files.pythonhosted.org/packages/6e/25/6f95bd18d5f506364379eabc0d5874873cc7dbdaf0757df8d1e82bc07a88/grpcio-1.67.1-cp312-cp312-linux_armv7l.whl", hash = "sha256:267d1745894200e4c604958da5f856da6293f063327cb049a51fe67348e4f953", size = 5089809, upload-time = "2024-10-29T06:24:31.24Z" }, + { url = "https://files.pythonhosted.org/packages/10/3f/d79e32e5d0354be33a12db2267c66d3cfeff700dd5ccdd09fd44a3ff4fb6/grpcio-1.67.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:85f69fdc1d28ce7cff8de3f9c67db2b0ca9ba4449644488c1e0303c146135ddb", size = 10981985, upload-time = "2024-10-29T06:24:34.942Z" }, + { url = "https://files.pythonhosted.org/packages/21/f2/36fbc14b3542e3a1c20fb98bd60c4732c55a44e374a4eb68f91f28f14aab/grpcio-1.67.1-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:f26b0b547eb8d00e195274cdfc63ce64c8fc2d3e2d00b12bf468ece41a0423a0", size = 5588770, upload-time = "2024-10-29T06:24:38.145Z" }, + { url = "https://files.pythonhosted.org/packages/0d/af/bbc1305df60c4e65de8c12820a942b5e37f9cf684ef5e49a63fbb1476a73/grpcio-1.67.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4422581cdc628f77302270ff839a44f4c24fdc57887dc2a45b7e53d8fc2376af", size = 6214476, upload-time = "2024-10-29T06:24:41.006Z" }, + { url = "https://files.pythonhosted.org/packages/92/cf/1d4c3e93efa93223e06a5c83ac27e32935f998bc368e276ef858b8883154/grpcio-1.67.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1d7616d2ded471231c701489190379e0c311ee0a6c756f3c03e6a62b95a7146e", size = 5850129, upload-time = "2024-10-29T06:24:43.553Z" }, + { url = "https://files.pythonhosted.org/packages/ae/ca/26195b66cb253ac4d5ef59846e354d335c9581dba891624011da0e95d67b/grpcio-1.67.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8a00efecde9d6fcc3ab00c13f816313c040a28450e5e25739c24f432fc6d3c75", size = 6568489, upload-time = "2024-10-29T06:24:46.453Z" }, + { url = "https://files.pythonhosted.org/packages/d1/94/16550ad6b3f13b96f0856ee5dfc2554efac28539ee84a51d7b14526da985/grpcio-1.67.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:699e964923b70f3101393710793289e42845791ea07565654ada0969522d0a38", size = 6149369, upload-time = "2024-10-29T06:24:49.112Z" }, + { url = "https://files.pythonhosted.org/packages/33/0d/4c3b2587e8ad7f121b597329e6c2620374fccbc2e4e1aa3c73ccc670fde4/grpcio-1.67.1-cp312-cp312-win32.whl", hash = "sha256:4e7b904484a634a0fff132958dabdb10d63e0927398273917da3ee103e8d1f78", size = 3599176, upload-time = "2024-10-29T06:24:51.443Z" }, + { url = "https://files.pythonhosted.org/packages/7d/36/0c03e2d80db69e2472cf81c6123aa7d14741de7cf790117291a703ae6ae1/grpcio-1.67.1-cp312-cp312-win_amd64.whl", hash = "sha256:5721e66a594a6c4204458004852719b38f3d5522082be9061d6510b455c90afc", size = 4346574, upload-time = "2024-10-29T06:24:54.587Z" }, + { url = "https://files.pythonhosted.org/packages/12/d2/2f032b7a153c7723ea3dea08bffa4bcaca9e0e5bdf643ce565b76da87461/grpcio-1.67.1-cp313-cp313-linux_armv7l.whl", hash = "sha256:aa0162e56fd10a5547fac8774c4899fc3e18c1aa4a4759d0ce2cd00d3696ea6b", size = 5091487, upload-time = "2024-10-29T06:24:57.416Z" }, + { url = "https://files.pythonhosted.org/packages/d0/ae/ea2ff6bd2475a082eb97db1104a903cf5fc57c88c87c10b3c3f41a184fc0/grpcio-1.67.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:beee96c8c0b1a75d556fe57b92b58b4347c77a65781ee2ac749d550f2a365dc1", size = 10943530, upload-time = "2024-10-29T06:25:01.062Z" }, + { url = "https://files.pythonhosted.org/packages/07/62/646be83d1a78edf8d69b56647327c9afc223e3140a744c59b25fbb279c3b/grpcio-1.67.1-cp313-cp313-manylinux_2_17_aarch64.whl", hash = "sha256:a93deda571a1bf94ec1f6fcda2872dad3ae538700d94dc283c672a3b508ba3af", size = 5589079, upload-time = "2024-10-29T06:25:04.254Z" }, + { url = "https://files.pythonhosted.org/packages/d0/25/71513d0a1b2072ce80d7f5909a93596b7ed10348b2ea4fdcbad23f6017bf/grpcio-1.67.1-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0e6f255980afef598a9e64a24efce87b625e3e3c80a45162d111a461a9f92955", size = 6213542, upload-time = "2024-10-29T06:25:06.824Z" }, + { url = "https://files.pythonhosted.org/packages/76/9a/d21236297111052dcb5dc85cd77dc7bf25ba67a0f55ae028b2af19a704bc/grpcio-1.67.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e838cad2176ebd5d4a8bb03955138d6589ce9e2ce5d51c3ada34396dbd2dba8", size = 5850211, upload-time = "2024-10-29T06:25:10.149Z" }, + { url = "https://files.pythonhosted.org/packages/2d/fe/70b1da9037f5055be14f359026c238821b9bcf6ca38a8d760f59a589aacd/grpcio-1.67.1-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:a6703916c43b1d468d0756c8077b12017a9fcb6a1ef13faf49e67d20d7ebda62", size = 6572129, upload-time = "2024-10-29T06:25:12.853Z" }, + { url = "https://files.pythonhosted.org/packages/74/0d/7df509a2cd2a54814598caf2fb759f3e0b93764431ff410f2175a6efb9e4/grpcio-1.67.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:917e8d8994eed1d86b907ba2a61b9f0aef27a2155bca6cbb322430fc7135b7bb", size = 6149819, upload-time = "2024-10-29T06:25:15.803Z" }, + { url = "https://files.pythonhosted.org/packages/0a/08/bc3b0155600898fd10f16b79054e1cca6cb644fa3c250c0fe59385df5e6f/grpcio-1.67.1-cp313-cp313-win32.whl", hash = "sha256:e279330bef1744040db8fc432becc8a727b84f456ab62b744d3fdb83f327e121", size = 3596561, upload-time = "2024-10-29T06:25:19.348Z" }, + { url = "https://files.pythonhosted.org/packages/5a/96/44759eca966720d0f3e1b105c43f8ad4590c97bf8eb3cd489656e9590baa/grpcio-1.67.1-cp313-cp313-win_amd64.whl", hash = "sha256:fa0c739ad8b1996bd24823950e3cb5152ae91fca1c09cc791190bf1627ffefba", size = 4346042, upload-time = "2024-10-29T06:25:21.939Z" }, + { url = "https://files.pythonhosted.org/packages/a3/1d/9fa4dc94a3cebe5ef9f6ba5bb9893947665885d4f565d216359a4699c54c/grpcio-1.67.1-cp39-cp39-linux_armv7l.whl", hash = "sha256:a25bdea92b13ff4d7790962190bf6bf5c4639876e01c0f3dda70fc2769616335", size = 5112899, upload-time = "2024-10-29T06:25:51.803Z" }, + { url = "https://files.pythonhosted.org/packages/91/d7/685b53b4dd7b5fffc0c48bc411065420136ab618d838f09ce41809233e2f/grpcio-1.67.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:cdc491ae35a13535fd9196acb5afe1af37c8237df2e54427be3eecda3653127e", size = 11000821, upload-time = "2024-10-29T06:25:55.397Z" }, + { url = "https://files.pythonhosted.org/packages/bd/49/7763443826c52dece03bca64e10ba2f981e7af9735d9dded1275f4e46f83/grpcio-1.67.1-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:85f862069b86a305497e74d0dc43c02de3d1d184fc2c180993aa8aa86fbd19b8", size = 5631716, upload-time = "2024-10-29T06:25:58.66Z" }, + { url = "https://files.pythonhosted.org/packages/7d/72/31753e27792b48cc14b4c80a5818224a33d167fd5e0770821111a4ea316c/grpcio-1.67.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ec74ef02010186185de82cc594058a3ccd8d86821842bbac9873fd4a2cf8be8d", size = 6240280, upload-time = "2024-10-29T06:26:01.352Z" }, + { url = "https://files.pythonhosted.org/packages/d4/ea/32bb9c4d58234383a4e617baf72da4e26e0ccf6396ca36ff7ddc95898ab6/grpcio-1.67.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01f616a964e540638af5130469451cf580ba8c7329f45ca998ab66e0c7dcdb04", size = 5884299, upload-time = "2024-10-29T06:26:04.537Z" }, + { url = "https://files.pythonhosted.org/packages/3d/4c/5f44e5c9feab14f3d93becb3dd76989f2e97d31cd0c2c421b859c4bbb9ff/grpcio-1.67.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:299b3d8c4f790c6bcca485f9963b4846dd92cf6f1b65d3697145d005c80f9fe8", size = 6584605, upload-time = "2024-10-29T06:26:08.05Z" }, + { url = "https://files.pythonhosted.org/packages/ec/dc/6cc20ce55d4cdc51c89f35900668d9429f47f3e5632c558636cd044b71cd/grpcio-1.67.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:60336bff760fbb47d7e86165408126f1dded184448e9a4c892189eb7c9d3f90f", size = 6162361, upload-time = "2024-10-29T06:26:10.946Z" }, + { url = "https://files.pythonhosted.org/packages/1e/16/5b7255a6d6d1ac174481fb5c257adf3a869f3839a426eead05d2f6d6537a/grpcio-1.67.1-cp39-cp39-win32.whl", hash = "sha256:5ed601c4c6008429e3d247ddb367fe8c7259c355757448d7c1ef7bd4a6739e8e", size = 3616599, upload-time = "2024-10-29T06:26:13.537Z" }, + { url = "https://files.pythonhosted.org/packages/41/ef/03860d260c56d018dc8327c7ec3ebd31d84cec98462cf1e44660c3c58c82/grpcio-1.67.1-cp39-cp39-win_amd64.whl", hash = "sha256:5db70d32d6703b89912af16d6d45d78406374a8b8ef0d28140351dd0ec610e98", size = 4353565, upload-time = "2024-10-29T06:26:16.348Z" }, ] [[package]] name = "grpcio-status" -version = "1.75.1" +version = "1.67.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "googleapis-common-protos" }, { name = "grpcio" }, { name = "protobuf" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/74/5b/1ce0e3eedcdc08b4739b3da5836f31142ec8bee1a9ae0ad8dc0dc39a14bf/grpcio_status-1.75.1.tar.gz", hash = "sha256:8162afa21833a2085c91089cc395ad880fac1378a1d60233d976649ed724cbf8", size = 13671, upload-time = "2025-09-26T09:13:16.412Z" } +sdist = { url = "https://files.pythonhosted.org/packages/be/c7/fe0e79a80ac6346e0c6c0a24e9e3cbc3ae1c2a009acffb59eab484a6f69b/grpcio_status-1.67.1.tar.gz", hash = "sha256:2bf38395e028ceeecfd8866b081f61628114b384da7d51ae064ddc8d766a5d11", size = 13673, upload-time = "2024-10-29T06:30:21.787Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d8/ad/6f414bb0b36eee20d93af6907256f208ffcda992ae6d3d7b6a778afe31e6/grpcio_status-1.75.1-py3-none-any.whl", hash = "sha256:f681b301be26dcf7abf5c765d4a22e4098765e1a65cbdfa3efca384edf8e4e3c", size = 14428, upload-time = "2025-09-26T09:12:55.516Z" }, + { url = "https://files.pythonhosted.org/packages/05/18/56999a1da3577d8ccc8698a575d6638e15fe25650cc88b2ce0a087f180b9/grpcio_status-1.67.1-py3-none-any.whl", hash = "sha256:16e6c085950bdacac97c779e6a502ea671232385e6e37f258884d6883392c2bd", size = 14427, upload-time = "2024-10-29T06:27:38.228Z" }, ] [[package]] @@ -1314,12 +1370,14 @@ wheels = [ [[package]] name = "litellm" -version = "1.75.5.post1" +version = "1.80.8" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiohttp" }, { name = "click", version = "8.1.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, { name = "click", version = "8.2.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "fastuuid" }, + { name = "grpcio" }, { name = "httpx" }, { name = "importlib-metadata" }, { name = "jinja2" }, @@ -1330,9 +1388,9 @@ dependencies = [ { name = "tiktoken" }, { name = "tokenizers" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/10/97/6091a020895102a20f1da204ebe68c1293123555476b38e749f95ba5981c/litellm-1.75.5.post1.tar.gz", hash = "sha256:e40a0e4b25032755dc5df7f02742abe9e3b8836236363f605f3bdd363cb5a0d0", size = 10127846, upload-time = "2025-08-10T16:30:23.788Z" } +sdist = { url = "https://files.pythonhosted.org/packages/05/73/1258421bd221484b0337702c770e95f7027d585c6c8dec0e534763513901/litellm-1.80.8.tar.gz", hash = "sha256:8cdf0f08ae9c977cd99f78257550c02910c064ce6d29ae794ac22d16a5a99980", size = 12325368, upload-time = "2025-12-07T01:15:20.068Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8f/76/780f68a3b26227136a5147c76860aacedcae9bf1b7afc1c991ec9cad11bc/litellm-1.75.5.post1-py3-none-any.whl", hash = "sha256:1c72809a9c8f6e132ad06eb7e628f674c775b0ce6bfb58cbd37e8b585d929cb7", size = 8895997, upload-time = "2025-08-10T16:30:21.325Z" }, + { url = "https://files.pythonhosted.org/packages/0b/54/0371456cf5317c1ebb95824ad38d607efa9e878adc1670f743c2de8953d8/litellm-1.80.8-py3-none-any.whl", hash = "sha256:8b04de6661d2c9646ad6c4e57a61a6cf549f52e72e6a41d8adbd5691f2f95b3b", size = 11045853, upload-time = "2025-12-07T01:15:16.96Z" }, ] [[package]] @@ -2102,7 +2160,7 @@ requires-dist = [ { name = "graphviz", marker = "extra == 'viz'", specifier = ">=0.17" }, { name = "griffe", specifier = ">=1.5.6,<2" }, { name = "grpcio", marker = "extra == 'dapr'", specifier = ">=1.60.0" }, - { name = "litellm", marker = "extra == 'litellm'", specifier = ">=1.67.4.post1,<2" }, + { name = "litellm", marker = "extra == 'litellm'", specifier = ">=1.80.8,<2" }, { name = "mcp", marker = "python_full_version >= '3.10'", specifier = ">=1.11.0,<2" }, { name = "numpy", marker = "python_full_version >= '3.10' and extra == 'voice'", specifier = ">=2.2.0,<3" }, { name = "openai", specifier = ">=2.8.0,<3" }, @@ -2319,19 +2377,18 @@ wheels = [ [[package]] name = "protobuf" -version = "6.33.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/19/ff/64a6c8f420818bb873713988ca5492cba3a7946be57e027ac63495157d97/protobuf-6.33.0.tar.gz", hash = "sha256:140303d5c8d2037730c548f8c7b93b20bb1dc301be280c378b82b8894589c954", size = 443463, upload-time = "2025-10-15T20:39:52.159Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7e/ee/52b3fa8feb6db4a833dfea4943e175ce645144532e8a90f72571ad85df4e/protobuf-6.33.0-cp310-abi3-win32.whl", hash = "sha256:d6101ded078042a8f17959eccd9236fb7a9ca20d3b0098bbcb91533a5680d035", size = 425593, upload-time = "2025-10-15T20:39:40.29Z" }, - { url = "https://files.pythonhosted.org/packages/7b/c6/7a465f1825872c55e0341ff4a80198743f73b69ce5d43ab18043699d1d81/protobuf-6.33.0-cp310-abi3-win_amd64.whl", hash = "sha256:9a031d10f703f03768f2743a1c403af050b6ae1f3480e9c140f39c45f81b13ee", size = 436882, upload-time = "2025-10-15T20:39:42.841Z" }, - { 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, upload-time = "2025-10-15T20:39:43.803Z" }, - { url = "https://files.pythonhosted.org/packages/10/35/16d31e0f92c6d2f0e77c2a3ba93185130ea13053dd16200a57434c882f2b/protobuf-6.33.0-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:e0697ece353e6239b90ee43a9231318302ad8353c70e6e45499fa52396debf90", size = 324445, upload-time = "2025-10-15T20:39:44.932Z" }, - { url = "https://files.pythonhosted.org/packages/e6/eb/2a981a13e35cda8b75b5585aaffae2eb904f8f351bdd3870769692acbd8a/protobuf-6.33.0-cp39-abi3-manylinux2014_s390x.whl", hash = "sha256:e0a1715e4f27355afd9570f3ea369735afc853a6c3951a6afe1f80d8569ad298", size = 339159, upload-time = "2025-10-15T20:39:46.186Z" }, - { url = "https://files.pythonhosted.org/packages/21/51/0b1cbad62074439b867b4e04cc09b93f6699d78fd191bed2bbb44562e077/protobuf-6.33.0-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:35be49fd3f4fefa4e6e2aacc35e8b837d6703c37a2168a55ac21e9b1bc7559ef", size = 323172, upload-time = "2025-10-15T20:39:47.465Z" }, - { url = "https://files.pythonhosted.org/packages/57/33/fbe61bbe91a656619f107b9dfd84b16e1438766bd62157b8d1c1214491fd/protobuf-6.33.0-cp39-cp39-win32.whl", hash = "sha256:cd33a8e38ea3e39df66e1bbc462b076d6e5ba3a4ebbde58219d777223a7873d3", size = 425690, upload-time = "2025-10-15T20:39:48.909Z" }, - { url = "https://files.pythonhosted.org/packages/2c/e4/ccc4814ad9d12fa404f7e5ce1983a2403644b0ed2588678c762b7a26ed92/protobuf-6.33.0-cp39-cp39-win_amd64.whl", hash = "sha256:c963e86c3655af3a917962c9619e1a6b9670540351d7af9439d06064e3317cc9", size = 436876, upload-time = "2025-10-15T20:39:50.009Z" }, - { url = "https://files.pythonhosted.org/packages/07/d1/0a28c21707807c6aacd5dc9c3704b2aa1effbf37adebd8caeaf68b17a636/protobuf-6.33.0-py3-none-any.whl", hash = "sha256:25c9e1963c6734448ea2d308cfa610e692b801304ba0908d7bfa564ac5132995", size = 170477, upload-time = "2025-10-15T20:39:51.311Z" }, +version = "5.29.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/43/29/d09e70352e4e88c9c7a198d5645d7277811448d76c23b00345670f7c8a38/protobuf-5.29.5.tar.gz", hash = "sha256:bc1463bafd4b0929216c35f437a8e28731a2b7fe3d98bb77a600efced5a15c84", size = 425226, upload-time = "2025-05-28T23:51:59.82Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5f/11/6e40e9fc5bba02988a214c07cf324595789ca7820160bfd1f8be96e48539/protobuf-5.29.5-cp310-abi3-win32.whl", hash = "sha256:3f1c6468a2cfd102ff4703976138844f78ebd1fb45f49011afc5139e9e283079", size = 422963, upload-time = "2025-05-28T23:51:41.204Z" }, + { url = "https://files.pythonhosted.org/packages/81/7f/73cefb093e1a2a7c3ffd839e6f9fcafb7a427d300c7f8aef9c64405d8ac6/protobuf-5.29.5-cp310-abi3-win_amd64.whl", hash = "sha256:3f76e3a3675b4a4d867b52e4a5f5b78a2ef9565549d4037e06cf7b0942b1d3fc", size = 434818, upload-time = "2025-05-28T23:51:44.297Z" }, + { url = "https://files.pythonhosted.org/packages/dd/73/10e1661c21f139f2c6ad9b23040ff36fee624310dc28fba20d33fdae124c/protobuf-5.29.5-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:e38c5add5a311f2a6eb0340716ef9b039c1dfa428b28f25a7838ac329204a671", size = 418091, upload-time = "2025-05-28T23:51:45.907Z" }, + { url = "https://files.pythonhosted.org/packages/6c/04/98f6f8cf5b07ab1294c13f34b4e69b3722bb609c5b701d6c169828f9f8aa/protobuf-5.29.5-cp38-abi3-manylinux2014_aarch64.whl", hash = "sha256:fa18533a299d7ab6c55a238bf8629311439995f2e7eca5caaff08663606e9015", size = 319824, upload-time = "2025-05-28T23:51:47.545Z" }, + { url = "https://files.pythonhosted.org/packages/85/e4/07c80521879c2d15f321465ac24c70efe2381378c00bf5e56a0f4fbac8cd/protobuf-5.29.5-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:63848923da3325e1bf7e9003d680ce6e14b07e55d0473253a690c3a8b8fd6e61", size = 319942, upload-time = "2025-05-28T23:51:49.11Z" }, + { url = "https://files.pythonhosted.org/packages/e5/59/ca89678bb0352f094fc92f2b358daa40e3acc91a93aa8f922b24762bf841/protobuf-5.29.5-cp39-cp39-win32.whl", hash = "sha256:6f642dc9a61782fa72b90878af134c5afe1917c89a568cd3476d758d3c3a0736", size = 423025, upload-time = "2025-05-28T23:51:54.003Z" }, + { url = "https://files.pythonhosted.org/packages/96/8b/2c62731fe3e92ddbbeca0174f78f0f8739197cdeb7c75ceb5aad3706963b/protobuf-5.29.5-cp39-cp39-win_amd64.whl", hash = "sha256:470f3af547ef17847a28e1f47200a1cbf0ba3ff57b7de50d22776607cd2ea353", size = 434906, upload-time = "2025-05-28T23:51:55.782Z" }, + { url = "https://files.pythonhosted.org/packages/7e/cc/7e77861000a0691aeea8f4566e5d3aa716f2b1dece4a24439437e41d3d25/protobuf-5.29.5-py3-none-any.whl", hash = "sha256:6cf42630262c59b2d8de33954443d94b746c952b01434fc58a417fdbd2e84bd5", size = 172823, upload-time = "2025-05-28T23:51:58.157Z" }, ] [[package]]