diff --git a/python/.pre-commit-config.yaml b/python/.pre-commit-config.yaml index 6d5df0b32c..98de81df06 100644 --- a/python/.pre-commit-config.yaml +++ b/python/.pre-commit-config.yaml @@ -27,12 +27,6 @@ repos: name: Check Valid Python Samples types: ["python"] exclude: ^python/packages/lab/cookiecutter-agent-framework-lab/ - - repo: https://github.com/nbQA-dev/nbQA - rev: 1.9.1 - hooks: - - id: nbqa-check-ast - name: Check Valid Python Notebooks - types: ["jupyter"] - repo: https://github.com/asottile/pyupgrade rev: v3.20.0 hooks: @@ -47,6 +41,13 @@ repos: entry: uv --directory ./python run poe pre-commit-check language: system files: ^python/ + - repo: https://github.com/PyCQA/bandit + rev: 1.8.5 + hooks: + - id: bandit + name: Bandit Security Checks + args: ["-c", "python/pyproject.toml"] + additional_dependencies: ["bandit[toml]"] - repo: https://github.com/astral-sh/uv-pre-commit # uv version. rev: 0.7.18 @@ -56,10 +57,3 @@ repos: name: Update uv lockfile files: python/pyproject.toml args: [--project, python] - - repo: https://github.com/PyCQA/bandit - rev: 1.8.5 - hooks: - - id: bandit - name: Bandit Security Checks - args: ["-c", "python/pyproject.toml"] - additional_dependencies: ["bandit[toml]"] diff --git a/python/AGENTS.md b/python/AGENTS.md index 1193ca6957..ee440b20ec 100644 --- a/python/AGENTS.md +++ b/python/AGENTS.md @@ -72,6 +72,12 @@ from agent_framework.azure import AzureOpenAIChatClient, AzureAIAgentClient When modifying samples, update associated README files in the same or parent folders. +### Samples Syntax Checking + +Run `uv run poe samples-syntax` to check samples for syntax errors and missing imports from `agent_framework`. This uses a relaxed pyright configuration that validates imports without strict type checking. + +Some samples depend on external packages (e.g., `azure.ai.agentserver.agentframework`, `microsoft_agents`) that are not installed in the dev environment. These are excluded in `pyrightconfig.samples.json`. When adding or modifying these excluded samples, add them to the exclude list and manually verify they have no import errors from `agent_framework` packages by temporarily removing them from the exclude list and running the check. + ## Package Documentation ### Core diff --git a/python/packages/core/agent_framework/_types.py b/python/packages/core/agent_framework/_types.py index 8180926324..b5fc029894 100644 --- a/python/packages/core/agent_framework/_types.py +++ b/python/packages/core/agent_framework/_types.py @@ -6,6 +6,7 @@ import json import re import sys +from asyncio import iscoroutine from collections.abc import AsyncIterable, AsyncIterator, Awaitable, Callable, Mapping, MutableMapping, Sequence from copy import deepcopy from typing import TYPE_CHECKING, Any, ClassVar, Final, Generic, Literal, NewType, cast, overload @@ -2676,7 +2677,10 @@ async def _get_stream(self) -> AsyncIterable[TUpdate]: if hasattr(self._stream_source, "__aiter__"): self._stream = self._stream_source # type: ignore[assignment] else: - self._stream = await self._stream_source # type: ignore[assignment] + if not iscoroutine(self._stream_source): + self._stream = self._stream_source # type: ignore[assignment] + else: + self._stream = await self._stream_source # type: ignore[assignment] if isinstance(self._stream, ResponseStream) and self._wrap_inner: self._inner_stream = self._stream return self._stream @@ -2739,12 +2743,12 @@ async def get_final_response(self) -> TFinal: """ if self._wrap_inner: if self._inner_stream is None: - if self._inner_stream_source is None: - raise ValueError("No inner stream configured for this stream.") - if isinstance(self._inner_stream_source, ResponseStream): - self._inner_stream = self._inner_stream_source - else: - self._inner_stream = await self._inner_stream_source + # Use _get_stream() to resolve the awaitable - this properly handles + # the case where _stream_source and _inner_stream_source are the same + # coroutine (e.g., from from_awaitable), avoiding double-await errors. + await self._get_stream() + if self._inner_stream is None: + raise RuntimeError("Inner stream not available") if not self._finalized: # Consume outer stream (which delegates to inner) if not already consumed if not self._consumed: diff --git a/python/pyproject.toml b/python/pyproject.toml index 844c9d09a9..60d70f1f68 100644 --- a/python/pyproject.toml +++ b/python/pyproject.toml @@ -225,8 +225,10 @@ test = "python run_tasks_in_packages_if_exists.py test" fmt = "python run_tasks_in_packages_if_exists.py fmt" format.ref = "fmt" lint = "python run_tasks_in_packages_if_exists.py lint" +samples-lint = "ruff check samples --fix --exclude samples/autogen-migration,samples/semantic-kernel-migration --ignore E501,ASYNC,B901,TD002" pyright = "python run_tasks_in_packages_if_exists.py pyright" mypy = "python run_tasks_in_packages_if_exists.py mypy" +samples-syntax = "pyright -p pyrightconfig.samples.json --warnings" typing = ["pyright", "mypy"] # cleaning clean-dist-packages = "python run_tasks_in_packages_if_exists.py clean-dist" @@ -238,7 +240,7 @@ build-meta = "python -m flit build" build = ["build-packages", "build-meta"] publish = "uv publish" # combined checks -check = ["fmt", "lint", "pyright", "mypy", "test", "markdown-code-lint"] +check = ["fmt", "lint", "pyright", "mypy", "samples-lint", "samples-syntax", "test", "markdown-code-lint"] [tool.poe.tasks.all-tests-cov] cmd = """ @@ -323,7 +325,9 @@ sequence = [ { ref = "fmt" }, { ref = "lint" }, { ref = "pre-commit-pyright ${files}" }, - { ref = "pre-commit-markdown-code-lint ${files}" } + { ref = "pre-commit-markdown-code-lint ${files}" }, + { ref = "samples-lint" }, + { ref = "samples-syntax" } ] args = [{ name = "files", default = ".", positional = true, multiple = true }] diff --git a/python/pyrightconfig.samples.json b/python/pyrightconfig.samples.json new file mode 100644 index 0000000000..a74e252474 --- /dev/null +++ b/python/pyrightconfig.samples.json @@ -0,0 +1,13 @@ +{ + "include": ["samples"], + "exclude": [ + "**/autogen/**", + "**/autogen-migration/**", + "**/semantic-kernel-migration/**", + "**/demos/**", + "**/agent_with_foundry_tracing.py" + ], + "typeCheckingMode": "off", + "reportMissingImports": "error", + "reportAttributeAccessIssue": "error" +} diff --git a/python/samples/autogen-migration/orchestrations/03_swarm.py b/python/samples/autogen-migration/orchestrations/03_swarm.py index fd6085bbef..7f221e8b48 100644 --- a/python/samples/autogen-migration/orchestrations/03_swarm.py +++ b/python/samples/autogen-migration/orchestrations/03_swarm.py @@ -7,7 +7,7 @@ import asyncio -from agent_framework import WorkflowEvent +from agent_framework import AgentResponseUpdate, WorkflowEvent from orderedmultidict import Any @@ -99,7 +99,6 @@ async def run_autogen() -> None: async def run_agent_framework() -> None: """Agent Framework's HandoffBuilder for agent coordination.""" from agent_framework import ( - AgentResponseUpdate, WorkflowRunState, ) from agent_framework.openai import OpenAIChatClient diff --git a/python/samples/autogen-migration/single_agent/04_agent_as_tool.py b/python/samples/autogen-migration/single_agent/04_agent_as_tool.py index 52edc1eec7..432e489d45 100644 --- a/python/samples/autogen-migration/single_agent/04_agent_as_tool.py +++ b/python/samples/autogen-migration/single_agent/04_agent_as_tool.py @@ -48,7 +48,7 @@ async def run_autogen() -> None: async def run_agent_framework() -> None: """Agent Framework's as_tool() for hierarchical agents with streaming.""" - from agent_framework import FunctionCallContent, FunctionResultContent + from agent_framework import Content from agent_framework.openai import OpenAIChatClient client = OpenAIChatClient(model_id="gpt-4.1-mini") @@ -78,7 +78,7 @@ async def run_agent_framework() -> None: print("[Agent Framework]") # Track accumulated function calls (they stream in incrementally) - accumulated_calls: dict[str, FunctionCallContent] = {} + accumulated_calls: dict[str, Content] = {} async for chunk in coordinator.run("Create a tagline for a coffee shop", stream=True): # Stream text tokens @@ -88,7 +88,7 @@ async def run_agent_framework() -> None: # Process streaming function calls and results if chunk.contents: for content in chunk.contents: - if isinstance(content, FunctionCallContent): + if content.type == "function_call": # Accumulate function call content as it streams in call_id = content.call_id if call_id in accumulated_calls: @@ -105,7 +105,7 @@ async def run_agent_framework() -> None: current_args = accumulated_calls[call_id].arguments print(f" Arguments: {current_args}", flush=True) - elif isinstance(content, FunctionResultContent): + elif content.type == "function_result": # Tool result - shows writer's response result_text = content.result if isinstance(content.result, str) else str(content.result) if result_text.strip(): diff --git a/python/samples/concepts/response_stream.py b/python/samples/concepts/response_stream.py index 98d5169760..6d99058062 100644 --- a/python/samples/concepts/response_stream.py +++ b/python/samples/concepts/response_stream.py @@ -154,11 +154,11 @@ async def generate_updates() -> AsyncIterable[ChatResponseUpdate]: words = ["Hello", " ", "from", " ", "the", " ", "streaming", " ", "response", "!"] for word in words: await asyncio.sleep(0.05) # Simulate network delay - yield ChatResponseUpdate(contents=[Content.from_text(word)], role=Role.ASSISTANT) + yield ChatResponseUpdate(contents=[Content.from_text(word)], role="assistant") def combine_updates(updates: Sequence[ChatResponseUpdate]) -> ChatResponse: """Finalizer that combines all updates into a single response.""" - return ChatResponse.from_chat_response_updates(updates) + return ChatResponse.from_updates(updates) stream = ResponseStream(generate_updates(), finalizer=combine_updates) @@ -237,7 +237,7 @@ async def cleanup_hook() -> None: ) print("Starting iteration (cleanup happens after):") - async for update in stream4: + async for _update in stream4: pass # Just consume the stream print(f"Cleanup was performed: {cleanup_performed['value']}") diff --git a/python/samples/demos/chatkit-integration/app.py b/python/samples/demos/chatkit-integration/app.py index 84ac060033..7ae37d28fc 100644 --- a/python/samples/demos/chatkit-integration/app.py +++ b/python/samples/demos/chatkit-integration/app.py @@ -18,7 +18,7 @@ import uvicorn # Agent Framework imports -from agent_framework import AgentResponseUpdate, ChatAgent, ChatMessage, FunctionResultContent, Role, tool +from agent_framework import AgentResponseUpdate, ChatAgent, ChatMessage, tool from agent_framework.azure import AzureOpenAIChatClient # Agent Framework ChatKit integration @@ -281,7 +281,7 @@ async def _update_thread_title( title_prompt = [ ChatMessage( - role=Role.USER, + role="user", text=( f"Generate a very short, concise title (max 40 characters) for a conversation " f"that starts with:\n\n{conversation_context}\n\n" @@ -332,7 +332,6 @@ async def respond( runs the agent, converts the response back to ChatKit events using stream_agent_response, and creates interactive weather widgets when weather data is queried. """ - from agent_framework import FunctionResultContent if input_user_message is None: logger.debug("Received None user message, skipping") @@ -375,7 +374,7 @@ async def intercept_stream() -> AsyncIterator[AgentResponseUpdate]: # Check for function results in the update if update.contents: for content in update.contents: - if isinstance(content, FunctionResultContent): + if content.type == "function_result": result = content.result # Check if it's a WeatherResponse (string subclass with weather_data attribute) @@ -458,7 +457,7 @@ async def action( weather_data: WeatherData | None = None # Create an agent message asking about the weather - agent_messages = [ChatMessage(role=Role.USER, text=f"What's the weather in {city_label}?")] + agent_messages = [ChatMessage(role="user", text=f"What's the weather in {city_label}?")] logger.debug(f"Processing weather query: {agent_messages[0].text}") @@ -472,7 +471,7 @@ async def intercept_stream() -> AsyncIterator[AgentResponseUpdate]: # Check for function results in the update if update.contents: for content in update.contents: - if isinstance(content, FunctionResultContent): + if content.type == "function_result": result = content.result # Check if it's a WeatherResponse (string subclass with weather_data attribute) @@ -563,7 +562,7 @@ async def chatkit_endpoint(request: Request): @app.post("/upload/{attachment_id}") -async def upload_file(attachment_id: str, file: UploadFile = File(...)): +async def upload_file(attachment_id: str, file: Annotated[UploadFile, File()]): """Handle file upload for two-phase upload. The client POSTs the file bytes here after creating the attachment @@ -585,7 +584,7 @@ async def upload_file(attachment_id: str, file: UploadFile = File(...)): attachment = await data_store.load_attachment(attachment_id, {"user_id": DEFAULT_USER_ID}) # Clear the upload_url since upload is complete - attachment.upload_url = None + attachment.upload_url = None # type: ignore[union-attr] # Save the updated attachment back to the store await data_store.save_attachment(attachment, {"user_id": DEFAULT_USER_ID}) diff --git a/python/samples/demos/hosted_agents/agents_in_workflow/main.py b/python/samples/demos/hosted_agents/agents_in_workflow/main.py index 5402e962ac..f1356be33d 100644 --- a/python/samples/demos/hosted_agents/agents_in_workflow/main.py +++ b/python/samples/demos/hosted_agents/agents_in_workflow/main.py @@ -1,7 +1,7 @@ # Copyright (c) Microsoft. All rights reserved. -from agent_framework import ConcurrentBuilder from agent_framework.azure import AzureOpenAIChatClient +from agent_framework_orchestrations import ConcurrentBuilder from azure.ai.agentserver.agentframework import from_agent_framework from azure.identity import DefaultAzureCredential # pyright: ignore[reportUnknownVariableType] diff --git a/python/samples/demos/workflow_evaluation/_tools.py b/python/samples/demos/workflow_evaluation/_tools.py index 0e5443d5b0..b9b6038191 100644 --- a/python/samples/demos/workflow_evaluation/_tools.py +++ b/python/samples/demos/workflow_evaluation/_tools.py @@ -21,7 +21,7 @@ def search_hotels( guests: Annotated[int, Field(description="Number of guests.")] = 2, ) -> str: """Search for available hotels based on location and dates. - + Returns: JSON string containing search results with hotel details including name, rating, price, distance to landmarks, amenities, and availability. @@ -88,7 +88,7 @@ def get_hotel_details( hotel_name: Annotated[str, Field(description="Name of the hotel to get details for.")], ) -> str: """Get detailed information about a specific hotel. - + Returns: JSON string containing detailed hotel information including description, check-in/out times, cancellation policy, reviews, and nearby attractions. @@ -167,7 +167,7 @@ def search_flights( passengers: Annotated[int, Field(description="Number of passengers.")] = 1, ) -> str: """Search for available flights between two locations. - + Returns: JSON string containing flight search results with details including flight numbers, airlines, departure/arrival times, prices, durations, and baggage allowances. @@ -289,7 +289,7 @@ def get_flight_details( flight_number: Annotated[str, Field(description="Flight number (e.g., 'AF007' or 'DL264').")], ) -> str: """Get detailed information about a specific flight. - + Returns: JSON string containing detailed flight information including airline, aircraft type, departure/arrival airports and times, gates, terminals, duration, and amenities. @@ -331,7 +331,7 @@ def search_activities( category: Annotated[str | None, Field(description="Activity category (e.g., 'Sightseeing', 'Culture', 'Culinary').")] = None, ) -> str: """Search for available activities and attractions at a destination. - + Returns: JSON string containing activity search results with details including name, category, duration, price, rating, description, availability, and booking requirements. @@ -440,10 +440,7 @@ def search_activities( } ] - if category: - activities = [act for act in all_activities if act["category"] == category] - else: - activities = all_activities + activities = [act for act in all_activities if act["category"] == category] if category else all_activities else: activities = [ { @@ -473,7 +470,7 @@ def get_activity_details( activity_name: Annotated[str, Field(description="Name of the activity to get details for.")], ) -> str: """Get detailed information about a specific activity. - + Returns: JSON string containing detailed activity information including description, duration, price, included items, meeting point, what to bring, cancellation policy, and reviews. @@ -552,7 +549,7 @@ def confirm_booking( customer_info: Annotated[dict, Field(description="Customer information including name and email.")], ) -> str: """Confirm a booking reservation. - + Returns: JSON string containing confirmation details including confirmation number, booking status, customer information, and next steps. @@ -587,9 +584,9 @@ def check_hotel_availability( rooms: Annotated[int, Field(description="Number of rooms needed.")] = 1, ) -> str: """Check availability for hotel rooms. - + Sample Date format: "December 15, 2025" - + Returns: JSON string containing availability status, available rooms count, price per night, and last checked timestamp. @@ -621,9 +618,9 @@ def check_flight_availability( passengers: Annotated[int, Field(description="Number of passengers.")] = 1, ) -> str: """Check availability for flight seats. - + Sample Date format: "December 15, 2025" - + Returns: JSON string containing availability status, available seats count, price per passenger, and last checked timestamp. @@ -654,9 +651,9 @@ def check_activity_availability( participants: Annotated[int, Field(description="Number of participants.")] = 1, ) -> str: """Check availability for activity bookings. - + Sample Date format: "December 16, 2025" - + Returns: JSON string containing availability status, available spots count, price per person, and last checked timestamp. @@ -688,7 +685,7 @@ def process_payment( booking_reference: Annotated[str, Field(description="Booking reference number for the payment.")], ) -> str: """Process payment for a booking. - + Returns: JSON string containing payment result with transaction ID, status, amount, currency, payment method details, and receipt URL. @@ -718,7 +715,7 @@ def validate_payment_method( payment_method: Annotated[dict, Field(description="Payment method to validate (type, number, expiry, cvv).")], ) -> str: """Validate payment method details. - + Returns: JSON string containing validation result with is_valid flag, payment method type, validation messages, supported currencies, and processing fee information. diff --git a/python/samples/demos/workflow_evaluation/create_workflow.py b/python/samples/demos/workflow_evaluation/create_workflow.py index 7e87a499da..2eb31d3492 100644 --- a/python/samples/demos/workflow_evaluation/create_workflow.py +++ b/python/samples/demos/workflow_evaluation/create_workflow.py @@ -154,19 +154,19 @@ async def run_workflow_with_response_tracking(query: str, chat_client: AzureAICl """ if chat_client is None: try: - # Create AIProjectClient with the correct API version for V2 prompt agents - project_client = AIProjectClient( - endpoint=os.environ["AZURE_AI_PROJECT_ENDPOINT"], - credential=credential, - api_version="2025-11-15-preview", - ) - - async with ( - DefaultAzureCredential() as credential, - project_client, - AzureAIClient(project_client=project_client, credential=credential) as client, - ): - return await _run_workflow_with_client(query, client) + async with DefaultAzureCredential() as credential: + # Create AIProjectClient with the correct API version for V2 prompt agents + project_client = AIProjectClient( + endpoint=os.environ["AZURE_AI_PROJECT_ENDPOINT"], + credential=credential, + api_version="2025-11-15-preview", + ) + + async with ( + project_client, + AzureAIClient(project_client=project_client, credential=credential) as client, + ): + return await _run_workflow_with_client(query, client) except Exception as e: print(f"Error during workflow execution: {e}") raise @@ -369,27 +369,36 @@ async def _process_workflow_events(events, conversation_ids, response_ids): def _track_agent_ids(event, agent, response_ids, conversation_ids): """Track agent response and conversation IDs - supporting multiple responses per agent.""" - if isinstance(event.data, AgentResponseUpdate): + if ( + isinstance(event.data, AgentResponseUpdate) + and hasattr(event.data, "raw_representation") + and event.data.raw_representation + ): # Check for conversation_id and response_id from raw_representation # V2 API stores conversation_id directly on raw_representation (ChatResponseUpdate) - if hasattr(event.data, "raw_representation") and event.data.raw_representation: - raw = event.data.raw_representation - - # Try conversation_id directly on raw representation - if hasattr(raw, "conversation_id") and raw.conversation_id: + raw = event.data.raw_representation + + # Try conversation_id directly on raw representation + if ( + hasattr(raw, "conversation_id") + and raw.conversation_id # type: ignore[union-attr] + and raw.conversation_id not in conversation_ids[agent] # type: ignore[union-attr] + ): + # Only add if not already in the list + conversation_ids[agent].append(raw.conversation_id) # type: ignore[union-attr] + + # Extract response_id from the OpenAI event (available from first event) + if hasattr(raw, "raw_representation") and raw.raw_representation: # type: ignore[union-attr] + openai_event = raw.raw_representation # type: ignore[union-attr] + + # Check if event has response object with id + if ( + hasattr(openai_event, "response") + and hasattr(openai_event.response, "id") + and openai_event.response.id not in response_ids[agent] + ): # Only add if not already in the list - if raw.conversation_id not in conversation_ids[agent]: - conversation_ids[agent].append(raw.conversation_id) - - # Extract response_id from the OpenAI event (available from first event) - if hasattr(raw, "raw_representation") and raw.raw_representation: - openai_event = raw.raw_representation - - # Check if event has response object with id - if hasattr(openai_event, "response") and hasattr(openai_event.response, "id"): - # Only add if not already in the list - if openai_event.response.id not in response_ids[agent]: - response_ids[agent].append(openai_event.response.id) + response_ids[agent].append(openai_event.response.id) async def create_and_run_workflow(): diff --git a/python/samples/demos/workflow_evaluation/run_evaluation.py b/python/samples/demos/workflow_evaluation/run_evaluation.py index defcde114f..ed17b54258 100644 --- a/python/samples/demos/workflow_evaluation/run_evaluation.py +++ b/python/samples/demos/workflow_evaluation/run_evaluation.py @@ -29,7 +29,7 @@ def print_section(title: str): async def run_workflow(): """Execute the multi-agent travel planning workflow. - + Returns: Dictionary containing workflow data with agent response IDs """ diff --git a/python/samples/getting_started/agents/anthropic/anthropic_advanced.py b/python/samples/getting_started/agents/anthropic/anthropic_advanced.py index 4737903ca5..8d15c2d91e 100644 --- a/python/samples/getting_started/agents/anthropic/anthropic_advanced.py +++ b/python/samples/getting_started/agents/anthropic/anthropic_advanced.py @@ -2,7 +2,7 @@ import asyncio -from agent_framework import HostedMCPTool, HostedWebSearchTool, TextReasoningContent, UsageContent +from agent_framework import HostedMCPTool, HostedWebSearchTool from agent_framework.anthropic import AnthropicChatOptions, AnthropicClient """ @@ -40,9 +40,9 @@ async def main() -> None: print("Agent: ", end="", flush=True) async for chunk in agent.run(query, stream=True): for content in chunk.contents: - if isinstance(content, TextReasoningContent): + if content.type == "text_reasoning": print(f"\033[32m{content.text}\033[0m", end="", flush=True) - if isinstance(content, UsageContent): + if content.type == "usage": print(f"\n\033[34m[Usage so far: {content.usage_details}]\033[0m\n", end="", flush=True) if chunk.text: print(chunk.text, end="", flush=True) diff --git a/python/samples/getting_started/agents/anthropic/anthropic_foundry.py b/python/samples/getting_started/agents/anthropic/anthropic_foundry.py index ac7c9ac95d..c9064dbe57 100644 --- a/python/samples/getting_started/agents/anthropic/anthropic_foundry.py +++ b/python/samples/getting_started/agents/anthropic/anthropic_foundry.py @@ -2,7 +2,7 @@ import asyncio -from agent_framework import HostedMCPTool, HostedWebSearchTool, TextReasoningContent, UsageContent +from agent_framework import HostedMCPTool, HostedWebSearchTool from agent_framework.anthropic import AnthropicClient from anthropic import AsyncAnthropicFoundry @@ -51,9 +51,9 @@ async def main() -> None: print("Agent: ", end="", flush=True) async for chunk in agent.run(query, stream=True): for content in chunk.contents: - if isinstance(content, TextReasoningContent): + if content.type == "text_reasoning": print(f"\033[32m{content.text}\033[0m", end="", flush=True) - if isinstance(content, UsageContent): + if content.type == "usage": print(f"\n\033[34m[Usage so far: {content.usage_details}]\033[0m\n", end="", flush=True) if chunk.text: print(chunk.text, end="", flush=True) diff --git a/python/samples/getting_started/agents/anthropic/anthropic_skills.py b/python/samples/getting_started/agents/anthropic/anthropic_skills.py index fa420269c0..108646543a 100644 --- a/python/samples/getting_started/agents/anthropic/anthropic_skills.py +++ b/python/samples/getting_started/agents/anthropic/anthropic_skills.py @@ -4,7 +4,7 @@ import logging from pathlib import Path -from agent_framework import HostedCodeInterpreterTool, HostedFileContent +from agent_framework import Content, HostedCodeInterpreterTool from agent_framework.anthropic import AnthropicChatOptions, AnthropicClient logger = logging.getLogger(__name__) @@ -52,7 +52,7 @@ async def main() -> None: query = "Create a presentation about renewable energy with 5 slides" print(f"User: {query}") print("Agent: ", end="", flush=True) - files: list[HostedFileContent] = [] + files: list[Content] = [] async for chunk in agent.run(query, stream=True): for content in chunk.contents: match content.type: diff --git a/python/samples/getting_started/agents/azure_ai/azure_ai_with_code_interpreter_file_download.py b/python/samples/getting_started/agents/azure_ai/azure_ai_with_code_interpreter_file_download.py index 19223c5195..ff0d9df4dc 100644 --- a/python/samples/getting_started/agents/azure_ai/azure_ai_with_code_interpreter_file_download.py +++ b/python/samples/getting_started/agents/azure_ai/azure_ai_with_code_interpreter_file_download.py @@ -6,11 +6,10 @@ from agent_framework import ( AgentResponseUpdate, + Annotation, ChatAgent, - CitationAnnotation, + Content, HostedCodeInterpreterTool, - HostedFileContent, - TextContent, ) from agent_framework.azure import AzureAIProjectAgentProvider from azure.identity.aio import AzureCliCredential @@ -34,19 +33,17 @@ ) -async def download_container_files( - file_contents: list[CitationAnnotation | HostedFileContent], agent: ChatAgent -) -> list[Path]: +async def download_container_files(file_contents: list[Annotation | Content], agent: ChatAgent) -> list[Path]: """Download container files using the OpenAI containers API. Code interpreter generates files in containers, which require both file_id and container_id to download. The container_id is stored in additional_properties. - This function works for both streaming (HostedFileContent) and non-streaming - (CitationAnnotation) responses. + This function works for both streaming (Content with type="hosted_file") and non-streaming + (Annotation) responses. Args: - file_contents: List of CitationAnnotation or HostedFileContent objects + file_contents: List of Annotation or Content objects containing file_id and container_id. agent: The ChatAgent instance with access to the AzureAIClient. @@ -64,28 +61,36 @@ async def download_container_files( print(f"\nDownloading {len(file_contents)} container file(s) to {output_dir.absolute()}...") # Access the OpenAI client from AzureAIClient - openai_client = agent.chat_client.client + openai_client = agent.chat_client.client # type: ignore[attr-defined] downloaded_files: list[Path] = [] for content in file_contents: - file_id = content.file_id + # Handle both Annotation (TypedDict) and Content objects + if isinstance(content, dict): # Annotation TypedDict + file_id = content.get("file_id") + additional_props = content.get("additional_properties", {}) + url = content.get("url") + else: # Content object + file_id = content.file_id + additional_props = content.additional_properties or {} + url = content.uri # Extract container_id from additional_properties - if not content.additional_properties or "container_id" not in content.additional_properties: + if not additional_props or "container_id" not in additional_props: print(f" File {file_id}: ✗ Missing container_id") continue - container_id = content.additional_properties["container_id"] + container_id = additional_props["container_id"] # Extract filename based on content type - if isinstance(content, CitationAnnotation): - filename = content.url or f"{file_id}.txt" + if isinstance(content, dict): # Annotation TypedDict + filename = url or f"{file_id}.txt" # Extract filename from sandbox URL if present (e.g., sandbox:/mnt/data/sample.txt) if filename.startswith("sandbox:"): filename = filename.split("/")[-1] - else: # HostedFileContent - filename = content.additional_properties.get("filename") or f"{file_id}.txt" + else: # Content + filename = additional_props.get("filename") or f"{file_id}.txt" output_path = output_dir / filename @@ -133,17 +138,18 @@ async def non_streaming_example() -> None: print(f"Agent: {result.text}\n") # Check for annotations in the response - annotations_found: list[CitationAnnotation] = [] + annotations_found: list[Annotation] = [] # AgentResponse has messages property, which contains ChatMessage objects for message in result.messages: for content in message.contents: if content.type == "text" and content.annotations: for annotation in content.annotations: - if isinstance(annotation, CitationAnnotation) and annotation.file_id: + if annotation.get("file_id"): annotations_found.append(annotation) - print(f"Found file annotation: file_id={annotation.file_id}") - if annotation.additional_properties and "container_id" in annotation.additional_properties: - print(f" container_id={annotation.additional_properties['container_id']}") + print(f"Found file annotation: file_id={annotation['file_id']}") + additional_props = annotation.get("additional_properties", {}) + if additional_props and "container_id" in additional_props: + print(f" container_id={additional_props['container_id']}") if annotations_found: print(f"SUCCESS: Found {len(annotations_found)} file annotation(s)") @@ -174,7 +180,7 @@ async def streaming_example() -> None: ) print(f"User: {QUERY}\n") - file_contents_found: list[HostedFileContent] = [] + file_contents_found: list[Content] = [] text_chunks: list[str] = [] async for update in agent.run(QUERY, stream=True): @@ -185,11 +191,11 @@ async def streaming_example() -> None: text_chunks.append(content.text) if content.annotations: for annotation in content.annotations: - if isinstance(annotation, CitationAnnotation) and annotation.file_id: - print(f"Found streaming CitationAnnotation: file_id={annotation.file_id}") - elif isinstance(content, HostedFileContent): + if annotation.get("file_id"): + print(f"Found streaming annotation: file_id={annotation['file_id']}") + elif content.type == "hosted_file": file_contents_found.append(content) - print(f"Found streaming HostedFileContent: file_id={content.file_id}") + print(f"Found streaming hosted_file: file_id={content.file_id}") if content.additional_properties and "container_id" in content.additional_properties: print(f" container_id={content.additional_properties['container_id']}") diff --git a/python/samples/getting_started/agents/azure_ai/azure_ai_with_code_interpreter_file_generation.py b/python/samples/getting_started/agents/azure_ai/azure_ai_with_code_interpreter_file_generation.py index b0c83dc206..9c9fc48feb 100644 --- a/python/samples/getting_started/agents/azure_ai/azure_ai_with_code_interpreter_file_generation.py +++ b/python/samples/getting_started/agents/azure_ai/azure_ai_with_code_interpreter_file_generation.py @@ -49,9 +49,9 @@ async def non_streaming_example() -> None: for content in message.contents: if content.type == "text" and content.annotations: for annotation in content.annotations: - if annotation.file_id: - annotations_found.append(annotation.file_id) - print(f"Found file annotation: file_id={annotation.file_id}") + if annotation.get("file_id"): + annotations_found.append(annotation["file_id"]) + print(f"Found file annotation: file_id={annotation['file_id']}") if annotations_found: print(f"SUCCESS: Found {len(annotations_found)} file annotation(s)") @@ -86,9 +86,9 @@ async def streaming_example() -> None: text_chunks.append(content.text) if content.annotations: for annotation in content.annotations: - if annotation.file_id: - annotations_found.append(annotation.file_id) - print(f"Found streaming annotation: file_id={annotation.file_id}") + if annotation.get("file_id"): + annotations_found.append(annotation["file_id"]) + print(f"Found streaming annotation: file_id={annotation['file_id']}") elif content.type == "hosted_file": file_ids_found.append(content.file_id) print(f"Found streaming HostedFileContent: file_id={content.file_id}") diff --git a/python/samples/getting_started/agents/azure_ai/azure_ai_with_file_search.py b/python/samples/getting_started/agents/azure_ai/azure_ai_with_file_search.py index 9558546093..6a45aca516 100644 --- a/python/samples/getting_started/agents/azure_ai/azure_ai_with_file_search.py +++ b/python/samples/getting_started/agents/azure_ai/azure_ai_with_file_search.py @@ -4,7 +4,7 @@ import os from pathlib import Path -from agent_framework import HostedFileSearchTool, HostedVectorStoreContent +from agent_framework import Content, HostedFileSearchTool from agent_framework.azure import AzureAIProjectAgentProvider from azure.ai.agents.aio import AgentsClient from azure.ai.agents.models import FileInfo, VectorStore @@ -46,7 +46,7 @@ async def main() -> None: print(f"Created vector store, vector store ID: {vector_store.id}") # 2. Create file search tool with uploaded resources - file_search_tool = HostedFileSearchTool(inputs=[HostedVectorStoreContent(vector_store_id=vector_store.id)]) + file_search_tool = HostedFileSearchTool(inputs=[Content.from_hosted_vector_store(vector_store_id=vector_store.id)]) # 3. Create an agent with file search capabilities using the provider agent = await provider.create_agent( diff --git a/python/samples/getting_started/agents/azure_ai/azure_ai_with_hosted_mcp.py b/python/samples/getting_started/agents/azure_ai/azure_ai_with_hosted_mcp.py index 1b44f34b54..7f0660a5e8 100644 --- a/python/samples/getting_started/agents/azure_ai/azure_ai_with_hosted_mcp.py +++ b/python/samples/getting_started/agents/azure_ai/azure_ai_with_hosted_mcp.py @@ -3,7 +3,7 @@ import asyncio from typing import Any -from agent_framework import SupportsAgentRun, AgentResponse, AgentThread, ChatMessage, HostedMCPTool +from agent_framework import AgentResponse, AgentThread, ChatMessage, HostedMCPTool, SupportsAgentRun from agent_framework.azure import AzureAIProjectAgentProvider from azure.identity.aio import AzureCliCredential diff --git a/python/samples/getting_started/agents/azure_ai_agent/azure_ai_with_code_interpreter_file_generation.py b/python/samples/getting_started/agents/azure_ai_agent/azure_ai_with_code_interpreter_file_generation.py index 385ca4dc92..ac8d64f3cb 100644 --- a/python/samples/getting_started/agents/azure_ai_agent/azure_ai_with_code_interpreter_file_generation.py +++ b/python/samples/getting_started/agents/azure_ai_agent/azure_ai_with_code_interpreter_file_generation.py @@ -5,7 +5,6 @@ from agent_framework import ( HostedCodeInterpreterTool, - HostedFileContent, ) from agent_framework.azure import AzureAIAgentsProvider from azure.ai.agents.aio import AgentsClient @@ -63,7 +62,7 @@ async def main() -> None: for content in chunk.contents: if content.type == "text": print(content.text, end="", flush=True) - elif content.type == "hosted_file" and isinstance(content, HostedFileContent): + elif content.type == "hosted_file" and content.file_id: file_ids.append(content.file_id) print(f"\n[File generated: {content.file_id}]") diff --git a/python/samples/getting_started/agents/azure_ai_agent/azure_ai_with_file_search.py b/python/samples/getting_started/agents/azure_ai_agent/azure_ai_with_file_search.py index 63845b215b..353b4aacd2 100644 --- a/python/samples/getting_started/agents/azure_ai_agent/azure_ai_with_file_search.py +++ b/python/samples/getting_started/agents/azure_ai_agent/azure_ai_with_file_search.py @@ -4,7 +4,7 @@ import os from pathlib import Path -from agent_framework import HostedFileSearchTool, HostedVectorStoreContent +from agent_framework import Content, HostedFileSearchTool from agent_framework.azure import AzureAIAgentsProvider from azure.ai.agents.aio import AgentsClient from azure.ai.agents.models import FileInfo, VectorStore @@ -46,7 +46,7 @@ async def main() -> None: print(f"Created vector store, vector store ID: {vector_store.id}") # 2. Create file search tool with uploaded resources - file_search_tool = HostedFileSearchTool(inputs=[HostedVectorStoreContent(vector_store_id=vector_store.id)]) + file_search_tool = HostedFileSearchTool(inputs=[Content.from_hosted_vector_store(vector_store_id=vector_store.id)]) # 3. Create an agent with file search capabilities agent = await provider.create_agent( diff --git a/python/samples/getting_started/agents/azure_ai_agent/azure_ai_with_hosted_mcp.py b/python/samples/getting_started/agents/azure_ai_agent/azure_ai_with_hosted_mcp.py index b5c4f9e16e..19de064106 100644 --- a/python/samples/getting_started/agents/azure_ai_agent/azure_ai_with_hosted_mcp.py +++ b/python/samples/getting_started/agents/azure_ai_agent/azure_ai_with_hosted_mcp.py @@ -3,7 +3,7 @@ import asyncio from typing import Any -from agent_framework import SupportsAgentRun, AgentResponse, AgentThread, HostedMCPTool +from agent_framework import AgentResponse, AgentThread, HostedMCPTool, SupportsAgentRun from agent_framework.azure import AzureAIAgentsProvider from azure.identity.aio import AzureCliCredential diff --git a/python/samples/getting_started/agents/azure_ai_agent/azure_ai_with_multiple_tools.py b/python/samples/getting_started/agents/azure_ai_agent/azure_ai_with_multiple_tools.py index e3d2d48f4f..b7700dd6c2 100644 --- a/python/samples/getting_started/agents/azure_ai_agent/azure_ai_with_multiple_tools.py +++ b/python/samples/getting_started/agents/azure_ai_agent/azure_ai_with_multiple_tools.py @@ -5,10 +5,10 @@ from typing import Any from agent_framework import ( - SupportsAgentRun, AgentThread, HostedMCPTool, HostedWebSearchTool, + SupportsAgentRun, tool, ) from agent_framework.azure import AzureAIAgentsProvider diff --git a/python/samples/getting_started/agents/azure_openai/azure_responses_client_with_file_search.py b/python/samples/getting_started/agents/azure_openai/azure_responses_client_with_file_search.py index b42c7acf2f..08f35eb659 100644 --- a/python/samples/getting_started/agents/azure_openai/azure_responses_client_with_file_search.py +++ b/python/samples/getting_started/agents/azure_openai/azure_responses_client_with_file_search.py @@ -2,7 +2,7 @@ import asyncio -from agent_framework import ChatAgent, HostedFileSearchTool, HostedVectorStoreContent +from agent_framework import ChatAgent, Content, HostedFileSearchTool from agent_framework.azure import AzureOpenAIResponsesClient from azure.identity import AzureCliCredential @@ -22,7 +22,7 @@ # Helper functions -async def create_vector_store(client: AzureOpenAIResponsesClient) -> tuple[str, HostedVectorStoreContent]: +async def create_vector_store(client: AzureOpenAIResponsesClient) -> tuple[str, Content]: """Create a vector store with sample documents.""" file = await client.client.files.create( file=("todays_weather.txt", b"The weather today is sunny with a high of 75F."), purpose="assistants" @@ -35,7 +35,7 @@ async def create_vector_store(client: AzureOpenAIResponsesClient) -> tuple[str, if result.last_error is not None: raise Exception(f"Vector store file processing failed with status: {result.last_error.message}") - return file.id, HostedVectorStoreContent(vector_store_id=vector_store.id) + return file.id, Content.from_hosted_vector_store(vector_store_id=vector_store.id) async def delete_vector_store(client: AzureOpenAIResponsesClient, file_id: str, vector_store_id: str) -> None: diff --git a/python/samples/getting_started/agents/azure_openai/azure_responses_client_with_hosted_mcp.py b/python/samples/getting_started/agents/azure_openai/azure_responses_client_with_hosted_mcp.py index 1083cbe5b5..eddc54d48c 100644 --- a/python/samples/getting_started/agents/azure_openai/azure_responses_client_with_hosted_mcp.py +++ b/python/samples/getting_started/agents/azure_openai/azure_responses_client_with_hosted_mcp.py @@ -15,7 +15,7 @@ """ if TYPE_CHECKING: - from agent_framework import SupportsAgentRun, AgentThread + from agent_framework import AgentThread, SupportsAgentRun async def handle_approvals_without_thread(query: str, agent: "SupportsAgentRun"): diff --git a/python/samples/getting_started/agents/custom/custom_agent.py b/python/samples/getting_started/agents/custom/custom_agent.py index c29424dcbf..7df37ba781 100644 --- a/python/samples/getting_started/agents/custom/custom_agent.py +++ b/python/samples/getting_started/agents/custom/custom_agent.py @@ -12,7 +12,7 @@ ChatMessage, Content, Role, - TextContent, + normalize_messages, ) """ @@ -88,12 +88,14 @@ async def _run( ) -> AgentResponse: """Non-streaming implementation.""" # Normalize input messages to a list - normalized_messages = self._normalize_messages(messages) + normalized_messages = normalize_messages(messages) if not normalized_messages: response_message = ChatMessage( role=Role.ASSISTANT, - contents=[Content.from_text(text="Hello! I'm a custom echo agent. Send me a message and I'll echo it back.")], + contents=[ + Content.from_text(text="Hello! I'm a custom echo agent. Send me a message and I'll echo it back.") + ], ) else: # For simplicity, echo the last user message @@ -120,7 +122,7 @@ async def _run_stream( ) -> AsyncIterable[AgentResponseUpdate]: """Streaming implementation.""" # Normalize input messages to a list - normalized_messages = self._normalize_messages(messages) + normalized_messages = normalize_messages(messages) if not normalized_messages: response_text = "Hello! I'm a custom echo agent. Send me a message and I'll echo it back." diff --git a/python/samples/getting_started/agents/openai/openai_responses_client_basic.py b/python/samples/getting_started/agents/openai/openai_responses_client_basic.py index 06ecb55473..5ed88814fb 100644 --- a/python/samples/getting_started/agents/openai/openai_responses_client_basic.py +++ b/python/samples/getting_started/agents/openai/openai_responses_client_basic.py @@ -5,7 +5,15 @@ from random import randint from typing import Annotated -from agent_framework import ChatAgent, ChatContext, ChatMessage, ChatResponse, Role, chat_middleware, tool +from agent_framework import ( + ChatAgent, + ChatContext, + ChatMessage, + ChatResponse, + MiddlewareTermination, + chat_middleware, + tool, +) from agent_framework.openai import OpenAIResponsesClient from pydantic import Field @@ -39,7 +47,7 @@ async def security_and_override_middleware( context.result = ChatResponse( messages=[ ChatMessage( - role=Role.ASSISTANT, + role="assistant", text="I cannot process requests containing sensitive information. " "Please rephrase your question without including passwords, secrets, or other " "sensitive data.", @@ -48,8 +56,7 @@ async def security_and_override_middleware( ) # Set terminate flag to stop execution - context.terminate = True - return + raise MiddlewareTermination # Continue to next middleware or AI execution await next(context) diff --git a/python/samples/getting_started/agents/openai/openai_responses_client_image_generation.py b/python/samples/getting_started/agents/openai/openai_responses_client_image_generation.py index 635b99e85f..7d3c724b08 100644 --- a/python/samples/getting_started/agents/openai/openai_responses_client_image_generation.py +++ b/python/samples/getting_started/agents/openai/openai_responses_client_image_generation.py @@ -70,7 +70,7 @@ async def main() -> None: # Show information about the generated image for message in result.messages: for content in message.contents: - if content.type == "image_generation" and content.outputs: + if content.type == "image_generation_tool_result" and content.outputs: for output in content.outputs: if output.type in ("data", "uri") and output.uri: show_image_info(output.uri) diff --git a/python/samples/getting_started/agents/openai/openai_responses_client_with_code_interpreter.py b/python/samples/getting_started/agents/openai/openai_responses_client_with_code_interpreter.py index 29f8fa358a..71d81d9ba8 100644 --- a/python/samples/getting_started/agents/openai/openai_responses_client_with_code_interpreter.py +++ b/python/samples/getting_started/agents/openai/openai_responses_client_with_code_interpreter.py @@ -32,7 +32,7 @@ async def main() -> None: print(f"Result: {result}\n") for message in result.messages: - code_blocks = [c for c in message.contents if c.type == "code_interpreter_tool_input"] + code_blocks = [c for c in message.contents if c.type == "code_interpreter_tool_call"] outputs = [c for c in message.contents if c.type == "code_interpreter_tool_result"] if code_blocks: code_inputs = code_blocks[0].inputs or [] diff --git a/python/samples/getting_started/agents/openai/openai_responses_client_with_hosted_mcp.py b/python/samples/getting_started/agents/openai/openai_responses_client_with_hosted_mcp.py index 5272bae1ca..526503f813 100644 --- a/python/samples/getting_started/agents/openai/openai_responses_client_with_hosted_mcp.py +++ b/python/samples/getting_started/agents/openai/openai_responses_client_with_hosted_mcp.py @@ -14,7 +14,7 @@ """ if TYPE_CHECKING: - from agent_framework import SupportsAgentRun, AgentThread + from agent_framework import AgentThread, SupportsAgentRun async def handle_approvals_without_thread(query: str, agent: "SupportsAgentRun"): diff --git a/python/samples/getting_started/azure_functions/01_single_agent/function_app.py b/python/samples/getting_started/azure_functions/01_single_agent/function_app.py index 2dd7b8cf74..db90c4d1a7 100644 --- a/python/samples/getting_started/azure_functions/01_single_agent/function_app.py +++ b/python/samples/getting_started/azure_functions/01_single_agent/function_app.py @@ -1,3 +1,5 @@ +# Copyright (c) Microsoft. All rights reserved. + """Host a single Azure OpenAI-powered agent inside Azure Functions. Components used in this sample: diff --git a/python/samples/getting_started/azure_functions/02_multi_agent/function_app.py b/python/samples/getting_started/azure_functions/02_multi_agent/function_app.py index cb735baecd..6a3f396bcb 100644 --- a/python/samples/getting_started/azure_functions/02_multi_agent/function_app.py +++ b/python/samples/getting_started/azure_functions/02_multi_agent/function_app.py @@ -1,3 +1,5 @@ +# Copyright (c) Microsoft. All rights reserved. + """Host multiple Azure OpenAI agents inside a single Azure Functions app. Components used in this sample: diff --git a/python/samples/getting_started/azure_functions/03_reliable_streaming/redis_stream_response_handler.py b/python/samples/getting_started/azure_functions/03_reliable_streaming/redis_stream_response_handler.py index ff9f544062..c17439589e 100644 --- a/python/samples/getting_started/azure_functions/03_reliable_streaming/redis_stream_response_handler.py +++ b/python/samples/getting_started/azure_functions/03_reliable_streaming/redis_stream_response_handler.py @@ -163,7 +163,7 @@ async def read_stream( has_seen_data = True # Process entries from the stream - for stream_name, stream_entries in entries: + for _stream_name, stream_entries in entries: for entry_id, entry_data in stream_entries: start_id = entry_id.decode() if isinstance(entry_id, bytes) else entry_id diff --git a/python/samples/getting_started/azure_functions/04_single_agent_orchestration_chaining/function_app.py b/python/samples/getting_started/azure_functions/04_single_agent_orchestration_chaining/function_app.py index 52b3612cda..33ccc5319f 100644 --- a/python/samples/getting_started/azure_functions/04_single_agent_orchestration_chaining/function_app.py +++ b/python/samples/getting_started/azure_functions/04_single_agent_orchestration_chaining/function_app.py @@ -1,3 +1,5 @@ +# Copyright (c) Microsoft. All rights reserved. + """Chain two runs of a single agent inside a Durable Functions orchestration. Components used in this sample: diff --git a/python/samples/getting_started/azure_functions/05_multi_agent_orchestration_concurrency/function_app.py b/python/samples/getting_started/azure_functions/05_multi_agent_orchestration_concurrency/function_app.py index f1772280f8..aad945288c 100644 --- a/python/samples/getting_started/azure_functions/05_multi_agent_orchestration_concurrency/function_app.py +++ b/python/samples/getting_started/azure_functions/05_multi_agent_orchestration_concurrency/function_app.py @@ -1,3 +1,5 @@ +# Copyright (c) Microsoft. All rights reserved. + """Fan out concurrent runs across two agents inside a Durable Functions orchestration. Components used in this sample: diff --git a/python/samples/getting_started/azure_functions/06_multi_agent_orchestration_conditionals/function_app.py b/python/samples/getting_started/azure_functions/06_multi_agent_orchestration_conditionals/function_app.py index 1165f0cc8e..54728332f0 100644 --- a/python/samples/getting_started/azure_functions/06_multi_agent_orchestration_conditionals/function_app.py +++ b/python/samples/getting_started/azure_functions/06_multi_agent_orchestration_conditionals/function_app.py @@ -1,3 +1,5 @@ +# Copyright (c) Microsoft. All rights reserved. + """Route email requests through conditional orchestration with two agents. Components used in this sample: diff --git a/python/samples/getting_started/azure_functions/07_single_agent_orchestration_hitl/function_app.py b/python/samples/getting_started/azure_functions/07_single_agent_orchestration_hitl/function_app.py index 6ed85081bc..931092c6cc 100644 --- a/python/samples/getting_started/azure_functions/07_single_agent_orchestration_hitl/function_app.py +++ b/python/samples/getting_started/azure_functions/07_single_agent_orchestration_hitl/function_app.py @@ -1,3 +1,5 @@ +# Copyright (c) Microsoft. All rights reserved. + """Iterate on generated content with a human-in-the-loop Durable orchestration. Components used in this sample: diff --git a/python/samples/getting_started/azure_functions/08_mcp_server/function_app.py b/python/samples/getting_started/azure_functions/08_mcp_server/function_app.py index c9243147f9..2d67ddec81 100644 --- a/python/samples/getting_started/azure_functions/08_mcp_server/function_app.py +++ b/python/samples/getting_started/azure_functions/08_mcp_server/function_app.py @@ -1,3 +1,5 @@ +# Copyright (c) Microsoft. All rights reserved. + """ Example showing how to configure AI agents with different trigger configurations. diff --git a/python/samples/getting_started/chat_client/azure_responses_client.py b/python/samples/getting_started/chat_client/azure_responses_client.py index a0c3fa69df..e2b9796826 100644 --- a/python/samples/getting_started/chat_client/azure_responses_client.py +++ b/python/samples/getting_started/chat_client/azure_responses_client.py @@ -4,10 +4,10 @@ from random import randint from typing import Annotated -from agent_framework import ChatResponse, tool +from agent_framework import tool from agent_framework.azure import AzureOpenAIResponsesClient from azure.identity import AzureCliCredential -from pydantic import BaseModel, Field +from pydantic import BaseModel """ Azure Responses Client Direct Usage Example @@ -20,42 +20,75 @@ # NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/getting_started/tools/function_tool_with_approval.py and samples/getting_started/tools/function_tool_with_approval_and_threads.py. @tool(approval_mode="never_require") def get_weather( - location: Annotated[str, Field(description="The location to get the weather for.")], + location: Annotated[str, "The location to get the weather for."], ) -> str: """Get the weather for a given location.""" conditions = ["sunny", "cloudy", "rainy", "stormy"] return f"The weather in {location} is {conditions[randint(0, 3)]} with a high of {randint(10, 30)}°C." -class OutputStruct(BaseModel): +@tool(approval_mode="never_require") +def get_time(): + """Get the current time.""" + from datetime import datetime + + now = datetime.now() + return f"The current date time is {now.strftime('%Y-%m-%d - %H:%M:%S')}." + + +class WeatherDetail(BaseModel): """Structured output for weather information.""" location: str weather: str +class Weather(BaseModel): + """Container for multiple outputs.""" + + date_time: str + weather_details: list[WeatherDetail] + + async def main() -> None: # For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred # authentication option. - client = AzureOpenAIResponsesClient(credential=AzureCliCredential()) + client = AzureOpenAIResponsesClient(credential=AzureCliCredential(), api_version="preview") message = "What's the weather in Amsterdam and in Paris?" stream = True print(f"User: {message}") + response = client.get_response( + message, + options={"response_format": Weather, "tools": [get_weather, get_time]}, + stream=stream, + ) if stream: - response = await ChatResponse.from_chat_response_generator( - client.get_response(message, tools=get_weather, options={"response_format": OutputStruct}, stream=True), - output_format_type=OutputStruct, - ) - if result := response.try_parse_value(OutputStruct): - print(f"Assistant: {result}") - else: - print(f"Assistant: {response.text}") + response = await response.get_final_response() + else: + response = await response + if result := response.value: + print(f"Assistant: {result.model_dump_json(indent=2)}") else: - response = await client.get_response(message, tools=get_weather, options={"response_format": OutputStruct}) - if result := response.try_parse_value(OutputStruct): - print(f"Assistant: {result}") - else: - print(f"Assistant: {response.text}") + print(f"Assistant: {response.text}") + + +# Expected output (time will be different): +""" +User: What's the weather in Amsterdam and in Paris? +Assistant: { + "date_time": "2026-02-06 - 13:30:40", + "weather_details": [ + { + "location": "Amsterdam", + "weather": "The weather in Amsterdam is cloudy with a high of 21°C." + }, + { + "location": "Paris", + "weather": "The weather in Paris is sunny with a high of 27°C." + } + ] +} +""" if __name__ == "__main__": diff --git a/python/samples/getting_started/chat_client/custom_chat_client.py b/python/samples/getting_started/chat_client/custom_chat_client.py index b55b7a38d6..af56e5456f 100644 --- a/python/samples/getting_started/chat_client/custom_chat_client.py +++ b/python/samples/getting_started/chat_client/custom_chat_client.py @@ -4,13 +4,12 @@ import random import sys from collections.abc import AsyncIterable, Awaitable, Mapping, Sequence -from typing import Any, ClassVar, Generic, TypedDict +from typing import Any, ClassVar, Generic from agent_framework import ( BaseChatClient, ChatMessage, ChatMiddlewareLayer, - ChatOptions, ChatResponse, ChatResponseUpdate, Content, @@ -22,9 +21,9 @@ from agent_framework.observability import ChatTelemetryLayer if sys.version_info >= (3, 13): - from typing import TypeVar + pass else: - from typing_extensions import TypeVar + pass if sys.version_info >= (3, 12): from typing import override # type: ignore # pragma: no cover else: @@ -38,13 +37,6 @@ middleware, telemetry, and function invocation layers explicitly. """ -TOptions_co = TypeVar( - "TOptions_co", - bound=TypedDict, # type: ignore[valid-type] - default="ChatOptions", - covariant=True, -) - class EchoingChatClient(BaseChatClient[TOptions_co], Generic[TOptions_co]): """A custom chat client that echoes messages back with modifications. diff --git a/python/samples/getting_started/chat_client/openai_responses_client.py b/python/samples/getting_started/chat_client/openai_responses_client.py index a84066ea87..ed58c0be29 100644 --- a/python/samples/getting_started/chat_client/openai_responses_client.py +++ b/python/samples/getting_started/chat_client/openai_responses_client.py @@ -32,14 +32,14 @@ async def main() -> None: message = "What's the weather in Amsterdam and in Paris?" stream = True print(f"User: {message}") + print("Assistant: ", end="") + response = client.get_response(message, stream=stream, options={"tools": get_weather}) if stream: - print("Assistant: ", end="") - response = client.get_response(message, stream=True, tools=get_weather) # TODO: review names of the methods, could be related to things like HTTP clients? - response.with_update_hook(lambda chunk: print(chunk.text, end="")) + response.with_transform_hook(lambda chunk: print(chunk.text, end="")) await response.get_final_response() else: - response = await client.get_response(message, tools=get_weather) + response = await response print(f"Assistant: {response}") diff --git a/python/samples/getting_started/context_providers/aggregate_context_provider.py b/python/samples/getting_started/context_providers/aggregate_context_provider.py index 1b682fadcb..4d44c0766c 100644 --- a/python/samples/getting_started/context_providers/aggregate_context_provider.py +++ b/python/samples/getting_started/context_providers/aggregate_context_provider.py @@ -211,7 +211,7 @@ async def invoked( msgs = [request_messages] if isinstance(request_messages, ChatMessage) else list(request_messages) for msg in msgs: - content = msg.content if hasattr(msg, "content") else "" + content = msg.text if hasattr(msg, "text") else "" # Very simple extraction - in production, use LLM-based extraction if isinstance(content, str) and "prefer" in content.lower() and ":" in content: parts = content.split(":") diff --git a/python/samples/getting_started/context_providers/redis/azure_redis_conversation.py b/python/samples/getting_started/context_providers/redis/azure_redis_conversation.py index e0305d4e8e..5c300abcbf 100644 --- a/python/samples/getting_started/context_providers/redis/azure_redis_conversation.py +++ b/python/samples/getting_started/context_providers/redis/azure_redis_conversation.py @@ -63,15 +63,16 @@ async def main() -> None: thread_id = "azure_test_thread" # Factory for creating Azure Redis chat message store - chat_message_store_factory = lambda: RedisChatMessageStore( - credential_provider=credential_provider, - host=redis_host, - port=10000, - ssl=True, - thread_id=thread_id, - key_prefix="chat_messages", - max_messages=100, - ) + def chat_message_store_factory(): + return RedisChatMessageStore( + credential_provider=credential_provider, + host=redis_host, + port=10000, + ssl=True, + thread_id=thread_id, + key_prefix="chat_messages", + max_messages=100, + ) # Create chat client client = OpenAIChatClient() diff --git a/python/samples/getting_started/context_providers/redis/redis_conversation.py b/python/samples/getting_started/context_providers/redis/redis_conversation.py index d4b2e527d4..f202a0cd2c 100644 --- a/python/samples/getting_started/context_providers/redis/redis_conversation.py +++ b/python/samples/getting_started/context_providers/redis/redis_conversation.py @@ -52,12 +52,14 @@ async def main() -> None: vector_distance_metric="cosine", thread_id=thread_id, ) - chat_message_store_factory = lambda: RedisChatMessageStore( - redis_url="redis://localhost:6379", - thread_id=thread_id, - key_prefix="chat_messages", - max_messages=100, - ) + + def chat_message_store_factory(): + return RedisChatMessageStore( + redis_url="redis://localhost:6379", + thread_id=thread_id, + key_prefix="chat_messages", + max_messages=100, + ) # Create chat client for the agent client = OpenAIChatClient(model_id=os.getenv("OPENAI_CHAT_MODEL_ID"), api_key=os.getenv("OPENAI_API_KEY")) diff --git a/python/samples/getting_started/devui/weather_agent_azure/agent.py b/python/samples/getting_started/devui/weather_agent_azure/agent.py index b4dd667bed..d3872e2141 100644 --- a/python/samples/getting_started/devui/weather_agent_azure/agent.py +++ b/python/samples/getting_started/devui/weather_agent_azure/agent.py @@ -14,8 +14,8 @@ ChatResponseUpdate, Content, FunctionInvocationContext, - Role, - TextContent, + MiddlewareTermination, + ResponseStream, chat_middleware, function_middleware, tool, @@ -44,7 +44,7 @@ async def security_filter_middleware( # Check only the last message (most recent user input) last_message = context.messages[-1] if context.messages else None - if last_message and last_message.role == Role.USER and last_message.text: + if last_message and last_message.role == "user" and last_message.text: message_lower = last_message.text.lower() for term in blocked_terms: if term in message_lower: @@ -56,26 +56,25 @@ async def security_filter_middleware( if context.stream: # Streaming mode: return async generator - async def blocked_stream() -> AsyncIterable[ChatResponseUpdate]: + async def blocked_stream(msg: str = error_message) -> AsyncIterable[ChatResponseUpdate]: yield ChatResponseUpdate( - contents=[Content.from_text(text=error_message)], - role=Role.ASSISTANT, + contents=[Content.from_text(text=msg)], + role="assistant", ) - context.result = blocked_stream() + context.result = ResponseStream(blocked_stream(), finalizer=ChatResponse.from_updates) else: # Non-streaming mode: return complete response context.result = ChatResponse( messages=[ ChatMessage( - role=Role.ASSISTANT, + role="assistant", text=error_message, ) ] ) - context.terminate = True - return + raise MiddlewareTermination await next(context) @@ -93,8 +92,7 @@ async def atlantis_location_filter_middleware( "Blocked! Hold up right there!! Tell the user that " "'Atlantis is a special place, we must never ask about the weather there!!'" ) - context.terminate = True - return + raise MiddlewareTermination await next(context) diff --git a/python/samples/getting_started/durabletask/01_single_agent/client.py b/python/samples/getting_started/durabletask/01_single_agent/client.py index 71d897e1f2..d88c9e857f 100644 --- a/python/samples/getting_started/durabletask/01_single_agent/client.py +++ b/python/samples/getting_started/durabletask/01_single_agent/client.py @@ -1,11 +1,13 @@ +# Copyright (c) Microsoft. All rights reserved. + """Client application for interacting with a Durable Task hosted agent. This client connects to the Durable Task Scheduler and sends requests to registered agents, demonstrating how to interact with agents from external processes. -Prerequisites: +Prerequisites: - The worker must be running with the agent registered -- Set AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_CHAT_DEPLOYMENT_NAME +- Set AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_CHAT_DEPLOYMENT_NAME (plus AZURE_OPENAI_API_KEY or Azure CLI authentication) - Durable Task Scheduler must be running """ @@ -29,12 +31,12 @@ def get_client( log_handler: logging.Handler | None = None ) -> DurableAIAgentClient: """Create a configured DurableAIAgentClient. - + Args: taskhub: Task hub name (defaults to TASKHUB env var or "default") endpoint: Scheduler endpoint (defaults to ENDPOINT env var or "http://localhost:8080") log_handler: Optional logging handler for client logging - + Returns: Configured DurableAIAgentClient instance """ @@ -59,7 +61,7 @@ def get_client( def run_client(agent_client: DurableAIAgentClient) -> None: """Run client interactions with the Joker agent. - + Args: agent_client: The DurableAIAgentClient instance """ diff --git a/python/samples/getting_started/durabletask/01_single_agent/sample.py b/python/samples/getting_started/durabletask/01_single_agent/sample.py index 323549d8bf..22d22927fd 100644 --- a/python/samples/getting_started/durabletask/01_single_agent/sample.py +++ b/python/samples/getting_started/durabletask/01_single_agent/sample.py @@ -1,11 +1,13 @@ +# Copyright (c) Microsoft. All rights reserved. + """Single Agent Sample - Durable Task Integration (Combined Worker + Client) This sample demonstrates running both the worker and client in a single process. The worker is started first to register the agent, then client operations are performed against the running worker. -Prerequisites: -- Set AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_CHAT_DEPLOYMENT_NAME +Prerequisites: +- Set AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_CHAT_DEPLOYMENT_NAME (plus AZURE_OPENAI_API_KEY or Azure CLI authentication) - Durable Task Scheduler must be running (e.g., using Docker) diff --git a/python/samples/getting_started/durabletask/01_single_agent/worker.py b/python/samples/getting_started/durabletask/01_single_agent/worker.py index d2212c9ddb..8afbbb3a44 100644 --- a/python/samples/getting_started/durabletask/01_single_agent/worker.py +++ b/python/samples/getting_started/durabletask/01_single_agent/worker.py @@ -1,3 +1,5 @@ +# Copyright (c) Microsoft. All rights reserved. + """Worker process for hosting a single Azure OpenAI-powered agent using Durable Task. This worker registers agents as durable entities and continuously listens for requests. diff --git a/python/samples/getting_started/durabletask/02_multi_agent/client.py b/python/samples/getting_started/durabletask/02_multi_agent/client.py index b3d8434062..4586186408 100644 --- a/python/samples/getting_started/durabletask/02_multi_agent/client.py +++ b/python/samples/getting_started/durabletask/02_multi_agent/client.py @@ -1,12 +1,14 @@ +# Copyright (c) Microsoft. All rights reserved. + """Client application for interacting with multiple hosted agents. This client connects to the Durable Task Scheduler and interacts with two different agents (WeatherAgent and MathAgent), demonstrating how to work with multiple agents each with their own specialized capabilities and tools. -Prerequisites: +Prerequisites: - The worker must be running with both agents registered -- Set AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_CHAT_DEPLOYMENT_NAME +- Set AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_CHAT_DEPLOYMENT_NAME (plus AZURE_OPENAI_API_KEY or Azure CLI authentication) - Durable Task Scheduler must be running """ @@ -30,12 +32,12 @@ def get_client( log_handler: logging.Handler | None = None ) -> DurableAIAgentClient: """Create a configured DurableAIAgentClient. - + Args: taskhub: Task hub name (defaults to TASKHUB env var or "default") endpoint: Scheduler endpoint (defaults to ENDPOINT env var or "http://localhost:8080") log_handler: Optional logging handler for client logging - + Returns: Configured DurableAIAgentClient instance """ @@ -60,7 +62,7 @@ def get_client( def run_client(agent_client: DurableAIAgentClient) -> None: """Run client interactions with both WeatherAgent and MathAgent. - + Args: agent_client: The DurableAIAgentClient instance """ diff --git a/python/samples/getting_started/durabletask/02_multi_agent/sample.py b/python/samples/getting_started/durabletask/02_multi_agent/sample.py index 17475ca06a..6357c145a2 100644 --- a/python/samples/getting_started/durabletask/02_multi_agent/sample.py +++ b/python/samples/getting_started/durabletask/02_multi_agent/sample.py @@ -1,11 +1,13 @@ +# Copyright (c) Microsoft. All rights reserved. + """Multi-Agent Sample - Durable Task Integration (Combined Worker + Client) This sample demonstrates running both the worker and client in a single process for multiple agents with different tools. The worker registers two agents (WeatherAgent and MathAgent), each with their own specialized capabilities. -Prerequisites: -- Set AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_CHAT_DEPLOYMENT_NAME +Prerequisites: +- Set AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_CHAT_DEPLOYMENT_NAME (plus AZURE_OPENAI_API_KEY or Azure CLI authentication) - Durable Task Scheduler must be running (e.g., using Docker) diff --git a/python/samples/getting_started/durabletask/02_multi_agent/worker.py b/python/samples/getting_started/durabletask/02_multi_agent/worker.py index 7ea7ad840d..88d9c2949d 100644 --- a/python/samples/getting_started/durabletask/02_multi_agent/worker.py +++ b/python/samples/getting_started/durabletask/02_multi_agent/worker.py @@ -1,3 +1,5 @@ +# Copyright (c) Microsoft. All rights reserved. + """Worker process for hosting multiple agents with different tools using Durable Task. This worker registers two agents - a weather assistant and a math assistant - each diff --git a/python/samples/getting_started/durabletask/03_single_agent_streaming/client.py b/python/samples/getting_started/durabletask/03_single_agent_streaming/client.py index c017829dfb..c65b27b2a9 100644 --- a/python/samples/getting_started/durabletask/03_single_agent_streaming/client.py +++ b/python/samples/getting_started/durabletask/03_single_agent_streaming/client.py @@ -7,7 +7,7 @@ 2. Streaming the response from Redis in real-time 3. Handling reconnection and cursor-based resumption -Prerequisites: +Prerequisites: - The worker must be running with the TravelPlanner agent registered - Set AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_CHAT_DEPLOYMENT_NAME - Redis must be running @@ -59,12 +59,12 @@ def get_client( log_handler: logging.Handler | None = None ) -> DurableAIAgentClient: """Create a configured DurableAIAgentClient. - + Args: taskhub: Task hub name (defaults to TASKHUB env var or "default") endpoint: Scheduler endpoint (defaults to ENDPOINT env var or "http://localhost:8080") log_handler: Optional log handler for client logging - + Returns: Configured DurableAIAgentClient instance """ @@ -89,7 +89,7 @@ def get_client( async def stream_from_redis(thread_id: str, cursor: str | None = None) -> None: """Stream agent responses from Redis. - + Args: thread_id: The conversation/thread ID to stream from cursor: Optional cursor to resume from. If None, starts from beginning. @@ -132,7 +132,7 @@ async def stream_from_redis(thread_id: str, cursor: str | None = None) -> None: def run_client(agent_client: DurableAIAgentClient) -> None: """Run client interactions with the TravelPlanner agent. - + Args: agent_client: The DurableAIAgentClient instance """ diff --git a/python/samples/getting_started/durabletask/03_single_agent_streaming/sample.py b/python/samples/getting_started/durabletask/03_single_agent_streaming/sample.py index e6d77c6785..800f3597c5 100644 --- a/python/samples/getting_started/durabletask/03_single_agent_streaming/sample.py +++ b/python/samples/getting_started/durabletask/03_single_agent_streaming/sample.py @@ -8,8 +8,8 @@ The worker is started first to register the TravelPlanner agent with Redis streaming callback, then client operations are performed against the running worker. -Prerequisites: -- Set AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_CHAT_DEPLOYMENT_NAME +Prerequisites: +- Set AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_CHAT_DEPLOYMENT_NAME (plus AZURE_OPENAI_API_KEY or Azure CLI authentication) - Durable Task Scheduler must be running (e.g., using Docker) - Redis must be running (e.g., docker run -d --name redis -p 6379:6379 redis:latest) diff --git a/python/samples/getting_started/durabletask/03_single_agent_streaming/worker.py b/python/samples/getting_started/durabletask/03_single_agent_streaming/worker.py index 318b222e54..c2eb2e973b 100644 --- a/python/samples/getting_started/durabletask/03_single_agent_streaming/worker.py +++ b/python/samples/getting_started/durabletask/03_single_agent_streaming/worker.py @@ -5,8 +5,8 @@ This worker registers the TravelPlanner agent with the Durable Task Scheduler and uses RedisStreamCallback to persist streaming responses to Redis for reliable delivery. -Prerequisites: -- Set AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_CHAT_DEPLOYMENT_NAME +Prerequisites: +- Set AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_CHAT_DEPLOYMENT_NAME (plus AZURE_OPENAI_API_KEY or Azure CLI authentication) - Start a Durable Task Scheduler (e.g., using Docker) - Start Redis (e.g., docker run -d --name redis -p 6379:6379 redis:latest) @@ -145,7 +145,7 @@ async def on_agent_response(self, response: object, context: AgentCallbackContex def create_travel_agent() -> "ChatAgent": """Create the TravelPlanner agent using Azure OpenAI. - + Returns: ChatAgent: The configured TravelPlanner agent with travel planning tools. """ @@ -174,12 +174,12 @@ def get_worker( log_handler: logging.Handler | None = None ) -> DurableTaskSchedulerWorker: """Create a configured DurableTaskSchedulerWorker. - + Args: taskhub: Task hub name (defaults to TASKHUB env var or "default") endpoint: Scheduler endpoint (defaults to ENDPOINT env var or "http://localhost:8080") log_handler: Optional log handler for worker logging - + Returns: Configured DurableTaskSchedulerWorker instance """ @@ -202,10 +202,10 @@ def get_worker( def setup_worker(worker: DurableTaskSchedulerWorker) -> DurableAIAgentWorker: """Set up the worker with the TravelPlanner agent and Redis streaming callback. - + Args: worker: The DurableTaskSchedulerWorker instance - + Returns: DurableAIAgentWorker with agent and callback registered """ diff --git a/python/samples/getting_started/durabletask/04_single_agent_orchestration_chaining/client.py b/python/samples/getting_started/durabletask/04_single_agent_orchestration_chaining/client.py index d9eb12c369..b438cd0da3 100644 --- a/python/samples/getting_started/durabletask/04_single_agent_orchestration_chaining/client.py +++ b/python/samples/getting_started/durabletask/04_single_agent_orchestration_chaining/client.py @@ -1,12 +1,14 @@ +# Copyright (c) Microsoft. All rights reserved. + """Client application for starting a single agent chaining orchestration. This client connects to the Durable Task Scheduler and starts an orchestration that runs a writer agent twice sequentially on the same thread, demonstrating how conversation context is maintained across multiple agent invocations. -Prerequisites: +Prerequisites: - The worker must be running with the writer agent and orchestration registered -- Set AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_CHAT_DEPLOYMENT_NAME +- Set AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_CHAT_DEPLOYMENT_NAME (plus AZURE_OPENAI_API_KEY or Azure CLI authentication) - Durable Task Scheduler must be running """ @@ -30,12 +32,12 @@ def get_client( log_handler: logging.Handler | None = None ) -> DurableTaskSchedulerClient: """Create a configured DurableTaskSchedulerClient. - + Args: taskhub: Task hub name (defaults to TASKHUB env var or "default") endpoint: Scheduler endpoint (defaults to ENDPOINT env var or "http://localhost:8080") log_handler: Optional logging handler for client logging - + Returns: Configured DurableTaskSchedulerClient instance """ @@ -58,7 +60,7 @@ def get_client( def run_client(client: DurableTaskSchedulerClient) -> None: """Run client to start and monitor the orchestration. - + Args: client: The DurableTaskSchedulerClient instance """ diff --git a/python/samples/getting_started/durabletask/04_single_agent_orchestration_chaining/sample.py b/python/samples/getting_started/durabletask/04_single_agent_orchestration_chaining/sample.py index d09421c6b4..44b20c2265 100644 --- a/python/samples/getting_started/durabletask/04_single_agent_orchestration_chaining/sample.py +++ b/python/samples/getting_started/durabletask/04_single_agent_orchestration_chaining/sample.py @@ -1,3 +1,5 @@ +# Copyright (c) Microsoft. All rights reserved. + """Single Agent Orchestration Chaining Sample - Durable Task Integration This sample demonstrates chaining two invocations of the same agent inside a Durable Task @@ -10,8 +12,8 @@ - DurableTaskSchedulerClient and orchestration for sequential agent invocations - Thread management to maintain conversation context across invocations -Prerequisites: -- Set AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_CHAT_DEPLOYMENT_NAME +Prerequisites: +- Set AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_CHAT_DEPLOYMENT_NAME (plus AZURE_OPENAI_API_KEY or Azure CLI authentication) - Durable Task Scheduler must be running (e.g., using Docker emulator) diff --git a/python/samples/getting_started/durabletask/04_single_agent_orchestration_chaining/worker.py b/python/samples/getting_started/durabletask/04_single_agent_orchestration_chaining/worker.py index 18c2fed8e3..f10a35b61b 100644 --- a/python/samples/getting_started/durabletask/04_single_agent_orchestration_chaining/worker.py +++ b/python/samples/getting_started/durabletask/04_single_agent_orchestration_chaining/worker.py @@ -1,11 +1,13 @@ +# Copyright (c) Microsoft. All rights reserved. + """Worker process for hosting a single agent with chaining orchestration using Durable Task. This worker registers a writer agent and an orchestration function that demonstrates chaining behavior by running the agent twice sequentially on the same thread, preserving conversation context between invocations. -Prerequisites: -- Set AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_CHAT_DEPLOYMENT_NAME +Prerequisites: +- Set AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_CHAT_DEPLOYMENT_NAME (plus AZURE_OPENAI_API_KEY or Azure CLI authentication) - Start a Durable Task Scheduler (e.g., using Docker) """ @@ -31,10 +33,10 @@ def create_writer_agent() -> "ChatAgent": """Create the Writer agent using Azure OpenAI. - + This agent refines short pieces of text, enhancing initial sentences and polishing improved versions further. - + Returns: ChatAgent: The configured Writer agent """ @@ -51,7 +53,7 @@ def create_writer_agent() -> "ChatAgent": def get_orchestration(): """Get the orchestration function for this sample. - + Returns: The orchestration function to register with the worker """ @@ -62,18 +64,18 @@ def single_agent_chaining_orchestration( context: OrchestrationContext, _: str ) -> Generator[Task[AgentResponse], AgentResponse, str]: """Orchestration that runs the writer agent twice on the same thread. - + This demonstrates chaining behavior where the output of the first agent run becomes part of the input for the second run, all while maintaining the conversation context through a shared thread. - + Args: context: The orchestration context _: Input parameter (unused) - + Yields: Task[AgentRunResponse]: Tasks that resolve to AgentRunResponse - + Returns: str: The final refined text from the second agent run """ @@ -123,12 +125,12 @@ def get_worker( log_handler: logging.Handler | None = None ) -> DurableTaskSchedulerWorker: """Create a configured DurableTaskSchedulerWorker. - + Args: taskhub: Task hub name (defaults to TASKHUB env var or "default") endpoint: Scheduler endpoint (defaults to ENDPOINT env var or "http://localhost:8080") log_handler: Optional logging handler for worker logging - + Returns: Configured DurableTaskSchedulerWorker instance """ @@ -151,10 +153,10 @@ def get_worker( def setup_worker(worker: DurableTaskSchedulerWorker) -> DurableAIAgentWorker: """Set up the worker with agents and orchestrations registered. - + Args: worker: The DurableTaskSchedulerWorker instance - + Returns: DurableAIAgentWorker with agents and orchestrations registered """ diff --git a/python/samples/getting_started/durabletask/05_multi_agent_orchestration_concurrency/client.py b/python/samples/getting_started/durabletask/05_multi_agent_orchestration_concurrency/client.py index f3d2ee8819..20f252fe21 100644 --- a/python/samples/getting_started/durabletask/05_multi_agent_orchestration_concurrency/client.py +++ b/python/samples/getting_started/durabletask/05_multi_agent_orchestration_concurrency/client.py @@ -1,12 +1,14 @@ +# Copyright (c) Microsoft. All rights reserved. + """Client application for starting a multi-agent concurrent orchestration. This client connects to the Durable Task Scheduler and starts an orchestration that runs two agents (physicist and chemist) concurrently, then retrieves and displays the aggregated results. -Prerequisites: +Prerequisites: - The worker must be running with both agents and orchestration registered -- Set AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_CHAT_DEPLOYMENT_NAME +- Set AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_CHAT_DEPLOYMENT_NAME (plus AZURE_OPENAI_API_KEY or Azure CLI authentication) - Durable Task Scheduler must be running """ @@ -30,12 +32,12 @@ def get_client( log_handler: logging.Handler | None = None ) -> DurableTaskSchedulerClient: """Create a configured DurableTaskSchedulerClient. - + Args: taskhub: Task hub name (defaults to TASKHUB env var or "default") endpoint: Scheduler endpoint (defaults to ENDPOINT env var or "http://localhost:8080") log_handler: Optional logging handler for client logging - + Returns: Configured DurableTaskSchedulerClient instance """ @@ -58,7 +60,7 @@ def get_client( def run_client(client: DurableTaskSchedulerClient, prompt: str = "What is temperature?") -> None: """Run client to start and monitor the orchestration. - + Args: client: The DurableTaskSchedulerClient instance prompt: The prompt to send to both agents diff --git a/python/samples/getting_started/durabletask/05_multi_agent_orchestration_concurrency/sample.py b/python/samples/getting_started/durabletask/05_multi_agent_orchestration_concurrency/sample.py index 02ee48c52f..808a45e6ea 100644 --- a/python/samples/getting_started/durabletask/05_multi_agent_orchestration_concurrency/sample.py +++ b/python/samples/getting_started/durabletask/05_multi_agent_orchestration_concurrency/sample.py @@ -1,3 +1,5 @@ +# Copyright (c) Microsoft. All rights reserved. + """Multi-Agent Orchestration Sample - Durable Task Integration (Combined Worker + Client) This sample demonstrates running both the worker and client in a single process for @@ -7,8 +9,8 @@ The orchestration uses OrchestrationAgentExecutor to execute agents concurrently and aggregate their responses. -Prerequisites: -- Set AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_CHAT_DEPLOYMENT_NAME +Prerequisites: +- Set AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_CHAT_DEPLOYMENT_NAME (plus AZURE_OPENAI_API_KEY or Azure CLI authentication) - Durable Task Scheduler must be running (e.g., using Docker) diff --git a/python/samples/getting_started/durabletask/05_multi_agent_orchestration_concurrency/worker.py b/python/samples/getting_started/durabletask/05_multi_agent_orchestration_concurrency/worker.py index bae292af0a..8f045805f0 100644 --- a/python/samples/getting_started/durabletask/05_multi_agent_orchestration_concurrency/worker.py +++ b/python/samples/getting_started/durabletask/05_multi_agent_orchestration_concurrency/worker.py @@ -1,11 +1,13 @@ +# Copyright (c) Microsoft. All rights reserved. + """Worker process for hosting multiple agents with orchestration using Durable Task. This worker registers two domain-specific agents (physicist and chemist) and an orchestration -function that runs them concurrently. The orchestration uses OrchestrationAgentExecutor +function that runs them concurrently. The orchestration uses OrchestrationAgentExecutor to execute agents in parallel and aggregate their responses. -Prerequisites: -- Set AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_CHAT_DEPLOYMENT_NAME +Prerequisites: +- Set AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_CHAT_DEPLOYMENT_NAME (plus AZURE_OPENAI_API_KEY or Azure CLI authentication) - Start a Durable Task Scheduler (e.g., using Docker) """ @@ -33,7 +35,7 @@ def create_physicist_agent() -> "ChatAgent": """Create the Physicist agent using Azure OpenAI. - + Returns: ChatAgent: The configured Physicist agent """ @@ -45,7 +47,7 @@ def create_physicist_agent() -> "ChatAgent": def create_chemist_agent() -> "ChatAgent": """Create the Chemist agent using Azure OpenAI. - + Returns: ChatAgent: The configured Chemist agent """ @@ -57,14 +59,14 @@ def create_chemist_agent() -> "ChatAgent": def multi_agent_concurrent_orchestration(context: OrchestrationContext, prompt: str) -> Generator[Task[Any], Any, dict[str, str]]: """Orchestration that runs both agents in parallel and aggregates results. - + Uses DurableAIAgentOrchestrationContext to wrap the orchestration context and access agents via the OrchestrationAgentExecutor. - + Args: context: The orchestration context prompt: The prompt to send to both agents - + Returns: dict: Dictionary with 'physicist' and 'chemist' response texts """ @@ -115,12 +117,12 @@ def get_worker( log_handler: logging.Handler | None = None ) -> DurableTaskSchedulerWorker: """Create a configured DurableTaskSchedulerWorker. - + Args: taskhub: Task hub name (defaults to TASKHUB env var or "default") endpoint: Scheduler endpoint (defaults to ENDPOINT env var or "http://localhost:8080") log_handler: Optional logging handler for worker logging - + Returns: Configured DurableTaskSchedulerWorker instance """ @@ -143,10 +145,10 @@ def get_worker( def setup_worker(worker: DurableTaskSchedulerWorker) -> DurableAIAgentWorker: """Set up the worker with agents and orchestrations registered. - + Args: worker: The DurableTaskSchedulerWorker instance - + Returns: DurableAIAgentWorker with agents and orchestrations registered """ diff --git a/python/samples/getting_started/durabletask/06_multi_agent_orchestration_conditionals/client.py b/python/samples/getting_started/durabletask/06_multi_agent_orchestration_conditionals/client.py index a0f7f6072c..5253568a53 100644 --- a/python/samples/getting_started/durabletask/06_multi_agent_orchestration_conditionals/client.py +++ b/python/samples/getting_started/durabletask/06_multi_agent_orchestration_conditionals/client.py @@ -1,11 +1,13 @@ +# Copyright (c) Microsoft. All rights reserved. + """Client application for starting a spam detection orchestration. This client connects to the Durable Task Scheduler and starts an orchestration that uses conditional logic to either handle spam emails or draft professional responses. -Prerequisites: +Prerequisites: - The worker must be running with both agents, orchestration, and activities registered -- Set AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_CHAT_DEPLOYMENT_NAME +- Set AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_CHAT_DEPLOYMENT_NAME (plus AZURE_OPENAI_API_KEY or Azure CLI authentication) - Durable Task Scheduler must be running """ @@ -28,12 +30,12 @@ def get_client( log_handler: logging.Handler | None = None ) -> DurableTaskSchedulerClient: """Create a configured DurableTaskSchedulerClient. - + Args: taskhub: Task hub name (defaults to TASKHUB env var or "default") endpoint: Scheduler endpoint (defaults to ENDPOINT env var or "http://localhost:8080") log_handler: Optional logging handler for client logging - + Returns: Configured DurableTaskSchedulerClient instance """ @@ -60,7 +62,7 @@ def run_client( email_content: str = "Hello! I wanted to reach out about our upcoming project meeting." ) -> None: """Run client to start and monitor the spam detection orchestration. - + Args: client: The DurableTaskSchedulerClient instance email_id: The email ID diff --git a/python/samples/getting_started/durabletask/06_multi_agent_orchestration_conditionals/sample.py b/python/samples/getting_started/durabletask/06_multi_agent_orchestration_conditionals/sample.py index 479158dea7..e098ba1be8 100644 --- a/python/samples/getting_started/durabletask/06_multi_agent_orchestration_conditionals/sample.py +++ b/python/samples/getting_started/durabletask/06_multi_agent_orchestration_conditionals/sample.py @@ -1,3 +1,5 @@ +# Copyright (c) Microsoft. All rights reserved. + """Multi-Agent Orchestration with Conditionals Sample - Durable Task Integration This sample demonstrates conditional orchestration logic with two agents: @@ -7,8 +9,8 @@ The orchestration branches based on spam detection results, calling different activity functions to handle spam or send legitimate email responses. -Prerequisites: -- Set AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_CHAT_DEPLOYMENT_NAME +Prerequisites: +- Set AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_CHAT_DEPLOYMENT_NAME (plus AZURE_OPENAI_API_KEY or Azure CLI authentication) - Durable Task Scheduler must be running (e.g., using Docker) diff --git a/python/samples/getting_started/durabletask/06_multi_agent_orchestration_conditionals/worker.py b/python/samples/getting_started/durabletask/06_multi_agent_orchestration_conditionals/worker.py index 5bea536867..92b689d5cf 100644 --- a/python/samples/getting_started/durabletask/06_multi_agent_orchestration_conditionals/worker.py +++ b/python/samples/getting_started/durabletask/06_multi_agent_orchestration_conditionals/worker.py @@ -1,3 +1,5 @@ +# Copyright (c) Microsoft. All rights reserved. + """Worker process for hosting spam detection and email assistant agents with conditional orchestration. This worker registers two domain-specific agents (spam detector and email assistant) and an @@ -51,7 +53,7 @@ class EmailPayload(BaseModel): def create_spam_agent() -> "ChatAgent": """Create the Spam Detection agent using Azure OpenAI. - + Returns: ChatAgent: The configured Spam Detection agent """ @@ -63,7 +65,7 @@ def create_spam_agent() -> "ChatAgent": def create_email_agent() -> "ChatAgent": """Create the Email Assistant agent using Azure OpenAI. - + Returns: ChatAgent: The configured Email Assistant agent """ @@ -75,11 +77,11 @@ def create_email_agent() -> "ChatAgent": def handle_spam_email(context: ActivityContext, reason: str) -> str: """Activity function to handle spam emails. - + Args: context: The activity context reason: The reason why the email was marked as spam - + Returns: str: Confirmation message """ @@ -89,11 +91,11 @@ def handle_spam_email(context: ActivityContext, reason: str) -> str: def send_email(context: ActivityContext, message: str) -> str: """Activity function to send emails. - + Args: context: The activity context message: The email message to send - + Returns: str: Confirmation message """ @@ -103,17 +105,17 @@ def send_email(context: ActivityContext, message: str) -> str: def spam_detection_orchestration(context: OrchestrationContext, payload_raw: Any) -> Generator[Task[Any], Any, str]: """Orchestration that detects spam and conditionally drafts email responses. - + This orchestration: 1. Validates the input payload 2. Runs the spam detection agent 3. If spam: calls handle_spam_email activity 4. If legitimate: runs email assistant agent and calls send_email activity - + Args: context: The orchestration context payload_raw: The input payload dictionary - + Returns: str: Result message from activity functions """ @@ -198,12 +200,12 @@ def get_worker( log_handler: logging.Handler | None = None ) -> DurableTaskSchedulerWorker: """Create a configured DurableTaskSchedulerWorker. - + Args: taskhub: Task hub name (defaults to TASKHUB env var or "default") endpoint: Scheduler endpoint (defaults to ENDPOINT env var or "http://localhost:8080") log_handler: Optional logging handler for worker logging - + Returns: Configured DurableTaskSchedulerWorker instance """ @@ -226,10 +228,10 @@ def get_worker( def setup_worker(worker: DurableTaskSchedulerWorker) -> DurableAIAgentWorker: """Set up the worker with agents, orchestrations, and activities registered. - + Args: worker: The DurableTaskSchedulerWorker instance - + Returns: DurableAIAgentWorker with agents, orchestrations, and activities registered """ diff --git a/python/samples/getting_started/durabletask/07_single_agent_orchestration_hitl/client.py b/python/samples/getting_started/durabletask/07_single_agent_orchestration_hitl/client.py index 8b7a24853d..7808a8a03f 100644 --- a/python/samples/getting_started/durabletask/07_single_agent_orchestration_hitl/client.py +++ b/python/samples/getting_started/durabletask/07_single_agent_orchestration_hitl/client.py @@ -1,11 +1,13 @@ +# Copyright (c) Microsoft. All rights reserved. + """Client application for starting a human-in-the-loop content generation orchestration. This client connects to the Durable Task Scheduler and demonstrates the HITL pattern by starting an orchestration, sending approval/rejection events, and monitoring progress. -Prerequisites: +Prerequisites: - The worker must be running with the agent, orchestration, and activities registered -- Set AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_CHAT_DEPLOYMENT_NAME +- Set AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_CHAT_DEPLOYMENT_NAME (plus AZURE_OPENAI_API_KEY or Azure CLI authentication) - Durable Task Scheduler must be running """ @@ -34,12 +36,12 @@ def get_client( log_handler: logging.Handler | None = None ) -> DurableTaskSchedulerClient: """Create a configured DurableTaskSchedulerClient. - + Args: taskhub: Task hub name (defaults to TASKHUB env var or "default") endpoint: Scheduler endpoint (defaults to ENDPOINT env var or "http://localhost:8080") log_handler: Optional logging handler for client logging - + Returns: Configured DurableTaskSchedulerClient instance """ @@ -64,7 +66,7 @@ def _log_completion_result( metadata: OrchestrationState | None, ) -> None: """Log the orchestration completion result. - + Args: metadata: The orchestration metadata """ @@ -94,7 +96,7 @@ def _wait_and_log_completion( timeout: int = 60 ) -> None: """Wait for orchestration completion and log the result. - + Args: client: The DurableTaskSchedulerClient instance instance_id: The orchestration instance ID @@ -116,7 +118,7 @@ def send_approval( feedback: str = "" ) -> None: """Send approval or rejection event to the orchestration. - + Args: client: The DurableTaskSchedulerClient instance instance_id: The orchestration instance ID @@ -148,14 +150,14 @@ def wait_for_notification( timeout_seconds: int = 10 ) -> bool: """Wait for the orchestration to reach a notification point. - + Polls the orchestration status until it appears to be waiting for approval. - + Args: client: The DurableTaskSchedulerClient instance instance_id: The orchestration instance ID timeout_seconds: Maximum time to wait - + Returns: True if notification detected, False if timeout """ @@ -202,7 +204,7 @@ def wait_for_notification( def run_interactive_client(client: DurableTaskSchedulerClient) -> None: """Run an interactive client that prompts for user input and handles approval workflow. - + Args: client: The DurableTaskSchedulerClient instance """ diff --git a/python/samples/getting_started/durabletask/07_single_agent_orchestration_hitl/sample.py b/python/samples/getting_started/durabletask/07_single_agent_orchestration_hitl/sample.py index 7843621db0..e9b9b43044 100644 --- a/python/samples/getting_started/durabletask/07_single_agent_orchestration_hitl/sample.py +++ b/python/samples/getting_started/durabletask/07_single_agent_orchestration_hitl/sample.py @@ -1,3 +1,5 @@ +# Copyright (c) Microsoft. All rights reserved. + """Human-in-the-Loop Orchestration Sample - Durable Task Integration This sample demonstrates the HITL pattern with a WriterAgent that generates content @@ -7,8 +9,8 @@ - Iterative refinement based on feedback - Activity functions for notifications and publishing -Prerequisites: -- Set AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_CHAT_DEPLOYMENT_NAME +Prerequisites: +- Set AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_CHAT_DEPLOYMENT_NAME (plus AZURE_OPENAI_API_KEY or Azure CLI authentication) - Durable Task Scheduler must be running (e.g., using Docker) diff --git a/python/samples/getting_started/durabletask/07_single_agent_orchestration_hitl/worker.py b/python/samples/getting_started/durabletask/07_single_agent_orchestration_hitl/worker.py index 77aef7fa22..db9a47002f 100644 --- a/python/samples/getting_started/durabletask/07_single_agent_orchestration_hitl/worker.py +++ b/python/samples/getting_started/durabletask/07_single_agent_orchestration_hitl/worker.py @@ -1,11 +1,13 @@ +# Copyright (c) Microsoft. All rights reserved. + """Worker process for hosting a writer agent with human-in-the-loop orchestration. This worker registers a WriterAgent and an orchestration function that implements a human-in-the-loop review workflow. The orchestration pauses for external events (human approval/rejection) with timeout handling, and iterates based on feedback. -Prerequisites: -- Set AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_CHAT_DEPLOYMENT_NAME +Prerequisites: +- Set AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_CHAT_DEPLOYMENT_NAME (plus AZURE_OPENAI_API_KEY or Azure CLI authentication) - Start a Durable Task Scheduler (e.g., using Docker) """ @@ -54,7 +56,7 @@ class HumanApproval(BaseModel): def create_writer_agent() -> "ChatAgent": """Create the Writer agent using Azure OpenAI. - + Returns: ChatAgent: The configured Writer agent """ @@ -73,7 +75,7 @@ def create_writer_agent() -> "ChatAgent": def notify_user_for_approval(context: ActivityContext, content: dict[str, str]) -> str: """Activity function to notify user for approval. - + Args: context: The activity context content: The generated content dictionary @@ -88,7 +90,7 @@ def notify_user_for_approval(context: ActivityContext, content: dict[str, str]) def publish_content(context: ActivityContext, content: dict[str, str]) -> str: """Activity function to publish approved content. - + Args: context: The activity context content: The generated content dictionary @@ -105,7 +107,7 @@ def content_generation_hitl_orchestration( payload_raw: Any ) -> Generator[Task[Any], Any, dict[str, str]]: """Human-in-the-loop orchestration for content generation with approval workflow. - + This orchestration: 1. Generates initial content using WriterAgent 2. Loops up to max_review_attempts times: @@ -115,14 +117,14 @@ def content_generation_hitl_orchestration( d. If rejected: incorporates feedback and regenerates e. If timeout: raises TimeoutError 3. Raises RuntimeError if max attempts exhausted - + Args: context: The orchestration context payload_raw: The input payload - + Returns: dict: Result with published content - + Raises: ValueError: If input is invalid or agent returns no content TimeoutError: If human approval times out @@ -285,12 +287,12 @@ def get_worker( log_handler: logging.Handler | None = None ) -> DurableTaskSchedulerWorker: """Create a configured DurableTaskSchedulerWorker. - + Args: taskhub: Task hub name (defaults to TASKHUB env var or "default") endpoint: Scheduler endpoint (defaults to ENDPOINT env var or "http://localhost:8080") log_handler: Optional logging handler for worker logging - + Returns: Configured DurableTaskSchedulerWorker instance """ @@ -313,10 +315,10 @@ def get_worker( def setup_worker(worker: DurableTaskSchedulerWorker) -> DurableAIAgentWorker: """Set up the worker with agents, orchestrations, and activities registered. - + Args: worker: The DurableTaskSchedulerWorker instance - + Returns: DurableAIAgentWorker with agents, orchestrations, and activities registered """ diff --git a/python/samples/getting_started/evaluation/self_reflection/self_reflection.py b/python/samples/getting_started/evaluation/self_reflection/self_reflection.py index bc079fbfcb..274fa901f3 100644 --- a/python/samples/getting_started/evaluation/self_reflection/self_reflection.py +++ b/python/samples/getting_started/evaluation/self_reflection/self_reflection.py @@ -26,7 +26,7 @@ Reflexion: language agents with verbal reinforcement learning. Noah Shinn, Federico Cassano, Ashwin Gopinath, Karthik Narasimhan, and Shunyu Yao. 2023. In Proceedings of the 37th International Conference on Neural Information Processing Systems (NIPS '23). Curran Associates Inc., Red Hook, NY, USA, Article 377, 8634–8652. -https://arxiv.org/abs/2303.11366 +https://arxiv.org/abs/2303.11366 This module implements a self-reflection loop for LLM responses using groundedness evaluation. It loads prompts from a JSONL file, runs them through an LLM with self-reflection, @@ -123,8 +123,7 @@ def run_eval( print(f"Eval run failed. Run ID: {run.id}, Status: {run.status}, Error: {getattr(run, 'error', 'Unknown error')}") continue if run.status == "completed": - output_items = list(client.evals.runs.output_items.list(run_id=run.id, eval_id=eval_object.id)) - return output_items + return list(client.evals.runs.output_items.list(run_id=run.id, eval_id=eval_object.id)) time.sleep(5) print("Eval result retrieval timeout.") @@ -142,14 +141,14 @@ async def execute_query_with_self_reflection( ) -> dict[str, Any]: """ Execute a query with self-reflection loop. - + Args: agent: ChatAgent instance to use for generating responses full_user_query: Complete prompt including system prompt, user request, and context context: Context document for groundedness evaluation evaluator: Groundedness evaluator function max_self_reflections: Maximum number of self-reflection iterations - + Returns: Dictionary containing: - best_response: The best response achieved diff --git a/python/samples/getting_started/middleware/chat_middleware.py b/python/samples/getting_started/middleware/chat_middleware.py index e7e807f27e..21ae052bdb 100644 --- a/python/samples/getting_started/middleware/chat_middleware.py +++ b/python/samples/getting_started/middleware/chat_middleware.py @@ -10,6 +10,7 @@ ChatMessage, ChatMiddleware, ChatResponse, + MiddlewareTermination, chat_middleware, tool, ) @@ -127,8 +128,7 @@ async def security_and_override_middleware( ) # Set terminate flag to stop execution - context.terminate = True - return + raise MiddlewareTermination # Continue to next middleware or AI execution await next(context) diff --git a/python/samples/getting_started/middleware/middleware_termination.py b/python/samples/getting_started/middleware/middleware_termination.py index 69fa5766d9..05fad65cf4 100644 --- a/python/samples/getting_started/middleware/middleware_termination.py +++ b/python/samples/getting_started/middleware/middleware_termination.py @@ -10,6 +10,7 @@ AgentMiddleware, AgentResponse, ChatMessage, + MiddlewareTermination, tool, ) from agent_framework.azure import AzureAIAgentClient @@ -72,8 +73,7 @@ async def process( ) # Set terminate flag to prevent further processing - context.terminate = True - break + raise MiddlewareTermination await next(context) @@ -98,7 +98,7 @@ async def process( f"[PostTerminationMiddleware] Maximum responses ({self.max_responses}) reached. " "Terminating further processing." ) - context.terminate = True + raise MiddlewareTermination # Allow the agent to process normally await next(context) diff --git a/python/samples/getting_started/middleware/override_result_with_middleware.py b/python/samples/getting_started/middleware/override_result_with_middleware.py index 8aef8f8e3b..6f27c6a7da 100644 --- a/python/samples/getting_started/middleware/override_result_with_middleware.py +++ b/python/samples/getting_started/middleware/override_result_with_middleware.py @@ -15,7 +15,6 @@ ChatResponse, ChatResponseUpdate, ResponseStream, - Role, tool, ) from agent_framework.openai import OpenAIResponsesClient @@ -76,12 +75,12 @@ def _update_hook(update: ChatResponseUpdate) -> ChatResponseUpdate: index["value"] += 1 return update - context.result.with_update_hook(_update_hook) + context.result.with_transform_hook(_update_hook) else: # For non-streaming: just replace with a new message - current_text = context.result.text or "" + current_text = context.result.text or "" # type: ignore custom_message = f"Weather Advisory: [0] {''.join(chunks)} Original message was: {current_text}" - context.result = ChatResponse(messages=[ChatMessage(role=Role.ASSISTANT, text=custom_message)]) + context.result = ChatResponse(messages=[ChatMessage(role="assistant", text=custom_message)]) async def validate_weather_middleware(context: ChatContext, next: Callable[[ChatContext], Awaitable[None]]) -> None: @@ -96,12 +95,12 @@ async def validate_weather_middleware(context: ChatContext, next: Callable[[Chat if context.stream and isinstance(context.result, ResponseStream): def _append_validation_note(response: ChatResponse) -> ChatResponse: - response.messages.append(ChatMessage(role=Role.ASSISTANT, text=validation_note)) + response.messages.append(ChatMessage(role="assistant", text=validation_note)) return response - context.result.with_finalizer(_append_validation_note) + context.result.with_result_hook(_append_validation_note) elif isinstance(context.result, ChatResponse): - context.result.messages.append(ChatMessage(role=Role.ASSISTANT, text=validation_note)) + context.result.messages.append(ChatMessage(role="assistant", text=validation_note)) async def agent_cleanup_middleware(context: AgentContext, next: Callable[[AgentContext], Awaitable[None]]) -> None: @@ -154,7 +153,7 @@ def _sanitize(response: AgentResponse) -> AgentResponse: if not found_validation: raise RuntimeError("Expected validation note not found in agent response.") - cleaned_messages.append(ChatMessage(role=Role.ASSISTANT, text=" Agent: OK")) + cleaned_messages.append(ChatMessage(role="assistant", text=" Agent: OK")) response.messages = cleaned_messages return response @@ -172,8 +171,8 @@ def _clean_update(update: AgentResponseUpdate) -> AgentResponseUpdate: content.text = text return update - context.result.with_update_hook(_clean_update) - context.result.with_finalizer(_sanitize) + context.result.with_transform_hook(_clean_update) + context.result.with_result_hook(_sanitize) elif isinstance(context.result, AgentResponse): context.result = _sanitize(context.result) @@ -192,24 +191,25 @@ async def main() -> None: tools=get_weather, middleware=[agent_cleanup_middleware], ) - # Non-streaming example - print("\n--- Non-streaming Example ---") - query = "What's the weather like in Seattle?" - print(f"User: {query}") - result = await agent.run(query) - print(f"Agent: {result}") - # Streaming example print("\n--- Streaming Example ---") query = "What's the weather like in Portland?" print(f"User: {query}") print("Agent: ", end="", flush=True) response = agent.run(query, stream=True) - async for chunk in response: - if chunk.text: - print(chunk.text, end="", flush=True) - print("\n") - print(f"Final Result: {(await response.get_final_response()).text}") + # add the hooks to print what you want to see + response.with_transform_hook(lambda chunk: print(chunk.text, end="", flush=True)).with_result_hook( + lambda final: print(f"\nFinal streamed response: {final.text}", flush=True) + ) + # consume the stream to trigger the hooks + await response.get_final_response() + + # Non-streaming example + print("\n--- Non-streaming Example ---") + query = "What's the weather like in Seattle?" + print(f"User: {query}") + result = await agent.run(query) + print(f"Agent: {result}") if __name__ == "__main__": diff --git a/python/samples/getting_started/observability/workflow_observability.py b/python/samples/getting_started/observability/workflow_observability.py index 1726117178..1a45069c59 100644 --- a/python/samples/getting_started/observability/workflow_observability.py +++ b/python/samples/getting_started/observability/workflow_observability.py @@ -6,7 +6,6 @@ Executor, WorkflowBuilder, WorkflowContext, - handler, ) from agent_framework.observability import configure_otel_providers, get_tracer diff --git a/python/samples/getting_started/orchestrations/concurrent_participant_factory.py b/python/samples/getting_started/orchestrations/concurrent_participant_factory.py index 8d1da7f0fd..acb824e1ef 100644 --- a/python/samples/getting_started/orchestrations/concurrent_participant_factory.py +++ b/python/samples/getting_started/orchestrations/concurrent_participant_factory.py @@ -1,7 +1,7 @@ # Copyright (c) Microsoft. All rights reserved. import asyncio -from typing import Any, Never +from typing import Any from agent_framework import ( ChatAgent, @@ -14,6 +14,7 @@ from agent_framework.azure import AzureOpenAIChatClient from agent_framework.orchestrations import ConcurrentBuilder from azure.identity import AzureCliCredential +from typing_extensions import Never """ Sample: Concurrent Orchestration with participant factories and Custom Aggregator diff --git a/python/samples/getting_started/orchestrations/handoff_participant_factory.py b/python/samples/getting_started/orchestrations/handoff_participant_factory.py index 7abb7b59c6..2465609071 100644 --- a/python/samples/getting_started/orchestrations/handoff_participant_factory.py +++ b/python/samples/getting_started/orchestrations/handoff_participant_factory.py @@ -150,11 +150,10 @@ def _handle_events(events: list[WorkflowEvent]) -> list[WorkflowEvent[HandoffAge speaker = message.author_name or message.role print(f"- {speaker}: {message.text or [content.type for content in message.contents]}") print("===================================") - elif event.type == "request_info": + elif event.type == "request_info" and isinstance(event.data, HandoffAgentUserRequest): # Request info event: Workflow is requesting user input - if isinstance(event.data, HandoffAgentUserRequest): - _print_handoff_agent_user_request(event.data.agent_response) - requests.append(cast(WorkflowEvent[HandoffAgentUserRequest], event)) + _print_handoff_agent_user_request(event.data.agent_response) + requests.append(cast(WorkflowEvent[HandoffAgentUserRequest], event)) return requests diff --git a/python/samples/getting_started/tools/function_invocation_configuration.py b/python/samples/getting_started/tools/function_invocation_configuration.py index a73c683cf9..b6cb27a7bc 100644 --- a/python/samples/getting_started/tools/function_invocation_configuration.py +++ b/python/samples/getting_started/tools/function_invocation_configuration.py @@ -25,10 +25,9 @@ def add( async def main(): client = OpenAIResponsesClient() - if client.function_invocation_configuration is not None: - client.function_invocation_configuration.include_detailed_errors = True - client.function_invocation_configuration.max_iterations = 40 - print(f"Function invocation configured as: \n{client.function_invocation_configuration.to_json(indent=2)}") + client.function_invocation_configuration["include_detailed_errors"] = True + client.function_invocation_configuration["max_iterations"] = 40 + print(f"Function invocation configured as: \n{client.function_invocation_configuration}") agent = client.as_agent(name="ToolAgent", instructions="Use the provided tools.", tools=add) diff --git a/python/samples/getting_started/tools/function_tool_declaration_only.py b/python/samples/getting_started/tools/function_tool_declaration_only.py index c82f04a371..f081e0823e 100644 --- a/python/samples/getting_started/tools/function_tool_declaration_only.py +++ b/python/samples/getting_started/tools/function_tool_declaration_only.py @@ -1,5 +1,7 @@ # Copyright (c) Microsoft. All rights reserved. +import asyncio + from agent_framework import FunctionTool from agent_framework.openai import OpenAIResponsesClient @@ -70,6 +72,5 @@ async def main(): if __name__ == "__main__": - import asyncio asyncio.run(main()) diff --git a/python/samples/getting_started/tools/function_tool_recover_from_failures.py b/python/samples/getting_started/tools/function_tool_recover_from_failures.py index 1637e6ab38..8c38a81e77 100644 --- a/python/samples/getting_started/tools/function_tool_recover_from_failures.py +++ b/python/samples/getting_started/tools/function_tool_recover_from_failures.py @@ -3,7 +3,7 @@ import asyncio from typing import Annotated -from agent_framework import FunctionCallContent, FunctionResultContent, tool +from agent_framework import tool from agent_framework.openai import OpenAIResponsesClient """ @@ -21,7 +21,6 @@ def greet(name: Annotated[str, "Name to greet"]) -> str: return f"Hello, {name}!" -@tool(approval_mode="never_require") # we trick the AI into calling this function with 0 as denominator to trigger the exception @tool(approval_mode="never_require") def safe_divide( @@ -62,11 +61,11 @@ async def main(): if msg.text: print(f"{idx + 1} {msg.author_name or msg.role}: {msg.text} ") for content in msg.contents: - if isinstance(content, FunctionCallContent): + if content.type == "function_call": print( f"{idx + 1} {msg.author_name}: calling function: {content.name} with arguments: {content.arguments}" ) - if isinstance(content, FunctionResultContent): + if content.type == "function_result": print(f"{idx + 1} {msg.role}: {content.result if content.result else content.exception}") diff --git a/python/samples/getting_started/tools/function_tool_with_max_exceptions.py b/python/samples/getting_started/tools/function_tool_with_max_exceptions.py index 7b83ead248..7e60487704 100644 --- a/python/samples/getting_started/tools/function_tool_with_max_exceptions.py +++ b/python/samples/getting_started/tools/function_tool_with_max_exceptions.py @@ -3,7 +3,7 @@ import asyncio from typing import Annotated -from agent_framework import FunctionCallContent, FunctionResultContent, tool +from agent_framework import tool from agent_framework.openai import OpenAIResponsesClient """ @@ -55,11 +55,11 @@ async def main(): if msg.text: print(f"{idx + 1} {msg.author_name or msg.role}: {msg.text} ") for content in msg.contents: - if isinstance(content, FunctionCallContent): + if content.type == "function_call": print( f"{idx + 1} {msg.author_name}: calling function: {content.name} with arguments: {content.arguments}" ) - if isinstance(content, FunctionResultContent): + if content.type == "function_result": print(f"{idx + 1} {msg.role}: {content.result if content.result else content.exception}") diff --git a/python/samples/getting_started/tools/function_tool_with_max_invocations.py b/python/samples/getting_started/tools/function_tool_with_max_invocations.py index 0b13d1cb3b..be9d37d807 100644 --- a/python/samples/getting_started/tools/function_tool_with_max_invocations.py +++ b/python/samples/getting_started/tools/function_tool_with_max_invocations.py @@ -3,7 +3,7 @@ import asyncio from typing import Annotated -from agent_framework import FunctionCallContent, FunctionResultContent, tool +from agent_framework import tool from agent_framework.openai import OpenAIResponsesClient """ @@ -44,11 +44,11 @@ async def main(): if msg.text: print(f"{idx + 1} {msg.author_name or msg.role}: {msg.text} ") for content in msg.contents: - if isinstance(content, FunctionCallContent): + if content.type == "function_call": print( f"{idx + 1} {msg.author_name}: calling function: {content.name} with arguments: {content.arguments}" ) - if isinstance(content, FunctionResultContent): + if content.type == "function_result": print(f"{idx + 1} {msg.role}: {content.result if content.result else content.exception}") diff --git a/python/samples/getting_started/workflows/control-flow/sequential_streaming.py b/python/samples/getting_started/workflows/control-flow/sequential_streaming.py index 3be1a4ef8d..86ad69652a 100644 --- a/python/samples/getting_started/workflows/control-flow/sequential_streaming.py +++ b/python/samples/getting_started/workflows/control-flow/sequential_streaming.py @@ -14,7 +14,7 @@ Purpose: Show how to declare executors with the @executor decorator, connect them with WorkflowBuilder, pass intermediate values using ctx.send_message, and yield final output using ctx.yield_output(). -Demonstrate how streaming exposes executor_invoked events (type='executor_invoked') and +Demonstrate how streaming exposes executor_invoked events (type='executor_invoked') and executor_completed events (type='executor_completed') for observability. Prerequisites: diff --git a/python/samples/getting_started/workflows/human-in-the-loop/agents_with_HITL.py b/python/samples/getting_started/workflows/human-in-the-loop/agents_with_HITL.py index 16810b68a9..7923bced7a 100644 --- a/python/samples/getting_started/workflows/human-in-the-loop/agents_with_HITL.py +++ b/python/samples/getting_started/workflows/human-in-the-loop/agents_with_HITL.py @@ -28,7 +28,7 @@ writer_agent -> Coordinator -> writer_agent -> Coordinator -> final_editor_agent -> Coordinator -> output The writer agent drafts marketing copy. A custom executor emits a request_info event (type='request_info') so a -human can comment, then relays the human guidance back into the conversation before the final editor agent +human can comment, then relays the human guidance back into the conversation before the final editor agent produces the polished output. Demonstrates: diff --git a/python/samples/getting_started/workflows/tool-approval/group_chat_builder_tool_approval.py b/python/samples/getting_started/workflows/tool-approval/group_chat_builder_tool_approval.py index f00f79698a..ebabfc508f 100644 --- a/python/samples/getting_started/workflows/tool-approval/group_chat_builder_tool_approval.py +++ b/python/samples/getting_started/workflows/tool-approval/group_chat_builder_tool_approval.py @@ -43,7 +43,9 @@ # 1. Define tools for different agents -# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/getting_started/tools/function_tool_with_approval.py and samples/getting_started/tools/function_tool_with_approval_and_threads.py. +# NOTE: approval_mode="never_require" is for sample brevity. +# Use "always_require" in production; see samples/getting_started/tools/function_tool_with_approval.py +# and samples/getting_started/tools/function_tool_with_approval_and_threads.py. @tool(approval_mode="never_require") def run_tests(test_suite: Annotated[str, "Name of the test suite to run"]) -> str: """Run automated tests for the application.""" diff --git a/python/uv.lock b/python/uv.lock index 283dd5d191..4759a01f66 100644 --- a/python/uv.lock +++ b/python/uv.lock @@ -916,7 +916,7 @@ wheels = [ [[package]] name = "anthropic" -version = "0.77.1" +version = "0.78.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, @@ -928,9 +928,9 @@ dependencies = [ { name = "sniffio", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/88/61/50aef0587acd9dd8bf1b8b7fd7fbb25ba4c6ec5387a6ffc195a697951fcc/anthropic-0.77.1.tar.gz", hash = "sha256:a19d78ff6fff9e05d211e3a936051cd5b9462f0eac043d2d45b2372f455d11cd", size = 504691, upload-time = "2026-02-03T17:44:22.667Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ec/51/32849a48f9b1cfe80a508fd269b20bd8f0b1357c70ba092890fde5a6a10b/anthropic-0.78.0.tar.gz", hash = "sha256:55fd978ab9b049c61857463f4c4e9e092b24f892519c6d8078cee1713d8af06e", size = 509136, upload-time = "2026-02-05T17:52:04.986Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2b/54/e83babf9833547c5548b4e25230ef3d62492e45925b0d104a43e501918a0/anthropic-0.77.1-py3-none-any.whl", hash = "sha256:76fd6f2ab36033a5294d58182a5f712dab9573c3a54413a275ecdf29e727c1e0", size = 397856, upload-time = "2026-02-03T17:44:20.962Z" }, + { url = "https://files.pythonhosted.org/packages/3b/03/2f50931a942e5e13f80e24d83406714672c57964be593fc046d81369335b/anthropic-0.78.0-py3-none-any.whl", hash = "sha256:2a9887d2e99d1b0f9fe08857a1e9fe5d2d4030455dbf9ac65aab052e2efaeac4", size = 405485, upload-time = "2026-02-05T17:52:03.674Z" }, ] [[package]] @@ -1426,19 +1426,19 @@ wheels = [ [[package]] name = "claude-agent-sdk" -version = "0.1.29" +version = "0.1.31" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "mcp", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "typing-extensions", marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f2/a7/e1449285606b98119729249394ad0e93e75ea6d25fa3006d734b21f73044/claude_agent_sdk-0.1.29.tar.gz", hash = "sha256:ece32436a81fc015ca325d4121edeb5627ae9af15b5079f7b42d5eda9dcdb7a3", size = 59801, upload-time = "2026-02-04T00:53:54.099Z" } +sdist = { url = "https://files.pythonhosted.org/packages/6d/df/071dce5803c4db8cd53708bcda3b6022c1c4b68fc00e9007593309515286/claude_agent_sdk-0.1.31.tar.gz", hash = "sha256:b68c681083d7cc985dd3e48f73aabf459f056c1a7e1c5b9c47033c6af94da1a1", size = 61191, upload-time = "2026-02-06T02:01:51.043Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/41/98/8915e3bb6acccf2b62b101545b286f30fd63e5421e9a3483b88a0c88f49b/claude_agent_sdk-0.1.29-py3-none-macosx_11_0_arm64.whl", hash = "sha256:811de31c92bd90250ebbfd79758c538766c672abde244ae0f7dec2d02ed5a1f7", size = 54225884, upload-time = "2026-02-04T00:53:38.169Z" }, - { url = "https://files.pythonhosted.org/packages/91/a7/9a8801ae25e453877bc71b5dc4f4818171bc9c04319e0681d3950fbe0232/claude_agent_sdk-0.1.29-py3-none-manylinux_2_17_aarch64.whl", hash = "sha256:6279360d251ce8b8e9d922b03e3492c88736648e7f5e7c9f301fde0eef37928f", size = 68426447, upload-time = "2026-02-04T00:53:42.539Z" }, - { url = "https://files.pythonhosted.org/packages/87/9c/aab63fe82c7cba80ee5234b0a928a032340cdaba0e48d23544e592b6f9ca/claude_agent_sdk-0.1.29-py3-none-manylinux_2_17_x86_64.whl", hash = "sha256:4d1f01fe5f7252126f35808e2887a40125b784ac0dbf73b9509a4065a4766149", size = 70124488, upload-time = "2026-02-04T00:53:46.675Z" }, - { url = "https://files.pythonhosted.org/packages/63/30/135575231e53c10d4a99f1fa7b0b548f2ae89b907e41d0b2d158bde1896e/claude_agent_sdk-0.1.29-py3-none-win_amd64.whl", hash = "sha256:67fb58a72f0dd54d079c538078130cc8c888bc60652d3d396768ffaee6716467", size = 72305314, upload-time = "2026-02-04T00:53:51.045Z" }, + { url = "https://files.pythonhosted.org/packages/0c/7c/e249a3b4215e28a9722b3d9ab6057bceeeaa2b948530f022065ef2154555/claude_agent_sdk-0.1.31-py3-none-macosx_11_0_arm64.whl", hash = "sha256:801bacfe4192782a7cc7b61b0d23a57f061c069993dd3dfa8109aa2e7050a530", size = 54284257, upload-time = "2026-02-06T02:01:35.61Z" }, + { url = "https://files.pythonhosted.org/packages/d6/a8/1a8288736aeafcc48e3dcb3326ec7f487dbf89ebba77d526e9464786a299/claude_agent_sdk-0.1.31-py3-none-manylinux_2_17_aarch64.whl", hash = "sha256:0b608e0cbfcedcb827427e6d16a73fe573d58e7f93e15f95435066feacbe6511", size = 68462461, upload-time = "2026-02-06T02:01:40.074Z" }, + { url = "https://files.pythonhosted.org/packages/26/7a/7dcd0b77263ed55b17554fa3a67a6772b788e7048a524fd06c9baa970564/claude_agent_sdk-0.1.31-py3-none-manylinux_2_17_x86_64.whl", hash = "sha256:d0cb30e026a22246e84d9237d23bb4df20be5146913a04d2802ddd37d4f8b8c9", size = 70173234, upload-time = "2026-02-06T02:01:44.486Z" }, + { url = "https://files.pythonhosted.org/packages/37/a5/4a8de7a9738f454b54aa97557f0fba9c74b0901ea418597008c668243fea/claude_agent_sdk-0.1.31-py3-none-win_amd64.whl", hash = "sha256:8ceca675c2770ad739bd1208362059a830e91c74efcf128045b5a7af14d36f2b", size = 72366975, upload-time = "2026-02-06T02:01:48.647Z" }, ] [[package]] @@ -1458,7 +1458,7 @@ name = "clr-loader" version = "0.2.10" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "cffi", marker = "(python_full_version < '3.14' and sys_platform == 'darwin') or (python_full_version < '3.14' and sys_platform == 'linux') or (python_full_version < '3.14' and sys_platform == 'win32')" }, + { name = "cffi", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/18/24/c12faf3f61614b3131b5c98d3bf0d376b49c7feaa73edca559aeb2aee080/clr_loader-0.2.10.tar.gz", hash = "sha256:81f114afbc5005bafc5efe5af1341d400e22137e275b042a8979f3feb9fc9446", size = 83605, upload-time = "2026-01-03T23:13:06.984Z" } wheels = [ @@ -1921,7 +1921,7 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "asyncio", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "grpcio", version = "1.67.1", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.14' and sys_platform == 'darwin') or (python_full_version < '3.14' and sys_platform == 'linux') or (python_full_version < '3.14' and sys_platform == 'win32')" }, - { name = "grpcio", version = "1.76.0", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.14' and sys_platform == 'darwin') or (python_full_version >= '3.14' and sys_platform == 'linux') or (python_full_version >= '3.14' and sys_platform == 'win32')" }, + { name = "grpcio", version = "1.78.0", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.14' and sys_platform == 'darwin') or (python_full_version >= '3.14' and sys_platform == 'linux') or (python_full_version >= '3.14' and sys_platform == 'win32')" }, { name = "packaging", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "protobuf", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] @@ -1961,7 +1961,7 @@ name = "exceptiongroup" version = "1.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions", marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, + { name = "typing-extensions", marker = "(python_full_version < '3.13' and sys_platform == 'darwin') or (python_full_version < '3.13' and sys_platform == 'linux') or (python_full_version < '3.13' and sys_platform == 'win32')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } wheels = [ @@ -1979,17 +1979,18 @@ wheels = [ [[package]] name = "fastapi" -version = "0.128.1" +version = "0.128.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-doc", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "pydantic", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "starlette", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "typing-inspection", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f6/59/28bde150415783ff084334e3de106eb7461a57864cf69f343950ad5a5ddd/fastapi-0.128.1.tar.gz", hash = "sha256:ce5be4fa26d4ce6f54debcc873d1fb8e0e248f5c48d7502ba6c61457ab2dc766", size = 374260, upload-time = "2026-02-04T17:35:10.542Z" } +sdist = { url = "https://files.pythonhosted.org/packages/4f/6e/45fb5390d46d7918426ea1c1ec4b06c1d3fd70be4a47a690ccb4f1f9438a/fastapi-0.128.2.tar.gz", hash = "sha256:7db9eb891866ac3a08e03f844b99e343a2c1cc41247e68e006c90b38d2464ea1", size = 376129, upload-time = "2026-02-05T19:48:33.957Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1a/08/3953db1979ea131c68279b997c6465080118b407f0800445b843f8e164b3/fastapi-0.128.1-py3-none-any.whl", hash = "sha256:ee82146bbf91ea5bbf2bb8629e4c6e056c4fbd997ea6068501b11b15260b50fb", size = 103810, upload-time = "2026-02-04T17:35:08.02Z" }, + { url = "https://files.pythonhosted.org/packages/c1/f2/80df24108572630bb2adef3d97f1e774b18ec25bfbab5528f36cba6478c0/fastapi-0.128.2-py3-none-any.whl", hash = "sha256:55bfd9490ca0125707d80e785583c2dc57840bb66e3a0bbc087d20c364964dc0", size = 104032, upload-time = "2026-02-05T19:48:32.118Z" }, ] [[package]] @@ -2328,11 +2329,11 @@ wheels = [ [[package]] name = "fsspec" -version = "2026.1.0" +version = "2026.2.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d5/7d/5df2650c57d47c57232af5ef4b4fdbff182070421e405e0d62c6cdbfaa87/fsspec-2026.1.0.tar.gz", hash = "sha256:e987cb0496a0d81bba3a9d1cee62922fb395e7d4c3b575e57f547953334fe07b", size = 310496, upload-time = "2026-01-09T15:21:35.562Z" } +sdist = { url = "https://files.pythonhosted.org/packages/51/7c/f60c259dcbf4f0c47cc4ddb8f7720d2dcdc8888c8e5ad84c73ea4531cc5b/fsspec-2026.2.0.tar.gz", hash = "sha256:6544e34b16869f5aacd5b90bdf1a71acb37792ea3ddf6125ee69a22a53fb8bff", size = 313441, upload-time = "2026-02-05T21:50:53.743Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/01/c9/97cc5aae1648dcb851958a3ddf73ccd7dbe5650d95203ecb4d7720b4cdbf/fsspec-2026.1.0-py3-none-any.whl", hash = "sha256:cb76aa913c2285a3b49bdd5fc55b1d7c708d7208126b60f2eb8194fe1b4cbdcc", size = 201838, upload-time = "2026-01-09T15:21:34.041Z" }, + { url = "https://files.pythonhosted.org/packages/e6/ab/fb21f4c939bb440104cc2b396d3be1d9b7a9fd3c6c2a53d98c45b3d7c954/fsspec-2026.2.0-py3-none-any.whl", hash = "sha256:98de475b5cb3bd66bedd5c4679e87b4fdfe1a3bf4d707b151b3c07e58c9a2437", size = 202505, upload-time = "2026-02-05T21:50:51.819Z" }, ] [[package]] @@ -2350,16 +2351,16 @@ wheels = [ [[package]] name = "github-copilot-sdk" -version = "0.1.21" +version = "0.1.22" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pydantic", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "python-dateutil", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/95/d0/f1b55044e1a3e3f368c867cbf91e68e36282efa9f53eb03532cf761a84e8/github_copilot_sdk-0.1.21.tar.gz", hash = "sha256:1c8572d1155fcedb1c3c4f02b4d4fe0aec97ccba63ab0c1b87f8f871da4922ea", size = 96353, upload-time = "2026-02-03T23:15:26.627Z" } +sdist = { url = "https://files.pythonhosted.org/packages/cf/b7/ae720a503c9b329f8c95036a04fae8e023db8dcdce9d24382259865f0760/github_copilot_sdk-0.1.22.tar.gz", hash = "sha256:8ea4534f0c8ab0fa04e0fec4c3ebd42d737cf7772277e4f8eb58a9fadac6bdb5", size = 97324, upload-time = "2026-02-05T17:33:33.726Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ba/39/b8107ca00e42c44bd964e187aa81a60ae2e09fcbae9f255f7e50d7c0cead/github_copilot_sdk-0.1.21-py3-none-any.whl", hash = "sha256:c09d4004d14171474680c6d9279c0f10d6b4636c370f574828da6181aafb6b34", size = 43732, upload-time = "2026-02-03T23:15:25.377Z" }, + { url = "https://files.pythonhosted.org/packages/7e/2e/68aa28018778fa86a8392b37c6a883d7a9a24b715ba5baa470ce018f1542/github_copilot_sdk-0.1.22-py3-none-any.whl", hash = "sha256:f75d84dd2633138834330597400b28fefbf8bd75541f78083831f58c9bdde81a", size = 44149, upload-time = "2026-02-05T17:33:31.948Z" }, ] [[package]] @@ -2422,7 +2423,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/fe/65/5b235b40581ad75ab97dcd8b4218022ae8e3ab77c13c919f1a1dfe9171fd/greenlet-3.3.1-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:04bee4775f40ecefcdaa9d115ab44736cd4b9c5fba733575bfe9379419582e13", size = 273723, upload-time = "2026-01-23T15:30:37.521Z" }, { url = "https://files.pythonhosted.org/packages/ce/ad/eb4729b85cba2d29499e0a04ca6fbdd8f540afd7be142fd571eea43d712f/greenlet-3.3.1-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:50e1457f4fed12a50e427988a07f0f9df53cf0ee8da23fab16e6732c2ec909d4", size = 574874, upload-time = "2026-01-23T16:00:54.551Z" }, { url = "https://files.pythonhosted.org/packages/87/32/57cad7fe4c8b82fdaa098c89498ef85ad92dfbb09d5eb713adedfc2ae1f5/greenlet-3.3.1-cp310-cp310-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:070472cd156f0656f86f92e954591644e158fd65aa415ffbe2d44ca77656a8f5", size = 586309, upload-time = "2026-01-23T16:05:25.18Z" }, - { url = "https://files.pythonhosted.org/packages/66/66/f041005cb87055e62b0d68680e88ec1a57f4688523d5e2fb305841bc8307/greenlet-3.3.1-cp310-cp310-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:1108b61b06b5224656121c3c8ee8876161c491cbe74e5c519e0634c837cf93d5", size = 597461, upload-time = "2026-01-23T16:15:51.943Z" }, { url = "https://files.pythonhosted.org/packages/87/eb/8a1ec2da4d55824f160594a75a9d8354a5fe0a300fb1c48e7944265217e1/greenlet-3.3.1-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3a300354f27dd86bae5fbf7002e6dd2b3255cd372e9242c933faf5e859b703fe", size = 586985, upload-time = "2026-01-23T15:32:47.968Z" }, { url = "https://files.pythonhosted.org/packages/15/1c/0621dd4321dd8c351372ee8f9308136acb628600658a49be1b7504208738/greenlet-3.3.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e84b51cbebf9ae573b5fbd15df88887815e3253fc000a7d0ff95170e8f7e9729", size = 1547271, upload-time = "2026-01-23T16:04:18.977Z" }, { url = "https://files.pythonhosted.org/packages/9d/53/24047f8924c83bea7a59c8678d9571209c6bfe5f4c17c94a78c06024e9f2/greenlet-3.3.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e0093bd1a06d899892427217f0ff2a3c8f306182b8c754336d32e2d587c131b4", size = 1613427, upload-time = "2026-01-23T15:33:44.428Z" }, @@ -2430,7 +2430,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ec/e8/2e1462c8fdbe0f210feb5ac7ad2d9029af8be3bf45bd9fa39765f821642f/greenlet-3.3.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:5fd23b9bc6d37b563211c6abbb1b3cab27db385a4449af5c32e932f93017080c", size = 274974, upload-time = "2026-01-23T15:31:02.891Z" }, { url = "https://files.pythonhosted.org/packages/7e/a8/530a401419a6b302af59f67aaf0b9ba1015855ea7e56c036b5928793c5bd/greenlet-3.3.1-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:09f51496a0bfbaa9d74d36a52d2580d1ef5ed4fdfcff0a73730abfbbbe1403dd", size = 577175, upload-time = "2026-01-23T16:00:56.213Z" }, { url = "https://files.pythonhosted.org/packages/8e/89/7e812bb9c05e1aaef9b597ac1d0962b9021d2c6269354966451e885c4e6b/greenlet-3.3.1-cp311-cp311-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cb0feb07fe6e6a74615ee62a880007d976cf739b6669cce95daa7373d4fc69c5", size = 590401, upload-time = "2026-01-23T16:05:26.365Z" }, - { url = "https://files.pythonhosted.org/packages/70/ae/e2d5f0e59b94a2269b68a629173263fa40b63da32f5c231307c349315871/greenlet-3.3.1-cp311-cp311-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:67ea3fc73c8cd92f42467a72b75e8f05ed51a0e9b1d15398c913416f2dafd49f", size = 601161, upload-time = "2026-01-23T16:15:53.456Z" }, { url = "https://files.pythonhosted.org/packages/5c/ae/8d472e1f5ac5efe55c563f3eabb38c98a44b832602e12910750a7c025802/greenlet-3.3.1-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:39eda9ba259cc9801da05351eaa8576e9aa83eb9411e8f0c299e05d712a210f2", size = 590272, upload-time = "2026-01-23T15:32:49.411Z" }, { url = "https://files.pythonhosted.org/packages/a8/51/0fde34bebfcadc833550717eade64e35ec8738e6b097d5d248274a01258b/greenlet-3.3.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e2e7e882f83149f0a71ac822ebf156d902e7a5d22c9045e3e0d1daf59cee2cc9", size = 1550729, upload-time = "2026-01-23T16:04:20.867Z" }, { url = "https://files.pythonhosted.org/packages/16/c9/2fb47bee83b25b119d5a35d580807bb8b92480a54b68fef009a02945629f/greenlet-3.3.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:80aa4d79eb5564f2e0a6144fcc744b5a37c56c4a92d60920720e99210d88db0f", size = 1615552, upload-time = "2026-01-23T15:33:45.743Z" }, @@ -2439,7 +2438,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f9/c8/9d76a66421d1ae24340dfae7e79c313957f6e3195c144d2c73333b5bfe34/greenlet-3.3.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:7e806ca53acf6d15a888405880766ec84721aa4181261cd11a457dfe9a7a4975", size = 276443, upload-time = "2026-01-23T15:30:10.066Z" }, { url = "https://files.pythonhosted.org/packages/81/99/401ff34bb3c032d1f10477d199724f5e5f6fbfb59816ad1455c79c1eb8e7/greenlet-3.3.1-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d842c94b9155f1c9b3058036c24ffb8ff78b428414a19792b2380be9cecf4f36", size = 597359, upload-time = "2026-01-23T16:00:57.394Z" }, { url = "https://files.pythonhosted.org/packages/2b/bc/4dcc0871ed557792d304f50be0f7487a14e017952ec689effe2180a6ff35/greenlet-3.3.1-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:20fedaadd422fa02695f82093f9a98bad3dab5fcda793c658b945fcde2ab27ba", size = 607805, upload-time = "2026-01-23T16:05:28.068Z" }, - { url = "https://files.pythonhosted.org/packages/3b/cd/7a7ca57588dac3389e97f7c9521cb6641fd8b6602faf1eaa4188384757df/greenlet-3.3.1-cp312-cp312-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c620051669fd04ac6b60ebc70478210119c56e2d5d5df848baec4312e260e4ca", size = 622363, upload-time = "2026-01-23T16:15:54.754Z" }, { url = "https://files.pythonhosted.org/packages/cf/05/821587cf19e2ce1f2b24945d890b164401e5085f9d09cbd969b0c193cd20/greenlet-3.3.1-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:14194f5f4305800ff329cbf02c5fcc88f01886cadd29941b807668a45f0d2336", size = 609947, upload-time = "2026-01-23T15:32:51.004Z" }, { url = "https://files.pythonhosted.org/packages/a4/52/ee8c46ed9f8babaa93a19e577f26e3d28a519feac6350ed6f25f1afee7e9/greenlet-3.3.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7b2fe4150a0cf59f847a67db8c155ac36aed89080a6a639e9f16df5d6c6096f1", size = 1567487, upload-time = "2026-01-23T16:04:22.125Z" }, { url = "https://files.pythonhosted.org/packages/8f/7c/456a74f07029597626f3a6db71b273a3632aecb9afafeeca452cfa633197/greenlet-3.3.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:49f4ad195d45f4a66a0eb9c1ba4832bb380570d361912fa3554746830d332149", size = 1636087, upload-time = "2026-01-23T15:33:47.486Z" }, @@ -2448,7 +2446,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ec/ab/d26750f2b7242c2b90ea2ad71de70cfcd73a948a49513188a0fc0d6fc15a/greenlet-3.3.1-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:7ab327905cabb0622adca5971e488064e35115430cec2c35a50fd36e72a315b3", size = 275205, upload-time = "2026-01-23T15:30:24.556Z" }, { url = "https://files.pythonhosted.org/packages/10/d3/be7d19e8fad7c5a78eeefb2d896a08cd4643e1e90c605c4be3b46264998f/greenlet-3.3.1-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:65be2f026ca6a176f88fb935ee23c18333ccea97048076aef4db1ef5bc0713ac", size = 599284, upload-time = "2026-01-23T16:00:58.584Z" }, { url = "https://files.pythonhosted.org/packages/ae/21/fe703aaa056fdb0f17e5afd4b5c80195bbdab701208918938bd15b00d39b/greenlet-3.3.1-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7a3ae05b3d225b4155bda56b072ceb09d05e974bc74be6c3fc15463cf69f33fd", size = 610274, upload-time = "2026-01-23T16:05:29.312Z" }, - { url = "https://files.pythonhosted.org/packages/06/00/95df0b6a935103c0452dad2203f5be8377e551b8466a29650c4c5a5af6cc/greenlet-3.3.1-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:12184c61e5d64268a160226fb4818af4df02cfead8379d7f8b99a56c3a54ff3e", size = 624375, upload-time = "2026-01-23T16:15:55.915Z" }, { url = "https://files.pythonhosted.org/packages/cb/86/5c6ab23bb3c28c21ed6bebad006515cfe08b04613eb105ca0041fecca852/greenlet-3.3.1-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6423481193bbbe871313de5fd06a082f2649e7ce6e08015d2a76c1e9186ca5b3", size = 612904, upload-time = "2026-01-23T15:32:52.317Z" }, { url = "https://files.pythonhosted.org/packages/c2/f3/7949994264e22639e40718c2daf6f6df5169bf48fb038c008a489ec53a50/greenlet-3.3.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:33a956fe78bbbda82bfc95e128d61129b32d66bcf0a20a1f0c08aa4839ffa951", size = 1567316, upload-time = "2026-01-23T16:04:23.316Z" }, { url = "https://files.pythonhosted.org/packages/8d/6e/d73c94d13b6465e9f7cd6231c68abde838bb22408596c05d9059830b7872/greenlet-3.3.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4b065d3284be43728dd280f6f9a13990b56470b81be20375a207cdc814a983f2", size = 1636549, upload-time = "2026-01-23T15:33:48.643Z" }, @@ -2457,7 +2454,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ae/fb/011c7c717213182caf78084a9bea51c8590b0afda98001f69d9f853a495b/greenlet-3.3.1-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:bd59acd8529b372775cd0fcbc5f420ae20681c5b045ce25bd453ed8455ab99b5", size = 275737, upload-time = "2026-01-23T15:32:16.889Z" }, { url = "https://files.pythonhosted.org/packages/41/2e/a3a417d620363fdbb08a48b1dd582956a46a61bf8fd27ee8164f9dfe87c2/greenlet-3.3.1-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b31c05dd84ef6871dd47120386aed35323c944d86c3d91a17c4b8d23df62f15b", size = 646422, upload-time = "2026-01-23T16:01:00.354Z" }, { url = "https://files.pythonhosted.org/packages/b4/09/c6c4a0db47defafd2d6bab8ddfe47ad19963b4e30f5bed84d75328059f8c/greenlet-3.3.1-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:02925a0bfffc41e542c70aa14c7eda3593e4d7e274bfcccca1827e6c0875902e", size = 658219, upload-time = "2026-01-23T16:05:30.956Z" }, - { url = "https://files.pythonhosted.org/packages/e2/89/b95f2ddcc5f3c2bc09c8ee8d77be312df7f9e7175703ab780f2014a0e781/greenlet-3.3.1-cp314-cp314-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3e0f3878ca3a3ff63ab4ea478585942b53df66ddde327b59ecb191b19dbbd62d", size = 671455, upload-time = "2026-01-23T16:15:57.232Z" }, { url = "https://files.pythonhosted.org/packages/80/38/9d42d60dffb04b45f03dbab9430898352dba277758640751dc5cc316c521/greenlet-3.3.1-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:34a729e2e4e4ffe9ae2408d5ecaf12f944853f40ad724929b7585bca808a9d6f", size = 660237, upload-time = "2026-01-23T15:32:53.967Z" }, { url = "https://files.pythonhosted.org/packages/96/61/373c30b7197f9e756e4c81ae90a8d55dc3598c17673f91f4d31c3c689c3f/greenlet-3.3.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:aec9ab04e82918e623415947921dea15851b152b822661cce3f8e4393c3df683", size = 1615261, upload-time = "2026-01-23T16:04:25.066Z" }, { url = "https://files.pythonhosted.org/packages/fd/d3/ca534310343f5945316f9451e953dcd89b36fe7a19de652a1dc5a0eeef3f/greenlet-3.3.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:71c767cf281a80d02b6c1bdc41c9468e1f5a494fb11bc8688c360524e273d7b1", size = 1683719, upload-time = "2026-01-23T15:33:50.61Z" }, @@ -2466,7 +2462,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/28/24/cbbec49bacdcc9ec652a81d3efef7b59f326697e7edf6ed775a5e08e54c2/greenlet-3.3.1-cp314-cp314t-macosx_11_0_universal2.whl", hash = "sha256:3e63252943c921b90abb035ebe9de832c436401d9c45f262d80e2d06cc659242", size = 282706, upload-time = "2026-01-23T15:33:05.525Z" }, { url = "https://files.pythonhosted.org/packages/86/2e/4f2b9323c144c4fe8842a4e0d92121465485c3c2c5b9e9b30a52e80f523f/greenlet-3.3.1-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:76e39058e68eb125de10c92524573924e827927df5d3891fbc97bd55764a8774", size = 651209, upload-time = "2026-01-23T16:01:01.517Z" }, { url = "https://files.pythonhosted.org/packages/d9/87/50ca60e515f5bb55a2fbc5f0c9b5b156de7d2fc51a0a69abc9d23914a237/greenlet-3.3.1-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c9f9d5e7a9310b7a2f416dd13d2e3fd8b42d803968ea580b7c0f322ccb389b97", size = 654300, upload-time = "2026-01-23T16:05:32.199Z" }, - { url = "https://files.pythonhosted.org/packages/7c/25/c51a63f3f463171e09cb586eb64db0861eb06667ab01a7968371a24c4f3b/greenlet-3.3.1-cp314-cp314t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4b9721549a95db96689458a1e0ae32412ca18776ed004463df3a9299c1b257ab", size = 662574, upload-time = "2026-01-23T16:15:58.364Z" }, { url = "https://files.pythonhosted.org/packages/1d/94/74310866dfa2b73dd08659a3d18762f83985ad3281901ba0ee9a815194fb/greenlet-3.3.1-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:92497c78adf3ac703b57f1e3813c2d874f27f71a178f9ea5887855da413cd6d2", size = 653842, upload-time = "2026-01-23T15:32:55.671Z" }, { url = "https://files.pythonhosted.org/packages/97/43/8bf0ffa3d498eeee4c58c212a3905dd6146c01c8dc0b0a046481ca29b18c/greenlet-3.3.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:ed6b402bc74d6557a705e197d47f9063733091ed6357b3de33619d8a8d93ac53", size = 1614917, upload-time = "2026-01-23T16:04:26.276Z" }, { url = "https://files.pythonhosted.org/packages/89/90/a3be7a5f378fc6e84abe4dcfb2ba32b07786861172e502388b4c90000d1b/greenlet-3.3.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:59913f1e5ada20fde795ba906916aea25d442abcc0593fba7e26c92b7ad76249", size = 1676092, upload-time = "2026-01-23T15:33:52.176Z" }, @@ -2545,7 +2540,7 @@ wheels = [ [[package]] name = "grpcio" -version = "1.76.0" +version = "1.78.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.14' and sys_platform == 'darwin'", @@ -2555,58 +2550,58 @@ resolution-markers = [ dependencies = [ { name = "typing-extensions", marker = "(python_full_version >= '3.14' and sys_platform == 'darwin') or (python_full_version >= '3.14' and sys_platform == 'linux') or (python_full_version >= '3.14' and sys_platform == 'win32')" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b6/e0/318c1ce3ae5a17894d5791e87aea147587c9e702f24122cc7a5c8bbaeeb1/grpcio-1.76.0.tar.gz", hash = "sha256:7be78388d6da1a25c0d5ec506523db58b18be22d9c37d8d3a32c08be4987bd73", size = 12785182, upload-time = "2025-10-21T16:23:12.106Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/88/17/ff4795dc9a34b6aee6ec379f1b66438a3789cd1315aac0cbab60d92f74b3/grpcio-1.76.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:65a20de41e85648e00305c1bb09a3598f840422e522277641145a32d42dcefcc", size = 5840037, upload-time = "2025-10-21T16:20:25.069Z" }, - { url = "https://files.pythonhosted.org/packages/4e/ff/35f9b96e3fa2f12e1dcd58a4513a2e2294a001d64dec81677361b7040c9a/grpcio-1.76.0-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:40ad3afe81676fd9ec6d9d406eda00933f218038433980aa19d401490e46ecde", size = 11836482, upload-time = "2025-10-21T16:20:30.113Z" }, - { url = "https://files.pythonhosted.org/packages/3e/1c/8374990f9545e99462caacea5413ed783014b3b66ace49e35c533f07507b/grpcio-1.76.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:035d90bc79eaa4bed83f524331d55e35820725c9fbb00ffa1904d5550ed7ede3", size = 6407178, upload-time = "2025-10-21T16:20:32.733Z" }, - { url = "https://files.pythonhosted.org/packages/1e/77/36fd7d7c75a6c12542c90a6d647a27935a1ecaad03e0ffdb7c42db6b04d2/grpcio-1.76.0-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:4215d3a102bd95e2e11b5395c78562967959824156af11fa93d18fdd18050990", size = 7075684, upload-time = "2025-10-21T16:20:35.435Z" }, - { url = "https://files.pythonhosted.org/packages/38/f7/e3cdb252492278e004722306c5a8935eae91e64ea11f0af3437a7de2e2b7/grpcio-1.76.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:49ce47231818806067aea3324d4bf13825b658ad662d3b25fada0bdad9b8a6af", size = 6611133, upload-time = "2025-10-21T16:20:37.541Z" }, - { url = "https://files.pythonhosted.org/packages/7e/20/340db7af162ccd20a0893b5f3c4a5d676af7b71105517e62279b5b61d95a/grpcio-1.76.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8cc3309d8e08fd79089e13ed4819d0af72aa935dd8f435a195fd152796752ff2", size = 7195507, upload-time = "2025-10-21T16:20:39.643Z" }, - { url = "https://files.pythonhosted.org/packages/10/f0/b2160addc1487bd8fa4810857a27132fb4ce35c1b330c2f3ac45d697b106/grpcio-1.76.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:971fd5a1d6e62e00d945423a567e42eb1fa678ba89072832185ca836a94daaa6", size = 8160651, upload-time = "2025-10-21T16:20:42.492Z" }, - { url = "https://files.pythonhosted.org/packages/2c/2c/ac6f98aa113c6ef111b3f347854e99ebb7fb9d8f7bb3af1491d438f62af4/grpcio-1.76.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:9d9adda641db7207e800a7f089068f6f645959f2df27e870ee81d44701dd9db3", size = 7620568, upload-time = "2025-10-21T16:20:45.995Z" }, - { url = "https://files.pythonhosted.org/packages/90/84/7852f7e087285e3ac17a2703bc4129fafee52d77c6c82af97d905566857e/grpcio-1.76.0-cp310-cp310-win32.whl", hash = "sha256:063065249d9e7e0782d03d2bca50787f53bd0fb89a67de9a7b521c4a01f1989b", size = 3998879, upload-time = "2025-10-21T16:20:48.592Z" }, - { url = "https://files.pythonhosted.org/packages/10/30/d3d2adcbb6dd3ff59d6ac3df6ef830e02b437fb5c90990429fd180e52f30/grpcio-1.76.0-cp310-cp310-win_amd64.whl", hash = "sha256:a6ae758eb08088d36812dd5d9af7a9859c05b1e0f714470ea243694b49278e7b", size = 4706892, upload-time = "2025-10-21T16:20:50.697Z" }, - { url = "https://files.pythonhosted.org/packages/a0/00/8163a1beeb6971f66b4bbe6ac9457b97948beba8dd2fc8e1281dce7f79ec/grpcio-1.76.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:2e1743fbd7f5fa713a1b0a8ac8ebabf0ec980b5d8809ec358d488e273b9cf02a", size = 5843567, upload-time = "2025-10-21T16:20:52.829Z" }, - { url = "https://files.pythonhosted.org/packages/10/c1/934202f5cf335e6d852530ce14ddb0fef21be612ba9ecbbcbd4d748ca32d/grpcio-1.76.0-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:a8c2cf1209497cf659a667d7dea88985e834c24b7c3b605e6254cbb5076d985c", size = 11848017, upload-time = "2025-10-21T16:20:56.705Z" }, - { url = "https://files.pythonhosted.org/packages/11/0b/8dec16b1863d74af6eb3543928600ec2195af49ca58b16334972f6775663/grpcio-1.76.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:08caea849a9d3c71a542827d6df9d5a69067b0a1efbea8a855633ff5d9571465", size = 6412027, upload-time = "2025-10-21T16:20:59.3Z" }, - { url = "https://files.pythonhosted.org/packages/d7/64/7b9e6e7ab910bea9d46f2c090380bab274a0b91fb0a2fe9b0cd399fffa12/grpcio-1.76.0-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:f0e34c2079d47ae9f6188211db9e777c619a21d4faba6977774e8fa43b085e48", size = 7075913, upload-time = "2025-10-21T16:21:01.645Z" }, - { url = "https://files.pythonhosted.org/packages/68/86/093c46e9546073cefa789bd76d44c5cb2abc824ca62af0c18be590ff13ba/grpcio-1.76.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8843114c0cfce61b40ad48df65abcfc00d4dba82eae8718fab5352390848c5da", size = 6615417, upload-time = "2025-10-21T16:21:03.844Z" }, - { url = "https://files.pythonhosted.org/packages/f7/b6/5709a3a68500a9c03da6fb71740dcdd5ef245e39266461a03f31a57036d8/grpcio-1.76.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8eddfb4d203a237da6f3cc8a540dad0517d274b5a1e9e636fd8d2c79b5c1d397", size = 7199683, upload-time = "2025-10-21T16:21:06.195Z" }, - { url = "https://files.pythonhosted.org/packages/91/d3/4b1f2bf16ed52ce0b508161df3a2d186e4935379a159a834cb4a7d687429/grpcio-1.76.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:32483fe2aab2c3794101c2a159070584e5db11d0aa091b2c0ea9c4fc43d0d749", size = 8163109, upload-time = "2025-10-21T16:21:08.498Z" }, - { url = "https://files.pythonhosted.org/packages/5c/61/d9043f95f5f4cf085ac5dd6137b469d41befb04bd80280952ffa2a4c3f12/grpcio-1.76.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:dcfe41187da8992c5f40aa8c5ec086fa3672834d2be57a32384c08d5a05b4c00", size = 7626676, upload-time = "2025-10-21T16:21:10.693Z" }, - { url = "https://files.pythonhosted.org/packages/36/95/fd9a5152ca02d8881e4dd419cdd790e11805979f499a2e5b96488b85cf27/grpcio-1.76.0-cp311-cp311-win32.whl", hash = "sha256:2107b0c024d1b35f4083f11245c0e23846ae64d02f40b2b226684840260ed054", size = 3997688, upload-time = "2025-10-21T16:21:12.746Z" }, - { url = "https://files.pythonhosted.org/packages/60/9c/5c359c8d4c9176cfa3c61ecd4efe5affe1f38d9bae81e81ac7186b4c9cc8/grpcio-1.76.0-cp311-cp311-win_amd64.whl", hash = "sha256:522175aba7af9113c48ec10cc471b9b9bd4f6ceb36aeb4544a8e2c80ed9d252d", size = 4709315, upload-time = "2025-10-21T16:21:15.26Z" }, - { url = "https://files.pythonhosted.org/packages/bf/05/8e29121994b8d959ffa0afd28996d452f291b48cfc0875619de0bde2c50c/grpcio-1.76.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:81fd9652b37b36f16138611c7e884eb82e0cec137c40d3ef7c3f9b3ed00f6ed8", size = 5799718, upload-time = "2025-10-21T16:21:17.939Z" }, - { url = "https://files.pythonhosted.org/packages/d9/75/11d0e66b3cdf998c996489581bdad8900db79ebd83513e45c19548f1cba4/grpcio-1.76.0-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:04bbe1bfe3a68bbfd4e52402ab7d4eb59d72d02647ae2042204326cf4bbad280", size = 11825627, upload-time = "2025-10-21T16:21:20.466Z" }, - { url = "https://files.pythonhosted.org/packages/28/50/2f0aa0498bc188048f5d9504dcc5c2c24f2eb1a9337cd0fa09a61a2e75f0/grpcio-1.76.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d388087771c837cdb6515539f43b9d4bf0b0f23593a24054ac16f7a960be16f4", size = 6359167, upload-time = "2025-10-21T16:21:23.122Z" }, - { url = "https://files.pythonhosted.org/packages/66/e5/bbf0bb97d29ede1d59d6588af40018cfc345b17ce979b7b45424628dc8bb/grpcio-1.76.0-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:9f8f757bebaaea112c00dba718fc0d3260052ce714e25804a03f93f5d1c6cc11", size = 7044267, upload-time = "2025-10-21T16:21:25.995Z" }, - { url = "https://files.pythonhosted.org/packages/f5/86/f6ec2164f743d9609691115ae8ece098c76b894ebe4f7c94a655c6b03e98/grpcio-1.76.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:980a846182ce88c4f2f7e2c22c56aefd515daeb36149d1c897f83cf57999e0b6", size = 6573963, upload-time = "2025-10-21T16:21:28.631Z" }, - { url = "https://files.pythonhosted.org/packages/60/bc/8d9d0d8505feccfdf38a766d262c71e73639c165b311c9457208b56d92ae/grpcio-1.76.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f92f88e6c033db65a5ae3d97905c8fea9c725b63e28d5a75cb73b49bda5024d8", size = 7164484, upload-time = "2025-10-21T16:21:30.837Z" }, - { url = "https://files.pythonhosted.org/packages/67/e6/5d6c2fc10b95edf6df9b8f19cf10a34263b7fd48493936fffd5085521292/grpcio-1.76.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4baf3cbe2f0be3289eb68ac8ae771156971848bb8aaff60bad42005539431980", size = 8127777, upload-time = "2025-10-21T16:21:33.577Z" }, - { url = "https://files.pythonhosted.org/packages/3f/c8/dce8ff21c86abe025efe304d9e31fdb0deaaa3b502b6a78141080f206da0/grpcio-1.76.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:615ba64c208aaceb5ec83bfdce7728b80bfeb8be97562944836a7a0a9647d882", size = 7594014, upload-time = "2025-10-21T16:21:41.882Z" }, - { url = "https://files.pythonhosted.org/packages/e0/42/ad28191ebf983a5d0ecef90bab66baa5a6b18f2bfdef9d0a63b1973d9f75/grpcio-1.76.0-cp312-cp312-win32.whl", hash = "sha256:45d59a649a82df5718fd9527ce775fd66d1af35e6d31abdcdc906a49c6822958", size = 3984750, upload-time = "2025-10-21T16:21:44.006Z" }, - { url = "https://files.pythonhosted.org/packages/9e/00/7bd478cbb851c04a48baccaa49b75abaa8e4122f7d86da797500cccdd771/grpcio-1.76.0-cp312-cp312-win_amd64.whl", hash = "sha256:c088e7a90b6017307f423efbb9d1ba97a22aa2170876223f9709e9d1de0b5347", size = 4704003, upload-time = "2025-10-21T16:21:46.244Z" }, - { url = "https://files.pythonhosted.org/packages/fc/ed/71467ab770effc9e8cef5f2e7388beb2be26ed642d567697bb103a790c72/grpcio-1.76.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:26ef06c73eb53267c2b319f43e6634c7556ea37672029241a056629af27c10e2", size = 5807716, upload-time = "2025-10-21T16:21:48.475Z" }, - { url = "https://files.pythonhosted.org/packages/2c/85/c6ed56f9817fab03fa8a111ca91469941fb514e3e3ce6d793cb8f1e1347b/grpcio-1.76.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:45e0111e73f43f735d70786557dc38141185072d7ff8dc1829d6a77ac1471468", size = 11821522, upload-time = "2025-10-21T16:21:51.142Z" }, - { url = "https://files.pythonhosted.org/packages/ac/31/2b8a235ab40c39cbc141ef647f8a6eb7b0028f023015a4842933bc0d6831/grpcio-1.76.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:83d57312a58dcfe2a3a0f9d1389b299438909a02db60e2f2ea2ae2d8034909d3", size = 6362558, upload-time = "2025-10-21T16:21:54.213Z" }, - { url = "https://files.pythonhosted.org/packages/bd/64/9784eab483358e08847498ee56faf8ff6ea8e0a4592568d9f68edc97e9e9/grpcio-1.76.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:3e2a27c89eb9ac3d81ec8835e12414d73536c6e620355d65102503064a4ed6eb", size = 7049990, upload-time = "2025-10-21T16:21:56.476Z" }, - { url = "https://files.pythonhosted.org/packages/2b/94/8c12319a6369434e7a184b987e8e9f3b49a114c489b8315f029e24de4837/grpcio-1.76.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:61f69297cba3950a524f61c7c8ee12e55c486cb5f7db47ff9dcee33da6f0d3ae", size = 6575387, upload-time = "2025-10-21T16:21:59.051Z" }, - { url = "https://files.pythonhosted.org/packages/15/0f/f12c32b03f731f4a6242f771f63039df182c8b8e2cf8075b245b409259d4/grpcio-1.76.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6a15c17af8839b6801d554263c546c69c4d7718ad4321e3166175b37eaacca77", size = 7166668, upload-time = "2025-10-21T16:22:02.049Z" }, - { url = "https://files.pythonhosted.org/packages/ff/2d/3ec9ce0c2b1d92dd59d1c3264aaec9f0f7c817d6e8ac683b97198a36ed5a/grpcio-1.76.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:25a18e9810fbc7e7f03ec2516addc116a957f8cbb8cbc95ccc80faa072743d03", size = 8124928, upload-time = "2025-10-21T16:22:04.984Z" }, - { url = "https://files.pythonhosted.org/packages/1a/74/fd3317be5672f4856bcdd1a9e7b5e17554692d3db9a3b273879dc02d657d/grpcio-1.76.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:931091142fd8cc14edccc0845a79248bc155425eee9a98b2db2ea4f00a235a42", size = 7589983, upload-time = "2025-10-21T16:22:07.881Z" }, - { url = "https://files.pythonhosted.org/packages/45/bb/ca038cf420f405971f19821c8c15bcbc875505f6ffadafe9ffd77871dc4c/grpcio-1.76.0-cp313-cp313-win32.whl", hash = "sha256:5e8571632780e08526f118f74170ad8d50fb0a48c23a746bef2a6ebade3abd6f", size = 3984727, upload-time = "2025-10-21T16:22:10.032Z" }, - { url = "https://files.pythonhosted.org/packages/41/80/84087dc56437ced7cdd4b13d7875e7439a52a261e3ab4e06488ba6173b0a/grpcio-1.76.0-cp313-cp313-win_amd64.whl", hash = "sha256:f9f7bd5faab55f47231ad8dba7787866b69f5e93bc306e3915606779bbfb4ba8", size = 4702799, upload-time = "2025-10-21T16:22:12.709Z" }, - { url = "https://files.pythonhosted.org/packages/b4/46/39adac80de49d678e6e073b70204091e76631e03e94928b9ea4ecf0f6e0e/grpcio-1.76.0-cp314-cp314-linux_armv7l.whl", hash = "sha256:ff8a59ea85a1f2191a0ffcc61298c571bc566332f82e5f5be1b83c9d8e668a62", size = 5808417, upload-time = "2025-10-21T16:22:15.02Z" }, - { url = "https://files.pythonhosted.org/packages/9c/f5/a4531f7fb8b4e2a60b94e39d5d924469b7a6988176b3422487be61fe2998/grpcio-1.76.0-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:06c3d6b076e7b593905d04fdba6a0525711b3466f43b3400266f04ff735de0cd", size = 11828219, upload-time = "2025-10-21T16:22:17.954Z" }, - { url = "https://files.pythonhosted.org/packages/4b/1c/de55d868ed7a8bd6acc6b1d6ddc4aa36d07a9f31d33c912c804adb1b971b/grpcio-1.76.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fd5ef5932f6475c436c4a55e4336ebbe47bd3272be04964a03d316bbf4afbcbc", size = 6367826, upload-time = "2025-10-21T16:22:20.721Z" }, - { url = "https://files.pythonhosted.org/packages/59/64/99e44c02b5adb0ad13ab3adc89cb33cb54bfa90c74770f2607eea629b86f/grpcio-1.76.0-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:b331680e46239e090f5b3cead313cc772f6caa7d0fc8de349337563125361a4a", size = 7049550, upload-time = "2025-10-21T16:22:23.637Z" }, - { url = "https://files.pythonhosted.org/packages/43/28/40a5be3f9a86949b83e7d6a2ad6011d993cbe9b6bd27bea881f61c7788b6/grpcio-1.76.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2229ae655ec4e8999599469559e97630185fdd53ae1e8997d147b7c9b2b72cba", size = 6575564, upload-time = "2025-10-21T16:22:26.016Z" }, - { url = "https://files.pythonhosted.org/packages/4b/a9/1be18e6055b64467440208a8559afac243c66a8b904213af6f392dc2212f/grpcio-1.76.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:490fa6d203992c47c7b9e4a9d39003a0c2bcc1c9aa3c058730884bbbb0ee9f09", size = 7176236, upload-time = "2025-10-21T16:22:28.362Z" }, - { url = "https://files.pythonhosted.org/packages/0f/55/dba05d3fcc151ce6e81327541d2cc8394f442f6b350fead67401661bf041/grpcio-1.76.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:479496325ce554792dba6548fae3df31a72cef7bad71ca2e12b0e58f9b336bfc", size = 8125795, upload-time = "2025-10-21T16:22:31.075Z" }, - { url = "https://files.pythonhosted.org/packages/4a/45/122df922d05655f63930cf42c9e3f72ba20aadb26c100ee105cad4ce4257/grpcio-1.76.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:1c9b93f79f48b03ada57ea24725d83a30284a012ec27eab2cf7e50a550cbbbcc", size = 7592214, upload-time = "2025-10-21T16:22:33.831Z" }, - { url = "https://files.pythonhosted.org/packages/4a/6e/0b899b7f6b66e5af39e377055fb4a6675c9ee28431df5708139df2e93233/grpcio-1.76.0-cp314-cp314-win32.whl", hash = "sha256:747fa73efa9b8b1488a95d0ba1039c8e2dca0f741612d80415b1e1c560febf4e", size = 4062961, upload-time = "2025-10-21T16:22:36.468Z" }, - { url = "https://files.pythonhosted.org/packages/19/41/0b430b01a2eb38ee887f88c1f07644a1df8e289353b78e82b37ef988fb64/grpcio-1.76.0-cp314-cp314-win_amd64.whl", hash = "sha256:922fa70ba549fce362d2e2871ab542082d66e2aaf0c19480ea453905b01f384e", size = 4834462, upload-time = "2025-10-21T16:22:39.772Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/06/8a/3d098f35c143a89520e568e6539cc098fcd294495910e359889ce8741c84/grpcio-1.78.0.tar.gz", hash = "sha256:7382b95189546f375c174f53a5fa873cef91c4b8005faa05cc5b3beea9c4f1c5", size = 12852416, upload-time = "2026-02-06T09:57:18.093Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5a/a8/690a085b4d1fe066130de97a87de32c45062cf2ecd218df9675add895550/grpcio-1.78.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:7cc47943d524ee0096f973e1081cb8f4f17a4615f2116882a5f1416e4cfe92b5", size = 5946986, upload-time = "2026-02-06T09:54:34.043Z" }, + { url = "https://files.pythonhosted.org/packages/c7/1b/e5213c5c0ced9d2d92778d30529ad5bb2dcfb6c48c4e2d01b1f302d33d64/grpcio-1.78.0-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:c3f293fdc675ccba4db5a561048cca627b5e7bd1c8a6973ffedabe7d116e22e2", size = 11816533, upload-time = "2026-02-06T09:54:37.04Z" }, + { url = "https://files.pythonhosted.org/packages/18/37/1ba32dccf0a324cc5ace744c44331e300b000a924bf14840f948c559ede7/grpcio-1.78.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:10a9a644b5dd5aec3b82b5b0b90d41c0fa94c85ef42cb42cf78a23291ddb5e7d", size = 6519964, upload-time = "2026-02-06T09:54:40.268Z" }, + { url = "https://files.pythonhosted.org/packages/ed/f5/c0e178721b818072f2e8b6fde13faaba942406c634009caf065121ce246b/grpcio-1.78.0-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:4c5533d03a6cbd7f56acfc9cfb44ea64f63d29091e40e44010d34178d392d7eb", size = 7198058, upload-time = "2026-02-06T09:54:42.389Z" }, + { url = "https://files.pythonhosted.org/packages/5b/b2/40d43c91ae9cd667edc960135f9f08e58faa1576dc95af29f66ec912985f/grpcio-1.78.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ff870aebe9a93a85283837801d35cd5f8814fe2ad01e606861a7fb47c762a2b7", size = 6727212, upload-time = "2026-02-06T09:54:44.91Z" }, + { url = "https://files.pythonhosted.org/packages/ed/88/9da42eed498f0efcfcd9156e48ae63c0cde3bea398a16c99fb5198c885b6/grpcio-1.78.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:391e93548644e6b2726f1bb84ed60048d4bcc424ce5e4af0843d28ca0b754fec", size = 7300845, upload-time = "2026-02-06T09:54:47.562Z" }, + { url = "https://files.pythonhosted.org/packages/23/3f/1c66b7b1b19a8828890e37868411a6e6925df5a9030bfa87ab318f34095d/grpcio-1.78.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:df2c8f3141f7cbd112a6ebbd760290b5849cda01884554f7c67acc14e7b1758a", size = 8284605, upload-time = "2026-02-06T09:54:50.475Z" }, + { url = "https://files.pythonhosted.org/packages/94/c4/ca1bd87394f7b033e88525384b4d1e269e8424ab441ea2fba1a0c5b50986/grpcio-1.78.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:bd8cb8026e5f5b50498a3c4f196f57f9db344dad829ffae16b82e4fdbaea2813", size = 7726672, upload-time = "2026-02-06T09:54:53.11Z" }, + { url = "https://files.pythonhosted.org/packages/41/09/f16e487d4cc65ccaf670f6ebdd1a17566b965c74fc3d93999d3b2821e052/grpcio-1.78.0-cp310-cp310-win32.whl", hash = "sha256:f8dff3d9777e5d2703a962ee5c286c239bf0ba173877cc68dc02c17d042e29de", size = 4076715, upload-time = "2026-02-06T09:54:55.549Z" }, + { url = "https://files.pythonhosted.org/packages/2a/32/4ce60d94e242725fd3bcc5673c04502c82a8e87b21ea411a63992dc39f8f/grpcio-1.78.0-cp310-cp310-win_amd64.whl", hash = "sha256:94f95cf5d532d0e717eed4fc1810e8e6eded04621342ec54c89a7c2f14b581bf", size = 4799157, upload-time = "2026-02-06T09:54:59.838Z" }, + { url = "https://files.pythonhosted.org/packages/86/c7/d0b780a29b0837bf4ca9580904dfb275c1fc321ded7897d620af7047ec57/grpcio-1.78.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:2777b783f6c13b92bd7b716667452c329eefd646bfb3f2e9dabea2e05dbd34f6", size = 5951525, upload-time = "2026-02-06T09:55:01.989Z" }, + { url = "https://files.pythonhosted.org/packages/c5/b1/96920bf2ee61df85a9503cb6f733fe711c0ff321a5a697d791b075673281/grpcio-1.78.0-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:9dca934f24c732750389ce49d638069c3892ad065df86cb465b3fa3012b70c9e", size = 11830418, upload-time = "2026-02-06T09:55:04.462Z" }, + { url = "https://files.pythonhosted.org/packages/83/0c/7c1528f098aeb75a97de2bae18c530f56959fb7ad6c882db45d9884d6edc/grpcio-1.78.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:459ab414b35f4496138d0ecd735fed26f1318af5e52cb1efbc82a09f0d5aa911", size = 6524477, upload-time = "2026-02-06T09:55:07.111Z" }, + { url = "https://files.pythonhosted.org/packages/8d/52/e7c1f3688f949058e19a011c4e0dec973da3d0ae5e033909677f967ae1f4/grpcio-1.78.0-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:082653eecbdf290e6e3e2c276ab2c54b9e7c299e07f4221872380312d8cf395e", size = 7198266, upload-time = "2026-02-06T09:55:10.016Z" }, + { url = "https://files.pythonhosted.org/packages/e5/61/8ac32517c1e856677282c34f2e7812d6c328fa02b8f4067ab80e77fdc9c9/grpcio-1.78.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:85f93781028ec63f383f6bc90db785a016319c561cc11151fbb7b34e0d012303", size = 6730552, upload-time = "2026-02-06T09:55:12.207Z" }, + { url = "https://files.pythonhosted.org/packages/bd/98/b8ee0158199250220734f620b12e4a345955ac7329cfd908d0bf0fda77f0/grpcio-1.78.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:f12857d24d98441af6a1d5c87442d624411db486f7ba12550b07788f74b67b04", size = 7304296, upload-time = "2026-02-06T09:55:15.044Z" }, + { url = "https://files.pythonhosted.org/packages/bd/0f/7b72762e0d8840b58032a56fdbd02b78fc645b9fa993d71abf04edbc54f4/grpcio-1.78.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5397fff416b79e4b284959642a4e95ac4b0f1ece82c9993658e0e477d40551ec", size = 8288298, upload-time = "2026-02-06T09:55:17.276Z" }, + { url = "https://files.pythonhosted.org/packages/24/ae/ae4ce56bc5bb5caa3a486d60f5f6083ac3469228faa734362487176c15c5/grpcio-1.78.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:fbe6e89c7ffb48518384068321621b2a69cab509f58e40e4399fdd378fa6d074", size = 7730953, upload-time = "2026-02-06T09:55:19.545Z" }, + { url = "https://files.pythonhosted.org/packages/b5/6e/8052e3a28eb6a820c372b2eb4b5e32d195c661e137d3eca94d534a4cfd8a/grpcio-1.78.0-cp311-cp311-win32.whl", hash = "sha256:6092beabe1966a3229f599d7088b38dfc8ffa1608b5b5cdda31e591e6500f856", size = 4076503, upload-time = "2026-02-06T09:55:21.521Z" }, + { url = "https://files.pythonhosted.org/packages/08/62/f22c98c5265dfad327251fa2f840b591b1df5f5e15d88b19c18c86965b27/grpcio-1.78.0-cp311-cp311-win_amd64.whl", hash = "sha256:1afa62af6e23f88629f2b29ec9e52ec7c65a7176c1e0a83292b93c76ca882558", size = 4799767, upload-time = "2026-02-06T09:55:24.107Z" }, + { url = "https://files.pythonhosted.org/packages/4e/f4/7384ed0178203d6074446b3c4f46c90a22ddf7ae0b3aee521627f54cfc2a/grpcio-1.78.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:f9ab915a267fc47c7e88c387a3a28325b58c898e23d4995f765728f4e3dedb97", size = 5913985, upload-time = "2026-02-06T09:55:26.832Z" }, + { url = "https://files.pythonhosted.org/packages/81/ed/be1caa25f06594463f685b3790b320f18aea49b33166f4141bfdc2bfb236/grpcio-1.78.0-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:3f8904a8165ab21e07e58bf3e30a73f4dffc7a1e0dbc32d51c61b5360d26f43e", size = 11811853, upload-time = "2026-02-06T09:55:29.224Z" }, + { url = "https://files.pythonhosted.org/packages/24/a7/f06d151afc4e64b7e3cc3e872d331d011c279aaab02831e40a81c691fb65/grpcio-1.78.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:859b13906ce098c0b493af92142ad051bf64c7870fa58a123911c88606714996", size = 6475766, upload-time = "2026-02-06T09:55:31.825Z" }, + { url = "https://files.pythonhosted.org/packages/8a/a8/4482922da832ec0082d0f2cc3a10976d84a7424707f25780b82814aafc0a/grpcio-1.78.0-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:b2342d87af32790f934a79c3112641e7b27d63c261b8b4395350dad43eff1dc7", size = 7170027, upload-time = "2026-02-06T09:55:34.7Z" }, + { url = "https://files.pythonhosted.org/packages/54/bf/f4a3b9693e35d25b24b0b39fa46d7d8a3c439e0a3036c3451764678fec20/grpcio-1.78.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:12a771591ae40bc65ba67048fa52ef4f0e6db8279e595fd349f9dfddeef571f9", size = 6690766, upload-time = "2026-02-06T09:55:36.902Z" }, + { url = "https://files.pythonhosted.org/packages/c7/b9/521875265cc99fe5ad4c5a17010018085cae2810a928bf15ebe7d8bcd9cc/grpcio-1.78.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:185dea0d5260cbb2d224c507bf2a5444d5abbb1fa3594c1ed7e4c709d5eb8383", size = 7266161, upload-time = "2026-02-06T09:55:39.824Z" }, + { url = "https://files.pythonhosted.org/packages/05/86/296a82844fd40a4ad4a95f100b55044b4f817dece732bf686aea1a284147/grpcio-1.78.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:51b13f9aed9d59ee389ad666b8c2214cc87b5de258fa712f9ab05f922e3896c6", size = 8253303, upload-time = "2026-02-06T09:55:42.353Z" }, + { url = "https://files.pythonhosted.org/packages/f3/e4/ea3c0caf5468537f27ad5aab92b681ed7cc0ef5f8c9196d3fd42c8c2286b/grpcio-1.78.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fd5f135b1bd58ab088930b3c613455796dfa0393626a6972663ccdda5b4ac6ce", size = 7698222, upload-time = "2026-02-06T09:55:44.629Z" }, + { url = "https://files.pythonhosted.org/packages/d7/47/7f05f81e4bb6b831e93271fb12fd52ba7b319b5402cbc101d588f435df00/grpcio-1.78.0-cp312-cp312-win32.whl", hash = "sha256:94309f498bcc07e5a7d16089ab984d42ad96af1d94b5a4eb966a266d9fcabf68", size = 4066123, upload-time = "2026-02-06T09:55:47.644Z" }, + { url = "https://files.pythonhosted.org/packages/ad/e7/d6914822c88aa2974dbbd10903d801a28a19ce9cd8bad7e694cbbcf61528/grpcio-1.78.0-cp312-cp312-win_amd64.whl", hash = "sha256:9566fe4ababbb2610c39190791e5b829869351d14369603702e890ef3ad2d06e", size = 4797657, upload-time = "2026-02-06T09:55:49.86Z" }, + { url = "https://files.pythonhosted.org/packages/05/a9/8f75894993895f361ed8636cd9237f4ab39ef87fd30db17467235ed1c045/grpcio-1.78.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:ce3a90455492bf8bfa38e56fbbe1dbd4f872a3d8eeaf7337dc3b1c8aa28c271b", size = 5920143, upload-time = "2026-02-06T09:55:52.035Z" }, + { url = "https://files.pythonhosted.org/packages/55/06/0b78408e938ac424100100fd081189451b472236e8a3a1f6500390dc4954/grpcio-1.78.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:2bf5e2e163b356978b23652c4818ce4759d40f4712ee9ec5a83c4be6f8c23a3a", size = 11803926, upload-time = "2026-02-06T09:55:55.494Z" }, + { url = "https://files.pythonhosted.org/packages/88/93/b59fe7832ff6ae3c78b813ea43dac60e295fa03606d14d89d2e0ec29f4f3/grpcio-1.78.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8f2ac84905d12918e4e55a16da17939eb63e433dc11b677267c35568aa63fc84", size = 6478628, upload-time = "2026-02-06T09:55:58.533Z" }, + { url = "https://files.pythonhosted.org/packages/ed/df/e67e3734527f9926b7d9c0dde6cd998d1d26850c3ed8eeec81297967ac67/grpcio-1.78.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:b58f37edab4a3881bc6c9bca52670610e0c9ca14e2ea3cf9debf185b870457fb", size = 7173574, upload-time = "2026-02-06T09:56:01.786Z" }, + { url = "https://files.pythonhosted.org/packages/a6/62/cc03fffb07bfba982a9ec097b164e8835546980aec25ecfa5f9c1a47e022/grpcio-1.78.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:735e38e176a88ce41840c21bb49098ab66177c64c82426e24e0082500cc68af5", size = 6692639, upload-time = "2026-02-06T09:56:04.529Z" }, + { url = "https://files.pythonhosted.org/packages/bf/9a/289c32e301b85bdb67d7ec68b752155e674ee3ba2173a1858f118e399ef3/grpcio-1.78.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2045397e63a7a0ee7957c25f7dbb36ddc110e0cfb418403d110c0a7a68a844e9", size = 7268838, upload-time = "2026-02-06T09:56:08.397Z" }, + { url = "https://files.pythonhosted.org/packages/0e/79/1be93f32add280461fa4773880196572563e9c8510861ac2da0ea0f892b6/grpcio-1.78.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:a9f136fbafe7ccf4ac7e8e0c28b31066e810be52d6e344ef954a3a70234e1702", size = 8251878, upload-time = "2026-02-06T09:56:10.914Z" }, + { url = "https://files.pythonhosted.org/packages/65/65/793f8e95296ab92e4164593674ae6291b204bb5f67f9d4a711489cd30ffa/grpcio-1.78.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:748b6138585379c737adc08aeffd21222abbda1a86a0dca2a39682feb9196c20", size = 7695412, upload-time = "2026-02-06T09:56:13.593Z" }, + { url = "https://files.pythonhosted.org/packages/1c/9f/1e233fe697ecc82845942c2822ed06bb522e70d6771c28d5528e4c50f6a4/grpcio-1.78.0-cp313-cp313-win32.whl", hash = "sha256:271c73e6e5676afe4fc52907686670c7cea22ab2310b76a59b678403ed40d670", size = 4064899, upload-time = "2026-02-06T09:56:15.601Z" }, + { url = "https://files.pythonhosted.org/packages/4d/27/d86b89e36de8a951501fb06a0f38df19853210f341d0b28f83f4aa0ffa08/grpcio-1.78.0-cp313-cp313-win_amd64.whl", hash = "sha256:f2d4e43ee362adfc05994ed479334d5a451ab7bc3f3fee1b796b8ca66895acb4", size = 4797393, upload-time = "2026-02-06T09:56:17.882Z" }, + { url = "https://files.pythonhosted.org/packages/29/f2/b56e43e3c968bfe822fa6ce5bca10d5c723aa40875b48791ce1029bb78c7/grpcio-1.78.0-cp314-cp314-linux_armv7l.whl", hash = "sha256:e87cbc002b6f440482b3519e36e1313eb5443e9e9e73d6a52d43bd2004fcfd8e", size = 5920591, upload-time = "2026-02-06T09:56:20.758Z" }, + { url = "https://files.pythonhosted.org/packages/5d/81/1f3b65bd30c334167bfa8b0d23300a44e2725ce39bba5b76a2460d85f745/grpcio-1.78.0-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:c41bc64626db62e72afec66b0c8a0da76491510015417c127bfc53b2fe6d7f7f", size = 11813685, upload-time = "2026-02-06T09:56:24.315Z" }, + { url = "https://files.pythonhosted.org/packages/0e/1c/bbe2f8216a5bd3036119c544d63c2e592bdf4a8ec6e4a1867592f4586b26/grpcio-1.78.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8dfffba826efcf366b1e3ccc37e67afe676f290e13a3b48d31a46739f80a8724", size = 6487803, upload-time = "2026-02-06T09:56:27.367Z" }, + { url = "https://files.pythonhosted.org/packages/16/5c/a6b2419723ea7ddce6308259a55e8e7593d88464ce8db9f4aa857aba96fa/grpcio-1.78.0-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:74be1268d1439eaaf552c698cdb11cd594f0c49295ae6bb72c34ee31abbe611b", size = 7173206, upload-time = "2026-02-06T09:56:29.876Z" }, + { url = "https://files.pythonhosted.org/packages/df/1e/b8801345629a415ea7e26c83d75eb5dbe91b07ffe5210cc517348a8d4218/grpcio-1.78.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:be63c88b32e6c0f1429f1398ca5c09bc64b0d80950c8bb7807d7d7fb36fb84c7", size = 6693826, upload-time = "2026-02-06T09:56:32.305Z" }, + { url = "https://files.pythonhosted.org/packages/34/84/0de28eac0377742679a510784f049738a80424b17287739fc47d63c2439e/grpcio-1.78.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:3c586ac70e855c721bda8f548d38c3ca66ac791dc49b66a8281a1f99db85e452", size = 7277897, upload-time = "2026-02-06T09:56:34.915Z" }, + { url = "https://files.pythonhosted.org/packages/ca/9c/ad8685cfe20559a9edb66f735afdcb2b7d3de69b13666fdfc542e1916ebd/grpcio-1.78.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:35eb275bf1751d2ffbd8f57cdbc46058e857cf3971041521b78b7db94bdaf127", size = 8252404, upload-time = "2026-02-06T09:56:37.553Z" }, + { url = "https://files.pythonhosted.org/packages/3c/05/33a7a4985586f27e1de4803887c417ec7ced145ebd069bc38a9607059e2b/grpcio-1.78.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:207db540302c884b8848036b80db352a832b99dfdf41db1eb554c2c2c7800f65", size = 7696837, upload-time = "2026-02-06T09:56:40.173Z" }, + { url = "https://files.pythonhosted.org/packages/73/77/7382241caf88729b106e49e7d18e3116216c778e6a7e833826eb96de22f7/grpcio-1.78.0-cp314-cp314-win32.whl", hash = "sha256:57bab6deef2f4f1ca76cc04565df38dc5713ae6c17de690721bdf30cb1e0545c", size = 4142439, upload-time = "2026-02-06T09:56:43.258Z" }, + { url = "https://files.pythonhosted.org/packages/48/b2/b096ccce418882fbfda4f7496f9357aaa9a5af1896a9a7f60d9f2b275a06/grpcio-1.78.0-cp314-cp314-win_amd64.whl", hash = "sha256:dce09d6116df20a96acfdbf85e4866258c3758180e8c49845d6ba8248b6d0bbb", size = 4929852, upload-time = "2026-02-06T09:56:45.885Z" }, ] [[package]] @@ -2734,7 +2729,7 @@ wheels = [ [[package]] name = "huggingface-hub" -version = "1.4.0" +version = "1.4.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "filelock", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, @@ -2748,9 +2743,9 @@ dependencies = [ { name = "typer-slim", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d9/0e/e73927175162b8a4702b9f59268860f441fbe037c3960b1b6791eeb1deb7/huggingface_hub-1.4.0.tar.gz", hash = "sha256:dd8ca29409be10f544b624265f7ffe13a1a5c3f049f493b5dc9816ef3c6bd57b", size = 641608, upload-time = "2026-02-04T13:48:55.341Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c4/fc/eb9bc06130e8bbda6a616e1b80a7aa127681c448d6b49806f61db2670b61/huggingface_hub-1.4.1.tar.gz", hash = "sha256:b41131ec35e631e7383ab26d6146b8d8972abc8b6309b963b306fbcca87f5ed5", size = 642156, upload-time = "2026-02-06T09:20:03.013Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3f/74/f0fb3a54fbca7c0aeff85f41d93b90ca3f6a36d918459401a3890763c54b/huggingface_hub-1.4.0-py3-none-any.whl", hash = "sha256:49d380ffddb31d9d4b6acc0792691f8fa077e1ed51980ed42c7abca62ec1b3b6", size = 553202, upload-time = "2026-02-04T13:48:53.545Z" }, + { url = "https://files.pythonhosted.org/packages/d5/ae/2f6d96b4e6c5478d87d606a1934b5d436c4a2bce6bb7c6fdece891c128e3/huggingface_hub-1.4.1-py3-none-any.whl", hash = "sha256:9931d075fb7a79af5abc487106414ec5fba2c0ae86104c0c62fd6cae38873d18", size = 553326, upload-time = "2026-02-06T09:20:00.728Z" }, ] [[package]] @@ -4032,7 +4027,7 @@ wheels = [ [[package]] name = "openai" -version = "2.16.0" +version = "2.17.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, @@ -4044,9 +4039,9 @@ dependencies = [ { name = "tqdm", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b1/6c/e4c964fcf1d527fdf4739e7cc940c60075a4114d50d03871d5d5b1e13a88/openai-2.16.0.tar.gz", hash = "sha256:42eaa22ca0d8ded4367a77374104d7a2feafee5bd60a107c3c11b5243a11cd12", size = 629649, upload-time = "2026-01-27T23:28:02.579Z" } +sdist = { url = "https://files.pythonhosted.org/packages/9c/a2/677f22c4b487effb8a09439fb6134034b5f0a39ca27df8b95fac23a93720/openai-2.17.0.tar.gz", hash = "sha256:47224b74bd20f30c6b0a6a329505243cb2f26d5cf84d9f8d0825ff8b35e9c999", size = 631445, upload-time = "2026-02-05T16:27:40.953Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/16/83/0315bf2cfd75a2ce8a7e54188e9456c60cec6c0cf66728ed07bd9859ff26/openai-2.16.0-py3-none-any.whl", hash = "sha256:5f46643a8f42899a84e80c38838135d7038e7718333ce61396994f887b09a59b", size = 1068612, upload-time = "2026-01-27T23:28:00.356Z" }, + { url = "https://files.pythonhosted.org/packages/44/97/284535aa75e6e84ab388248b5a323fc296b1f70530130dee37f7f4fbe856/openai-2.17.0-py3-none-any.whl", hash = "sha256:4f393fd886ca35e113aac7ff239bcd578b81d8f104f5aedc7d3693eb2af1d338", size = 1069524, upload-time = "2026-02-05T16:27:38.941Z" }, ] [[package]] @@ -4128,7 +4123,7 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "googleapis-common-protos", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "grpcio", version = "1.67.1", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.14' and sys_platform == 'darwin') or (python_full_version < '3.14' and sys_platform == 'linux') or (python_full_version < '3.14' and sys_platform == 'win32')" }, - { name = "grpcio", version = "1.76.0", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.14' and sys_platform == 'darwin') or (python_full_version >= '3.14' and sys_platform == 'linux') or (python_full_version >= '3.14' and sys_platform == 'win32')" }, + { name = "grpcio", version = "1.78.0", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.14' and sys_platform == 'darwin') or (python_full_version >= '3.14' and sys_platform == 'linux') or (python_full_version >= '3.14' and sys_platform == 'win32')" }, { name = "opentelemetry-api", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "opentelemetry-exporter-otlp-proto-common", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "opentelemetry-proto", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, @@ -4702,7 +4697,7 @@ wheels = [ [[package]] name = "posthog" -version = "7.8.2" +version = "7.8.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "backoff", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, @@ -4712,9 +4707,9 @@ dependencies = [ { name = "six", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9c/5c/35edae017d92b2f7625a2b3be45dc36c8e6e14acbe5dbeeaa5a20a932ccf/posthog-7.8.2.tar.gz", hash = "sha256:d36472763750d8da60ebc3cbf6349a91222ba6a43dfdbdcdb6a9f03796514239", size = 166995, upload-time = "2026-02-04T15:10:31.251Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d1/ad/2f116cd9b83dc83ece4328a4efe0bcb80e5c2993837f89a788467d261da8/posthog-7.8.3.tar.gz", hash = "sha256:2b85e818bf818ac2768a890b772b7c12d4f909797226acd9327d66a319dbcf83", size = 167083, upload-time = "2026-02-06T13:16:22.938Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/53/d9/8f2374c559a6e50d2e92601b42540aae296f6e0a2066e913fed8bd603f23/posthog-7.8.2-py3-none-any.whl", hash = "sha256:d3fa69f7e15830a8e19cd4de4e7b40982838efa5d0f448133be3115bd556feef", size = 192440, upload-time = "2026-02-04T15:10:29.767Z" }, + { url = "https://files.pythonhosted.org/packages/e7/e5/5a4b060cbb9aa9defb8bfd55d15899b3146fece14147f4d66be80e81955a/posthog-7.8.3-py3-none-any.whl", hash = "sha256:1840796e4f7e14dd91ec5fdeb939712c3383fe9e758cfcdeb0317d8f30f7b901", size = 192528, upload-time = "2026-02-06T13:16:21.385Z" }, ] [[package]] @@ -4722,8 +4717,8 @@ name = "powerfx" version = "0.0.34" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "cffi", marker = "(python_full_version < '3.14' and sys_platform == 'darwin') or (python_full_version < '3.14' and sys_platform == 'linux') or (python_full_version < '3.14' and sys_platform == 'win32')" }, - { name = "pythonnet", marker = "(python_full_version < '3.14' and sys_platform == 'darwin') or (python_full_version < '3.14' and sys_platform == 'linux') or (python_full_version < '3.14' and sys_platform == 'win32')" }, + { name = "cffi", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "pythonnet", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/9f/fb/6c4bf87e0c74ca1c563921ce89ca1c5785b7576bca932f7255cdf81082a7/powerfx-0.0.34.tar.gz", hash = "sha256:956992e7afd272657ed16d80f4cad24ec95d9e4a79fb9dfa4a068a09e136af32", size = 3237555, upload-time = "2025-12-22T15:50:59.682Z" } wheels = [ @@ -5390,7 +5385,7 @@ name = "pythonnet" version = "3.0.5" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "clr-loader", marker = "(python_full_version < '3.14' and sys_platform == 'darwin') or (python_full_version < '3.14' and sys_platform == 'linux') or (python_full_version < '3.14' and sys_platform == 'win32')" }, + { name = "clr-loader", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/9a/d6/1afd75edd932306ae9bd2c2d961d603dc2b52fcec51b04afea464f1f6646/pythonnet-3.0.5.tar.gz", hash = "sha256:48e43ca463941b3608b32b4e236db92d8d40db4c58a75ace902985f76dac21cf", size = 239212, upload-time = "2024-12-13T08:30:44.393Z" } wheels = [ @@ -5498,7 +5493,7 @@ version = "1.16.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "grpcio", version = "1.67.1", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.14' and sys_platform == 'darwin') or (python_full_version < '3.14' and sys_platform == 'linux') or (python_full_version < '3.14' and sys_platform == 'win32')" }, - { name = "grpcio", version = "1.76.0", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.14' and sys_platform == 'darwin') or (python_full_version >= '3.14' and sys_platform == 'linux') or (python_full_version >= '3.14' and sys_platform == 'win32')" }, + { name = "grpcio", version = "1.78.0", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.14' and sys_platform == 'darwin') or (python_full_version >= '3.14' and sys_platform == 'linux') or (python_full_version >= '3.14' and sys_platform == 'win32')" }, { name = "httpx", extra = ["http2"], marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, { name = "numpy", version = "2.4.2", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.11' and sys_platform == 'darwin') or (python_full_version >= '3.11' and sys_platform == 'linux') or (python_full_version >= '3.11' and sys_platform == 'win32')" }, @@ -6830,28 +6825,27 @@ wheels = [ [[package]] name = "uv" -version = "0.9.30" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4e/a0/63cea38fe839fb89592728b91928ee6d15705f1376a7940fee5bbc77fea0/uv-0.9.30.tar.gz", hash = "sha256:03ebd4b22769e0a8d825fa09d038e31cbab5d3d48edf755971cb0cec7920ab95", size = 3846526, upload-time = "2026-02-04T21:45:37.58Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a3/3c/71be72f125f0035348b415468559cc3b335ec219376d17a3d242d2bd9b23/uv-0.9.30-py3-none-linux_armv6l.whl", hash = "sha256:a5467dddae1cd5f4e093f433c0f0d9a0df679b92696273485ec91bbb5a8620e6", size = 21927585, upload-time = "2026-02-04T21:46:14.935Z" }, - { url = "https://files.pythonhosted.org/packages/0f/fd/8070b5423a77d4058d14e48a970aa075762bbff4c812dda3bb3171543e44/uv-0.9.30-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:6ec38ae29aa83a37c6e50331707eac8ecc90cf2b356d60ea6382a94de14973be", size = 21050392, upload-time = "2026-02-04T21:45:55.649Z" }, - { url = "https://files.pythonhosted.org/packages/42/5f/3ccc9415ef62969ed01829572338ea7bdf4c5cf1ffb9edc1f8cb91b571f3/uv-0.9.30-py3-none-macosx_11_0_arm64.whl", hash = "sha256:777ecd117cf1d8d6bb07de8c9b7f6c5f3e802415b926cf059d3423699732eb8c", size = 19817085, upload-time = "2026-02-04T21:45:40.881Z" }, - { url = "https://files.pythonhosted.org/packages/8b/3f/76b44e2a224f4c4a8816fc92686ef6d4c2656bc5fc9d4f673816162c994d/uv-0.9.30-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:93049ba3c41fa2cc38b467cb78ef61b2ddedca34b6be924a5481d7750c8111c6", size = 21620537, upload-time = "2026-02-04T21:45:47.846Z" }, - { url = "https://files.pythonhosted.org/packages/60/2a/50f7e8c6d532af8dd327f77bdc75ce4652322ac34f5e29f79a8e04ea3cc8/uv-0.9.30-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.musllinux_1_1_armv7l.whl", hash = "sha256:f295604fee71224ebe2685a0f1f4ff7a45c77211a60bd57133a4a02056d7c775", size = 21550855, upload-time = "2026-02-04T21:46:26.269Z" }, - { url = "https://files.pythonhosted.org/packages/0e/10/f823d4af1125fae559194b356757dc7d4a8ac79d10d11db32c2d4c9e2f63/uv-0.9.30-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2faf84e1f3b6fc347a34c07f1291d11acf000b0dd537a61d541020f22b17ccd9", size = 21516576, upload-time = "2026-02-04T21:46:03.494Z" }, - { url = "https://files.pythonhosted.org/packages/91/f3/64b02db11f38226ed34458c7fbdb6f16b6d4fd951de24c3e51acf02b30f8/uv-0.9.30-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0b3b3700ecf64a09a07fd04d10ec35f0973ec15595d38bbafaa0318252f7e31f", size = 22718097, upload-time = "2026-02-04T21:45:51.875Z" }, - { url = "https://files.pythonhosted.org/packages/28/21/a48d1872260f04a68bb5177b0f62ddef62ab892d544ed1922f2d19fd2b00/uv-0.9.30-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:b176fc2937937dd81820445cb7e7e2e3cd1009a003c512f55fa0ae10064c8a38", size = 24107844, upload-time = "2026-02-04T21:46:19.032Z" }, - { url = "https://files.pythonhosted.org/packages/1c/c6/d7e5559bfe1ab7a215a7ad49c58c8a5701728f2473f7f436ef00b4664e88/uv-0.9.30-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:180e8070b8c438b9a3fb3fde8a37b365f85c3c06e17090f555dc68fdebd73333", size = 23685378, upload-time = "2026-02-04T21:46:07.166Z" }, - { url = "https://files.pythonhosted.org/packages/a8/bf/b937bbd50d14c6286e353fd4c7bdc09b75f6b3a26bd4e2f3357e99891f28/uv-0.9.30-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4125a9aa2a751e1589728f6365cfe204d1be41499148ead44b6180b7df576f27", size = 22848471, upload-time = "2026-02-04T21:45:18.728Z" }, - { url = "https://files.pythonhosted.org/packages/6a/57/12a67c569e69b71508ad669adad266221f0b1d374be88eaf60109f551354/uv-0.9.30-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4366dd740ac9ad3ec50a58868a955b032493bb7d7e6ed368289e6ced8bbc70f3", size = 22774258, upload-time = "2026-02-04T21:46:10.798Z" }, - { url = "https://files.pythonhosted.org/packages/3d/b8/a26cc64685dddb9fb13f14c3dc1b12009f800083405f854f84eb8c86b494/uv-0.9.30-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:33e50f208e01a0c20b3c5f87d453356a5cbcfd68f19e47a28b274cd45618881c", size = 21699573, upload-time = "2026-02-04T21:45:44.365Z" }, - { url = "https://files.pythonhosted.org/packages/c8/59/995af0c5f0740f8acb30468e720269e720352df1d204e82c2d52d9a8c586/uv-0.9.30-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:5e7a6fa7a3549ce893cf91fe4b06629e3e594fc1dca0a6050aba2ea08722e964", size = 22460799, upload-time = "2026-02-04T21:45:26.658Z" }, - { url = "https://files.pythonhosted.org/packages/bb/0b/6affe815ecbaebf38b35d6230fbed2f44708c67d5dd5720f81f2ec8f96ff/uv-0.9.30-py3-none-musllinux_1_1_i686.whl", hash = "sha256:62d7e408d41e392b55ffa4cf9b07f7bbd8b04e0929258a42e19716c221ac0590", size = 22001777, upload-time = "2026-02-04T21:45:34.656Z" }, - { url = "https://files.pythonhosted.org/packages/f3/b6/47a515171c891b0d29f8e90c8a1c0e233e4813c95a011799605cfe04c74c/uv-0.9.30-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:6dc65c24f5b9cdc78300fa6631368d3106e260bbffa66fb1e831a318374da2df", size = 22968416, upload-time = "2026-02-04T21:45:22.863Z" }, - { url = "https://files.pythonhosted.org/packages/3d/3a/c1df8615385138bb7c43342586431ca32b77466c5fb086ac0ed14ab6ca28/uv-0.9.30-py3-none-win32.whl", hash = "sha256:74e94c65d578657db94a753d41763d0364e5468ec0d368fb9ac8ddab0fb6e21f", size = 20889232, upload-time = "2026-02-04T21:46:22.617Z" }, - { url = "https://files.pythonhosted.org/packages/f2/a8/e8761c8414a880d70223723946576069e042765475f73b4436d78b865dba/uv-0.9.30-py3-none-win_amd64.whl", hash = "sha256:88a2190810684830a1ba4bb1cf8fb06b0308988a1589559404259d295260891c", size = 23432208, upload-time = "2026-02-04T21:45:30.85Z" }, - { url = "https://files.pythonhosted.org/packages/49/e8/6f2ebab941ec559f97110bbbae1279cd0333d6bc352b55f6fa3fefb020d9/uv-0.9.30-py3-none-win_arm64.whl", hash = "sha256:7fde83a5b5ea027315223c33c30a1ab2f2186910b933d091a1b7652da879e230", size = 21887273, upload-time = "2026-02-04T21:45:59.787Z" }, +version = "0.10.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/09/36/f7fe4de0ad81234ac43938fe39c6ba84595c6b3a1868d786a4d7ad19e670/uv-0.10.0.tar.gz", hash = "sha256:ad01dd614a4bb8eb732da31ade41447026427397c5ad171cc98bd59579ef57ea", size = 3854103, upload-time = "2026-02-05T20:57:55.248Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f4/69/33fb64aee6ba138b1aaf957e20778e94a8c23732e41cdf68e6176aa2cf4e/uv-0.10.0-py3-none-linux_armv6l.whl", hash = "sha256:38dc0ccbda6377eb94095688c38e5001b8b40dfce14b9654949c1f0b6aa889df", size = 21984662, upload-time = "2026-02-05T20:57:19.076Z" }, + { url = "https://files.pythonhosted.org/packages/1a/5a/e3ff8a98cfbabc5c2d09bf304d2d9d2d7b2e7d60744241ac5ed762015e5c/uv-0.10.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:a165582c1447691109d49d09dccb065d2a23852ff42bf77824ff169909aa85da", size = 21057249, upload-time = "2026-02-05T20:56:48.921Z" }, + { url = "https://files.pythonhosted.org/packages/ee/77/ec8f24f8d0f19c4fda0718d917bb78b9e6f02a4e1963b401f1c4f4614a54/uv-0.10.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:aefea608971f4f23ac3dac2006afb8eb2b2c1a2514f5fee1fac18e6c45fd70c4", size = 19827174, upload-time = "2026-02-05T20:57:10.581Z" }, + { url = "https://files.pythonhosted.org/packages/c6/7e/09b38b93208906728f591f66185a425be3acdb97c448460137d0e6ecb30a/uv-0.10.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:d4b621bcc5d0139502789dc299bae8bf55356d07b95cb4e57e50e2afcc5f43e1", size = 21629522, upload-time = "2026-02-05T20:57:29.959Z" }, + { url = "https://files.pythonhosted.org/packages/89/f3/48d92c90e869331306979efaa29a44c3e7e8376ae343edc729df0d534dfb/uv-0.10.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.musllinux_1_1_armv7l.whl", hash = "sha256:b4bea728a6b64826d0091f95f28de06dd2dc786384b3d336a90297f123b4da0e", size = 21614812, upload-time = "2026-02-05T20:56:58.103Z" }, + { url = "https://files.pythonhosted.org/packages/ff/43/d0dedfcd4fe6e36cabdbeeb43425cd788604db9d48425e7b659d0f7ba112/uv-0.10.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bc0cc2a4bcf9efbff9a57e2aed21c2d4b5a7ec2cc0096e0c33d7b53da17f6a3b", size = 21577072, upload-time = "2026-02-05T20:57:45.455Z" }, + { url = "https://files.pythonhosted.org/packages/c5/90/b8c9320fd8d86f356e37505a02aa2978ed28f9c63b59f15933e98bce97e5/uv-0.10.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:070ca2f0e8c67ca9a8f70ce403c956b7ed9d51e0c2e9dbbcc4efa5e0a2483f79", size = 22829664, upload-time = "2026-02-05T20:57:22.689Z" }, + { url = "https://files.pythonhosted.org/packages/56/9c/2c36b30b05c74b2af0e663e0e68f1d10b91a02a145e19b6774c121120c0b/uv-0.10.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8070c66149c06f9b39092a06f593a2241345ea2b1d42badc6f884c2cc089a1b1", size = 23705815, upload-time = "2026-02-05T20:57:37.604Z" }, + { url = "https://files.pythonhosted.org/packages/6c/a1/8c7fdb14ab72e26ca872e07306e496a6b8cf42353f9bf6251b015be7f535/uv-0.10.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3db1d5390b3a624de672d7b0f9c9d8197693f3b2d3d9c4d9e34686dcbc34197a", size = 22890313, upload-time = "2026-02-05T20:57:26.35Z" }, + { url = "https://files.pythonhosted.org/packages/f3/f8/5c152350b1a6d0af019801f91a1bdeac854c33deb36275f6c934f0113cb5/uv-0.10.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82b46db718763bf742e986ebbc7a30ca33648957a0dcad34382970b992f5e900", size = 22769440, upload-time = "2026-02-05T20:56:53.859Z" }, + { url = "https://files.pythonhosted.org/packages/87/44/980e5399c6f4943b81754be9b7deb87bd56430e035c507984e17267d6a97/uv-0.10.0-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:eb95d28590edd73b8fdd80c27d699c45c52f8305170c6a90b830caf7f36670a4", size = 21695296, upload-time = "2026-02-05T20:57:06.732Z" }, + { url = "https://files.pythonhosted.org/packages/ae/e7/f44ad40275be2087b3910df4678ed62cf0c82eeb3375c4a35037a79747db/uv-0.10.0-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:5871eef5046a81df3f1636a3d2b4ccac749c23c7f4d3a4bae5496cb2876a1814", size = 22424291, upload-time = "2026-02-05T20:57:49.067Z" }, + { url = "https://files.pythonhosted.org/packages/c2/81/31c0c0a8673140756e71a1112bf8f0fcbb48a4cf4587a7937f5bd55256b6/uv-0.10.0-py3-none-musllinux_1_1_i686.whl", hash = "sha256:1af0ec125a07edb434dfaa98969f6184c1313dbec2860c3c5ce2d533b257132a", size = 22109479, upload-time = "2026-02-05T20:57:02.258Z" }, + { url = "https://files.pythonhosted.org/packages/d7/d1/2eb51bc233bad3d13ad64a0c280fd4d1ebebf5c2939b3900a46670fa2b91/uv-0.10.0-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:45909b9a734250da05b10101e0a067e01ffa2d94bbb07de4b501e3cee4ae0ff3", size = 22972087, upload-time = "2026-02-05T20:57:52.847Z" }, + { url = "https://files.pythonhosted.org/packages/d2/f7/49987207b87b5c21e1f0e81c52892813e8cdf7e318b6373d6585773ebcdd/uv-0.10.0-py3-none-win32.whl", hash = "sha256:d5498851b1f07aa9c9af75578b2029a11743cb933d741f84dcbb43109a968c29", size = 20896746, upload-time = "2026-02-05T20:57:33.426Z" }, + { url = "https://files.pythonhosted.org/packages/80/b2/1370049596c6ff7fa1fe22fccf86a093982eac81017b8c8aff541d7263b2/uv-0.10.0-py3-none-win_amd64.whl", hash = "sha256:edd469425cd62bcd8c8cc0226c5f9043a94e37ed869da8268c80fdbfd3e5015e", size = 23433041, upload-time = "2026-02-05T20:57:41.41Z" }, + { url = "https://files.pythonhosted.org/packages/e3/76/1034c46244feafec2c274ac52b094f35d47c94cdb11461c24cf4be8a0c0c/uv-0.10.0-py3-none-win_arm64.whl", hash = "sha256:e90c509749b3422eebb54057434b7119892330d133b9690a88f8a6b0f3116be3", size = 21880261, upload-time = "2026-02-05T20:57:14.724Z" }, ] [[package]]