diff --git a/.github/scripts/verify_registry.py b/.github/scripts/verify_registry.py index 211ac22c..7adb6c3d 100644 --- a/.github/scripts/verify_registry.py +++ b/.github/scripts/verify_registry.py @@ -17,14 +17,15 @@ schema Verify YAML files match their JSON schemas """ -import yaml -import requests -import sys import json +import sys from pathlib import Path +import requests +import yaml + try: - from jsonschema import validate, ValidationError + from jsonschema import ValidationError, validate HAS_JSONSCHEMA = True except ImportError: @@ -172,7 +173,7 @@ def verify_schemas(repo_root, authors, registry): if authors_schema_path.exists(): print("Checking authors.yaml against schema...") try: - with open(authors_schema_path, "r") as f: + with open(authors_schema_path) as f: authors_schema = json.load(f) validate(instance=authors, schema=authors_schema) print(" ✓ authors.yaml matches schema") @@ -190,7 +191,7 @@ def verify_schemas(repo_root, authors, registry): if registry_schema_path.exists(): print("\nChecking registry.yaml against schema...") try: - with open(registry_schema_path, "r") as f: + with open(registry_schema_path) as f: registry_schema = json.load(f) validate(instance=registry, schema=registry_schema) print(" ✓ registry.yaml matches schema") @@ -227,11 +228,11 @@ def main(): registry = None if command in ["all", "authors", "registry", "schema"]: - with open(authors_path, "r") as f: + with open(authors_path) as f: authors = yaml.safe_load(f) if command in ["all", "paths", "registry", "schema"]: - with open(registry_path, "r") as f: + with open(registry_path) as f: registry = yaml.safe_load(f) # Run verifications based on command diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 45428fef..7c029663 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,8 +1,8 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.12.12 + rev: v0.14.6 hooks: - - id: ruff + - id: ruff-check types_or: [python, pyi, jupyter] args: ['--fix'] - id: ruff-format diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000..22a48aff --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,102 @@ +# Claude Cookbooks + +A collection of Jupyter notebooks and Python examples for building with the Claude API. + +## Quick Start + +```bash +# Install dependencies +uv sync --all-extras + +# Install pre-commit hooks +uv run pre-commit install + +# Set up API key +cp .env.example .env +# Edit .env and add your ANTHROPIC_API_KEY +``` + +## Development Commands + +```bash +make format # Format code with ruff +make lint # Run linting +make check # Run format-check + lint +make fix # Auto-fix issues + format +make test # Run pytest +``` + +Or directly with uv: + +```bash +uv run ruff format . # Format +uv run ruff check . # Lint +uv run ruff check --fix . # Auto-fix +uv run pre-commit run --all-files +``` + +## Code Style + +- **Line length:** 100 characters +- **Quotes:** Double quotes +- **Formatter:** Ruff + +Notebooks have relaxed rules for mid-file imports (E402), redefinitions (F811), and variable naming (N803, N806). + +## Git Workflow + +**Branch naming:** `/` + +**Commit format (conventional commits):** +``` +feat(scope): add new feature +fix(scope): fix bug +docs(scope): update documentation +style: lint/format +``` + +## Key Rules + +1. **API Keys:** Never commit `.env` files. Always use `os.environ.get("ANTHROPIC_API_KEY")` + +2. **Dependencies:** Use `uv add ` or `uv add --dev `. Never edit pyproject.toml directly. + +3. **Models:** Use current Claude models. Check docs.anthropic.com for latest versions. + - Sonnet: `claude-sonnet-4-5-20250929` + - Haiku: `claude-haiku-4-5-20251001` + +4. **Notebooks:** + - Keep outputs in notebooks (intentional for demonstration) + - One concept per notebook + - Test that notebooks run top-to-bottom without errors + +5. **Quality checks:** Run `make check` before committing. Pre-commit hooks validate formatting and notebook structure. + +## Slash Commands + +These commands are available in Claude Code and CI: + +- `/notebook-review` - Review notebook quality +- `/model-check` - Validate Claude model references +- `/link-review` - Check links in changed files + +## Project Structure + +``` +capabilities/ # Core Claude capabilities (RAG, classification, etc.) +skills/ # Advanced skill-based notebooks +tool_use/ # Tool use and integration patterns +multimodal/ # Vision and image processing +misc/ # Batch processing, caching, utilities +third_party/ # Pinecone, Voyage, Wikipedia integrations +extended_thinking/ # Extended reasoning patterns +scripts/ # Validation scripts +.claude/ # Claude Code commands and skills +``` + +## Adding a New Cookbook + +1. Create notebook in the appropriate directory +2. Add entry to `registry.yaml` with title, description, path, authors, categories +3. Add author info to `authors.yaml` if new contributor +4. Run quality checks and submit PR diff --git a/capabilities/classification/evaluation/prompts.py b/capabilities/classification/evaluation/prompts.py index c694b701..6de1c3fa 100644 --- a/capabilities/classification/evaluation/prompts.py +++ b/capabilities/classification/evaluation/prompts.py @@ -1,59 +1,60 @@ -from vectordb import VectorDB import textwrap +from vectordb import VectorDB + vectordb = VectorDB() # Load the vector database vectordb.load_db() -categories = """ +categories = """ - Questions about invoices, charges, fees, and premiums Requests for clarification on billing statements Inquiries about payment methods and due dates - - - + Questions about invoices, charges, fees, and premiums Requests for clarification on billing statements Inquiries about payment methods and due dates + + + - Requests for policy changes, updates, or cancellations Questions about policy renewals and reinstatements Inquiries about adding or removing coverage options - - - - - Questions about the claims process and filing procedures Requests for help with submitting claim documentation Inquiries about claim status and payout timelines - - - - - Questions about what is covered under specific policy types Requests for clarification on coverage limits and exclusions Inquiries about deductibles and out-of-pocket expenses - - - - - Requests for new policy quotes and price comparisons Questions about available discounts and bundling options Inquiries about switching from another insurer - - - - - Requests for login credentials or password resets Questions about online account features and functionality Inquiries about updating contact or personal information - - - - - Complaints about unexpected or incorrect charges Requests for refunds or premium adjustments Inquiries about late fees or collection notices - - - - - Complaints about denied or underpaid claims Requests for reconsideration of claim decisions Inquiries about appealing a claim outcome - - - - - Questions about the differences between policy options Requests for help deciding between coverage levels Inquiries about how policies compare to competitors' offerings - - - - - Questions about company contact information or hours of operation Requests for general information about products or services Inquiries that don't fit neatly into other categories - + Requests for policy changes, updates, or cancellations Questions about policy renewals and reinstatements Inquiries about adding or removing coverage options + + + + + Questions about the claims process and filing procedures Requests for help with submitting claim documentation Inquiries about claim status and payout timelines + + + + + Questions about what is covered under specific policy types Requests for clarification on coverage limits and exclusions Inquiries about deductibles and out-of-pocket expenses + + + + + Requests for new policy quotes and price comparisons Questions about available discounts and bundling options Inquiries about switching from another insurer + + + + + Requests for login credentials or password resets Questions about online account features and functionality Inquiries about updating contact or personal information + + + + + Complaints about unexpected or incorrect charges Requests for refunds or premium adjustments Inquiries about late fees or collection notices + + + + + Complaints about denied or underpaid claims Requests for reconsideration of claim decisions Inquiries about appealing a claim outcome + + + + + Questions about the differences between policy options Requests for help deciding between coverage levels Inquiries about how policies compare to competitors' offerings + + + + + Questions about company contact information or hours of operation Requests for general information about products or services Inquiries that don't fit neatly into other categories + """ @@ -154,7 +155,7 @@ def rag_chain_of_thought_classify(context: dict): First you will think step-by-step about the problem in scratchpad tags. You should consider all the information provided and create a concrete argument for your classification. - + Respond using this format: Your thoughts and analysis go here diff --git a/capabilities/classification/evaluation/vectordb.py b/capabilities/classification/evaluation/vectordb.py index 85be5cda..8829cc12 100644 --- a/capabilities/classification/evaluation/vectordb.py +++ b/capabilities/classification/evaluation/vectordb.py @@ -1,8 +1,9 @@ +import json import os +import pickle + import numpy as np import voyageai -import pickle -import json class VectorDB: diff --git a/capabilities/classification/guide.ipynb b/capabilities/classification/guide.ipynb index 5f698646..186cab67 100644 --- a/capabilities/classification/guide.ipynb +++ b/capabilities/classification/guide.ipynb @@ -52,9 +52,10 @@ "outputs": [], "source": [ "# Setup our environment\n", - "import anthropic\n", "import os\n", "\n", + "import anthropic\n", + "\n", "MODEL = \"claude-haiku-4-5\"\n", "client = anthropic.Anthropic(\n", " # This is the default and can be omitted\n", @@ -245,10 +246,11 @@ "metadata": {}, "outputs": [], "source": [ - "import matplotlib.pyplot as plt\n", - "from sklearn.metrics import classification_report, confusion_matrix\n", "import concurrent.futures\n", + "\n", + "import matplotlib.pyplot as plt\n", "import numpy as np\n", + "from sklearn.metrics import classification_report, confusion_matrix\n", "\n", "MAXIMUM_CONCURRENT_REQUESTS = 5\n", "\n", @@ -419,55 +421,55 @@ "source": [ "import textwrap\n", "\n", - "categories = textwrap.dedent(\"\"\" \n", + "categories = textwrap.dedent(\"\"\"\n", " \n", - " Questions about invoices, charges, fees, and premiums Requests for clarification on billing statements Inquiries about payment methods and due dates \n", - " \n", - " \n", - " \n", + " Questions about invoices, charges, fees, and premiums Requests for clarification on billing statements Inquiries about payment methods and due dates\n", + " \n", + "\n", + "\n", " \n", - " Requests for policy changes, updates, or cancellations Questions about policy renewals and reinstatements Inquiries about adding or removing coverage options \n", - " \n", - " \n", - " \n", - " \n", - " Questions about the claims process and filing procedures Requests for help with submitting claim documentation Inquiries about claim status and payout timelines \n", - " \n", - " \n", - " \n", - " \n", - " Questions about what is covered under specific policy types Requests for clarification on coverage limits and exclusions Inquiries about deductibles and out-of-pocket expenses \n", - " \n", - " \n", - " \n", - " \n", - " Requests for new policy quotes and price comparisons Questions about available discounts and bundling options Inquiries about switching from another insurer \n", - " \n", - " \n", - " \n", - " \n", - " Requests for login credentials or password resets Questions about online account features and functionality Inquiries about updating contact or personal information \n", - " \n", - " \n", - " \n", - " \n", - " Complaints about unexpected or incorrect charges Requests for refunds or premium adjustments Inquiries about late fees or collection notices \n", - " \n", - " \n", - " \n", - " \n", - " Complaints about denied or underpaid claims Requests for reconsideration of claim decisions Inquiries about appealing a claim outcome \n", - " \n", - " \n", - " \n", - " \n", - " Questions about the differences between policy options Requests for help deciding between coverage levels Inquiries about how policies compare to competitors' offerings \n", - " \n", - " \n", - " \n", - " \n", - " Questions about company contact information or hours of operation Requests for general information about products or services Inquiries that don't fit neatly into other categories \n", - " \n", + " Requests for policy changes, updates, or cancellations Questions about policy renewals and reinstatements Inquiries about adding or removing coverage options\n", + " \n", + "\n", + "\n", + " \n", + " Questions about the claims process and filing procedures Requests for help with submitting claim documentation Inquiries about claim status and payout timelines\n", + " \n", + "\n", + "\n", + " \n", + " Questions about what is covered under specific policy types Requests for clarification on coverage limits and exclusions Inquiries about deductibles and out-of-pocket expenses\n", + " \n", + "\n", + "\n", + " \n", + " Requests for new policy quotes and price comparisons Questions about available discounts and bundling options Inquiries about switching from another insurer\n", + " \n", + "\n", + "\n", + " \n", + " Requests for login credentials or password resets Questions about online account features and functionality Inquiries about updating contact or personal information\n", + " \n", + "\n", + "\n", + " \n", + " Complaints about unexpected or incorrect charges Requests for refunds or premium adjustments Inquiries about late fees or collection notices\n", + " \n", + "\n", + "\n", + " \n", + " Complaints about denied or underpaid claims Requests for reconsideration of claim decisions Inquiries about appealing a claim outcome\n", + " \n", + "\n", + "\n", + " \n", + " Questions about the differences between policy options Requests for help deciding between coverage levels Inquiries about how policies compare to competitors' offerings\n", + " \n", + "\n", + "\n", + " \n", + " Questions about company contact information or hours of operation Requests for general information about products or services Inquiries that don't fit neatly into other categories\n", + " \n", "\"\"\")" ] }, @@ -599,11 +601,12 @@ "metadata": {}, "outputs": [], "source": [ + "import json\n", "import os\n", + "import pickle\n", + "\n", "import numpy as np\n", "import voyageai\n", - "import pickle\n", - "import json\n", "\n", "\n", "class VectorDB:\n", @@ -905,7 +908,7 @@ "\n", " First you will think step-by-step about the problem in scratchpad tags.\n", " You should consider all the information provided and create a concrete argument for your classification.\n", - " \n", + "\n", " Respond using this format:\n", " \n", " Your thoughts and analysis go here\n", @@ -1061,6 +1064,7 @@ ], "source": [ "import json\n", + "\n", "import pandas as pd\n", "\n", "promptfoo_results = pd.read_csv(\"./data/results.csv\")\n", diff --git a/capabilities/contextual-embeddings/contextual-rag-lambda-function/inference_adapter.py b/capabilities/contextual-embeddings/contextual-rag-lambda-function/inference_adapter.py index be22494f..6f98e379 100644 --- a/capabilities/contextual-embeddings/contextual-rag-lambda-function/inference_adapter.py +++ b/capabilities/contextual-embeddings/contextual-rag-lambda-function/inference_adapter.py @@ -1,4 +1,5 @@ import json + import boto3 from botocore.exceptions import ClientError diff --git a/capabilities/contextual-embeddings/contextual-rag-lambda-function/lambda_function.py b/capabilities/contextual-embeddings/contextual-rag-lambda-function/lambda_function.py index 55c0dd74..c8b11d90 100644 --- a/capabilities/contextual-embeddings/contextual-rag-lambda-function/lambda_function.py +++ b/capabilities/contextual-embeddings/contextual-rag-lambda-function/lambda_function.py @@ -1,5 +1,6 @@ import json import logging + from inference_adapter import InferenceAdapter from s3_adapter import S3Adapter @@ -24,7 +25,7 @@ def lambda_handler(event, context): - logger.debug("input={}".format(json.dumps(event))) + logger.debug(f"input={json.dumps(event)}") s3_adapter = S3Adapter() inference_adapter = InferenceAdapter() diff --git a/capabilities/contextual-embeddings/contextual-rag-lambda-function/s3_adapter.py b/capabilities/contextual-embeddings/contextual-rag-lambda-function/s3_adapter.py index 0636e277..dc0c8587 100644 --- a/capabilities/contextual-embeddings/contextual-rag-lambda-function/s3_adapter.py +++ b/capabilities/contextual-embeddings/contextual-rag-lambda-function/s3_adapter.py @@ -1,4 +1,5 @@ import json + import boto3 from botocore.exceptions import ClientError diff --git a/capabilities/contextual-embeddings/guide.ipynb b/capabilities/contextual-embeddings/guide.ipynb index 3c24c6f2..640885c5 100644 --- a/capabilities/contextual-embeddings/guide.ipynb +++ b/capabilities/contextual-embeddings/guide.ipynb @@ -182,11 +182,12 @@ "metadata": {}, "outputs": [], "source": [ - "import pickle\n", "import json\n", + "import pickle\n", + "from typing import Any\n", + "\n", "import numpy as np\n", "import voyageai\n", - "from typing import List, Dict, Any\n", "from tqdm import tqdm\n", "\n", "\n", @@ -201,7 +202,7 @@ " self.query_cache = {}\n", " self.db_path = f\"./data/{name}/vector_db.pkl\"\n", "\n", - " def load_data(self, dataset: List[Dict[str, Any]]):\n", + " def load_data(self, dataset: list[dict[str, Any]]):\n", " if self.embeddings and self.metadata:\n", " print(\"Vector database is already loaded. Skipping data loading.\")\n", " return\n", @@ -234,7 +235,7 @@ "\n", " print(f\"Vector database loaded and saved. Total chunks processed: {len(texts_to_embed)}\")\n", "\n", - " def _embed_and_store(self, texts: List[str], data: List[Dict[str, Any]]):\n", + " def _embed_and_store(self, texts: list[str], data: list[dict[str, Any]]):\n", " batch_size = 128\n", " with tqdm(total=len(texts), desc=\"Embedding chunks\") as pbar:\n", " result = []\n", @@ -247,7 +248,7 @@ " self.embeddings = result\n", " self.metadata = data\n", "\n", - " def search(self, query: str, k: int = 20) -> List[Dict[str, Any]]:\n", + " def search(self, query: str, k: int = 20) -> list[dict[str, Any]]:\n", " if query in self.query_cache:\n", " query_embedding = self.query_cache[query]\n", " else:\n", @@ -329,7 +330,7 @@ ], "source": [ "# Load your transformed dataset\n", - "with open(\"data/codebase_chunks.json\", \"r\") as f:\n", + "with open(\"data/codebase_chunks.json\") as f:\n", " transformed_dataset = json.load(f)\n", "\n", "# Initialize the VectorDB\n", @@ -361,19 +362,21 @@ "outputs": [], "source": [ "import json\n", - "from typing import List, Dict, Any, Callable\n", + "from collections.abc import Callable\n", + "from typing import Any\n", + "\n", "from tqdm import tqdm\n", "\n", "\n", - "def load_jsonl(file_path: str) -> List[Dict[str, Any]]:\n", + "def load_jsonl(file_path: str) -> list[dict[str, Any]]:\n", " \"\"\"Load JSONL file and return a list of dictionaries.\"\"\"\n", - " with open(file_path, \"r\") as file:\n", + " with open(file_path) as file:\n", " return [json.loads(line) for line in file]\n", "\n", "\n", "def evaluate_retrieval(\n", - " queries: List[Dict[str, Any]], retrieval_function: Callable, db, k: int = 20\n", - ") -> Dict[str, float]:\n", + " queries: list[dict[str, Any]], retrieval_function: Callable, db, k: int = 20\n", + ") -> dict[str, float]:\n", " total_score = 0\n", " total_queries = len(queries)\n", "\n", @@ -429,7 +432,7 @@ " return {\"pass_at_n\": pass_at_n, \"average_score\": average_score, \"total_queries\": total_queries}\n", "\n", "\n", - "def retrieve_base(query: str, db, k: int = 20) -> List[Dict[str, Any]]:\n", + "def retrieve_base(query: str, db, k: int = 20) -> list[dict[str, Any]]:\n", " \"\"\"\n", " Retrieve relevant documents using either VectorDB or ContextualVectorDB.\n", "\n", @@ -450,7 +453,7 @@ " return results\n", "\n", "\n", - "def evaluate_and_display(db, jsonl_path: str, k_values: List[int] = [5, 10, 20], db_name: str = \"\"):\n", + "def evaluate_and_display(db, jsonl_path: str, k_values: list[int] = None, db_name: str = \"\"):\n", " \"\"\"\n", " Evaluate retrieval performance across multiple k values and display formatted results.\n", "\n", @@ -463,6 +466,8 @@ " Returns:\n", " Dict mapping k values to their results\n", " \"\"\"\n", + " if k_values is None:\n", + " k_values = [5, 10, 20]\n", " results = {}\n", "\n", " print(f\"{'=' * 60}\")\n", @@ -721,17 +726,18 @@ "metadata": {}, "outputs": [], "source": [ + "import json\n", "import os\n", "import pickle\n", - "import json\n", - "import numpy as np\n", - "import voyageai\n", - "from typing import List, Dict, Any\n", - "from tqdm import tqdm\n", - "import anthropic\n", "import threading\n", "import time\n", "from concurrent.futures import ThreadPoolExecutor, as_completed\n", + "from typing import Any\n", + "\n", + "import anthropic\n", + "import numpy as np\n", + "import voyageai\n", + "from tqdm import tqdm\n", "\n", "\n", "class ContextualVectorDB:\n", @@ -795,7 +801,7 @@ " )\n", " return response.content[0].text, response.usage\n", "\n", - " def load_data(self, dataset: List[Dict[str, Any]], parallel_threads: int = 1):\n", + " def load_data(self, dataset: list[dict[str, Any]], parallel_threads: int = 1):\n", " if self.embeddings and self.metadata:\n", " print(\"Vector database is already loaded. Skipping data loading.\")\n", " return\n", @@ -868,7 +874,7 @@ " print(\"Tokens read from cache come at a 90 percent discount!\")\n", "\n", " # we use voyage AI here for embeddings. Read more here: https://docs.voyageai.com/docs/embeddings\n", - " def _embed_and_store(self, texts: List[str], data: List[Dict[str, Any]]):\n", + " def _embed_and_store(self, texts: list[str], data: list[dict[str, Any]]):\n", " batch_size = 128\n", " result = [\n", " self.voyage_client.embed(texts[i : i + batch_size], model=\"voyage-2\").embeddings\n", @@ -877,7 +883,7 @@ " self.embeddings = [embedding for batch in result for embedding in batch]\n", " self.metadata = data\n", "\n", - " def search(self, query: str, k: int = 20) -> List[Dict[str, Any]]:\n", + " def search(self, query: str, k: int = 20) -> list[dict[str, Any]]:\n", " if query in self.query_cache:\n", " query_embedding = self.query_cache[query]\n", " else:\n", @@ -956,7 +962,7 @@ ], "source": [ "# Load the transformed dataset\n", - "with open(\"data/codebase_chunks.json\", \"r\") as f:\n", + "with open(\"data/codebase_chunks.json\") as f:\n", " transformed_dataset = json.load(f)\n", "\n", "# Initialize the ContextualVectorDB\n", @@ -1139,12 +1145,13 @@ "metadata": {}, "outputs": [], "source": [ - "import os\n", "import json\n", - "from typing import List, Dict, Any\n", - "from tqdm import tqdm\n", + "import os\n", + "from typing import Any\n", + "\n", "from elasticsearch import Elasticsearch\n", "from elasticsearch.helpers import bulk\n", + "from tqdm import tqdm\n", "\n", "\n", "class ElasticsearchBM25:\n", @@ -1180,7 +1187,7 @@ " )\n", " print(f\"Created index: {self.index_name}\")\n", "\n", - " def index_documents(self, documents: List[Dict[str, Any]]):\n", + " def index_documents(self, documents: list[dict[str, Any]]):\n", " actions = [\n", " {\n", " \"_index\": self.index_name,\n", @@ -1198,7 +1205,7 @@ " self.es_client.indices.refresh(index=self.index_name)\n", " return success\n", "\n", - " def search(self, query: str, k: int = 20) -> List[Dict[str, Any]]:\n", + " def search(self, query: str, k: int = 20) -> list[dict[str, Any]]:\n", " self.es_client.indices.refresh(index=self.index_name)\n", "\n", " # Change this - remove 'body=' and pass query directly\n", @@ -1313,7 +1320,7 @@ "def evaluate_db_advanced(\n", " db: ContextualVectorDB,\n", " original_jsonl_path: str,\n", - " k_values: List[int] = [5, 10, 20],\n", + " k_values: list[int] = None,\n", " db_name: str = \"Hybrid Search\",\n", "):\n", " \"\"\"\n", @@ -1328,6 +1335,8 @@ " Returns:\n", " Dict mapping k values to their results and source breakdowns\n", " \"\"\"\n", + " if k_values is None:\n", + " k_values = [5, 10, 20]\n", " original_data = load_jsonl(original_jsonl_path)\n", " es_bm25 = create_elasticsearch_bm25_index(db)\n", " results = {}\n", @@ -1563,14 +1572,16 @@ "metadata": {}, "outputs": [], "source": [ - "import cohere\n", - "from typing import List, Dict, Any, Callable\n", "import json\n", + "from collections.abc import Callable\n", + "from typing import Any\n", + "\n", + "import cohere\n", "from tqdm import tqdm\n", "\n", "\n", "def evaluate_db_rerank(\n", - " db, original_jsonl_path: str, k_values: List[int] = [5, 10, 20], db_name: str = \"Reranking\"\n", + " db, original_jsonl_path: str, k_values: list[int] = None, db_name: str = \"Reranking\"\n", "):\n", " \"\"\"\n", " Evaluate reranking performance at multiple k values with formatted results.\n", @@ -1584,6 +1595,8 @@ " Returns:\n", " Dict mapping k values to their results\n", " \"\"\"\n", + " if k_values is None:\n", + " k_values = [5, 10, 20]\n", " original_data = load_jsonl(original_jsonl_path)\n", " co = cohere.Client(os.getenv(\"COHERE_API_KEY\"))\n", " results = {}\n", diff --git a/capabilities/retrieval_augmented_generation/evaluation/eval_end_to_end.py b/capabilities/retrieval_augmented_generation/evaluation/eval_end_to_end.py index 652b9a77..b0de200c 100644 --- a/capabilities/retrieval_augmented_generation/evaluation/eval_end_to_end.py +++ b/capabilities/retrieval_augmented_generation/evaluation/eval_end_to_end.py @@ -1,27 +1,28 @@ -from typing import Dict, Union, Any -from anthropic import Anthropic -import re import os +import re +from typing import Any + +from anthropic import Anthropic def evaluate_end_to_end(query, generated_answer, correct_answer): prompt = f""" You are an AI assistant tasked with evaluating the correctness of answers to questions about Anthropic's documentation. - + Question: {query} - + Correct Answer: {correct_answer} - + Generated Answer: {generated_answer} - - Is the Generated Answer correct based on the Correct Answer? You should pay attention to the substance of the answer, and ignore minute details that may differ. - - Small differences or changes in wording don't matter. If the generated answer and correct answer are saying essentially the same thing then that generated answer should be marked correct. - - However, if there is any critical piece of information which is missing from the generated answer in comparison to the correct answer, then we should mark this as incorrect. - + + Is the Generated Answer correct based on the Correct Answer? You should pay attention to the substance of the answer, and ignore minute details that may differ. + + Small differences or changes in wording don't matter. If the generated answer and correct answer are saying essentially the same thing then that generated answer should be marked correct. + + However, if there is any critical piece of information which is missing from the generated answer in comparison to the correct answer, then we should mark this as incorrect. + Finally, if there are any direct contradictions between the correct answer and generated answer, we should deem the generated answer to be incorrect. - + Respond in the following XML format: @@ -78,7 +79,7 @@ def evaluate_end_to_end(query, generated_answer, correct_answer): return result -def get_assert(output: str, context) -> Union[bool, float, Dict[str, Any]]: +def get_assert(output: str, context) -> bool | float | dict[str, Any]: correct_answer = context["vars"]["correct_answer"] query = context["vars"]["query"] result = evaluate_end_to_end(query, output, correct_answer) diff --git a/capabilities/retrieval_augmented_generation/evaluation/eval_retrieval.py b/capabilities/retrieval_augmented_generation/evaluation/eval_retrieval.py index 0857d519..3fbae99e 100644 --- a/capabilities/retrieval_augmented_generation/evaluation/eval_retrieval.py +++ b/capabilities/retrieval_augmented_generation/evaluation/eval_retrieval.py @@ -1,8 +1,8 @@ -from typing import Dict, Union, Any, List import ast +from typing import Any -def calculate_mrr(retrieved_links: List[str], correct_links) -> float: +def calculate_mrr(retrieved_links: list[str], correct_links) -> float: for i, link in enumerate(retrieved_links, 1): if link in correct_links: return 1 / i @@ -19,12 +19,12 @@ def evaluate_retrieval(retrieved_links, correct_links): return precision, recall, mrr, f1 -def get_assert(output: str, context) -> Union[bool, float, Dict[str, Any]]: +def get_assert(output: str, context) -> bool | float | dict[str, Any]: correct_chunks = context["vars"]["correct_chunks"] try: precision, recall, mrr, f1 = evaluate_retrieval(output, correct_chunks) - metrics: Dict[str, float] = {} + metrics: dict[str, float] = {} metrics["precision"] = precision metrics["recall"] = recall metrics["f1"] = f1 diff --git a/capabilities/retrieval_augmented_generation/evaluation/prompts.py b/capabilities/retrieval_augmented_generation/evaluation/prompts.py index 9f7b33a9..963dcd7b 100644 --- a/capabilities/retrieval_augmented_generation/evaluation/prompts.py +++ b/capabilities/retrieval_augmented_generation/evaluation/prompts.py @@ -1,15 +1,15 @@ import json import os -from typing import List, Dict, Tuple -from vectordb import VectorDB, SummaryIndexedVectorDB + from anthropic import Anthropic +from vectordb import SummaryIndexedVectorDB, VectorDB client = Anthropic(api_key=os.environ.get("ANTHROPIC_API_KEY")) # Initialize the VectorDB db = VectorDB("anthropic_docs") # Load the Claude Documentation -with open("../data/anthropic_docs.json", "r") as f: +with open("../data/anthropic_docs.json") as f: anthropic_docs = json.load(f) db.load_data(anthropic_docs) @@ -27,7 +27,7 @@ def answer_query_base(context): input_query = context["vars"]["query"] documents, document_context = _retrieve_base(input_query, db) prompt = f""" - You have been tasked with helping us to answer the following query: + You have been tasked with helping us to answer the following query: {input_query} @@ -35,7 +35,7 @@ def answer_query_base(context): {document_context} - Please remain faithful to the underlying context, and only deviate from it if you are 100% sure that you know the answer already. + Please remain faithful to the underlying context, and only deviate from it if you are 100% sure that you know the answer already. Answer the question now, and avoid providing preamble such as 'Here is the answer', etc """ @@ -45,7 +45,7 @@ def answer_query_base(context): # Initialize the VectorDB db_summary = SummaryIndexedVectorDB("anthropic_docs_summaries") # Load the Claude Documentation -with open("../data/anthropic_summary_indexed_docs.json", "r") as f: +with open("../data/anthropic_summary_indexed_docs.json") as f: anthropic_docs_summaries = json.load(f) db_summary.load_data(anthropic_docs_summaries) @@ -63,7 +63,7 @@ def answer_query_level_two(context): input_query = context["vars"]["query"] documents, document_context = retrieve_level_two(input_query) prompt = f""" - You have been tasked with helping us to answer the following query: + You have been tasked with helping us to answer the following query: {input_query} @@ -71,7 +71,7 @@ def answer_query_level_two(context): {document_context} - Please remain faithful to the underlying context, and only deviate from it if you are 100% sure that you know the answer already. + Please remain faithful to the underlying context, and only deviate from it if you are 100% sure that you know the answer already. Answer the question now, and avoid providing preamble such as 'Here is the answer', etc """ @@ -81,12 +81,12 @@ def answer_query_level_two(context): # Initialize the VectorDB db_rerank = SummaryIndexedVectorDB("anthropic_docs_rerank") # Load the Claude Documentation -with open("../data/anthropic_summary_indexed_docs.json", "r") as f: +with open("../data/anthropic_summary_indexed_docs.json") as f: anthropic_docs_summaries = json.load(f) db_rerank.load_data(anthropic_docs_summaries) -def _rerank_results(query: str, results: List[Dict], k: int = 5) -> List[Dict]: +def _rerank_results(query: str, results: list[dict], k: int = 5) -> list[dict]: # Prepare the summaries with their indices summaries = [] print(len(results)) @@ -103,9 +103,9 @@ def _rerank_results(query: str, results: List[Dict], k: int = 5) -> List[Dict]: prompt = f""" Query: {query} You are about to be given a group of documents, each preceded by its index number in square brackets. Your task is to select the only {k} most relevant documents from the list to help us answer the query. - + {joined_summaries} - + Output only the indices of {k} most relevant documents in order of relevance, separated by commas, enclosed in XML tags here: put the numbers of your indices here, seeparted by commas """ @@ -155,7 +155,7 @@ def _rerank_results(query: str, results: List[Dict], k: int = 5) -> List[Dict]: return results[:k] -def _retrieve_advanced(query: str, k: int = 3, initial_k: int = 20) -> Tuple[List[Dict], str]: +def _retrieve_advanced(query: str, k: int = 3, initial_k: int = 20) -> tuple[list[dict], str]: # Step 1: Get initial results initial_results = db_rerank.search(query, k=initial_k) @@ -178,7 +178,7 @@ def answer_query_level_three(context): input_query = context["vars"]["query"] documents, document_context = _retrieve_advanced(input_query) prompt = f""" - You have been tasked with helping us to answer the following query: + You have been tasked with helping us to answer the following query: {input_query} @@ -186,7 +186,7 @@ def answer_query_level_three(context): {document_context} - Please remain faithful to the underlying context, and only deviate from it if you are 100% sure that you know the answer already. + Please remain faithful to the underlying context, and only deviate from it if you are 100% sure that you know the answer already. Answer the question now, and avoid providing preamble such as 'Here is the answer', etc """ return prompt diff --git a/capabilities/retrieval_augmented_generation/evaluation/provider_retrieval.py b/capabilities/retrieval_augmented_generation/evaluation/provider_retrieval.py index 0dd8419f..7570e1cc 100644 --- a/capabilities/retrieval_augmented_generation/evaluation/provider_retrieval.py +++ b/capabilities/retrieval_augmented_generation/evaluation/provider_retrieval.py @@ -1,13 +1,13 @@ import json import os -from typing import List, Dict -from vectordb import VectorDB, SummaryIndexedVectorDB + from anthropic import Anthropic +from vectordb import SummaryIndexedVectorDB, VectorDB # Initialize the VectorDB db = VectorDB("anthropic_docs") # Load the Claude Documentation -with open("../data/anthropic_docs.json", "r") as f: +with open("../data/anthropic_docs.json") as f: anthropic_docs = json.load(f) db.load_data(anthropic_docs) @@ -26,7 +26,7 @@ def retrieve_base(query, options, context): # Initialize the VectorDB db_summary = SummaryIndexedVectorDB("anthropic_docs_summaries") # Load the Claude Documentation -with open("../data/anthropic_summary_indexed_docs.json", "r") as f: +with open("../data/anthropic_summary_indexed_docs.json") as f: anthropic_docs_summaries = json.load(f) db_summary.load_data(anthropic_docs_summaries) @@ -42,7 +42,7 @@ def retrieve_level_two(query, options, context): return result -def _rerank_results(query: str, results: List[Dict], k: int = 3) -> List[Dict]: +def _rerank_results(query: str, results: list[dict], k: int = 3) -> list[dict]: # Prepare the summaries with their indices summaries = [] print(len(results)) @@ -59,9 +59,9 @@ def _rerank_results(query: str, results: List[Dict], k: int = 3) -> List[Dict]: prompt = f""" Query: {query} You are about to be given a group of documents, each preceded by its index number in square brackets. Your task is to select the only {k} most relevant documents from the list to help us answer the query. - + {joined_summaries} - + Output only the indices of {k} most relevant documents in order of relevance, separated by commas, enclosed in XML tags here: put the numbers of your indices here, seeparted by commas """ @@ -116,7 +116,7 @@ def _rerank_results(query: str, results: List[Dict], k: int = 3) -> List[Dict]: # Initialize the VectorDB db_rerank = SummaryIndexedVectorDB("anthropic_docs_summaries_rerank") # Load the Claude Documentation -with open("../data/anthropic_summary_indexed_docs.json", "r") as f: +with open("../data/anthropic_summary_indexed_docs.json") as f: anthropic_docs_summaries = json.load(f) db_rerank.load_data(anthropic_docs_summaries) diff --git a/capabilities/retrieval_augmented_generation/evaluation/vectordb.py b/capabilities/retrieval_augmented_generation/evaluation/vectordb.py index 285abec1..dd099bf0 100644 --- a/capabilities/retrieval_augmented_generation/evaluation/vectordb.py +++ b/capabilities/retrieval_augmented_generation/evaluation/vectordb.py @@ -1,6 +1,7 @@ +import json import os import pickle -import json + import numpy as np import voyageai diff --git a/capabilities/retrieval_augmented_generation/guide.ipynb b/capabilities/retrieval_augmented_generation/guide.ipynb index db6197c3..88405d0a 100644 --- a/capabilities/retrieval_augmented_generation/guide.ipynb +++ b/capabilities/retrieval_augmented_generation/guide.ipynb @@ -68,7 +68,6 @@ "name": "stdout", "output_type": "stream", "text": [ - "Looking in indexes: https://reader2:****@artifactory.infra.ant.dev/artifactory/api/pypi/pypi-all/simple\n", "Requirement already satisfied: anthropic in /opt/homebrew/Caskroom/miniforge/base/envs/py311/lib/python3.11/site-packages (0.34.1)\n", "Requirement already satisfied: anyio<5,>=3.5.0 in /opt/homebrew/Caskroom/miniforge/base/envs/py311/lib/python3.11/site-packages (from anthropic) (3.7.1)\n", "Requirement already satisfied: distro<2,>=1.7.0 in /opt/homebrew/Caskroom/miniforge/base/envs/py311/lib/python3.11/site-packages (from anthropic) (1.8.0)\n", @@ -84,7 +83,6 @@ "Requirement already satisfied: h11<0.15,>=0.13 in /opt/homebrew/Caskroom/miniforge/base/envs/py311/lib/python3.11/site-packages (from httpcore==1.*->httpx<1,>=0.23.0->anthropic) (0.14.0)\n", "Requirement already satisfied: annotated-types>=0.4.0 in /opt/homebrew/Caskroom/miniforge/base/envs/py311/lib/python3.11/site-packages (from pydantic<3,>=1.9.0->anthropic) (0.6.0)\n", "Requirement already satisfied: pydantic-core==2.18.3 in /opt/homebrew/Caskroom/miniforge/base/envs/py311/lib/python3.11/site-packages (from pydantic<3,>=1.9.0->anthropic) (2.18.3)\n", - "Looking in indexes: https://reader2:****@artifactory.infra.ant.dev/artifactory/api/pypi/pypi-all/simple\n", "Requirement already satisfied: voyageai in /opt/homebrew/Caskroom/miniforge/base/envs/py311/lib/python3.11/site-packages (0.2.2)\n", "Requirement already satisfied: aiohttp<4.0,>=3.5 in /opt/homebrew/Caskroom/miniforge/base/envs/py311/lib/python3.11/site-packages (from voyageai) (3.9.3)\n", "Requirement already satisfied: aiolimiter<2.0.0,>=1.1.0 in /opt/homebrew/Caskroom/miniforge/base/envs/py311/lib/python3.11/site-packages (from voyageai) (1.1.0)\n", @@ -100,16 +98,13 @@ "Requirement already satisfied: idna<4,>=2.5 in /opt/homebrew/Caskroom/miniforge/base/envs/py311/lib/python3.11/site-packages (from requests<3.0,>=2.20->voyageai) (3.4)\n", "Requirement already satisfied: urllib3<3,>=1.21.1 in /opt/homebrew/Caskroom/miniforge/base/envs/py311/lib/python3.11/site-packages (from requests<3.0,>=2.20->voyageai) (1.26.18)\n", "Requirement already satisfied: certifi>=2017.4.17 in /opt/homebrew/Caskroom/miniforge/base/envs/py311/lib/python3.11/site-packages (from requests<3.0,>=2.20->voyageai) (2023.11.17)\n", - "Looking in indexes: https://reader2:****@artifactory.infra.ant.dev/artifactory/api/pypi/pypi-all/simple\n", "Requirement already satisfied: pandas in /opt/homebrew/Caskroom/miniforge/base/envs/py311/lib/python3.11/site-packages (2.0.3)\n", "Requirement already satisfied: python-dateutil>=2.8.2 in /opt/homebrew/Caskroom/miniforge/base/envs/py311/lib/python3.11/site-packages (from pandas) (2.8.2)\n", "Requirement already satisfied: pytz>=2020.1 in /opt/homebrew/Caskroom/miniforge/base/envs/py311/lib/python3.11/site-packages (from pandas) (2023.3)\n", "Requirement already satisfied: tzdata>=2022.1 in /opt/homebrew/Caskroom/miniforge/base/envs/py311/lib/python3.11/site-packages (from pandas) (2023.3)\n", "Requirement already satisfied: numpy>=1.21.0 in /opt/homebrew/Caskroom/miniforge/base/envs/py311/lib/python3.11/site-packages (from pandas) (1.24.4)\n", "Requirement already satisfied: six>=1.5 in /opt/homebrew/Caskroom/miniforge/base/envs/py311/lib/python3.11/site-packages (from python-dateutil>=2.8.2->pandas) (1.16.0)\n", - "Looking in indexes: https://reader2:****@artifactory.infra.ant.dev/artifactory/api/pypi/pypi-all/simple\n", "Requirement already satisfied: numpy in /opt/homebrew/Caskroom/miniforge/base/envs/py311/lib/python3.11/site-packages (1.24.4)\n", - "Looking in indexes: https://reader2:****@artifactory.infra.ant.dev/artifactory/api/pypi/pypi-all/simple\n", "Requirement already satisfied: matplotlib in /opt/homebrew/Caskroom/miniforge/base/envs/py311/lib/python3.11/site-packages (3.7.2)\n", "Requirement already satisfied: contourpy>=1.0.1 in /opt/homebrew/Caskroom/miniforge/base/envs/py311/lib/python3.11/site-packages (from matplotlib) (1.2.1)\n", "Requirement already satisfied: cycler>=0.10 in /opt/homebrew/Caskroom/miniforge/base/envs/py311/lib/python3.11/site-packages (from matplotlib) (0.11.0)\n", @@ -121,7 +116,6 @@ "Requirement already satisfied: pyparsing<3.1,>=2.3.1 in /opt/homebrew/Caskroom/miniforge/base/envs/py311/lib/python3.11/site-packages (from matplotlib) (3.0.9)\n", "Requirement already satisfied: python-dateutil>=2.7 in /opt/homebrew/Caskroom/miniforge/base/envs/py311/lib/python3.11/site-packages (from matplotlib) (2.8.2)\n", "Requirement already satisfied: six>=1.5 in /opt/homebrew/Caskroom/miniforge/base/envs/py311/lib/python3.11/site-packages (from python-dateutil>=2.7->matplotlib) (1.16.0)\n", - "Looking in indexes: https://reader2:****@artifactory.infra.ant.dev/artifactory/api/pypi/pypi-all/simple\n", "Requirement already satisfied: seaborn in /opt/homebrew/Caskroom/miniforge/base/envs/py311/lib/python3.11/site-packages (0.12.2)\n", "Requirement already satisfied: numpy!=1.24.0,>=1.17 in /opt/homebrew/Caskroom/miniforge/base/envs/py311/lib/python3.11/site-packages (from seaborn) (1.24.4)\n", "Requirement already satisfied: pandas>=0.25 in /opt/homebrew/Caskroom/miniforge/base/envs/py311/lib/python3.11/site-packages (from seaborn) (2.0.3)\n", @@ -137,7 +131,6 @@ "Requirement already satisfied: pytz>=2020.1 in /opt/homebrew/Caskroom/miniforge/base/envs/py311/lib/python3.11/site-packages (from pandas>=0.25->seaborn) (2023.3)\n", "Requirement already satisfied: tzdata>=2022.1 in /opt/homebrew/Caskroom/miniforge/base/envs/py311/lib/python3.11/site-packages (from pandas>=0.25->seaborn) (2023.3)\n", "Requirement already satisfied: six>=1.5 in /opt/homebrew/Caskroom/miniforge/base/envs/py311/lib/python3.11/site-packages (from python-dateutil>=2.7->matplotlib!=3.6.1,>=3.1->seaborn) (1.16.0)\n", - "Looking in indexes: https://reader2:****@artifactory.infra.ant.dev/artifactory/api/pypi/pypi-all/simple\n", "Requirement already satisfied: scikit-learn in /opt/homebrew/Caskroom/miniforge/base/envs/py311/lib/python3.11/site-packages (1.5.1)\n", "Requirement already satisfied: numpy>=1.19.5 in /opt/homebrew/Caskroom/miniforge/base/envs/py311/lib/python3.11/site-packages (from scikit-learn) (1.24.4)\n", "Requirement already satisfied: scipy>=1.6.0 in /opt/homebrew/Caskroom/miniforge/base/envs/py311/lib/python3.11/site-packages (from scikit-learn) (1.11.1)\n", @@ -175,9 +168,10 @@ "metadata": {}, "outputs": [], "source": [ - "import anthropic\n", "import os\n", "\n", + "import anthropic\n", + "\n", "client = anthropic.Anthropic(\n", " # This is the default and can be omitted\n", " api_key=os.getenv(\"ANTHROPIC_API_KEY\"),\n", @@ -199,9 +193,10 @@ "metadata": {}, "outputs": [], "source": [ + "import json\n", "import os\n", "import pickle\n", - "import json\n", + "\n", "import numpy as np\n", "import voyageai\n", "\n", @@ -306,31 +301,25 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Loading vector database from disk.\n" - ] - } - ], + "outputs": [], "source": [ "import json\n", + "import logging\n", + "import xml.etree.ElementTree as ET # noqa: S314\n", + "from collections.abc import Callable\n", + "from typing import Any\n", + "\n", "import matplotlib.pyplot as plt\n", - "import xml.etree.ElementTree as ET\n", "from tqdm import tqdm\n", - "import logging\n", - "from typing import Callable, List, Dict, Any, Tuple, Set\n", "\n", "# Load the evaluation dataset\n", - "with open(\"evaluation/docs_evaluation_dataset.json\", \"r\") as f:\n", + "with open(\"evaluation/docs_evaluation_dataset.json\") as f:\n", " eval_data = json.load(f)\n", "\n", "# Load the Claude Documentation\n", - "with open(\"data/anthropic_docs.json\", \"r\") as f:\n", + "with open(\"data/anthropic_docs.json\") as f:\n", " anthropic_docs = json.load(f)\n", "\n", "# Initialize the VectorDB\n", @@ -350,7 +339,7 @@ "def answer_query_base(query, db):\n", " documents, context = retrieve_base(query, db)\n", " prompt = f\"\"\"\n", - " You have been tasked with helping us to answer the following query: \n", + " You have been tasked with helping us to answer the following query:\n", " \n", " {query}\n", " \n", @@ -358,7 +347,7 @@ " \n", " {context}\n", " \n", - " Please remain faithful to the underlying context, and only deviate from it if you are 100% sure that you know the answer already. \n", + " Please remain faithful to the underlying context, and only deviate from it if you are 100% sure that you know the answer already.\n", " Answer the question now, and avoid providing preamble such as 'Here is the answer', etc\n", " \"\"\"\n", " response = client.messages.create(\n", @@ -439,7 +428,7 @@ "\n", "def preview_json(file_path, num_items=3):\n", " try:\n", - " with open(file_path, \"r\") as file:\n", + " with open(file_path) as file:\n", " data = json.load(file)\n", "\n", " if isinstance(data, list):\n", @@ -563,11 +552,11 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ - "def calculate_mrr(retrieved_links: List[str], correct_links: Set[str]) -> float:\n", + "def calculate_mrr(retrieved_links: list[str], correct_links: set[str]) -> float:\n", " for i, link in enumerate(retrieved_links, 1):\n", " if link in correct_links:\n", " return 1 / i\n", @@ -575,8 +564,8 @@ "\n", "\n", "def evaluate_retrieval(\n", - " retrieval_function: Callable, evaluation_data: List[Dict[str, Any]], db: Any\n", - ") -> Tuple[float, float, float, float, List[float], List[float], List[float]]:\n", + " retrieval_function: Callable, evaluation_data: list[dict[str, Any]], db: Any\n", + ") -> tuple[float, float, float, float, list[float], list[float], list[float]]:\n", " precisions = []\n", " recalls = []\n", " mrrs = []\n", @@ -632,21 +621,21 @@ "\n", " prompt = f\"\"\"\n", " You are an AI assistant tasked with evaluating the correctness of answers to questions about Anthropic's documentation.\n", - " \n", + "\n", " Question: {query}\n", - " \n", + "\n", " Correct Answer: {correct_answer}\n", - " \n", + "\n", " Generated Answer: {generated_answer}\n", - " \n", - " Is the Generated Answer correct based on the Correct Answer? You should pay attention to the substance of the answer, and ignore minute details that may differ. \n", - " \n", - " Small differences or changes in wording don't matter. If the generated answer and correct answer are saying essentially the same thing then that generated answer should be marked correct. \n", - " \n", - " However, if there is any critical piece of information which is missing from the generated answer in comparison to the correct answer, then we should mark this as incorrect. \n", - " \n", + "\n", + " Is the Generated Answer correct based on the Correct Answer? You should pay attention to the substance of the answer, and ignore minute details that may differ.\n", + "\n", + " Small differences or changes in wording don't matter. If the generated answer and correct answer are saying essentially the same thing then that generated answer should be marked correct.\n", + "\n", + " However, if there is any critical piece of information which is missing from the generated answer in comparison to the correct answer, then we should mark this as incorrect.\n", + "\n", " Finally, if there are any direct contradictions between the correect answer and generated answer, we should deem the generated answer to be incorrect.\n", - " \n", + "\n", " Respond in the following XML format:\n", " \n", " \n", @@ -670,7 +659,8 @@ "\n", " response_text = response.content[0].text\n", " print(response_text)\n", - " evaluation = ET.fromstring(response_text)\n", + " # Parse XML from trusted LLM response\n", + " evaluation = ET.fromstring(response_text) # noqa: S314\n", " is_correct = evaluation.find(\"is_correct\").text.lower() == \"true\"\n", "\n", " if is_correct:\n", @@ -712,8 +702,9 @@ "metadata": {}, "outputs": [], "source": [ - "import os\n", "import json\n", + "import os\n", + "\n", "import matplotlib.pyplot as plt\n", "import seaborn as sns\n", "\n", @@ -729,7 +720,7 @@ " for filename in os.listdir(results_folder):\n", " if filename.endswith(\".json\"):\n", " file_path = os.path.join(results_folder, filename)\n", - " with open(file_path, \"r\") as f:\n", + " with open(file_path) as f:\n", " try:\n", " data = json.load(f)\n", " if \"name\" not in data:\n", @@ -780,7 +771,7 @@ " color_palette = colors[:num_methods] + sns.color_palette(\"husl\", num_methods - len(colors))\n", "\n", " # Plot bars for each method\n", - " for i, (result, color) in enumerate(zip(results, color_palette)):\n", + " for i, (result, color) in enumerate(zip(results, color_palette, strict=False)):\n", " values = [result[metric] for metric in metrics]\n", " offset = (i - len(results) / 2 + 0.5) * width\n", " bars = plt.bar([xi + offset for xi in x], values, width, label=result[\"name\"], color=color)\n", @@ -3273,12 +3264,13 @@ "outputs": [], "source": [ "import json\n", + "\n", "from tqdm import tqdm\n", "\n", "\n", "def generate_summaries(input_file, output_file):\n", " # Load the original documents\n", - " with open(input_file, \"r\") as f:\n", + " with open(input_file) as f:\n", " docs = json.load(f)\n", "\n", " # Prepare the context about the overall knowledge base\n", @@ -3288,7 +3280,7 @@ "\n", " for doc in tqdm(docs, desc=\"Generating summaries\"):\n", " prompt = f\"\"\"\n", - " You are tasked with creating a short summary of the following content from Anthropic's documentation. \n", + " You are tasked with creating a short summary of the following content from Anthropic's documentation.\n", "\n", " Context about the knowledge base:\n", " {knowledge_base_context}\n", @@ -3297,7 +3289,7 @@ " Heading: {doc[\"chunk_heading\"]}\n", " {doc[\"text\"]}\n", "\n", - " Please provide a brief summary of the above content in 2-3 sentences. The summary should capture the key points and be concise. We will be using it as a key part of our search pipeline when answering user queries about this content. \n", + " Please provide a brief summary of the above content in 2-3 sentences. The summary should capture the key points and be concise. We will be using it as a key part of our search pipeline when answering user queries about this content.\n", "\n", " Avoid using any preamble whatsoever in your response. Statements such as 'here is the summary' or 'the summary is as follows' are prohibited. You should get straight into the summary itself and be concise. Every word matters.\n", " \"\"\"\n", @@ -3352,10 +3344,11 @@ "metadata": {}, "outputs": [], "source": [ + "import json\n", "import os\n", - "import numpy as np\n", "import pickle\n", - "import json\n", + "\n", + "import numpy as np\n", "import voyageai\n", "\n", "\n", @@ -3381,7 +3374,7 @@ " self.load_db()\n", " return\n", "\n", - " with open(data_file, \"r\") as f:\n", + " with open(data_file) as f:\n", " data = json.load(f)\n", "\n", " texts = [\n", @@ -3490,7 +3483,7 @@ "def answer_query_level_two(query, db):\n", " documents, context = retrieve_base(query, db)\n", " prompt = f\"\"\"\n", - " You have been tasked with helping us to answer the following query: \n", + " You have been tasked with helping us to answer the following query:\n", " \n", " {query}\n", " \n", @@ -3498,7 +3491,7 @@ " \n", " {context}\n", " \n", - " Please remain faithful to the underlying context, and only deviate from it if you are 100% sure that you know the answer already. \n", + " Please remain faithful to the underlying context, and only deviate from it if you are 100% sure that you know the answer already.\n", " Answer the question now, and avoid providing preamble such as 'Here is the answer', etc\n", " \"\"\"\n", " response = client.messages.create(\n", @@ -5999,10 +5992,7 @@ "metadata": {}, "outputs": [], "source": [ - "from typing import List, Dict\n", - "\n", - "\n", - "def rerank_results(query: str, results: List[Dict], k: int = 5) -> List[Dict]:\n", + "def rerank_results(query: str, results: list[dict], k: int = 5) -> list[dict]:\n", " # Prepare the summaries with their indices\n", " summaries = []\n", " print(len(results))\n", @@ -6015,7 +6005,7 @@ " prompt = f\"\"\"\n", " Query: {query}\n", " You are about to be given a group of documents, each preceded by its index number in square brackets. Your task is to select the only {k} most relevant documents from the list to help us answer the query.\n", - " \n", + "\n", " \n", " {joined_summaries}\n", " \n", @@ -6071,7 +6061,7 @@ "\n", "def retrieve_advanced(\n", " query: str, db: SummaryIndexedVectorDB, k: int = 3, initial_k: int = 20\n", - ") -> Tuple[List[Dict], str]:\n", + ") -> tuple[list[dict], str]:\n", " # Step 1: Get initial results\n", " initial_results = db.search(query, k=initial_k)\n", "\n", @@ -6093,7 +6083,7 @@ "def answer_query_advanced(query: str, db: SummaryIndexedVectorDB):\n", " documents, context = retrieve_advanced(query, db)\n", " prompt = f\"\"\"\n", - " You have been tasked with helping us to answer the following query: \n", + " You have been tasked with helping us to answer the following query:\n", " \n", " {query}\n", " \n", @@ -6101,7 +6091,7 @@ " \n", " {context}\n", " \n", - " Please remain faithful to the underlying context, and only deviate from it if you are 100% sure that you know the answer already. \n", + " Please remain faithful to the underlying context, and only deviate from it if you are 100% sure that you know the answer already.\n", " Answer the question now, and avoid providing preamble such as 'Here is the answer', etc\n", " \"\"\"\n", " response = client.messages.create(\n", @@ -10425,11 +10415,12 @@ ], "source": [ "import json\n", - "import pandas as pd\n", + "\n", "import numpy as np\n", + "import pandas as pd\n", "\n", "# Load the JSON file\n", - "with open(\"data/end_to_end_results.json\", \"r\") as f:\n", + "with open(\"data/end_to_end_results.json\") as f:\n", " data = json.load(f)\n", "\n", "# Extract the results\n", diff --git a/capabilities/summarization/evaluation/custom_evals/bleu_eval.py b/capabilities/summarization/evaluation/custom_evals/bleu_eval.py index 74f687d1..b50264e6 100644 --- a/capabilities/summarization/evaluation/custom_evals/bleu_eval.py +++ b/capabilities/summarization/evaluation/custom_evals/bleu_eval.py @@ -1,8 +1,9 @@ -import numpy as np -from typing import Dict, Union, Any +from typing import Any + import nltk -from nltk.translate.bleu_score import sentence_bleu +import numpy as np from nltk.tokenize import word_tokenize +from nltk.translate.bleu_score import sentence_bleu # Download required NLTK data nltk.download("punkt", quiet=True) @@ -49,7 +50,7 @@ def nltk_bleu_eval(output, ground_truth) -> float: return bleu_score_float -def get_assert(output: str, context, threshold=0.3) -> Union[bool, float, Dict[str, Any]]: +def get_assert(output: str, context, threshold=0.3) -> bool | float | dict[str, Any]: ground_truth = context["vars"]["ground_truth"] score = nltk_bleu_eval(output, ground_truth) diff --git a/capabilities/summarization/evaluation/custom_evals/llm_eval.py b/capabilities/summarization/evaluation/custom_evals/llm_eval.py index d58ed820..f62e300b 100644 --- a/capabilities/summarization/evaluation/custom_evals/llm_eval.py +++ b/capabilities/summarization/evaluation/custom_evals/llm_eval.py @@ -1,7 +1,8 @@ -import anthropic -import os import json -from typing import Dict, Union, Any +import os +from typing import Any + +import anthropic def llm_eval(summary, input): @@ -34,7 +35,7 @@ def llm_eval(summary, input): 5. Does the summary accurately reflect the language and tone of the legal document? 6. Does the summary capture the key concepts and principles embodied in the legal document? 7. Does the summary omit any important ideas that should be captured to make decisions using the document? - + Provide a score for each criterion in JSON format. Here is the format you should follow always: @@ -48,9 +49,9 @@ def llm_eval(summary, input): Original Text: {input} - + Summary to Evaluate: {summary} - + Evaluation (JSON format):""" response = client.messages.create( @@ -68,7 +69,7 @@ def llm_eval(summary, input): return avg_score, evaluation["explanation"] -def get_assert(output: str, context, threshold=0.5) -> Union[bool, float, Dict[str, Any]]: +def get_assert(output: str, context, threshold=0.5) -> bool | float | dict[str, Any]: input = context["vars"]["input"] score, evaluation = llm_eval(output, input) diff --git a/capabilities/summarization/evaluation/custom_evals/rouge_eval.py b/capabilities/summarization/evaluation/custom_evals/rouge_eval.py index 5454b7a3..04cf2efd 100644 --- a/capabilities/summarization/evaluation/custom_evals/rouge_eval.py +++ b/capabilities/summarization/evaluation/custom_evals/rouge_eval.py @@ -1,5 +1,6 @@ +from typing import Any + import numpy as np -from typing import Dict, Union, Any from rouge_score import rouge_scorer @@ -26,7 +27,7 @@ def rouge_eval(summary, ground_truth, threshold=0.3) -> float: return float(avg_rouge) -def get_assert(output: str, context, threshold=0.3) -> Union[bool, float, Dict[str, Any]]: +def get_assert(output: str, context, threshold=0.3) -> bool | float | dict[str, Any]: ground_truth = context["vars"]["ground_truth"] score = rouge_eval(output, ground_truth) diff --git a/capabilities/summarization/evaluation/prompts.py b/capabilities/summarization/evaluation/prompts.py index d9ab7ca8..3e2c6f91 100644 --- a/capabilities/summarization/evaluation/prompts.py +++ b/capabilities/summarization/evaluation/prompts.py @@ -2,9 +2,9 @@ def basic_summarize(text): prompt = f""" You are a legal analyst known for highly accurate and detailed summaries of legal documents. Summarize the following text in bullet points. Focus on the main ideas and key details: - + {text} - + Here is the summary of the legal document: """ @@ -14,7 +14,7 @@ def basic_summarize(text): def guided_legal_summary(text): prompt = f""" You are a legal analyst known for highly accurate and detailed summaries of legal documents. - + Summarize the following legal document. Focus on these key aspects: 1. Parties involved @@ -29,7 +29,7 @@ def guided_legal_summary(text): {text} Here is the summary of the sublease agreement: - + """ return prompt @@ -54,12 +54,12 @@ def summarize_long_document(text): - Sublessor: [Name] // Add more details as needed - + If any information is not explicitly stated in the document, note it as "Not specified". Sublease agreement text: {text} - + Here is the summary of the sublease agreement: """ diff --git a/capabilities/summarization/guide.ipynb b/capabilities/summarization/guide.ipynb index 03440a52..c5e3eaac 100644 --- a/capabilities/summarization/guide.ipynb +++ b/capabilities/summarization/guide.ipynb @@ -84,11 +84,11 @@ ], "source": [ "import re\n", + "\n", "import anthropic\n", - "import pandas as pd\n", "import matplotlib.pyplot as plt\n", "import numpy as np\n", - "from typing import List, Dict, Tuple\n", + "import pandas as pd\n", "import seaborn as sns\n", "\n", "# Set up Anthropic client\n", @@ -129,9 +129,10 @@ } ], "source": [ - "import pypdf\n", "import re\n", "\n", + "import pypdf\n", + "\n", "pdf_path = \"data/Sample Sublease Agreement.pdf\"\n", "\n", "\n", @@ -454,7 +455,7 @@ "\n", " Document text:\n", " {text}\n", - " \n", + "\n", " \"\"\"\n", "\n", " response = client.messages.create(\n", @@ -570,12 +571,12 @@ " - Sublessor: [Name]\n", " // Add more details as needed\n", " \n", - " \n", + "\n", " If any information is not explicitly stated in the document, note it as \"Not specified\". Do not preamble.\n", "\n", " Sublease agreement text:\n", " {text}\n", - " \n", + "\n", " \"\"\"\n", "\n", " response = client.messages.create(\n", @@ -789,7 +790,7 @@ " ]\n", "\n", " final_summary_prompt = f\"\"\"\n", - " \n", + "\n", " You are looking at the chunked summaries of multiple documents that are all related. Combine the following summaries of the document from different truthful sources into a coherent overall summary:\n", "\n", " {\"\".join(chunk_summaries)}\n", @@ -807,7 +808,7 @@ " - Sublessor: [Name]\n", " // Add more details as needed\n", " \n", - " \n", + "\n", " If any information is not explicitly stated in the document, note it as \"Not specified\".\n", "\n", " Summary:\n", @@ -870,8 +871,8 @@ "class LegalSummaryIndexedDocuments:\n", " def __init__(self, client):\n", " self.client = client # Claude client\n", - " self.documents: List[Dict[str, str]] = [] # List of docs to store\n", - " self.summaries: List[str] = []\n", + " self.documents: list[dict[str, str]] = [] # List of docs to store\n", + " self.summaries: list[str] = []\n", "\n", " def add_document(self, doc_id: str, content: str):\n", " # Adds a document to the index\n", @@ -901,7 +902,7 @@ " - Sublessor: [Name]\n", " // Add more details as needed\n", " \n", - " \n", + "\n", " If any information is not explicitly stated in the document, note it as \"Not specified\".\n", "\n", " Sublease agreement text:\n", @@ -925,7 +926,7 @@ " )\n", " return response.content[0].text\n", "\n", - " def rank_documents(self, query: str, top_k: int = 3) -> List[Tuple[str, float]]:\n", + " def rank_documents(self, query: str, top_k: int = 3) -> list[tuple[str, float]]:\n", " \"\"\"\n", " Rank documents based on their relevance to the given query.\n", " We use Haiku here as a cheaper, faster model for ranking.\n", @@ -946,14 +947,14 @@ " ranked_indices = np.argsort(ranked_scores)[::-1][:top_k]\n", " return [(self.documents[i][\"id\"], ranked_scores[i]) for i in ranked_indices]\n", "\n", - " def extract_relevant_clauses(self, doc_id: str, query: str) -> List[str]:\n", + " def extract_relevant_clauses(self, doc_id: str, query: str) -> list[str]:\n", " \"\"\"\n", " Extracts relevant clauses from a document based on a query.\n", " \"\"\"\n", " doc_content = next(doc[\"content\"] for doc in self.documents if doc[\"id\"] == doc_id)\n", "\n", " prompt = f\"\"\"\n", - " Given the following legal query and document content, extract the most relevant clauses or sections and write the answer to the query. \n", + " Given the following legal query and document content, extract the most relevant clauses or sections and write the answer to the query.\n", " Provide each relevant clause or section separately, preserving the original legal language:\n", "\n", " Legal query: {query}\n", @@ -1269,10 +1270,11 @@ } ], "source": [ - "import pandas as pd\n", + "import re\n", + "\n", "import matplotlib.pyplot as plt\n", + "import pandas as pd\n", "import seaborn as sns\n", - "import re\n", "\n", "%matplotlib inline\n", "plt.style.use(\"seaborn\")\n", @@ -1291,7 +1293,7 @@ "\n", "# Apply the extraction to relevant columns\n", "for col in df.columns[2:]:\n", - " df[f\"{col}_result\"], df[f\"{col}_score\"] = zip(*df[col].apply(extract_result))\n", + " df[f\"{col}_result\"], df[f\"{col}_score\"] = zip(*df[col].apply(extract_result), strict=False)\n", "\n", "# Prepare data for grouped accuracy score\n", "models = [\"3.5 Sonnet\", \"3.0 Haiku\"]\n", diff --git a/capabilities/text_to_sql/evaluation/prompts.py b/capabilities/text_to_sql/evaluation/prompts.py index e001078a..349af41e 100644 --- a/capabilities/text_to_sql/evaluation/prompts.py +++ b/capabilities/text_to_sql/evaluation/prompts.py @@ -30,7 +30,7 @@ def generate_prompt(context): user_query = context["vars"]["user_query"] schema = get_schema_info() return f""" - You are an AI assistant that converts natural language queries into SQL. + You are an AI assistant that converts natural language queries into SQL. Given the following SQL database schema: {schema} diff --git a/capabilities/text_to_sql/evaluation/tests/test_above_average_salary.py b/capabilities/text_to_sql/evaluation/tests/test_above_average_salary.py index af58d4e6..0c402248 100644 --- a/capabilities/text_to_sql/evaluation/tests/test_above_average_salary.py +++ b/capabilities/text_to_sql/evaluation/tests/test_above_average_salary.py @@ -1,4 +1,4 @@ -from utils import extract_sql, execute_sql +from utils import execute_sql, extract_sql def get_assert(output, context): diff --git a/capabilities/text_to_sql/evaluation/tests/test_average_salary.py b/capabilities/text_to_sql/evaluation/tests/test_average_salary.py index 2fdc1e88..5c70215d 100644 --- a/capabilities/text_to_sql/evaluation/tests/test_average_salary.py +++ b/capabilities/text_to_sql/evaluation/tests/test_average_salary.py @@ -1,4 +1,4 @@ -from utils import extract_sql, execute_sql +from utils import execute_sql, extract_sql def get_assert(output, context): diff --git a/capabilities/text_to_sql/evaluation/tests/test_budget_allocation.py b/capabilities/text_to_sql/evaluation/tests/test_budget_allocation.py index 839c27f3..8d9df843 100644 --- a/capabilities/text_to_sql/evaluation/tests/test_budget_allocation.py +++ b/capabilities/text_to_sql/evaluation/tests/test_budget_allocation.py @@ -1,4 +1,4 @@ -from utils import extract_sql, execute_sql +from utils import execute_sql, extract_sql def get_assert(output, context): diff --git a/capabilities/text_to_sql/evaluation/tests/test_employee_count.py b/capabilities/text_to_sql/evaluation/tests/test_employee_count.py index 8d17b71e..236f6b71 100644 --- a/capabilities/text_to_sql/evaluation/tests/test_employee_count.py +++ b/capabilities/text_to_sql/evaluation/tests/test_employee_count.py @@ -1,4 +1,4 @@ -from utils import extract_sql, execute_sql +from utils import execute_sql, extract_sql def get_assert(output, context): diff --git a/capabilities/text_to_sql/evaluation/tests/test_employee_details.py b/capabilities/text_to_sql/evaluation/tests/test_employee_details.py index efe26369..605faa48 100644 --- a/capabilities/text_to_sql/evaluation/tests/test_employee_details.py +++ b/capabilities/text_to_sql/evaluation/tests/test_employee_details.py @@ -1,4 +1,4 @@ -from utils import extract_sql, execute_sql +from utils import execute_sql, extract_sql def get_assert(output, context): diff --git a/capabilities/text_to_sql/evaluation/tests/test_hierarchical_query.py b/capabilities/text_to_sql/evaluation/tests/test_hierarchical_query.py index 6b2a199b..d2c9069d 100644 --- a/capabilities/text_to_sql/evaluation/tests/test_hierarchical_query.py +++ b/capabilities/text_to_sql/evaluation/tests/test_hierarchical_query.py @@ -1,4 +1,4 @@ -from utils import extract_sql, execute_sql +from utils import execute_sql, extract_sql def get_assert(output, context): diff --git a/capabilities/text_to_sql/evaluation/vectordb.py b/capabilities/text_to_sql/evaluation/vectordb.py index 25bfdeee..084cc373 100644 --- a/capabilities/text_to_sql/evaluation/vectordb.py +++ b/capabilities/text_to_sql/evaluation/vectordb.py @@ -1,8 +1,9 @@ +import json import os +import pickle + import numpy as np import voyageai -import pickle -import json class VectorDB: diff --git a/capabilities/text_to_sql/guide.ipynb b/capabilities/text_to_sql/guide.ipynb index ad486861..92ec3f8e 100644 --- a/capabilities/text_to_sql/guide.ipynb +++ b/capabilities/text_to_sql/guide.ipynb @@ -95,9 +95,10 @@ "outputs": [], "source": [ "import os\n", - "from anthropic import Anthropic\n", "import sqlite3\n", + "\n", "import pandas as pd\n", + "from anthropic import Anthropic\n", "from IPython.display import display\n", "\n", "# Set your Claude API key\n", @@ -1599,13 +1600,14 @@ } ], "source": [ + "import json\n", "import os\n", - "import numpy as np\n", - "import voyageai\n", "import pickle\n", - "import json\n", "import sqlite3\n", "\n", + "import numpy as np\n", + "import voyageai\n", + "\n", "\n", "class VectorDB:\n", " def __init__(self, db_path=\"./data/vector_db.pkl\"):\n", diff --git a/claude_agent_sdk/00_The_one_liner_research_agent.ipynb b/claude_agent_sdk/00_The_one_liner_research_agent.ipynb index cb47eadd..556e707a 100644 --- a/claude_agent_sdk/00_The_one_liner_research_agent.ipynb +++ b/claude_agent_sdk/00_The_one_liner_research_agent.ipynb @@ -65,9 +65,8 @@ "metadata": {}, "outputs": [], "source": [ - "\n", "%%capture\n", - "%pip install -U claude-agent-sdk python-dotenv\n" + "%pip install -U claude-agent-sdk python-dotenv" ] }, { @@ -130,16 +129,17 @@ } ], "source": [ - "from claude_agent_sdk import ClaudeAgentOptions, query\n", "from utils.agent_visualizer import print_activity, print_final_result\n", "\n", + "from claude_agent_sdk import ClaudeAgentOptions, query\n", + "\n", "messages = []\n", "async for msg in query(\n", " prompt=\"Research the latest trends in AI agents and give me a brief summary\",\n", " options=ClaudeAgentOptions(model=\"claude-sonnet-4-5\", allowed_tools=[\"WebSearch\"]),\n", "):\n", " print_activity(msg)\n", - " messages.append(msg)\n" + " messages.append(msg)" ] }, { diff --git a/claude_agent_sdk/02_The_observability_agent.ipynb b/claude_agent_sdk/02_The_observability_agent.ipynb index a3eb18f3..2fb722e8 100644 --- a/claude_agent_sdk/02_The_observability_agent.ipynb +++ b/claude_agent_sdk/02_The_observability_agent.ipynb @@ -281,7 +281,7 @@ "load_dotenv(override=True)\n", "\n", "prompt = \"\"\"Monitor the GitHub Actions workflows for facebook/react.\n", - "Look at the last triggered CI pipeline. \n", + "Look at the last triggered CI pipeline.\n", "1. Analyze the trigger for the pipeline\n", "2. Identify whether the pipeline passed or not\n", "3. If it failed, explain which test failed\n", diff --git a/claude_agent_sdk/utils/agent_visualizer.py b/claude_agent_sdk/utils/agent_visualizer.py index c92aabe8..c6552b45 100644 --- a/claude_agent_sdk/utils/agent_visualizer.py +++ b/claude_agent_sdk/utils/agent_visualizer.py @@ -37,7 +37,7 @@ def visualize_conversation(messages): print("🤖 AGENT CONVERSATION TIMELINE") print("=" * 60 + "\n") - for i, msg in enumerate(messages): + for _i, msg in enumerate(messages): msg_type = msg.__class__.__name__ if msg_type == "SystemMessage": diff --git a/coding/prompting_for_frontend_aesthetics.ipynb b/coding/prompting_for_frontend_aesthetics.ipynb index 650a97cd..a466bc39 100644 --- a/coding/prompting_for_frontend_aesthetics.ipynb +++ b/coding/prompting_for_frontend_aesthetics.ipynb @@ -47,7 +47,7 @@ "\n", "Color & Theme: Commit to a cohesive aesthetic. Use CSS variables for consistency. Dominant colors with sharp accents outperform timid, evenly-distributed palettes. Draw from IDE themes and cultural aesthetics for inspiration.\n", "\n", - "Motion: Use animations for effects and micro-interactions. Prioritize CSS-only solutions for HTML. Use Motion library for React when available. Focus on high-impact moments: one well-orchestrated page load with staggered reveals (animation-delay) creates more delight than scattered micro-interactions. \n", + "Motion: Use animations for effects and micro-interactions. Prioritize CSS-only solutions for HTML. Use Motion library for React when available. Focus on high-impact moments: one well-orchestrated page load with staggered reveals (animation-delay) creates more delight than scattered micro-interactions.\n", "\n", "Backgrounds: Create atmosphere and depth rather than defaulting to solid colors. Layer CSS gradients, use geometric patterns, or add contextual effects that match the overall aesthetic.\n", "\n", @@ -154,15 +154,17 @@ "metadata": {}, "outputs": [], "source": [ + "import html\n", "import os\n", "import re\n", - "import webbrowser\n", "import time\n", - "import html\n", - "from pathlib import Path\n", + "import webbrowser\n", "from datetime import datetime\n", + "from pathlib import Path\n", + "\n", "from anthropic import Anthropic\n", - "from IPython.display import display, HTML as DisplayHTML\n", + "from IPython.display import HTML as DisplayHTML\n", + "from IPython.display import display\n", "\n", "client = Anthropic(api_key=os.environ.get(\"ANTHROPIC_API_KEY\"))\n", "\n", @@ -1042,7 +1044,7 @@ "\n", "\n", "\n", - "Generate complete, self-contained HTML code for the requested frontend application. Include all CSS and JavaScript inline. \n", + "Generate complete, self-contained HTML code for the requested frontend application. Include all CSS and JavaScript inline.\n", "\n", "CRITICAL: You must wrap your HTML code in triple backticks with html language identifier like this:\n", "```html\n", diff --git a/finetuning/finetuning_on_bedrock.ipynb b/finetuning/finetuning_on_bedrock.ipynb index e8b8a2fa..4a0a646d 100644 --- a/finetuning/finetuning_on_bedrock.ipynb +++ b/finetuning/finetuning_on_bedrock.ipynb @@ -75,7 +75,7 @@ "\n", "sample_dataset = []\n", "dataset_path = \"datasets/json_mode_dataset.jsonl\"\n", - "with open(dataset_path, \"r\") as f:\n", + "with open(dataset_path) as f:\n", " for line in f:\n", " sample_dataset.append(json.loads(line))\n", "\n", diff --git a/misc/batch_processing.ipynb b/misc/batch_processing.ipynb index 0815e9b9..6394b588 100644 --- a/misc/batch_processing.ipynb +++ b/misc/batch_processing.ipynb @@ -44,9 +44,10 @@ "metadata": {}, "outputs": [], "source": [ - "import anthropic\n", "import time\n", "\n", + "import anthropic\n", + "\n", "client = anthropic.Anthropic()\n", "MODEL_NAME = \"claude-sonnet-4-5\"" ] diff --git a/misc/building_evals.ipynb b/misc/building_evals.ipynb index da260d2d..3a729b5c 100644 --- a/misc/building_evals.ipynb +++ b/misc/building_evals.ipynb @@ -71,10 +71,10 @@ "# Define our input prompt template for the task.\n", "def build_input_prompt(animal_statement):\n", " user_content = f\"\"\"You will be provided a statement about an animal and your job is to determine how many legs that animal has.\n", - " \n", + "\n", " Here is the animal statment.\n", " {animal_statement}\n", - " \n", + "\n", " How many legs does the animal have? Return just the number of legs as an integer and nothing else.\"\"\"\n", "\n", " messages = [{\"role\": \"user\", \"content\": user_content}]\n", @@ -134,7 +134,7 @@ "outputs = [get_completion(build_input_prompt(question[\"animal_statement\"])) for question in eval]\n", "\n", "# Let's take a quick look at our outputs\n", - "for output, question in zip(outputs, eval):\n", + "for output, question in zip(outputs, eval, strict=False):\n", " print(\n", " f\"Animal Statement: {question['animal_statement']}\\nGolden Answer: {question['golden_answer']}\\nOutput: {output}\\n\"\n", " )" @@ -162,7 +162,8 @@ "\n", "# Run the grader function on our outputs and print the score.\n", "grades = [\n", - " grade_completion(output, question[\"golden_answer\"]) for output, question in zip(outputs, eval)\n", + " grade_completion(output, question[\"golden_answer\"])\n", + " for output, question in zip(outputs, eval, strict=False)\n", "]\n", "print(f\"Score: {sum(grades) / len(grades) * 100}%\")" ] @@ -287,7 +288,7 @@ "outputs = [get_completion(build_input_prompt(question[\"question\"])) for question in eval]\n", "\n", "# Let's take a quick look at our outputs\n", - "for output, question in zip(outputs, eval):\n", + "for output, question in zip(outputs, eval, strict=False):\n", " print(\n", " f\"Question: {question['question']}\\nGolden Answer: {question['golden_answer']}\\nOutput: {output}\\n\"\n", " )" @@ -325,13 +326,13 @@ "# We start by defining a \"grader prompt\" template.\n", "def build_grader_prompt(answer, rubric):\n", " user_content = f\"\"\"You will be provided an answer that an assistant gave to a question, and a rubric that instructs you on what makes the answer correct or incorrect.\n", - " \n", + "\n", " Here is the answer that the assistant gave to the question.\n", " {answer}\n", - " \n", + "\n", " Here is the rubric on what makes the answer correct or incorrect.\n", " {rubric}\n", - " \n", + "\n", " An answer is correct if it entirely meets the rubric criteria, and is otherwise incorrect. =\n", " First, think through whether the answer is correct or incorrect based on the rubric inside tags. Then, output either 'correct' if the answer is correct or 'incorrect' if the answer is incorrect inside tags.\"\"\"\n", "\n", @@ -357,7 +358,8 @@ "\n", "# Run the grader function on our outputs and print the score.\n", "grades = [\n", - " grade_completion(output, question[\"golden_answer\"]) for output, question in zip(outputs, eval)\n", + " grade_completion(output, question[\"golden_answer\"])\n", + " for output, question in zip(outputs, eval, strict=False)\n", "]\n", "print(f\"Score: {grades.count('correct') / len(grades) * 100}%\")" ] diff --git a/misc/generate_test_cases.ipynb b/misc/generate_test_cases.ipynb index 3bcd0e97..39ca9484 100644 --- a/misc/generate_test_cases.ipynb +++ b/misc/generate_test_cases.ipynb @@ -49,6 +49,7 @@ "outputs": [], "source": [ "import re\n", + "\n", "import anthropic\n", "\n", "# Enter your API key here\n", @@ -344,7 +345,7 @@ "# Replace this with your prompt template!\n", "# Use double-brackets to indicate variables\n", "# Here's an example:\n", - "prompt_template = \"\"\"You are a customer support bot for Acme Corporation. \n", + "prompt_template = \"\"\"You are a customer support bot for Acme Corporation.\n", "Here is an FAQ with Acme's relevant policies:\n", "\n", "\n", diff --git a/misc/how_to_enable_json_mode.ipynb b/misc/how_to_enable_json_mode.ipynb index 95eed632..593974de 100644 --- a/misc/how_to_enable_json_mode.ipynb +++ b/misc/how_to_enable_json_mode.ipynb @@ -41,10 +41,11 @@ "metadata": {}, "outputs": [], "source": [ - "from anthropic import Anthropic\n", "import json\n", "import re\n", - "from pprint import pprint" + "from pprint import pprint\n", + "\n", + "from anthropic import Anthropic" ] }, { @@ -388,7 +389,7 @@ " {\n", " \"role\": \"user\",\n", " \"content\": \"\"\"Give me a JSON dict with the names of 5 famous athletes & their sports.\n", - "Put this dictionary in tags. \n", + "Put this dictionary in tags.\n", "\n", "Then, for each athlete, output an additional JSON dictionary. In each of these additional dictionaries:\n", "- Include two keys: the athlete's first name and last name.\n", diff --git a/misc/how_to_make_sql_queries.ipynb b/misc/how_to_make_sql_queries.ipynb index c80479e2..a9dfd5d2 100644 --- a/misc/how_to_make_sql_queries.ipynb +++ b/misc/how_to_make_sql_queries.ipynb @@ -36,9 +36,10 @@ "outputs": [], "source": [ "# Import the required libraries\n", - "from anthropic import Anthropic\n", "import sqlite3\n", "\n", + "from anthropic import Anthropic\n", + "\n", "# Set up the Claude API client\n", "client = Anthropic()\n", "MODEL_NAME = \"claude-opus-4-1\"" diff --git a/misc/metaprompt.ipynb b/misc/metaprompt.ipynb index 06386b5a..7871d998 100644 --- a/misc/metaprompt.ipynb +++ b/misc/metaprompt.ipynb @@ -47,9 +47,10 @@ }, "outputs": [], "source": [ - "import anthropic\n", "import re\n", "\n", + "import anthropic\n", + "\n", "ANTHROPIC_API_KEY = \"\" # Put your API key here!\n", "MODEL_NAME = \"claude-sonnet-4-5\"\n", "CLIENT = anthropic.Anthropic(api_key=ANTHROPIC_API_KEY)" diff --git a/misc/prompt_caching.ipynb b/misc/prompt_caching.ipynb index b2309949..44d87f4c 100644 --- a/misc/prompt_caching.ipynb +++ b/misc/prompt_caching.ipynb @@ -49,8 +49,9 @@ "metadata": {}, "outputs": [], "source": [ - "import anthropic\n", "import time\n", + "\n", + "import anthropic\n", "import requests\n", "from bs4 import BeautifulSoup\n", "\n", @@ -67,30 +68,12 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Fetched 737525 characters from the book.\n", - "First 500 characters:\n", - "The Project Gutenberg eBook of Pride and Prejudice\n", - "This ebook is for the use of anyone anywhere in the United States and\n", - "most other parts of the world at no cost and with almost no restrictions\n", - "whatsoever. You may copy it, give it away or re-use it under the terms\n", - "of the Project Gutenberg License included with this ebook or online\n", - "at www.gutenberg.org. If you are not located in the United States,\n", - "you will have to check the laws of the country where you are located\n", - "before using this eBook.\n", - "Title:\n" - ] - } - ], + "outputs": [], "source": [ "def fetch_article_content(url):\n", - " response = requests.get(url)\n", + " response = requests.get(url, timeout=30)\n", " soup = BeautifulSoup(response.content, \"html.parser\")\n", "\n", " # Remove script and style elements\n", @@ -500,7 +483,7 @@ ], "metadata": { "kernelspec": { - "display_name": "base", + "display_name": "anthropic-cookbook (3.12.12)", "language": "python", "name": "python3" }, @@ -514,7 +497,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.14" + "version": "3.12.12" } }, "nbformat": 4, diff --git a/misc/read_web_pages_with_haiku.ipynb b/misc/read_web_pages_with_haiku.ipynb index a7636fdc..e1c2283a 100644 --- a/misc/read_web_pages_with_haiku.ipynb +++ b/misc/read_web_pages_with_haiku.ipynb @@ -52,21 +52,10 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": null, "metadata": {}, "outputs": [], - "source": [ - "import requests\n", - "\n", - "url = \"https://en.wikipedia.org/wiki/96th_Academy_Awards\"\n", - "response = requests.get(url)\n", - "\n", - "if response.status_code == 200:\n", - " page_content = response.text\n", - "else:\n", - " print(f\"Failed to fetch the web page. Status code: {response.status_code}\")\n", - " exit(1)" - ] + "source": "import requests\n\nurl = \"https://en.wikipedia.org/wiki/96th_Academy_Awards\"\nresponse = requests.get(url, timeout=30)\n\nif response.status_code == 200:\n page_content = response.text\nelse:\n print(f\"Failed to fetch the web page. Status code: {response.status_code}\")\n exit(1)" }, { "cell_type": "markdown", @@ -143,4 +132,4 @@ }, "nbformat": 4, "nbformat_minor": 2 -} +} \ No newline at end of file diff --git a/misc/speculative_prompt_caching.ipynb b/misc/speculative_prompt_caching.ipynb index 8205d451..06abde37 100644 --- a/misc/speculative_prompt_caching.ipynb +++ b/misc/speculative_prompt_caching.ipynb @@ -56,10 +56,11 @@ "metadata": {}, "outputs": [], "source": [ + "import asyncio\n", "import copy\n", "import datetime\n", "import time\n", - "import asyncio\n", + "\n", "import httpx\n", "from anthropic import AsyncAnthropic\n", "\n", diff --git a/misc/using_citations.ipynb b/misc/using_citations.ipynb index 184f0480..0c2ca2a8 100644 --- a/misc/using_citations.ipynb +++ b/misc/using_citations.ipynb @@ -44,9 +44,10 @@ "metadata": {}, "outputs": [], "source": [ - "import anthropic\n", - "import os\n", "import json\n", + "import os\n", + "\n", + "import anthropic\n", "\n", "ANTHROPIC_API_KEY = os.environ.get(\"ANTHROPIC_API_KEY\")\n", "# ANTHROPIC_API_KEY = \"\" # Put your API key here!\n", @@ -133,7 +134,7 @@ "\n", "for filename in sorted(os.listdir(articles_dir)):\n", " if filename.endswith(\".txt\"):\n", - " with open(os.path.join(articles_dir, filename), \"r\") as f:\n", + " with open(os.path.join(articles_dir, filename)) as f:\n", " content = f.read()\n", " # Split into title and body\n", " title_line, body = content.split(\"\\n\", 1)\n", @@ -554,7 +555,7 @@ "\n", "for filename in sorted(os.listdir(articles_dir)):\n", " if filename.endswith(\".txt\"):\n", - " with open(os.path.join(articles_dir, filename), \"r\") as f:\n", + " with open(os.path.join(articles_dir, filename)) as f:\n", " content = f.read()\n", " # Split into title and body\n", " title_line, body = content.split(\"\\n\", 1)\n", diff --git a/multimodal/best_practices_for_vision.ipynb b/multimodal/best_practices_for_vision.ipynb index 20339eea..dde4ef53 100644 --- a/multimodal/best_practices_for_vision.ipynb +++ b/multimodal/best_practices_for_vision.ipynb @@ -25,6 +25,7 @@ "outputs": [], "source": [ "import base64\n", + "\n", "from anthropic import Anthropic\n", "from IPython.display import Image\n", "\n", diff --git a/multimodal/crop_tool.ipynb b/multimodal/crop_tool.ipynb index 2685015a..bc318e7e 100644 --- a/multimodal/crop_tool.ipynb +++ b/multimodal/crop_tool.ipynb @@ -104,19 +104,21 @@ "# Load a small subset of FigureQA\n", "dataset = load_dataset(\"vikhyatk/figureqa\", split=\"train[:10]\")\n", "\n", + "\n", "# Helper to convert dataset images to PIL\n", "def get_pil_image(img) -> PILImage.Image:\n", " if isinstance(img, PILImage.Image):\n", " return img\n", - " if isinstance(img, dict) and 'bytes' in img:\n", - " return PILImage.open(BytesIO(img['bytes']))\n", + " if isinstance(img, dict) and \"bytes\" in img:\n", + " return PILImage.open(BytesIO(img[\"bytes\"]))\n", " raise ValueError(f\"Cannot convert {type(img)}\")\n", "\n", + "\n", "# Get an example chart\n", "example = dataset[3]\n", - "chart_image = get_pil_image(example['image'])\n", - "question = example['qa'][0]['question']\n", - "answer = example['qa'][0]['answer']\n", + "chart_image = get_pil_image(example[\"image\"])\n", + "question = example[\"qa\"][0][\"question\"]\n", + "answer = example[\"qa\"][0][\"answer\"]\n", "\n", "print(f\"Question: {question}\")\n", "print(f\"Answer: {answer}\")\n", @@ -143,8 +145,8 @@ "source": [ "def pil_to_base64(image: PILImage.Image) -> str:\n", " \"\"\"Convert PIL Image to base64 string.\"\"\"\n", - " if image.mode in ('RGBA', 'P'):\n", - " image = image.convert('RGB')\n", + " if image.mode in (\"RGBA\", \"P\"):\n", + " image = image.convert(\"RGB\")\n", " buffer = BytesIO()\n", " image.save(buffer, format=\"PNG\")\n", " return base64.standard_b64encode(buffer.getvalue()).decode(\"utf-8\")\n", @@ -161,29 +163,29 @@ " \"type\": \"number\",\n", " \"minimum\": 0,\n", " \"maximum\": 1,\n", - " \"description\": \"Left edge of bounding box as normalized 0-1 value, where 0.5 is the horizontal center of the image\"\n", + " \"description\": \"Left edge of bounding box as normalized 0-1 value, where 0.5 is the horizontal center of the image\",\n", " },\n", " \"y1\": {\n", " \"type\": \"number\",\n", " \"minimum\": 0,\n", " \"maximum\": 1,\n", - " \"description\": \"Top edge of bounding box as normalized 0-1 value, where 0.5 is the vertical center of the image\"\n", + " \"description\": \"Top edge of bounding box as normalized 0-1 value, where 0.5 is the vertical center of the image\",\n", " },\n", " \"x2\": {\n", " \"type\": \"number\",\n", " \"minimum\": 0,\n", " \"maximum\": 1,\n", - " \"description\": \"Right edge of bounding box as normalized 0-1 value, where 0.5 is the horizontal center of the image\"\n", + " \"description\": \"Right edge of bounding box as normalized 0-1 value, where 0.5 is the horizontal center of the image\",\n", " },\n", " \"y2\": {\n", " \"type\": \"number\",\n", " \"minimum\": 0,\n", " \"maximum\": 1,\n", - " \"description\": \"Bottom edge of bounding box as normalized 0-1 value, where 0.5 is the vertical center of the image\"\n", + " \"description\": \"Bottom edge of bounding box as normalized 0-1 value, where 0.5 is the vertical center of the image\",\n", " },\n", " },\n", - " \"required\": [\"x1\", \"y1\", \"x2\", \"y2\"]\n", - " }\n", + " \"required\": [\"x1\", \"y1\", \"x2\", \"y2\"],\n", + " },\n", "}\n", "\n", "\n", @@ -194,14 +196,20 @@ " return [{\"type\": \"text\", \"text\": \"Error: Coordinates must be between 0 and 1\"}]\n", " if x1 >= x2 or y1 >= y2:\n", " return [{\"type\": \"text\", \"text\": \"Error: Invalid bounding box (need x1 < x2 and y1 < y2)\"}]\n", - " \n", + "\n", " # Crop\n", " w, h = image.size\n", - " cropped = image.crop((int(x1*w), int(y1*h), int(x2*w), int(y2*h)))\n", - " \n", + " cropped = image.crop((int(x1 * w), int(y1 * h), int(x2 * w), int(y2 * h)))\n", + "\n", " return [\n", - " {\"type\": \"text\", \"text\": f\"Cropped to ({x1:.2f},{y1:.2f})-({x2:.2f},{y2:.2f}): {cropped.width}x{cropped.height}px\"},\n", - " {\"type\": \"image\", \"source\": {\"type\": \"base64\", \"media_type\": \"image/png\", \"data\": pil_to_base64(cropped)}}\n", + " {\n", + " \"type\": \"text\",\n", + " \"text\": f\"Cropped to ({x1:.2f},{y1:.2f})-({x2:.2f},{y2:.2f}): {cropped.width}x{cropped.height}px\",\n", + " },\n", + " {\n", + " \"type\": \"image\",\n", + " \"source\": {\"type\": \"base64\", \"media_type\": \"image/png\", \"data\": pil_to_base64(cropped)},\n", + " },\n", " ]" ] }, @@ -237,9 +245,9 @@ ], "source": [ "# Crop the legend area (top-right of most charts)\n", - "result = handle_crop(chart_image, x1=0.0, y1=0.0, x2=.4, y2=0.35)\n", - "print(result[0]['text'])\n", - "display(Image(data=base64.b64decode(result[1]['source']['data'])))" + "result = handle_crop(chart_image, x1=0.0, y1=0.0, x2=0.4, y2=0.35)\n", + "print(result[0][\"text\"])\n", + "display(Image(data=base64.b64decode(result[1][\"source\"][\"data\"])))" ] }, { @@ -259,38 +267,50 @@ "source": [ "def ask_with_crop_tool(image: PILImage.Image, question: str) -> str:\n", " \"\"\"Ask Claude a question about an image, with the crop tool available.\"\"\"\n", - " \n", - " messages = [{\n", - " \"role\": \"user\",\n", - " \"content\": [\n", - " {\"type\": \"text\", \"text\": f\"Answer the following question about this image.\\n\\nThe question is: {question}\\n\\n\"},\n", - " {\"type\": \"image\", \"source\": {\"type\": \"base64\", \"media_type\": \"image/png\", \"data\": pil_to_base64(image)}},\n", - " {\"type\": \"text\", \"text\": f\"\\n\\nUse your crop_image tool to examine specific regions including legends and axes.\"},\n", - " ]\n", - " }]\n", - " \n", + "\n", + " messages = [\n", + " {\n", + " \"role\": \"user\",\n", + " \"content\": [\n", + " {\n", + " \"type\": \"text\",\n", + " \"text\": f\"Answer the following question about this image.\\n\\nThe question is: {question}\\n\\n\",\n", + " },\n", + " {\n", + " \"type\": \"image\",\n", + " \"source\": {\n", + " \"type\": \"base64\",\n", + " \"media_type\": \"image/png\",\n", + " \"data\": pil_to_base64(image),\n", + " },\n", + " },\n", + " {\n", + " \"type\": \"text\",\n", + " \"text\": \"\\n\\nUse your crop_image tool to examine specific regions including legends and axes.\",\n", + " },\n", + " ],\n", + " }\n", + " ]\n", + "\n", " while True:\n", " response = client.messages.create(\n", - " model=MODEL,\n", - " max_tokens=1024,\n", - " tools=[CROP_TOOL],\n", - " messages=messages\n", + " model=MODEL, max_tokens=1024, tools=[CROP_TOOL], messages=messages\n", " )\n", - " \n", + "\n", " # Print assistant's response\n", " for block in response.content:\n", - " if hasattr(block, 'text'):\n", + " if hasattr(block, \"text\"):\n", " print(f\"[Assistant] {block.text}\")\n", " elif block.type == \"tool_use\":\n", " print(f\"[Tool] crop_image({block.input})\")\n", - " \n", + "\n", " # If Claude is done, return\n", " if response.stop_reason != \"tool_use\":\n", " return\n", - " \n", + "\n", " # Execute tool calls and continue\n", " messages.append({\"role\": \"assistant\", \"content\": response.content})\n", - " \n", + "\n", " tool_results = []\n", " for block in response.content:\n", " if block.type == \"tool_use\":\n", @@ -299,12 +319,10 @@ " for item in result:\n", " if item.get(\"type\") == \"image\":\n", " display(Image(data=base64.b64decode(item[\"source\"][\"data\"])))\n", - " tool_results.append({\n", - " \"type\": \"tool_result\",\n", - " \"tool_use_id\": block.id,\n", - " \"content\": result\n", - " })\n", - " \n", + " tool_results.append(\n", + " {\"type\": \"tool_result\", \"tool_use_id\": block.id, \"content\": result}\n", + " )\n", + "\n", " messages.append({\"role\": \"user\", \"content\": tool_results})" ] }, @@ -484,9 +502,9 @@ "source": [ "# Try a different chart and question\n", "example2 = dataset[6]\n", - "chart2 = get_pil_image(example2['image'])\n", - "q2 = example2['qa'][2]['question']\n", - "a2 = example2['qa'][2]['answer']\n", + "chart2 = get_pil_image(example2[\"image\"])\n", + "q2 = example2[\"qa\"][2][\"question\"]\n", + "a2 = example2[\"qa\"][2][\"answer\"]\n", "\n", "print(f\"Question: {q2}\\n\")\n", "display(chart2)\n", @@ -560,47 +578,51 @@ " \"properties\": {\n", " \"image_path\": {\n", " \"type\": \"string\",\n", - " \"description\": \"Relative path to the image file (e.g., 'chart.png')\"\n", + " \"description\": \"Relative path to the image file (e.g., 'chart.png')\",\n", " },\n", " \"x1\": {\n", " \"type\": \"number\",\n", " \"minimum\": 0,\n", " \"maximum\": 1,\n", - " \"description\": \"Left edge of bounding box as normalized 0-1 value, where 0.5 is the horizontal center of the image\"\n", + " \"description\": \"Left edge of bounding box as normalized 0-1 value, where 0.5 is the horizontal center of the image\",\n", " },\n", " \"y1\": {\n", " \"type\": \"number\",\n", " \"minimum\": 0,\n", " \"maximum\": 1,\n", - " \"description\": \"Top edge of bounding box as normalized 0-1 value, where 0.5 is the vertical center of the image\"\n", + " \"description\": \"Top edge of bounding box as normalized 0-1 value, where 0.5 is the vertical center of the image\",\n", " },\n", " \"x2\": {\n", " \"type\": \"number\",\n", " \"minimum\": 0,\n", " \"maximum\": 1,\n", - " \"description\": \"Right edge of bounding box as normalized 0-1 value, where 0.5 is the horizontal center of the image\"\n", + " \"description\": \"Right edge of bounding box as normalized 0-1 value, where 0.5 is the horizontal center of the image\",\n", " },\n", " \"y2\": {\n", " \"type\": \"number\",\n", " \"minimum\": 0,\n", " \"maximum\": 1,\n", - " \"description\": \"Bottom edge of bounding box as normalized 0-1 value, where 0.5 is the vertical center of the image\"\n", + " \"description\": \"Bottom edge of bounding box as normalized 0-1 value, where 0.5 is the vertical center of the image\",\n", " },\n", " },\n", - " \"required\": [\"image_path\", \"x1\", \"y1\", \"x2\", \"y2\"]\n", + " \"required\": [\"image_path\", \"x1\", \"y1\", \"x2\", \"y2\"],\n", " },\n", ")\n", "async def crop_image_tool(args: dict):\n", " \"\"\"Crop tool that loads images from a filepath.\"\"\"\n", " global tool_working_dir\n", - " \n", + "\n", " image_path = args[\"image_path\"]\n", " x1, y1, x2, y2 = args[\"x1\"], args[\"y1\"], args[\"x2\"], args[\"y2\"]\n", "\n", " if not all(0 <= c <= 1 for c in [x1, y1, x2, y2]):\n", " return {\"content\": [{\"type\": \"text\", \"text\": \"Error: Coordinates must be between 0 and 1\"}]}\n", " if x1 >= x2 or y1 >= y2:\n", - " return {\"content\": [{\"type\": \"text\", \"text\": \"Error: Invalid bounding box (need x1 < x2 and y1 < y2)\"}]}\n", + " return {\n", + " \"content\": [\n", + " {\"type\": \"text\", \"text\": \"Error: Invalid bounding box (need x1 < x2 and y1 < y2)\"}\n", + " ]\n", + " }\n", "\n", " # Resolve relative paths against working directory\n", " path = Path(image_path)\n", @@ -616,12 +638,15 @@ " return {\"content\": [{\"type\": \"text\", \"text\": f\"Error loading image: {e}\"}]}\n", "\n", " w, h = image.size\n", - " cropped = image.crop((int(x1*w), int(y1*h), int(x2*w), int(y2*h)))\n", + " cropped = image.crop((int(x1 * w), int(y1 * h), int(x2 * w), int(y2 * h)))\n", "\n", " # Return using MCP image format (data + mimeType, not Anthropic API source format)\n", " return {\n", " \"content\": [\n", - " {\"type\": \"text\", \"text\": f\"Cropped {image_path} to ({x1:.2f},{y1:.2f})-({x2:.2f},{y2:.2f}): {cropped.width}x{cropped.height}px\"},\n", + " {\n", + " \"type\": \"text\",\n", + " \"text\": f\"Cropped {image_path} to ({x1:.2f},{y1:.2f})-({x2:.2f},{y2:.2f}): {cropped.width}x{cropped.height}px\",\n", + " },\n", " {\"type\": \"image\", \"data\": pil_to_base64(cropped), \"mimeType\": \"image/png\"},\n", " ]\n", " }\n", @@ -700,6 +725,7 @@ "source": [ "import tempfile\n", "\n", + "\n", "async def ask_with_agent_sdk(image: PILImage.Image, question: str):\n", " \"\"\"Ask a question using the Claude Agent SDK with file-based image access.\"\"\"\n", " global tool_working_dir\n", @@ -736,7 +762,9 @@ " elif hasattr(block, \"content\") and isinstance(block.content, list):\n", " for item in block.content:\n", " if isinstance(item, dict) and item.get(\"type\") == \"image\":\n", - " img_data = item.get(\"data\") or item.get(\"source\", {}).get(\"data\")\n", + " img_data = item.get(\"data\") or item.get(\"source\", {}).get(\n", + " \"data\"\n", + " )\n", " if img_data:\n", " display(Image(data=base64.b64decode(img_data)))\n", "\n", diff --git a/multimodal/getting_started_with_vision.ipynb b/multimodal/getting_started_with_vision.ipynb index e6587b12..969ae74e 100644 --- a/multimodal/getting_started_with_vision.ipynb +++ b/multimodal/getting_started_with_vision.ipynb @@ -72,6 +72,7 @@ ], "source": [ "import base64\n", + "\n", "from anthropic import Anthropic\n", "\n", "client = Anthropic()\n", diff --git a/multimodal/how_to_transcribe_text.ipynb b/multimodal/how_to_transcribe_text.ipynb index dd835c95..eee112ed 100644 --- a/multimodal/how_to_transcribe_text.ipynb +++ b/multimodal/how_to_transcribe_text.ipynb @@ -25,6 +25,7 @@ "outputs": [], "source": [ "import base64\n", + "\n", "from anthropic import Anthropic\n", "\n", "client = Anthropic()\n", diff --git a/multimodal/reading_charts_graphs_powerpoints.ipynb b/multimodal/reading_charts_graphs_powerpoints.ipynb index ecbd6f28..fb2f573e 100644 --- a/multimodal/reading_charts_graphs_powerpoints.ipynb +++ b/multimodal/reading_charts_graphs_powerpoints.ipynb @@ -57,6 +57,7 @@ "outputs": [], "source": [ "import base64\n", + "\n", "from anthropic import Anthropic\n", "\n", "# While PDF support is in beta, you must pass in the correct beta header\n", diff --git a/multimodal/using_sub_agents.ipynb b/multimodal/using_sub_agents.ipynb index 51fdb935..08e0178f 100644 --- a/multimodal/using_sub_agents.ipynb +++ b/multimodal/using_sub_agents.ipynb @@ -33,14 +33,15 @@ "outputs": [], "source": [ "# Import the required libraries\n", - "import fitz\n", "import base64\n", - "from PIL import Image\n", "import io\n", + "import os\n", "from concurrent.futures import ThreadPoolExecutor\n", - "from anthropic import Anthropic\n", + "\n", + "import fitz\n", "import requests\n", - "import os\n", + "from anthropic import Anthropic\n", + "from PIL import Image\n", "\n", "# Set up the Claude API client\n", "client = Anthropic()\n", @@ -85,72 +86,10 @@ }, { "cell_type": "code", - "execution_count": 111, + "execution_count": null, "metadata": {}, "outputs": [], - "source": [ - "# Function to download a PDF file from a URL and save it to a specified folder\n", - "def download_pdf(url, folder):\n", - " response = requests.get(url)\n", - " if response.status_code == 200:\n", - " file_name = os.path.join(folder, url.split(\"/\")[-1])\n", - " with open(file_name, \"wb\") as file:\n", - " file.write(response.content)\n", - " return file_name\n", - " else:\n", - " print(f\"Failed to download PDF from {url}\")\n", - " return None\n", - "\n", - "\n", - "# Define the function to convert a PDF to a list of base64-encoded PNG images\n", - "def pdf_to_base64_pngs(pdf_path, quality=75, max_size=(1024, 1024)):\n", - " # Open the PDF file\n", - " doc = fitz.open(pdf_path)\n", - "\n", - " base64_encoded_pngs = []\n", - "\n", - " # Iterate through each page of the PDF\n", - " for page_num in range(doc.page_count):\n", - " # Load the page\n", - " page = doc.load_page(page_num)\n", - "\n", - " # Render the page as a PNG image\n", - " pix = page.get_pixmap(matrix=fitz.Matrix(300 / 72, 300 / 72))\n", - "\n", - " # Convert the pixmap to a PIL Image\n", - " image = Image.frombytes(\"RGB\", [pix.width, pix.height], pix.samples)\n", - "\n", - " # Resize the image if it exceeds the maximum size\n", - " if image.size[0] > max_size[0] or image.size[1] > max_size[1]:\n", - " image.thumbnail(max_size, Image.Resampling.LANCZOS)\n", - "\n", - " # Convert the PIL Image to base64-encoded PNG\n", - " image_data = io.BytesIO()\n", - " image.save(image_data, format=\"PNG\", optimize=True, quality=quality)\n", - " image_data.seek(0)\n", - " base64_encoded = base64.b64encode(image_data.getvalue()).decode(\"utf-8\")\n", - " base64_encoded_pngs.append(base64_encoded)\n", - "\n", - " # Close the PDF document\n", - " doc.close()\n", - "\n", - " return base64_encoded_pngs\n", - "\n", - "\n", - "# Folder to save the downloaded PDFs\n", - "folder = \"../images/using_sub_agents\"\n", - "\n", - "\n", - "# Create the directory if it doesn't exist\n", - "os.makedirs(folder)\n", - "\n", - "# Download the PDFs concurrently\n", - "with ThreadPoolExecutor() as executor:\n", - " pdf_paths = list(executor.map(download_pdf, pdf_urls, [folder] * len(pdf_urls)))\n", - "\n", - "# Remove any None values (failed downloads) from pdf_paths\n", - "pdf_paths = [path for path in pdf_paths if path is not None]" - ] + "source": "# Function to download a PDF file from a URL and save it to a specified folder\ndef download_pdf(url, folder):\n response = requests.get(url, timeout=60)\n if response.status_code == 200:\n file_name = os.path.join(folder, url.split(\"/\")[-1])\n with open(file_name, \"wb\") as file:\n file.write(response.content)\n return file_name\n else:\n print(f\"Failed to download PDF from {url}\")\n return None\n\n\n# Define the function to convert a PDF to a list of base64-encoded PNG images\ndef pdf_to_base64_pngs(pdf_path, quality=75, max_size=(1024, 1024)):\n # Open the PDF file\n doc = fitz.open(pdf_path)\n\n base64_encoded_pngs = []\n\n # Iterate through each page of the PDF\n for page_num in range(doc.page_count):\n # Load the page\n page = doc.load_page(page_num)\n\n # Render the page as a PNG image\n pix = page.get_pixmap(matrix=fitz.Matrix(300 / 72, 300 / 72))\n\n # Convert the pixmap to a PIL Image\n image = Image.frombytes(\"RGB\", [pix.width, pix.height], pix.samples)\n\n # Resize the image if it exceeds the maximum size\n if image.size[0] > max_size[0] or image.size[1] > max_size[1]:\n image.thumbnail(max_size, Image.Resampling.LANCZOS)\n\n # Convert the PIL Image to base64-encoded PNG\n image_data = io.BytesIO()\n image.save(image_data, format=\"PNG\", optimize=True, quality=quality)\n image_data.seek(0)\n base64_encoded = base64.b64encode(image_data.getvalue()).decode(\"utf-8\")\n base64_encoded_pngs.append(base64_encoded)\n\n # Close the PDF document\n doc.close()\n\n return base64_encoded_pngs\n\n\n# Folder to save the downloaded PDFs\nfolder = \"../images/using_sub_agents\"\n\n\n# Create the directory if it doesn't exist\nos.makedirs(folder)\n\n# Download the PDFs concurrently\nwith ThreadPoolExecutor() as executor:\n pdf_paths = list(executor.map(download_pdf, pdf_urls, [folder] * len(pdf_urls)))\n\n# Remove any None values (failed downloads) from pdf_paths\npdf_paths = [path for path in pdf_paths if path is not None]" }, { "cell_type": "markdown", @@ -416,62 +355,10 @@ }, { "cell_type": "code", - "execution_count": 112, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Based on the extracted information from Apple's earnings releases, Apple's net sales changed as follows in the 2023 financial year:\n", - "\n", - "In Q1, net sales increased from $117,154 million in the previous quarter to $123,945 million, driven by increases in both product sales and services revenue.\n", - "\n", - "In Q2, net sales decreased by around $2,442 million compared to the prior six-month period, primarily due to a decrease in product sales, which was partially offset by an increase in services sales.\n", - "\n", - "In Q3, net sales increased by approximately $1,162 million compared to the previous quarter, with growth in both product sales and services sales contributing to the overall increase.\n", - "\n", - "In Q4, total net sales decreased slightly compared to the same quarter in the prior year, driven by a decline in product sales, which was partially offset by growth in services sales.\n", - "\n", - "Here's a Python code snippet using the matplotlib library to visualize the quarterly net sales data:\n" - ] - }, - { - "data": { - "image/png": "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/4SWPhU9hArDzFWM6py58tKnTx/06tULkZGRmD9/Pl544QUYDAZs2LABERERcr+XXnoJFy9eRHR0tNx248YNhIeHw8XFBVu2bCk2zcJkMiE+Ph716tWDVquV241GI3Jzc+Hr64u2bduidevWWLhwYYnjy8zMhJubG6KjoxEeHi63CyFw5coVeHp64vz582jUqBEOHDiANm3aKPXQVGt8jasYar7GWVuvWf2NhXFxcVb1u++++zBjxgxrN0tERGSXPDw88Morr2Dbtm34+eef0b9/f+Tl5RU7kaTVai2+NCU9PR3h4eFwcnLC5s2bixXQd2IwGAAA8fHxOHToEKZNm1Zq32PHjgEAAgMDLdolSUJQUBAAYM2aNQgJCcEDDzxg9RiIyDqKfO23yWTCiRMnULNmTXh6eiqxSarmBCSk1gjgVcSkGmaOgILVOfr06YMWLVrAZDIhJiYGe/bswaRJk+Trf8aNGwdnZ2fUrFkTe/bswcqVKzF37lwABQV0jx49kJWVha+++grp6elIT08HAPj6+spnnTUaDcLCwuSCfN26dfD19UVoaChOnDiB119/HX369JEvWExISMDq1avRu3dveHt74/jx44iMjESnTp3QrFkzefyzZ89Gz549odFosH79esyYMQPffPONxdluqp74Gqe8MhXRY8aMQdOmTTFs2DCYTCZ07twZsbGx8sdWXbp0UXiYVB2ZNYq8xyOyGjNHoaGhGDt2LOLj45GZmYndu3fjxRdfxOjRowEAX3/9NSZOnIiBAwciJSUFNWvWxAcffCB/6cmRI0ewf/9+AEDdunUttn3u3DnUqlULABAWFoZBgwZh6tSpAAou/Bs7diySkpIQGBiIQYMG4X//+5/8uzqdDjt27MD8+fORmZmJkJAQPPPMM5g0aZLFffz444/44IMPkJOTg+bNm2PTpk3o1atXuTxWVPnwNU5ZVs+JLio4OBgbN25E69atsXHjRowcORIxMTH48ssvsWvXLvz666/lMdZKj3Oirce5W5UXM1cxqnPmysuQIUOwfPlyxbeblZUFb29vLF68GAMHDuRZ4kqGr3EVwx7nRJfpUUxOTkZAQAAAYOvWrXj22WfllTtOnDhRthETERFVAzExMejatSsefPDBih4KEd2DMhXR/v7+OHXqFEwmE6Kjo/Hoo48CKHh3zXfURERUFZTHWWgAiIiI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uYhv3rw5vv32W2zcuBFNmjRRbH+JiIiIqHIo05zol19+Ga+//jq++OILSJKEy5cvIy4uDm+++Sb+97//KTrAxx57DI899lipt0uShPfeew/vvfdeqX28vLywevXqO95Ps2bN7vpti88++yyeffbZOw+YiIiIiKq8MhXREyZMgNlsRrdu3ZCVlYVOnTrByckJb775JkaPHq30GKkaEpCQ4eJV6oUMREpj5khNzBupjZlTXpmKaEmS8M4772DcuHH466+/kJGRgUaNGslrHxPdM0lCto55IhUxc6Qm5o3UxswprkxFdCGdTldsCToiRQgzPDOSkOrmX7BWJFF5Y+ZITcwbqY2ZU5zVRfTTTz9t9UbXr19fpsEQFZIAaE15kFDyGqpESmPmSE3MG6mNmVOe1UW0wWAoz3EQEREREVUaVhfRy5YtK89xEBERERFVGpwUQ3ZJQILR1ZdXEZNqmDlSE/NGamPmlFfmCwu//fZbfPPNN7hw4QJyc3MtblPyq7+pmpIk5Dk6V/QoqDph5khNzBupjZlTXJnORH/yyScYOnQo/P39cfToUTz44IPw9vbG2bNn0atXL6XHSNWQJMzwNl6EJMwVPRSqJpg5UhPzRmpj5pRXpiJ60aJFWLJkCT799FPodDqMHz8e27dvx2uvvQaj0aj0GKma4hOd1MbMkZqYN1IbM6esMhXRFy5cQIcOHQAAzs7OuHHjBgDghRdewJo1a5QbHRERERGRHSpTER0QEICUlBQAQGhoKPbt2wcAOHfuHITg6oNEREREVLWVqYh+5JFHsHnzZgDA0KFDERkZiUcffRTPPfccnnrqKUUHSNWTgITUGgG8iphUw8yRmpg3Uhszp7wyrc6xZMkSmM0F82pGjhwJb29vxMbG4oknnsArr7yi6ACp+jJr7ulb6YlsxsyRmpg3Uhszp6wyPZoajQYaza2T2P3790f//v0VGxSRBAFv40VcNwTzXTOpgpkjNTFvpDZmTnk2TedITk7G33//bdF28uRJDB06FP369cPq1asVHRwRERERkT2yqYgePXo0PvnkE/nnq1ev4uGHH8bBgweRk5ODIUOG4Msvv1R8kERERERE9sSmInrfvn144okn5J9XrlwJLy8vHDt2DJs2bcL06dOxcOFCxQdJRERERGRPbCqiExMTUatWLfnnXbt24emnn4aDQ8HU6ieeeALx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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# Extract the matplotlib code from the response\n", - "# Function to extract the code and non-code parts from the response\n", - "def extract_code_and_response(response):\n", - " start_tag = \"\"\n", - " end_tag = \"\"\n", - " start_index = response.find(start_tag)\n", - " end_index = response.find(end_tag)\n", - " if start_index != -1 and end_index != -1:\n", - " code = response[start_index + len(start_tag) : end_index].strip()\n", - " non_code_response = response[:start_index].strip()\n", - " return code, non_code_response\n", - " else:\n", - " return None, response.strip()\n", - "\n", - "\n", - "matplotlib_code, non_code_response = extract_code_and_response(generated_response)\n", - "\n", - "print(non_code_response)\n", - "if matplotlib_code:\n", - " # Execute the extracted matplotlib code\n", - " exec(matplotlib_code)\n", - "else:\n", - " print(\"No matplotlib code found in the response.\")" - ] + "outputs": [], + "source": "# Extract the matplotlib code from the response\n# Function to extract the code and non-code parts from the response\ndef extract_code_and_response(response):\n start_tag = \"\"\n end_tag = \"\"\n start_index = response.find(start_tag)\n end_index = response.find(end_tag)\n if start_index != -1 and end_index != -1:\n code = response[start_index + len(start_tag) : end_index].strip()\n non_code_response = response[:start_index].strip()\n return code, non_code_response\n else:\n return None, response.strip()\n\n\nmatplotlib_code, non_code_response = extract_code_and_response(generated_response)\n\nprint(non_code_response)\nif matplotlib_code:\n # Execute the extracted matplotlib code\n # Note: exec is used here for demonstration purposes to run model-generated visualization code.\n # In production, use a sandboxed environment for executing untrusted code.\n exec(matplotlib_code) # noqa: S102\nelse:\n print(\"No matplotlib code found in the response.\")" } ], "metadata": { @@ -495,4 +382,4 @@ }, "nbformat": 4, "nbformat_minor": 2 -} +} \ No newline at end of file diff --git a/observability/usage_cost_api.ipynb b/observability/usage_cost_api.ipynb index cd41f45b..0b47cfc5 100644 --- a/observability/usage_cost_api.ipynb +++ b/observability/usage_cost_api.ipynb @@ -60,87 +60,8 @@ "id": "edd50a16", "outputId": "68f38db3-48ee-429f-cd6e-2d2ae74e009a" }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "✅ Connection successful!\n" - ] - } - ], - "source": [ - "import os\n", - "import requests\n", - "from datetime import datetime, timedelta, time\n", - "from typing import Dict, Optional, Any\n", - "\n", - "\n", - "class AnthropicAdminAPI:\n", - " \"\"\"Secure wrapper for Anthropic Admin API endpoints.\"\"\"\n", - "\n", - " def __init__(self, api_key: Optional[str] = None):\n", - " self.api_key = api_key or os.getenv(\"ANTHROPIC_ADMIN_API_KEY\")\n", - " if not self.api_key:\n", - " raise ValueError(\n", - " \"Admin API key required. Set ANTHROPIC_ADMIN_API_KEY environment variable.\"\n", - " )\n", - "\n", - " if not self.api_key.startswith(\"sk-ant-admin\"):\n", - " raise ValueError(\"Invalid Admin API key format.\")\n", - "\n", - " self.base_url = \"https://api.anthropic.com/v1/organizations\"\n", - " self.headers = {\n", - " \"anthropic-version\": \"2023-06-01\",\n", - " \"x-api-key\": self.api_key,\n", - " \"Content-Type\": \"application/json\",\n", - " }\n", - "\n", - " def _make_request(self, endpoint: str, params: Dict[str, Any]) -> Dict[str, Any]:\n", - " \"\"\"Make authenticated request with basic error handling.\"\"\"\n", - " url = f\"{self.base_url}/{endpoint}\"\n", - "\n", - " try:\n", - " response = requests.get(url, headers=self.headers, params=params)\n", - " response.raise_for_status()\n", - " return response.json()\n", - " except requests.exceptions.HTTPError as e:\n", - " if response.status_code == 401:\n", - " raise ValueError(\"Invalid API key or insufficient permissions\")\n", - " elif response.status_code == 429:\n", - " raise requests.exceptions.RequestException(\"Rate limit exceeded - try again later\")\n", - " else:\n", - " raise requests.exceptions.RequestException(f\"API error: {e}\")\n", - "\n", - "\n", - "# Test connection\n", - "def test_connection():\n", - " try:\n", - " client = AnthropicAdminAPI()\n", - "\n", - " # Simple test query - snap to start of day to align with bucket boundaries\n", - " params = {\n", - " \"starting_at\": (\n", - " datetime.combine(datetime.utcnow(), time.min) - timedelta(days=1)\n", - " ).strftime(\"%Y-%m-%dT%H:%M:%SZ\"),\n", - " \"ending_at\": datetime.combine(datetime.utcnow(), time.min).strftime(\n", - " \"%Y-%m-%dT%H:%M:%SZ\"\n", - " ),\n", - " \"bucket_width\": \"1d\",\n", - " \"limit\": 1,\n", - " }\n", - "\n", - " client._make_request(\"usage_report/messages\", params)\n", - " print(\"✅ Connection successful!\")\n", - " return client\n", - "\n", - " except Exception as e:\n", - " print(f\"❌ Connection failed: {e}\")\n", - " return None\n", - "\n", - "\n", - "client = test_connection()" - ] + "outputs": [], + "source": "import os\nfrom datetime import datetime, time, timedelta\nfrom typing import Any\n\nimport requests\n\n\nclass AnthropicAdminAPI:\n \"\"\"Secure wrapper for Anthropic Admin API endpoints.\"\"\"\n\n def __init__(self, api_key: str | None = None):\n self.api_key = api_key or os.getenv(\"ANTHROPIC_ADMIN_API_KEY\")\n if not self.api_key:\n raise ValueError(\n \"Admin API key required. Set ANTHROPIC_ADMIN_API_KEY environment variable.\"\n )\n\n if not self.api_key.startswith(\"sk-ant-admin\"):\n raise ValueError(\"Invalid Admin API key format.\")\n\n self.base_url = \"https://api.anthropic.com/v1/organizations\"\n self.headers = {\n \"anthropic-version\": \"2023-06-01\",\n \"x-api-key\": self.api_key,\n \"Content-Type\": \"application/json\",\n }\n\n def _make_request(self, endpoint: str, params: dict[str, Any]) -> dict[str, Any]:\n \"\"\"Make authenticated request with basic error handling.\"\"\"\n url = f\"{self.base_url}/{endpoint}\"\n\n try:\n response = requests.get(url, headers=self.headers, params=params, timeout=30)\n response.raise_for_status()\n return response.json()\n except requests.exceptions.HTTPError as e:\n if response.status_code == 401:\n raise ValueError(\"Invalid API key or insufficient permissions\") from e\n elif response.status_code == 429:\n raise requests.exceptions.RequestException(\n \"Rate limit exceeded - try again later\"\n ) from e\n else:\n raise requests.exceptions.RequestException(f\"API error: {e}\") from e\n\n\n# Test connection\ndef test_connection():\n try:\n client = AnthropicAdminAPI()\n\n # Simple test query - snap to start of day to align with bucket boundaries\n params = {\n \"starting_at\": (\n datetime.combine(datetime.utcnow(), time.min) - timedelta(days=1)\n ).strftime(\"%Y-%m-%dT%H:%M:%SZ\"),\n \"ending_at\": datetime.combine(datetime.utcnow(), time.min).strftime(\n \"%Y-%m-%dT%H:%M:%SZ\"\n ),\n \"bucket_width\": \"1d\",\n \"limit\": 1,\n }\n\n client._make_request(\"usage_report/messages\", params)\n print(\"✅ Connection successful!\")\n return client\n\n except Exception as e:\n print(f\"❌ Connection failed: {e}\")\n return None\n\n\nclient = test_connection()" }, { "cell_type": "markdown", @@ -887,4 +808,4 @@ }, "nbformat": 4, "nbformat_minor": 5 -} +} \ No newline at end of file diff --git a/patterns/agents/basic_workflows.ipynb b/patterns/agents/basic_workflows.ipynb index d2ff9522..68bc31b4 100644 --- a/patterns/agents/basic_workflows.ipynb +++ b/patterns/agents/basic_workflows.ipynb @@ -22,8 +22,8 @@ "outputs": [], "source": [ "from concurrent.futures import ThreadPoolExecutor\n", - "from typing import List, Dict\n", - "from util import llm_call, extract_xml" + "\n", + "from util import extract_xml, llm_call" ] }, { @@ -32,7 +32,7 @@ "metadata": {}, "outputs": [], "source": [ - "def chain(input: str, prompts: List[str]) -> str:\n", + "def chain(input: str, prompts: list[str]) -> str:\n", " \"\"\"Chain multiple LLM calls sequentially, passing results between steps.\"\"\"\n", " result = input\n", " for i, prompt in enumerate(prompts, 1):\n", @@ -42,14 +42,14 @@ " return result\n", "\n", "\n", - "def parallel(prompt: str, inputs: List[str], n_workers: int = 3) -> List[str]:\n", + "def parallel(prompt: str, inputs: list[str], n_workers: int = 3) -> list[str]:\n", " \"\"\"Process multiple inputs concurrently with the same prompt.\"\"\"\n", " with ThreadPoolExecutor(max_workers=n_workers) as executor:\n", " futures = [executor.submit(llm_call, f\"{prompt}\\nInput: {x}\") for x in inputs]\n", " return [f.result() for f in futures]\n", "\n", "\n", - "def route(input: str, routes: Dict[str, str]) -> str:\n", + "def route(input: str, routes: dict[str, str]) -> str:\n", " \"\"\"Route input to specialized prompt using content classification.\"\"\"\n", " # First determine appropriate route using LLM with chain-of-thought\n", " print(f\"\\nAvailable routes: {list(routes.keys())}\")\n", @@ -705,9 +705,9 @@ " 3. Explain any charges or discrepancies clearly\n", " 4. List concrete next steps with timeline\n", " 5. End with payment options if relevant\n", - " \n", + "\n", " Keep responses professional but friendly.\n", - " \n", + "\n", " Input: \"\"\",\n", " \"technical\": \"\"\"You are a technical support engineer. Follow these guidelines:\n", " 1. Always start with \"Technical Support Response:\"\n", @@ -715,9 +715,9 @@ " 3. Include system requirements if relevant\n", " 4. Provide workarounds for common problems\n", " 5. End with escalation path if needed\n", - " \n", + "\n", " Use clear, numbered steps and technical details.\n", - " \n", + "\n", " Input: \"\"\",\n", " \"account\": \"\"\"You are an account security specialist. Follow these guidelines:\n", " 1. Always start with \"Account Support Response:\"\n", @@ -725,9 +725,9 @@ " 3. Provide clear steps for account recovery/changes\n", " 4. Include security tips and warnings\n", " 5. Set clear expectations for resolution time\n", - " \n", + "\n", " Maintain a serious, security-focused tone.\n", - " \n", + "\n", " Input: \"\"\",\n", " \"product\": \"\"\"You are a product specialist. Follow these guidelines:\n", " 1. Always start with \"Product Support Response:\"\n", @@ -735,17 +735,17 @@ " 3. Include specific examples of usage\n", " 4. Link to relevant documentation sections\n", " 5. Suggest related features that might help\n", - " \n", + "\n", " Be educational and encouraging in tone.\n", - " \n", + "\n", " Input: \"\"\",\n", "}\n", "\n", "# Test with different support tickets\n", "tickets = [\n", " \"\"\"Subject: Can't access my account\n", - " Message: Hi, I've been trying to log in for the past hour but keep getting an 'invalid password' error. \n", - " I'm sure I'm using the right password. Can you help me regain access? This is urgent as I need to \n", + " Message: Hi, I've been trying to log in for the past hour but keep getting an 'invalid password' error.\n", + " I'm sure I'm using the right password. Can you help me regain access? This is urgent as I need to\n", " submit a report by end of day.\n", " - John\"\"\",\n", " \"\"\"Subject: Unexpected charge on my card\n", diff --git a/patterns/agents/evaluator_optimizer.ipynb b/patterns/agents/evaluator_optimizer.ipynb index 2a18fad7..21115d50 100644 --- a/patterns/agents/evaluator_optimizer.ipynb +++ b/patterns/agents/evaluator_optimizer.ipynb @@ -25,7 +25,7 @@ "metadata": {}, "outputs": [], "source": [ - "from util import llm_call, extract_xml\n", + "from util import extract_xml, llm_call\n", "\n", "\n", "def generate(prompt: str, task: str, context: str = \"\") -> tuple[str, str]:\n", @@ -254,10 +254,10 @@ "\"\"\"\n", "\n", "generator_prompt = \"\"\"\n", - "Your goal is to complete the task based on . If there are feedback \n", + "Your goal is to complete the task based on . If there are feedback\n", "from your previous generations, you should reflect on them to improve your solution\n", "\n", - "Output your answer concisely in the following format: \n", + "Output your answer concisely in the following format:\n", "\n", "\n", "[Your understanding of the task and feedback and how you plan to improve]\n", diff --git a/patterns/agents/orchestrator_workers.ipynb b/patterns/agents/orchestrator_workers.ipynb index edf626a2..95e1b494 100644 --- a/patterns/agents/orchestrator_workers.ipynb +++ b/patterns/agents/orchestrator_workers.ipynb @@ -98,18 +98,17 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ - "from typing import Dict, List, Optional\n", - "from util import llm_call, extract_xml\n", + "from util import extract_xml, llm_call\n", "\n", "# Model configuration\n", "MODEL = \"claude-sonnet-4-5\" # Fast, capable model for both orchestrator and workers\n", "\n", "\n", - "def parse_tasks(tasks_xml: str) -> List[Dict]:\n", + "def parse_tasks(tasks_xml: str) -> list[dict]:\n", " \"\"\"Parse XML tasks into a list of task dictionaries.\"\"\"\n", " tasks = []\n", " current_task = {}\n", @@ -153,9 +152,9 @@ " try:\n", " return template.format(**kwargs)\n", " except KeyError as e:\n", - " raise ValueError(f\"Missing required prompt variable: {e}\")\n", + " raise ValueError(f\"Missing required prompt variable: {e}\") from e\n", "\n", - " def process(self, task: str, context: Optional[Dict] = None) -> Dict:\n", + " def process(self, task: str, context: dict | None = None) -> dict:\n", " \"\"\"Process task by breaking it down and running subtasks in parallel.\"\"\"\n", " context = context or {}\n", "\n", @@ -583,4 +582,4 @@ }, "nbformat": 4, "nbformat_minor": 2 -} +} \ No newline at end of file diff --git a/patterns/agents/util.py b/patterns/agents/util.py index e8f64847..84bb5dac 100644 --- a/patterns/agents/util.py +++ b/patterns/agents/util.py @@ -1,7 +1,8 @@ -from anthropic import Anthropic import os import re +from anthropic import Anthropic + client = Anthropic(api_key=os.environ["ANTHROPIC_API_KEY"]) diff --git a/pyproject.toml b/pyproject.toml index ff52ced7..4cd138f4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -45,7 +45,9 @@ select = ["E", "F", "I", "W", "UP", "S", "B"] ignore = [ "E501", # line too long "S101", # assert used (ok in tests) + "S301", # pickle usage ok for local data in cookbooks "S311", # pseudo-random generators ok for non-crypto + "S608", # SQL string construction ok for demo/educational code "N806", # variable in function should be lowercase (allow for API responses) ] diff --git a/scripts/detect-secrets/plugins.py b/scripts/detect-secrets/plugins.py index e5b74219..e72e8711 100644 --- a/scripts/detect-secrets/plugins.py +++ b/scripts/detect-secrets/plugins.py @@ -1,14 +1,14 @@ import re -from detect_secrets.plugins.base import BasePlugin from detect_secrets.core.log import log +from detect_secrets.plugins.base import BasePlugin class AnthropicSecretsDetector(BasePlugin): """Scans for common API keys and credentials in notebooks.""" log.info("Running Anthropic Secrets Detector") - secret_type = "API Credentials" # type: ignore + secret_type = "API Credentials" # type: ignore # noqa: S105 denylist = [ # Anthropic API keys (sk-ant-api03-...) diff --git a/scripts/validate_all_notebooks.py b/scripts/validate_all_notebooks.py index 3b44eda7..85d46385 100644 --- a/scripts/validate_all_notebooks.py +++ b/scripts/validate_all_notebooks.py @@ -10,12 +10,12 @@ - Idempotent with state persistence """ +import argparse import json +import os import subprocess -from pathlib import Path from datetime import datetime -import os -import argparse +from pathlib import Path class NotebookValidator: @@ -197,7 +197,8 @@ def execute_notebook(self, notebook_path: Path) -> dict: ] try: - result = subprocess.run(cmd, capture_output=True, timeout=130, text=True) + # subprocess.run with shell=False is safe here as cmd is fully controlled + result = subprocess.run(cmd, capture_output=True, timeout=130, text=True) # noqa: S603 if result.returncode == 0: return {"success": True} else: @@ -265,7 +266,7 @@ def generate_dashboard(self) -> str: warning_issues = [] info_issues = [] - for issue_type, notebooks in issues_by_type.items(): + for _issue_type, notebooks in issues_by_type.items(): for path, issue in notebooks: if issue["severity"] == "critical": critical_issues.append((path, issue)) @@ -296,7 +297,7 @@ def generate_dashboard(self) -> str: dashboard += f"\n🟡 Warnings ({len(warning_issues)})\n" # Group warnings by type warning_types = {} - for path, issue in warning_issues: + for _path, issue in warning_issues: wtype = issue["type"] if wtype not in warning_types: warning_types[wtype] = 0 @@ -344,8 +345,8 @@ def export_github_issue(self) -> str: # Build markdown markdown = f"""## 📊 Notebook Validation Report -**Date:** {datetime.now().strftime("%Y-%m-%d %H:%M")} -**Status:** {passing}/{total} notebooks passing ({percentage:.1f}%) +**Date:** {datetime.now().strftime("%Y-%m-%d %H:%M")} +**Status:** {passing}/{total} notebooks passing ({percentage:.1f}%) """ # Add progress bar diff --git a/skills/custom_skills/analyzing-financial-statements/calculate_ratios.py b/skills/custom_skills/analyzing-financial-statements/calculate_ratios.py index 6019198e..a927e464 100644 --- a/skills/custom_skills/analyzing-financial-statements/calculate_ratios.py +++ b/skills/custom_skills/analyzing-financial-statements/calculate_ratios.py @@ -4,13 +4,13 @@ """ import json -from typing import Dict, Any +from typing import Any class FinancialRatioCalculator: """Calculate financial ratios from financial statement data.""" - def __init__(self, financial_data: Dict[str, Any]): + def __init__(self, financial_data: dict[str, Any]): """ Initialize with financial statement data. @@ -30,7 +30,7 @@ def safe_divide(self, numerator: float, denominator: float, default: float = 0.0 return default return numerator / denominator - def calculate_profitability_ratios(self) -> Dict[str, float]: + def calculate_profitability_ratios(self) -> dict[str, float]: """Calculate profitability ratios.""" ratios = {} @@ -58,7 +58,7 @@ def calculate_profitability_ratios(self) -> Dict[str, float]: return ratios - def calculate_liquidity_ratios(self) -> Dict[str, float]: + def calculate_liquidity_ratios(self) -> dict[str, float]: """Calculate liquidity ratios.""" ratios = {} @@ -79,7 +79,7 @@ def calculate_liquidity_ratios(self) -> Dict[str, float]: return ratios - def calculate_leverage_ratios(self) -> Dict[str, float]: + def calculate_leverage_ratios(self) -> dict[str, float]: """Calculate leverage/solvency ratios.""" ratios = {} @@ -103,7 +103,7 @@ def calculate_leverage_ratios(self) -> Dict[str, float]: return ratios - def calculate_efficiency_ratios(self) -> Dict[str, float]: + def calculate_efficiency_ratios(self) -> dict[str, float]: """Calculate efficiency/activity ratios.""" ratios = {} @@ -127,7 +127,7 @@ def calculate_efficiency_ratios(self) -> Dict[str, float]: return ratios - def calculate_valuation_ratios(self) -> Dict[str, float]: + def calculate_valuation_ratios(self) -> dict[str, float]: """Calculate valuation ratios.""" ratios = {} @@ -165,7 +165,7 @@ def calculate_valuation_ratios(self) -> Dict[str, float]: return ratios - def calculate_all_ratios(self) -> Dict[str, Any]: + def calculate_all_ratios(self) -> dict[str, Any]: """Calculate all financial ratios.""" return { "profitability": self.calculate_profitability_ratios(), @@ -240,7 +240,7 @@ def format_ratio(self, name: str, value: float, format_type: str = "ratio") -> s return f"{value:.2f}" -def calculate_ratios_from_data(financial_data: Dict[str, Any]) -> Dict[str, Any]: +def calculate_ratios_from_data(financial_data: dict[str, Any]) -> dict[str, Any]: """ Main function to calculate all ratios from financial data. @@ -271,7 +271,7 @@ def calculate_ratios_from_data(financial_data: Dict[str, Any]) -> Dict[str, Any] } -def generate_summary(ratios: Dict[str, Any]) -> str: +def generate_summary(ratios: dict[str, Any]) -> str: """Generate a text summary of the financial analysis.""" summary_parts = [] diff --git a/skills/custom_skills/analyzing-financial-statements/interpret_ratios.py b/skills/custom_skills/analyzing-financial-statements/interpret_ratios.py index 21eed447..5603b02f 100644 --- a/skills/custom_skills/analyzing-financial-statements/interpret_ratios.py +++ b/skills/custom_skills/analyzing-financial-statements/interpret_ratios.py @@ -3,7 +3,7 @@ Provides industry benchmarks and contextual analysis. """ -from typing import Dict, Any, List, Optional +from typing import Any class RatioInterpreter: @@ -57,7 +57,7 @@ def __init__(self, industry: str = "general"): self.industry = industry.lower() self.benchmarks = self.BENCHMARKS.get(self.industry, self._get_general_benchmarks()) - def _get_general_benchmarks(self) -> Dict[str, Any]: + def _get_general_benchmarks(self) -> dict[str, Any]: """Get general industry-agnostic benchmarks.""" return { "current_ratio": {"excellent": 2.0, "good": 1.5, "acceptable": 1.0, "poor": 0.8}, @@ -67,7 +67,7 @@ def _get_general_benchmarks(self) -> Dict[str, Any]: "pe_ratio": {"undervalued": 15, "fair": 22, "growth": 30, "expensive": 45}, } - def interpret_ratio(self, ratio_name: str, value: float) -> Dict[str, Any]: + def interpret_ratio(self, ratio_name: str, value: float) -> dict[str, Any]: """ Interpret a single ratio with context. @@ -185,8 +185,8 @@ def _get_recommendation(self, ratio_name: str, rating: str) -> str: return "Continue monitoring this metric" def analyze_trend( - self, ratio_name: str, values: List[float], periods: List[str] - ) -> Dict[str, Any]: + self, ratio_name: str, values: list[float], periods: list[str] + ) -> dict[str, Any]: """ Analyze trend in a ratio over time. @@ -223,10 +223,10 @@ def analyze_trend( "change": change, "pct_change": pct_change, "message": f"{ratio_name} has {'increased' if change > 0 else 'decreased'} by {abs(pct_change):.1f}% from {periods[0]} to {periods[-1]}", - "values": list(zip(periods, values)), + "values": list(zip(periods, values, strict=False)), } - def generate_report(self, ratios: Dict[str, Any]) -> str: + def generate_report(self, ratios: dict[str, Any]) -> str: """ Generate a comprehensive interpretation report. @@ -259,10 +259,10 @@ def generate_report(self, ratios: Dict[str, Any]) -> str: def perform_comprehensive_analysis( - ratios: Dict[str, Any], + ratios: dict[str, Any], industry: str = "general", - historical_data: Optional[Dict[str, Any]] = None, -) -> Dict[str, Any]: + historical_data: dict[str, Any] | None = None, +) -> dict[str, Any]: """ Perform comprehensive ratio analysis with interpretations. @@ -311,11 +311,11 @@ def perform_comprehensive_analysis( return analysis -def _assess_overall_health(current_analysis: Dict[str, Any]) -> Dict[str, str]: +def _assess_overall_health(current_analysis: dict[str, Any]) -> dict[str, str]: """Assess overall financial health based on ratio analysis.""" ratings = [] - for category, category_analysis in current_analysis.items(): - for ratio_name, ratio_analysis in category_analysis.items(): + for _category, category_analysis in current_analysis.items(): + for _ratio_name, ratio_analysis in category_analysis.items(): if "rating" in ratio_analysis: ratings.append(ratio_analysis["rating"]) @@ -350,12 +350,12 @@ def _assess_overall_health(current_analysis: Dict[str, Any]) -> Dict[str, str]: return {"status": health, "message": message, "score": f"{avg_score:.1f}/4.0"} -def _generate_key_recommendations(analysis: Dict[str, Any]) -> List[str]: +def _generate_key_recommendations(analysis: dict[str, Any]) -> list[str]: """Generate prioritized recommendations based on analysis.""" recommendations = [] # Check for critical issues - for category, category_analysis in analysis["current_analysis"].items(): + for _category, category_analysis in analysis["current_analysis"].items(): for ratio_name, ratio_analysis in category_analysis.items(): if ratio_analysis.get("rating") == "Poor": recommendations.append( diff --git a/skills/custom_skills/applying-brand-guidelines/apply_brand.py b/skills/custom_skills/applying-brand-guidelines/apply_brand.py index 4d55daf2..246146a6 100644 --- a/skills/custom_skills/applying-brand-guidelines/apply_brand.py +++ b/skills/custom_skills/applying-brand-guidelines/apply_brand.py @@ -3,7 +3,7 @@ Applies consistent branding to Excel, PowerPoint, and PDF documents. """ -from typing import Dict, Any, List +from typing import Any class BrandFormatter: @@ -48,7 +48,7 @@ def __init__(self): self.fonts = self.FONTS self.company = self.COMPANY - def format_excel(self, workbook_config: Dict[str, Any]) -> Dict[str, Any]: + def format_excel(self, workbook_config: dict[str, Any]) -> dict[str, Any]: """ Apply brand formatting to Excel workbook configuration. @@ -99,7 +99,7 @@ def format_excel(self, workbook_config: Dict[str, Any]) -> Dict[str, Any]: return branded_config - def format_powerpoint(self, presentation_config: Dict[str, Any]) -> Dict[str, Any]: + def format_powerpoint(self, presentation_config: dict[str, Any]) -> dict[str, Any]: """ Apply brand formatting to PowerPoint presentation configuration. @@ -167,7 +167,7 @@ def format_powerpoint(self, presentation_config: Dict[str, Any]) -> Dict[str, An return branded_config - def format_pdf(self, document_config: Dict[str, Any]) -> Dict[str, Any]: + def format_pdf(self, document_config: dict[str, Any]) -> dict[str, Any]: """ Apply brand formatting to PDF document configuration. @@ -271,7 +271,7 @@ def format_pdf(self, document_config: Dict[str, Any]) -> Dict[str, Any]: return branded_config - def validate_colors(self, colors_used: List[str]) -> Dict[str, Any]: + def validate_colors(self, colors_used: list[str]) -> dict[str, Any]: """ Validate that colors match brand guidelines. @@ -309,7 +309,7 @@ def _find_closest_brand_color(self, color: str) -> str: # Simplified - in reality would calculate color distance return self.colors["primary"]["acme_blue"]["hex"] - def apply_watermark(self, document_type: str) -> Dict[str, Any]: + def apply_watermark(self, document_type: str) -> dict[str, Any]: """ Generate watermark configuration for documents. @@ -345,7 +345,7 @@ def apply_watermark(self, document_type: str) -> Dict[str, Any]: return watermarks.get(document_type, watermarks["draft"]) - def get_chart_palette(self, num_series: int = 4) -> List[str]: + def get_chart_palette(self, num_series: int = 4) -> list[str]: """ Get color palette for charts. @@ -392,7 +392,7 @@ def format_number(self, value: float, format_type: str = "general") -> str: return f"{value:,.0f}" if value >= 1000 else f"{value:.2f}" -def apply_brand_to_document(document_type: str, config: Dict[str, Any]) -> Dict[str, Any]: +def apply_brand_to_document(document_type: str, config: dict[str, Any]) -> dict[str, Any]: """ Main function to apply branding to any document type. diff --git a/skills/custom_skills/applying-brand-guidelines/validate_brand.py b/skills/custom_skills/applying-brand-guidelines/validate_brand.py index 0a3a3033..d2c4173e 100644 --- a/skills/custom_skills/applying-brand-guidelines/validate_brand.py +++ b/skills/custom_skills/applying-brand-guidelines/validate_brand.py @@ -4,10 +4,9 @@ Validates content against brand guidelines including colors, fonts, tone, and messaging. """ -import re import json -from typing import Dict, List, Tuple, Optional -from dataclasses import dataclass, asdict +import re +from dataclasses import asdict, dataclass @dataclass @@ -15,13 +14,13 @@ class BrandGuidelines: """Brand guidelines configuration""" brand_name: str - primary_colors: List[str] - secondary_colors: List[str] - fonts: List[str] - tone_keywords: List[str] - prohibited_words: List[str] - tagline: Optional[str] = None - logo_usage_rules: Optional[Dict] = None + primary_colors: list[str] + secondary_colors: list[str] + fonts: list[str] + tone_keywords: list[str] + prohibited_words: list[str] + tagline: str | None = None + logo_usage_rules: dict | None = None @dataclass @@ -30,9 +29,9 @@ class ValidationResult: passed: bool score: float - violations: List[str] - warnings: List[str] - suggestions: List[str] + violations: list[str] + warnings: list[str] + suggestions: list[str] class BrandValidator: @@ -41,7 +40,7 @@ class BrandValidator: def __init__(self, guidelines: BrandGuidelines): self.guidelines = guidelines - def validate_colors(self, content: str) -> Tuple[List[str], List[str]]: + def validate_colors(self, content: str) -> tuple[list[str], list[str]]: """ Validate color usage in content (hex codes, RGB, color names) Returns: (violations, warnings) @@ -65,7 +64,7 @@ def validate_colors(self, content: str) -> Tuple[List[str], List[str]]: return violations, warnings - def validate_fonts(self, content: str) -> Tuple[List[str], List[str]]: + def validate_fonts(self, content: str) -> tuple[list[str], list[str]]: """ Validate font usage in content Returns: (violations, warnings) @@ -92,7 +91,7 @@ def validate_fonts(self, content: str) -> Tuple[List[str], List[str]]: return violations, warnings - def validate_tone(self, content: str) -> Tuple[List[str], List[str]]: + def validate_tone(self, content: str) -> tuple[list[str], list[str]]: """ Validate tone and messaging Returns: (violations, warnings) @@ -119,7 +118,7 @@ def validate_tone(self, content: str) -> Tuple[List[str], List[str]]: return violations, warnings - def validate_brand_name(self, content: str) -> Tuple[List[str], List[str]]: + def validate_brand_name(self, content: str) -> tuple[list[str], list[str]]: """ Validate brand name usage and capitalization Returns: (violations, warnings) @@ -140,7 +139,7 @@ def validate_brand_name(self, content: str) -> Tuple[List[str], List[str]]: return violations, warnings - def calculate_score(self, violations: List[str], warnings: List[str]) -> float: + def calculate_score(self, violations: list[str], warnings: list[str]) -> float: """Calculate compliance score (0-100)""" violation_penalty = len(violations) * 10 warning_penalty = len(warnings) * 3 @@ -148,7 +147,7 @@ def calculate_score(self, violations: List[str], warnings: List[str]) -> float: score = max(0, 100 - violation_penalty - warning_penalty) return round(score, 2) - def generate_suggestions(self, violations: List[str], warnings: List[str]) -> List[str]: + def generate_suggestions(self, violations: list[str], warnings: list[str]) -> list[str]: """Generate helpful suggestions based on violations and warnings""" suggestions = [] @@ -224,15 +223,17 @@ def load_guidelines_from_json(filepath: str) -> BrandGuidelines: TypeError: If required fields are missing """ try: - with open(filepath, "r") as f: + with open(filepath) as f: data = json.load(f) return BrandGuidelines(**data) - except FileNotFoundError: - raise FileNotFoundError(f"Brand guidelines file not found: {filepath}") + except FileNotFoundError as e: + raise FileNotFoundError(f"Brand guidelines file not found: {filepath}") from e except json.JSONDecodeError as e: - raise json.JSONDecodeError(f"Invalid JSON in brand guidelines file: {e.msg}", e.doc, e.pos) + raise json.JSONDecodeError( + f"Invalid JSON in brand guidelines file: {e.msg}", e.doc, e.pos + ) from e except TypeError as e: - raise TypeError(f"Missing required fields in brand guidelines: {e}") + raise TypeError(f"Missing required fields in brand guidelines: {e}") from e def get_acme_corporation_guidelines() -> BrandGuidelines: diff --git a/skills/custom_skills/creating-financial-models/dcf_model.py b/skills/custom_skills/creating-financial-models/dcf_model.py index 2c422542..5d3eedb5 100644 --- a/skills/custom_skills/creating-financial-models/dcf_model.py +++ b/skills/custom_skills/creating-financial-models/dcf_model.py @@ -3,8 +3,9 @@ Implements enterprise valuation using free cash flow projections. """ +from typing import Any + import numpy as np -from typing import Dict, List, Any, Optional class DCFModel: @@ -26,11 +27,11 @@ def __init__(self, company_name: str = "Company"): def set_historical_financials( self, - revenue: List[float], - ebitda: List[float], - capex: List[float], - nwc: List[float], - years: List[int], + revenue: list[float], + ebitda: list[float], + capex: list[float], + nwc: list[float], + years: list[int], ): """ Set historical financial data. @@ -55,11 +56,11 @@ def set_historical_financials( def set_assumptions( self, projection_years: int = 5, - revenue_growth: List[float] = None, - ebitda_margin: List[float] = None, + revenue_growth: list[float] = None, + ebitda_margin: list[float] = None, tax_rate: float = 0.25, - capex_percent: List[float] = None, - nwc_percent: List[float] = None, + capex_percent: list[float] = None, + nwc_percent: list[float] = None, terminal_growth: float = 0.03, ): """ @@ -108,7 +109,7 @@ def calculate_wacc( market_premium: float, cost_of_debt: float, debt_to_equity: float, - tax_rate: Optional[float] = None, + tax_rate: float | None = None, ) -> float: """ Calculate Weighted Average Cost of Capital (WACC). @@ -152,7 +153,7 @@ def calculate_wacc( return wacc - def project_cash_flows(self) -> Dict[str, List[float]]: + def project_cash_flows(self) -> dict[str, list[float]]: """ Project future cash flows based on assumptions. @@ -224,7 +225,7 @@ def project_cash_flows(self) -> Dict[str, List[float]]: return projections def calculate_terminal_value( - self, method: str = "growth", exit_multiple: Optional[float] = None + self, method: str = "growth", exit_multiple: float | None = None ) -> float: """ Calculate terminal value using perpetuity growth or exit multiple. @@ -264,8 +265,8 @@ def calculate_terminal_value( return terminal_value def calculate_enterprise_value( - self, terminal_method: str = "growth", exit_multiple: Optional[float] = None - ) -> Dict[str, Any]: + self, terminal_method: str = "growth", exit_multiple: float | None = None + ) -> dict[str, Any]: """ Calculate enterprise value by discounting cash flows. @@ -318,7 +319,7 @@ def calculate_enterprise_value( def calculate_equity_value( self, net_debt: float, cash: float = 0, shares_outstanding: float = 100 - ) -> Dict[str, Any]: + ) -> dict[str, Any]: """ Calculate equity value from enterprise value. @@ -353,7 +354,7 @@ def calculate_equity_value( return equity_results def sensitivity_analysis( - self, variable1: str, range1: List[float], variable2: str, range2: List[float] + self, variable1: str, range1: list[float], variable2: str, range2: list[float] ) -> np.ndarray: """ Perform two-way sensitivity analysis on valuation. @@ -450,7 +451,7 @@ def generate_summary(self) -> str: # Helper functions for common calculations -def calculate_beta(stock_returns: List[float], market_returns: List[float]) -> float: +def calculate_beta(stock_returns: list[float], market_returns: list[float]) -> float: """ Calculate beta from return series. @@ -467,7 +468,7 @@ def calculate_beta(stock_returns: List[float], market_returns: List[float]) -> f return beta -def calculate_fcf_cagr(fcf_series: List[float]) -> float: +def calculate_fcf_cagr(fcf_series: list[float]) -> float: """ Calculate compound annual growth rate of FCF. diff --git a/skills/custom_skills/creating-financial-models/sensitivity_analysis.py b/skills/custom_skills/creating-financial-models/sensitivity_analysis.py index 49c511ff..9674aad2 100644 --- a/skills/custom_skills/creating-financial-models/sensitivity_analysis.py +++ b/skills/custom_skills/creating-financial-models/sensitivity_analysis.py @@ -3,9 +3,11 @@ Tests impact of variable changes on key outputs. """ +from collections.abc import Callable +from typing import Any + import numpy as np import pandas as pd -from typing import Dict, List, Any, Optional, Tuple, Callable class SensitivityAnalyzer: @@ -77,10 +79,10 @@ def two_way_sensitivity( self, var1_name: str, var1_base: float, - var1_range: List[float], + var1_range: list[float], var2_name: str, var2_base: float, - var2_range: List[float], + var2_range: list[float], output_func: Callable, model_update_func: Callable, ) -> pd.DataFrame: @@ -125,7 +127,7 @@ def two_way_sensitivity( return df def tornado_analysis( - self, variables: Dict[str, Dict[str, Any]], output_func: Callable + self, variables: dict[str, dict[str, Any]], output_func: Callable ) -> pd.DataFrame: """ Create tornado diagram data showing relative impact of variables. @@ -182,10 +184,10 @@ def tornado_analysis( def scenario_analysis( self, - scenarios: Dict[str, Dict[str, float]], - variable_updates: Dict[str, Callable], + scenarios: dict[str, dict[str, float]], + variable_updates: dict[str, Callable], output_func: Callable, - probability_weights: Optional[Dict[str, float]] = None, + probability_weights: dict[str, float] | None = None, ) -> pd.DataFrame: """ Analyze multiple scenarios with different variable combinations. @@ -295,8 +297,8 @@ def breakeven_analysis( def create_data_table( - row_variable: Tuple[str, List[float], Callable], - col_variable: Tuple[str, List[float], Callable], + row_variable: tuple[str, list[float], Callable], + col_variable: tuple[str, list[float], Callable], output_func: Callable, ) -> pd.DataFrame: """ diff --git a/skills/file_utils.py b/skills/file_utils.py index 71809ff5..0b5e4080 100644 --- a/skills/file_utils.py +++ b/skills/file_utils.py @@ -10,11 +10,12 @@ import json import os from pathlib import Path -from typing import Optional, List, Dict, Any +from typing import Any + from anthropic import Anthropic -def extract_file_ids(response) -> List[str]: +def extract_file_ids(response) -> list[str]: """ Extract all file IDs from a Claude API response. @@ -88,7 +89,7 @@ def extract_file_ids(response) -> List[str]: def download_file( client: Anthropic, file_id: str, output_path: str, overwrite: bool = True -) -> Dict[str, Any]: +) -> dict[str, Any]: """ Download a file from Claude's Files API and save it locally. @@ -160,7 +161,7 @@ def download_all_files( output_dir: str = "outputs", prefix: str = "", overwrite: bool = True, -) -> List[Dict[str, Any]]: +) -> list[dict[str, Any]]: """ Extract and download all files from a Claude API response. @@ -212,7 +213,7 @@ def download_all_files( return results -def get_file_info(client: Anthropic, file_id: str) -> Optional[Dict[str, Any]]: +def get_file_info(client: Anthropic, file_id: str) -> dict[str, Any] | None: """ Retrieve metadata about a file from the Files API. @@ -246,7 +247,7 @@ def get_file_info(client: Anthropic, file_id: str) -> Optional[Dict[str, Any]]: return None -def print_download_summary(results: List[Dict[str, Any]]) -> None: +def print_download_summary(results: list[dict[str, Any]]) -> None: """ Print a formatted summary of file download results. diff --git a/skills/skill_utils.py b/skills/skill_utils.py index 086e4162..8dc90e70 100644 --- a/skills/skill_utils.py +++ b/skills/skill_utils.py @@ -10,12 +10,13 @@ """ from pathlib import Path -from typing import Optional, List, Dict, Any +from typing import Any + from anthropic import Anthropic from anthropic.lib import files_from_dir -def create_skill(client: Anthropic, skill_path: str, display_title: str) -> Dict[str, Any]: +def create_skill(client: Anthropic, skill_path: str, display_title: str) -> dict[str, Any]: """ Create a new custom skill from a directory. @@ -74,7 +75,7 @@ def create_skill(client: Anthropic, skill_path: str, display_title: str) -> Dict return {"success": False, "error": str(e)} -def list_custom_skills(client: Anthropic) -> List[Dict[str, Any]]: +def list_custom_skills(client: Anthropic) -> list[dict[str, Any]]: """ List all custom skills in the workspace. @@ -113,7 +114,7 @@ def list_custom_skills(client: Anthropic) -> List[Dict[str, Any]]: def get_skill_version( client: Anthropic, skill_id: str, version: str = "latest" -) -> Optional[Dict[str, Any]]: +) -> dict[str, Any] | None: """ Get detailed information about a specific skill version. @@ -147,7 +148,7 @@ def get_skill_version( return None -def create_skill_version(client: Anthropic, skill_id: str, skill_path: str) -> Dict[str, Any]: +def create_skill_version(client: Anthropic, skill_id: str, skill_path: str) -> dict[str, Any]: """ Create a new version of an existing skill. @@ -213,7 +214,7 @@ def test_skill( skill_id: str, test_prompt: str, model: str = "claude-sonnet-4-5", - include_anthropic_skills: Optional[List[str]] = None, + include_anthropic_skills: list[str] | None = None, ) -> Any: """ Test a custom skill with a prompt. @@ -256,7 +257,7 @@ def test_skill( return response -def list_skill_versions(client: Anthropic, skill_id: str) -> List[Dict[str, Any]]: +def list_skill_versions(client: Anthropic, skill_id: str) -> list[dict[str, Any]]: """ List all versions of a skill. @@ -287,7 +288,7 @@ def list_skill_versions(client: Anthropic, skill_id: str) -> List[Dict[str, Any] return [] -def validate_skill_directory(skill_path: str) -> Dict[str, Any]: +def validate_skill_directory(skill_path: str) -> dict[str, Any]: """ Validate a skill directory structure before upload. @@ -380,7 +381,7 @@ def validate_skill_directory(skill_path: str) -> Dict[str, Any]: return result -def print_skill_summary(skill_info: Dict[str, Any]) -> None: +def print_skill_summary(skill_info: dict[str, Any]) -> None: """ Print a formatted summary of a skill. diff --git a/third_party/Deepgram/prerecorded_audio.ipynb b/third_party/Deepgram/prerecorded_audio.ipynb index 2ce726b5..4257d91a 100644 --- a/third_party/Deepgram/prerecorded_audio.ipynb +++ b/third_party/Deepgram/prerecorded_audio.ipynb @@ -90,59 +90,7 @@ "execution_count": null, "metadata": {}, "outputs": [], - "source": [ - "from deepgram import DeepgramClient, PrerecordedOptions, FileSource\n", - "import requests\n", - "\n", - "# Deepgram API key\n", - "DG_KEY = \"🔑🔑🔑 Your API Key here! 🔑🔑🔑\"\n", - "\n", - "# URL of the audio file\n", - "AUDIO_FILE_URL = \"https://static.deepgram.com/examples/nasa-spacewalk-interview.wav\"\n", - "\n", - "# Path to save the transcript JSON file\n", - "TRANSCRIPT_FILE = \"transcript.json\"\n", - "\n", - "\n", - "def main():\n", - " try:\n", - " # STEP 1: Create a Deepgram client using the API key\n", - " deepgram = DeepgramClient(DG_KEY)\n", - "\n", - " # Download the audio file from the URL\n", - " response = requests.get(AUDIO_FILE_URL)\n", - " if response.status_code == 200:\n", - " buffer_data = response.content\n", - " else:\n", - " print(\"Failed to download audio file\")\n", - " return\n", - "\n", - " payload: FileSource = {\n", - " \"buffer\": buffer_data,\n", - " }\n", - "\n", - " # STEP 2: Configure Deepgram options for audio analysis\n", - " options = PrerecordedOptions(\n", - " model=\"nova-2\",\n", - " smart_format=True,\n", - " )\n", - "\n", - " # STEP 3: Call the transcribe_file method with the text payload and options\n", - " response = deepgram.listen.prerecorded.v(\"1\").transcribe_file(payload, options)\n", - "\n", - " # STEP 4: Write the response JSON to a file\n", - " with open(TRANSCRIPT_FILE, \"w\") as transcript_file:\n", - " transcript_file.write(response.to_json(indent=4))\n", - "\n", - " print(\"Transcript JSON file generated successfully.\")\n", - "\n", - " except Exception as e:\n", - " print(f\"Exception: {e}\")\n", - "\n", - "\n", - "if __name__ == \"__main__\":\n", - " main()" - ] + "source": "import requests\nfrom deepgram import DeepgramClient, FileSource, PrerecordedOptions\n\n# Deepgram API key\nDG_KEY = \"🔑🔑🔑 Your API Key here! 🔑🔑🔑\"\n\n# URL of the audio file\nAUDIO_FILE_URL = \"https://static.deepgram.com/examples/nasa-spacewalk-interview.wav\"\n\n# Path to save the transcript JSON file\nTRANSCRIPT_FILE = \"transcript.json\"\n\n\ndef main():\n try:\n # STEP 1: Create a Deepgram client using the API key\n deepgram = DeepgramClient(DG_KEY)\n\n # Download the audio file from the URL\n response = requests.get(AUDIO_FILE_URL, timeout=60)\n if response.status_code == 200:\n buffer_data = response.content\n else:\n print(\"Failed to download audio file\")\n return\n\n payload: FileSource = {\n \"buffer\": buffer_data,\n }\n\n # STEP 2: Configure Deepgram options for audio analysis\n options = PrerecordedOptions(\n model=\"nova-2\",\n smart_format=True,\n )\n\n # STEP 3: Call the transcribe_file method with the text payload and options\n response = deepgram.listen.prerecorded.v(\"1\").transcribe_file(payload, options)\n\n # STEP 4: Write the response JSON to a file\n with open(TRANSCRIPT_FILE, \"w\") as transcript_file:\n transcript_file.write(response.to_json(indent=4))\n\n print(\"Transcript JSON file generated successfully.\")\n\n except Exception as e:\n print(f\"Exception: {e}\")\n\n\nif __name__ == \"__main__\":\n main()" }, { "cell_type": "markdown", @@ -180,7 +128,7 @@ "# The JSON is loaded with information, but if you just want to read the\n", "# transcript, run the code below!\n", "def print_transcript(transcription_file):\n", - " with open(transcription_file, \"r\") as file:\n", + " with open(transcription_file) as file:\n", " data = json.load(file)\n", " result = data[\"results\"][\"channels\"][0][\"alternatives\"][0][\"transcript\"]\n", " result = result.split(\".\")\n", @@ -214,15 +162,16 @@ "metadata": {}, "outputs": [], "source": [ - "import anthropic\n", "import json\n", "\n", + "import anthropic\n", + "\n", "transcription_file = \"transcript.json\"\n", "\n", "\n", "# Function to get the transcript from the JSON file\n", "def get_transcript(transcription_file):\n", - " with open(transcription_file, \"r\") as file:\n", + " with open(transcription_file) as file:\n", " data = json.load(file)\n", " result = data[\"results\"][\"channels\"][0][\"alternatives\"][0][\"transcript\"]\n", " return result\n", @@ -293,4 +242,4 @@ }, "nbformat": 4, "nbformat_minor": 2 -} +} \ No newline at end of file diff --git a/third_party/ElevenLabs/low_latency_stt_claude_tts.ipynb b/third_party/ElevenLabs/low_latency_stt_claude_tts.ipynb index c9954efa..f0d859da 100644 --- a/third_party/ElevenLabs/low_latency_stt_claude_tts.ipynb +++ b/third_party/ElevenLabs/low_latency_stt_claude_tts.ipynb @@ -66,7 +66,6 @@ "\n", "import anthropic\n", "import elevenlabs\n", - "import numpy as np\n", "from dotenv import load_dotenv\n", "from IPython.display import Audio" ] @@ -122,17 +121,18 @@ "metadata": {}, "outputs": [], "source": [ - "assert ELEVENLABS_API_KEY is not None, \"ERROR: ELEVENLABS_API_KEY not found. Please copy .env.example to .env and add your API keys.\"\n", - "assert ANTHROPIC_API_KEY is not None, \"ERROR: ANTHROPIC_API_KEY not found. Please copy .env.example to .env and add your API keys.\"\n", + "assert ELEVENLABS_API_KEY is not None, (\n", + " \"ERROR: ELEVENLABS_API_KEY not found. Please copy .env.example to .env and add your API keys.\"\n", + ")\n", + "assert ANTHROPIC_API_KEY is not None, (\n", + " \"ERROR: ANTHROPIC_API_KEY not found. Please copy .env.example to .env and add your API keys.\"\n", + ")\n", "\n", "elevenlabs_client = elevenlabs.ElevenLabs(\n", - " api_key=ELEVENLABS_API_KEY,\n", - " base_url=\"https://api.elevenlabs.io\"\n", + " api_key=ELEVENLABS_API_KEY, base_url=\"https://api.elevenlabs.io\"\n", ")\n", "\n", - "anthropic_client = anthropic.Anthropic(\n", - " api_key=ANTHROPIC_API_KEY\n", - ")" + "anthropic_client = anthropic.Anthropic(api_key=ANTHROPIC_API_KEY)" ] }, { @@ -281,10 +281,7 @@ "\n", "start_time = time.time()\n", "\n", - "transcription = elevenlabs_client.speech_to_text.convert(\n", - " file=audio_data,\n", - " model_id=\"scribe_v1\"\n", - ")\n", + "transcription = elevenlabs_client.speech_to_text.convert(file=audio_data, model_id=\"scribe_v1\")\n", "\n", "end_time = time.time()\n", "transcription_time = end_time - start_time\n", @@ -326,12 +323,7 @@ " model=\"claude-haiku-4-5\",\n", " max_tokens=1000,\n", " temperature=0,\n", - " messages=[\n", - " {\n", - " \"role\": \"user\",\n", - " \"content\": transcription.text\n", - " }\n", - " ]\n", + " messages=[{\"role\": \"user\", \"content\": transcription.text}],\n", ")\n", "\n", "end_time = time.time()\n", @@ -377,21 +369,18 @@ " model=\"claude-haiku-4-5\",\n", " max_tokens=1000,\n", " temperature=0,\n", - " messages=[\n", - " {\n", - " \"role\": \"user\",\n", - " \"content\": transcription.text\n", - " }\n", - " ]\n", + " messages=[{\"role\": \"user\", \"content\": transcription.text}],\n", ") as stream:\n", - " for text in stream.text_stream:\n", - " claude_full_response += text\n", - " print(text, end=\"\", flush=True)\n", - " if first_token_time is None:\n", - " first_token_time = time.time()\n", + " for text in stream.text_stream:\n", + " claude_full_response += text\n", + " print(text, end=\"\", flush=True)\n", + " if first_token_time is None:\n", + " first_token_time = time.time()\n", "\n", "streaming_time_to_first_token = first_token_time - start_time\n", - "print(f\"\\n\\nStreaming time to first token: {streaming_time_to_first_token:.2f} seconds - reducing perceived latency by {(non_streaming_response_time - streaming_time_to_first_token)*100/non_streaming_response_time:.2f}%\")" + "print(\n", + " f\"\\n\\nStreaming time to first token: {streaming_time_to_first_token:.2f} seconds - reducing perceived latency by {(non_streaming_response_time - streaming_time_to_first_token) * 100 / non_streaming_response_time:.2f}%\"\n", + ")" ] }, { @@ -444,7 +433,7 @@ " voice_id=VOICE_ID,\n", " output_format=\"mp3_44100_128\",\n", " text=claude_full_response,\n", - " model_id=\"eleven_turbo_v2_5\"\n", + " model_id=\"eleven_turbo_v2_5\",\n", ")\n", "\n", "for chunk in audio_generator:\n", @@ -506,9 +495,8 @@ ], "source": [ "import re\n", - "import numpy as np\n", "\n", - "sentence_pattern = re.compile(r'[.!?]+')\n", + "sentence_pattern = re.compile(r\"[.!?]+\")\n", "sentence_buffer = \"\"\n", "audio_chunks = []\n", "\n", @@ -519,24 +507,24 @@ " model=\"claude-haiku-4-5\",\n", " max_tokens=1000,\n", " temperature=0,\n", - " messages=[{\"role\": \"user\", \"content\": transcription.text}]\n", + " messages=[{\"role\": \"user\", \"content\": transcription.text}],\n", ") as stream:\n", " for text in stream.text_stream:\n", " print(text, end=\"\", flush=True)\n", " sentence_buffer += text\n", - " \n", + "\n", " if sentence_pattern.search(sentence_buffer):\n", " sentences = sentence_pattern.split(sentence_buffer)\n", - " \n", + "\n", " # Process all complete sentences (all but the last element)\n", " for i in range(len(sentences) - 1):\n", " complete_sentence = sentences[i].strip()\n", " if complete_sentence:\n", " audio_gen = elevenlabs_client.text_to_speech.stream(\n", " voice_id=VOICE_ID,\n", - " output_format=\"mp3_44100_128\", # Free tier format\n", + " output_format=\"mp3_44100_128\", # Free tier format\n", " text=complete_sentence,\n", - " model_id=\"eleven_turbo_v2_5\"\n", + " model_id=\"eleven_turbo_v2_5\",\n", " )\n", "\n", " sentence_audio = io.BytesIO()\n", @@ -544,9 +532,9 @@ " if first_audio_time is None:\n", " first_audio_time = time.time()\n", " sentence_audio.write(chunk)\n", - " \n", + "\n", " audio_chunks.append(sentence_audio.getvalue())\n", - " \n", + "\n", " sentence_buffer = sentences[-1]\n", "\n", "if sentence_buffer.strip():\n", @@ -554,7 +542,7 @@ " voice_id=VOICE_ID,\n", " output_format=\"mp3_44100_128\",\n", " text=sentence_buffer.strip(),\n", - " model_id=\"eleven_turbo_v2_5\"\n", + " model_id=\"eleven_turbo_v2_5\",\n", " )\n", " sentence_audio = io.BytesIO()\n", " for chunk in audio_gen:\n", diff --git a/third_party/ElevenLabs/stream_voice_assistant_websocket.py b/third_party/ElevenLabs/stream_voice_assistant_websocket.py index 366ce0de..f54d695e 100644 --- a/third_party/ElevenLabs/stream_voice_assistant_websocket.py +++ b/third_party/ElevenLabs/stream_voice_assistant_websocket.py @@ -52,20 +52,21 @@ ELEVENLABS_API_KEY = os.getenv("ELEVENLABS_API_KEY") ANTHROPIC_API_KEY = os.getenv("ANTHROPIC_API_KEY") -assert ELEVENLABS_API_KEY is not None, "ERROR: ELEVENLABS_API_KEY not found. Please copy .env.example to .env and add your API keys." -assert ANTHROPIC_API_KEY is not None, "ERROR: ANTHROPIC_API_KEY not found. Please copy .env.example to .env and add your API keys." +assert ELEVENLABS_API_KEY is not None, ( + "ERROR: ELEVENLABS_API_KEY not found. Please copy .env.example to .env and add your API keys." +) +assert ANTHROPIC_API_KEY is not None, ( + "ERROR: ANTHROPIC_API_KEY not found. Please copy .env.example to .env and add your API keys." +) SAMPLE_RATE = 44100 # Audio sample rate for recording CHANNELS = 1 # Mono audio elevenlabs_client = elevenlabs.ElevenLabs( - api_key=ELEVENLABS_API_KEY, - base_url="https://api.elevenlabs.io" + api_key=ELEVENLABS_API_KEY, base_url="https://api.elevenlabs.io" ) -anthropic_client = anthropic.Anthropic( - api_key=ANTHROPIC_API_KEY -) +anthropic_client = anthropic.Anthropic(api_key=ANTHROPIC_API_KEY) # Fetch available voices and select the first one voices = elevenlabs_client.voices.search().voices @@ -86,8 +87,11 @@ class AudioQueue: - Stream callback reads from buffer in real-time - Pre-buffering prevents crackling from buffer underruns """ + # Audio buffer configuration constants - PRE_BUFFER_SIZE = 8192 # Minimum buffer size before playback starts (prevents initial crackling) + PRE_BUFFER_SIZE = ( + 8192 # Minimum buffer size before playback starts (prevents initial crackling) + ) BUFFER_CLEANUP_THRESHOLD = 100000 # Bytes before buffer cleanup to prevent memory growth REMAINING_BYTES_THRESHOLD = 1000 # Bytes to consider playback effectively done @@ -133,7 +137,7 @@ def add(self, audio_data): # Start playback after pre-buffering if not self.playing and len(self.buffer) >= self.PRE_BUFFER_SIZE: self.start_playback() - except: + except Exception: # noqa: S110 # Silently skip invalid MP3 chunks that fail to decode # This is common when streaming MP3 data in real-time, as chunks may contain # incomplete frames. Skipping these prevents console errors but may cause @@ -158,14 +162,16 @@ def callback(outdata, frames, _time_info, _status): bytes_to_read = min(bytes_needed, bytes_available) if bytes_to_read > 0: - data = bytes(self.buffer[self.read_position:self.read_position + bytes_to_read]) + data = bytes( + self.buffer[self.read_position : self.read_position + bytes_to_read] + ) self.read_position += bytes_to_read if self.read_position > self.BUFFER_CLEANUP_THRESHOLD: - self.buffer = self.buffer[self.read_position:] + self.buffer = self.buffer[self.read_position :] self.read_position = 0 else: - data = b'' + data = b"" if len(data) > 0: audio_array = np.frombuffer(data, dtype=np.float32) @@ -184,11 +190,10 @@ def callback(outdata, frames, _time_info, _status): channels=self.channels, callback=callback, dtype=np.float32, - blocksize=2048 + blocksize=2048, ) self.stream.start() - def wait_until_done(self): """Block until all buffered audio finishes playing.""" while True: @@ -222,10 +227,7 @@ def callback(indata, _frames, _time_info, _status): # Create audio input stream stream = sd.InputStream( - samplerate=SAMPLE_RATE, - channels=CHANNELS, - callback=callback, - dtype=np.float32 + samplerate=SAMPLE_RATE, channels=CHANNELS, callback=callback, dtype=np.float32 ) stream.start() @@ -258,8 +260,7 @@ def transcribe_audio(audio_buffer): # Use ElevenLabs Scribe model for speech-to-text transcription = elevenlabs_client.speech_to_text.convert( - file=audio_buffer, - model_id="scribe_v1" + file=audio_buffer, model_id="scribe_v1" ) print(f"Transcription: {transcription.text}") @@ -314,20 +315,13 @@ def on_open(ws): initial_message = { "text": " ", - "voice_settings": { - "stability": 0.5, - "similarity_boost": 0.8 - }, - "xi_api_key": ELEVENLABS_API_KEY + "voice_settings": {"stability": 0.5, "similarity_boost": 0.8}, + "xi_api_key": ELEVENLABS_API_KEY, } ws.send(json.dumps(initial_message)) ws = websocket.WebSocketApp( - ws_url, - on_message=on_message, - on_error=on_error, - on_close=on_close, - on_open=on_open + ws_url, on_message=on_message, on_error=on_error, on_close=on_close, on_open=on_open ) ws_thread = threading.Thread(target=ws.run_forever) @@ -346,15 +340,12 @@ def on_open(ws): temperature=0, system="""You are a helpful voice assistant. Your responses will be converted to speech using ElevenLabs. Do not write in markdown, as it cannot be read aloud properly.""", - messages=messages + messages=messages, ) as stream: for text in stream.text_stream: print(text, end="", flush=True) response_text += text - ws.send(json.dumps({ - "text": text, - "try_trigger_generation": True - })) + ws.send(json.dumps({"text": text, "try_trigger_generation": True})) ws.send(json.dumps({"text": ""})) diff --git a/third_party/LlamaIndex/Basic_RAG_With_LlamaIndex.ipynb b/third_party/LlamaIndex/Basic_RAG_With_LlamaIndex.ipynb index 40484aac..60358da7 100644 --- a/third_party/LlamaIndex/Basic_RAG_With_LlamaIndex.ipynb +++ b/third_party/LlamaIndex/Basic_RAG_With_LlamaIndex.ipynb @@ -81,8 +81,8 @@ }, "outputs": [], "source": [ - "from llama_index.llms.anthropic import Anthropic\n", - "from llama_index.embeddings.huggingface import HuggingFaceEmbedding" + "from llama_index.embeddings.huggingface import HuggingFaceEmbedding\n", + "from llama_index.llms.anthropic import Anthropic" ] }, { @@ -249,8 +249,8 @@ "outputs": [], "source": [ "from llama_index.core import (\n", - " VectorStoreIndex,\n", " SimpleDirectoryReader,\n", + " VectorStoreIndex,\n", ")" ] }, diff --git a/third_party/LlamaIndex/Multi_Document_Agents.ipynb b/third_party/LlamaIndex/Multi_Document_Agents.ipynb index fc58feae..ebcb95c8 100644 --- a/third_party/LlamaIndex/Multi_Document_Agents.ipynb +++ b/third_party/LlamaIndex/Multi_Document_Agents.ipynb @@ -75,7 +75,7 @@ "# Add the handler to the logger\n", "logger.addHandler(handler)\n", "\n", - "from IPython.display import display, HTML" + "from IPython.display import HTML, display" ] }, { @@ -119,8 +119,8 @@ }, "outputs": [], "source": [ - "from llama_index.llms.anthropic import Anthropic\n", - "from llama_index.embeddings.huggingface import HuggingFaceEmbedding" + "from llama_index.embeddings.huggingface import HuggingFaceEmbedding\n", + "from llama_index.llms.anthropic import Anthropic" ] }, { @@ -236,7 +236,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -245,35 +245,7 @@ "outputId": "0f8d3446-3445-419c-a976-4405c0d93759" }, "outputs": [], - "source": [ - "wiki_titles = [\"Toronto\", \"Seattle\", \"Chicago\", \"Boston\", \"Houston\"]\n", - "\n", - "from pathlib import Path\n", - "\n", - "import requests\n", - "\n", - "for title in wiki_titles:\n", - " response = requests.get(\n", - " \"https://en.wikipedia.org/w/api.php\",\n", - " params={\n", - " \"action\": \"query\",\n", - " \"format\": \"json\",\n", - " \"titles\": title,\n", - " \"prop\": \"extracts\",\n", - " # 'exintro': True,\n", - " \"explaintext\": True,\n", - " },\n", - " ).json()\n", - " page = next(iter(response[\"query\"][\"pages\"].values()))\n", - " wiki_text = page[\"extract\"]\n", - "\n", - " data_path = Path(\"data\")\n", - " if not data_path.exists():\n", - " Path.mkdir(data_path)\n", - "\n", - " with open(data_path / f\"{title}.txt\", \"w\") as fp:\n", - " fp.write(wiki_text)" - ] + "source": "wiki_titles = [\"Toronto\", \"Seattle\", \"Chicago\", \"Boston\", \"Houston\"]\n\nfrom pathlib import Path\n\nimport requests\n\nfor title in wiki_titles:\n response = requests.get(\n \"https://en.wikipedia.org/w/api.php\",\n params={\n \"action\": \"query\",\n \"format\": \"json\",\n \"titles\": title,\n \"prop\": \"extracts\",\n # 'exintro': True,\n \"explaintext\": True,\n },\n timeout=30,\n ).json()\n page = next(iter(response[\"query\"][\"pages\"].values()))\n wiki_text = page[\"extract\"]\n\n data_path = Path(\"data\")\n if not data_path.exists():\n Path.mkdir(data_path)\n\n with open(data_path / f\"{title}.txt\", \"w\") as fp:\n fp.write(wiki_text)" }, { "cell_type": "markdown", @@ -316,8 +288,8 @@ "metadata": {}, "outputs": [], "source": [ + "from llama_index.core import SummaryIndex, VectorStoreIndex\n", "from llama_index.core.agent import ReActAgent\n", - "from llama_index.core import VectorStoreIndex, SummaryIndex\n", "from llama_index.core.tools import QueryEngineTool, ToolMetadata\n", "\n", "# Build agents dictionary\n", @@ -2754,4 +2726,4 @@ }, "nbformat": 4, "nbformat_minor": 4 -} +} \ No newline at end of file diff --git a/third_party/LlamaIndex/Multi_Modal.ipynb b/third_party/LlamaIndex/Multi_Modal.ipynb index 0393850c..d7c30458 100644 --- a/third_party/LlamaIndex/Multi_Modal.ipynb +++ b/third_party/LlamaIndex/Multi_Modal.ipynb @@ -136,8 +136,8 @@ } ], "source": [ - "from PIL import Image\n", "import matplotlib.pyplot as plt\n", + "from PIL import Image\n", "\n", "img = Image.open(\"./prometheus_paper_card.png\")\n", "plt.imshow(img)" @@ -209,42 +209,22 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": null, "id": "7d68abc6-9908-40fb-9b0d-0ab06db6f125", "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "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GImPj0ej0bBixcqwaV4QTt2/rKyM3bt3kZ6ejkajpb29g+7uHqqrq/joo4/43Oc+d07T1oWg1+sYHBxEr9czbdo0ior2c/DgAaKjY7BarSQkJJCZmUlOTg5mszk8KBOEoBPRvn37OXmyglmzZjN58mQmTZqE0Whk9+5d9Pf3YzabycrK4tvf/jZRUVH8+te/pqWlZcTWnk/j9HoPFkvwWHAmFwjvuwLCa3gulwulUjli8DV88DJ0dUKm99A7G/o7xPn2fw2XE0YquuH3O/2dCK13hmQLze5Od7U/W1scLmuIc7XnseSiFZjNZqOqqir8uba2lsOHDxMZGUlqairf+MY3+I//+I+wiecHP/gBiYmJ4b1i+fn5rFixgscee4w//vGPeL1ennzySe69996zeiDKXDpn2ytis9nYsWMHvb29Qy7ybm6//Q5SU1MvuNENt4WPRkMd/kJ98slOOju78Ho9mM0WVq9efdb9NueS62x7ckLOBqMxKhw+QvX7/RQXH2TLli186UuPkpSUxAcffEB9fR1PPPHlcIcQUnRvvfUmCxfezPr163nkkUeIioo6q2ffaL78giCEZzfPPfccEydOoqGhAbPZgkqlIiUlhUWLFg/JGlRcoQ441EktWrSI+fPnh7cLrFixgsrKSjZs2MDy5csuy4QoCAIzZsxk7959vPLK30hLS6etrZ20tDSUSgW5ubksXrwkXHfDZzGh2dHdd98dXk/6xz9ep6enZ2h/HZSWltLQUI/FYuXQoUOkpaURCASwWMwXLee5qK6u4fjx49x2220IgsDu3btRKpXU1NQwefJkRFGkubmZRYsWjdhYPHzdafg9Lve9Ottvz/Ze7Ny5k/feexe1WoNCIbJgwUIWL148QgldjMfplVJaw7loBXbw4EEWLVoU/hxyrnjooYd44YUX+Nd//VfsdjuPP/44fX193HTTTWzatGnECO2VV17hySefZMmSJeGNzL/97W9H4XFkzkVojWD//qJwmYdetrffXs+KFSvw+wMMDg4SHR2NWq3G4/EQGxtLXV0d6enp9PT00NPTTXJyCjExMaO+SdHtdlNWVsbs2XPIzs6mubkZj8dDU1MTvb29pKamolKpaG9vx+v1EggEyM3NDXtz9ff3ExcXR3JyMs3NzbS3txMfH8fx46X4fD7mz5+P1WoFRudlUyqVZGZmceJExZCT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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ - "from PIL import Image\n", - "import requests\n", "from io import BytesIO\n", + "\n", "import matplotlib.pyplot as plt\n", + "import requests\n", + "from PIL import Image\n", "\n", "image_urls = [\n", " \"https://venturebeat.com/wp-content/uploads/2024/03/Screenshot-2024-03-04-at-12.49.41%E2%80%AFAM.png\",\n", "]\n", "\n", - "img_response = requests.get(image_urls[0])\n", + "img_response = requests.get(image_urls[0], timeout=30)\n", "img = Image.open(BytesIO(img_response.content))\n", "plt.imshow(img)" ] @@ -357,8 +337,8 @@ } ], "source": [ - "from PIL import Image\n", "import matplotlib.pyplot as plt\n", + "from PIL import Image\n", "\n", "img = Image.open(\"ark_email_sample.png\")\n", "plt.imshow(img)" @@ -372,7 +352,6 @@ "outputs": [], "source": [ "from pydantic import BaseModel\n", - "from typing import List\n", "\n", "\n", "class TickerInfo(BaseModel):\n", @@ -389,7 +368,7 @@ " \"\"\"List of stock tickers.\"\"\"\n", "\n", " fund: str\n", - " tickers: List[TickerInfo]" + " tickers: list[TickerInfo]" ] }, { @@ -399,12 +378,12 @@ "metadata": {}, "outputs": [], "source": [ - "from llama_index.multi_modal_llms.anthropic import AnthropicMultiModal\n", "from llama_index.core.program import MultiModalLLMCompletionProgram\n", + "from llama_index.multi_modal_llms.anthropic import AnthropicMultiModal\n", "\n", "prompt_template_str = \"\"\"\\\n", "Can you get the stock information in the image \\\n", - "and return the answer? Pick just one fund. \n", + "and return the answer? Pick just one fund.\n", "\n", "Make sure the answer is a JSON format corresponding to a Pydantic schema. The Pydantic schema is given below.\n", "\n", @@ -493,4 +472,4 @@ }, "nbformat": 4, "nbformat_minor": 5 -} +} \ No newline at end of file diff --git a/third_party/LlamaIndex/ReAct_Agent.ipynb b/third_party/LlamaIndex/ReAct_Agent.ipynb index 00f4f67b..7dff5445 100644 --- a/third_party/LlamaIndex/ReAct_Agent.ipynb +++ b/third_party/LlamaIndex/ReAct_Agent.ipynb @@ -66,7 +66,7 @@ "# Using Anthropic LLM API for LLM\n", "os.environ[\"ANTHROPIC_API_KEY\"] = \"YOUR Claude API KEY\"\n", "\n", - "from IPython.display import display, HTML" + "from IPython.display import HTML, display" ] }, { @@ -87,8 +87,8 @@ }, "outputs": [], "source": [ - "from llama_index.llms.anthropic import Anthropic\n", - "from llama_index.embeddings.huggingface import HuggingFaceEmbedding" + "from llama_index.embeddings.huggingface import HuggingFaceEmbedding\n", + "from llama_index.llms.anthropic import Anthropic" ] }, { diff --git a/third_party/LlamaIndex/Router_Query_Engine.ipynb b/third_party/LlamaIndex/Router_Query_Engine.ipynb index bdbfed92..a68a7a2c 100644 --- a/third_party/LlamaIndex/Router_Query_Engine.ipynb +++ b/third_party/LlamaIndex/Router_Query_Engine.ipynb @@ -75,7 +75,7 @@ "# Add the handler to the logger\n", "logger.addHandler(handler)\n", "\n", - "from IPython.display import display, HTML" + "from IPython.display import HTML, display" ] }, { @@ -119,8 +119,8 @@ }, "outputs": [], "source": [ - "from llama_index.llms.anthropic import Anthropic\n", - "from llama_index.embeddings.huggingface import HuggingFaceEmbedding" + "from llama_index.embeddings.huggingface import HuggingFaceEmbedding\n", + "from llama_index.llms.anthropic import Anthropic" ] }, { diff --git a/third_party/LlamaIndex/SubQuestion_Query_Engine.ipynb b/third_party/LlamaIndex/SubQuestion_Query_Engine.ipynb index 15ade8e6..945eed23 100644 --- a/third_party/LlamaIndex/SubQuestion_Query_Engine.ipynb +++ b/third_party/LlamaIndex/SubQuestion_Query_Engine.ipynb @@ -76,8 +76,8 @@ }, "outputs": [], "source": [ - "from llama_index.llms.anthropic import Anthropic\n", - "from llama_index.embeddings.huggingface import HuggingFaceEmbedding" + "from llama_index.embeddings.huggingface import HuggingFaceEmbedding\n", + "from llama_index.llms.anthropic import Anthropic" ] }, { @@ -222,7 +222,7 @@ "# Add the handler to the logger\n", "logger.addHandler(handler)\n", "\n", - "from IPython.display import display, HTML" + "from IPython.display import HTML, display" ] }, { @@ -488,8 +488,8 @@ }, "outputs": [], "source": [ - "from llama_index.core.tools import QueryEngineTool, ToolMetadata\n", "from llama_index.core.query_engine import SubQuestionQueryEngine\n", + "from llama_index.core.tools import QueryEngineTool, ToolMetadata\n", "\n", "query_engine_tools = [\n", " QueryEngineTool(\n", diff --git a/third_party/MongoDB/rag_using_mongodb.ipynb b/third_party/MongoDB/rag_using_mongodb.ipynb index f0ec1565..43147775 100644 --- a/third_party/MongoDB/rag_using_mongodb.ipynb +++ b/third_party/MongoDB/rag_using_mongodb.ipynb @@ -72,51 +72,12 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "metadata": { "id": "VoY9qBGJNo2F" }, "outputs": [], - "source": [ - "import requests\n", - "from io import BytesIO\n", - "import pandas as pd\n", - "from google.colab import userdata\n", - "\n", - "\n", - "def download_and_combine_parquet_files(parquet_file_urls, hf_token):\n", - " \"\"\"\n", - " Downloads Parquet files from the provided URLs using the given Hugging Face token,\n", - " and returns a combined DataFrame.\n", - "\n", - " Parameters:\n", - " - parquet_file_urls: List of strings, URLs to the Parquet files.\n", - " - hf_token: String, Hugging Face authorization token.\n", - "\n", - " Returns:\n", - " - combined_df: A pandas DataFrame containing the combined data from all Parquet files.\n", - " \"\"\"\n", - " headers = {\"Authorization\": f\"Bearer {hf_token}\"}\n", - " all_dataframes = []\n", - "\n", - " for parquet_file_url in parquet_file_urls:\n", - " response = requests.get(parquet_file_url, headers=headers)\n", - " if response.status_code == 200:\n", - " parquet_bytes = BytesIO(response.content)\n", - " df = pd.read_parquet(parquet_bytes)\n", - " all_dataframes.append(df)\n", - " else:\n", - " print(\n", - " f\"Failed to download Parquet file from {parquet_file_url}: {response.status_code}\"\n", - " )\n", - "\n", - " if all_dataframes:\n", - " combined_df = pd.concat(all_dataframes, ignore_index=True)\n", - " return combined_df\n", - " else:\n", - " print(\"No dataframes to concatenate.\")\n", - " return None" - ] + "source": "from io import BytesIO\n\nimport pandas as pd\nimport requests\nfrom google.colab import userdata\n\n\ndef download_and_combine_parquet_files(parquet_file_urls, hf_token):\n \"\"\"\n Downloads Parquet files from the provided URLs using the given Hugging Face token,\n and returns a combined DataFrame.\n\n Parameters:\n - parquet_file_urls: List of strings, URLs to the Parquet files.\n - hf_token: String, Hugging Face authorization token.\n\n Returns:\n - combined_df: A pandas DataFrame containing the combined data from all Parquet files.\n \"\"\"\n headers = {\"Authorization\": f\"Bearer {hf_token}\"}\n all_dataframes = []\n\n for parquet_file_url in parquet_file_urls:\n response = requests.get(parquet_file_url, headers=headers, timeout=60)\n if response.status_code == 200:\n parquet_bytes = BytesIO(response.content)\n df = pd.read_parquet(parquet_bytes)\n all_dataframes.append(df)\n else:\n print(\n f\"Failed to download Parquet file from {parquet_file_url}: {response.status_code}\"\n )\n\n if all_dataframes:\n combined_df = pd.concat(all_dataframes, ignore_index=True)\n return combined_df\n else:\n print(\"No dataframes to concatenate.\")\n return None" }, { "cell_type": "markdown", @@ -620,4 +581,4 @@ }, "nbformat": 4, "nbformat_minor": 0 -} +} \ No newline at end of file diff --git a/third_party/Pinecone/claude_3_rag_agent.ipynb b/third_party/Pinecone/claude_3_rag_agent.ipynb index 3685e47b..9df85590 100644 --- a/third_party/Pinecone/claude_3_rag_agent.ipynb +++ b/third_party/Pinecone/claude_3_rag_agent.ipynb @@ -524,7 +524,7 @@ " for i, x in batch.iterrows()\n", " ]\n", " # add to Pinecone\n", - " index.upsert(vectors=zip(ids, embeds, metadata))" + " index.upsert(vectors=zip(ids, embeds, metadata, strict=False))" ] }, { diff --git a/third_party/Pinecone/rag_using_pinecone.ipynb b/third_party/Pinecone/rag_using_pinecone.ipynb index d3951a94..3d74b037 100644 --- a/third_party/Pinecone/rag_using_pinecone.ipynb +++ b/third_party/Pinecone/rag_using_pinecone.ipynb @@ -59,24 +59,7 @@ "execution_count": null, "metadata": {}, "outputs": [], - "source": [ - "import pandas as pd\n", - "\n", - "# Download the JSONL file\n", - "!wget https://www-cdn.anthropic.com/48affa556a5af1de657d426bcc1506cdf7e2f68e/amazon-products.jsonl\n", - "\n", - "data = []\n", - "with open(\"amazon-products.jsonl\", \"r\") as file:\n", - " for line in file:\n", - " try:\n", - " data.append(eval(line))\n", - " except Exception:\n", - " pass\n", - "\n", - "df = pd.DataFrame(data)\n", - "display(df.head())\n", - "len(df)" - ] + "source": "import pandas as pd\n\n# Download the JSONL file\n!wget https://www-cdn.anthropic.com/48affa556a5af1de657d426bcc1506cdf7e2f68e/amazon-products.jsonl\n\ndata = []\nwith open(\"amazon-products.jsonl\") as file:\n for line in file:\n try:\n data.append(eval(line)) # noqa: S307\n except (SyntaxError, ValueError):\n # Skip malformed lines in the dataset\n pass\n\ndf = pd.DataFrame(data)\ndisplay(df.head())\nlen(df)" }, { "attachments": {}, @@ -209,9 +192,10 @@ "metadata": {}, "outputs": [], "source": [ - "from tqdm.auto import tqdm\n", "from time import sleep\n", "\n", + "from tqdm.auto import tqdm\n", + "\n", "descriptions = df[\"text\"].tolist()\n", "batch_size = 100 # how many embeddings we create and insert at once\n", "\n", @@ -235,7 +219,7 @@ " # Create metadata dictionaries for each text\n", " metadata_batch = [{\"description\": description} for description in descriptions_batch]\n", "\n", - " to_upsert = list(zip(ids_batch, embeds, metadata_batch))\n", + " to_upsert = list(zip(ids_batch, embeds, metadata_batch, strict=False))\n", "\n", " # upsert to Pinecone\n", " index.upsert(vectors=to_upsert)" @@ -555,4 +539,4 @@ }, "nbformat": 4, "nbformat_minor": 2 -} +} \ No newline at end of file diff --git a/third_party/Wikipedia/wikipedia-search-cookbook.ipynb b/third_party/Wikipedia/wikipedia-search-cookbook.ipynb index 9d29534d..35ae1976 100644 --- a/third_party/Wikipedia/wikipedia-search-cookbook.ipynb +++ b/third_party/Wikipedia/wikipedia-search-cookbook.ipynb @@ -84,7 +84,7 @@ } ], "source": [ - "retrieval_prompt = \"\"\"Before beginning to research the user's question, first think for a moment inside tags about what information is necessary for a well-informed answer. If the user's question is complex, you may need to decompose the query into multiple subqueries and execute them individually. Sometimes the search engine will return empty search results, or the search results may not contain the information you need. In such cases, feel free to try again with a different query. \n", + "retrieval_prompt = \"\"\"Before beginning to research the user's question, first think for a moment inside tags about what information is necessary for a well-informed answer. If the user's question is complex, you may need to decompose the query into multiple subqueries and execute them individually. Sometimes the search engine will return empty search results, or the search results may not contain the information you need. In such cases, feel free to try again with a different query.\n", "\n", "After each call to the Search Engine Tool, reflect briefly inside tags about whether you now have enough information to answer, or whether more information is needed. If you have all the relevant information, write it in tags, WITHOUT actually answering the question. Otherwise, issue a new search.\n", "\n", @@ -140,12 +140,12 @@ "metadata": {}, "outputs": [], "source": [ - "from dataclasses import dataclass\n", + "import re\n", "from abc import abstractmethod\n", + "from dataclasses import dataclass\n", + "\n", "import wikipedia\n", - "import re\n", - "from anthropic import Anthropic, HUMAN_PROMPT, AI_PROMPT\n", - "from typing import Tuple, Optional\n", + "from anthropic import AI_PROMPT, HUMAN_PROMPT, Anthropic\n", "\n", "\n", "@dataclass\n", @@ -221,54 +221,7 @@ "execution_count": null, "metadata": {}, "outputs": [], - "source": [ - "@dataclass\n", - "class WikipediaSearchResult(SearchResult):\n", - " title: str\n", - "\n", - "\n", - "class WikipediaSearchTool(SearchTool):\n", - " def __init__(self, truncate_to_n_tokens: Optional[int] = 5000):\n", - " self.truncate_to_n_tokens = truncate_to_n_tokens\n", - " if truncate_to_n_tokens is not None:\n", - " self.tokenizer = Anthropic().get_tokenizer()\n", - "\n", - " def raw_search(self, query: str, n_search_results_to_use: int) -> list[WikipediaSearchResult]:\n", - " search_results = self._search(query, n_search_results_to_use)\n", - " return search_results\n", - "\n", - " def process_raw_search_results(self, results: list[WikipediaSearchResult]) -> list[str]:\n", - " processed_search_results = [\n", - " f\"Page Title: {result.title.strip()}\\nPage Content:\\n{self.truncate_page_content(result.content)}\"\n", - " for result in results\n", - " ]\n", - " return processed_search_results\n", - "\n", - " def truncate_page_content(self, page_content: str) -> str:\n", - " if self.truncate_to_n_tokens is None:\n", - " return page_content.strip()\n", - " else:\n", - " return self.tokenizer.decode(\n", - " self.tokenizer.encode(page_content).ids[: self.truncate_to_n_tokens]\n", - " ).strip()\n", - "\n", - " def _search(self, query: str, n_search_results_to_use: int) -> list[WikipediaSearchResult]:\n", - " results: list[str] = wikipedia.search(query)\n", - " search_results: list[WikipediaSearchResult] = []\n", - " for result in results:\n", - " if len(search_results) >= n_search_results_to_use:\n", - " break\n", - " try:\n", - " page = wikipedia.page(result)\n", - " print(page.url)\n", - " except Exception:\n", - " # The Wikipedia API is a little flaky, so we just skip over pages that fail to load\n", - " continue\n", - " content = page.content\n", - " title = page.title\n", - " search_results.append(WikipediaSearchResult(content=content, title=title))\n", - " return search_results" - ] + "source": "@dataclass\nclass WikipediaSearchResult(SearchResult):\n title: str\n\n\nclass WikipediaSearchTool(SearchTool):\n def __init__(self, truncate_to_n_tokens: int | None = 5000):\n self.truncate_to_n_tokens = truncate_to_n_tokens\n if truncate_to_n_tokens is not None:\n self.tokenizer = Anthropic().get_tokenizer()\n\n def raw_search(self, query: str, n_search_results_to_use: int) -> list[WikipediaSearchResult]:\n search_results = self._search(query, n_search_results_to_use)\n return search_results\n\n def process_raw_search_results(self, results: list[WikipediaSearchResult]) -> list[str]:\n processed_search_results = [\n f\"Page Title: {result.title.strip()}\\nPage Content:\\n{self.truncate_page_content(result.content)}\"\n for result in results\n ]\n return processed_search_results\n\n def truncate_page_content(self, page_content: str) -> str:\n if self.truncate_to_n_tokens is None:\n return page_content.strip()\n else:\n return self.tokenizer.decode(\n self.tokenizer.encode(page_content).ids[: self.truncate_to_n_tokens]\n ).strip()\n\n def _search(self, query: str, n_search_results_to_use: int) -> list[WikipediaSearchResult]:\n results: list[str] = wikipedia.search(query)\n search_results: list[WikipediaSearchResult] = []\n for result in results:\n if len(search_results) >= n_search_results_to_use:\n break\n try:\n page = wikipedia.page(result)\n print(page.url)\n except wikipedia.exceptions.WikipediaException:\n # The Wikipedia API is a little flaky, so we just skip over pages that fail to load\n continue\n content = page.content\n title = page.title\n search_results.append(WikipediaSearchResult(content=content, title=title))\n return search_results" }, { "cell_type": "code", @@ -277,7 +230,7 @@ "outputs": [], "source": [ "def extract_between_tags(tag: str, string: str, strip: bool = True) -> list[str]:\n", - " ext_list = re.findall(f\"<{tag}\\s?>(.+?)\", string, re.DOTALL)\n", + " ext_list = re.findall(rf\"<{tag}\\s?>(.+?)\", string, re.DOTALL)\n", " if strip:\n", " ext_list = [e.strip() for e in ext_list]\n", " return ext_list\n", @@ -292,7 +245,7 @@ " # Helper methods\n", " def _search_query_stop(\n", " self, partial_completion: str, n_search_results_to_use: int\n", - " ) -> Tuple[list[SearchResult], str]:\n", + " ) -> tuple[list[SearchResult], str]:\n", " search_query = extract_between_tags(\"search_query\", partial_completion + \"\")\n", " if search_query is None:\n", " raise Exception(\n", @@ -309,11 +262,13 @@ " query: str,\n", " model: str,\n", " n_search_results_to_use: int = 3,\n", - " stop_sequences: list[str] = [HUMAN_PROMPT],\n", + " stop_sequences: list[str] = None,\n", " max_tokens_to_sample: int = 1000,\n", " max_searches_to_try: int = 5,\n", " temperature: float = 1.0,\n", " ) -> tuple[list[SearchResult], str]:\n", + " if stop_sequences is None:\n", + " stop_sequences = [HUMAN_PROMPT]\n", " prompt = (\n", " f\"{HUMAN_PROMPT} {wikipedia_prompt} {retrieval_prompt.format(query=query)}{AI_PROMPT}\"\n", " )\n", @@ -355,11 +310,13 @@ " query: str,\n", " model: str,\n", " n_search_results_to_use: int = 3,\n", - " stop_sequences: list[str] = [HUMAN_PROMPT],\n", + " stop_sequences: list[str] = None,\n", " max_tokens_to_sample: int = 1000,\n", " max_searches_to_try: int = 5,\n", " temperature: float = 1.0,\n", " ) -> str:\n", + " if stop_sequences is None:\n", + " stop_sequences = [HUMAN_PROMPT]\n", " _, retrieval_response = self.retrieve(\n", " query,\n", " model=model,\n", @@ -593,4 +550,4 @@ }, "nbformat": 4, "nbformat_minor": 2 -} +} \ No newline at end of file diff --git a/third_party/WolframAlpha/using_llm_api.ipynb b/third_party/WolframAlpha/using_llm_api.ipynb index c514bbab..6791722f 100644 --- a/third_party/WolframAlpha/using_llm_api.ipynb +++ b/third_party/WolframAlpha/using_llm_api.ipynb @@ -22,10 +22,11 @@ "metadata": {}, "outputs": [], "source": [ - "from anthropic import Anthropic\n", - "import requests\n", - "import urllib.parse\n", "import json\n", + "import urllib.parse\n", + "\n", + "import requests\n", + "from anthropic import Anthropic\n", "\n", "client = Anthropic()\n", "\n", @@ -47,44 +48,7 @@ "execution_count": null, "metadata": {}, "outputs": [], - "source": [ - "import urllib.parse\n", - "import requests\n", - "\n", - "\n", - "def wolfram_alpha_query(query):\n", - " # URL-encode the query\n", - " encoded_query = urllib.parse.quote(query)\n", - "\n", - " # Make a request to the Wolfram Alpha LLM API\n", - " url = (\n", - " f\"https://www.wolframalpha.com/api/v1/llm-api?input={encoded_query}&appid={WOLFRAM_APP_ID}\"\n", - " )\n", - " response = requests.get(url)\n", - "\n", - " if response.status_code == 200:\n", - " return response.text\n", - " else:\n", - " return f\"Error: {response.status_code}: {response.text}\"\n", - "\n", - "\n", - "tools = [\n", - " {\n", - " \"name\": \"wolfram_alpha\",\n", - " \"description\": \"A tool that allows querying the Wolfram Alpha knowledge base. Useful for mathematical calculations, scientific data, and general knowledge questions.\",\n", - " \"input_schema\": {\n", - " \"type\": \"object\",\n", - " \"properties\": {\n", - " \"search_query\": {\n", - " \"type\": \"string\",\n", - " \"description\": \"The query to send to the Wolfram Alpha API.\",\n", - " }\n", - " },\n", - " \"required\": [\"query\"],\n", - " },\n", - " }\n", - "]" - ] + "source": "import urllib.parse\n\nimport requests\n\n\ndef wolfram_alpha_query(query):\n # URL-encode the query\n encoded_query = urllib.parse.quote(query)\n\n # Make a request to the Wolfram Alpha LLM API\n url = (\n f\"https://www.wolframalpha.com/api/v1/llm-api?input={encoded_query}&appid={WOLFRAM_APP_ID}\"\n )\n response = requests.get(url, timeout=30)\n\n if response.status_code == 200:\n return response.text\n else:\n return f\"Error: {response.status_code}: {response.text}\"\n\n\ntools = [\n {\n \"name\": \"wolfram_alpha\",\n \"description\": \"A tool that allows querying the Wolfram Alpha knowledge base. Useful for mathematical calculations, scientific data, and general knowledge questions.\",\n \"input_schema\": {\n \"type\": \"object\",\n \"properties\": {\n \"search_query\": {\n \"type\": \"string\",\n \"description\": \"The query to send to the Wolfram Alpha API.\",\n }\n },\n \"required\": [\"query\"],\n },\n }\n]" }, { "cell_type": "markdown", @@ -329,4 +293,4 @@ }, "nbformat": 4, "nbformat_minor": 2 -} +} \ No newline at end of file diff --git a/tool_evaluation/tool_evaluation.ipynb b/tool_evaluation/tool_evaluation.ipynb index 5ece2fbe..1eca6482 100644 --- a/tool_evaluation/tool_evaluation.ipynb +++ b/tool_evaluation/tool_evaluation.ipynb @@ -11,20 +11,10 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "metadata": {}, "outputs": [], - "source": [ - "import json\n", - "import re\n", - "import time\n", - "import traceback\n", - "import xml.etree.ElementTree as ET\n", - "from pathlib import Path\n", - "from typing import Any, Dict, List, Tuple\n", - "\n", - "from anthropic import Anthropic" - ] + "source": "import json\nimport re\nimport time\nimport traceback\nimport xml.etree.ElementTree as ET # noqa: S314\nfrom pathlib import Path\nfrom typing import Any\n\nfrom anthropic import Anthropic" }, { "cell_type": "markdown", @@ -64,7 +54,7 @@ " - Comment on any errors encountered during tool usage: Did the tool fail to execute? Did the tool return too many tokens?\n", " - Identify specific areas for improvement and explain WHY they would help\n", " - Be specific and actionable in your suggestions\n", - " \n", + "\n", "Response Requirements:\n", "- Your response should be concise and directly address what was asked\n", "- Always wrap your final response in tags\n", @@ -84,80 +74,10 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "metadata": {}, "outputs": [], - "source": [ - "client = Anthropic()\n", - "model = \"claude-sonnet-4-5\"\n", - "\n", - "\n", - "def agent_loop(prompt: str, tools: List[Dict[str, Any]] = None) -> Tuple[str, Dict[str, Any]]:\n", - " \"\"\"Simplified agent class for tool evaluation\"\"\"\n", - " messages = [{\"role\": \"user\", \"content\": prompt}]\n", - "\n", - " response = client.messages.create(\n", - " model=model,\n", - " max_tokens=4096,\n", - " system=EVALUATION_PROMPT,\n", - " messages=messages,\n", - " tools=tools,\n", - " )\n", - "\n", - " messages.append({\"role\": \"assistant\", \"content\": response.content})\n", - "\n", - " # Track tool calls with timing\n", - " tool_metrics = {} # {tool_name: {\"count\": N, \"durations\": [X1, X2, ...]}}\n", - "\n", - " def _prepare_tool_result(tool_use_id, tool_result):\n", - " return {\n", - " \"role\": \"user\",\n", - " \"content\": [\n", - " {\n", - " \"type\": \"tool_result\",\n", - " \"tool_use_id\": tool_use_id,\n", - " \"content\": tool_result,\n", - " }\n", - " ],\n", - " }\n", - "\n", - " while response.stop_reason == \"tool_use\":\n", - " tool_use = next(block for block in response.content if block.type == \"tool_use\")\n", - " tool_name = tool_use.name\n", - "\n", - " tool_start_ts = time.time()\n", - " try:\n", - " tool_response = eval(\n", - " f\"{tool_name}(**tool_use.input)\"\n", - " ) # Call the tool function with its input\n", - " except Exception as e:\n", - " tool_response = f\"Error executing tool {tool_name}: {str(e)}\\n\"\n", - " tool_response += traceback.format_exc()\n", - " tool_duration = time.time() - tool_start_ts\n", - "\n", - " # Update tool metrics\n", - " if tool_name not in tool_metrics:\n", - " tool_metrics[tool_name] = {\"count\": 0, \"durations\": []}\n", - " tool_metrics[tool_name][\"count\"] += 1\n", - " tool_metrics[tool_name][\"durations\"].append(tool_duration)\n", - "\n", - " # Prepare tool result and append to messages\n", - " messages.append(_prepare_tool_result(tool_use.id, tool_response))\n", - " response = client.messages.create(\n", - " model=model,\n", - " max_tokens=4096,\n", - " system=EVALUATION_PROMPT,\n", - " messages=messages,\n", - " tools=tools,\n", - " )\n", - " messages.append({\"role\": \"assistant\", \"content\": response.content})\n", - "\n", - " response = next(\n", - " (block.text for block in response.content if hasattr(block, \"text\")),\n", - " None,\n", - " )\n", - " return response, tool_metrics" - ] + "source": "client = Anthropic()\nmodel = \"claude-sonnet-4-5\"\n\n\ndef agent_loop(prompt: str, tools: list[dict[str, Any]] = None) -> tuple[str, dict[str, Any]]:\n \"\"\"Simplified agent class for tool evaluation\"\"\"\n messages = [{\"role\": \"user\", \"content\": prompt}]\n\n response = client.messages.create(\n model=model,\n max_tokens=4096,\n system=EVALUATION_PROMPT,\n messages=messages,\n tools=tools,\n )\n\n messages.append({\"role\": \"assistant\", \"content\": response.content})\n\n # Track tool calls with timing\n tool_metrics = {} # {tool_name: {\"count\": N, \"durations\": [X1, X2, ...]}}\n\n def _prepare_tool_result(tool_use_id, tool_result):\n return {\n \"role\": \"user\",\n \"content\": [\n {\n \"type\": \"tool_result\",\n \"tool_use_id\": tool_use_id,\n \"content\": tool_result,\n }\n ],\n }\n\n while response.stop_reason == \"tool_use\":\n tool_use = next(block for block in response.content if block.type == \"tool_use\")\n tool_name = tool_use.name\n\n tool_start_ts = time.time()\n try:\n # Note: eval is used here for demonstration purposes to dynamically call tool functions.\n # In production, use a safer dispatch mechanism like a dictionary of functions.\n tool_response = eval( # noqa: S307\n f\"{tool_name}(**tool_use.input)\"\n ) # Call the tool function with its input\n except Exception as e:\n tool_response = f\"Error executing tool {tool_name}: {str(e)}\\n\"\n tool_response += traceback.format_exc()\n tool_duration = time.time() - tool_start_ts\n\n # Update tool metrics\n if tool_name not in tool_metrics:\n tool_metrics[tool_name] = {\"count\": 0, \"durations\": []}\n tool_metrics[tool_name][\"count\"] += 1\n tool_metrics[tool_name][\"durations\"].append(tool_duration)\n\n # Prepare tool result and append to messages\n messages.append(_prepare_tool_result(tool_use.id, tool_response))\n response = client.messages.create(\n model=model,\n max_tokens=4096,\n system=EVALUATION_PROMPT,\n messages=messages,\n tools=tools,\n )\n messages.append({\"role\": \"assistant\", \"content\": response.content})\n\n response = next(\n (block.text for block in response.content if hasattr(block, \"text\")),\n None,\n )\n return response, tool_metrics" }, { "cell_type": "markdown", @@ -168,35 +88,10 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, "metadata": {}, "outputs": [], - "source": [ - "def parse_evaluation_file(file_path: Path) -> List[Dict[str, Any]]:\n", - " \"\"\"Parse XML evaluation file and return list of evaluation tasks.\"\"\"\n", - " try:\n", - " tree = ET.parse(file_path)\n", - " root = tree.getroot()\n", - " evaluations = []\n", - "\n", - " # Check for task elements\n", - " tasks = root.findall(\".//task\")\n", - " for task in tasks:\n", - " prompt_elem = task.find(\"prompt\")\n", - " response_elem = task.find(\"response\")\n", - "\n", - " if prompt_elem is not None and response_elem is not None:\n", - " eval_dict = {\n", - " \"prompt\": (prompt_elem.text or \"\").strip(),\n", - " \"response\": (response_elem.text or \"\").strip(),\n", - " }\n", - " evaluations.append(eval_dict)\n", - "\n", - " return evaluations\n", - " except Exception as e:\n", - " print(f\"Error parsing evaluation file {file_path}: {e}\")\n", - " return []" - ] + "source": "def parse_evaluation_file(file_path: Path) -> list[dict[str, Any]]:\n \"\"\"Parse XML evaluation file and return list of evaluation tasks.\"\"\"\n try:\n # Parse trusted local XML evaluation file\n tree = ET.parse(file_path) # noqa: S314\n root = tree.getroot()\n evaluations = []\n\n # Check for task elements\n tasks = root.findall(\".//task\")\n for task in tasks:\n prompt_elem = task.find(\"prompt\")\n response_elem = task.find(\"response\")\n\n if prompt_elem is not None and response_elem is not None:\n eval_dict = {\n \"prompt\": (prompt_elem.text or \"\").strip(),\n \"response\": (response_elem.text or \"\").strip(),\n }\n evaluations.append(eval_dict)\n\n return evaluations\n except Exception as e:\n print(f\"Error parsing evaluation file {file_path}: {e}\")\n return []" }, { "cell_type": "code", @@ -205,8 +100,8 @@ "outputs": [], "source": [ "def evaluate_single_task(\n", - " task: Dict[str, Any], tools: List[Dict[str, Any]], task_index: int\n", - ") -> Dict[str, Any]:\n", + " task: dict[str, Any], tools: list[dict[str, Any]], task_index: int\n", + ") -> dict[str, Any]:\n", " \"\"\"Evaluate a single task with the given tools.\"\"\"\n", " start_time = time.time()\n", "\n", @@ -285,7 +180,7 @@ "\"\"\"\n", "\n", "\n", - "def run_evaluation(eval_path: str, tools: List[Dict[str, Any]]) -> str:\n", + "def run_evaluation(eval_path: str, tools: list[dict[str, Any]]) -> str:\n", " \"\"\"\n", " Run evaluation with provided tools using a simple loop.\n", "\n", @@ -337,7 +232,7 @@ " summary=result[\"summary\"] or \"N/A\",\n", " feedback=result[\"feedback\"] or \"N/A\",\n", " )\n", - " for task, result in zip(tasks, results)\n", + " for task, result in zip(tasks, results, strict=False)\n", " ]\n", " )\n", " # Join all sections into final report\n", @@ -353,38 +248,10 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": null, "metadata": {}, "outputs": [], - "source": [ - "def calculator(expression: str) -> str:\n", - " \"\"\"A basic calculator that performs arithmetic operations.\"\"\"\n", - " try:\n", - " result = eval(expression, {\"__builtins__\": {}}, {})\n", - " return str(result)\n", - " except Exception as e:\n", - " return f\"Error: {str(e)}\"\n", - "\n", - "\n", - "# Define the tool schema for the calculator\n", - "calculator_tool = {\n", - " \"name\": \"calculator\",\n", - " \"description\": \"\", # An unhelpful tool description.\n", - " \"input_schema\": {\n", - " \"type\": \"object\",\n", - " \"properties\": {\n", - " \"expression\": {\n", - " \"type\": \"string\",\n", - " \"description\": \"\", # An unhelpful schema description.\n", - " }\n", - " },\n", - " \"required\": [\"expression\"],\n", - " },\n", - "}\n", - "\n", - "# Set the tools list\n", - "tools = [calculator_tool]" - ] + "source": "def calculator(expression: str) -> str:\n \"\"\"A basic calculator that performs arithmetic operations.\"\"\"\n try:\n # Note: eval with restricted builtins is used here for demonstration.\n # In production, use a safer alternative like a math expression parser.\n result = eval(expression, {\"__builtins__\": {}}, {}) # noqa: S307\n return str(result)\n except Exception as e:\n return f\"Error: {str(e)}\"\n\n\n# Define the tool schema for the calculator\ncalculator_tool = {\n \"name\": \"calculator\",\n \"description\": \"\", # An unhelpful tool description.\n \"input_schema\": {\n \"type\": \"object\",\n \"properties\": {\n \"expression\": {\n \"type\": \"string\",\n \"description\": \"\", # An unhelpful schema description.\n }\n },\n \"required\": [\"expression\"],\n },\n}\n\n# Set the tools list\ntools = [calculator_tool]" }, { "cell_type": "markdown", @@ -929,4 +796,4 @@ }, "nbformat": 4, "nbformat_minor": 4 -} +} \ No newline at end of file diff --git a/tool_use/calculator_tool.ipynb b/tool_use/calculator_tool.ipynb index ce24b3be..4a360f20 100644 --- a/tool_use/calculator_tool.ipynb +++ b/tool_use/calculator_tool.ipynb @@ -50,42 +50,10 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "metadata": {}, "outputs": [], - "source": [ - "import re\n", - "\n", - "\n", - "def calculate(expression):\n", - " # Remove any non-digit or non-operator characters from the expression\n", - " expression = re.sub(r\"[^0-9+\\-*/().]\", \"\", expression)\n", - "\n", - " try:\n", - " # Evaluate the expression using the built-in eval() function\n", - " result = eval(expression)\n", - " return str(result)\n", - " except (SyntaxError, ZeroDivisionError, NameError, TypeError, OverflowError):\n", - " return \"Error: Invalid expression\"\n", - "\n", - "\n", - "tools = [\n", - " {\n", - " \"name\": \"calculator\",\n", - " \"description\": \"A simple calculator that performs basic arithmetic operations.\",\n", - " \"input_schema\": {\n", - " \"type\": \"object\",\n", - " \"properties\": {\n", - " \"expression\": {\n", - " \"type\": \"string\",\n", - " \"description\": \"The mathematical expression to evaluate (e.g., '2 + 3 * 4').\",\n", - " }\n", - " },\n", - " \"required\": [\"expression\"],\n", - " },\n", - " }\n", - "]" - ] + "source": "import re\n\n\ndef calculate(expression):\n # Remove any non-digit or non-operator characters from the expression\n expression = re.sub(r\"[^0-9+\\-*/().]\", \"\", expression)\n\n try:\n # Evaluate the expression using the built-in eval() function\n # Note: eval is used here for demonstration purposes only.\n # In production, use a safer alternative like ast.literal_eval or a math parser.\n result = eval(expression) # noqa: S307\n return str(result)\n except (SyntaxError, ZeroDivisionError, NameError, TypeError, OverflowError):\n return \"Error: Invalid expression\"\n\n\ntools = [\n {\n \"name\": \"calculator\",\n \"description\": \"A simple calculator that performs basic arithmetic operations.\",\n \"input_schema\": {\n \"type\": \"object\",\n \"properties\": {\n \"expression\": {\n \"type\": \"string\",\n \"description\": \"The mathematical expression to evaluate (e.g., '2 + 3 * 4').\",\n }\n },\n \"required\": [\"expression\"],\n },\n }\n]" }, { "cell_type": "markdown", @@ -277,4 +245,4 @@ }, "nbformat": 4, "nbformat_minor": 2 -} +} \ No newline at end of file diff --git a/tool_use/extracting_structured_json.ipynb b/tool_use/extracting_structured_json.ipynb index d72750b7..8f4bec58 100644 --- a/tool_use/extracting_structured_json.ipynb +++ b/tool_use/extracting_structured_json.ipynb @@ -35,10 +35,11 @@ "metadata": {}, "outputs": [], "source": [ - "from anthropic import Anthropic\n", + "import json\n", + "\n", "import requests\n", + "from anthropic import Anthropic\n", "from bs4 import BeautifulSoup\n", - "import json\n", "\n", "client = Anthropic()\n", "MODEL_NAME = \"claude-haiku-4-5\"" @@ -55,88 +56,10 @@ }, { "cell_type": "code", - "execution_count": 61, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "JSON Summary:\n", - "{\n", - " \"author\": \"Anthropic\",\n", - " \"topics\": [\n", - " \"AI policy\",\n", - " \"AI safety\",\n", - " \"third-party testing\"\n", - " ],\n", - " \"summary\": \"The article argues that the AI sector needs effective third-party testing for frontier AI systems to avoid societal harm, whether deliberate or accidental. It discusses what third-party testing looks like, why it's needed, and the research Anthropic has done to arrive at this policy position. The article states that such a testing regime is necessary because frontier AI systems like large-scale generative models don't fit neatly into use-case and sector-specific frameworks, and can pose risks of serious misuse or AI-caused accidents. Though Anthropic and other organizations have implemented self-governance systems, the article argues that industry-wide third-party testing is ultimately needed to be broadly trusted. The article outlines key components of an effective third-party testing regime, including identifying national security risks, and discusses how it could be accomplished by a diverse ecosystem of organizations. Anthropic plans to advocate for greater funding and public sector infrastructure for AI testing and evaluation, as well as developing tests for specific capabilities.\",\n", - " \"coherence\": 90,\n", - " \"persuasion\": 0.8\n", - "}\n" - ] - } - ], - "source": [ - "tools = [\n", - " {\n", - " \"name\": \"print_summary\",\n", - " \"description\": \"Prints a summary of the article.\",\n", - " \"input_schema\": {\n", - " \"type\": \"object\",\n", - " \"properties\": {\n", - " \"author\": {\"type\": \"string\", \"description\": \"Name of the article author\"},\n", - " \"topics\": {\n", - " \"type\": \"array\",\n", - " \"items\": {\"type\": \"string\"},\n", - " \"description\": 'Array of topics, e.g. [\"tech\", \"politics\"]. Should be as specific as possible, and can overlap.',\n", - " },\n", - " \"summary\": {\n", - " \"type\": \"string\",\n", - " \"description\": \"Summary of the article. One or two paragraphs max.\",\n", - " },\n", - " \"coherence\": {\n", - " \"type\": \"integer\",\n", - " \"description\": \"Coherence of the article's key points, 0-100 (inclusive)\",\n", - " },\n", - " \"persuasion\": {\n", - " \"type\": \"number\",\n", - " \"description\": \"Article's persuasion score, 0.0-1.0 (inclusive)\",\n", - " },\n", - " },\n", - " \"required\": [\"author\", \"topics\", \"summary\", \"coherence\", \"persuasion\", \"counterpoint\"],\n", - " },\n", - " }\n", - "]\n", - "\n", - "url = \"https://www.anthropic.com/news/third-party-testing\"\n", - "response = requests.get(url)\n", - "soup = BeautifulSoup(response.text, \"html.parser\")\n", - "article = \" \".join([p.text for p in soup.find_all(\"p\")])\n", - "\n", - "query = f\"\"\"\n", - "
\n", - "{article}\n", - "
\n", - "\n", - "Use the `print_summary` tool.\n", - "\"\"\"\n", - "\n", - "response = client.messages.create(\n", - " model=MODEL_NAME, max_tokens=4096, tools=tools, messages=[{\"role\": \"user\", \"content\": query}]\n", - ")\n", - "json_summary = None\n", - "for content in response.content:\n", - " if content.type == \"tool_use\" and content.name == \"print_summary\":\n", - " json_summary = content.input\n", - " break\n", - "\n", - "if json_summary:\n", - " print(\"JSON Summary:\")\n", - " print(json.dumps(json_summary, indent=2))\n", - "else:\n", - " print(\"No JSON summary found in the response.\")" - ] + "outputs": [], + "source": "tools = [\n {\n \"name\": \"print_summary\",\n \"description\": \"Prints a summary of the article.\",\n \"input_schema\": {\n \"type\": \"object\",\n \"properties\": {\n \"author\": {\"type\": \"string\", \"description\": \"Name of the article author\"},\n \"topics\": {\n \"type\": \"array\",\n \"items\": {\"type\": \"string\"},\n \"description\": 'Array of topics, e.g. [\"tech\", \"politics\"]. Should be as specific as possible, and can overlap.',\n },\n \"summary\": {\n \"type\": \"string\",\n \"description\": \"Summary of the article. One or two paragraphs max.\",\n },\n \"coherence\": {\n \"type\": \"integer\",\n \"description\": \"Coherence of the article's key points, 0-100 (inclusive)\",\n },\n \"persuasion\": {\n \"type\": \"number\",\n \"description\": \"Article's persuasion score, 0.0-1.0 (inclusive)\",\n },\n },\n \"required\": [\"author\", \"topics\", \"summary\", \"coherence\", \"persuasion\", \"counterpoint\"],\n },\n }\n]\n\nurl = \"https://www.anthropic.com/news/third-party-testing\"\nresponse = requests.get(url, timeout=30)\nsoup = BeautifulSoup(response.text, \"html.parser\")\narticle = \" \".join([p.text for p in soup.find_all(\"p\")])\n\nquery = f\"\"\"\n
\n{article}\n
\n\nUse the `print_summary` tool.\n\"\"\"\n\nresponse = client.messages.create(\n model=MODEL_NAME, max_tokens=4096, tools=tools, messages=[{\"role\": \"user\", \"content\": query}]\n)\njson_summary = None\nfor content in response.content:\n if content.type == \"tool_use\" and content.name == \"print_summary\":\n json_summary = content.input\n break\n\nif json_summary:\n print(\"JSON Summary:\")\n print(json.dumps(json_summary, indent=2))\nelse:\n print(\"No JSON summary found in the response.\")" }, { "cell_type": "markdown", @@ -496,4 +419,4 @@ }, "nbformat": 4, "nbformat_minor": 2 -} +} \ No newline at end of file diff --git a/tool_use/memory_demo/__init__.py b/tool_use/memory_demo/__init__.py index 207dea57..c45baeb7 100644 --- a/tool_use/memory_demo/__init__.py +++ b/tool_use/memory_demo/__init__.py @@ -1,10 +1,10 @@ """Memory cookbook demo package.""" from .demo_helpers import ( + execute_tool, + print_context_management_info, run_conversation_loop, run_conversation_turn, - print_context_management_info, - execute_tool, ) __all__ = [ diff --git a/tool_use/memory_demo/sample_code/api_client_v1.py b/tool_use/memory_demo/sample_code/api_client_v1.py index 871bacd9..1c3c0c61 100644 --- a/tool_use/memory_demo/sample_code/api_client_v1.py +++ b/tool_use/memory_demo/sample_code/api_client_v1.py @@ -4,7 +4,7 @@ """ import asyncio -from typing import List, Dict, Any +from typing import Any import aiohttp @@ -17,7 +17,7 @@ def __init__(self, base_url: str): self.responses = [] # BUG: Shared state accessed from multiple coroutines! self.error_count = 0 # BUG: Race condition on counter increment! - async def fetch_endpoint(self, session: aiohttp.ClientSession, endpoint: str) -> Dict[str, Any]: + async def fetch_endpoint(self, session: aiohttp.ClientSession, endpoint: str) -> dict[str, Any]: """Fetch a single endpoint.""" url = f"{self.base_url}/{endpoint}" try: @@ -34,7 +34,7 @@ async def fetch_endpoint(self, session: aiohttp.ClientSession, endpoint: str) -> "error": str(e), } - async def fetch_all(self, endpoints: List[str]) -> List[Dict[str, Any]]: + async def fetch_all(self, endpoints: list[str]) -> list[dict[str, Any]]: """ Fetch multiple endpoints concurrently. @@ -57,7 +57,7 @@ async def fetch_all(self, endpoints: List[str]) -> List[Dict[str, Any]]: return self.responses - def get_summary(self) -> Dict[str, Any]: + def get_summary(self) -> dict[str, Any]: """Get summary statistics.""" return { "total_responses": len(self.responses), diff --git a/tool_use/memory_demo/sample_code/cache_manager.py b/tool_use/memory_demo/sample_code/cache_manager.py index 32c0ff22..3bf3f464 100644 --- a/tool_use/memory_demo/sample_code/cache_manager.py +++ b/tool_use/memory_demo/sample_code/cache_manager.py @@ -4,7 +4,7 @@ """ from datetime import datetime -from typing import Dict, List, Optional, Any +from typing import Any class CacheManager: @@ -16,7 +16,7 @@ def __init__(self): def add_items( self, key: str, - items: List[str] = [], # BUG: Mutable default argument! + items: list[str] = None, # BUG: Mutable default argument! ) -> None: """ Add items to cache. @@ -25,10 +25,12 @@ def add_items( This is one of Python's classic gotchas. """ # The items list is shared across ALL calls that don't provide items + if items is None: + items = [] items.append(f"Added at {datetime.now()}") self.cache[key] = items - def add_items_fixed(self, key: str, items: Optional[List[str]] = None) -> None: + def add_items_fixed(self, key: str, items: list[str] | None = None) -> None: """Add items with proper default handling.""" if items is None: items = [] @@ -39,13 +41,15 @@ def add_items_fixed(self, key: str, items: Optional[List[str]] = None) -> None: def merge_configs( self, name: str, - overrides: Dict[str, Any] = {}, # BUG: Mutable default! - ) -> Dict[str, Any]: + overrides: dict[str, Any] = None, # BUG: Mutable default! + ) -> dict[str, Any]: """ Merge configuration with overrides. BUG: The default dict is shared across all calls! """ + if overrides is None: + overrides = {} defaults = {"timeout": 30, "retries": 3, "cache_enabled": True} # This modifies the SHARED overrides dict @@ -53,8 +57,8 @@ def merge_configs( return overrides def merge_configs_fixed( - self, name: str, overrides: Optional[Dict[str, Any]] = None - ) -> Dict[str, Any]: + self, name: str, overrides: dict[str, Any] | None = None + ) -> dict[str, Any]: """Merge configs properly.""" if overrides is None: overrides = {} @@ -71,14 +75,16 @@ class DataProcessor: def process_batch( self, - data: List[int], - filters: List[str] = [], # BUG: Mutable default! - ) -> List[int]: + data: list[int], + filters: list[str] = None, # BUG: Mutable default! + ) -> list[int]: """ Process data with optional filters. BUG: filters list is shared across calls! """ + if filters is None: + filters = [] filters.append("default_filter") # Modifies shared list! result = [] diff --git a/tool_use/memory_demo/sample_code/data_processor_v1.py b/tool_use/memory_demo/sample_code/data_processor_v1.py index 97a480ea..271b5e21 100644 --- a/tool_use/memory_demo/sample_code/data_processor_v1.py +++ b/tool_use/memory_demo/sample_code/data_processor_v1.py @@ -7,7 +7,7 @@ import threading from concurrent.futures import ThreadPoolExecutor from pathlib import Path -from typing import List, Dict, Any +from typing import Any class DataProcessor: @@ -20,10 +20,10 @@ def __init__(self, max_workers: int = 5): self.errors = {} # BUG: Shared dict without locking self.lock = threading.Lock() # Available but not used! - def process_file(self, file_path: str) -> Dict[str, Any]: + def process_file(self, file_path: str) -> dict[str, Any]: """Process a single file.""" try: - with open(file_path, "r") as f: + with open(file_path) as f: data = json.load(f) # Simulate some processing @@ -38,7 +38,7 @@ def process_file(self, file_path: str) -> Dict[str, Any]: except Exception as e: return {"file": file_path, "error": str(e)} - def process_batch(self, file_paths: List[str]) -> List[Dict[str, Any]]: + def process_batch(self, file_paths: list[str]) -> list[dict[str, Any]]: """ Process multiple files concurrently. @@ -66,7 +66,7 @@ def process_batch(self, file_paths: List[str]) -> List[Dict[str, Any]]: return self.results - def get_statistics(self) -> Dict[str, Any]: + def get_statistics(self) -> dict[str, Any]: """ Get processing statistics. @@ -121,7 +121,7 @@ def set(self, key: str, value: Any): """Set in cache - RACE CONDITION on dict modification.""" self.cache[key] = value # BUG: Dict access not synchronized! - def get_stats(self) -> Dict[str, Any]: + def get_stats(self) -> dict[str, Any]: """Get cache statistics - may be inconsistent.""" total = self.hit_count + self.miss_count return { diff --git a/tool_use/memory_demo/sample_code/sql_query_builder.py b/tool_use/memory_demo/sample_code/sql_query_builder.py index 668b544f..b146ad28 100644 --- a/tool_use/memory_demo/sample_code/sql_query_builder.py +++ b/tool_use/memory_demo/sample_code/sql_query_builder.py @@ -3,13 +3,11 @@ Demonstrates dangerous string formatting in SQL queries. """ -from typing import List, Optional - class UserDatabase: """Simple database interface (mock).""" - def execute(self, query: str) -> List[dict]: + def execute(self, query: str) -> list[dict]: """Mock execute - just returns the query for inspection.""" print(f"Executing: {query}") return [] @@ -21,7 +19,7 @@ class QueryBuilder: def __init__(self, db: UserDatabase): self.db = db - def get_user_by_name(self, username: str) -> Optional[dict]: + def get_user_by_name(self, username: str) -> dict | None: """ Get user by username. @@ -33,7 +31,7 @@ def get_user_by_name(self, username: str) -> Optional[dict]: results = self.db.execute(query) return results[0] if results else None - def get_user_by_name_safe(self, username: str) -> Optional[dict]: + def get_user_by_name_safe(self, username: str) -> dict | None: """Safe version using parameterized queries.""" # Use parameterized queries (this is pseudo-code for the concept) query = "SELECT * FROM users WHERE username = ?" @@ -41,7 +39,7 @@ def get_user_by_name_safe(self, username: str) -> Optional[dict]: print(f"Safe query with parameter: {query}, params: ({username},)") return None - def search_users(self, search_term: str, limit: int = 10) -> List[dict]: + def search_users(self, search_term: str, limit: int = 10) -> list[dict]: """ Search users by term. @@ -63,7 +61,7 @@ def delete_user(self, user_id: str) -> bool: self.db.execute(query) return True - def get_users_by_role(self, role: str, order_by: str = "name") -> List[dict]: + def get_users_by_role(self, role: str, order_by: str = "name") -> list[dict]: """ Get users by role. diff --git a/tool_use/memory_demo/sample_code/web_scraper_v1.py b/tool_use/memory_demo/sample_code/web_scraper_v1.py index d907104a..cc805e03 100644 --- a/tool_use/memory_demo/sample_code/web_scraper_v1.py +++ b/tool_use/memory_demo/sample_code/web_scraper_v1.py @@ -4,7 +4,7 @@ """ from concurrent.futures import ThreadPoolExecutor, as_completed -from typing import List, Dict, Any +from typing import Any import requests @@ -17,7 +17,7 @@ def __init__(self, max_workers: int = 10): self.results = [] # BUG: Shared mutable state accessed by multiple threads! self.failed_urls = [] # BUG: Another race condition! - def fetch_url(self, url: str) -> Dict[str, Any]: + def fetch_url(self, url: str) -> dict[str, Any]: """Fetch a single URL and return the result.""" try: response = requests.get(url, timeout=5) @@ -30,7 +30,7 @@ def fetch_url(self, url: str) -> Dict[str, Any]: except requests.exceptions.RequestException as e: return {"url": url, "error": str(e)} - def scrape_urls(self, urls: List[str]) -> List[Dict[str, Any]]: + def scrape_urls(self, urls: list[str]) -> list[dict[str, Any]]: """ Scrape multiple URLs concurrently. @@ -51,7 +51,7 @@ def scrape_urls(self, urls: List[str]) -> List[Dict[str, Any]]: return self.results - def get_stats(self) -> Dict[str, int]: + def get_stats(self) -> dict[str, int]: """Get scraping statistics.""" return { "total_results": len(self.results), diff --git a/tool_use/tool_choice.ipynb b/tool_use/tool_choice.ipynb index 20b1683e..c7f69d9d 100644 --- a/tool_use/tool_choice.ipynb +++ b/tool_use/tool_choice.ipynb @@ -98,7 +98,7 @@ " messages = [{\"role\": \"user\", \"content\": user_query}]\n", "\n", " system_prompt = f\"\"\"\n", - " Answer as many questions as you can using your existing knowledge. \n", + " Answer as many questions as you can using your existing knowledge.\n", " Only search the web for queries that you can not confidently answer.\n", " Today's date is {date.today().strftime(\"%B %d %Y\")}\n", " If you think a user's question involves something in the future that hasn't happened yet, use the search tool.\n", @@ -488,7 +488,7 @@ "source": [ "def analyze_tweet_sentiment(query):\n", " prompt = f\"\"\"\n", - " Analyze the sentiment in the following tweet: \n", + " Analyze the sentiment in the following tweet:\n", " {query}\n", " \"\"\"\n", "\n", @@ -580,7 +580,7 @@ "\n", "system_prompt = \"\"\"\n", "All your communication with a user is done via text message.\n", - "Only call tools when you have enough information to accurately call them. \n", + "Only call tools when you have enough information to accurately call them.\n", "Do not call the get_customer_info tool until a user has provided you with their username. This is important.\n", "If you do not know a user's username, simply ask a user for their username.\n", "\"\"\"\n", diff --git a/tool_use/tool_search_with_embeddings.ipynb b/tool_use/tool_search_with_embeddings.ipynb index f7444f9c..770b7624 100644 --- a/tool_use/tool_search_with_embeddings.ipynb +++ b/tool_use/tool_search_with_embeddings.ipynb @@ -90,14 +90,15 @@ } ], "source": [ - "import anthropic\n", - "from sentence_transformers import SentenceTransformer\n", - "import numpy as np\n", "import json\n", - "from typing import List, Dict, Any\n", - "from dotenv import load_dotenv\n", "import random\n", "from datetime import datetime, timedelta\n", + "from typing import Any\n", + "\n", + "import anthropic\n", + "import numpy as np\n", + "from dotenv import load_dotenv\n", + "from sentence_transformers import SentenceTransformer\n", "\n", "# Load environment variables from .env file\n", "load_dotenv()\n", @@ -333,7 +334,7 @@ } ], "source": [ - "def tool_to_text(tool: Dict[str, Any]) -> str:\n", + "def tool_to_text(tool: dict[str, Any]) -> str:\n", " \"\"\"\n", " Convert a tool definition into a text representation for embedding.\n", " Combines the tool name, description, and parameter information.\n", @@ -350,9 +351,7 @@ " for param_name, param_info in params.items():\n", " param_desc = param_info.get(\"description\", \"\")\n", " param_type = param_info.get(\"type\", \"\")\n", - " param_descriptions.append(\n", - " f\"{param_name} ({param_type}): {param_desc}\"\n", - " )\n", + " param_descriptions.append(f\"{param_name} ({param_type}): {param_desc}\")\n", "\n", " if param_descriptions:\n", " text_parts.append(\"Parameters: \" + \", \".join(param_descriptions))\n", @@ -441,7 +440,7 @@ } ], "source": [ - "def search_tools(query: str, top_k: int = 5) -> List[Dict[str, Any]]:\n", + "def search_tools(query: str, top_k: int = 5) -> list[dict[str, Any]]:\n", " \"\"\"\n", " Search for tools using semantic similarity.\n", "\n", @@ -465,9 +464,7 @@ " # Return the corresponding tools with their scores\n", " results = []\n", " for idx in top_indices:\n", - " results.append(\n", - " {\"tool\": TOOL_LIBRARY[idx], \"similarity_score\": float(similarities[idx])}\n", - " )\n", + " results.append({\"tool\": TOOL_LIBRARY[idx], \"similarity_score\": float(similarities[idx])})\n", "\n", " return results\n", "\n", @@ -563,7 +560,7 @@ } ], "source": [ - "def handle_tool_search(query: str, top_k: int = 5) -> List[Dict[str, Any]]:\n", + "def handle_tool_search(query: str, top_k: int = 5) -> list[dict[str, Any]]:\n", " \"\"\"\n", " Handle a tool_search invocation and return tool references.\n", "\n", @@ -574,16 +571,13 @@ "\n", " # Create tool_reference objects instead of full definitions\n", " tool_references = [\n", - " {\"type\": \"tool_reference\", \"tool_name\": result[\"tool\"][\"name\"]}\n", - " for result in results\n", + " {\"type\": \"tool_reference\", \"tool_name\": result[\"tool\"][\"name\"]} for result in results\n", " ]\n", "\n", " print(f\"\\n🔍 Tool search: '{query}'\")\n", " print(f\" Found {len(tool_references)} tools:\")\n", " for i, result in enumerate(results, 1):\n", - " print(\n", - " f\" {i}. {result['tool']['name']} (similarity: {result['similarity_score']:.3f})\"\n", - " )\n", + " print(f\" {i}. {result['tool']['name']} (similarity: {result['similarity_score']:.3f})\")\n", "\n", " return tool_references\n", "\n", @@ -618,7 +612,7 @@ } ], "source": [ - "def mock_tool_execution(tool_name: str, tool_input: Dict[str, Any]) -> str:\n", + "def mock_tool_execution(tool_name: str, tool_input: dict[str, Any]) -> str:\n", " \"\"\"\n", " Generate realistic mock responses for tool executions.\n", "\n", @@ -633,11 +627,7 @@ " if tool_name == \"get_weather\":\n", " location = tool_input.get(\"location\", \"Unknown\")\n", " unit = tool_input.get(\"unit\", \"fahrenheit\")\n", - " temp = (\n", - " random.randint(15, 30)\n", - " if unit == \"celsius\"\n", - " else random.randint(60, 85)\n", - " )\n", + " temp = random.randint(15, 30) if unit == \"celsius\" else random.randint(60, 85)\n", " conditions = random.choice([\"sunny\", \"partly cloudy\", \"cloudy\", \"rainy\"])\n", " return json.dumps(\n", " {\n", @@ -661,9 +651,7 @@ " \"date\": date,\n", " \"high\": random.randint(20, 30),\n", " \"low\": random.randint(10, 20),\n", - " \"conditions\": random.choice(\n", - " [\"sunny\", \"cloudy\", \"rainy\", \"partly cloudy\"]\n", - " ),\n", + " \"conditions\": random.choice([\"sunny\", \"cloudy\", \"rainy\", \"partly cloudy\"]),\n", " }\n", " )\n", " return json.dumps({\"location\": location, \"forecast\": forecast})\n", @@ -687,9 +675,7 @@ " (51, 100): \"Moderate\",\n", " (101, 150): \"Unhealthy for Sensitive Groups\",\n", " }\n", - " category = next(\n", - " cat for (low, high), cat in categories.items() if low <= aqi <= high\n", - " )\n", + " category = next(cat for (low, high), cat in categories.items() if low <= aqi <= high)\n", " return json.dumps(\n", " {\n", " \"location\": location,\n", @@ -764,7 +750,7 @@ " for i in range(min(limit, 5)):\n", " news.append(\n", " {\n", - " \"title\": f\"{query} - News Article {i+1}\",\n", + " \"title\": f\"{query} - News Article {i + 1}\",\n", " \"source\": random.choice(\n", " [\n", " \"Bloomberg\",\n", @@ -833,9 +819,9 @@ " user_message: The initial user message\n", " max_turns: Maximum number of conversation turns\n", " \"\"\"\n", - " print(f\"\\n{'='*80}\")\n", + " print(f\"\\n{'=' * 80}\")\n", " print(f\"USER: {user_message}\")\n", - " print(f\"{'='*80}\\n\")\n", + " print(f\"{'=' * 80}\\n\")\n", "\n", " # Initialize conversation with only tool_search available\n", " messages = [{\"role\": \"user\", \"content\": user_message}]\n", @@ -850,9 +836,7 @@ " tools=TOOL_LIBRARY + [TOOL_SEARCH_DEFINITION],\n", " messages=messages,\n", " # IMPORTANT: This beta header enables tool definitions in tool results\n", - " extra_headers={\n", - " \"anthropic-beta\": \"advanced-tool-use-2025-11-20\"\n", - " },\n", + " extra_headers={\"anthropic-beta\": \"advanced-tool-use-2025-11-20\"},\n", " )\n", "\n", " # Add assistant's response to messages\n", @@ -924,7 +908,7 @@ " print(f\"\\nUnexpected stop reason: {response.stop_reason}\")\n", " break\n", "\n", - " print(f\"\\n{'='*80}\\n\")\n", + " print(f\"\\n{'=' * 80}\\n\")\n", "\n", "\n", "print(\"✓ Conversation loop implemented\")" diff --git a/tool_use/tool_use_with_pydantic.ipynb b/tool_use/tool_use_with_pydantic.ipynb index 7129094f..63781be6 100644 --- a/tool_use/tool_use_with_pydantic.ipynb +++ b/tool_use/tool_use_with_pydantic.ipynb @@ -34,7 +34,6 @@ "source": [ "from anthropic import Anthropic\n", "from pydantic import BaseModel, EmailStr, Field\n", - "from typing import Optional\n", "\n", "client = Anthropic()\n", "MODEL_NAME = \"claude-opus-4-1\"" @@ -63,7 +62,7 @@ "class Note(BaseModel):\n", " note: str\n", " author: Author\n", - " tags: Optional[list[str]] = None\n", + " tags: list[str] | None = None\n", " priority: int = Field(ge=1, le=5, default=3)\n", " is_public: bool = False\n", "\n", diff --git a/tool_use/vision_with_tools.ipynb b/tool_use/vision_with_tools.ipynb index ad9423e8..5f05775e 100644 --- a/tool_use/vision_with_tools.ipynb +++ b/tool_use/vision_with_tools.ipynb @@ -37,10 +37,11 @@ "metadata": {}, "outputs": [], "source": [ - "from IPython.display import Image\n", - "from anthropic import Anthropic\n", "import base64\n", "\n", + "from anthropic import Anthropic\n", + "from IPython.display import Image\n", + "\n", "client = Anthropic()\n", "MODEL_NAME = \"claude-opus-4-1\"" ] diff --git a/uv.lock b/uv.lock index 0d400771..db658d5a 100644 --- a/uv.lock +++ b/uv.lock @@ -17,7 +17,7 @@ wheels = [ [[package]] name = "aiohttp" -version = "3.13.1" +version = "3.13.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiohappyeyeballs" }, @@ -28,42 +28,42 @@ dependencies = [ { name = "propcache" }, { name = "yarl" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ba/fa/3ae643cd525cf6844d3dc810481e5748107368eb49563c15a5fb9f680750/aiohttp-3.13.1.tar.gz", hash = "sha256:4b7ee9c355015813a6aa085170b96ec22315dabc3d866fd77d147927000e9464", size = 7835344, upload-time = "2025-10-17T14:03:29.337Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/be/2c/739d03730ffce57d2093e2e611e1541ac9a4b3bb88288c33275058b9ffc2/aiohttp-3.13.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9eefa0a891e85dca56e2d00760945a6325bd76341ec386d3ad4ff72eb97b7e64", size = 742004, upload-time = "2025-10-17T13:59:29.73Z" }, - { url = "https://files.pythonhosted.org/packages/fc/f8/7f5b7f7184d7c80e421dbaecbd13e0b2a0bb8663fd0406864f9a167a438c/aiohttp-3.13.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6c20eb646371a5a57a97de67e52aac6c47badb1564e719b3601bbb557a2e8fd0", size = 495601, upload-time = "2025-10-17T13:59:31.312Z" }, - { url = "https://files.pythonhosted.org/packages/3e/af/fb78d028b9642dd33ff127d9a6a151586f33daff631b05250fecd0ab23f8/aiohttp-3.13.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bfc28038cd86fb1deed5cc75c8fda45c6b0f5c51dfd76f8c63d3d22dc1ab3d1b", size = 491790, upload-time = "2025-10-17T13:59:33.304Z" }, - { url = "https://files.pythonhosted.org/packages/1e/ae/e40e422ee995e4f91f7f087b86304e3dd622d3a5b9ca902a1e94ebf9a117/aiohttp-3.13.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b22eeffca2e522451990c31a36fe0e71079e6112159f39a4391f1c1e259a795", size = 1746350, upload-time = "2025-10-17T13:59:35.158Z" }, - { url = "https://files.pythonhosted.org/packages/28/a5/fe6022bb869bf2d2633b155ed8348d76358c22d5ff9692a15016b2d1019f/aiohttp-3.13.1-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:65782b2977c05ebd78787e3c834abe499313bf69d6b8be4ff9c340901ee7541f", size = 1703046, upload-time = "2025-10-17T13:59:37.077Z" }, - { url = "https://files.pythonhosted.org/packages/5a/a5/c4ef3617d7cdc49f2d5af077f19794946f0f2d94b93c631ace79047361a2/aiohttp-3.13.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:dacba54f9be3702eb866b0b9966754b475e1e39996e29e442c3cd7f1117b43a9", size = 1806161, upload-time = "2025-10-17T13:59:38.837Z" }, - { url = "https://files.pythonhosted.org/packages/ad/45/b87d2430aee7e7d00b24e3dff2c5bd69f21017f6edb19cfd91e514664fc8/aiohttp-3.13.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:aa878da718e8235302c365e376b768035add36b55177706d784a122cb822a6a4", size = 1894546, upload-time = "2025-10-17T13:59:40.741Z" }, - { url = "https://files.pythonhosted.org/packages/e8/a2/79eb466786a7f11a0292c353a8a9b95e88268c48c389239d7531d66dbb48/aiohttp-3.13.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e4b4e607fbd4964d65945a7b9d1e7f98b0d5545736ea613f77d5a2a37ff1e46", size = 1745683, upload-time = "2025-10-17T13:59:42.59Z" }, - { url = "https://files.pythonhosted.org/packages/93/1a/153b0ad694f377e94eacc85338efe03ed4776a396c8bb47bd9227135792a/aiohttp-3.13.1-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0c3db2d0e5477ad561bf7ba978c3ae5f8f78afda70daa05020179f759578754f", size = 1605418, upload-time = "2025-10-17T13:59:45.229Z" }, - { url = "https://files.pythonhosted.org/packages/3f/4e/18605b1bfeb4b00d3396d833647cdb213118e2a96862e5aebee62ad065b4/aiohttp-3.13.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9739d34506fdf59bf2c092560d502aa728b8cdb33f34ba15fb5e2852c35dd829", size = 1722379, upload-time = "2025-10-17T13:59:46.969Z" }, - { url = "https://files.pythonhosted.org/packages/72/13/0a38ad385d547fb283e0e1fe1ff1dff8899bd4ed0aaceeb13ec14abbf136/aiohttp-3.13.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:b902e30a268a85d50197b4997edc6e78842c14c0703450f632c2d82f17577845", size = 1716693, upload-time = "2025-10-17T13:59:49.217Z" }, - { url = "https://files.pythonhosted.org/packages/55/65/7029d7573ab9009adde380052c6130d02c8db52195fda112db35e914fe7b/aiohttp-3.13.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:1bbfc04c8de7def6504cce0a97f9885a5c805fd2395a0634bc10f9d6ecb42524", size = 1784174, upload-time = "2025-10-17T13:59:51.439Z" }, - { url = "https://files.pythonhosted.org/packages/2d/36/fd46e39cb85418e45b0e4a8bfc39651ee0b8f08ea006adf217a221cdb269/aiohttp-3.13.1-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:6941853405a38a5eeb7d9776db77698df373ff7fa8c765cb81ea14a344fccbeb", size = 1593716, upload-time = "2025-10-17T13:59:53.367Z" }, - { url = "https://files.pythonhosted.org/packages/85/b8/188e0cb1be37b4408373171070fda17c3bf9c67c0d3d4fd5ee5b1fa108e1/aiohttp-3.13.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:7764adcd2dc8bd21c8228a53dda2005428498dc4d165f41b6086f0ac1c65b1c9", size = 1799254, upload-time = "2025-10-17T13:59:55.352Z" }, - { url = "https://files.pythonhosted.org/packages/67/ff/fdf768764eb427b0cc9ebb2cebddf990f94d98b430679f8383c35aa114be/aiohttp-3.13.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c09e08d38586fa59e5a2f9626505a0326fadb8e9c45550f029feeb92097a0afc", size = 1738122, upload-time = "2025-10-17T13:59:57.263Z" }, - { url = "https://files.pythonhosted.org/packages/94/84/fce7a4d575943394d7c0e632273838eb6f39de8edf25386017bf5f0de23b/aiohttp-3.13.1-cp311-cp311-win32.whl", hash = "sha256:ce1371675e74f6cf271d0b5530defb44cce713fd0ab733713562b3a2b870815c", size = 430491, upload-time = "2025-10-17T13:59:59.466Z" }, - { url = "https://files.pythonhosted.org/packages/ac/d2/d21b8ab6315a5d588c550ab285b4f02ae363edf012920e597904c5a56608/aiohttp-3.13.1-cp311-cp311-win_amd64.whl", hash = "sha256:77a2f5cc28cf4704cc157be135c6a6cfb38c9dea478004f1c0fd7449cf445c28", size = 454808, upload-time = "2025-10-17T14:00:01.247Z" }, - { url = "https://files.pythonhosted.org/packages/1a/72/d463a10bf29871f6e3f63bcf3c91362dc4d72ed5917a8271f96672c415ad/aiohttp-3.13.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0760bd9a28efe188d77b7c3fe666e6ef74320d0f5b105f2e931c7a7e884c8230", size = 736218, upload-time = "2025-10-17T14:00:03.51Z" }, - { url = "https://files.pythonhosted.org/packages/26/13/f7bccedbe52ea5a6eef1e4ebb686a8d7765319dfd0a5939f4238cb6e79e6/aiohttp-3.13.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7129a424b441c3fe018a414401bf1b9e1d49492445f5676a3aecf4f74f67fcdb", size = 491251, upload-time = "2025-10-17T14:00:05.756Z" }, - { url = "https://files.pythonhosted.org/packages/0c/7c/7ea51b5aed6cc69c873f62548da8345032aa3416336f2d26869d4d37b4a2/aiohttp-3.13.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e1cb04ae64a594f6ddf5cbb024aba6b4773895ab6ecbc579d60414f8115e9e26", size = 490394, upload-time = "2025-10-17T14:00:07.504Z" }, - { url = "https://files.pythonhosted.org/packages/31/05/1172cc4af4557f6522efdee6eb2b9f900e1e320a97e25dffd3c5a6af651b/aiohttp-3.13.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:782d656a641e755decd6bd98d61d2a8ea062fd45fd3ff8d4173605dd0d2b56a1", size = 1737455, upload-time = "2025-10-17T14:00:09.403Z" }, - { url = "https://files.pythonhosted.org/packages/24/3d/ce6e4eca42f797d6b1cd3053cf3b0a22032eef3e4d1e71b9e93c92a3f201/aiohttp-3.13.1-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:f92ad8169767429a6d2237331726c03ccc5f245222f9373aa045510976af2b35", size = 1699176, upload-time = "2025-10-17T14:00:11.314Z" }, - { url = "https://files.pythonhosted.org/packages/25/04/7127ba55653e04da51477372566b16ae786ef854e06222a1c96b4ba6c8ef/aiohttp-3.13.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0e778f634ca50ec005eefa2253856921c429581422d887be050f2c1c92e5ce12", size = 1767216, upload-time = "2025-10-17T14:00:13.668Z" }, - { url = "https://files.pythonhosted.org/packages/b8/3b/43bca1e75847e600f40df829a6b2f0f4e1d4c70fb6c4818fdc09a462afd5/aiohttp-3.13.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:9bc36b41cf4aab5d3b34d22934a696ab83516603d1bc1f3e4ff9930fe7d245e5", size = 1865870, upload-time = "2025-10-17T14:00:15.852Z" }, - { url = "https://files.pythonhosted.org/packages/9e/69/b204e5d43384197a614c88c1717c324319f5b4e7d0a1b5118da583028d40/aiohttp-3.13.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3fd4570ea696aee27204dd524f287127ed0966d14d309dc8cc440f474e3e7dbd", size = 1751021, upload-time = "2025-10-17T14:00:18.297Z" }, - { url = "https://files.pythonhosted.org/packages/1c/af/845dc6b6fdf378791d720364bf5150f80d22c990f7e3a42331d93b337cc7/aiohttp-3.13.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7bda795f08b8a620836ebfb0926f7973972a4bf8c74fdf9145e489f88c416811", size = 1561448, upload-time = "2025-10-17T14:00:20.152Z" }, - { url = "https://files.pythonhosted.org/packages/7a/91/d2ab08cd77ed76a49e4106b1cfb60bce2768242dd0c4f9ec0cb01e2cbf94/aiohttp-3.13.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:055a51d90e351aae53dcf324d0eafb2abe5b576d3ea1ec03827d920cf81a1c15", size = 1698196, upload-time = "2025-10-17T14:00:22.131Z" }, - { url = "https://files.pythonhosted.org/packages/5e/d1/082f0620dc428ecb8f21c08a191a4694915cd50f14791c74a24d9161cc50/aiohttp-3.13.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:d4131df864cbcc09bb16d3612a682af0db52f10736e71312574d90f16406a867", size = 1719252, upload-time = "2025-10-17T14:00:24.453Z" }, - { url = "https://files.pythonhosted.org/packages/fc/78/2af2f44491be7b08e43945b72d2b4fd76f0a14ba850ba9e41d28a7ce716a/aiohttp-3.13.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:163d3226e043f79bf47c87f8dfc89c496cc7bc9128cb7055ce026e435d551720", size = 1736529, upload-time = "2025-10-17T14:00:26.567Z" }, - { url = "https://files.pythonhosted.org/packages/b0/34/3e919ecdc93edaea8d140138049a0d9126141072e519535e2efa38eb7a02/aiohttp-3.13.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:a2370986a3b75c1a5f3d6f6d763fc6be4b430226577b0ed16a7c13a75bf43d8f", size = 1553723, upload-time = "2025-10-17T14:00:28.592Z" }, - { url = "https://files.pythonhosted.org/packages/21/4b/d8003aeda2f67f359b37e70a5a4b53fee336d8e89511ac307ff62aeefcdb/aiohttp-3.13.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:d7c14de0c7c9f1e6e785ce6cbe0ed817282c2af0012e674f45b4e58c6d4ea030", size = 1763394, upload-time = "2025-10-17T14:00:31.051Z" }, - { url = "https://files.pythonhosted.org/packages/4c/7b/1dbe6a39e33af9baaafc3fc016a280663684af47ba9f0e5d44249c1f72ec/aiohttp-3.13.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:bb611489cf0db10b99beeb7280bd39e0ef72bc3eb6d8c0f0a16d8a56075d1eb7", size = 1718104, upload-time = "2025-10-17T14:00:33.407Z" }, - { url = "https://files.pythonhosted.org/packages/5c/88/bd1b38687257cce67681b9b0fa0b16437be03383fa1be4d1a45b168bef25/aiohttp-3.13.1-cp312-cp312-win32.whl", hash = "sha256:f90fe0ee75590f7428f7c8b5479389d985d83c949ea10f662ab928a5ed5cf5e6", size = 425303, upload-time = "2025-10-17T14:00:35.829Z" }, - { url = "https://files.pythonhosted.org/packages/0e/e3/4481f50dd6f27e9e58c19a60cff44029641640237e35d32b04aaee8cf95f/aiohttp-3.13.1-cp312-cp312-win_amd64.whl", hash = "sha256:3461919a9dca272c183055f2aab8e6af0adc810a1b386cce28da11eb00c859d9", size = 452071, upload-time = "2025-10-17T14:00:37.764Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/1c/ce/3b83ebba6b3207a7135e5fcaba49706f8a4b6008153b4e30540c982fae26/aiohttp-3.13.2.tar.gz", hash = "sha256:40176a52c186aefef6eb3cad2cdd30cd06e3afbe88fe8ab2af9c0b90f228daca", size = 7837994, upload-time = "2025-10-28T20:59:39.937Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/35/74/b321e7d7ca762638cdf8cdeceb39755d9c745aff7a64c8789be96ddf6e96/aiohttp-3.13.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4647d02df098f6434bafd7f32ad14942f05a9caa06c7016fdcc816f343997dd0", size = 743409, upload-time = "2025-10-28T20:56:00.354Z" }, + { url = "https://files.pythonhosted.org/packages/99/3d/91524b905ec473beaf35158d17f82ef5a38033e5809fe8742e3657cdbb97/aiohttp-3.13.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e3403f24bcb9c3b29113611c3c16a2a447c3953ecf86b79775e7be06f7ae7ccb", size = 497006, upload-time = "2025-10-28T20:56:01.85Z" }, + { url = "https://files.pythonhosted.org/packages/eb/d3/7f68bc02a67716fe80f063e19adbd80a642e30682ce74071269e17d2dba1/aiohttp-3.13.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:43dff14e35aba17e3d6d5ba628858fb8cb51e30f44724a2d2f0c75be492c55e9", size = 493195, upload-time = "2025-10-28T20:56:03.314Z" }, + { url = "https://files.pythonhosted.org/packages/98/31/913f774a4708775433b7375c4f867d58ba58ead833af96c8af3621a0d243/aiohttp-3.13.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e2a9ea08e8c58bb17655630198833109227dea914cd20be660f52215f6de5613", size = 1747759, upload-time = "2025-10-28T20:56:04.904Z" }, + { url = "https://files.pythonhosted.org/packages/e8/63/04efe156f4326f31c7c4a97144f82132c3bb21859b7bb84748d452ccc17c/aiohttp-3.13.2-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:53b07472f235eb80e826ad038c9d106c2f653584753f3ddab907c83f49eedead", size = 1704456, upload-time = "2025-10-28T20:56:06.986Z" }, + { url = "https://files.pythonhosted.org/packages/8e/02/4e16154d8e0a9cf4ae76f692941fd52543bbb148f02f098ca73cab9b1c1b/aiohttp-3.13.2-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e736c93e9c274fce6419af4aac199984d866e55f8a4cec9114671d0ea9688780", size = 1807572, upload-time = "2025-10-28T20:56:08.558Z" }, + { url = "https://files.pythonhosted.org/packages/34/58/b0583defb38689e7f06798f0285b1ffb3a6fb371f38363ce5fd772112724/aiohttp-3.13.2-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ff5e771f5dcbc81c64898c597a434f7682f2259e0cd666932a913d53d1341d1a", size = 1895954, upload-time = "2025-10-28T20:56:10.545Z" }, + { url = "https://files.pythonhosted.org/packages/6b/f3/083907ee3437425b4e376aa58b2c915eb1a33703ec0dc30040f7ae3368c6/aiohttp-3.13.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a3b6fb0c207cc661fa0bf8c66d8d9b657331ccc814f4719468af61034b478592", size = 1747092, upload-time = "2025-10-28T20:56:12.118Z" }, + { url = "https://files.pythonhosted.org/packages/ac/61/98a47319b4e425cc134e05e5f3fc512bf9a04bf65aafd9fdcda5d57ec693/aiohttp-3.13.2-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:97a0895a8e840ab3520e2288db7cace3a1981300d48babeb50e7425609e2e0ab", size = 1606815, upload-time = "2025-10-28T20:56:14.191Z" }, + { url = "https://files.pythonhosted.org/packages/97/4b/e78b854d82f66bb974189135d31fce265dee0f5344f64dd0d345158a5973/aiohttp-3.13.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9e8f8afb552297aca127c90cb840e9a1d4bfd6a10d7d8f2d9176e1acc69bad30", size = 1723789, upload-time = "2025-10-28T20:56:16.101Z" }, + { url = "https://files.pythonhosted.org/packages/ed/fc/9d2ccc794fc9b9acd1379d625c3a8c64a45508b5091c546dea273a41929e/aiohttp-3.13.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:ed2f9c7216e53c3df02264f25d824b079cc5914f9e2deba94155190ef648ee40", size = 1718104, upload-time = "2025-10-28T20:56:17.655Z" }, + { url = "https://files.pythonhosted.org/packages/66/65/34564b8765ea5c7d79d23c9113135d1dd3609173da13084830f1507d56cf/aiohttp-3.13.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:99c5280a329d5fa18ef30fd10c793a190d996567667908bef8a7f81f8202b948", size = 1785584, upload-time = "2025-10-28T20:56:19.238Z" }, + { url = "https://files.pythonhosted.org/packages/30/be/f6a7a426e02fc82781afd62016417b3948e2207426d90a0e478790d1c8a4/aiohttp-3.13.2-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:2ca6ffef405fc9c09a746cb5d019c1672cd7f402542e379afc66b370833170cf", size = 1595126, upload-time = "2025-10-28T20:56:20.836Z" }, + { url = "https://files.pythonhosted.org/packages/e5/c7/8e22d5d28f94f67d2af496f14a83b3c155d915d1fe53d94b66d425ec5b42/aiohttp-3.13.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:47f438b1a28e926c37632bff3c44df7d27c9b57aaf4e34b1def3c07111fdb782", size = 1800665, upload-time = "2025-10-28T20:56:22.922Z" }, + { url = "https://files.pythonhosted.org/packages/d1/11/91133c8b68b1da9fc16555706aa7276fdf781ae2bb0876c838dd86b8116e/aiohttp-3.13.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9acda8604a57bb60544e4646a4615c1866ee6c04a8edef9b8ee6fd1d8fa2ddc8", size = 1739532, upload-time = "2025-10-28T20:56:25.924Z" }, + { url = "https://files.pythonhosted.org/packages/17/6b/3747644d26a998774b21a616016620293ddefa4d63af6286f389aedac844/aiohttp-3.13.2-cp311-cp311-win32.whl", hash = "sha256:868e195e39b24aaa930b063c08bb0c17924899c16c672a28a65afded9c46c6ec", size = 431876, upload-time = "2025-10-28T20:56:27.524Z" }, + { url = "https://files.pythonhosted.org/packages/c3/63/688462108c1a00eb9f05765331c107f95ae86f6b197b865d29e930b7e462/aiohttp-3.13.2-cp311-cp311-win_amd64.whl", hash = "sha256:7fd19df530c292542636c2a9a85854fab93474396a52f1695e799186bbd7f24c", size = 456205, upload-time = "2025-10-28T20:56:29.062Z" }, + { url = "https://files.pythonhosted.org/packages/29/9b/01f00e9856d0a73260e86dd8ed0c2234a466c5c1712ce1c281548df39777/aiohttp-3.13.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:b1e56bab2e12b2b9ed300218c351ee2a3d8c8fdab5b1ec6193e11a817767e47b", size = 737623, upload-time = "2025-10-28T20:56:30.797Z" }, + { url = "https://files.pythonhosted.org/packages/5a/1b/4be39c445e2b2bd0aab4ba736deb649fabf14f6757f405f0c9685019b9e9/aiohttp-3.13.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:364e25edaabd3d37b1db1f0cbcee8c73c9a3727bfa262b83e5e4cf3489a2a9dc", size = 492664, upload-time = "2025-10-28T20:56:32.708Z" }, + { url = "https://files.pythonhosted.org/packages/28/66/d35dcfea8050e131cdd731dff36434390479b4045a8d0b9d7111b0a968f1/aiohttp-3.13.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c5c94825f744694c4b8db20b71dba9a257cd2ba8e010a803042123f3a25d50d7", size = 491808, upload-time = "2025-10-28T20:56:34.57Z" }, + { url = "https://files.pythonhosted.org/packages/00/29/8e4609b93e10a853b65f8291e64985de66d4f5848c5637cddc70e98f01f8/aiohttp-3.13.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ba2715d842ffa787be87cbfce150d5e88c87a98e0b62e0f5aa489169a393dbbb", size = 1738863, upload-time = "2025-10-28T20:56:36.377Z" }, + { url = "https://files.pythonhosted.org/packages/9d/fa/4ebdf4adcc0def75ced1a0d2d227577cd7b1b85beb7edad85fcc87693c75/aiohttp-3.13.2-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:585542825c4bc662221fb257889e011a5aa00f1ae4d75d1d246a5225289183e3", size = 1700586, upload-time = "2025-10-28T20:56:38.034Z" }, + { url = "https://files.pythonhosted.org/packages/da/04/73f5f02ff348a3558763ff6abe99c223381b0bace05cd4530a0258e52597/aiohttp-3.13.2-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:39d02cb6025fe1aabca329c5632f48c9532a3dabccd859e7e2f110668972331f", size = 1768625, upload-time = "2025-10-28T20:56:39.75Z" }, + { url = "https://files.pythonhosted.org/packages/f8/49/a825b79ffec124317265ca7d2344a86bcffeb960743487cb11988ffb3494/aiohttp-3.13.2-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e67446b19e014d37342f7195f592a2a948141d15a312fe0e700c2fd2f03124f6", size = 1867281, upload-time = "2025-10-28T20:56:41.471Z" }, + { url = "https://files.pythonhosted.org/packages/b9/48/adf56e05f81eac31edcfae45c90928f4ad50ef2e3ea72cb8376162a368f8/aiohttp-3.13.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4356474ad6333e41ccefd39eae869ba15a6c5299c9c01dfdcfdd5c107be4363e", size = 1752431, upload-time = "2025-10-28T20:56:43.162Z" }, + { url = "https://files.pythonhosted.org/packages/30/ab/593855356eead019a74e862f21523db09c27f12fd24af72dbc3555b9bfd9/aiohttp-3.13.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:eeacf451c99b4525f700f078becff32c32ec327b10dcf31306a8a52d78166de7", size = 1562846, upload-time = "2025-10-28T20:56:44.85Z" }, + { url = "https://files.pythonhosted.org/packages/39/0f/9f3d32271aa8dc35036e9668e31870a9d3b9542dd6b3e2c8a30931cb27ae/aiohttp-3.13.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d8a9b889aeabd7a4e9af0b7f4ab5ad94d42e7ff679aaec6d0db21e3b639ad58d", size = 1699606, upload-time = "2025-10-28T20:56:46.519Z" }, + { url = "https://files.pythonhosted.org/packages/2c/3c/52d2658c5699b6ef7692a3f7128b2d2d4d9775f2a68093f74bca06cf01e1/aiohttp-3.13.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:fa89cb11bc71a63b69568d5b8a25c3ca25b6d54c15f907ca1c130d72f320b76b", size = 1720663, upload-time = "2025-10-28T20:56:48.528Z" }, + { url = "https://files.pythonhosted.org/packages/9b/d4/8f8f3ff1fb7fb9e3f04fcad4e89d8a1cd8fc7d05de67e3de5b15b33008ff/aiohttp-3.13.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:8aa7c807df234f693fed0ecd507192fc97692e61fee5702cdc11155d2e5cadc8", size = 1737939, upload-time = "2025-10-28T20:56:50.77Z" }, + { url = "https://files.pythonhosted.org/packages/03/d3/ddd348f8a27a634daae39a1b8e291ff19c77867af438af844bf8b7e3231b/aiohttp-3.13.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:9eb3e33fdbe43f88c3c75fa608c25e7c47bbd80f48d012763cb67c47f39a7e16", size = 1555132, upload-time = "2025-10-28T20:56:52.568Z" }, + { url = "https://files.pythonhosted.org/packages/39/b8/46790692dc46218406f94374903ba47552f2f9f90dad554eed61bfb7b64c/aiohttp-3.13.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9434bc0d80076138ea986833156c5a48c9c7a8abb0c96039ddbb4afc93184169", size = 1764802, upload-time = "2025-10-28T20:56:54.292Z" }, + { url = "https://files.pythonhosted.org/packages/ba/e4/19ce547b58ab2a385e5f0b8aa3db38674785085abcf79b6e0edd1632b12f/aiohttp-3.13.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ff15c147b2ad66da1f2cbb0622313f2242d8e6e8f9b79b5206c84523a4473248", size = 1719512, upload-time = "2025-10-28T20:56:56.428Z" }, + { url = "https://files.pythonhosted.org/packages/70/30/6355a737fed29dcb6dfdd48682d5790cb5eab050f7b4e01f49b121d3acad/aiohttp-3.13.2-cp312-cp312-win32.whl", hash = "sha256:27e569eb9d9e95dbd55c0fc3ec3a9335defbf1d8bc1d20171a49f3c4c607b93e", size = 426690, upload-time = "2025-10-28T20:56:58.736Z" }, + { url = "https://files.pythonhosted.org/packages/0a/0d/b10ac09069973d112de6ef980c1f6bb31cb7dcd0bc363acbdad58f927873/aiohttp-3.13.2-cp312-cp312-win_amd64.whl", hash = "sha256:8709a0f05d59a71f33fd05c17fc11fcb8c30140506e13c2f5e8ee1b8964e1b45", size = 453465, upload-time = "2025-10-28T20:57:00.795Z" }, ] [[package]] @@ -99,7 +99,7 @@ wheels = [ [[package]] name = "anthropic" -version = "0.71.0" +version = "0.75.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, @@ -111,9 +111,9 @@ dependencies = [ { name = "sniffio" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/82/4f/70682b068d897841f43223df82d96ec1d617435a8b759c4a2d901a50158b/anthropic-0.71.0.tar.gz", hash = "sha256:eb8e6fa86d049061b3ef26eb4cbae0174ebbff21affa6de7b3098da857d8de6a", size = 489102, upload-time = "2025-10-16T15:54:40.08Z" } +sdist = { url = "https://files.pythonhosted.org/packages/04/1f/08e95f4b7e2d35205ae5dcbb4ae97e7d477fc521c275c02609e2931ece2d/anthropic-0.75.0.tar.gz", hash = "sha256:e8607422f4ab616db2ea5baacc215dd5f028da99ce2f022e33c7c535b29f3dfb", size = 439565, upload-time = "2025-11-24T20:41:45.28Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5d/77/073e8ac488f335aec7001952825275582fb8f433737e90f24eeef9d878f6/anthropic-0.71.0-py3-none-any.whl", hash = "sha256:85c5015fcdbdc728390f11b17642a65a4365d03b12b799b18b6cc57e71fdb327", size = 355035, upload-time = "2025-10-16T15:54:38.238Z" }, + { url = "https://files.pythonhosted.org/packages/60/1c/1cd02b7ae64302a6e06724bf80a96401d5313708651d277b1458504a1730/anthropic-0.75.0-py3-none-any.whl", hash = "sha256:ea8317271b6c15d80225a9f3c670152746e88805a7a61e14d4a374577164965b", size = 388164, upload-time = "2025-11-24T20:41:43.587Z" }, ] [[package]] @@ -234,11 +234,11 @@ wheels = [ [[package]] name = "asttokens" -version = "3.0.0" +version = "3.0.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4a/e7/82da0a03e7ba5141f05cce0d302e6eed121ae055e0456ca228bf693984bc/asttokens-3.0.0.tar.gz", hash = "sha256:0dcd8baa8d62b0c1d118b399b2ddba3c4aff271d0d7a9e0d4c1681c79035bbc7", size = 61978, upload-time = "2024-11-30T04:30:14.439Z" } +sdist = { url = "https://files.pythonhosted.org/packages/be/a5/8e3f9b6771b0b408517c82d97aed8f2036509bc247d46114925e32fe33f0/asttokens-3.0.1.tar.gz", hash = "sha256:71a4ee5de0bde6a31d64f6b13f2293ac190344478f081c3d1bccfcf5eacb0cb7", size = 62308, upload-time = "2025-11-15T16:43:48.578Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/25/8a/c46dcc25341b5bce5472c718902eb3d38600a903b14fa6aeecef3f21a46f/asttokens-3.0.0-py3-none-any.whl", hash = "sha256:e3078351a059199dd5138cb1c706e6430c05eff2ff136af5eb4790f9d28932e2", size = 26918, upload-time = "2024-11-30T04:30:10.946Z" }, + { url = "https://files.pythonhosted.org/packages/d2/39/e7eaf1799466a4aef85b6a4fe7bd175ad2b1c6345066aa33f1f58d4b18d0/asttokens-3.0.1-py3-none-any.whl", hash = "sha256:15a3ebc0f43c2d0a50eeafea25e19046c68398e487b9f1f5b517f7c0f40f976a", size = 27047, upload-time = "2025-11-15T16:43:16.109Z" }, ] [[package]] @@ -300,11 +300,11 @@ css = [ [[package]] name = "certifi" -version = "2025.10.5" +version = "2025.11.12" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4c/5b/b6ce21586237c77ce67d01dc5507039d444b630dd76611bbca2d8e5dcd91/certifi-2025.10.5.tar.gz", hash = "sha256:47c09d31ccf2acf0be3f701ea53595ee7e0b8fa08801c6624be771df09ae7b43", size = 164519, upload-time = "2025-10-05T04:12:15.808Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/8c/58f469717fa48465e4a50c014a0400602d3c437d7c0c468e17ada824da3a/certifi-2025.11.12.tar.gz", hash = "sha256:d8ab5478f2ecd78af242878415affce761ca6bc54a22a27e026d7c25357c3316", size = 160538, upload-time = "2025-11-12T02:54:51.517Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e4/37/af0d2ef3967ac0d6113837b44a4f0bfe1328c2b9763bd5b1744520e5cfed/certifi-2025.10.5-py3-none-any.whl", hash = "sha256:0f212c2744a9bb6de0c56639a6f68afe01ecd92d91f14ae897c4fe7bbeeef0de", size = 163286, upload-time = "2025-10-05T04:12:14.03Z" }, + { url = "https://files.pythonhosted.org/packages/70/7d/9bc192684cea499815ff478dfcdc13835ddf401365057044fb721ec6bddb/certifi-2025.11.12-py3-none-any.whl", hash = "sha256:97de8790030bbd5c2d96b7ec782fc2f7820ef8dba6db909ccf95449f2d062d4b", size = 159438, upload-time = "2025-11-12T02:54:49.735Z" }, ] [[package]] @@ -345,11 +345,11 @@ wheels = [ [[package]] name = "cfgv" -version = "3.4.0" +version = "3.5.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/11/74/539e56497d9bd1d484fd863dd69cbbfa653cd2aa27abfe35653494d85e94/cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560", size = 7114, upload-time = "2023-08-12T20:38:17.776Z" } +sdist = { url = "https://files.pythonhosted.org/packages/4e/b5/721b8799b04bf9afe054a3899c6cf4e880fcf8563cc71c15610242490a0c/cfgv-3.5.0.tar.gz", hash = "sha256:d5b1034354820651caa73ede66a6294d6e95c1b00acc5e9b098e917404669132", size = 7334, upload-time = "2025-11-19T20:55:51.612Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c5/55/51844dd50c4fc7a33b653bfaba4c2456f06955289ca770a5dbd5fd267374/cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9", size = 7249, upload-time = "2023-08-12T20:38:16.269Z" }, + { url = "https://files.pythonhosted.org/packages/db/3c/33bac158f8ab7f89b2e59426d5fe2e4f63f7ed25df84c036890172b412b5/cfgv-3.5.0-py2.py3-none-any.whl", hash = "sha256:a8dc6b26ad22ff227d2634a65cb388215ce6cc96bbcc5cfde7641ae87e8dacc0", size = 7445, upload-time = "2025-11-19T20:55:50.744Z" }, ] [[package]] @@ -395,14 +395,14 @@ wheels = [ [[package]] name = "click" -version = "8.3.0" +version = "8.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/46/61/de6cd827efad202d7057d93e0fed9294b96952e188f7384832791c7b2254/click-8.3.0.tar.gz", hash = "sha256:e7b8232224eba16f4ebe410c25ced9f7875cb5f3263ffc93cc3e8da705e229c4", size = 276943, upload-time = "2025-09-18T17:32:23.696Z" } +sdist = { url = "https://files.pythonhosted.org/packages/3d/fa/656b739db8587d7b5dfa22e22ed02566950fbfbcdc20311993483657a5c0/click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a", size = 295065, upload-time = "2025-11-15T20:45:42.706Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/db/d3/9dcc0f5797f070ec8edf30fbadfb200e71d9db6b84d211e3b2085a7589a0/click-8.3.0-py3-none-any.whl", hash = "sha256:9b9f285302c6e3064f4330c05f05b81945b2a39544279343e6e7c5f27a9baddc", size = 107295, upload-time = "2025-09-18T17:32:22.42Z" }, + { url = "https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6", size = 108274, upload-time = "2025-11-15T20:45:41.139Z" }, ] [[package]] @@ -425,37 +425,37 @@ wheels = [ [[package]] name = "coverage" -version = "7.11.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1c/38/ee22495420457259d2f3390309505ea98f98a5eed40901cf62196abad006/coverage-7.11.0.tar.gz", hash = "sha256:167bd504ac1ca2af7ff3b81d245dfea0292c5032ebef9d66cc08a7d28c1b8050", size = 811905, upload-time = "2025-10-15T15:15:08.542Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/49/3a/ee1074c15c408ddddddb1db7dd904f6b81bc524e01f5a1c5920e13dbde23/coverage-7.11.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3d58ecaa865c5b9fa56e35efc51d1014d4c0d22838815b9fce57a27dd9576847", size = 215912, upload-time = "2025-10-15T15:12:40.665Z" }, - { url = "https://files.pythonhosted.org/packages/70/c4/9f44bebe5cb15f31608597b037d78799cc5f450044465bcd1ae8cb222fe1/coverage-7.11.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b679e171f1c104a5668550ada700e3c4937110dbdd153b7ef9055c4f1a1ee3cc", size = 216310, upload-time = "2025-10-15T15:12:42.461Z" }, - { url = "https://files.pythonhosted.org/packages/42/01/5e06077cfef92d8af926bdd86b84fb28bf9bc6ad27343d68be9b501d89f2/coverage-7.11.0-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:ca61691ba8c5b6797deb221a0d09d7470364733ea9c69425a640f1f01b7c5bf0", size = 246706, upload-time = "2025-10-15T15:12:44.001Z" }, - { url = "https://files.pythonhosted.org/packages/40/b8/7a3f1f33b35cc4a6c37e759137533119560d06c0cc14753d1a803be0cd4a/coverage-7.11.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:aef1747ede4bd8ca9cfc04cc3011516500c6891f1b33a94add3253f6f876b7b7", size = 248634, upload-time = "2025-10-15T15:12:45.768Z" }, - { url = "https://files.pythonhosted.org/packages/7a/41/7f987eb33de386bc4c665ab0bf98d15fcf203369d6aacae74f5dd8ec489a/coverage-7.11.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a1839d08406e4cba2953dcc0ffb312252f14d7c4c96919f70167611f4dee2623", size = 250741, upload-time = "2025-10-15T15:12:47.222Z" }, - { url = "https://files.pythonhosted.org/packages/23/c1/a4e0ca6a4e83069fb8216b49b30a7352061ca0cb38654bd2dc96b7b3b7da/coverage-7.11.0-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e0eb0a2dcc62478eb5b4cbb80b97bdee852d7e280b90e81f11b407d0b81c4287", size = 246837, upload-time = "2025-10-15T15:12:48.904Z" }, - { url = "https://files.pythonhosted.org/packages/5d/03/ced062a17f7c38b4728ff76c3acb40d8465634b20b4833cdb3cc3a74e115/coverage-7.11.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:bc1fbea96343b53f65d5351d8fd3b34fd415a2670d7c300b06d3e14a5af4f552", size = 248429, upload-time = "2025-10-15T15:12:50.73Z" }, - { url = "https://files.pythonhosted.org/packages/97/af/a7c6f194bb8c5a2705ae019036b8fe7f49ea818d638eedb15fdb7bed227c/coverage-7.11.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:214b622259dd0cf435f10241f1333d32caa64dbc27f8790ab693428a141723de", size = 246490, upload-time = "2025-10-15T15:12:52.646Z" }, - { url = "https://files.pythonhosted.org/packages/ab/c3/aab4df02b04a8fde79068c3c41ad7a622b0ef2b12e1ed154da986a727c3f/coverage-7.11.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:258d9967520cca899695d4eb7ea38be03f06951d6ca2f21fb48b1235f791e601", size = 246208, upload-time = "2025-10-15T15:12:54.586Z" }, - { url = "https://files.pythonhosted.org/packages/30/d8/e282ec19cd658238d60ed404f99ef2e45eed52e81b866ab1518c0d4163cf/coverage-7.11.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:cf9e6ff4ca908ca15c157c409d608da77a56a09877b97c889b98fb2c32b6465e", size = 247126, upload-time = "2025-10-15T15:12:56.485Z" }, - { url = "https://files.pythonhosted.org/packages/d1/17/a635fa07fac23adb1a5451ec756216768c2767efaed2e4331710342a3399/coverage-7.11.0-cp311-cp311-win32.whl", hash = "sha256:fcc15fc462707b0680cff6242c48625da7f9a16a28a41bb8fd7a4280920e676c", size = 218314, upload-time = "2025-10-15T15:12:58.365Z" }, - { url = "https://files.pythonhosted.org/packages/2a/29/2ac1dfcdd4ab9a70026edc8d715ece9b4be9a1653075c658ee6f271f394d/coverage-7.11.0-cp311-cp311-win_amd64.whl", hash = "sha256:865965bf955d92790f1facd64fe7ff73551bd2c1e7e6b26443934e9701ba30b9", size = 219203, upload-time = "2025-10-15T15:12:59.902Z" }, - { url = "https://files.pythonhosted.org/packages/03/21/5ce8b3a0133179115af4c041abf2ee652395837cb896614beb8ce8ddcfd9/coverage-7.11.0-cp311-cp311-win_arm64.whl", hash = "sha256:5693e57a065760dcbeb292d60cc4d0231a6d4b6b6f6a3191561e1d5e8820b745", size = 217879, upload-time = "2025-10-15T15:13:01.35Z" }, - { url = "https://files.pythonhosted.org/packages/c4/db/86f6906a7c7edc1a52b2c6682d6dd9be775d73c0dfe2b84f8923dfea5784/coverage-7.11.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9c49e77811cf9d024b95faf86c3f059b11c0c9be0b0d61bc598f453703bd6fd1", size = 216098, upload-time = "2025-10-15T15:13:02.916Z" }, - { url = "https://files.pythonhosted.org/packages/21/54/e7b26157048c7ba555596aad8569ff903d6cd67867d41b75287323678ede/coverage-7.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a61e37a403a778e2cda2a6a39abcc895f1d984071942a41074b5c7ee31642007", size = 216331, upload-time = "2025-10-15T15:13:04.403Z" }, - { url = "https://files.pythonhosted.org/packages/b9/19/1ce6bf444f858b83a733171306134a0544eaddf1ca8851ede6540a55b2ad/coverage-7.11.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:c79cae102bb3b1801e2ef1511fb50e91ec83a1ce466b2c7c25010d884336de46", size = 247825, upload-time = "2025-10-15T15:13:05.92Z" }, - { url = "https://files.pythonhosted.org/packages/71/0b/d3bcbbc259fcced5fb67c5d78f6e7ee965f49760c14afd931e9e663a83b2/coverage-7.11.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:16ce17ceb5d211f320b62df002fa7016b7442ea0fd260c11cec8ce7730954893", size = 250573, upload-time = "2025-10-15T15:13:07.471Z" }, - { url = "https://files.pythonhosted.org/packages/58/8d/b0ff3641a320abb047258d36ed1c21d16be33beed4152628331a1baf3365/coverage-7.11.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:80027673e9d0bd6aef86134b0771845e2da85755cf686e7c7c59566cf5a89115", size = 251706, upload-time = "2025-10-15T15:13:09.4Z" }, - { url = "https://files.pythonhosted.org/packages/59/c8/5a586fe8c7b0458053d9c687f5cff515a74b66c85931f7fe17a1c958b4ac/coverage-7.11.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4d3ffa07a08657306cd2215b0da53761c4d73cb54d9143b9303a6481ec0cd415", size = 248221, upload-time = "2025-10-15T15:13:10.964Z" }, - { url = "https://files.pythonhosted.org/packages/d0/ff/3a25e3132804ba44cfa9a778cdf2b73dbbe63ef4b0945e39602fc896ba52/coverage-7.11.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a3b6a5f8b2524fd6c1066bc85bfd97e78709bb5e37b5b94911a6506b65f47186", size = 249624, upload-time = "2025-10-15T15:13:12.5Z" }, - { url = "https://files.pythonhosted.org/packages/c5/12/ff10c8ce3895e1b17a73485ea79ebc1896a9e466a9d0f4aef63e0d17b718/coverage-7.11.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:fcc0a4aa589de34bc56e1a80a740ee0f8c47611bdfb28cd1849de60660f3799d", size = 247744, upload-time = "2025-10-15T15:13:14.554Z" }, - { url = "https://files.pythonhosted.org/packages/16/02/d500b91f5471b2975947e0629b8980e5e90786fe316b6d7299852c1d793d/coverage-7.11.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:dba82204769d78c3fd31b35c3d5f46e06511936c5019c39f98320e05b08f794d", size = 247325, upload-time = "2025-10-15T15:13:16.438Z" }, - { url = "https://files.pythonhosted.org/packages/77/11/dee0284fbbd9cd64cfce806b827452c6df3f100d9e66188e82dfe771d4af/coverage-7.11.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:81b335f03ba67309a95210caf3eb43bd6fe75a4e22ba653ef97b4696c56c7ec2", size = 249180, upload-time = "2025-10-15T15:13:17.959Z" }, - { url = "https://files.pythonhosted.org/packages/59/1b/cdf1def928f0a150a057cab03286774e73e29c2395f0d30ce3d9e9f8e697/coverage-7.11.0-cp312-cp312-win32.whl", hash = "sha256:037b2d064c2f8cc8716fe4d39cb705779af3fbf1ba318dc96a1af858888c7bb5", size = 218479, upload-time = "2025-10-15T15:13:19.608Z" }, - { url = "https://files.pythonhosted.org/packages/ff/55/e5884d55e031da9c15b94b90a23beccc9d6beee65e9835cd6da0a79e4f3a/coverage-7.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:d66c0104aec3b75e5fd897e7940188ea1892ca1d0235316bf89286d6a22568c0", size = 219290, upload-time = "2025-10-15T15:13:21.593Z" }, - { url = "https://files.pythonhosted.org/packages/23/a8/faa930cfc71c1d16bc78f9a19bb73700464f9c331d9e547bfbc1dbd3a108/coverage-7.11.0-cp312-cp312-win_arm64.whl", hash = "sha256:d91ebeac603812a09cf6a886ba6e464f3bbb367411904ae3790dfe28311b15ad", size = 217924, upload-time = "2025-10-15T15:13:23.39Z" }, - { url = "https://files.pythonhosted.org/packages/5f/04/642c1d8a448ae5ea1369eac8495740a79eb4e581a9fb0cbdce56bbf56da1/coverage-7.11.0-py3-none-any.whl", hash = "sha256:4b7589765348d78fb4e5fb6ea35d07564e387da2fc5efff62e0222971f155f68", size = 207761, upload-time = "2025-10-15T15:15:06.439Z" }, +version = "7.12.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/89/26/4a96807b193b011588099c3b5c89fbb05294e5b90e71018e065465f34eb6/coverage-7.12.0.tar.gz", hash = "sha256:fc11e0a4e372cb5f282f16ef90d4a585034050ccda536451901abfb19a57f40c", size = 819341, upload-time = "2025-11-18T13:34:20.766Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5a/0c/0dfe7f0487477d96432e4815537263363fb6dd7289743a796e8e51eabdf2/coverage-7.12.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:aa124a3683d2af98bd9d9c2bfa7a5076ca7e5ab09fdb96b81fa7d89376ae928f", size = 217535, upload-time = "2025-11-18T13:32:08.812Z" }, + { url = "https://files.pythonhosted.org/packages/9b/f5/f9a4a053a5bbff023d3bec259faac8f11a1e5a6479c2ccf586f910d8dac7/coverage-7.12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d93fbf446c31c0140208dcd07c5d882029832e8ed7891a39d6d44bd65f2316c3", size = 218044, upload-time = "2025-11-18T13:32:10.329Z" }, + { url = "https://files.pythonhosted.org/packages/95/c5/84fc3697c1fa10cd8571919bf9693f693b7373278daaf3b73e328d502bc8/coverage-7.12.0-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:52ca620260bd8cd6027317bdd8b8ba929be1d741764ee765b42c4d79a408601e", size = 248440, upload-time = "2025-11-18T13:32:12.536Z" }, + { url = "https://files.pythonhosted.org/packages/f4/36/2d93fbf6a04670f3874aed397d5a5371948a076e3249244a9e84fb0e02d6/coverage-7.12.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f3433ffd541380f3a0e423cff0f4926d55b0cc8c1d160fdc3be24a4c03aa65f7", size = 250361, upload-time = "2025-11-18T13:32:13.852Z" }, + { url = "https://files.pythonhosted.org/packages/5d/49/66dc65cc456a6bfc41ea3d0758c4afeaa4068a2b2931bf83be6894cf1058/coverage-7.12.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f7bbb321d4adc9f65e402c677cd1c8e4c2d0105d3ce285b51b4d87f1d5db5245", size = 252472, upload-time = "2025-11-18T13:32:15.068Z" }, + { url = "https://files.pythonhosted.org/packages/35/1f/ebb8a18dffd406db9fcd4b3ae42254aedcaf612470e8712f12041325930f/coverage-7.12.0-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:22a7aade354a72dff3b59c577bfd18d6945c61f97393bc5fb7bd293a4237024b", size = 248592, upload-time = "2025-11-18T13:32:16.328Z" }, + { url = "https://files.pythonhosted.org/packages/da/a8/67f213c06e5ea3b3d4980df7dc344d7fea88240b5fe878a5dcbdfe0e2315/coverage-7.12.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3ff651dcd36d2fea66877cd4a82de478004c59b849945446acb5baf9379a1b64", size = 250167, upload-time = "2025-11-18T13:32:17.687Z" }, + { url = "https://files.pythonhosted.org/packages/f0/00/e52aef68154164ea40cc8389c120c314c747fe63a04b013a5782e989b77f/coverage-7.12.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:31b8b2e38391a56e3cea39d22a23faaa7c3fc911751756ef6d2621d2a9daf742", size = 248238, upload-time = "2025-11-18T13:32:19.2Z" }, + { url = "https://files.pythonhosted.org/packages/1f/a4/4d88750bcf9d6d66f77865e5a05a20e14db44074c25fd22519777cb69025/coverage-7.12.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:297bc2da28440f5ae51c845a47c8175a4db0553a53827886e4fb25c66633000c", size = 247964, upload-time = "2025-11-18T13:32:21.027Z" }, + { url = "https://files.pythonhosted.org/packages/a7/6b/b74693158899d5b47b0bf6238d2c6722e20ba749f86b74454fac0696bb00/coverage-7.12.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6ff7651cc01a246908eac162a6a86fc0dbab6de1ad165dfb9a1e2ec660b44984", size = 248862, upload-time = "2025-11-18T13:32:22.304Z" }, + { url = "https://files.pythonhosted.org/packages/18/de/6af6730227ce0e8ade307b1cc4a08e7f51b419a78d02083a86c04ccceb29/coverage-7.12.0-cp311-cp311-win32.whl", hash = "sha256:313672140638b6ddb2c6455ddeda41c6a0b208298034544cfca138978c6baed6", size = 220033, upload-time = "2025-11-18T13:32:23.714Z" }, + { url = "https://files.pythonhosted.org/packages/e2/a1/e7f63021a7c4fe20994359fcdeae43cbef4a4d0ca36a5a1639feeea5d9e1/coverage-7.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:a1783ed5bd0d5938d4435014626568dc7f93e3cb99bc59188cc18857c47aa3c4", size = 220966, upload-time = "2025-11-18T13:32:25.599Z" }, + { url = "https://files.pythonhosted.org/packages/77/e8/deae26453f37c20c3aa0c4433a1e32cdc169bf415cce223a693117aa3ddd/coverage-7.12.0-cp311-cp311-win_arm64.whl", hash = "sha256:4648158fd8dd9381b5847622df1c90ff314efbfc1df4550092ab6013c238a5fc", size = 219637, upload-time = "2025-11-18T13:32:27.265Z" }, + { url = "https://files.pythonhosted.org/packages/02/bf/638c0427c0f0d47638242e2438127f3c8ee3cfc06c7fdeb16778ed47f836/coverage-7.12.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:29644c928772c78512b48e14156b81255000dcfd4817574ff69def189bcb3647", size = 217704, upload-time = "2025-11-18T13:32:28.906Z" }, + { url = "https://files.pythonhosted.org/packages/08/e1/706fae6692a66c2d6b871a608bbde0da6281903fa0e9f53a39ed441da36a/coverage-7.12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8638cbb002eaa5d7c8d04da667813ce1067080b9a91099801a0053086e52b736", size = 218064, upload-time = "2025-11-18T13:32:30.161Z" }, + { url = "https://files.pythonhosted.org/packages/a9/8b/eb0231d0540f8af3ffda39720ff43cb91926489d01524e68f60e961366e4/coverage-7.12.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:083631eeff5eb9992c923e14b810a179798bb598e6a0dd60586819fc23be6e60", size = 249560, upload-time = "2025-11-18T13:32:31.835Z" }, + { url = "https://files.pythonhosted.org/packages/e9/a1/67fb52af642e974d159b5b379e4d4c59d0ebe1288677fbd04bbffe665a82/coverage-7.12.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:99d5415c73ca12d558e07776bd957c4222c687b9f1d26fa0e1b57e3598bdcde8", size = 252318, upload-time = "2025-11-18T13:32:33.178Z" }, + { url = "https://files.pythonhosted.org/packages/41/e5/38228f31b2c7665ebf9bdfdddd7a184d56450755c7e43ac721c11a4b8dab/coverage-7.12.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e949ebf60c717c3df63adb4a1a366c096c8d7fd8472608cd09359e1bd48ef59f", size = 253403, upload-time = "2025-11-18T13:32:34.45Z" }, + { url = "https://files.pythonhosted.org/packages/ec/4b/df78e4c8188f9960684267c5a4897836f3f0f20a20c51606ee778a1d9749/coverage-7.12.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6d907ddccbca819afa2cd014bc69983b146cca2735a0b1e6259b2a6c10be1e70", size = 249984, upload-time = "2025-11-18T13:32:35.747Z" }, + { url = "https://files.pythonhosted.org/packages/ba/51/bb163933d195a345c6f63eab9e55743413d064c291b6220df754075c2769/coverage-7.12.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b1518ecbad4e6173f4c6e6c4a46e49555ea5679bf3feda5edb1b935c7c44e8a0", size = 251339, upload-time = "2025-11-18T13:32:37.352Z" }, + { url = "https://files.pythonhosted.org/packages/15/40/c9b29cdb8412c837cdcbc2cfa054547dd83affe6cbbd4ce4fdb92b6ba7d1/coverage-7.12.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:51777647a749abdf6f6fd8c7cffab12de68ab93aab15efc72fbbb83036c2a068", size = 249489, upload-time = "2025-11-18T13:32:39.212Z" }, + { url = "https://files.pythonhosted.org/packages/c8/da/b3131e20ba07a0de4437a50ef3b47840dfabf9293675b0cd5c2c7f66dd61/coverage-7.12.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:42435d46d6461a3b305cdfcad7cdd3248787771f53fe18305548cba474e6523b", size = 249070, upload-time = "2025-11-18T13:32:40.598Z" }, + { url = "https://files.pythonhosted.org/packages/70/81/b653329b5f6302c08d683ceff6785bc60a34be9ae92a5c7b63ee7ee7acec/coverage-7.12.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5bcead88c8423e1855e64b8057d0544e33e4080b95b240c2a355334bb7ced937", size = 250929, upload-time = "2025-11-18T13:32:42.915Z" }, + { url = "https://files.pythonhosted.org/packages/a3/00/250ac3bca9f252a5fb1338b5ad01331ebb7b40223f72bef5b1b2cb03aa64/coverage-7.12.0-cp312-cp312-win32.whl", hash = "sha256:dcbb630ab034e86d2a0f79aefd2be07e583202f41e037602d438c80044957baa", size = 220241, upload-time = "2025-11-18T13:32:44.665Z" }, + { url = "https://files.pythonhosted.org/packages/64/1c/77e79e76d37ce83302f6c21980b45e09f8aa4551965213a10e62d71ce0ab/coverage-7.12.0-cp312-cp312-win_amd64.whl", hash = "sha256:2fd8354ed5d69775ac42986a691fbf68b4084278710cee9d7c3eaa0c28fa982a", size = 221051, upload-time = "2025-11-18T13:32:46.008Z" }, + { url = "https://files.pythonhosted.org/packages/31/f5/641b8a25baae564f9e52cac0e2667b123de961985709a004e287ee7663cc/coverage-7.12.0-cp312-cp312-win_arm64.whl", hash = "sha256:737c3814903be30695b2de20d22bcc5428fdae305c61ba44cdc8b3252984c49c", size = 219692, upload-time = "2025-11-18T13:32:47.372Z" }, + { url = "https://files.pythonhosted.org/packages/ce/a3/43b749004e3c09452e39bb56347a008f0a0668aad37324a99b5c8ca91d9e/coverage-7.12.0-py3-none-any.whl", hash = "sha256:159d50c0b12e060b15ed3d39f87ed43d4f7f7ad40b8a534f4dd331adbb51104a", size = 209503, upload-time = "2025-11-18T13:34:18.892Z" }, ] [[package]] @@ -599,11 +599,11 @@ wheels = [ [[package]] name = "fsspec" -version = "2025.9.0" +version = "2025.10.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/de/e0/bab50af11c2d75c9c4a2a26a5254573c0bd97cea152254401510950486fa/fsspec-2025.9.0.tar.gz", hash = "sha256:19fd429483d25d28b65ec68f9f4adc16c17ea2c7c7bf54ec61360d478fb19c19", size = 304847, upload-time = "2025-09-02T19:10:49.215Z" } +sdist = { url = "https://files.pythonhosted.org/packages/24/7f/2747c0d332b9acfa75dc84447a066fdf812b5a6b8d30472b74d309bfe8cb/fsspec-2025.10.0.tar.gz", hash = "sha256:b6789427626f068f9a83ca4e8a3cc050850b6c0f71f99ddb4f542b8266a26a59", size = 309285, upload-time = "2025-10-30T14:58:44.036Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/47/71/70db47e4f6ce3e5c37a607355f80da8860a33226be640226ac52cb05ef2e/fsspec-2025.9.0-py3-none-any.whl", hash = "sha256:530dc2a2af60a414a832059574df4a6e10cce927f6f4a78209390fe38955cfb7", size = 199289, upload-time = "2025-09-02T19:10:47.708Z" }, + { url = "https://files.pythonhosted.org/packages/eb/02/a6b21098b1d5d6249b7c5ab69dde30108a71e4e819d4a9778f1de1d5b70d/fsspec-2025.10.0-py3-none-any.whl", hash = "sha256:7c7712353ae7d875407f97715f0e1ffcc21e33d5b24556cb1e090ae9409ec61d", size = 200966, upload-time = "2025-10-30T14:58:42.53Z" }, ] [[package]] @@ -660,7 +660,7 @@ wheels = [ [[package]] name = "huggingface-hub" -version = "1.0.0" +version = "1.1.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "filelock" }, @@ -674,9 +674,9 @@ dependencies = [ { name = "typer-slim" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/91/8d/91f5aa3ad3f4fa39e6758cc3f6aaeb971bd8b8e1f5b1d6a58074c9edb4bb/huggingface_hub-1.0.0.tar.gz", hash = "sha256:0cf0c9e841817e1f8e46fc2e00fc242613db383b790a447bf1d6945b3a3fed62", size = 419427, upload-time = "2025-10-27T13:29:52.159Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fb/02/c3d534d7498ba2792da1d2ce56b5d38bbcbcbbba62071c90ee289b408e8d/huggingface_hub-1.1.5.tar.gz", hash = "sha256:40ba5c9a08792d888fde6088920a0a71ab3cd9d5e6617c81a797c657f1fd9968", size = 607199, upload-time = "2025-11-20T15:49:32.809Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6e/5a/221bb56bff551f5f5652c2a0bcb56ad347b8e1f13e95709b3bb9b8a6e545/huggingface_hub-1.0.0-py3-none-any.whl", hash = "sha256:0f444cfc18ab3e40007a2ba0aa8649389430255c23a2e5f280d43bbcff40e276", size = 503919, upload-time = "2025-10-27T13:29:50.187Z" }, + { url = "https://files.pythonhosted.org/packages/35/f4/124858007ddf3c61e9b144107304c9152fa80b5b6c168da07d86fe583cc1/huggingface_hub-1.1.5-py3-none-any.whl", hash = "sha256:e88ecc129011f37b868586bbcfae6c56868cae80cd56a79d61575426a3aa0d7d", size = 516000, upload-time = "2025-11-20T15:49:30.926Z" }, ] [[package]] @@ -732,7 +732,7 @@ wheels = [ [[package]] name = "ipython" -version = "9.6.0" +version = "9.7.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, @@ -747,9 +747,9 @@ dependencies = [ { name = "traitlets" }, { name = "typing-extensions", marker = "python_full_version < '3.12'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/2a/34/29b18c62e39ee2f7a6a3bba7efd952729d8aadd45ca17efc34453b717665/ipython-9.6.0.tar.gz", hash = "sha256:5603d6d5d356378be5043e69441a072b50a5b33b4503428c77b04cb8ce7bc731", size = 4396932, upload-time = "2025-09-29T10:55:53.948Z" } +sdist = { url = "https://files.pythonhosted.org/packages/29/e6/48c74d54039241a456add616464ea28c6ebf782e4110d419411b83dae06f/ipython-9.7.0.tar.gz", hash = "sha256:5f6de88c905a566c6a9d6c400a8fed54a638e1f7543d17aae2551133216b1e4e", size = 4422115, upload-time = "2025-11-05T12:18:54.646Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/48/c5/d5e07995077e48220269c28a221e168c91123ad5ceee44d548f54a057fc0/ipython-9.6.0-py3-none-any.whl", hash = "sha256:5f77efafc886d2f023442479b8149e7d86547ad0a979e9da9f045d252f648196", size = 616170, upload-time = "2025-09-29T10:55:47.676Z" }, + { url = "https://files.pythonhosted.org/packages/05/aa/62893d6a591d337aa59dcc4c6f6c842f1fe20cd72c8c5c1f980255243252/ipython-9.7.0-py3-none-any.whl", hash = "sha256:bce8ac85eb9521adc94e1845b4c03d88365fd6ac2f4908ec4ed1eb1b0a065f9f", size = 618911, upload-time = "2025-11-05T12:18:52.484Z" }, ] [[package]] @@ -766,7 +766,7 @@ wheels = [ [[package]] name = "ipywidgets" -version = "8.1.7" +version = "8.1.8" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "comm" }, @@ -775,9 +775,9 @@ dependencies = [ { name = "traitlets" }, { name = "widgetsnbextension" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/3e/48/d3dbac45c2814cb73812f98dd6b38bbcc957a4e7bb31d6ea9c03bf94ed87/ipywidgets-8.1.7.tar.gz", hash = "sha256:15f1ac050b9ccbefd45dccfbb2ef6bed0029d8278682d569d71b8dd96bee0376", size = 116721, upload-time = "2025-05-05T12:42:03.489Z" } +sdist = { url = "https://files.pythonhosted.org/packages/4c/ae/c5ce1edc1afe042eadb445e95b0671b03cee61895264357956e61c0d2ac0/ipywidgets-8.1.8.tar.gz", hash = "sha256:61f969306b95f85fba6b6986b7fe45d73124d1d9e3023a8068710d47a22ea668", size = 116739, upload-time = "2025-11-01T21:18:12.393Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/58/6a/9166369a2f092bd286d24e6307de555d63616e8ddb373ebad2b5635ca4cd/ipywidgets-8.1.7-py3-none-any.whl", hash = "sha256:764f2602d25471c213919b8a1997df04bef869251db4ca8efba1b76b1bd9f7bb", size = 139806, upload-time = "2025-05-05T12:41:56.833Z" }, + { url = "https://files.pythonhosted.org/packages/56/6d/0d9848617b9f753b87f214f1c682592f7ca42de085f564352f10f0843026/ipywidgets-8.1.8-py3-none-any.whl", hash = "sha256:ecaca67aed704a338f88f67b1181b58f821ab5dc89c1f0f5ef99db43c1c2921e", size = 139808, upload-time = "2025-11-01T21:18:10.956Z" }, ] [[package]] @@ -818,44 +818,44 @@ wheels = [ [[package]] name = "jiter" -version = "0.11.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a3/68/0357982493a7b20925aece061f7fb7a2678e3b232f8d73a6edb7e5304443/jiter-0.11.1.tar.gz", hash = "sha256:849dcfc76481c0ea0099391235b7ca97d7279e0fa4c86005457ac7c88e8b76dc", size = 168385, upload-time = "2025-10-17T11:31:15.186Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8b/34/c9e6cfe876f9a24f43ed53fe29f052ce02bd8d5f5a387dbf46ad3764bef0/jiter-0.11.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:9b0088ff3c374ce8ce0168523ec8e97122ebb788f950cf7bb8e39c7dc6a876a2", size = 310160, upload-time = "2025-10-17T11:28:59.174Z" }, - { url = "https://files.pythonhosted.org/packages/bc/9f/b06ec8181d7165858faf2ac5287c54fe52b2287760b7fe1ba9c06890255f/jiter-0.11.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:74433962dd3c3090655e02e461267095d6c84f0741c7827de11022ef8d7ff661", size = 316573, upload-time = "2025-10-17T11:29:00.905Z" }, - { url = "https://files.pythonhosted.org/packages/66/49/3179d93090f2ed0c6b091a9c210f266d2d020d82c96f753260af536371d0/jiter-0.11.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6d98030e345e6546df2cc2c08309c502466c66c4747b043f1a0d415fada862b8", size = 348998, upload-time = "2025-10-17T11:29:02.321Z" }, - { url = "https://files.pythonhosted.org/packages/ae/9d/63db2c8eabda7a9cad65a2e808ca34aaa8689d98d498f5a2357d7a2e2cec/jiter-0.11.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1d6db0b2e788db46bec2cf729a88b6dd36959af2abd9fa2312dfba5acdd96dcb", size = 363413, upload-time = "2025-10-17T11:29:03.787Z" }, - { url = "https://files.pythonhosted.org/packages/25/ff/3e6b3170c5053053c7baddb8d44e2bf11ff44cd71024a280a8438ae6ba32/jiter-0.11.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:55678fbbda261eafe7289165dd2ddd0e922df5f9a1ae46d7c79a5a15242bd7d1", size = 487144, upload-time = "2025-10-17T11:29:05.37Z" }, - { url = "https://files.pythonhosted.org/packages/b0/50/b63fcadf699893269b997f4c2e88400bc68f085c6db698c6e5e69d63b2c1/jiter-0.11.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6a6b74fae8e40497653b52ce6ca0f1b13457af769af6fb9c1113efc8b5b4d9be", size = 376215, upload-time = "2025-10-17T11:29:07.123Z" }, - { url = "https://files.pythonhosted.org/packages/39/8c/57a8a89401134167e87e73471b9cca321cf651c1fd78c45f3a0f16932213/jiter-0.11.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0a55a453f8b035eb4f7852a79a065d616b7971a17f5e37a9296b4b38d3b619e4", size = 359163, upload-time = "2025-10-17T11:29:09.047Z" }, - { url = "https://files.pythonhosted.org/packages/4b/96/30b0cdbffbb6f753e25339d3dbbe26890c9ef119928314578201c758aace/jiter-0.11.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2638148099022e6bdb3f42904289cd2e403609356fb06eb36ddec2d50958bc29", size = 385344, upload-time = "2025-10-17T11:29:10.69Z" }, - { url = "https://files.pythonhosted.org/packages/c6/d5/31dae27c1cc9410ad52bb514f11bfa4f286f7d6ef9d287b98b8831e156ec/jiter-0.11.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:252490567a5d990986f83b95a5f1ca1bf205ebd27b3e9e93bb7c2592380e29b9", size = 517972, upload-time = "2025-10-17T11:29:12.174Z" }, - { url = "https://files.pythonhosted.org/packages/61/1e/5905a7a3aceab80de13ab226fd690471a5e1ee7e554dc1015e55f1a6b896/jiter-0.11.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d431d52b0ca2436eea6195f0f48528202100c7deda354cb7aac0a302167594d5", size = 508408, upload-time = "2025-10-17T11:29:13.597Z" }, - { url = "https://files.pythonhosted.org/packages/91/12/1c49b97aa49077e136e8591cef7162f0d3e2860ae457a2d35868fd1521ef/jiter-0.11.1-cp311-cp311-win32.whl", hash = "sha256:db6f41e40f8bae20c86cb574b48c4fd9f28ee1c71cb044e9ec12e78ab757ba3a", size = 203937, upload-time = "2025-10-17T11:29:14.894Z" }, - { url = "https://files.pythonhosted.org/packages/6d/9d/2255f7c17134ee9892c7e013c32d5bcf4bce64eb115402c9fe5e727a67eb/jiter-0.11.1-cp311-cp311-win_amd64.whl", hash = "sha256:0cc407b8e6cdff01b06bb80f61225c8b090c3df108ebade5e0c3c10993735b19", size = 207589, upload-time = "2025-10-17T11:29:16.166Z" }, - { url = "https://files.pythonhosted.org/packages/3c/28/6307fc8f95afef84cae6caf5429fee58ef16a582c2ff4db317ceb3e352fa/jiter-0.11.1-cp311-cp311-win_arm64.whl", hash = "sha256:fe04ea475392a91896d1936367854d346724a1045a247e5d1c196410473b8869", size = 188391, upload-time = "2025-10-17T11:29:17.488Z" }, - { url = "https://files.pythonhosted.org/packages/15/8b/318e8af2c904a9d29af91f78c1e18f0592e189bbdb8a462902d31fe20682/jiter-0.11.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:c92148eec91052538ce6823dfca9525f5cfc8b622d7f07e9891a280f61b8c96c", size = 305655, upload-time = "2025-10-17T11:29:18.859Z" }, - { url = "https://files.pythonhosted.org/packages/f7/29/6c7de6b5d6e511d9e736312c0c9bfcee8f9b6bef68182a08b1d78767e627/jiter-0.11.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ecd4da91b5415f183a6be8f7158d127bdd9e6a3174138293c0d48d6ea2f2009d", size = 315645, upload-time = "2025-10-17T11:29:20.889Z" }, - { url = "https://files.pythonhosted.org/packages/ac/5f/ef9e5675511ee0eb7f98dd8c90509e1f7743dbb7c350071acae87b0145f3/jiter-0.11.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7e3ac25c00b9275684d47aa42febaa90a9958e19fd1726c4ecf755fbe5e553b", size = 348003, upload-time = "2025-10-17T11:29:22.712Z" }, - { url = "https://files.pythonhosted.org/packages/56/1b/abe8c4021010b0a320d3c62682769b700fb66f92c6db02d1a1381b3db025/jiter-0.11.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:57d7305c0a841858f866cd459cd9303f73883fb5e097257f3d4a3920722c69d4", size = 365122, upload-time = "2025-10-17T11:29:24.408Z" }, - { url = "https://files.pythonhosted.org/packages/2a/2d/4a18013939a4f24432f805fbd5a19893e64650b933edb057cd405275a538/jiter-0.11.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e86fa10e117dce22c547f31dd6d2a9a222707d54853d8de4e9a2279d2c97f239", size = 488360, upload-time = "2025-10-17T11:29:25.724Z" }, - { url = "https://files.pythonhosted.org/packages/f0/77/38124f5d02ac4131f0dfbcfd1a19a0fac305fa2c005bc4f9f0736914a1a4/jiter-0.11.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ae5ef1d48aec7e01ee8420155d901bb1d192998fa811a65ebb82c043ee186711", size = 376884, upload-time = "2025-10-17T11:29:27.056Z" }, - { url = "https://files.pythonhosted.org/packages/7b/43/59fdc2f6267959b71dd23ce0bd8d4aeaf55566aa435a5d00f53d53c7eb24/jiter-0.11.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb68e7bf65c990531ad8715e57d50195daf7c8e6f1509e617b4e692af1108939", size = 358827, upload-time = "2025-10-17T11:29:28.698Z" }, - { url = "https://files.pythonhosted.org/packages/7d/d0/b3cc20ff5340775ea3bbaa0d665518eddecd4266ba7244c9cb480c0c82ec/jiter-0.11.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:43b30c8154ded5845fa454ef954ee67bfccce629b2dea7d01f795b42bc2bda54", size = 385171, upload-time = "2025-10-17T11:29:30.078Z" }, - { url = "https://files.pythonhosted.org/packages/d2/bc/94dd1f3a61f4dc236f787a097360ec061ceeebebf4ea120b924d91391b10/jiter-0.11.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:586cafbd9dd1f3ce6a22b4a085eaa6be578e47ba9b18e198d4333e598a91db2d", size = 518359, upload-time = "2025-10-17T11:29:31.464Z" }, - { url = "https://files.pythonhosted.org/packages/7e/8c/12ee132bd67e25c75f542c227f5762491b9a316b0dad8e929c95076f773c/jiter-0.11.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:677cc2517d437a83bb30019fd4cf7cad74b465914c56ecac3440d597ac135250", size = 509205, upload-time = "2025-10-17T11:29:32.895Z" }, - { url = "https://files.pythonhosted.org/packages/39/d5/9de848928ce341d463c7e7273fce90ea6d0ea4343cd761f451860fa16b59/jiter-0.11.1-cp312-cp312-win32.whl", hash = "sha256:fa992af648fcee2b850a3286a35f62bbbaeddbb6dbda19a00d8fbc846a947b6e", size = 205448, upload-time = "2025-10-17T11:29:34.217Z" }, - { url = "https://files.pythonhosted.org/packages/ee/b0/8002d78637e05009f5e3fb5288f9d57d65715c33b5d6aa20fd57670feef5/jiter-0.11.1-cp312-cp312-win_amd64.whl", hash = "sha256:88b5cae9fa51efeb3d4bd4e52bfd4c85ccc9cac44282e2a9640893a042ba4d87", size = 204285, upload-time = "2025-10-17T11:29:35.446Z" }, - { url = "https://files.pythonhosted.org/packages/9f/a2/bb24d5587e4dff17ff796716542f663deee337358006a80c8af43ddc11e5/jiter-0.11.1-cp312-cp312-win_arm64.whl", hash = "sha256:9a6cae1ab335551917f882f2c3c1efe7617b71b4c02381e4382a8fc80a02588c", size = 188712, upload-time = "2025-10-17T11:29:37.027Z" }, - { url = "https://files.pythonhosted.org/packages/9d/51/bd41562dd284e2a18b6dc0a99d195fd4a3560d52ab192c42e56fe0316643/jiter-0.11.1-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:e642b5270e61dd02265866398707f90e365b5db2eb65a4f30c789d826682e1f6", size = 306871, upload-time = "2025-10-17T11:31:03.616Z" }, - { url = "https://files.pythonhosted.org/packages/ba/cb/64e7f21dd357e8cd6b3c919c26fac7fc198385bbd1d85bb3b5355600d787/jiter-0.11.1-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:464ba6d000585e4e2fd1e891f31f1231f497273414f5019e27c00a4b8f7a24ad", size = 301454, upload-time = "2025-10-17T11:31:05.338Z" }, - { url = "https://files.pythonhosted.org/packages/55/b0/54bdc00da4ef39801b1419a01035bd8857983de984fd3776b0be6b94add7/jiter-0.11.1-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:055568693ab35e0bf3a171b03bb40b2dcb10352359e0ab9b5ed0da2bf1eb6f6f", size = 336801, upload-time = "2025-10-17T11:31:06.893Z" }, - { url = "https://files.pythonhosted.org/packages/de/8f/87176ed071d42e9db415ed8be787ef4ef31a4fa27f52e6a4fbf34387bd28/jiter-0.11.1-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0c69ea798d08a915ba4478113efa9e694971e410056392f4526d796f136d3fa", size = 343452, upload-time = "2025-10-17T11:31:08.259Z" }, - { url = "https://files.pythonhosted.org/packages/a6/bc/950dd7f170c6394b6fdd73f989d9e729bd98907bcc4430ef080a72d06b77/jiter-0.11.1-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:0d4d6993edc83cf75e8c6828a8d6ce40a09ee87e38c7bfba6924f39e1337e21d", size = 302626, upload-time = "2025-10-17T11:31:09.645Z" }, - { url = "https://files.pythonhosted.org/packages/3a/65/43d7971ca82ee100b7b9b520573eeef7eabc0a45d490168ebb9a9b5bb8b2/jiter-0.11.1-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:f78d151c83a87a6cf5461d5ee55bc730dd9ae227377ac6f115b922989b95f838", size = 297034, upload-time = "2025-10-17T11:31:10.975Z" }, - { url = "https://files.pythonhosted.org/packages/19/4c/000e1e0c0c67e96557a279f8969487ea2732d6c7311698819f977abae837/jiter-0.11.1-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c9022974781155cd5521d5cb10997a03ee5e31e8454c9d999dcdccd253f2353f", size = 337328, upload-time = "2025-10-17T11:31:12.399Z" }, - { url = "https://files.pythonhosted.org/packages/d9/71/71408b02c6133153336d29fa3ba53000f1e1a3f78bb2fc2d1a1865d2e743/jiter-0.11.1-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18c77aaa9117510d5bdc6a946baf21b1f0cfa58ef04d31c8d016f206f2118960", size = 343697, upload-time = "2025-10-17T11:31:13.773Z" }, +version = "0.12.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/45/9d/e0660989c1370e25848bb4c52d061c71837239738ad937e83edca174c273/jiter-0.12.0.tar.gz", hash = "sha256:64dfcd7d5c168b38d3f9f8bba7fc639edb3418abcc74f22fdbe6b8938293f30b", size = 168294, upload-time = "2025-11-09T20:49:23.302Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/32/f9/eaca4633486b527ebe7e681c431f529b63fe2709e7c5242fc0f43f77ce63/jiter-0.12.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:d8f8a7e317190b2c2d60eb2e8aa835270b008139562d70fe732e1c0020ec53c9", size = 316435, upload-time = "2025-11-09T20:47:02.087Z" }, + { url = "https://files.pythonhosted.org/packages/10/c1/40c9f7c22f5e6ff715f28113ebaba27ab85f9af2660ad6e1dd6425d14c19/jiter-0.12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2218228a077e784c6c8f1a8e5d6b8cb1dea62ce25811c356364848554b2056cd", size = 320548, upload-time = "2025-11-09T20:47:03.409Z" }, + { url = "https://files.pythonhosted.org/packages/6b/1b/efbb68fe87e7711b00d2cfd1f26bb4bfc25a10539aefeaa7727329ffb9cb/jiter-0.12.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9354ccaa2982bf2188fd5f57f79f800ef622ec67beb8329903abf6b10da7d423", size = 351915, upload-time = "2025-11-09T20:47:05.171Z" }, + { url = "https://files.pythonhosted.org/packages/15/2d/c06e659888c128ad1e838123d0638f0efad90cc30860cb5f74dd3f2fc0b3/jiter-0.12.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8f2607185ea89b4af9a604d4c7ec40e45d3ad03ee66998b031134bc510232bb7", size = 368966, upload-time = "2025-11-09T20:47:06.508Z" }, + { url = "https://files.pythonhosted.org/packages/6b/20/058db4ae5fb07cf6a4ab2e9b9294416f606d8e467fb74c2184b2a1eeacba/jiter-0.12.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3a585a5e42d25f2e71db5f10b171f5e5ea641d3aa44f7df745aa965606111cc2", size = 482047, upload-time = "2025-11-09T20:47:08.382Z" }, + { url = "https://files.pythonhosted.org/packages/49/bb/dc2b1c122275e1de2eb12905015d61e8316b2f888bdaac34221c301495d6/jiter-0.12.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd9e21d34edff5a663c631f850edcb786719c960ce887a5661e9c828a53a95d9", size = 380835, upload-time = "2025-11-09T20:47:09.81Z" }, + { url = "https://files.pythonhosted.org/packages/23/7d/38f9cd337575349de16da575ee57ddb2d5a64d425c9367f5ef9e4612e32e/jiter-0.12.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a612534770470686cd5431478dc5a1b660eceb410abade6b1b74e320ca98de6", size = 364587, upload-time = "2025-11-09T20:47:11.529Z" }, + { url = "https://files.pythonhosted.org/packages/f0/a3/b13e8e61e70f0bb06085099c4e2462647f53cc2ca97614f7fedcaa2bb9f3/jiter-0.12.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3985aea37d40a908f887b34d05111e0aae822943796ebf8338877fee2ab67725", size = 390492, upload-time = "2025-11-09T20:47:12.993Z" }, + { url = "https://files.pythonhosted.org/packages/07/71/e0d11422ed027e21422f7bc1883c61deba2d9752b720538430c1deadfbca/jiter-0.12.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b1207af186495f48f72529f8d86671903c8c10127cac6381b11dddc4aaa52df6", size = 522046, upload-time = "2025-11-09T20:47:14.6Z" }, + { url = "https://files.pythonhosted.org/packages/9f/59/b968a9aa7102a8375dbbdfbd2aeebe563c7e5dddf0f47c9ef1588a97e224/jiter-0.12.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ef2fb241de583934c9915a33120ecc06d94aa3381a134570f59eed784e87001e", size = 513392, upload-time = "2025-11-09T20:47:16.011Z" }, + { url = "https://files.pythonhosted.org/packages/ca/e4/7df62002499080dbd61b505c5cb351aa09e9959d176cac2aa8da6f93b13b/jiter-0.12.0-cp311-cp311-win32.whl", hash = "sha256:453b6035672fecce8007465896a25b28a6b59cfe8fbc974b2563a92f5a92a67c", size = 206096, upload-time = "2025-11-09T20:47:17.344Z" }, + { url = "https://files.pythonhosted.org/packages/bb/60/1032b30ae0572196b0de0e87dce3b6c26a1eff71aad5fe43dee3082d32e0/jiter-0.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:ca264b9603973c2ad9435c71a8ec8b49f8f715ab5ba421c85a51cde9887e421f", size = 204899, upload-time = "2025-11-09T20:47:19.365Z" }, + { url = "https://files.pythonhosted.org/packages/49/d5/c145e526fccdb834063fb45c071df78b0cc426bbaf6de38b0781f45d956f/jiter-0.12.0-cp311-cp311-win_arm64.whl", hash = "sha256:cb00ef392e7d684f2754598c02c409f376ddcef857aae796d559e6cacc2d78a5", size = 188070, upload-time = "2025-11-09T20:47:20.75Z" }, + { url = "https://files.pythonhosted.org/packages/92/c9/5b9f7b4983f1b542c64e84165075335e8a236fa9e2ea03a0c79780062be8/jiter-0.12.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:305e061fa82f4680607a775b2e8e0bcb071cd2205ac38e6ef48c8dd5ebe1cf37", size = 314449, upload-time = "2025-11-09T20:47:22.999Z" }, + { url = "https://files.pythonhosted.org/packages/98/6e/e8efa0e78de00db0aee82c0cf9e8b3f2027efd7f8a71f859d8f4be8e98ef/jiter-0.12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5c1860627048e302a528333c9307c818c547f214d8659b0705d2195e1a94b274", size = 319855, upload-time = "2025-11-09T20:47:24.779Z" }, + { url = "https://files.pythonhosted.org/packages/20/26/894cd88e60b5d58af53bec5c6759d1292bd0b37a8b5f60f07abf7a63ae5f/jiter-0.12.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df37577a4f8408f7e0ec3205d2a8f87672af8f17008358063a4d6425b6081ce3", size = 350171, upload-time = "2025-11-09T20:47:26.469Z" }, + { url = "https://files.pythonhosted.org/packages/f5/27/a7b818b9979ac31b3763d25f3653ec3a954044d5e9f5d87f2f247d679fd1/jiter-0.12.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:75fdd787356c1c13a4f40b43c2156276ef7a71eb487d98472476476d803fb2cf", size = 365590, upload-time = "2025-11-09T20:47:27.918Z" }, + { url = "https://files.pythonhosted.org/packages/ba/7e/e46195801a97673a83746170b17984aa8ac4a455746354516d02ca5541b4/jiter-0.12.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1eb5db8d9c65b112aacf14fcd0faae9913d07a8afea5ed06ccdd12b724e966a1", size = 479462, upload-time = "2025-11-09T20:47:29.654Z" }, + { url = "https://files.pythonhosted.org/packages/ca/75/f833bfb009ab4bd11b1c9406d333e3b4357709ed0570bb48c7c06d78c7dd/jiter-0.12.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:73c568cc27c473f82480abc15d1301adf333a7ea4f2e813d6a2c7d8b6ba8d0df", size = 378983, upload-time = "2025-11-09T20:47:31.026Z" }, + { url = "https://files.pythonhosted.org/packages/71/b3/7a69d77943cc837d30165643db753471aff5df39692d598da880a6e51c24/jiter-0.12.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4321e8a3d868919bcb1abb1db550d41f2b5b326f72df29e53b2df8b006eb9403", size = 361328, upload-time = "2025-11-09T20:47:33.286Z" }, + { url = "https://files.pythonhosted.org/packages/b0/ac/a78f90caf48d65ba70d8c6efc6f23150bc39dc3389d65bbec2a95c7bc628/jiter-0.12.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0a51bad79f8cc9cac2b4b705039f814049142e0050f30d91695a2d9a6611f126", size = 386740, upload-time = "2025-11-09T20:47:34.703Z" }, + { url = "https://files.pythonhosted.org/packages/39/b6/5d31c2cc8e1b6a6bcf3c5721e4ca0a3633d1ab4754b09bc7084f6c4f5327/jiter-0.12.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:2a67b678f6a5f1dd6c36d642d7db83e456bc8b104788262aaefc11a22339f5a9", size = 520875, upload-time = "2025-11-09T20:47:36.058Z" }, + { url = "https://files.pythonhosted.org/packages/30/b5/4df540fae4e9f68c54b8dab004bd8c943a752f0b00efd6e7d64aa3850339/jiter-0.12.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efe1a211fe1fd14762adea941e3cfd6c611a136e28da6c39272dbb7a1bbe6a86", size = 511457, upload-time = "2025-11-09T20:47:37.932Z" }, + { url = "https://files.pythonhosted.org/packages/07/65/86b74010e450a1a77b2c1aabb91d4a91dd3cd5afce99f34d75fd1ac64b19/jiter-0.12.0-cp312-cp312-win32.whl", hash = "sha256:d779d97c834b4278276ec703dc3fc1735fca50af63eb7262f05bdb4e62203d44", size = 204546, upload-time = "2025-11-09T20:47:40.47Z" }, + { url = "https://files.pythonhosted.org/packages/1c/c7/6659f537f9562d963488e3e55573498a442503ced01f7e169e96a6110383/jiter-0.12.0-cp312-cp312-win_amd64.whl", hash = "sha256:e8269062060212b373316fe69236096aaf4c49022d267c6736eebd66bbbc60bb", size = 205196, upload-time = "2025-11-09T20:47:41.794Z" }, + { url = "https://files.pythonhosted.org/packages/21/f4/935304f5169edadfec7f9c01eacbce4c90bb9a82035ac1de1f3bd2d40be6/jiter-0.12.0-cp312-cp312-win_arm64.whl", hash = "sha256:06cb970936c65de926d648af0ed3d21857f026b1cf5525cb2947aa5e01e05789", size = 186100, upload-time = "2025-11-09T20:47:43.007Z" }, + { url = "https://files.pythonhosted.org/packages/fe/54/5339ef1ecaa881c6948669956567a64d2670941925f245c434f494ffb0e5/jiter-0.12.0-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:4739a4657179ebf08f85914ce50332495811004cc1747852e8b2041ed2aab9b8", size = 311144, upload-time = "2025-11-09T20:49:10.503Z" }, + { url = "https://files.pythonhosted.org/packages/27/74/3446c652bffbd5e81ab354e388b1b5fc1d20daac34ee0ed11ff096b1b01a/jiter-0.12.0-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:41da8def934bf7bec16cb24bd33c0ca62126d2d45d81d17b864bd5ad721393c3", size = 305877, upload-time = "2025-11-09T20:49:12.269Z" }, + { url = "https://files.pythonhosted.org/packages/a1/f4/ed76ef9043450f57aac2d4fbeb27175aa0eb9c38f833be6ef6379b3b9a86/jiter-0.12.0-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c44ee814f499c082e69872d426b624987dbc5943ab06e9bbaa4f81989fdb79e", size = 340419, upload-time = "2025-11-09T20:49:13.803Z" }, + { url = "https://files.pythonhosted.org/packages/21/01/857d4608f5edb0664aa791a3d45702e1a5bcfff9934da74035e7b9803846/jiter-0.12.0-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cd2097de91cf03eaa27b3cbdb969addf83f0179c6afc41bbc4513705e013c65d", size = 347212, upload-time = "2025-11-09T20:49:15.643Z" }, + { url = "https://files.pythonhosted.org/packages/cb/f5/12efb8ada5f5c9edc1d4555fe383c1fb2eac05ac5859258a72d61981d999/jiter-0.12.0-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:e8547883d7b96ef2e5fe22b88f8a4c8725a56e7f4abafff20fd5272d634c7ecb", size = 309974, upload-time = "2025-11-09T20:49:17.187Z" }, + { url = "https://files.pythonhosted.org/packages/85/15/d6eb3b770f6a0d332675141ab3962fd4a7c270ede3515d9f3583e1d28276/jiter-0.12.0-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:89163163c0934854a668ed783a2546a0617f71706a2551a4a0666d91ab365d6b", size = 304233, upload-time = "2025-11-09T20:49:18.734Z" }, + { url = "https://files.pythonhosted.org/packages/8c/3e/e7e06743294eea2cf02ced6aa0ff2ad237367394e37a0e2b4a1108c67a36/jiter-0.12.0-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d96b264ab7d34bbb2312dedc47ce07cd53f06835eacbc16dde3761f47c3a9e7f", size = 338537, upload-time = "2025-11-09T20:49:20.317Z" }, + { url = "https://files.pythonhosted.org/packages/2f/9c/6753e6522b8d0ef07d3a3d239426669e984fb0eba15a315cdbc1253904e4/jiter-0.12.0-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c24e864cb30ab82311c6425655b0cdab0a98c5d973b065c66a3f020740c2324c", size = 346110, upload-time = "2025-11-09T20:49:21.817Z" }, ] [[package]] @@ -1069,7 +1069,7 @@ wheels = [ [[package]] name = "jupyterlab" -version = "4.4.10" +version = "4.5.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "async-lru" }, @@ -1086,9 +1086,9 @@ dependencies = [ { name = "tornado" }, { name = "traitlets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/6a/5d/75c42a48ff5fc826a7dff3fe4004cda47c54f9d981c351efacfbc9139d3c/jupyterlab-4.4.10.tar.gz", hash = "sha256:521c017508af4e1d6d9d8a9d90f47a11c61197ad63b2178342489de42540a615", size = 22969303, upload-time = "2025-10-22T14:50:58.768Z" } +sdist = { url = "https://files.pythonhosted.org/packages/df/e5/4fa382a796a6d8e2cd867816b64f1ff27f906e43a7a83ad9eb389e448cd8/jupyterlab-4.5.0.tar.gz", hash = "sha256:aec33d6d8f1225b495ee2cf20f0514f45e6df8e360bdd7ac9bace0b7ac5177ea", size = 23989880, upload-time = "2025-11-18T13:19:00.365Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f7/46/1eaa5db8d54a594bdade67afbcae42e9a2da676628be3eb39f36dcff6390/jupyterlab-4.4.10-py3-none-any.whl", hash = "sha256:65939ab4c8dcd0c42185c2d0d1a9d60b254dc8c46fc4fdb286b63c51e9358e07", size = 12293385, upload-time = "2025-10-22T14:50:54.075Z" }, + { url = "https://files.pythonhosted.org/packages/6c/1e/5a4d5498eba382fee667ed797cf64ae5d1b13b04356df62f067f48bb0f61/jupyterlab-4.5.0-py3-none-any.whl", hash = "sha256:88e157c75c1afff64c7dc4b801ec471450b922a4eae4305211ddd40da8201c8a", size = 12380641, upload-time = "2025-11-18T13:18:56.252Z" }, ] [[package]] @@ -1120,16 +1120,16 @@ wheels = [ [[package]] name = "jupyterlab-widgets" -version = "3.0.15" +version = "3.0.16" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b9/7d/160595ca88ee87ac6ba95d82177d29ec60aaa63821d3077babb22ce031a5/jupyterlab_widgets-3.0.15.tar.gz", hash = "sha256:2920888a0c2922351a9202817957a68c07d99673504d6cd37345299e971bb08b", size = 213149, upload-time = "2025-05-05T12:32:31.004Z" } +sdist = { url = "https://files.pythonhosted.org/packages/26/2d/ef58fed122b268c69c0aa099da20bc67657cdfb2e222688d5731bd5b971d/jupyterlab_widgets-3.0.16.tar.gz", hash = "sha256:423da05071d55cf27a9e602216d35a3a65a3e41cdf9c5d3b643b814ce38c19e0", size = 897423, upload-time = "2025-11-01T21:11:29.724Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/43/6a/ca128561b22b60bd5a0c4ea26649e68c8556b82bc70a0c396eebc977fe86/jupyterlab_widgets-3.0.15-py3-none-any.whl", hash = "sha256:d59023d7d7ef71400d51e6fee9a88867f6e65e10a4201605d2d7f3e8f012a31c", size = 216571, upload-time = "2025-05-05T12:32:29.534Z" }, + { url = "https://files.pythonhosted.org/packages/ab/b5/36c712098e6191d1b4e349304ef73a8d06aed77e56ceaac8c0a306c7bda1/jupyterlab_widgets-3.0.16-py3-none-any.whl", hash = "sha256:45fa36d9c6422cf2559198e4db481aa243c7a32d9926b500781c830c80f7ecf8", size = 914926, upload-time = "2025-11-01T21:11:28.008Z" }, ] [[package]] name = "langchain-core" -version = "1.0.1" +version = "1.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "jsonpatch" }, @@ -1140,9 +1140,9 @@ dependencies = [ { name = "tenacity" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e7/37/1bc4badb93eaa32406a7afdef011336b21719d21ce5ecebf35409a524f8c/langchain_core-1.0.1.tar.gz", hash = "sha256:d769e8d25854466abb672a721143a01bea11cc6ee2d7dae776aa092556db0a26", size = 764566, upload-time = "2025-10-24T16:39:35.495Z" } +sdist = { url = "https://files.pythonhosted.org/packages/1e/17/67c1cc2ace919e2b02dd9d783154d7fb3f1495a4ef835d9cd163b7855ac2/langchain_core-1.1.0.tar.gz", hash = "sha256:2b76a82d427922c8bc51c08404af4fc2a29e9f161dfe2297cb05091e810201e7", size = 781995, upload-time = "2025-11-21T21:01:26.958Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c0/54/3cdbe9d151d06cd689b5aa937ac11403b64bbfe76486fda6431a24061721/langchain_core-1.0.1-py3-none-any.whl", hash = "sha256:c7ce58fc487359c44166e255cc0009ef30290da0b6307b75091152847919661e", size = 467122, upload-time = "2025-10-24T16:39:33.788Z" }, + { url = "https://files.pythonhosted.org/packages/71/1e/e129fc471a2d2a7b3804480a937b5ab9319cab9f4142624fcb115f925501/langchain_core-1.1.0-py3-none-any.whl", hash = "sha256:2c9f27dadc6d21ed4aa46506a37a56e6a7e2d2f9141922dc5c251ba921822ee6", size = 473752, upload-time = "2025-11-21T21:01:25.841Z" }, ] [[package]] @@ -1159,7 +1159,7 @@ wheels = [ [[package]] name = "langsmith" -version = "0.4.38" +version = "0.4.47" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "httpx" }, @@ -1170,9 +1170,9 @@ dependencies = [ { name = "requests-toolbelt" }, { name = "zstandard" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/37/21/f1ba48412c64bf3bb8feb532fc9d247b396935b5d8242332d44a4195ec2d/langsmith-0.4.38.tar.gz", hash = "sha256:3aa57f9c16a5880256cd1eab0452533c1fb5ee14ec5250e23ed919cc2b07f6d3", size = 942789, upload-time = "2025-10-23T22:28:20.458Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ec/dd/d69922b79fb692b9736206574e5ba69b6354080cf1cc9796449d9fe61f9a/langsmith-0.4.47.tar.gz", hash = "sha256:6a576405696ee97147ccb96c9ae5c9437430500a5d118bd447ec2d1f8cf26de1", size = 986584, upload-time = "2025-11-24T16:02:00.914Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b4/2b/7e0248f65e35800ea8e4e3dbb3bcc36c61b81f5b8abeddaceec8320ab491/langsmith-0.4.38-py3-none-any.whl", hash = "sha256:326232a24b1c6dd308a3188557cc023adf8fb14144263b2982c115a6be5141e7", size = 397341, upload-time = "2025-10-23T22:28:18.333Z" }, + { url = "https://files.pythonhosted.org/packages/80/1a/0c84f7096d41d64425d29db549c8d6fe075f925a5f2022e8087d01d862c2/langsmith-0.4.47-py3-none-any.whl", hash = "sha256:b9e514611d4e1570e33595d33ccb1fe6eda9f96c5f961095a138651f746c1ef5", size = 411207, upload-time = "2025-11-24T16:01:59.123Z" }, ] [[package]] @@ -1392,7 +1392,7 @@ wheels = [ [[package]] name = "notebook" -version = "7.4.7" +version = "7.5.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "jupyter-server" }, @@ -1401,9 +1401,9 @@ dependencies = [ { name = "notebook-shim" }, { name = "tornado" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/04/09/f6f64ba156842ef68d3ea763fa171a2f7e7224f200a15dd4af5b83c34756/notebook-7.4.7.tar.gz", hash = "sha256:3f0a04027dfcee8a876de48fba13ab77ec8c12f72f848a222ed7f5081b9e342a", size = 13937702, upload-time = "2025-09-27T08:00:22.536Z" } +sdist = { url = "https://files.pythonhosted.org/packages/89/ac/a97041621250a4fc5af379fb377942841eea2ca146aab166b8fcdfba96c2/notebook-7.5.0.tar.gz", hash = "sha256:3b27eaf9913033c28dde92d02139414c608992e1df4b969c843219acf2ff95e4", size = 14052074, upload-time = "2025-11-19T08:36:20.093Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6c/d7/06d13087e20388926e7423d2489e728d2e59f2453039cdb0574a7c070e76/notebook-7.4.7-py3-none-any.whl", hash = "sha256:362b7c95527f7dd3c4c84d410b782872fd9c734fb2524c11dd92758527b6eda6", size = 14342894, upload-time = "2025-09-27T08:00:18.496Z" }, + { url = "https://files.pythonhosted.org/packages/73/96/00df2a4760f10f5af0f45c4955573cae6189931f9a30265a35865f8c1031/notebook-7.5.0-py3-none-any.whl", hash = "sha256:3300262d52905ca271bd50b22617681d95f08a8360d099e097726e6d2efb5811", size = 14460968, upload-time = "2025-11-19T08:36:15.869Z" }, ] [[package]] @@ -1420,39 +1420,39 @@ wheels = [ [[package]] name = "numpy" -version = "2.3.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b5/f4/098d2270d52b41f1bd7db9fc288aaa0400cb48c2a3e2af6fa365d9720947/numpy-2.3.4.tar.gz", hash = "sha256:a7d018bfedb375a8d979ac758b120ba846a7fe764911a64465fd87b8729f4a6a", size = 20582187, upload-time = "2025-10-15T16:18:11.77Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/60/e7/0e07379944aa8afb49a556a2b54587b828eb41dc9adc56fb7615b678ca53/numpy-2.3.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e78aecd2800b32e8347ce49316d3eaf04aed849cd5b38e0af39f829a4e59f5eb", size = 21259519, upload-time = "2025-10-15T16:15:19.012Z" }, - { url = "https://files.pythonhosted.org/packages/d0/cb/5a69293561e8819b09e34ed9e873b9a82b5f2ade23dce4c51dc507f6cfe1/numpy-2.3.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7fd09cc5d65bda1e79432859c40978010622112e9194e581e3415a3eccc7f43f", size = 14452796, upload-time = "2025-10-15T16:15:23.094Z" }, - { url = "https://files.pythonhosted.org/packages/e4/04/ff11611200acd602a1e5129e36cfd25bf01ad8e5cf927baf2e90236eb02e/numpy-2.3.4-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:1b219560ae2c1de48ead517d085bc2d05b9433f8e49d0955c82e8cd37bd7bf36", size = 5381639, upload-time = "2025-10-15T16:15:25.572Z" }, - { url = "https://files.pythonhosted.org/packages/ea/77/e95c757a6fe7a48d28a009267408e8aa382630cc1ad1db7451b3bc21dbb4/numpy-2.3.4-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:bafa7d87d4c99752d07815ed7a2c0964f8ab311eb8168f41b910bd01d15b6032", size = 6914296, upload-time = "2025-10-15T16:15:27.079Z" }, - { url = "https://files.pythonhosted.org/packages/a3/d2/137c7b6841c942124eae921279e5c41b1c34bab0e6fc60c7348e69afd165/numpy-2.3.4-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:36dc13af226aeab72b7abad501d370d606326a0029b9f435eacb3b8c94b8a8b7", size = 14591904, upload-time = "2025-10-15T16:15:29.044Z" }, - { url = "https://files.pythonhosted.org/packages/bb/32/67e3b0f07b0aba57a078c4ab777a9e8e6bc62f24fb53a2337f75f9691699/numpy-2.3.4-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a7b2f9a18b5ff9824a6af80de4f37f4ec3c2aab05ef08f51c77a093f5b89adda", size = 16939602, upload-time = "2025-10-15T16:15:31.106Z" }, - { url = "https://files.pythonhosted.org/packages/95/22/9639c30e32c93c4cee3ccdb4b09c2d0fbff4dcd06d36b357da06146530fb/numpy-2.3.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9984bd645a8db6ca15d850ff996856d8762c51a2239225288f08f9050ca240a0", size = 16372661, upload-time = "2025-10-15T16:15:33.546Z" }, - { url = "https://files.pythonhosted.org/packages/12/e9/a685079529be2b0156ae0c11b13d6be647743095bb51d46589e95be88086/numpy-2.3.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:64c5825affc76942973a70acf438a8ab618dbd692b84cd5ec40a0a0509edc09a", size = 18884682, upload-time = "2025-10-15T16:15:36.105Z" }, - { url = "https://files.pythonhosted.org/packages/cf/85/f6f00d019b0cc741e64b4e00ce865a57b6bed945d1bbeb1ccadbc647959b/numpy-2.3.4-cp311-cp311-win32.whl", hash = "sha256:ed759bf7a70342f7817d88376eb7142fab9fef8320d6019ef87fae05a99874e1", size = 6570076, upload-time = "2025-10-15T16:15:38.225Z" }, - { url = "https://files.pythonhosted.org/packages/7d/10/f8850982021cb90e2ec31990291f9e830ce7d94eef432b15066e7cbe0bec/numpy-2.3.4-cp311-cp311-win_amd64.whl", hash = "sha256:faba246fb30ea2a526c2e9645f61612341de1a83fb1e0c5edf4ddda5a9c10996", size = 13089358, upload-time = "2025-10-15T16:15:40.404Z" }, - { url = "https://files.pythonhosted.org/packages/d1/ad/afdd8351385edf0b3445f9e24210a9c3971ef4de8fd85155462fc4321d79/numpy-2.3.4-cp311-cp311-win_arm64.whl", hash = "sha256:4c01835e718bcebe80394fd0ac66c07cbb90147ebbdad3dcecd3f25de2ae7e2c", size = 10462292, upload-time = "2025-10-15T16:15:42.896Z" }, - { url = "https://files.pythonhosted.org/packages/96/7a/02420400b736f84317e759291b8edaeee9dc921f72b045475a9cbdb26b17/numpy-2.3.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ef1b5a3e808bc40827b5fa2c8196151a4c5abe110e1726949d7abddfe5c7ae11", size = 20957727, upload-time = "2025-10-15T16:15:44.9Z" }, - { url = "https://files.pythonhosted.org/packages/18/90/a014805d627aa5750f6f0e878172afb6454552da929144b3c07fcae1bb13/numpy-2.3.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c2f91f496a87235c6aaf6d3f3d89b17dba64996abadccb289f48456cff931ca9", size = 14187262, upload-time = "2025-10-15T16:15:47.761Z" }, - { url = "https://files.pythonhosted.org/packages/c7/e4/0a94b09abe89e500dc748e7515f21a13e30c5c3fe3396e6d4ac108c25fca/numpy-2.3.4-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:f77e5b3d3da652b474cc80a14084927a5e86a5eccf54ca8ca5cbd697bf7f2667", size = 5115992, upload-time = "2025-10-15T16:15:50.144Z" }, - { url = "https://files.pythonhosted.org/packages/88/dd/db77c75b055c6157cbd4f9c92c4458daef0dd9cbe6d8d2fe7f803cb64c37/numpy-2.3.4-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:8ab1c5f5ee40d6e01cbe96de5863e39b215a4d24e7d007cad56c7184fdf4aeef", size = 6648672, upload-time = "2025-10-15T16:15:52.442Z" }, - { url = "https://files.pythonhosted.org/packages/e1/e6/e31b0d713719610e406c0ea3ae0d90760465b086da8783e2fd835ad59027/numpy-2.3.4-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:77b84453f3adcb994ddbd0d1c5d11db2d6bda1a2b7fd5ac5bd4649d6f5dc682e", size = 14284156, upload-time = "2025-10-15T16:15:54.351Z" }, - { url = "https://files.pythonhosted.org/packages/f9/58/30a85127bfee6f108282107caf8e06a1f0cc997cb6b52cdee699276fcce4/numpy-2.3.4-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4121c5beb58a7f9e6dfdee612cb24f4df5cd4db6e8261d7f4d7450a997a65d6a", size = 16641271, upload-time = "2025-10-15T16:15:56.67Z" }, - { url = "https://files.pythonhosted.org/packages/06/f2/2e06a0f2adf23e3ae29283ad96959267938d0efd20a2e25353b70065bfec/numpy-2.3.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:65611ecbb00ac9846efe04db15cbe6186f562f6bb7e5e05f077e53a599225d16", size = 16059531, upload-time = "2025-10-15T16:15:59.412Z" }, - { url = "https://files.pythonhosted.org/packages/b0/e7/b106253c7c0d5dc352b9c8fab91afd76a93950998167fa3e5afe4ef3a18f/numpy-2.3.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dabc42f9c6577bcc13001b8810d300fe814b4cfbe8a92c873f269484594f9786", size = 18578983, upload-time = "2025-10-15T16:16:01.804Z" }, - { url = "https://files.pythonhosted.org/packages/73/e3/04ecc41e71462276ee867ccbef26a4448638eadecf1bc56772c9ed6d0255/numpy-2.3.4-cp312-cp312-win32.whl", hash = "sha256:a49d797192a8d950ca59ee2d0337a4d804f713bb5c3c50e8db26d49666e351dc", size = 6291380, upload-time = "2025-10-15T16:16:03.938Z" }, - { url = "https://files.pythonhosted.org/packages/3d/a8/566578b10d8d0e9955b1b6cd5db4e9d4592dd0026a941ff7994cedda030a/numpy-2.3.4-cp312-cp312-win_amd64.whl", hash = "sha256:985f1e46358f06c2a09921e8921e2c98168ed4ae12ccd6e5e87a4f1857923f32", size = 12787999, upload-time = "2025-10-15T16:16:05.801Z" }, - { url = "https://files.pythonhosted.org/packages/58/22/9c903a957d0a8071b607f5b1bff0761d6e608b9a965945411f867d515db1/numpy-2.3.4-cp312-cp312-win_arm64.whl", hash = "sha256:4635239814149e06e2cb9db3dd584b2fa64316c96f10656983b8026a82e6e4db", size = 10197412, upload-time = "2025-10-15T16:16:07.854Z" }, - { url = "https://files.pythonhosted.org/packages/b1/b6/64898f51a86ec88ca1257a59c1d7fd077b60082a119affefcdf1dd0df8ca/numpy-2.3.4-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:6e274603039f924c0fe5cb73438fa9246699c78a6df1bd3decef9ae592ae1c05", size = 21131552, upload-time = "2025-10-15T16:17:55.845Z" }, - { url = "https://files.pythonhosted.org/packages/ce/4c/f135dc6ebe2b6a3c77f4e4838fa63d350f85c99462012306ada1bd4bc460/numpy-2.3.4-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d149aee5c72176d9ddbc6803aef9c0f6d2ceeea7626574fc68518da5476fa346", size = 14377796, upload-time = "2025-10-15T16:17:58.308Z" }, - { url = "https://files.pythonhosted.org/packages/d0/a4/f33f9c23fcc13dd8412fc8614559b5b797e0aba9d8e01dfa8bae10c84004/numpy-2.3.4-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:6d34ed9db9e6395bb6cd33286035f73a59b058169733a9db9f85e650b88df37e", size = 5306904, upload-time = "2025-10-15T16:18:00.596Z" }, - { url = "https://files.pythonhosted.org/packages/28/af/c44097f25f834360f9fb960fa082863e0bad14a42f36527b2a121abdec56/numpy-2.3.4-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:fdebe771ca06bb8d6abce84e51dca9f7921fe6ad34a0c914541b063e9a68928b", size = 6819682, upload-time = "2025-10-15T16:18:02.32Z" }, - { url = "https://files.pythonhosted.org/packages/c5/8c/cd283b54c3c2b77e188f63e23039844f56b23bba1712318288c13fe86baf/numpy-2.3.4-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:957e92defe6c08211eb77902253b14fe5b480ebc5112bc741fd5e9cd0608f847", size = 14422300, upload-time = "2025-10-15T16:18:04.271Z" }, - { url = "https://files.pythonhosted.org/packages/b0/f0/8404db5098d92446b3e3695cf41c6f0ecb703d701cb0b7566ee2177f2eee/numpy-2.3.4-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:13b9062e4f5c7ee5c7e5be96f29ba71bc5a37fed3d1d77c37390ae00724d296d", size = 16760806, upload-time = "2025-10-15T16:18:06.668Z" }, - { url = "https://files.pythonhosted.org/packages/95/8e/2844c3959ce9a63acc7c8e50881133d86666f0420bcde695e115ced0920f/numpy-2.3.4-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:81b3a59793523e552c4a96109dde028aa4448ae06ccac5a76ff6532a85558a7f", size = 12973130, upload-time = "2025-10-15T16:18:09.397Z" }, +version = "2.3.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/76/65/21b3bc86aac7b8f2862db1e808f1ea22b028e30a225a34a5ede9bf8678f2/numpy-2.3.5.tar.gz", hash = "sha256:784db1dcdab56bf0517743e746dfb0f885fc68d948aba86eeec2cba234bdf1c0", size = 20584950, upload-time = "2025-11-16T22:52:42.067Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/43/77/84dd1d2e34d7e2792a236ba180b5e8fcc1e3e414e761ce0253f63d7f572e/numpy-2.3.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:de5672f4a7b200c15a4127042170a694d4df43c992948f5e1af57f0174beed10", size = 17034641, upload-time = "2025-11-16T22:49:19.336Z" }, + { url = "https://files.pythonhosted.org/packages/2a/ea/25e26fa5837106cde46ae7d0b667e20f69cbbc0efd64cba8221411ab26ae/numpy-2.3.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:acfd89508504a19ed06ef963ad544ec6664518c863436306153e13e94605c218", size = 12528324, upload-time = "2025-11-16T22:49:22.582Z" }, + { url = "https://files.pythonhosted.org/packages/4d/1a/e85f0eea4cf03d6a0228f5c0256b53f2df4bc794706e7df019fc622e47f1/numpy-2.3.5-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:ffe22d2b05504f786c867c8395de703937f934272eb67586817b46188b4ded6d", size = 5356872, upload-time = "2025-11-16T22:49:25.408Z" }, + { url = "https://files.pythonhosted.org/packages/5c/bb/35ef04afd567f4c989c2060cde39211e4ac5357155c1833bcd1166055c61/numpy-2.3.5-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:872a5cf366aec6bb1147336480fef14c9164b154aeb6542327de4970282cd2f5", size = 6893148, upload-time = "2025-11-16T22:49:27.549Z" }, + { url = "https://files.pythonhosted.org/packages/f2/2b/05bbeb06e2dff5eab512dfc678b1cc5ee94d8ac5956a0885c64b6b26252b/numpy-2.3.5-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3095bdb8dd297e5920b010e96134ed91d852d81d490e787beca7e35ae1d89cf7", size = 14557282, upload-time = "2025-11-16T22:49:30.964Z" }, + { url = "https://files.pythonhosted.org/packages/65/fb/2b23769462b34398d9326081fad5655198fcf18966fcb1f1e49db44fbf31/numpy-2.3.5-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8cba086a43d54ca804ce711b2a940b16e452807acebe7852ff327f1ecd49b0d4", size = 16897903, upload-time = "2025-11-16T22:49:34.191Z" }, + { url = "https://files.pythonhosted.org/packages/ac/14/085f4cf05fc3f1e8aa95e85404e984ffca9b2275a5dc2b1aae18a67538b8/numpy-2.3.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6cf9b429b21df6b99f4dee7a1218b8b7ffbbe7df8764dc0bd60ce8a0708fed1e", size = 16341672, upload-time = "2025-11-16T22:49:37.2Z" }, + { url = "https://files.pythonhosted.org/packages/6f/3b/1f73994904142b2aa290449b3bb99772477b5fd94d787093e4f24f5af763/numpy-2.3.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:396084a36abdb603546b119d96528c2f6263921c50df3c8fd7cb28873a237748", size = 18838896, upload-time = "2025-11-16T22:49:39.727Z" }, + { url = "https://files.pythonhosted.org/packages/cd/b9/cf6649b2124f288309ffc353070792caf42ad69047dcc60da85ee85fea58/numpy-2.3.5-cp311-cp311-win32.whl", hash = "sha256:b0c7088a73aef3d687c4deef8452a3ac7c1be4e29ed8bf3b366c8111128ac60c", size = 6563608, upload-time = "2025-11-16T22:49:42.079Z" }, + { url = "https://files.pythonhosted.org/packages/aa/44/9fe81ae1dcc29c531843852e2874080dc441338574ccc4306b39e2ff6e59/numpy-2.3.5-cp311-cp311-win_amd64.whl", hash = "sha256:a414504bef8945eae5f2d7cb7be2d4af77c5d1cb5e20b296c2c25b61dff2900c", size = 13078442, upload-time = "2025-11-16T22:49:43.99Z" }, + { url = "https://files.pythonhosted.org/packages/6d/a7/f99a41553d2da82a20a2f22e93c94f928e4490bb447c9ff3c4ff230581d3/numpy-2.3.5-cp311-cp311-win_arm64.whl", hash = "sha256:0cd00b7b36e35398fa2d16af7b907b65304ef8bb4817a550e06e5012929830fa", size = 10458555, upload-time = "2025-11-16T22:49:47.092Z" }, + { url = "https://files.pythonhosted.org/packages/44/37/e669fe6cbb2b96c62f6bbedc6a81c0f3b7362f6a59230b23caa673a85721/numpy-2.3.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:74ae7b798248fe62021dbf3c914245ad45d1a6b0cb4a29ecb4b31d0bfbc4cc3e", size = 16733873, upload-time = "2025-11-16T22:49:49.84Z" }, + { url = "https://files.pythonhosted.org/packages/c5/65/df0db6c097892c9380851ab9e44b52d4f7ba576b833996e0080181c0c439/numpy-2.3.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ee3888d9ff7c14604052b2ca5535a30216aa0a58e948cdd3eeb8d3415f638769", size = 12259838, upload-time = "2025-11-16T22:49:52.863Z" }, + { url = "https://files.pythonhosted.org/packages/5b/e1/1ee06e70eb2136797abe847d386e7c0e830b67ad1d43f364dd04fa50d338/numpy-2.3.5-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:612a95a17655e213502f60cfb9bf9408efdc9eb1d5f50535cc6eb365d11b42b5", size = 5088378, upload-time = "2025-11-16T22:49:55.055Z" }, + { url = "https://files.pythonhosted.org/packages/6d/9c/1ca85fb86708724275103b81ec4cf1ac1d08f465368acfc8da7ab545bdae/numpy-2.3.5-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:3101e5177d114a593d79dd79658650fe28b5a0d8abeb8ce6f437c0e6df5be1a4", size = 6628559, upload-time = "2025-11-16T22:49:57.371Z" }, + { url = "https://files.pythonhosted.org/packages/74/78/fcd41e5a0ce4f3f7b003da85825acddae6d7ecb60cf25194741b036ca7d6/numpy-2.3.5-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b973c57ff8e184109db042c842423ff4f60446239bd585a5131cc47f06f789d", size = 14250702, upload-time = "2025-11-16T22:49:59.632Z" }, + { url = "https://files.pythonhosted.org/packages/b6/23/2a1b231b8ff672b4c450dac27164a8b2ca7d9b7144f9c02d2396518352eb/numpy-2.3.5-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0d8163f43acde9a73c2a33605353a4f1bc4798745a8b1d73183b28e5b435ae28", size = 16606086, upload-time = "2025-11-16T22:50:02.127Z" }, + { url = "https://files.pythonhosted.org/packages/a0/c5/5ad26fbfbe2012e190cc7d5003e4d874b88bb18861d0829edc140a713021/numpy-2.3.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:51c1e14eb1e154ebd80e860722f9e6ed6ec89714ad2db2d3aa33c31d7c12179b", size = 16025985, upload-time = "2025-11-16T22:50:04.536Z" }, + { url = "https://files.pythonhosted.org/packages/d2/fa/dd48e225c46c819288148d9d060b047fd2a6fb1eb37eae25112ee4cb4453/numpy-2.3.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b46b4ec24f7293f23adcd2d146960559aaf8020213de8ad1909dba6c013bf89c", size = 18542976, upload-time = "2025-11-16T22:50:07.557Z" }, + { url = "https://files.pythonhosted.org/packages/05/79/ccbd23a75862d95af03d28b5c6901a1b7da4803181513d52f3b86ed9446e/numpy-2.3.5-cp312-cp312-win32.whl", hash = "sha256:3997b5b3c9a771e157f9aae01dd579ee35ad7109be18db0e85dbdbe1de06e952", size = 6285274, upload-time = "2025-11-16T22:50:10.746Z" }, + { url = "https://files.pythonhosted.org/packages/2d/57/8aeaf160312f7f489dea47ab61e430b5cb051f59a98ae68b7133ce8fa06a/numpy-2.3.5-cp312-cp312-win_amd64.whl", hash = "sha256:86945f2ee6d10cdfd67bcb4069c1662dd711f7e2a4343db5cecec06b87cf31aa", size = 12782922, upload-time = "2025-11-16T22:50:12.811Z" }, + { url = "https://files.pythonhosted.org/packages/78/a6/aae5cc2ca78c45e64b9ef22f089141d661516856cf7c8a54ba434576900d/numpy-2.3.5-cp312-cp312-win_arm64.whl", hash = "sha256:f28620fe26bee16243be2b7b874da327312240a7cdc38b769a697578d2100013", size = 10194667, upload-time = "2025-11-16T22:50:16.16Z" }, + { url = "https://files.pythonhosted.org/packages/c6/65/f9dea8e109371ade9c782b4e4756a82edf9d3366bca495d84d79859a0b79/numpy-2.3.5-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:f0963b55cdd70fad460fa4c1341f12f976bb26cb66021a5580329bd498988310", size = 16910689, upload-time = "2025-11-16T22:52:23.247Z" }, + { url = "https://files.pythonhosted.org/packages/00/4f/edb00032a8fb92ec0a679d3830368355da91a69cab6f3e9c21b64d0bb986/numpy-2.3.5-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:f4255143f5160d0de972d28c8f9665d882b5f61309d8362fdd3e103cf7bf010c", size = 12457053, upload-time = "2025-11-16T22:52:26.367Z" }, + { url = "https://files.pythonhosted.org/packages/16/a4/e8a53b5abd500a63836a29ebe145fc1ab1f2eefe1cfe59276020373ae0aa/numpy-2.3.5-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:a4b9159734b326535f4dd01d947f919c6eefd2d9827466a696c44ced82dfbc18", size = 5285635, upload-time = "2025-11-16T22:52:29.266Z" }, + { url = "https://files.pythonhosted.org/packages/a3/2f/37eeb9014d9c8b3e9c55bc599c68263ca44fdbc12a93e45a21d1d56df737/numpy-2.3.5-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:2feae0d2c91d46e59fcd62784a3a83b3fb677fead592ce51b5a6fbb4f95965ff", size = 6801770, upload-time = "2025-11-16T22:52:31.421Z" }, + { url = "https://files.pythonhosted.org/packages/7d/e4/68d2f474df2cb671b2b6c2986a02e520671295647dad82484cde80ca427b/numpy-2.3.5-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ffac52f28a7849ad7576293c0cb7b9f08304e8f7d738a8cb8a90ec4c55a998eb", size = 14391768, upload-time = "2025-11-16T22:52:33.593Z" }, + { url = "https://files.pythonhosted.org/packages/b8/50/94ccd8a2b141cb50651fddd4f6a48874acb3c91c8f0842b08a6afc4b0b21/numpy-2.3.5-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:63c0e9e7eea69588479ebf4a8a270d5ac22763cc5854e9a7eae952a3908103f7", size = 16729263, upload-time = "2025-11-16T22:52:36.369Z" }, + { url = "https://files.pythonhosted.org/packages/2d/ee/346fa473e666fe14c52fcdd19ec2424157290a032d4c41f98127bfb31ac7/numpy-2.3.5-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:f16417ec91f12f814b10bafe79ef77e70113a2f5f7018640e7425ff979253425", size = 12967213, upload-time = "2025-11-16T22:52:39.38Z" }, ] [[package]] @@ -1626,7 +1626,7 @@ wheels = [ [[package]] name = "pre-commit" -version = "4.3.0" +version = "4.5.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cfgv" }, @@ -1635,9 +1635,9 @@ dependencies = [ { name = "pyyaml" }, { name = "virtualenv" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ff/29/7cf5bbc236333876e4b41f56e06857a87937ce4bf91e117a6991a2dbb02a/pre_commit-4.3.0.tar.gz", hash = "sha256:499fe450cc9d42e9d58e606262795ecb64dd05438943c62b66f6a8673da30b16", size = 193792, upload-time = "2025-08-09T18:56:14.651Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f4/9b/6a4ffb4ed980519da959e1cf3122fc6cb41211daa58dbae1c73c0e519a37/pre_commit-4.5.0.tar.gz", hash = "sha256:dc5a065e932b19fc1d4c653c6939068fe54325af8e741e74e88db4d28a4dd66b", size = 198428, upload-time = "2025-11-22T21:02:42.304Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5b/a5/987a405322d78a73b66e39e4a90e4ef156fd7141bf71df987e50717c321b/pre_commit-4.3.0-py2.py3-none-any.whl", hash = "sha256:2b0747ad7e6e967169136edffee14c16e148a778a54e4f967921aa1ebf2308d8", size = 220965, upload-time = "2025-08-09T18:56:13.192Z" }, + { url = "https://files.pythonhosted.org/packages/5d/c4/b2d28e9d2edf4f1713eb3c29307f1a63f3d67cf09bdda29715a36a68921a/pre_commit-4.5.0-py2.py3-none-any.whl", hash = "sha256:25e2ce09595174d9c97860a95609f9f852c0614ba602de3561e267547f2335e1", size = 226429, upload-time = "2025-11-22T21:02:40.836Z" }, ] [[package]] @@ -1702,16 +1702,16 @@ wheels = [ [[package]] name = "psutil" -version = "7.1.2" +version = "7.1.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cd/ec/7b8e6b9b1d22708138630ef34c53ab2b61032c04f16adfdbb96791c8c70c/psutil-7.1.2.tar.gz", hash = "sha256:aa225cdde1335ff9684708ee8c72650f6598d5ed2114b9a7c5802030b1785018", size = 487424, upload-time = "2025-10-25T10:46:34.931Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e1/88/bdd0a41e5857d5d703287598cbf08dad90aed56774ea52ae071bae9071b6/psutil-7.1.3.tar.gz", hash = "sha256:6c86281738d77335af7aec228328e944b30930899ea760ecf33a4dba66be5e74", size = 489059, upload-time = "2025-11-02T12:25:54.619Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ae/89/b9f8d47ddbc52d7301fc868e8224e5f44ed3c7f55e6d0f54ecaf5dd9ff5e/psutil-7.1.2-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:c9ba5c19f2d46203ee8c152c7b01df6eec87d883cfd8ee1af2ef2727f6b0f814", size = 237244, upload-time = "2025-10-25T10:47:07.086Z" }, - { url = "https://files.pythonhosted.org/packages/c8/7a/8628c2f6b240680a67d73d8742bb9ff39b1820a693740e43096d5dcb01e5/psutil-7.1.2-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:2a486030d2fe81bec023f703d3d155f4823a10a47c36784c84f1cc7f8d39bedb", size = 238101, upload-time = "2025-10-25T10:47:09.523Z" }, - { url = "https://files.pythonhosted.org/packages/30/28/5e27f4d5a0e347f8e3cc16cd7d35533dbce086c95807f1f0e9cd77e26c10/psutil-7.1.2-cp36-abi3-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3efd8fc791492e7808a51cb2b94889db7578bfaea22df931424f874468e389e3", size = 258675, upload-time = "2025-10-25T10:47:11.082Z" }, - { url = "https://files.pythonhosted.org/packages/e5/5c/79cf60c9acf36d087f0db0f82066fca4a780e97e5b3a2e4c38209c03d170/psutil-7.1.2-cp36-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e2aeb9b64f481b8eabfc633bd39e0016d4d8bbcd590d984af764d80bf0851b8a", size = 260203, upload-time = "2025-10-25T10:47:13.226Z" }, - { url = "https://files.pythonhosted.org/packages/f7/03/0a464404c51685dcb9329fdd660b1721e076ccd7b3d97dee066bcc9ffb15/psutil-7.1.2-cp37-abi3-win_amd64.whl", hash = "sha256:8e17852114c4e7996fe9da4745c2bdef001ebbf2f260dec406290e66628bdb91", size = 246714, upload-time = "2025-10-25T10:47:15.093Z" }, - { url = "https://files.pythonhosted.org/packages/6a/32/97ca2090f2f1b45b01b6aa7ae161cfe50671de097311975ca6eea3e7aabc/psutil-7.1.2-cp37-abi3-win_arm64.whl", hash = "sha256:3e988455e61c240cc879cb62a008c2699231bf3e3d061d7fce4234463fd2abb4", size = 243742, upload-time = "2025-10-25T10:47:17.302Z" }, + { url = "https://files.pythonhosted.org/packages/ef/94/46b9154a800253e7ecff5aaacdf8ebf43db99de4a2dfa18575b02548654e/psutil-7.1.3-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:2bdbcd0e58ca14996a42adf3621a6244f1bb2e2e528886959c72cf1e326677ab", size = 238359, upload-time = "2025-11-02T12:26:25.284Z" }, + { url = "https://files.pythonhosted.org/packages/68/3a/9f93cff5c025029a36d9a92fef47220ab4692ee7f2be0fba9f92813d0cb8/psutil-7.1.3-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:bc31fa00f1fbc3c3802141eede66f3a2d51d89716a194bf2cd6fc68310a19880", size = 239171, upload-time = "2025-11-02T12:26:27.23Z" }, + { url = "https://files.pythonhosted.org/packages/ce/b1/5f49af514f76431ba4eea935b8ad3725cdeb397e9245ab919dbc1d1dc20f/psutil-7.1.3-cp36-abi3-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3bb428f9f05c1225a558f53e30ccbad9930b11c3fc206836242de1091d3e7dd3", size = 263261, upload-time = "2025-11-02T12:26:29.48Z" }, + { url = "https://files.pythonhosted.org/packages/e0/95/992c8816a74016eb095e73585d747e0a8ea21a061ed3689474fabb29a395/psutil-7.1.3-cp36-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:56d974e02ca2c8eb4812c3f76c30e28836fffc311d55d979f1465c1feeb2b68b", size = 264635, upload-time = "2025-11-02T12:26:31.74Z" }, + { url = "https://files.pythonhosted.org/packages/55/4c/c3ed1a622b6ae2fd3c945a366e64eb35247a31e4db16cf5095e269e8eb3c/psutil-7.1.3-cp37-abi3-win_amd64.whl", hash = "sha256:f39c2c19fe824b47484b96f9692932248a54c43799a84282cfe58d05a6449efd", size = 247633, upload-time = "2025-11-02T12:26:33.887Z" }, + { url = "https://files.pythonhosted.org/packages/c9/ad/33b2ccec09bf96c2b2ef3f9a6f66baac8253d7565d8839e024a6b905d45d/psutil-7.1.3-cp37-abi3-win_arm64.whl", hash = "sha256:bd0d69cee829226a761e92f28140bec9a5ee9d5b4fb4b0cc589068dbfff559b1", size = 244608, upload-time = "2025-11-02T12:26:36.136Z" }, ] [[package]] @@ -1743,7 +1743,7 @@ wheels = [ [[package]] name = "pydantic" -version = "2.12.3" +version = "2.12.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-types" }, @@ -1751,64 +1751,64 @@ dependencies = [ { name = "typing-extensions" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f3/1e/4f0a3233767010308f2fd6bd0814597e3f63f1dc98304a9112b8759df4ff/pydantic-2.12.3.tar.gz", hash = "sha256:1da1c82b0fc140bb0103bc1441ffe062154c8d38491189751ee00fd8ca65ce74", size = 819383, upload-time = "2025-10-17T15:04:21.222Z" } +sdist = { url = "https://files.pythonhosted.org/packages/96/ad/a17bc283d7d81837c061c49e3eaa27a45991759a1b7eae1031921c6bd924/pydantic-2.12.4.tar.gz", hash = "sha256:0f8cb9555000a4b5b617f66bfd2566264c4984b27589d3b845685983e8ea85ac", size = 821038, upload-time = "2025-11-05T10:50:08.59Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a1/6b/83661fa77dcefa195ad5f8cd9af3d1a7450fd57cc883ad04d65446ac2029/pydantic-2.12.3-py3-none-any.whl", hash = "sha256:6986454a854bc3bc6e5443e1369e06a3a456af9d339eda45510f517d9ea5c6bf", size = 462431, upload-time = "2025-10-17T15:04:19.346Z" }, + { url = "https://files.pythonhosted.org/packages/82/2f/e68750da9b04856e2a7ec56fc6f034a5a79775e9b9a81882252789873798/pydantic-2.12.4-py3-none-any.whl", hash = "sha256:92d3d202a745d46f9be6df459ac5a064fdaa3c1c4cd8adcfa332ccf3c05f871e", size = 463400, upload-time = "2025-11-05T10:50:06.732Z" }, ] [[package]] name = "pydantic-core" -version = "2.41.4" +version = "2.41.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/df/18/d0944e8eaaa3efd0a91b0f1fc537d3be55ad35091b6a87638211ba691964/pydantic_core-2.41.4.tar.gz", hash = "sha256:70e47929a9d4a1905a67e4b687d5946026390568a8e952b92824118063cee4d5", size = 457557, upload-time = "2025-10-14T10:23:47.909Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/62/4c/f6cbfa1e8efacd00b846764e8484fe173d25b8dab881e277a619177f3384/pydantic_core-2.41.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:28ff11666443a1a8cf2a044d6a545ebffa8382b5f7973f22c36109205e65dc80", size = 2109062, upload-time = "2025-10-14T10:20:04.486Z" }, - { url = "https://files.pythonhosted.org/packages/21/f8/40b72d3868896bfcd410e1bd7e516e762d326201c48e5b4a06446f6cf9e8/pydantic_core-2.41.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:61760c3925d4633290292bad462e0f737b840508b4f722247d8729684f6539ae", size = 1916301, upload-time = "2025-10-14T10:20:06.857Z" }, - { url = "https://files.pythonhosted.org/packages/94/4d/d203dce8bee7faeca791671c88519969d98d3b4e8f225da5b96dad226fc8/pydantic_core-2.41.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eae547b7315d055b0de2ec3965643b0ab82ad0106a7ffd29615ee9f266a02827", size = 1968728, upload-time = "2025-10-14T10:20:08.353Z" }, - { url = "https://files.pythonhosted.org/packages/65/f5/6a66187775df87c24d526985b3a5d78d861580ca466fbd9d4d0e792fcf6c/pydantic_core-2.41.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ef9ee5471edd58d1fcce1c80ffc8783a650e3e3a193fe90d52e43bb4d87bff1f", size = 2050238, upload-time = "2025-10-14T10:20:09.766Z" }, - { url = "https://files.pythonhosted.org/packages/5e/b9/78336345de97298cf53236b2f271912ce11f32c1e59de25a374ce12f9cce/pydantic_core-2.41.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:15dd504af121caaf2c95cb90c0ebf71603c53de98305621b94da0f967e572def", size = 2249424, upload-time = "2025-10-14T10:20:11.732Z" }, - { url = "https://files.pythonhosted.org/packages/99/bb/a4584888b70ee594c3d374a71af5075a68654d6c780369df269118af7402/pydantic_core-2.41.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3a926768ea49a8af4d36abd6a8968b8790f7f76dd7cbd5a4c180db2b4ac9a3a2", size = 2366047, upload-time = "2025-10-14T10:20:13.647Z" }, - { url = "https://files.pythonhosted.org/packages/5f/8d/17fc5de9d6418e4d2ae8c675f905cdafdc59d3bf3bf9c946b7ab796a992a/pydantic_core-2.41.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6916b9b7d134bff5440098a4deb80e4cb623e68974a87883299de9124126c2a8", size = 2071163, upload-time = "2025-10-14T10:20:15.307Z" }, - { url = "https://files.pythonhosted.org/packages/54/e7/03d2c5c0b8ed37a4617430db68ec5e7dbba66358b629cd69e11b4d564367/pydantic_core-2.41.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5cf90535979089df02e6f17ffd076f07237efa55b7343d98760bde8743c4b265", size = 2190585, upload-time = "2025-10-14T10:20:17.3Z" }, - { url = "https://files.pythonhosted.org/packages/be/fc/15d1c9fe5ad9266a5897d9b932b7f53d7e5cfc800573917a2c5d6eea56ec/pydantic_core-2.41.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:7533c76fa647fade2d7ec75ac5cc079ab3f34879626dae5689b27790a6cf5a5c", size = 2150109, upload-time = "2025-10-14T10:20:19.143Z" }, - { url = "https://files.pythonhosted.org/packages/26/ef/e735dd008808226c83ba56972566138665b71477ad580fa5a21f0851df48/pydantic_core-2.41.4-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:37e516bca9264cbf29612539801ca3cd5d1be465f940417b002905e6ed79d38a", size = 2315078, upload-time = "2025-10-14T10:20:20.742Z" }, - { url = "https://files.pythonhosted.org/packages/90/00/806efdcf35ff2ac0f938362350cd9827b8afb116cc814b6b75cf23738c7c/pydantic_core-2.41.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0c19cb355224037c83642429b8ce261ae108e1c5fbf5c028bac63c77b0f8646e", size = 2318737, upload-time = "2025-10-14T10:20:22.306Z" }, - { url = "https://files.pythonhosted.org/packages/41/7e/6ac90673fe6cb36621a2283552897838c020db343fa86e513d3f563b196f/pydantic_core-2.41.4-cp311-cp311-win32.whl", hash = "sha256:09c2a60e55b357284b5f31f5ab275ba9f7f70b7525e18a132ec1f9160b4f1f03", size = 1974160, upload-time = "2025-10-14T10:20:23.817Z" }, - { url = "https://files.pythonhosted.org/packages/e0/9d/7c5e24ee585c1f8b6356e1d11d40ab807ffde44d2db3b7dfd6d20b09720e/pydantic_core-2.41.4-cp311-cp311-win_amd64.whl", hash = "sha256:711156b6afb5cb1cb7c14a2cc2c4a8b4c717b69046f13c6b332d8a0a8f41ca3e", size = 2021883, upload-time = "2025-10-14T10:20:25.48Z" }, - { url = "https://files.pythonhosted.org/packages/33/90/5c172357460fc28b2871eb4a0fb3843b136b429c6fa827e4b588877bf115/pydantic_core-2.41.4-cp311-cp311-win_arm64.whl", hash = "sha256:6cb9cf7e761f4f8a8589a45e49ed3c0d92d1d696a45a6feaee8c904b26efc2db", size = 1968026, upload-time = "2025-10-14T10:20:27.039Z" }, - { url = "https://files.pythonhosted.org/packages/e9/81/d3b3e95929c4369d30b2a66a91db63c8ed0a98381ae55a45da2cd1cc1288/pydantic_core-2.41.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:ab06d77e053d660a6faaf04894446df7b0a7e7aba70c2797465a0a1af00fc887", size = 2099043, upload-time = "2025-10-14T10:20:28.561Z" }, - { url = "https://files.pythonhosted.org/packages/58/da/46fdac49e6717e3a94fc9201403e08d9d61aa7a770fab6190b8740749047/pydantic_core-2.41.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c53ff33e603a9c1179a9364b0a24694f183717b2e0da2b5ad43c316c956901b2", size = 1910699, upload-time = "2025-10-14T10:20:30.217Z" }, - { url = "https://files.pythonhosted.org/packages/1e/63/4d948f1b9dd8e991a5a98b77dd66c74641f5f2e5225fee37994b2e07d391/pydantic_core-2.41.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:304c54176af2c143bd181d82e77c15c41cbacea8872a2225dd37e6544dce9999", size = 1952121, upload-time = "2025-10-14T10:20:32.246Z" }, - { url = "https://files.pythonhosted.org/packages/b2/a7/e5fc60a6f781fc634ecaa9ecc3c20171d238794cef69ae0af79ac11b89d7/pydantic_core-2.41.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:025ba34a4cf4fb32f917d5d188ab5e702223d3ba603be4d8aca2f82bede432a4", size = 2041590, upload-time = "2025-10-14T10:20:34.332Z" }, - { url = "https://files.pythonhosted.org/packages/70/69/dce747b1d21d59e85af433428978a1893c6f8a7068fa2bb4a927fba7a5ff/pydantic_core-2.41.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b9f5f30c402ed58f90c70e12eff65547d3ab74685ffe8283c719e6bead8ef53f", size = 2219869, upload-time = "2025-10-14T10:20:35.965Z" }, - { url = "https://files.pythonhosted.org/packages/83/6a/c070e30e295403bf29c4df1cb781317b6a9bac7cd07b8d3acc94d501a63c/pydantic_core-2.41.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dd96e5d15385d301733113bcaa324c8bcf111275b7675a9c6e88bfb19fc05e3b", size = 2345169, upload-time = "2025-10-14T10:20:37.627Z" }, - { url = "https://files.pythonhosted.org/packages/f0/83/06d001f8043c336baea7fd202a9ac7ad71f87e1c55d8112c50b745c40324/pydantic_core-2.41.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98f348cbb44fae6e9653c1055db7e29de67ea6a9ca03a5fa2c2e11a47cff0e47", size = 2070165, upload-time = "2025-10-14T10:20:39.246Z" }, - { url = "https://files.pythonhosted.org/packages/14/0a/e567c2883588dd12bcbc110232d892cf385356f7c8a9910311ac997ab715/pydantic_core-2.41.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ec22626a2d14620a83ca583c6f5a4080fa3155282718b6055c2ea48d3ef35970", size = 2189067, upload-time = "2025-10-14T10:20:41.015Z" }, - { url = "https://files.pythonhosted.org/packages/f4/1d/3d9fca34273ba03c9b1c5289f7618bc4bd09c3ad2289b5420481aa051a99/pydantic_core-2.41.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3a95d4590b1f1a43bf33ca6d647b990a88f4a3824a8c4572c708f0b45a5290ed", size = 2132997, upload-time = "2025-10-14T10:20:43.106Z" }, - { url = "https://files.pythonhosted.org/packages/52/70/d702ef7a6cd41a8afc61f3554922b3ed8d19dd54c3bd4bdbfe332e610827/pydantic_core-2.41.4-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:f9672ab4d398e1b602feadcffcdd3af44d5f5e6ddc15bc7d15d376d47e8e19f8", size = 2307187, upload-time = "2025-10-14T10:20:44.849Z" }, - { url = "https://files.pythonhosted.org/packages/68/4c/c06be6e27545d08b802127914156f38d10ca287a9e8489342793de8aae3c/pydantic_core-2.41.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:84d8854db5f55fead3b579f04bda9a36461dab0730c5d570e1526483e7bb8431", size = 2305204, upload-time = "2025-10-14T10:20:46.781Z" }, - { url = "https://files.pythonhosted.org/packages/b0/e5/35ae4919bcd9f18603419e23c5eaf32750224a89d41a8df1a3704b69f77e/pydantic_core-2.41.4-cp312-cp312-win32.whl", hash = "sha256:9be1c01adb2ecc4e464392c36d17f97e9110fbbc906bcbe1c943b5b87a74aabd", size = 1972536, upload-time = "2025-10-14T10:20:48.39Z" }, - { url = "https://files.pythonhosted.org/packages/1e/c2/49c5bb6d2a49eb2ee3647a93e3dae7080c6409a8a7558b075027644e879c/pydantic_core-2.41.4-cp312-cp312-win_amd64.whl", hash = "sha256:d682cf1d22bab22a5be08539dca3d1593488a99998f9f412137bc323179067ff", size = 2031132, upload-time = "2025-10-14T10:20:50.421Z" }, - { url = "https://files.pythonhosted.org/packages/06/23/936343dbcba6eec93f73e95eb346810fc732f71ba27967b287b66f7b7097/pydantic_core-2.41.4-cp312-cp312-win_arm64.whl", hash = "sha256:833eebfd75a26d17470b58768c1834dfc90141b7afc6eb0429c21fc5a21dcfb8", size = 1969483, upload-time = "2025-10-14T10:20:52.35Z" }, - { url = "https://files.pythonhosted.org/packages/b0/12/5ba58daa7f453454464f92b3ca7b9d7c657d8641c48e370c3ebc9a82dd78/pydantic_core-2.41.4-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:a1b2cfec3879afb742a7b0bcfa53e4f22ba96571c9e54d6a3afe1052d17d843b", size = 2122139, upload-time = "2025-10-14T10:22:47.288Z" }, - { url = "https://files.pythonhosted.org/packages/21/fb/6860126a77725c3108baecd10fd3d75fec25191d6381b6eb2ac660228eac/pydantic_core-2.41.4-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:d175600d975b7c244af6eb9c9041f10059f20b8bbffec9e33fdd5ee3f67cdc42", size = 1936674, upload-time = "2025-10-14T10:22:49.555Z" }, - { url = "https://files.pythonhosted.org/packages/de/be/57dcaa3ed595d81f8757e2b44a38240ac5d37628bce25fb20d02c7018776/pydantic_core-2.41.4-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0f184d657fa4947ae5ec9c47bd7e917730fa1cbb78195037e32dcbab50aca5ee", size = 1956398, upload-time = "2025-10-14T10:22:52.19Z" }, - { url = "https://files.pythonhosted.org/packages/2f/1d/679a344fadb9695f1a6a294d739fbd21d71fa023286daeea8c0ed49e7c2b/pydantic_core-2.41.4-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ed810568aeffed3edc78910af32af911c835cc39ebbfacd1f0ab5dd53028e5c", size = 2138674, upload-time = "2025-10-14T10:22:54.499Z" }, - { url = "https://files.pythonhosted.org/packages/c4/48/ae937e5a831b7c0dc646b2ef788c27cd003894882415300ed21927c21efa/pydantic_core-2.41.4-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:4f5d640aeebb438517150fdeec097739614421900e4a08db4a3ef38898798537", size = 2112087, upload-time = "2025-10-14T10:22:56.818Z" }, - { url = "https://files.pythonhosted.org/packages/5e/db/6db8073e3d32dae017da7e0d16a9ecb897d0a4d92e00634916e486097961/pydantic_core-2.41.4-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:4a9ab037b71927babc6d9e7fc01aea9e66dc2a4a34dff06ef0724a4049629f94", size = 1920387, upload-time = "2025-10-14T10:22:59.342Z" }, - { url = "https://files.pythonhosted.org/packages/0d/c1/dd3542d072fcc336030d66834872f0328727e3b8de289c662faa04aa270e/pydantic_core-2.41.4-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4dab9484ec605c3016df9ad4fd4f9a390bc5d816a3b10c6550f8424bb80b18c", size = 1951495, upload-time = "2025-10-14T10:23:02.089Z" }, - { url = "https://files.pythonhosted.org/packages/2b/c6/db8d13a1f8ab3f1eb08c88bd00fd62d44311e3456d1e85c0e59e0a0376e7/pydantic_core-2.41.4-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd8a5028425820731d8c6c098ab642d7b8b999758e24acae03ed38a66eca8335", size = 2139008, upload-time = "2025-10-14T10:23:04.539Z" }, - { url = "https://files.pythonhosted.org/packages/7e/7d/138e902ed6399b866f7cfe4435d22445e16fff888a1c00560d9dc79a780f/pydantic_core-2.41.4-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:491535d45cd7ad7e4a2af4a5169b0d07bebf1adfd164b0368da8aa41e19907a5", size = 2104721, upload-time = "2025-10-14T10:23:26.906Z" }, - { url = "https://files.pythonhosted.org/packages/47/13/0525623cf94627f7b53b4c2034c81edc8491cbfc7c28d5447fa318791479/pydantic_core-2.41.4-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:54d86c0cada6aba4ec4c047d0e348cbad7063b87ae0f005d9f8c9ad04d4a92a2", size = 1931608, upload-time = "2025-10-14T10:23:29.306Z" }, - { url = "https://files.pythonhosted.org/packages/d6/f9/744bc98137d6ef0a233f808bfc9b18cf94624bf30836a18d3b05d08bf418/pydantic_core-2.41.4-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eca1124aced216b2500dc2609eade086d718e8249cb9696660ab447d50a758bd", size = 2132986, upload-time = "2025-10-14T10:23:32.057Z" }, - { url = "https://files.pythonhosted.org/packages/17/c8/629e88920171173f6049386cc71f893dff03209a9ef32b4d2f7e7c264bcf/pydantic_core-2.41.4-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6c9024169becccf0cb470ada03ee578d7348c119a0d42af3dcf9eda96e3a247c", size = 2187516, upload-time = "2025-10-14T10:23:34.871Z" }, - { url = "https://files.pythonhosted.org/packages/2e/0f/4f2734688d98488782218ca61bcc118329bf5de05bb7fe3adc7dd79b0b86/pydantic_core-2.41.4-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:26895a4268ae5a2849269f4991cdc97236e4b9c010e51137becf25182daac405", size = 2146146, upload-time = "2025-10-14T10:23:37.342Z" }, - { url = "https://files.pythonhosted.org/packages/ed/f2/ab385dbd94a052c62224b99cf99002eee99dbec40e10006c78575aead256/pydantic_core-2.41.4-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:ca4df25762cf71308c446e33c9b1fdca2923a3f13de616e2a949f38bf21ff5a8", size = 2311296, upload-time = "2025-10-14T10:23:40.145Z" }, - { url = "https://files.pythonhosted.org/packages/fc/8e/e4f12afe1beeb9823bba5375f8f258df0cc61b056b0195fb1cf9f62a1a58/pydantic_core-2.41.4-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:5a28fcedd762349519276c36634e71853b4541079cab4acaaac60c4421827308", size = 2315386, upload-time = "2025-10-14T10:23:42.624Z" }, - { url = "https://files.pythonhosted.org/packages/48/f7/925f65d930802e3ea2eb4d5afa4cb8730c8dc0d2cb89a59dc4ed2fcb2d74/pydantic_core-2.41.4-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:c173ddcd86afd2535e2b695217e82191580663a1d1928239f877f5a1649ef39f", size = 2147775, upload-time = "2025-10-14T10:23:45.406Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/71/70/23b021c950c2addd24ec408e9ab05d59b035b39d97cdc1130e1bce647bb6/pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e", size = 460952, upload-time = "2025-11-04T13:43:49.098Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e8/72/74a989dd9f2084b3d9530b0915fdda64ac48831c30dbf7c72a41a5232db8/pydantic_core-2.41.5-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:a3a52f6156e73e7ccb0f8cced536adccb7042be67cb45f9562e12b319c119da6", size = 2105873, upload-time = "2025-11-04T13:39:31.373Z" }, + { url = "https://files.pythonhosted.org/packages/12/44/37e403fd9455708b3b942949e1d7febc02167662bf1a7da5b78ee1ea2842/pydantic_core-2.41.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7f3bf998340c6d4b0c9a2f02d6a400e51f123b59565d74dc60d252ce888c260b", size = 1899826, upload-time = "2025-11-04T13:39:32.897Z" }, + { url = "https://files.pythonhosted.org/packages/33/7f/1d5cab3ccf44c1935a359d51a8a2a9e1a654b744b5e7f80d41b88d501eec/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:378bec5c66998815d224c9ca994f1e14c0c21cb95d2f52b6021cc0b2a58f2a5a", size = 1917869, upload-time = "2025-11-04T13:39:34.469Z" }, + { url = "https://files.pythonhosted.org/packages/6e/6a/30d94a9674a7fe4f4744052ed6c5e083424510be1e93da5bc47569d11810/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e7b576130c69225432866fe2f4a469a85a54ade141d96fd396dffcf607b558f8", size = 2063890, upload-time = "2025-11-04T13:39:36.053Z" }, + { url = "https://files.pythonhosted.org/packages/50/be/76e5d46203fcb2750e542f32e6c371ffa9b8ad17364cf94bb0818dbfb50c/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6cb58b9c66f7e4179a2d5e0f849c48eff5c1fca560994d6eb6543abf955a149e", size = 2229740, upload-time = "2025-11-04T13:39:37.753Z" }, + { url = "https://files.pythonhosted.org/packages/d3/ee/fed784df0144793489f87db310a6bbf8118d7b630ed07aa180d6067e653a/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:88942d3a3dff3afc8288c21e565e476fc278902ae4d6d134f1eeda118cc830b1", size = 2350021, upload-time = "2025-11-04T13:39:40.94Z" }, + { url = "https://files.pythonhosted.org/packages/c8/be/8fed28dd0a180dca19e72c233cbf58efa36df055e5b9d90d64fd1740b828/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f31d95a179f8d64d90f6831d71fa93290893a33148d890ba15de25642c5d075b", size = 2066378, upload-time = "2025-11-04T13:39:42.523Z" }, + { url = "https://files.pythonhosted.org/packages/b0/3b/698cf8ae1d536a010e05121b4958b1257f0b5522085e335360e53a6b1c8b/pydantic_core-2.41.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c1df3d34aced70add6f867a8cf413e299177e0c22660cc767218373d0779487b", size = 2175761, upload-time = "2025-11-04T13:39:44.553Z" }, + { url = "https://files.pythonhosted.org/packages/b8/ba/15d537423939553116dea94ce02f9c31be0fa9d0b806d427e0308ec17145/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4009935984bd36bd2c774e13f9a09563ce8de4abaa7226f5108262fa3e637284", size = 2146303, upload-time = "2025-11-04T13:39:46.238Z" }, + { url = "https://files.pythonhosted.org/packages/58/7f/0de669bf37d206723795f9c90c82966726a2ab06c336deba4735b55af431/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:34a64bc3441dc1213096a20fe27e8e128bd3ff89921706e83c0b1ac971276594", size = 2340355, upload-time = "2025-11-04T13:39:48.002Z" }, + { url = "https://files.pythonhosted.org/packages/e5/de/e7482c435b83d7e3c3ee5ee4451f6e8973cff0eb6007d2872ce6383f6398/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c9e19dd6e28fdcaa5a1de679aec4141f691023916427ef9bae8584f9c2fb3b0e", size = 2319875, upload-time = "2025-11-04T13:39:49.705Z" }, + { url = "https://files.pythonhosted.org/packages/fe/e6/8c9e81bb6dd7560e33b9053351c29f30c8194b72f2d6932888581f503482/pydantic_core-2.41.5-cp311-cp311-win32.whl", hash = "sha256:2c010c6ded393148374c0f6f0bf89d206bf3217f201faa0635dcd56bd1520f6b", size = 1987549, upload-time = "2025-11-04T13:39:51.842Z" }, + { url = "https://files.pythonhosted.org/packages/11/66/f14d1d978ea94d1bc21fc98fcf570f9542fe55bfcc40269d4e1a21c19bf7/pydantic_core-2.41.5-cp311-cp311-win_amd64.whl", hash = "sha256:76ee27c6e9c7f16f47db7a94157112a2f3a00e958bc626e2f4ee8bec5c328fbe", size = 2011305, upload-time = "2025-11-04T13:39:53.485Z" }, + { url = "https://files.pythonhosted.org/packages/56/d8/0e271434e8efd03186c5386671328154ee349ff0354d83c74f5caaf096ed/pydantic_core-2.41.5-cp311-cp311-win_arm64.whl", hash = "sha256:4bc36bbc0b7584de96561184ad7f012478987882ebf9f9c389b23f432ea3d90f", size = 1972902, upload-time = "2025-11-04T13:39:56.488Z" }, + { url = "https://files.pythonhosted.org/packages/5f/5d/5f6c63eebb5afee93bcaae4ce9a898f3373ca23df3ccaef086d0233a35a7/pydantic_core-2.41.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f41a7489d32336dbf2199c8c0a215390a751c5b014c2c1c5366e817202e9cdf7", size = 2110990, upload-time = "2025-11-04T13:39:58.079Z" }, + { url = "https://files.pythonhosted.org/packages/aa/32/9c2e8ccb57c01111e0fd091f236c7b371c1bccea0fa85247ac55b1e2b6b6/pydantic_core-2.41.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:070259a8818988b9a84a449a2a7337c7f430a22acc0859c6b110aa7212a6d9c0", size = 1896003, upload-time = "2025-11-04T13:39:59.956Z" }, + { url = "https://files.pythonhosted.org/packages/68/b8/a01b53cb0e59139fbc9e4fda3e9724ede8de279097179be4ff31f1abb65a/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e96cea19e34778f8d59fe40775a7a574d95816eb150850a85a7a4c8f4b94ac69", size = 1919200, upload-time = "2025-11-04T13:40:02.241Z" }, + { url = "https://files.pythonhosted.org/packages/38/de/8c36b5198a29bdaade07b5985e80a233a5ac27137846f3bc2d3b40a47360/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed2e99c456e3fadd05c991f8f437ef902e00eedf34320ba2b0842bd1c3ca3a75", size = 2052578, upload-time = "2025-11-04T13:40:04.401Z" }, + { url = "https://files.pythonhosted.org/packages/00/b5/0e8e4b5b081eac6cb3dbb7e60a65907549a1ce035a724368c330112adfdd/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:65840751b72fbfd82c3c640cff9284545342a4f1eb1586ad0636955b261b0b05", size = 2208504, upload-time = "2025-11-04T13:40:06.072Z" }, + { url = "https://files.pythonhosted.org/packages/77/56/87a61aad59c7c5b9dc8caad5a41a5545cba3810c3e828708b3d7404f6cef/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e536c98a7626a98feb2d3eaf75944ef6f3dbee447e1f841eae16f2f0a72d8ddc", size = 2335816, upload-time = "2025-11-04T13:40:07.835Z" }, + { url = "https://files.pythonhosted.org/packages/0d/76/941cc9f73529988688a665a5c0ecff1112b3d95ab48f81db5f7606f522d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eceb81a8d74f9267ef4081e246ffd6d129da5d87e37a77c9bde550cb04870c1c", size = 2075366, upload-time = "2025-11-04T13:40:09.804Z" }, + { url = "https://files.pythonhosted.org/packages/d3/43/ebef01f69baa07a482844faaa0a591bad1ef129253ffd0cdaa9d8a7f72d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d38548150c39b74aeeb0ce8ee1d8e82696f4a4e16ddc6de7b1d8823f7de4b9b5", size = 2171698, upload-time = "2025-11-04T13:40:12.004Z" }, + { url = "https://files.pythonhosted.org/packages/b1/87/41f3202e4193e3bacfc2c065fab7706ebe81af46a83d3e27605029c1f5a6/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c23e27686783f60290e36827f9c626e63154b82b116d7fe9adba1fda36da706c", size = 2132603, upload-time = "2025-11-04T13:40:13.868Z" }, + { url = "https://files.pythonhosted.org/packages/49/7d/4c00df99cb12070b6bccdef4a195255e6020a550d572768d92cc54dba91a/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:482c982f814460eabe1d3bb0adfdc583387bd4691ef00b90575ca0d2b6fe2294", size = 2329591, upload-time = "2025-11-04T13:40:15.672Z" }, + { url = "https://files.pythonhosted.org/packages/cc/6a/ebf4b1d65d458f3cda6a7335d141305dfa19bdc61140a884d165a8a1bbc7/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:bfea2a5f0b4d8d43adf9d7b8bf019fb46fdd10a2e5cde477fbcb9d1fa08c68e1", size = 2319068, upload-time = "2025-11-04T13:40:17.532Z" }, + { url = "https://files.pythonhosted.org/packages/49/3b/774f2b5cd4192d5ab75870ce4381fd89cf218af999515baf07e7206753f0/pydantic_core-2.41.5-cp312-cp312-win32.whl", hash = "sha256:b74557b16e390ec12dca509bce9264c3bbd128f8a2c376eaa68003d7f327276d", size = 1985908, upload-time = "2025-11-04T13:40:19.309Z" }, + { url = "https://files.pythonhosted.org/packages/86/45/00173a033c801cacf67c190fef088789394feaf88a98a7035b0e40d53dc9/pydantic_core-2.41.5-cp312-cp312-win_amd64.whl", hash = "sha256:1962293292865bca8e54702b08a4f26da73adc83dd1fcf26fbc875b35d81c815", size = 2020145, upload-time = "2025-11-04T13:40:21.548Z" }, + { url = "https://files.pythonhosted.org/packages/f9/22/91fbc821fa6d261b376a3f73809f907cec5ca6025642c463d3488aad22fb/pydantic_core-2.41.5-cp312-cp312-win_arm64.whl", hash = "sha256:1746d4a3d9a794cacae06a5eaaccb4b8643a131d45fbc9af23e353dc0a5ba5c3", size = 1976179, upload-time = "2025-11-04T13:40:23.393Z" }, + { url = "https://files.pythonhosted.org/packages/11/72/90fda5ee3b97e51c494938a4a44c3a35a9c96c19bba12372fb9c634d6f57/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:b96d5f26b05d03cc60f11a7761a5ded1741da411e7fe0909e27a5e6a0cb7b034", size = 2115441, upload-time = "2025-11-04T13:42:39.557Z" }, + { url = "https://files.pythonhosted.org/packages/1f/53/8942f884fa33f50794f119012dc6a1a02ac43a56407adaac20463df8e98f/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:634e8609e89ceecea15e2d61bc9ac3718caaaa71963717bf3c8f38bfde64242c", size = 1930291, upload-time = "2025-11-04T13:42:42.169Z" }, + { url = "https://files.pythonhosted.org/packages/79/c8/ecb9ed9cd942bce09fc888ee960b52654fbdbede4ba6c2d6e0d3b1d8b49c/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:93e8740d7503eb008aa2df04d3b9735f845d43ae845e6dcd2be0b55a2da43cd2", size = 1948632, upload-time = "2025-11-04T13:42:44.564Z" }, + { url = "https://files.pythonhosted.org/packages/2e/1b/687711069de7efa6af934e74f601e2a4307365e8fdc404703afc453eab26/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f15489ba13d61f670dcc96772e733aad1a6f9c429cc27574c6cdaed82d0146ad", size = 2138905, upload-time = "2025-11-04T13:42:47.156Z" }, + { url = "https://files.pythonhosted.org/packages/09/32/59b0c7e63e277fa7911c2fc70ccfb45ce4b98991e7ef37110663437005af/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:7da7087d756b19037bc2c06edc6c170eeef3c3bafcb8f532ff17d64dc427adfd", size = 2110495, upload-time = "2025-11-04T13:42:49.689Z" }, + { url = "https://files.pythonhosted.org/packages/aa/81/05e400037eaf55ad400bcd318c05bb345b57e708887f07ddb2d20e3f0e98/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:aabf5777b5c8ca26f7824cb4a120a740c9588ed58df9b2d196ce92fba42ff8dc", size = 1915388, upload-time = "2025-11-04T13:42:52.215Z" }, + { url = "https://files.pythonhosted.org/packages/6e/0d/e3549b2399f71d56476b77dbf3cf8937cec5cd70536bdc0e374a421d0599/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c007fe8a43d43b3969e8469004e9845944f1a80e6acd47c150856bb87f230c56", size = 1942879, upload-time = "2025-11-04T13:42:56.483Z" }, + { url = "https://files.pythonhosted.org/packages/f7/07/34573da085946b6a313d7c42f82f16e8920bfd730665de2d11c0c37a74b5/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76d0819de158cd855d1cbb8fcafdf6f5cf1eb8e470abe056d5d161106e38062b", size = 2139017, upload-time = "2025-11-04T13:42:59.471Z" }, + { url = "https://files.pythonhosted.org/packages/5f/9b/1b3f0e9f9305839d7e84912f9e8bfbd191ed1b1ef48083609f0dabde978c/pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b2379fa7ed44ddecb5bfe4e48577d752db9fc10be00a6b7446e9663ba143de26", size = 2101980, upload-time = "2025-11-04T13:43:25.97Z" }, + { url = "https://files.pythonhosted.org/packages/a4/ed/d71fefcb4263df0da6a85b5d8a7508360f2f2e9b3bf5814be9c8bccdccc1/pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:266fb4cbf5e3cbd0b53669a6d1b039c45e3ce651fd5442eff4d07c2cc8d66808", size = 1923865, upload-time = "2025-11-04T13:43:28.763Z" }, + { url = "https://files.pythonhosted.org/packages/ce/3a/626b38db460d675f873e4444b4bb030453bbe7b4ba55df821d026a0493c4/pydantic_core-2.41.5-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58133647260ea01e4d0500089a8c4f07bd7aa6ce109682b1426394988d8aaacc", size = 2134256, upload-time = "2025-11-04T13:43:31.71Z" }, + { url = "https://files.pythonhosted.org/packages/83/d9/8412d7f06f616bbc053d30cb4e5f76786af3221462ad5eee1f202021eb4e/pydantic_core-2.41.5-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:287dad91cfb551c363dc62899a80e9e14da1f0e2b6ebde82c806612ca2a13ef1", size = 2174762, upload-time = "2025-11-04T13:43:34.744Z" }, + { url = "https://files.pythonhosted.org/packages/55/4c/162d906b8e3ba3a99354e20faa1b49a85206c47de97a639510a0e673f5da/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:03b77d184b9eb40240ae9fd676ca364ce1085f203e1b1256f8ab9984dca80a84", size = 2143141, upload-time = "2025-11-04T13:43:37.701Z" }, + { url = "https://files.pythonhosted.org/packages/1f/f2/f11dd73284122713f5f89fc940f370d035fa8e1e078d446b3313955157fe/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:a668ce24de96165bb239160b3d854943128f4334822900534f2fe947930e5770", size = 2330317, upload-time = "2025-11-04T13:43:40.406Z" }, + { url = "https://files.pythonhosted.org/packages/88/9d/b06ca6acfe4abb296110fb1273a4d848a0bfb2ff65f3ee92127b3244e16b/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f14f8f046c14563f8eb3f45f499cc658ab8d10072961e07225e507adb700e93f", size = 2316992, upload-time = "2025-11-04T13:43:43.602Z" }, + { url = "https://files.pythonhosted.org/packages/36/c7/cfc8e811f061c841d7990b0201912c3556bfeb99cdcb7ed24adc8d6f8704/pydantic_core-2.41.5-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:56121965f7a4dc965bff783d70b907ddf3d57f6eba29b6d2e5dabfaf07799c51", size = 2145302, upload-time = "2025-11-04T13:43:46.64Z" }, ] [[package]] @@ -1822,7 +1822,7 @@ wheels = [ [[package]] name = "pytest" -version = "8.4.2" +version = "9.0.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, @@ -1831,9 +1831,9 @@ dependencies = [ { name = "pluggy" }, { name = "pygments" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a3/5c/00a0e072241553e1a7496d638deababa67c5058571567b92a7eaa258397c/pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01", size = 1519618, upload-time = "2025-09-04T14:34:22.711Z" } +sdist = { url = "https://files.pythonhosted.org/packages/07/56/f013048ac4bc4c1d9be45afd4ab209ea62822fb1598f40687e6bf45dcea4/pytest-9.0.1.tar.gz", hash = "sha256:3e9c069ea73583e255c3b21cf46b8d3c56f6e3a1a8f6da94ccb0fcf57b9d73c8", size = 1564125, upload-time = "2025-11-12T13:05:09.333Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a8/a4/20da314d277121d6534b3a980b29035dcd51e6744bd79075a6ce8fa4eb8d/pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79", size = 365750, upload-time = "2025-09-04T14:34:20.226Z" }, + { url = "https://files.pythonhosted.org/packages/0b/8b/6300fb80f858cda1c51ffa17075df5d846757081d11ab4aa35cef9e6258b/pytest-9.0.1-py3-none-any.whl", hash = "sha256:67be0030d194df2dfa7b556f2e56fb3c3315bd5c8822c6951162b92b32ce7dad", size = 373668, upload-time = "2025-11-12T13:05:07.379Z" }, ] [[package]] @@ -2037,78 +2037,78 @@ wheels = [ [[package]] name = "rpds-py" -version = "0.28.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/48/dc/95f074d43452b3ef5d06276696ece4b3b5d696e7c9ad7173c54b1390cd70/rpds_py-0.28.0.tar.gz", hash = "sha256:abd4df20485a0983e2ca334a216249b6186d6e3c1627e106651943dbdb791aea", size = 27419, upload-time = "2025-10-22T22:24:29.327Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a6/34/058d0db5471c6be7bef82487ad5021ff8d1d1d27794be8730aad938649cf/rpds_py-0.28.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:03065002fd2e287725d95fbc69688e0c6daf6c6314ba38bdbaa3895418e09296", size = 362344, upload-time = "2025-10-22T22:21:39.713Z" }, - { url = "https://files.pythonhosted.org/packages/5d/67/9503f0ec8c055a0782880f300c50a2b8e5e72eb1f94dfc2053da527444dd/rpds_py-0.28.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:28ea02215f262b6d078daec0b45344c89e161eab9526b0d898221d96fdda5f27", size = 348440, upload-time = "2025-10-22T22:21:41.056Z" }, - { url = "https://files.pythonhosted.org/packages/68/2e/94223ee9b32332a41d75b6f94b37b4ce3e93878a556fc5f152cbd856a81f/rpds_py-0.28.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25dbade8fbf30bcc551cb352376c0ad64b067e4fc56f90e22ba70c3ce205988c", size = 379068, upload-time = "2025-10-22T22:21:42.593Z" }, - { url = "https://files.pythonhosted.org/packages/b4/25/54fd48f9f680cfc44e6a7f39a5fadf1d4a4a1fd0848076af4a43e79f998c/rpds_py-0.28.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3c03002f54cc855860bfdc3442928ffdca9081e73b5b382ed0b9e8efe6e5e205", size = 390518, upload-time = "2025-10-22T22:21:43.998Z" }, - { url = "https://files.pythonhosted.org/packages/1b/85/ac258c9c27f2ccb1bd5d0697e53a82ebcf8088e3186d5d2bf8498ee7ed44/rpds_py-0.28.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b9699fa7990368b22032baf2b2dce1f634388e4ffc03dfefaaac79f4695edc95", size = 525319, upload-time = "2025-10-22T22:21:45.645Z" }, - { url = "https://files.pythonhosted.org/packages/40/cb/c6734774789566d46775f193964b76627cd5f42ecf246d257ce84d1912ed/rpds_py-0.28.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b9b06fe1a75e05e0713f06ea0c89ecb6452210fd60e2f1b6ddc1067b990e08d9", size = 404896, upload-time = "2025-10-22T22:21:47.544Z" }, - { url = "https://files.pythonhosted.org/packages/1f/53/14e37ce83202c632c89b0691185dca9532288ff9d390eacae3d2ff771bae/rpds_py-0.28.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac9f83e7b326a3f9ec3ef84cda98fb0a74c7159f33e692032233046e7fd15da2", size = 382862, upload-time = "2025-10-22T22:21:49.176Z" }, - { url = "https://files.pythonhosted.org/packages/6a/83/f3642483ca971a54d60caa4449f9d6d4dbb56a53e0072d0deff51b38af74/rpds_py-0.28.0-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:0d3259ea9ad8743a75a43eb7819324cdab393263c91be86e2d1901ee65c314e0", size = 398848, upload-time = "2025-10-22T22:21:51.024Z" }, - { url = "https://files.pythonhosted.org/packages/44/09/2d9c8b2f88e399b4cfe86efdf2935feaf0394e4f14ab30c6c5945d60af7d/rpds_py-0.28.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9a7548b345f66f6695943b4ef6afe33ccd3f1b638bd9afd0f730dd255c249c9e", size = 412030, upload-time = "2025-10-22T22:21:52.665Z" }, - { url = "https://files.pythonhosted.org/packages/dd/f5/e1cec473d4bde6df1fd3738be8e82d64dd0600868e76e92dfeaebbc2d18f/rpds_py-0.28.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c9a40040aa388b037eb39416710fbcce9443498d2eaab0b9b45ae988b53f5c67", size = 559700, upload-time = "2025-10-22T22:21:54.123Z" }, - { url = "https://files.pythonhosted.org/packages/8d/be/73bb241c1649edbf14e98e9e78899c2c5e52bbe47cb64811f44d2cc11808/rpds_py-0.28.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8f60c7ea34e78c199acd0d3cda37a99be2c861dd2b8cf67399784f70c9f8e57d", size = 584581, upload-time = "2025-10-22T22:21:56.102Z" }, - { url = "https://files.pythonhosted.org/packages/9c/9c/ffc6e9218cd1eb5c2c7dbd276c87cd10e8c2232c456b554169eb363381df/rpds_py-0.28.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1571ae4292649100d743b26d5f9c63503bb1fedf538a8f29a98dce2d5ba6b4e6", size = 549981, upload-time = "2025-10-22T22:21:58.253Z" }, - { url = "https://files.pythonhosted.org/packages/5f/50/da8b6d33803a94df0149345ee33e5d91ed4d25fc6517de6a25587eae4133/rpds_py-0.28.0-cp311-cp311-win32.whl", hash = "sha256:5cfa9af45e7c1140af7321fa0bef25b386ee9faa8928c80dc3a5360971a29e8c", size = 214729, upload-time = "2025-10-22T22:21:59.625Z" }, - { url = "https://files.pythonhosted.org/packages/12/fd/b0f48c4c320ee24c8c20df8b44acffb7353991ddf688af01eef5f93d7018/rpds_py-0.28.0-cp311-cp311-win_amd64.whl", hash = "sha256:dd8d86b5d29d1b74100982424ba53e56033dc47720a6de9ba0259cf81d7cecaa", size = 223977, upload-time = "2025-10-22T22:22:01.092Z" }, - { url = "https://files.pythonhosted.org/packages/b4/21/c8e77a2ac66e2ec4e21f18a04b4e9a0417ecf8e61b5eaeaa9360a91713b4/rpds_py-0.28.0-cp311-cp311-win_arm64.whl", hash = "sha256:4e27d3a5709cc2b3e013bf93679a849213c79ae0573f9b894b284b55e729e120", size = 217326, upload-time = "2025-10-22T22:22:02.944Z" }, - { url = "https://files.pythonhosted.org/packages/b8/5c/6c3936495003875fe7b14f90ea812841a08fca50ab26bd840e924097d9c8/rpds_py-0.28.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:6b4f28583a4f247ff60cd7bdda83db8c3f5b05a7a82ff20dd4b078571747708f", size = 366439, upload-time = "2025-10-22T22:22:04.525Z" }, - { url = "https://files.pythonhosted.org/packages/56/f9/a0f1ca194c50aa29895b442771f036a25b6c41a35e4f35b1a0ea713bedae/rpds_py-0.28.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d678e91b610c29c4b3d52a2c148b641df2b4676ffe47c59f6388d58b99cdc424", size = 348170, upload-time = "2025-10-22T22:22:06.397Z" }, - { url = "https://files.pythonhosted.org/packages/18/ea/42d243d3a586beb72c77fa5def0487daf827210069a95f36328e869599ea/rpds_py-0.28.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e819e0e37a44a78e1383bf1970076e2ccc4dc8c2bbaa2f9bd1dc987e9afff628", size = 378838, upload-time = "2025-10-22T22:22:07.932Z" }, - { url = "https://files.pythonhosted.org/packages/e7/78/3de32e18a94791af8f33601402d9d4f39613136398658412a4e0b3047327/rpds_py-0.28.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5ee514e0f0523db5d3fb171f397c54875dbbd69760a414dccf9d4d7ad628b5bd", size = 393299, upload-time = "2025-10-22T22:22:09.435Z" }, - { url = "https://files.pythonhosted.org/packages/13/7e/4bdb435afb18acea2eb8a25ad56b956f28de7c59f8a1d32827effa0d4514/rpds_py-0.28.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5f3fa06d27fdcee47f07a39e02862da0100cb4982508f5ead53ec533cd5fe55e", size = 518000, upload-time = "2025-10-22T22:22:11.326Z" }, - { url = "https://files.pythonhosted.org/packages/31/d0/5f52a656875cdc60498ab035a7a0ac8f399890cc1ee73ebd567bac4e39ae/rpds_py-0.28.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:46959ef2e64f9e4a41fc89aa20dbca2b85531f9a72c21099a3360f35d10b0d5a", size = 408746, upload-time = "2025-10-22T22:22:13.143Z" }, - { url = "https://files.pythonhosted.org/packages/3e/cd/49ce51767b879cde77e7ad9fae164ea15dce3616fe591d9ea1df51152706/rpds_py-0.28.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8455933b4bcd6e83fde3fefc987a023389c4b13f9a58c8d23e4b3f6d13f78c84", size = 386379, upload-time = "2025-10-22T22:22:14.602Z" }, - { url = "https://files.pythonhosted.org/packages/6a/99/e4e1e1ee93a98f72fc450e36c0e4d99c35370220e815288e3ecd2ec36a2a/rpds_py-0.28.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:ad50614a02c8c2962feebe6012b52f9802deec4263946cddea37aaf28dd25a66", size = 401280, upload-time = "2025-10-22T22:22:16.063Z" }, - { url = "https://files.pythonhosted.org/packages/61/35/e0c6a57488392a8b319d2200d03dad2b29c0db9996f5662c3b02d0b86c02/rpds_py-0.28.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e5deca01b271492553fdb6c7fd974659dce736a15bae5dad7ab8b93555bceb28", size = 412365, upload-time = "2025-10-22T22:22:17.504Z" }, - { url = "https://files.pythonhosted.org/packages/ff/6a/841337980ea253ec797eb084665436007a1aad0faac1ba097fb906c5f69c/rpds_py-0.28.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:735f8495a13159ce6a0d533f01e8674cec0c57038c920495f87dcb20b3ddb48a", size = 559573, upload-time = "2025-10-22T22:22:19.108Z" }, - { url = "https://files.pythonhosted.org/packages/e7/5e/64826ec58afd4c489731f8b00729c5f6afdb86f1df1df60bfede55d650bb/rpds_py-0.28.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:961ca621ff10d198bbe6ba4957decca61aa2a0c56695384c1d6b79bf61436df5", size = 583973, upload-time = "2025-10-22T22:22:20.768Z" }, - { url = "https://files.pythonhosted.org/packages/b6/ee/44d024b4843f8386a4eeaa4c171b3d31d55f7177c415545fd1a24c249b5d/rpds_py-0.28.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2374e16cc9131022e7d9a8f8d65d261d9ba55048c78f3b6e017971a4f5e6353c", size = 553800, upload-time = "2025-10-22T22:22:22.25Z" }, - { url = "https://files.pythonhosted.org/packages/7d/89/33e675dccff11a06d4d85dbb4d1865f878d5020cbb69b2c1e7b2d3f82562/rpds_py-0.28.0-cp312-cp312-win32.whl", hash = "sha256:d15431e334fba488b081d47f30f091e5d03c18527c325386091f31718952fe08", size = 216954, upload-time = "2025-10-22T22:22:24.105Z" }, - { url = "https://files.pythonhosted.org/packages/af/36/45f6ebb3210887e8ee6dbf1bc710ae8400bb417ce165aaf3024b8360d999/rpds_py-0.28.0-cp312-cp312-win_amd64.whl", hash = "sha256:a410542d61fc54710f750d3764380b53bf09e8c4edbf2f9141a82aa774a04f7c", size = 227844, upload-time = "2025-10-22T22:22:25.551Z" }, - { url = "https://files.pythonhosted.org/packages/57/91/f3fb250d7e73de71080f9a221d19bd6a1c1eb0d12a1ea26513f6c1052ad6/rpds_py-0.28.0-cp312-cp312-win_arm64.whl", hash = "sha256:1f0cfd1c69e2d14f8c892b893997fa9a60d890a0c8a603e88dca4955f26d1edd", size = 217624, upload-time = "2025-10-22T22:22:26.914Z" }, - { url = "https://files.pythonhosted.org/packages/ae/bc/b43f2ea505f28119bd551ae75f70be0c803d2dbcd37c1b3734909e40620b/rpds_py-0.28.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f5e7101145427087e493b9c9b959da68d357c28c562792300dd21a095118ed16", size = 363913, upload-time = "2025-10-22T22:24:07.129Z" }, - { url = "https://files.pythonhosted.org/packages/28/f2/db318195d324c89a2c57dc5195058cbadd71b20d220685c5bd1da79ee7fe/rpds_py-0.28.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:31eb671150b9c62409a888850aaa8e6533635704fe2b78335f9aaf7ff81eec4d", size = 350452, upload-time = "2025-10-22T22:24:08.754Z" }, - { url = "https://files.pythonhosted.org/packages/ae/f2/1391c819b8573a4898cedd6b6c5ec5bc370ce59e5d6bdcebe3c9c1db4588/rpds_py-0.28.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48b55c1f64482f7d8bd39942f376bfdf2f6aec637ee8c805b5041e14eeb771db", size = 380957, upload-time = "2025-10-22T22:24:10.826Z" }, - { url = "https://files.pythonhosted.org/packages/5a/5c/e5de68ee7eb7248fce93269833d1b329a196d736aefb1a7481d1e99d1222/rpds_py-0.28.0-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:24743a7b372e9a76171f6b69c01aedf927e8ac3e16c474d9fe20d552a8cb45c7", size = 391919, upload-time = "2025-10-22T22:24:12.559Z" }, - { url = "https://files.pythonhosted.org/packages/fb/4f/2376336112cbfeb122fd435d608ad8d5041b3aed176f85a3cb32c262eb80/rpds_py-0.28.0-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:389c29045ee8bbb1627ea190b4976a310a295559eaf9f1464a1a6f2bf84dde78", size = 528541, upload-time = "2025-10-22T22:24:14.197Z" }, - { url = "https://files.pythonhosted.org/packages/68/53/5ae232e795853dd20da7225c5dd13a09c0a905b1a655e92bdf8d78a99fd9/rpds_py-0.28.0-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:23690b5827e643150cf7b49569679ec13fe9a610a15949ed48b85eb7f98f34ec", size = 405629, upload-time = "2025-10-22T22:24:16.001Z" }, - { url = "https://files.pythonhosted.org/packages/b9/2d/351a3b852b683ca9b6b8b38ed9efb2347596973849ba6c3a0e99877c10aa/rpds_py-0.28.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f0c9266c26580e7243ad0d72fc3e01d6b33866cfab5084a6da7576bcf1c4f72", size = 384123, upload-time = "2025-10-22T22:24:17.585Z" }, - { url = "https://files.pythonhosted.org/packages/e0/15/870804daa00202728cc91cb8e2385fa9f1f4eb49857c49cfce89e304eae6/rpds_py-0.28.0-pp311-pypy311_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:4c6c4db5d73d179746951486df97fd25e92396be07fc29ee8ff9a8f5afbdfb27", size = 400923, upload-time = "2025-10-22T22:24:19.512Z" }, - { url = "https://files.pythonhosted.org/packages/53/25/3706b83c125fa2a0bccceac951de3f76631f6bd0ee4d02a0ed780712ef1b/rpds_py-0.28.0-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a3b695a8fa799dd2cfdb4804b37096c5f6dba1ac7f48a7fbf6d0485bcd060316", size = 413767, upload-time = "2025-10-22T22:24:21.316Z" }, - { url = "https://files.pythonhosted.org/packages/ef/f9/ce43dbe62767432273ed2584cef71fef8411bddfb64125d4c19128015018/rpds_py-0.28.0-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:6aa1bfce3f83baf00d9c5fcdbba93a3ab79958b4c7d7d1f55e7fe68c20e63912", size = 561530, upload-time = "2025-10-22T22:24:22.958Z" }, - { url = "https://files.pythonhosted.org/packages/46/c9/ffe77999ed8f81e30713dd38fd9ecaa161f28ec48bb80fa1cd9118399c27/rpds_py-0.28.0-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:7b0f9dceb221792b3ee6acb5438eb1f02b0cb2c247796a72b016dcc92c6de829", size = 585453, upload-time = "2025-10-22T22:24:24.779Z" }, - { url = "https://files.pythonhosted.org/packages/ed/d2/4a73b18821fd4669762c855fd1f4e80ceb66fb72d71162d14da58444a763/rpds_py-0.28.0-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:5d0145edba8abd3db0ab22b5300c99dc152f5c9021fab861be0f0544dc3cbc5f", size = 552199, upload-time = "2025-10-22T22:24:26.54Z" }, +version = "0.29.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/98/33/23b3b3419b6a3e0f559c7c0d2ca8fc1b9448382b25245033788785921332/rpds_py-0.29.0.tar.gz", hash = "sha256:fe55fe686908f50154d1dc599232016e50c243b438c3b7432f24e2895b0e5359", size = 69359, upload-time = "2025-11-16T14:50:39.532Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/36/ab/7fb95163a53ab122c74a7c42d2d2f012819af2cf3deb43fb0d5acf45cc1a/rpds_py-0.29.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:9b9c764a11fd637e0322a488560533112837f5334ffeb48b1be20f6d98a7b437", size = 372344, upload-time = "2025-11-16T14:47:57.279Z" }, + { url = "https://files.pythonhosted.org/packages/b3/45/f3c30084c03b0d0f918cb4c5ae2c20b0a148b51ba2b3f6456765b629bedd/rpds_py-0.29.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3fd2164d73812026ce970d44c3ebd51e019d2a26a4425a5dcbdfa93a34abc383", size = 363041, upload-time = "2025-11-16T14:47:58.908Z" }, + { url = "https://files.pythonhosted.org/packages/e3/e9/4d044a1662608c47a87cbb37b999d4d5af54c6d6ebdda93a4d8bbf8b2a10/rpds_py-0.29.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a097b7f7f7274164566ae90a221fd725363c0e9d243e2e9ed43d195ccc5495c", size = 391775, upload-time = "2025-11-16T14:48:00.197Z" }, + { url = "https://files.pythonhosted.org/packages/50/c9/7616d3ace4e6731aeb6e3cd85123e03aec58e439044e214b9c5c60fd8eb1/rpds_py-0.29.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7cdc0490374e31cedefefaa1520d5fe38e82fde8748cbc926e7284574c714d6b", size = 405624, upload-time = "2025-11-16T14:48:01.496Z" }, + { url = "https://files.pythonhosted.org/packages/c2/e2/6d7d6941ca0843609fd2d72c966a438d6f22617baf22d46c3d2156c31350/rpds_py-0.29.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:89ca2e673ddd5bde9b386da9a0aac0cab0e76f40c8f0aaf0d6311b6bbf2aa311", size = 527894, upload-time = "2025-11-16T14:48:03.167Z" }, + { url = "https://files.pythonhosted.org/packages/8d/f7/aee14dc2db61bb2ae1e3068f134ca9da5f28c586120889a70ff504bb026f/rpds_py-0.29.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a5d9da3ff5af1ca1249b1adb8ef0573b94c76e6ae880ba1852f033bf429d4588", size = 412720, upload-time = "2025-11-16T14:48:04.413Z" }, + { url = "https://files.pythonhosted.org/packages/2f/e2/2293f236e887c0360c2723d90c00d48dee296406994d6271faf1712e94ec/rpds_py-0.29.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8238d1d310283e87376c12f658b61e1ee23a14c0e54c7c0ce953efdbdc72deed", size = 392945, upload-time = "2025-11-16T14:48:06.252Z" }, + { url = "https://files.pythonhosted.org/packages/14/cd/ceea6147acd3bd1fd028d1975228f08ff19d62098078d5ec3eed49703797/rpds_py-0.29.0-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:2d6fb2ad1c36f91c4646989811e84b1ea5e0c3cf9690b826b6e32b7965853a63", size = 406385, upload-time = "2025-11-16T14:48:07.575Z" }, + { url = "https://files.pythonhosted.org/packages/52/36/fe4dead19e45eb77a0524acfdbf51e6cda597b26fc5b6dddbff55fbbb1a5/rpds_py-0.29.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:534dc9df211387547267ccdb42253aa30527482acb38dd9b21c5c115d66a96d2", size = 423943, upload-time = "2025-11-16T14:48:10.175Z" }, + { url = "https://files.pythonhosted.org/packages/a1/7b/4551510803b582fa4abbc8645441a2d15aa0c962c3b21ebb380b7e74f6a1/rpds_py-0.29.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d456e64724a075441e4ed648d7f154dc62e9aabff29bcdf723d0c00e9e1d352f", size = 574204, upload-time = "2025-11-16T14:48:11.499Z" }, + { url = "https://files.pythonhosted.org/packages/64/ba/071ccdd7b171e727a6ae079f02c26f75790b41555f12ca8f1151336d2124/rpds_py-0.29.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:a738f2da2f565989401bd6fd0b15990a4d1523c6d7fe83f300b7e7d17212feca", size = 600587, upload-time = "2025-11-16T14:48:12.822Z" }, + { url = "https://files.pythonhosted.org/packages/03/09/96983d48c8cf5a1e03c7d9cc1f4b48266adfb858ae48c7c2ce978dbba349/rpds_py-0.29.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a110e14508fd26fd2e472bb541f37c209409876ba601cf57e739e87d8a53cf95", size = 562287, upload-time = "2025-11-16T14:48:14.108Z" }, + { url = "https://files.pythonhosted.org/packages/40/f0/8c01aaedc0fa92156f0391f39ea93b5952bc0ec56b897763858f95da8168/rpds_py-0.29.0-cp311-cp311-win32.whl", hash = "sha256:923248a56dd8d158389a28934f6f69ebf89f218ef96a6b216a9be6861804d3f4", size = 221394, upload-time = "2025-11-16T14:48:15.374Z" }, + { url = "https://files.pythonhosted.org/packages/7e/a5/a8b21c54c7d234efdc83dc034a4d7cd9668e3613b6316876a29b49dece71/rpds_py-0.29.0-cp311-cp311-win_amd64.whl", hash = "sha256:539eb77eb043afcc45314d1be09ea6d6cafb3addc73e0547c171c6d636957f60", size = 235713, upload-time = "2025-11-16T14:48:16.636Z" }, + { url = "https://files.pythonhosted.org/packages/a7/1f/df3c56219523947b1be402fa12e6323fe6d61d883cf35d6cb5d5bb6db9d9/rpds_py-0.29.0-cp311-cp311-win_arm64.whl", hash = "sha256:bdb67151ea81fcf02d8f494703fb728d4d34d24556cbff5f417d74f6f5792e7c", size = 229157, upload-time = "2025-11-16T14:48:17.891Z" }, + { url = "https://files.pythonhosted.org/packages/3c/50/bc0e6e736d94e420df79be4deb5c9476b63165c87bb8f19ef75d100d21b3/rpds_py-0.29.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a0891cfd8db43e085c0ab93ab7e9b0c8fee84780d436d3b266b113e51e79f954", size = 376000, upload-time = "2025-11-16T14:48:19.141Z" }, + { url = "https://files.pythonhosted.org/packages/3e/3a/46676277160f014ae95f24de53bed0e3b7ea66c235e7de0b9df7bd5d68ba/rpds_py-0.29.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3897924d3f9a0361472d884051f9a2460358f9a45b1d85a39a158d2f8f1ad71c", size = 360575, upload-time = "2025-11-16T14:48:20.443Z" }, + { url = "https://files.pythonhosted.org/packages/75/ba/411d414ed99ea1afdd185bbabeeaac00624bd1e4b22840b5e9967ade6337/rpds_py-0.29.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a21deb8e0d1571508c6491ce5ea5e25669b1dd4adf1c9d64b6314842f708b5d", size = 392159, upload-time = "2025-11-16T14:48:22.12Z" }, + { url = "https://files.pythonhosted.org/packages/8f/b1/e18aa3a331f705467a48d0296778dc1fea9d7f6cf675bd261f9a846c7e90/rpds_py-0.29.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9efe71687d6427737a0a2de9ca1c0a216510e6cd08925c44162be23ed7bed2d5", size = 410602, upload-time = "2025-11-16T14:48:23.563Z" }, + { url = "https://files.pythonhosted.org/packages/2f/6c/04f27f0c9f2299274c76612ac9d2c36c5048bb2c6c2e52c38c60bf3868d9/rpds_py-0.29.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:40f65470919dc189c833e86b2c4bd21bd355f98436a2cef9e0a9a92aebc8e57e", size = 515808, upload-time = "2025-11-16T14:48:24.949Z" }, + { url = "https://files.pythonhosted.org/packages/83/56/a8412aa464fb151f8bc0d91fb0bb888adc9039bd41c1c6ba8d94990d8cf8/rpds_py-0.29.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:def48ff59f181130f1a2cb7c517d16328efac3ec03951cca40c1dc2049747e83", size = 416015, upload-time = "2025-11-16T14:48:26.782Z" }, + { url = "https://files.pythonhosted.org/packages/04/4c/f9b8a05faca3d9e0a6397c90d13acb9307c9792b2bff621430c58b1d6e76/rpds_py-0.29.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad7bd570be92695d89285a4b373006930715b78d96449f686af422debb4d3949", size = 395325, upload-time = "2025-11-16T14:48:28.055Z" }, + { url = "https://files.pythonhosted.org/packages/34/60/869f3bfbf8ed7b54f1ad9a5543e0fdffdd40b5a8f587fe300ee7b4f19340/rpds_py-0.29.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:5a572911cd053137bbff8e3a52d31c5d2dba51d3a67ad902629c70185f3f2181", size = 410160, upload-time = "2025-11-16T14:48:29.338Z" }, + { url = "https://files.pythonhosted.org/packages/91/aa/e5b496334e3aba4fe4c8a80187b89f3c1294c5c36f2a926da74338fa5a73/rpds_py-0.29.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d583d4403bcbf10cffc3ab5cee23d7643fcc960dff85973fd3c2d6c86e8dbb0c", size = 425309, upload-time = "2025-11-16T14:48:30.691Z" }, + { url = "https://files.pythonhosted.org/packages/85/68/4e24a34189751ceb6d66b28f18159922828dd84155876551f7ca5b25f14f/rpds_py-0.29.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:070befbb868f257d24c3bb350dbd6e2f645e83731f31264b19d7231dd5c396c7", size = 574644, upload-time = "2025-11-16T14:48:31.964Z" }, + { url = "https://files.pythonhosted.org/packages/8c/cf/474a005ea4ea9c3b4f17b6108b6b13cebfc98ebaff11d6e1b193204b3a93/rpds_py-0.29.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:fc935f6b20b0c9f919a8ff024739174522abd331978f750a74bb68abd117bd19", size = 601605, upload-time = "2025-11-16T14:48:33.252Z" }, + { url = "https://files.pythonhosted.org/packages/f4/b1/c56f6a9ab8c5f6bb5c65c4b5f8229167a3a525245b0773f2c0896686b64e/rpds_py-0.29.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8c5a8ecaa44ce2d8d9d20a68a2483a74c07f05d72e94a4dff88906c8807e77b0", size = 564593, upload-time = "2025-11-16T14:48:34.643Z" }, + { url = "https://files.pythonhosted.org/packages/b3/13/0494cecce4848f68501e0a229432620b4b57022388b071eeff95f3e1e75b/rpds_py-0.29.0-cp312-cp312-win32.whl", hash = "sha256:ba5e1aeaf8dd6d8f6caba1f5539cddda87d511331714b7b5fc908b6cfc3636b7", size = 223853, upload-time = "2025-11-16T14:48:36.419Z" }, + { url = "https://files.pythonhosted.org/packages/1f/6a/51e9aeb444a00cdc520b032a28b07e5f8dc7bc328b57760c53e7f96997b4/rpds_py-0.29.0-cp312-cp312-win_amd64.whl", hash = "sha256:b5f6134faf54b3cb83375db0f113506f8b7770785be1f95a631e7e2892101977", size = 239895, upload-time = "2025-11-16T14:48:37.956Z" }, + { url = "https://files.pythonhosted.org/packages/d1/d4/8bce56cdad1ab873e3f27cb31c6a51d8f384d66b022b820525b879f8bed1/rpds_py-0.29.0-cp312-cp312-win_arm64.whl", hash = "sha256:b016eddf00dca7944721bf0cd85b6af7f6c4efaf83ee0b37c4133bd39757a8c7", size = 230321, upload-time = "2025-11-16T14:48:39.71Z" }, + { url = "https://files.pythonhosted.org/packages/f2/ac/b97e80bf107159e5b9ba9c91df1ab95f69e5e41b435f27bdd737f0d583ac/rpds_py-0.29.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:acd82a9e39082dc5f4492d15a6b6c8599aa21db5c35aaf7d6889aea16502c07d", size = 373963, upload-time = "2025-11-16T14:50:16.205Z" }, + { url = "https://files.pythonhosted.org/packages/40/5a/55e72962d5d29bd912f40c594e68880d3c7a52774b0f75542775f9250712/rpds_py-0.29.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:715b67eac317bf1c7657508170a3e011a1ea6ccb1c9d5f296e20ba14196be6b3", size = 364644, upload-time = "2025-11-16T14:50:18.22Z" }, + { url = "https://files.pythonhosted.org/packages/99/2a/6b6524d0191b7fc1351c3c0840baac42250515afb48ae40c7ed15499a6a2/rpds_py-0.29.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3b1b87a237cb2dba4db18bcfaaa44ba4cd5936b91121b62292ff21df577fc43", size = 393847, upload-time = "2025-11-16T14:50:20.012Z" }, + { url = "https://files.pythonhosted.org/packages/1c/b8/c5692a7df577b3c0c7faed7ac01ee3c608b81750fc5d89f84529229b6873/rpds_py-0.29.0-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1c3c3e8101bb06e337c88eb0c0ede3187131f19d97d43ea0e1c5407ea74c0cbf", size = 407281, upload-time = "2025-11-16T14:50:21.64Z" }, + { url = "https://files.pythonhosted.org/packages/f0/57/0546c6f84031b7ea08b76646a8e33e45607cc6bd879ff1917dc077bb881e/rpds_py-0.29.0-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2b8e54d6e61f3ecd3abe032065ce83ea63417a24f437e4a3d73d2f85ce7b7cfe", size = 529213, upload-time = "2025-11-16T14:50:23.219Z" }, + { url = "https://files.pythonhosted.org/packages/fa/c1/01dd5f444233605555bc11fe5fed6a5c18f379f02013870c176c8e630a23/rpds_py-0.29.0-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3fbd4e9aebf110473a420dea85a238b254cf8a15acb04b22a5a6b5ce8925b760", size = 413808, upload-time = "2025-11-16T14:50:25.262Z" }, + { url = "https://files.pythonhosted.org/packages/aa/0a/60f98b06156ea2a7af849fb148e00fbcfdb540909a5174a5ed10c93745c7/rpds_py-0.29.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80fdf53d36e6c72819993e35d1ebeeb8e8fc688d0c6c2b391b55e335b3afba5a", size = 394600, upload-time = "2025-11-16T14:50:26.956Z" }, + { url = "https://files.pythonhosted.org/packages/37/f1/dc9312fc9bec040ece08396429f2bd9e0977924ba7a11c5ad7056428465e/rpds_py-0.29.0-pp311-pypy311_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:ea7173df5d86f625f8dde6d5929629ad811ed8decda3b60ae603903839ac9ac0", size = 408634, upload-time = "2025-11-16T14:50:28.989Z" }, + { url = "https://files.pythonhosted.org/packages/ed/41/65024c9fd40c89bb7d604cf73beda4cbdbcebe92d8765345dd65855b6449/rpds_py-0.29.0-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:76054d540061eda273274f3d13a21a4abdde90e13eaefdc205db37c05230efce", size = 426064, upload-time = "2025-11-16T14:50:30.674Z" }, + { url = "https://files.pythonhosted.org/packages/a2/e0/cf95478881fc88ca2fdbf56381d7df36567cccc39a05394beac72182cd62/rpds_py-0.29.0-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:9f84c549746a5be3bc7415830747a3a0312573afc9f95785eb35228bb17742ec", size = 575871, upload-time = "2025-11-16T14:50:33.428Z" }, + { url = "https://files.pythonhosted.org/packages/ea/c0/df88097e64339a0218b57bd5f9ca49898e4c394db756c67fccc64add850a/rpds_py-0.29.0-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:0ea962671af5cb9a260489e311fa22b2e97103e3f9f0caaea6f81390af96a9ed", size = 601702, upload-time = "2025-11-16T14:50:36.051Z" }, + { url = "https://files.pythonhosted.org/packages/87/f4/09ffb3ebd0cbb9e2c7c9b84d252557ecf434cd71584ee1e32f66013824df/rpds_py-0.29.0-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:f7728653900035fb7b8d06e1e5900545d8088efc9d5d4545782da7df03ec803f", size = 564054, upload-time = "2025-11-16T14:50:37.733Z" }, ] [[package]] name = "ruff" -version = "0.14.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ee/34/8218a19b2055b80601e8fd201ec723c74c7fe1ca06d525a43ed07b6d8e85/ruff-0.14.2.tar.gz", hash = "sha256:98da787668f239313d9c902ca7c523fe11b8ec3f39345553a51b25abc4629c96", size = 5539663, upload-time = "2025-10-23T19:37:00.956Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/16/dd/23eb2db5ad9acae7c845700493b72d3ae214dce0b226f27df89216110f2b/ruff-0.14.2-py3-none-linux_armv6l.whl", hash = "sha256:7cbe4e593505bdec5884c2d0a4d791a90301bc23e49a6b1eb642dd85ef9c64f1", size = 12533390, upload-time = "2025-10-23T19:36:18.044Z" }, - { url = "https://files.pythonhosted.org/packages/5a/8c/5f9acff43ddcf3f85130d0146d0477e28ccecc495f9f684f8f7119b74c0d/ruff-0.14.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:8d54b561729cee92f8d89c316ad7a3f9705533f5903b042399b6ae0ddfc62e11", size = 12887187, upload-time = "2025-10-23T19:36:22.664Z" }, - { url = "https://files.pythonhosted.org/packages/99/fa/047646491479074029665022e9f3dc6f0515797f40a4b6014ea8474c539d/ruff-0.14.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:5c8753dfa44ebb2cde10ce5b4d2ef55a41fb9d9b16732a2c5df64620dbda44a3", size = 11925177, upload-time = "2025-10-23T19:36:24.778Z" }, - { url = "https://files.pythonhosted.org/packages/15/8b/c44cf7fe6e59ab24a9d939493a11030b503bdc2a16622cede8b7b1df0114/ruff-0.14.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d0bbeffb8d9f4fccf7b5198d566d0bad99a9cb622f1fc3467af96cb8773c9e3", size = 12358285, upload-time = "2025-10-23T19:36:26.979Z" }, - { url = "https://files.pythonhosted.org/packages/45/01/47701b26254267ef40369aea3acb62a7b23e921c27372d127e0f3af48092/ruff-0.14.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7047f0c5a713a401e43a88d36843d9c83a19c584e63d664474675620aaa634a8", size = 12303832, upload-time = "2025-10-23T19:36:29.192Z" }, - { url = "https://files.pythonhosted.org/packages/2d/5c/ae7244ca4fbdf2bee9d6405dcd5bc6ae51ee1df66eb7a9884b77b8af856d/ruff-0.14.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3bf8d2f9aa1602599217d82e8e0af7fd33e5878c4d98f37906b7c93f46f9a839", size = 13036995, upload-time = "2025-10-23T19:36:31.861Z" }, - { url = "https://files.pythonhosted.org/packages/27/4c/0860a79ce6fd4c709ac01173f76f929d53f59748d0dcdd662519835dae43/ruff-0.14.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:1c505b389e19c57a317cf4b42db824e2fca96ffb3d86766c1c9f8b96d32048a7", size = 14512649, upload-time = "2025-10-23T19:36:33.915Z" }, - { url = "https://files.pythonhosted.org/packages/7f/7f/d365de998069720a3abfc250ddd876fc4b81a403a766c74ff9bde15b5378/ruff-0.14.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a307fc45ebd887b3f26b36d9326bb70bf69b01561950cdcc6c0bdf7bb8e0f7cc", size = 14088182, upload-time = "2025-10-23T19:36:36.983Z" }, - { url = "https://files.pythonhosted.org/packages/6c/ea/d8e3e6b209162000a7be1faa41b0a0c16a133010311edc3329753cc6596a/ruff-0.14.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:61ae91a32c853172f832c2f40bd05fd69f491db7289fb85a9b941ebdd549781a", size = 13599516, upload-time = "2025-10-23T19:36:39.208Z" }, - { url = "https://files.pythonhosted.org/packages/fa/ea/c7810322086db68989fb20a8d5221dd3b79e49e396b01badca07b433ab45/ruff-0.14.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1967e40286f63ee23c615e8e7e98098dedc7301568bd88991f6e544d8ae096", size = 13272690, upload-time = "2025-10-23T19:36:41.453Z" }, - { url = "https://files.pythonhosted.org/packages/a9/39/10b05acf8c45786ef501d454e00937e1b97964f846bf28883d1f9619928a/ruff-0.14.2-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:2877f02119cdebf52a632d743a2e302dea422bfae152ebe2f193d3285a3a65df", size = 13496497, upload-time = "2025-10-23T19:36:43.61Z" }, - { url = "https://files.pythonhosted.org/packages/59/a1/1f25f8301e13751c30895092485fada29076e5e14264bdacc37202e85d24/ruff-0.14.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:e681c5bc777de5af898decdcb6ba3321d0d466f4cb43c3e7cc2c3b4e7b843a05", size = 12266116, upload-time = "2025-10-23T19:36:45.625Z" }, - { url = "https://files.pythonhosted.org/packages/5c/fa/0029bfc9ce16ae78164e6923ef392e5f173b793b26cc39aa1d8b366cf9dc/ruff-0.14.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:e21be42d72e224736f0c992cdb9959a2fa53c7e943b97ef5d081e13170e3ffc5", size = 12281345, upload-time = "2025-10-23T19:36:47.618Z" }, - { url = "https://files.pythonhosted.org/packages/a5/ab/ece7baa3c0f29b7683be868c024f0838770c16607bea6852e46b202f1ff6/ruff-0.14.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:b8264016f6f209fac16262882dbebf3f8be1629777cf0f37e7aff071b3e9b92e", size = 12629296, upload-time = "2025-10-23T19:36:49.789Z" }, - { url = "https://files.pythonhosted.org/packages/a4/7f/638f54b43f3d4e48c6a68062794e5b367ddac778051806b9e235dfb7aa81/ruff-0.14.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:5ca36b4cb4db3067a3b24444463ceea5565ea78b95fe9a07ca7cb7fd16948770", size = 13371610, upload-time = "2025-10-23T19:36:51.882Z" }, - { url = "https://files.pythonhosted.org/packages/8d/35/3654a973ebe5b32e1fd4a08ed2d46755af7267da7ac710d97420d7b8657d/ruff-0.14.2-py3-none-win32.whl", hash = "sha256:41775927d287685e08f48d8eb3f765625ab0b7042cc9377e20e64f4eb0056ee9", size = 12415318, upload-time = "2025-10-23T19:36:53.961Z" }, - { url = "https://files.pythonhosted.org/packages/71/30/3758bcf9e0b6a4193a6f51abf84254aba00887dfa8c20aba18aa366c5f57/ruff-0.14.2-py3-none-win_amd64.whl", hash = "sha256:0df3424aa5c3c08b34ed8ce099df1021e3adaca6e90229273496b839e5a7e1af", size = 13565279, upload-time = "2025-10-23T19:36:56.578Z" }, - { url = "https://files.pythonhosted.org/packages/2e/5d/aa883766f8ef9ffbe6aa24f7192fb71632f31a30e77eb39aa2b0dc4290ac/ruff-0.14.2-py3-none-win_arm64.whl", hash = "sha256:ea9d635e83ba21569fbacda7e78afbfeb94911c9434aff06192d9bc23fd5495a", size = 12554956, upload-time = "2025-10-23T19:36:58.714Z" }, +version = "0.14.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/52/f0/62b5a1a723fe183650109407fa56abb433b00aa1c0b9ba555f9c4efec2c6/ruff-0.14.6.tar.gz", hash = "sha256:6f0c742ca6a7783a736b867a263b9a7a80a45ce9bee391eeda296895f1b4e1cc", size = 5669501, upload-time = "2025-11-21T14:26:17.903Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/67/d2/7dd544116d107fffb24a0064d41a5d2ed1c9d6372d142f9ba108c8e39207/ruff-0.14.6-py3-none-linux_armv6l.whl", hash = "sha256:d724ac2f1c240dbd01a2ae98db5d1d9a5e1d9e96eba999d1c48e30062df578a3", size = 13326119, upload-time = "2025-11-21T14:25:24.2Z" }, + { url = "https://files.pythonhosted.org/packages/36/6a/ad66d0a3315d6327ed6b01f759d83df3c4d5f86c30462121024361137b6a/ruff-0.14.6-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:9f7539ea257aa4d07b7ce87aed580e485c40143f2473ff2f2b75aee003186004", size = 13526007, upload-time = "2025-11-21T14:25:26.906Z" }, + { url = "https://files.pythonhosted.org/packages/a3/9d/dae6db96df28e0a15dea8e986ee393af70fc97fd57669808728080529c37/ruff-0.14.6-py3-none-macosx_11_0_arm64.whl", hash = "sha256:7f6007e55b90a2a7e93083ba48a9f23c3158c433591c33ee2e99a49b889c6332", size = 12676572, upload-time = "2025-11-21T14:25:29.826Z" }, + { url = "https://files.pythonhosted.org/packages/76/a4/f319e87759949062cfee1b26245048e92e2acce900ad3a909285f9db1859/ruff-0.14.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a8e7b9d73d8728b68f632aa8e824ef041d068d231d8dbc7808532d3629a6bef", size = 13140745, upload-time = "2025-11-21T14:25:32.788Z" }, + { url = "https://files.pythonhosted.org/packages/95/d3/248c1efc71a0a8ed4e8e10b4b2266845d7dfc7a0ab64354afe049eaa1310/ruff-0.14.6-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d50d45d4553a3ebcbd33e7c5e0fe6ca4aafd9a9122492de357205c2c48f00775", size = 13076486, upload-time = "2025-11-21T14:25:35.601Z" }, + { url = "https://files.pythonhosted.org/packages/a5/19/b68d4563fe50eba4b8c92aa842149bb56dd24d198389c0ed12e7faff4f7d/ruff-0.14.6-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:118548dd121f8a21bfa8ab2c5b80e5b4aed67ead4b7567790962554f38e598ce", size = 13727563, upload-time = "2025-11-21T14:25:38.514Z" }, + { url = "https://files.pythonhosted.org/packages/47/ac/943169436832d4b0e867235abbdb57ce3a82367b47e0280fa7b4eabb7593/ruff-0.14.6-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:57256efafbfefcb8748df9d1d766062f62b20150691021f8ab79e2d919f7c11f", size = 15199755, upload-time = "2025-11-21T14:25:41.516Z" }, + { url = "https://files.pythonhosted.org/packages/c9/b9/288bb2399860a36d4bb0541cb66cce3c0f4156aaff009dc8499be0c24bf2/ruff-0.14.6-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ff18134841e5c68f8e5df1999a64429a02d5549036b394fafbe410f886e1989d", size = 14850608, upload-time = "2025-11-21T14:25:44.428Z" }, + { url = "https://files.pythonhosted.org/packages/ee/b1/a0d549dd4364e240f37e7d2907e97ee80587480d98c7799d2d8dc7a2f605/ruff-0.14.6-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:29c4b7ec1e66a105d5c27bd57fa93203637d66a26d10ca9809dc7fc18ec58440", size = 14118754, upload-time = "2025-11-21T14:25:47.214Z" }, + { url = "https://files.pythonhosted.org/packages/13/ac/9b9fe63716af8bdfddfacd0882bc1586f29985d3b988b3c62ddce2e202c3/ruff-0.14.6-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:167843a6f78680746d7e226f255d920aeed5e4ad9c03258094a2d49d3028b105", size = 13949214, upload-time = "2025-11-21T14:25:50.002Z" }, + { url = "https://files.pythonhosted.org/packages/12/27/4dad6c6a77fede9560b7df6802b1b697e97e49ceabe1f12baf3ea20862e9/ruff-0.14.6-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:16a33af621c9c523b1ae006b1b99b159bf5ac7e4b1f20b85b2572455018e0821", size = 14106112, upload-time = "2025-11-21T14:25:52.841Z" }, + { url = "https://files.pythonhosted.org/packages/6a/db/23e322d7177873eaedea59a7932ca5084ec5b7e20cb30f341ab594130a71/ruff-0.14.6-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:1432ab6e1ae2dc565a7eea707d3b03a0c234ef401482a6f1621bc1f427c2ff55", size = 13035010, upload-time = "2025-11-21T14:25:55.536Z" }, + { url = "https://files.pythonhosted.org/packages/a8/9c/20e21d4d69dbb35e6a1df7691e02f363423658a20a2afacf2a2c011800dc/ruff-0.14.6-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:4c55cfbbe7abb61eb914bfd20683d14cdfb38a6d56c6c66efa55ec6570ee4e71", size = 13054082, upload-time = "2025-11-21T14:25:58.625Z" }, + { url = "https://files.pythonhosted.org/packages/66/25/906ee6a0464c3125c8d673c589771a974965c2be1a1e28b5c3b96cb6ef88/ruff-0.14.6-py3-none-musllinux_1_2_i686.whl", hash = "sha256:efea3c0f21901a685fff4befda6d61a1bf4cb43de16da87e8226a281d614350b", size = 13303354, upload-time = "2025-11-21T14:26:01.816Z" }, + { url = "https://files.pythonhosted.org/packages/4c/58/60577569e198d56922b7ead07b465f559002b7b11d53f40937e95067ca1c/ruff-0.14.6-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:344d97172576d75dc6afc0e9243376dbe1668559c72de1864439c4fc95f78185", size = 14054487, upload-time = "2025-11-21T14:26:05.058Z" }, + { url = "https://files.pythonhosted.org/packages/67/0b/8e4e0639e4cc12547f41cb771b0b44ec8225b6b6a93393176d75fe6f7d40/ruff-0.14.6-py3-none-win32.whl", hash = "sha256:00169c0c8b85396516fdd9ce3446c7ca20c2a8f90a77aa945ba6b8f2bfe99e85", size = 13013361, upload-time = "2025-11-21T14:26:08.152Z" }, + { url = "https://files.pythonhosted.org/packages/fb/02/82240553b77fd1341f80ebb3eaae43ba011c7a91b4224a9f317d8e6591af/ruff-0.14.6-py3-none-win_amd64.whl", hash = "sha256:390e6480c5e3659f8a4c8d6a0373027820419ac14fa0d2713bd8e6c3e125b8b9", size = 14432087, upload-time = "2025-11-21T14:26:10.891Z" }, + { url = "https://files.pythonhosted.org/packages/a5/1f/93f9b0fad9470e4c829a5bb678da4012f0c710d09331b860ee555216f4ea/ruff-0.14.6-py3-none-win_arm64.whl", hash = "sha256:d43c81fbeae52cfa8728d8766bbf46ee4298c888072105815b392da70ca836b2", size = 13520930, upload-time = "2025-11-21T14:26:13.951Z" }, ] [[package]] @@ -2342,16 +2342,16 @@ wheels = [ [[package]] name = "virtualenv" -version = "20.35.3" +version = "20.35.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "distlib" }, { name = "filelock" }, { name = "platformdirs" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a4/d5/b0ccd381d55c8f45d46f77df6ae59fbc23d19e901e2d523395598e5f4c93/virtualenv-20.35.3.tar.gz", hash = "sha256:4f1a845d131133bdff10590489610c98c168ff99dc75d6c96853801f7f67af44", size = 6002907, upload-time = "2025-10-10T21:23:33.178Z" } +sdist = { url = "https://files.pythonhosted.org/packages/20/28/e6f1a6f655d620846bd9df527390ecc26b3805a0c5989048c210e22c5ca9/virtualenv-20.35.4.tar.gz", hash = "sha256:643d3914d73d3eeb0c552cbb12d7e82adf0e504dbf86a3182f8771a153a1971c", size = 6028799, upload-time = "2025-10-29T06:57:40.511Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/27/73/d9a94da0e9d470a543c1b9d3ccbceb0f59455983088e727b8a1824ed90fb/virtualenv-20.35.3-py3-none-any.whl", hash = "sha256:63d106565078d8c8d0b206d48080f938a8b25361e19432d2c9db40d2899c810a", size = 5981061, upload-time = "2025-10-10T21:23:30.433Z" }, + { url = "https://files.pythonhosted.org/packages/79/0c/c05523fa3181fdf0c9c52a6ba91a23fbf3246cc095f26f6516f9c60e6771/virtualenv-20.35.4-py3-none-any.whl", hash = "sha256:c21c9cede36c9753eeade68ba7d523529f228a403463376cf821eaae2b650f1b", size = 6005095, upload-time = "2025-10-29T06:57:37.598Z" }, ] [[package]] @@ -2385,11 +2385,11 @@ wheels = [ [[package]] name = "webcolors" -version = "24.11.1" +version = "25.10.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7b/29/061ec845fb58521848f3739e466efd8250b4b7b98c1b6c5bf4d40b419b7e/webcolors-24.11.1.tar.gz", hash = "sha256:ecb3d768f32202af770477b8b65f318fa4f566c22948673a977b00d589dd80f6", size = 45064, upload-time = "2024-11-11T07:43:24.224Z" } +sdist = { url = "https://files.pythonhosted.org/packages/1d/7a/eb316761ec35664ea5174709a68bbd3389de60d4a1ebab8808bfc264ed67/webcolors-25.10.0.tar.gz", hash = "sha256:62abae86504f66d0f6364c2a8520de4a0c47b80c03fc3a5f1815fedbef7c19bf", size = 53491, upload-time = "2025-10-31T07:51:03.977Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/60/e8/c0e05e4684d13459f93d312077a9a2efbe04d59c393bc2b8802248c908d4/webcolors-24.11.1-py3-none-any.whl", hash = "sha256:515291393b4cdf0eb19c155749a096f779f7d909f7cceea072791cb9095b92e9", size = 14934, upload-time = "2024-11-11T07:43:22.529Z" }, + { url = "https://files.pythonhosted.org/packages/e2/cc/e097523dd85c9cf5d354f78310927f1656c422bd7b2613b2db3e3f9a0f2c/webcolors-25.10.0-py3-none-any.whl", hash = "sha256:032c727334856fc0b968f63daa252a1ac93d33db2f5267756623c210e57a4f1d", size = 14905, upload-time = "2025-10-31T07:51:01.778Z" }, ] [[package]] @@ -2412,11 +2412,11 @@ wheels = [ [[package]] name = "widgetsnbextension" -version = "4.0.14" +version = "4.0.15" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/41/53/2e0253c5efd69c9656b1843892052a31c36d37ad42812b5da45c62191f7e/widgetsnbextension-4.0.14.tar.gz", hash = "sha256:a3629b04e3edb893212df862038c7232f62973373869db5084aed739b437b5af", size = 1097428, upload-time = "2025-04-10T13:01:25.628Z" } +sdist = { url = "https://files.pythonhosted.org/packages/bd/f4/c67440c7fb409a71b7404b7aefcd7569a9c0d6bd071299bf4198ae7a5d95/widgetsnbextension-4.0.15.tar.gz", hash = "sha256:de8610639996f1567952d763a5a41af8af37f2575a41f9852a38f947eb82a3b9", size = 1097402, upload-time = "2025-11-01T21:15:55.178Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ca/51/5447876806d1088a0f8f71e16542bf350918128d0a69437df26047c8e46f/widgetsnbextension-4.0.14-py3-none-any.whl", hash = "sha256:4875a9eaf72fbf5079dc372a51a9f268fc38d46f767cbf85c43a36da5cb9b575", size = 2196503, upload-time = "2025-04-10T13:01:23.086Z" }, + { url = "https://files.pythonhosted.org/packages/3f/0e/fa3b193432cfc60c93b42f3be03365f5f909d2b3ea410295cf36df739e31/widgetsnbextension-4.0.15-py3-none-any.whl", hash = "sha256:8156704e4346a571d9ce73b84bee86a29906c9abfd7223b7228a28899ccf3366", size = 2196503, upload-time = "2025-11-01T21:15:53.565Z" }, ] [[package]]