From a43c1cbcdde9c92a49b380c4230bb06c29c597aa Mon Sep 17 00:00:00 2001 From: Andrew Ginns Date: Wed, 4 Jun 2025 14:53:55 +0000 Subject: [PATCH 01/14] chore: Bump model versions --- agents_mcp_usage/multi_mcp/multi_mcp_use/adk_mcp.py | 2 +- agents_mcp_usage/multi_mcp/multi_mcp_use/pydantic_mcp.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/agents_mcp_usage/multi_mcp/multi_mcp_use/adk_mcp.py b/agents_mcp_usage/multi_mcp/multi_mcp_use/adk_mcp.py index 5e56efe..4d5d3da 100644 --- a/agents_mcp_usage/multi_mcp/multi_mcp_use/adk_mcp.py +++ b/agents_mcp_usage/multi_mcp/multi_mcp_use/adk_mcp.py @@ -83,7 +83,7 @@ async def main(query: str = "Hi!", request_limit: int = 5) -> None: # Create agent with tools agent = LlmAgent( - model="gemini-2.5-pro-preview-03-25", + model="gemini-2.5-pro-preview-05-06", name="multi_mcp_adk", tools=tools, ) diff --git a/agents_mcp_usage/multi_mcp/multi_mcp_use/pydantic_mcp.py b/agents_mcp_usage/multi_mcp/multi_mcp_use/pydantic_mcp.py index 7e60749..4d3e1ff 100644 --- a/agents_mcp_usage/multi_mcp/multi_mcp_use/pydantic_mcp.py +++ b/agents_mcp_usage/multi_mcp/multi_mcp_use/pydantic_mcp.py @@ -33,7 +33,7 @@ ) # Create Agent with MCP servers agent = Agent( - "gemini-2.5-pro-preview-03-25", + "gemini-2.5-pro-preview-05-06", # "openai:o4-mini", mcp_servers=[local_server, mermaid_server], ) From b1733ff3699d909c232ec54b4113a701cc08ba2a Mon Sep 17 00:00:00 2001 From: Andrew Ginns Date: Wed, 4 Jun 2025 14:58:31 +0000 Subject: [PATCH 02/14] feat: Simplified python wrapper for MCP server over the npx mermaid validator --- mcp_servers/mermaid_validator.py | 215 +++++++------------------------ 1 file changed, 45 insertions(+), 170 deletions(-) diff --git a/mcp_servers/mermaid_validator.py b/mcp_servers/mermaid_validator.py index fcf5c13..a796983 100644 --- a/mcp_servers/mermaid_validator.py +++ b/mcp_servers/mermaid_validator.py @@ -1,79 +1,14 @@ -import json import os -import subprocess import tempfile -import argparse -import asyncio -import sys -import time +import subprocess +import base64 +import json from typing import Optional - -from loguru import logger -from mcp.server.fastmcp import FastMCP from pydantic import BaseModel, Field +from dotenv import load_dotenv +from mcp.server.fastmcp import FastMCP -# Configure loguru -logger.remove() # Remove default handler -logger.add( - sys.stderr, - format="{time:YYYY-MM-DD HH:mm:ss.SSS} | {level: <8} | {name}:{function}:{line} - {message}", - level="DEBUG", -) -logger.add( - "mermaid_validator.log", - rotation="10 MB", - retention="1 week", - level="DEBUG", -) - -# Add a file logger with more details for debugging -logger.add( - "mermaid_raw_input.log", - format="{time:YYYY-MM-DD HH:mm:ss.SSS} | {level} | {message}", - level="DEBUG", - filter=lambda record: "raw_input" in record["extra"], - rotation="10 MB", -) - -# Patch mcp.run to capture raw input -original_run = FastMCP.run - - -def patched_run(self, transport: str = "stdio", *args, **kwargs): - logger.info(f"Starting MCP server with transport: {transport}") - - # Patch stdio handling if needed - if transport == "stdio": - # Store the original stdin.read - original_stdin_read = sys.stdin.read - original_stdin_readline = sys.stdin.readline - - def patched_read(n=-1): - data = original_stdin_read(n) - timestamp = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) - logger.bind(raw_input=True).debug( - f"[STDIN READ][{timestamp}] Length: {len(data)}, Data: {data}" - ) - return data - - def patched_readline(size=-1): - data = original_stdin_readline(size) - timestamp = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) - logger.bind(raw_input=True).debug( - f"[STDIN READLINE][{timestamp}] Line: {data}" - ) - return data - - # Patch stdin.read - sys.stdin.read = patched_read - sys.stdin.readline = patched_readline - - # Call the original run method - return original_run(self, transport, *args, **kwargs) - - -# Apply the patch -FastMCP.run = patched_run +load_dotenv() mcp = FastMCP("mermaid-validator") @@ -85,14 +20,18 @@ class MermaidValidationResult(BaseModel): error_message: Optional[str] = Field( None, description="Error message if the diagram is invalid" ) + diagram_image: Optional[str] = Field( + None, description="Base64-encoded PNG image of the rendered diagram if valid" + ) @mcp.tool() async def validate_mermaid_diagram(diagram_text: str) -> MermaidValidationResult: - """Validate a mermaid diagram. + """ + Validate a mermaid diagram and render it as a PNG image if valid. - Uses mermaid-cli to validate an input string as a mermaid diagram. - Expects input to be mermaid syntax only, no wrapping code blocks or ```mermaid tags. + Uses mermaid-cli to validate and render the diagram. Requires mermaid-cli + to be installed globally via npm: npm install -g @mermaid-js/mermaid-cli Args: diagram_text: The mermaid diagram text to validate @@ -100,15 +39,8 @@ async def validate_mermaid_diagram(diagram_text: str) -> MermaidValidationResult Returns: A MermaidValidationResult object containing validation results """ - timestamp = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) - logger.info(f"MCP tool called: validate_mermaid_diagram at {timestamp}") - logger.debug(f"Input diagram text (first 100 chars): {diagram_text[:100]}...") - # Log the full diagram text to the raw input log - logger.bind(raw_input=True).debug( - f"[TOOL CALL][{timestamp}] Full diagram text: {diagram_text}" - ) - temp_file_path = None + output_file_name = None puppeteer_config_path = None try: @@ -117,7 +49,10 @@ async def validate_mermaid_diagram(diagram_text: str) -> MermaidValidationResult ) as temp_file: temp_file.write(diagram_text) temp_file_path = temp_file.name - logger.debug(f"Created temporary file: {temp_file_path}") + + output_file = tempfile.NamedTemporaryFile(suffix=".png", delete=False) + output_file.close() + output_file_name = output_file.name puppeteer_config = {"args": ["--no-sandbox", "--disable-setuid-sandbox"]} with tempfile.NamedTemporaryFile( @@ -125,10 +60,7 @@ async def validate_mermaid_diagram(diagram_text: str) -> MermaidValidationResult ) as config_file: json.dump(puppeteer_config, config_file) puppeteer_config_path = config_file.name - logger.debug(f"Created puppeteer config file: {puppeteer_config_path}") - # Run mermaid-cli to validate input string as mermaid diagram - logger.debug("Running mermaid-cli validation...") result = subprocess.run( [ "npx", @@ -136,6 +68,8 @@ async def validate_mermaid_diagram(diagram_text: str) -> MermaidValidationResult "@mermaid-js/mermaid-cli", "-i", temp_file_path, + "-o", + output_file_name, "--puppeteerConfigFile", puppeteer_config_path, ], @@ -144,113 +78,54 @@ async def validate_mermaid_diagram(diagram_text: str) -> MermaidValidationResult ) if result.returncode == 0: - logger.info("Mermaid diagram validation successful") - return MermaidValidationResult(is_valid=True, error_message=None) - else: - logger.warning(f"Mermaid diagram validation failed: {result.stderr}") - logger.bind(raw_input=True).debug( - f"[VALIDATION ERROR][{timestamp}] {result.stderr}" + with open(output_file_name, "rb") as f: + diagram_image = base64.b64encode(f.read()).decode("utf-8") + + return MermaidValidationResult( + is_valid=True, error_message=None, diagram_image=diagram_image ) + else: return MermaidValidationResult( is_valid=False, error_message=f"Mermaid diagram is invalid: {result.stderr}", + diagram_image=None, ) except Exception as e: - logger.error(f"Error validating mermaid diagram: {str(e)}") - logger.bind(raw_input=True).debug(f"[EXCEPTION][{timestamp}] {str(e)}") return MermaidValidationResult( is_valid=False, error_message=f"Error validating mermaid diagram: {str(e)}", + diagram_image=None, ) finally: - for file_path in [temp_file_path, puppeteer_config_path]: + for file_path in [temp_file_path, output_file_name, puppeteer_config_path]: if file_path and os.path.exists(file_path): try: os.unlink(file_path) - logger.debug(f"Deleted temporary file: {file_path}") - except Exception as e: - logger.error(f"Error deleting temporary file {file_path}: {e}") + except: + pass @mcp.resource("example://mermaid-diagram") def get_example_mermaid_diagram(): - """Provides an example mermaid diagram for the client. + """ + Provides an example mermaid diagram for the client. Returns: Dict containing an example mermaid diagram """ - timestamp = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) - logger.info(f"Resource called: example://mermaid-diagram at {timestamp}") - logger.bind(raw_input=True).debug( - f"[RESOURCE CALL][{timestamp}] example://mermaid-diagram" - ) - return """ - ```mermaid - graph TD - A[Start] --> B{Is it valid?} - B -->|Yes| C[Output valid result] - B -->|No| D[Output error message] - C --> E[End] - D --> E - ``` - """ - - -@mcp.prompt("validate-string-as-mermaid") -def validate_string_as_mermaid(diagram_text: str) -> MermaidValidationResult: - """Validate a string as a mermaid diagram. - - Uses mermaid-cli to validate an input string as a mermaid diagram. - Expects input to be mermaid syntax only, no wrapping code blocks or ```mermaid tags. - """ - timestamp = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) - logger.info(f"Prompt called: validate-string-as-mermaid at {timestamp}") - logger.debug(f"Original input (first 100 chars): {diagram_text[:100]}...") - logger.bind(raw_input=True).debug( - f"[PROMPT CALL][{timestamp}] Original full input: {diagram_text}" - ) - - # Strip whitespace, remove backticks and ```mermaid markers - input_str = diagram_text.strip() - - # Remove ```mermaid and ``` markers - if input_str.startswith("```mermaid"): - input_str = input_str[len("```mermaid") :].strip() - logger.debug("Removed ```mermaid prefix") - if input_str.endswith("```"): - input_str = input_str[:-3].strip() - logger.debug("Removed ``` suffix") - - # Remove any remaining backticks - input_str = input_str.replace("`", "") - - logger.debug( - f"Cleaned input for validation (first 100 chars): {input_str[:100]}..." - ) - logger.bind(raw_input=True).debug( - f"[PROMPT CALL][{timestamp}] Cleaned full input: {input_str}" - ) - - # Validate the cleaned diagram - return validate_mermaid_diagram(input_str) + return { + "diagram": """ + graph TD + A[Start] --> B{Is it valid?} + B -->|Yes| C[Output valid result] + B -->|No| D[Output error message] + C --> E[End] + D --> E + """ + } if __name__ == "__main__": - logger.info("Starting mermaid-validator MCP server") - parser = argparse.ArgumentParser(description="Mermaid diagram validator") - parser.add_argument( - "--debug", action="store_true", help="Run validation on a placeholder diagram" - ) - args = parser.parse_args() + transport = os.getenv("MCP_TRANSPORT", "stdio") - if args.debug: - # Example diagram for debugging - logger.info("Running in debug mode with example diagram") - debug_diagram = get_example_mermaid_diagram() - # Run the validation function - result = asyncio.run(validate_string_as_mermaid(debug_diagram)) - logger.info(f"Debug validation result: {result}") - print(f"Debug validation result: {result}") - else: - transport = os.getenv("MCP_TRANSPORT", "stdio") - mcp.run(transport=transport) + mcp.run(transport=transport) From 8b3e82ca3424d98091a4b118931b4927f730de5f Mon Sep 17 00:00:00 2001 From: Andrew Ginns Date: Sat, 7 Jun 2025 18:53:31 +0000 Subject: [PATCH 03/14] feat: Write results of evals to csv --- .../eval_multi_mcp/evals_pydantic_mcp.py | 270 ++++++++++++++++-- 1 file changed, 243 insertions(+), 27 deletions(-) diff --git a/agents_mcp_usage/multi_mcp/eval_multi_mcp/evals_pydantic_mcp.py b/agents_mcp_usage/multi_mcp/eval_multi_mcp/evals_pydantic_mcp.py index 079708e..0db2c81 100644 --- a/agents_mcp_usage/multi_mcp/eval_multi_mcp/evals_pydantic_mcp.py +++ b/agents_mcp_usage/multi_mcp/eval_multi_mcp/evals_pydantic_mcp.py @@ -1,11 +1,14 @@ import asyncio +import csv import os -from typing import Any +from datetime import datetime +from typing import Any, Dict, List import logfire from dotenv import load_dotenv from pydantic import BaseModel from pydantic_ai import Agent +from pydantic_ai.exceptions import UsageLimitExceeded from pydantic_ai.mcp import MCPServerStdio from pydantic_ai.usage import UsageLimits from pydantic_evals import Case, Dataset @@ -65,6 +68,9 @@ class MermaidInput(BaseModel): class MermaidOutput(BaseModel): fixed_diagram: str + failure_reason: str = "" # Add failure reason to track why a case failed + metrics: Dict[str, Any] = {} # Add metrics field to capture LLM usage metrics + tools_used: List[str] = [] # Add field to track which MCP tools were called # Custom evaluator to check if both MCP tools were used @@ -72,9 +78,41 @@ class UsedBothMCPTools(Evaluator[MermaidInput, MermaidOutput]): async def evaluate( self, ctx: EvaluatorContext[MermaidInput, MermaidOutput] ) -> float: - # In a real implementation, we would check logs to verify both servers were used - # For now, we'll assume success if we get a valid diagram output - return 1.0 if ctx.output and ctx.output.fixed_diagram else 0.0 + if not ctx.output or not ctx.output.tools_used: + return 0.0 + + # Look for tools from both MCP servers + has_example_server_tool = any( + "example" in tool.lower() or "time" in tool.lower() + for tool in ctx.output.tools_used + ) + has_mermaid_server_tool = any( + "mermaid" in tool.lower() or "validate" in tool.lower() + for tool in ctx.output.tools_used + ) + + if has_example_server_tool and has_mermaid_server_tool: + return 1.0 + elif has_example_server_tool or has_mermaid_server_tool: + return 0.5 # Partial credit for using one server + else: + return 0.0 + + +# Custom evaluator to detect usage limit failures +class UsageLimitNotExceeded(Evaluator[MermaidInput, MermaidOutput]): + async def evaluate( + self, ctx: EvaluatorContext[MermaidInput, MermaidOutput] + ) -> float: + """Check if the case failed due to usage limits being exceeded.""" + if ctx.output and ctx.output.failure_reason == "usage_limit_exceeded": + logfire.warning( + "Case failed due to usage limit exceeded", + case_name=getattr(ctx, "case_name", "unknown"), + ) + return 0.0 + # Return 1.0 if no usage limit failure occurred + return 1.0 # Custom evaluator to check if the mermaid diagram is valid @@ -82,6 +120,14 @@ class MermaidDiagramValid(Evaluator[MermaidInput, MermaidOutput]): async def evaluate( self, ctx: EvaluatorContext[MermaidInput, MermaidOutput] ) -> float: + # Skip validation if there was a failure + if ctx.output and ctx.output.failure_reason: + logfire.info( + "Skipping diagram validation due to failure", + failure_reason=ctx.output.failure_reason, + ) + return 0.0 + # Strip whitespace, remove backticks and ```mermaid markers input_str = ctx.output.fixed_diagram.strip() @@ -123,7 +169,7 @@ async def fix_mermaid_diagram( model: The model to use for the agent Returns: - MermaidOutput with the fixed diagram + MermaidOutput with the fixed diagram and captured metrics """ query = f"Add the current time and fix the mermaid diagram syntax using the validator: {inputs.invalid_diagram}. Return only the fixed mermaid diagram between backticks." @@ -131,22 +177,72 @@ async def fix_mermaid_diagram( current_agent = create_agent(model) usage_limits = UsageLimits(request_limit=5) - # Use the agent's context manager directly in this function - async with current_agent.run_mcp_servers(): - result = await current_agent.run(query, usage_limits=usage_limits) + try: + # Use the agent's context manager directly in this function + async with current_agent.run_mcp_servers(): + result = await current_agent.run(query, usage_limits=usage_limits) + + # Extract the mermaid diagram from the result output + usage = result.usage() + metrics = { + "requests": usage.requests, + "request_tokens": usage.request_tokens, + "response_tokens": usage.response_tokens, + "total_tokens": usage.total_tokens, + "details": usage.details or {}, + } + + # Extract tool usage information from agent messages + tools_used = [] + for message in result.all_messages(): + for part in message.parts: + if hasattr(part, "tool_name") and part.tool_name: + tools_used.append(part.tool_name) + + tools_used = list(dict.fromkeys(tools_used)) + + output = result.output + + # Logic to extract the diagram from between backticks + if "```" in output: + start = output.find("```") + end = output.rfind("```") + 3 + diagram = output[start:end] + else: + diagram = output - # Extract the mermaid diagram from the result output - output = result.output + return MermaidOutput( + fixed_diagram=diagram, metrics=metrics, tools_used=tools_used + ) - # Logic to extract the diagram from between backticks - if "```" in output: - start = output.find("```") - end = output.rfind("```") + 3 - diagram = output[start:end] - else: - diagram = output + except UsageLimitExceeded as e: + logfire.warning( + "Usage limit exceeded during mermaid diagram fix", + error_message=str(e), + model=model, + ) + # Return empty diagram with failure reason to indicate usage limit failure + return MermaidOutput( + fixed_diagram="", + failure_reason="usage_limit_exceeded", + metrics={}, + tools_used=[], + ) - return MermaidOutput(fixed_diagram=diagram) + except Exception as e: + logfire.error( + "Unexpected error during mermaid diagram fix", + error_message=str(e), + error_type=type(e).__name__, + model=model, + ) + # Return empty diagram with failure reason to indicate general failure + return MermaidOutput( + fixed_diagram="", + failure_reason=f"error_{type(e).__name__}", + metrics={}, + tools_used=[], + ) def create_evaluation_dataset(judge_model: str = DEFAULT_MODEL): @@ -164,34 +260,50 @@ def create_evaluation_dataset(judge_model: str = DEFAULT_MODEL): Case( name="fix_invalid_diagram_easy", inputs=MermaidInput(invalid_diagram=invalid_mermaid_diagram_easy), - expected_output=MermaidOutput(fixed_diagram=valid_mermaid_diagram), + expected_output=MermaidOutput( + fixed_diagram=valid_mermaid_diagram, + failure_reason="", + metrics={}, + tools_used=[], + ), metadata={"test_type": "mermaid_easy_fix"}, ), Case( name="fix_invalid_diagram_medium", inputs=MermaidInput(invalid_diagram=invalid_mermaid_diagram_medium), - expected_output=MermaidOutput(fixed_diagram=valid_mermaid_diagram), + expected_output=MermaidOutput( + fixed_diagram=valid_mermaid_diagram, + failure_reason="", + metrics={}, + tools_used=[], + ), metadata={"test_type": "mermaid_medium_fix"}, ), Case( name="fix_invalid_diagram_hard", inputs=MermaidInput(invalid_diagram=invalid_mermaid_diagram_hard), - expected_output=MermaidOutput(fixed_diagram=valid_mermaid_diagram), + expected_output=MermaidOutput( + fixed_diagram=valid_mermaid_diagram, + failure_reason="", + metrics={}, + tools_used=[], + ), metadata={"test_type": "mermaid_hard_fix"}, ), ], evaluators=[ UsedBothMCPTools(), + UsageLimitNotExceeded(), MermaidDiagramValid(), LLMJudge( - rubric="The response only contains a mermaid diagram inside the response JSON, no other text.", + rubric="The response only contains a mermaid diagram inside the fixed_diagram field, no other text. Ignore the metrics, failure_reason, and tools_used fields.", include_input=False, model=judge_model, ), LLMJudge( - rubric="The output diagram should maintain the same overall structure and intent as the expected output diagram while fixing any syntax errors." + rubric="The fixed_diagram field should maintain the same overall structure and intent as the expected output diagram while fixing any syntax errors." + "Check if nodes, connections, and labels are preserved." - + "The current time should be placeholder should be replace with a valid datetime", + + "The current time placeholder should be replaced with a valid datetime. Ignore the metrics, failure_reason, and tools_used fields.", include_input=False, model=judge_model, ), @@ -199,12 +311,109 @@ def create_evaluation_dataset(judge_model: str = DEFAULT_MODEL): ) -async def run_evaluations(model: str = DEFAULT_MODEL, judge_model: str = DEFAULT_MODEL): +def get_timestamp_prefix(): + """Get a timestamp prefix in the format yyyy-mm-dd_H-M-s.""" + now = datetime.now() + return now.strftime("%Y-%m-%d_%H-%M-%S") + + +def write_mermaid_results_to_csv(report, model: str, output_dir="./mermaid_results"): + """Write mermaid evaluation results with metrics to a CSV file. + + Args: + report: The evaluation report from pydantic_evals + model: The model name used for evaluation + output_dir: Directory to write the CSV file + + Returns: + Path to the created CSV file + """ + os.makedirs(output_dir, exist_ok=True) + + timestamp = get_timestamp_prefix() + filepath = os.path.join( + output_dir, f"{timestamp}_mermaid_results_{model.replace(':', '_')}.csv" + ) + + all_evaluator_names = set() + all_metric_names = set() + + for case in report.cases: + all_evaluator_names.update(case.scores.keys()) + if hasattr(case.output, "metrics") and case.output.metrics: + all_metric_names.update(case.output.metrics.keys()) + + headers = [ + "Model", + "Case", + "Duration", + "Fixed_Diagram_Length", + "Failure_Reason", + "Tools_Used", + ] + + for evaluator in sorted(all_evaluator_names): + headers.append(f"Score_{evaluator}") + + for metric in sorted(all_metric_names): + headers.append(f"Metric_{metric}") + + with open(filepath, "w", newline="", encoding="utf-8") as csvfile: + writer = csv.writer(csvfile) + writer.writerow(headers) + + for case in report.cases: + row = [ + model, + case.name, + case.task_duration, + len(case.output.fixed_diagram) + if case.output and case.output.fixed_diagram + else 0, + case.output.failure_reason if case.output else "", + "|".join(case.output.tools_used) + if case.output and case.output.tools_used + else "", + ] + + for evaluator in sorted(all_evaluator_names): + if evaluator in case.scores: + row.append(case.scores[evaluator].value) + else: + row.append("") + + for metric in sorted(all_metric_names): + if ( + case.output + and hasattr(case.output, "metrics") + and case.output.metrics + and metric in case.output.metrics + ): + metric_value = case.output.metrics[metric] + if isinstance(metric_value, dict): + row.append(str(metric_value)) + else: + row.append(metric_value) + else: + row.append("") + + writer.writerow(row) + + print(f"Mermaid evaluation results written to {filepath}") + return filepath + + +async def run_evaluations( + model: str = DEFAULT_MODEL, + judge_model: str = DEFAULT_MODEL, + export_csv: bool = True, +): """Run the evaluations on the mermaid diagram fixing task. Args: model: The model to use for the agent judge_model: The model to use for LLM judging + export_csv: Whether to export results to CSV Returns: The evaluation report @@ -222,18 +431,25 @@ async def fix_with_model(inputs: MermaidInput) -> MermaidOutput: ) report.print(include_input=False, include_output=False) + + if export_csv: + csv_path = write_mermaid_results_to_csv(report, model) + print(f"Results exported to: {csv_path}") + return report if __name__ == "__main__": # You can use different models for the agent and the judge # agent_model = os.getenv("AGENT_MODEL", DEFAULT_MODEL) - agent_model = "gemini-2.5-flash-preview-04-17" + agent_model = "gemini-2.5-pro-preview-06-05" # agent_model = "openai:o4-mini" # agent_model = "gemini-2.5-flash-preview-04-17" judge_model = os.getenv("JUDGE_MODEL", DEFAULT_MODEL) async def run_all(): - await run_evaluations(model=agent_model, judge_model=judge_model) + await run_evaluations( + model=agent_model, judge_model=judge_model, export_csv=True + ) asyncio.run(run_all()) From c621f80a6c4bb3f2fae61e1d0a154beca570d4f3 Mon Sep 17 00:00:00 2001 From: Andrew Ginns Date: Sat, 7 Jun 2025 21:42:03 +0000 Subject: [PATCH 04/14] feat: Multiple eval runs and results output --- .../multi_mcp/eval_multi_mcp/README.md | 250 +++++++++ .../eval_multi_mcp/evals_pydantic_mcp.py | 353 +++++++++++-- .../eval_multi_mcp/run_multi_evals.py | 484 ++++++++++++++++++ mcp_servers/mermaid_validator.py | 3 +- 4 files changed, 1039 insertions(+), 51 deletions(-) create mode 100644 agents_mcp_usage/multi_mcp/eval_multi_mcp/README.md create mode 100644 agents_mcp_usage/multi_mcp/eval_multi_mcp/run_multi_evals.py diff --git a/agents_mcp_usage/multi_mcp/eval_multi_mcp/README.md b/agents_mcp_usage/multi_mcp/eval_multi_mcp/README.md new file mode 100644 index 0000000..c9972dd --- /dev/null +++ b/agents_mcp_usage/multi_mcp/eval_multi_mcp/README.md @@ -0,0 +1,250 @@ +# Multi-MCP Mermaid Diagram Evaluation System + +This directory contains evaluation modules for testing LLM agents on mermaid diagram fixing tasks using multiple MCP (Model Context Protocol) servers. The system evaluates how well language models can fix invalid mermaid diagrams while utilizing multiple external tools. + +## Overview + +The evaluation system consists of two main components: + +1. **`evals_pydantic_mcp.py`** - Core evaluation module for single-model testing +2. **`run_multi_evals.py`** - Multi-model evaluation runner with parallel execution + +## Evaluation Task + +The system tests LLM agents on their ability to: +- Fix syntactically invalid mermaid diagrams +- Use both MCP servers (example server for time, mermaid validator for validation) +- Handle errors gracefully with proper categorization +- Provide meaningful failure reasons for debugging + +### Test Cases + +The evaluation includes three test cases of increasing difficulty: +1. **Easy** - Simple syntax errors in mermaid diagrams +2. **Medium** - More complex structural issues +3. **Hard** - Advanced mermaid syntax problems + +## Output Schema + +### MermaidOutput + +The main output schema captures comprehensive information about each evaluation: + +```python +class MermaidOutput(BaseModel): + fixed_diagram: str # The corrected mermaid diagram + failure_reason: str = "" # Why the case failed (if applicable) + metrics: Dict[str, Any] = {} # Usage metrics from the LLM + tools_used: List[str] = [] # Which MCP tools were called +``` + +### Metrics Captured + +The system automatically captures detailed usage metrics: + +- **`requests`** - Number of API requests made +- **`request_tokens`** - Total tokens in requests +- **`response_tokens`** - Total tokens in responses +- **`total_tokens`** - Sum of request and response tokens +- **`details`** - Additional model-specific usage details + +## Failure Reasons + +The system provides meaningful failure categorization for debugging and analysis: + +### Agent-Level Failures (from `fix_mermaid_diagram`) + +- **`usage_limit_exceeded`** - Agent hit configured usage limits +- **`response_validation_failed`** - Agent response failed Pydantic validation +- **`agent_timeout`** - Agent operation timed out +- **`http_error_{status_code}`** - HTTP errors (e.g., `http_error_502`, `http_error_503`) +- **`timeout_error`** - General timeout errors +- **`connection_error`** - Network/connection issues +- **`rate_limit_error`** - API rate limiting or quota exceeded +- **`error_{ExceptionType}`** - Other specific exceptions (fallback) + +### Evaluation-Level Failures (from `run_multi_evals`) + +- **`evaluation_timeout`** - Entire evaluation run timed out +- **`evaluation_validation_failed`** - Evaluation framework validation error +- **`model_api_error`** - Model API-specific errors +- **`network_error`** - Network-related evaluation failures +- **`evaluation_error_{ExceptionType}`** - Other evaluation framework errors + +## Evaluators + +The system uses five different evaluators to assess performance: + +### 1. UsedBothMCPTools +- **Score**: 0.0, 0.5, or 1.0 +- **Purpose**: Checks if the agent used tools from both MCP servers +- **Scoring**: + - 1.0: Used both example server and mermaid validator + - 0.5: Used only one MCP server + - 0.0: Used no MCP tools or only non-MCP tools + +### 2. UsageLimitNotExceeded +- **Score**: 0.0 or 1.0 +- **Purpose**: Detects if the case failed due to usage limits +- **Scoring**: + - 1.0: No usage limit failure + - 0.0: Failed due to `usage_limit_exceeded` + +### 3. MermaidDiagramValid +- **Score**: 0.0 or 1.0 +- **Purpose**: Validates the fixed diagram using the mermaid validator MCP server +- **Features**: + - Skips validation if there was a prior failure + - Strips markdown formatting and backticks + - Uses retry logic for transient validation errors +- **Scoring**: + - 1.0: Diagram passes mermaid syntax validation + - 0.0: Diagram is invalid or validation failed + +### 4. LLMJudge (Format Check) +- **Score**: 0.0 to 1.0 (continuous) +- **Purpose**: Evaluates if response contains only a mermaid diagram +- **Rubric**: "The response only contains a mermaid diagram inside the fixed_diagram field, no other text" + +### 5. LLMJudge (Structure Check) +- **Score**: 0.0 to 1.0 (continuous) +- **Purpose**: Evaluates if the fixed diagram maintains original structure and intent +- **Rubric**: "The fixed_diagram field should maintain the same overall structure and intent as the expected output diagram while fixing any syntax errors" + +## Retry Logic + +The system includes robust retry logic for handling transient API failures: + +### Retryable Errors +- HTTP status codes: 429, 500, 502, 503, 504 +- Connection errors and network issues +- General `OSError` exceptions + +### Retry Configuration +- **Max attempts**: 3 +- **Base delay**: 1 second +- **Exponential backoff**: 1s → 2s → 4s +- **Max delay**: 30 seconds +- **Jitter**: ±50% randomization to prevent thundering herd + +### Non-Retryable Errors +- HTTP 4xx errors (except 429) +- Validation errors +- Authentication errors + +## CSV Output Format + +Results are exported to CSV files with the following columns: + +### Basic Information +- **Model** - LLM model used +- **Run** - Run number (for multi-run evaluations) +- **Case** - Test case name (easy/medium/hard) +- **Duration** - Task execution time in seconds +- **Fixed_Diagram_Length** - Length of the output diagram +- **Failure_Reason** - Categorized failure reason (if any) +- **Tools_Used** - Pipe-separated list of MCP tools used + +### Evaluator Scores +- **Score_UsedBothMCPTools** - MCP tool usage score +- **Score_UsageLimitNotExceeded** - Usage limit check score +- **Score_MermaidDiagramValid** - Diagram validity score +- **Score_LLMJudge** - Format evaluation scores (2 columns) + +### Metrics +- **Metric_requests** - Number of API requests +- **Metric_request_tokens** - Input token count +- **Metric_response_tokens** - Output token count +- **Metric_total_tokens** - Total token usage +- **Metric_details** - Additional usage details + +## Usage + +### Single Model Evaluation + +```bash +# Run evaluation with default model +uv run agents_mcp_usage/multi_mcp/eval_multi_mcp/evals_pydantic_mcp.py + +# Customize model and judge +AGENT_MODEL="gemini-2.5-pro" JUDGE_MODEL="gemini-2.0-flash" \ +uv run agents_mcp_usage/multi_mcp/eval_multi_mcp/evals_pydantic_mcp.py +``` + +### Multi-Model Evaluation + +```bash +# Run evaluation across multiple models +uv run agents_mcp_usage/multi_mcp/eval_multi_mcp/run_multi_evals.py \ + --models "gemini-2.5-pro,gemini-2.0-flash" \ + --runs 5 \ + --parallel \ + --timeout 600 \ + --output-dir ./results + +# Sequential execution with custom judge +uv run agents_mcp_usage/multi_mcp/eval_multi_mcp/run_multi_evals.py \ + --models "gemini-2.5-pro,claude-3-opus" \ + --runs 3 \ + --sequential \ + --judge-model "gemini-2.5-pro" \ + --output-dir ./eval_results +``` + +### Available Options + +- **`--models`** - Comma-separated list of models to evaluate +- **`--runs`** - Number of evaluation runs per model (default: 3) +- **`--judge-model`** - Model for LLM judging (default: gemini-2.5-pro-preview-03-25) +- **`--parallel`** - Run evaluations in parallel (default: true) +- **`--sequential`** - Force sequential execution +- **`--timeout`** - Timeout in seconds per evaluation run (default: 600) +- **`--output-dir`** - Directory to save results (default: ./mermaid_eval_results) + +## MCP Servers + +The evaluation uses two MCP servers: + +1. **Example Server** (`mcp_servers/example_server.py`) + - Provides time-related tools + - Used to add timestamps to diagrams + +2. **Mermaid Validator** (`mcp_servers/mermaid_validator.py`) + - Validates mermaid diagram syntax + - Returns validation results with error details + +## Output Files + +### Single Model +- `YYYY-MM-DD_HH-MM-SS_mermaid_results_{model}.csv` + +### Multi-Model +- `YYYY-MM-DD_HH-MM-SS_individual_{model}.csv` - Per-model results +- `YYYY-MM-DD_HH-MM-SS_combined_results.csv` - All models combined + +## Logging and Monitoring + +The system integrates with Logfire for comprehensive monitoring: + +- **Agent operations** - MCP server interactions, tool usage +- **Retry attempts** - Failure reasons, backoff delays +- **Evaluation progress** - Success rates, timing metrics +- **Error categorization** - Detailed failure analysis + +## Error Handling Best Practices + +The system implements robust error handling: + +1. **Graceful degradation** - Partial results rather than complete failure +2. **Meaningful categorization** - Specific failure reasons for debugging +3. **Retry logic** - Automatic recovery from transient issues +4. **Comprehensive logging** - Full context for error analysis +5. **Resource cleanup** - Proper MCP server lifecycle management + +## Dependencies + +- **pydantic-ai** - Core agent framework with MCP support +- **pydantic-evals** - Evaluation framework and metrics +- **logfire** - Logging and monitoring +- **rich** - Console output and progress bars +- **asyncio** - Asynchronous evaluation execution \ No newline at end of file diff --git a/agents_mcp_usage/multi_mcp/eval_multi_mcp/evals_pydantic_mcp.py b/agents_mcp_usage/multi_mcp/eval_multi_mcp/evals_pydantic_mcp.py index 0db2c81..e71ddf5 100644 --- a/agents_mcp_usage/multi_mcp/eval_multi_mcp/evals_pydantic_mcp.py +++ b/agents_mcp_usage/multi_mcp/eval_multi_mcp/evals_pydantic_mcp.py @@ -1,18 +1,36 @@ +#!/usr/bin/env python3 +""" +Single-Model Evaluation Module for Mermaid Diagram Fixing + +This module provides the core functionality for evaluating LLM models +on mermaid diagram fixing tasks using multiple MCP servers. It includes: +- Schema definitions for inputs and outputs +- Custom evaluators for multi-MCP tool usage validation +- Agent creation and mermaid diagram fixing functions +- Dataset creation and evaluation utilities +- CSV export functionality +- Robust retry logic for handling transient API failures + +This module is designed to be imported by multi-model evaluation scripts. +""" + import asyncio import csv import os +import random from datetime import datetime -from typing import Any, Dict, List +from typing import Any, Dict, List, Optional import logfire from dotenv import load_dotenv -from pydantic import BaseModel +from pydantic import BaseModel, ValidationError from pydantic_ai import Agent -from pydantic_ai.exceptions import UsageLimitExceeded +from pydantic_ai.exceptions import UsageLimitExceeded, ModelHTTPError from pydantic_ai.mcp import MCPServerStdio from pydantic_ai.usage import UsageLimits from pydantic_evals import Case, Dataset from pydantic_evals.evaluators import Evaluator, EvaluatorContext, LLMJudge +from pydantic_evals.reporting import EvaluationReport from agents_mcp_usage.multi_mcp.mermaid_diagrams import ( invalid_mermaid_diagram_easy, @@ -31,50 +49,185 @@ logfire.instrument_mcp() logfire.instrument_pydantic_ai() -# Default model to use +# Default model configurations DEFAULT_MODEL = "gemini-2.5-pro-preview-05-06" +DEFAULT_MODELS = [ + "gemini-2.5-pro-preview-06-05", + "gemini-2.0-flash", + "gemini-2.5-flash-preview-04-17", +] + +# Retry configuration +RETRYABLE_HTTP_STATUS_CODES = {429, 500, 502, 503, 504} +MAX_RETRY_ATTEMPTS = 3 +BASE_RETRY_DELAY = 1.0 # seconds +MAX_RETRY_DELAY = 30.0 # seconds + +# ============================================================================ +# Retry Utilities +# ============================================================================ + + +def is_retryable_error(exception: Exception) -> bool: + """Check if an exception should be retried.""" + if isinstance(exception, ModelHTTPError): + return exception.status_code in RETRYABLE_HTTP_STATUS_CODES + + # Also retry on general connection errors that might be transient + if isinstance(exception, (ConnectionError, OSError)): + return True + + return False + + +async def exponential_backoff_retry( + func_call, + max_attempts: int = MAX_RETRY_ATTEMPTS, + base_delay: float = BASE_RETRY_DELAY, + max_delay: float = MAX_RETRY_DELAY, + jitter: bool = True, +) -> Any: + """ + Execute a function with exponential backoff retry logic. -# Configure MCP servers -local_server = MCPServerStdio( - command="uv", - args=[ - "run", - "mcp_servers/example_server.py", - "stdio", - ], -) -mermaid_server = MCPServerStdio( - command="uv", - args=[ - "run", - "mcp_servers/mermaid_validator.py", - ], -) + Args: + func_call: Async function to retry + max_attempts: Maximum number of retry attempts + base_delay: Base delay between retries in seconds + max_delay: Maximum delay between retries in seconds + jitter: Whether to add random jitter to delays + + Returns: + Result of the function call + + Raises: + The last exception if all retries are exhausted + """ + last_exception = None + + for attempt in range(max_attempts): + try: + return await func_call() + except Exception as e: + last_exception = e + + if not is_retryable_error(e): + logfire.warning( + "Non-retryable error encountered", + error_type=type(e).__name__, + error_message=str(e), + attempt=attempt + 1, + ) + raise + + if attempt == max_attempts - 1: + logfire.error( + "Max retry attempts exhausted", + error_type=type(e).__name__, + error_message=str(e), + attempts=max_attempts, + ) + break + + # Calculate delay with exponential backoff + delay = min(base_delay * (2**attempt), max_delay) + if jitter: + delay = delay * (0.5 + random.random() * 0.5) # Add 50% jitter + + logfire.warning( + "Retryable error encountered, retrying", + error_type=type(e).__name__, + error_message=str(e), + attempt=attempt + 1, + max_attempts=max_attempts, + delay_seconds=delay, + ) + + await asyncio.sleep(delay) + + # Re-raise the last exception if all retries failed + if last_exception: + raise last_exception + + # This should never be reached, but just in case + raise RuntimeError("Unexpected error in retry logic") + + +# ============================================================================ +# MCP Server Configuration +# ============================================================================ -# Create Agent with MCP servers -def create_agent(model: str = DEFAULT_MODEL, model_settings: dict[str, Any] = {}): +def get_mcp_servers() -> List[MCPServerStdio]: + """Get the configured MCP servers for the evaluation.""" + local_server = MCPServerStdio( + command="uv", + args=[ + "run", + "mcp_servers/example_server.py", + "stdio", + ], + ) + mermaid_server = MCPServerStdio( + command="uv", + args=[ + "run", + "mcp_servers/mermaid_validator.py", + ], + ) + return [local_server, mermaid_server] + + +def create_agent( + model: str = DEFAULT_MODEL, model_settings: Dict[str, Any] = None +) -> Agent: + """Create an agent with MCP servers for the specified model. + + Args: + model: The model to use for the agent + model_settings: Optional model-specific settings + + Returns: + Configured Agent instance + """ + if model_settings is None: + model_settings = {} + return Agent( model, - mcp_servers=[local_server, mermaid_server], + mcp_servers=get_mcp_servers(), model_settings=model_settings, ) -# Define input and output schema for evaluations +# ============================================================================ +# Schema Definitions +# ============================================================================ + + class MermaidInput(BaseModel): + """Input schema for mermaid diagram fixing.""" + invalid_diagram: str class MermaidOutput(BaseModel): + """Output schema for mermaid diagram fixing with comprehensive metrics.""" + fixed_diagram: str - failure_reason: str = "" # Add failure reason to track why a case failed - metrics: Dict[str, Any] = {} # Add metrics field to capture LLM usage metrics - tools_used: List[str] = [] # Add field to track which MCP tools were called + failure_reason: str = "" # Track why a case failed + metrics: Dict[str, Any] = {} # Capture LLM usage metrics + tools_used: List[str] = [] # Track which MCP tools were called + + +# ============================================================================ +# Custom Evaluators +# ============================================================================ -# Custom evaluator to check if both MCP tools were used class UsedBothMCPTools(Evaluator[MermaidInput, MermaidOutput]): + """Evaluator to check if both MCP tools were used.""" + async def evaluate( self, ctx: EvaluatorContext[MermaidInput, MermaidOutput] ) -> float: @@ -99,8 +252,9 @@ async def evaluate( return 0.0 -# Custom evaluator to detect usage limit failures class UsageLimitNotExceeded(Evaluator[MermaidInput, MermaidOutput]): + """Evaluator to detect usage limit failures.""" + async def evaluate( self, ctx: EvaluatorContext[MermaidInput, MermaidOutput] ) -> float: @@ -115,8 +269,9 @@ async def evaluate( return 1.0 -# Custom evaluator to check if the mermaid diagram is valid class MermaidDiagramValid(Evaluator[MermaidInput, MermaidOutput]): + """Evaluator to check if the mermaid diagram is valid.""" + async def evaluate( self, ctx: EvaluatorContext[MermaidInput, MermaidOutput] ) -> float: @@ -146,8 +301,18 @@ async def evaluate( diagram_preview=input_str[:100], ) - # Use the MCP server's validation function - result = await validate_mermaid_diagram(input_str) + # Use the MCP server's validation function with retry logic + try: + result = await exponential_backoff_retry( + lambda: validate_mermaid_diagram(input_str) + ) + except Exception as e: + logfire.error( + "Failed to validate mermaid diagram after retries", + error_type=type(e).__name__, + error_message=str(e), + ) + return 0.0 if result.is_valid: logfire.info("Mermaid diagram validation succeeded") @@ -159,6 +324,11 @@ async def evaluate( return 1.0 if result.is_valid else 0.0 +# ============================================================================ +# Core Evaluation Functions +# ============================================================================ + + async def fix_mermaid_diagram( inputs: MermaidInput, model: str = DEFAULT_MODEL ) -> MermaidOutput: @@ -177,12 +347,15 @@ async def fix_mermaid_diagram( current_agent = create_agent(model) usage_limits = UsageLimits(request_limit=5) - try: - # Use the agent's context manager directly in this function + async def _run_agent(): async with current_agent.run_mcp_servers(): - result = await current_agent.run(query, usage_limits=usage_limits) + return await current_agent.run(query, usage_limits=usage_limits) + + try: + # Use retry logic for the agent run + result = await exponential_backoff_retry(_run_agent) - # Extract the mermaid diagram from the result output + # Extract usage metrics usage = result.usage() metrics = { "requests": usage.requests, @@ -199,11 +372,12 @@ async def fix_mermaid_diagram( if hasattr(part, "tool_name") and part.tool_name: tools_used.append(part.tool_name) - tools_used = list(dict.fromkeys(tools_used)) - + tools_used = list( + dict.fromkeys(tools_used) + ) # Remove duplicates while preserving order output = result.output - # Logic to extract the diagram from between backticks + # Extract the diagram from between backticks if "```" in output: start = output.find("```") end = output.rfind("```") + 3 @@ -229,23 +403,81 @@ async def fix_mermaid_diagram( tools_used=[], ) + except ModelHTTPError as e: + logfire.error( + "HTTP error during mermaid diagram fix after retries", + error_message=str(e), + status_code=e.status_code, + model_name=e.model_name, + model=model, + ) + # Return empty diagram with failure reason to indicate HTTP error + return MermaidOutput( + fixed_diagram="", + failure_reason=f"http_error_{e.status_code}", + metrics={}, + tools_used=[], + ) + + except ValidationError as e: + logfire.error( + "Response validation error during mermaid diagram fix", + error_message=str(e), + model=model, + ) + # Return empty diagram with failure reason to indicate validation failure + return MermaidOutput( + fixed_diagram="", + failure_reason="response_validation_failed", + metrics={}, + tools_used=[], + ) + + except asyncio.TimeoutError as e: + logfire.error( + "Timeout error during mermaid diagram fix", + error_message=str(e), + model=model, + ) + # Return empty diagram with failure reason to indicate timeout + return MermaidOutput( + fixed_diagram="", + failure_reason="agent_timeout", + metrics={}, + tools_used=[], + ) + except Exception as e: + # Provide more specific error categorization + error_type = type(e).__name__ + if "timeout" in str(e).lower() or "timed out" in str(e).lower(): + failure_reason = "timeout_error" + elif "connection" in str(e).lower() or "network" in str(e).lower(): + failure_reason = "connection_error" + elif "rate limit" in str(e).lower() or "quota" in str(e).lower(): + failure_reason = "rate_limit_error" + else: + failure_reason = f"error_{error_type}" + logfire.error( - "Unexpected error during mermaid diagram fix", + "Unexpected error during mermaid diagram fix after retries", error_message=str(e), - error_type=type(e).__name__, + error_type=error_type, + categorized_failure_reason=failure_reason, model=model, ) # Return empty diagram with failure reason to indicate general failure return MermaidOutput( fixed_diagram="", - failure_reason=f"error_{type(e).__name__}", + failure_reason=failure_reason, metrics={}, tools_used=[], ) -def create_evaluation_dataset(judge_model: str = DEFAULT_MODEL): +def create_evaluation_dataset( + judge_model: str = DEFAULT_MODEL, +) -> Dataset[MermaidInput, MermaidOutput, Any]: """Create the dataset for evaluating mermaid diagram fixing. Args: @@ -301,9 +533,7 @@ def create_evaluation_dataset(judge_model: str = DEFAULT_MODEL): model=judge_model, ), LLMJudge( - rubric="The fixed_diagram field should maintain the same overall structure and intent as the expected output diagram while fixing any syntax errors." - + "Check if nodes, connections, and labels are preserved." - + "The current time placeholder should be replaced with a valid datetime. Ignore the metrics, failure_reason, and tools_used fields.", + rubric="The fixed_diagram field should maintain the same overall structure and intent as the expected output diagram while fixing any syntax errors. Check if nodes, connections, and labels are preserved. The current time placeholder should be replaced with a valid datetime. Ignore the metrics, failure_reason, and tools_used fields.", include_input=False, model=judge_model, ), @@ -311,13 +541,20 @@ def create_evaluation_dataset(judge_model: str = DEFAULT_MODEL): ) -def get_timestamp_prefix(): +# ============================================================================ +# Utility Functions +# ============================================================================ + + +def get_timestamp_prefix() -> str: """Get a timestamp prefix in the format yyyy-mm-dd_H-M-s.""" now = datetime.now() return now.strftime("%Y-%m-%d_%H-%M-%S") -def write_mermaid_results_to_csv(report, model: str, output_dir="./mermaid_results"): +def write_mermaid_results_to_csv( + report: EvaluationReport, model: str, output_dir: str = "./mermaid_results" +) -> str: """Write mermaid evaluation results with metrics to a CSV file. Args: @@ -335,6 +572,7 @@ def write_mermaid_results_to_csv(report, model: str, output_dir="./mermaid_resul output_dir, f"{timestamp}_mermaid_results_{model.replace(':', '_')}.csv" ) + # Collect all unique evaluator and metric names all_evaluator_names = set() all_metric_names = set() @@ -343,6 +581,7 @@ def write_mermaid_results_to_csv(report, model: str, output_dir="./mermaid_resul if hasattr(case.output, "metrics") and case.output.metrics: all_metric_names.update(case.output.metrics.keys()) + # Build CSV headers headers = [ "Model", "Case", @@ -358,6 +597,7 @@ def write_mermaid_results_to_csv(report, model: str, output_dir="./mermaid_resul for metric in sorted(all_metric_names): headers.append(f"Metric_{metric}") + # Write the CSV file with open(filepath, "w", newline="", encoding="utf-8") as csvfile: writer = csv.writer(csvfile) writer.writerow(headers) @@ -376,12 +616,14 @@ def write_mermaid_results_to_csv(report, model: str, output_dir="./mermaid_resul else "", ] + # Add evaluator scores for evaluator in sorted(all_evaluator_names): if evaluator in case.scores: row.append(case.scores[evaluator].value) else: row.append("") + # Add metrics for metric in sorted(all_metric_names): if ( case.output @@ -403,17 +645,24 @@ def write_mermaid_results_to_csv(report, model: str, output_dir="./mermaid_resul return filepath +# ============================================================================ +# Single Model Evaluation Function +# ============================================================================ + + async def run_evaluations( model: str = DEFAULT_MODEL, judge_model: str = DEFAULT_MODEL, export_csv: bool = True, -): + output_dir: str = "./mermaid_results", +) -> EvaluationReport: """Run the evaluations on the mermaid diagram fixing task. Args: model: The model to use for the agent judge_model: The model to use for LLM judging export_csv: Whether to export results to CSV + output_dir: Directory to save results Returns: The evaluation report @@ -433,12 +682,16 @@ async def fix_with_model(inputs: MermaidInput) -> MermaidOutput: report.print(include_input=False, include_output=False) if export_csv: - csv_path = write_mermaid_results_to_csv(report, model) + csv_path = write_mermaid_results_to_csv(report, model, output_dir) print(f"Results exported to: {csv_path}") return report +# ============================================================================ +# Main Execution (for standalone use) +# ============================================================================ + if __name__ == "__main__": # You can use different models for the agent and the judge # agent_model = os.getenv("AGENT_MODEL", DEFAULT_MODEL) diff --git a/agents_mcp_usage/multi_mcp/eval_multi_mcp/run_multi_evals.py b/agents_mcp_usage/multi_mcp/eval_multi_mcp/run_multi_evals.py new file mode 100644 index 0000000..94f2a38 --- /dev/null +++ b/agents_mcp_usage/multi_mcp/eval_multi_mcp/run_multi_evals.py @@ -0,0 +1,484 @@ +#!/usr/bin/env python3 +""" +Multi-Model Evaluation Script for Mermaid Diagram Fixing + +This script extends single-model evaluation to handle multiple LLM models with: +- Robust failure handling and recovery +- Individual model results written to disk before combining +- Configurable number of runs per model +- Combined metrics output to single CSV file +- Handling of variable response metrics from different models +- Parallel and sequential execution modes +""" + +import argparse +import asyncio +import csv +import os +import statistics +import sys +from datetime import datetime +from pathlib import Path +from typing import Any, Dict, List, Optional, Set + +import logfire +from dotenv import load_dotenv +from pydantic_evals.reporting import EvaluationReport +from rich.console import Console +from rich.progress import ( + Progress, + SpinnerColumn, + TextColumn, + BarColumn, + TaskProgressColumn, +) +from rich.table import Table + +# Import shared functionality from the improved evals module +from agents_mcp_usage.multi_mcp.eval_multi_mcp.evals_pydantic_mcp import ( + DEFAULT_MODELS, + MermaidInput, + MermaidOutput, + UsedBothMCPTools, + UsageLimitNotExceeded, + MermaidDiagramValid, + fix_mermaid_diagram, + create_evaluation_dataset, + get_timestamp_prefix, +) + +load_dotenv() + +logfire.configure( + send_to_logfire="if-token-present", service_name="multi-model-mermaid-evals" +) +logfire.instrument_mcp() +logfire.instrument_pydantic_ai() + + +class ModelEvaluationResults: + """Container for storing and managing evaluation results for a single model.""" + + def __init__(self, model: str): + self.model = model + self.reports: List[EvaluationReport] = [] + self.failed_runs: List[Dict[str, Any]] = [] + + def add_successful_run(self, report: EvaluationReport): + """Add a successful evaluation report.""" + self.reports.append(report) + + def add_failed_run(self, run_index: int, error: str): + """Add information about a failed run.""" + self.failed_runs.append( + { + "run_index": run_index, + "error": error, + "timestamp": datetime.now().isoformat(), + } + ) + + def get_success_rate(self) -> float: + """Calculate the success rate for this model.""" + total_runs = len(self.reports) + len(self.failed_runs) + if total_runs == 0: + return 0.0 + return len(self.reports) / total_runs + + def write_individual_results(self, output_dir: str) -> Optional[str]: + """Write individual model results to CSV file.""" + if not self.reports: + return None + + os.makedirs(output_dir, exist_ok=True) + timestamp = get_timestamp_prefix() + filepath = os.path.join( + output_dir, f"{timestamp}_individual_{self.model.replace(':', '_')}.csv" + ) + + all_evaluator_names = set() + all_metric_names = set() + + for report in self.reports: + for case in report.cases: + all_evaluator_names.update(case.scores.keys()) + if hasattr(case.output, "metrics") and case.output.metrics: + all_metric_names.update(case.output.metrics.keys()) + + headers = [ + "Model", + "Run", + "Case", + "Duration", + "Fixed_Diagram_Length", + "Failure_Reason", + "Tools_Used", + ] + + for evaluator in sorted(all_evaluator_names): + headers.append(f"Score_{evaluator}") + + for metric in sorted(all_metric_names): + headers.append(f"Metric_{metric}") + + with open(filepath, "w", newline="", encoding="utf-8") as csvfile: + writer = csv.writer(csvfile) + writer.writerow(headers) + + for run_idx, report in enumerate(self.reports): + for case in report.cases: + row = [ + self.model, + run_idx + 1, + case.name, + case.task_duration, + len(case.output.fixed_diagram) + if case.output and case.output.fixed_diagram + else 0, + case.output.failure_reason if case.output else "", + "|".join(case.output.tools_used) + if case.output and case.output.tools_used + else "", + ] + + for evaluator in sorted(all_evaluator_names): + if evaluator in case.scores: + row.append(case.scores[evaluator].value) + else: + row.append("") + + for metric in sorted(all_metric_names): + if ( + case.output + and hasattr(case.output, "metrics") + and case.output.metrics + and metric in case.output.metrics + ): + metric_value = case.output.metrics[metric] + if isinstance(metric_value, dict): + row.append(str(metric_value)) + else: + row.append(metric_value) + else: + row.append("") + + writer.writerow(row) + + return filepath + + +class MultiModelEvaluator: + """Main class for running evaluations across multiple models.""" + + def __init__( + self, models: List[str], judge_model: str, output_dir: str = "./results" + ): + self.models = models + self.judge_model = judge_model + self.output_dir = output_dir + self.console = Console() + self.results: Dict[str, ModelEvaluationResults] = {} + + for model in models: + self.results[model] = ModelEvaluationResults(model) + + async def run_single_evaluation( + self, model: str, run_index: int, dataset, timeout: int = 120 + ) -> Optional[EvaluationReport]: + """Run a single evaluation for a model with timeout and error handling.""" + try: + + async def fix_with_model(inputs: MermaidInput) -> MermaidOutput: + return await fix_mermaid_diagram(inputs, model=model) + + report = await asyncio.wait_for( + dataset.evaluate( + fix_with_model, + name=f"{model}-multi-mcp-mermaid-diagram-fix-run{run_index + 1}", + max_concurrency=1, + ), + timeout=timeout, + ) + + return report + + except asyncio.TimeoutError: + error_msg = f"Evaluation timed out after {timeout}s" + logfire.warning( + "Evaluation timeout", model=model, run_index=run_index, timeout=timeout + ) + self.results[model].add_failed_run(run_index, error_msg) + return None + + except Exception as e: + error_msg = f"Error during evaluation: {str(e)}" + logfire.error( + "Evaluation error", + model=model, + run_index=run_index, + error=str(e), + error_type=type(e).__name__, + ) + self.results[model].add_failed_run(run_index, error_msg) + return None + + async def run_model_evaluations( + self, + model: str, + n_runs: int, + dataset, + parallel: bool = True, + timeout: int = 600, + ): + """Run multiple evaluations for a single model.""" + self.console.print(f"\n[bold cyan]Evaluating model: {model}[/bold cyan]") + + if parallel: + tasks = [ + self.run_single_evaluation(model, i, dataset, timeout) + for i in range(n_runs) + ] + + with Progress( + SpinnerColumn(), + TextColumn("[progress.description]{task.description}"), + BarColumn(), + TaskProgressColumn(), + console=self.console, + ) as progress: + task = progress.add_task( + f"Running {n_runs} evaluations for {model}", total=n_runs + ) + + results = await asyncio.gather(*tasks, return_exceptions=True) + + for result in results: + if isinstance(result, EvaluationReport): + self.results[model].add_successful_run(result) + progress.advance(task) + else: + for i in range(n_runs): + self.console.print(f"[yellow]Run {i + 1}/{n_runs} for {model}[/yellow]") + + result = await self.run_single_evaluation(model, i, dataset, timeout) + if result: + self.results[model].add_successful_run(result) + + individual_file = self.results[model].write_individual_results(self.output_dir) + if individual_file: + self.console.print( + f"[green]Individual results saved: {individual_file}[/green]" + ) + + success_rate = self.results[model].get_success_rate() + successful_runs = len(self.results[model].reports) + failed_runs = len(self.results[model].failed_runs) + + self.console.print(f"[bold]Model {model} Summary:[/bold]") + self.console.print(f" Successful runs: {successful_runs}") + self.console.print(f" Failed runs: {failed_runs}") + self.console.print(f" Success rate: {success_rate:.1%}") + + def write_combined_results(self) -> str: + """Write combined results from all models to a single CSV file.""" + os.makedirs(self.output_dir, exist_ok=True) + timestamp = get_timestamp_prefix() + filepath = os.path.join(self.output_dir, f"{timestamp}_combined_results.csv") + + all_evaluator_names = set() + all_metric_names = set() + + for model_results in self.results.values(): + for report in model_results.reports: + for case in report.cases: + all_evaluator_names.update(case.scores.keys()) + if hasattr(case.output, "metrics") and case.output.metrics: + all_metric_names.update(case.output.metrics.keys()) + + headers = [ + "Model", + "Run", + "Case", + "Duration", + "Fixed_Diagram_Length", + "Failure_Reason", + "Tools_Used", + ] + + for evaluator in sorted(all_evaluator_names): + headers.append(f"Score_{evaluator}") + + for metric in sorted(all_metric_names): + headers.append(f"Metric_{metric}") + + with open(filepath, "w", newline="", encoding="utf-8") as csvfile: + writer = csv.writer(csvfile) + writer.writerow(headers) + + for model, model_results in self.results.items(): + for run_idx, report in enumerate(model_results.reports): + for case in report.cases: + row = [ + model, + run_idx + 1, + case.name, + case.task_duration, + len(case.output.fixed_diagram) + if case.output and case.output.fixed_diagram + else 0, + case.output.failure_reason if case.output else "", + "|".join(case.output.tools_used) + if case.output and case.output.tools_used + else "", + ] + + for evaluator in sorted(all_evaluator_names): + if evaluator in case.scores: + row.append(case.scores[evaluator].value) + else: + row.append("") + + for metric in sorted(all_metric_names): + if ( + case.output + and hasattr(case.output, "metrics") + and case.output.metrics + and metric in case.output.metrics + ): + metric_value = case.output.metrics[metric] + if isinstance(metric_value, dict): + row.append(str(metric_value)) + else: + row.append(metric_value) + else: + row.append("") + + writer.writerow(row) + + return filepath + + def print_final_summary(self): + """Print a comprehensive summary of all results.""" + table = Table(title="Multi-Model Evaluation Summary") + + table.add_column("Model", style="cyan") + table.add_column("Successful Runs", style="green") + table.add_column("Failed Runs", style="red") + table.add_column("Success Rate", style="yellow") + table.add_column("Avg Duration", style="blue") + + for model, model_results in self.results.items(): + successful_runs = len(model_results.reports) + failed_runs = len(model_results.failed_runs) + success_rate = model_results.get_success_rate() + + if model_results.reports: + all_durations = [] + for report in model_results.reports: + for case in report.cases: + all_durations.append(case.task_duration) + avg_duration = statistics.mean(all_durations) if all_durations else 0 + else: + avg_duration = 0 + + table.add_row( + model, + str(successful_runs), + str(failed_runs), + f"{success_rate:.1%}", + f"{avg_duration:.1f}s", + ) + + self.console.print(table) + + async def run_all_evaluations( + self, n_runs: int, parallel: bool = True, timeout: int = 120 + ) -> str: + """Run evaluations for all models and return path to combined results.""" + self.console.print(f"[bold green]Starting multi-model evaluation[/bold green]") + self.console.print(f"Models: {', '.join(self.models)}") + self.console.print(f"Runs per model: {n_runs}") + self.console.print(f"Parallel execution: {parallel}") + self.console.print(f"Timeout per run: {timeout}s") + + dataset = create_evaluation_dataset(self.judge_model) + + for model in self.models: + await self.run_model_evaluations(model, n_runs, dataset, parallel, timeout) + + combined_file = self.write_combined_results() + + self.print_final_summary() + + self.console.print(f"\n[bold green]All evaluations complete![/bold green]") + self.console.print(f"Combined results: {combined_file}") + + return combined_file + + +async def main(): + """Main entry point for the script.""" + parser = argparse.ArgumentParser( + description="Run mermaid diagram evaluations across multiple LLM models" + ) + parser.add_argument( + "--models", + type=str, + default=",".join(DEFAULT_MODELS), + help="Comma-separated list of models to evaluate", + ) + parser.add_argument( + "--runs", + type=int, + default=3, + help="Number of evaluation runs per model", + ) + parser.add_argument( + "--judge-model", + type=str, + default="gemini-2.5-pro-preview-03-25", + help="Model to use for LLM judging", + ) + parser.add_argument( + "--parallel", + action="store_true", + default=False, + help="Run evaluations in parallel", + ) + parser.add_argument( + "--sequential", + action="store_true", + help="Run evaluations sequentially (overrides --parallel)", + ) + parser.add_argument( + "--timeout", + type=int, + default=600, + help="Timeout in seconds for each evaluation run", + ) + parser.add_argument( + "--output-dir", + type=str, + default="./mermaid_eval_results", + help="Directory to save results", + ) + + args = parser.parse_args() + + models = [model.strip() for model in args.models.split(",")] + + parallel = args.parallel and not args.sequential + + evaluator = MultiModelEvaluator( + models=models, judge_model=args.judge_model, output_dir=args.output_dir + ) + + combined_results_file = await evaluator.run_all_evaluations( + n_runs=args.runs, parallel=parallel, timeout=args.timeout + ) + + print(f"\nEvaluation complete. Combined results saved to: {combined_results_file}") + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/mcp_servers/mermaid_validator.py b/mcp_servers/mermaid_validator.py index a796983..92ba9ad 100644 --- a/mcp_servers/mermaid_validator.py +++ b/mcp_servers/mermaid_validator.py @@ -101,7 +101,8 @@ async def validate_mermaid_diagram(diagram_text: str) -> MermaidValidationResult if file_path and os.path.exists(file_path): try: os.unlink(file_path) - except: + except OSError: + # Ignore file deletion errors during cleanup pass From 7a067c7a4d460361401691a9e71b742cbc210ce3 Mon Sep 17 00:00:00 2001 From: Andrew Ginns Date: Sun, 8 Jun 2025 09:38:32 +0000 Subject: [PATCH 05/14] feat: Streamlit UI for leaderboard --- .../multi_mcp/eval_multi_mcp/merbench_ui.py | 626 ++++++++++++++++++ 1 file changed, 626 insertions(+) create mode 100644 agents_mcp_usage/multi_mcp/eval_multi_mcp/merbench_ui.py diff --git a/agents_mcp_usage/multi_mcp/eval_multi_mcp/merbench_ui.py b/agents_mcp_usage/multi_mcp/eval_multi_mcp/merbench_ui.py new file mode 100644 index 0000000..43e4700 --- /dev/null +++ b/agents_mcp_usage/multi_mcp/eval_multi_mcp/merbench_ui.py @@ -0,0 +1,626 @@ +import os +import glob +import pandas as pd +import plotly.graph_objects as go +import streamlit as st +import numpy as np +from typing import List, Dict +import json + +st.set_page_config( + page_title="Merbench - LLM Evaluation Benchmark", page_icon="🏆", layout="wide" +) + +DEFAULT_COSTS = { + "gemini-2.5-pro-preview-06-05": {"input": 3.50, "output": 10.50}, + "gemini-2.0-flash": {"input": 0.075, "output": 0.30}, + "gemini-2.5-flash-preview-04-17": {"input": 0.075, "output": 0.30}, + "openai:o4-mini": {"input": 0.15, "output": 0.60}, + "openai:gpt-4.1-mini": {"input": 0.15, "output": 0.60}, + "openai:gpt-4.1": {"input": 2.50, "output": 10.00}, +} + + +def find_all_combined_results_csvs(directory_path: str) -> list[str]: + """ + Finds all '*_combined_results.csv' files in the given directory, + sorted by modification time (newest first). + """ + if not os.path.isdir(directory_path): + return [] + try: + search_pattern = os.path.join(directory_path, "*_combined_results.csv") + files = glob.glob(search_pattern) + return sorted(files, key=os.path.getmtime, reverse=True) + except Exception as e: + st.error(f"Error finding CSV files in '{directory_path}': {e}") + return [] + + +def detect_csv_files(directory: str = None) -> List[str]: + """Detect CSV result files in the specified directory.""" + if directory is None: + script_dir = os.path.dirname( + os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + ) + directory = os.path.join(script_dir, "mermaid_eval_results") + + return find_all_combined_results_csvs(directory) + + +def load_csv_data(file_paths: List[str]) -> pd.DataFrame: + """Load and combine multiple CSV files.""" + if not file_paths: + return pd.DataFrame() + + dataframes = [] + for file_path in file_paths: + try: + if not os.path.exists(file_path): + st.error(f"File not found: {file_path}") + continue + + df = pd.read_csv(file_path) + if df.empty: + st.warning(f"Empty file: {os.path.basename(file_path)}") + continue + + df["source_file"] = os.path.basename(file_path) + dataframes.append(df) + except pd.errors.EmptyDataError: + st.error(f"Empty or invalid CSV file: {os.path.basename(file_path)}") + except pd.errors.ParserError as e: + st.error(f"CSV parsing error in {os.path.basename(file_path)}: {e}") + except Exception as e: + st.error(f"Unexpected error loading {os.path.basename(file_path)}: {e}") + + if not dataframes: + return pd.DataFrame() + + combined_df = pd.concat(dataframes, ignore_index=True) + return combined_df + + +def extract_case_difficulty(case_name: str) -> str: + """Extract difficulty level from case name.""" + case_lower = case_name.lower() + if "easy" in case_lower: + return "easy" + elif "medium" in case_lower: + return "medium" + elif "hard" in case_lower: + return "hard" + else: + return "unknown" + + +def parse_metric_details(metric_details_str: str) -> Dict: + """Parse the metric details JSON string.""" + if pd.isna(metric_details_str) or metric_details_str == "": + return {} + try: + return json.loads(metric_details_str.replace("'", '"')) + except (json.JSONDecodeError, TypeError, AttributeError) as e: + st.warning(f"Failed to parse metric details: {e}") + return {} + + +def calculate_costs(df: pd.DataFrame, cost_config: Dict) -> pd.DataFrame: + """Calculate costs based on token usage and cost configuration.""" + df_with_costs = df.copy() + + df_with_costs["input_cost"] = 0.0 + df_with_costs["output_cost"] = 0.0 + df_with_costs["total_cost"] = 0.0 + + for idx, row in df_with_costs.iterrows(): + model = row.get("Model", "") + if model in cost_config: + try: + input_tokens = max(0, row.get("Metric_request_tokens", 0) or 0) + input_cost_per_1m = cost_config[model]["input"] + input_cost = (input_tokens / 1_000_000) * input_cost_per_1m + + response_tokens = max(0, row.get("Metric_response_tokens", 0) or 0) + thinking_tokens = max(0, row.get("thinking_tokens", 0) or 0) + total_output_tokens = response_tokens + thinking_tokens + output_cost_per_1m = cost_config[model]["output"] + output_cost = (total_output_tokens / 1_000_000) * output_cost_per_1m + + df_with_costs.at[idx, "input_cost"] = input_cost + df_with_costs.at[idx, "output_cost"] = output_cost + df_with_costs.at[idx, "total_cost"] = input_cost + output_cost + except (TypeError, ValueError, KeyError) as e: + st.warning( + f"Error calculating costs for model {model} at row {idx}: {e}" + ) + continue + + return df_with_costs + + +def process_data(df: pd.DataFrame, cost_config: Dict) -> pd.DataFrame: + """Process and clean the data for visualization.""" + if df.empty: + return df + + processed_df = df.copy() + + processed_df["difficulty"] = processed_df["Case"].apply(extract_case_difficulty) + + processed_df["thinking_tokens"] = 0 + processed_df["text_tokens"] = 0 + + if "Metric_details" in processed_df.columns: + for idx, row in processed_df.iterrows(): + details = parse_metric_details(row["Metric_details"]) + processed_df.at[idx, "thinking_tokens"] = details.get("thoughts_tokens", 0) + processed_df.at[idx, "text_tokens"] = details.get("text_prompt_tokens", 0) + + if "Metric_response_tokens" in processed_df.columns: + processed_df["total_response_tokens"] = ( + processed_df["Metric_response_tokens"] + processed_df["thinking_tokens"] + ) + else: + processed_df["total_response_tokens"] = processed_df["thinking_tokens"] + + score_columns = [col for col in processed_df.columns if col.startswith("Score_")] + if score_columns: + processed_df["overall_score"] = processed_df[score_columns].mean(axis=1) * 100 + else: + processed_df["overall_score"] = 50 # Default neutral score + + processed_df = calculate_costs(processed_df, cost_config) + + return processed_df + + +def create_leaderboard(df: pd.DataFrame, selected_cases: List[str]) -> pd.DataFrame: + """Create a leaderboard table with model performance metrics.""" + if df.empty: + return pd.DataFrame() + + if selected_cases and "all" not in selected_cases: + df_filtered = df[df["difficulty"].isin(selected_cases)] + else: + df_filtered = df + + leaderboard_data = [] + + for model in df_filtered["Model"].unique(): + model_data = df_filtered[df_filtered["Model"] == model] + + for difficulty in ["easy", "medium", "hard"]: + diff_data = model_data[model_data["difficulty"] == difficulty] + + if not diff_data.empty: + avg_score = diff_data["overall_score"].mean() + avg_duration = diff_data["Duration"].mean() + avg_tokens = diff_data["total_response_tokens"].mean() + avg_cost = diff_data["total_cost"].mean() + run_count = len(diff_data) + + leaderboard_data.append( + { + "Model": model, + "Difficulty": difficulty, + "Avg_Score": avg_score, + "Avg_Duration": avg_duration, + "Avg_Tokens": avg_tokens, + "Avg_Cost": avg_cost, + "Run_Count": run_count, + "Efficiency": avg_tokens / avg_duration + if avg_duration > 0 + else 0, + "Cost_Efficiency": avg_score / avg_cost if avg_cost > 0 else 0, + } + ) + + leaderboard_df = pd.DataFrame(leaderboard_data) + + if not leaderboard_df.empty: + leaderboard_df = leaderboard_df.sort_values("Avg_Score", ascending=False) + + return leaderboard_df + + +def create_pareto_frontier_plot( + df: pd.DataFrame, selected_cases: List[str], x_axis_mode: str = "tokens" +) -> go.Figure: + """Create a Pareto frontier plot showing model performance vs efficiency.""" + if df.empty: + return go.Figure() + + if selected_cases and "all" not in selected_cases: + df_filtered = df[df["difficulty"].isin(selected_cases)] + else: + df_filtered = df + + model_metrics = ( + df_filtered.groupby("Model") + .agg( + { + "overall_score": "mean", + "Duration": "mean", + "total_response_tokens": "mean", + "thinking_tokens": "mean", + "total_cost": "mean", + } + ) + .reset_index() + ) + + model_metrics["efficiency"] = np.where( + model_metrics["Duration"] > 0, + model_metrics["total_response_tokens"] / model_metrics["Duration"], + 0, + ) + + if x_axis_mode == "cost": + x_data = model_metrics["total_cost"] + x_title = "Average Total Cost ($)" + hover_x_label = "Avg Cost" + hover_x_format = ":.4f" + else: # tokens + x_data = model_metrics["total_response_tokens"] + x_title = "Average Total Response Tokens" + hover_x_label = "Avg Tokens" + hover_x_format = ":.0f" + + fig = go.Figure() + + fig.add_trace( + go.Scatter( + x=x_data, + y=model_metrics["overall_score"], + mode="markers+text", + marker=dict( + size=15, + color=model_metrics["Duration"], + colorscale="RdYlGn_r", # Red for slow, green for fast + showscale=True, + colorbar=dict(title="Avg Duration (sec)"), + ), + text=model_metrics["Model"], + textposition="top center", + hovertemplate="%{text}
" + + "Avg Score: %{y:.1f}%
" + + f"{hover_x_label}: %{{x{hover_x_format}}}
" + + "Avg Duration: %{marker.color:.1f}s
" + + "", + ) + ) + + fig.update_layout( + title=f"Pareto Frontier: Model Performance vs {'Cost' if x_axis_mode == 'cost' else 'Token Usage'}", + xaxis_title=x_title, + yaxis_title="Average Overall Score (%)", + showlegend=False, + height=600, + ) + + return fig + + +def create_cost_configuration_ui(models: List[str]) -> Dict: + """Create UI for configuring model costs.""" + st.sidebar.subheader("💰 Cost Configuration") + st.sidebar.write("Set costs per 1M tokens:") + + cost_config = {} + + with st.sidebar.expander("Model Costs", expanded=False): + for model in models: + st.write(f"**{model}**") + + default_input = DEFAULT_COSTS.get(model, {"input": 1.0, "output": 3.0})[ + "input" + ] + default_output = DEFAULT_COSTS.get(model, {"input": 1.0, "output": 3.0})[ + "output" + ] + + col1, col2 = st.columns(2) + with col1: + input_cost = st.number_input( + "Input", + min_value=0.0, + value=default_input, + step=0.01, + format="%.3f", + key=f"input_{model}", + ) + with col2: + output_cost = st.number_input( + "Output", + min_value=0.0, + value=default_output, + step=0.01, + format="%.3f", + key=f"output_{model}", + ) + + cost_config[model] = {"input": input_cost, "output": output_cost} + + return cost_config + + +def create_token_breakdown_plot( + df: pd.DataFrame, selected_cases: List[str] +) -> go.Figure: + """Create a stacked bar chart showing token breakdown by model.""" + if df.empty: + return go.Figure() + + if selected_cases and "all" not in selected_cases: + df_filtered = df[df["difficulty"].isin(selected_cases)] + else: + df_filtered = df + + token_data = ( + df_filtered.groupby("Model") + .agg( + { + "Metric_request_tokens": "mean", + "Metric_response_tokens": "mean", + "thinking_tokens": "mean", + } + ) + .reset_index() + ) + + fig = go.Figure() + + fig.add_trace( + go.Bar( + name="Request Tokens", + x=token_data["Model"], + y=token_data["Metric_request_tokens"], + marker_color="lightblue", + ) + ) + + fig.add_trace( + go.Bar( + name="Response Tokens", + x=token_data["Model"], + y=token_data["Metric_response_tokens"], + marker_color="orange", + ) + ) + + fig.add_trace( + go.Bar( + name="Thinking Tokens", + x=token_data["Model"], + y=token_data["thinking_tokens"], + marker_color="lightgreen", + ) + ) + + fig.update_layout( + title="Token Usage Breakdown by Model", + xaxis_title="Model", + yaxis_title="Average Tokens", + barmode="stack", + height=500, + ) + + return fig + + +def create_success_rates_plot(df: pd.DataFrame, selected_cases: List[str]) -> go.Figure: + """Create a bar chart showing overall average success rates by metric.""" + if df.empty: + return go.Figure() + + if selected_cases and "all" not in selected_cases: + df_filtered = df[df["difficulty"].isin(selected_cases)] + else: + df_filtered = df + + metric_columns = [col for col in df_filtered.columns if col.startswith("Score_")] + + if not metric_columns: + return go.Figure() + + models = sorted(df_filtered["Model"].unique()) + + metric_data = [] + for metric_col in metric_columns: + metric_name = metric_col.replace("Score_", "") + model_scores = [] + for model in models: + model_data = df_filtered[df_filtered["Model"] == model] + if not model_data.empty: + avg_score = model_data[metric_col].mean() * 100 + model_scores.append(avg_score) + else: + model_scores.append(0) + + metric_data.append( + {"metric": metric_name, "models": models, "scores": model_scores} + ) + + fig = go.Figure() + + colors = ["#1f77b4", "#ff7f0e", "#2ca02c", "#d62728", "#9467bd", "#8c564b"] + + for i, data in enumerate(metric_data): + fig.add_trace( + go.Bar( + name=data["metric"], + x=data["models"], + y=data["scores"], + marker_color=colors[i % len(colors)], + ) + ) + + fig.update_layout( + title="Overall Average Success Rates by Metric", + xaxis_title="Model", + yaxis_title="Success Rate (%)", + barmode="group", + height=500, + legend=dict(title="Metric"), + ) + + return fig + + +def main(): + """Main Streamlit application.""" + + st.title("🏆 Merbench") + st.subheader("LLM Evaluation Benchmark Dashboard") + + st.sidebar.header("Configuration") + + st.sidebar.subheader("Detected CSV Files") + + script_dir = os.path.dirname( + os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + ) + default_dir = os.path.join(script_dir, "mermaid_eval_results") + + custom_dir = st.sidebar.text_input( + "Directory path:", + value=default_dir, + help="Path to directory containing *_combined_results.csv files", + ) + + if "refresh_files" not in st.session_state: + st.session_state.refresh_files = False + + if st.sidebar.button("🔄 Refresh Detected Files"): + st.session_state.refresh_files = True + st.rerun() + + csv_files = detect_csv_files(custom_dir) + + if not csv_files: + st.warning(f"No *_combined_results.csv files found in {custom_dir}") + st.info("Looking for files matching pattern: *_combined_results.csv") + return + + st.sidebar.write(f"Found {len(csv_files)} files (sorted by newest first):") + + selected_files = [] + for file in csv_files: + file_name = os.path.basename(file) + if st.sidebar.checkbox(file_name, value=True, key=f"file_{file_name}"): + selected_files.append(file) + + if not selected_files: + st.warning("Please select at least one CSV file to visualize.") + return + + with st.spinner("Loading data..."): + df_initial = load_csv_data(selected_files) + if df_initial.empty: + st.error("No data could be loaded from the selected files.") + return + + available_models = sorted(df_initial["Model"].unique()) + cost_config = create_cost_configuration_ui(available_models) + + df = process_data(df_initial, cost_config) + + available_cases = sorted(df["difficulty"].unique()) + st.sidebar.subheader("Case Selection") + + case_options = ["all"] + available_cases + selected_cases = st.sidebar.multiselect( + "Select difficulty levels", options=case_options, default=["all"] + ) + + st.subheader("📊 Data Overview") + + overview_col1, overview_col2 = st.columns(2) + with overview_col1: + st.metric("Evaluation Runs", len(df)) + st.metric("Files Loaded", len(selected_files)) + + with overview_col2: + st.metric("Models", len(df["Model"].unique())) + st.metric("Cases", len(df["Case"].unique())) + + st.write(f"**Models:** {', '.join(sorted(df['Model'].unique()))}") + st.write(f"**Cases:** {', '.join(sorted(df['Case'].unique()))}") + + if "all" not in selected_cases: + st.info(f"🎯 **Filtered by difficulty:** {', '.join(selected_cases)}") + else: + st.info("🎯 **Showing all difficulty levels**") + + st.header("🏅 Leaderboard") + leaderboard_df = create_leaderboard(df, selected_cases) + + if not leaderboard_df.empty: + display_df = leaderboard_df.copy() + display_df["Avg_Score"] = display_df["Avg_Score"].round(1) + display_df["Avg_Duration"] = display_df["Avg_Duration"].round(2) + display_df["Avg_Tokens"] = display_df["Avg_Tokens"].round(0) + display_df["Avg_Cost"] = display_df["Avg_Cost"].round(4) + display_df["Efficiency"] = display_df["Efficiency"].round(1) + display_df["Cost_Efficiency"] = display_df["Cost_Efficiency"].round(1) + + st.dataframe( + display_df, + column_config={ + "Avg_Score": st.column_config.NumberColumn( + "Avg Score (%)", format="%.1f" + ), + "Avg_Duration": st.column_config.NumberColumn( + "Avg Duration (s)", format="%.2f" + ), + "Avg_Tokens": st.column_config.NumberColumn( + "Avg Tokens", format="%.0f" + ), + "Avg_Cost": st.column_config.NumberColumn( + "Avg Cost ($)", format="%.4f" + ), + "Efficiency": st.column_config.NumberColumn( + "Tokens/sec", format="%.1f" + ), + "Cost_Efficiency": st.column_config.NumberColumn( + "Score/Cost", format="%.1f" + ), + "Run_Count": st.column_config.NumberColumn("Runs", format="%d"), + }, + use_container_width=True, + ) + else: + st.warning("No data available for the selected filters.") + + st.header("📈 Pareto Frontier Analysis") + + x_axis_mode = st.radio( + "X-Axis Mode:", + options=["tokens", "cost"], + format_func=lambda x: "Token Count" if x == "tokens" else "Cost ($)", + horizontal=True, + ) + + pareto_fig = create_pareto_frontier_plot(df, selected_cases, x_axis_mode) + st.plotly_chart(pareto_fig, use_container_width=True) + + st.header("📊 Overall Average Success Rates by Metric") + success_fig = create_success_rates_plot(df, selected_cases) + st.plotly_chart(success_fig, use_container_width=True) + + st.header("🔢 Token Usage Analysis") + token_fig = create_token_breakdown_plot(df, selected_cases) + st.plotly_chart(token_fig, use_container_width=True) + + with st.expander("📋 Raw Data"): + if selected_cases and "all" not in selected_cases: + filtered_df = df[df["difficulty"].isin(selected_cases)] + else: + filtered_df = df + + st.dataframe(filtered_df, use_container_width=True) + + +if __name__ == "__main__": + main() From ca1f86d714fb47127bfe0952725de9baa5b0d9a3 Mon Sep 17 00:00:00 2001 From: Andrew Ginns Date: Sun, 8 Jun 2025 13:09:01 +0000 Subject: [PATCH 06/14] feat: Many usability and data vis improvements --- .../multi_mcp/eval_multi_mcp/merbench_ui.py | 738 +++++++----------- 1 file changed, 293 insertions(+), 445 deletions(-) diff --git a/agents_mcp_usage/multi_mcp/eval_multi_mcp/merbench_ui.py b/agents_mcp_usage/multi_mcp/eval_multi_mcp/merbench_ui.py index 43e4700..dea0154 100644 --- a/agents_mcp_usage/multi_mcp/eval_multi_mcp/merbench_ui.py +++ b/agents_mcp_usage/multi_mcp/eval_multi_mcp/merbench_ui.py @@ -7,10 +7,12 @@ from typing import List, Dict import json +# Page configuration st.set_page_config( page_title="Merbench - LLM Evaluation Benchmark", page_icon="🏆", layout="wide" ) +# Default model costs (per 1M tokens) DEFAULT_COSTS = { "gemini-2.5-pro-preview-06-05": {"input": 3.50, "output": 10.50}, "gemini-2.0-flash": {"input": 0.075, "output": 0.30}, @@ -20,12 +22,10 @@ "openai:gpt-4.1": {"input": 2.50, "output": 10.00}, } +# --- Data Loading and Processing --- def find_all_combined_results_csvs(directory_path: str) -> list[str]: - """ - Finds all '*_combined_results.csv' files in the given directory, - sorted by modification time (newest first). - """ + """Finds all '*_combined_results.csv' files, sorted by modification time.""" if not os.path.isdir(directory_path): return [] try: @@ -36,590 +36,438 @@ def find_all_combined_results_csvs(directory_path: str) -> list[str]: st.error(f"Error finding CSV files in '{directory_path}': {e}") return [] - def detect_csv_files(directory: str = None) -> List[str]: """Detect CSV result files in the specified directory.""" if directory is None: - script_dir = os.path.dirname( - os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) - ) - directory = os.path.join(script_dir, "mermaid_eval_results") - + # Go up three levels from the current script to the project root + script_dir = os.path.dirname(os.path.abspath(__file__)) + project_root = os.path.dirname(os.path.dirname(os.path.dirname(script_dir))) + directory = os.path.join(project_root, "mermaid_eval_results") return find_all_combined_results_csvs(directory) def load_csv_data(file_paths: List[str]) -> pd.DataFrame: - """Load and combine multiple CSV files.""" + """Load and combine multiple CSV files into a single DataFrame.""" if not file_paths: return pd.DataFrame() - + dataframes = [] for file_path in file_paths: try: - if not os.path.exists(file_path): - st.error(f"File not found: {file_path}") - continue - df = pd.read_csv(file_path) - if df.empty: - st.warning(f"Empty file: {os.path.basename(file_path)}") - continue - - df["source_file"] = os.path.basename(file_path) - dataframes.append(df) - except pd.errors.EmptyDataError: - st.error(f"Empty or invalid CSV file: {os.path.basename(file_path)}") - except pd.errors.ParserError as e: - st.error(f"CSV parsing error in {os.path.basename(file_path)}: {e}") + if not df.empty: + df["source_file"] = os.path.basename(file_path) + dataframes.append(df) + else: + st.warning(f"Empty file skipped: {os.path.basename(file_path)}") + except (pd.errors.EmptyDataError, FileNotFoundError) as e: + st.error(f"Could not load {os.path.basename(file_path)}: {e}") except Exception as e: - st.error(f"Unexpected error loading {os.path.basename(file_path)}: {e}") - - if not dataframes: - return pd.DataFrame() + st.error(f"An unexpected error occurred while loading {os.path.basename(file_path)}: {e}") - combined_df = pd.concat(dataframes, ignore_index=True) - return combined_df + return pd.concat(dataframes, ignore_index=True) if dataframes else pd.DataFrame() def extract_case_difficulty(case_name: str) -> str: - """Extract difficulty level from case name.""" - case_lower = case_name.lower() + """Extracts difficulty ('easy', 'medium', 'hard') from a case name.""" + case_lower = str(case_name).lower() if "easy" in case_lower: return "easy" - elif "medium" in case_lower: + if "medium" in case_lower: return "medium" - elif "hard" in case_lower: + if "hard" in case_lower: return "hard" - else: - return "unknown" + return "unknown" def parse_metric_details(metric_details_str: str) -> Dict: - """Parse the metric details JSON string.""" - if pd.isna(metric_details_str) or metric_details_str == "": + """Safely parses a JSON string from the 'Metric_details' column.""" + if pd.isna(metric_details_str) or not metric_details_str: return {} try: + # Attempt to fix common JSON issues like single quotes return json.loads(metric_details_str.replace("'", '"')) - except (json.JSONDecodeError, TypeError, AttributeError) as e: - st.warning(f"Failed to parse metric details: {e}") + except (json.JSONDecodeError, TypeError): return {} def calculate_costs(df: pd.DataFrame, cost_config: Dict) -> pd.DataFrame: - """Calculate costs based on token usage and cost configuration.""" + """Calculates input, output, and total costs for each run.""" df_with_costs = df.copy() - df_with_costs["input_cost"] = 0.0 df_with_costs["output_cost"] = 0.0 df_with_costs["total_cost"] = 0.0 for idx, row in df_with_costs.iterrows(): - model = row.get("Model", "") + model = row.get("Model") if model in cost_config: try: - input_tokens = max(0, row.get("Metric_request_tokens", 0) or 0) - input_cost_per_1m = cost_config[model]["input"] - input_cost = (input_tokens / 1_000_000) * input_cost_per_1m - - response_tokens = max(0, row.get("Metric_response_tokens", 0) or 0) - thinking_tokens = max(0, row.get("thinking_tokens", 0) or 0) - total_output_tokens = response_tokens + thinking_tokens - output_cost_per_1m = cost_config[model]["output"] - output_cost = (total_output_tokens / 1_000_000) * output_cost_per_1m + input_tokens = row.get("Metric_request_tokens", 0) or 0 + output_tokens = row.get("Metric_response_tokens", 0) or 0 + thinking_tokens = row.get("thinking_tokens", 0) or 0 + input_cost = (input_tokens / 1_000_000) * cost_config[model]["input"] + output_cost = ((output_tokens + thinking_tokens) / 1_000_000) * cost_config[model]["output"] + df_with_costs.at[idx, "input_cost"] = input_cost df_with_costs.at[idx, "output_cost"] = output_cost df_with_costs.at[idx, "total_cost"] = input_cost + output_cost - except (TypeError, ValueError, KeyError) as e: - st.warning( - f"Error calculating costs for model {model} at row {idx}: {e}" - ) - continue - + except (TypeError, KeyError) as e: + st.warning(f"Cost calculation error for model {model} at row {idx}: {e}") return df_with_costs def process_data(df: pd.DataFrame, cost_config: Dict) -> pd.DataFrame: - """Process and clean the data for visualization.""" + """Main data processing pipeline.""" if df.empty: return df processed_df = df.copy() - processed_df["difficulty"] = processed_df["Case"].apply(extract_case_difficulty) + + # Extract token counts from metric details + metric_details = processed_df["Metric_details"].apply(parse_metric_details) + processed_df["thinking_tokens"] = metric_details.apply(lambda x: x.get("thoughts_tokens", 0)) + processed_df["text_tokens"] = metric_details.apply(lambda x: x.get("text_prompt_tokens", 0)) + + # Calculate total response tokens + processed_df["total_response_tokens"] = ( + processed_df.get("Metric_response_tokens", 0) + processed_df["thinking_tokens"] + ) + + # Standardize correctness score + processed_df["correctness_score"] = processed_df.get("Score_MermaidDiagramValid", 0.5) * 100 - processed_df["thinking_tokens"] = 0 - processed_df["text_tokens"] = 0 - - if "Metric_details" in processed_df.columns: - for idx, row in processed_df.iterrows(): - details = parse_metric_details(row["Metric_details"]) - processed_df.at[idx, "thinking_tokens"] = details.get("thoughts_tokens", 0) - processed_df.at[idx, "text_tokens"] = details.get("text_prompt_tokens", 0) - - if "Metric_response_tokens" in processed_df.columns: - processed_df["total_response_tokens"] = ( - processed_df["Metric_response_tokens"] + processed_df["thinking_tokens"] - ) - else: - processed_df["total_response_tokens"] = processed_df["thinking_tokens"] - - score_columns = [col for col in processed_df.columns if col.startswith("Score_")] - if score_columns: - processed_df["overall_score"] = processed_df[score_columns].mean(axis=1) * 100 - else: - processed_df["overall_score"] = 50 # Default neutral score - - processed_df = calculate_costs(processed_df, cost_config) + return calculate_costs(processed_df, cost_config) - return processed_df +# --- UI & Plotting --- def create_leaderboard(df: pd.DataFrame, selected_cases: List[str]) -> pd.DataFrame: - """Create a leaderboard table with model performance metrics.""" - if df.empty: + """Creates a leaderboard DataFrame with key performance indicators.""" + if df.empty or not selected_cases: return pd.DataFrame() - if selected_cases and "all" not in selected_cases: - df_filtered = df[df["difficulty"].isin(selected_cases)] - else: - df_filtered = df - - leaderboard_data = [] - - for model in df_filtered["Model"].unique(): - model_data = df_filtered[df_filtered["Model"] == model] - - for difficulty in ["easy", "medium", "hard"]: - diff_data = model_data[model_data["difficulty"] == difficulty] - - if not diff_data.empty: - avg_score = diff_data["overall_score"].mean() - avg_duration = diff_data["Duration"].mean() - avg_tokens = diff_data["total_response_tokens"].mean() - avg_cost = diff_data["total_cost"].mean() - run_count = len(diff_data) - - leaderboard_data.append( - { - "Model": model, - "Difficulty": difficulty, - "Avg_Score": avg_score, - "Avg_Duration": avg_duration, - "Avg_Tokens": avg_tokens, - "Avg_Cost": avg_cost, - "Run_Count": run_count, - "Efficiency": avg_tokens / avg_duration - if avg_duration > 0 - else 0, - "Cost_Efficiency": avg_score / avg_cost if avg_cost > 0 else 0, - } - ) - - leaderboard_df = pd.DataFrame(leaderboard_data) - - if not leaderboard_df.empty: - leaderboard_df = leaderboard_df.sort_values("Avg_Score", ascending=False) - - return leaderboard_df - + df_filtered = df[df["difficulty"].isin(selected_cases)] + if df_filtered.empty: + return pd.DataFrame() -def create_pareto_frontier_plot( - df: pd.DataFrame, selected_cases: List[str], x_axis_mode: str = "tokens" -) -> go.Figure: - """Create a Pareto frontier plot showing model performance vs efficiency.""" - if df.empty: - return go.Figure() + leaderboard = df_filtered.groupby("Model").agg( + Correct=("correctness_score", "mean"), + Cost=("total_cost", "mean"), + Duration=("Duration", "mean"), + Tokens=("total_response_tokens", "mean"), + Runs=("Model", "size"), + ).reset_index() - if selected_cases and "all" not in selected_cases: - df_filtered = df[df["difficulty"].isin(selected_cases)] - else: - df_filtered = df - - model_metrics = ( - df_filtered.groupby("Model") - .agg( - { - "overall_score": "mean", - "Duration": "mean", - "total_response_tokens": "mean", - "thinking_tokens": "mean", - "total_cost": "mean", - } - ) - .reset_index() - ) + return leaderboard.sort_values("Correct", ascending=False) - model_metrics["efficiency"] = np.where( - model_metrics["Duration"] > 0, - model_metrics["total_response_tokens"] / model_metrics["Duration"], - 0, - ) - if x_axis_mode == "cost": - x_data = model_metrics["total_cost"] - x_title = "Average Total Cost ($)" - hover_x_label = "Avg Cost" - hover_x_format = ":.4f" - else: # tokens - x_data = model_metrics["total_response_tokens"] - x_title = "Average Total Response Tokens" - hover_x_label = "Avg Tokens" - hover_x_format = ":.0f" +def create_pareto_frontier_plot(df: pd.DataFrame, selected_cases: List[str], x_axis_mode: str) -> go.Figure: + """Visualizes the trade-off between model performance and cost/token usage.""" fig = go.Figure() - - fig.add_trace( - go.Scatter( - x=x_data, - y=model_metrics["overall_score"], - mode="markers+text", - marker=dict( - size=15, - color=model_metrics["Duration"], - colorscale="RdYlGn_r", # Red for slow, green for fast - showscale=True, - colorbar=dict(title="Avg Duration (sec)"), - ), - text=model_metrics["Model"], - textposition="top center", - hovertemplate="%{text}
" - + "Avg Score: %{y:.1f}%
" - + f"{hover_x_label}: %{{x{hover_x_format}}}
" - + "Avg Duration: %{marker.color:.1f}s
" - + "", - ) + if df.empty or not selected_cases: + return fig.update_layout(title="No data available for selected filters.") + + df_filtered = df[df["difficulty"].isin(selected_cases)] + model_metrics = df_filtered.groupby("Model").agg( + correctness_score=("correctness_score", "mean"), + total_cost=("total_cost", "mean"), + total_response_tokens=("total_response_tokens", "mean"), + Duration=("Duration", "mean"), + ).reset_index() + + x_data, x_title, hover_label, hover_format = ( + (model_metrics["total_cost"], "Average Total Cost ($)", "Avg Cost", ":.4f") + if x_axis_mode == "cost" + else (model_metrics["total_response_tokens"], "Average Total Response Tokens", "Avg Tokens", ":.0f") ) + fig.add_trace(go.Scatter( + x=x_data, + y=model_metrics["correctness_score"], + mode="markers+text", + marker=dict( + size=18, + color=model_metrics["Duration"], + colorscale="RdYlGn_r", + showscale=True, + colorbar=dict(title="Avg Duration (s)"), + ), + text=model_metrics["Model"], + textposition="top center", + hovertemplate=( + "%{text}
" + "Avg Score: %{y:.1f}%
" + f"{hover_label}: %{{x{hover_format}}}
" + "Avg Duration: %{marker.color:.1f}s" + ), + )) + fig.update_layout( - title=f"Pareto Frontier: Model Performance vs {'Cost' if x_axis_mode == 'cost' else 'Token Usage'}", + title=f"Performance vs. {'Cost' if x_axis_mode == 'cost' else 'Tokens'}", xaxis_title=x_title, - yaxis_title="Average Overall Score (%)", + yaxis_title="Average Success Rate (%)", showlegend=False, height=600, ) - return fig -def create_cost_configuration_ui(models: List[str]) -> Dict: - """Create UI for configuring model costs.""" - st.sidebar.subheader("💰 Cost Configuration") - st.sidebar.write("Set costs per 1M tokens:") - - cost_config = {} - - with st.sidebar.expander("Model Costs", expanded=False): - for model in models: - st.write(f"**{model}**") - - default_input = DEFAULT_COSTS.get(model, {"input": 1.0, "output": 3.0})[ - "input" - ] - default_output = DEFAULT_COSTS.get(model, {"input": 1.0, "output": 3.0})[ - "output" - ] - - col1, col2 = st.columns(2) - with col1: - input_cost = st.number_input( - "Input", - min_value=0.0, - value=default_input, - step=0.01, - format="%.3f", - key=f"input_{model}", - ) - with col2: - output_cost = st.number_input( - "Output", - min_value=0.0, - value=default_output, - step=0.01, - format="%.3f", - key=f"output_{model}", - ) - - cost_config[model] = {"input": input_cost, "output": output_cost} - - return cost_config +def create_success_rates_plot(df: pd.DataFrame, selected_cases: List[str]) -> go.Figure: + """Compares models across different success metrics.""" + fig = go.Figure() + if df.empty or not selected_cases: + return fig.update_layout(title="No data available for selected filters.") + df_filtered = df[df["difficulty"].isin(selected_cases)] + metric_cols = [col for col in df_filtered.columns if col.startswith("Score_")] + if not metric_cols: + return fig.update_layout(title="No 'Score_' columns found.") -def create_token_breakdown_plot( - df: pd.DataFrame, selected_cases: List[str] -) -> go.Figure: - """Create a stacked bar chart showing token breakdown by model.""" - if df.empty: - return go.Figure() + models = sorted(df_filtered["Model"].unique()) + + for metric_col in metric_cols: + metric_name = metric_col.replace("Score_", "") + avg_scores = [ + df_filtered[df_filtered["Model"] == model][metric_col].mean() * 100 + for model in models + ] + fig.add_trace(go.Bar(name=metric_name, x=models, y=avg_scores)) - if selected_cases and "all" not in selected_cases: - df_filtered = df[df["difficulty"].isin(selected_cases)] - else: - df_filtered = df - - token_data = ( - df_filtered.groupby("Model") - .agg( - { - "Metric_request_tokens": "mean", - "Metric_response_tokens": "mean", - "thinking_tokens": "mean", - } - ) - .reset_index() + fig.update_layout( + title="Success Rate by Metric", + xaxis_title="Model", + yaxis_title="Success Rate (%)", + barmode="group", + height=500, + legend_title="Metric", ) + return fig - fig = go.Figure() - fig.add_trace( - go.Bar( - name="Request Tokens", - x=token_data["Model"], - y=token_data["Metric_request_tokens"], - marker_color="lightblue", - ) - ) +def create_failure_analysis_plot(df: pd.DataFrame, selected_cases: List[str]) -> go.Figure: + """Shows common failure reasons by model.""" + fig = go.Figure() + if df.empty or not selected_cases: + return fig.update_layout(title="No data available for selected filters.") - fig.add_trace( - go.Bar( - name="Response Tokens", - x=token_data["Model"], - y=token_data["Metric_response_tokens"], - marker_color="orange", - ) - ) + df_filtered = df[df["difficulty"].isin(selected_cases)] + models = sorted(df_filtered["Model"].unique()) + + failure_counts = { + "Invalid Diagram": [], + "MCP Tool Failure": [], + "Usage Limit Exceeded": [], + } + + for model in models: + model_data = df_filtered[df_filtered["Model"] == model] + failure_counts["Invalid Diagram"].append((model_data["Score_MermaidDiagramValid"] == 0).sum()) + failure_counts["MCP Tool Failure"].append((model_data["Score_UsedBothMCPTools"] < 1).sum()) + failure_counts["Usage Limit Exceeded"].append((model_data["Score_UsageLimitNotExceeded"] == 0).sum()) - fig.add_trace( - go.Bar( - name="Thinking Tokens", - x=token_data["Model"], - y=token_data["thinking_tokens"], - marker_color="lightgreen", - ) - ) + for reason, counts in failure_counts.items(): + fig.add_trace(go.Bar(name=reason, x=models, y=counts)) fig.update_layout( - title="Token Usage Breakdown by Model", + title="Failure Analysis by Reason", xaxis_title="Model", - yaxis_title="Average Tokens", + yaxis_title="Number of Failures", barmode="stack", height=500, + legend_title="Failure Reason", ) - return fig -def create_success_rates_plot(df: pd.DataFrame, selected_cases: List[str]) -> go.Figure: - """Create a bar chart showing overall average success rates by metric.""" - if df.empty: - return go.Figure() - - if selected_cases and "all" not in selected_cases: - df_filtered = df[df["difficulty"].isin(selected_cases)] - else: - df_filtered = df - - metric_columns = [col for col in df_filtered.columns if col.startswith("Score_")] +def create_token_breakdown_plot(df: pd.DataFrame, selected_cases: List[str]) -> go.Figure: + """Creates a stacked bar chart showing token breakdown.""" + fig = go.Figure() + if df.empty or not selected_cases: + return fig.update_layout(title="No data available for selected filters.") - if not metric_columns: - return go.Figure() + df_filtered = df[df["difficulty"].isin(selected_cases)] + token_data = df_filtered.groupby("Model").agg( + Request=("Metric_request_tokens", "mean"), + Response=("Metric_response_tokens", "mean"), + Thinking=("thinking_tokens", "mean"), + ).reset_index() - models = sorted(df_filtered["Model"].unique()) + fig.add_trace(go.Bar(name="Request", x=token_data["Model"], y=token_data["Request"])) + fig.add_trace(go.Bar(name="Response", x=token_data["Model"], y=token_data["Response"])) + fig.add_trace(go.Bar(name="Thinking", x=token_data["Model"], y=token_data["Thinking"])) - metric_data = [] - for metric_col in metric_columns: - metric_name = metric_col.replace("Score_", "") - model_scores = [] - for model in models: - model_data = df_filtered[df_filtered["Model"] == model] - if not model_data.empty: - avg_score = model_data[metric_col].mean() * 100 - model_scores.append(avg_score) - else: - model_scores.append(0) + fig.update_layout( + title="Average Token Usage by Type", + xaxis_title="Model", + yaxis_title="Average Tokens", + barmode="stack", + height=500, + legend_title="Token Type", + ) + return fig - metric_data.append( - {"metric": metric_name, "models": models, "scores": model_scores} - ) +def create_cost_breakdown_plot(df: pd.DataFrame, selected_cases: List[str]) -> go.Figure: + """Creates a stacked bar chart for cost breakdown.""" fig = go.Figure() + if df.empty or not selected_cases: + return fig.update_layout(title="No data available for selected filters.") - colors = ["#1f77b4", "#ff7f0e", "#2ca02c", "#d62728", "#9467bd", "#8c564b"] + df_filtered = df[df["difficulty"].isin(selected_cases)] + cost_data = df_filtered.groupby("Model").agg( + Input=("input_cost", "mean"), + Output=("output_cost", "mean"), + ).reset_index() - for i, data in enumerate(metric_data): - fig.add_trace( - go.Bar( - name=data["metric"], - x=data["models"], - y=data["scores"], - marker_color=colors[i % len(colors)], - ) - ) + fig.add_trace(go.Bar(name="Input Cost", x=cost_data["Model"], y=cost_data["Input"])) + fig.add_trace(go.Bar(name="Output Cost", x=cost_data["Model"], y=cost_data["Output"])) fig.update_layout( - title="Overall Average Success Rates by Metric", + title="Average Cost Breakdown by Type", xaxis_title="Model", - yaxis_title="Success Rate (%)", - barmode="group", + yaxis_title="Average Cost ($)", + barmode="stack", height=500, - legend=dict(title="Metric"), + legend_title="Cost Type", ) - return fig def main(): - """Main Streamlit application.""" - + """Main Streamlit application entrypoint.""" st.title("🏆 Merbench") st.subheader("LLM Evaluation Benchmark Dashboard") - st.sidebar.header("Configuration") - - st.sidebar.subheader("Detected CSV Files") - - script_dir = os.path.dirname( - os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) - ) - default_dir = os.path.join(script_dir, "mermaid_eval_results") - - custom_dir = st.sidebar.text_input( - "Directory path:", - value=default_dir, - help="Path to directory containing *_combined_results.csv files", - ) - - if "refresh_files" not in st.session_state: - st.session_state.refresh_files = False - - if st.sidebar.button("🔄 Refresh Detected Files"): - st.session_state.refresh_files = True + # --- Sidebar Setup --- + st.sidebar.header("⚙️ Configuration") + + # File selection + default_dir_path = os.path.dirname(detect_csv_files()[0]) if detect_csv_files() else "" + custom_dir = st.sidebar.text_input("Results Directory:", value=default_dir_path) + + if st.sidebar.button("🔄 Refresh Files"): st.rerun() csv_files = detect_csv_files(custom_dir) - if not csv_files: - st.warning(f"No *_combined_results.csv files found in {custom_dir}") - st.info("Looking for files matching pattern: *_combined_results.csv") + st.warning(f"No `*_combined_results.csv` files found in `{custom_dir}`.") return - st.sidebar.write(f"Found {len(csv_files)} files (sorted by newest first):") - - selected_files = [] - for file in csv_files: - file_name = os.path.basename(file) - if st.sidebar.checkbox(file_name, value=True, key=f"file_{file_name}"): - selected_files.append(file) - + selected_files = st.sidebar.multiselect( + "Select result files:", + options=[os.path.basename(f) for f in csv_files], + default=[os.path.basename(f) for f in csv_files], + ) + if not selected_files: - st.warning("Please select at least one CSV file to visualize.") + st.info("Select one or more result files to begin analysis.") return - with st.spinner("Loading data..."): - df_initial = load_csv_data(selected_files) - if df_initial.empty: - st.error("No data could be loaded from the selected files.") - return + # --- Data Loading and Filtering --- + full_file_paths = [os.path.join(custom_dir, f) for f in selected_files] + df_initial = load_csv_data(full_file_paths) + + if df_initial.empty: + st.error("No data loaded. Please check the selected files.") + return available_models = sorted(df_initial["Model"].unique()) - cost_config = create_cost_configuration_ui(available_models) + + # Cost configuration in sidebar + st.sidebar.subheader("💰 Cost Configuration") + cost_config = {} + with st.sidebar.expander("Edit Model Costs (per 1M tokens)", expanded=False): + for model in available_models: + cols = st.columns(2) + default = DEFAULT_COSTS.get(model, {"input": 0.0, "output": 0.0}) + input_cost = cols[0].number_input(f"{model} Input", value=float(default["input"]), step=0.01, format="%.2f") + output_cost = cols[1].number_input(f"{model} Output", value=float(default["output"]), step=0.01, format="%.2f") + cost_config[model] = {"input": input_cost, "output": output_cost} df = process_data(df_initial, cost_config) - available_cases = sorted(df["difficulty"].unique()) - st.sidebar.subheader("Case Selection") - - case_options = ["all"] + available_cases - selected_cases = st.sidebar.multiselect( - "Select difficulty levels", options=case_options, default=["all"] + # Difficulty filter + st.sidebar.subheader("🎯 Difficulty Filter") + available_difficulties = sorted(df["difficulty"].unique()) + selected_difficulties = st.sidebar.multiselect( + "Filter by difficulty:", + options=available_difficulties, + default=available_difficulties, ) - st.subheader("📊 Data Overview") - - overview_col1, overview_col2 = st.columns(2) - with overview_col1: - st.metric("Evaluation Runs", len(df)) - st.metric("Files Loaded", len(selected_files)) + # --- Main Panel --- + st.header("📊 Overview") + + # Key metrics + cols = st.columns(4) + cols[0].metric("Evaluation Runs", len(df)) + cols[1].metric("Models Evaluated", df["Model"].nunique()) + cols[2].metric("Test Cases", df["Case"].nunique()) + cols[3].metric("Files Loaded", len(selected_files)) - with overview_col2: - st.metric("Models", len(df["Model"].unique())) - st.metric("Cases", len(df["Case"].unique())) - - st.write(f"**Models:** {', '.join(sorted(df['Model'].unique()))}") - st.write(f"**Cases:** {', '.join(sorted(df['Case'].unique()))}") - - if "all" not in selected_cases: - st.info(f"🎯 **Filtered by difficulty:** {', '.join(selected_cases)}") - else: - st.info("🎯 **Showing all difficulty levels**") + st.info(f"**Showing results for difficulties:** {', '.join(selected_difficulties) if selected_difficulties else 'None'}") + # --- Leaderboard & Pareto --- st.header("🏅 Leaderboard") - leaderboard_df = create_leaderboard(df, selected_cases) - + leaderboard_df = create_leaderboard(df, selected_difficulties) + if not leaderboard_df.empty: - display_df = leaderboard_df.copy() - display_df["Avg_Score"] = display_df["Avg_Score"].round(1) - display_df["Avg_Duration"] = display_df["Avg_Duration"].round(2) - display_df["Avg_Tokens"] = display_df["Avg_Tokens"].round(0) - display_df["Avg_Cost"] = display_df["Avg_Cost"].round(4) - display_df["Efficiency"] = display_df["Efficiency"].round(1) - display_df["Cost_Efficiency"] = display_df["Cost_Efficiency"].round(1) - + st.markdown(""" + + """, unsafe_allow_html=True) st.dataframe( - display_df, + leaderboard_df, column_config={ - "Avg_Score": st.column_config.NumberColumn( - "Avg Score (%)", format="%.1f" - ), - "Avg_Duration": st.column_config.NumberColumn( - "Avg Duration (s)", format="%.2f" - ), - "Avg_Tokens": st.column_config.NumberColumn( - "Avg Tokens", format="%.0f" - ), - "Avg_Cost": st.column_config.NumberColumn( - "Avg Cost ($)", format="%.4f" + "Correct": st.column_config.ProgressColumn( + "Avg. Success", + format="%.1f%%", + min_value=0, + max_value=100, ), - "Efficiency": st.column_config.NumberColumn( - "Tokens/sec", format="%.1f" + "Cost": st.column_config.ProgressColumn( + "Avg Cost ($)", + format="$%.4f", + min_value=0, + max_value=leaderboard_df["Cost"].max(), ), - "Cost_Efficiency": st.column_config.NumberColumn( - "Score/Cost", format="%.1f" - ), - "Run_Count": st.column_config.NumberColumn("Runs", format="%d"), + "Duration": st.column_config.NumberColumn("Avg Duration (s)", format="%.2fs"), + "Tokens": st.column_config.NumberColumn("Avg Tokens", format="%.0f"), }, + column_order=["Model", "Correct", "Cost", "Duration", "Tokens", "Runs"], use_container_width=True, ) else: - st.warning("No data available for the selected filters.") + st.warning("No data available for the current filter selection.") st.header("📈 Pareto Frontier Analysis") - - x_axis_mode = st.radio( - "X-Axis Mode:", - options=["tokens", "cost"], - format_func=lambda x: "Token Count" if x == "tokens" else "Cost ($)", - horizontal=True, - ) - - pareto_fig = create_pareto_frontier_plot(df, selected_cases, x_axis_mode) - st.plotly_chart(pareto_fig, use_container_width=True) - - st.header("📊 Overall Average Success Rates by Metric") - success_fig = create_success_rates_plot(df, selected_cases) - st.plotly_chart(success_fig, use_container_width=True) - - st.header("🔢 Token Usage Analysis") - token_fig = create_token_breakdown_plot(df, selected_cases) - st.plotly_chart(token_fig, use_container_width=True) - - with st.expander("📋 Raw Data"): - if selected_cases and "all" not in selected_cases: - filtered_df = df[df["difficulty"].isin(selected_cases)] - else: - filtered_df = df - - st.dataframe(filtered_df, use_container_width=True) + x_axis_mode = st.radio("Compare performance against:", ["cost", "tokens"], format_func=lambda x: x.capitalize(), horizontal=True) + st.plotly_chart(create_pareto_frontier_plot(df, selected_difficulties, x_axis_mode), use_container_width=True) + + # --- Deep Dive Analysis --- + with st.expander("🔍 Deep Dive Analysis", expanded=False): + tab1, tab2, tab3, tab4 = st.tabs(["📊 Success Rates", "📉 Failure Analysis", "⚙️ Resource Usage", "📋 Raw Data"]) + + with tab1: + st.plotly_chart(create_success_rates_plot(df, selected_difficulties), use_container_width=True) + with tab2: + st.plotly_chart(create_failure_analysis_plot(df, selected_difficulties), use_container_width=True) + with tab3: + st.plotly_chart(create_token_breakdown_plot(df, selected_difficulties), use_container_width=True) + st.plotly_chart(create_cost_breakdown_plot(df, selected_difficulties), use_container_width=True) + with tab4: + st.dataframe(df[df["difficulty"].isin(selected_difficulties)], use_container_width=True) if __name__ == "__main__": From 486789b7b45679adfe4b91ef8336cb26df7256c2 Mon Sep 17 00:00:00 2001 From: Andrew Ginns Date: Sun, 8 Jun 2025 13:09:26 +0000 Subject: [PATCH 07/14] chore: Env deps updates --- pyproject.toml | 9 +- uv.lock | 429 ++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 416 insertions(+), 22 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index eea26ac..23be2b7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,12 +14,15 @@ dependencies = [ "langgraph>=0.3.31", "logfire>=3.14.0", "loguru>=0.7.3", - "mcp==1.6.0", + "mcp==1.9.0", "openai-agents>=0.0.12", - "pydantic-ai-slim[mcp]>=0.1.3", - "pydantic-evals[logfire]>=0.1.3", + "pandas>=2.3.0", + "plotly>=6.1.2", + "pydantic-ai-slim[mcp]>=0.2.15", + "pydantic-evals[logfire]>=0.2.15", "python-dotenv>=1.1.0", "ruff>=0.11.10", + "streamlit>=1.45.1", ] [tool.hatch.build.targets.wheel] diff --git a/uv.lock b/uv.lock index fb6ebc2..ab54dfd 100644 --- a/uv.lock +++ b/uv.lock @@ -56,6 +56,22 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ec/6a/bc7e17a3e87a2985d3e8f4da4cd0f481060eb78fb08596c42be62c90a4d9/aiosignal-1.3.2-py2.py3-none-any.whl", hash = "sha256:45cde58e409a301715980c2b01d0c28bdde3770d8290b5eb2173759d9acb31a5", size = 7597, upload-time = "2024-12-13T17:10:38.469Z" }, ] +[[package]] +name = "altair" +version = "5.5.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jinja2" }, + { name = "jsonschema" }, + { name = "narwhals" }, + { name = "packaging" }, + { name = "typing-extensions", marker = "python_full_version < '3.14'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/16/b1/f2969c7bdb8ad8bbdda031687defdce2c19afba2aa2c8e1d2a17f78376d8/altair-5.5.0.tar.gz", hash = "sha256:d960ebe6178c56de3855a68c47b516be38640b73fb3b5111c2a9ca90546dd73d", size = 705305, upload-time = "2024-11-23T23:39:58.542Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/aa/f3/0b6ced594e51cc95d8c1fc1640d3623770d01e4969d29c0bd09945fafefa/altair-5.5.0-py3-none-any.whl", hash = "sha256:91a310b926508d560fe0148d02a194f38b824122641ef528113d029fcd129f8c", size = 731200, upload-time = "2024-11-23T23:39:56.4Z" }, +] + [[package]] name = "annotated-types" version = "0.7.0" @@ -99,6 +115,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e3/71/8dcec996ea8cc882cec9cace91ae1b630a226b88b0f04ab2ffa778f565ad/authlib-1.5.2-py2.py3-none-any.whl", hash = "sha256:8804dd4402ac5e4a0435ac49e0b6e19e395357cfa632a3f624dcb4f6df13b4b1", size = 232055, upload-time = "2025-04-02T10:31:34.59Z" }, ] +[[package]] +name = "blinker" +version = "1.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/21/28/9b3f50ce0e048515135495f198351908d99540d69bfdc8c1d15b73dc55ce/blinker-1.9.0.tar.gz", hash = "sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf", size = 22460, upload-time = "2024-11-08T17:25:47.436Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/10/cb/f2ad4230dc2eb1a74edf38f1a38b9b52277f75bef262d8908e60d957e13c/blinker-1.9.0-py3-none-any.whl", hash = "sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc", size = 8458, upload-time = "2024-11-08T17:25:46.184Z" }, +] + [[package]] name = "cachetools" version = "5.5.2" @@ -344,6 +369,30 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/71/3e/b04a0adda73bd52b390d730071c0d577073d3d26740ee1bad25c3ad0f37b/frozenlist-1.6.0-py3-none-any.whl", hash = "sha256:535eec9987adb04701266b92745d6cdcef2e77669299359c3009c3404dd5d191", size = 12404, upload-time = "2025-04-17T22:38:51.668Z" }, ] +[[package]] +name = "gitdb" +version = "4.0.12" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "smmap" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/72/94/63b0fc47eb32792c7ba1fe1b694daec9a63620db1e313033d18140c2320a/gitdb-4.0.12.tar.gz", hash = "sha256:5ef71f855d191a3326fcfbc0d5da835f26b13fbcba60c32c21091c349ffdb571", size = 394684, upload-time = "2025-01-02T07:20:46.413Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl", hash = "sha256:67073e15955400952c6565cc3e707c554a4eea2e428946f7a4c162fab9bd9bcf", size = 62794, upload-time = "2025-01-02T07:20:43.624Z" }, +] + +[[package]] +name = "gitpython" +version = "3.1.44" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "gitdb" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c0/89/37df0b71473153574a5cdef8f242de422a0f5d26d7a9e231e6f169b4ad14/gitpython-3.1.44.tar.gz", hash = "sha256:c87e30b26253bf5418b01b0660f818967f3c503193838337fe5e573331249269", size = 214196, upload-time = "2025-01-02T07:32:43.59Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1d/9a/4114a9057db2f1462d5c8f8390ab7383925fe1ac012eaa42402ad65c2963/GitPython-3.1.44-py3-none-any.whl", hash = "sha256:9e0e10cda9bed1ee64bc9a6de50e7e38a9c9943241cd7f585f6df3ed28011110", size = 207599, upload-time = "2025-01-02T07:32:40.731Z" }, +] + [[package]] name = "google-adk" version = "0.2.0" @@ -821,6 +870,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/79/9d/0fb148dc4d6fa4a7dd1d8378168d9b4cd8d4560a6fbf6f0121c5fc34eb68/importlib_metadata-8.6.1-py3-none-any.whl", hash = "sha256:02a89390c1e15fdfdc0d7c6b25cb3e62650d0494005c97d6f148bf5b9787525e", size = 26971, upload-time = "2025-01-20T22:21:29.177Z" }, ] +[[package]] +name = "jinja2" +version = "3.1.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, +] + [[package]] name = "jiter" version = "0.9.0" @@ -865,6 +926,33 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/71/92/5e77f98553e9e75130c78900d000368476aed74276eb8ae8796f65f00918/jsonpointer-3.0.0-py2.py3-none-any.whl", hash = "sha256:13e088adc14fca8b6aa8177c044e12701e6ad4b28ff10e65f2267a90109c9942", size = 7595, upload-time = "2024-06-10T19:24:40.698Z" }, ] +[[package]] +name = "jsonschema" +version = "4.24.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "jsonschema-specifications" }, + { name = "referencing" }, + { name = "rpds-py" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/bf/d3/1cf5326b923a53515d8f3a2cd442e6d7e94fcc444716e879ea70a0ce3177/jsonschema-4.24.0.tar.gz", hash = "sha256:0b4e8069eb12aedfa881333004bccaec24ecef5a8a6a4b6df142b2cc9599d196", size = 353480, upload-time = "2025-05-26T18:48:10.459Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a2/3d/023389198f69c722d039351050738d6755376c8fd343e91dc493ea485905/jsonschema-4.24.0-py3-none-any.whl", hash = "sha256:a462455f19f5faf404a7902952b6f0e3ce868f3ee09a359b05eca6673bd8412d", size = 88709, upload-time = "2025-05-26T18:48:08.417Z" }, +] + +[[package]] +name = "jsonschema-specifications" +version = "2025.4.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "referencing" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/bf/ce/46fbd9c8119cfc3581ee5643ea49464d168028cfb5caff5fc0596d0cf914/jsonschema_specifications-2025.4.1.tar.gz", hash = "sha256:630159c9f4dbea161a6a2205c3011cc4f18ff381b189fff48bb39b9bf26ae608", size = 15513, upload-time = "2025-04-23T12:34:07.418Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/01/0e/b27cdbaccf30b890c40ed1da9fd4a3593a5cf94dae54fb34f8a4b74fcd3f/jsonschema_specifications-2025.4.1-py3-none-any.whl", hash = "sha256:4653bffbd6584f7de83a67e0d620ef16900b390ddc7939d56684d6c81e33f1af", size = 18437, upload-time = "2025-04-23T12:34:05.422Z" }, +] + [[package]] name = "langchain" version = "0.3.22" @@ -941,15 +1029,15 @@ wheels = [ [[package]] name = "langchain-mcp-adapters" -version = "0.0.9" +version = "0.1.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "langchain-core" }, { name = "mcp" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/1a/48/dc5544f5b919b4ff9e736ec8db71217431c585c5c87acd3ab7558cc06cee/langchain_mcp_adapters-0.0.9.tar.gz", hash = "sha256:9ecd10fc420d98b3c14115bbca3174575e0a4ea29bd125ef39d11191a72ff1a1", size = 14827, upload-time = "2025-04-16T15:03:05.158Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b0/de/62a6ee2c21f74eed961773e75a4e3170f8abc79fd5fd7a1b4e2ea07f4c04/langchain_mcp_adapters-0.1.1.tar.gz", hash = "sha256:e43ddf06e4ce237ad80f5c91d0efa7fd7c845b274a5a07e7e8a7f9d7239331d7", size = 17359, upload-time = "2025-05-20T14:20:35.025Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6f/24/3a4be149e8db15936533357f987b4b89c74c7f039427d6229679dbcc53b9/langchain_mcp_adapters-0.0.9-py3-none-any.whl", hash = "sha256:7c3dedd7830de826f418706c8a2fe388afcf8daf2037a1b39d1e065a5eacb082", size = 10065, upload-time = "2025-04-16T15:03:04.324Z" }, + { url = "https://files.pythonhosted.org/packages/30/68/13405f252b38a8e1bd7ef345907a4e0eda535c2ca36fe4d6821fc7e9f5de/langchain_mcp_adapters-0.1.1-py3-none-any.whl", hash = "sha256:81594b265d824012040ebd24056fbdb5aabf0b46f780e369ed132421e3411e4d", size = 12100, upload-time = "2025-05-20T14:20:34.236Z" }, ] [[package]] @@ -1089,6 +1177,34 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528, upload-time = "2023-06-03T06:41:11.019Z" }, ] +[[package]] +name = "markupsafe" +version = "3.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537, upload-time = "2024-10-18T15:21:54.129Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/83/0e/67eb10a7ecc77a0c2bbe2b0235765b98d164d81600746914bebada795e97/MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", size = 14274, upload-time = "2024-10-18T15:21:24.577Z" }, + { url = "https://files.pythonhosted.org/packages/2b/6d/9409f3684d3335375d04e5f05744dfe7e9f120062c9857df4ab490a1031a/MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", size = 12352, upload-time = "2024-10-18T15:21:25.382Z" }, + { url = "https://files.pythonhosted.org/packages/d2/f5/6eadfcd3885ea85fe2a7c128315cc1bb7241e1987443d78c8fe712d03091/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", size = 24122, upload-time = "2024-10-18T15:21:26.199Z" }, + { url = "https://files.pythonhosted.org/packages/0c/91/96cf928db8236f1bfab6ce15ad070dfdd02ed88261c2afafd4b43575e9e9/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", size = 23085, upload-time = "2024-10-18T15:21:27.029Z" }, + { url = "https://files.pythonhosted.org/packages/c2/cf/c9d56af24d56ea04daae7ac0940232d31d5a8354f2b457c6d856b2057d69/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79", size = 22978, upload-time = "2024-10-18T15:21:27.846Z" }, + { url = "https://files.pythonhosted.org/packages/2a/9f/8619835cd6a711d6272d62abb78c033bda638fdc54c4e7f4272cf1c0962b/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", size = 24208, upload-time = "2024-10-18T15:21:28.744Z" }, + { url = "https://files.pythonhosted.org/packages/f9/bf/176950a1792b2cd2102b8ffeb5133e1ed984547b75db47c25a67d3359f77/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca", size = 23357, upload-time = "2024-10-18T15:21:29.545Z" }, + { url = "https://files.pythonhosted.org/packages/ce/4f/9a02c1d335caabe5c4efb90e1b6e8ee944aa245c1aaaab8e8a618987d816/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", size = 23344, upload-time = "2024-10-18T15:21:30.366Z" }, + { url = "https://files.pythonhosted.org/packages/ee/55/c271b57db36f748f0e04a759ace9f8f759ccf22b4960c270c78a394f58be/MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", size = 15101, upload-time = "2024-10-18T15:21:31.207Z" }, + { url = "https://files.pythonhosted.org/packages/29/88/07df22d2dd4df40aba9f3e402e6dc1b8ee86297dddbad4872bd5e7b0094f/MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", size = 15603, upload-time = "2024-10-18T15:21:32.032Z" }, + { url = "https://files.pythonhosted.org/packages/62/6a/8b89d24db2d32d433dffcd6a8779159da109842434f1dd2f6e71f32f738c/MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", size = 14510, upload-time = "2024-10-18T15:21:33.625Z" }, + { url = "https://files.pythonhosted.org/packages/7a/06/a10f955f70a2e5a9bf78d11a161029d278eeacbd35ef806c3fd17b13060d/MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", size = 12486, upload-time = "2024-10-18T15:21:34.611Z" }, + { url = "https://files.pythonhosted.org/packages/34/cf/65d4a571869a1a9078198ca28f39fba5fbb910f952f9dbc5220afff9f5e6/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", size = 25480, upload-time = "2024-10-18T15:21:35.398Z" }, + { url = "https://files.pythonhosted.org/packages/0c/e3/90e9651924c430b885468b56b3d597cabf6d72be4b24a0acd1fa0e12af67/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", size = 23914, upload-time = "2024-10-18T15:21:36.231Z" }, + { url = "https://files.pythonhosted.org/packages/66/8c/6c7cf61f95d63bb866db39085150df1f2a5bd3335298f14a66b48e92659c/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe", size = 23796, upload-time = "2024-10-18T15:21:37.073Z" }, + { url = "https://files.pythonhosted.org/packages/bb/35/cbe9238ec3f47ac9a7c8b3df7a808e7cb50fe149dc7039f5f454b3fba218/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", size = 25473, upload-time = "2024-10-18T15:21:37.932Z" }, + { url = "https://files.pythonhosted.org/packages/e6/32/7621a4382488aa283cc05e8984a9c219abad3bca087be9ec77e89939ded9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a", size = 24114, upload-time = "2024-10-18T15:21:39.799Z" }, + { url = "https://files.pythonhosted.org/packages/0d/80/0985960e4b89922cb5a0bac0ed39c5b96cbc1a536a99f30e8c220a996ed9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", size = 24098, upload-time = "2024-10-18T15:21:40.813Z" }, + { url = "https://files.pythonhosted.org/packages/82/78/fedb03c7d5380df2427038ec8d973587e90561b2d90cd472ce9254cf348b/MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", size = 15208, upload-time = "2024-10-18T15:21:41.814Z" }, + { url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739, upload-time = "2024-10-18T15:21:42.784Z" }, +] + [[package]] name = "marshmallow" version = "3.26.1" @@ -1103,7 +1219,7 @@ wheels = [ [[package]] name = "mcp" -version = "1.6.0" +version = "1.9.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, @@ -1111,13 +1227,14 @@ dependencies = [ { name = "httpx-sse" }, { name = "pydantic" }, { name = "pydantic-settings" }, + { name = "python-multipart" }, { name = "sse-starlette" }, { name = "starlette" }, - { name = "uvicorn" }, + { name = "uvicorn", marker = "sys_platform != 'emscripten'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/95/d2/f587cb965a56e992634bebc8611c5b579af912b74e04eb9164bd49527d21/mcp-1.6.0.tar.gz", hash = "sha256:d9324876de2c5637369f43161cd71eebfd803df5a95e46225cab8d280e366723", size = 200031, upload-time = "2025-03-27T16:46:32.336Z" } +sdist = { url = "https://files.pythonhosted.org/packages/bc/8d/0f4468582e9e97b0a24604b585c651dfd2144300ecffd1c06a680f5c8861/mcp-1.9.0.tar.gz", hash = "sha256:905d8d208baf7e3e71d70c82803b89112e321581bcd2530f9de0fe4103d28749", size = 281432, upload-time = "2025-05-15T18:51:06.615Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/10/30/20a7f33b0b884a9d14dd3aa94ff1ac9da1479fe2ad66dd9e2736075d2506/mcp-1.6.0-py3-none-any.whl", hash = "sha256:7bd24c6ea042dbec44c754f100984d186620d8b841ec30f1b19eda9b93a634d0", size = 76077, upload-time = "2025-03-27T16:46:29.919Z" }, + { url = "https://files.pythonhosted.org/packages/a5/d5/22e36c95c83c80eb47c83f231095419cf57cf5cca5416f1c960032074c78/mcp-1.9.0-py3-none-any.whl", hash = "sha256:9dfb89c8c56f742da10a5910a1f64b0d2ac2c3ed2bd572ddb1cfab7f35957178", size = 125082, upload-time = "2025-05-15T18:51:04.916Z" }, ] [[package]] @@ -1181,6 +1298,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2a/e2/5d3f6ada4297caebe1a2add3b126fe800c96f56dbe5d1988a2cbe0b267aa/mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d", size = 4695, upload-time = "2023-02-04T12:11:25.002Z" }, ] +[[package]] +name = "narwhals" +version = "1.41.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ce/1b/877c22912b78f8b3fe60de1fb908a624c47afab0d6f9f32b5a1703566ff1/narwhals-1.41.1.tar.gz", hash = "sha256:be973f27b9eca2bab82c789b9c63135b5cd2a726c80309146356dd923b6f5104", size = 489404, upload-time = "2025-06-06T07:29:24.141Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/47/9f/ba87ba354282d81c681b98733479c17d9f3dcfa5532e6105509db44a04b6/narwhals-1.41.1-py3-none-any.whl", hash = "sha256:42325449d9e1133e235b9a5b45c71132845dd5a4524940828753d9f7ca5ae303", size = 358034, upload-time = "2025-06-06T07:29:22.236Z" }, +] + [[package]] name = "numpy" version = "2.2.5" @@ -1420,6 +1546,76 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451, upload-time = "2024-11-08T09:47:44.722Z" }, ] +[[package]] +name = "pandas" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, + { name = "python-dateutil" }, + { name = "pytz" }, + { name = "tzdata" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/72/51/48f713c4c728d7c55ef7444ba5ea027c26998d96d1a40953b346438602fc/pandas-2.3.0.tar.gz", hash = "sha256:34600ab34ebf1131a7613a260a61dbe8b62c188ec0ea4c296da7c9a06b004133", size = 4484490, upload-time = "2025-06-05T03:27:54.133Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d3/57/5cb75a56a4842bbd0511c3d1c79186d8315b82dac802118322b2de1194fe/pandas-2.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2c7e2fc25f89a49a11599ec1e76821322439d90820108309bf42130d2f36c983", size = 11518913, upload-time = "2025-06-05T03:27:02.757Z" }, + { url = "https://files.pythonhosted.org/packages/05/01/0c8785610e465e4948a01a059562176e4c8088aa257e2e074db868f86d4e/pandas-2.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c6da97aeb6a6d233fb6b17986234cc723b396b50a3c6804776351994f2a658fd", size = 10655249, upload-time = "2025-06-05T16:50:20.17Z" }, + { url = "https://files.pythonhosted.org/packages/e8/6a/47fd7517cd8abe72a58706aab2b99e9438360d36dcdb052cf917b7bf3bdc/pandas-2.3.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb32dc743b52467d488e7a7c8039b821da2826a9ba4f85b89ea95274f863280f", size = 11328359, upload-time = "2025-06-05T03:27:06.431Z" }, + { url = "https://files.pythonhosted.org/packages/2a/b3/463bfe819ed60fb7e7ddffb4ae2ee04b887b3444feee6c19437b8f834837/pandas-2.3.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:213cd63c43263dbb522c1f8a7c9d072e25900f6975596f883f4bebd77295d4f3", size = 12024789, upload-time = "2025-06-05T03:27:09.875Z" }, + { url = "https://files.pythonhosted.org/packages/04/0c/e0704ccdb0ac40aeb3434d1c641c43d05f75c92e67525df39575ace35468/pandas-2.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1d2b33e68d0ce64e26a4acc2e72d747292084f4e8db4c847c6f5f6cbe56ed6d8", size = 12480734, upload-time = "2025-06-06T00:00:22.246Z" }, + { url = "https://files.pythonhosted.org/packages/e9/df/815d6583967001153bb27f5cf075653d69d51ad887ebbf4cfe1173a1ac58/pandas-2.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:430a63bae10b5086995db1b02694996336e5a8ac9a96b4200572b413dfdfccb9", size = 13223381, upload-time = "2025-06-05T03:27:15.641Z" }, + { url = "https://files.pythonhosted.org/packages/79/88/ca5973ed07b7f484c493e941dbff990861ca55291ff7ac67c815ce347395/pandas-2.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:4930255e28ff5545e2ca404637bcc56f031893142773b3468dc021c6c32a1390", size = 10970135, upload-time = "2025-06-05T03:27:24.131Z" }, + { url = "https://files.pythonhosted.org/packages/24/fb/0994c14d1f7909ce83f0b1fb27958135513c4f3f2528bde216180aa73bfc/pandas-2.3.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:f925f1ef673b4bd0271b1809b72b3270384f2b7d9d14a189b12b7fc02574d575", size = 12141356, upload-time = "2025-06-05T03:27:34.547Z" }, + { url = "https://files.pythonhosted.org/packages/9d/a2/9b903e5962134497ac4f8a96f862ee3081cb2506f69f8e4778ce3d9c9d82/pandas-2.3.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e78ad363ddb873a631e92a3c063ade1ecfb34cae71e9a2be6ad100f875ac1042", size = 11474674, upload-time = "2025-06-05T03:27:39.448Z" }, + { url = "https://files.pythonhosted.org/packages/81/3a/3806d041bce032f8de44380f866059437fb79e36d6b22c82c187e65f765b/pandas-2.3.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:951805d146922aed8357e4cc5671b8b0b9be1027f0619cea132a9f3f65f2f09c", size = 11439876, upload-time = "2025-06-05T03:27:43.652Z" }, + { url = "https://files.pythonhosted.org/packages/15/aa/3fc3181d12b95da71f5c2537c3e3b3af6ab3a8c392ab41ebb766e0929bc6/pandas-2.3.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a881bc1309f3fce34696d07b00f13335c41f5f5a8770a33b09ebe23261cfc67", size = 11966182, upload-time = "2025-06-05T03:27:47.652Z" }, + { url = "https://files.pythonhosted.org/packages/37/e7/e12f2d9b0a2c4a2cc86e2aabff7ccfd24f03e597d770abfa2acd313ee46b/pandas-2.3.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:e1991bbb96f4050b09b5f811253c4f3cf05ee89a589379aa36cd623f21a31d6f", size = 12547686, upload-time = "2025-06-06T00:00:26.142Z" }, + { url = "https://files.pythonhosted.org/packages/39/c2/646d2e93e0af70f4e5359d870a63584dacbc324b54d73e6b3267920ff117/pandas-2.3.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:bb3be958022198531eb7ec2008cfc78c5b1eed51af8600c6c5d9160d89d8d249", size = 13231847, upload-time = "2025-06-05T03:27:51.465Z" }, +] + +[[package]] +name = "pillow" +version = "11.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/af/cb/bb5c01fcd2a69335b86c22142b2bccfc3464087efb7fd382eee5ffc7fdf7/pillow-11.2.1.tar.gz", hash = "sha256:a64dd61998416367b7ef979b73d3a85853ba9bec4c2925f74e588879a58716b6", size = 47026707, upload-time = "2025-04-12T17:50:03.289Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/36/9c/447528ee3776e7ab8897fe33697a7ff3f0475bb490c5ac1456a03dc57956/pillow-11.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:fdec757fea0b793056419bca3e9932eb2b0ceec90ef4813ea4c1e072c389eb28", size = 3190098, upload-time = "2025-04-12T17:48:23.915Z" }, + { url = "https://files.pythonhosted.org/packages/b5/09/29d5cd052f7566a63e5b506fac9c60526e9ecc553825551333e1e18a4858/pillow-11.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b0e130705d568e2f43a17bcbe74d90958e8a16263868a12c3e0d9c8162690830", size = 3030166, upload-time = "2025-04-12T17:48:25.738Z" }, + { url = "https://files.pythonhosted.org/packages/71/5d/446ee132ad35e7600652133f9c2840b4799bbd8e4adba881284860da0a36/pillow-11.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bdb5e09068332578214cadd9c05e3d64d99e0e87591be22a324bdbc18925be0", size = 4408674, upload-time = "2025-04-12T17:48:27.908Z" }, + { url = "https://files.pythonhosted.org/packages/69/5f/cbe509c0ddf91cc3a03bbacf40e5c2339c4912d16458fcb797bb47bcb269/pillow-11.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d189ba1bebfbc0c0e529159631ec72bb9e9bc041f01ec6d3233d6d82eb823bc1", size = 4496005, upload-time = "2025-04-12T17:48:29.888Z" }, + { url = "https://files.pythonhosted.org/packages/f9/b3/dd4338d8fb8a5f312021f2977fb8198a1184893f9b00b02b75d565c33b51/pillow-11.2.1-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:191955c55d8a712fab8934a42bfefbf99dd0b5875078240943f913bb66d46d9f", size = 4518707, upload-time = "2025-04-12T17:48:31.874Z" }, + { url = "https://files.pythonhosted.org/packages/13/eb/2552ecebc0b887f539111c2cd241f538b8ff5891b8903dfe672e997529be/pillow-11.2.1-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:ad275964d52e2243430472fc5d2c2334b4fc3ff9c16cb0a19254e25efa03a155", size = 4610008, upload-time = "2025-04-12T17:48:34.422Z" }, + { url = "https://files.pythonhosted.org/packages/72/d1/924ce51bea494cb6e7959522d69d7b1c7e74f6821d84c63c3dc430cbbf3b/pillow-11.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:750f96efe0597382660d8b53e90dd1dd44568a8edb51cb7f9d5d918b80d4de14", size = 4585420, upload-time = "2025-04-12T17:48:37.641Z" }, + { url = "https://files.pythonhosted.org/packages/43/ab/8f81312d255d713b99ca37479a4cb4b0f48195e530cdc1611990eb8fd04b/pillow-11.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fe15238d3798788d00716637b3d4e7bb6bde18b26e5d08335a96e88564a36b6b", size = 4667655, upload-time = "2025-04-12T17:48:39.652Z" }, + { url = "https://files.pythonhosted.org/packages/94/86/8f2e9d2dc3d308dfd137a07fe1cc478df0a23d42a6c4093b087e738e4827/pillow-11.2.1-cp313-cp313-win32.whl", hash = "sha256:3fe735ced9a607fee4f481423a9c36701a39719252a9bb251679635f99d0f7d2", size = 2332329, upload-time = "2025-04-12T17:48:41.765Z" }, + { url = "https://files.pythonhosted.org/packages/6d/ec/1179083b8d6067a613e4d595359b5fdea65d0a3b7ad623fee906e1b3c4d2/pillow-11.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:74ee3d7ecb3f3c05459ba95eed5efa28d6092d751ce9bf20e3e253a4e497e691", size = 2676388, upload-time = "2025-04-12T17:48:43.625Z" }, + { url = "https://files.pythonhosted.org/packages/23/f1/2fc1e1e294de897df39fa8622d829b8828ddad938b0eaea256d65b84dd72/pillow-11.2.1-cp313-cp313-win_arm64.whl", hash = "sha256:5119225c622403afb4b44bad4c1ca6c1f98eed79db8d3bc6e4e160fc6339d66c", size = 2414950, upload-time = "2025-04-12T17:48:45.475Z" }, + { url = "https://files.pythonhosted.org/packages/c4/3e/c328c48b3f0ead7bab765a84b4977acb29f101d10e4ef57a5e3400447c03/pillow-11.2.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:8ce2e8411c7aaef53e6bb29fe98f28cd4fbd9a1d9be2eeea434331aac0536b22", size = 3192759, upload-time = "2025-04-12T17:48:47.866Z" }, + { url = "https://files.pythonhosted.org/packages/18/0e/1c68532d833fc8b9f404d3a642991441d9058eccd5606eab31617f29b6d4/pillow-11.2.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:9ee66787e095127116d91dea2143db65c7bb1e232f617aa5957c0d9d2a3f23a7", size = 3033284, upload-time = "2025-04-12T17:48:50.189Z" }, + { url = "https://files.pythonhosted.org/packages/b7/cb/6faf3fb1e7705fd2db74e070f3bf6f88693601b0ed8e81049a8266de4754/pillow-11.2.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9622e3b6c1d8b551b6e6f21873bdcc55762b4b2126633014cea1803368a9aa16", size = 4445826, upload-time = "2025-04-12T17:48:52.346Z" }, + { url = "https://files.pythonhosted.org/packages/07/94/8be03d50b70ca47fb434a358919d6a8d6580f282bbb7af7e4aa40103461d/pillow-11.2.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63b5dff3a68f371ea06025a1a6966c9a1e1ee452fc8020c2cd0ea41b83e9037b", size = 4527329, upload-time = "2025-04-12T17:48:54.403Z" }, + { url = "https://files.pythonhosted.org/packages/fd/a4/bfe78777076dc405e3bd2080bc32da5ab3945b5a25dc5d8acaa9de64a162/pillow-11.2.1-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:31df6e2d3d8fc99f993fd253e97fae451a8db2e7207acf97859732273e108406", size = 4549049, upload-time = "2025-04-12T17:48:56.383Z" }, + { url = "https://files.pythonhosted.org/packages/65/4d/eaf9068dc687c24979e977ce5677e253624bd8b616b286f543f0c1b91662/pillow-11.2.1-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:062b7a42d672c45a70fa1f8b43d1d38ff76b63421cbbe7f88146b39e8a558d91", size = 4635408, upload-time = "2025-04-12T17:48:58.782Z" }, + { url = "https://files.pythonhosted.org/packages/1d/26/0fd443365d9c63bc79feb219f97d935cd4b93af28353cba78d8e77b61719/pillow-11.2.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4eb92eca2711ef8be42fd3f67533765d9fd043b8c80db204f16c8ea62ee1a751", size = 4614863, upload-time = "2025-04-12T17:49:00.709Z" }, + { url = "https://files.pythonhosted.org/packages/49/65/dca4d2506be482c2c6641cacdba5c602bc76d8ceb618fd37de855653a419/pillow-11.2.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f91ebf30830a48c825590aede79376cb40f110b387c17ee9bd59932c961044f9", size = 4692938, upload-time = "2025-04-12T17:49:02.946Z" }, + { url = "https://files.pythonhosted.org/packages/b3/92/1ca0c3f09233bd7decf8f7105a1c4e3162fb9142128c74adad0fb361b7eb/pillow-11.2.1-cp313-cp313t-win32.whl", hash = "sha256:e0b55f27f584ed623221cfe995c912c61606be8513bfa0e07d2c674b4516d9dd", size = 2335774, upload-time = "2025-04-12T17:49:04.889Z" }, + { url = "https://files.pythonhosted.org/packages/a5/ac/77525347cb43b83ae905ffe257bbe2cc6fd23acb9796639a1f56aa59d191/pillow-11.2.1-cp313-cp313t-win_amd64.whl", hash = "sha256:36d6b82164c39ce5482f649b437382c0fb2395eabc1e2b1702a6deb8ad647d6e", size = 2681895, upload-time = "2025-04-12T17:49:06.635Z" }, + { url = "https://files.pythonhosted.org/packages/67/32/32dc030cfa91ca0fc52baebbba2e009bb001122a1daa8b6a79ad830b38d3/pillow-11.2.1-cp313-cp313t-win_arm64.whl", hash = "sha256:225c832a13326e34f212d2072982bb1adb210e0cc0b153e688743018c94a2681", size = 2417234, upload-time = "2025-04-12T17:49:08.399Z" }, +] + +[[package]] +name = "plotly" +version = "6.1.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "narwhals" }, + { name = "packaging" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ae/77/431447616eda6a432dc3ce541b3f808ecb8803ea3d4ab2573b67f8eb4208/plotly-6.1.2.tar.gz", hash = "sha256:4fdaa228926ba3e3a213f4d1713287e69dcad1a7e66cf2025bd7d7026d5014b4", size = 7662971, upload-time = "2025-05-27T20:21:52.56Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bf/6f/759d5da0517547a5d38aabf05d04d9f8adf83391d2c7fc33f904417d3ba2/plotly-6.1.2-py3-none-any.whl", hash = "sha256:f1548a8ed9158d59e03d7fed548c7db5549f3130d9ae19293c8638c202648f6d", size = 16265530, upload-time = "2025-05-27T20:21:46.6Z" }, +] + [[package]] name = "propcache" version = "0.3.1" @@ -1487,6 +1683,32 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/12/fb/a586e0c973c95502e054ac5f81f88394f24ccc7982dac19c515acd9e2c93/protobuf-5.29.4-py3-none-any.whl", hash = "sha256:3fde11b505e1597f71b875ef2fc52062b6a9740e5f7c8997ce878b6009145862", size = 172551, upload-time = "2025-03-19T21:23:22.682Z" }, ] +[[package]] +name = "pyarrow" +version = "20.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/ee/a7810cb9f3d6e9238e61d312076a9859bf3668fd21c69744de9532383912/pyarrow-20.0.0.tar.gz", hash = "sha256:febc4a913592573c8d5805091a6c2b5064c8bd6e002131f01061797d91c783c1", size = 1125187, upload-time = "2025-04-27T12:34:23.264Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9b/aa/daa413b81446d20d4dad2944110dcf4cf4f4179ef7f685dd5a6d7570dc8e/pyarrow-20.0.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:a15532e77b94c61efadde86d10957950392999503b3616b2ffcef7621a002893", size = 30798501, upload-time = "2025-04-27T12:30:48.351Z" }, + { url = "https://files.pythonhosted.org/packages/ff/75/2303d1caa410925de902d32ac215dc80a7ce7dd8dfe95358c165f2adf107/pyarrow-20.0.0-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:dd43f58037443af715f34f1322c782ec463a3c8a94a85fdb2d987ceb5658e061", size = 32277895, upload-time = "2025-04-27T12:30:55.238Z" }, + { url = "https://files.pythonhosted.org/packages/92/41/fe18c7c0b38b20811b73d1bdd54b1fccba0dab0e51d2048878042d84afa8/pyarrow-20.0.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa0d288143a8585806e3cc7c39566407aab646fb9ece164609dac1cfff45f6ae", size = 41327322, upload-time = "2025-04-27T12:31:05.587Z" }, + { url = "https://files.pythonhosted.org/packages/da/ab/7dbf3d11db67c72dbf36ae63dcbc9f30b866c153b3a22ef728523943eee6/pyarrow-20.0.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b6953f0114f8d6f3d905d98e987d0924dabce59c3cda380bdfaa25a6201563b4", size = 42411441, upload-time = "2025-04-27T12:31:15.675Z" }, + { url = "https://files.pythonhosted.org/packages/90/c3/0c7da7b6dac863af75b64e2f827e4742161128c350bfe7955b426484e226/pyarrow-20.0.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:991f85b48a8a5e839b2128590ce07611fae48a904cae6cab1f089c5955b57eb5", size = 40677027, upload-time = "2025-04-27T12:31:24.631Z" }, + { url = "https://files.pythonhosted.org/packages/be/27/43a47fa0ff9053ab5203bb3faeec435d43c0d8bfa40179bfd076cdbd4e1c/pyarrow-20.0.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:97c8dc984ed09cb07d618d57d8d4b67a5100a30c3818c2fb0b04599f0da2de7b", size = 42281473, upload-time = "2025-04-27T12:31:31.311Z" }, + { url = "https://files.pythonhosted.org/packages/bc/0b/d56c63b078876da81bbb9ba695a596eabee9b085555ed12bf6eb3b7cab0e/pyarrow-20.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9b71daf534f4745818f96c214dbc1e6124d7daf059167330b610fc69b6f3d3e3", size = 42893897, upload-time = "2025-04-27T12:31:39.406Z" }, + { url = "https://files.pythonhosted.org/packages/92/ac/7d4bd020ba9145f354012838692d48300c1b8fe5634bfda886abcada67ed/pyarrow-20.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e8b88758f9303fa5a83d6c90e176714b2fd3852e776fc2d7e42a22dd6c2fb368", size = 44543847, upload-time = "2025-04-27T12:31:45.997Z" }, + { url = "https://files.pythonhosted.org/packages/9d/07/290f4abf9ca702c5df7b47739c1b2c83588641ddfa2cc75e34a301d42e55/pyarrow-20.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:30b3051b7975801c1e1d387e17c588d8ab05ced9b1e14eec57915f79869b5031", size = 25653219, upload-time = "2025-04-27T12:31:54.11Z" }, + { url = "https://files.pythonhosted.org/packages/95/df/720bb17704b10bd69dde086e1400b8eefb8f58df3f8ac9cff6c425bf57f1/pyarrow-20.0.0-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:ca151afa4f9b7bc45bcc791eb9a89e90a9eb2772767d0b1e5389609c7d03db63", size = 30853957, upload-time = "2025-04-27T12:31:59.215Z" }, + { url = "https://files.pythonhosted.org/packages/d9/72/0d5f875efc31baef742ba55a00a25213a19ea64d7176e0fe001c5d8b6e9a/pyarrow-20.0.0-cp313-cp313t-macosx_12_0_x86_64.whl", hash = "sha256:4680f01ecd86e0dd63e39eb5cd59ef9ff24a9d166db328679e36c108dc993d4c", size = 32247972, upload-time = "2025-04-27T12:32:05.369Z" }, + { url = "https://files.pythonhosted.org/packages/d5/bc/e48b4fa544d2eea72f7844180eb77f83f2030b84c8dad860f199f94307ed/pyarrow-20.0.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7f4c8534e2ff059765647aa69b75d6543f9fef59e2cd4c6d18015192565d2b70", size = 41256434, upload-time = "2025-04-27T12:32:11.814Z" }, + { url = "https://files.pythonhosted.org/packages/c3/01/974043a29874aa2cf4f87fb07fd108828fc7362300265a2a64a94965e35b/pyarrow-20.0.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3e1f8a47f4b4ae4c69c4d702cfbdfe4d41e18e5c7ef6f1bb1c50918c1e81c57b", size = 42353648, upload-time = "2025-04-27T12:32:20.766Z" }, + { url = "https://files.pythonhosted.org/packages/68/95/cc0d3634cde9ca69b0e51cbe830d8915ea32dda2157560dda27ff3b3337b/pyarrow-20.0.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:a1f60dc14658efaa927f8214734f6a01a806d7690be4b3232ba526836d216122", size = 40619853, upload-time = "2025-04-27T12:32:28.1Z" }, + { url = "https://files.pythonhosted.org/packages/29/c2/3ad40e07e96a3e74e7ed7cc8285aadfa84eb848a798c98ec0ad009eb6bcc/pyarrow-20.0.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:204a846dca751428991346976b914d6d2a82ae5b8316a6ed99789ebf976551e6", size = 42241743, upload-time = "2025-04-27T12:32:35.792Z" }, + { url = "https://files.pythonhosted.org/packages/eb/cb/65fa110b483339add6a9bc7b6373614166b14e20375d4daa73483755f830/pyarrow-20.0.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:f3b117b922af5e4c6b9a9115825726cac7d8b1421c37c2b5e24fbacc8930612c", size = 42839441, upload-time = "2025-04-27T12:32:46.64Z" }, + { url = "https://files.pythonhosted.org/packages/98/7b/f30b1954589243207d7a0fbc9997401044bf9a033eec78f6cb50da3f304a/pyarrow-20.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:e724a3fd23ae5b9c010e7be857f4405ed5e679db5c93e66204db1a69f733936a", size = 44503279, upload-time = "2025-04-27T12:32:56.503Z" }, + { url = "https://files.pythonhosted.org/packages/37/40/ad395740cd641869a13bcf60851296c89624662575621968dcfafabaa7f6/pyarrow-20.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:82f1ee5133bd8f49d31be1299dc07f585136679666b502540db854968576faf9", size = 25944982, upload-time = "2025-04-27T12:33:04.72Z" }, +] + [[package]] name = "pyasn1" version = "0.6.1" @@ -1547,10 +1769,13 @@ dependencies = [ { name = "loguru" }, { name = "mcp" }, { name = "openai-agents" }, + { name = "pandas" }, + { name = "plotly" }, { name = "pydantic-ai-slim", extra = ["mcp"] }, { name = "pydantic-evals", extra = ["logfire"] }, { name = "python-dotenv" }, { name = "ruff" }, + { name = "streamlit" }, ] [package.metadata] @@ -1564,17 +1789,20 @@ requires-dist = [ { name = "langgraph", specifier = ">=0.3.31" }, { name = "logfire", specifier = ">=3.14.0" }, { name = "loguru", specifier = ">=0.7.3" }, - { name = "mcp", specifier = "==1.6.0" }, + { name = "mcp", specifier = "==1.9.0" }, { name = "openai-agents", specifier = ">=0.0.12" }, - { name = "pydantic-ai-slim", extras = ["mcp"], specifier = ">=0.1.3" }, - { name = "pydantic-evals", extras = ["logfire"], specifier = ">=0.1.3" }, + { name = "pandas", specifier = ">=2.3.0" }, + { name = "plotly", specifier = ">=6.1.2" }, + { name = "pydantic-ai-slim", extras = ["mcp"], specifier = ">=0.2.15" }, + { name = "pydantic-evals", extras = ["logfire"], specifier = ">=0.2.15" }, { name = "python-dotenv", specifier = ">=1.1.0" }, { name = "ruff", specifier = ">=0.11.10" }, + { name = "streamlit", specifier = ">=1.45.1" }, ] [[package]] name = "pydantic-ai-slim" -version = "0.1.3" +version = "0.2.15" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "eval-type-backport" }, @@ -1585,9 +1813,9 @@ dependencies = [ { name = "pydantic-graph" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a1/35/19abd306fd62d32a56089badeb0e45ed0b3889dee3497dd9b3b44733f4fd/pydantic_ai_slim-0.1.3.tar.gz", hash = "sha256:9c23db1372c8965f324fcde51481f70d6ae4139287f56d3d8ceab82228d4bbf6", size = 118745, upload-time = "2025-04-18T15:10:56.925Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b3/7d/24a128014c647f047611eea7fa4e18494240eafbc9565fcebafe1a188102/pydantic_ai_slim-0.2.15.tar.gz", hash = "sha256:5c98d1ef74da49d7566c39664cf00d993eb1c167e5a896f833155dffc65a5e82", size = 138333, upload-time = "2025-06-05T19:37:04.175Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c0/bd/823ba9aff48490bd51ec5aaf760307a6d7f695489f5f162b1dbeaad6adb9/pydantic_ai_slim-0.1.3-py3-none-any.whl", hash = "sha256:685b59aa9875e57177cbdd32d9fdaebe7dd3e69b8e37bc12a2644e470fa202f4", size = 152749, upload-time = "2025-04-18T15:10:48.613Z" }, + { url = "https://files.pythonhosted.org/packages/29/7a/36696ef1284fd8c54c09aea0d9dba87cecfae6f3fe1c561e17cdd843f602/pydantic_ai_slim-0.2.15-py3-none-any.whl", hash = "sha256:33f6211af4b4eaa9cb5139e01f0881a7b91cd42dbfde185ad97f1fd457f41844", size = 186703, upload-time = "2025-06-05T19:36:51.998Z" }, ] [package.optional-dependencies] @@ -1622,7 +1850,7 @@ wheels = [ [[package]] name = "pydantic-evals" -version = "0.1.3" +version = "0.2.15" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, @@ -1632,9 +1860,9 @@ dependencies = [ { name = "pyyaml" }, { name = "rich" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/8d/32/90227a60e3a2e10f74166d0b7daa4c3c157a03b82fb90eace2e365c75e89/pydantic_evals-0.1.3.tar.gz", hash = "sha256:162e56ebdd1a85c06a689b977a3f25825a6f5873a54e6ce78d17a54cc6c787ba", size = 40846, upload-time = "2025-04-18T15:10:57.907Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d4/61/060a56a8d00886df3fecd02314b024370871fb15195c23f9591d6498c032/pydantic_evals-0.2.15.tar.gz", hash = "sha256:82fe113b58af51f0c1b44d36c8ab52e67970421a8bc5b837b5124c19263a0ecc", size = 42908, upload-time = "2025-06-05T19:37:05.217Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5f/38/13b550fc87ef599ff939bec582b54e465159e75b8a587bb120825baeb950/pydantic_evals-0.1.3-py3-none-any.whl", hash = "sha256:72d21bf006daa7344c57dbcdd812e5c02003c699af717dc80a49327b32adbf56", size = 49420, upload-time = "2025-04-18T15:10:50.116Z" }, + { url = "https://files.pythonhosted.org/packages/80/89/930acc3c98db21b3a1e8f0f9dd39958ea4a34ad54cbe6c91f8bb3bded2c3/pydantic_evals-0.2.15-py3-none-any.whl", hash = "sha256:b74aeffe9858fd71215e9eae4c025b39d3a2fa445f9f1ea57067936fc4ead418", size = 51643, upload-time = "2025-06-05T19:36:53.632Z" }, ] [package.optional-dependencies] @@ -1644,7 +1872,7 @@ logfire = [ [[package]] name = "pydantic-graph" -version = "0.1.3" +version = "0.2.15" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "httpx" }, @@ -1652,9 +1880,9 @@ dependencies = [ { name = "pydantic" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ee/b3/3b6ebd8dc234e63712fbcb40ca3a7de584c34ae155c7d72de0997e35db01/pydantic_graph-0.1.3.tar.gz", hash = "sha256:ce9e494a7db62cf27095e42a4ac43dbff36e8736824436140b436c4a745df97a", size = 20745, upload-time = "2025-04-18T15:10:59.272Z" } +sdist = { url = "https://files.pythonhosted.org/packages/68/c3/af16b3ca23dc4029cbb8453e81c87079897534402c0da8a548118682ef43/pydantic_graph-0.2.15.tar.gz", hash = "sha256:47d19fa047ed996e7f448ef493462444f626da1046f8cf9bddc2567732e8336f", size = 21844, upload-time = "2025-06-05T19:37:06.378Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a6/da/6aeb8f67a009c70986cd27ad1244bc105e1145e09faa2ae8b3b7ff7ca5c0/pydantic_graph-0.1.3-py3-none-any.whl", hash = "sha256:60f26fefdddbfedbe7e57d0289c8a42b903004b5e44038adec5b92db36b55f89", size = 26103, upload-time = "2025-04-18T15:10:51.799Z" }, + { url = "https://files.pythonhosted.org/packages/4c/f6/94bc071815298a4de961d7e683ea495803fb3a4fae799675c7c80a66983f/pydantic_graph-0.2.15-py3-none-any.whl", hash = "sha256:7bbc9e5eb39ca6f79b51e270c255c5811d3d5b7ee8b9c36e64ad00577ecef365", size = 27486, upload-time = "2025-06-05T19:36:54.967Z" }, ] [[package]] @@ -1671,6 +1899,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b6/5f/d6d641b490fd3ec2c4c13b4244d68deea3a1b970a97be64f34fb5504ff72/pydantic_settings-2.9.1-py3-none-any.whl", hash = "sha256:59b4f431b1defb26fe620c71a7d3968a710d719f5f4cdbbdb7926edeb770f6ef", size = 44356, upload-time = "2025-04-18T16:44:46.617Z" }, ] +[[package]] +name = "pydeck" +version = "0.9.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jinja2" }, + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a1/ca/40e14e196864a0f61a92abb14d09b3d3da98f94ccb03b49cf51688140dab/pydeck-0.9.1.tar.gz", hash = "sha256:f74475ae637951d63f2ee58326757f8d4f9cd9f2a457cf42950715003e2cb605", size = 3832240, upload-time = "2024-05-10T15:36:21.153Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ab/4c/b888e6cf58bd9db9c93f40d1c6be8283ff49d88919231afe93a6bcf61626/pydeck-0.9.1-py2.py3-none-any.whl", hash = "sha256:b3f75ba0d273fc917094fa61224f3f6076ca8752b93d46faf3bcfd9f9d59b038", size = 6900403, upload-time = "2024-05-10T15:36:17.36Z" }, +] + [[package]] name = "pygments" version = "2.19.1" @@ -1710,6 +1951,24 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/1e/18/98a99ad95133c6a6e2005fe89faedf294a748bd5dc803008059409ac9b1e/python_dotenv-1.1.0-py3-none-any.whl", hash = "sha256:d7c01d9e2293916c18baf562d95698754b0dbbb5e74d457c45d4f6561fb9d55d", size = 20256, upload-time = "2025-03-25T10:14:55.034Z" }, ] +[[package]] +name = "python-multipart" +version = "0.0.20" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f3/87/f44d7c9f274c7ee665a29b885ec97089ec5dc034c7f3fafa03da9e39a09e/python_multipart-0.0.20.tar.gz", hash = "sha256:8dd0cab45b8e23064ae09147625994d090fa46f5b0d1e13af944c331a7fa9d13", size = 37158, upload-time = "2024-12-16T19:45:46.972Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/45/58/38b5afbc1a800eeea951b9285d3912613f2603bdf897a4ab0f4bd7f405fc/python_multipart-0.0.20-py3-none-any.whl", hash = "sha256:8a62d3a8335e06589fe01f2a3e178cdcc632f3fbe0d492ad9ee0ec35aab1f104", size = 24546, upload-time = "2024-12-16T19:45:44.423Z" }, +] + +[[package]] +name = "pytz" +version = "2025.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f8/bf/abbd3cdfb8fbc7fb3d4d38d320f2441b1e7cbe29be4f23797b4a2b5d8aac/pytz-2025.2.tar.gz", hash = "sha256:360b9e3dbb49a209c21ad61809c7fb453643e048b38924c765813546746e81c3", size = 320884, upload-time = "2025-03-25T02:25:00.538Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/81/c4/34e93fe5f5429d7570ec1fa436f1986fb1f00c3e0f43a589fe2bbcd22c3f/pytz-2025.2-py2.py3-none-any.whl", hash = "sha256:5ddf76296dd8c44c26eb8f4b6f35488f3ccbf6fbbd7adee0b7262d43f0ec2f00", size = 509225, upload-time = "2025-03-25T02:24:58.468Z" }, +] + [[package]] name = "pyyaml" version = "6.0.2" @@ -1727,6 +1986,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446, upload-time = "2024-08-06T20:33:04.33Z" }, ] +[[package]] +name = "referencing" +version = "0.36.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "rpds-py" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2f/db/98b5c277be99dd18bfd91dd04e1b759cad18d1a338188c936e92f921c7e2/referencing-0.36.2.tar.gz", hash = "sha256:df2e89862cd09deabbdba16944cc3f10feb6b3e6f18e902f7cc25609a34775aa", size = 74744, upload-time = "2025-01-25T08:48:16.138Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c1/b1/3baf80dc6d2b7bc27a95a67752d0208e410351e3feb4eb78de5f77454d8d/referencing-0.36.2-py3-none-any.whl", hash = "sha256:e8699adbbf8b5c7de96d8ffa0eb5c158b3beafce084968e2ea8bb08c6794dcd0", size = 26775, upload-time = "2025-01-25T08:48:14.241Z" }, +] + [[package]] name = "requests" version = "2.32.3" @@ -1767,6 +2039,41 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/0d/9b/63f4c7ebc259242c89b3acafdb37b41d1185c07ff0011164674e9076b491/rich-14.0.0-py3-none-any.whl", hash = "sha256:1c9491e1951aac09caffd42f448ee3d04e58923ffe14993f6e83068dc395d7e0", size = 243229, upload-time = "2025-03-30T14:15:12.283Z" }, ] +[[package]] +name = "rpds-py" +version = "0.25.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8c/a6/60184b7fc00dd3ca80ac635dd5b8577d444c57e8e8742cecabfacb829921/rpds_py-0.25.1.tar.gz", hash = "sha256:8960b6dac09b62dac26e75d7e2c4a22efb835d827a7278c34f72b2b84fa160e3", size = 27304, upload-time = "2025-05-21T12:46:12.502Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2b/da/323848a2b62abe6a0fec16ebe199dc6889c5d0a332458da8985b2980dffe/rpds_py-0.25.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:659d87430a8c8c704d52d094f5ba6fa72ef13b4d385b7e542a08fc240cb4a559", size = 364498, upload-time = "2025-05-21T12:43:54.841Z" }, + { url = "https://files.pythonhosted.org/packages/1f/b4/4d3820f731c80fd0cd823b3e95b9963fec681ae45ba35b5281a42382c67d/rpds_py-0.25.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:68f6f060f0bbdfb0245267da014d3a6da9be127fe3e8cc4a68c6f833f8a23bb1", size = 350083, upload-time = "2025-05-21T12:43:56.428Z" }, + { url = "https://files.pythonhosted.org/packages/d5/b1/3a8ee1c9d480e8493619a437dec685d005f706b69253286f50f498cbdbcf/rpds_py-0.25.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:083a9513a33e0b92cf6e7a6366036c6bb43ea595332c1ab5c8ae329e4bcc0a9c", size = 389023, upload-time = "2025-05-21T12:43:57.995Z" }, + { url = "https://files.pythonhosted.org/packages/3b/31/17293edcfc934dc62c3bf74a0cb449ecd549531f956b72287203e6880b87/rpds_py-0.25.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:816568614ecb22b18a010c7a12559c19f6fe993526af88e95a76d5a60b8b75fb", size = 403283, upload-time = "2025-05-21T12:43:59.546Z" }, + { url = "https://files.pythonhosted.org/packages/d1/ca/e0f0bc1a75a8925024f343258c8ecbd8828f8997ea2ac71e02f67b6f5299/rpds_py-0.25.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3c6564c0947a7f52e4792983f8e6cf9bac140438ebf81f527a21d944f2fd0a40", size = 524634, upload-time = "2025-05-21T12:44:01.087Z" }, + { url = "https://files.pythonhosted.org/packages/3e/03/5d0be919037178fff33a6672ffc0afa04ea1cfcb61afd4119d1b5280ff0f/rpds_py-0.25.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c4a128527fe415d73cf1f70a9a688d06130d5810be69f3b553bf7b45e8acf79", size = 416233, upload-time = "2025-05-21T12:44:02.604Z" }, + { url = "https://files.pythonhosted.org/packages/05/7c/8abb70f9017a231c6c961a8941403ed6557664c0913e1bf413cbdc039e75/rpds_py-0.25.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a49e1d7a4978ed554f095430b89ecc23f42014a50ac385eb0c4d163ce213c325", size = 390375, upload-time = "2025-05-21T12:44:04.162Z" }, + { url = "https://files.pythonhosted.org/packages/7a/ac/a87f339f0e066b9535074a9f403b9313fd3892d4a164d5d5f5875ac9f29f/rpds_py-0.25.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d74ec9bc0e2feb81d3f16946b005748119c0f52a153f6db6a29e8cd68636f295", size = 424537, upload-time = "2025-05-21T12:44:06.175Z" }, + { url = "https://files.pythonhosted.org/packages/1f/8f/8d5c1567eaf8c8afe98a838dd24de5013ce6e8f53a01bd47fe8bb06b5533/rpds_py-0.25.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3af5b4cc10fa41e5bc64e5c198a1b2d2864337f8fcbb9a67e747e34002ce812b", size = 566425, upload-time = "2025-05-21T12:44:08.242Z" }, + { url = "https://files.pythonhosted.org/packages/95/33/03016a6be5663b389c8ab0bbbcca68d9e96af14faeff0a04affcb587e776/rpds_py-0.25.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:79dc317a5f1c51fd9c6a0c4f48209c6b8526d0524a6904fc1076476e79b00f98", size = 595197, upload-time = "2025-05-21T12:44:10.449Z" }, + { url = "https://files.pythonhosted.org/packages/33/8d/da9f4d3e208c82fda311bff0cf0a19579afceb77cf456e46c559a1c075ba/rpds_py-0.25.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1521031351865e0181bc585147624d66b3b00a84109b57fcb7a779c3ec3772cd", size = 561244, upload-time = "2025-05-21T12:44:12.387Z" }, + { url = "https://files.pythonhosted.org/packages/e2/b3/39d5dcf7c5f742ecd6dbc88f6f84ae54184b92f5f387a4053be2107b17f1/rpds_py-0.25.1-cp313-cp313-win32.whl", hash = "sha256:5d473be2b13600b93a5675d78f59e63b51b1ba2d0476893415dfbb5477e65b31", size = 222254, upload-time = "2025-05-21T12:44:14.261Z" }, + { url = "https://files.pythonhosted.org/packages/5f/19/2d6772c8eeb8302c5f834e6d0dfd83935a884e7c5ce16340c7eaf89ce925/rpds_py-0.25.1-cp313-cp313-win_amd64.whl", hash = "sha256:a7b74e92a3b212390bdce1d93da9f6488c3878c1d434c5e751cbc202c5e09500", size = 234741, upload-time = "2025-05-21T12:44:16.236Z" }, + { url = "https://files.pythonhosted.org/packages/5b/5a/145ada26cfaf86018d0eb304fe55eafdd4f0b6b84530246bb4a7c4fb5c4b/rpds_py-0.25.1-cp313-cp313-win_arm64.whl", hash = "sha256:dd326a81afe332ede08eb39ab75b301d5676802cdffd3a8f287a5f0b694dc3f5", size = 224830, upload-time = "2025-05-21T12:44:17.749Z" }, + { url = "https://files.pythonhosted.org/packages/4b/ca/d435844829c384fd2c22754ff65889c5c556a675d2ed9eb0e148435c6690/rpds_py-0.25.1-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:a58d1ed49a94d4183483a3ce0af22f20318d4a1434acee255d683ad90bf78129", size = 359668, upload-time = "2025-05-21T12:44:19.322Z" }, + { url = "https://files.pythonhosted.org/packages/1f/01/b056f21db3a09f89410d493d2f6614d87bb162499f98b649d1dbd2a81988/rpds_py-0.25.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:f251bf23deb8332823aef1da169d5d89fa84c89f67bdfb566c49dea1fccfd50d", size = 345649, upload-time = "2025-05-21T12:44:20.962Z" }, + { url = "https://files.pythonhosted.org/packages/e0/0f/e0d00dc991e3d40e03ca36383b44995126c36b3eafa0ccbbd19664709c88/rpds_py-0.25.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8dbd586bfa270c1103ece2109314dd423df1fa3d9719928b5d09e4840cec0d72", size = 384776, upload-time = "2025-05-21T12:44:22.516Z" }, + { url = "https://files.pythonhosted.org/packages/9f/a2/59374837f105f2ca79bde3c3cd1065b2f8c01678900924949f6392eab66d/rpds_py-0.25.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6d273f136e912aa101a9274c3145dcbddbe4bac560e77e6d5b3c9f6e0ed06d34", size = 395131, upload-time = "2025-05-21T12:44:24.147Z" }, + { url = "https://files.pythonhosted.org/packages/9c/dc/48e8d84887627a0fe0bac53f0b4631e90976fd5d35fff8be66b8e4f3916b/rpds_py-0.25.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:666fa7b1bd0a3810a7f18f6d3a25ccd8866291fbbc3c9b912b917a6715874bb9", size = 520942, upload-time = "2025-05-21T12:44:25.915Z" }, + { url = "https://files.pythonhosted.org/packages/7c/f5/ee056966aeae401913d37befeeab57a4a43a4f00099e0a20297f17b8f00c/rpds_py-0.25.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:921954d7fbf3fccc7de8f717799304b14b6d9a45bbeec5a8d7408ccbf531faf5", size = 411330, upload-time = "2025-05-21T12:44:27.638Z" }, + { url = "https://files.pythonhosted.org/packages/ab/74/b2cffb46a097cefe5d17f94ede7a174184b9d158a0aeb195f39f2c0361e8/rpds_py-0.25.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3d86373ff19ca0441ebeb696ef64cb58b8b5cbacffcda5a0ec2f3911732a194", size = 387339, upload-time = "2025-05-21T12:44:29.292Z" }, + { url = "https://files.pythonhosted.org/packages/7f/9a/0ff0b375dcb5161c2b7054e7d0b7575f1680127505945f5cabaac890bc07/rpds_py-0.25.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c8980cde3bb8575e7c956a530f2c217c1d6aac453474bf3ea0f9c89868b531b6", size = 418077, upload-time = "2025-05-21T12:44:30.877Z" }, + { url = "https://files.pythonhosted.org/packages/0d/a1/fda629bf20d6b698ae84c7c840cfb0e9e4200f664fc96e1f456f00e4ad6e/rpds_py-0.25.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:8eb8c84ecea987a2523e057c0d950bcb3f789696c0499290b8d7b3107a719d78", size = 562441, upload-time = "2025-05-21T12:44:32.541Z" }, + { url = "https://files.pythonhosted.org/packages/20/15/ce4b5257f654132f326f4acd87268e1006cc071e2c59794c5bdf4bebbb51/rpds_py-0.25.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:e43a005671a9ed5a650f3bc39e4dbccd6d4326b24fb5ea8be5f3a43a6f576c72", size = 590750, upload-time = "2025-05-21T12:44:34.557Z" }, + { url = "https://files.pythonhosted.org/packages/fb/ab/e04bf58a8d375aeedb5268edcc835c6a660ebf79d4384d8e0889439448b0/rpds_py-0.25.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:58f77c60956501a4a627749a6dcb78dac522f249dd96b5c9f1c6af29bfacfb66", size = 558891, upload-time = "2025-05-21T12:44:37.358Z" }, + { url = "https://files.pythonhosted.org/packages/90/82/cb8c6028a6ef6cd2b7991e2e4ced01c854b6236ecf51e81b64b569c43d73/rpds_py-0.25.1-cp313-cp313t-win32.whl", hash = "sha256:2cb9e5b5e26fc02c8a4345048cd9998c2aca7c2712bd1b36da0c72ee969a3523", size = 218718, upload-time = "2025-05-21T12:44:38.969Z" }, + { url = "https://files.pythonhosted.org/packages/b6/97/5a4b59697111c89477d20ba8a44df9ca16b41e737fa569d5ae8bff99e650/rpds_py-0.25.1-cp313-cp313t-win_amd64.whl", hash = "sha256:401ca1c4a20cc0510d3435d89c069fe0a9ae2ee6495135ac46bdd49ec0495763", size = 232218, upload-time = "2025-05-21T12:44:40.512Z" }, +] + [[package]] name = "rsa" version = "4.9.1" @@ -1840,6 +2147,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, ] +[[package]] +name = "smmap" +version = "5.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/44/cd/a040c4b3119bbe532e5b0732286f805445375489fceaec1f48306068ee3b/smmap-5.0.2.tar.gz", hash = "sha256:26ea65a03958fa0c8a1c7e8c7a58fdc77221b8910f6be2131affade476898ad5", size = 22329, upload-time = "2025-01-02T07:14:40.909Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/be/d09147ad1ec7934636ad912901c5fd7667e1c858e19d355237db0d0cd5e4/smmap-5.0.2-py3-none-any.whl", hash = "sha256:b30115f0def7d7531d22a0fb6502488d879e75b260a9db4d0819cfb25403af5e", size = 24303, upload-time = "2025-01-02T07:14:38.724Z" }, +] + [[package]] name = "sniffio" version = "1.3.1" @@ -1895,6 +2211,35 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/8b/0c/9d30a4ebeb6db2b25a841afbb80f6ef9a854fc3b41be131d249a977b4959/starlette-0.46.2-py3-none-any.whl", hash = "sha256:595633ce89f8ffa71a015caed34a5b2dc1c0cdb3f0f1fbd1e69339cf2abeec35", size = 72037, upload-time = "2025-04-13T13:56:16.21Z" }, ] +[[package]] +name = "streamlit" +version = "1.45.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "altair" }, + { name = "blinker" }, + { name = "cachetools" }, + { name = "click" }, + { name = "gitpython" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "pandas" }, + { name = "pillow" }, + { name = "protobuf" }, + { name = "pyarrow" }, + { name = "pydeck" }, + { name = "requests" }, + { name = "tenacity" }, + { name = "toml" }, + { name = "tornado" }, + { name = "typing-extensions" }, + { name = "watchdog", marker = "sys_platform != 'darwin'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f0/46/9b3f73886f82d27849ce1e7a74ae7c39f5323e46da0b6e8847ad4c25f44c/streamlit-1.45.1.tar.gz", hash = "sha256:e37d56c0af5240dbc240976880e81366689c290a559376417246f9b3f51b4217", size = 9463953, upload-time = "2025-05-12T20:40:30.562Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/13/e6/69fcbae3dd2fcb2f54283a7cbe03c8b944b79997f1b526984f91d4796a02/streamlit-1.45.1-py3-none-any.whl", hash = "sha256:9ab6951585e9444672dd650850f81767b01bba5d87c8dac9bc2e1c859d6cc254", size = 9856294, upload-time = "2025-05-12T20:40:27.875Z" }, +] + [[package]] name = "tenacity" version = "9.1.2" @@ -1904,6 +2249,34 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e5/30/643397144bfbfec6f6ef821f36f33e57d35946c44a2352d3c9f0ae847619/tenacity-9.1.2-py3-none-any.whl", hash = "sha256:f77bf36710d8b73a50b2dd155c97b870017ad21afe6ab300326b0371b3b05138", size = 28248, upload-time = "2025-04-02T08:25:07.678Z" }, ] +[[package]] +name = "toml" +version = "0.10.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f", size = 22253, upload-time = "2020-11-01T01:40:22.204Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", size = 16588, upload-time = "2020-11-01T01:40:20.672Z" }, +] + +[[package]] +name = "tornado" +version = "6.5.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/51/89/c72771c81d25d53fe33e3dca61c233b665b2780f21820ba6fd2c6793c12b/tornado-6.5.1.tar.gz", hash = "sha256:84ceece391e8eb9b2b95578db65e920d2a61070260594819589609ba9bc6308c", size = 509934, upload-time = "2025-05-22T18:15:38.788Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/77/89/f4532dee6843c9e0ebc4e28d4be04c67f54f60813e4bf73d595fe7567452/tornado-6.5.1-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:d50065ba7fd11d3bd41bcad0825227cc9a95154bad83239357094c36708001f7", size = 441948, upload-time = "2025-05-22T18:15:20.862Z" }, + { url = "https://files.pythonhosted.org/packages/15/9a/557406b62cffa395d18772e0cdcf03bed2fff03b374677348eef9f6a3792/tornado-6.5.1-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:9e9ca370f717997cb85606d074b0e5b247282cf5e2e1611568b8821afe0342d6", size = 440112, upload-time = "2025-05-22T18:15:22.591Z" }, + { url = "https://files.pythonhosted.org/packages/55/82/7721b7319013a3cf881f4dffa4f60ceff07b31b394e459984e7a36dc99ec/tornado-6.5.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b77e9dfa7ed69754a54c89d82ef746398be82f749df69c4d3abe75c4d1ff4888", size = 443672, upload-time = "2025-05-22T18:15:24.027Z" }, + { url = "https://files.pythonhosted.org/packages/7d/42/d11c4376e7d101171b94e03cef0cbce43e823ed6567ceda571f54cf6e3ce/tornado-6.5.1-cp39-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:253b76040ee3bab8bcf7ba9feb136436a3787208717a1fb9f2c16b744fba7331", size = 443019, upload-time = "2025-05-22T18:15:25.735Z" }, + { url = "https://files.pythonhosted.org/packages/7d/f7/0c48ba992d875521ac761e6e04b0a1750f8150ae42ea26df1852d6a98942/tornado-6.5.1-cp39-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:308473f4cc5a76227157cdf904de33ac268af770b2c5f05ca6c1161d82fdd95e", size = 443252, upload-time = "2025-05-22T18:15:27.499Z" }, + { url = "https://files.pythonhosted.org/packages/89/46/d8d7413d11987e316df4ad42e16023cd62666a3c0dfa1518ffa30b8df06c/tornado-6.5.1-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:caec6314ce8a81cf69bd89909f4b633b9f523834dc1a352021775d45e51d9401", size = 443930, upload-time = "2025-05-22T18:15:29.299Z" }, + { url = "https://files.pythonhosted.org/packages/78/b2/f8049221c96a06df89bed68260e8ca94beca5ea532ffc63b1175ad31f9cc/tornado-6.5.1-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:13ce6e3396c24e2808774741331638ee6c2f50b114b97a55c5b442df65fd9692", size = 443351, upload-time = "2025-05-22T18:15:31.038Z" }, + { url = "https://files.pythonhosted.org/packages/76/ff/6a0079e65b326cc222a54720a748e04a4db246870c4da54ece4577bfa702/tornado-6.5.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:5cae6145f4cdf5ab24744526cc0f55a17d76f02c98f4cff9daa08ae9a217448a", size = 443328, upload-time = "2025-05-22T18:15:32.426Z" }, + { url = "https://files.pythonhosted.org/packages/49/18/e3f902a1d21f14035b5bc6246a8c0f51e0eef562ace3a2cea403c1fb7021/tornado-6.5.1-cp39-abi3-win32.whl", hash = "sha256:e0a36e1bc684dca10b1aa75a31df8bdfed656831489bc1e6a6ebed05dc1ec365", size = 444396, upload-time = "2025-05-22T18:15:34.205Z" }, + { url = "https://files.pythonhosted.org/packages/7b/09/6526e32bf1049ee7de3bebba81572673b19a2a8541f795d887e92af1a8bc/tornado-6.5.1-cp39-abi3-win_amd64.whl", hash = "sha256:908e7d64567cecd4c2b458075589a775063453aeb1d2a1853eedb806922f568b", size = 444840, upload-time = "2025-05-22T18:15:36.1Z" }, + { url = "https://files.pythonhosted.org/packages/55/a7/535c44c7bea4578e48281d83c615219f3ab19e6abc67625ef637c73987be/tornado-6.5.1-cp39-abi3-win_arm64.whl", hash = "sha256:02420a0eb7bf617257b9935e2b754d1b63897525d8a289c9d65690d580b4dcf7", size = 443596, upload-time = "2025-05-22T18:15:37.433Z" }, +] + [[package]] name = "tqdm" version = "4.67.1" @@ -2014,6 +2387,24 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b1/4b/4cef6ce21a2aaca9d852a6e84ef4f135d99fcd74fa75105e2fc0c8308acd/uvicorn-0.34.2-py3-none-any.whl", hash = "sha256:deb49af569084536d269fe0a6d67e3754f104cf03aba7c11c40f01aadf33c403", size = 62483, upload-time = "2025-04-19T06:02:48.42Z" }, ] +[[package]] +name = "watchdog" +version = "6.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/db/7d/7f3d619e951c88ed75c6037b246ddcf2d322812ee8ea189be89511721d54/watchdog-6.0.0.tar.gz", hash = "sha256:9ddf7c82fda3ae8e24decda1338ede66e1c99883db93711d8fb941eaa2d8c282", size = 131220, upload-time = "2024-11-01T14:07:13.037Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a9/c7/ca4bf3e518cb57a686b2feb4f55a1892fd9a3dd13f470fca14e00f80ea36/watchdog-6.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13", size = 79079, upload-time = "2024-11-01T14:06:59.472Z" }, + { url = "https://files.pythonhosted.org/packages/5c/51/d46dc9332f9a647593c947b4b88e2381c8dfc0942d15b8edc0310fa4abb1/watchdog-6.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:9041567ee8953024c83343288ccc458fd0a2d811d6a0fd68c4c22609e3490379", size = 79078, upload-time = "2024-11-01T14:07:01.431Z" }, + { url = "https://files.pythonhosted.org/packages/d4/57/04edbf5e169cd318d5f07b4766fee38e825d64b6913ca157ca32d1a42267/watchdog-6.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:82dc3e3143c7e38ec49d61af98d6558288c415eac98486a5c581726e0737c00e", size = 79076, upload-time = "2024-11-01T14:07:02.568Z" }, + { url = "https://files.pythonhosted.org/packages/ab/cc/da8422b300e13cb187d2203f20b9253e91058aaf7db65b74142013478e66/watchdog-6.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:212ac9b8bf1161dc91bd09c048048a95ca3a4c4f5e5d4a7d1b1a7d5752a7f96f", size = 79077, upload-time = "2024-11-01T14:07:03.893Z" }, + { url = "https://files.pythonhosted.org/packages/2c/3b/b8964e04ae1a025c44ba8e4291f86e97fac443bca31de8bd98d3263d2fcf/watchdog-6.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:e3df4cbb9a450c6d49318f6d14f4bbc80d763fa587ba46ec86f99f9e6876bb26", size = 79078, upload-time = "2024-11-01T14:07:05.189Z" }, + { url = "https://files.pythonhosted.org/packages/62/ae/a696eb424bedff7407801c257d4b1afda455fe40821a2be430e173660e81/watchdog-6.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:2cce7cfc2008eb51feb6aab51251fd79b85d9894e98ba847408f662b3395ca3c", size = 79077, upload-time = "2024-11-01T14:07:06.376Z" }, + { url = "https://files.pythonhosted.org/packages/b5/e8/dbf020b4d98251a9860752a094d09a65e1b436ad181faf929983f697048f/watchdog-6.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2", size = 79078, upload-time = "2024-11-01T14:07:07.547Z" }, + { url = "https://files.pythonhosted.org/packages/07/f6/d0e5b343768e8bcb4cda79f0f2f55051bf26177ecd5651f84c07567461cf/watchdog-6.0.0-py3-none-win32.whl", hash = "sha256:07df1fdd701c5d4c8e55ef6cf55b8f0120fe1aef7ef39a1c6fc6bc2e606d517a", size = 79065, upload-time = "2024-11-01T14:07:09.525Z" }, + { url = "https://files.pythonhosted.org/packages/db/d9/c495884c6e548fce18a8f40568ff120bc3a4b7b99813081c8ac0c936fa64/watchdog-6.0.0-py3-none-win_amd64.whl", hash = "sha256:cbafb470cf848d93b5d013e2ecb245d4aa1c8fd0504e863ccefa32445359d680", size = 79070, upload-time = "2024-11-01T14:07:10.686Z" }, + { url = "https://files.pythonhosted.org/packages/33/e8/e40370e6d74ddba47f002a32919d91310d6074130fe4e17dabcafc15cbf1/watchdog-6.0.0-py3-none-win_ia64.whl", hash = "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f", size = 79067, upload-time = "2024-11-01T14:07:11.845Z" }, +] + [[package]] name = "websockets" version = "15.0.1" From 7ada57cea6dc4d258d0332777348764e20738ba8 Mon Sep 17 00:00:00 2001 From: Andrew Ginns Date: Sun, 8 Jun 2025 13:09:53 +0000 Subject: [PATCH 08/14] chore: Better error tracking and cleanup --- .../eval_multi_mcp/evals_pydantic_mcp.py | 1 - .../eval_multi_mcp/run_multi_evals.py | 22 +++++++++++++++---- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/agents_mcp_usage/multi_mcp/eval_multi_mcp/evals_pydantic_mcp.py b/agents_mcp_usage/multi_mcp/eval_multi_mcp/evals_pydantic_mcp.py index e71ddf5..e7cc1e4 100644 --- a/agents_mcp_usage/multi_mcp/eval_multi_mcp/evals_pydantic_mcp.py +++ b/agents_mcp_usage/multi_mcp/eval_multi_mcp/evals_pydantic_mcp.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 """ Single-Model Evaluation Module for Mermaid Diagram Fixing diff --git a/agents_mcp_usage/multi_mcp/eval_multi_mcp/run_multi_evals.py b/agents_mcp_usage/multi_mcp/eval_multi_mcp/run_multi_evals.py index 94f2a38..9c42c28 100644 --- a/agents_mcp_usage/multi_mcp/eval_multi_mcp/run_multi_evals.py +++ b/agents_mcp_usage/multi_mcp/eval_multi_mcp/run_multi_evals.py @@ -207,19 +207,33 @@ async def fix_with_model(inputs: MermaidInput) -> MermaidOutput: logfire.warning( "Evaluation timeout", model=model, run_index=run_index, timeout=timeout ) - self.results[model].add_failed_run(run_index, error_msg) + self.results[model].add_failed_run(run_index, "evaluation_timeout") return None except Exception as e: + # Categorize the error for better reporting + error_type = type(e).__name__ + if "ValidationError" in error_type: + categorized_error = "evaluation_validation_failed" + elif "timeout" in str(e).lower() or "timed out" in str(e).lower(): + categorized_error = "evaluation_timeout" + elif "ModelHTTPError" in error_type: + categorized_error = "model_api_error" + elif "ConnectionError" in error_type or "network" in str(e).lower(): + categorized_error = "network_error" + else: + categorized_error = f"evaluation_error_{error_type}" + error_msg = f"Error during evaluation: {str(e)}" logfire.error( "Evaluation error", model=model, run_index=run_index, error=str(e), - error_type=type(e).__name__, + error_type=error_type, + categorized_error=categorized_error, ) - self.results[model].add_failed_run(run_index, error_msg) + self.results[model].add_failed_run(run_index, categorized_error) return None async def run_model_evaluations( @@ -442,7 +456,7 @@ async def main(): parser.add_argument( "--parallel", action="store_true", - default=False, + default=True, help="Run evaluations in parallel", ) parser.add_argument( From 87bdffcc20c0e10fcf89d03e98ae303cadff5f53 Mon Sep 17 00:00:00 2001 From: Andrew Ginns Date: Mon, 9 Jun 2025 09:35:27 +0000 Subject: [PATCH 09/14] feat: Make benchmark dashboard more flexible for eval tasks --- .../eval_multi_mcp/dashboard_config.py | 120 ++++ .../multi_mcp/eval_multi_mcp/merbench_ui.py | 532 ++++++++++++------ .../multi_mcp/eval_multi_mcp/schemas.py | 96 ++++ 3 files changed, 574 insertions(+), 174 deletions(-) create mode 100644 agents_mcp_usage/multi_mcp/eval_multi_mcp/dashboard_config.py create mode 100644 agents_mcp_usage/multi_mcp/eval_multi_mcp/schemas.py diff --git a/agents_mcp_usage/multi_mcp/eval_multi_mcp/dashboard_config.py b/agents_mcp_usage/multi_mcp/eval_multi_mcp/dashboard_config.py new file mode 100644 index 0000000..8167d96 --- /dev/null +++ b/agents_mcp_usage/multi_mcp/eval_multi_mcp/dashboard_config.py @@ -0,0 +1,120 @@ +""" +Dashboard Configuration for the Generic Evaluation UI + +This file defines the "personality" of the Streamlit dashboard. By modifying this +configuration, you can adapt the UI to display results from any evaluation +that produces a CSV file with a compatible format. + +Each section of the configuration is documented to explain its purpose and +the available options. +""" + +# ============================================================================== +# MERBENCH DASHBOARD CONFIGURATION +# ============================================================================== +# This is an example configuration for the "Merbench" evaluation. +# You can duplicate and modify this structure to create configurations +# for other evaluations. +# ============================================================================== + +MERBENCH_CONFIG = { + # --- General Dashboard Settings --- + "title": "Merbench - LLM Evaluation Benchmark", + "icon": "🏆", # Emoji for the browser tab + # --- Primary Metric Configuration --- + # The primary metric is the main score used for the leaderboard and + # the y-axis of the Pareto frontier plot. + "primary_metric": { + "name": "correctness_score", # The column name in the CSV + "label": "Avg. Success Rate", # How the metric is displayed in the UI + "goal": "maximize", # 'maximize' or 'minimize' + "score_column": "Score_MermaidDiagramValid", + }, + # --- Grouping and Filtering --- + # Defines how to group test cases (e.g., by difficulty). + "grouping": { + "column": "Case", # The column containing the case names + "label": "Difficulty", # The label for the filter in the sidebar + # A regex to extract group names from the 'column'. The first + # capture group will be used as the group name. + "extractor_regex": r".*_(easy|medium|hard)", + "target_column": "difficulty", + }, + # --- Plot and Analysis Tab Configuration --- + # This section defines which plots are displayed in the UI. + "plots": { + "pareto": { + "enabled": True, + "title": "Performance vs. {x_axis_label}", + "y_axis": "primary_metric", # Uses the primary_metric defined above + "x_axis_options": { + "cost": {"column": "total_cost", "label": "Cost"}, + "tokens": {"column": "total_response_tokens", "label": "Tokens"}, + }, + "color_axis": "Duration", # Column to use for the color scale + }, + "success_rates": { + "enabled": True, + "title": "Success Rate by Metric", + "type": "grouped_bar", + # Finds all columns starting with this prefix to create a bar for each. + "y_prefix": "Score_", + }, + "failure_analysis": { + "enabled": True, + "title": "Failure Analysis by Reason", + "type": "stacked_bar", + # Defines the series for the stacked bar chart. + # Each item represents a condition that counts as a "failure". + "series": [ + { + "name": "Invalid Diagram", + "column": "Score_MermaidDiagramValid", + "condition": "== 0", + }, + { + "name": "MCP Tool Failure", + "column": "Score_UsedBothMCPTools", + "condition": "< 1", + }, + { + "name": "Usage Limit Exceeded", + "column": "Score_UsageLimitNotExceeded", + "condition": "== 0", + }, + ], + }, + "token_breakdown": { + "enabled": True, + "title": "Average Token Usage by Type", + "type": "stacked_bar", + "series": [ + {"name": "Request", "column": "Metric_request_tokens"}, + {"name": "Response", "column": "Metric_response_tokens"}, + {"name": "Thinking", "column": "thinking_tokens"}, + ], + }, + "cost_breakdown": { + "enabled": True, + "title": "Average Cost Breakdown by Type", + "type": "stacked_bar", + "series": [ + {"name": "Input Cost", "column": "input_cost"}, + {"name": "Output Cost", "column": "output_cost"}, + ], + }, + }, + # --- Cost Calculation --- + # Defines which columns are used to calculate the total cost. + "cost_calculation": { + "input_token_cols": ["Metric_request_tokens"], + "output_token_cols": ["Metric_response_tokens", "thinking_tokens"], + }, +} + +# --- Add other configurations for different evaluations below --- +# EXAMPLE_OTHER_EVAL_CONFIG = { ... } + +# The default configuration to use when the dashboard starts. +# You can change this to point to a different configuration. +DEFAULT_CONFIG = MERBENCH_CONFIG diff --git a/agents_mcp_usage/multi_mcp/eval_multi_mcp/merbench_ui.py b/agents_mcp_usage/multi_mcp/eval_multi_mcp/merbench_ui.py index dea0154..76676af 100644 --- a/agents_mcp_usage/multi_mcp/eval_multi_mcp/merbench_ui.py +++ b/agents_mcp_usage/multi_mcp/eval_multi_mcp/merbench_ui.py @@ -6,10 +6,25 @@ import numpy as np from typing import List, Dict import json +import re +from pydantic import ValidationError + +from agents_mcp_usage.multi_mcp.eval_multi_mcp.dashboard_config import ( + DEFAULT_CONFIG, +) +from agents_mcp_usage.multi_mcp.eval_multi_mcp.schemas import DashboardConfig + +# Load and validate the configuration +try: + EVAL_CONFIG = DashboardConfig(**DEFAULT_CONFIG) +except ValidationError as e: + st.error(f"Dashboard configuration error: {e}") + st.stop() + # Page configuration st.set_page_config( - page_title="Merbench - LLM Evaluation Benchmark", page_icon="🏆", layout="wide" + page_title=EVAL_CONFIG.title, page_icon=EVAL_CONFIG.icon, layout="wide" ) # Default model costs (per 1M tokens) @@ -24,6 +39,7 @@ # --- Data Loading and Processing --- + def find_all_combined_results_csvs(directory_path: str) -> list[str]: """Finds all '*_combined_results.csv' files, sorted by modification time.""" if not os.path.isdir(directory_path): @@ -36,6 +52,7 @@ def find_all_combined_results_csvs(directory_path: str) -> list[str]: st.error(f"Error finding CSV files in '{directory_path}': {e}") return [] + def detect_csv_files(directory: str = None) -> List[str]: """Detect CSV result files in the specified directory.""" if directory is None: @@ -50,7 +67,7 @@ def load_csv_data(file_paths: List[str]) -> pd.DataFrame: """Load and combine multiple CSV files into a single DataFrame.""" if not file_paths: return pd.DataFrame() - + dataframes = [] for file_path in file_paths: try: @@ -63,21 +80,25 @@ def load_csv_data(file_paths: List[str]) -> pd.DataFrame: except (pd.errors.EmptyDataError, FileNotFoundError) as e: st.error(f"Could not load {os.path.basename(file_path)}: {e}") except Exception as e: - st.error(f"An unexpected error occurred while loading {os.path.basename(file_path)}: {e}") + st.error( + f"An unexpected error occurred while loading {os.path.basename(file_path)}: {e}" + ) return pd.concat(dataframes, ignore_index=True) if dataframes else pd.DataFrame() -def extract_case_difficulty(case_name: str) -> str: - """Extracts difficulty ('easy', 'medium', 'hard') from a case name.""" - case_lower = str(case_name).lower() - if "easy" in case_lower: - return "easy" - if "medium" in case_lower: - return "medium" - if "hard" in case_lower: - return "hard" - return "unknown" +def extract_grouping_column(df: pd.DataFrame, config: Dict) -> pd.DataFrame: + """Extracts a grouping column based on regex from the config.""" + source_col = config["grouping"]["column"] + target_col = config["grouping"]["target_column"] + regex = config["grouping"]["extractor_regex"] + + def extract_group(value: str) -> str: + match = re.match(regex, str(value)) + return match.group(1) if match else "other" + + df[target_col] = df[source_col].apply(extract_group) + return df def parse_metric_details(metric_details_str: str) -> Dict: @@ -91,9 +112,15 @@ def parse_metric_details(metric_details_str: str) -> Dict: return {} -def calculate_costs(df: pd.DataFrame, cost_config: Dict) -> pd.DataFrame: - """Calculates input, output, and total costs for each run.""" +def calculate_costs( + df: pd.DataFrame, cost_config: Dict, eval_config: Dict +) -> pd.DataFrame: + """Calculates input, output, and total costs for each run based on eval config.""" df_with_costs = df.copy() + cost_calc_config = eval_config.get("cost_calculation", {}) + input_token_cols = cost_calc_config.get("input_token_cols", []) + output_token_cols = cost_calc_config.get("output_token_cols", []) + df_with_costs["input_cost"] = 0.0 df_with_costs["output_cost"] = 0.0 df_with_costs["total_cost"] = 0.0 @@ -102,134 +129,206 @@ def calculate_costs(df: pd.DataFrame, cost_config: Dict) -> pd.DataFrame: model = row.get("Model") if model in cost_config: try: - input_tokens = row.get("Metric_request_tokens", 0) or 0 - output_tokens = row.get("Metric_response_tokens", 0) or 0 - thinking_tokens = row.get("thinking_tokens", 0) or 0 + input_tokens = sum(row.get(col, 0) or 0 for col in input_token_cols) + output_tokens = sum(row.get(col, 0) or 0 for col in output_token_cols) input_cost = (input_tokens / 1_000_000) * cost_config[model]["input"] - output_cost = ((output_tokens + thinking_tokens) / 1_000_000) * cost_config[model]["output"] - + output_cost = (output_tokens / 1_000_000) * cost_config[model]["output"] + df_with_costs.at[idx, "input_cost"] = input_cost df_with_costs.at[idx, "output_cost"] = output_cost df_with_costs.at[idx, "total_cost"] = input_cost + output_cost except (TypeError, KeyError) as e: - st.warning(f"Cost calculation error for model {model} at row {idx}: {e}") + st.warning( + f"Cost calculation error for model {model} at row {idx}: {e}" + ) return df_with_costs -def process_data(df: pd.DataFrame, cost_config: Dict) -> pd.DataFrame: +def process_data( + df: pd.DataFrame, cost_config: Dict, eval_config: DashboardConfig +) -> pd.DataFrame: """Main data processing pipeline.""" if df.empty: return df processed_df = df.copy() - processed_df["difficulty"] = processed_df["Case"].apply(extract_case_difficulty) - - # Extract token counts from metric details - metric_details = processed_df["Metric_details"].apply(parse_metric_details) - processed_df["thinking_tokens"] = metric_details.apply(lambda x: x.get("thoughts_tokens", 0)) - processed_df["text_tokens"] = metric_details.apply(lambda x: x.get("text_prompt_tokens", 0)) + + # Generic grouping + processed_df = extract_grouping_column(processed_df, eval_config.model_dump()) + + # Extract token counts from metric details (assuming 'Metric_details' exists) + if "Metric_details" in processed_df.columns: + metric_details = processed_df["Metric_details"].apply(parse_metric_details) + processed_df["thinking_tokens"] = metric_details.apply( + lambda x: x.get("thoughts_tokens", 0) + ) + processed_df["text_tokens"] = metric_details.apply( + lambda x: x.get("text_prompt_tokens", 0) + ) + else: + # Ensure these columns exist even if Metric_details is missing + processed_df["thinking_tokens"] = 0 + processed_df["text_tokens"] = 0 # Calculate total response tokens processed_df["total_response_tokens"] = ( processed_df.get("Metric_response_tokens", 0) + processed_df["thinking_tokens"] ) - - # Standardize correctness score - processed_df["correctness_score"] = processed_df.get("Score_MermaidDiagramValid", 0.5) * 100 - return calculate_costs(processed_df, cost_config) + # Standardize primary metric score + primary_metric_config = eval_config.primary_metric + if ( + primary_metric_config.name not in processed_df.columns + and primary_metric_config.score_column + ): + if primary_metric_config.score_column in processed_df.columns: + # Create the primary metric from the specified score column, defaulting to 0 + processed_df[primary_metric_config.name] = ( + processed_df.get(primary_metric_config.score_column, 0) * 100 + ) + else: + st.warning( + f"Specified score_column '{primary_metric_config.score_column}' not found. Primary metric will be 0." + ) + processed_df[primary_metric_config.name] = 0 + elif primary_metric_config.name in processed_df.columns: + # if the column already exists, make sure it's scaled to 100 + processed_df[primary_metric_config.name] = ( + processed_df[primary_metric_config.name] * 100 + ) + else: + st.error( + f"Primary metric column '{primary_metric_config.name}' not found and no 'score_column' provided in config." + ) + st.stop() + + return calculate_costs(processed_df, cost_config, eval_config.model_dump()) # --- UI & Plotting --- -def create_leaderboard(df: pd.DataFrame, selected_cases: List[str]) -> pd.DataFrame: + +def create_leaderboard( + df: pd.DataFrame, selected_groups: List[str], config: Dict +) -> pd.DataFrame: """Creates a leaderboard DataFrame with key performance indicators.""" - if df.empty or not selected_cases: + if df.empty or not selected_groups: return pd.DataFrame() - df_filtered = df[df["difficulty"].isin(selected_cases)] + grouping_col = config["grouping"]["target_column"] + df_filtered = df[df[grouping_col].isin(selected_groups)] if df_filtered.empty: return pd.DataFrame() - leaderboard = df_filtered.groupby("Model").agg( - Correct=("correctness_score", "mean"), - Cost=("total_cost", "mean"), - Duration=("Duration", "mean"), - Tokens=("total_response_tokens", "mean"), - Runs=("Model", "size"), - ).reset_index() + primary_metric_name = config["primary_metric"]["name"] + sort_ascending = config["primary_metric"]["goal"] == "minimize" - return leaderboard.sort_values("Correct", ascending=False) + agg_config = { + "Correct": (primary_metric_name, "mean"), + "Cost": ("total_cost", "mean"), + "Duration": ("Duration", "mean"), + "Tokens": ("total_response_tokens", "mean"), + "Runs": ("Model", "size"), + } + + leaderboard = df_filtered.groupby("Model").agg(**agg_config).reset_index() + return leaderboard.sort_values("Correct", ascending=sort_ascending) -def create_pareto_frontier_plot(df: pd.DataFrame, selected_cases: List[str], x_axis_mode: str) -> go.Figure: +def create_pareto_frontier_plot( + df: pd.DataFrame, selected_groups: List[str], x_axis_mode: str, config: Dict +) -> go.Figure: """Visualizes the trade-off between model performance and cost/token usage.""" fig = go.Figure() - if df.empty or not selected_cases: + plot_config = config["plots"]["pareto"] + if df.empty or not selected_groups or not plot_config.get("enabled", False): return fig.update_layout(title="No data available for selected filters.") - df_filtered = df[df["difficulty"].isin(selected_cases)] - model_metrics = df_filtered.groupby("Model").agg( - correctness_score=("correctness_score", "mean"), - total_cost=("total_cost", "mean"), - total_response_tokens=("total_response_tokens", "mean"), - Duration=("Duration", "mean"), - ).reset_index() - - x_data, x_title, hover_label, hover_format = ( - (model_metrics["total_cost"], "Average Total Cost ($)", "Avg Cost", ":.4f") - if x_axis_mode == "cost" - else (model_metrics["total_response_tokens"], "Average Total Response Tokens", "Avg Tokens", ":.0f") + grouping_col = config["grouping"]["target_column"] + df_filtered = df[df[grouping_col].isin(selected_groups)] + + primary_metric_name = config["primary_metric"]["name"] + y_axis_label = config["primary_metric"]["label"] + + model_metrics = ( + df_filtered.groupby("Model") + .agg( + y_axis=(primary_metric_name, "mean"), + total_cost=("total_cost", "mean"), + total_response_tokens=("total_response_tokens", "mean"), + color_axis=(plot_config["color_axis"], "mean"), + ) + .reset_index() + ) + + x_axis_config = plot_config["x_axis_options"][x_axis_mode] + x_data = model_metrics[x_axis_config["column"]] + x_title = x_axis_config["label"] + hover_label = x_axis_config["label"] + hover_format = ":.4f" if x_axis_mode == "cost" else ":.0f" + + fig.add_trace( + go.Scatter( + x=x_data, + y=model_metrics["y_axis"], + mode="markers+text", + marker=dict( + size=18, + color=model_metrics["color_axis"], + colorscale="RdYlGn_r", + showscale=True, + colorbar=dict(title=f"Avg {plot_config['color_axis']} (s)"), + ), + text=model_metrics["Model"], + textposition="top center", + hovertemplate=( + "%{text}
" + f"{y_axis_label}: %{{y:.1f}}%
" + f"{hover_label}: %{{x{hover_format}}}
" + f"Avg {plot_config['color_axis']}: %{{marker.color:.1f}}s" + ), + ) ) - fig.add_trace(go.Scatter( - x=x_data, - y=model_metrics["correctness_score"], - mode="markers+text", - marker=dict( - size=18, - color=model_metrics["Duration"], - colorscale="RdYlGn_r", - showscale=True, - colorbar=dict(title="Avg Duration (s)"), - ), - text=model_metrics["Model"], - textposition="top center", - hovertemplate=( - "%{text}
" - "Avg Score: %{y:.1f}%
" - f"{hover_label}: %{{x{hover_format}}}
" - "Avg Duration: %{marker.color:.1f}s" - ), - )) - fig.update_layout( - title=f"Performance vs. {'Cost' if x_axis_mode == 'cost' else 'Tokens'}", - xaxis_title=x_title, - yaxis_title="Average Success Rate (%)", + title=plot_config["title"].format(x_axis_label=x_title), + xaxis_title=f"Average {x_title}", + yaxis_title=y_axis_label, showlegend=False, height=600, ) return fig -def create_success_rates_plot(df: pd.DataFrame, selected_cases: List[str]) -> go.Figure: +def create_success_rates_plot( + df: pd.DataFrame, selected_groups: List[str], config: Dict +) -> go.Figure: """Compares models across different success metrics.""" fig = go.Figure() - if df.empty or not selected_cases: + plot_config = config["plots"]["success_rates"] + if df.empty or not selected_groups or not plot_config.get("enabled", False): return fig.update_layout(title="No data available for selected filters.") - df_filtered = df[df["difficulty"].isin(selected_cases)] - metric_cols = [col for col in df_filtered.columns if col.startswith("Score_")] + grouping_col = config["grouping"]["target_column"] + df_filtered = df[df[grouping_col].isin(selected_groups)] + + y_prefix = plot_config.get("y_prefix") + y_columns = plot_config.get("y_columns", []) + metric_cols = ( + y_columns + if y_columns + else [col for col in df_filtered.columns if col.startswith(y_prefix)] + ) + if not metric_cols: - return fig.update_layout(title="No 'Score_' columns found.") + return fig.update_layout(title=f"No columns with prefix '{y_prefix}' found.") models = sorted(df_filtered["Model"].unique()) - + for metric_col in metric_cols: - metric_name = metric_col.replace("Score_", "") + metric_name = metric_col.replace(y_prefix, "") if y_prefix else metric_col avg_scores = [ df_filtered[df_filtered["Model"] == model][metric_col].mean() * 100 for model in models @@ -237,7 +336,7 @@ def create_success_rates_plot(df: pd.DataFrame, selected_cases: List[str]) -> go fig.add_trace(go.Bar(name=metric_name, x=models, y=avg_scores)) fig.update_layout( - title="Success Rate by Metric", + title=plot_config["title"], xaxis_title="Model", yaxis_title="Success Rate (%)", barmode="group", @@ -247,32 +346,34 @@ def create_success_rates_plot(df: pd.DataFrame, selected_cases: List[str]) -> go return fig -def create_failure_analysis_plot(df: pd.DataFrame, selected_cases: List[str]) -> go.Figure: +def create_failure_analysis_plot( + df: pd.DataFrame, selected_groups: List[str], config: Dict +) -> go.Figure: """Shows common failure reasons by model.""" fig = go.Figure() - if df.empty or not selected_cases: + plot_config = config["plots"]["failure_analysis"] + if df.empty or not selected_groups or not plot_config.get("enabled", False): return fig.update_layout(title="No data available for selected filters.") - df_filtered = df[df["difficulty"].isin(selected_cases)] + grouping_col = config["grouping"]["target_column"] + df_filtered = df[df[grouping_col].isin(selected_groups)] models = sorted(df_filtered["Model"].unique()) - - failure_counts = { - "Invalid Diagram": [], - "MCP Tool Failure": [], - "Usage Limit Exceeded": [], - } + + failure_series = plot_config["series"] + failure_counts = {series["name"]: [] for series in failure_series} for model in models: model_data = df_filtered[df_filtered["Model"] == model] - failure_counts["Invalid Diagram"].append((model_data["Score_MermaidDiagramValid"] == 0).sum()) - failure_counts["MCP Tool Failure"].append((model_data["Score_UsedBothMCPTools"] < 1).sum()) - failure_counts["Usage Limit Exceeded"].append((model_data["Score_UsageLimitNotExceeded"] == 0).sum()) + for series in failure_series: + # Use pandas.eval to safely evaluate the condition string + count = model_data.eval(f"`{series['column']}` {series['condition']}").sum() + failure_counts[series["name"]].append(count) for reason, counts in failure_counts.items(): fig.add_trace(go.Bar(name=reason, x=models, y=counts)) fig.update_layout( - title="Failure Analysis by Reason", + title=plot_config["title"], xaxis_title="Model", yaxis_title="Number of Failures", barmode="stack", @@ -282,25 +383,31 @@ def create_failure_analysis_plot(df: pd.DataFrame, selected_cases: List[str]) -> return fig -def create_token_breakdown_plot(df: pd.DataFrame, selected_cases: List[str]) -> go.Figure: +def create_token_breakdown_plot( + df: pd.DataFrame, selected_groups: List[str], config: Dict +) -> go.Figure: """Creates a stacked bar chart showing token breakdown.""" fig = go.Figure() - if df.empty or not selected_cases: + plot_config = config["plots"]["token_breakdown"] + if df.empty or not selected_groups or not plot_config.get("enabled", False): return fig.update_layout(title="No data available for selected filters.") - df_filtered = df[df["difficulty"].isin(selected_cases)] - token_data = df_filtered.groupby("Model").agg( - Request=("Metric_request_tokens", "mean"), - Response=("Metric_response_tokens", "mean"), - Thinking=("thinking_tokens", "mean"), - ).reset_index() + grouping_col = config["grouping"]["target_column"] + df_filtered = df[df[grouping_col].isin(selected_groups)] - fig.add_trace(go.Bar(name="Request", x=token_data["Model"], y=token_data["Request"])) - fig.add_trace(go.Bar(name="Response", x=token_data["Model"], y=token_data["Response"])) - fig.add_trace(go.Bar(name="Thinking", x=token_data["Model"], y=token_data["Thinking"])) + series_config = plot_config["series"] + agg_dict = {series["name"]: (series["column"], "mean") for series in series_config} + token_data = df_filtered.groupby("Model").agg(**agg_dict).reset_index() + + for series in series_config: + fig.add_trace( + go.Bar( + name=series["name"], x=token_data["Model"], y=token_data[series["name"]] + ) + ) fig.update_layout( - title="Average Token Usage by Type", + title=plot_config["title"], xaxis_title="Model", yaxis_title="Average Tokens", barmode="stack", @@ -310,23 +417,31 @@ def create_token_breakdown_plot(df: pd.DataFrame, selected_cases: List[str]) -> return fig -def create_cost_breakdown_plot(df: pd.DataFrame, selected_cases: List[str]) -> go.Figure: +def create_cost_breakdown_plot( + df: pd.DataFrame, selected_groups: List[str], config: Dict +) -> go.Figure: """Creates a stacked bar chart for cost breakdown.""" fig = go.Figure() - if df.empty or not selected_cases: + plot_config = config["plots"]["cost_breakdown"] + if df.empty or not selected_groups or not plot_config.get("enabled", False): return fig.update_layout(title="No data available for selected filters.") - df_filtered = df[df["difficulty"].isin(selected_cases)] - cost_data = df_filtered.groupby("Model").agg( - Input=("input_cost", "mean"), - Output=("output_cost", "mean"), - ).reset_index() + grouping_col = config["grouping"]["target_column"] + df_filtered = df[df[grouping_col].isin(selected_groups)] - fig.add_trace(go.Bar(name="Input Cost", x=cost_data["Model"], y=cost_data["Input"])) - fig.add_trace(go.Bar(name="Output Cost", x=cost_data["Model"], y=cost_data["Output"])) + series_config = plot_config["series"] + agg_dict = {series["name"]: (series["column"], "mean") for series in series_config} + cost_data = df_filtered.groupby("Model").agg(**agg_dict).reset_index() + + for series in series_config: + fig.add_trace( + go.Bar( + name=series["name"], x=cost_data["Model"], y=cost_data[series["name"]] + ) + ) fig.update_layout( - title="Average Cost Breakdown by Type", + title=plot_config["title"], xaxis_title="Model", yaxis_title="Average Cost ($)", barmode="stack", @@ -338,16 +453,20 @@ def create_cost_breakdown_plot(df: pd.DataFrame, selected_cases: List[str]) -> g def main(): """Main Streamlit application entrypoint.""" - st.title("🏆 Merbench") + eval_config = EVAL_CONFIG # Use the validated config + + st.title(eval_config.title) st.subheader("LLM Evaluation Benchmark Dashboard") # --- Sidebar Setup --- st.sidebar.header("⚙️ Configuration") - + # File selection - default_dir_path = os.path.dirname(detect_csv_files()[0]) if detect_csv_files() else "" + default_dir_path = ( + os.path.dirname(detect_csv_files()[0]) if detect_csv_files() else "" + ) custom_dir = st.sidebar.text_input("Results Directory:", value=default_dir_path) - + if st.sidebar.button("🔄 Refresh Files"): st.rerun() @@ -361,7 +480,7 @@ def main(): options=[os.path.basename(f) for f in csv_files], default=[os.path.basename(f) for f in csv_files], ) - + if not selected_files: st.info("Select one or more result files to begin analysis.") return @@ -369,13 +488,13 @@ def main(): # --- Data Loading and Filtering --- full_file_paths = [os.path.join(custom_dir, f) for f in selected_files] df_initial = load_csv_data(full_file_paths) - + if df_initial.empty: st.error("No data loaded. Please check the selected files.") return available_models = sorted(df_initial["Model"].unique()) - + # Cost configuration in sidebar st.sidebar.subheader("💰 Cost Configuration") cost_config = {} @@ -383,55 +502,57 @@ def main(): for model in available_models: cols = st.columns(2) default = DEFAULT_COSTS.get(model, {"input": 0.0, "output": 0.0}) - input_cost = cols[0].number_input(f"{model} Input", value=float(default["input"]), step=0.01, format="%.2f") - output_cost = cols[1].number_input(f"{model} Output", value=float(default["output"]), step=0.01, format="%.2f") + input_cost = cols[0].number_input( + f"{model} Input", + value=float(default["input"]), + step=0.01, + format="%.2f", + ) + output_cost = cols[1].number_input( + f"{model} Output", + value=float(default["output"]), + step=0.01, + format="%.2f", + ) cost_config[model] = {"input": input_cost, "output": output_cost} - df = process_data(df_initial, cost_config) + df = process_data(df_initial, cost_config, eval_config) - # Difficulty filter - st.sidebar.subheader("🎯 Difficulty Filter") - available_difficulties = sorted(df["difficulty"].unique()) - selected_difficulties = st.sidebar.multiselect( - "Filter by difficulty:", - options=available_difficulties, - default=available_difficulties, + # Grouping filter + grouping_config = eval_config.grouping + st.sidebar.subheader(f"🎯 {grouping_config.label} Filter") + available_groups = sorted(df[grouping_config.target_column].unique()) + selected_groups = st.sidebar.multiselect( + f"Filter by {grouping_config.label.lower()}:", + options=available_groups, + default=available_groups, ) # --- Main Panel --- st.header("📊 Overview") - + # Key metrics cols = st.columns(4) cols[0].metric("Evaluation Runs", len(df)) cols[1].metric("Models Evaluated", df["Model"].nunique()) - cols[2].metric("Test Cases", df["Case"].nunique()) + cols[2].metric("Test Cases", df[grouping_config.column].nunique()) cols[3].metric("Files Loaded", len(selected_files)) - st.info(f"**Showing results for difficulties:** {', '.join(selected_difficulties) if selected_difficulties else 'None'}") + st.info( + f"**Showing results for {grouping_config.label.lower()}:** {', '.join(selected_groups) if selected_groups else 'None'}" + ) # --- Leaderboard & Pareto --- st.header("🏅 Leaderboard") - leaderboard_df = create_leaderboard(df, selected_difficulties) - + leaderboard_df = create_leaderboard(df, selected_groups, eval_config.model_dump()) + if not leaderboard_df.empty: - st.markdown(""" - - """, unsafe_allow_html=True) + primary_metric_label = eval_config.primary_metric.label st.dataframe( leaderboard_df, column_config={ "Correct": st.column_config.ProgressColumn( - "Avg. Success", + primary_metric_label, format="%.1f%%", min_value=0, max_value=100, @@ -442,32 +563,95 @@ def main(): min_value=0, max_value=leaderboard_df["Cost"].max(), ), - "Duration": st.column_config.NumberColumn("Avg Duration (s)", format="%.2fs"), + "Duration": st.column_config.NumberColumn( + "Avg Duration (s)", format="%.2fs" + ), "Tokens": st.column_config.NumberColumn("Avg Tokens", format="%.0f"), }, - column_order=["Model", "Correct", "Cost", "Duration", "Tokens", "Runs"], use_container_width=True, ) else: st.warning("No data available for the current filter selection.") st.header("📈 Pareto Frontier Analysis") - x_axis_mode = st.radio("Compare performance against:", ["cost", "tokens"], format_func=lambda x: x.capitalize(), horizontal=True) - st.plotly_chart(create_pareto_frontier_plot(df, selected_difficulties, x_axis_mode), use_container_width=True) + pareto_config = eval_config.plots.pareto + x_axis_mode = st.radio( + "Compare performance against:", + list(pareto_config.x_axis_options.keys()), + format_func=lambda x: x.capitalize(), + horizontal=True, + ) + st.plotly_chart( + create_pareto_frontier_plot( + df, selected_groups, x_axis_mode, eval_config.model_dump() + ), + use_container_width=True, + ) # --- Deep Dive Analysis --- with st.expander("🔍 Deep Dive Analysis", expanded=False): - tab1, tab2, tab3, tab4 = st.tabs(["📊 Success Rates", "📉 Failure Analysis", "⚙️ Resource Usage", "📋 Raw Data"]) - - with tab1: - st.plotly_chart(create_success_rates_plot(df, selected_difficulties), use_container_width=True) - with tab2: - st.plotly_chart(create_failure_analysis_plot(df, selected_difficulties), use_container_width=True) - with tab3: - st.plotly_chart(create_token_breakdown_plot(df, selected_difficulties), use_container_width=True) - st.plotly_chart(create_cost_breakdown_plot(df, selected_difficulties), use_container_width=True) - with tab4: - st.dataframe(df[df["difficulty"].isin(selected_difficulties)], use_container_width=True) + plot_configs = eval_config.plots + + # Build a dictionary of active plot configs + active_plots = { + name: plot + for name, plot in plot_configs.model_dump().items() + if plot.get("enabled") + } + + tabs_to_create = {} + if "success_rates" in active_plots: + tabs_to_create["Success Rates"] = True + if "failure_analysis" in active_plots: + tabs_to_create["Failure Analysis"] = True + if "token_breakdown" in active_plots or "cost_breakdown" in active_plots: + tabs_to_create["Resource Usage"] = True + tabs_to_create["Raw Data"] = True + + if tabs_to_create: + tabs = st.tabs(list(tabs_to_create.keys())) + tab_map = dict(zip(tabs_to_create.keys(), tabs)) + + if "Success Rates" in tab_map: + with tab_map["Success Rates"]: + st.plotly_chart( + create_success_rates_plot( + df, selected_groups, eval_config.model_dump() + ), + use_container_width=True, + ) + if "Failure Analysis" in tab_map: + with tab_map["Failure Analysis"]: + st.plotly_chart( + create_failure_analysis_plot( + df, selected_groups, eval_config.model_dump() + ), + use_container_width=True, + ) + if "Resource Usage" in tab_map: + with tab_map["Resource Usage"]: + if "token_breakdown" in active_plots: + st.plotly_chart( + create_token_breakdown_plot( + df, selected_groups, eval_config.model_dump() + ), + use_container_width=True, + ) + if "cost_breakdown" in active_plots: + st.plotly_chart( + create_cost_breakdown_plot( + df, selected_groups, eval_config.model_dump() + ), + use_container_width=True, + ) + if "Raw Data" in tab_map: + with tab_map["Raw Data"]: + st.dataframe( + df[ + df[eval_config.grouping.target_column].isin(selected_groups) + ], + use_container_width=True, + ) if __name__ == "__main__": diff --git a/agents_mcp_usage/multi_mcp/eval_multi_mcp/schemas.py b/agents_mcp_usage/multi_mcp/eval_multi_mcp/schemas.py new file mode 100644 index 0000000..ed7e40c --- /dev/null +++ b/agents_mcp_usage/multi_mcp/eval_multi_mcp/schemas.py @@ -0,0 +1,96 @@ +""" +Pydantic Schemas for Dashboard Configuration Validation +""" + +from pydantic import BaseModel, Field, validator +from typing import List, Dict, Optional, Any + + +class PrimaryMetricConfig(BaseModel): + name: str = Field(..., description="The column name for the primary metric.") + label: str = Field(..., description="How the metric is displayed in the UI.") + goal: str = Field(..., description="'maximize' or 'minimize'") + score_column: Optional[str] = Field( + None, + description="Optional source column to calculate the primary metric from if it doesn't exist.", + ) + + @validator("goal") + def goal_must_be_max_or_min(cls, v): + if v not in ["maximize", "minimize"]: + raise ValueError("goal must be 'maximize' or 'minimize'") + return v + + +class GroupingConfig(BaseModel): + column: str = Field(..., description="The column containing the case names.") + label: str = Field(..., description="The label for the filter in the sidebar.") + extractor_regex: str = Field( + ..., + description="A regex to extract group names. The first capture group will be used.", + ) + target_column: str = Field( + ..., description="The name of the new column to store the extracted group." + ) + + +class ParetoAxisOption(BaseModel): + column: str + label: str + + +class ParetoPlotConfig(BaseModel): + enabled: bool + title: str + y_axis: str + x_axis_options: Dict[str, ParetoAxisOption] + color_axis: str + + +class BarPlotSeries(BaseModel): + name: str + column: str + + +class StackedBarPlotSeries(BarPlotSeries): + condition: Optional[str] = None + + +class BarPlotConfig(BaseModel): + enabled: bool + title: str + type: str + y_prefix: Optional[str] = None + y_columns: Optional[List[str]] = None + series: Optional[List[StackedBarPlotSeries]] = None + + @validator("y_columns", always=True) + def check_prefix_or_columns(cls, v, values): + if not values.get("y_prefix") and not v: + if values.get("type") == "grouped_bar": + raise ValueError( + "Either 'y_prefix' or 'y_columns' must be provided for grouped_bar plots." + ) + return v + + +class PlotConfig(BaseModel): + pareto: ParetoPlotConfig + success_rates: BarPlotConfig + failure_analysis: BarPlotConfig + token_breakdown: BarPlotConfig + cost_breakdown: BarPlotConfig + + +class CostCalculationConfig(BaseModel): + input_token_cols: List[str] + output_token_cols: List[str] + + +class DashboardConfig(BaseModel): + title: str + icon: str + primary_metric: PrimaryMetricConfig + grouping: GroupingConfig + plots: PlotConfig + cost_calculation: CostCalculationConfig From 4bc6f87fdc80e1ea9818d945f4198a50224c32ab Mon Sep 17 00:00:00 2001 From: Andrew Ginns Date: Mon, 9 Jun 2025 09:35:57 +0000 Subject: [PATCH 10/14] feat: Improve default costs and UI representation --- .../multi_mcp/eval_multi_mcp/costs.csv | 103 +++++++++ .../multi_mcp/eval_multi_mcp/merbench_ui.py | 211 ++++++++++++++---- 2 files changed, 270 insertions(+), 44 deletions(-) create mode 100644 agents_mcp_usage/multi_mcp/eval_multi_mcp/costs.csv diff --git a/agents_mcp_usage/multi_mcp/eval_multi_mcp/costs.csv b/agents_mcp_usage/multi_mcp/eval_multi_mcp/costs.csv new file mode 100644 index 0000000..acafc79 --- /dev/null +++ b/agents_mcp_usage/multi_mcp/eval_multi_mcp/costs.csv @@ -0,0 +1,103 @@ +# Prices are per 1 million tokens +# Gemini prices from https://ai.google.dev/gemini-api/docs/pricing +# OpenAI prices from https://openai.com/api/pricing/ +MODEL_COSTS = { + "gemini-2.5-pro-preview-05-06": { + "input": [ + {"up_to": 200000, "price": 1.25}, + {"up_to": float('inf'), "price": 2.50}, + ], + "output": { + "default": [ + {"up_to": 200000, "price": 10.00}, + {"up_to": float('inf'), "price": 15.00}, + ] + }, + }, + "gemini-2.5-pro-preview-06-05": { + "input": [ + {"up_to": 200000, "price": 1.25}, + {"up_to": float('inf'), "price": 2.50}, + ], + "output": { + "default": [ + {"up_to": 200000, "price": 10.00}, + {"up_to": float('inf'), "price": 15.00}, + ] + }, + }, + "gemini-2.5-pro-preview": { + "input": [ + {"up_to": 200000, "price": 1.25}, + {"up_to": float('inf'), "price": 2.50}, + ], + "output": { + "default": [ + {"up_to": 200000, "price": 10.00}, + {"up_to": float('inf'), "price": 15.00}, + ] + }, + }, + "gemini-1.5-pro": { + "input": [ + {"up_to": 128000, "price": 1.25}, + {"up_to": float('inf'), "price": 2.50}, + ], + "output": { + "default": [ + {"up_to": 128000, "price": 5.00}, + {"up_to": float('inf'), "price": 10.00}, + ] + }, + }, + "gemini-1.5-flash": { + "input": [ + {"up_to": 128000, "price": 0.075}, + {"up_to": float('inf'), "price": 0.15}, + ], + "output": { + "default": [ + {"up_to": 128000, "price": 0.30}, + {"up_to": float('inf'), "price": 0.60}, + ] + }, + }, + "gemini-2.0-flash": { + "input": [{"up_to": float('inf'), "price": 0.10}], + "output": {"default": [{"up_to": float('inf'), "price": 0.40}]}, + }, + "gemini-2.5-flash-preview-04-17": { + "input": [{"up_to": float('inf'), "price": 0.15}], + "output": { + "non_thinking": [{"up_to": float('inf'), "price": 0.60}], + "thinking": [{"up_to": float('inf'), "price": 3.50}], + }, + }, + "gemini-2.5-flash-preview": { + "input": [{"up_to": float('inf'), "price": 0.15}], + "output": { + "non_thinking": [{"up_to": float('inf'), "price": 0.60}], + "thinking": [{"up_to": float('inf'), "price": 3.50}], + }, + }, + "openai:o4-mini": { + "input": [{"up_to": float('inf'), "price": 1.10}], + "output": {"default": [{"up_to": float('inf'), "price": 4.40}]}, + }, + "openai:o3": { + "input": [{"up_to": float('inf'), "price": 10.00}], + "output": {"default": [{"up_to": float('inf'), "price": 40.00}]}, + }, + "openai:gpt-4.1": { + "input": [{"up_to": float('inf'), "price": 2.00}], + "output": {"default": [{"up_to": float('inf'), "price": 8.00}]}, + }, + "openai:gpt-4.1-mini": { + "input": [{"up_to": float('inf'), "price": 0.40}], + "output": {"default": [{"up_to": float('inf'), "price": 1.60}]}, + }, + "openai:gpt-4.1-nano": { + "input": [{"up_to": float('inf'), "price": 0.10}], + "output": {"default": [{"up_to": float('inf'), "price": 0.40}]}, + }, +} \ No newline at end of file diff --git a/agents_mcp_usage/multi_mcp/eval_multi_mcp/merbench_ui.py b/agents_mcp_usage/multi_mcp/eval_multi_mcp/merbench_ui.py index 76676af..5ce233d 100644 --- a/agents_mcp_usage/multi_mcp/eval_multi_mcp/merbench_ui.py +++ b/agents_mcp_usage/multi_mcp/eval_multi_mcp/merbench_ui.py @@ -8,6 +8,8 @@ import json import re from pydantic import ValidationError +import csv +import io from agents_mcp_usage.multi_mcp.eval_multi_mcp.dashboard_config import ( DEFAULT_CONFIG, @@ -27,15 +29,37 @@ page_title=EVAL_CONFIG.title, page_icon=EVAL_CONFIG.icon, layout="wide" ) -# Default model costs (per 1M tokens) -DEFAULT_COSTS = { - "gemini-2.5-pro-preview-06-05": {"input": 3.50, "output": 10.50}, - "gemini-2.0-flash": {"input": 0.075, "output": 0.30}, - "gemini-2.5-flash-preview-04-17": {"input": 0.075, "output": 0.30}, - "openai:o4-mini": {"input": 0.15, "output": 0.60}, - "openai:gpt-4.1-mini": {"input": 0.15, "output": 0.60}, - "openai:gpt-4.1": {"input": 2.50, "output": 10.00}, -} +# --- Cost Loading --- + + +def load_model_costs(file_path: str) -> Dict: + """Loads model costs from a CSV file and returns a structured dictionary.""" + try: + with open(file_path, "r", encoding="utf-8") as f: + # Read lines, skipping comments and empty lines + lines = [ + line for line in f if not line.strip().startswith("#") and line.strip() + ] + + # Find the start of the dictionary-like definition + dict_str = "".join(lines) + match = re.search(r"MODEL_COSTS\s*=\s*({.*})", dict_str, re.DOTALL) + if not match: + st.error(f"Could not find 'MODEL_COSTS' dictionary in {file_path}") + return {} + + # Safely evaluate the dictionary string + model_costs_raw = eval(match.group(1), {"float": float}) + + return model_costs_raw + + except FileNotFoundError: + st.warning(f"Cost file not found at {file_path}. Using empty cost config.") + return {} + except (SyntaxError, NameError, Exception) as e: + st.error(f"Error parsing cost file {file_path}: {e}") + return {} + # --- Data Loading and Processing --- @@ -112,10 +136,18 @@ def parse_metric_details(metric_details_str: str) -> Dict: return {} +def get_price_for_tokens(token_count: int, price_tiers: List[Dict]) -> float: + """Finds the correct price for a given number of tokens from a list of tiers.""" + for tier in price_tiers: + if token_count <= tier["up_to"]: + return tier["price"] + return price_tiers[-1]["price"] # Fallback to the highest tier price + + def calculate_costs( df: pd.DataFrame, cost_config: Dict, eval_config: Dict ) -> pd.DataFrame: - """Calculates input, output, and total costs for each run based on eval config.""" + """Calculates input, output, and total costs for each run based on new tiered pricing.""" df_with_costs = df.copy() cost_calc_config = eval_config.get("cost_calculation", {}) input_token_cols = cost_calc_config.get("input_token_cols", []) @@ -127,21 +159,56 @@ def calculate_costs( for idx, row in df_with_costs.iterrows(): model = row.get("Model") - if model in cost_config: - try: - input_tokens = sum(row.get(col, 0) or 0 for col in input_token_cols) - output_tokens = sum(row.get(col, 0) or 0 for col in output_token_cols) - - input_cost = (input_tokens / 1_000_000) * cost_config[model]["input"] - output_cost = (output_tokens / 1_000_000) * cost_config[model]["output"] - - df_with_costs.at[idx, "input_cost"] = input_cost - df_with_costs.at[idx, "output_cost"] = output_cost - df_with_costs.at[idx, "total_cost"] = input_cost + output_cost - except (TypeError, KeyError) as e: - st.warning( - f"Cost calculation error for model {model} at row {idx}: {e}" + model_costs = cost_config.get(model) + + if not model_costs: + continue + + try: + input_tokens = sum(row.get(col, 0) or 0 for col in input_token_cols) + output_tokens = sum(row.get(col, 0) or 0 for col in output_token_cols) + thinking_tokens = row.get("thinking_tokens", 0) or 0 + non_thinking_output_tokens = output_tokens - thinking_tokens + + total_tokens = input_tokens + output_tokens + + # Determine input cost + input_price_tiers = model_costs.get("input", []) + input_price = get_price_for_tokens(total_tokens, input_price_tiers) + input_cost = (input_tokens / 1_000_000) * input_price + + # Determine output cost + output_cost = 0 + output_pricing = model_costs.get("output", {}) + + if "thinking" in output_pricing and thinking_tokens > 0: + thinking_price_tiers = output_pricing["thinking"] + thinking_price = get_price_for_tokens( + total_tokens, thinking_price_tiers + ) + output_cost += (thinking_tokens / 1_000_000) * thinking_price + + if "non_thinking" in output_pricing and non_thinking_output_tokens > 0: + non_thinking_price_tiers = output_pricing["non_thinking"] + non_thinking_price = get_price_for_tokens( + total_tokens, non_thinking_price_tiers ) + output_cost += ( + non_thinking_output_tokens / 1_000_000 + ) * non_thinking_price + + elif "default" in output_pricing: + default_price_tiers = output_pricing["default"] + default_price = get_price_for_tokens(total_tokens, default_price_tiers) + output_cost += (output_tokens / 1_000_000) * default_price + + df_with_costs.at[idx, "input_cost"] = input_cost + df_with_costs.at[idx, "output_cost"] = output_cost + df_with_costs.at[idx, "total_cost"] = input_cost + output_cost + + except (TypeError, KeyError, IndexError) as e: + st.warning(f"Cost calculation error for model {model} at row {idx}: {e}") + return df_with_costs @@ -176,6 +243,15 @@ def process_data( processed_df.get("Metric_response_tokens", 0) + processed_df["thinking_tokens"] ) + # Calculate total tokens for leaderboard + cost_calc_config = eval_config.cost_calculation + input_token_cols = cost_calc_config.input_token_cols + output_token_cols = cost_calc_config.output_token_cols + + processed_df["total_tokens"] = 0 + for col in input_token_cols + output_token_cols: + processed_df["total_tokens"] += processed_df.get(col, 0).fillna(0) + # Standardize primary metric score primary_metric_config = eval_config.primary_metric if ( @@ -228,7 +304,7 @@ def create_leaderboard( "Correct": (primary_metric_name, "mean"), "Cost": ("total_cost", "mean"), "Duration": ("Duration", "mean"), - "Tokens": ("total_response_tokens", "mean"), + "Avg Total Tokens": ("total_tokens", "mean"), "Runs": ("Model", "size"), } @@ -459,7 +535,7 @@ def main(): st.subheader("LLM Evaluation Benchmark Dashboard") # --- Sidebar Setup --- - st.sidebar.header("⚙️ Configuration") + st.sidebar.header("⚙️ Data Configuration") # File selection default_dir_path = ( @@ -493,40 +569,85 @@ def main(): st.error("No data loaded. Please check the selected files.") return - available_models = sorted(df_initial["Model"].unique()) + # Grouping filter + grouping_config = eval_config.grouping + st.sidebar.subheader(f"🎯 {grouping_config.label} Filter") + + # Ensure the target column exists before trying to access it + if grouping_config.target_column not in df_initial.columns: + df_initial = extract_grouping_column(df_initial, eval_config.model_dump()) + + available_groups = sorted(df_initial[grouping_config.target_column].unique()) + selected_groups = st.sidebar.multiselect( + f"Filter by {grouping_config.label.lower()}:", + options=available_groups, + default=available_groups, + ) # Cost configuration in sidebar st.sidebar.subheader("💰 Cost Configuration") + cost_file_path = os.path.join(os.path.dirname(__file__), "costs.csv") + model_costs = load_model_costs(cost_file_path) + available_models = sorted(df_initial["Model"].unique()) + cost_config = {} + user_cost_override = {} + with st.sidebar.expander("Edit Model Costs (per 1M tokens)", expanded=False): + for model in available_models: + if model in model_costs: + cost_config[model] = model_costs[model] + else: + st.warning(f"No cost data found for model: {model}. Using zeros.") + cost_config[model] = { + "input": [{"up_to": float("inf"), "price": 0.0}], + "output": {"default": [{"up_to": float("inf"), "price": 0.0}]}, + } + + st.markdown("---") + st.markdown("Override costs below (optional, simplified):") + for model in available_models: cols = st.columns(2) - default = DEFAULT_COSTS.get(model, {"input": 0.0, "output": 0.0}) + default_input = ( + cost_config.get(model, {}).get("input", [{}])[0].get("price", 0.0) + ) + output_pricing = cost_config.get(model, {}).get("output", {}) + if "default" in output_pricing: + default_output = output_pricing["default"][0].get("price", 0.0) + elif "non_thinking" in output_pricing: + default_output = output_pricing["non_thinking"][0].get("price", 0.0) + else: + default_output = 0.0 + input_cost = cols[0].number_input( f"{model} Input", - value=float(default["input"]), + value=float(default_input), step=0.01, - format="%.2f", + format="%.4f", + key=f"{model}_input_cost", ) output_cost = cols[1].number_input( f"{model} Output", - value=float(default["output"]), + value=float(default_output), step=0.01, - format="%.2f", + format="%.4f", + key=f"{model}_output_cost", ) - cost_config[model] = {"input": input_cost, "output": output_cost} - df = process_data(df_initial, cost_config, eval_config) + if input_cost != default_input or output_cost != default_output: + user_cost_override[model] = { + "input": [{"up_to": float("inf"), "price": input_cost}], + "output": { + "default": [{"up_to": float("inf"), "price": output_cost}] + }, + } - # Grouping filter - grouping_config = eval_config.grouping - st.sidebar.subheader(f"🎯 {grouping_config.label} Filter") - available_groups = sorted(df[grouping_config.target_column].unique()) - selected_groups = st.sidebar.multiselect( - f"Filter by {grouping_config.label.lower()}:", - options=available_groups, - default=available_groups, - ) + # Apply overrides + final_cost_config = cost_config.copy() + final_cost_config.update(user_cost_override) + + df = process_data(df_initial, final_cost_config, eval_config) # --- Main Panel --- st.header("📊 Overview") @@ -566,7 +687,9 @@ def main(): "Duration": st.column_config.NumberColumn( "Avg Duration (s)", format="%.2fs" ), - "Tokens": st.column_config.NumberColumn("Avg Tokens", format="%.0f"), + "Avg Total Tokens": st.column_config.NumberColumn( + "Avg Total Tokens", format="%.0f" + ), }, use_container_width=True, ) From 42bb6186206b72d449ae89137ee88fc5f5049d22 Mon Sep 17 00:00:00 2001 From: Andrew Ginns Date: Mon, 9 Jun 2025 09:38:26 +0000 Subject: [PATCH 11/14] chore: Ruff linting fixes --- .../multi_mcp/eval_multi_mcp/evals_pydantic_mcp.py | 2 +- .../multi_mcp/eval_multi_mcp/merbench_ui.py | 3 --- .../multi_mcp/eval_multi_mcp/run_multi_evals.py | 13 ++++--------- .../multi_mcp/eval_multi_mcp/schemas.py | 2 +- 4 files changed, 6 insertions(+), 14 deletions(-) diff --git a/agents_mcp_usage/multi_mcp/eval_multi_mcp/evals_pydantic_mcp.py b/agents_mcp_usage/multi_mcp/eval_multi_mcp/evals_pydantic_mcp.py index e7cc1e4..b2ec38c 100644 --- a/agents_mcp_usage/multi_mcp/eval_multi_mcp/evals_pydantic_mcp.py +++ b/agents_mcp_usage/multi_mcp/eval_multi_mcp/evals_pydantic_mcp.py @@ -18,7 +18,7 @@ import os import random from datetime import datetime -from typing import Any, Dict, List, Optional +from typing import Any, Dict, List import logfire from dotenv import load_dotenv diff --git a/agents_mcp_usage/multi_mcp/eval_multi_mcp/merbench_ui.py b/agents_mcp_usage/multi_mcp/eval_multi_mcp/merbench_ui.py index 5ce233d..a54934a 100644 --- a/agents_mcp_usage/multi_mcp/eval_multi_mcp/merbench_ui.py +++ b/agents_mcp_usage/multi_mcp/eval_multi_mcp/merbench_ui.py @@ -3,13 +3,10 @@ import pandas as pd import plotly.graph_objects as go import streamlit as st -import numpy as np from typing import List, Dict import json import re from pydantic import ValidationError -import csv -import io from agents_mcp_usage.multi_mcp.eval_multi_mcp.dashboard_config import ( DEFAULT_CONFIG, diff --git a/agents_mcp_usage/multi_mcp/eval_multi_mcp/run_multi_evals.py b/agents_mcp_usage/multi_mcp/eval_multi_mcp/run_multi_evals.py index 9c42c28..ae316a7 100644 --- a/agents_mcp_usage/multi_mcp/eval_multi_mcp/run_multi_evals.py +++ b/agents_mcp_usage/multi_mcp/eval_multi_mcp/run_multi_evals.py @@ -16,10 +16,8 @@ import csv import os import statistics -import sys from datetime import datetime -from pathlib import Path -from typing import Any, Dict, List, Optional, Set +from typing import Any, Dict, List, Optional import logfire from dotenv import load_dotenv @@ -39,9 +37,6 @@ DEFAULT_MODELS, MermaidInput, MermaidOutput, - UsedBothMCPTools, - UsageLimitNotExceeded, - MermaidDiagramValid, fix_mermaid_diagram, create_evaluation_dataset, get_timestamp_prefix, @@ -226,7 +221,7 @@ async def fix_with_model(inputs: MermaidInput) -> MermaidOutput: error_msg = f"Error during evaluation: {str(e)}" logfire.error( - "Evaluation error", + f"Evaluation error: {error_msg}", model=model, run_index=run_index, error=str(e), @@ -409,7 +404,7 @@ async def run_all_evaluations( self, n_runs: int, parallel: bool = True, timeout: int = 120 ) -> str: """Run evaluations for all models and return path to combined results.""" - self.console.print(f"[bold green]Starting multi-model evaluation[/bold green]") + self.console.print("[bold green]Starting multi-model evaluation[/bold green]") self.console.print(f"Models: {', '.join(self.models)}") self.console.print(f"Runs per model: {n_runs}") self.console.print(f"Parallel execution: {parallel}") @@ -424,7 +419,7 @@ async def run_all_evaluations( self.print_final_summary() - self.console.print(f"\n[bold green]All evaluations complete![/bold green]") + self.console.print("\n[bold green]All evaluations complete![/bold green]") self.console.print(f"Combined results: {combined_file}") return combined_file diff --git a/agents_mcp_usage/multi_mcp/eval_multi_mcp/schemas.py b/agents_mcp_usage/multi_mcp/eval_multi_mcp/schemas.py index ed7e40c..e641ee5 100644 --- a/agents_mcp_usage/multi_mcp/eval_multi_mcp/schemas.py +++ b/agents_mcp_usage/multi_mcp/eval_multi_mcp/schemas.py @@ -3,7 +3,7 @@ """ from pydantic import BaseModel, Field, validator -from typing import List, Dict, Optional, Any +from typing import List, Dict, Optional class PrimaryMetricConfig(BaseModel): From 9375eebf5db6a7eb83b7edf920aa5c59d7786206 Mon Sep 17 00:00:00 2001 From: Andrew Ginns Date: Mon, 9 Jun 2025 09:57:12 +0000 Subject: [PATCH 12/14] docs: Google style docstrings --- .../basic_mcp/basic_mcp_use/adk_mcp.py | 9 +- .../basic_mcp/basic_mcp_use/langgraph_mcp.py | 10 +- .../basic_mcp/basic_mcp_use/oai-agent_mcp.py | 8 +- .../basic_mcp/basic_mcp_use/pydantic_mcp.py | 8 +- .../eval_multi_mcp/evals_pydantic_mcp.py | 143 ++++++++++++---- .../multi_mcp/eval_multi_mcp/merbench_ui.py | 158 ++++++++++++++++-- .../eval_multi_mcp/run_multi_evals.py | 81 +++++++-- .../multi_mcp/eval_multi_mcp/schemas.py | 10 +- .../multi_mcp/multi_mcp_use/adk_mcp.py | 23 ++- .../multi_mcp/multi_mcp_use/pydantic_mcp.py | 16 +- mcp_servers/example_server.py | 25 ++- mcp_servers/mermaid_validator.py | 20 +-- 12 files changed, 403 insertions(+), 108 deletions(-) diff --git a/agents_mcp_usage/basic_mcp/basic_mcp_use/adk_mcp.py b/agents_mcp_usage/basic_mcp/basic_mcp_use/adk_mcp.py index d167efe..2848f51 100644 --- a/agents_mcp_usage/basic_mcp/basic_mcp_use/adk_mcp.py +++ b/agents_mcp_usage/basic_mcp/basic_mcp_use/adk_mcp.py @@ -20,11 +20,14 @@ async def main(query: str = "Greet Andrew and give him the current time") -> None: - """ - Main function to run the agent + """Runs the agent with a given query. + + This function sets up the MCP server, creates an LLM agent, and runs it + with a specified query. It also handles the cleanup of the MCP server + connection. Args: - query (str): The query to run the agent with + query: The query to run the agent with. """ # Set up MCP server connection server_params = StdioServerParameters( diff --git a/agents_mcp_usage/basic_mcp/basic_mcp_use/langgraph_mcp.py b/agents_mcp_usage/basic_mcp/basic_mcp_use/langgraph_mcp.py index 72bad31..e1981e4 100644 --- a/agents_mcp_usage/basic_mcp/basic_mcp_use/langgraph_mcp.py +++ b/agents_mcp_usage/basic_mcp/basic_mcp_use/langgraph_mcp.py @@ -21,7 +21,7 @@ # Create server parameters for stdio connection server = StdioServerParameters( command="uv", - args=["run", "mcp_servers/example_server.py", "stdio"], + args=["run", "mcp_servers/example_server.py", "stdio"], ) model = ChatGoogleGenerativeAI( @@ -30,11 +30,13 @@ async def main(query: str = "Greet Andrew and give him the current time") -> None: - """ - Main function to run the agent + """Runs the LangGraph agent with a given query. + + This function connects to the MCP server, loads the tools, creates a + LangGraph agent, and invokes it with the provided query. Args: - query (str): The query to run the agent with + query: The query to run the agent with. """ async with stdio_client(server) as (read, write): async with ClientSession(read, write) as session: diff --git a/agents_mcp_usage/basic_mcp/basic_mcp_use/oai-agent_mcp.py b/agents_mcp_usage/basic_mcp/basic_mcp_use/oai-agent_mcp.py index 4dcc8d4..85a0bd8 100644 --- a/agents_mcp_usage/basic_mcp/basic_mcp_use/oai-agent_mcp.py +++ b/agents_mcp_usage/basic_mcp/basic_mcp_use/oai-agent_mcp.py @@ -14,11 +14,13 @@ async def main(query: str = "Greet Andrew and give him the current time") -> None: - """ - Main function to run the agent + """Runs the OpenAI agent with a given query. + + This function creates an MCP server, initializes an OpenAI agent with the + server, and runs the agent with the provided query. Args: - query (str): The query to run the agent with + query: The query to run the agent with. """ # Create and use the MCP server in an async context async with MCPServerStdio( diff --git a/agents_mcp_usage/basic_mcp/basic_mcp_use/pydantic_mcp.py b/agents_mcp_usage/basic_mcp/basic_mcp_use/pydantic_mcp.py index 6584e54..5b81afd 100644 --- a/agents_mcp_usage/basic_mcp/basic_mcp_use/pydantic_mcp.py +++ b/agents_mcp_usage/basic_mcp/basic_mcp_use/pydantic_mcp.py @@ -25,11 +25,13 @@ async def main(query: str = "Greet Andrew and give him the current time") -> None: - """ - Main function to run the agent + """Runs the Pydantic agent with a given query. + + This function runs the Pydantic agent with the provided query and prints the + output. Args: - query (str): The query to run the agent with + query: The query to run the agent with. """ async with agent.run_mcp_servers(): result = await agent.run(query) diff --git a/agents_mcp_usage/multi_mcp/eval_multi_mcp/evals_pydantic_mcp.py b/agents_mcp_usage/multi_mcp/eval_multi_mcp/evals_pydantic_mcp.py index b2ec38c..d1a9338 100644 --- a/agents_mcp_usage/multi_mcp/eval_multi_mcp/evals_pydantic_mcp.py +++ b/agents_mcp_usage/multi_mcp/eval_multi_mcp/evals_pydantic_mcp.py @@ -68,7 +68,17 @@ def is_retryable_error(exception: Exception) -> bool: - """Check if an exception should be retried.""" + """Checks if an exception is retryable. + + This function checks if the given exception is a retryable HTTP error or a + general connection error. + + Args: + exception: The exception to check. + + Returns: + True if the exception is retryable, False otherwise. + """ if isinstance(exception, ModelHTTPError): return exception.status_code in RETRYABLE_HTTP_STATUS_CODES @@ -80,27 +90,29 @@ def is_retryable_error(exception: Exception) -> bool: async def exponential_backoff_retry( - func_call, + func_call: callable, max_attempts: int = MAX_RETRY_ATTEMPTS, base_delay: float = BASE_RETRY_DELAY, max_delay: float = MAX_RETRY_DELAY, jitter: bool = True, ) -> Any: - """ - Execute a function with exponential backoff retry logic. + """Executes a function with exponential backoff retry logic. + + This function attempts to execute the given asynchronous function call, + retrying with an exponential backoff delay if a retryable error occurs. Args: - func_call: Async function to retry - max_attempts: Maximum number of retry attempts - base_delay: Base delay between retries in seconds - max_delay: Maximum delay between retries in seconds - jitter: Whether to add random jitter to delays + func_call: The async function to retry. + max_attempts: The maximum number of retry attempts. + base_delay: The base delay between retries in seconds. + max_delay: The maximum delay between retries in seconds. + jitter: Whether to add random jitter to the delays. Returns: - Result of the function call + The result of the function call. Raises: - The last exception if all retries are exhausted + The last exception if all retries are exhausted. """ last_exception = None @@ -158,7 +170,14 @@ async def exponential_backoff_retry( def get_mcp_servers() -> List[MCPServerStdio]: - """Get the configured MCP servers for the evaluation.""" + """Gets the configured MCP servers for the evaluation. + + This function returns a list of MCP servers required for the evaluation, + including the local example server and the mermaid validator server. + + Returns: + A list of configured MCP servers. + """ local_server = MCPServerStdio( command="uv", args=[ @@ -180,14 +199,17 @@ def get_mcp_servers() -> List[MCPServerStdio]: def create_agent( model: str = DEFAULT_MODEL, model_settings: Dict[str, Any] = None ) -> Agent: - """Create an agent with MCP servers for the specified model. + """Creates an agent with MCP servers for the specified model. + + This function initializes and returns an agent with the necessary MCP + servers and model settings. Args: - model: The model to use for the agent - model_settings: Optional model-specific settings + model: The model to use for the agent. + model_settings: Optional model-specific settings. Returns: - Configured Agent instance + A configured Agent instance. """ if model_settings is None: model_settings = {} @@ -230,6 +252,18 @@ class UsedBothMCPTools(Evaluator[MermaidInput, MermaidOutput]): async def evaluate( self, ctx: EvaluatorContext[MermaidInput, MermaidOutput] ) -> float: + """Evaluates if both MCP tools were used in the given context. + + This method checks the tools used in the output and returns a score + based on whether tools from both MCP servers were utilized. + + Args: + ctx: The evaluator context containing the input and output. + + Returns: + A score of 1.0 if both tools were used, 0.5 if one was used, + and 0.0 otherwise. + """ if not ctx.output or not ctx.output.tools_used: return 0.0 @@ -257,7 +291,17 @@ class UsageLimitNotExceeded(Evaluator[MermaidInput, MermaidOutput]): async def evaluate( self, ctx: EvaluatorContext[MermaidInput, MermaidOutput] ) -> float: - """Check if the case failed due to usage limits being exceeded.""" + """Checks if the case failed due to usage limits being exceeded. + + This method examines the output for a usage limit failure reason and + returns a score accordingly. + + Args: + ctx: The evaluator context. + + Returns: + 0.0 if a usage limit failure occurred, 1.0 otherwise. + """ if ctx.output and ctx.output.failure_reason == "usage_limit_exceeded": logfire.warning( "Case failed due to usage limit exceeded", @@ -274,6 +318,17 @@ class MermaidDiagramValid(Evaluator[MermaidInput, MermaidOutput]): async def evaluate( self, ctx: EvaluatorContext[MermaidInput, MermaidOutput] ) -> float: + """Evaluates if the generated mermaid diagram is valid. + + This method validates the mermaid diagram in the output, handling + retries and logging the results. + + Args: + ctx: The evaluator context. + + Returns: + 1.0 if the diagram is valid, 0.0 otherwise. + """ # Skip validation if there was a failure if ctx.output and ctx.output.failure_reason: logfire.info( @@ -331,14 +386,17 @@ async def evaluate( async def fix_mermaid_diagram( inputs: MermaidInput, model: str = DEFAULT_MODEL ) -> MermaidOutput: - """Fix an invalid mermaid diagram using the agent with multiple MCP servers. + """Fixes an invalid mermaid diagram using an agent with multiple MCP servers. + + This function runs an agent to fix a given mermaid diagram, handling + various exceptions and capturing metrics. Args: - inputs: The input containing the invalid diagram - model: The model to use for the agent + inputs: The input containing the invalid diagram. + model: The model to use for the agent. Returns: - MermaidOutput with the fixed diagram and captured metrics + A MermaidOutput object with the fixed diagram and captured metrics. """ query = f"Add the current time and fix the mermaid diagram syntax using the validator: {inputs.invalid_diagram}. Return only the fixed mermaid diagram between backticks." @@ -477,13 +535,16 @@ async def _run_agent(): def create_evaluation_dataset( judge_model: str = DEFAULT_MODEL, ) -> Dataset[MermaidInput, MermaidOutput, Any]: - """Create the dataset for evaluating mermaid diagram fixing. + """Creates the dataset for evaluating mermaid diagram fixing. + + This function constructs a dataset with test cases of varying difficulty + and a set of evaluators for judging the results. Args: - judge_model: The model to use for LLM judging + judge_model: The model to use for LLM judging. Returns: - The evaluation dataset + The evaluation dataset. """ return Dataset[MermaidInput, MermaidOutput, Any]( # Construct 3 tests, each asks the LLM to fix an invalid mermaid diagram of increasing difficulty @@ -546,7 +607,11 @@ def create_evaluation_dataset( def get_timestamp_prefix() -> str: - """Get a timestamp prefix in the format yyyy-mm-dd_H-M-s.""" + """Gets a timestamp prefix in the format yyyy-mm-dd_H-M-s. + + Returns: + A string representing the current timestamp. + """ now = datetime.now() return now.strftime("%Y-%m-%d_%H-%M-%S") @@ -554,15 +619,18 @@ def get_timestamp_prefix() -> str: def write_mermaid_results_to_csv( report: EvaluationReport, model: str, output_dir: str = "./mermaid_results" ) -> str: - """Write mermaid evaluation results with metrics to a CSV file. + """Writes mermaid evaluation results with metrics to a CSV file. + + This function takes an evaluation report and writes the results to a CSV + file, including scores and metrics. Args: - report: The evaluation report from pydantic_evals - model: The model name used for evaluation - output_dir: Directory to write the CSV file + report: The evaluation report from pydantic_evals. + model: The model name used for evaluation. + output_dir: The directory to write the CSV file to. Returns: - Path to the created CSV file + The path to the created CSV file. """ os.makedirs(output_dir, exist_ok=True) @@ -655,16 +723,19 @@ async def run_evaluations( export_csv: bool = True, output_dir: str = "./mermaid_results", ) -> EvaluationReport: - """Run the evaluations on the mermaid diagram fixing task. + """Runs the evaluations on the mermaid diagram fixing task. + + This function sets up the evaluation dataset, runs the evaluation for a + given model, and exports the results to a CSV file. Args: - model: The model to use for the agent - judge_model: The model to use for LLM judging - export_csv: Whether to export results to CSV - output_dir: Directory to save results + model: The model to use for the agent. + judge_model: The model to use for LLM judging. + export_csv: Whether to export the results to a CSV file. + output_dir: The directory to save the results to. Returns: - The evaluation report + The evaluation report. """ dataset = create_evaluation_dataset(judge_model) diff --git a/agents_mcp_usage/multi_mcp/eval_multi_mcp/merbench_ui.py b/agents_mcp_usage/multi_mcp/eval_multi_mcp/merbench_ui.py index a54934a..4d2240d 100644 --- a/agents_mcp_usage/multi_mcp/eval_multi_mcp/merbench_ui.py +++ b/agents_mcp_usage/multi_mcp/eval_multi_mcp/merbench_ui.py @@ -30,7 +30,14 @@ def load_model_costs(file_path: str) -> Dict: - """Loads model costs from a CSV file and returns a structured dictionary.""" + """Loads model costs from a CSV file and returns a structured dictionary. + + Args: + file_path: The path to the cost file. + + Returns: + A dictionary containing the model costs. + """ try: with open(file_path, "r", encoding="utf-8") as f: # Read lines, skipping comments and empty lines @@ -62,7 +69,14 @@ def load_model_costs(file_path: str) -> Dict: def find_all_combined_results_csvs(directory_path: str) -> list[str]: - """Finds all '*_combined_results.csv' files, sorted by modification time.""" + """Finds all '*_combined_results.csv' files, sorted by modification time. + + Args: + directory_path: The path to the directory to search. + + Returns: + A list of CSV file paths. + """ if not os.path.isdir(directory_path): return [] try: @@ -75,7 +89,14 @@ def find_all_combined_results_csvs(directory_path: str) -> list[str]: def detect_csv_files(directory: str = None) -> List[str]: - """Detect CSV result files in the specified directory.""" + """Detects CSV result files in the specified directory. + + Args: + directory: The directory to search for CSV files. + + Returns: + A list of detected CSV file paths. + """ if directory is None: # Go up three levels from the current script to the project root script_dir = os.path.dirname(os.path.abspath(__file__)) @@ -85,7 +106,14 @@ def detect_csv_files(directory: str = None) -> List[str]: def load_csv_data(file_paths: List[str]) -> pd.DataFrame: - """Load and combine multiple CSV files into a single DataFrame.""" + """Loads and combines multiple CSV files into a single DataFrame. + + Args: + file_paths: A list of paths to the CSV files. + + Returns: + A pandas DataFrame containing the combined data. + """ if not file_paths: return pd.DataFrame() @@ -109,7 +137,15 @@ def load_csv_data(file_paths: List[str]) -> pd.DataFrame: def extract_grouping_column(df: pd.DataFrame, config: Dict) -> pd.DataFrame: - """Extracts a grouping column based on regex from the config.""" + """Extracts a grouping column based on a regex from the config. + + Args: + df: The DataFrame to process. + config: The dashboard configuration. + + Returns: + The DataFrame with the new grouping column. + """ source_col = config["grouping"]["column"] target_col = config["grouping"]["target_column"] regex = config["grouping"]["extractor_regex"] @@ -123,7 +159,14 @@ def extract_group(value: str) -> str: def parse_metric_details(metric_details_str: str) -> Dict: - """Safely parses a JSON string from the 'Metric_details' column.""" + """Safely parses a JSON string from the 'Metric_details' column. + + Args: + metric_details_str: The JSON string to parse. + + Returns: + A dictionary containing the parsed data. + """ if pd.isna(metric_details_str) or not metric_details_str: return {} try: @@ -134,7 +177,15 @@ def parse_metric_details(metric_details_str: str) -> Dict: def get_price_for_tokens(token_count: int, price_tiers: List[Dict]) -> float: - """Finds the correct price for a given number of tokens from a list of tiers.""" + """Finds the correct price for a given number of tokens from a list of tiers. + + Args: + token_count: The number of tokens. + price_tiers: A list of price tiers. + + Returns: + The price for the given number of tokens. + """ for tier in price_tiers: if token_count <= tier["up_to"]: return tier["price"] @@ -144,7 +195,16 @@ def get_price_for_tokens(token_count: int, price_tiers: List[Dict]) -> float: def calculate_costs( df: pd.DataFrame, cost_config: Dict, eval_config: Dict ) -> pd.DataFrame: - """Calculates input, output, and total costs for each run based on new tiered pricing.""" + """Calculates input, output, and total costs for each run based on tiered pricing. + + Args: + df: The DataFrame to process. + cost_config: The model cost configuration. + eval_config: The dashboard evaluation configuration. + + Returns: + The DataFrame with the calculated costs. + """ df_with_costs = df.copy() cost_calc_config = eval_config.get("cost_calculation", {}) input_token_cols = cost_calc_config.get("input_token_cols", []) @@ -212,7 +272,16 @@ def calculate_costs( def process_data( df: pd.DataFrame, cost_config: Dict, eval_config: DashboardConfig ) -> pd.DataFrame: - """Main data processing pipeline.""" + """The main data processing pipeline. + + Args: + df: The DataFrame to process. + cost_config: The model cost configuration. + eval_config: The dashboard evaluation configuration. + + Returns: + The processed DataFrame. + """ if df.empty: return df @@ -285,7 +354,16 @@ def process_data( def create_leaderboard( df: pd.DataFrame, selected_groups: List[str], config: Dict ) -> pd.DataFrame: - """Creates a leaderboard DataFrame with key performance indicators.""" + """Creates a leaderboard DataFrame with key performance indicators. + + Args: + df: The DataFrame to process. + selected_groups: A list of selected groups to include. + config: The dashboard configuration. + + Returns: + A DataFrame representing the leaderboard. + """ if df.empty or not selected_groups: return pd.DataFrame() @@ -313,7 +391,17 @@ def create_leaderboard( def create_pareto_frontier_plot( df: pd.DataFrame, selected_groups: List[str], x_axis_mode: str, config: Dict ) -> go.Figure: - """Visualizes the trade-off between model performance and cost/token usage.""" + """Visualizes the trade-off between model performance and cost/token usage. + + Args: + df: The DataFrame to process. + selected_groups: A list of selected groups to include. + x_axis_mode: The mode for the x-axis (e.g., 'cost' or 'tokens'). + config: The dashboard configuration. + + Returns: + A Plotly figure object. + """ fig = go.Figure() plot_config = config["plots"]["pareto"] if df.empty or not selected_groups or not plot_config.get("enabled", False): @@ -378,7 +466,16 @@ def create_pareto_frontier_plot( def create_success_rates_plot( df: pd.DataFrame, selected_groups: List[str], config: Dict ) -> go.Figure: - """Compares models across different success metrics.""" + """Compares models across different success metrics. + + Args: + df: The DataFrame to process. + selected_groups: A list of selected groups to include. + config: The dashboard configuration. + + Returns: + A Plotly figure object. + """ fig = go.Figure() plot_config = config["plots"]["success_rates"] if df.empty or not selected_groups or not plot_config.get("enabled", False): @@ -422,7 +519,16 @@ def create_success_rates_plot( def create_failure_analysis_plot( df: pd.DataFrame, selected_groups: List[str], config: Dict ) -> go.Figure: - """Shows common failure reasons by model.""" + """Shows common failure reasons by model. + + Args: + df: The DataFrame to process. + selected_groups: A list of selected groups to include. + config: The dashboard configuration. + + Returns: + A Plotly figure object. + """ fig = go.Figure() plot_config = config["plots"]["failure_analysis"] if df.empty or not selected_groups or not plot_config.get("enabled", False): @@ -459,7 +565,16 @@ def create_failure_analysis_plot( def create_token_breakdown_plot( df: pd.DataFrame, selected_groups: List[str], config: Dict ) -> go.Figure: - """Creates a stacked bar chart showing token breakdown.""" + """Creates a stacked bar chart showing token breakdown. + + Args: + df: The DataFrame to process. + selected_groups: A list of selected groups to include. + config: The dashboard configuration. + + Returns: + A Plotly figure object. + """ fig = go.Figure() plot_config = config["plots"]["token_breakdown"] if df.empty or not selected_groups or not plot_config.get("enabled", False): @@ -493,7 +608,16 @@ def create_token_breakdown_plot( def create_cost_breakdown_plot( df: pd.DataFrame, selected_groups: List[str], config: Dict ) -> go.Figure: - """Creates a stacked bar chart for cost breakdown.""" + """Creates a stacked bar chart for cost breakdown. + + Args: + df: The DataFrame to process. + selected_groups: A list of selected groups to include. + config: The dashboard configuration. + + Returns: + A Plotly figure object. + """ fig = go.Figure() plot_config = config["plots"]["cost_breakdown"] if df.empty or not selected_groups or not plot_config.get("enabled", False): @@ -524,8 +648,8 @@ def create_cost_breakdown_plot( return fig -def main(): - """Main Streamlit application entrypoint.""" +def main() -> None: + """The main Streamlit application entrypoint.""" eval_config = EVAL_CONFIG # Use the validated config st.title(eval_config.title) diff --git a/agents_mcp_usage/multi_mcp/eval_multi_mcp/run_multi_evals.py b/agents_mcp_usage/multi_mcp/eval_multi_mcp/run_multi_evals.py index ae316a7..8f56ba1 100644 --- a/agents_mcp_usage/multi_mcp/eval_multi_mcp/run_multi_evals.py +++ b/agents_mcp_usage/multi_mcp/eval_multi_mcp/run_multi_evals.py @@ -59,12 +59,21 @@ def __init__(self, model: str): self.reports: List[EvaluationReport] = [] self.failed_runs: List[Dict[str, Any]] = [] - def add_successful_run(self, report: EvaluationReport): - """Add a successful evaluation report.""" + def add_successful_run(self, report: EvaluationReport) -> None: + """Adds a successful evaluation report. + + Args: + report: The evaluation report to add. + """ self.reports.append(report) - def add_failed_run(self, run_index: int, error: str): - """Add information about a failed run.""" + def add_failed_run(self, run_index: int, error: str) -> None: + """Adds information about a failed run. + + Args: + run_index: The index of the failed run. + error: The error message. + """ self.failed_runs.append( { "run_index": run_index, @@ -74,14 +83,25 @@ def add_failed_run(self, run_index: int, error: str): ) def get_success_rate(self) -> float: - """Calculate the success rate for this model.""" + """Calculates the success rate for this model. + + Returns: + The success rate as a float. + """ total_runs = len(self.reports) + len(self.failed_runs) if total_runs == 0: return 0.0 return len(self.reports) / total_runs def write_individual_results(self, output_dir: str) -> Optional[str]: - """Write individual model results to CSV file.""" + """Writes individual model results to a CSV file. + + Args: + output_dir: The directory to write the CSV file to. + + Returns: + The path to the created CSV file, or None if no results were written. + """ if not self.reports: return None @@ -180,7 +200,17 @@ def __init__( async def run_single_evaluation( self, model: str, run_index: int, dataset, timeout: int = 120 ) -> Optional[EvaluationReport]: - """Run a single evaluation for a model with timeout and error handling.""" + """Runs a single evaluation for a model with timeout and error handling. + + Args: + model: The model to evaluate. + run_index: The index of the run. + dataset: The evaluation dataset. + timeout: The timeout in seconds for the evaluation. + + Returns: + An EvaluationReport if successful, otherwise None. + """ try: async def fix_with_model(inputs: MermaidInput) -> MermaidOutput: @@ -238,8 +268,16 @@ async def run_model_evaluations( dataset, parallel: bool = True, timeout: int = 600, - ): - """Run multiple evaluations for a single model.""" + ) -> None: + """Runs multiple evaluations for a single model. + + Args: + model: The model to evaluate. + n_runs: The number of runs to perform. + dataset: The evaluation dataset. + parallel: Whether to run the evaluations in parallel. + timeout: The timeout in seconds for each evaluation. + """ self.console.print(f"\n[bold cyan]Evaluating model: {model}[/bold cyan]") if parallel: @@ -289,7 +327,11 @@ async def run_model_evaluations( self.console.print(f" Success rate: {success_rate:.1%}") def write_combined_results(self) -> str: - """Write combined results from all models to a single CSV file.""" + """Writes combined results from all models to a single CSV file. + + Returns: + The path to the combined results CSV file. + """ os.makedirs(self.output_dir, exist_ok=True) timestamp = get_timestamp_prefix() filepath = os.path.join(self.output_dir, f"{timestamp}_combined_results.csv") @@ -366,8 +408,8 @@ def write_combined_results(self) -> str: return filepath - def print_final_summary(self): - """Print a comprehensive summary of all results.""" + def print_final_summary(self) -> None: + """Prints a comprehensive summary of all results.""" table = Table(title="Multi-Model Evaluation Summary") table.add_column("Model", style="cyan") @@ -403,7 +445,16 @@ def print_final_summary(self): async def run_all_evaluations( self, n_runs: int, parallel: bool = True, timeout: int = 120 ) -> str: - """Run evaluations for all models and return path to combined results.""" + """Runs evaluations for all models and returns the path to the combined results. + + Args: + n_runs: The number of runs per model. + parallel: Whether to run the evaluations in parallel. + timeout: The timeout in seconds for each evaluation. + + Returns: + The path to the combined results CSV file. + """ self.console.print("[bold green]Starting multi-model evaluation[/bold green]") self.console.print(f"Models: {', '.join(self.models)}") self.console.print(f"Runs per model: {n_runs}") @@ -425,8 +476,8 @@ async def run_all_evaluations( return combined_file -async def main(): - """Main entry point for the script.""" +async def main() -> None: + """The main entry point for the script.""" parser = argparse.ArgumentParser( description="Run mermaid diagram evaluations across multiple LLM models" ) diff --git a/agents_mcp_usage/multi_mcp/eval_multi_mcp/schemas.py b/agents_mcp_usage/multi_mcp/eval_multi_mcp/schemas.py index e641ee5..dc8059b 100644 --- a/agents_mcp_usage/multi_mcp/eval_multi_mcp/schemas.py +++ b/agents_mcp_usage/multi_mcp/eval_multi_mcp/schemas.py @@ -3,7 +3,7 @@ """ from pydantic import BaseModel, Field, validator -from typing import List, Dict, Optional +from typing import List, Dict, Optional, Any class PrimaryMetricConfig(BaseModel): @@ -16,7 +16,8 @@ class PrimaryMetricConfig(BaseModel): ) @validator("goal") - def goal_must_be_max_or_min(cls, v): + def goal_must_be_max_or_min(cls, v: str) -> str: + """Validates that the goal is either 'maximize' or 'minimize'.""" if v not in ["maximize", "minimize"]: raise ValueError("goal must be 'maximize' or 'minimize'") return v @@ -65,7 +66,10 @@ class BarPlotConfig(BaseModel): series: Optional[List[StackedBarPlotSeries]] = None @validator("y_columns", always=True) - def check_prefix_or_columns(cls, v, values): + def check_prefix_or_columns( + cls, v: Optional[List[str]], values: Dict[str, Any] + ) -> Optional[List[str]]: + """Validates that either 'y_prefix' or 'y_columns' is provided for grouped_bar plots.""" if not values.get("y_prefix") and not v: if values.get("type") == "grouped_bar": raise ValueError( diff --git a/agents_mcp_usage/multi_mcp/multi_mcp_use/adk_mcp.py b/agents_mcp_usage/multi_mcp/multi_mcp_use/adk_mcp.py index 4d5d3da..0cdd3c4 100644 --- a/agents_mcp_usage/multi_mcp/multi_mcp_use/adk_mcp.py +++ b/agents_mcp_usage/multi_mcp/multi_mcp_use/adk_mcp.py @@ -24,8 +24,16 @@ logfire.instrument_mcp() -async def get_tools_async(): - """Initializes connections to MCP servers and returns their tools along with a combined exit stack.""" +async def get_tools_async() -> tuple[list, contextlib.AsyncExitStack]: + """Initializes connections to MCP servers and returns their tools. + + This function connects to the local example server and the mermaid + validator server, and returns the combined tools and a combined exit + stack for cleanup. + + Returns: + A tuple containing the list of all tools and the combined exit stack. + """ print("Connecting to MCP servers...") # Create a single exit stack for all connections @@ -71,12 +79,15 @@ async def get_tools_async(): async def main(query: str = "Hi!", request_limit: int = 5) -> None: - """ - Main function to run the agent + """Runs the agent with a given query and request limit. + + This function initializes the tools, creates an agent, and runs it with + the provided query and request limit. It also handles the cleanup of + the MCP server connections and Logfire. Args: - query (str): The query to run the agent with - request_limit (int): The number of requests to make to the MCP servers + query: The query to run the agent with. + request_limit: The number of requests to make to the MCP servers. """ # Get tools from MCP servers tools, exit_stack = await get_tools_async() diff --git a/agents_mcp_usage/multi_mcp/multi_mcp_use/pydantic_mcp.py b/agents_mcp_usage/multi_mcp/multi_mcp_use/pydantic_mcp.py index 4d3e1ff..96f7d0b 100644 --- a/agents_mcp_usage/multi_mcp/multi_mcp_use/pydantic_mcp.py +++ b/agents_mcp_usage/multi_mcp/multi_mcp_use/pydantic_mcp.py @@ -1,4 +1,5 @@ import asyncio +from typing import Any import logfire from dotenv import load_dotenv @@ -40,13 +41,18 @@ Agent.instrument_all() -async def main(query: str = "Hi!", request_limit: int = 5) -> None: - """ - Main function to run the agent +async def main(query: str = "Hi!", request_limit: int = 5) -> Any: + """Runs the Pydantic agent with a given query and request limit. + + This function invokes the Pydantic agent with the provided query and + usage limits, and prints the output. Args: - query (str): The query to run the agent with - request_limit (int): The number of requests to make to the MCP servers + query: The query to run the agent with. + request_limit: The number of requests to make to the MCP servers. + + Returns: + The result of the agent run. """ # Set a request limit for LLM calls usage_limits = UsageLimits(request_limit=request_limit) diff --git a/mcp_servers/example_server.py b/mcp_servers/example_server.py index 6ddc1b2..fa1e015 100644 --- a/mcp_servers/example_server.py +++ b/mcp_servers/example_server.py @@ -9,21 +9,40 @@ # Add an addition tool @mcp.tool() def add(a: int, b: int) -> int: - """Add two numbers""" + """Adds two numbers. + + Args: + a: The first number. + b: The second number. + + Returns: + The sum of the two numbers. + """ return a + b # Add a tool to get the current time @mcp.tool() def get_current_time() -> str: - """Get the current time""" + """Gets the current time. + + Returns: + The current time in 'YYYY-MM-DD HH:MM:SS' format. + """ return datetime.now().strftime("%Y-%m-%d %H:%M:%S") # Add a dynamic greeting resource @mcp.resource("greeting://{name}") def get_greeting(name: str) -> str: - """Get a personalised greeting""" + """Gets a personalised greeting. + + Args: + name: The name to include in the greeting. + + Returns: + A personalised greeting string. + """ return f"Hello, {name}!" diff --git a/mcp_servers/mermaid_validator.py b/mcp_servers/mermaid_validator.py index 92ba9ad..2a9a9fc 100644 --- a/mcp_servers/mermaid_validator.py +++ b/mcp_servers/mermaid_validator.py @@ -27,17 +27,18 @@ class MermaidValidationResult(BaseModel): @mcp.tool() async def validate_mermaid_diagram(diagram_text: str) -> MermaidValidationResult: - """ - Validate a mermaid diagram and render it as a PNG image if valid. + """Validates a mermaid diagram and renders it as a PNG image if valid. + + This function uses the mermaid-cli to validate and render the provided + mermaid diagram text.Requires mermaid-cli to be installed globally via npm + `npm install -g @mermaid-js/mermaid-cli` - Uses mermaid-cli to validate and render the diagram. Requires mermaid-cli - to be installed globally via npm: npm install -g @mermaid-js/mermaid-cli Args: - diagram_text: The mermaid diagram text to validate + diagram_text: The mermaid diagram text to validate. Returns: - A MermaidValidationResult object containing validation results + A MermaidValidationResult object containing the validation results. """ temp_file_path = None output_file_name = None @@ -107,12 +108,11 @@ async def validate_mermaid_diagram(diagram_text: str) -> MermaidValidationResult @mcp.resource("example://mermaid-diagram") -def get_example_mermaid_diagram(): - """ - Provides an example mermaid diagram for the client. +def get_example_mermaid_diagram() -> dict: + """Provides an example mermaid diagram for the client. Returns: - Dict containing an example mermaid diagram + A dictionary containing an example mermaid diagram. """ return { "diagram": """ From 09cf03ac2b98d213a25c72c802389ac3b894b6b5 Mon Sep 17 00:00:00 2001 From: Andrew Ginns Date: Mon, 9 Jun 2025 09:57:20 +0000 Subject: [PATCH 13/14] chore: Update lock --- uv.lock | 1047 +++++++++++++++++++++++++++++-------------------------- 1 file changed, 560 insertions(+), 487 deletions(-) diff --git a/uv.lock b/uv.lock index ab54dfd..5c3c391 100644 --- a/uv.lock +++ b/uv.lock @@ -13,7 +13,7 @@ wheels = [ [[package]] name = "aiohttp" -version = "3.11.18" +version = "3.12.11" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiohappyeyeballs" }, @@ -24,24 +24,25 @@ dependencies = [ { name = "propcache" }, { name = "yarl" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/63/e7/fa1a8c00e2c54b05dc8cb5d1439f627f7c267874e3f7bb047146116020f9/aiohttp-3.11.18.tar.gz", hash = "sha256:ae856e1138612b7e412db63b7708735cff4d38d0399f6a5435d3dac2669f558a", size = 7678653, upload-time = "2025-04-21T09:43:09.191Z" } +sdist = { url = "https://files.pythonhosted.org/packages/93/6b/850a842871ab7be0d00686750d0ee9d8fb8e7be981e4e5700bb6c88f1b8f/aiohttp-3.12.11.tar.gz", hash = "sha256:a5149ae1b11ce4cf8b122846bfa3d7c5f29fe3bfe6745ab21b3eea9615bc5564", size = 7814403, upload-time = "2025-06-07T15:53:26.157Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0a/18/be8b5dd6b9cf1b2172301dbed28e8e5e878ee687c21947a6c81d6ceaa15d/aiohttp-3.11.18-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:474215ec618974054cf5dc465497ae9708543cbfc312c65212325d4212525811", size = 699833, upload-time = "2025-04-21T09:42:00.298Z" }, - { url = "https://files.pythonhosted.org/packages/0d/84/ecdc68e293110e6f6f6d7b57786a77555a85f70edd2b180fb1fafaff361a/aiohttp-3.11.18-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:6ced70adf03920d4e67c373fd692123e34d3ac81dfa1c27e45904a628567d804", size = 462774, upload-time = "2025-04-21T09:42:02.015Z" }, - { url = "https://files.pythonhosted.org/packages/d7/85/f07718cca55884dad83cc2433746384d267ee970e91f0dcc75c6d5544079/aiohttp-3.11.18-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2d9f6c0152f8d71361905aaf9ed979259537981f47ad099c8b3d81e0319814bd", size = 454429, upload-time = "2025-04-21T09:42:03.728Z" }, - { url = "https://files.pythonhosted.org/packages/82/02/7f669c3d4d39810db8842c4e572ce4fe3b3a9b82945fdd64affea4c6947e/aiohttp-3.11.18-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a35197013ed929c0aed5c9096de1fc5a9d336914d73ab3f9df14741668c0616c", size = 1670283, upload-time = "2025-04-21T09:42:06.053Z" }, - { url = "https://files.pythonhosted.org/packages/ec/79/b82a12f67009b377b6c07a26bdd1b81dab7409fc2902d669dbfa79e5ac02/aiohttp-3.11.18-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:540b8a1f3a424f1af63e0af2d2853a759242a1769f9f1ab053996a392bd70118", size = 1717231, upload-time = "2025-04-21T09:42:07.953Z" }, - { url = "https://files.pythonhosted.org/packages/a6/38/d5a1f28c3904a840642b9a12c286ff41fc66dfa28b87e204b1f242dbd5e6/aiohttp-3.11.18-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f9e6710ebebfce2ba21cee6d91e7452d1125100f41b906fb5af3da8c78b764c1", size = 1769621, upload-time = "2025-04-21T09:42:09.855Z" }, - { url = "https://files.pythonhosted.org/packages/53/2d/deb3749ba293e716b5714dda06e257f123c5b8679072346b1eb28b766a0b/aiohttp-3.11.18-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8af2ef3b4b652ff109f98087242e2ab974b2b2b496304063585e3d78de0b000", size = 1678667, upload-time = "2025-04-21T09:42:11.741Z" }, - { url = "https://files.pythonhosted.org/packages/b8/a8/04b6e11683a54e104b984bd19a9790eb1ae5f50968b601bb202d0406f0ff/aiohttp-3.11.18-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:28c3f975e5ae3dbcbe95b7e3dcd30e51da561a0a0f2cfbcdea30fc1308d72137", size = 1601592, upload-time = "2025-04-21T09:42:14.137Z" }, - { url = "https://files.pythonhosted.org/packages/5e/9d/c33305ae8370b789423623f0e073d09ac775cd9c831ac0f11338b81c16e0/aiohttp-3.11.18-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c28875e316c7b4c3e745172d882d8a5c835b11018e33432d281211af35794a93", size = 1621679, upload-time = "2025-04-21T09:42:16.056Z" }, - { url = "https://files.pythonhosted.org/packages/56/45/8e9a27fff0538173d47ba60362823358f7a5f1653c6c30c613469f94150e/aiohttp-3.11.18-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:13cd38515568ae230e1ef6919e2e33da5d0f46862943fcda74e7e915096815f3", size = 1656878, upload-time = "2025-04-21T09:42:18.368Z" }, - { url = "https://files.pythonhosted.org/packages/84/5b/8c5378f10d7a5a46b10cb9161a3aac3eeae6dba54ec0f627fc4ddc4f2e72/aiohttp-3.11.18-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:0e2a92101efb9f4c2942252c69c63ddb26d20f46f540c239ccfa5af865197bb8", size = 1620509, upload-time = "2025-04-21T09:42:20.141Z" }, - { url = "https://files.pythonhosted.org/packages/9e/2f/99dee7bd91c62c5ff0aa3c55f4ae7e1bc99c6affef780d7777c60c5b3735/aiohttp-3.11.18-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:e6d3e32b8753c8d45ac550b11a1090dd66d110d4ef805ffe60fa61495360b3b2", size = 1680263, upload-time = "2025-04-21T09:42:21.993Z" }, - { url = "https://files.pythonhosted.org/packages/03/0a/378745e4ff88acb83e2d5c884a4fe993a6e9f04600a4560ce0e9b19936e3/aiohttp-3.11.18-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:ea4cf2488156e0f281f93cc2fd365025efcba3e2d217cbe3df2840f8c73db261", size = 1715014, upload-time = "2025-04-21T09:42:23.87Z" }, - { url = "https://files.pythonhosted.org/packages/f6/0b/b5524b3bb4b01e91bc4323aad0c2fcaebdf2f1b4d2eb22743948ba364958/aiohttp-3.11.18-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9d4df95ad522c53f2b9ebc07f12ccd2cb15550941e11a5bbc5ddca2ca56316d7", size = 1666614, upload-time = "2025-04-21T09:42:25.764Z" }, - { url = "https://files.pythonhosted.org/packages/c7/b7/3d7b036d5a4ed5a4c704e0754afe2eef24a824dfab08e6efbffb0f6dd36a/aiohttp-3.11.18-cp313-cp313-win32.whl", hash = "sha256:cdd1bbaf1e61f0d94aced116d6e95fe25942f7a5f42382195fd9501089db5d78", size = 411358, upload-time = "2025-04-21T09:42:27.558Z" }, - { url = "https://files.pythonhosted.org/packages/1e/3c/143831b32cd23b5263a995b2a1794e10aa42f8a895aae5074c20fda36c07/aiohttp-3.11.18-cp313-cp313-win_amd64.whl", hash = "sha256:bdd619c27e44382cf642223f11cfd4d795161362a5a1fc1fa3940397bc89db01", size = 437658, upload-time = "2025-04-21T09:42:29.209Z" }, + { url = "https://files.pythonhosted.org/packages/87/ac/15e21c6a17b5183d1617505b125c773f554a56e06be577a289151a8e5ce7/aiohttp-3.12.11-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:5fadc4b67f972a701805aa501cd9d22cdbeda21f9c9ae85e60678f84b1727a16", size = 694170, upload-time = "2025-06-07T15:52:07.973Z" }, + { url = "https://files.pythonhosted.org/packages/02/5b/347f8aff5793829b3a31a927bd039ec4f22221a32c459b9d19fe880921e3/aiohttp-3.12.11-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:144d67c29ae36f052584fc45a363e92798441a5af5762d83037aade3e2aa9dc5", size = 471832, upload-time = "2025-06-07T15:52:09.954Z" }, + { url = "https://files.pythonhosted.org/packages/4b/e5/9ed82f5b6a2dca30940e90820ce2f8203e15111de464bba0980e2c7e169b/aiohttp-3.12.11-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6b73299e4bf37d14c6e4ca5ce7087b44914a8d9e1f40faedc271f28d64ec277e", size = 464133, upload-time = "2025-06-07T15:52:12.447Z" }, + { url = "https://files.pythonhosted.org/packages/3c/8d/edcddc41d4f1157a2536143476070ae66de2b839af3724655c2a6358670a/aiohttp-3.12.11-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1226325e98e6d3cdfdaca639efdc3af8e82cd17287ae393626d1bd60626b0e93", size = 1702942, upload-time = "2025-06-07T15:52:14.373Z" }, + { url = "https://files.pythonhosted.org/packages/b1/2e/efcb6a35d0646ced659edc3172e8e9384246d2cd0b0f3080fc3c441cb511/aiohttp-3.12.11-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7a0ecae011f2f779271407f2959877230670de3c48f67e5db9fbafa9fddbfa3a", size = 1684207, upload-time = "2025-06-07T15:52:16.349Z" }, + { url = "https://files.pythonhosted.org/packages/56/f7/0324c499b7c610633d2f5e8af5457fd3a0584f5f4827bc46b673866596ac/aiohttp-3.12.11-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a8a711883eedcd55f2e1ba218d8224b9f20f1dfac90ffca28e78daf891667e3a", size = 1736275, upload-time = "2025-06-07T15:52:18.88Z" }, + { url = "https://files.pythonhosted.org/packages/98/0f/b7aa0fd1ed777b5d6fb62c0dcf82effb717e8b51c802067fc3bcb703e003/aiohttp-3.12.11-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2601c1fcd9b67e632548cfd3c760741b31490502f6f3e5e21287678c1c6fa1b2", size = 1785648, upload-time = "2025-06-07T15:52:20.902Z" }, + { url = "https://files.pythonhosted.org/packages/2c/2a/7defcf31010a2964bf17f6c9d9190e3be889f0c5edc3ff2cdac6e60764d7/aiohttp-3.12.11-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d5b11ea794ee54b33d0d817a1aec0ef0dd2026f070b493bc5a67b7e413b95d4", size = 1707981, upload-time = "2025-06-07T15:52:23.011Z" }, + { url = "https://files.pythonhosted.org/packages/b6/9e/ff3d9a01f533752e81fd92bfe1301ae5a7bd5a306d752ad54f8bc61570fa/aiohttp-3.12.11-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:109b3544138ce8a5aca598d5e7ff958699e3e19ee3675d27d5ee9c2e30765a4a", size = 1621683, upload-time = "2025-06-07T15:52:25.22Z" }, + { url = "https://files.pythonhosted.org/packages/2c/98/446c96927f2e7d2eaea95660a60eb6077771d00df834430cec002cadd96b/aiohttp-3.12.11-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b795085d063d24c6d09300c85ddd6b9c49816d5c498b40b6899ca24584e936e4", size = 1674706, upload-time = "2025-06-07T15:52:27.767Z" }, + { url = "https://files.pythonhosted.org/packages/e1/2a/038cb4af5e58994bc9315d0cb6a906d20ddfffb8eb3d0dfcfe8fe95b1939/aiohttp-3.12.11-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:ebcbc113f40e4c9c0f8d2b6b31a2dd2a9768f3fa5f623b7e1285684e24f5159f", size = 1706372, upload-time = "2025-06-07T15:52:30.399Z" }, + { url = "https://files.pythonhosted.org/packages/28/18/dc16cc7cb9b8baf9308f23ecf1e787d916238d01060bea272d5c29e9aa6b/aiohttp-3.12.11-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:590e5d792150d75fa34029d0555b126e65ad50d66818a996303de4af52b65b32", size = 1648967, upload-time = "2025-06-07T15:52:32.935Z" }, + { url = "https://files.pythonhosted.org/packages/44/f5/f427ef971e00088c7f0f5a4a7e405732e0ce0b87dfc3eec0f1a8c16863d2/aiohttp-3.12.11-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:9c2a4dec596437b02f0c34f92ea799d6e300184a0304c1e54e462af52abeb0a8", size = 1725099, upload-time = "2025-06-07T15:52:35.019Z" }, + { url = "https://files.pythonhosted.org/packages/d4/0a/34fc018d4e193115b512bc08f6afaf79c23609a6487e47f0d593d1d9df41/aiohttp-3.12.11-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:aace119abc495cc4ced8745e3faceb0c22e8202c60b55217405c5f389b569576", size = 1758571, upload-time = "2025-06-07T15:52:37.701Z" }, + { url = "https://files.pythonhosted.org/packages/b6/69/b466ec346506384a93bcb864ab75a21b6520c64fcc3720ab2056470a657f/aiohttp-3.12.11-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:cd749731390520a2dc1ce215bcf0ee1018c3e2e3cd834f966a02c0e71ad7d637", size = 1707461, upload-time = "2025-06-07T15:52:40.321Z" }, + { url = "https://files.pythonhosted.org/packages/f4/fc/3437d3e40581bc7d0816e134fdcae3c7e5c3f21dbdcfbd54402af3973b1c/aiohttp-3.12.11-cp313-cp313-win32.whl", hash = "sha256:65952736356d1fbc9efdd17492dce36e2501f609a14ccb298156e392d3ad8b83", size = 420053, upload-time = "2025-06-07T15:52:42.364Z" }, + { url = "https://files.pythonhosted.org/packages/6c/cf/cd84df67147c986315c63fef29a6ecadf03bf5528340b8c82eedd988cf57/aiohttp-3.12.11-cp313-cp313-win_amd64.whl", hash = "sha256:854132093e12dd77f5c07975581c42ae51a6a8868dcbbb509c77d1963c3713b7", size = 445988, upload-time = "2025-06-07T15:52:44.475Z" }, ] [[package]] @@ -105,14 +106,14 @@ wheels = [ [[package]] name = "authlib" -version = "1.5.2" +version = "1.6.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cryptography" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/2a/b3/5f5bc73c6558a21f951ffd267f41c6340d15f5fe0ff4b6bf37694f3558b8/authlib-1.5.2.tar.gz", hash = "sha256:fe85ec7e50c5f86f1e2603518bb3b4f632985eb4a355e52256530790e326c512", size = 153000, upload-time = "2025-04-02T10:31:36.488Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/9d/b1e08d36899c12c8b894a44a5583ee157789f26fc4b176f8e4b6217b56e1/authlib-1.6.0.tar.gz", hash = "sha256:4367d32031b7af175ad3a323d571dc7257b7099d55978087ceae4a0d88cd3210", size = 158371, upload-time = "2025-05-23T00:21:45.011Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e3/71/8dcec996ea8cc882cec9cace91ae1b630a226b88b0f04ab2ffa778f565ad/authlib-1.5.2-py2.py3-none-any.whl", hash = "sha256:8804dd4402ac5e4a0435ac49e0b6e19e395357cfa632a3f624dcb4f6df13b4b1", size = 232055, upload-time = "2025-04-02T10:31:34.59Z" }, + { url = "https://files.pythonhosted.org/packages/84/29/587c189bbab1ccc8c86a03a5d0e13873df916380ef1be461ebe6acebf48d/authlib-1.6.0-py2.py3-none-any.whl", hash = "sha256:91685589498f79e8655e8a8947431ad6288831d643f11c55c2143ffcc738048d", size = 239981, upload-time = "2025-05-23T00:21:43.075Z" }, ] [[package]] @@ -135,11 +136,11 @@ wheels = [ [[package]] name = "certifi" -version = "2025.1.31" +version = "2025.4.26" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1c/ab/c9f1e32b7b1bf505bf26f0ef697775960db7932abeb7b516de930ba2705f/certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651", size = 167577, upload-time = "2025-01-31T02:16:47.166Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e8/9e/c05b3920a3b7d20d3d3310465f50348e5b3694f4f88c6daf736eef3024c4/certifi-2025.4.26.tar.gz", hash = "sha256:0a816057ea3cdefcef70270d2c515e4506bbc954f417fa5ade2021213bb8f0c6", size = 160705, upload-time = "2025-04-26T02:12:29.51Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/38/fc/bce832fd4fd99766c04d1ee0eead6b0ec6486fb100ae5e74c1d91292b982/certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe", size = 166393, upload-time = "2025-01-31T02:16:45.015Z" }, + { url = "https://files.pythonhosted.org/packages/4a/7e/3db2bd1b1f9e95f7cddca6d6e75e2f2bd9f51b1246e546d88addca0106bd/certifi-2025.4.26-py3-none-any.whl", hash = "sha256:30350364dfe371162649852c63336a15c70c6510c2ad5015b21c2345311805f3", size = 159618, upload-time = "2025-04-26T02:12:27.662Z" }, ] [[package]] @@ -166,36 +167,45 @@ wheels = [ [[package]] name = "charset-normalizer" -version = "3.4.1" +version = "3.4.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/16/b0/572805e227f01586461c80e0fd25d65a2115599cc9dad142fee4b747c357/charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3", size = 123188, upload-time = "2024-12-24T18:12:35.43Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e4/33/89c2ced2b67d1c2a61c19c6751aa8902d46ce3dacb23600a283619f5a12d/charset_normalizer-3.4.2.tar.gz", hash = "sha256:5baececa9ecba31eff645232d59845c07aa030f0c81ee70184a90d35099a0e63", size = 126367, upload-time = "2025-05-02T08:34:42.01Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/38/94/ce8e6f63d18049672c76d07d119304e1e2d7c6098f0841b51c666e9f44a0/charset_normalizer-3.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda", size = 195698, upload-time = "2024-12-24T18:11:05.834Z" }, - { url = "https://files.pythonhosted.org/packages/24/2e/dfdd9770664aae179a96561cc6952ff08f9a8cd09a908f259a9dfa063568/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313", size = 140162, upload-time = "2024-12-24T18:11:07.064Z" }, - { url = "https://files.pythonhosted.org/packages/24/4e/f646b9093cff8fc86f2d60af2de4dc17c759de9d554f130b140ea4738ca6/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9", size = 150263, upload-time = "2024-12-24T18:11:08.374Z" }, - { url = "https://files.pythonhosted.org/packages/5e/67/2937f8d548c3ef6e2f9aab0f6e21001056f692d43282b165e7c56023e6dd/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b", size = 142966, upload-time = "2024-12-24T18:11:09.831Z" }, - { url = "https://files.pythonhosted.org/packages/52/ed/b7f4f07de100bdb95c1756d3a4d17b90c1a3c53715c1a476f8738058e0fa/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11", size = 144992, upload-time = "2024-12-24T18:11:12.03Z" }, - { url = "https://files.pythonhosted.org/packages/96/2c/d49710a6dbcd3776265f4c923bb73ebe83933dfbaa841c5da850fe0fd20b/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f", size = 147162, upload-time = "2024-12-24T18:11:13.372Z" }, - { url = "https://files.pythonhosted.org/packages/b4/41/35ff1f9a6bd380303dea55e44c4933b4cc3c4850988927d4082ada230273/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd", size = 140972, upload-time = "2024-12-24T18:11:14.628Z" }, - { url = "https://files.pythonhosted.org/packages/fb/43/c6a0b685fe6910d08ba971f62cd9c3e862a85770395ba5d9cad4fede33ab/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2", size = 149095, upload-time = "2024-12-24T18:11:17.672Z" }, - { url = "https://files.pythonhosted.org/packages/4c/ff/a9a504662452e2d2878512115638966e75633519ec11f25fca3d2049a94a/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886", size = 152668, upload-time = "2024-12-24T18:11:18.989Z" }, - { url = "https://files.pythonhosted.org/packages/6c/71/189996b6d9a4b932564701628af5cee6716733e9165af1d5e1b285c530ed/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601", size = 150073, upload-time = "2024-12-24T18:11:21.507Z" }, - { url = "https://files.pythonhosted.org/packages/e4/93/946a86ce20790e11312c87c75ba68d5f6ad2208cfb52b2d6a2c32840d922/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd", size = 145732, upload-time = "2024-12-24T18:11:22.774Z" }, - { url = "https://files.pythonhosted.org/packages/cd/e5/131d2fb1b0dddafc37be4f3a2fa79aa4c037368be9423061dccadfd90091/charset_normalizer-3.4.1-cp313-cp313-win32.whl", hash = "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407", size = 95391, upload-time = "2024-12-24T18:11:24.139Z" }, - { url = "https://files.pythonhosted.org/packages/27/f2/4f9a69cc7712b9b5ad8fdb87039fd89abba997ad5cbe690d1835d40405b0/charset_normalizer-3.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971", size = 102702, upload-time = "2024-12-24T18:11:26.535Z" }, - { url = "https://files.pythonhosted.org/packages/0e/f6/65ecc6878a89bb1c23a086ea335ad4bf21a588990c3f535a227b9eea9108/charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85", size = 49767, upload-time = "2024-12-24T18:12:32.852Z" }, + { url = "https://files.pythonhosted.org/packages/ea/12/a93df3366ed32db1d907d7593a94f1fe6293903e3e92967bebd6950ed12c/charset_normalizer-3.4.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:926ca93accd5d36ccdabd803392ddc3e03e6d4cd1cf17deff3b989ab8e9dbcf0", size = 199622, upload-time = "2025-05-02T08:32:56.363Z" }, + { url = "https://files.pythonhosted.org/packages/04/93/bf204e6f344c39d9937d3c13c8cd5bbfc266472e51fc8c07cb7f64fcd2de/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eba9904b0f38a143592d9fc0e19e2df0fa2e41c3c3745554761c5f6447eedabf", size = 143435, upload-time = "2025-05-02T08:32:58.551Z" }, + { url = "https://files.pythonhosted.org/packages/22/2a/ea8a2095b0bafa6c5b5a55ffdc2f924455233ee7b91c69b7edfcc9e02284/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3fddb7e2c84ac87ac3a947cb4e66d143ca5863ef48e4a5ecb83bd48619e4634e", size = 153653, upload-time = "2025-05-02T08:33:00.342Z" }, + { url = "https://files.pythonhosted.org/packages/b6/57/1b090ff183d13cef485dfbe272e2fe57622a76694061353c59da52c9a659/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98f862da73774290f251b9df8d11161b6cf25b599a66baf087c1ffe340e9bfd1", size = 146231, upload-time = "2025-05-02T08:33:02.081Z" }, + { url = "https://files.pythonhosted.org/packages/e2/28/ffc026b26f441fc67bd21ab7f03b313ab3fe46714a14b516f931abe1a2d8/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c9379d65defcab82d07b2a9dfbfc2e95bc8fe0ebb1b176a3190230a3ef0e07c", size = 148243, upload-time = "2025-05-02T08:33:04.063Z" }, + { url = "https://files.pythonhosted.org/packages/c0/0f/9abe9bd191629c33e69e47c6ef45ef99773320e9ad8e9cb08b8ab4a8d4cb/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e635b87f01ebc977342e2697d05b56632f5f879a4f15955dfe8cef2448b51691", size = 150442, upload-time = "2025-05-02T08:33:06.418Z" }, + { url = "https://files.pythonhosted.org/packages/67/7c/a123bbcedca91d5916c056407f89a7f5e8fdfce12ba825d7d6b9954a1a3c/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1c95a1e2902a8b722868587c0e1184ad5c55631de5afc0eb96bc4b0d738092c0", size = 145147, upload-time = "2025-05-02T08:33:08.183Z" }, + { url = "https://files.pythonhosted.org/packages/ec/fe/1ac556fa4899d967b83e9893788e86b6af4d83e4726511eaaad035e36595/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ef8de666d6179b009dce7bcb2ad4c4a779f113f12caf8dc77f0162c29d20490b", size = 153057, upload-time = "2025-05-02T08:33:09.986Z" }, + { url = "https://files.pythonhosted.org/packages/2b/ff/acfc0b0a70b19e3e54febdd5301a98b72fa07635e56f24f60502e954c461/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:32fc0341d72e0f73f80acb0a2c94216bd704f4f0bce10aedea38f30502b271ff", size = 156454, upload-time = "2025-05-02T08:33:11.814Z" }, + { url = "https://files.pythonhosted.org/packages/92/08/95b458ce9c740d0645feb0e96cea1f5ec946ea9c580a94adfe0b617f3573/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:289200a18fa698949d2b39c671c2cc7a24d44096784e76614899a7ccf2574b7b", size = 154174, upload-time = "2025-05-02T08:33:13.707Z" }, + { url = "https://files.pythonhosted.org/packages/78/be/8392efc43487ac051eee6c36d5fbd63032d78f7728cb37aebcc98191f1ff/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4a476b06fbcf359ad25d34a057b7219281286ae2477cc5ff5e3f70a246971148", size = 149166, upload-time = "2025-05-02T08:33:15.458Z" }, + { url = "https://files.pythonhosted.org/packages/44/96/392abd49b094d30b91d9fbda6a69519e95802250b777841cf3bda8fe136c/charset_normalizer-3.4.2-cp313-cp313-win32.whl", hash = "sha256:aaeeb6a479c7667fbe1099af9617c83aaca22182d6cf8c53966491a0f1b7ffb7", size = 98064, upload-time = "2025-05-02T08:33:17.06Z" }, + { url = "https://files.pythonhosted.org/packages/e9/b0/0200da600134e001d91851ddc797809e2fe0ea72de90e09bec5a2fbdaccb/charset_normalizer-3.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:aa6af9e7d59f9c12b33ae4e9450619cf2488e2bbe9b44030905877f0b2324980", size = 105641, upload-time = "2025-05-02T08:33:18.753Z" }, + { url = "https://files.pythonhosted.org/packages/20/94/c5790835a017658cbfabd07f3bfb549140c3ac458cfc196323996b10095a/charset_normalizer-3.4.2-py3-none-any.whl", hash = "sha256:7f56930ab0abd1c45cd15be65cc741c28b1c9a34876ce8c17a2fa107810c0af0", size = 52626, upload-time = "2025-05-02T08:34:40.053Z" }, ] [[package]] name = "click" -version = "8.1.8" +version = "8.2.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593, upload-time = "2024-12-21T18:38:44.339Z" } +sdist = { url = "https://files.pythonhosted.org/packages/60/6c/8ca2efa64cf75a977a0d7fac081354553ebe483345c734fb6b6515d96bbc/click-8.2.1.tar.gz", hash = "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202", size = 286342, upload-time = "2025-05-20T23:19:49.832Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/85/32/10bb5764d90a8eee674e9dc6f4db6a0ab47c8c4d0d83c27f7c39ac415a4d/click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b", size = 102215, upload-time = "2025-05-20T23:19:47.796Z" }, +] + +[[package]] +name = "cloudpickle" +version = "3.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/52/39/069100b84d7418bc358d81669d5748efb14b9cceacd2f9c75f550424132f/cloudpickle-3.1.1.tar.gz", hash = "sha256:b216fa8ae4019d5482a8ac3c95d8f6346115d8835911fd4aefd1a445e4242c64", size = 22113, upload-time = "2025-01-14T17:02:05.085Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188, upload-time = "2024-12-21T18:38:41.666Z" }, + { url = "https://files.pythonhosted.org/packages/7e/e8/64c37fadfc2816a7701fa8a6ed8d87327c7d54eacfbfb6edab14a2f2be75/cloudpickle-3.1.1-py3-none-any.whl", hash = "sha256:c8c5a44295039331ee9dad40ba100a9c7297b6f988e50e87ccdf3765a668350e", size = 20992, upload-time = "2025-01-14T17:02:02.417Z" }, ] [[package]] @@ -209,37 +219,37 @@ wheels = [ [[package]] name = "cryptography" -version = "44.0.2" +version = "45.0.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/cd/25/4ce80c78963834b8a9fd1cc1266be5ed8d1840785c0f2e1b73b8d128d505/cryptography-44.0.2.tar.gz", hash = "sha256:c63454aa261a0cf0c5b4718349629793e9e634993538db841165b3df74f37ec0", size = 710807, upload-time = "2025-03-02T00:01:37.692Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/92/ef/83e632cfa801b221570c5f58c0369db6fa6cef7d9ff859feab1aae1a8a0f/cryptography-44.0.2-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:efcfe97d1b3c79e486554efddeb8f6f53a4cdd4cf6086642784fa31fc384e1d7", size = 6676361, upload-time = "2025-03-02T00:00:06.528Z" }, - { url = "https://files.pythonhosted.org/packages/30/ec/7ea7c1e4c8fc8329506b46c6c4a52e2f20318425d48e0fe597977c71dbce/cryptography-44.0.2-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29ecec49f3ba3f3849362854b7253a9f59799e3763b0c9d0826259a88efa02f1", size = 3952350, upload-time = "2025-03-02T00:00:09.537Z" }, - { url = "https://files.pythonhosted.org/packages/27/61/72e3afdb3c5ac510330feba4fc1faa0fe62e070592d6ad00c40bb69165e5/cryptography-44.0.2-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc821e161ae88bfe8088d11bb39caf2916562e0a2dc7b6d56714a48b784ef0bb", size = 4166572, upload-time = "2025-03-02T00:00:12.03Z" }, - { url = "https://files.pythonhosted.org/packages/26/e4/ba680f0b35ed4a07d87f9e98f3ebccb05091f3bf6b5a478b943253b3bbd5/cryptography-44.0.2-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:3c00b6b757b32ce0f62c574b78b939afab9eecaf597c4d624caca4f9e71e7843", size = 3958124, upload-time = "2025-03-02T00:00:14.518Z" }, - { url = "https://files.pythonhosted.org/packages/9c/e8/44ae3e68c8b6d1cbc59040288056df2ad7f7f03bbcaca6b503c737ab8e73/cryptography-44.0.2-cp37-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7bdcd82189759aba3816d1f729ce42ffded1ac304c151d0a8e89b9996ab863d5", size = 3678122, upload-time = "2025-03-02T00:00:17.212Z" }, - { url = "https://files.pythonhosted.org/packages/27/7b/664ea5e0d1eab511a10e480baf1c5d3e681c7d91718f60e149cec09edf01/cryptography-44.0.2-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:4973da6ca3db4405c54cd0b26d328be54c7747e89e284fcff166132eb7bccc9c", size = 4191831, upload-time = "2025-03-02T00:00:19.696Z" }, - { url = "https://files.pythonhosted.org/packages/2a/07/79554a9c40eb11345e1861f46f845fa71c9e25bf66d132e123d9feb8e7f9/cryptography-44.0.2-cp37-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:4e389622b6927d8133f314949a9812972711a111d577a5d1f4bee5e58736b80a", size = 3960583, upload-time = "2025-03-02T00:00:22.488Z" }, - { url = "https://files.pythonhosted.org/packages/bb/6d/858e356a49a4f0b591bd6789d821427de18432212e137290b6d8a817e9bf/cryptography-44.0.2-cp37-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:f514ef4cd14bb6fb484b4a60203e912cfcb64f2ab139e88c2274511514bf7308", size = 4191753, upload-time = "2025-03-02T00:00:25.038Z" }, - { url = "https://files.pythonhosted.org/packages/b2/80/62df41ba4916067fa6b125aa8c14d7e9181773f0d5d0bd4dcef580d8b7c6/cryptography-44.0.2-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:1bc312dfb7a6e5d66082c87c34c8a62176e684b6fe3d90fcfe1568de675e6688", size = 4079550, upload-time = "2025-03-02T00:00:26.929Z" }, - { url = "https://files.pythonhosted.org/packages/f3/cd/2558cc08f7b1bb40683f99ff4327f8dcfc7de3affc669e9065e14824511b/cryptography-44.0.2-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:3b721b8b4d948b218c88cb8c45a01793483821e709afe5f622861fc6182b20a7", size = 4298367, upload-time = "2025-03-02T00:00:28.735Z" }, - { url = "https://files.pythonhosted.org/packages/71/59/94ccc74788945bc3bd4cf355d19867e8057ff5fdbcac781b1ff95b700fb1/cryptography-44.0.2-cp37-abi3-win32.whl", hash = "sha256:51e4de3af4ec3899d6d178a8c005226491c27c4ba84101bfb59c901e10ca9f79", size = 2772843, upload-time = "2025-03-02T00:00:30.592Z" }, - { url = "https://files.pythonhosted.org/packages/ca/2c/0d0bbaf61ba05acb32f0841853cfa33ebb7a9ab3d9ed8bb004bd39f2da6a/cryptography-44.0.2-cp37-abi3-win_amd64.whl", hash = "sha256:c505d61b6176aaf982c5717ce04e87da5abc9a36a5b39ac03905c4aafe8de7aa", size = 3209057, upload-time = "2025-03-02T00:00:33.393Z" }, - { url = "https://files.pythonhosted.org/packages/9e/be/7a26142e6d0f7683d8a382dd963745e65db895a79a280a30525ec92be890/cryptography-44.0.2-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:8e0ddd63e6bf1161800592c71ac794d3fb8001f2caebe0966e77c5234fa9efc3", size = 6677789, upload-time = "2025-03-02T00:00:36.009Z" }, - { url = "https://files.pythonhosted.org/packages/06/88/638865be7198a84a7713950b1db7343391c6066a20e614f8fa286eb178ed/cryptography-44.0.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:81276f0ea79a208d961c433a947029e1a15948966658cf6710bbabb60fcc2639", size = 3951919, upload-time = "2025-03-02T00:00:38.581Z" }, - { url = "https://files.pythonhosted.org/packages/d7/fc/99fe639bcdf58561dfad1faa8a7369d1dc13f20acd78371bb97a01613585/cryptography-44.0.2-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a1e657c0f4ea2a23304ee3f964db058c9e9e635cc7019c4aa21c330755ef6fd", size = 4167812, upload-time = "2025-03-02T00:00:42.934Z" }, - { url = "https://files.pythonhosted.org/packages/53/7b/aafe60210ec93d5d7f552592a28192e51d3c6b6be449e7fd0a91399b5d07/cryptography-44.0.2-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:6210c05941994290f3f7f175a4a57dbbb2afd9273657614c506d5976db061181", size = 3958571, upload-time = "2025-03-02T00:00:46.026Z" }, - { url = "https://files.pythonhosted.org/packages/16/32/051f7ce79ad5a6ef5e26a92b37f172ee2d6e1cce09931646eef8de1e9827/cryptography-44.0.2-cp39-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d1c3572526997b36f245a96a2b1713bf79ce99b271bbcf084beb6b9b075f29ea", size = 3679832, upload-time = "2025-03-02T00:00:48.647Z" }, - { url = "https://files.pythonhosted.org/packages/78/2b/999b2a1e1ba2206f2d3bca267d68f350beb2b048a41ea827e08ce7260098/cryptography-44.0.2-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:b042d2a275c8cee83a4b7ae30c45a15e6a4baa65a179a0ec2d78ebb90e4f6699", size = 4193719, upload-time = "2025-03-02T00:00:51.397Z" }, - { url = "https://files.pythonhosted.org/packages/72/97/430e56e39a1356e8e8f10f723211a0e256e11895ef1a135f30d7d40f2540/cryptography-44.0.2-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:d03806036b4f89e3b13b6218fefea8d5312e450935b1a2d55f0524e2ed7c59d9", size = 3960852, upload-time = "2025-03-02T00:00:53.317Z" }, - { url = "https://files.pythonhosted.org/packages/89/33/c1cf182c152e1d262cac56850939530c05ca6c8d149aa0dcee490b417e99/cryptography-44.0.2-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:c7362add18b416b69d58c910caa217f980c5ef39b23a38a0880dfd87bdf8cd23", size = 4193906, upload-time = "2025-03-02T00:00:56.49Z" }, - { url = "https://files.pythonhosted.org/packages/e1/99/87cf26d4f125380dc674233971069bc28d19b07f7755b29861570e513650/cryptography-44.0.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:8cadc6e3b5a1f144a039ea08a0bdb03a2a92e19c46be3285123d32029f40a922", size = 4081572, upload-time = "2025-03-02T00:00:59.995Z" }, - { url = "https://files.pythonhosted.org/packages/b3/9f/6a3e0391957cc0c5f84aef9fbdd763035f2b52e998a53f99345e3ac69312/cryptography-44.0.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:6f101b1f780f7fc613d040ca4bdf835c6ef3b00e9bd7125a4255ec574c7916e4", size = 4298631, upload-time = "2025-03-02T00:01:01.623Z" }, - { url = "https://files.pythonhosted.org/packages/e2/a5/5bc097adb4b6d22a24dea53c51f37e480aaec3465285c253098642696423/cryptography-44.0.2-cp39-abi3-win32.whl", hash = "sha256:3dc62975e31617badc19a906481deacdeb80b4bb454394b4098e3f2525a488c5", size = 2773792, upload-time = "2025-03-02T00:01:04.133Z" }, - { url = "https://files.pythonhosted.org/packages/33/cf/1f7649b8b9a3543e042d3f348e398a061923ac05b507f3f4d95f11938aa9/cryptography-44.0.2-cp39-abi3-win_amd64.whl", hash = "sha256:5f6f90b72d8ccadb9c6e311c775c8305381db88374c65fa1a68250aa8a9cb3a6", size = 3210957, upload-time = "2025-03-02T00:01:06.987Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/13/1f/9fa001e74a1993a9cadd2333bb889e50c66327b8594ac538ab8a04f915b7/cryptography-45.0.3.tar.gz", hash = "sha256:ec21313dd335c51d7877baf2972569f40a4291b76a0ce51391523ae358d05899", size = 744738, upload-time = "2025-05-25T14:17:24.777Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/82/b2/2345dc595998caa6f68adf84e8f8b50d18e9fc4638d32b22ea8daedd4b7a/cryptography-45.0.3-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:7573d9eebaeceeb55285205dbbb8753ac1e962af3d9640791d12b36864065e71", size = 7056239, upload-time = "2025-05-25T14:16:12.22Z" }, + { url = "https://files.pythonhosted.org/packages/71/3d/ac361649a0bfffc105e2298b720d8b862330a767dab27c06adc2ddbef96a/cryptography-45.0.3-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d377dde61c5d67eb4311eace661c3efda46c62113ff56bf05e2d679e02aebb5b", size = 4205541, upload-time = "2025-05-25T14:16:14.333Z" }, + { url = "https://files.pythonhosted.org/packages/70/3e/c02a043750494d5c445f769e9c9f67e550d65060e0bfce52d91c1362693d/cryptography-45.0.3-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fae1e637f527750811588e4582988932c222f8251f7b7ea93739acb624e1487f", size = 4433275, upload-time = "2025-05-25T14:16:16.421Z" }, + { url = "https://files.pythonhosted.org/packages/40/7a/9af0bfd48784e80eef3eb6fd6fde96fe706b4fc156751ce1b2b965dada70/cryptography-45.0.3-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:ca932e11218bcc9ef812aa497cdf669484870ecbcf2d99b765d6c27a86000942", size = 4209173, upload-time = "2025-05-25T14:16:18.163Z" }, + { url = "https://files.pythonhosted.org/packages/31/5f/d6f8753c8708912df52e67969e80ef70b8e8897306cd9eb8b98201f8c184/cryptography-45.0.3-cp311-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:af3f92b1dc25621f5fad065288a44ac790c5798e986a34d393ab27d2b27fcff9", size = 3898150, upload-time = "2025-05-25T14:16:20.34Z" }, + { url = "https://files.pythonhosted.org/packages/8b/50/f256ab79c671fb066e47336706dc398c3b1e125f952e07d54ce82cf4011a/cryptography-45.0.3-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:2f8f8f0b73b885ddd7f3d8c2b2234a7d3ba49002b0223f58cfde1bedd9563c56", size = 4466473, upload-time = "2025-05-25T14:16:22.605Z" }, + { url = "https://files.pythonhosted.org/packages/62/e7/312428336bb2df0848d0768ab5a062e11a32d18139447a76dfc19ada8eed/cryptography-45.0.3-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:9cc80ce69032ffa528b5e16d217fa4d8d4bb7d6ba8659c1b4d74a1b0f4235fca", size = 4211890, upload-time = "2025-05-25T14:16:24.738Z" }, + { url = "https://files.pythonhosted.org/packages/e7/53/8a130e22c1e432b3c14896ec5eb7ac01fb53c6737e1d705df7e0efb647c6/cryptography-45.0.3-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:c824c9281cb628015bfc3c59335163d4ca0540d49de4582d6c2637312907e4b1", size = 4466300, upload-time = "2025-05-25T14:16:26.768Z" }, + { url = "https://files.pythonhosted.org/packages/ba/75/6bb6579688ef805fd16a053005fce93944cdade465fc92ef32bbc5c40681/cryptography-45.0.3-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:5833bb4355cb377ebd880457663a972cd044e7f49585aee39245c0d592904578", size = 4332483, upload-time = "2025-05-25T14:16:28.316Z" }, + { url = "https://files.pythonhosted.org/packages/2f/11/2538f4e1ce05c6c4f81f43c1ef2bd6de7ae5e24ee284460ff6c77e42ca77/cryptography-45.0.3-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:9bb5bf55dcb69f7067d80354d0a348368da907345a2c448b0babc4215ccd3497", size = 4573714, upload-time = "2025-05-25T14:16:30.474Z" }, + { url = "https://files.pythonhosted.org/packages/f5/bb/e86e9cf07f73a98d84a4084e8fd420b0e82330a901d9cac8149f994c3417/cryptography-45.0.3-cp311-abi3-win32.whl", hash = "sha256:3ad69eeb92a9de9421e1f6685e85a10fbcfb75c833b42cc9bc2ba9fb00da4710", size = 2934752, upload-time = "2025-05-25T14:16:32.204Z" }, + { url = "https://files.pythonhosted.org/packages/c7/75/063bc9ddc3d1c73e959054f1fc091b79572e716ef74d6caaa56e945b4af9/cryptography-45.0.3-cp311-abi3-win_amd64.whl", hash = "sha256:97787952246a77d77934d41b62fb1b6f3581d83f71b44796a4158d93b8f5c490", size = 3412465, upload-time = "2025-05-25T14:16:33.888Z" }, + { url = "https://files.pythonhosted.org/packages/71/9b/04ead6015229a9396890d7654ee35ef630860fb42dc9ff9ec27f72157952/cryptography-45.0.3-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:c92519d242703b675ccefd0f0562eb45e74d438e001f8ab52d628e885751fb06", size = 7031892, upload-time = "2025-05-25T14:16:36.214Z" }, + { url = "https://files.pythonhosted.org/packages/46/c7/c7d05d0e133a09fc677b8a87953815c522697bdf025e5cac13ba419e7240/cryptography-45.0.3-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5edcb90da1843df85292ef3a313513766a78fbbb83f584a5a58fb001a5a9d57", size = 4196181, upload-time = "2025-05-25T14:16:37.934Z" }, + { url = "https://files.pythonhosted.org/packages/08/7a/6ad3aa796b18a683657cef930a986fac0045417e2dc428fd336cfc45ba52/cryptography-45.0.3-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:38deed72285c7ed699864f964a3f4cf11ab3fb38e8d39cfcd96710cd2b5bb716", size = 4423370, upload-time = "2025-05-25T14:16:39.502Z" }, + { url = "https://files.pythonhosted.org/packages/4f/58/ec1461bfcb393525f597ac6a10a63938d18775b7803324072974b41a926b/cryptography-45.0.3-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:5555365a50efe1f486eed6ac7062c33b97ccef409f5970a0b6f205a7cfab59c8", size = 4197839, upload-time = "2025-05-25T14:16:41.322Z" }, + { url = "https://files.pythonhosted.org/packages/d4/3d/5185b117c32ad4f40846f579369a80e710d6146c2baa8ce09d01612750db/cryptography-45.0.3-cp37-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:9e4253ed8f5948a3589b3caee7ad9a5bf218ffd16869c516535325fece163dcc", size = 3886324, upload-time = "2025-05-25T14:16:43.041Z" }, + { url = "https://files.pythonhosted.org/packages/67/85/caba91a57d291a2ad46e74016d1f83ac294f08128b26e2a81e9b4f2d2555/cryptography-45.0.3-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:cfd84777b4b6684955ce86156cfb5e08d75e80dc2585e10d69e47f014f0a5342", size = 4450447, upload-time = "2025-05-25T14:16:44.759Z" }, + { url = "https://files.pythonhosted.org/packages/ae/d1/164e3c9d559133a38279215c712b8ba38e77735d3412f37711b9f8f6f7e0/cryptography-45.0.3-cp37-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:a2b56de3417fd5f48773ad8e91abaa700b678dc7fe1e0c757e1ae340779acf7b", size = 4200576, upload-time = "2025-05-25T14:16:46.438Z" }, + { url = "https://files.pythonhosted.org/packages/71/7a/e002d5ce624ed46dfc32abe1deff32190f3ac47ede911789ee936f5a4255/cryptography-45.0.3-cp37-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:57a6500d459e8035e813bd8b51b671977fb149a8c95ed814989da682314d0782", size = 4450308, upload-time = "2025-05-25T14:16:48.228Z" }, + { url = "https://files.pythonhosted.org/packages/87/ad/3fbff9c28cf09b0a71e98af57d74f3662dea4a174b12acc493de00ea3f28/cryptography-45.0.3-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:f22af3c78abfbc7cbcdf2c55d23c3e022e1a462ee2481011d518c7fb9c9f3d65", size = 4325125, upload-time = "2025-05-25T14:16:49.844Z" }, + { url = "https://files.pythonhosted.org/packages/f5/b4/51417d0cc01802304c1984d76e9592f15e4801abd44ef7ba657060520bf0/cryptography-45.0.3-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:232954730c362638544758a8160c4ee1b832dc011d2c41a306ad8f7cccc5bb0b", size = 4560038, upload-time = "2025-05-25T14:16:51.398Z" }, + { url = "https://files.pythonhosted.org/packages/80/38/d572f6482d45789a7202fb87d052deb7a7b136bf17473ebff33536727a2c/cryptography-45.0.3-cp37-abi3-win32.whl", hash = "sha256:cb6ab89421bc90e0422aca911c69044c2912fc3debb19bb3c1bfe28ee3dff6ab", size = 2924070, upload-time = "2025-05-25T14:16:53.472Z" }, + { url = "https://files.pythonhosted.org/packages/91/5a/61f39c0ff4443651cc64e626fa97ad3099249152039952be8f344d6b0c86/cryptography-45.0.3-cp37-abi3-win_amd64.whl", hash = "sha256:d54ae41e6bd70ea23707843021c778f151ca258081586f0cfa31d936ae43d1b2", size = 3395005, upload-time = "2025-05-25T14:16:55.134Z" }, ] [[package]] @@ -255,18 +265,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c3/be/d0d44e092656fe7a06b55e6103cbce807cdbdee17884a5367c68c9860853/dataclasses_json-0.6.7-py3-none-any.whl", hash = "sha256:0dbf33f26c8d5305befd61b39d2b3414e8a407bedc2834dea9b8d642666fb40a", size = 28686, upload-time = "2024-06-09T16:20:16.715Z" }, ] -[[package]] -name = "deprecated" -version = "1.2.18" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "wrapt" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/98/97/06afe62762c9a8a86af0cfb7bfdab22a43ad17138b07af5b1a58442690a2/deprecated-1.2.18.tar.gz", hash = "sha256:422b6f6d859da6f2ef57857761bfb392480502a64c3028ca9bbe86085d72115d", size = 2928744, upload-time = "2025-01-27T10:46:25.7Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6e/c6/ac0b6c1e2d138f1002bcf799d330bd6d85084fece321e662a14223794041/Deprecated-1.2.18-py2.py3-none-any.whl", hash = "sha256:bd5011788200372a32418f888e326a09ff80d0214bd961147cfed01b5c018eec", size = 9998, upload-time = "2025-01-27T10:46:09.186Z" }, -] - [[package]] name = "distro" version = "1.9.0" @@ -328,45 +326,45 @@ wheels = [ [[package]] name = "frozenlist" -version = "1.6.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ee/f4/d744cba2da59b5c1d88823cf9e8a6c74e4659e2b27604ed973be2a0bf5ab/frozenlist-1.6.0.tar.gz", hash = "sha256:b99655c32c1c8e06d111e7f41c06c29a5318cb1835df23a45518e02a47c63b68", size = 42831, upload-time = "2025-04-17T22:38:53.099Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6f/e5/04c7090c514d96ca00887932417f04343ab94904a56ab7f57861bf63652d/frozenlist-1.6.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:1d7fb014fe0fbfee3efd6a94fc635aeaa68e5e1720fe9e57357f2e2c6e1a647e", size = 158182, upload-time = "2025-04-17T22:37:16.837Z" }, - { url = "https://files.pythonhosted.org/packages/e9/8f/60d0555c61eec855783a6356268314d204137f5e0c53b59ae2fc28938c99/frozenlist-1.6.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:01bcaa305a0fdad12745502bfd16a1c75b14558dabae226852f9159364573117", size = 122838, upload-time = "2025-04-17T22:37:18.352Z" }, - { url = "https://files.pythonhosted.org/packages/5a/a7/d0ec890e3665b4b3b7c05dc80e477ed8dc2e2e77719368e78e2cd9fec9c8/frozenlist-1.6.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8b314faa3051a6d45da196a2c495e922f987dc848e967d8cfeaee8a0328b1cd4", size = 120980, upload-time = "2025-04-17T22:37:19.857Z" }, - { url = "https://files.pythonhosted.org/packages/cc/19/9b355a5e7a8eba903a008579964192c3e427444752f20b2144b10bb336df/frozenlist-1.6.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da62fecac21a3ee10463d153549d8db87549a5e77eefb8c91ac84bb42bb1e4e3", size = 305463, upload-time = "2025-04-17T22:37:21.328Z" }, - { url = "https://files.pythonhosted.org/packages/9c/8d/5b4c758c2550131d66935ef2fa700ada2461c08866aef4229ae1554b93ca/frozenlist-1.6.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d1eb89bf3454e2132e046f9599fbcf0a4483ed43b40f545551a39316d0201cd1", size = 297985, upload-time = "2025-04-17T22:37:23.55Z" }, - { url = "https://files.pythonhosted.org/packages/48/2c/537ec09e032b5865715726b2d1d9813e6589b571d34d01550c7aeaad7e53/frozenlist-1.6.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d18689b40cb3936acd971f663ccb8e2589c45db5e2c5f07e0ec6207664029a9c", size = 311188, upload-time = "2025-04-17T22:37:25.221Z" }, - { url = "https://files.pythonhosted.org/packages/31/2f/1aa74b33f74d54817055de9a4961eff798f066cdc6f67591905d4fc82a84/frozenlist-1.6.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e67ddb0749ed066b1a03fba812e2dcae791dd50e5da03be50b6a14d0c1a9ee45", size = 311874, upload-time = "2025-04-17T22:37:26.791Z" }, - { url = "https://files.pythonhosted.org/packages/bf/f0/cfec18838f13ebf4b37cfebc8649db5ea71a1b25dacd691444a10729776c/frozenlist-1.6.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fc5e64626e6682638d6e44398c9baf1d6ce6bc236d40b4b57255c9d3f9761f1f", size = 291897, upload-time = "2025-04-17T22:37:28.958Z" }, - { url = "https://files.pythonhosted.org/packages/ea/a5/deb39325cbbea6cd0a46db8ccd76150ae2fcbe60d63243d9df4a0b8c3205/frozenlist-1.6.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:437cfd39564744ae32ad5929e55b18ebd88817f9180e4cc05e7d53b75f79ce85", size = 305799, upload-time = "2025-04-17T22:37:30.889Z" }, - { url = "https://files.pythonhosted.org/packages/78/22/6ddec55c5243a59f605e4280f10cee8c95a449f81e40117163383829c241/frozenlist-1.6.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:62dd7df78e74d924952e2feb7357d826af8d2f307557a779d14ddf94d7311be8", size = 302804, upload-time = "2025-04-17T22:37:32.489Z" }, - { url = "https://files.pythonhosted.org/packages/5d/b7/d9ca9bab87f28855063c4d202936800219e39db9e46f9fb004d521152623/frozenlist-1.6.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:a66781d7e4cddcbbcfd64de3d41a61d6bdde370fc2e38623f30b2bd539e84a9f", size = 316404, upload-time = "2025-04-17T22:37:34.59Z" }, - { url = "https://files.pythonhosted.org/packages/a6/3a/1255305db7874d0b9eddb4fe4a27469e1fb63720f1fc6d325a5118492d18/frozenlist-1.6.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:482fe06e9a3fffbcd41950f9d890034b4a54395c60b5e61fae875d37a699813f", size = 295572, upload-time = "2025-04-17T22:37:36.337Z" }, - { url = "https://files.pythonhosted.org/packages/2a/f2/8d38eeee39a0e3a91b75867cc102159ecccf441deb6ddf67be96d3410b84/frozenlist-1.6.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:e4f9373c500dfc02feea39f7a56e4f543e670212102cc2eeb51d3a99c7ffbde6", size = 307601, upload-time = "2025-04-17T22:37:37.923Z" }, - { url = "https://files.pythonhosted.org/packages/38/04/80ec8e6b92f61ef085422d7b196822820404f940950dde5b2e367bede8bc/frozenlist-1.6.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:e69bb81de06827147b7bfbaeb284d85219fa92d9f097e32cc73675f279d70188", size = 314232, upload-time = "2025-04-17T22:37:39.669Z" }, - { url = "https://files.pythonhosted.org/packages/3a/58/93b41fb23e75f38f453ae92a2f987274c64637c450285577bd81c599b715/frozenlist-1.6.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7613d9977d2ab4a9141dde4a149f4357e4065949674c5649f920fec86ecb393e", size = 308187, upload-time = "2025-04-17T22:37:41.662Z" }, - { url = "https://files.pythonhosted.org/packages/6a/a2/e64df5c5aa36ab3dee5a40d254f3e471bb0603c225f81664267281c46a2d/frozenlist-1.6.0-cp313-cp313-win32.whl", hash = "sha256:4def87ef6d90429f777c9d9de3961679abf938cb6b7b63d4a7eb8a268babfce4", size = 114772, upload-time = "2025-04-17T22:37:43.132Z" }, - { url = "https://files.pythonhosted.org/packages/a0/77/fead27441e749b2d574bb73d693530d59d520d4b9e9679b8e3cb779d37f2/frozenlist-1.6.0-cp313-cp313-win_amd64.whl", hash = "sha256:37a8a52c3dfff01515e9bbbee0e6063181362f9de3db2ccf9bc96189b557cbfd", size = 119847, upload-time = "2025-04-17T22:37:45.118Z" }, - { url = "https://files.pythonhosted.org/packages/df/bd/cc6d934991c1e5d9cafda83dfdc52f987c7b28343686aef2e58a9cf89f20/frozenlist-1.6.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:46138f5a0773d064ff663d273b309b696293d7a7c00a0994c5c13a5078134b64", size = 174937, upload-time = "2025-04-17T22:37:46.635Z" }, - { url = "https://files.pythonhosted.org/packages/f2/a2/daf945f335abdbfdd5993e9dc348ef4507436936ab3c26d7cfe72f4843bf/frozenlist-1.6.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:f88bc0a2b9c2a835cb888b32246c27cdab5740059fb3688852bf91e915399b91", size = 136029, upload-time = "2025-04-17T22:37:48.192Z" }, - { url = "https://files.pythonhosted.org/packages/51/65/4c3145f237a31247c3429e1c94c384d053f69b52110a0d04bfc8afc55fb2/frozenlist-1.6.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:777704c1d7655b802c7850255639672e90e81ad6fa42b99ce5ed3fbf45e338dd", size = 134831, upload-time = "2025-04-17T22:37:50.485Z" }, - { url = "https://files.pythonhosted.org/packages/77/38/03d316507d8dea84dfb99bdd515ea245628af964b2bf57759e3c9205cc5e/frozenlist-1.6.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85ef8d41764c7de0dcdaf64f733a27352248493a85a80661f3c678acd27e31f2", size = 392981, upload-time = "2025-04-17T22:37:52.558Z" }, - { url = "https://files.pythonhosted.org/packages/37/02/46285ef9828f318ba400a51d5bb616ded38db8466836a9cfa39f3903260b/frozenlist-1.6.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:da5cb36623f2b846fb25009d9d9215322318ff1c63403075f812b3b2876c8506", size = 371999, upload-time = "2025-04-17T22:37:54.092Z" }, - { url = "https://files.pythonhosted.org/packages/0d/64/1212fea37a112c3c5c05bfb5f0a81af4836ce349e69be75af93f99644da9/frozenlist-1.6.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cbb56587a16cf0fb8acd19e90ff9924979ac1431baea8681712716a8337577b0", size = 392200, upload-time = "2025-04-17T22:37:55.951Z" }, - { url = "https://files.pythonhosted.org/packages/81/ce/9a6ea1763e3366e44a5208f76bf37c76c5da570772375e4d0be85180e588/frozenlist-1.6.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c6154c3ba59cda3f954c6333025369e42c3acd0c6e8b6ce31eb5c5b8116c07e0", size = 390134, upload-time = "2025-04-17T22:37:57.633Z" }, - { url = "https://files.pythonhosted.org/packages/bc/36/939738b0b495b2c6d0c39ba51563e453232813042a8d908b8f9544296c29/frozenlist-1.6.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e8246877afa3f1ae5c979fe85f567d220f86a50dc6c493b9b7d8191181ae01e", size = 365208, upload-time = "2025-04-17T22:37:59.742Z" }, - { url = "https://files.pythonhosted.org/packages/b4/8b/939e62e93c63409949c25220d1ba8e88e3960f8ef6a8d9ede8f94b459d27/frozenlist-1.6.0-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b0f6cce16306d2e117cf9db71ab3a9e8878a28176aeaf0dbe35248d97b28d0c", size = 385548, upload-time = "2025-04-17T22:38:01.416Z" }, - { url = "https://files.pythonhosted.org/packages/62/38/22d2873c90102e06a7c5a3a5b82ca47e393c6079413e8a75c72bff067fa8/frozenlist-1.6.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:1b8e8cd8032ba266f91136d7105706ad57770f3522eac4a111d77ac126a25a9b", size = 391123, upload-time = "2025-04-17T22:38:03.049Z" }, - { url = "https://files.pythonhosted.org/packages/44/78/63aaaf533ee0701549500f6d819be092c6065cb5c577edb70c09df74d5d0/frozenlist-1.6.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:e2ada1d8515d3ea5378c018a5f6d14b4994d4036591a52ceaf1a1549dec8e1ad", size = 394199, upload-time = "2025-04-17T22:38:04.776Z" }, - { url = "https://files.pythonhosted.org/packages/54/45/71a6b48981d429e8fbcc08454dc99c4c2639865a646d549812883e9c9dd3/frozenlist-1.6.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:cdb2c7f071e4026c19a3e32b93a09e59b12000751fc9b0b7758da899e657d215", size = 373854, upload-time = "2025-04-17T22:38:06.576Z" }, - { url = "https://files.pythonhosted.org/packages/3f/f3/dbf2a5e11736ea81a66e37288bf9f881143a7822b288a992579ba1b4204d/frozenlist-1.6.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:03572933a1969a6d6ab509d509e5af82ef80d4a5d4e1e9f2e1cdd22c77a3f4d2", size = 395412, upload-time = "2025-04-17T22:38:08.197Z" }, - { url = "https://files.pythonhosted.org/packages/b3/f1/c63166806b331f05104d8ea385c4acd511598568b1f3e4e8297ca54f2676/frozenlist-1.6.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:77effc978947548b676c54bbd6a08992759ea6f410d4987d69feea9cd0919911", size = 394936, upload-time = "2025-04-17T22:38:10.056Z" }, - { url = "https://files.pythonhosted.org/packages/ef/ea/4f3e69e179a430473eaa1a75ff986526571215fefc6b9281cdc1f09a4eb8/frozenlist-1.6.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:a2bda8be77660ad4089caf2223fdbd6db1858462c4b85b67fbfa22102021e497", size = 391459, upload-time = "2025-04-17T22:38:11.826Z" }, - { url = "https://files.pythonhosted.org/packages/d3/c3/0fc2c97dea550df9afd072a37c1e95421652e3206bbeaa02378b24c2b480/frozenlist-1.6.0-cp313-cp313t-win32.whl", hash = "sha256:a4d96dc5bcdbd834ec6b0f91027817214216b5b30316494d2b1aebffb87c534f", size = 128797, upload-time = "2025-04-17T22:38:14.013Z" }, - { url = "https://files.pythonhosted.org/packages/ae/f5/79c9320c5656b1965634fe4be9c82b12a3305bdbc58ad9cb941131107b20/frozenlist-1.6.0-cp313-cp313t-win_amd64.whl", hash = "sha256:e18036cb4caa17ea151fd5f3d70be9d354c99eb8cf817a3ccde8a7873b074348", size = 134709, upload-time = "2025-04-17T22:38:15.551Z" }, - { url = "https://files.pythonhosted.org/packages/71/3e/b04a0adda73bd52b390d730071c0d577073d3d26740ee1bad25c3ad0f37b/frozenlist-1.6.0-py3-none-any.whl", hash = "sha256:535eec9987adb04701266b92745d6cdcef2e77669299359c3009c3404dd5d191", size = 12404, upload-time = "2025-04-17T22:38:51.668Z" }, +version = "1.6.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5b/bf/a812e2fe6cb3f6c6cfc8d0303bf1742f2286004e5ec41ac8c89cf68cdb54/frozenlist-1.6.2.tar.gz", hash = "sha256:effc641518696471cf4962e8e32050133bc1f7b2851ae8fd0cb8797dd70dc202", size = 43108, upload-time = "2025-06-03T21:48:04.467Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b8/f6/973abfcb8b68f2e8b58071a04ec72f5e1f0acd19dae0d3b7a8abc3d9ab07/frozenlist-1.6.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:2ad8851ae1f6695d735f8646bf1e68675871789756f7f7e8dc8224a74eabb9d0", size = 85517, upload-time = "2025-06-03T21:46:42.124Z" }, + { url = "https://files.pythonhosted.org/packages/c8/d0/ac45f2dcf0afd5f7d57204af8b7516ecbc3599ea681e06f4b25d3845bea8/frozenlist-1.6.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:cd2d5abc0ccd99a2a5b437987f3b1e9c265c1044d2855a09ac68f09bbb8082ca", size = 49916, upload-time = "2025-06-03T21:46:43.93Z" }, + { url = "https://files.pythonhosted.org/packages/50/cc/99c3f31823630b7411f7c1e83399e91d6b56a5661a5b724935ef5b51f5f5/frozenlist-1.6.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:15c33f665faa9b8f8e525b987eeaae6641816e0f6873e8a9c4d224338cebbb55", size = 48107, upload-time = "2025-06-03T21:46:45.188Z" }, + { url = "https://files.pythonhosted.org/packages/85/4e/38643ce3ee80d222892b694d02c15ea476c4d564493a6fe530347163744e/frozenlist-1.6.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3e6c0681783723bb472b6b8304e61ecfcb4c2b11cf7f243d923813c21ae5d2a", size = 255771, upload-time = "2025-06-03T21:46:46.53Z" }, + { url = "https://files.pythonhosted.org/packages/ca/e6/ceed85a7d5c0f666485384fc393e32353f8088e154a1109e5ef60165d366/frozenlist-1.6.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:61bae4d345a26550d0ed9f2c9910ea060f89dbfc642b7b96e9510a95c3a33b3c", size = 252519, upload-time = "2025-06-03T21:46:48.101Z" }, + { url = "https://files.pythonhosted.org/packages/29/99/9f2e2b90cf918465e3b6ca4eea79e6be53d24fba33937e37d86c3764bbf9/frozenlist-1.6.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:90e5a84016d0d2fb828f770ede085b5d89155fcb9629b8a3237c960c41c120c3", size = 263348, upload-time = "2025-06-03T21:46:49.64Z" }, + { url = "https://files.pythonhosted.org/packages/4e/ac/59f3ec4c1b4897186efb4757379915734a48bb16bbc15a9fe0bf0857b679/frozenlist-1.6.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55dc289a064c04819d669e6e8a85a1c0416e6c601782093bdc749ae14a2f39da", size = 257858, upload-time = "2025-06-03T21:46:51.189Z" }, + { url = "https://files.pythonhosted.org/packages/48/4a/19c97510d0c2be1ebaae68383d1b5a256a12a660ca17b0c427b1024d9b92/frozenlist-1.6.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b79bcf97ca03c95b044532a4fef6e5ae106a2dd863875b75fde64c553e3f4820", size = 238248, upload-time = "2025-06-03T21:46:52.649Z" }, + { url = "https://files.pythonhosted.org/packages/ef/64/641aa2b0944fa3d881323948e0d8d6fee746dae03d9023eb510bb80bc46a/frozenlist-1.6.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e5e7564d232a782baa3089b25a0d979e2e4d6572d3c7231fcceacc5c22bf0f7", size = 255932, upload-time = "2025-06-03T21:46:54.175Z" }, + { url = "https://files.pythonhosted.org/packages/6c/f8/5b68d5658fac7332e5d26542a4af0ffc2edca8da8f854f6274882889ee1e/frozenlist-1.6.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6fcd8d56880dccdd376afb18f483ab55a0e24036adc9a83c914d4b7bb5729d4e", size = 253329, upload-time = "2025-06-03T21:46:55.69Z" }, + { url = "https://files.pythonhosted.org/packages/e9/20/379d7a27eb82748b41319bf376bf2c034e7ee11dda94f12b331edcc261ff/frozenlist-1.6.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:4fbce985c7fe7bafb4d9bf647c835dbe415b465a897b0c79d1bdf0f3fae5fe50", size = 266164, upload-time = "2025-06-03T21:46:57.19Z" }, + { url = "https://files.pythonhosted.org/packages/13/bd/d7dbf94220020850392cb661bedfdf786398bafae85d1045dd108971d261/frozenlist-1.6.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:3bd12d727cd616387d50fe283abebb2db93300c98f8ff1084b68460acd551926", size = 241641, upload-time = "2025-06-03T21:46:59.769Z" }, + { url = "https://files.pythonhosted.org/packages/a4/70/916fef6284d294077265cd69ad05f228e44f7ed88d9acb690df5a1174049/frozenlist-1.6.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:38544cae535ed697960891131731b33bb865b7d197ad62dc380d2dbb1bceff48", size = 261215, upload-time = "2025-06-03T21:47:01.752Z" }, + { url = "https://files.pythonhosted.org/packages/8f/98/1326a7189fa519692698cddf598f56766b0fea6ac71cddaf64760a055397/frozenlist-1.6.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:47396898f98fae5c9b9bb409c3d2cf6106e409730f35a0926aad09dd7acf1ef5", size = 262597, upload-time = "2025-06-03T21:47:03.495Z" }, + { url = "https://files.pythonhosted.org/packages/f4/d6/0a95ab9289c72e86c37c9b8afe82576556456b6f66a35d242526634130f2/frozenlist-1.6.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d10d835f8ce8571fd555db42d3aef325af903535dad7e6faa7b9c8abe191bffc", size = 258766, upload-time = "2025-06-03T21:47:05.411Z" }, + { url = "https://files.pythonhosted.org/packages/1b/d0/9e946aabd89ebfcb71ec1371327f0e25d4868cd4439471a6fcb6eaf7b366/frozenlist-1.6.2-cp313-cp313-win32.whl", hash = "sha256:a400fe775a41b6d7a3fef00d88f10cbae4f0074c9804e282013d7797671ba58d", size = 40961, upload-time = "2025-06-03T21:47:06.89Z" }, + { url = "https://files.pythonhosted.org/packages/43/e9/d714f5eb0fde1413344ded982ae9638307b59651d5c04263af42eb81a315/frozenlist-1.6.2-cp313-cp313-win_amd64.whl", hash = "sha256:cc8b25b321863ed46992558a29bb09b766c41e25f31461666d501be0f893bada", size = 46204, upload-time = "2025-06-03T21:47:08.2Z" }, + { url = "https://files.pythonhosted.org/packages/f5/7a/8f6dde73862499e60eb390778a1e46b87c1fe3c5722622d731ccda7a173c/frozenlist-1.6.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:56de277a0e0ad26a1dcdc99802b4f5becd7fd890807b68e3ecff8ced01d58132", size = 91326, upload-time = "2025-06-03T21:47:09.566Z" }, + { url = "https://files.pythonhosted.org/packages/79/60/dcdc75edbcf8241e7cb15fced68b3be63f67ff3faaf559c540a7eb63233b/frozenlist-1.6.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:9cb386dd69ae91be586aa15cb6f39a19b5f79ffc1511371eca8ff162721c4867", size = 52426, upload-time = "2025-06-03T21:47:10.828Z" }, + { url = "https://files.pythonhosted.org/packages/64/e6/df2a43ccb2c4f1ea3692aae9a89cfc5dd932a90b7898f98f13ed9e2680a9/frozenlist-1.6.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:53835d8a6929c2f16e02616f8b727bd140ce8bf0aeddeafdb290a67c136ca8ad", size = 51460, upload-time = "2025-06-03T21:47:12.089Z" }, + { url = "https://files.pythonhosted.org/packages/fd/b3/c4f2f7fca9487b25c39bf64535f029316e184072a82f3660ce72defc5421/frozenlist-1.6.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc49f2277e8173abf028d744f8b7d69fe8cc26bffc2de97d47a3b529599fbf50", size = 310270, upload-time = "2025-06-03T21:47:13.495Z" }, + { url = "https://files.pythonhosted.org/packages/2b/5b/046eb34d8d0fee1a8c9dc91a9ba581283c67a1ace20bcc01c86a53595105/frozenlist-1.6.2-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:65eb9e8a973161bdac5fa06ea6bd261057947adc4f47a7a6ef3d6db30c78c5b4", size = 289062, upload-time = "2025-06-03T21:47:14.92Z" }, + { url = "https://files.pythonhosted.org/packages/48/7b/80991efaa0aa25e867cf93033c28e9d1310f34f90421eb59eb1f2073d937/frozenlist-1.6.2-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:301eb2f898d863031f8c5a56c88a6c5d976ba11a4a08a1438b96ee3acb5aea80", size = 312202, upload-time = "2025-06-03T21:47:16.436Z" }, + { url = "https://files.pythonhosted.org/packages/78/6b/6fe30bdababdf82c5b34f0093770c4be6211071e23570721b80b11c9d52a/frozenlist-1.6.2-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:207f717fd5e65fddb77d33361ab8fa939f6d89195f11307e073066886b33f2b8", size = 309557, upload-time = "2025-06-03T21:47:17.939Z" }, + { url = "https://files.pythonhosted.org/packages/9d/ef/b7bf48802fc7d084703ba2173e6a8d0590bea378dcd6a480051c41bddf47/frozenlist-1.6.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f83992722642ee0db0333b1dbf205b1a38f97d51a7382eb304ba414d8c3d1e05", size = 282135, upload-time = "2025-06-03T21:47:19.521Z" }, + { url = "https://files.pythonhosted.org/packages/af/f8/6911a085bce8d0d0df3dfc2560e3e0fb4d6c19ff101014bcf61aa32ba39a/frozenlist-1.6.2-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:12af99e6023851b36578e5bcc60618b5b30f4650340e29e565cd1936326dbea7", size = 303392, upload-time = "2025-06-03T21:47:21.16Z" }, + { url = "https://files.pythonhosted.org/packages/9c/5d/b4e0cc6dbd6b9282926a470a919da7c6599ff324ab5268c7ecaff82cb858/frozenlist-1.6.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6f01620444a674eaad900a3263574418e99c49e2a5d6e5330753857363b5d59f", size = 309402, upload-time = "2025-06-03T21:47:22.705Z" }, + { url = "https://files.pythonhosted.org/packages/0f/1b/bf777de3c810e68e8758337fcc97ee8c956376c87aecee9a61ba19a94123/frozenlist-1.6.2-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:82b94c8948341512306ca8ccc702771600b442c6abe5f8ee017e00e452a209e8", size = 312924, upload-time = "2025-06-03T21:47:24.251Z" }, + { url = "https://files.pythonhosted.org/packages/0e/03/a69b890bc310790fcae61fd3b5be64876811b12db5d50b32e62f65e766bd/frozenlist-1.6.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:324a4cf4c220ddb3db1f46ade01e48432c63fa8c26812c710006e7f6cfba4a08", size = 291768, upload-time = "2025-06-03T21:47:25.874Z" }, + { url = "https://files.pythonhosted.org/packages/70/cc/559386adf987b47c8977c929271d11a72efd92778a0a2f4cc97827a9a25b/frozenlist-1.6.2-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:695284e51458dabb89af7f7dc95c470aa51fd259207aba5378b187909297feef", size = 313305, upload-time = "2025-06-03T21:47:29.305Z" }, + { url = "https://files.pythonhosted.org/packages/e7/fa/eb0e21730ffccfb2d0d367d863cbaacf8367bdc277b44eabf72f7329ab91/frozenlist-1.6.2-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:9ccbeb1c8dda4f42d0678076aa5cbde941a232be71c67b9d8ca89fbaf395807c", size = 312228, upload-time = "2025-06-03T21:47:30.967Z" }, + { url = "https://files.pythonhosted.org/packages/d1/c1/8471b67172abc9478ad78c70a3f3a5c4fed6d4bcadc748e1b6dfa06ab2ae/frozenlist-1.6.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:cbbdf62fcc1864912c592a1ec748fee94f294c6b23215d5e8e9569becb7723ee", size = 309905, upload-time = "2025-06-03T21:47:32.526Z" }, + { url = "https://files.pythonhosted.org/packages/bb/2c/ee21987c3a175b49d0b827b1e45394a7a5d08c7de5b766ed6d0889d30568/frozenlist-1.6.2-cp313-cp313t-win32.whl", hash = "sha256:76857098ee17258df1a61f934f2bae052b8542c9ea6b187684a737b2e3383a65", size = 44644, upload-time = "2025-06-03T21:47:34.514Z" }, + { url = "https://files.pythonhosted.org/packages/65/46/fce60f65b1fb17a90c4bf410a5c90cb3b40616cc229e75866f8be97c112c/frozenlist-1.6.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c06a88daba7e891add42f9278cdf7506a49bc04df9b1648be54da1bf1c79b4c6", size = 50607, upload-time = "2025-06-03T21:47:36.227Z" }, + { url = "https://files.pythonhosted.org/packages/13/be/0ebbb283f2d91b72beaee2d07760b2c47dab875c49c286f5591d3d157198/frozenlist-1.6.2-py3-none-any.whl", hash = "sha256:947abfcc8c42a329bbda6df97a4b9c9cdb4e12c85153b3b57b9d2f02aa5877dc", size = 12582, upload-time = "2025-06-03T21:48:03.201Z" }, ] [[package]] @@ -395,14 +393,14 @@ wheels = [ [[package]] name = "google-adk" -version = "0.2.0" +version = "1.2.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "authlib" }, { name = "click" }, { name = "fastapi" }, { name = "google-api-python-client" }, - { name = "google-cloud-aiplatform" }, + { name = "google-cloud-aiplatform", extra = ["agent-engines"] }, { name = "google-cloud-secret-manager" }, { name = "google-cloud-speech" }, { name = "google-cloud-storage" }, @@ -416,17 +414,18 @@ dependencies = [ { name = "python-dotenv" }, { name = "pyyaml" }, { name = "sqlalchemy" }, + { name = "typing-extensions" }, { name = "tzlocal" }, { name = "uvicorn" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/85/7c/440b64c5701f9263f489a2fe9220ea249f179de4836f7289ddf3bd4dcd04/google_adk-0.2.0.tar.gz", hash = "sha256:5d7525c10d86766f8e048d87858a992c99fa5df637af88bddba5e1a4985a6247", size = 1054528, upload-time = "2025-04-18T19:57:42.701Z" } +sdist = { url = "https://files.pythonhosted.org/packages/8c/3f/c17e938b53e6638c75964db92915a866dee3bca29535df2bfd89ca57c999/google_adk-1.2.1.tar.gz", hash = "sha256:7ffac56d1ee457f64b3877f81aed099171c52e03ac2ef7b42de2f7f4973995e7", size = 1104116, upload-time = "2025-06-04T23:43:25.754Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/13/98/be79252680e174ec390f0cdd053f3204c3a90cb005dff66c6577e65fd7f2/google_adk-0.2.0-py3-none-any.whl", hash = "sha256:85f4d0aa6345bda8dc15e1cd8d6784e222507d1b73e21368fea14198f8ff346e", size = 1174834, upload-time = "2025-04-18T19:57:40.553Z" }, + { url = "https://files.pythonhosted.org/packages/d2/83/4fa0a0e765258e4f3f6cf452a28cde0d335cce27542041fcf3afe9df4cef/google_adk-1.2.1-py3-none-any.whl", hash = "sha256:93b805180caadb5b2265a9737c7ac472d10443ddec18ad83efdef8a7a6217d2a", size = 1247947, upload-time = "2025-06-04T23:43:23.737Z" }, ] [[package]] name = "google-ai-generativelanguage" -version = "0.6.17" +version = "0.6.18" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "google-api-core", extra = ["grpc"] }, @@ -434,14 +433,14 @@ dependencies = [ { name = "proto-plus" }, { name = "protobuf" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e6/aa/809e7de6af007e36d456d9a5361250bc906a3f3a6dfc9babebb0de146641/google_ai_generativelanguage-0.6.17.tar.gz", hash = "sha256:8439503503aba6c85b3871504f490bbc66be854031d8bb4d5ac95d3ca2173f38", size = 1423814, upload-time = "2025-03-17T11:26:25.535Z" } +sdist = { url = "https://files.pythonhosted.org/packages/eb/77/3e89a4c4200135eac74eca2f6c9153127e3719a825681ad55f5a4a58b422/google_ai_generativelanguage-0.6.18.tar.gz", hash = "sha256:274ba9fcf69466ff64e971d565884434388e523300afd468fc8e3033cd8e606e", size = 1444757, upload-time = "2025-04-29T15:45:45.527Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e7/ae/1c1553aea9209db84ed8c0b2f8d2dd3feb146290fdae5988b54ee71b5c9d/google_ai_generativelanguage-0.6.17-py3-none-any.whl", hash = "sha256:1aedc8df9bf27c9b6b7e0a70944a29a3db2e7a34bfc0cc8e0ca54a84d361a8a1", size = 1356577, upload-time = "2025-03-17T11:26:24.018Z" }, + { url = "https://files.pythonhosted.org/packages/e5/77/ca2889903a2d93b3072a49056d48b3f55410219743e338a1d7f94dc6455e/google_ai_generativelanguage-0.6.18-py3-none-any.whl", hash = "sha256:13d8174fea90b633f520789d32df7b422058fd5883b022989c349f1017db7fcf", size = 1372256, upload-time = "2025-04-29T15:45:43.601Z" }, ] [[package]] name = "google-api-core" -version = "2.24.2" +version = "2.25.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "google-auth" }, @@ -450,9 +449,9 @@ dependencies = [ { name = "protobuf" }, { name = "requests" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/09/5c/085bcb872556934bb119e5e09de54daa07873f6866b8f0303c49e72287f7/google_api_core-2.24.2.tar.gz", hash = "sha256:81718493daf06d96d6bc76a91c23874dbf2fac0adbbf542831b805ee6e974696", size = 163516, upload-time = "2025-03-10T15:55:26.201Z" } +sdist = { url = "https://files.pythonhosted.org/packages/98/a2/8176b416ca08106b2ae30cd4a006c8176945f682c3a5b42f141c9173f505/google_api_core-2.25.0.tar.gz", hash = "sha256:9b548e688702f82a34ed8409fb8a6961166f0b7795032f0be8f48308dff4333a", size = 164914, upload-time = "2025-06-02T14:45:34.789Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/46/95/f472d85adab6e538da2025dfca9e976a0d125cc0af2301f190e77b76e51c/google_api_core-2.24.2-py3-none-any.whl", hash = "sha256:810a63ac95f3c441b7c0e43d344e372887f62ce9071ba972eacf32672e072de9", size = 160061, upload-time = "2025-03-10T15:55:24.386Z" }, + { url = "https://files.pythonhosted.org/packages/ac/ca/149e41a277bb0855e8ded85fd7579d7747c1223e253d82c5c0f1be236875/google_api_core-2.25.0-py3-none-any.whl", hash = "sha256:1db79d1281dcf9f3d10023283299ba38f3dc9f639ec41085968fd23e5bcf512e", size = 160668, upload-time = "2025-06-02T14:45:33.272Z" }, ] [package.optional-dependencies] @@ -463,7 +462,7 @@ grpc = [ [[package]] name = "google-api-python-client" -version = "2.167.0" +version = "2.171.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "google-api-core" }, @@ -472,23 +471,20 @@ dependencies = [ { name = "httplib2" }, { name = "uritemplate" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/2a/d1/3a23d5b4b967f08aebbfe76e2651416869a765791fbc42f4b78774f48137/google_api_python_client-2.167.0.tar.gz", hash = "sha256:a458d402572e1c2caf9db090d8e7b270f43ff326bd9349c731a86b19910e3995", size = 12725129, upload-time = "2025-04-14T10:14:55.21Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/53/12/4a5322bf1d044114ce59736bb2c260d8d3f843cbedbaa34b553dcad8d9da/google_api_python_client-2.167.0-py2.py3-none-any.whl", hash = "sha256:ce25290cc229505d770ca5c8d03850e0ae87d8e998fc6dd743ecece018baa396", size = 13235609, upload-time = "2025-04-14T10:14:52.083Z" }, -] +sdist = { url = "https://files.pythonhosted.org/packages/35/99/237cd2510aecca9fabb54007e58553274cc43cb3c18512ee1ea574d11b87/google_api_python_client-2.171.0.tar.gz", hash = "sha256:057a5c08d28463c6b9eb89746355de5f14b7ed27a65c11fdbf1d06c66bb66b23", size = 13028937, upload-time = "2025-06-03T18:57:38.732Z" } [[package]] name = "google-auth" -version = "2.39.0" +version = "2.40.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cachetools" }, { name = "pyasn1-modules" }, { name = "rsa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/cb/8e/8f45c9a32f73e786e954b8f9761c61422955d23c45d1e8c347f9b4b59e8e/google_auth-2.39.0.tar.gz", hash = "sha256:73222d43cdc35a3aeacbfdcaf73142a97839f10de930550d89ebfe1d0a00cde7", size = 274834, upload-time = "2025-04-14T17:44:49.402Z" } +sdist = { url = "https://files.pythonhosted.org/packages/9e/9b/e92ef23b84fa10a64ce4831390b7a4c2e53c0132568d99d4ae61d04c8855/google_auth-2.40.3.tar.gz", hash = "sha256:500c3a29adedeb36ea9cf24b8d10858e152f2412e3ca37829b3fa18e33d63b77", size = 281029, upload-time = "2025-06-04T18:04:57.577Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ce/12/ad37a1ef86006d0a0117fc06a4a00bd461c775356b534b425f00dde208ea/google_auth-2.39.0-py2.py3-none-any.whl", hash = "sha256:0150b6711e97fb9f52fe599f55648950cc4540015565d8fbb31be2ad6e1548a2", size = 212319, upload-time = "2025-04-14T17:44:47.699Z" }, + { url = "https://files.pythonhosted.org/packages/17/63/b19553b658a1692443c62bd07e5868adaa0ad746a0751ba62c59568cd45b/google_auth-2.40.3-py2.py3-none-any.whl", hash = "sha256:1370d4593e86213563547f97a92752fc658456fe4514c809544f330fed45a7ca", size = 216137, upload-time = "2025-06-04T18:04:55.573Z" }, ] [[package]] @@ -506,7 +502,7 @@ wheels = [ [[package]] name = "google-cloud-aiplatform" -version = "1.89.0" +version = "1.96.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "docstring-parser" }, @@ -515,6 +511,7 @@ dependencies = [ { name = "google-cloud-bigquery" }, { name = "google-cloud-resource-manager" }, { name = "google-cloud-storage" }, + { name = "google-genai" }, { name = "packaging" }, { name = "proto-plus" }, { name = "protobuf" }, @@ -522,14 +519,54 @@ dependencies = [ { name = "shapely" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/48/95/2147af499e16640bcc4ca9e4cce4ff6e49b33d9c1f7e9e1d7b4040d87ad7/google_cloud_aiplatform-1.89.0.tar.gz", hash = "sha256:bb84c489ea14a9658a38d400e35108740a7663c9ceb2414e892c0c502cb5c050", size = 9089628, upload-time = "2025-04-16T23:30:21.536Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7f/e9/4343714ae0b3361e7b5dc3c31ae8a884f6b5854806072dce85f50bf37e6a/google_cloud_aiplatform-1.96.0.tar.gz", hash = "sha256:c704bf2409d3aca548df5cf036e6e54adf6acf8c114b01a926925e4c65085a53", size = 9201786, upload-time = "2025-06-04T00:19:35.537Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bc/e6/fe00d5871137f4172e512c1844dfd010ea87e62cefcbc2d4760a416f0ebc/google_cloud_aiplatform-1.96.0-py2.py3-none-any.whl", hash = "sha256:fca9edab98caf354f415285bbcf4a282b7015b58b82fab7fb422d2a535940b8f", size = 7665371, upload-time = "2025-06-04T00:28:07.456Z" }, +] + +[package.optional-dependencies] +agent-engines = [ + { name = "cloudpickle" }, + { name = "google-cloud-logging" }, + { name = "google-cloud-trace" }, + { name = "opentelemetry-exporter-gcp-trace" }, + { name = "opentelemetry-sdk" }, + { name = "packaging" }, + { name = "pydantic" }, + { name = "typing-extensions" }, +] + +[[package]] +name = "google-cloud-appengine-logging" +version = "1.6.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "google-api-core", extra = ["grpc"] }, + { name = "google-auth" }, + { name = "proto-plus" }, + { name = "protobuf" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/cb/ec/ac5eed8660dd49a68d425c1e9594a40dc0c757d3d06af1e7731e5ff5d4ee/google_cloud_appengine_logging-1.6.1.tar.gz", hash = "sha256:f97bde36c7f7ff541123c2570813158bdda0c3f2385c8d32fdf1211c561ae56d", size = 16520, upload-time = "2025-03-17T11:27:40.505Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/34/d4/1e515977b876199ba476e58890c88c92b2680a04370c63d90b89dd9bff37/google_cloud_appengine_logging-1.6.1-py3-none-any.whl", hash = "sha256:48f4dcf43000899c7b411bc27181f70240e81a958a44e44ce800ba8e5d5184ac", size = 16809, upload-time = "2025-03-17T11:27:39.104Z" }, +] + +[[package]] +name = "google-cloud-audit-log" +version = "0.3.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "googleapis-common-protos" }, + { name = "protobuf" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/85/af/53b4ef636e492d136b3c217e52a07bee569430dda07b8e515d5f2b701b1e/google_cloud_audit_log-0.3.2.tar.gz", hash = "sha256:2598f1533a7d7cdd6c7bf448c12e5519c1d53162d78784e10bcdd1df67791bc3", size = 33377, upload-time = "2025-03-17T11:27:59.808Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/de/d3/f1e2fda8bd8ecd12c93d9a377abe2dc0002f76e60f5abd4d7257243ea2ef/google_cloud_aiplatform-1.89.0-py2.py3-none-any.whl", hash = "sha256:b4e532058c87f71f8f4265b9de760f9765388b07f6d16e0c2a0a2275428580e7", size = 7586263, upload-time = "2025-04-16T23:30:18.269Z" }, + { url = "https://files.pythonhosted.org/packages/b1/74/38a70339e706b174b3c1117ad931aaa0ff0565b599869317a220d1967e1b/google_cloud_audit_log-0.3.2-py3-none-any.whl", hash = "sha256:daaedfb947a0d77f524e1bd2b560242ab4836fe1afd6b06b92f152b9658554ed", size = 32472, upload-time = "2025-03-17T11:27:58.51Z" }, ] [[package]] name = "google-cloud-bigquery" -version = "3.31.0" +version = "3.34.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "google-api-core", extra = ["grpc"] }, @@ -540,9 +577,9 @@ dependencies = [ { name = "python-dateutil" }, { name = "requests" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/73/91/4c7274f4d5faf13ac000b06353deaf3579575bf0e4bbad07fa68b9f09ba9/google_cloud_bigquery-3.31.0.tar.gz", hash = "sha256:b89dc716dbe4abdb7a4f873f7050100287bc98514e0614c5d54cd6a8e9fb0991", size = 479961, upload-time = "2025-03-25T18:54:40.43Z" } +sdist = { url = "https://files.pythonhosted.org/packages/24/f9/e9da2d56d7028f05c0e2f5edf6ce43c773220c3172666c3dd925791d763d/google_cloud_bigquery-3.34.0.tar.gz", hash = "sha256:5ee1a78ba5c2ccb9f9a8b2bf3ed76b378ea68f49b6cac0544dc55cc97ff7c1ce", size = 489091, upload-time = "2025-05-29T17:18:06.03Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e8/bc/4cb8c61fc6dd817a4a390b745ec7b305f4578f547a16d09d54c8a790624b/google_cloud_bigquery-3.31.0-py3-none-any.whl", hash = "sha256:97f4a3219854ff01d6a3a57312feecb0b6e13062226b823f867e2d3619c4787b", size = 250099, upload-time = "2025-03-25T18:54:38.241Z" }, + { url = "https://files.pythonhosted.org/packages/b1/7e/7115c4f67ca0bc678f25bff1eab56cc37d06eb9a3978940b2ebd0705aa0a/google_cloud_bigquery-3.34.0-py3-none-any.whl", hash = "sha256:de20ded0680f8136d92ff5256270b5920dfe4fae479f5d0f73e90e5df30b1cf7", size = 253555, upload-time = "2025-05-29T17:18:02.904Z" }, ] [[package]] @@ -558,6 +595,26 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/40/86/bda7241a8da2d28a754aad2ba0f6776e35b67e37c36ae0c45d49370f1014/google_cloud_core-2.4.3-py2.py3-none-any.whl", hash = "sha256:5130f9f4c14b4fafdff75c79448f9495cfade0d8775facf1b09c3bf67e027f6e", size = 29348, upload-time = "2025-03-10T21:05:37.785Z" }, ] +[[package]] +name = "google-cloud-logging" +version = "3.12.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "google-api-core", extra = ["grpc"] }, + { name = "google-auth" }, + { name = "google-cloud-appengine-logging" }, + { name = "google-cloud-audit-log" }, + { name = "google-cloud-core" }, + { name = "grpc-google-iam-v1" }, + { name = "opentelemetry-api" }, + { name = "proto-plus" }, + { name = "protobuf" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/14/9c/d42ecc94f795a6545930e5f846a7ae59ff685ded8bc086648dd2bee31a1a/google_cloud_logging-3.12.1.tar.gz", hash = "sha256:36efc823985055b203904e83e1c8f9f999b3c64270bcda39d57386ca4effd678", size = 289569, upload-time = "2025-04-22T20:50:24.71Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b1/41/f8a3197d39b773a91f335dee36c92ef26a8ec96efe78d64baad89d367df4/google_cloud_logging-3.12.1-py2.py3-none-any.whl", hash = "sha256:6817878af76ec4e7568976772839ab2c43ddfd18fbbf2ce32b13ef549cd5a862", size = 229466, upload-time = "2025-04-22T20:50:23.294Z" }, +] + [[package]] name = "google-cloud-resource-manager" version = "1.14.2" @@ -576,7 +633,7 @@ wheels = [ [[package]] name = "google-cloud-secret-manager" -version = "2.23.3" +version = "2.24.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "google-api-core", extra = ["grpc"] }, @@ -585,10 +642,7 @@ dependencies = [ { name = "proto-plus" }, { name = "protobuf" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/99/73/533fca3a94ef6cf57fbd0cf6fa57c8865ba8173542a505272869a1b7be85/google_cloud_secret_manager-2.23.3.tar.gz", hash = "sha256:598c4c0a9d10d49d500eb4aea3255dff250aa2f92c028f5c97e3b367f768c808", size = 268671, upload-time = "2025-04-17T19:01:20.023Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/ec/230606fd826d466ee5b014c6a3f04d47038b1146a3a560645889817d9fea/google_cloud_secret_manager-2.23.3-py3-none-any.whl", hash = "sha256:fe06ebb2f71eb739ecc6c14ea9e8dafcb9bbc6123b78b2f8986ece6733d23a1a", size = 217097, upload-time = "2025-04-17T19:01:18.173Z" }, -] +sdist = { url = "https://files.pythonhosted.org/packages/58/7a/2fa6735ec693d822fe08a76709c4d95d9b5b4c02e83e720497355039d2ee/google_cloud_secret_manager-2.24.0.tar.gz", hash = "sha256:ce573d40ffc2fb7d01719243a94ee17aa243ea642a6ae6c337501e58fbf642b5", size = 269516, upload-time = "2025-06-05T22:22:22.965Z" } [[package]] name = "google-cloud-speech" @@ -654,7 +708,7 @@ wheels = [ [[package]] name = "google-genai" -version = "1.11.0" +version = "1.19.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, @@ -665,9 +719,9 @@ dependencies = [ { name = "typing-extensions" }, { name = "websockets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/73/44/64c6c23724580add879cbcca81ffed500955c1c21850468cd4dcf9c62a03/google_genai-1.11.0.tar.gz", hash = "sha256:0643b2f5373fbeae945d0cd5a37d157eab0c172bb5e14e905f2f8d45aa51cabb", size = 160955, upload-time = "2025-04-16T23:34:37.979Z" } +sdist = { url = "https://files.pythonhosted.org/packages/14/17/8f717f43732ae2b7775f816f0d8f0b39e2a020bbe7ba202f2ddb2f948c3b/google_genai-1.19.0.tar.gz", hash = "sha256:66f5de78075781bfd9e423f1e3592e4240759dfe0ac42ac74a9dcb2c4f662e9d", size = 198000, upload-time = "2025-06-04T23:10:04.69Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/dc/9b/55f97203720cbda5a1c8e0460793914980e41c6ca4859fea735dd66d2c3a/google_genai-1.11.0-py3-none-any.whl", hash = "sha256:34fbe3c85419adbcddcb8222f99514596b3a69c80ff1a4ae30a01a763da27acc", size = 159687, upload-time = "2025-04-16T23:34:36.595Z" }, + { url = "https://files.pythonhosted.org/packages/c4/ae/64fccdebf5811453ce53b0d5ee23d4f27ef173ef36d3b67dad791a0007aa/google_genai-1.19.0-py3-none-any.whl", hash = "sha256:a2955612e4af8c84f83eb43c1ce4e74e1b714732926d0705e639761938192466", size = 200043, upload-time = "2025-06-04T23:10:02.692Z" }, ] [[package]] @@ -710,39 +764,38 @@ wheels = [ [[package]] name = "greenlet" -version = "3.2.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b0/9c/666d8c71b18d0189cf801c0e0b31c4bfc609ac823883286045b1f3ae8994/greenlet-3.2.0.tar.gz", hash = "sha256:1d2d43bd711a43db8d9b9187500e6432ddb4fafe112d082ffabca8660a9e01a7", size = 183685, upload-time = "2025-04-15T16:21:26.141Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c9/43/c0b655d4d7eae19282b028bcec449e5c80626ad0d8d0ca3703f9b1c29258/greenlet-3.2.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:b86a3ccc865ae601f446af042707b749eebc297928ea7bd0c5f60c56525850be", size = 269131, upload-time = "2025-04-15T16:19:19.469Z" }, - { url = "https://files.pythonhosted.org/packages/7c/7d/c8f51c373c7f7ac0f73d04a6fd77ab34f6f643cb41a0d186d05ba96708e7/greenlet-3.2.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:144283ad88ed77f3ebd74710dd419b55dd15d18704b0ae05935766a93f5671c5", size = 637323, upload-time = "2025-04-15T16:49:02.677Z" }, - { url = "https://files.pythonhosted.org/packages/89/65/c3ee41b2e56586737d6e124b250583695628ffa6b324855b3a1267a8d1d9/greenlet-3.2.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5be69cd50994b8465c3ad1467f9e63001f76e53a89440ad4440d1b6d52591280", size = 651430, upload-time = "2025-04-15T16:50:43.445Z" }, - { url = "https://files.pythonhosted.org/packages/f0/07/33bd7a3dcde1db7259371d026ce76be1eb653d2d892334fc79a500b3c5ee/greenlet-3.2.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:47aeadd1e8fbdef8fdceb8fb4edc0cbb398a57568d56fd68f2bc00d0d809e6b6", size = 645798, upload-time = "2025-04-15T16:55:03.795Z" }, - { url = "https://files.pythonhosted.org/packages/35/5b/33c221a6a867030b0b770513a1b78f6c30e04294131dafdc8da78906bbe6/greenlet-3.2.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18adc14ab154ca6e53eecc9dc50ff17aeb7ba70b7e14779b26e16d71efa90038", size = 648271, upload-time = "2025-04-15T16:22:42.458Z" }, - { url = "https://files.pythonhosted.org/packages/4d/dd/d6452248fa6093504e3b7525dc2bdc4e55a4296ec6ee74ba241a51d852e2/greenlet-3.2.0-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e8622b33d8694ec373ad55050c3d4e49818132b44852158442e1931bb02af336", size = 606779, upload-time = "2025-04-15T16:22:41.417Z" }, - { url = "https://files.pythonhosted.org/packages/9d/24/160f04d2589bcb15b8661dcd1763437b22e01643626899a4139bf98f02af/greenlet-3.2.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:e8ac9a2c20fbff3d0b853e9ef705cdedb70d9276af977d1ec1cde86a87a4c821", size = 1117968, upload-time = "2025-04-15T16:52:53.627Z" }, - { url = "https://files.pythonhosted.org/packages/6c/ff/c6e3f3a5168fef5209cfd9498b2b5dd77a0bf29dfc686a03dcc614cf4432/greenlet-3.2.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:cd37273dc7ca1d5da149b58c8b3ce0711181672ba1b09969663905a765affe21", size = 1145510, upload-time = "2025-04-15T16:23:01.873Z" }, - { url = "https://files.pythonhosted.org/packages/dc/62/5215e374819052e542b5bde06bd7d4a171454b6938c96a2384f21cb94279/greenlet-3.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:8a8940a8d301828acd8b9f3f85db23069a692ff2933358861b19936e29946b95", size = 296004, upload-time = "2025-04-15T16:55:46.007Z" }, - { url = "https://files.pythonhosted.org/packages/62/6d/dc9c909cba5cbf4b0833fce69912927a8ca74791c23c47b9fd4f28092108/greenlet-3.2.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ee59db626760f1ca8da697a086454210d36a19f7abecc9922a2374c04b47735b", size = 629900, upload-time = "2025-04-15T16:49:04.099Z" }, - { url = "https://files.pythonhosted.org/packages/5e/a9/f3f304fbbbd604858ff3df303d7fa1d8f7f9e45a6ef74481aaf03aaac021/greenlet-3.2.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7154b13ef87a8b62fc05419f12d75532d7783586ad016c57b5de8a1c6feeb517", size = 635270, upload-time = "2025-04-15T16:50:44.769Z" }, - { url = "https://files.pythonhosted.org/packages/34/92/4b7b4e2e23ecc723cceef9fe3898e78c8e14e106cc7ba2f276a66161da3e/greenlet-3.2.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:199453d64b02d0c9d139e36d29681efd0e407ed8e2c0bf89d88878d6a787c28f", size = 632534, upload-time = "2025-04-15T16:55:05.203Z" }, - { url = "https://files.pythonhosted.org/packages/da/7f/91f0ecbe72c9d789fb7f400b39da9d1e87fcc2cf8746a9636479ba79ab01/greenlet-3.2.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0010e928e1901d36625f21d008618273f9dda26b516dbdecf873937d39c9dff0", size = 628826, upload-time = "2025-04-15T16:22:44.545Z" }, - { url = "https://files.pythonhosted.org/packages/9f/59/e449a44ce52b13751f55376d85adc155dd311608f6d2aa5b6bd2c8d15486/greenlet-3.2.0-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6005f7a86de836a1dc4b8d824a2339cdd5a1ca7cb1af55ea92575401f9952f4c", size = 593697, upload-time = "2025-04-15T16:22:43.796Z" }, - { url = "https://files.pythonhosted.org/packages/bb/09/cca3392927c5c990b7a8ede64ccd0712808438d6490d63ce6b8704d6df5f/greenlet-3.2.0-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:17fd241c0d50bacb7ce8ff77a30f94a2d0ca69434ba2e0187cf95a5414aeb7e1", size = 1105762, upload-time = "2025-04-15T16:52:55.245Z" }, - { url = "https://files.pythonhosted.org/packages/4d/b9/3d201f819afc3b7a8cd7ebe645f1a17799603e2d62c968154518f79f4881/greenlet-3.2.0-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:7b17a26abc6a1890bf77d5d6b71c0999705386b00060d15c10b8182679ff2790", size = 1125173, upload-time = "2025-04-15T16:23:03.009Z" }, - { url = "https://files.pythonhosted.org/packages/80/7b/773a30602234597fc2882091f8e1d1a38ea0b4419d99ca7ed82c827e2c3a/greenlet-3.2.0-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:397b6bbda06f8fe895893d96218cd6f6d855a6701dc45012ebe12262423cec8b", size = 269908, upload-time = "2025-04-15T16:20:33.58Z" }, +version = "3.2.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c9/92/bb85bd6e80148a4d2e0c59f7c0c2891029f8fd510183afc7d8d2feeed9b6/greenlet-3.2.3.tar.gz", hash = "sha256:8b0dd8ae4c0d6f5e54ee55ba935eeb3d735a9b58a8a1e5b5cbab64e01a39f365", size = 185752, upload-time = "2025-06-05T16:16:09.955Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b1/cf/f5c0b23309070ae93de75c90d29300751a5aacefc0a3ed1b1d8edb28f08b/greenlet-3.2.3-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:500b8689aa9dd1ab26872a34084503aeddefcb438e2e7317b89b11eaea1901ad", size = 270732, upload-time = "2025-06-05T16:10:08.26Z" }, + { url = "https://files.pythonhosted.org/packages/48/ae/91a957ba60482d3fecf9be49bc3948f341d706b52ddb9d83a70d42abd498/greenlet-3.2.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:a07d3472c2a93117af3b0136f246b2833fdc0b542d4a9799ae5f41c28323faef", size = 639033, upload-time = "2025-06-05T16:38:53.983Z" }, + { url = "https://files.pythonhosted.org/packages/6f/df/20ffa66dd5a7a7beffa6451bdb7400d66251374ab40b99981478c69a67a8/greenlet-3.2.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:8704b3768d2f51150626962f4b9a9e4a17d2e37c8a8d9867bbd9fa4eb938d3b3", size = 652999, upload-time = "2025-06-05T16:41:37.89Z" }, + { url = "https://files.pythonhosted.org/packages/51/b4/ebb2c8cb41e521f1d72bf0465f2f9a2fd803f674a88db228887e6847077e/greenlet-3.2.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:5035d77a27b7c62db6cf41cf786cfe2242644a7a337a0e155c80960598baab95", size = 647368, upload-time = "2025-06-05T16:48:21.467Z" }, + { url = "https://files.pythonhosted.org/packages/8e/6a/1e1b5aa10dced4ae876a322155705257748108b7fd2e4fae3f2a091fe81a/greenlet-3.2.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2d8aa5423cd4a396792f6d4580f88bdc6efcb9205891c9d40d20f6e670992efb", size = 650037, upload-time = "2025-06-05T16:13:06.402Z" }, + { url = "https://files.pythonhosted.org/packages/26/f2/ad51331a157c7015c675702e2d5230c243695c788f8f75feba1af32b3617/greenlet-3.2.3-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2c724620a101f8170065d7dded3f962a2aea7a7dae133a009cada42847e04a7b", size = 608402, upload-time = "2025-06-05T16:12:51.91Z" }, + { url = "https://files.pythonhosted.org/packages/26/bc/862bd2083e6b3aff23300900a956f4ea9a4059de337f5c8734346b9b34fc/greenlet-3.2.3-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:873abe55f134c48e1f2a6f53f7d1419192a3d1a4e873bace00499a4e45ea6af0", size = 1119577, upload-time = "2025-06-05T16:36:49.787Z" }, + { url = "https://files.pythonhosted.org/packages/86/94/1fc0cc068cfde885170e01de40a619b00eaa8f2916bf3541744730ffb4c3/greenlet-3.2.3-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:024571bbce5f2c1cfff08bf3fbaa43bbc7444f580ae13b0099e95d0e6e67ed36", size = 1147121, upload-time = "2025-06-05T16:12:42.527Z" }, + { url = "https://files.pythonhosted.org/packages/27/1a/199f9587e8cb08a0658f9c30f3799244307614148ffe8b1e3aa22f324dea/greenlet-3.2.3-cp313-cp313-win_amd64.whl", hash = "sha256:5195fb1e75e592dd04ce79881c8a22becdfa3e6f500e7feb059b1e6fdd54d3e3", size = 297603, upload-time = "2025-06-05T16:20:12.651Z" }, + { url = "https://files.pythonhosted.org/packages/d8/ca/accd7aa5280eb92b70ed9e8f7fd79dc50a2c21d8c73b9a0856f5b564e222/greenlet-3.2.3-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:3d04332dddb10b4a211b68111dabaee2e1a073663d117dc10247b5b1642bac86", size = 271479, upload-time = "2025-06-05T16:10:47.525Z" }, + { url = "https://files.pythonhosted.org/packages/55/71/01ed9895d9eb49223280ecc98a557585edfa56b3d0e965b9fa9f7f06b6d9/greenlet-3.2.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8186162dffde068a465deab08fc72c767196895c39db26ab1c17c0b77a6d8b97", size = 683952, upload-time = "2025-06-05T16:38:55.125Z" }, + { url = "https://files.pythonhosted.org/packages/ea/61/638c4bdf460c3c678a0a1ef4c200f347dff80719597e53b5edb2fb27ab54/greenlet-3.2.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f4bfbaa6096b1b7a200024784217defedf46a07c2eee1a498e94a1b5f8ec5728", size = 696917, upload-time = "2025-06-05T16:41:38.959Z" }, + { url = "https://files.pythonhosted.org/packages/22/cc/0bd1a7eb759d1f3e3cc2d1bc0f0b487ad3cc9f34d74da4b80f226fde4ec3/greenlet-3.2.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:ed6cfa9200484d234d8394c70f5492f144b20d4533f69262d530a1a082f6ee9a", size = 692443, upload-time = "2025-06-05T16:48:23.113Z" }, + { url = "https://files.pythonhosted.org/packages/67/10/b2a4b63d3f08362662e89c103f7fe28894a51ae0bc890fabf37d1d780e52/greenlet-3.2.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:02b0df6f63cd15012bed5401b47829cfd2e97052dc89da3cfaf2c779124eb892", size = 692995, upload-time = "2025-06-05T16:13:07.972Z" }, + { url = "https://files.pythonhosted.org/packages/5a/c6/ad82f148a4e3ce9564056453a71529732baf5448ad53fc323e37efe34f66/greenlet-3.2.3-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:86c2d68e87107c1792e2e8d5399acec2487a4e993ab76c792408e59394d52141", size = 655320, upload-time = "2025-06-05T16:12:53.453Z" }, + { url = "https://files.pythonhosted.org/packages/5c/4f/aab73ecaa6b3086a4c89863d94cf26fa84cbff63f52ce9bc4342b3087a06/greenlet-3.2.3-cp314-cp314-win_amd64.whl", hash = "sha256:8c47aae8fbbfcf82cc13327ae802ba13c9c36753b67e760023fd116bc124a62a", size = 301236, upload-time = "2025-06-05T16:15:20.111Z" }, ] [[package]] name = "griffe" -version = "1.7.2" +version = "1.7.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/59/08/7df7e90e34d08ad890bd71d7ba19451052f88dc3d2c483d228d1331a4736/griffe-1.7.2.tar.gz", hash = "sha256:98d396d803fab3b680c2608f300872fd57019ed82f0672f5b5323a9ad18c540c", size = 394919, upload-time = "2025-04-01T14:38:44.887Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a9/3e/5aa9a61f7c3c47b0b52a1d930302992229d191bf4bc76447b324b731510a/griffe-1.7.3.tar.gz", hash = "sha256:52ee893c6a3a968b639ace8015bec9d36594961e156e23315c8e8e51401fa50b", size = 395137, upload-time = "2025-04-23T11:29:09.147Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b1/5e/38b408f41064c9fcdbb0ea27c1bd13a1c8657c4846e04dab9f5ea770602c/griffe-1.7.2-py3-none-any.whl", hash = "sha256:1ed9c2e338a75741fc82083fe5a1bc89cb6142efe126194cc313e34ee6af5423", size = 129187, upload-time = "2025-04-01T14:38:43.227Z" }, + { url = "https://files.pythonhosted.org/packages/58/c6/5c20af38c2a57c15d87f7f38bee77d63c1d2a3689f74fefaf35915dd12b2/griffe-1.7.3-py3-none-any.whl", hash = "sha256:c6b3ee30c2f0f17f30bcdef5068d6ab7a2a4f1b8bf1a3e74b56fffd21e1c5f75", size = 129303, upload-time = "2025-04-23T11:29:07.145Z" }, ] [[package]] @@ -761,20 +814,20 @@ wheels = [ [[package]] name = "grpcio" -version = "1.71.0" +version = "1.72.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1c/95/aa11fc09a85d91fbc7dd405dcb2a1e0256989d67bf89fa65ae24b3ba105a/grpcio-1.71.0.tar.gz", hash = "sha256:2b85f7820475ad3edec209d3d89a7909ada16caab05d3f2e08a7e8ae3200a55c", size = 12549828, upload-time = "2025-03-10T19:28:49.203Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fe/45/ff8c80a5a2e7e520d9c4d3c41484a11d33508253f6f4dd06d2c4b4158999/grpcio-1.72.1.tar.gz", hash = "sha256:87f62c94a40947cec1a0f91f95f5ba0aa8f799f23a1d42ae5be667b6b27b959c", size = 12584286, upload-time = "2025-06-02T10:14:11.595Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/04/dd/b00cbb45400d06b26126dcfdbdb34bb6c4f28c3ebbd7aea8228679103ef6/grpcio-1.71.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:cebc1b34ba40a312ab480ccdb396ff3c529377a2fce72c45a741f7215bfe8379", size = 5184138, upload-time = "2025-03-10T19:25:15.101Z" }, - { url = "https://files.pythonhosted.org/packages/ed/0a/4651215983d590ef53aac40ba0e29dda941a02b097892c44fa3357e706e5/grpcio-1.71.0-cp313-cp313-macosx_10_14_universal2.whl", hash = "sha256:85da336e3649a3d2171e82f696b5cad2c6231fdd5bad52616476235681bee5b3", size = 11310747, upload-time = "2025-03-10T19:25:17.201Z" }, - { url = "https://files.pythonhosted.org/packages/57/a3/149615b247f321e13f60aa512d3509d4215173bdb982c9098d78484de216/grpcio-1.71.0-cp313-cp313-manylinux_2_17_aarch64.whl", hash = "sha256:f9a412f55bb6e8f3bb000e020dbc1e709627dcb3a56f6431fa7076b4c1aab0db", size = 5653991, upload-time = "2025-03-10T19:25:20.39Z" }, - { url = "https://files.pythonhosted.org/packages/ca/56/29432a3e8d951b5e4e520a40cd93bebaa824a14033ea8e65b0ece1da6167/grpcio-1.71.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:47be9584729534660416f6d2a3108aaeac1122f6b5bdbf9fd823e11fe6fbaa29", size = 6312781, upload-time = "2025-03-10T19:25:22.823Z" }, - { url = "https://files.pythonhosted.org/packages/a3/f8/286e81a62964ceb6ac10b10925261d4871a762d2a763fbf354115f9afc98/grpcio-1.71.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7c9c80ac6091c916db81131d50926a93ab162a7e97e4428ffc186b6e80d6dda4", size = 5910479, upload-time = "2025-03-10T19:25:24.828Z" }, - { url = "https://files.pythonhosted.org/packages/35/67/d1febb49ec0f599b9e6d4d0d44c2d4afdbed9c3e80deb7587ec788fcf252/grpcio-1.71.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:789d5e2a3a15419374b7b45cd680b1e83bbc1e52b9086e49308e2c0b5bbae6e3", size = 6013262, upload-time = "2025-03-10T19:25:26.987Z" }, - { url = "https://files.pythonhosted.org/packages/a1/04/f9ceda11755f0104a075ad7163fc0d96e2e3a9fe25ef38adfc74c5790daf/grpcio-1.71.0-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:1be857615e26a86d7363e8a163fade914595c81fec962b3d514a4b1e8760467b", size = 6643356, upload-time = "2025-03-10T19:25:29.606Z" }, - { url = "https://files.pythonhosted.org/packages/fb/ce/236dbc3dc77cf9a9242adcf1f62538734ad64727fabf39e1346ad4bd5c75/grpcio-1.71.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:a76d39b5fafd79ed604c4be0a869ec3581a172a707e2a8d7a4858cb05a5a7637", size = 6186564, upload-time = "2025-03-10T19:25:31.537Z" }, - { url = "https://files.pythonhosted.org/packages/10/fd/b3348fce9dd4280e221f513dd54024e765b21c348bc475516672da4218e9/grpcio-1.71.0-cp313-cp313-win32.whl", hash = "sha256:74258dce215cb1995083daa17b379a1a5a87d275387b7ffe137f1d5131e2cfbb", size = 3601890, upload-time = "2025-03-10T19:25:33.421Z" }, - { url = "https://files.pythonhosted.org/packages/be/f8/db5d5f3fc7e296166286c2a397836b8b042f7ad1e11028d82b061701f0f7/grpcio-1.71.0-cp313-cp313-win_amd64.whl", hash = "sha256:22c3bc8d488c039a199f7a003a38cb7635db6656fa96437a8accde8322ce2366", size = 4273308, upload-time = "2025-03-10T19:25:35.79Z" }, + { url = "https://files.pythonhosted.org/packages/c3/69/219b0df426cf187535254825b4d4eda8ed3d3bc7dc844725a1ed14f642bf/grpcio-1.72.1-cp313-cp313-linux_armv7l.whl", hash = "sha256:294be6e9c323a197434569a41e0fb5b5aa0962fd5d55a3dc890ec5df985f611a", size = 5183578, upload-time = "2025-06-02T10:09:53.151Z" }, + { url = "https://files.pythonhosted.org/packages/b2/34/a5a5e037a862b2e90c1465791e091d3d2965d893d90dda6c1e7c0a991eb8/grpcio-1.72.1-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:41ec164dac8df2862f67457d9cdf8d8f8b6a4ca475a3ed1ba6547fff98d93717", size = 10306253, upload-time = "2025-06-02T10:09:55.629Z" }, + { url = "https://files.pythonhosted.org/packages/56/8a/8aa932e3833e45772015b2c4a2ebf61649633698f24a84bf55477230b019/grpcio-1.72.1-cp313-cp313-manylinux_2_17_aarch64.whl", hash = "sha256:761736f75c6ddea3732d97eaabe70c616271f5f542a8be95515135fdd1a638f6", size = 5586381, upload-time = "2025-06-02T10:09:58.538Z" }, + { url = "https://files.pythonhosted.org/packages/0e/43/aff1cc76f8e04a060ec8e733d3c91e198ea9f1602a2a26f05db4185aa2dd/grpcio-1.72.1-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:082003cb93618964c111c70d69b60ac0dc6566d4c254c9b2a775faa2965ba8f8", size = 6231049, upload-time = "2025-06-02T10:10:00.827Z" }, + { url = "https://files.pythonhosted.org/packages/64/6e/89e5692ee8b67cedcf802553c77538cc0e21c392b37dd51525d89884db17/grpcio-1.72.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8660f736da75424949c14f7c8b1ac60a25b2f37cabdec95181834b405373e8a7", size = 5826465, upload-time = "2025-06-02T10:10:03.236Z" }, + { url = "https://files.pythonhosted.org/packages/b2/09/bc0b2ea40f797f413f1db4a33dc83c562918b8f970938144756bced82414/grpcio-1.72.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:2ada1abe2ad122b42407b2bfd79d6706a4940d4797f44bd740f5c98ca1ecda9b", size = 5944393, upload-time = "2025-06-02T10:10:05.778Z" }, + { url = "https://files.pythonhosted.org/packages/54/92/9aa2c0c8d855e5b16062ec023ac0a1500b502790bbd724262f188253e90b/grpcio-1.72.1-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:0db2766d0c482ee740abbe7d00a06cc4fb54f7e5a24d3cf27c3352be18a2b1e8", size = 6573460, upload-time = "2025-06-02T10:10:08.33Z" }, + { url = "https://files.pythonhosted.org/packages/aa/27/9fdfd66f65ab7e6a4477f7d0b7adf25171d3425760f138f075bc548f6bf4/grpcio-1.72.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:c4bdb404d9c2187260b34e2b22783c204fba8a9023a166cf77376190d9cf5a08", size = 6120589, upload-time = "2025-06-02T10:10:11.06Z" }, + { url = "https://files.pythonhosted.org/packages/c3/f3/630c7a00a29001e0b82763fbd50ddcaa7c656d521f29aa58a6c8dd2b7800/grpcio-1.72.1-cp313-cp313-win32.whl", hash = "sha256:bb64722c3124c906a5b66e50a90fd36442642f653ba88a24f67d08e94bca59f3", size = 3545905, upload-time = "2025-06-02T10:10:13.521Z" }, + { url = "https://files.pythonhosted.org/packages/c4/10/b6186e92eba035315affc30dfeabf65594dd6f778b92627fae5f40e7beec/grpcio-1.72.1-cp313-cp313-win_amd64.whl", hash = "sha256:329cc6ff5b431df9614340d3825b066a1ff0a5809a01ba2e976ef48c65a0490b", size = 4221454, upload-time = "2025-06-02T10:10:16.73Z" }, ] [[package]] @@ -793,24 +846,24 @@ wheels = [ [[package]] name = "h11" -version = "0.14.0" +version = "0.16.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f5/38/3af3d3633a34a3316095b39c8e8fb4853a28a536e55d347bd8d8e9a14b03/h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d", size = 100418, upload-time = "2022-09-25T15:40:01.519Z" } +sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/95/04/ff642e65ad6b90db43e668d70ffb6736436c7ce41fcc549f4e9472234127/h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761", size = 58259, upload-time = "2022-09-25T15:39:59.68Z" }, + { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" }, ] [[package]] name = "httpcore" -version = "1.0.8" +version = "1.0.9" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "certifi" }, { name = "h11" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9f/45/ad3e1b4d448f22c0cff4f5692f5ed0666658578e358b8d58a19846048059/httpcore-1.0.8.tar.gz", hash = "sha256:86e94505ed24ea06514883fd44d2bc02d90e77e7979c8eb71b90f41d364a1bad", size = 85385, upload-time = "2025-04-11T14:42:46.661Z" } +sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/18/8d/f052b1e336bb2c1fc7ed1aaed898aa570c0b61a09707b108979d9fc6e308/httpcore-1.0.8-py3-none-any.whl", hash = "sha256:5254cf149bcb5f75e9d1b2b9f729ea4a4b883d1ad7379fc632b727cec23674be", size = 78732, upload-time = "2025-04-11T14:42:44.896Z" }, + { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" }, ] [[package]] @@ -860,14 +913,14 @@ wheels = [ [[package]] name = "importlib-metadata" -version = "8.6.1" +version = "8.7.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "zipp" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/33/08/c1395a292bb23fd03bdf572a1357c5a733d3eecbab877641ceacab23db6e/importlib_metadata-8.6.1.tar.gz", hash = "sha256:310b41d755445d74569f993ccfc22838295d9fe005425094fad953d7f15c8580", size = 55767, upload-time = "2025-01-20T22:21:30.429Z" } +sdist = { url = "https://files.pythonhosted.org/packages/76/66/650a33bd90f786193e4de4b3ad86ea60b53c89b669a5c7be931fac31cdb0/importlib_metadata-8.7.0.tar.gz", hash = "sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000", size = 56641, upload-time = "2025-04-27T15:29:01.736Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/79/9d/0fb148dc4d6fa4a7dd1d8378168d9b4cd8d4560a6fbf6f0121c5fc34eb68/importlib_metadata-8.6.1-py3-none-any.whl", hash = "sha256:02a89390c1e15fdfdc0d7c6b25cb3e62650d0494005c97d6f148bf5b9787525e", size = 26971, upload-time = "2025-01-20T22:21:29.177Z" }, + { url = "https://files.pythonhosted.org/packages/20/b0/36bd937216ec521246249be3bf9855081de4c5e06a0c9b4219dbeda50373/importlib_metadata-8.7.0-py3-none-any.whl", hash = "sha256:e5dd1551894c77868a30651cef00984d50e1002d06942a7101d34870c5f02afd", size = 27656, upload-time = "2025-04-27T15:29:00.214Z" }, ] [[package]] @@ -884,25 +937,38 @@ wheels = [ [[package]] name = "jiter" -version = "0.9.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1e/c2/e4562507f52f0af7036da125bb699602ead37a2332af0788f8e0a3417f36/jiter-0.9.0.tar.gz", hash = "sha256:aadba0964deb424daa24492abc3d229c60c4a31bfee205aedbf1acc7639d7893", size = 162604, upload-time = "2025-03-10T21:37:03.278Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e7/1b/4cd165c362e8f2f520fdb43245e2b414f42a255921248b4f8b9c8d871ff1/jiter-0.9.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:2764891d3f3e8b18dce2cff24949153ee30c9239da7c00f032511091ba688ff7", size = 308197, upload-time = "2025-03-10T21:36:03.828Z" }, - { url = "https://files.pythonhosted.org/packages/13/aa/7a890dfe29c84c9a82064a9fe36079c7c0309c91b70c380dc138f9bea44a/jiter-0.9.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:387b22fbfd7a62418d5212b4638026d01723761c75c1c8232a8b8c37c2f1003b", size = 318160, upload-time = "2025-03-10T21:36:05.281Z" }, - { url = "https://files.pythonhosted.org/packages/6a/38/5888b43fc01102f733f085673c4f0be5a298f69808ec63de55051754e390/jiter-0.9.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40d8da8629ccae3606c61d9184970423655fb4e33d03330bcdfe52d234d32f69", size = 341259, upload-time = "2025-03-10T21:36:06.716Z" }, - { url = "https://files.pythonhosted.org/packages/3d/5e/bbdbb63305bcc01006de683b6228cd061458b9b7bb9b8d9bc348a58e5dc2/jiter-0.9.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a1be73d8982bdc278b7b9377426a4b44ceb5c7952073dd7488e4ae96b88e1103", size = 363730, upload-time = "2025-03-10T21:36:08.138Z" }, - { url = "https://files.pythonhosted.org/packages/75/85/53a3edc616992fe4af6814c25f91ee3b1e22f7678e979b6ea82d3bc0667e/jiter-0.9.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2228eaaaa111ec54b9e89f7481bffb3972e9059301a878d085b2b449fbbde635", size = 405126, upload-time = "2025-03-10T21:36:10.934Z" }, - { url = "https://files.pythonhosted.org/packages/ae/b3/1ee26b12b2693bd3f0b71d3188e4e5d817b12e3c630a09e099e0a89e28fa/jiter-0.9.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:11509bfecbc319459647d4ac3fd391d26fdf530dad00c13c4dadabf5b81f01a4", size = 393668, upload-time = "2025-03-10T21:36:12.468Z" }, - { url = "https://files.pythonhosted.org/packages/11/87/e084ce261950c1861773ab534d49127d1517b629478304d328493f980791/jiter-0.9.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f22238da568be8bbd8e0650e12feeb2cfea15eda4f9fc271d3b362a4fa0604d", size = 352350, upload-time = "2025-03-10T21:36:14.148Z" }, - { url = "https://files.pythonhosted.org/packages/f0/06/7dca84b04987e9df563610aa0bc154ea176e50358af532ab40ffb87434df/jiter-0.9.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:17f5d55eb856597607562257c8e36c42bc87f16bef52ef7129b7da11afc779f3", size = 384204, upload-time = "2025-03-10T21:36:15.545Z" }, - { url = "https://files.pythonhosted.org/packages/16/2f/82e1c6020db72f397dd070eec0c85ebc4df7c88967bc86d3ce9864148f28/jiter-0.9.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:6a99bed9fbb02f5bed416d137944419a69aa4c423e44189bc49718859ea83bc5", size = 520322, upload-time = "2025-03-10T21:36:17.016Z" }, - { url = "https://files.pythonhosted.org/packages/36/fd/4f0cd3abe83ce208991ca61e7e5df915aa35b67f1c0633eb7cf2f2e88ec7/jiter-0.9.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:e057adb0cd1bd39606100be0eafe742de2de88c79df632955b9ab53a086b3c8d", size = 512184, upload-time = "2025-03-10T21:36:18.47Z" }, - { url = "https://files.pythonhosted.org/packages/a0/3c/8a56f6d547731a0b4410a2d9d16bf39c861046f91f57c98f7cab3d2aa9ce/jiter-0.9.0-cp313-cp313-win32.whl", hash = "sha256:f7e6850991f3940f62d387ccfa54d1a92bd4bb9f89690b53aea36b4364bcab53", size = 206504, upload-time = "2025-03-10T21:36:19.809Z" }, - { url = "https://files.pythonhosted.org/packages/f4/1c/0c996fd90639acda75ed7fa698ee5fd7d80243057185dc2f63d4c1c9f6b9/jiter-0.9.0-cp313-cp313-win_amd64.whl", hash = "sha256:c8ae3bf27cd1ac5e6e8b7a27487bf3ab5f82318211ec2e1346a5b058756361f7", size = 204943, upload-time = "2025-03-10T21:36:21.536Z" }, - { url = "https://files.pythonhosted.org/packages/78/0f/77a63ca7aa5fed9a1b9135af57e190d905bcd3702b36aca46a01090d39ad/jiter-0.9.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:f0b2827fb88dda2cbecbbc3e596ef08d69bda06c6f57930aec8e79505dc17001", size = 317281, upload-time = "2025-03-10T21:36:22.959Z" }, - { url = "https://files.pythonhosted.org/packages/f9/39/a3a1571712c2bf6ec4c657f0d66da114a63a2e32b7e4eb8e0b83295ee034/jiter-0.9.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:062b756ceb1d40b0b28f326cba26cfd575a4918415b036464a52f08632731e5a", size = 350273, upload-time = "2025-03-10T21:36:24.414Z" }, - { url = "https://files.pythonhosted.org/packages/ee/47/3729f00f35a696e68da15d64eb9283c330e776f3b5789bac7f2c0c4df209/jiter-0.9.0-cp313-cp313t-win_amd64.whl", hash = "sha256:6f7838bc467ab7e8ef9f387bd6de195c43bad82a569c1699cb822f6609dd4cdf", size = 206867, upload-time = "2025-03-10T21:36:25.843Z" }, +version = "0.10.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/9d/ae7ddb4b8ab3fb1b51faf4deb36cb48a4fbbd7cb36bad6a5fca4741306f7/jiter-0.10.0.tar.gz", hash = "sha256:07a7142c38aacc85194391108dc91b5b57093c978a9932bd86a36862759d9500", size = 162759, upload-time = "2025-05-18T19:04:59.73Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2e/b0/279597e7a270e8d22623fea6c5d4eeac328e7d95c236ed51a2b884c54f70/jiter-0.10.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:e0588107ec8e11b6f5ef0e0d656fb2803ac6cf94a96b2b9fc675c0e3ab5e8644", size = 311617, upload-time = "2025-05-18T19:04:02.078Z" }, + { url = "https://files.pythonhosted.org/packages/91/e3/0916334936f356d605f54cc164af4060e3e7094364add445a3bc79335d46/jiter-0.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cafc4628b616dc32530c20ee53d71589816cf385dd9449633e910d596b1f5c8a", size = 318947, upload-time = "2025-05-18T19:04:03.347Z" }, + { url = "https://files.pythonhosted.org/packages/6a/8e/fd94e8c02d0e94539b7d669a7ebbd2776e51f329bb2c84d4385e8063a2ad/jiter-0.10.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:520ef6d981172693786a49ff5b09eda72a42e539f14788124a07530f785c3ad6", size = 344618, upload-time = "2025-05-18T19:04:04.709Z" }, + { url = "https://files.pythonhosted.org/packages/6f/b0/f9f0a2ec42c6e9c2e61c327824687f1e2415b767e1089c1d9135f43816bd/jiter-0.10.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:554dedfd05937f8fc45d17ebdf298fe7e0c77458232bcb73d9fbbf4c6455f5b3", size = 368829, upload-time = "2025-05-18T19:04:06.912Z" }, + { url = "https://files.pythonhosted.org/packages/e8/57/5bbcd5331910595ad53b9fd0c610392ac68692176f05ae48d6ce5c852967/jiter-0.10.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5bc299da7789deacf95f64052d97f75c16d4fc8c4c214a22bf8d859a4288a1c2", size = 491034, upload-time = "2025-05-18T19:04:08.222Z" }, + { url = "https://files.pythonhosted.org/packages/9b/be/c393df00e6e6e9e623a73551774449f2f23b6ec6a502a3297aeeece2c65a/jiter-0.10.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5161e201172de298a8a1baad95eb85db4fb90e902353b1f6a41d64ea64644e25", size = 388529, upload-time = "2025-05-18T19:04:09.566Z" }, + { url = "https://files.pythonhosted.org/packages/42/3e/df2235c54d365434c7f150b986a6e35f41ebdc2f95acea3036d99613025d/jiter-0.10.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e2227db6ba93cb3e2bf67c87e594adde0609f146344e8207e8730364db27041", size = 350671, upload-time = "2025-05-18T19:04:10.98Z" }, + { url = "https://files.pythonhosted.org/packages/c6/77/71b0b24cbcc28f55ab4dbfe029f9a5b73aeadaba677843fc6dc9ed2b1d0a/jiter-0.10.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:15acb267ea5e2c64515574b06a8bf393fbfee6a50eb1673614aa45f4613c0cca", size = 390864, upload-time = "2025-05-18T19:04:12.722Z" }, + { url = "https://files.pythonhosted.org/packages/6a/d3/ef774b6969b9b6178e1d1e7a89a3bd37d241f3d3ec5f8deb37bbd203714a/jiter-0.10.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:901b92f2e2947dc6dfcb52fd624453862e16665ea909a08398dde19c0731b7f4", size = 522989, upload-time = "2025-05-18T19:04:14.261Z" }, + { url = "https://files.pythonhosted.org/packages/0c/41/9becdb1d8dd5d854142f45a9d71949ed7e87a8e312b0bede2de849388cb9/jiter-0.10.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:d0cb9a125d5a3ec971a094a845eadde2db0de85b33c9f13eb94a0c63d463879e", size = 513495, upload-time = "2025-05-18T19:04:15.603Z" }, + { url = "https://files.pythonhosted.org/packages/9c/36/3468e5a18238bdedae7c4d19461265b5e9b8e288d3f86cd89d00cbb48686/jiter-0.10.0-cp313-cp313-win32.whl", hash = "sha256:48a403277ad1ee208fb930bdf91745e4d2d6e47253eedc96e2559d1e6527006d", size = 211289, upload-time = "2025-05-18T19:04:17.541Z" }, + { url = "https://files.pythonhosted.org/packages/7e/07/1c96b623128bcb913706e294adb5f768fb7baf8db5e1338ce7b4ee8c78ef/jiter-0.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:75f9eb72ecb640619c29bf714e78c9c46c9c4eaafd644bf78577ede459f330d4", size = 205074, upload-time = "2025-05-18T19:04:19.21Z" }, + { url = "https://files.pythonhosted.org/packages/54/46/caa2c1342655f57d8f0f2519774c6d67132205909c65e9aa8255e1d7b4f4/jiter-0.10.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:28ed2a4c05a1f32ef0e1d24c2611330219fed727dae01789f4a335617634b1ca", size = 318225, upload-time = "2025-05-18T19:04:20.583Z" }, + { url = "https://files.pythonhosted.org/packages/43/84/c7d44c75767e18946219ba2d703a5a32ab37b0bc21886a97bc6062e4da42/jiter-0.10.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14a4c418b1ec86a195f1ca69da8b23e8926c752b685af665ce30777233dfe070", size = 350235, upload-time = "2025-05-18T19:04:22.363Z" }, + { url = "https://files.pythonhosted.org/packages/01/16/f5a0135ccd968b480daad0e6ab34b0c7c5ba3bc447e5088152696140dcb3/jiter-0.10.0-cp313-cp313t-win_amd64.whl", hash = "sha256:d7bfed2fe1fe0e4dda6ef682cee888ba444b21e7a6553e03252e4feb6cf0adca", size = 207278, upload-time = "2025-05-18T19:04:23.627Z" }, + { url = "https://files.pythonhosted.org/packages/1c/9b/1d646da42c3de6c2188fdaa15bce8ecb22b635904fc68be025e21249ba44/jiter-0.10.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:5e9251a5e83fab8d87799d3e1a46cb4b7f2919b895c6f4483629ed2446f66522", size = 310866, upload-time = "2025-05-18T19:04:24.891Z" }, + { url = "https://files.pythonhosted.org/packages/ad/0e/26538b158e8a7c7987e94e7aeb2999e2e82b1f9d2e1f6e9874ddf71ebda0/jiter-0.10.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:023aa0204126fe5b87ccbcd75c8a0d0261b9abdbbf46d55e7ae9f8e22424eeb8", size = 318772, upload-time = "2025-05-18T19:04:26.161Z" }, + { url = "https://files.pythonhosted.org/packages/7b/fb/d302893151caa1c2636d6574d213e4b34e31fd077af6050a9c5cbb42f6fb/jiter-0.10.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c189c4f1779c05f75fc17c0c1267594ed918996a231593a21a5ca5438445216", size = 344534, upload-time = "2025-05-18T19:04:27.495Z" }, + { url = "https://files.pythonhosted.org/packages/01/d8/5780b64a149d74e347c5128d82176eb1e3241b1391ac07935693466d6219/jiter-0.10.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:15720084d90d1098ca0229352607cd68256c76991f6b374af96f36920eae13c4", size = 369087, upload-time = "2025-05-18T19:04:28.896Z" }, + { url = "https://files.pythonhosted.org/packages/e8/5b/f235a1437445160e777544f3ade57544daf96ba7e96c1a5b24a6f7ac7004/jiter-0.10.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e4f2fb68e5f1cfee30e2b2a09549a00683e0fde4c6a2ab88c94072fc33cb7426", size = 490694, upload-time = "2025-05-18T19:04:30.183Z" }, + { url = "https://files.pythonhosted.org/packages/85/a9/9c3d4617caa2ff89cf61b41e83820c27ebb3f7b5fae8a72901e8cd6ff9be/jiter-0.10.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ce541693355fc6da424c08b7edf39a2895f58d6ea17d92cc2b168d20907dee12", size = 388992, upload-time = "2025-05-18T19:04:32.028Z" }, + { url = "https://files.pythonhosted.org/packages/68/b1/344fd14049ba5c94526540af7eb661871f9c54d5f5601ff41a959b9a0bbd/jiter-0.10.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31c50c40272e189d50006ad5c73883caabb73d4e9748a688b216e85a9a9ca3b9", size = 351723, upload-time = "2025-05-18T19:04:33.467Z" }, + { url = "https://files.pythonhosted.org/packages/41/89/4c0e345041186f82a31aee7b9d4219a910df672b9fef26f129f0cda07a29/jiter-0.10.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fa3402a2ff9815960e0372a47b75c76979d74402448509ccd49a275fa983ef8a", size = 392215, upload-time = "2025-05-18T19:04:34.827Z" }, + { url = "https://files.pythonhosted.org/packages/55/58/ee607863e18d3f895feb802154a2177d7e823a7103f000df182e0f718b38/jiter-0.10.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:1956f934dca32d7bb647ea21d06d93ca40868b505c228556d3373cbd255ce853", size = 522762, upload-time = "2025-05-18T19:04:36.19Z" }, + { url = "https://files.pythonhosted.org/packages/15/d0/9123fb41825490d16929e73c212de9a42913d68324a8ce3c8476cae7ac9d/jiter-0.10.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:fcedb049bdfc555e261d6f65a6abe1d5ad68825b7202ccb9692636c70fcced86", size = 513427, upload-time = "2025-05-18T19:04:37.544Z" }, + { url = "https://files.pythonhosted.org/packages/d8/b3/2bd02071c5a2430d0b70403a34411fc519c2f227da7b03da9ba6a956f931/jiter-0.10.0-cp314-cp314-win32.whl", hash = "sha256:ac509f7eccca54b2a29daeb516fb95b6f0bd0d0d8084efaf8ed5dfc7b9f0b357", size = 210127, upload-time = "2025-05-18T19:04:38.837Z" }, + { url = "https://files.pythonhosted.org/packages/03/0c/5fe86614ea050c3ecd728ab4035534387cd41e7c1855ef6c031f1ca93e3f/jiter-0.10.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5ed975b83a2b8639356151cef5c0d597c68376fc4922b45d0eb384ac058cfa00", size = 318527, upload-time = "2025-05-18T19:04:40.612Z" }, + { url = "https://files.pythonhosted.org/packages/b3/4a/4175a563579e884192ba6e81725fc0448b042024419be8d83aa8a80a3f44/jiter-0.10.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3aa96f2abba33dc77f79b4cf791840230375f9534e5fac927ccceb58c5e604a5", size = 354213, upload-time = "2025-05-18T19:04:41.894Z" }, ] [[package]] @@ -955,7 +1021,7 @@ wheels = [ [[package]] name = "langchain" -version = "0.3.22" +version = "0.3.25" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "langchain-core" }, @@ -966,14 +1032,14 @@ dependencies = [ { name = "requests" }, { name = "sqlalchemy" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e9/66/36ccbd6285b29473ada883b0e06fdc0973ca181431d6a0175e473160fbfb/langchain-0.3.22.tar.gz", hash = "sha256:fd7781ef02cac6f074f9c6a902236482c61976e21da96ab577874d4e5396eeda", size = 10225573, upload-time = "2025-03-31T12:38:08.521Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fc/f9/a256609096a9fc7a1b3a6300a97000091efabdf21555a97988f93d4d9258/langchain-0.3.25.tar.gz", hash = "sha256:a1d72aa39546a23db08492d7228464af35c9ee83379945535ceef877340d2a3a", size = 10225045, upload-time = "2025-05-02T18:39:04.353Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/36/0e/032de736a8f9b5b5fcfec77bd92831f9f2c8a8b5072289dd1e5cc95e6edc/langchain-0.3.22-py3-none-any.whl", hash = "sha256:2e7f71a1b0280eb70af9c332c7580f6162a97fb9d5e3e87e9d579ad167f50129", size = 1011714, upload-time = "2025-03-31T12:38:05.982Z" }, + { url = "https://files.pythonhosted.org/packages/ed/5c/5c0be747261e1f8129b875fa3bfea736bc5fe17652f9d5e15ca118571b6f/langchain-0.3.25-py3-none-any.whl", hash = "sha256:931f7d2d1eaf182f9f41c5e3272859cfe7f94fc1f7cef6b3e5a46024b4884c21", size = 1011008, upload-time = "2025-05-02T18:39:02.21Z" }, ] [[package]] name = "langchain-community" -version = "0.3.20" +version = "0.3.24" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiohttp" }, @@ -989,14 +1055,14 @@ dependencies = [ { name = "sqlalchemy" }, { name = "tenacity" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/86/bb/a07609679781199738934226bb2764c12541573bc4feeaf21e9f3ad5caf4/langchain_community-0.3.20.tar.gz", hash = "sha256:bd83b4f2f818338423439aff3b5be362e1d686342ffada0478cd34c6f5ef5969", size = 33221203, upload-time = "2025-03-18T22:07:34.81Z" } +sdist = { url = "https://files.pythonhosted.org/packages/01/f6/4892d1f1cf6d3e89da6ee6cfb0eb82b908c706c58bde7df28367ee76a93f/langchain_community-0.3.24.tar.gz", hash = "sha256:62d9e8cf9aadf35182ec3925f9ec1c8e5e84fb4f199f67a01aee496d289dc264", size = 33233643, upload-time = "2025-05-12T13:26:39.222Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ab/4b/2652cfd2baa482cb3cdbec1ccccae1674418b7576f21ba7724d8730de9db/langchain_community-0.3.20-py3-none-any.whl", hash = "sha256:ea3dbf37fbc21020eca8850627546f3c95a8770afc06c4142b40b9ba86b970f7", size = 2524455, upload-time = "2025-03-18T22:07:32.064Z" }, + { url = "https://files.pythonhosted.org/packages/d5/cb/582f22d74d69f4dbd41e98d361ee36922b79a245a9411383327bd4b63747/langchain_community-0.3.24-py3-none-any.whl", hash = "sha256:b6cdb376bf1c2f4d2503aca20f8f35f2d5b3d879c52848277f20ce1950e7afaf", size = 2528335, upload-time = "2025-05-12T13:26:37.375Z" }, ] [[package]] name = "langchain-core" -version = "0.3.49" +version = "0.3.64" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "jsonpatch" }, @@ -1007,14 +1073,14 @@ dependencies = [ { name = "tenacity" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/73/bd/db939ba59f28a4ac73fa64281e21f5011ce61fd694c03b88946a554d8442/langchain_core-0.3.49.tar.gz", hash = "sha256:d9dbff9bac0021463a986355c13864d6a68c41f8559dbbd399a68e1ebd9b04b9", size = 536469, upload-time = "2025-03-26T18:42:00.598Z" } +sdist = { url = "https://files.pythonhosted.org/packages/58/40/89a80157f495d4adc9e5e770171806e3231600647f4ca0e89bdf743702ff/langchain_core-0.3.64.tar.gz", hash = "sha256:71b51bf77003eb57e74b8fa2a84ac380e24aa7357f173b51645c5834b9fc0d62", size = 558483, upload-time = "2025-06-05T21:27:10.817Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/dd/35/27164f5f23517be8639b518130e6235293dae52c41988790e0b50dd7ba11/langchain_core-0.3.49-py3-none-any.whl", hash = "sha256:893ee42c9af13bf2a2d8c2ec15ba00a5c73cccde21a2bd005234ee0e78a2bdf8", size = 420102, upload-time = "2025-03-26T18:41:58.854Z" }, + { url = "https://files.pythonhosted.org/packages/c3/43/94b486eeb778443887e4eb76326e704ee0c6244f5fab6a46686e09968e9a/langchain_core-0.3.64-py3-none-any.whl", hash = "sha256:e844c425329d450cb3010001d86b61fd59a6a17691641109bae39322c85e27dd", size = 438113, upload-time = "2025-06-05T21:27:07.981Z" }, ] [[package]] name = "langchain-google-genai" -version = "2.1.2" +version = "2.1.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "filetype" }, @@ -1022,9 +1088,9 @@ dependencies = [ { name = "langchain-core" }, { name = "pydantic" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fc/32/aeaa30a23f495417d71a7b8d9f6a71a40500b9994424c57e89418d96fc52/langchain_google_genai-2.1.2.tar.gz", hash = "sha256:f605501b498288d32914f6f8c0b7c9cfa67432757f596dcb2dbbd8042e892963", size = 38091, upload-time = "2025-03-27T16:04:22.879Z" } +sdist = { url = "https://files.pythonhosted.org/packages/2a/a5/d9b8d5afdf4a33f13e7d973f2705891cd13cc1dfb578719c9861a8d8385b/langchain_google_genai-2.1.5.tar.gz", hash = "sha256:6e71375a7707667bdecc5a7d1c86438ec10f2c7bb6dc6e3f095f5b22523c4fc9", size = 40813, upload-time = "2025-05-28T13:49:09.574Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/59/82/2a5d3fe54df23d6471768b9558f9a73e1a712065e6c20a228aa3254092aa/langchain_google_genai-2.1.2-py3-none-any.whl", hash = "sha256:eb9c95d551ecc0216e5baef2f2e6ae1b60897e618f273356d31b680022a1a755", size = 42030, upload-time = "2025-03-27T16:04:21.601Z" }, + { url = "https://files.pythonhosted.org/packages/5e/70/0747358eca996f713f715e2bfc2d0805804f8f705af57381fbee91bb475a/langchain_google_genai-2.1.5-py3-none-any.whl", hash = "sha256:6c8ccaf33a41f83b1d08a2398edbf47a1eebea27a7ec6930f34a0c019f309253", size = 44788, upload-time = "2025-05-28T13:49:08.22Z" }, ] [[package]] @@ -1042,74 +1108,75 @@ wheels = [ [[package]] name = "langchain-text-splitters" -version = "0.3.7" +version = "0.3.8" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "langchain-core" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5a/e7/638b44a41e56c3e32cc90cab3622ac2e4c73645252485427d6b2742fcfa8/langchain_text_splitters-0.3.7.tar.gz", hash = "sha256:7dbf0fb98e10bb91792a1d33f540e2287f9cc1dc30ade45b7aedd2d5cd3dc70b", size = 42180, upload-time = "2025-03-18T19:15:42.664Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e7/ac/b4a25c5716bb0103b1515f1f52cc69ffb1035a5a225ee5afe3aed28bf57b/langchain_text_splitters-0.3.8.tar.gz", hash = "sha256:116d4b9f2a22dda357d0b79e30acf005c5518177971c66a9f1ab0edfdb0f912e", size = 42128, upload-time = "2025-04-04T14:03:51.521Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d3/85/b7a34b6d34bcc89a2252f5ffea30b94077ba3d7adf72e31b9e04e68c901a/langchain_text_splitters-0.3.7-py3-none-any.whl", hash = "sha256:31ba826013e3f563359d7c7f1e99b1cdb94897f665675ee505718c116e7e20ad", size = 32513, upload-time = "2025-03-18T19:15:41.79Z" }, + { url = "https://files.pythonhosted.org/packages/8b/a3/3696ff2444658053c01b6b7443e761f28bb71217d82bb89137a978c5f66f/langchain_text_splitters-0.3.8-py3-none-any.whl", hash = "sha256:e75cc0f4ae58dcf07d9f18776400cf8ade27fadd4ff6d264df6278bb302f6f02", size = 32440, upload-time = "2025-04-04T14:03:50.6Z" }, ] [[package]] name = "langgraph" -version = "0.3.31" +version = "0.4.8" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "langchain-core" }, { name = "langgraph-checkpoint" }, { name = "langgraph-prebuilt" }, { name = "langgraph-sdk" }, + { name = "pydantic" }, { name = "xxhash" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b5/49/f6814bc1b016fd8f403856e986a4a0b35fd9cc34671f2abf649753643194/langgraph-0.3.31.tar.gz", hash = "sha256:c76bbc8f26604929d6c2520d937b5602d5bb1dde2c0580398d015f7f6841f14c", size = 120379, upload-time = "2025-04-17T17:11:51.326Z" } +sdist = { url = "https://files.pythonhosted.org/packages/9b/53/03380b675fef3d00d2d270e530d1a8bfe4e6f27117016a478670c9c74469/langgraph-0.4.8.tar.gz", hash = "sha256:48445ac8a351b7bdc6dee94e2e6a597f8582e0516ebd9dea0fd0164ae01b915e", size = 453277, upload-time = "2025-06-02T23:26:16.979Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/53/0f/c4802c26da60b84af1ee4a1d3013378da2354220aa2d0e766d07c5db1de2/langgraph-0.3.31-py3-none-any.whl", hash = "sha256:f42a6850d03696f2a54d3c833db39aa201cfda78217846a5b8936ad95fe41e6c", size = 145197, upload-time = "2025-04-17T17:11:50.059Z" }, + { url = "https://files.pythonhosted.org/packages/17/8a/fe05ec63ee4c3889a8b89679a6bdd1be6087962818996f3b361da23a5529/langgraph-0.4.8-py3-none-any.whl", hash = "sha256:273b02782669a474ba55ef4296607ac3bac9e93639d37edc0d32d8cf1a41a45b", size = 152444, upload-time = "2025-06-02T23:26:15.107Z" }, ] [[package]] name = "langgraph-checkpoint" -version = "2.0.24" +version = "2.0.26" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "langchain-core" }, + { name = "langchain-core", marker = "python_full_version < '4.0'" }, { name = "ormsgpack" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/0d/df/bacef68562ba4c391ded751eecda8e579ec78a581506064cf625e0ebd93a/langgraph_checkpoint-2.0.24.tar.gz", hash = "sha256:9596dad332344e7e871257be464df8a07c2e9bac66143081b11b9422b0167e5b", size = 37328, upload-time = "2025-04-02T22:47:34.255Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c5/61/e2518ac9216a4e9f4efda3ac61595e3c9e9ac00833141c9688e8d56bd7eb/langgraph_checkpoint-2.0.26.tar.gz", hash = "sha256:2b800195532d5efb079db9754f037281225ae175f7a395523f4bf41223cbc9d6", size = 37874, upload-time = "2025-05-15T17:31:22.466Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/bc/60/30397e8fd2b7dead3754aa79d708caff9dbb371f30b4cd21802c60f6b921/langgraph_checkpoint-2.0.24-py3-none-any.whl", hash = "sha256:3836e2909ef2387d1fa8d04ee3e2a353f980d519fd6c649af352676dc73d66b8", size = 42028, upload-time = "2025-04-02T22:47:33.017Z" }, + { url = "https://files.pythonhosted.org/packages/38/48/d7cec540a3011b3207470bb07294a399e3b94b2e8a602e38cb007ce5bc10/langgraph_checkpoint-2.0.26-py3-none-any.whl", hash = "sha256:ad4907858ed320a208e14ac037e4b9244ec1cb5aa54570518166ae8b25752cec", size = 44247, upload-time = "2025-05-15T17:31:21.38Z" }, ] [[package]] name = "langgraph-prebuilt" -version = "0.1.8" +version = "0.2.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "langchain-core" }, { name = "langgraph-checkpoint" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/57/30/f31f0e076c37d097b53e4cff5d479a3686e1991f6c86a1a4727d5d1f5489/langgraph_prebuilt-0.1.8.tar.gz", hash = "sha256:4de7659151829b2b955b6798df6800e580e617782c15c2c5b29b139697491831", size = 24543, upload-time = "2025-04-03T16:04:19.932Z" } +sdist = { url = "https://files.pythonhosted.org/packages/88/f5/15b26cda94ebb89400048d478a3b1927005d85e273a557d8683f4cda775c/langgraph_prebuilt-0.2.2.tar.gz", hash = "sha256:0a5d1f651f97c848cd1c3dd0ef017614f47ee74effb7375b59ac639e41b253f9", size = 112785, upload-time = "2025-05-28T13:39:54.235Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/36/72/9e092665502f8f52f2708065ed14fbbba3f95d1a1b65d62049b0c5fcdf00/langgraph_prebuilt-0.1.8-py3-none-any.whl", hash = "sha256:ae97b828ae00be2cefec503423aa782e1bff165e9b94592e224da132f2526968", size = 25903, upload-time = "2025-04-03T16:04:18.993Z" }, + { url = "https://files.pythonhosted.org/packages/14/46/c98fec1f8620cbffbabda346a2c68155eec3720c6c3393ab3b9529618810/langgraph_prebuilt-0.2.2-py3-none-any.whl", hash = "sha256:72de5ef1d969a8f02ad7adc7cc1915bb9b4467912d57ba60da34b5a70fdad1f6", size = 23748, upload-time = "2025-05-28T13:39:53.361Z" }, ] [[package]] name = "langgraph-sdk" -version = "0.1.63" +version = "0.1.70" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "httpx" }, { name = "orjson" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/38/bc/23571ccda8bd442b7090e5f5263fbae439dcfbe7c06c2d51b55d464dfb69/langgraph_sdk-0.1.63.tar.gz", hash = "sha256:62bf2cc31e5aa6c5b9011ee1702bcf1e36e67e142a60bd97af2611162fb58e18", size = 43837, upload-time = "2025-04-22T00:52:23.283Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c1/dd/c074adf91d2fe67f00dc3be4348119f40a9d0ead9e55c958f81492c522c0/langgraph_sdk-0.1.70.tar.gz", hash = "sha256:cc65ec33bcdf8c7008d43da2d2b0bc1dd09f98d21a7f636828d9379535069cf9", size = 71530, upload-time = "2025-05-21T22:23:22.502Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5a/1f/f1462fef67ddca45b2e4dd1cfbb452aa4b6532edaf77b99f9588406229b0/langgraph_sdk-0.1.63-py3-none-any.whl", hash = "sha256:6fb78a7fc6a30eea43bd0d6401dbc9e3263d0d4c03f63c04035980da7e586b05", size = 47343, upload-time = "2025-04-22T00:52:21.788Z" }, + { url = "https://files.pythonhosted.org/packages/8c/77/b0930ca5d54ef91e2bdb37e0f7dbeda1923e1e0b5b71ab3af35c103c2e39/langgraph_sdk-0.1.70-py3-none-any.whl", hash = "sha256:47f2b04a964f40a610c1636b387ea52f961ce7a233afc21d3103e5faac8ca1e5", size = 49986, upload-time = "2025-05-21T22:23:21.377Z" }, ] [[package]] name = "langsmith" -version = "0.3.33" +version = "0.3.45" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "httpx" }, @@ -1120,14 +1187,14 @@ dependencies = [ { name = "requests-toolbelt" }, { name = "zstandard" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/3a/3f/7143f47d161951e7cfe535c45f88f6a3e663681f0aa7ee29cac8ff7c5681/langsmith-0.3.33.tar.gz", hash = "sha256:0f439e945528c6d14140137b918cc048aea04c6a987525926dbfda2560002924", size = 344144, upload-time = "2025-04-21T21:30:08.144Z" } +sdist = { url = "https://files.pythonhosted.org/packages/be/86/b941012013260f95af2e90a3d9415af4a76a003a28412033fc4b09f35731/langsmith-0.3.45.tar.gz", hash = "sha256:1df3c6820c73ed210b2c7bc5cdb7bfa19ddc9126cd03fdf0da54e2e171e6094d", size = 348201, upload-time = "2025-06-05T05:10:28.948Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/bb/8c/5846d3896fd98670ea2f1408375e445f1315f9e250b6884df500d0906112/langsmith-0.3.33-py3-none-any.whl", hash = "sha256:6fa453942014945e1de7e283880ed3e8031b5d84e0dc75b87d101ecedb62371b", size = 358793, upload-time = "2025-04-21T21:30:05.914Z" }, + { url = "https://files.pythonhosted.org/packages/6a/f4/c206c0888f8a506404cb4f16ad89593bdc2f70cf00de26a1a0a7a76ad7a3/langsmith-0.3.45-py3-none-any.whl", hash = "sha256:5b55f0518601fa65f3bb6b1a3100379a96aa7b3ed5e9380581615ba9c65ed8ed", size = 363002, upload-time = "2025-06-05T05:10:27.228Z" }, ] [[package]] name = "logfire" -version = "3.14.0" +version = "3.18.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "executing" }, @@ -1138,18 +1205,18 @@ dependencies = [ { name = "rich" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9d/5b/ca57ad7a9e78fc9f7779bb2da8a3776233b832d211eaf49cea2582fe3f77/logfire-3.14.0.tar.gz", hash = "sha256:afdd23386a8a57da7ab97938cc5eec17928ce9195907b85860d906f04c5d33e3", size = 473733, upload-time = "2025-04-11T16:08:14.803Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c7/52/b39eb4b8f4f9d5066c5b91c4ad6aab4e71e69d87a69b9b55ed05376accb9/logfire-3.18.0.tar.gz", hash = "sha256:86efe521e8d7161edb66c8f4839efcd46e3ef4b17f641b96bf05111807df4bd5", size = 486165, upload-time = "2025-06-05T14:30:32.941Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7f/13/e06647ca3d7fb9167dd82260cc0e978ffcae88aa396025cea3d86a04875d/logfire-3.14.0-py3-none-any.whl", hash = "sha256:4f95cf98a7c29cd7cd00e093ba75ce1e4e19e5069acda8b1577a4b7790e0237a", size = 193168, upload-time = "2025-04-11T16:08:11.426Z" }, + { url = "https://files.pythonhosted.org/packages/4d/38/1b26834130d5aef342ba828ea3c794696e3637aa8a2bd3288aa100e3a73e/logfire-3.18.0-py3-none-any.whl", hash = "sha256:da1a4d15679c19391f423ed76c9a7b730a00bdc639cfdc09ef5b4c05b7cb6b6d", size = 197653, upload-time = "2025-06-05T14:30:29.515Z" }, ] [[package]] name = "logfire-api" -version = "3.14.0" +version = "3.18.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cd/31/b0e0cb016bb32989932cebd38e2e26800764cdea0f03fcb4fcda72680e58/logfire_api-3.14.0.tar.gz", hash = "sha256:70d5bcf075a50e89ecf8cdabe6220e3b00978fa5c0bb56cb9c75d8619107df49", size = 47978, upload-time = "2025-04-11T16:08:16.399Z" } +sdist = { url = "https://files.pythonhosted.org/packages/16/b5/0893c970673c0a3ca422edd2488db0e0b9f1126ad036efe79f1f833ea3f5/logfire_api-3.18.0.tar.gz", hash = "sha256:87dbae56c8d50560a20ce67a9ef08ee208506fa0dd2c0c3c6b3d709dde1d9795", size = 48618, upload-time = "2025-06-05T14:30:34.873Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5b/3e/6dbe2d1051d7463eb3a8e492e42f464c1eef501b355a5bb25876ca68da28/logfire_api-3.14.0-py3-none-any.whl", hash = "sha256:e01f9049bca809cc102eb7550c4263fe560fa26abd68688e6dc2b8666e506a57", size = 79475, upload-time = "2025-04-11T16:08:13.134Z" }, + { url = "https://files.pythonhosted.org/packages/95/10/35f0e4811c55264e7c45200a90807877f755c955a44e0f3e9a0e7e081b9a/logfire_api-3.18.0-py3-none-any.whl", hash = "sha256:9c23ac20dbaecf95a5aee87944839543f02ecc6f89369bbd23bb0b2ce4e52ed7", size = 80869, upload-time = "2025-06-05T14:30:31.612Z" }, ] [[package]] @@ -1248,96 +1315,98 @@ wheels = [ [[package]] name = "multidict" -version = "6.4.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/da/2c/e367dfb4c6538614a0c9453e510d75d66099edf1c4e69da1b5ce691a1931/multidict-6.4.3.tar.gz", hash = "sha256:3ada0b058c9f213c5f95ba301f922d402ac234f1111a7d8fd70f1b99f3c281ec", size = 89372, upload-time = "2025-04-10T22:20:17.956Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6c/4b/86fd786d03915c6f49998cf10cd5fe6b6ac9e9a071cb40885d2e080fb90d/multidict-6.4.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:7a76534263d03ae0cfa721fea40fd2b5b9d17a6f85e98025931d41dc49504474", size = 63831, upload-time = "2025-04-10T22:18:48.748Z" }, - { url = "https://files.pythonhosted.org/packages/45/05/9b51fdf7aef2563340a93be0a663acba2c428c4daeaf3960d92d53a4a930/multidict-6.4.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:805031c2f599eee62ac579843555ed1ce389ae00c7e9f74c2a1b45e0564a88dd", size = 37888, upload-time = "2025-04-10T22:18:50.021Z" }, - { url = "https://files.pythonhosted.org/packages/0b/43/53fc25394386c911822419b522181227ca450cf57fea76e6188772a1bd91/multidict-6.4.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c56c179839d5dcf51d565132185409d1d5dd8e614ba501eb79023a6cab25576b", size = 36852, upload-time = "2025-04-10T22:18:51.246Z" }, - { url = "https://files.pythonhosted.org/packages/8a/68/7b99c751e822467c94a235b810a2fd4047d4ecb91caef6b5c60116991c4b/multidict-6.4.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c64f4ddb3886dd8ab71b68a7431ad4aa01a8fa5be5b11543b29674f29ca0ba3", size = 223644, upload-time = "2025-04-10T22:18:52.965Z" }, - { url = "https://files.pythonhosted.org/packages/80/1b/d458d791e4dd0f7e92596667784fbf99e5c8ba040affe1ca04f06b93ae92/multidict-6.4.3-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3002a856367c0b41cad6784f5b8d3ab008eda194ed7864aaa58f65312e2abcac", size = 230446, upload-time = "2025-04-10T22:18:54.509Z" }, - { url = "https://files.pythonhosted.org/packages/e2/46/9793378d988905491a7806d8987862dc5a0bae8a622dd896c4008c7b226b/multidict-6.4.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3d75e621e7d887d539d6e1d789f0c64271c250276c333480a9e1de089611f790", size = 231070, upload-time = "2025-04-10T22:18:56.019Z" }, - { url = "https://files.pythonhosted.org/packages/a7/b8/b127d3e1f8dd2a5bf286b47b24567ae6363017292dc6dec44656e6246498/multidict-6.4.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:995015cf4a3c0d72cbf453b10a999b92c5629eaf3a0c3e1efb4b5c1f602253bb", size = 229956, upload-time = "2025-04-10T22:18:59.146Z" }, - { url = "https://files.pythonhosted.org/packages/0c/93/f70a4c35b103fcfe1443059a2bb7f66e5c35f2aea7804105ff214f566009/multidict-6.4.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a2b0fabae7939d09d7d16a711468c385272fa1b9b7fb0d37e51143585d8e72e0", size = 222599, upload-time = "2025-04-10T22:19:00.657Z" }, - { url = "https://files.pythonhosted.org/packages/63/8c/e28e0eb2fe34921d6aa32bfc4ac75b09570b4d6818cc95d25499fe08dc1d/multidict-6.4.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:61ed4d82f8a1e67eb9eb04f8587970d78fe7cddb4e4d6230b77eda23d27938f9", size = 216136, upload-time = "2025-04-10T22:19:02.244Z" }, - { url = "https://files.pythonhosted.org/packages/72/f5/fbc81f866585b05f89f99d108be5d6ad170e3b6c4d0723d1a2f6ba5fa918/multidict-6.4.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:062428944a8dc69df9fdc5d5fc6279421e5f9c75a9ee3f586f274ba7b05ab3c8", size = 228139, upload-time = "2025-04-10T22:19:04.151Z" }, - { url = "https://files.pythonhosted.org/packages/bb/ba/7d196bad6b85af2307d81f6979c36ed9665f49626f66d883d6c64d156f78/multidict-6.4.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:b90e27b4674e6c405ad6c64e515a505c6d113b832df52fdacb6b1ffd1fa9a1d1", size = 226251, upload-time = "2025-04-10T22:19:06.117Z" }, - { url = "https://files.pythonhosted.org/packages/cc/e2/fae46a370dce79d08b672422a33df721ec8b80105e0ea8d87215ff6b090d/multidict-6.4.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:7d50d4abf6729921e9613d98344b74241572b751c6b37feed75fb0c37bd5a817", size = 221868, upload-time = "2025-04-10T22:19:07.981Z" }, - { url = "https://files.pythonhosted.org/packages/26/20/bbc9a3dec19d5492f54a167f08546656e7aef75d181d3d82541463450e88/multidict-6.4.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:43fe10524fb0a0514be3954be53258e61d87341008ce4914f8e8b92bee6f875d", size = 233106, upload-time = "2025-04-10T22:19:09.5Z" }, - { url = "https://files.pythonhosted.org/packages/ee/8d/f30ae8f5ff7a2461177f4d8eb0d8f69f27fb6cfe276b54ec4fd5a282d918/multidict-6.4.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:236966ca6c472ea4e2d3f02f6673ebfd36ba3f23159c323f5a496869bc8e47c9", size = 230163, upload-time = "2025-04-10T22:19:11Z" }, - { url = "https://files.pythonhosted.org/packages/15/e9/2833f3c218d3c2179f3093f766940ded6b81a49d2e2f9c46ab240d23dfec/multidict-6.4.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:422a5ec315018e606473ba1f5431e064cf8b2a7468019233dcf8082fabad64c8", size = 225906, upload-time = "2025-04-10T22:19:12.875Z" }, - { url = "https://files.pythonhosted.org/packages/f1/31/6edab296ac369fd286b845fa5dd4c409e63bc4655ed8c9510fcb477e9ae9/multidict-6.4.3-cp313-cp313-win32.whl", hash = "sha256:f901a5aace8e8c25d78960dcc24c870c8d356660d3b49b93a78bf38eb682aac3", size = 35238, upload-time = "2025-04-10T22:19:14.41Z" }, - { url = "https://files.pythonhosted.org/packages/23/57/2c0167a1bffa30d9a1383c3dab99d8caae985defc8636934b5668830d2ef/multidict-6.4.3-cp313-cp313-win_amd64.whl", hash = "sha256:1c152c49e42277bc9a2f7b78bd5fa10b13e88d1b0328221e7aef89d5c60a99a5", size = 38799, upload-time = "2025-04-10T22:19:15.869Z" }, - { url = "https://files.pythonhosted.org/packages/c9/13/2ead63b9ab0d2b3080819268acb297bd66e238070aa8d42af12b08cbee1c/multidict-6.4.3-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:be8751869e28b9c0d368d94f5afcb4234db66fe8496144547b4b6d6a0645cfc6", size = 68642, upload-time = "2025-04-10T22:19:17.527Z" }, - { url = "https://files.pythonhosted.org/packages/85/45/f1a751e1eede30c23951e2ae274ce8fad738e8a3d5714be73e0a41b27b16/multidict-6.4.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0d4b31f8a68dccbcd2c0ea04f0e014f1defc6b78f0eb8b35f2265e8716a6df0c", size = 40028, upload-time = "2025-04-10T22:19:19.465Z" }, - { url = "https://files.pythonhosted.org/packages/a7/29/fcc53e886a2cc5595cc4560df333cb9630257bda65003a7eb4e4e0d8f9c1/multidict-6.4.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:032efeab3049e37eef2ff91271884303becc9e54d740b492a93b7e7266e23756", size = 39424, upload-time = "2025-04-10T22:19:20.762Z" }, - { url = "https://files.pythonhosted.org/packages/f6/f0/056c81119d8b88703971f937b371795cab1407cd3c751482de5bfe1a04a9/multidict-6.4.3-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9e78006af1a7c8a8007e4f56629d7252668344442f66982368ac06522445e375", size = 226178, upload-time = "2025-04-10T22:19:22.17Z" }, - { url = "https://files.pythonhosted.org/packages/a3/79/3b7e5fea0aa80583d3a69c9d98b7913dfd4fbc341fb10bb2fb48d35a9c21/multidict-6.4.3-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:daeac9dd30cda8703c417e4fddccd7c4dc0c73421a0b54a7da2713be125846be", size = 222617, upload-time = "2025-04-10T22:19:23.773Z" }, - { url = "https://files.pythonhosted.org/packages/06/db/3ed012b163e376fc461e1d6a67de69b408339bc31dc83d39ae9ec3bf9578/multidict-6.4.3-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1f6f90700881438953eae443a9c6f8a509808bc3b185246992c4233ccee37fea", size = 227919, upload-time = "2025-04-10T22:19:25.35Z" }, - { url = "https://files.pythonhosted.org/packages/b1/db/0433c104bca380989bc04d3b841fc83e95ce0c89f680e9ea4251118b52b6/multidict-6.4.3-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f84627997008390dd15762128dcf73c3365f4ec0106739cde6c20a07ed198ec8", size = 226097, upload-time = "2025-04-10T22:19:27.183Z" }, - { url = "https://files.pythonhosted.org/packages/c2/95/910db2618175724dd254b7ae635b6cd8d2947a8b76b0376de7b96d814dab/multidict-6.4.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3307b48cd156153b117c0ea54890a3bdbf858a5b296ddd40dc3852e5f16e9b02", size = 220706, upload-time = "2025-04-10T22:19:28.882Z" }, - { url = "https://files.pythonhosted.org/packages/d1/af/aa176c6f5f1d901aac957d5258d5e22897fe13948d1e69063ae3d5d0ca01/multidict-6.4.3-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ead46b0fa1dcf5af503a46e9f1c2e80b5d95c6011526352fa5f42ea201526124", size = 211728, upload-time = "2025-04-10T22:19:30.481Z" }, - { url = "https://files.pythonhosted.org/packages/e7/42/d51cc5fc1527c3717d7f85137d6c79bb7a93cd214c26f1fc57523774dbb5/multidict-6.4.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:1748cb2743bedc339d63eb1bca314061568793acd603a6e37b09a326334c9f44", size = 226276, upload-time = "2025-04-10T22:19:32.454Z" }, - { url = "https://files.pythonhosted.org/packages/28/6b/d836dea45e0b8432343ba4acf9a8ecaa245da4c0960fb7ab45088a5e568a/multidict-6.4.3-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:acc9fa606f76fc111b4569348cc23a771cb52c61516dcc6bcef46d612edb483b", size = 212069, upload-time = "2025-04-10T22:19:34.17Z" }, - { url = "https://files.pythonhosted.org/packages/55/34/0ee1a7adb3560e18ee9289c6e5f7db54edc312b13e5c8263e88ea373d12c/multidict-6.4.3-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:31469d5832b5885adeb70982e531ce86f8c992334edd2f2254a10fa3182ac504", size = 217858, upload-time = "2025-04-10T22:19:35.879Z" }, - { url = "https://files.pythonhosted.org/packages/04/08/586d652c2f5acefe0cf4e658eedb4d71d4ba6dfd4f189bd81b400fc1bc6b/multidict-6.4.3-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:ba46b51b6e51b4ef7bfb84b82f5db0dc5e300fb222a8a13b8cd4111898a869cf", size = 226988, upload-time = "2025-04-10T22:19:37.434Z" }, - { url = "https://files.pythonhosted.org/packages/82/e3/cc59c7e2bc49d7f906fb4ffb6d9c3a3cf21b9f2dd9c96d05bef89c2b1fd1/multidict-6.4.3-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:389cfefb599edf3fcfd5f64c0410da686f90f5f5e2c4d84e14f6797a5a337af4", size = 220435, upload-time = "2025-04-10T22:19:39.005Z" }, - { url = "https://files.pythonhosted.org/packages/e0/32/5c3a556118aca9981d883f38c4b1bfae646f3627157f70f4068e5a648955/multidict-6.4.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:64bc2bbc5fba7b9db5c2c8d750824f41c6994e3882e6d73c903c2afa78d091e4", size = 221494, upload-time = "2025-04-10T22:19:41.447Z" }, - { url = "https://files.pythonhosted.org/packages/b9/3b/1599631f59024b75c4d6e3069f4502409970a336647502aaf6b62fb7ac98/multidict-6.4.3-cp313-cp313t-win32.whl", hash = "sha256:0ecdc12ea44bab2807d6b4a7e5eef25109ab1c82a8240d86d3c1fc9f3b72efd5", size = 41775, upload-time = "2025-04-10T22:19:43.707Z" }, - { url = "https://files.pythonhosted.org/packages/e8/4e/09301668d675d02ca8e8e1a3e6be046619e30403f5ada2ed5b080ae28d02/multidict-6.4.3-cp313-cp313t-win_amd64.whl", hash = "sha256:7146a8742ea71b5d7d955bffcef58a9e6e04efba704b52a460134fefd10a8208", size = 45946, upload-time = "2025-04-10T22:19:45.071Z" }, - { url = "https://files.pythonhosted.org/packages/96/10/7d526c8974f017f1e7ca584c71ee62a638e9334d8d33f27d7cdfc9ae79e4/multidict-6.4.3-py3-none-any.whl", hash = "sha256:59fe01ee8e2a1e8ceb3f6dbb216b09c8d9f4ef1c22c4fc825d045a147fa2ebc9", size = 10400, upload-time = "2025-04-10T22:20:16.445Z" }, +version = "6.4.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/91/2f/a3470242707058fe856fe59241eee5635d79087100b7042a867368863a27/multidict-6.4.4.tar.gz", hash = "sha256:69ee9e6ba214b5245031b76233dd95408a0fd57fdb019ddcc1ead4790932a8e8", size = 90183, upload-time = "2025-05-19T14:16:37.381Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/df/2a/e166d2ffbf4b10131b2d5b0e458f7cee7d986661caceae0de8753042d4b2/multidict-6.4.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:82ffabefc8d84c2742ad19c37f02cde5ec2a1ee172d19944d380f920a340e4b9", size = 64123, upload-time = "2025-05-19T14:15:11.044Z" }, + { url = "https://files.pythonhosted.org/packages/8c/96/e200e379ae5b6f95cbae472e0199ea98913f03d8c9a709f42612a432932c/multidict-6.4.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:6a2f58a66fe2c22615ad26156354005391e26a2f3721c3621504cd87c1ea87bf", size = 38049, upload-time = "2025-05-19T14:15:12.902Z" }, + { url = "https://files.pythonhosted.org/packages/75/fb/47afd17b83f6a8c7fa863c6d23ac5ba6a0e6145ed8a6bcc8da20b2b2c1d2/multidict-6.4.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5883d6ee0fd9d8a48e9174df47540b7545909841ac82354c7ae4cbe9952603bd", size = 37078, upload-time = "2025-05-19T14:15:14.282Z" }, + { url = "https://files.pythonhosted.org/packages/fa/70/1af3143000eddfb19fd5ca5e78393985ed988ac493bb859800fe0914041f/multidict-6.4.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9abcf56a9511653fa1d052bfc55fbe53dbee8f34e68bd6a5a038731b0ca42d15", size = 224097, upload-time = "2025-05-19T14:15:15.566Z" }, + { url = "https://files.pythonhosted.org/packages/b1/39/d570c62b53d4fba844e0378ffbcd02ac25ca423d3235047013ba2f6f60f8/multidict-6.4.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6ed5ae5605d4ad5a049fad2a28bb7193400700ce2f4ae484ab702d1e3749c3f9", size = 230768, upload-time = "2025-05-19T14:15:17.308Z" }, + { url = "https://files.pythonhosted.org/packages/fd/f8/ed88f2c4d06f752b015933055eb291d9bc184936903752c66f68fb3c95a7/multidict-6.4.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bbfcb60396f9bcfa63e017a180c3105b8c123a63e9d1428a36544e7d37ca9e20", size = 231331, upload-time = "2025-05-19T14:15:18.73Z" }, + { url = "https://files.pythonhosted.org/packages/9c/6f/8e07cffa32f483ab887b0d56bbd8747ac2c1acd00dc0af6fcf265f4a121e/multidict-6.4.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b0f1987787f5f1e2076b59692352ab29a955b09ccc433c1f6b8e8e18666f608b", size = 230169, upload-time = "2025-05-19T14:15:20.179Z" }, + { url = "https://files.pythonhosted.org/packages/e6/2b/5dcf173be15e42f330110875a2668ddfc208afc4229097312212dc9c1236/multidict-6.4.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1d0121ccce8c812047d8d43d691a1ad7641f72c4f730474878a5aeae1b8ead8c", size = 222947, upload-time = "2025-05-19T14:15:21.714Z" }, + { url = "https://files.pythonhosted.org/packages/39/75/4ddcbcebe5ebcd6faa770b629260d15840a5fc07ce8ad295a32e14993726/multidict-6.4.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:83ec4967114295b8afd120a8eec579920c882831a3e4c3331d591a8e5bfbbc0f", size = 215761, upload-time = "2025-05-19T14:15:23.242Z" }, + { url = "https://files.pythonhosted.org/packages/6a/c9/55e998ae45ff15c5608e384206aa71a11e1b7f48b64d166db400b14a3433/multidict-6.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:995f985e2e268deaf17867801b859a282e0448633f1310e3704b30616d269d69", size = 227605, upload-time = "2025-05-19T14:15:24.763Z" }, + { url = "https://files.pythonhosted.org/packages/04/49/c2404eac74497503c77071bd2e6f88c7e94092b8a07601536b8dbe99be50/multidict-6.4.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:d832c608f94b9f92a0ec8b7e949be7792a642b6e535fcf32f3e28fab69eeb046", size = 226144, upload-time = "2025-05-19T14:15:26.249Z" }, + { url = "https://files.pythonhosted.org/packages/62/c5/0cd0c3c6f18864c40846aa2252cd69d308699cb163e1c0d989ca301684da/multidict-6.4.4-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d21c1212171cf7da703c5b0b7a0e85be23b720818aef502ad187d627316d5645", size = 221100, upload-time = "2025-05-19T14:15:28.303Z" }, + { url = "https://files.pythonhosted.org/packages/71/7b/f2f3887bea71739a046d601ef10e689528d4f911d84da873b6be9194ffea/multidict-6.4.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:cbebaa076aaecad3d4bb4c008ecc73b09274c952cf6a1b78ccfd689e51f5a5b0", size = 232731, upload-time = "2025-05-19T14:15:30.263Z" }, + { url = "https://files.pythonhosted.org/packages/e5/b3/d9de808349df97fa75ec1372758701b5800ebad3c46ae377ad63058fbcc6/multidict-6.4.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:c93a6fb06cc8e5d3628b2b5fda215a5db01e8f08fc15fadd65662d9b857acbe4", size = 229637, upload-time = "2025-05-19T14:15:33.337Z" }, + { url = "https://files.pythonhosted.org/packages/5e/57/13207c16b615eb4f1745b44806a96026ef8e1b694008a58226c2d8f5f0a5/multidict-6.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8cd8f81f1310182362fb0c7898145ea9c9b08a71081c5963b40ee3e3cac589b1", size = 225594, upload-time = "2025-05-19T14:15:34.832Z" }, + { url = "https://files.pythonhosted.org/packages/3a/e4/d23bec2f70221604f5565000632c305fc8f25ba953e8ce2d8a18842b9841/multidict-6.4.4-cp313-cp313-win32.whl", hash = "sha256:3e9f1cd61a0ab857154205fb0b1f3d3ace88d27ebd1409ab7af5096e409614cd", size = 35359, upload-time = "2025-05-19T14:15:36.246Z" }, + { url = "https://files.pythonhosted.org/packages/a7/7a/cfe1a47632be861b627f46f642c1d031704cc1c0f5c0efbde2ad44aa34bd/multidict-6.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:8ffb40b74400e4455785c2fa37eba434269149ec525fc8329858c862e4b35373", size = 38903, upload-time = "2025-05-19T14:15:37.507Z" }, + { url = "https://files.pythonhosted.org/packages/68/7b/15c259b0ab49938a0a1c8f3188572802704a779ddb294edc1b2a72252e7c/multidict-6.4.4-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:6a602151dbf177be2450ef38966f4be3467d41a86c6a845070d12e17c858a156", size = 68895, upload-time = "2025-05-19T14:15:38.856Z" }, + { url = "https://files.pythonhosted.org/packages/f1/7d/168b5b822bccd88142e0a3ce985858fea612404edd228698f5af691020c9/multidict-6.4.4-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0d2b9712211b860d123815a80b859075d86a4d54787e247d7fbee9db6832cf1c", size = 40183, upload-time = "2025-05-19T14:15:40.197Z" }, + { url = "https://files.pythonhosted.org/packages/e0/b7/d4b8d98eb850ef28a4922ba508c31d90715fd9b9da3801a30cea2967130b/multidict-6.4.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:d2fa86af59f8fc1972e121ade052145f6da22758f6996a197d69bb52f8204e7e", size = 39592, upload-time = "2025-05-19T14:15:41.508Z" }, + { url = "https://files.pythonhosted.org/packages/18/28/a554678898a19583548e742080cf55d169733baf57efc48c2f0273a08583/multidict-6.4.4-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50855d03e9e4d66eab6947ba688ffb714616f985838077bc4b490e769e48da51", size = 226071, upload-time = "2025-05-19T14:15:42.877Z" }, + { url = "https://files.pythonhosted.org/packages/ee/dc/7ba6c789d05c310e294f85329efac1bf5b450338d2542498db1491a264df/multidict-6.4.4-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:5bce06b83be23225be1905dcdb6b789064fae92499fbc458f59a8c0e68718601", size = 222597, upload-time = "2025-05-19T14:15:44.412Z" }, + { url = "https://files.pythonhosted.org/packages/24/4f/34eadbbf401b03768dba439be0fb94b0d187facae9142821a3d5599ccb3b/multidict-6.4.4-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:66ed0731f8e5dfd8369a883b6e564aca085fb9289aacabd9decd70568b9a30de", size = 228253, upload-time = "2025-05-19T14:15:46.474Z" }, + { url = "https://files.pythonhosted.org/packages/c0/e6/493225a3cdb0d8d80d43a94503fc313536a07dae54a3f030d279e629a2bc/multidict-6.4.4-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:329ae97fc2f56f44d91bc47fe0972b1f52d21c4b7a2ac97040da02577e2daca2", size = 226146, upload-time = "2025-05-19T14:15:48.003Z" }, + { url = "https://files.pythonhosted.org/packages/2f/70/e411a7254dc3bff6f7e6e004303b1b0591358e9f0b7c08639941e0de8bd6/multidict-6.4.4-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c27e5dcf520923d6474d98b96749e6805f7677e93aaaf62656005b8643f907ab", size = 220585, upload-time = "2025-05-19T14:15:49.546Z" }, + { url = "https://files.pythonhosted.org/packages/08/8f/beb3ae7406a619100d2b1fb0022c3bb55a8225ab53c5663648ba50dfcd56/multidict-6.4.4-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:058cc59b9e9b143cc56715e59e22941a5d868c322242278d28123a5d09cdf6b0", size = 212080, upload-time = "2025-05-19T14:15:51.151Z" }, + { url = "https://files.pythonhosted.org/packages/9c/ec/355124e9d3d01cf8edb072fd14947220f357e1c5bc79c88dff89297e9342/multidict-6.4.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:69133376bc9a03f8c47343d33f91f74a99c339e8b58cea90433d8e24bb298031", size = 226558, upload-time = "2025-05-19T14:15:52.665Z" }, + { url = "https://files.pythonhosted.org/packages/fd/22/d2b95cbebbc2ada3be3812ea9287dcc9712d7f1a012fad041770afddb2ad/multidict-6.4.4-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:d6b15c55721b1b115c5ba178c77104123745b1417527ad9641a4c5e2047450f0", size = 212168, upload-time = "2025-05-19T14:15:55.279Z" }, + { url = "https://files.pythonhosted.org/packages/4d/c5/62bfc0b2f9ce88326dbe7179f9824a939c6c7775b23b95de777267b9725c/multidict-6.4.4-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:a887b77f51d3d41e6e1a63cf3bc7ddf24de5939d9ff69441387dfefa58ac2e26", size = 217970, upload-time = "2025-05-19T14:15:56.806Z" }, + { url = "https://files.pythonhosted.org/packages/79/74/977cea1aadc43ff1c75d23bd5bc4768a8fac98c14e5878d6ee8d6bab743c/multidict-6.4.4-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:632a3bf8f1787f7ef7d3c2f68a7bde5be2f702906f8b5842ad6da9d974d0aab3", size = 226980, upload-time = "2025-05-19T14:15:58.313Z" }, + { url = "https://files.pythonhosted.org/packages/48/fc/cc4a1a2049df2eb84006607dc428ff237af38e0fcecfdb8a29ca47b1566c/multidict-6.4.4-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:a145c550900deb7540973c5cdb183b0d24bed6b80bf7bddf33ed8f569082535e", size = 220641, upload-time = "2025-05-19T14:15:59.866Z" }, + { url = "https://files.pythonhosted.org/packages/3b/6a/a7444d113ab918701988d4abdde373dbdfd2def7bd647207e2bf645c7eac/multidict-6.4.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:cc5d83c6619ca5c9672cb78b39ed8542f1975a803dee2cda114ff73cbb076edd", size = 221728, upload-time = "2025-05-19T14:16:01.535Z" }, + { url = "https://files.pythonhosted.org/packages/2b/b0/fdf4c73ad1c55e0f4dbbf2aa59dd37037334091f9a4961646d2b7ac91a86/multidict-6.4.4-cp313-cp313t-win32.whl", hash = "sha256:3312f63261b9df49be9d57aaa6abf53a6ad96d93b24f9cc16cf979956355ce6e", size = 41913, upload-time = "2025-05-19T14:16:03.199Z" }, + { url = "https://files.pythonhosted.org/packages/8e/92/27989ecca97e542c0d01d05a98a5ae12198a243a9ee12563a0313291511f/multidict-6.4.4-cp313-cp313t-win_amd64.whl", hash = "sha256:ba852168d814b2c73333073e1c7116d9395bea69575a01b0b3c89d2d5a87c8fb", size = 46112, upload-time = "2025-05-19T14:16:04.909Z" }, + { url = "https://files.pythonhosted.org/packages/84/5d/e17845bb0fa76334477d5de38654d27946d5b5d3695443987a094a71b440/multidict-6.4.4-py3-none-any.whl", hash = "sha256:bd4557071b561a8b3b6075c3ce93cf9bfb6182cb241805c3d66ced3b75eff4ac", size = 10481, upload-time = "2025-05-19T14:16:36.024Z" }, ] [[package]] name = "mypy-extensions" -version = "1.0.0" +version = "1.1.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/98/a4/1ab47638b92648243faf97a5aeb6ea83059cc3624972ab6b8d2316078d3f/mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782", size = 4433, upload-time = "2023-02-04T12:11:27.157Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558", size = 6343, upload-time = "2025-04-22T14:54:24.164Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2a/e2/5d3f6ada4297caebe1a2add3b126fe800c96f56dbe5d1988a2cbe0b267aa/mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d", size = 4695, upload-time = "2023-02-04T12:11:25.002Z" }, + { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" }, ] [[package]] name = "narwhals" -version = "1.41.1" +version = "1.42.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ce/1b/877c22912b78f8b3fe60de1fb908a624c47afab0d6f9f32b5a1703566ff1/narwhals-1.41.1.tar.gz", hash = "sha256:be973f27b9eca2bab82c789b9c63135b5cd2a726c80309146356dd923b6f5104", size = 489404, upload-time = "2025-06-06T07:29:24.141Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/7e/9484c2427453bd0024fd36cf7923de4367d749f0b216b9ca56b9dfc3c516/narwhals-1.42.0.tar.gz", hash = "sha256:a5e554782446d1197593312651352cd39b2025e995053d8e6bdfaa01a70a91d3", size = 490671, upload-time = "2025-06-09T09:20:27.794Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/47/9f/ba87ba354282d81c681b98733479c17d9f3dcfa5532e6105509db44a04b6/narwhals-1.41.1-py3-none-any.whl", hash = "sha256:42325449d9e1133e235b9a5b45c71132845dd5a4524940828753d9f7ca5ae303", size = 358034, upload-time = "2025-06-06T07:29:22.236Z" }, + { url = "https://files.pythonhosted.org/packages/8d/0f/f9ae7c8c55f9078c852b13ea4a6e92e5f4d6d4c8fc0781ec2882957006bb/narwhals-1.42.0-py3-none-any.whl", hash = "sha256:ef6cedf7700dc22c09d17973b9ede11b53e25331e238b24ac73884a8c5e27c19", size = 359033, upload-time = "2025-06-09T09:20:25.668Z" }, ] [[package]] name = "numpy" -version = "2.2.5" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/dc/b2/ce4b867d8cd9c0ee84938ae1e6a6f7926ebf928c9090d036fc3c6a04f946/numpy-2.2.5.tar.gz", hash = "sha256:a9c0d994680cd991b1cb772e8b297340085466a6fe964bc9d4e80f5e2f43c291", size = 20273920, upload-time = "2025-04-19T23:27:42.561Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e2/a0/0aa7f0f4509a2e07bd7a509042967c2fab635690d4f48c6c7b3afd4f448c/numpy-2.2.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:059b51b658f4414fff78c6d7b1b4e18283ab5fa56d270ff212d5ba0c561846f4", size = 20935102, upload-time = "2025-04-19T22:41:16.234Z" }, - { url = "https://files.pythonhosted.org/packages/7e/e4/a6a9f4537542912ec513185396fce52cdd45bdcf3e9d921ab02a93ca5aa9/numpy-2.2.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:47f9ed103af0bc63182609044b0490747e03bd20a67e391192dde119bf43d52f", size = 14191709, upload-time = "2025-04-19T22:41:38.472Z" }, - { url = "https://files.pythonhosted.org/packages/be/65/72f3186b6050bbfe9c43cb81f9df59ae63603491d36179cf7a7c8d216758/numpy-2.2.5-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:261a1ef047751bb02f29dfe337230b5882b54521ca121fc7f62668133cb119c9", size = 5149173, upload-time = "2025-04-19T22:41:47.823Z" }, - { url = "https://files.pythonhosted.org/packages/e5/e9/83e7a9432378dde5802651307ae5e9ea07bb72b416728202218cd4da2801/numpy-2.2.5-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:4520caa3807c1ceb005d125a75e715567806fed67e315cea619d5ec6e75a4191", size = 6684502, upload-time = "2025-04-19T22:41:58.689Z" }, - { url = "https://files.pythonhosted.org/packages/ea/27/b80da6c762394c8ee516b74c1f686fcd16c8f23b14de57ba0cad7349d1d2/numpy-2.2.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d14b17b9be5f9c9301f43d2e2a4886a33b53f4e6fdf9ca2f4cc60aeeee76372", size = 14084417, upload-time = "2025-04-19T22:42:19.897Z" }, - { url = "https://files.pythonhosted.org/packages/aa/fc/ebfd32c3e124e6a1043e19c0ab0769818aa69050ce5589b63d05ff185526/numpy-2.2.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ba321813a00e508d5421104464510cc962a6f791aa2fca1c97b1e65027da80d", size = 16133807, upload-time = "2025-04-19T22:42:44.433Z" }, - { url = "https://files.pythonhosted.org/packages/bf/9b/4cc171a0acbe4666f7775cfd21d4eb6bb1d36d3a0431f48a73e9212d2278/numpy-2.2.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4cbdef3ddf777423060c6f81b5694bad2dc9675f110c4b2a60dc0181543fac7", size = 15575611, upload-time = "2025-04-19T22:43:09.928Z" }, - { url = "https://files.pythonhosted.org/packages/a3/45/40f4135341850df48f8edcf949cf47b523c404b712774f8855a64c96ef29/numpy-2.2.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:54088a5a147ab71a8e7fdfd8c3601972751ded0739c6b696ad9cb0343e21ab73", size = 17895747, upload-time = "2025-04-19T22:43:36.983Z" }, - { url = "https://files.pythonhosted.org/packages/f8/4c/b32a17a46f0ffbde8cc82df6d3daeaf4f552e346df143e1b188a701a8f09/numpy-2.2.5-cp313-cp313-win32.whl", hash = "sha256:c8b82a55ef86a2d8e81b63da85e55f5537d2157165be1cb2ce7cfa57b6aef38b", size = 6309594, upload-time = "2025-04-19T22:47:10.523Z" }, - { url = "https://files.pythonhosted.org/packages/13/ae/72e6276feb9ef06787365b05915bfdb057d01fceb4a43cb80978e518d79b/numpy-2.2.5-cp313-cp313-win_amd64.whl", hash = "sha256:d8882a829fd779f0f43998e931c466802a77ca1ee0fe25a3abe50278616b1471", size = 12638356, upload-time = "2025-04-19T22:47:30.253Z" }, - { url = "https://files.pythonhosted.org/packages/79/56/be8b85a9f2adb688e7ded6324e20149a03541d2b3297c3ffc1a73f46dedb/numpy-2.2.5-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:e8b025c351b9f0e8b5436cf28a07fa4ac0204d67b38f01433ac7f9b870fa38c6", size = 20963778, upload-time = "2025-04-19T22:44:09.251Z" }, - { url = "https://files.pythonhosted.org/packages/ff/77/19c5e62d55bff507a18c3cdff82e94fe174957bad25860a991cac719d3ab/numpy-2.2.5-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:8dfa94b6a4374e7851bbb6f35e6ded2120b752b063e6acdd3157e4d2bb922eba", size = 14207279, upload-time = "2025-04-19T22:44:31.383Z" }, - { url = "https://files.pythonhosted.org/packages/75/22/aa11f22dc11ff4ffe4e849d9b63bbe8d4ac6d5fae85ddaa67dfe43be3e76/numpy-2.2.5-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:97c8425d4e26437e65e1d189d22dff4a079b747ff9c2788057bfb8114ce1e133", size = 5199247, upload-time = "2025-04-19T22:44:40.361Z" }, - { url = "https://files.pythonhosted.org/packages/4f/6c/12d5e760fc62c08eded0394f62039f5a9857f758312bf01632a81d841459/numpy-2.2.5-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:352d330048c055ea6db701130abc48a21bec690a8d38f8284e00fab256dc1376", size = 6711087, upload-time = "2025-04-19T22:44:51.188Z" }, - { url = "https://files.pythonhosted.org/packages/ef/94/ece8280cf4218b2bee5cec9567629e61e51b4be501e5c6840ceb593db945/numpy-2.2.5-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b4c0773b6ada798f51f0f8e30c054d32304ccc6e9c5d93d46cb26f3d385ab19", size = 14059964, upload-time = "2025-04-19T22:45:12.451Z" }, - { url = "https://files.pythonhosted.org/packages/39/41/c5377dac0514aaeec69115830a39d905b1882819c8e65d97fc60e177e19e/numpy-2.2.5-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:55f09e00d4dccd76b179c0f18a44f041e5332fd0e022886ba1c0bbf3ea4a18d0", size = 16121214, upload-time = "2025-04-19T22:45:37.734Z" }, - { url = "https://files.pythonhosted.org/packages/db/54/3b9f89a943257bc8e187145c6bc0eb8e3d615655f7b14e9b490b053e8149/numpy-2.2.5-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:02f226baeefa68f7d579e213d0f3493496397d8f1cff5e2b222af274c86a552a", size = 15575788, upload-time = "2025-04-19T22:46:01.908Z" }, - { url = "https://files.pythonhosted.org/packages/b1/c4/2e407e85df35b29f79945751b8f8e671057a13a376497d7fb2151ba0d290/numpy-2.2.5-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c26843fd58f65da9491165072da2cccc372530681de481ef670dcc8e27cfb066", size = 17893672, upload-time = "2025-04-19T22:46:28.585Z" }, - { url = "https://files.pythonhosted.org/packages/29/7e/d0b44e129d038dba453f00d0e29ebd6eaf2f06055d72b95b9947998aca14/numpy-2.2.5-cp313-cp313t-win32.whl", hash = "sha256:1a161c2c79ab30fe4501d5a2bbfe8b162490757cf90b7f05be8b80bc02f7bb8e", size = 6377102, upload-time = "2025-04-19T22:46:39.949Z" }, - { url = "https://files.pythonhosted.org/packages/63/be/b85e4aa4bf42c6502851b971f1c326d583fcc68227385f92089cf50a7b45/numpy-2.2.5-cp313-cp313t-win_amd64.whl", hash = "sha256:d403c84991b5ad291d3809bace5e85f4bbf44a04bdc9a88ed2bb1807b3360bb8", size = 12750096, upload-time = "2025-04-19T22:47:00.147Z" }, +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f3/db/8e12381333aea300890829a0a36bfa738cac95475d88982d538725143fd9/numpy-2.3.0.tar.gz", hash = "sha256:581f87f9e9e9db2cba2141400e160e9dd644ee248788d6f90636eeb8fd9260a6", size = 20382813, upload-time = "2025-06-07T14:54:32.608Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/73/fc/1d67f751fd4dbafc5780244fe699bc4084268bad44b7c5deb0492473127b/numpy-2.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5754ab5595bfa2c2387d241296e0381c21f44a4b90a776c3c1d39eede13a746a", size = 20889633, upload-time = "2025-06-07T14:44:06.839Z" }, + { url = "https://files.pythonhosted.org/packages/e8/95/73ffdb69e5c3f19ec4530f8924c4386e7ba097efc94b9c0aff607178ad94/numpy-2.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d11fa02f77752d8099573d64e5fe33de3229b6632036ec08f7080f46b6649959", size = 14151683, upload-time = "2025-06-07T14:44:28.847Z" }, + { url = "https://files.pythonhosted.org/packages/64/d5/06d4bb31bb65a1d9c419eb5676173a2f90fd8da3c59f816cc54c640ce265/numpy-2.3.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:aba48d17e87688a765ab1cd557882052f238e2f36545dfa8e29e6a91aef77afe", size = 5102683, upload-time = "2025-06-07T14:44:38.417Z" }, + { url = "https://files.pythonhosted.org/packages/12/8b/6c2cef44f8ccdc231f6b56013dff1d71138c48124334aded36b1a1b30c5a/numpy-2.3.0-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:4dc58865623023b63b10d52f18abaac3729346a7a46a778381e0e3af4b7f3beb", size = 6640253, upload-time = "2025-06-07T14:44:49.359Z" }, + { url = "https://files.pythonhosted.org/packages/62/aa/fca4bf8de3396ddb59544df9b75ffe5b73096174de97a9492d426f5cd4aa/numpy-2.3.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:df470d376f54e052c76517393fa443758fefcdd634645bc9c1f84eafc67087f0", size = 14258658, upload-time = "2025-06-07T14:45:10.156Z" }, + { url = "https://files.pythonhosted.org/packages/1c/12/734dce1087eed1875f2297f687e671cfe53a091b6f2f55f0c7241aad041b/numpy-2.3.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:87717eb24d4a8a64683b7a4e91ace04e2f5c7c77872f823f02a94feee186168f", size = 16628765, upload-time = "2025-06-07T14:45:35.076Z" }, + { url = "https://files.pythonhosted.org/packages/48/03/ffa41ade0e825cbcd5606a5669962419528212a16082763fc051a7247d76/numpy-2.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d8fa264d56882b59dcb5ea4d6ab6f31d0c58a57b41aec605848b6eb2ef4a43e8", size = 15564335, upload-time = "2025-06-07T14:45:58.797Z" }, + { url = "https://files.pythonhosted.org/packages/07/58/869398a11863310aee0ff85a3e13b4c12f20d032b90c4b3ee93c3b728393/numpy-2.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e651756066a0eaf900916497e20e02fe1ae544187cb0fe88de981671ee7f6270", size = 18360608, upload-time = "2025-06-07T14:46:25.687Z" }, + { url = "https://files.pythonhosted.org/packages/2f/8a/5756935752ad278c17e8a061eb2127c9a3edf4ba2c31779548b336f23c8d/numpy-2.3.0-cp313-cp313-win32.whl", hash = "sha256:e43c3cce3b6ae5f94696669ff2a6eafd9a6b9332008bafa4117af70f4b88be6f", size = 6310005, upload-time = "2025-06-07T14:50:13.138Z" }, + { url = "https://files.pythonhosted.org/packages/08/60/61d60cf0dfc0bf15381eaef46366ebc0c1a787856d1db0c80b006092af84/numpy-2.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:81ae0bf2564cf475f94be4a27ef7bcf8af0c3e28da46770fc904da9abd5279b5", size = 12729093, upload-time = "2025-06-07T14:50:31.82Z" }, + { url = "https://files.pythonhosted.org/packages/66/31/2f2f2d2b3e3c32d5753d01437240feaa32220b73258c9eef2e42a0832866/numpy-2.3.0-cp313-cp313-win_arm64.whl", hash = "sha256:c8738baa52505fa6e82778580b23f945e3578412554d937093eac9205e845e6e", size = 9885689, upload-time = "2025-06-07T14:50:47.888Z" }, + { url = "https://files.pythonhosted.org/packages/f1/89/c7828f23cc50f607ceb912774bb4cff225ccae7131c431398ad8400e2c98/numpy-2.3.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:39b27d8b38942a647f048b675f134dd5a567f95bfff481f9109ec308515c51d8", size = 20986612, upload-time = "2025-06-07T14:46:56.077Z" }, + { url = "https://files.pythonhosted.org/packages/dd/46/79ecf47da34c4c50eedec7511e53d57ffdfd31c742c00be7dc1d5ffdb917/numpy-2.3.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:0eba4a1ea88f9a6f30f56fdafdeb8da3774349eacddab9581a21234b8535d3d3", size = 14298953, upload-time = "2025-06-07T14:47:18.053Z" }, + { url = "https://files.pythonhosted.org/packages/59/44/f6caf50713d6ff4480640bccb2a534ce1d8e6e0960c8f864947439f0ee95/numpy-2.3.0-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:b0f1f11d0a1da54927436505a5a7670b154eac27f5672afc389661013dfe3d4f", size = 5225806, upload-time = "2025-06-07T14:47:27.524Z" }, + { url = "https://files.pythonhosted.org/packages/a6/43/e1fd1aca7c97e234dd05e66de4ab7a5be54548257efcdd1bc33637e72102/numpy-2.3.0-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:690d0a5b60a47e1f9dcec7b77750a4854c0d690e9058b7bef3106e3ae9117808", size = 6735169, upload-time = "2025-06-07T14:47:38.057Z" }, + { url = "https://files.pythonhosted.org/packages/84/89/f76f93b06a03177c0faa7ca94d0856c4e5c4bcaf3c5f77640c9ed0303e1c/numpy-2.3.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:8b51ead2b258284458e570942137155978583e407babc22e3d0ed7af33ce06f8", size = 14330701, upload-time = "2025-06-07T14:47:59.113Z" }, + { url = "https://files.pythonhosted.org/packages/aa/f5/4858c3e9ff7a7d64561b20580cf7cc5d085794bd465a19604945d6501f6c/numpy-2.3.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:aaf81c7b82c73bd9b45e79cfb9476cb9c29e937494bfe9092c26aece812818ad", size = 16692983, upload-time = "2025-06-07T14:48:24.196Z" }, + { url = "https://files.pythonhosted.org/packages/08/17/0e3b4182e691a10e9483bcc62b4bb8693dbf9ea5dc9ba0b77a60435074bb/numpy-2.3.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:f420033a20b4f6a2a11f585f93c843ac40686a7c3fa514060a97d9de93e5e72b", size = 15641435, upload-time = "2025-06-07T14:48:47.712Z" }, + { url = "https://files.pythonhosted.org/packages/4e/d5/463279fda028d3c1efa74e7e8d507605ae87f33dbd0543cf4c4527c8b882/numpy-2.3.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:d344ca32ab482bcf8735d8f95091ad081f97120546f3d250240868430ce52555", size = 18433798, upload-time = "2025-06-07T14:49:14.866Z" }, + { url = "https://files.pythonhosted.org/packages/0e/1e/7a9d98c886d4c39a2b4d3a7c026bffcf8fbcaf518782132d12a301cfc47a/numpy-2.3.0-cp313-cp313t-win32.whl", hash = "sha256:48a2e8eaf76364c32a1feaa60d6925eaf32ed7a040183b807e02674305beef61", size = 6438632, upload-time = "2025-06-07T14:49:25.67Z" }, + { url = "https://files.pythonhosted.org/packages/fe/ab/66fc909931d5eb230107d016861824f335ae2c0533f422e654e5ff556784/numpy-2.3.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ba17f93a94e503551f154de210e4d50c5e3ee20f7e7a1b5f6ce3f22d419b93bb", size = 12868491, upload-time = "2025-06-07T14:49:44.898Z" }, + { url = "https://files.pythonhosted.org/packages/ee/e8/2c8a1c9e34d6f6d600c83d5ce5b71646c32a13f34ca5c518cc060639841c/numpy-2.3.0-cp313-cp313t-win_arm64.whl", hash = "sha256:f14e016d9409680959691c109be98c436c6249eaf7f118b424679793607b5944", size = 9935345, upload-time = "2025-06-07T14:50:02.311Z" }, ] [[package]] name = "openai" -version = "1.75.0" +version = "1.84.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, @@ -1349,14 +1418,14 @@ dependencies = [ { name = "tqdm" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/99/b1/318f5d4c482f19c5fcbcde190801bfaaaec23413cda0b88a29f6897448ff/openai-1.75.0.tar.gz", hash = "sha256:fb3ea907efbdb1bcfd0c44507ad9c961afd7dce3147292b54505ecfd17be8fd1", size = 429492, upload-time = "2025-04-16T16:49:29.25Z" } +sdist = { url = "https://files.pythonhosted.org/packages/91/a3/128caf24e116f48fad3e4d5122cdf84db06c5127911849d51663c66158c8/openai-1.84.0.tar.gz", hash = "sha256:4caa43bdab262cc75680ce1a2322cfc01626204074f7e8d9939ab372acf61698", size = 467066, upload-time = "2025-06-03T17:10:53.651Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/80/9a/f34f163294345f123673ed03e77c33dee2534f3ac1f9d18120384457304d/openai-1.75.0-py3-none-any.whl", hash = "sha256:fe6f932d2ded3b429ff67cc9ad118c71327db32eb9d32dd723de3acfca337125", size = 646972, upload-time = "2025-04-16T16:49:27.196Z" }, + { url = "https://files.pythonhosted.org/packages/2a/10/f245db006a860dbc1f2e2c8382e0a1762c7753e7971ba43a1dc3f3ec1404/openai-1.84.0-py3-none-any.whl", hash = "sha256:7ec4436c3c933d68dc0f5a0cef0cb3dbc0864a54d62bddaf2ed5f3d521844711", size = 725512, upload-time = "2025-06-03T17:10:51.195Z" }, ] [[package]] name = "openai-agents" -version = "0.0.12" +version = "0.0.17" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "griffe" }, @@ -1367,22 +1436,22 @@ dependencies = [ { name = "types-requests" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/98/33/b892aa49a467efac6262fdee302510733b059856eec334956c92bdd7fd94/openai_agents-0.0.12.tar.gz", hash = "sha256:5d889a5abb2e335dc21d5c11cf8b133667d919905259a35fd1405c5b6a8c484f", size = 1334685, upload-time = "2025-04-22T03:08:35.231Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5d/82/75e18932102756e3c6e2c8a63f6a3a3058b32580f7aa53d87daab4f2a26b/openai_agents-0.0.17.tar.gz", hash = "sha256:44f9c8e80b461c64cfdcf55d162ca8bb0594f4b2ada48daf1be34a8d4cd0759f", size = 1349794, upload-time = "2025-06-04T02:37:55.852Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c9/aa/d1486f1b971eca782b1ee3626037b7dedd9302e98ff7d10dc34462b8a5dd/openai_agents-0.0.12-py3-none-any.whl", hash = "sha256:122d9b71b408c70ffc8cd9e78751cc765ef104938fb937484d5b163790132e31", size = 116247, upload-time = "2025-04-22T03:08:33.756Z" }, + { url = "https://files.pythonhosted.org/packages/c6/72/bfa64eaa006d78e6480a4463267a55c31920fdcca1f3ebb331ac1217f598/openai_agents-0.0.17-py3-none-any.whl", hash = "sha256:924e0be145b42fb8984e35ab9d14e4948a2251c3b122a02ca989b223e4d39c27", size = 121886, upload-time = "2025-06-04T02:37:54.351Z" }, ] [[package]] name = "opentelemetry-api" -version = "1.32.1" +version = "1.34.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "deprecated" }, { name = "importlib-metadata" }, + { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/42/40/2359245cd33641c2736a0136a50813352d72f3fc209de28fb226950db4a1/opentelemetry_api-1.32.1.tar.gz", hash = "sha256:a5be71591694a4d9195caf6776b055aa702e964d961051a0715d05f8632c32fb", size = 64138, upload-time = "2025-04-15T16:02:13.97Z" } +sdist = { url = "https://files.pythonhosted.org/packages/4c/0b/4433d3f18301b541d98ea775fcbeab817fc7f962e980a75d17c967471b64/opentelemetry_api-1.34.0.tar.gz", hash = "sha256:48d167589134799093005b7f7f347c69cc67859c693b17787f334fbe8871279f", size = 64983, upload-time = "2025-06-04T13:31:26.107Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/12/f2/89ea3361a305466bc6460a532188830351220b5f0851a5fa133155c16eca/opentelemetry_api-1.32.1-py3-none-any.whl", hash = "sha256:bbd19f14ab9f15f0e85e43e6a958aa4cb1f36870ee62b7fd205783a112012724", size = 65287, upload-time = "2025-04-15T16:01:49.747Z" }, + { url = "https://files.pythonhosted.org/packages/98/f9/d50ba0c92a97a6d0861357d0ecd67e850d319ac7e7be1895cc236b6ed2b5/opentelemetry_api-1.34.0-py3-none-any.whl", hash = "sha256:390b81984affe4453180820ca518de55e3be051111e70cc241bb3b0071ca3a2c", size = 65768, upload-time = "2025-06-04T13:31:02.706Z" }, ] [[package]] @@ -1402,37 +1471,37 @@ wheels = [ [[package]] name = "opentelemetry-exporter-otlp-proto-common" -version = "1.32.1" +version = "1.34.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-proto" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/10/a1/466fad0e6a21709f0502ff346545a3d81bc8121b2d87357f74c8a3bc856e/opentelemetry_exporter_otlp_proto_common-1.32.1.tar.gz", hash = "sha256:da4edee4f24aaef109bfe924efad3a98a2e27c91278115505b298ee61da5d68e", size = 20623, upload-time = "2025-04-15T16:02:16.105Z" } +sdist = { url = "https://files.pythonhosted.org/packages/81/12/0d549f53e70a8297c1817705febe2bdb81479dc74c5b2496014f35f74455/opentelemetry_exporter_otlp_proto_common-1.34.0.tar.gz", hash = "sha256:5916d9ceda8c733adbec5e9cecf654fbf359e9f619ff43214277076fba888557", size = 20818, upload-time = "2025-06-04T13:31:28.136Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/72/1a/a51584a8b13cd9d4cb0d8f14f2164d0cf1a1bd1e5d7c81b7974fde2fb47b/opentelemetry_exporter_otlp_proto_common-1.32.1-py3-none-any.whl", hash = "sha256:a1e9ad3d0d9a9405c7ff8cdb54ba9b265da16da9844fe36b8c9661114b56c5d9", size = 18816, upload-time = "2025-04-15T16:01:53.353Z" }, + { url = "https://files.pythonhosted.org/packages/e9/78/7bfd2d027aa36a68fff4019950569f8cda27793441098cda0a82ea2ecb89/opentelemetry_exporter_otlp_proto_common-1.34.0-py3-none-any.whl", hash = "sha256:a5ab7a9b7c3c7ba957c8ddcb08c0c93b1d732e066f544682a250ecf4d7a9ceef", size = 18835, upload-time = "2025-06-04T13:31:05.797Z" }, ] [[package]] name = "opentelemetry-exporter-otlp-proto-http" -version = "1.32.1" +version = "1.34.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "deprecated" }, { name = "googleapis-common-protos" }, { name = "opentelemetry-api" }, { name = "opentelemetry-exporter-otlp-proto-common" }, { name = "opentelemetry-proto" }, { name = "opentelemetry-sdk" }, { name = "requests" }, + { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/7b/b1/35d88066d628c469ec5152c4b8f072cc203c13c55d591df190ba91eaea13/opentelemetry_exporter_otlp_proto_http-1.32.1.tar.gz", hash = "sha256:f854a6e7128858213850dbf1929478a802faf50e799ffd2eb4d7424390023828", size = 15133, upload-time = "2025-04-15T16:02:18.701Z" } +sdist = { url = "https://files.pythonhosted.org/packages/99/80/c382acdddc75d440a4bc5283a1cda997435985031ec2d978d99ab3ef9461/opentelemetry_exporter_otlp_proto_http-1.34.0.tar.gz", hash = "sha256:3f674dbc32549a2fae413a77428d59b38e8c8b4caaf7f594ae2c2f8d2f018014", size = 15353, upload-time = "2025-06-04T13:31:29.388Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/01/75/73f96e43431c7ed4deac7a61820f218b0a8e75d2ad6ba78c354b16a9c730/opentelemetry_exporter_otlp_proto_http-1.32.1-py3-none-any.whl", hash = "sha256:3cc048b0c295aa2cbafb883feaf217c7525b396567eeeabb5459affb08b7fefe", size = 17242, upload-time = "2025-04-15T16:01:55.357Z" }, + { url = "https://files.pythonhosted.org/packages/69/c5/468c245231feff02ac41573a1d73b1bbd5ff0412365f441de785a4fa178c/opentelemetry_exporter_otlp_proto_http-1.34.0-py3-none-any.whl", hash = "sha256:b3cc9dd5152fae2dd32f3566bbfbc7d26d6ab3ef6c6b3f85bc9f6adc059d713f", size = 17743, upload-time = "2025-06-04T13:31:09.34Z" }, ] [[package]] name = "opentelemetry-instrumentation" -version = "0.53b1" +version = "0.55b0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-api" }, @@ -1440,21 +1509,21 @@ dependencies = [ { name = "packaging" }, { name = "wrapt" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5a/84/d778d8900c5694727516af205f84fa646fad4fb9bef6b2d21ba361ff25aa/opentelemetry_instrumentation-0.53b1.tar.gz", hash = "sha256:0e69ca2c75727e8a300de671c4a2ec0e86e63a8e906beaa5d6c9f5228e8687e5", size = 28175, upload-time = "2025-04-15T16:04:47.422Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f3/1b/9e423d5f4d731039a5a7fb06aab3a8215fe3eb7384b98ee2dc4786c45d79/opentelemetry_instrumentation-0.55b0.tar.gz", hash = "sha256:c0c64c16d2abae80a0f43906d3c68de10a700a4fc11d22b1c31f32d628e95e31", size = 28553, upload-time = "2025-06-04T14:39:29.718Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3f/5e/1897e0cb579f4a215c42316021a52f588eaee4d008477e85b3ca9fa792c4/opentelemetry_instrumentation-0.53b1-py3-none-any.whl", hash = "sha256:c07850cecfbc51e8b357f56d5886ae5ccaa828635b220d0f5e78f941ea9a83ca", size = 30814, upload-time = "2025-04-15T16:03:47.497Z" }, + { url = "https://files.pythonhosted.org/packages/c3/4e/712ef979207582cdecf27b64f2ae1051f1cddbc60b59815b6a9a25c23afa/opentelemetry_instrumentation-0.55b0-py3-none-any.whl", hash = "sha256:9669f19a561f7eacd9974823e48949bc12506d34cb2dd277e9d7b70987c7cc66", size = 31105, upload-time = "2025-06-04T14:38:27.356Z" }, ] [[package]] name = "opentelemetry-proto" -version = "1.32.1" +version = "1.34.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "protobuf" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/31/9b/17f31b0dff06b21fc30bf032ce3f3d443391d3f5cebb65b4d680c4e770c4/opentelemetry_proto-1.32.1.tar.gz", hash = "sha256:bc6385ccf87768f029371535312071a2d09e6c9ebf119ac17dbc825a6a56ba53", size = 34360, upload-time = "2025-04-15T16:02:27.82Z" } +sdist = { url = "https://files.pythonhosted.org/packages/95/19/45adb533d0a34990942d12eefb2077d59b22958940c71484a45e694f5dd7/opentelemetry_proto-1.34.0.tar.gz", hash = "sha256:73e40509b692630a47192888424f7e0b8fb19d9ecf2f04e6f708170cd3346dfe", size = 34343, upload-time = "2025-06-04T13:31:35.695Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a5/89/16a40a3c64611cb32509751ef6370e3e96c24a39ba493b4d67f5671ef4c1/opentelemetry_proto-1.32.1-py3-none-any.whl", hash = "sha256:fe56df31033ab0c40af7525f8bf4c487313377bbcfdf94184b701a8ccebc800e", size = 55854, upload-time = "2025-04-15T16:02:07.582Z" }, + { url = "https://files.pythonhosted.org/packages/db/58/708881f5ad3c72954caa61ac970d3c01209dbebf5e534fb840dfb777bad2/opentelemetry_proto-1.34.0-py3-none-any.whl", hash = "sha256:ffb1f1b27552fda5a1cd581e34243cc0b6f134fb14c1c2a33cc3b4b208c9bf97", size = 55691, upload-time = "2025-06-04T13:31:20.333Z" }, ] [[package]] @@ -1474,67 +1543,68 @@ wheels = [ [[package]] name = "opentelemetry-sdk" -version = "1.32.1" +version = "1.34.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-api" }, { name = "opentelemetry-semantic-conventions" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a3/65/2069caef9257fae234ca0040d945c741aa7afbd83a7298ee70fc0bc6b6f4/opentelemetry_sdk-1.32.1.tar.gz", hash = "sha256:8ef373d490961848f525255a42b193430a0637e064dd132fd2a014d94792a092", size = 161044, upload-time = "2025-04-15T16:02:28.905Z" } +sdist = { url = "https://files.pythonhosted.org/packages/52/07/8ca4b295322b5978e2cc4fab3f743ddabf72b82b5d2c50141471f573149d/opentelemetry_sdk-1.34.0.tar.gz", hash = "sha256:719559622afcd515c2aec462ccb749ba2e70075a01df45837623643814d33716", size = 159322, upload-time = "2025-06-04T13:31:36.333Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/dc/00/d3976cdcb98027aaf16f1e980e54935eb820872792f0eaedd4fd7abb5964/opentelemetry_sdk-1.32.1-py3-none-any.whl", hash = "sha256:bba37b70a08038613247bc42beee5a81b0ddca422c7d7f1b097b32bf1c7e2f17", size = 118989, upload-time = "2025-04-15T16:02:08.814Z" }, + { url = "https://files.pythonhosted.org/packages/55/96/5b788eef90a65543a67988729f0e44fc46eac1da455505ae5091f418a9d9/opentelemetry_sdk-1.34.0-py3-none-any.whl", hash = "sha256:7850bcd5b5c95f9aae48603d6592bdad5c7bdef50c03e06393f8f457d891fe32", size = 118385, upload-time = "2025-06-04T13:31:21.372Z" }, ] [[package]] name = "opentelemetry-semantic-conventions" -version = "0.53b1" +version = "0.55b0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "deprecated" }, { name = "opentelemetry-api" }, + { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5e/b6/3c56e22e9b51bcb89edab30d54830958f049760bbd9ab0a759cece7bca88/opentelemetry_semantic_conventions-0.53b1.tar.gz", hash = "sha256:4c5a6fede9de61211b2e9fc1e02e8acacce882204cd770177342b6a3be682992", size = 114350, upload-time = "2025-04-15T16:02:29.793Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f1/64/b99165f7e205e103a83406fb5c3dde668c3a990b3fa0cbe358011095f4fa/opentelemetry_semantic_conventions-0.55b0.tar.gz", hash = "sha256:933d2e20c2dbc0f9b2f4f52138282875b4b14c66c491f5273bcdef1781368e9c", size = 119828, upload-time = "2025-06-04T13:31:37.118Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/27/6b/a8fb94760ef8da5ec283e488eb43235eac3ae7514385a51b6accf881e671/opentelemetry_semantic_conventions-0.53b1-py3-none-any.whl", hash = "sha256:21df3ed13f035f8f3ea42d07cbebae37020367a53b47f1ebee3b10a381a00208", size = 188443, upload-time = "2025-04-15T16:02:10.095Z" }, + { url = "https://files.pythonhosted.org/packages/ca/b1/d7a2472f7da7e39f1a85f63951ad653c1126a632d6491c056ec6284a10a7/opentelemetry_semantic_conventions-0.55b0-py3-none-any.whl", hash = "sha256:63bb15b67377700e51c422d0d24092ca6ce9f3a4cb6f032375aa8af1fc2aab65", size = 196224, upload-time = "2025-06-04T13:31:22.451Z" }, ] [[package]] name = "orjson" -version = "3.10.16" +version = "3.10.18" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/98/c7/03913cc4332174071950acf5b0735463e3f63760c80585ef369270c2b372/orjson-3.10.16.tar.gz", hash = "sha256:d2aaa5c495e11d17b9b93205f5fa196737ee3202f000aaebf028dc9a73750f10", size = 5410415, upload-time = "2025-03-24T17:00:23.312Z" } +sdist = { url = "https://files.pythonhosted.org/packages/81/0b/fea456a3ffe74e70ba30e01ec183a9b26bec4d497f61dcfce1b601059c60/orjson-3.10.18.tar.gz", hash = "sha256:e8da3947d92123eda795b68228cafe2724815621fe35e8e320a9e9593a4bcd53", size = 5422810, upload-time = "2025-04-29T23:30:08.423Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/87/b9/ff6aa28b8c86af9526160905593a2fe8d004ac7a5e592ee0b0ff71017511/orjson-3.10.16-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:148a97f7de811ba14bc6dbc4a433e0341ffd2cc285065199fb5f6a98013744bd", size = 249289, upload-time = "2025-03-24T16:59:40.117Z" }, - { url = "https://files.pythonhosted.org/packages/6c/81/6d92a586149b52684ab8fd70f3623c91d0e6a692f30fd8c728916ab2263c/orjson-3.10.16-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:1d960c1bf0e734ea36d0adc880076de3846aaec45ffad29b78c7f1b7962516b8", size = 133640, upload-time = "2025-03-24T16:59:41.469Z" }, - { url = "https://files.pythonhosted.org/packages/c2/88/b72443f4793d2e16039ab85d0026677932b15ab968595fb7149750d74134/orjson-3.10.16-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a318cd184d1269f68634464b12871386808dc8b7c27de8565234d25975a7a137", size = 138286, upload-time = "2025-03-24T16:59:42.769Z" }, - { url = "https://files.pythonhosted.org/packages/c3/3c/72a22d4b28c076c4016d5a52bd644a8e4d849d3bb0373d9e377f9e3b2250/orjson-3.10.16-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:df23f8df3ef9223d1d6748bea63fca55aae7da30a875700809c500a05975522b", size = 132307, upload-time = "2025-03-24T16:59:44.143Z" }, - { url = "https://files.pythonhosted.org/packages/8a/a2/f1259561bdb6ad7061ff1b95dab082fe32758c4bc143ba8d3d70831f0a06/orjson-3.10.16-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b94dda8dd6d1378f1037d7f3f6b21db769ef911c4567cbaa962bb6dc5021cf90", size = 136739, upload-time = "2025-03-24T16:59:45.995Z" }, - { url = "https://files.pythonhosted.org/packages/3d/af/c7583c4b34f33d8b8b90cfaab010ff18dd64e7074cc1e117a5f1eff20dcf/orjson-3.10.16-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f12970a26666a8775346003fd94347d03ccb98ab8aa063036818381acf5f523e", size = 138076, upload-time = "2025-03-24T16:59:47.776Z" }, - { url = "https://files.pythonhosted.org/packages/d7/59/d7fc7fbdd3d4a64c2eae4fc7341a5aa39cf9549bd5e2d7f6d3c07f8b715b/orjson-3.10.16-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:15a1431a245d856bd56e4d29ea0023eb4d2c8f71efe914beb3dee8ab3f0cd7fb", size = 142643, upload-time = "2025-03-24T16:59:49.258Z" }, - { url = "https://files.pythonhosted.org/packages/92/0e/3bd8f2197d27601f16b4464ae948826da2bcf128af31230a9dbbad7ceb57/orjson-3.10.16-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c83655cfc247f399a222567d146524674a7b217af7ef8289c0ff53cfe8db09f0", size = 133168, upload-time = "2025-03-24T16:59:51.027Z" }, - { url = "https://files.pythonhosted.org/packages/af/a8/351fd87b664b02f899f9144d2c3dc848b33ac04a5df05234cbfb9e2a7540/orjson-3.10.16-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:fa59ae64cb6ddde8f09bdbf7baf933c4cd05734ad84dcf4e43b887eb24e37652", size = 135271, upload-time = "2025-03-24T16:59:52.449Z" }, - { url = "https://files.pythonhosted.org/packages/ba/b0/a6d42a7d412d867c60c0337d95123517dd5a9370deea705ea1be0f89389e/orjson-3.10.16-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:ca5426e5aacc2e9507d341bc169d8af9c3cbe88f4cd4c1cf2f87e8564730eb56", size = 412444, upload-time = "2025-03-24T16:59:53.825Z" }, - { url = "https://files.pythonhosted.org/packages/79/ec/7572cd4e20863f60996f3f10bc0a6da64a6fd9c35954189a914cec0b7377/orjson-3.10.16-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:6fd5da4edf98a400946cd3a195680de56f1e7575109b9acb9493331047157430", size = 152737, upload-time = "2025-03-24T16:59:55.599Z" }, - { url = "https://files.pythonhosted.org/packages/a9/19/ceb9e8fed5403b2e76a8ac15f581b9d25780a3be3c9b3aa54b7777a210d5/orjson-3.10.16-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:980ecc7a53e567169282a5e0ff078393bac78320d44238da4e246d71a4e0e8f5", size = 137482, upload-time = "2025-03-24T16:59:57.045Z" }, - { url = "https://files.pythonhosted.org/packages/1b/78/a78bb810f3786579dbbbd94768284cbe8f2fd65167cd7020260679665c17/orjson-3.10.16-cp313-cp313-win32.whl", hash = "sha256:28f79944dd006ac540a6465ebd5f8f45dfdf0948ff998eac7a908275b4c1add6", size = 141714, upload-time = "2025-03-24T16:59:58.666Z" }, - { url = "https://files.pythonhosted.org/packages/81/9c/b66ce9245ff319df2c3278acd351a3f6145ef34b4a2d7f4b0f739368370f/orjson-3.10.16-cp313-cp313-win_amd64.whl", hash = "sha256:fe0a145e96d51971407cb8ba947e63ead2aa915db59d6631a355f5f2150b56b7", size = 133954, upload-time = "2025-03-24T17:00:00.101Z" }, + { url = "https://files.pythonhosted.org/packages/04/f0/8aedb6574b68096f3be8f74c0b56d36fd94bcf47e6c7ed47a7bd1474aaa8/orjson-3.10.18-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:69c34b9441b863175cc6a01f2935de994025e773f814412030f269da4f7be147", size = 249087, upload-time = "2025-04-29T23:29:19.083Z" }, + { url = "https://files.pythonhosted.org/packages/bc/f7/7118f965541aeac6844fcb18d6988e111ac0d349c9b80cda53583e758908/orjson-3.10.18-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:1ebeda919725f9dbdb269f59bc94f861afbe2a27dce5608cdba2d92772364d1c", size = 133273, upload-time = "2025-04-29T23:29:20.602Z" }, + { url = "https://files.pythonhosted.org/packages/fb/d9/839637cc06eaf528dd8127b36004247bf56e064501f68df9ee6fd56a88ee/orjson-3.10.18-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5adf5f4eed520a4959d29ea80192fa626ab9a20b2ea13f8f6dc58644f6927103", size = 136779, upload-time = "2025-04-29T23:29:22.062Z" }, + { url = "https://files.pythonhosted.org/packages/2b/6d/f226ecfef31a1f0e7d6bf9a31a0bbaf384c7cbe3fce49cc9c2acc51f902a/orjson-3.10.18-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7592bb48a214e18cd670974f289520f12b7aed1fa0b2e2616b8ed9e069e08595", size = 132811, upload-time = "2025-04-29T23:29:23.602Z" }, + { url = "https://files.pythonhosted.org/packages/73/2d/371513d04143c85b681cf8f3bce743656eb5b640cb1f461dad750ac4b4d4/orjson-3.10.18-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f872bef9f042734110642b7a11937440797ace8c87527de25e0c53558b579ccc", size = 137018, upload-time = "2025-04-29T23:29:25.094Z" }, + { url = "https://files.pythonhosted.org/packages/69/cb/a4d37a30507b7a59bdc484e4a3253c8141bf756d4e13fcc1da760a0b00cb/orjson-3.10.18-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0315317601149c244cb3ecef246ef5861a64824ccbcb8018d32c66a60a84ffbc", size = 138368, upload-time = "2025-04-29T23:29:26.609Z" }, + { url = "https://files.pythonhosted.org/packages/1e/ae/cd10883c48d912d216d541eb3db8b2433415fde67f620afe6f311f5cd2ca/orjson-3.10.18-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e0da26957e77e9e55a6c2ce2e7182a36a6f6b180ab7189315cb0995ec362e049", size = 142840, upload-time = "2025-04-29T23:29:28.153Z" }, + { url = "https://files.pythonhosted.org/packages/6d/4c/2bda09855c6b5f2c055034c9eda1529967b042ff8d81a05005115c4e6772/orjson-3.10.18-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb70d489bc79b7519e5803e2cc4c72343c9dc1154258adf2f8925d0b60da7c58", size = 133135, upload-time = "2025-04-29T23:29:29.726Z" }, + { url = "https://files.pythonhosted.org/packages/13/4a/35971fd809a8896731930a80dfff0b8ff48eeb5d8b57bb4d0d525160017f/orjson-3.10.18-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e9e86a6af31b92299b00736c89caf63816f70a4001e750bda179e15564d7a034", size = 134810, upload-time = "2025-04-29T23:29:31.269Z" }, + { url = "https://files.pythonhosted.org/packages/99/70/0fa9e6310cda98365629182486ff37a1c6578e34c33992df271a476ea1cd/orjson-3.10.18-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:c382a5c0b5931a5fc5405053d36c1ce3fd561694738626c77ae0b1dfc0242ca1", size = 413491, upload-time = "2025-04-29T23:29:33.315Z" }, + { url = "https://files.pythonhosted.org/packages/32/cb/990a0e88498babddb74fb97855ae4fbd22a82960e9b06eab5775cac435da/orjson-3.10.18-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:8e4b2ae732431127171b875cb2668f883e1234711d3c147ffd69fe5be51a8012", size = 153277, upload-time = "2025-04-29T23:29:34.946Z" }, + { url = "https://files.pythonhosted.org/packages/92/44/473248c3305bf782a384ed50dd8bc2d3cde1543d107138fd99b707480ca1/orjson-3.10.18-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2d808e34ddb24fc29a4d4041dcfafbae13e129c93509b847b14432717d94b44f", size = 137367, upload-time = "2025-04-29T23:29:36.52Z" }, + { url = "https://files.pythonhosted.org/packages/ad/fd/7f1d3edd4ffcd944a6a40e9f88af2197b619c931ac4d3cfba4798d4d3815/orjson-3.10.18-cp313-cp313-win32.whl", hash = "sha256:ad8eacbb5d904d5591f27dee4031e2c1db43d559edb8f91778efd642d70e6bea", size = 142687, upload-time = "2025-04-29T23:29:38.292Z" }, + { url = "https://files.pythonhosted.org/packages/4b/03/c75c6ad46be41c16f4cfe0352a2d1450546f3c09ad2c9d341110cd87b025/orjson-3.10.18-cp313-cp313-win_amd64.whl", hash = "sha256:aed411bcb68bf62e85588f2a7e03a6082cc42e5a2796e06e72a962d7c6310b52", size = 134794, upload-time = "2025-04-29T23:29:40.349Z" }, + { url = "https://files.pythonhosted.org/packages/c2/28/f53038a5a72cc4fd0b56c1eafb4ef64aec9685460d5ac34de98ca78b6e29/orjson-3.10.18-cp313-cp313-win_arm64.whl", hash = "sha256:f54c1385a0e6aba2f15a40d703b858bedad36ded0491e55d35d905b2c34a4cc3", size = 131186, upload-time = "2025-04-29T23:29:41.922Z" }, ] [[package]] name = "ormsgpack" -version = "1.9.1" +version = "1.10.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/25/a7/462cf8ff5e29241868b82d3a5ec124d690eb6a6a5c6fa5bb1367b839e027/ormsgpack-1.9.1.tar.gz", hash = "sha256:3da6e63d82565e590b98178545e64f0f8506137b92bd31a2d04fd7c82baf5794", size = 56887, upload-time = "2025-03-28T07:14:38.843Z" } +sdist = { url = "https://files.pythonhosted.org/packages/92/36/44eed5ef8ce93cded76a576780bab16425ce7876f10d3e2e6265e46c21ea/ormsgpack-1.10.0.tar.gz", hash = "sha256:7f7a27efd67ef22d7182ec3b7fa7e9d147c3ad9be2a24656b23c989077e08b16", size = 58629, upload-time = "2025-05-24T19:07:53.944Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b8/54/0390d5d092831e4df29dbafe32402891fc14b3e6ffe5a644b16cbbc9d9bc/ormsgpack-1.9.1-cp313-cp313-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:ac61c18d9dd085e8519b949f7e655f7fb07909fd09c53b4338dd33309012e289", size = 383226, upload-time = "2025-03-28T07:14:13.868Z" }, - { url = "https://files.pythonhosted.org/packages/47/64/8b15d262d1caefead8fb22ec144f5ff7d9505fc31c22bc34598053d46fbe/ormsgpack-1.9.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:134840b8c6615da2c24ce77bd12a46098015c808197a9995c7a2d991e1904eec", size = 214057, upload-time = "2025-03-28T07:14:15.307Z" }, - { url = "https://files.pythonhosted.org/packages/57/00/65823609266bad4d5ed29ea753d24a3bdb01c7edaf923da80967fc31f9c5/ormsgpack-1.9.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:38fd42618f626394b2c7713c5d4bcbc917254e9753d5d4cde460658b51b11a74", size = 217340, upload-time = "2025-03-28T07:14:16.69Z" }, - { url = "https://files.pythonhosted.org/packages/a0/51/e535c50f7f87b49110233647f55300d7975139ef5e51f1adb4c55f58c124/ormsgpack-1.9.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d36397333ad07b9eba4c2e271fa78951bd81afc059c85a6e9f6c0eb2de07cda", size = 223815, upload-time = "2025-03-28T07:14:18.651Z" }, - { url = "https://files.pythonhosted.org/packages/0c/ee/393e4a6de2a62124bf589602648f295a9fb3907a0e2fe80061b88899d072/ormsgpack-1.9.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:603063089597917d04e4c1b1d53988a34f7dc2ff1a03adcfd1cf4ae966d5fba6", size = 394287, upload-time = "2025-03-28T07:14:20.569Z" }, - { url = "https://files.pythonhosted.org/packages/c6/d8/e56d7c3cb73a0e533e3e2a21ae5838b2aa36a9dac1ca9c861af6bae5a369/ormsgpack-1.9.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:94bbf2b185e0cb721ceaba20e64b7158e6caf0cecd140ca29b9f05a8d5e91e2f", size = 480707, upload-time = "2025-03-28T07:14:22.006Z" }, - { url = "https://files.pythonhosted.org/packages/e6/e0/6a3c6a6dc98583a721c54b02f5195bde8f801aebdeda9b601fa2ab30ad39/ormsgpack-1.9.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c38f380b1e8c96a712eb302b9349347385161a8e29046868ae2bfdfcb23e2692", size = 397246, upload-time = "2025-03-28T07:14:23.868Z" }, - { url = "https://files.pythonhosted.org/packages/b0/60/0ee5d790f13507e1f75ac21fc82dc1ef29afe1f520bd0f249d65b2f4839b/ormsgpack-1.9.1-cp313-cp313-win_amd64.whl", hash = "sha256:a4bc63fb30db94075611cedbbc3d261dd17cf2aa8ff75a0fd684cd45ca29cb1b", size = 125371, upload-time = "2025-03-28T07:14:25.176Z" }, + { url = "https://files.pythonhosted.org/packages/61/f8/ec5f4e03268d0097545efaab2893aa63f171cf2959cb0ea678a5690e16a1/ormsgpack-1.10.0-cp313-cp313-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:8d816d45175a878993b7372bd5408e0f3ec5a40f48e2d5b9d8f1cc5d31b61f1f", size = 376806, upload-time = "2025-05-24T19:07:29.555Z" }, + { url = "https://files.pythonhosted.org/packages/c1/19/b3c53284aad1e90d4d7ed8c881a373d218e16675b8b38e3569d5b40cc9b8/ormsgpack-1.10.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a90345ccb058de0f35262893751c603b6376b05f02be2b6f6b7e05d9dd6d5643", size = 204433, upload-time = "2025-05-24T19:07:30.977Z" }, + { url = "https://files.pythonhosted.org/packages/09/0b/845c258f59df974a20a536c06cace593698491defdd3d026a8a5f9b6e745/ormsgpack-1.10.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:144b5e88f1999433e54db9d637bae6fe21e935888be4e3ac3daecd8260bd454e", size = 215549, upload-time = "2025-05-24T19:07:32.345Z" }, + { url = "https://files.pythonhosted.org/packages/61/56/57fce8fb34ca6c9543c026ebebf08344c64dbb7b6643d6ddd5355d37e724/ormsgpack-1.10.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2190b352509d012915921cca76267db136cd026ddee42f1b0d9624613cc7058c", size = 216747, upload-time = "2025-05-24T19:07:34.075Z" }, + { url = "https://files.pythonhosted.org/packages/b8/3f/655b5f6a2475c8d209f5348cfbaaf73ce26237b92d79ef2ad439407dd0fa/ormsgpack-1.10.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:86fd9c1737eaba43d3bb2730add9c9e8b5fbed85282433705dd1b1e88ea7e6fb", size = 384785, upload-time = "2025-05-24T19:07:35.83Z" }, + { url = "https://files.pythonhosted.org/packages/4b/94/687a0ad8afd17e4bce1892145d6a1111e58987ddb176810d02a1f3f18686/ormsgpack-1.10.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:33afe143a7b61ad21bb60109a86bb4e87fec70ef35db76b89c65b17e32da7935", size = 479076, upload-time = "2025-05-24T19:07:37.533Z" }, + { url = "https://files.pythonhosted.org/packages/c8/34/68925232e81e0e062a2f0ac678f62aa3b6f7009d6a759e19324dbbaebae7/ormsgpack-1.10.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f23d45080846a7b90feabec0d330a9cc1863dc956728412e4f7986c80ab3a668", size = 390446, upload-time = "2025-05-24T19:07:39.469Z" }, + { url = "https://files.pythonhosted.org/packages/12/ad/f4e1a36a6d1714afb7ffb74b3ababdcb96529cf4e7a216f9f7c8eda837b6/ormsgpack-1.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:534d18acb805c75e5fba09598bf40abe1851c853247e61dda0c01f772234da69", size = 121399, upload-time = "2025-05-24T19:07:40.854Z" }, ] [[package]] @@ -1671,16 +1741,16 @@ wheels = [ [[package]] name = "protobuf" -version = "5.29.4" +version = "5.29.5" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/17/7d/b9dca7365f0e2c4fa7c193ff795427cfa6290147e5185ab11ece280a18e7/protobuf-5.29.4.tar.gz", hash = "sha256:4f1dfcd7997b31ef8f53ec82781ff434a28bf71d9102ddde14d076adcfc78c99", size = 424902, upload-time = "2025-03-19T21:23:24.25Z" } +sdist = { url = "https://files.pythonhosted.org/packages/43/29/d09e70352e4e88c9c7a198d5645d7277811448d76c23b00345670f7c8a38/protobuf-5.29.5.tar.gz", hash = "sha256:bc1463bafd4b0929216c35f437a8e28731a2b7fe3d98bb77a600efced5a15c84", size = 425226, upload-time = "2025-05-28T23:51:59.82Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9a/b2/043a1a1a20edd134563699b0e91862726a0dc9146c090743b6c44d798e75/protobuf-5.29.4-cp310-abi3-win32.whl", hash = "sha256:13eb236f8eb9ec34e63fc8b1d6efd2777d062fa6aaa68268fb67cf77f6839ad7", size = 422709, upload-time = "2025-03-19T21:23:08.293Z" }, - { url = "https://files.pythonhosted.org/packages/79/fc/2474b59570daa818de6124c0a15741ee3e5d6302e9d6ce0bdfd12e98119f/protobuf-5.29.4-cp310-abi3-win_amd64.whl", hash = "sha256:bcefcdf3976233f8a502d265eb65ea740c989bacc6c30a58290ed0e519eb4b8d", size = 434506, upload-time = "2025-03-19T21:23:11.253Z" }, - { url = "https://files.pythonhosted.org/packages/46/de/7c126bbb06aa0f8a7b38aaf8bd746c514d70e6a2a3f6dd460b3b7aad7aae/protobuf-5.29.4-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:307ecba1d852ec237e9ba668e087326a67564ef83e45a0189a772ede9e854dd0", size = 417826, upload-time = "2025-03-19T21:23:13.132Z" }, - { url = "https://files.pythonhosted.org/packages/a2/b5/bade14ae31ba871a139aa45e7a8183d869efe87c34a4850c87b936963261/protobuf-5.29.4-cp38-abi3-manylinux2014_aarch64.whl", hash = "sha256:aec4962f9ea93c431d5714ed1be1c93f13e1a8618e70035ba2b0564d9e633f2e", size = 319574, upload-time = "2025-03-19T21:23:14.531Z" }, - { url = "https://files.pythonhosted.org/packages/46/88/b01ed2291aae68b708f7d334288ad5fb3e7aa769a9c309c91a0d55cb91b0/protobuf-5.29.4-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:d7d3f7d1d5a66ed4942d4fefb12ac4b14a29028b209d4bfb25c68ae172059922", size = 319672, upload-time = "2025-03-19T21:23:15.839Z" }, - { url = "https://files.pythonhosted.org/packages/12/fb/a586e0c973c95502e054ac5f81f88394f24ccc7982dac19c515acd9e2c93/protobuf-5.29.4-py3-none-any.whl", hash = "sha256:3fde11b505e1597f71b875ef2fc52062b6a9740e5f7c8997ce878b6009145862", size = 172551, upload-time = "2025-03-19T21:23:22.682Z" }, + { url = "https://files.pythonhosted.org/packages/5f/11/6e40e9fc5bba02988a214c07cf324595789ca7820160bfd1f8be96e48539/protobuf-5.29.5-cp310-abi3-win32.whl", hash = "sha256:3f1c6468a2cfd102ff4703976138844f78ebd1fb45f49011afc5139e9e283079", size = 422963, upload-time = "2025-05-28T23:51:41.204Z" }, + { url = "https://files.pythonhosted.org/packages/81/7f/73cefb093e1a2a7c3ffd839e6f9fcafb7a427d300c7f8aef9c64405d8ac6/protobuf-5.29.5-cp310-abi3-win_amd64.whl", hash = "sha256:3f76e3a3675b4a4d867b52e4a5f5b78a2ef9565549d4037e06cf7b0942b1d3fc", size = 434818, upload-time = "2025-05-28T23:51:44.297Z" }, + { url = "https://files.pythonhosted.org/packages/dd/73/10e1661c21f139f2c6ad9b23040ff36fee624310dc28fba20d33fdae124c/protobuf-5.29.5-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:e38c5add5a311f2a6eb0340716ef9b039c1dfa428b28f25a7838ac329204a671", size = 418091, upload-time = "2025-05-28T23:51:45.907Z" }, + { url = "https://files.pythonhosted.org/packages/6c/04/98f6f8cf5b07ab1294c13f34b4e69b3722bb609c5b701d6c169828f9f8aa/protobuf-5.29.5-cp38-abi3-manylinux2014_aarch64.whl", hash = "sha256:fa18533a299d7ab6c55a238bf8629311439995f2e7eca5caaff08663606e9015", size = 319824, upload-time = "2025-05-28T23:51:47.545Z" }, + { url = "https://files.pythonhosted.org/packages/85/e4/07c80521879c2d15f321465ac24c70efe2381378c00bf5e56a0f4fbac8cd/protobuf-5.29.5-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:63848923da3325e1bf7e9003d680ce6e14b07e55d0473253a690c3a8b8fd6e61", size = 319942, upload-time = "2025-05-28T23:51:49.11Z" }, + { url = "https://files.pythonhosted.org/packages/7e/cc/7e77861000a0691aeea8f4566e5d3aa716f2b1dece4a24439437e41d3d25/protobuf-5.29.5-py3-none-any.whl", hash = "sha256:6cf42630262c59b2d8de33954443d94b746c952b01434fc58a417fdbd2e84bd5", size = 172823, upload-time = "2025-05-28T23:51:58.157Z" }, ] [[package]] @@ -1741,16 +1811,17 @@ wheels = [ [[package]] name = "pydantic" -version = "2.10.6" +version = "2.11.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-types" }, { name = "pydantic-core" }, { name = "typing-extensions" }, + { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b7/ae/d5220c5c52b158b1de7ca89fc5edb72f304a70a4c540c84c8844bf4008de/pydantic-2.10.6.tar.gz", hash = "sha256:ca5daa827cce33de7a42be142548b0096bf05a7e7b365aebfa5f8eeec7128236", size = 761681, upload-time = "2025-01-24T01:42:12.693Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f0/86/8ce9040065e8f924d642c58e4a344e33163a07f6b57f836d0d734e0ad3fb/pydantic-2.11.5.tar.gz", hash = "sha256:7f853db3d0ce78ce8bbb148c401c2cdd6431b3473c0cdff2755c7690952a7b7a", size = 787102, upload-time = "2025-05-22T21:18:08.761Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f4/3c/8cc1cc84deffa6e25d2d0c688ebb80635dfdbf1dbea3e30c541c8cf4d860/pydantic-2.10.6-py3-none-any.whl", hash = "sha256:427d664bf0b8a2b34ff5dd0f5a18df00591adcee7198fbd71981054cef37b584", size = 431696, upload-time = "2025-01-24T01:42:10.371Z" }, + { url = "https://files.pythonhosted.org/packages/b5/69/831ed22b38ff9b4b64b66569f0e5b7b97cf3638346eb95a2147fdb49ad5f/pydantic-2.11.5-py3-none-any.whl", hash = "sha256:f9c26ba06f9747749ca1e5c94d6a85cb84254577553c8785576fd38fa64dc0f7", size = 444229, upload-time = "2025-05-22T21:18:06.329Z" }, ] [[package]] @@ -1802,7 +1873,7 @@ requires-dist = [ [[package]] name = "pydantic-ai-slim" -version = "0.2.15" +version = "0.2.16" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "eval-type-backport" }, @@ -1813,9 +1884,9 @@ dependencies = [ { name = "pydantic-graph" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b3/7d/24a128014c647f047611eea7fa4e18494240eafbc9565fcebafe1a188102/pydantic_ai_slim-0.2.15.tar.gz", hash = "sha256:5c98d1ef74da49d7566c39664cf00d993eb1c167e5a896f833155dffc65a5e82", size = 138333, upload-time = "2025-06-05T19:37:04.175Z" } +sdist = { url = "https://files.pythonhosted.org/packages/97/1d/159d6707c14672ab35072dbf1d22214eb45993fa4f4a89b946e8bb915417/pydantic_ai_slim-0.2.16.tar.gz", hash = "sha256:fcd8d2d596d554f8471e64909e3a354172a174ede2e09f898557bd2cf3142f6a", size = 139744, upload-time = "2025-06-08T20:09:50.309Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/29/7a/36696ef1284fd8c54c09aea0d9dba87cecfae6f3fe1c561e17cdd843f602/pydantic_ai_slim-0.2.15-py3-none-any.whl", hash = "sha256:33f6211af4b4eaa9cb5139e01f0881a7b91cd42dbfde185ad97f1fd457f41844", size = 186703, upload-time = "2025-06-05T19:36:51.998Z" }, + { url = "https://files.pythonhosted.org/packages/5f/f6/55ff99c2c3512ca9140c5d7d3260937c4c742b8b1577a84fc6592974959d/pydantic_ai_slim-0.2.16-py3-none-any.whl", hash = "sha256:bcc2691003ca982e64f0fbcf207968e45b97fc91b4e113bfeabf67e49f8a9dd5", size = 189409, upload-time = "2025-06-08T20:09:38.341Z" }, ] [package.optional-dependencies] @@ -1825,32 +1896,35 @@ mcp = [ [[package]] name = "pydantic-core" -version = "2.27.2" +version = "2.33.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fc/01/f3e5ac5e7c25833db5eb555f7b7ab24cd6f8c322d3a3ad2d67a952dc0abc/pydantic_core-2.27.2.tar.gz", hash = "sha256:eb026e5a4c1fee05726072337ff51d1efb6f59090b7da90d30ea58625b1ffb39", size = 413443, upload-time = "2024-12-18T11:31:54.917Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ad/88/5f2260bdfae97aabf98f1778d43f69574390ad787afb646292a638c923d4/pydantic_core-2.33.2.tar.gz", hash = "sha256:7cb8bc3605c29176e1b105350d2e6474142d7c1bd1d9327c4a9bdb46bf827acc", size = 435195, upload-time = "2025-04-23T18:33:52.104Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/41/b1/9bc383f48f8002f99104e3acff6cba1231b29ef76cfa45d1506a5cad1f84/pydantic_core-2.27.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7d14bd329640e63852364c306f4d23eb744e0f8193148d4044dd3dacdaacbd8b", size = 1892709, upload-time = "2024-12-18T11:29:03.193Z" }, - { url = "https://files.pythonhosted.org/packages/10/6c/e62b8657b834f3eb2961b49ec8e301eb99946245e70bf42c8817350cbefc/pydantic_core-2.27.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:82f91663004eb8ed30ff478d77c4d1179b3563df6cdb15c0817cd1cdaf34d154", size = 1811273, upload-time = "2024-12-18T11:29:05.306Z" }, - { url = "https://files.pythonhosted.org/packages/ba/15/52cfe49c8c986e081b863b102d6b859d9defc63446b642ccbbb3742bf371/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71b24c7d61131bb83df10cc7e687433609963a944ccf45190cfc21e0887b08c9", size = 1823027, upload-time = "2024-12-18T11:29:07.294Z" }, - { url = "https://files.pythonhosted.org/packages/b1/1c/b6f402cfc18ec0024120602bdbcebc7bdd5b856528c013bd4d13865ca473/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fa8e459d4954f608fa26116118bb67f56b93b209c39b008277ace29937453dc9", size = 1868888, upload-time = "2024-12-18T11:29:09.249Z" }, - { url = "https://files.pythonhosted.org/packages/bd/7b/8cb75b66ac37bc2975a3b7de99f3c6f355fcc4d89820b61dffa8f1e81677/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce8918cbebc8da707ba805b7fd0b382816858728ae7fe19a942080c24e5b7cd1", size = 2037738, upload-time = "2024-12-18T11:29:11.23Z" }, - { url = "https://files.pythonhosted.org/packages/c8/f1/786d8fe78970a06f61df22cba58e365ce304bf9b9f46cc71c8c424e0c334/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eda3f5c2a021bbc5d976107bb302e0131351c2ba54343f8a496dc8783d3d3a6a", size = 2685138, upload-time = "2024-12-18T11:29:16.396Z" }, - { url = "https://files.pythonhosted.org/packages/a6/74/d12b2cd841d8724dc8ffb13fc5cef86566a53ed358103150209ecd5d1999/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd8086fa684c4775c27f03f062cbb9eaa6e17f064307e86b21b9e0abc9c0f02e", size = 1997025, upload-time = "2024-12-18T11:29:20.25Z" }, - { url = "https://files.pythonhosted.org/packages/a0/6e/940bcd631bc4d9a06c9539b51f070b66e8f370ed0933f392db6ff350d873/pydantic_core-2.27.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8d9b3388db186ba0c099a6d20f0604a44eabdeef1777ddd94786cdae158729e4", size = 2004633, upload-time = "2024-12-18T11:29:23.877Z" }, - { url = "https://files.pythonhosted.org/packages/50/cc/a46b34f1708d82498c227d5d80ce615b2dd502ddcfd8376fc14a36655af1/pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7a66efda2387de898c8f38c0cf7f14fca0b51a8ef0b24bfea5849f1b3c95af27", size = 1999404, upload-time = "2024-12-18T11:29:25.872Z" }, - { url = "https://files.pythonhosted.org/packages/ca/2d/c365cfa930ed23bc58c41463bae347d1005537dc8db79e998af8ba28d35e/pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:18a101c168e4e092ab40dbc2503bdc0f62010e95d292b27827871dc85450d7ee", size = 2130130, upload-time = "2024-12-18T11:29:29.252Z" }, - { url = "https://files.pythonhosted.org/packages/f4/d7/eb64d015c350b7cdb371145b54d96c919d4db516817f31cd1c650cae3b21/pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ba5dd002f88b78a4215ed2f8ddbdf85e8513382820ba15ad5ad8955ce0ca19a1", size = 2157946, upload-time = "2024-12-18T11:29:31.338Z" }, - { url = "https://files.pythonhosted.org/packages/a4/99/bddde3ddde76c03b65dfd5a66ab436c4e58ffc42927d4ff1198ffbf96f5f/pydantic_core-2.27.2-cp313-cp313-win32.whl", hash = "sha256:1ebaf1d0481914d004a573394f4be3a7616334be70261007e47c2a6fe7e50130", size = 1834387, upload-time = "2024-12-18T11:29:33.481Z" }, - { url = "https://files.pythonhosted.org/packages/71/47/82b5e846e01b26ac6f1893d3c5f9f3a2eb6ba79be26eef0b759b4fe72946/pydantic_core-2.27.2-cp313-cp313-win_amd64.whl", hash = "sha256:953101387ecf2f5652883208769a79e48db18c6df442568a0b5ccd8c2723abee", size = 1990453, upload-time = "2024-12-18T11:29:35.533Z" }, - { url = "https://files.pythonhosted.org/packages/51/b2/b2b50d5ecf21acf870190ae5d093602d95f66c9c31f9d5de6062eb329ad1/pydantic_core-2.27.2-cp313-cp313-win_arm64.whl", hash = "sha256:ac4dbfd1691affb8f48c2c13241a2e3b60ff23247cbcf981759c768b6633cf8b", size = 1885186, upload-time = "2024-12-18T11:29:37.649Z" }, + { url = "https://files.pythonhosted.org/packages/46/8c/99040727b41f56616573a28771b1bfa08a3d3fe74d3d513f01251f79f172/pydantic_core-2.33.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1082dd3e2d7109ad8b7da48e1d4710c8d06c253cbc4a27c1cff4fbcaa97a9e3f", size = 2015688, upload-time = "2025-04-23T18:31:53.175Z" }, + { url = "https://files.pythonhosted.org/packages/3a/cc/5999d1eb705a6cefc31f0b4a90e9f7fc400539b1a1030529700cc1b51838/pydantic_core-2.33.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f517ca031dfc037a9c07e748cefd8d96235088b83b4f4ba8939105d20fa1dcd6", size = 1844808, upload-time = "2025-04-23T18:31:54.79Z" }, + { url = "https://files.pythonhosted.org/packages/6f/5e/a0a7b8885c98889a18b6e376f344da1ef323d270b44edf8174d6bce4d622/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a9f2c9dd19656823cb8250b0724ee9c60a82f3cdf68a080979d13092a3b0fef", size = 1885580, upload-time = "2025-04-23T18:31:57.393Z" }, + { url = "https://files.pythonhosted.org/packages/3b/2a/953581f343c7d11a304581156618c3f592435523dd9d79865903272c256a/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b0a451c263b01acebe51895bfb0e1cc842a5c666efe06cdf13846c7418caa9a", size = 1973859, upload-time = "2025-04-23T18:31:59.065Z" }, + { url = "https://files.pythonhosted.org/packages/e6/55/f1a813904771c03a3f97f676c62cca0c0a4138654107c1b61f19c644868b/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ea40a64d23faa25e62a70ad163571c0b342b8bf66d5fa612ac0dec4f069d916", size = 2120810, upload-time = "2025-04-23T18:32:00.78Z" }, + { url = "https://files.pythonhosted.org/packages/aa/c3/053389835a996e18853ba107a63caae0b9deb4a276c6b472931ea9ae6e48/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fb2d542b4d66f9470e8065c5469ec676978d625a8b7a363f07d9a501a9cb36a", size = 2676498, upload-time = "2025-04-23T18:32:02.418Z" }, + { url = "https://files.pythonhosted.org/packages/eb/3c/f4abd740877a35abade05e437245b192f9d0ffb48bbbbd708df33d3cda37/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdac5d6ffa1b5a83bca06ffe7583f5576555e6c8b3a91fbd25ea7780f825f7d", size = 2000611, upload-time = "2025-04-23T18:32:04.152Z" }, + { url = "https://files.pythonhosted.org/packages/59/a7/63ef2fed1837d1121a894d0ce88439fe3e3b3e48c7543b2a4479eb99c2bd/pydantic_core-2.33.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:04a1a413977ab517154eebb2d326da71638271477d6ad87a769102f7c2488c56", size = 2107924, upload-time = "2025-04-23T18:32:06.129Z" }, + { url = "https://files.pythonhosted.org/packages/04/8f/2551964ef045669801675f1cfc3b0d74147f4901c3ffa42be2ddb1f0efc4/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c8e7af2f4e0194c22b5b37205bfb293d166a7344a5b0d0eaccebc376546d77d5", size = 2063196, upload-time = "2025-04-23T18:32:08.178Z" }, + { url = "https://files.pythonhosted.org/packages/26/bd/d9602777e77fc6dbb0c7db9ad356e9a985825547dce5ad1d30ee04903918/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:5c92edd15cd58b3c2d34873597a1e20f13094f59cf88068adb18947df5455b4e", size = 2236389, upload-time = "2025-04-23T18:32:10.242Z" }, + { url = "https://files.pythonhosted.org/packages/42/db/0e950daa7e2230423ab342ae918a794964b053bec24ba8af013fc7c94846/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:65132b7b4a1c0beded5e057324b7e16e10910c106d43675d9bd87d4f38dde162", size = 2239223, upload-time = "2025-04-23T18:32:12.382Z" }, + { url = "https://files.pythonhosted.org/packages/58/4d/4f937099c545a8a17eb52cb67fe0447fd9a373b348ccfa9a87f141eeb00f/pydantic_core-2.33.2-cp313-cp313-win32.whl", hash = "sha256:52fb90784e0a242bb96ec53f42196a17278855b0f31ac7c3cc6f5c1ec4811849", size = 1900473, upload-time = "2025-04-23T18:32:14.034Z" }, + { url = "https://files.pythonhosted.org/packages/a0/75/4a0a9bac998d78d889def5e4ef2b065acba8cae8c93696906c3a91f310ca/pydantic_core-2.33.2-cp313-cp313-win_amd64.whl", hash = "sha256:c083a3bdd5a93dfe480f1125926afcdbf2917ae714bdb80b36d34318b2bec5d9", size = 1955269, upload-time = "2025-04-23T18:32:15.783Z" }, + { url = "https://files.pythonhosted.org/packages/f9/86/1beda0576969592f1497b4ce8e7bc8cbdf614c352426271b1b10d5f0aa64/pydantic_core-2.33.2-cp313-cp313-win_arm64.whl", hash = "sha256:e80b087132752f6b3d714f041ccf74403799d3b23a72722ea2e6ba2e892555b9", size = 1893921, upload-time = "2025-04-23T18:32:18.473Z" }, + { url = "https://files.pythonhosted.org/packages/a4/7d/e09391c2eebeab681df2b74bfe6c43422fffede8dc74187b2b0bf6fd7571/pydantic_core-2.33.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61c18fba8e5e9db3ab908620af374db0ac1baa69f0f32df4f61ae23f15e586ac", size = 1806162, upload-time = "2025-04-23T18:32:20.188Z" }, + { url = "https://files.pythonhosted.org/packages/f1/3d/847b6b1fed9f8ed3bb95a9ad04fbd0b212e832d4f0f50ff4d9ee5a9f15cf/pydantic_core-2.33.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95237e53bb015f67b63c91af7518a62a8660376a6a0db19b89acc77a4d6199f5", size = 1981560, upload-time = "2025-04-23T18:32:22.354Z" }, + { url = "https://files.pythonhosted.org/packages/6f/9a/e73262f6c6656262b5fdd723ad90f518f579b7bc8622e43a942eec53c938/pydantic_core-2.33.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9", size = 1935777, upload-time = "2025-04-23T18:32:25.088Z" }, ] [[package]] name = "pydantic-evals" -version = "0.2.15" +version = "0.2.16" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, @@ -1860,9 +1934,9 @@ dependencies = [ { name = "pyyaml" }, { name = "rich" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d4/61/060a56a8d00886df3fecd02314b024370871fb15195c23f9591d6498c032/pydantic_evals-0.2.15.tar.gz", hash = "sha256:82fe113b58af51f0c1b44d36c8ab52e67970421a8bc5b837b5124c19263a0ecc", size = 42908, upload-time = "2025-06-05T19:37:05.217Z" } +sdist = { url = "https://files.pythonhosted.org/packages/9a/90/70774af4ebe568bc9fa3c9b8de0da3ddb95f40dec4ede20f993d88ff86c6/pydantic_evals-0.2.16.tar.gz", hash = "sha256:b9011f5939cdaf5f8b0e470df964142c3305abc4741f53cab09fbc44926b86e8", size = 42906, upload-time = "2025-06-08T20:09:51.37Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/80/89/930acc3c98db21b3a1e8f0f9dd39958ea4a34ad54cbe6c91f8bb3bded2c3/pydantic_evals-0.2.15-py3-none-any.whl", hash = "sha256:b74aeffe9858fd71215e9eae4c025b39d3a2fa445f9f1ea57067936fc4ead418", size = 51643, upload-time = "2025-06-05T19:36:53.632Z" }, + { url = "https://files.pythonhosted.org/packages/00/8f/c5812d1d5a1889c4802aa4effdb2cd306ae0134a3b00187c83cb9d0e575f/pydantic_evals-0.2.16-py3-none-any.whl", hash = "sha256:76ea6255e3154a75447d3c35ad5bc4c553123ac098677c65f30807de1eec1d51", size = 51643, upload-time = "2025-06-08T20:09:39.902Z" }, ] [package.optional-dependencies] @@ -1872,7 +1946,7 @@ logfire = [ [[package]] name = "pydantic-graph" -version = "0.2.15" +version = "0.2.16" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "httpx" }, @@ -1880,9 +1954,9 @@ dependencies = [ { name = "pydantic" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/68/c3/af16b3ca23dc4029cbb8453e81c87079897534402c0da8a548118682ef43/pydantic_graph-0.2.15.tar.gz", hash = "sha256:47d19fa047ed996e7f448ef493462444f626da1046f8cf9bddc2567732e8336f", size = 21844, upload-time = "2025-06-05T19:37:06.378Z" } +sdist = { url = "https://files.pythonhosted.org/packages/6c/8a/ea3ed8c500e7092d9da32b3a54b02db7e79a8d675f5b920f48a807a5d84f/pydantic_graph-0.2.16.tar.gz", hash = "sha256:c98e254d5239767d3a27068fed017717039241829731480d892f66a0e25b9ce7", size = 21844, upload-time = "2025-06-08T20:09:52.328Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4c/f6/94bc071815298a4de961d7e683ea495803fb3a4fae799675c7c80a66983f/pydantic_graph-0.2.15-py3-none-any.whl", hash = "sha256:7bbc9e5eb39ca6f79b51e270c255c5811d3d5b7ee8b9c36e64ad00577ecef365", size = 27486, upload-time = "2025-06-05T19:36:54.967Z" }, + { url = "https://files.pythonhosted.org/packages/08/28/b1e8d0ec29ae8b3a3c5a74a2ec51034994a464a847127b048be278983402/pydantic_graph-0.2.16-py3-none-any.whl", hash = "sha256:56ea080a479bda04be6cffb9a2ea0173e16826efa47e93b8b4e20a66ae067e33", size = 27487, upload-time = "2025-06-08T20:09:41.217Z" }, ] [[package]] @@ -2088,54 +2162,54 @@ wheels = [ [[package]] name = "ruff" -version = "0.11.10" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e8/4c/4a3c5a97faaae6b428b336dcca81d03ad04779f8072c267ad2bd860126bf/ruff-0.11.10.tar.gz", hash = "sha256:d522fb204b4959909ecac47da02830daec102eeb100fb50ea9554818d47a5fa6", size = 4165632, upload-time = "2025-05-15T14:08:56.76Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2f/9f/596c628f8824a2ce4cd12b0f0b4c0629a62dfffc5d0f742c19a1d71be108/ruff-0.11.10-py3-none-linux_armv6l.whl", hash = "sha256:859a7bfa7bc8888abbea31ef8a2b411714e6a80f0d173c2a82f9041ed6b50f58", size = 10316243, upload-time = "2025-05-15T14:08:12.884Z" }, - { url = "https://files.pythonhosted.org/packages/3c/38/c1e0b77ab58b426f8c332c1d1d3432d9fc9a9ea622806e208220cb133c9e/ruff-0.11.10-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:968220a57e09ea5e4fd48ed1c646419961a0570727c7e069842edd018ee8afed", size = 11083636, upload-time = "2025-05-15T14:08:16.551Z" }, - { url = "https://files.pythonhosted.org/packages/23/41/b75e15961d6047d7fe1b13886e56e8413be8467a4e1be0a07f3b303cd65a/ruff-0.11.10-py3-none-macosx_11_0_arm64.whl", hash = "sha256:1067245bad978e7aa7b22f67113ecc6eb241dca0d9b696144256c3a879663bca", size = 10441624, upload-time = "2025-05-15T14:08:19.032Z" }, - { url = "https://files.pythonhosted.org/packages/b6/2c/e396b6703f131406db1811ea3d746f29d91b41bbd43ad572fea30da1435d/ruff-0.11.10-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4854fd09c7aed5b1590e996a81aeff0c9ff51378b084eb5a0b9cd9518e6cff2", size = 10624358, upload-time = "2025-05-15T14:08:21.542Z" }, - { url = "https://files.pythonhosted.org/packages/bd/8c/ee6cca8bdaf0f9a3704796022851a33cd37d1340bceaf4f6e991eb164e2e/ruff-0.11.10-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8b4564e9f99168c0f9195a0fd5fa5928004b33b377137f978055e40008a082c5", size = 10176850, upload-time = "2025-05-15T14:08:23.682Z" }, - { url = "https://files.pythonhosted.org/packages/e9/ce/4e27e131a434321b3b7c66512c3ee7505b446eb1c8a80777c023f7e876e6/ruff-0.11.10-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5b6a9cc5b62c03cc1fea0044ed8576379dbaf751d5503d718c973d5418483641", size = 11759787, upload-time = "2025-05-15T14:08:25.733Z" }, - { url = "https://files.pythonhosted.org/packages/58/de/1e2e77fc72adc7cf5b5123fd04a59ed329651d3eab9825674a9e640b100b/ruff-0.11.10-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:607ecbb6f03e44c9e0a93aedacb17b4eb4f3563d00e8b474298a201622677947", size = 12430479, upload-time = "2025-05-15T14:08:28.013Z" }, - { url = "https://files.pythonhosted.org/packages/07/ed/af0f2340f33b70d50121628ef175523cc4c37619e98d98748c85764c8d88/ruff-0.11.10-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7b3a522fa389402cd2137df9ddefe848f727250535c70dafa840badffb56b7a4", size = 11919760, upload-time = "2025-05-15T14:08:30.956Z" }, - { url = "https://files.pythonhosted.org/packages/24/09/d7b3d3226d535cb89234390f418d10e00a157b6c4a06dfbe723e9322cb7d/ruff-0.11.10-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2f071b0deed7e9245d5820dac235cbdd4ef99d7b12ff04c330a241ad3534319f", size = 14041747, upload-time = "2025-05-15T14:08:33.297Z" }, - { url = "https://files.pythonhosted.org/packages/62/b3/a63b4e91850e3f47f78795e6630ee9266cb6963de8f0191600289c2bb8f4/ruff-0.11.10-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a60e3a0a617eafba1f2e4186d827759d65348fa53708ca547e384db28406a0b", size = 11550657, upload-time = "2025-05-15T14:08:35.639Z" }, - { url = "https://files.pythonhosted.org/packages/46/63/a4f95c241d79402ccdbdb1d823d156c89fbb36ebfc4289dce092e6c0aa8f/ruff-0.11.10-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:da8ec977eaa4b7bf75470fb575bea2cb41a0e07c7ea9d5a0a97d13dbca697bf2", size = 10489671, upload-time = "2025-05-15T14:08:38.437Z" }, - { url = "https://files.pythonhosted.org/packages/6a/9b/c2238bfebf1e473495659c523d50b1685258b6345d5ab0b418ca3f010cd7/ruff-0.11.10-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:ddf8967e08227d1bd95cc0851ef80d2ad9c7c0c5aab1eba31db49cf0a7b99523", size = 10160135, upload-time = "2025-05-15T14:08:41.247Z" }, - { url = "https://files.pythonhosted.org/packages/ba/ef/ba7251dd15206688dbfba7d413c0312e94df3b31b08f5d695580b755a899/ruff-0.11.10-py3-none-musllinux_1_2_i686.whl", hash = "sha256:5a94acf798a82db188f6f36575d80609072b032105d114b0f98661e1679c9125", size = 11170179, upload-time = "2025-05-15T14:08:43.762Z" }, - { url = "https://files.pythonhosted.org/packages/73/9f/5c336717293203ba275dbfa2ea16e49b29a9fd9a0ea8b6febfc17e133577/ruff-0.11.10-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:3afead355f1d16d95630df28d4ba17fb2cb9c8dfac8d21ced14984121f639bad", size = 11626021, upload-time = "2025-05-15T14:08:46.451Z" }, - { url = "https://files.pythonhosted.org/packages/d9/2b/162fa86d2639076667c9aa59196c020dc6d7023ac8f342416c2f5ec4bda0/ruff-0.11.10-py3-none-win32.whl", hash = "sha256:dc061a98d32a97211af7e7f3fa1d4ca2fcf919fb96c28f39551f35fc55bdbc19", size = 10494958, upload-time = "2025-05-15T14:08:49.601Z" }, - { url = "https://files.pythonhosted.org/packages/24/f3/66643d8f32f50a4b0d09a4832b7d919145ee2b944d43e604fbd7c144d175/ruff-0.11.10-py3-none-win_amd64.whl", hash = "sha256:5cc725fbb4d25b0f185cb42df07ab6b76c4489b4bfb740a175f3a59c70e8a224", size = 11650285, upload-time = "2025-05-15T14:08:52.392Z" }, - { url = "https://files.pythonhosted.org/packages/95/3a/2e8704d19f376c799748ff9cb041225c1d59f3e7711bc5596c8cfdc24925/ruff-0.11.10-py3-none-win_arm64.whl", hash = "sha256:ef69637b35fb8b210743926778d0e45e1bffa850a7c61e428c6b971549b5f5d1", size = 10765278, upload-time = "2025-05-15T14:08:54.56Z" }, +version = "0.11.13" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ed/da/9c6f995903b4d9474b39da91d2d626659af3ff1eeb43e9ae7c119349dba6/ruff-0.11.13.tar.gz", hash = "sha256:26fa247dc68d1d4e72c179e08889a25ac0c7ba4d78aecfc835d49cbfd60bf514", size = 4282054, upload-time = "2025-06-05T21:00:15.721Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7d/ce/a11d381192966e0b4290842cc8d4fac7dc9214ddf627c11c1afff87da29b/ruff-0.11.13-py3-none-linux_armv6l.whl", hash = "sha256:4bdfbf1240533f40042ec00c9e09a3aade6f8c10b6414cf11b519488d2635d46", size = 10292516, upload-time = "2025-06-05T20:59:32.944Z" }, + { url = "https://files.pythonhosted.org/packages/78/db/87c3b59b0d4e753e40b6a3b4a2642dfd1dcaefbff121ddc64d6c8b47ba00/ruff-0.11.13-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:aef9c9ed1b5ca28bb15c7eac83b8670cf3b20b478195bd49c8d756ba0a36cf48", size = 11106083, upload-time = "2025-06-05T20:59:37.03Z" }, + { url = "https://files.pythonhosted.org/packages/77/79/d8cec175856ff810a19825d09ce700265f905c643c69f45d2b737e4a470a/ruff-0.11.13-py3-none-macosx_11_0_arm64.whl", hash = "sha256:53b15a9dfdce029c842e9a5aebc3855e9ab7771395979ff85b7c1dedb53ddc2b", size = 10436024, upload-time = "2025-06-05T20:59:39.741Z" }, + { url = "https://files.pythonhosted.org/packages/8b/5b/f6d94f2980fa1ee854b41568368a2e1252681b9238ab2895e133d303538f/ruff-0.11.13-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ab153241400789138d13f362c43f7edecc0edfffce2afa6a68434000ecd8f69a", size = 10646324, upload-time = "2025-06-05T20:59:42.185Z" }, + { url = "https://files.pythonhosted.org/packages/6c/9c/b4c2acf24ea4426016d511dfdc787f4ce1ceb835f3c5fbdbcb32b1c63bda/ruff-0.11.13-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6c51f93029d54a910d3d24f7dd0bb909e31b6cd989a5e4ac513f4eb41629f0dc", size = 10174416, upload-time = "2025-06-05T20:59:44.319Z" }, + { url = "https://files.pythonhosted.org/packages/f3/10/e2e62f77c65ede8cd032c2ca39c41f48feabedb6e282bfd6073d81bb671d/ruff-0.11.13-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1808b3ed53e1a777c2ef733aca9051dc9bf7c99b26ece15cb59a0320fbdbd629", size = 11724197, upload-time = "2025-06-05T20:59:46.935Z" }, + { url = "https://files.pythonhosted.org/packages/bb/f0/466fe8469b85c561e081d798c45f8a1d21e0b4a5ef795a1d7f1a9a9ec182/ruff-0.11.13-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:d28ce58b5ecf0f43c1b71edffabe6ed7f245d5336b17805803312ec9bc665933", size = 12511615, upload-time = "2025-06-05T20:59:49.534Z" }, + { url = "https://files.pythonhosted.org/packages/17/0e/cefe778b46dbd0cbcb03a839946c8f80a06f7968eb298aa4d1a4293f3448/ruff-0.11.13-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:55e4bc3a77842da33c16d55b32c6cac1ec5fb0fbec9c8c513bdce76c4f922165", size = 12117080, upload-time = "2025-06-05T20:59:51.654Z" }, + { url = "https://files.pythonhosted.org/packages/5d/2c/caaeda564cbe103bed145ea557cb86795b18651b0f6b3ff6a10e84e5a33f/ruff-0.11.13-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:633bf2c6f35678c56ec73189ba6fa19ff1c5e4807a78bf60ef487b9dd272cc71", size = 11326315, upload-time = "2025-06-05T20:59:54.469Z" }, + { url = "https://files.pythonhosted.org/packages/75/f0/782e7d681d660eda8c536962920c41309e6dd4ebcea9a2714ed5127d44bd/ruff-0.11.13-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4ffbc82d70424b275b089166310448051afdc6e914fdab90e08df66c43bb5ca9", size = 11555640, upload-time = "2025-06-05T20:59:56.986Z" }, + { url = "https://files.pythonhosted.org/packages/5d/d4/3d580c616316c7f07fb3c99dbecfe01fbaea7b6fd9a82b801e72e5de742a/ruff-0.11.13-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:4a9ddd3ec62a9a89578c85842b836e4ac832d4a2e0bfaad3b02243f930ceafcc", size = 10507364, upload-time = "2025-06-05T20:59:59.154Z" }, + { url = "https://files.pythonhosted.org/packages/5a/dc/195e6f17d7b3ea6b12dc4f3e9de575db7983db187c378d44606e5d503319/ruff-0.11.13-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:d237a496e0778d719efb05058c64d28b757c77824e04ffe8796c7436e26712b7", size = 10141462, upload-time = "2025-06-05T21:00:01.481Z" }, + { url = "https://files.pythonhosted.org/packages/f4/8e/39a094af6967faa57ecdeacb91bedfb232474ff8c3d20f16a5514e6b3534/ruff-0.11.13-py3-none-musllinux_1_2_i686.whl", hash = "sha256:26816a218ca6ef02142343fd24c70f7cd8c5aa6c203bca284407adf675984432", size = 11121028, upload-time = "2025-06-05T21:00:04.06Z" }, + { url = "https://files.pythonhosted.org/packages/5a/c0/b0b508193b0e8a1654ec683ebab18d309861f8bd64e3a2f9648b80d392cb/ruff-0.11.13-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:51c3f95abd9331dc5b87c47ac7f376db5616041173826dfd556cfe3d4977f492", size = 11602992, upload-time = "2025-06-05T21:00:06.249Z" }, + { url = "https://files.pythonhosted.org/packages/7c/91/263e33ab93ab09ca06ce4f8f8547a858cc198072f873ebc9be7466790bae/ruff-0.11.13-py3-none-win32.whl", hash = "sha256:96c27935418e4e8e77a26bb05962817f28b8ef3843a6c6cc49d8783b5507f250", size = 10474944, upload-time = "2025-06-05T21:00:08.459Z" }, + { url = "https://files.pythonhosted.org/packages/46/f4/7c27734ac2073aae8efb0119cae6931b6fb48017adf048fdf85c19337afc/ruff-0.11.13-py3-none-win_amd64.whl", hash = "sha256:29c3189895a8a6a657b7af4e97d330c8a3afd2c9c8f46c81e2fc5a31866517e3", size = 11548669, upload-time = "2025-06-05T21:00:11.147Z" }, + { url = "https://files.pythonhosted.org/packages/ec/bf/b273dd11673fed8a6bd46032c0ea2a04b2ac9bfa9c628756a5856ba113b0/ruff-0.11.13-py3-none-win_arm64.whl", hash = "sha256:b4385285e9179d608ff1d2fb9922062663c658605819a6876d8beef0c30b7f3b", size = 10683928, upload-time = "2025-06-05T21:00:13.758Z" }, ] [[package]] name = "shapely" -version = "2.1.0" +version = "2.1.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fb/fe/3b0d2f828ffaceadcdcb51b75b9c62d98e62dd95ce575278de35f24a1c20/shapely-2.1.0.tar.gz", hash = "sha256:2cbe90e86fa8fc3ca8af6ffb00a77b246b918c7cf28677b7c21489b678f6b02e", size = 313617, upload-time = "2025-04-03T09:15:05.725Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ca/3c/2da625233f4e605155926566c0e7ea8dda361877f48e8b1655e53456f252/shapely-2.1.1.tar.gz", hash = "sha256:500621967f2ffe9642454808009044c21e5b35db89ce69f8a2042c2ffd0e2772", size = 315422, upload-time = "2025-05-19T11:04:41.265Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8d/77/4e368704b2193e74498473db4461d697cc6083c96f8039367e59009d78bd/shapely-2.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b64423295b563f43a043eb786e7a03200ebe68698e36d2b4b1c39f31dfb50dfb", size = 1830029, upload-time = "2025-04-03T09:14:38.795Z" }, - { url = "https://files.pythonhosted.org/packages/71/3c/d888597bda680e4de987316b05ca9db07416fa29523beff64f846503302f/shapely-2.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1b5578f45adc25b235b22d1ccb9a0348c8dc36f31983e57ea129a88f96f7b870", size = 1637999, upload-time = "2025-04-03T09:14:40.209Z" }, - { url = "https://files.pythonhosted.org/packages/03/8d/ee0e23b7ef88fba353c63a81f1f329c77f5703835db7b165e7c0b8b7f839/shapely-2.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1a7e83d383b27f02b684e50ab7f34e511c92e33b6ca164a6a9065705dd64bcb", size = 2929348, upload-time = "2025-04-03T09:14:42.11Z" }, - { url = "https://files.pythonhosted.org/packages/d1/a7/5c9cb413e4e2ce52c16be717e94abd40ce91b1f8974624d5d56154c5d40b/shapely-2.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:942031eb4d8f7b3b22f43ba42c09c7aa3d843aa10d5cc1619fe816e923b66e55", size = 3048973, upload-time = "2025-04-03T09:14:43.841Z" }, - { url = "https://files.pythonhosted.org/packages/84/23/45b90c0bd2157b238490ca56ef2eedf959d3514c7d05475f497a2c88b6d9/shapely-2.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d2843c456a2e5627ee6271800f07277c0d2652fb287bf66464571a057dbc00b3", size = 3873148, upload-time = "2025-04-03T09:14:45.924Z" }, - { url = "https://files.pythonhosted.org/packages/c0/bc/ed7d5d37f5395166042576f0c55a12d7e56102799464ba7ea3a72a38c769/shapely-2.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8c4b17469b7f39a5e6a7cfea79f38ae08a275427f41fe8b48c372e1449147908", size = 4052655, upload-time = "2025-04-03T09:14:47.475Z" }, - { url = "https://files.pythonhosted.org/packages/c0/8f/a1dafbb10d20d1c569f2db3fb1235488f624dafe8469e8ce65356800ba31/shapely-2.1.0-cp313-cp313-win32.whl", hash = "sha256:30e967abd08fce49513d4187c01b19f139084019f33bec0673e8dbeb557c45e4", size = 1526600, upload-time = "2025-04-03T09:14:48.952Z" }, - { url = "https://files.pythonhosted.org/packages/e3/f0/9f8cdf2258d7aed742459cea51c70d184de92f5d2d6f5f7f1ded90a18c31/shapely-2.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:1dc8d4364483a14aba4c844b7bd16a6fa3728887e2c33dfa1afa34a3cf4d08a5", size = 1707115, upload-time = "2025-04-03T09:14:50.445Z" }, - { url = "https://files.pythonhosted.org/packages/75/ed/32952df461753a65b3e5d24c8efb361d3a80aafaef0b70d419063f6f2c11/shapely-2.1.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:673e073fea099d1c82f666fb7ab0a00a77eff2999130a69357ce11941260d855", size = 1824847, upload-time = "2025-04-03T09:14:52.358Z" }, - { url = "https://files.pythonhosted.org/packages/ff/b9/2284de512af30b02f93ddcdd2e5c79834a3cf47fa3ca11b0f74396feb046/shapely-2.1.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:6d1513f915a56de67659fe2047c1ad5ff0f8cbff3519d1e74fced69c9cb0e7da", size = 1631035, upload-time = "2025-04-03T09:14:53.739Z" }, - { url = "https://files.pythonhosted.org/packages/35/16/a59f252a7e736b73008f10d0950ffeeb0d5953be7c0bdffd39a02a6ba310/shapely-2.1.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d6a7043178890b9e028d80496ff4c79dc7629bff4d78a2f25323b661756bab8", size = 2968639, upload-time = "2025-04-03T09:14:55.674Z" }, - { url = "https://files.pythonhosted.org/packages/a5/0a/6a20eca7b0092cfa243117e8e145a58631a4833a0a519ec9b445172e83a0/shapely-2.1.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb638378dc3d76f7e85b67d7e2bb1366811912430ac9247ac00c127c2b444cdc", size = 3055713, upload-time = "2025-04-03T09:14:57.564Z" }, - { url = "https://files.pythonhosted.org/packages/fb/44/eeb0c7583b1453d1cf7a319a1d738e08f98a5dc993fa1ef3c372983e4cb5/shapely-2.1.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:737124e87d91d616acf9a911f74ac55e05db02a43a6a7245b3d663817b876055", size = 3890478, upload-time = "2025-04-03T09:14:59.139Z" }, - { url = "https://files.pythonhosted.org/packages/5d/6e/37ff3c6af1d408cacb0a7d7bfea7b8ab163a5486e35acb08997eae9d8756/shapely-2.1.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8e6c229e7bb87aae5df82fa00b6718987a43ec168cc5affe095cca59d233f314", size = 4036148, upload-time = "2025-04-03T09:15:01.328Z" }, - { url = "https://files.pythonhosted.org/packages/c8/6a/8c0b7de3aeb5014a23f06c5e9d3c7852ebcf0d6b00fe660b93261e310e24/shapely-2.1.0-cp313-cp313t-win32.whl", hash = "sha256:a9580bda119b1f42f955aa8e52382d5c73f7957e0203bc0c0c60084846f3db94", size = 1535993, upload-time = "2025-04-03T09:15:02.973Z" }, - { url = "https://files.pythonhosted.org/packages/a8/91/ae80359a58409d52e4d62c7eacc7eb3ddee4b9135f1db884b6a43cf2e174/shapely-2.1.0-cp313-cp313t-win_amd64.whl", hash = "sha256:e8ff4e5cfd799ba5b6f37b5d5527dbd85b4a47c65b6d459a03d0962d2a9d4d10", size = 1717777, upload-time = "2025-04-03T09:15:04.461Z" }, + { url = "https://files.pythonhosted.org/packages/71/8e/2bc836437f4b84d62efc1faddce0d4e023a5d990bbddd3c78b2004ebc246/shapely-2.1.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:3004a644d9e89e26c20286d5fdc10f41b1744c48ce910bd1867fdff963fe6c48", size = 1832107, upload-time = "2025-05-19T11:04:19.736Z" }, + { url = "https://files.pythonhosted.org/packages/12/a2/12c7cae5b62d5d851c2db836eadd0986f63918a91976495861f7c492f4a9/shapely-2.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1415146fa12d80a47d13cfad5310b3c8b9c2aa8c14a0c845c9d3d75e77cb54f6", size = 1642355, upload-time = "2025-05-19T11:04:21.035Z" }, + { url = "https://files.pythonhosted.org/packages/5b/7e/6d28b43d53fea56de69c744e34c2b999ed4042f7a811dc1bceb876071c95/shapely-2.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21fcab88b7520820ec16d09d6bea68652ca13993c84dffc6129dc3607c95594c", size = 2968871, upload-time = "2025-05-19T11:04:22.167Z" }, + { url = "https://files.pythonhosted.org/packages/dd/87/1017c31e52370b2b79e4d29e07cbb590ab9e5e58cf7e2bdfe363765d6251/shapely-2.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e5ce6a5cc52c974b291237a96c08c5592e50f066871704fb5b12be2639d9026a", size = 3080830, upload-time = "2025-05-19T11:04:23.997Z" }, + { url = "https://files.pythonhosted.org/packages/1d/fe/f4a03d81abd96a6ce31c49cd8aaba970eaaa98e191bd1e4d43041e57ae5a/shapely-2.1.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:04e4c12a45a1d70aeb266618d8cf81a2de9c4df511b63e105b90bfdfb52146de", size = 3908961, upload-time = "2025-05-19T11:04:25.702Z" }, + { url = "https://files.pythonhosted.org/packages/ef/59/7605289a95a6844056a2017ab36d9b0cb9d6a3c3b5317c1f968c193031c9/shapely-2.1.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6ca74d851ca5264aae16c2b47e96735579686cb69fa93c4078070a0ec845b8d8", size = 4079623, upload-time = "2025-05-19T11:04:27.171Z" }, + { url = "https://files.pythonhosted.org/packages/bc/4d/9fea036eff2ef4059d30247128b2d67aaa5f0b25e9fc27e1d15cc1b84704/shapely-2.1.1-cp313-cp313-win32.whl", hash = "sha256:fd9130501bf42ffb7e0695b9ea17a27ae8ce68d50b56b6941c7f9b3d3453bc52", size = 1521916, upload-time = "2025-05-19T11:04:28.405Z" }, + { url = "https://files.pythonhosted.org/packages/12/d9/6d13b8957a17c95794f0c4dfb65ecd0957e6c7131a56ce18d135c1107a52/shapely-2.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:ab8d878687b438a2f4c138ed1a80941c6ab0029e0f4c785ecfe114413b498a97", size = 1702746, upload-time = "2025-05-19T11:04:29.643Z" }, + { url = "https://files.pythonhosted.org/packages/60/36/b1452e3e7f35f5f6454d96f3be6e2bb87082720ff6c9437ecc215fa79be0/shapely-2.1.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0c062384316a47f776305ed2fa22182717508ffdeb4a56d0ff4087a77b2a0f6d", size = 1833482, upload-time = "2025-05-19T11:04:30.852Z" }, + { url = "https://files.pythonhosted.org/packages/ce/ca/8e6f59be0718893eb3e478141285796a923636dc8f086f83e5b0ec0036d0/shapely-2.1.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4ecf6c196b896e8f1360cc219ed4eee1c1e5f5883e505d449f263bd053fb8c05", size = 1642256, upload-time = "2025-05-19T11:04:32.068Z" }, + { url = "https://files.pythonhosted.org/packages/ab/78/0053aea449bb1d4503999525fec6232f049abcdc8df60d290416110de943/shapely-2.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb00070b4c4860f6743c600285109c273cca5241e970ad56bb87bef0be1ea3a0", size = 3016614, upload-time = "2025-05-19T11:04:33.7Z" }, + { url = "https://files.pythonhosted.org/packages/ee/53/36f1b1de1dfafd1b457dcbafa785b298ce1b8a3e7026b79619e708a245d5/shapely-2.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d14a9afa5fa980fbe7bf63706fdfb8ff588f638f145a1d9dbc18374b5b7de913", size = 3093542, upload-time = "2025-05-19T11:04:34.952Z" }, + { url = "https://files.pythonhosted.org/packages/b9/bf/0619f37ceec6b924d84427c88835b61f27f43560239936ff88915c37da19/shapely-2.1.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:b640e390dabde790e3fb947198b466e63223e0a9ccd787da5f07bcb14756c28d", size = 3945961, upload-time = "2025-05-19T11:04:36.32Z" }, + { url = "https://files.pythonhosted.org/packages/93/c9/20ca4afeb572763b07a7997f00854cb9499df6af85929e93012b189d8917/shapely-2.1.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:69e08bf9697c1b73ec6aa70437db922bafcea7baca131c90c26d59491a9760f9", size = 4089514, upload-time = "2025-05-19T11:04:37.683Z" }, + { url = "https://files.pythonhosted.org/packages/33/6a/27036a5a560b80012a544366bceafd491e8abb94a8db14047b5346b5a749/shapely-2.1.1-cp313-cp313t-win32.whl", hash = "sha256:ef2d09d5a964cc90c2c18b03566cf918a61c248596998a0301d5b632beadb9db", size = 1540607, upload-time = "2025-05-19T11:04:38.925Z" }, + { url = "https://files.pythonhosted.org/packages/ea/f1/5e9b3ba5c7aa7ebfaf269657e728067d16a7c99401c7973ddf5f0cf121bd/shapely-2.1.1-cp313-cp313t-win_amd64.whl", hash = "sha256:8cb8f17c377260452e9d7720eeaf59082c5f8ea48cf104524d953e5d36d4bdb7", size = 1723061, upload-time = "2025-05-19T11:04:40.082Z" }, ] [[package]] @@ -2167,36 +2241,35 @@ wheels = [ [[package]] name = "sqlalchemy" -version = "2.0.40" +version = "2.0.41" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "greenlet", marker = "(python_full_version < '3.14' and platform_machine == 'AMD64') or (python_full_version < '3.14' and platform_machine == 'WIN32') or (python_full_version < '3.14' and platform_machine == 'aarch64') or (python_full_version < '3.14' and platform_machine == 'amd64') or (python_full_version < '3.14' and platform_machine == 'ppc64le') or (python_full_version < '3.14' and platform_machine == 'win32') or (python_full_version < '3.14' and platform_machine == 'x86_64')" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/68/c3/3f2bfa5e4dcd9938405fe2fab5b6ab94a9248a4f9536ea2fd497da20525f/sqlalchemy-2.0.40.tar.gz", hash = "sha256:d827099289c64589418ebbcaead0145cd19f4e3e8a93919a0100247af245fa00", size = 9664299, upload-time = "2025-03-27T17:52:31.876Z" } +sdist = { url = "https://files.pythonhosted.org/packages/63/66/45b165c595ec89aa7dcc2c1cd222ab269bc753f1fc7a1e68f8481bd957bf/sqlalchemy-2.0.41.tar.gz", hash = "sha256:edba70118c4be3c2b1f90754d308d0b79c6fe2c0fdc52d8ddf603916f83f4db9", size = 9689424, upload-time = "2025-05-14T17:10:32.339Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8c/18/4e3a86cc0232377bc48c373a9ba6a1b3fb79ba32dbb4eda0b357f5a2c59d/sqlalchemy-2.0.40-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:915866fd50dd868fdcc18d61d8258db1bf9ed7fbd6dfec960ba43365952f3b01", size = 2107887, upload-time = "2025-03-27T18:40:05.461Z" }, - { url = "https://files.pythonhosted.org/packages/cb/60/9fa692b1d2ffc4cbd5f47753731fd332afed30137115d862d6e9a1e962c7/sqlalchemy-2.0.40-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4a4c5a2905a9ccdc67a8963e24abd2f7afcd4348829412483695c59e0af9a705", size = 2098367, upload-time = "2025-03-27T18:40:07.182Z" }, - { url = "https://files.pythonhosted.org/packages/4c/9f/84b78357ca641714a439eb3fbbddb17297dacfa05d951dbf24f28d7b5c08/sqlalchemy-2.0.40-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55028d7a3ebdf7ace492fab9895cbc5270153f75442a0472d8516e03159ab364", size = 3184806, upload-time = "2025-03-27T18:51:29.356Z" }, - { url = "https://files.pythonhosted.org/packages/4b/7d/e06164161b6bfce04c01bfa01518a20cccbd4100d5c951e5a7422189191a/sqlalchemy-2.0.40-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6cfedff6878b0e0d1d0a50666a817ecd85051d12d56b43d9d425455e608b5ba0", size = 3198131, upload-time = "2025-03-27T18:50:31.616Z" }, - { url = "https://files.pythonhosted.org/packages/6d/51/354af20da42d7ec7b5c9de99edafbb7663a1d75686d1999ceb2c15811302/sqlalchemy-2.0.40-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bb19e30fdae77d357ce92192a3504579abe48a66877f476880238a962e5b96db", size = 3131364, upload-time = "2025-03-27T18:51:31.336Z" }, - { url = "https://files.pythonhosted.org/packages/7a/2f/48a41ff4e6e10549d83fcc551ab85c268bde7c03cf77afb36303c6594d11/sqlalchemy-2.0.40-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:16d325ea898f74b26ffcd1cf8c593b0beed8714f0317df2bed0d8d1de05a8f26", size = 3159482, upload-time = "2025-03-27T18:50:33.201Z" }, - { url = "https://files.pythonhosted.org/packages/33/ac/e5e0a807163652a35be878c0ad5cfd8b1d29605edcadfb5df3c512cdf9f3/sqlalchemy-2.0.40-cp313-cp313-win32.whl", hash = "sha256:a669cbe5be3c63f75bcbee0b266779706f1a54bcb1000f302685b87d1b8c1500", size = 2080704, upload-time = "2025-03-27T18:46:00.193Z" }, - { url = "https://files.pythonhosted.org/packages/1c/cb/f38c61f7f2fd4d10494c1c135ff6a6ddb63508d0b47bccccd93670637309/sqlalchemy-2.0.40-cp313-cp313-win_amd64.whl", hash = "sha256:641ee2e0834812d657862f3a7de95e0048bdcb6c55496f39c6fa3d435f6ac6ad", size = 2104564, upload-time = "2025-03-27T18:46:01.442Z" }, - { url = "https://files.pythonhosted.org/packages/d1/7c/5fc8e802e7506fe8b55a03a2e1dab156eae205c91bee46305755e086d2e2/sqlalchemy-2.0.40-py3-none-any.whl", hash = "sha256:32587e2e1e359276957e6fe5dad089758bc042a971a8a09ae8ecf7a8fe23d07a", size = 1903894, upload-time = "2025-03-27T18:40:43.796Z" }, + { url = "https://files.pythonhosted.org/packages/d3/ad/2e1c6d4f235a97eeef52d0200d8ddda16f6c4dd70ae5ad88c46963440480/sqlalchemy-2.0.41-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4eeb195cdedaf17aab6b247894ff2734dcead6c08f748e617bfe05bd5a218443", size = 2115491, upload-time = "2025-05-14T17:55:31.177Z" }, + { url = "https://files.pythonhosted.org/packages/cf/8d/be490e5db8400dacc89056f78a52d44b04fbf75e8439569d5b879623a53b/sqlalchemy-2.0.41-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d4ae769b9c1c7757e4ccce94b0641bc203bbdf43ba7a2413ab2523d8d047d8dc", size = 2102827, upload-time = "2025-05-14T17:55:34.921Z" }, + { url = "https://files.pythonhosted.org/packages/a0/72/c97ad430f0b0e78efaf2791342e13ffeafcbb3c06242f01a3bb8fe44f65d/sqlalchemy-2.0.41-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a62448526dd9ed3e3beedc93df9bb6b55a436ed1474db31a2af13b313a70a7e1", size = 3225224, upload-time = "2025-05-14T17:50:41.418Z" }, + { url = "https://files.pythonhosted.org/packages/5e/51/5ba9ea3246ea068630acf35a6ba0d181e99f1af1afd17e159eac7e8bc2b8/sqlalchemy-2.0.41-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc56c9788617b8964ad02e8fcfeed4001c1f8ba91a9e1f31483c0dffb207002a", size = 3230045, upload-time = "2025-05-14T17:51:54.722Z" }, + { url = "https://files.pythonhosted.org/packages/78/2f/8c14443b2acea700c62f9b4a8bad9e49fc1b65cfb260edead71fd38e9f19/sqlalchemy-2.0.41-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c153265408d18de4cc5ded1941dcd8315894572cddd3c58df5d5b5705b3fa28d", size = 3159357, upload-time = "2025-05-14T17:50:43.483Z" }, + { url = "https://files.pythonhosted.org/packages/fc/b2/43eacbf6ccc5276d76cea18cb7c3d73e294d6fb21f9ff8b4eef9b42bbfd5/sqlalchemy-2.0.41-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4f67766965996e63bb46cfbf2ce5355fc32d9dd3b8ad7e536a920ff9ee422e23", size = 3197511, upload-time = "2025-05-14T17:51:57.308Z" }, + { url = "https://files.pythonhosted.org/packages/fa/2e/677c17c5d6a004c3c45334ab1dbe7b7deb834430b282b8a0f75ae220c8eb/sqlalchemy-2.0.41-cp313-cp313-win32.whl", hash = "sha256:bfc9064f6658a3d1cadeaa0ba07570b83ce6801a1314985bf98ec9b95d74e15f", size = 2082420, upload-time = "2025-05-14T17:55:52.69Z" }, + { url = "https://files.pythonhosted.org/packages/e9/61/e8c1b9b6307c57157d328dd8b8348ddc4c47ffdf1279365a13b2b98b8049/sqlalchemy-2.0.41-cp313-cp313-win_amd64.whl", hash = "sha256:82ca366a844eb551daff9d2e6e7a9e5e76d2612c8564f58db6c19a726869c1df", size = 2108329, upload-time = "2025-05-14T17:55:54.495Z" }, + { url = "https://files.pythonhosted.org/packages/1c/fc/9ba22f01b5cdacc8f5ed0d22304718d2c758fce3fd49a5372b886a86f37c/sqlalchemy-2.0.41-py3-none-any.whl", hash = "sha256:57df5dc6fdb5ed1a88a1ed2195fd31927e705cad62dedd86b46972752a80f576", size = 1911224, upload-time = "2025-05-14T17:39:42.154Z" }, ] [[package]] name = "sse-starlette" -version = "2.2.1" +version = "2.3.6" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, - { name = "starlette" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/71/a4/80d2a11af59fe75b48230846989e93979c892d3a20016b42bb44edb9e398/sse_starlette-2.2.1.tar.gz", hash = "sha256:54470d5f19274aeed6b2d473430b08b4b379ea851d953b11d7f1c4a2c118b419", size = 17376, upload-time = "2024-12-25T09:09:30.616Z" } +sdist = { url = "https://files.pythonhosted.org/packages/8c/f4/989bc70cb8091eda43a9034ef969b25145291f3601703b82766e5172dfed/sse_starlette-2.3.6.tar.gz", hash = "sha256:0382336f7d4ec30160cf9ca0518962905e1b69b72d6c1c995131e0a703b436e3", size = 18284, upload-time = "2025-05-30T13:34:12.914Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d9/e0/5b8bd393f27f4a62461c5cf2479c75a2cc2ffa330976f9f00f5f6e4f50eb/sse_starlette-2.2.1-py3-none-any.whl", hash = "sha256:6410a3d3ba0c89e7675d4c273a301d64649c03a5ef1ca101f10b47f895fd0e99", size = 10120, upload-time = "2024-12-25T09:09:26.761Z" }, + { url = "https://files.pythonhosted.org/packages/81/05/78850ac6e79af5b9508f8841b0f26aa9fd329a1ba00bf65453c2d312bcc8/sse_starlette-2.3.6-py3-none-any.whl", hash = "sha256:d49a8285b182f6e2228e2609c350398b2ca2c36216c2675d875f81e93548f760", size = 10606, upload-time = "2025-05-30T13:34:11.703Z" }, ] [[package]] @@ -2291,23 +2364,23 @@ wheels = [ [[package]] name = "types-requests" -version = "2.32.0.20250328" +version = "2.32.0.20250602" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/00/7d/eb174f74e3f5634eaacb38031bbe467dfe2e545bc255e5c90096ec46bc46/types_requests-2.32.0.20250328.tar.gz", hash = "sha256:c9e67228ea103bd811c96984fac36ed2ae8da87a36a633964a21f199d60baf32", size = 22995, upload-time = "2025-03-28T02:55:13.271Z" } +sdist = { url = "https://files.pythonhosted.org/packages/48/b0/5321e6eeba5d59e4347fcf9bf06a5052f085c3aa0f4876230566d6a4dc97/types_requests-2.32.0.20250602.tar.gz", hash = "sha256:ee603aeefec42051195ae62ca7667cd909a2f8128fdf8aad9e8a5219ecfab3bf", size = 23042, upload-time = "2025-06-02T03:15:02.958Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/cc/15/3700282a9d4ea3b37044264d3e4d1b1f0095a4ebf860a99914fd544e3be3/types_requests-2.32.0.20250328-py3-none-any.whl", hash = "sha256:72ff80f84b15eb3aa7a8e2625fffb6a93f2ad5a0c20215fc1dcfa61117bcb2a2", size = 20663, upload-time = "2025-03-28T02:55:11.946Z" }, + { url = "https://files.pythonhosted.org/packages/da/18/9b782980e575c6581d5c0c1c99f4c6f89a1d7173dad072ee96b2756c02e6/types_requests-2.32.0.20250602-py3-none-any.whl", hash = "sha256:f4f335f87779b47ce10b8b8597b409130299f6971ead27fead4fe7ba6ea3e726", size = 20638, upload-time = "2025-06-02T03:15:01.959Z" }, ] [[package]] name = "typing-extensions" -version = "4.13.2" +version = "4.14.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f6/37/23083fcd6e35492953e8d2aaaa68b860eb422b34627b13f2ce3eb6106061/typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef", size = 106967, upload-time = "2025-04-10T14:19:05.416Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d1/bc/51647cd02527e87d05cb083ccc402f93e441606ff1f01739a62c8ad09ba5/typing_extensions-4.14.0.tar.gz", hash = "sha256:8676b788e32f02ab42d9e7c61324048ae4c6d844a399eebace3d4979d75ceef4", size = 107423, upload-time = "2025-06-02T14:52:11.399Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8b/54/b1ae86c0973cc6f0210b53d508ca3641fb6d0c56823f288d108bc7ab3cc8/typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c", size = 45806, upload-time = "2025-04-10T14:19:03.967Z" }, + { url = "https://files.pythonhosted.org/packages/69/e0/552843e0d356fbb5256d21449fa957fa4eff3bbc135a74a691ee70c7c5da/typing_extensions-4.14.0-py3-none-any.whl", hash = "sha256:a1514509136dd0b477638fc68d6a91497af5076466ad0fa6c338e44e359944af", size = 43839, upload-time = "2025-06-02T14:52:10.026Z" }, ] [[package]] @@ -2325,14 +2398,14 @@ wheels = [ [[package]] name = "typing-inspection" -version = "0.4.0" +version = "0.4.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/82/5c/e6082df02e215b846b4b8c0b887a64d7d08ffaba30605502639d44c06b82/typing_inspection-0.4.0.tar.gz", hash = "sha256:9765c87de36671694a67904bf2c96e395be9c6439bb6c87b5142569dcdd65122", size = 76222, upload-time = "2025-02-25T17:27:59.638Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f8/b1/0c11f5058406b3af7609f121aaa6b609744687f1d158b3c3a5bf4cc94238/typing_inspection-0.4.1.tar.gz", hash = "sha256:6ae134cc0203c33377d43188d4064e9b357dba58cff3185f22924610e70a9d28", size = 75726, upload-time = "2025-05-21T18:55:23.885Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/31/08/aa4fdfb71f7de5176385bd9e90852eaf6b5d622735020ad600f2bab54385/typing_inspection-0.4.0-py3-none-any.whl", hash = "sha256:50e72559fcd2a6367a19f7a7e610e6afcb9fac940c650290eed893d61386832f", size = 14125, upload-time = "2025-02-25T17:27:57.754Z" }, + { url = "https://files.pythonhosted.org/packages/17/69/cd203477f944c353c31bade965f880aa1061fd6bf05ded0726ca845b6ff7/typing_inspection-0.4.1-py3-none-any.whl", hash = "sha256:389055682238f53b04f7badcb49b989835495a96700ced5dab2d8feae4b26f51", size = 14552, upload-time = "2025-05-21T18:55:22.152Z" }, ] [[package]] @@ -2358,11 +2431,11 @@ wheels = [ [[package]] name = "uritemplate" -version = "4.1.1" +version = "4.2.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d2/5a/4742fdba39cd02a56226815abfa72fe0aa81c33bed16ed045647d6000eba/uritemplate-4.1.1.tar.gz", hash = "sha256:4346edfc5c3b79f694bccd6d6099a322bbeb628dbf2cd86eea55a456ce5124f0", size = 273898, upload-time = "2021-10-13T11:15:14.84Z" } +sdist = { url = "https://files.pythonhosted.org/packages/98/60/f174043244c5306c9988380d2cb10009f91563fc4b31293d27e17201af56/uritemplate-4.2.0.tar.gz", hash = "sha256:480c2ed180878955863323eea31b0ede668795de182617fef9c6ca09e6ec9d0e", size = 33267, upload-time = "2025-06-02T15:12:06.318Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/81/c0/7461b49cd25aeece13766f02ee576d1db528f1c37ce69aee300e075b485b/uritemplate-4.1.1-py2.py3-none-any.whl", hash = "sha256:830c08b8d99bdd312ea4ead05994a38e8936266f84b9a7878232db50b044e02e", size = 10356, upload-time = "2021-10-13T11:15:12.316Z" }, + { url = "https://files.pythonhosted.org/packages/a9/99/3ae339466c9183ea5b8ae87b34c0b897eda475d2aec2307cae60e5cd4f29/uritemplate-4.2.0-py3-none-any.whl", hash = "sha256:962201ba1c4edcab02e60f9a0d3821e82dfc5d2d6662a21abd533879bdb8a686", size = 11488, upload-time = "2025-06-02T15:12:03.405Z" }, ] [[package]] @@ -2376,15 +2449,15 @@ wheels = [ [[package]] name = "uvicorn" -version = "0.34.2" +version = "0.34.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, { name = "h11" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a6/ae/9bbb19b9e1c450cf9ecaef06463e40234d98d95bf572fab11b4f19ae5ded/uvicorn-0.34.2.tar.gz", hash = "sha256:0e929828f6186353a80b58ea719861d2629d766293b6d19baf086ba31d4f3328", size = 76815, upload-time = "2025-04-19T06:02:50.101Z" } +sdist = { url = "https://files.pythonhosted.org/packages/de/ad/713be230bcda622eaa35c28f0d328c3675c371238470abdea52417f17a8e/uvicorn-0.34.3.tar.gz", hash = "sha256:35919a9a979d7a59334b6b10e05d77c1d0d574c50e0fc98b8b1a0f165708b55a", size = 76631, upload-time = "2025-06-01T07:48:17.531Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b1/4b/4cef6ce21a2aaca9d852a6e84ef4f135d99fcd74fa75105e2fc0c8308acd/uvicorn-0.34.2-py3-none-any.whl", hash = "sha256:deb49af569084536d269fe0a6d67e3754f104cf03aba7c11c40f01aadf33c403", size = 62483, upload-time = "2025-04-19T06:02:48.42Z" }, + { url = "https://files.pythonhosted.org/packages/6d/0d/8adfeaa62945f90d19ddc461c55f4a50c258af7662d34b6a3d5d1f8646f6/uvicorn-0.34.3-py3-none-any.whl", hash = "sha256:16246631db62bdfbf069b0645177d6e8a77ba950cfedbfd093acef9444e4d885", size = 62431, upload-time = "2025-06-01T07:48:15.664Z" }, ] [[package]] @@ -2538,11 +2611,11 @@ wheels = [ [[package]] name = "zipp" -version = "3.21.0" +version = "3.23.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/3f/50/bad581df71744867e9468ebd0bcd6505de3b275e06f202c2cb016e3ff56f/zipp-3.21.0.tar.gz", hash = "sha256:2c9958f6430a2040341a52eb608ed6dd93ef4392e02ffe219417c1b28b5dd1f4", size = 24545, upload-time = "2024-11-10T15:05:20.202Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e3/02/0f2892c661036d50ede074e376733dca2ae7c6eb617489437771209d4180/zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166", size = 25547, upload-time = "2025-06-08T17:06:39.4Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/1a/7e4798e9339adc931158c9d69ecc34f5e6791489d469f5e50ec15e35f458/zipp-3.21.0-py3-none-any.whl", hash = "sha256:ac1bbe05fd2991f160ebce24ffbac5f6d11d83dc90891255885223d42b3cd931", size = 9630, upload-time = "2024-11-10T15:05:19.275Z" }, + { url = "https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e", size = 10276, upload-time = "2025-06-08T17:06:38.034Z" }, ] [[package]] From b97b853c66b3c3ef6e627920f47c8ed1d85815a9 Mon Sep 17 00:00:00 2001 From: Andrew Ginns Date: Mon, 9 Jun 2025 10:18:44 +0000 Subject: [PATCH 14/14] docs: Update with mention of evals and dashboard --- README.md | 64 +++++++++++++++-- agents_mcp_usage/multi_mcp/README.md | 101 +++++++++++++++++++++++++-- 2 files changed, 155 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 939036a..e2181e5 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Model Context Protocol (MCP) Agent Frameworks Demo +# Model Context Protocol (MCP) Agent Frameworks Demo & Benchmarking Platform This repository demonstrates LLM Agents using tools from Model Context Protocol (MCP) servers with several frameworks: - Google Agent Development Kit (ADK) @@ -6,10 +6,13 @@ This repository demonstrates LLM Agents using tools from Model Context Protocol - OpenAI Agents - Pydantic-AI Agents -Both single and multiple MCP server examples are demonstrated -- [Agent with a single MCP server](agents_mcp_usage/basic_mcp/README.md) -- [Agent with multiple MCP servers](agents_mcp_usage/multi_mcp/README.md) - - Also includes Agent evaluations +## Repository Structure + +- [Agent with a single MCP server](agents_mcp_usage/basic_mcp/README.md) - Learning examples and basic patterns +- [Agent with multiple MCP servers](agents_mcp_usage/multi_mcp/README.md) - Advanced usage with comprehensive evaluation suite + - **Evaluation Dashboard**: Interactive Streamlit UI for model comparison + - **Multi-Model Benchmarking**: Parallel/sequential evaluation across multiple LLMs + - **Rich Metrics**: Usage analysis, cost comparison, and performance leaderboards The repo also includes Python MCP Servers: - [`example_server.py`](mcp_servers/example_server.py) based on [MCP Python SDK Quickstart](https://github.com/modelcontextprotocol/python-sdk/blob/b4c7db6a50a5c88bae1db5c1f7fba44d16eebc6e/README.md?plain=1#L104) - Modified to include a datetime tool and run as a server invoked by Agents @@ -217,10 +220,59 @@ uv run agents_mcp_usage/multi_mcp/multi_mcp_use/pydantic_mcp.py # Run the multi-MCP evaluation uv run agents_mcp_usage/multi_mcp/eval_multi_mcp/evals_pydantic_mcp.py + +# Run multi-model benchmarking +uv run agents_mcp_usage/multi_mcp/eval_multi_mcp/run_multi_evals.py --models "gemini-2.5-pro,gemini-2.0-flash" --runs 5 --parallel + +# Launch the evaluation dashboard +uv run streamlit run agents_mcp_usage/multi_mcp/eval_multi_mcp/merbench_ui.py ``` More details on multi-MCP implementation can be found in the [multi_mcp README](agents_mcp_usage/multi_mcp/README.md). +## Evaluation Suite & Benchmarking Dashboard + +This repository includes a comprehensive evaluation system for benchmarking LLM agent performance across multiple frameworks and models. The evaluation suite tests agents on mermaid diagram correction tasks using multiple MCP servers, providing rich metrics and analysis capabilities. + +### Key Evaluation Features + +- **Multi-Level Difficulty**: Easy, medium, and hard test cases for comprehensive assessment +- **Multi-Model Benchmarking**: Parallel or sequential evaluation across multiple LLM models +- **Interactive Dashboard**: Streamlit-based UI for visualising results, cost analysis, and model comparison +- **Rich Metrics Collection**: Token usage, cost analysis, success rates, and failure categorisation +- **Robust Error Handling**: Comprehensive retry logic and detailed failure analysis +- **Export Capabilities**: CSV results for downstream analysis and reporting + +### Dashboard Features + +The included Streamlit dashboard (`merbench_ui.py`) provides: + +- **Model Leaderboards**: Performance rankings by accuracy, cost efficiency, and speed +- **Cost Analysis**: Detailed cost breakdowns and cost-per-success metrics +- **Failure Analysis**: Categorised failure reasons with debugging insights +- **Performance Trends**: Visualisation of model behaviour across difficulty levels +- **Resource Usage**: Token consumption and API call patterns +- **Comparative Analysis**: Side-by-side model performance comparison + +### Quick Evaluation Commands + +```bash +# Single model evaluation +uv run agents_mcp_usage/multi_mcp/eval_multi_mcp/evals_pydantic_mcp.py + +# Multi-model parallel benchmarking +uv run agents_mcp_usage/multi_mcp/eval_multi_mcp/run_multi_evals.py \ + --models "gemini-2.5-pro,gemini-2.0-flash,gemini-2.5-flash-preview-04-17" \ + --runs 5 \ + --parallel \ + --output-dir ./results + +# Launch interactive dashboard +uv run streamlit run agents_mcp_usage/multi_mcp/eval_multi_mcp/merbench_ui.py +``` + +The evaluation system enables robust, repeatable benchmarking across LLM models and agent frameworks, supporting both research and production model selection decisions. + ## What is MCP? The Model Context Protocol allows applications to provide context for LLMs in a standardised way, separating the concerns of providing context from the actual LLM interaction. @@ -258,4 +310,4 @@ A key advantage highlighted is flexibility; MCP allows developers to more easily - OpenTelemetry support for leveraging existing tooling - Pydantic integration for analytics on validations -Logfire gives you visibility into how your code is running, which is especially valuable for LLM applications where understanding model behaviour is critical. \ No newline at end of file +Logfire gives you visibility into how your code is running, which is especially valuable for LLM applications where understanding model behaviour is critical. diff --git a/agents_mcp_usage/multi_mcp/README.md b/agents_mcp_usage/multi_mcp/README.md index f910806..1cd8170 100644 --- a/agents_mcp_usage/multi_mcp/README.md +++ b/agents_mcp_usage/multi_mcp/README.md @@ -1,8 +1,9 @@ -# Multi-MCP Usage +# Multi-MCP Usage & Evaluation Suite -This directory contains examples demonstrating the integration of tools from multiple Model Context Protocol (MCP) servers with various LLM agent frameworks. +This directory contains examples demonstrating the integration of tools from multiple Model Context Protocol (MCP) servers with various LLM agent frameworks, along with a comprehensive evaluation and benchmarking system. + +Agents utilising multiple MCP servers can be dramatically more complex than an Agent using a single server. This is because as the number of servers grow the number of tools that the Agent must reason on when and how to use increases. As a result this component not only demonstrates an Agent's use of multiple MCP servers, but also includes a production-ready evaluation suite to validate performance, analyse costs, and compare models across multiple difficulty levels. -Agents utilising multiple MCP servers can be dramatically more complex than an Agent using a single server. This is because as the number of servers grow the number of tools that the Agent must reason on when and how to use increases. As a result this component not only demonstrates an Agent's use of multiple MCP servers, but also includes evaluations to validate that they are being used to successfully accomplish the task according to various evaluation criterias. ## Quickstart @@ -22,9 +23,15 @@ Agents utilising multiple MCP servers can be dramatically more complex than an A # Run the multi-MCP evaluation uv run agents_mcp_usage/multi_mcp/eval_multi_mcp/evals_pydantic_mcp.py + + # Run multi-model benchmarking + uv run agents_mcp_usage/multi_mcp/eval_multi_mcp/run_multi_evals.py --models "gemini-2.5-pro,gemini-2.0-flash" --runs 5 --parallel + + # Launch the evaluation dashboard + uv run streamlit run agents_mcp_usage/multi_mcp/eval_multi_mcp/merbench_ui.py ``` -5. Check the console output or Logfire for results. +5. Check the console output, Logfire, or dashboard for results. ### Multi-MCP Architecture @@ -180,6 +187,92 @@ Research in LLM agent development has identified tool overload as a significant The evaluation framework included in this component is essential for validating that agents can effectively navigate the increased complexity of multiple MCP servers. By measuring success against specific evaluation criteria, developers can ensure that the benefits of tool specialisation outweigh the potential pitfalls of tool overload. +## Comprehensive Evaluation Suite + +The `eval_multi_mcp/` directory contains a production-ready evaluation system for benchmarking LLM agent performance across multiple frameworks and models. The suite tests agents on mermaid diagram correction tasks using multiple MCP servers, providing rich metrics and analysis capabilities. + +### Evaluation Components + +#### Core Modules +- **`evals_pydantic_mcp.py`** - Single-model evaluation with comprehensive metrics collection +- **`run_multi_evals.py`** - Multi-model parallel/sequential benchmarking with CSV export +- **`merbench_ui.py`** - Interactive Streamlit dashboard for visualisation and analysis +- **`dashboard_config.py`** - Configuration-driven UI setup for flexible dashboard customisation +- **`costs.csv`** - Pricing integration for cost analysis and budget planning + +#### Test Difficulty Levels +The evaluation includes three test cases of increasing complexity: +1. **Easy** - Simple syntax errors in mermaid diagrams +2. **Medium** - More complex structural issues requiring deeper reasoning +3. **Hard** - Advanced mermaid syntax problems testing sophisticated tool usage + +#### Evaluation Metrics +The system captures five key performance indicators: +- **UsedBothMCPTools** - Validates proper coordination between multiple MCP servers +- **UsageLimitNotExceeded** - Monitors resource consumption and efficiency +- **MermaidDiagramValid** - Assesses technical correctness of outputs +- **LLMJudge (Format)** - Evaluates response formatting and structure +- **LLMJudge (Structure)** - Measures preservation of original diagram intent + +## Interactive Dashboard & Visualisation + +The Streamlit-based dashboard (`merbench_ui.py`) provides comprehensive analysis and comparison capabilities: + +### Dashboard Features +- **Model Leaderboards** - Performance rankings by accuracy, cost efficiency, and execution speed +- **Cost Analysis** - Detailed cost breakdowns with cost-per-success metrics and budget projections +- **Failure Analysis** - Categorised failure reasons with debugging insights and error patterns +- **Performance Trends** - Visualisation of model behaviour across difficulty levels and test iterations +- **Resource Usage** - Token consumption patterns and API call efficiency metrics +- **Comparative Analysis** - Side-by-side model performance comparison with statistical significance + +### Dashboard Quick Launch +```bash +# Launch the interactive evaluation dashboard +uv run streamlit run agents_mcp_usage/multi_mcp/eval_multi_mcp/merbench_ui.py +``` + +The dashboard automatically loads evaluation results from the `mermaid_eval_results/` directory, providing immediate insights into model performance and cost efficiency. + +## Multi-Model Benchmarking + +The `run_multi_evals.py` script enables systematic comparison across multiple LLM models with flexible execution options: + +### Benchmarking Features +- **Parallel Execution** - Simultaneous evaluation across models for faster results +- **Sequential Mode** - Conservative execution for resource-constrained environments +- **Configurable Runs** - Multiple iterations per model for statistical reliability +- **Comprehensive Error Handling** - Robust retry logic with exponential backoff +- **CSV Export** - Structured results for downstream analysis and reporting + +### Example Benchmarking Commands + +```bash +# Parallel benchmarking across multiple models +uv run agents_mcp_usage/multi_mcp/eval_multi_mcp/run_multi_evals.py \ + --models "gemini-2.5-pro,gemini-2.0-flash,gemini-2.5-flash-preview-04-17" \ + --runs 5 \ + --parallel \ + --timeout 600 \ + --output-dir ./benchmark_results + +# Sequential execution with custom judge model +uv run agents_mcp_usage/multi_mcp/eval_multi_mcp/run_multi_evals.py \ + --models "gemini-2.5-pro,claude-3-opus" \ + --runs 3 \ + --sequential \ + --judge-model "gemini-2.5-pro" \ + --output-dir ./comparative_analysis +``` + +### Output Structure +Results are organised with timestamped files: +- **Individual model results** - `YYYY-MM-DD_HH-MM-SS_individual_{model}.csv` +- **Combined analysis** - `YYYY-MM-DD_HH-MM-SS_combined_results.csv` +- **Dashboard integration** - Automatic loading into visualisation interface + +The evaluation system enables robust, repeatable benchmarking across LLM models and agent frameworks, supporting both research investigations and production model selection decisions. + ## Example Files ### Pydantic-AI Multi-MCP