diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1f7f83e..699b8f1 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -20,13 +20,14 @@ repos: entry: isort language: system types: [python] - # - repo: local - # hooks: - # - id: zuban - # name: zuban - # entry: zuban check - # language: system - # types: [python] + - repo: local + hooks: + - id: mypy + name: mypy + entry: mypy + language: system + types: [python] + exclude: ^tests/ # Prettier - formats Markdown (and other files) - repo: https://github.com/pre-commit/mirrors-prettier rev: v3.1.0 # Use the latest version diff --git a/CLAUDE.md b/CLAUDE.md index ef6ac02..d7d3835 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -19,7 +19,7 @@ Do not be afraid to question what I say. Do not always respond with "You're righ - Test specific cache modes: `poetry run pytest -e ABZU_CACHE_MODE=hybrid tests/test_sync.py` - Lint: `pre-commit run` is better than `poetry run flake8`. Don't use `--all-files` with pre-commit. - Format: `poetry run black abzu tests`, `poetry run isort abzu tests` -- Type check: `poetry run zuban check` +- Type check: `poetry run mypy abzu` - Place temporary scripts for debugging in the `scripts/` directory. - Use the `pqrs` utility to inspect Parquet files: `pqrs schema `, `pqrs row-count `, `pqrs head ` @@ -30,6 +30,9 @@ Test one iteration of the full entity resolution pipeline each time you make cha - Block with `abzu process er block names --iteration 1 -m 30` - Match with `abzu process er match names --iteration 1 -b 50` - note you can use the `-n` option to limit the number of rows processed for faster testing. - Evaluate with `abzu process er eval names --iteration 1` +- Evaluate with `abzu process er final --iteration 1` + +You can do all of these steps in one command with `abzu process er all --iteration 1 -m 100 -b 40 ` to process only 1000 rows for faster testing. ### Docker Development (via Taskfile) @@ -50,6 +53,10 @@ Test one iteration of the full entity resolution pipeline each time you make cha - API operations: `abzu api financialdatasets`, `abzu api sec download --ticker NVDA` - Run pipeline steps: `abzu steps` +## Bloomberg Exchange Data Standard + +Download and use the [Bloomberg Exchange codes](https://www.inforeachinc.com/wp-content/uploads/exchanges.xlsx) for encoding all BAML or other `Ticker.exchange` fields. Use the two letter Bloomberg code not `NASDAQ`, etc. or if unknown use the three letter code `UNK`. + ## Architecture Overview ### Project Structure @@ -88,6 +95,12 @@ Test one iteration of the full entity resolution pipeline each time you make cha 4. **KG Refine**: Deduplicate and enrich → refined Parquet files 5. **APIs**: Enrich with financial/SEC data +### Semantic Matching + +- **Blocking**: Semantic clustering to group similar entities. See @markdown/BLOCKING.md +- **Matching**: LLM-based pairwise entity comparison. See @abzu/er/match.py +- **Evaluation**: Precision/recall metrics against ground truth + ## Code Style - KISS: KEEP IT SIMPLE STUPID. Do not over-engineer solutions. ESPECIALLY for Spark / PySpark. @@ -107,11 +120,11 @@ Test one iteration of the full entity resolution pipeline each time you make cha - Tests: Use pytest for testing, include type hints in test functions, use fixtures for setup/teardown - Tests: Don't make a class to contain unit tests. Just write the tests in pytest style. - Type hints: Use Python 3.9 type hints for all function parameters and return types. Use `list`, `dict`, `tuple`, etc. instead of `List`, `Dict`, `Tuple` from the `typing` module. Use `Optional` from the `typing` module for optional parameters. -- Type checking: Use zuban for type checking, run zuban before committing code +- Type checking: Use mypy for type checking, run mypy before committing code - Logging: Use logging for error handling, avoid print statements. Always use `from abzu.logs import get_logger` and `logger = get_logger(__name__)` - Documentation: Use Sphinx for documentation, include docstrings in all public functions/classes - Code style: Follow PEP 8 for Python code style, use flake8 for linting -- Zuban: Use zuban for type checking, it is a faster version of mypy. Run zuban via pre-commit before committing code. Configure it in `pyproject.toml` under `[tool.zuban]`. +- Mypy: Use mypy for type checking. Run mypy via pre-commit before committing code. Configure it in `pyproject.toml` under `[tool.mypy]`. - Pre-commit: Use pre-commit for linting and formatting, configure it in `.pre-commit-config.yaml` - Git: Use git for version control, commit often with clear messages, use branches for new features/bug fixes. Always test new features in the CLI before you commit them. - Poetry: Use poetry for dependency management and packaging, configure it in `pyproject.toml` @@ -123,6 +136,27 @@ Test one iteration of the full entity resolution pipeline each time you make cha - Space Lines - never create a line with only spaces. - Imports - don't check if things are installed and handle it with a try/except. Instead, assume they are installed and import them directly. If they are not installed, the code will fail at runtime, which is acceptable in this project. +## Schema Management + +All schemas are currently defined in `baml_src/` files, which are used to generate the schemas in `abzu/baml_client/types.py`. There is code in `abzu/ dit the BAML source files in `baml_src/`and regenerate the client with`baml-cli generate`. + +When defining new schemas, ensure they are compatible with PySpark and include all necessary fields for data processing. + +### SparkDantic + +We use our own branch of SparkDantic for schema management to convert from BAML/PyDantic schemas to PySpark. Do not use the original SparkDantic package. Always use the code in `sparkdantic @ git+https://github.com/Graphlet-AI/sparkdantic.git@ff1d38d595b2c62700ccba6c10dc613832fe368c` until we get our [SparkDantic PR](https://github.com/mitchelllisle/sparkdantic/pull/799) merged. When making changes to schemas, ensure compatibility with our SparkDantic fork. + +When in doubt, specify the schema when loading data into Spark DataFrames to avoid schema inference issues. + +## File Formats + +Always save data in two formats: + +1. Parquet - for efficient processing with Spark and Pandas without processing sub-folders manually. +2. Single file JSONL - for debugging and manual inspection. + +When writing code that outputs data, ensure both formats are written unless explicitly instructed otherwise. We serialize entire records in Parquet format in ALL stages except for `abzu/er/match.py` because of a Parquet error in nested structures. + ## Development Guidelines - Command Line Interfaces - at the end of your coding tasks, please alter the 'abzu' CLI to accommodate the changes. It is a Python / Click CLI. @@ -141,7 +175,7 @@ Test one iteration of the full entity resolution pipeline each time you make cha - Flake8 - fix flake8 errors without being asked and without my verification. - Black - fix black errors without being asked and without my verification. - Isort - fix isort errors without being asked and without my verification. -- Zuban - fix zuban errors without being asked and without my verification. +- Mypy - fix mypy errors without being asked and without my verification. - Pre-commit - fix pre-commit errors without being asked and without my verification. - New Modules - create a folder for a new module without being asked and without my verification. - **init**.py - add these files to new module directories without being asked and without my verification. @@ -217,7 +251,7 @@ In addition, when writing PySpark code: - Python 3.12 required - Core packages: pyspark==3.5.5, scrapy==2.11, baml, kuzu, discord.py -- Development tools: poetry, black, isort, flake8, zuban, pytest +- Development tools: poetry, black, isort, flake8, mypy, pytest - See pyproject.toml for complete dependency list ### Environment Variables diff --git a/abzu/api/wiki.py b/abzu/api/wiki.py index 55ccea5..f8c8b32 100644 --- a/abzu/api/wiki.py +++ b/abzu/api/wiki.py @@ -2,16 +2,21 @@ import asyncio import re +import warnings from pathlib import Path from typing import Any, Optional import aiohttp import mwparserfromhell import wikipedia +from bs4 import GuessedAtParserWarning from abzu.logs import get_logger from abzu.utils import append_jsonl +# Suppress Wikipedia library's HTML parser warning +warnings.filterwarnings("ignore", category=GuessedAtParserWarning) + logger = get_logger(__name__) @@ -104,16 +109,13 @@ def wikitext_to_markdown(wikitext: str) -> str: return markdown.strip() -def extract_company_info( - page_data: dict[str, Any], ticker: Optional[str] = None, name: Optional[str] = None -) -> dict[str, Any]: +def extract_company_info(page_data: dict[str, Any], name: str) -> dict[str, Any]: """ Extract structured company information from Wikipedia page data. Args: page_data: Wikipedia page data with plain text content - ticker: Stock ticker if provided - name: Company name if provided + name: Company name Returns: Dictionary with structured company information @@ -122,31 +124,24 @@ def extract_company_info( content = page_data.get("content", "") summary = page_data.get("summary", "") title = page_data.get("title", "") - url = page_data.get("url", "") - # Try to extract ticker from content if not provided - ticker_symbol = ticker + # Try to extract ticker from content + ticker_symbol = None ticker_exchange = None - if not ticker_symbol: - # For NVIDIA specifically, we know it's NVDA on NASDAQ - if "Nvidia" in title: - ticker_symbol = "NVDA" - ticker_exchange = "NASDAQ" - else: - # Simple patterns for common exchanges in plain text - exchange_patterns = [ - (r"NASDAQ:\s*([A-Z]{1,5})", "NASDAQ"), - (r"NYSE:\s*([A-Z]{1,5})", "NYSE"), - (r"traded as ([A-Z]{1,5})", None), - ] - - for pattern, exchange in exchange_patterns: - match = re.search(pattern, content) - if match: - ticker_symbol = match.group(1) - ticker_exchange = exchange - break + # Simple patterns for common exchanges in plain text + exchange_patterns = [ + (r"NASDAQ:\s*([A-Z]{1,5})", "NASDAQ"), + (r"NYSE:\s*([A-Z]{1,5})", "NYSE"), + (r"traded as ([A-Z]{1,5})", None), + ] + + for pattern, exchange in exchange_patterns: + match = re.search(pattern, content) + if match: + ticker_symbol = match.group(1) + ticker_exchange = exchange + break # Initialize result with structured format result = { @@ -264,96 +259,94 @@ def extract_company_info( return result -def crawl_company_structured( - ticker: Optional[str] = None, name: Optional[str] = None -) -> dict[str, Any]: +def crawl_company_structured(name: str) -> dict[str, Any]: """ Crawl Wikipedia page for a company and return structured data. Args: - ticker: Stock ticker symbol name: Company name Returns: Dictionary with structured company information """ # Get plain text content from wikipedia package - page_data = crawl_company_wikipedia(ticker=ticker, name=name) + page_data = crawl_company_wikipedia(name=name) # Extract structured information from the plain text - structured_data = extract_company_info(page_data, ticker=ticker, name=name) + structured_data = extract_company_info(page_data, name=name) return structured_data -def crawl_company_wikipedia( - ticker: Optional[str] = None, name: Optional[str] = None +async def crawl_company_structured_async( + session: aiohttp.ClientSession, + name: str, ) -> dict[str, Any]: """ - Crawl Wikipedia page for a company. + Async version: Crawl Wikipedia page for a company and return structured data. + + Args: + session: aiohttp ClientSession for making requests + name: Company name + + Returns: + Dictionary with structured company information + """ + # Get plain text content from wikipedia package (async version) + page_data = await crawl_company_wikipedia_async(session, name=name) + + # Extract structured information from the plain text + structured_data = extract_company_info(page_data, name=name) + + return structured_data + + +def crawl_company_wikipedia(name: str) -> dict[str, Any]: + """ + Crawl Wikipedia page for a company using company name. Args: - ticker: Stock ticker symbol name: Company name Returns: Dictionary with Wikipedia content """ - if not ticker and not name: - raise ValueError("Either ticker or name must be provided") + if not name: + raise ValueError("Company name must be provided") # Set Wikipedia language wikipedia.set_lang("en") - # Try different search variations - search_terms = [] - if ticker: - search_terms.extend( - [f"{ticker} (company)", f"{ticker} Inc", f"{ticker} Corporation", ticker] - ) - if name: - search_terms.extend([name, f"{name} (company)", f"{name} Inc", f"{name} Corporation"]) - page = None - used_search_term = None - - # Search for the Wikipedia page using various search terms - for term in search_terms: - logger.info(f"Trying search term: {term}") - - # Use wikipedia package to search for pages - search_results = wikipedia.search(term, results=3) - if search_results: - # Try to get the first search result - for result in search_results: - try: - # Get the page using wikipedia package - page = wikipedia.page(result, auto_suggest=False) - used_search_term = term - logger.info(f"Found page: {page.title}") - break - except wikipedia.exceptions.DisambiguationError as e: - # Handle disambiguation pages by trying the first option - if e.options: - try: - page = wikipedia.page(e.options[0], auto_suggest=False) - used_search_term = term - logger.info(f"Found page from disambiguation: {page.title}") - break - except Exception: - continue - except wikipedia.exceptions.PageError: - continue - except Exception as e: - logger.warning(f"Error getting page for {result}: {e}") - continue - - if page: - break + + # Search Wikipedia with just the company name + logger.info(f"Searching Wikipedia for: {name}") + search_results = wikipedia.search(name, results=3) + + if search_results: + # Try the first result that works + for result in search_results: + try: + page = wikipedia.page(result, auto_suggest=False) + logger.info(f"Found page: {page.title}") + break + except wikipedia.exceptions.DisambiguationError as e: + # Take the first option from disambiguation + if e.options: + try: + page = wikipedia.page(e.options[0], auto_suggest=False) + logger.info(f"Found page from disambiguation: {page.title}") + break + except Exception: + continue + except wikipedia.exceptions.PageError: + continue + except Exception as e: + logger.warning(f"Error getting page for {result}: {e}") + continue if not page: - search_type = f"ticker {ticker}" if ticker else f"company {name}" - raise ValueError(f"Could not find Wikipedia page for {search_type}") + raise ValueError(f"Could not find Wikipedia page for {name}") # Get the plain text content from wikipedia package content = page.content @@ -377,9 +370,7 @@ def crawl_company_wikipedia( "content": content, "categories": categories, "links": links, - "search_ticker": ticker, "search_name": name, - "search_term_used": used_search_term, } logger.info(f"Successfully crawled Wikipedia page: {page.title}") @@ -393,8 +384,7 @@ def crawl_company_wikipedia( async def crawl_company_wikipedia_async( session: aiohttp.ClientSession, - ticker: Optional[str] = None, - name: Optional[str] = None, + name: str, raw: bool = False, ) -> dict[str, Any]: """ @@ -402,75 +392,49 @@ async def crawl_company_wikipedia_async( Args: session: aiohttp ClientSession for making requests - ticker: Stock ticker symbol name: Company name raw: If True, return raw wikitext instead of Markdown (default: False) Returns: Dictionary with Wikipedia content in Markdown or raw wikitext format """ - if not ticker and not name: - raise ValueError("Either ticker or name must be provided") + if not name: + raise ValueError("Company name must be provided") # Set Wikipedia language wikipedia.set_lang("en") - # Try different search variations - search_terms = [] - if ticker: - search_terms.extend( - [f"{ticker} (company)", f"{ticker} Inc", f"{ticker} Corporation", ticker] - ) - if name: - search_terms.extend([name, f"{name} (company)", f"{name} Inc", f"{name} Corporation"]) - page = None - used_search_term = None - # Search for the Wikipedia page using various search terms - for term in search_terms: - logger.info(f"Trying search term: {term}") + # Search Wikipedia with just the company name + logger.info(f"Searching Wikipedia for: {name}") + loop = asyncio.get_event_loop() + search_results = await loop.run_in_executor(None, wikipedia.search, name, 3) - # Use wikipedia package to search for pages (this is synchronous) - # We run it in a thread pool to avoid blocking - loop = asyncio.get_event_loop() - search_results = await loop.run_in_executor(None, wikipedia.search, term, 3) - - if search_results: - # Try to get the first search result - for result in search_results: - try: - # Get the page using wikipedia package (run in thread pool) - page = await loop.run_in_executor(None, wikipedia.page, result, False) - used_search_term = term - if page: - logger.info(f"Found page: {page.title}") - break - except wikipedia.exceptions.DisambiguationError as e: - # Handle disambiguation pages by trying the first option - if e.options: - try: - page = await loop.run_in_executor( - None, wikipedia.page, e.options[0], False - ) - used_search_term = term - if page: - logger.info(f"Found page from disambiguation: {page.title}") - break - except Exception: - continue - except wikipedia.exceptions.PageError: - continue - except Exception as e: - logger.warning(f"Error getting page for {result}: {e}") - continue - - if page: - break + if search_results: + # Try the first result that works + for result in search_results: + try: + page = await loop.run_in_executor(None, wikipedia.page, result, False) + logger.info(f"Found page: {page.title}") + break + except wikipedia.exceptions.DisambiguationError as e: + # Take the first option from disambiguation + if e.options: + try: + page = await loop.run_in_executor(None, wikipedia.page, e.options[0], False) + logger.info(f"Found page from disambiguation: {page.title}") + break + except Exception: + continue + except wikipedia.exceptions.PageError: + continue + except Exception as e: + logger.warning(f"Error getting page for {result}: {e}") + continue if not page: - search_type = f"ticker {ticker}" if ticker else f"company {name}" - raise ValueError(f"Could not find Wikipedia page for {search_type}") + raise ValueError(f"Could not find Wikipedia page for {name}") # Get the raw MediaWiki wikitext using async HTTP # NOTE: We use the Wikipedia API directly here because the wikipedia package @@ -540,9 +504,7 @@ async def crawl_company_wikipedia_async( "content": content, "categories": categories, "links": links, - "search_ticker": ticker, "search_name": name, - "search_term_used": used_search_term, } logger.info(f"Successfully crawled Wikipedia page: {page.title}") @@ -565,7 +527,7 @@ async def crawl_companies_batch_async( Results are written to output file as they complete. Args: - companies: List of company dictionaries with 'ticker' and/or 'name' fields + companies: List of company dictionaries with 'name' field output_file: Path to output file for results (optional) max_concurrent: Maximum number of concurrent requests (default: 5) raw: If True, return raw wikitext instead of Markdown (default: False) @@ -582,12 +544,12 @@ async def crawl_with_semaphore( ) -> dict[str, Any]: """Crawl a single company with semaphore rate limiting.""" async with semaphore: + name = company.get("name") + if not name: + return {"error": "No company name provided", "company": company} + try: - ticker = company.get("ticker") - name = company.get("name") - result = await crawl_company_wikipedia_async( - session, ticker=ticker, name=name, raw=raw - ) + result = await crawl_company_wikipedia_async(session, name=name, raw=raw) # Write to output file if provided if output_file: @@ -596,11 +558,10 @@ async def crawl_with_semaphore( return result except Exception as e: - logger.error(f"Error crawling {company}: {e}") + logger.error(f"Error crawling {name}: {e}") error_result = { "error": str(e), - "search_ticker": company.get("ticker"), - "search_name": company.get("name"), + "search_name": name, } # Write error to output file if provided diff --git a/abzu/articles/processor.py b/abzu/articles/processor.py index c8b0333..7459652 100644 --- a/abzu/articles/processor.py +++ b/abzu/articles/processor.py @@ -19,14 +19,55 @@ logger = get_logger(__name__) +# Titles that indicate blocked or invalid articles (Cloudflare, CAPTCHA, etc.) +INVALID_TITLES = [ + "just a moment...", + "access denied", + "please wait...", + "checking your browser", + "attention required", + "one more step", + "verify you are human", +] + +# Minimum content length for a valid article (in characters) +MIN_CONTENT_LENGTH = 200 + + +def is_valid_article(article: dict[str, Any]) -> bool: + """Check if an article is valid for processing. + + Filters out Cloudflare-blocked pages, CAPTCHAs, and articles with insufficient content. + + Args: + article: Dictionary containing article data + + Returns: + True if article is valid for processing, False otherwise + """ + title = (article.get("title") or "").lower().strip() + content = article.get("content") or "" + + # Check for blocked/invalid titles + for invalid_title in INVALID_TITLES: + if invalid_title in title: + return False + + # Check minimum content length + if len(content) < MIN_CONTENT_LENGTH: + return False + + return True + + def load_articles(file_path: str) -> list[dict[str, Any]]: - """Load articles from a JSONL file and deduplicate them. + """Load articles from a JSONL file, deduplicate and filter invalid ones. Args: file_path: Path to the JSONL file containing articles Returns: - List of deduplicated article dictionaries + List of valid, deduplicated article dictionaries """ logger.info(f"Loading articles from {file_path}") articles = load_jsonl(file_path) @@ -38,7 +79,14 @@ def load_articles(file_path: str) -> list[dict[str, Any]]: deduped_articles = deduplicate_articles(articles) logger.info(f"After deduplication: {len(deduped_articles)} unique articles") - return deduped_articles + # Filter out invalid articles (Cloudflare blocks, CAPTCHAs, too short) + valid_articles = [a for a in deduped_articles if is_valid_article(a)] + filtered_count = len(deduped_articles) - len(valid_articles) + if filtered_count > 0: + logger.info(f"Filtered out {filtered_count} invalid articles (blocked/too short)") + logger.info(f"Valid articles for processing: {len(valid_articles)}") + + return valid_articles def get_client_registry() -> ClientRegistry: @@ -46,7 +94,7 @@ def get_client_registry() -> ClientRegistry: cr: ClientRegistry = ClientRegistry() cr.add_llm_client( - name="Gemini25Flash", + name="Gemini20Flash", provider="google-ai", options={ "model": "gemini-2.5-flash", @@ -58,7 +106,7 @@ def get_client_registry() -> ClientRegistry: ) cr.add_llm_client( - name="Gemini25Pro", + name="Gemini20Flash", provider="google-ai", options={ "model": "gemini-2.5-pro", @@ -70,7 +118,7 @@ def get_client_registry() -> ClientRegistry: ) # Start with cheaper Gemini 2.5 Flash, fall back to 2.5 Pro - cr.set_primary("Gemini25Flash") + cr.set_primary("Gemini20Flash") return cr @@ -97,23 +145,13 @@ async def process_article_async( try: # Content is already extracted text, just pass it to BAML logger.info( - f"Processing article: {article.get('title', 'Empty Article') | 'Empty Article'} posted at {article.get('posted_at', 'Unknown Time') | 'Unknown Time'} ({len(article_text):,} chars)" + f"Processing article: {article.get('title') or 'Empty Article'} posted at {article.get('posted_at') or 'Unknown Time'} ({len(article_text):,} chars)" ) # # Will try this later... # - # try: - # result = await async_b.ExtractIndustryArticle(article_text, {"client_registry": cr}) - # except BamlValidationError as e: - # logger.error(f"BAML validation error: {e}") - # # Retry exceptions with Gemini 2.5 Pro - # cr.set_primary("Gemini25Pro") - # result = await async_b.ExtractIndustryArticle(article_text, {"client_registry": cr}) - # finally: - # cr.set_primary("Gemini25Flash") - try: result = await async_b.ExtractIndustryArticle(article_text) except BamlValidationError as e: @@ -201,11 +239,14 @@ async def process_articles_async( """ # Process in batches - starting with the most recent articles all_results: list[IndustryArticle | BaseException | None] = [] - for i in tqdm(reversed(range(0, len(articles), batch_size))): + batch_indices = list(reversed(range(0, len(articles), batch_size))) + total_batches = len(batch_indices) + + for batch_num, i in enumerate( + tqdm(batch_indices, desc="Processing articles", unit="batch"), start=1 + ): batch = articles[i : i + batch_size] - logger.info( - f"Processing batch {i // batch_size + 1}/{(len(articles) + batch_size - 1) // batch_size}" - ) + logger.info(f"Processing batch {batch_num}/{total_batches}") batch_results = await process_batch(batch) all_results.extend(batch_results) diff --git a/abzu/articles/rss_processor.py b/abzu/articles/rss_processor.py index 71c9f94..bf2077c 100644 --- a/abzu/articles/rss_processor.py +++ b/abzu/articles/rss_processor.py @@ -10,19 +10,19 @@ def process_rss_feeds( feeds_file: Optional[str] = None, - input_dir: str = config.get("process.rss.input_dir"), + input_path: str = config.get("process.rss.input_dir"), output_dir: str = config.get("process.rss.output_dir"), batch_size: int = 5, ) -> int: - """Process all RSS feed articles defined in configuration or a feeds file. + """Process RSS feed articles from a file, directory, or configuration. Parameters ---------- feeds_file: Optional path to the feeds.txt file with ``source:url`` pairs. If not provided, uses feeds from configuration. - input_dir: - Directory where the crawled JSONL files are stored. + input_path: + Path to a single JSONL file or directory containing crawled JSONL files. output_dir: Directory where processed JSONL files will be written. batch_size: @@ -32,10 +32,20 @@ def process_rss_feeds( int ``0`` on success, ``1`` if any feed processing fails. """ - input_base = Path(input_dir) + input_path_obj = Path(input_path) output_base = Path(output_dir) output_base.mkdir(parents=True, exist_ok=True) + # If input is a single file, process it directly + if input_path_obj.is_file(): + source = input_path_obj.stem # e.g., "planet-analog" from "planet-analog.jsonl" + output_file = output_base / f"processed_{source}.jsonl" + + logger.info("Processing single RSS file: %s", input_path_obj) + return process_main(str(input_path_obj), str(output_file), batch_size) + + # Input is a directory - process multiple sources + input_base = input_path_obj any_failed = False # Get feed sources either from file or configuration diff --git a/abzu/cli/api/wiki.py b/abzu/cli/api/wiki.py index 6d2bd29..b27720c 100644 --- a/abzu/cli/api/wiki.py +++ b/abzu/cli/api/wiki.py @@ -5,6 +5,7 @@ import click from abzu.api.wiki import crawl_company_structured +from abzu.config import config from abzu.logs import get_logger logger = get_logger(__name__) @@ -25,78 +26,54 @@ "--input", "-i", type=click.Path(exists=True), - help="Input JSON Lines file with 'name' or 'ticker' fields", + default=config.get("process.kg.wiki.input"), + help="Input JSON Lines file with 'name' or 'ticker' fields.", ) @click.option( "--output", "-o", type=click.Path(), - help="Output file path for JSON results", + default=config.get("process.kg.wiki.output"), + help="Output file path for JSON results.", ) @click.option( "--concurrent", "-c", type=int, - default=5, - help="Number of concurrent requests for batch processing", + default=config.get("process.kg.wiki.concurrent"), + help="Number of concurrent requests for batch processing.", ) -def wiki(ticker: str, name: str, input: str, output: str, concurrent: int) -> None: - """Crawl Wikipedia page for a company by ticker or name.""" - if not ticker and not name and not input: - raise click.UsageError("Either --ticker, --name, or --input must be provided") - - if (ticker or name) and input: - raise click.UsageError("Cannot use --ticker/--name with --input") - - # Process single company +def wiki(ticker: str, name: str, input: str, output: str, concurrent: int) -> int: + """Crawl Wikipedia page for a company by ticker or name. + + For single company lookups, use --ticker or --name. + For batch processing, use --input to process a JSONL file of companies + and enrich them with Wikipedia metadata. + """ + # Single company lookup mode if ticker or name: + if input and input != config.get("process.kg.wiki.input"): + raise click.UsageError("Cannot use --ticker/--name with --input") + try: result = crawl_company_structured(ticker=ticker, name=name) - if output: + if output and output != config.get("process.kg.wiki.output"): with open(output, "w") as f: json.dump(result, f, indent=2) else: print(json.dumps(result, indent=2)) + return 0 except Exception as e: logger.error(f"Failed to crawl Wikipedia: {e}") raise click.ClickException(str(e)) - # Process batch from file - else: - results = [] - - with open(input, "r") as f: - for line_num, line in enumerate(f, 1): - line = line.strip() - if not line: - # Skip empty lines - continue - try: - data = json.loads(line) - company_ticker = data.get("ticker") - company_name = data.get("name") + # Batch processing mode - enrich companies with Wikipedia data + from abzu.kg.wiki import process_wiki - if not company_ticker and not company_name: - logger.warning( - f"Line {line_num}: No 'ticker' or 'name' field found, skipping" - ) - continue - - result = crawl_company_structured(ticker=company_ticker, name=company_name) - results.append(result) - - except json.JSONDecodeError as e: - logger.error(f"Line {line_num}: Invalid JSON - {e}") - except Exception as e: - logger.error(f"Line {line_num}: Failed to crawl - {e}") - - # Output results - if output: - with open(output, "w") as f: - for result in results: - f.write(json.dumps(result) + "\n") - else: - for result in results: - print(json.dumps(result, indent=2)) + return process_wiki( + companies_path=input, + output_path=output, + concurrent=concurrent, + ) diff --git a/abzu/cli/download/__init__.py b/abzu/cli/download/__init__.py index d4efe0c..ad5d5d1 100644 --- a/abzu/cli/download/__init__.py +++ b/abzu/cli/download/__init__.py @@ -1,30 +1,8 @@ """CLI commands for downloading datasets.""" -from typing import Any - import click - -class OrderedLazyGroup(click.Group): - """A Click group that preserves command order and loads commands lazily.""" - - def __init__( - self, *args: Any, lazy_subcommands: dict[str, str] | None = None, **kwargs: Any - ) -> None: - super().__init__(*args, **kwargs) - self.lazy_subcommands: dict[str, str] = lazy_subcommands or {} - - def list_commands(self, ctx: click.Context) -> list[str]: - """Return commands in the order they were added.""" - return list(self.lazy_subcommands.keys()) - - def get_command(self, ctx: click.Context, name: str) -> click.Command | None: - if name in self.lazy_subcommands: - import_path = self.lazy_subcommands[name] - module_name, attr_name = import_path.rsplit(":", 1) - module = __import__(module_name, fromlist=[attr_name]) - return getattr(module, attr_name) # type: ignore[no-any-return] - return None +from abzu.cli.utils import OrderedLazyGroup @click.command( diff --git a/abzu/cli/process/er/__init__.py b/abzu/cli/process/er/__init__.py index a0d82d2..4428cd3 100644 --- a/abzu/cli/process/er/__init__.py +++ b/abzu/cli/process/er/__init__.py @@ -1,37 +1,8 @@ """CLI commands for entity resolution processing.""" -from typing import Any - import click - -class OrderedLazyGroup(click.Group): - """A Click group that preserves command order and loads commands lazily.""" - - def __init__( - self, *args: Any, lazy_subcommands: dict[str, str] | None = None, **kwargs: Any - ) -> None: - super().__init__(*args, **kwargs) - self.lazy_subcommands: dict[str, str] = lazy_subcommands or {} - - def list_commands(self, ctx: click.Context) -> list[str]: - """Return commands in the order they were added.""" - return [ - "all", - "block", - "match", - "final", - "eval", - "clean", - ] - - def get_command(self, ctx: click.Context, name: str) -> click.Command | None: - if name in self.lazy_subcommands: - import_path = self.lazy_subcommands[name] - module_name, attr_name = import_path.rsplit(":", 1) - module = __import__(module_name, fromlist=[attr_name]) - return getattr(module, attr_name) # type: ignore[no-any-return] - return None +from abzu.cli.utils import OrderedLazyGroup @click.command( diff --git a/abzu/cli/process/er/all.py b/abzu/cli/process/er/all.py index 04cba8d..99dbd07 100644 --- a/abzu/cli/process/er/all.py +++ b/abzu/cli/process/er/all.py @@ -1,6 +1,7 @@ """CLI for running complete entity resolution cycle (block, match, eval).""" import json +import os import time from datetime import timedelta from pathlib import Path @@ -172,11 +173,18 @@ def get_evaluation_metrics(eval_path: str, metrics_path: str) -> dict[str, int | is_flag=True, help="Run in local mode", ) +@click.option( + "--debug", + "-d", + is_flag=True, + help="Enable BAML debug output (shows LLM input/output)", +) def all( iteration: int, max_block_size: int, batch_size: int, local_mode: bool, + debug: bool, ) -> None: """Run complete entity resolution cycle: block, match, final, and evaluate. @@ -188,6 +196,10 @@ def all( At the end, prints a comprehensive report of the entire cycle. """ + # Set BAML log level before importing BAML (must be set before import) + if not debug: + os.environ["BAML_LOG"] = "warn" + from abzu.er.match import match_entities from abzu.spark.er_block import build_blocks from abzu.spark.er_eval import evaluate_er_matches @@ -224,6 +236,12 @@ def all( eval_path = config.get("process.kg.er.paths.names.eval").format( iteration=iteration, format="json" ) + uuid_blocks_path = config.get("process.kg.er.paths.names.uuid_blocks").format( + iteration=iteration + ) + uuid_matches_path = config.get("process.kg.er.paths.names.uuid_matches").format( + iteration=iteration + ) # Step 1: Blocking click.echo(f"[1/4] BLOCKING (max_block_size={max_block_size})") @@ -287,6 +305,8 @@ def all( final_metrics = deduplicate_resolved_companies( matches_path=matches_path, output_path=final_path, + uuid_blocks_path=uuid_blocks_path, + uuid_matches_path=uuid_matches_path, iteration=iteration, batch_size=batch_size, local_mode=local_mode if local_mode else None, diff --git a/abzu/cli/process/er/clean.py b/abzu/cli/process/er/clean.py index 3e17155..e77d786 100644 --- a/abzu/cli/process/er/clean.py +++ b/abzu/cli/process/er/clean.py @@ -42,8 +42,8 @@ def clean(base_path: str, iteration: int | None, dry_run: bool, force: bool) -> patterns_to_clean = [ "*_blocks.parquet", "*_blocks.json", - "all_blocks.parquet", - "all_blocks.json", + "union_blocks.parquet", + "union_blocks.json", "combined_blocks.parquet", "combined_blocks.json", "matches.parquet", diff --git a/abzu/cli/process/er/eval/names.py b/abzu/cli/process/er/eval/names.py index 686d99f..2007ee2 100644 --- a/abzu/cli/process/er/eval/names.py +++ b/abzu/cli/process/er/eval/names.py @@ -16,9 +16,9 @@ @click.option( "--matches-path", "-m", - default=config.get("process.kg.er.paths.names.final"), + default=config.get("process.kg.er.paths.names.matches"), type=click.Path(exists=False, file_okay=True, dir_okay=True), - help="Path to deduplicated companies file (with {format} placeholder)", + help="Path to matches.jsonl file from match step", ) @click.option( "--raw-companies-path", @@ -32,7 +32,7 @@ "-o", default=config.get("process.kg.er.paths.names.eval"), type=click.Path(exists=False, file_okay=True, dir_okay=False), - help="Path to save evaluation results (with {format} placeholder)", + help="Path to save evaluation results", ) @click.option( "--local-mode", diff --git a/abzu/cli/process/er/final.py b/abzu/cli/process/er/final.py index 9ae418e..ce89954 100644 --- a/abzu/cli/process/er/final.py +++ b/abzu/cli/process/er/final.py @@ -16,6 +16,13 @@ type=int, help="Iteration number to process", ) +@click.option( + "--output-path", + "-o", + default=config.get("process.kg.er.paths.names.final"), + type=str, + help="Path to output final companies file (with {iteration} placeholder)", +) @click.option( "--batch-size", "-b", @@ -31,6 +38,7 @@ ) def final( iteration: int, + output_path: str, batch_size: int, local_mode: bool, ) -> None: @@ -44,13 +52,9 @@ def final( """ from abzu.spark.er_final import deduplicate_resolved_companies - # Get paths from config - matches_path = config.get("process.kg.er.paths.names.matches").format( - iteration=iteration, format="json" - ) - final_path = config.get("process.kg.er.paths.names.final").format( - iteration=iteration, format="json" - ) + # Format paths with iteration + matches_path = config.get("process.kg.er.paths.names.matches").format(iteration=iteration) + final_path = output_path.format(iteration=iteration) click.echo(f"Final deduplication for iteration {iteration}") click.echo(f"Input: {matches_path}") @@ -61,6 +65,12 @@ def final( metrics = deduplicate_resolved_companies( matches_path=matches_path, output_path=final_path, + uuid_blocks_path=config.get("process.kg.er.paths.names.uuid_blocks").format( + iteration=iteration + ), + uuid_matches_path=config.get("process.kg.er.paths.names.uuid_matches").format( + iteration=iteration + ), iteration=iteration, batch_size=batch_size, local_mode=local_mode if local_mode else None, diff --git a/abzu/cli/process/er/match/names.py b/abzu/cli/process/er/match/names.py index 590a107..3801b17 100644 --- a/abzu/cli/process/er/match/names.py +++ b/abzu/cli/process/er/match/names.py @@ -1,9 +1,10 @@ """CLI for name similarity-based entity resolution matching.""" +import os + import click from abzu.config import config -from abzu.er.match import match_entities @click.command(context_settings={"show_default": True}) @@ -47,6 +48,12 @@ default=None, help="Range of block sizes to process (e.g., 50:100)", ) +@click.option( + "--debug", + "-d", + is_flag=True, + help="Enable BAML debug output (shows LLM input/output)", +) def names( iteration: int, blocks_path: str, @@ -54,8 +61,15 @@ def names( batch_size: int, limit: int | None, size_range: str | None, + debug: bool, ) -> None: """Match entities within name similarity-based blocks.""" + # Set BAML log level before importing BAML (must be set before import) + if not debug: + os.environ["BAML_LOG"] = "warn" + + # Import after setting env var + from abzu.er.match import match_entities # Parse size range if provided min_size = None diff --git a/abzu/cli/process/kg/__init__.py b/abzu/cli/process/kg/__init__.py index 7759416..0f9ee18 100644 --- a/abzu/cli/process/kg/__init__.py +++ b/abzu/cli/process/kg/__init__.py @@ -32,6 +32,7 @@ def get_command(self, ctx: click.Context, name: str) -> click.Command | None: lazy_subcommands={ "raw": "abzu.cli.process.kg.raw:raw", "refine": "abzu.cli.process.kg.refine:refine", + "tickers": "abzu.cli.process.kg.tickers:tickers", }, ) def kg() -> None: diff --git a/abzu/cli/process/kg/refine.py b/abzu/cli/process/kg/refine.py index badbbae..d583857 100644 --- a/abzu/cli/process/kg/refine.py +++ b/abzu/cli/process/kg/refine.py @@ -12,8 +12,34 @@ default=config.get("process.kg.er.iteration", 3), help="ER iteration number to use for resolved companies.", ) -def refine(iteration: int) -> int: - """Refine knowledge graph by mapping relationships to resolved companies.""" +@click.option( + "--wiki/--no-wiki", + "enrich_wiki", + default=False, + help="Enrich companies with Wikipedia data after refinement.", +) +@click.option( + "--wiki-batch-size", + type=int, + default=config.get("process.kg.wiki.concurrent", 5), + help="Number of concurrent Wikipedia requests.", +) +@click.option( + "--wiki-limit", + type=int, + default=None, + help="Maximum number of companies to enrich with Wikipedia (for testing).", +) +def refine( + iteration: int, + enrich_wiki: bool, + wiki_batch_size: int, + wiki_limit: int | None, +) -> int: + """Refine knowledge graph by mapping relationships to resolved companies. + + Optionally enriches companies with Wikipedia data using --wiki flag. + """ # Import heavy module only when command is executed from abzu.kg.processor import process_refine_kg @@ -31,4 +57,7 @@ def refine(iteration: int) -> int: input_paths=input_paths, output_paths=output_paths, iteration=iteration, + enrich_wiki=enrich_wiki, + wiki_batch_size=wiki_batch_size, + wiki_limit=wiki_limit, ) diff --git a/abzu/cli/process/kg/tickers.py b/abzu/cli/process/kg/tickers.py new file mode 100644 index 0000000..d084d40 --- /dev/null +++ b/abzu/cli/process/kg/tickers.py @@ -0,0 +1,96 @@ +"""CLI command for matching companies with SEC tickers.""" + +import click + +from abzu.config import config + + +@click.command(context_settings={"show_default": True}) +@click.option( + "-i", + "--input", + "companies_path", + type=click.Path(exists=True), + default=config.get("process.kg.tickers.input"), + help="Input companies file (JSONL or Parquet) from raw KG.", +) +@click.option( + "-t", + "--tickers", + "tickers_path", + type=click.Path(), + default=config.get("process.kg.tickers.cache"), + help="Path to cache/load SEC tickers (downloaded automatically if missing).", +) +@click.option( + "-o", + "--output", + "output_path", + type=click.Path(), + default=config.get("process.kg.tickers.output"), + help="Output JSONL file for companies with tickers.", +) +@click.option( + "-u", + "--url", + "url", + type=str, + default=config.get("process.kg.tickers.url"), + help="URL to download SEC tickers from.", +) +@click.option( + "--threshold", + type=float, + default=0.85, + help="Minimum similarity threshold for a match (0-1).", +) +@click.option( + "-m", + "--model", + "model_name", + type=str, + default="intfloat/e5-base-v2", + help="Embedding model to use for similarity matching.", +) +@click.option( + "-n", + "--limit", + "limit", + type=int, + default=None, + help="Maximum number of companies to process (for testing).", +) +@click.option( + "--download/--no-download", + default=True, + help="Download tickers from SEC EDGAR (uses cache if exists).", +) +def tickers( + companies_path: str, + tickers_path: str, + output_path: str, + url: str, + threshold: float, + model_name: str, + limit: int | None, + download: bool, +) -> int: + """Match companies with SEC tickers using embedding similarity. + + Uses embedding-based similarity matching to find the best ticker match + for each company. Only matches above the similarity threshold are assigned. + + Downloads ticker data from SEC EDGAR automatically and caches it locally. + """ + from abzu.kg.tickers import process_tickers + + return process_tickers( + companies_path=companies_path, + tickers_path=tickers_path, + output_path=output_path, + url=url, + threshold=threshold, + model_name=model_name, + limit=limit, + download=download, + ) diff --git a/abzu/cli/process/rss/process.py b/abzu/cli/process/rss/process.py index f73b239..c1af644 100644 --- a/abzu/cli/process/rss/process.py +++ b/abzu/cli/process/rss/process.py @@ -16,10 +16,11 @@ ) @click.option( "-i", - "--input-dir", + "--input", + "input_path", default=config.get("process.rss.input_dir"), - type=click.Path(dir_okay=True, file_okay=False), - help="Directory containing crawled RSS JSONL files", + type=click.Path(exists=True), + help="Input directory or single JSONL file to process", ) @click.option( "-o", @@ -35,15 +36,16 @@ default=5, help="Number of articles to process concurrently", ) -def rss(feeds_file: str, input_dir: str, output_dir: str, batch_size: int) -> int: +def rss(feeds_file: str, input_path: str, output_dir: str, batch_size: int) -> int: """Process RSS articles through the LLM extraction pipeline. By default, uses feeds defined in config.yml under crawl.rss.feeds. Can optionally use a feeds file for backward compatibility. + Accepts either a directory of JSONL files or a single JSONL file. """ return process_rss_feeds( feeds_file=feeds_file, - input_dir=input_dir, + input_path=input_path, output_dir=output_dir, batch_size=batch_size, ) diff --git a/abzu/cli/utils.py b/abzu/cli/utils.py new file mode 100644 index 0000000..9f11794 --- /dev/null +++ b/abzu/cli/utils.py @@ -0,0 +1,27 @@ +"""Shared utility classes for CLI commands.""" + +from typing import Any + +import click + + +class OrderedLazyGroup(click.Group): + """A Click group that preserves command order and loads commands lazily.""" + + def __init__( + self, *args: Any, lazy_subcommands: dict[str, str] | None = None, **kwargs: Any + ) -> None: + super().__init__(*args, **kwargs) + self.lazy_subcommands: dict[str, str] = lazy_subcommands or {} + + def list_commands(self, ctx: click.Context) -> list[str]: + """Return commands in the order they were added.""" + return list(self.lazy_subcommands.keys()) + + def get_command(self, ctx: click.Context, name: str) -> click.Command | None: + if name in self.lazy_subcommands: + import_path = self.lazy_subcommands[name] + module_name, attr_name = import_path.rsplit(":", 1) + module = __import__(module_name, fromlist=[attr_name]) + return getattr(module, attr_name) # type: ignore[no-any-return] + return None diff --git a/abzu/crawl/base.py b/abzu/crawl/base.py index aac0b44..f714af9 100644 --- a/abzu/crawl/base.py +++ b/abzu/crawl/base.py @@ -491,7 +491,7 @@ def timeout_handler() -> None: cast(Any, reactor).stop() # Schedule the timeout (5 minutes) - timeout_id = cast(Any, reactor).callLater(300, timeout_handler) + timeout_id = cast(Any, reactor).callLater(3000, timeout_handler) # Start the process d = process_batches() diff --git a/abzu/crawl/dcbyte/extract_html.py b/abzu/crawl/dcbyte/extract_html.py index 8119366..ca25bf7 100644 --- a/abzu/crawl/dcbyte/extract_html.py +++ b/abzu/crawl/dcbyte/extract_html.py @@ -267,9 +267,11 @@ def main() -> None: logger.info(f"Found {len(posts)} blog posts") # Save post list for inspection - with open("dcbyte_posts_extracted.json", "w") as f: + extracted_path = Path("data/dcbyte_posts_extracted.json") + extracted_path.parent.mkdir(parents=True, exist_ok=True) + with open(extracted_path, "w") as f: json.dump(posts, f, indent=2) - logger.info("Post list saved to dcbyte_posts_extracted.json") + logger.info(f"Post list saved to {extracted_path}") # Fetch full content for each post Path(output_path).parent.mkdir(parents=True, exist_ok=True) diff --git a/abzu/er/match.py b/abzu/er/match.py index dc6df2a..28a9eac 100644 --- a/abzu/er/match.py +++ b/abzu/er/match.py @@ -17,7 +17,6 @@ from abzu.logs import get_logger from abzu.spark.config import get_spark_session from abzu.spark.schemas import validate_block_schema -from abzu.utils import save_jsonl logger = get_logger(__name__) @@ -84,6 +83,7 @@ async def process_blocks_async( # Process with progress bar results = [] + pbar: Any with tqdm(total=len(tasks), desc="Processing blocks") as pbar: for coro in asyncio.as_completed(tasks): result = await coro @@ -141,20 +141,20 @@ def match_entities( Match entities within blocks using BAML MultiEntityResolution. Args: - blocks_path: Path to the blocks parquet file (with {iteration} and {format} placeholders) - output_path: Path to save the matched entities (with {iteration} and {format} placeholders) + blocks_path: Path to the blocks parquet file (with {iteration} placeholder) + output_path: Path to save the matched entities JSONL file (with {iteration} placeholder) iteration: Iteration number for multi-round ER processing batch_size: Number of concurrent API calls limit: Maximum number of blocks to process (for testing) min_block_size: Minimum block size to process (inclusive) max_block_size: Maximum block size to process (inclusive) """ - # Format paths with iteration and format early for logging - blocks_json_path = blocks_path.format(iteration=iteration, format="json") - output_json_path = output_path.format(iteration=iteration, format="json") + # Format paths with iteration + blocks_parquet_path = blocks_path.format(iteration=iteration) + output_jsonl_path = output_path.format(iteration=iteration) - logger.info(f"Starting entity matching from {blocks_json_path}") - logger.info(f"Output path: {output_json_path}") + logger.info(f"Starting entity matching from {blocks_parquet_path}") + logger.info(f"Output path: {output_jsonl_path}") logger.info(f"Batch size: {batch_size}") if limit: logger.info(f"Limiting to {limit} blocks") @@ -162,9 +162,9 @@ def match_entities( logger.info(f"Block size range: {min_block_size or 'any'}:{max_block_size or 'any'}") # Check if blocks file exists - if not Path(blocks_json_path).exists(): + if not Path(blocks_parquet_path).exists(): error_msg = ( - f"Blocks file not found: {blocks_json_path}\n\n" + f"Blocks file not found: {blocks_parquet_path}\n\n" f"The matching step requires blocks from the blocking step.\n" f"Please run the blocking step first:\n" f" abzu process er block names --iteration {iteration}\n\n" @@ -174,7 +174,7 @@ def match_entities( logger.error(error_msg) raise FileNotFoundError(error_msg) - # Load blocks from JSON using PySpark to preserve Python lists + # Load blocks from Parquet using PySpark to preserve Python lists # Create or get SparkSession spark = get_spark_session(f"EntityResolutionMatch_Iteration{iteration}") @@ -182,8 +182,8 @@ def match_entities( # Disable Arrow optimization to preserve Python object types spark.conf.set("spark.sql.execution.arrow.pyspark.enabled", "false") - # Load JSON file with Spark (preserves lists as Python lists, not numpy arrays) - spark_df = spark.read.json(blocks_json_path) + # Load Parquet file with Spark (preserves lists as Python lists, not numpy arrays) + spark_df = spark.read.parquet(blocks_parquet_path) # Convert to pandas DataFrame # With Arrow disabled, toPandas() preserves Python lists without converting to numpy arrays @@ -425,8 +425,8 @@ def convert_row_to_dict(item: Any) -> Any: # No longer need ensure_list workaround - PySpark handles None values properly # Create output directory if it doesn't exist - json_output_path_obj = Path(output_json_path) - output_dir = json_output_path_obj.parent + jsonl_output_path_obj = Path(output_jsonl_path) + output_dir = jsonl_output_path_obj.parent output_dir.mkdir(parents=True, exist_ok=True) # Save error blocks to a separate file for debugging if any exist @@ -439,11 +439,17 @@ def convert_row_to_dict(item: Any) -> Any: if len(error_blocks) > 0: logger.warning(f"Found {len(error_blocks)} blocks with errors") - # Backup existing JSON file if it exists - backup_file(json_output_path_obj) + # Backup existing JSONL file if it exists + backup_file(jsonl_output_path_obj) - # Save to JSON format - save_jsonl(results_df, json_output_path_obj) + # # Deduplicate the pandas UDF + # duplicates = results_df.duplicated().count() + # logger.info(f"Found {duplicates} duplicate rows before deduplicating before saving") + # results_df = results_df.drop_duplicates() + + # Save to JSONL format using pandas + results_df.to_json(output_jsonl_path, orient="records", lines=True) + logger.info(f"Saved {len(results_df)} resolved blocks to {output_jsonl_path}") # Count recovered records by category error_recovered_records = 0 # Recovered from BAML API errors @@ -525,5 +531,5 @@ def convert_row_to_dict(item: Any) -> Any: logger.info(" ✓ All original UUIDs preserved in source_uuids arrays") logger.info("") logger.info("OUTPUT FILE:") - logger.info(f" Matches saved to: {output_json_path}") + logger.info(f" Matches saved to: {output_jsonl_path}") logger.info("=" * 60) diff --git a/abzu/er/tickers.py b/abzu/er/tickers.py deleted file mode 100644 index 3416690..0000000 --- a/abzu/er/tickers.py +++ /dev/null @@ -1,83 +0,0 @@ -import asyncio -import json -import logging -from pathlib import Path - -from abzu.baml_client import b as baml_client -from abzu.baml_client.types import Company, CompanyList, Ticker, TickerList - -logging.basicConfig(level=logging.DEBUG) -logger = logging.getLogger(__name__) - - -async def add_tickers_to_companies_blocked() -> list[Company]: - - block_size = 1000 - all_results = [] - # total_with_tickers = 0 - - ticker_file = Path("data/financialdatasets/all_tickers_facts_baml.jsonl") - tickers = [] - with open(ticker_file, "r") as f: - # Load first 100 tickers for testing - for i, line in enumerate(f): - # if i >= 100: - # break - ticker_data = json.loads(line) - ticker = Ticker( - name=ticker_data["name"], # type: ignore[call-arg] - symbol=ticker_data["symbol"], - exchange=ticker_data.get("exchange"), - ) - tickers.append(ticker) - - # Load company names - company_file = Path("data/company_names.json") - companies = [] - with open(company_file, "r") as f: - # Load first 100 companies for testing - for i, line in enumerate(f): - company_data = json.loads(line) - company = Company(name=company_data["name"]) # type: ignore[call-arg] - companies.append(company) - - # Create input objects - company_list = CompanyList(companies=companies) - ticker_list = TickerList(tickers=tickers) - - for i in range(0, len(companies), block_size): - # block_companies = companies[i : i + block_size] - print( - f"\nProcessing block {i // block_size + 1}: companies {i} to {min(i + block_size, len(companies))}" - ) - # Call the BAML function - result = await baml_client.AddTickersToCompanies( - company_list=company_list, ticker_list=ticker_list - ) - - all_results.extend(result.companies) - - # Show some examples of matched companies - matched_examples = [c for c in result.companies if c.ticker is not None][:5] - for company in matched_examples: - if company.ticker: - logger.debug(f"{company.name} -> {company.ticker.symbol}") - - # Count how many companies have tickers assigned - companies_with_tickers = sum(1 for c in result.companies if c.ticker is not None) - - # Print results for debugging - print(f"Total companies: {len(result.companies)}") - print(f"Companies with tickers: {companies_with_tickers}") - print(f"Companies without tickers: {len(result.companies) - companies_with_tickers}") - - return all_results - - -if __name__ == "__main__": - # Run the tests - companies_with_tickers = asyncio.run(add_tickers_to_companies_blocked()) - - with open("data/financialdatasets/companies_with_tickers.jsonl", "w") as f: - for company in companies_with_tickers: - f.write(json.dumps(company.to_dict()) + "\n") # type: ignore[attr-defined] diff --git a/abzu/er/uuid.py b/abzu/er/uuid.py index a6fad0f..44f4669 100644 --- a/abzu/er/uuid.py +++ b/abzu/er/uuid.py @@ -130,7 +130,7 @@ async def process_block_with_uuid_mapping( companies_data = block["companies"] # Log the input companies and their source_uuids - logger.debug(f"Processing block {block_key} with {len(companies_data)} companies") + logger.debug(f"Processing block {block_key} with {len(companies_data):,} companies") for i, comp in enumerate(companies_data): logger.debug( f" Company {i}: uuid={comp.get('uuid')}, source_uuids={comp.get('source_uuids')}" @@ -171,7 +171,7 @@ async def process_block_with_uuid_mapping( } try: - logger.info(f"Processing block '{block_key}' with {len(companies_data)} companies") + logger.debug(f"Processing block '{block_key}' with {len(companies_data)} companies") # Create UUID mapper for this block mapper = UUIDMapper() diff --git a/abzu/kg/processor.py b/abzu/kg/processor.py index 2215a84..936db3b 100644 --- a/abzu/kg/processor.py +++ b/abzu/kg/processor.py @@ -174,15 +174,22 @@ def process_refine_kg( "edges": config.get("process.kg.refine.output.edges"), }, iteration: int = 3, + enrich_wiki: bool = False, + wiki_batch_size: int = 5, + wiki_limit: int | None = None, ) -> int: """Refine the knowledge graph by mapping relationships to resolved companies. - This function calls the refine_knowledge_graph function directly. + This function calls the refine_knowledge_graph function directly, + and optionally enriches companies with Wikipedia data. Args: - input_dir: Path to the directory with raw knowledge graph - output_dir: Directory to store the refined knowledge graph + input_paths: Paths to input data (companies and relationships) + output_paths: Paths to output data (nodes and edges) iteration: ER iteration number to use for resolved companies + enrich_wiki: Whether to enrich companies with Wikipedia data + wiki_batch_size: Number of concurrent Wikipedia requests + wiki_limit: Maximum number of companies to enrich (for testing) Returns: 0 on success, 1 on failure @@ -199,6 +206,26 @@ def process_refine_kg( input_paths=input_paths, output_paths=output_paths, iteration=iteration ) logger.info("Knowledge graph refinement completed successfully") + + # Optionally enrich with Wikipedia data + if enrich_wiki: + logger.info("Enriching companies with Wikipedia data...") + from abzu.kg.wiki import process_wiki + + # The nodes are saved as JSONL - use that path + nodes_jsonl = output_paths["nodes"].replace(".parquet", ".jsonl") + enriched_output = nodes_jsonl.replace(".jsonl", "_enriched.jsonl") + + result = process_wiki( + companies_path=nodes_jsonl, + output_path=enriched_output, + limit=wiki_limit, + tickers_only=False, # Enrich all companies, not just those with tickers + batch_size=wiki_batch_size, + ) + if result != 0: + logger.warning("Wikipedia enrichment had issues but continuing") + return 0 except Exception as e: diff --git a/abzu/kg/tickers.py b/abzu/kg/tickers.py new file mode 100644 index 0000000..5418bdc --- /dev/null +++ b/abzu/kg/tickers.py @@ -0,0 +1,342 @@ +"""Ticker matching module for enriching companies with SEC ticker data. + +Uses embedding-based similarity to match company names from SEC tickers +to company names in the knowledge graph. +""" + +import json +from pathlib import Path +from typing import Any, Literal + +import numpy as np +import requests +import torch +from sentence_transformers import SentenceTransformer +from sentence_transformers.util import cos_sim +from tqdm import tqdm + +from abzu.config import config +from abzu.logs import get_logger +from abzu.utils import load_jsonl, save_jsonl + +logger = get_logger(__name__) + +USER_AGENT = "AbzuBot/1.0 (russell.jurney@gmail.com)" + + +def download_sec_tickers( + cache_path: str = config.get("process.kg.tickers.cache"), + url: str = config.get("process.kg.tickers.url"), + force: bool = False, +) -> str: + """Download SEC tickers from SEC EDGAR if not cached. + + Args: + cache_path: Path to cache the downloaded file + url: URL to download SEC tickers from + force: Force re-download even if cached + + Returns: + Path to the downloaded/cached file + """ + cache_file = Path(cache_path) + + if cache_file.exists() and not force: + logger.info(f"Using cached SEC tickers from {cache_path}") + return cache_path + + logger.info(f"Downloading SEC tickers from {url}") + + cache_file.parent.mkdir(parents=True, exist_ok=True) + + response = requests.get(url, headers={"User-Agent": USER_AGENT}, timeout=30) + response.raise_for_status() + + with open(cache_path, "w") as f: + f.write(response.text) + + logger.info(f"Downloaded and cached SEC tickers to {cache_path}") + return cache_path + + +def load_tickers(tickers_path: str) -> list[dict[str, Any]]: + """Load tickers from JSON or JSONL file. + + Supports: + - SEC company_tickers_exchange.json format: {"fields": [...], "data": [[cik, name, ticker, exchange], ...]} + - SEC company_tickers.json format: {"0": {"cik_str": ..., "ticker": ..., "title": ...}, ...} + - JSONL format: {"name": ..., "symbol": ..., "exchange": ...} + + Args: + tickers_path: Path to the tickers file + + Returns: + List of ticker dictionaries normalized to {cik, symbol, name, exchange} format + """ + logger.info(f"Loading tickers from {tickers_path}") + + if tickers_path.endswith(".json"): + with open(tickers_path) as f: + data = json.load(f) + + # SEC exchange format: {"fields": ["cik", "name", "ticker", "exchange"], "data": [[...], ...]} + if "fields" in data and "data" in data: + fields = data["fields"] + tickers = [] + for row in data["data"]: + record = dict(zip(fields, row)) + tickers.append( + { + "cik": record.get("cik"), + "symbol": record.get("ticker", ""), + "name": record.get("name", ""), + "exchange": record.get("exchange"), + } + ) + # Old SEC format: {"0": {"cik_str": ..., "ticker": ..., "title": ...}, ...} + elif isinstance(data, dict) and all(k.isdigit() for k in list(data.keys())[:10]): + tickers = [ + { + "cik": int(v.get("cik_str", 0)) if v.get("cik_str") else None, + "symbol": v.get("ticker", ""), + "name": v.get("title", ""), + "exchange": None, + } + for v in data.values() + ] + else: + raise ValueError(f"Unknown JSON format in {tickers_path}") + else: + # JSONL format - normalize field names if needed + raw_tickers = load_jsonl(tickers_path) + tickers = [ + { + "cik": t.get("cik"), + "symbol": t.get("symbol", t.get("ticker", "")), + "name": t.get("name", t.get("title", "")), + "exchange": t.get("exchange"), + } + for t in raw_tickers + ] + + logger.info(f"Loaded {len(tickers):,} tickers") + return tickers + + +def load_companies(companies_path: str) -> list[dict[str, Any]]: + """Load companies from Parquet file or directory. + + Args: + companies_path: Path to the companies.parquet file or Spark output directory + + Returns: + List of company dictionaries + """ + import math + + import numpy as np + import pandas as pd + + logger.info(f"Loading companies from {companies_path}") + + # Pandas read_parquet handles both files and directories + df = pd.read_parquet(companies_path) + + # Convert to list of dicts + companies = df.to_dict(orient="records") + + # Replace NaN/NaT with None for Pydantic compatibility + # Pandas converts null integers to float NaN, which Pydantic rejects + for company in companies: + for key, value in company.items(): + if isinstance(value, float) and math.isnan(value): + company[key] = None + elif isinstance(value, np.floating) and np.isnan(value): + company[key] = None + elif pd.isna(value): + company[key] = None + + logger.info(f"Loaded {len(companies):,} companies") + return companies + + +class TickerMatcher: + """Match companies with SEC tickers using embedding similarity.""" + + def __init__(self, model_name: str = "intfloat/e5-base-v2"): + """Initialize the ticker matcher with an embedding model. + + Args: + model_name: Name of the sentence-transformers model to use + """ + device: Literal["cpu", "cuda", "mps"] + if torch.cuda.is_available(): + device = "cuda" + elif torch.backends.mps.is_available(): + device = "mps" + else: + device = "cpu" + + logger.info(f"Loading embedding model {model_name} on {device}") + self.model = SentenceTransformer(model_name, device=device) + self.device = device + + def encode_names(self, names: list[str], batch_size: int = 64) -> np.ndarray: + """Encode company names into embeddings. + + Args: + names: List of company names + batch_size: Batch size for encoding + + Returns: + Numpy array of embeddings + """ + embeddings = self.model.encode( + names, + normalize_embeddings=True, + show_progress_bar=True, + batch_size=batch_size, + ) + return embeddings + + def find_matches( + self, + company_names: list[str], + ticker_names: list[str], + threshold: float = 0.85, + ) -> list[tuple[int, int, float]]: + """Find matches between company names and ticker names. + + Args: + company_names: List of company names from KG + ticker_names: List of company names from SEC tickers + threshold: Minimum similarity threshold for a match + + Returns: + List of (company_idx, ticker_idx, similarity) tuples + """ + logger.info(f"Encoding {len(company_names):,} company names...") + company_embeddings = self.encode_names(company_names) + + logger.info(f"Encoding {len(ticker_names):,} ticker names...") + ticker_embeddings = self.encode_names(ticker_names) + + logger.info("Computing similarities...") + # Compute cosine similarity matrix + similarities = cos_sim(company_embeddings, ticker_embeddings).numpy() + + # Find best matches above threshold + matches = [] + for i in tqdm(range(len(company_names)), desc="Finding matches"): + best_j = int(np.argmax(similarities[i])) + best_sim = float(similarities[i, best_j]) + if best_sim >= threshold: + matches.append((i, best_j, best_sim)) + + return matches + + +def process_tickers( + companies_path: str = config.get("process.kg.tickers.input"), + tickers_path: str = config.get("process.kg.tickers.cache"), + output_path: str = config.get("process.kg.tickers.output"), + url: str = config.get("process.kg.tickers.url"), + threshold: float = 0.85, + model_name: str = "intfloat/e5-base-v2", + limit: int | None = None, + download: bool = True, + **kwargs: Any, # Accept but ignore old BAML-related args +) -> int: + """Process companies and match them with SEC tickers using embeddings. + + Uses embedding similarity to match company names from the knowledge graph + with company names from SEC ticker data. Only matches above the similarity + threshold are assigned. + + Args: + companies_path: Path to companies.jsonl or companies.parquet + tickers_path: Path to cache/load SEC tickers + output_path: Path to write enriched companies + url: URL to download SEC tickers from + threshold: Minimum similarity threshold for a match (0-1) + model_name: Embedding model to use + limit: Maximum number of companies to process (for testing) + download: Whether to download tickers from SEC (uses cache if exists) + + Returns: + 0 on success, 1 on failure + """ + # Download SEC tickers if requested + if download: + tickers_path = download_sec_tickers(cache_path=tickers_path, url=url) + + # Load data + companies_data = load_companies(companies_path) + tickers_data = load_tickers(tickers_path) + + # Apply limit if specified + if limit is not None: + companies_data = companies_data[:limit] + logger.info(f"Limited to {len(companies_data):,} companies") + + if not companies_data: + logger.error("No companies to process") + return 1 + + if not tickers_data: + logger.error("No tickers to process") + return 1 + + logger.info(f"Processing {len(companies_data):,} companies with {len(tickers_data):,} tickers") + logger.info(f"Using similarity threshold: {threshold}") + + # Extract names + company_names = [c.get("name", "") for c in companies_data] + ticker_names = [t.get("name", "") for t in tickers_data] + + # Initialize matcher and find matches + matcher = TickerMatcher(model_name=model_name) + matches = matcher.find_matches(company_names, ticker_names, threshold=threshold) + + logger.info(f"Found {len(matches):,} matches above threshold {threshold}") + + # Build lookup from company index to ticker data + match_lookup = {company_idx: (ticker_idx, sim) for company_idx, ticker_idx, sim in matches} + + # Enrich companies with matched tickers + enriched_companies = [] + for i, company in tqdm( + enumerate(companies_data), desc="Enriching companies", total=len(companies_data) + ): + enriched = company.copy() + if i in match_lookup: + ticker_idx, similarity = match_lookup[i] + ticker_data = tickers_data[ticker_idx] + enriched["ticker"] = { + "symbol": ticker_data.get("symbol", ""), + "name": ticker_data.get("name", ""), + "exchange": ticker_data.get("exchange"), + "cik": ticker_data.get("cik"), + } + enriched["ticker_similarity"] = similarity + logger.debug( + f"Matched '{company.get('name')}' -> '{ticker_data.get('name')}' " + f"({ticker_data.get('symbol')}) with similarity {similarity:.3f}" + ) + enriched_companies.append(enriched) + + # Count matches + matched_count = len(matches) + logger.info( + f"Matched {matched_count:,} companies with tickers " + f"({matched_count * 100 / len(companies_data):.1f}%)" + ) + + # Save results + if save_jsonl(enriched_companies, output_path, create_backup=True): + logger.info(f"Saved enriched companies to {output_path}") + else: + logger.error(f"Failed to save results to {output_path}") + return 1 + + return 0 diff --git a/abzu/kg/wiki.py b/abzu/kg/wiki.py new file mode 100644 index 0000000..8c911ad --- /dev/null +++ b/abzu/kg/wiki.py @@ -0,0 +1,230 @@ +"""Wikipedia enrichment module for companies.""" + +import asyncio +import glob +import os +from typing import Any + +import aiohttp +from tqdm.asyncio import tqdm as atqdm + +from abzu.api.wiki import crawl_company_structured_async +from abzu.config import config +from abzu.logs import get_logger +from abzu.utils import load_jsonl, save_jsonl + +logger = get_logger(__name__) + + +def load_companies(companies_path: str) -> list[dict[str, Any]]: + """Load companies from JSONL file or Spark output directory. + + Args: + companies_path: Path to the companies.jsonl file or Spark output directory + + Returns: + List of company dictionaries + """ + logger.info(f"Loading companies from {companies_path}") + + # Handle Spark output directory (contains part-*.json files) + if os.path.isdir(companies_path): + part_files = glob.glob(os.path.join(companies_path, "part-*.json")) + if part_files: + companies: list[dict[str, Any]] = [] + for part_file in sorted(part_files): + companies.extend(load_jsonl(part_file)) + logger.info(f"Loaded {len(companies):,} companies from Spark output directory") + return companies + + # Regular JSONL file + companies = load_jsonl(companies_path) + logger.info(f"Loaded {len(companies):,} companies") + return companies + + +def has_ticker(company: dict[str, Any]) -> bool: + """Check if a company has a valid ticker. + + Args: + company: Company dictionary + + Returns: + True if company has a non-null ticker with a symbol + """ + ticker = company.get("ticker") + if not ticker: + return False + if isinstance(ticker, dict): + return bool(ticker.get("symbol")) + return False + + +async def enrich_company_with_wiki_async( + session: aiohttp.ClientSession, + company: dict[str, Any], + semaphore: asyncio.Semaphore, +) -> dict[str, Any]: + """Async version: Enrich a single company with Wikipedia data. + + Args: + session: aiohttp ClientSession for making requests + company: Company dictionary + semaphore: Semaphore to limit concurrency + + Returns: + Company dictionary with Wikipedia enrichment applied + """ + async with semaphore: + company_name = company.get("name") + + if not company_name: + logger.warning(f"Company {company.get('uuid', 'unknown')} has no name, skipping") + return company + + try: + wiki_data = await crawl_company_structured_async(session, name=company_name) + + # Merge Wikipedia data into company, but don't overwrite existing non-null values + enriched_company = company.copy() + + # Fields to merge from Wikipedia (only if not already set in company) + wiki_fields = [ + "description", + "website_url", + "headquarters_location", + "revenue_usd", + "employees", + "founded_year", + "ceo", + "linkedin_url", + ] + + for field in wiki_fields: + wiki_value = wiki_data.get(field) + company_value = enriched_company.get(field) + + # Only use Wikipedia value if company value is empty/null + if wiki_value and not company_value: + enriched_company[field] = wiki_value + logger.debug(f"Enriched {company_name} {field} from Wikipedia") + + return enriched_company + + except Exception as e: + logger.warning(f"Failed to enrich {company_name} with Wikipedia data: {e}") + return company + + +async def process_wiki_async( + companies_to_enrich: list[dict[str, Any]], + batch_size: int = 5, +) -> tuple[list[dict[str, Any]], int]: + """Async processing of Wikipedia enrichment with concurrency control. + + Args: + companies_to_enrich: List of companies to enrich + batch_size: Maximum concurrent requests + + Returns: + Tuple of (enriched_companies, enriched_count) + """ + semaphore = asyncio.Semaphore(batch_size) + enriched_companies: list[dict[str, Any]] = [] + enriched_count = 0 + + timeout = aiohttp.ClientTimeout(total=60) + async with aiohttp.ClientSession(timeout=timeout) as session: + tasks = [ + enrich_company_with_wiki_async(session, company, semaphore) + for company in companies_to_enrich + ] + + # Use tqdm.asyncio for async progress bar + results = await atqdm.gather(*tasks, desc="Enriching with Wikipedia") + + for company, enriched in zip(companies_to_enrich, results): + enriched_companies.append(enriched) + if enriched != company: + enriched_count += 1 + + return enriched_companies, enriched_count + + +def process_wiki( + companies_path: str = config.get("process.kg.wiki.input"), + output_path: str = config.get("process.kg.wiki.output"), + limit: int | None = None, + tickers_only: bool = True, + batch_size: int = config.get("process.kg.wiki.concurrent", 5), +) -> int: + """Enrich companies with Wikipedia data. + + Only processes companies that have tickers, since Wikipedia lookups + work best with ticker symbols for disambiguation. + + Args: + companies_path: Path to input companies JSONL file + output_path: Path to write enriched companies + limit: Maximum number of companies to process (for testing) + tickers_only: If True, only process companies with tickers (default: True) + batch_size: Number of concurrent Wikipedia requests (default: 5) + + Returns: + 0 on success, 1 on failure + """ + # Load companies + all_companies = load_companies(companies_path) + + if not all_companies: + logger.error("No companies to process") + return 1 + + # Filter to companies with tickers if requested + if tickers_only: + companies_to_enrich = [c for c in all_companies if has_ticker(c)] + companies_without_tickers = [c for c in all_companies if not has_ticker(c)] + logger.info( + f"Found {len(companies_to_enrich):,} companies with tickers, " + f"{len(companies_without_tickers):,} without" + ) + else: + companies_to_enrich = all_companies + companies_without_tickers = [] + + # Apply limit if specified + if limit is not None: + companies_to_enrich = companies_to_enrich[:limit] + logger.info(f"Limited to {len(companies_to_enrich):,} companies for processing") + + if not companies_to_enrich: + logger.warning("No companies with tickers to enrich") + # Still save all companies (unchanged) to output + if save_jsonl(all_companies, output_path, create_backup=True): + logger.info(f"Saved {len(all_companies):,} companies (unchanged) to {output_path}") + return 0 + + logger.info( + f"Processing {len(companies_to_enrich):,} companies for Wikipedia enrichment " + f"(batch_size={batch_size})" + ) + + # Run async enrichment + enriched_companies, enriched_count = asyncio.run( + process_wiki_async(companies_to_enrich, batch_size=batch_size) + ) + + logger.info(f"Enriched {enriched_count:,} companies with Wikipedia data") + + # Combine enriched companies with those without tickers + final_companies = enriched_companies + companies_without_tickers + logger.info(f"Total companies to save: {len(final_companies):,}") + + # Save results + if save_jsonl(final_companies, output_path, create_backup=True): + logger.info(f"Saved enriched companies to {output_path}") + else: + logger.error(f"Failed to save results to {output_path}") + return 1 + + return 0 diff --git a/abzu/spark/README.md b/abzu/spark/README.md index 31bb9e9..ab1f1fc 100644 --- a/abzu/spark/README.md +++ b/abzu/spark/README.md @@ -580,3 +580,13 @@ WIP - To enforce consistent code style, each main repository should have [Pylint # Data formats - Save data in Parquet and JSON Lines Save all datasets in both Parquet and JSON Lines formats. This ensures we can easily inspect the data in a human-readable format (JSON Lines) with `jq` while also benefiting from the performance and storage efficiency of Parquet. + +# Things to NEVER Do + +Never replace part of a path that didn't come from @config.yml with a parameter such as: + +```python +uuid_blocks_path = output_path.replace("companies_final.parquet", "uuid_blocks.parquet") +``` + +This makes it impossible to track dependencies between datasets. Always use explicit parameters for paths from `abzu.config.config.get` or function parameters. diff --git a/abzu/spark/er_block.py b/abzu/spark/er_block.py index 6f6d68a..0980810 100644 --- a/abzu/spark/er_block.py +++ b/abzu/spark/er_block.py @@ -2,7 +2,7 @@ """Entity resolution blocking strategies for company matching.""" import logging import os -from typing import Any, Optional +from typing import Optional import pyspark.sql.functions as F import pyspark.sql.types as T @@ -16,6 +16,7 @@ get_company_fields_without_blocks, normalize_company_dataframe, ) +from abzu.spark.utils import create_split_large_blocks_udtf logger = get_logger(__name__) @@ -148,6 +149,7 @@ def get_acronym(name: str) -> str | None: """Generate acronyms from company names, fallback to first word if no acronym.""" if not name or not name.strip(): return "UNKNOWN" # Fallback for empty names + # Remove domain suffix (only known TLDs like .com, .org, etc.) name_without_suffix = remove_domain_suffix(name.strip()) acronym = get_acronyms(name_without_suffix) @@ -211,7 +213,10 @@ def build_blocks( # Load companies (always in regular format after UUID resolution) logger.info(f"Loading companies from {input_path}") - companies_df_raw: DataFrame = spark.read.json(input_path) + if input_path.endswith(".parquet") or input_path.endswith(".parquet/"): + companies_df_raw: DataFrame = spark.read.parquet(input_path) + else: + companies_df_raw = spark.read.json(input_path) # Normalize schema to ensure consistency across iterations logger.info("Normalizing company schema to match BAML definition...") @@ -420,7 +425,10 @@ def build_blocks( logger.info("Grouping complete company records by blocking keys...") # Join filtered companies back with full company data - full_companies_df_raw = spark.read.json(input_path) + if input_path.endswith(".parquet") or input_path.endswith(".parquet/"): + full_companies_df_raw = spark.read.parquet(input_path) + else: + full_companies_df_raw = spark.read.json(input_path) # Normalize again to ensure consistency full_companies_df = normalize_company_dataframe( full_companies_df_raw, preserve_extra_fields=False @@ -542,26 +550,8 @@ def build_blocks( ) logger.debug(f"UDTF return type: {udtf_return_type}") - @F.udtf(returnType=udtf_return_type) # type: ignore - class SplitLargeBlocks: - def eval( - self, - block_key: str, - block_key_type: str, - companies: list[dict[str, Any]], - block_size: int, - ): # type: ignore - if block_size <= actual_max_block_size: - yield (block_key, block_key_type, companies, block_size) - else: - chunk_num = 1 - for i in range(0, len(companies), actual_max_block_size): - chunk_companies = companies[i : i + actual_max_block_size] - chunk_key = f"{block_key}_chunk_{chunk_num}" - yield (chunk_key, block_key_type, chunk_companies, len(chunk_companies)) - chunk_num += 1 - - # 2) Register the UDTF for SQL use + # Use shared UDTF factory from utils.py + SplitLargeBlocks = create_split_large_blocks_udtf(udtf_return_type, actual_max_block_size) spark.udtf.register("split_large_blocks", SplitLargeBlocks) # type: ignore # 3) Create temp views for the DataFrames @@ -638,26 +628,19 @@ def eval( logger.info(f"No blocks exceeded max size of {actual_max_block_size}, no splitting needed") # Save combined blocks separately - # Use ignoreNullFields=false to preserve all Company fields even when null - combined_blocks_json_path = os.path.join(output_path, "combined_blocks.json") - logger.info(f"Persisting combined blocks to {combined_blocks_json_path}") - combined_blocks_final.repartition(1).write.mode("overwrite").option( - "ignoreNullFields", "false" - ).json(combined_blocks_json_path) + combined_blocks_path = os.path.join(output_path, "combined_blocks.parquet") + logger.info(f"Persisting combined blocks to {combined_blocks_path}") + combined_blocks_final.repartition(1).write.mode("overwrite").parquet(combined_blocks_path) # Save first_word_only blocks separately - first_word_json_path = os.path.join(output_path, "first_word_blocks.json") - logger.info(f"Persisting first word blocks to {first_word_json_path}") - first_word_blocks_final.repartition(1).write.mode("overwrite").option( - "ignoreNullFields", "false" - ).json(first_word_json_path) + first_word_path = os.path.join(output_path, "first_word_blocks.parquet") + logger.info(f"Persisting first word blocks to {first_word_path}") + first_word_blocks_final.repartition(1).write.mode("overwrite").parquet(first_word_path) # Save acronym_only blocks separately - acronym_json_path = os.path.join(output_path, "acronym_blocks.json") - logger.info(f"Persisting acronym blocks to {acronym_json_path}") - acronym_blocks_final.repartition(1).write.mode("overwrite").option( - "ignoreNullFields", "false" - ).json(acronym_json_path) + acronym_path = os.path.join(output_path, "acronym_blocks.parquet") + logger.info(f"Persisting acronym blocks to {acronym_path}") + acronym_blocks_final.repartition(1).write.mode("overwrite").parquet(acronym_path) # Handle companies with UNKNOWN block keys - create singleton blocks for them unblocked_blocks_df = None @@ -683,34 +666,34 @@ def eval( logger.info(f"Created {unblocked_blocks_df.count()} singleton blocks for UNKNOWN companies") - # Create unified all_blocks output by combining all block types - logger.info("Creating unified all_blocks output...") + # Create unified union_blocks output by combining all block types + logger.info("Creating unified union_blocks output...") if unblocked_blocks_df is not None: - all_blocks_df = ( + union_blocks_df = ( combined_blocks_final.unionByName(first_word_blocks_final) .unionByName(acronym_blocks_final) .unionByName(unblocked_blocks_df) ) else: - all_blocks_df = combined_blocks_final.unionByName(first_word_blocks_final).unionByName( + union_blocks_df = combined_blocks_final.unionByName(first_word_blocks_final).unionByName( acronym_blocks_final ) - # Save all_blocks to both JSON and Parquet formats - all_blocks_json_path = os.path.join(output_path, "all_blocks.json") + # Save union_blocks to Parquet format + union_blocks_path = os.path.join(output_path, "union_blocks.parquet") - logger.info(f"Persisting all blocks to {all_blocks_json_path}") - all_blocks_df.repartition(1).write.mode("overwrite").option("ignoreNullFields", "false").json( - all_blocks_json_path - ) + logger.info(f"Persisting all blocks to {union_blocks_path}") + union_blocks_df.repartition(1).write.mode("overwrite").parquet(union_blocks_path) # Count total blocks and companies in unified output - all_blocks_count = all_blocks_df.count() - all_blocks_companies = all_blocks_df.agg( + union_blocks_count = union_blocks_df.count() + union_blocks_companies = union_blocks_df.agg( F.coalesce(F.sum("block_size"), F.lit(0)).alias("total") ).collect()[0]["total"] - logger.info(f"Unified all_blocks: {all_blocks_count} blocks, {all_blocks_companies} companies") + logger.info( + f"Unified union_blocks: {union_blocks_count} blocks, {union_blocks_companies} companies" + ) # Count blocks by type (after filtering and splitting) combined_block_count = combined_blocks_final.count() @@ -820,10 +803,10 @@ def eval( ) logger.info("") logger.info("OUTPUT FILES:") - logger.info(f" Combined blocks: {combined_blocks_json_path}") - logger.info(f" First word blocks: {first_word_json_path}") - logger.info(f" Acronym blocks: {acronym_json_path}") - logger.info(f" All blocks unified: {all_blocks_json_path}") + logger.info(f" Combined blocks: {combined_blocks_path}") + logger.info(f" First word blocks: {first_word_path}") + logger.info(f" Acronym blocks: {acronym_path}") + logger.info(f" All blocks unified: {union_blocks_path}") logger.info("=" * 60) # Clean up diff --git a/abzu/spark/er_eval.py b/abzu/spark/er_eval.py index 61b805d..964df84 100644 --- a/abzu/spark/er_eval.py +++ b/abzu/spark/er_eval.py @@ -10,13 +10,13 @@ from abzu.config import config from abzu.logs import get_logger from abzu.spark.config import get_spark_session -from abzu.spark.schemas import get_company_spark_schema +from abzu.spark.schemas import get_company_spark_schema, get_matches_schema logger = get_logger(__name__) def evaluate_er_matches( - matches_path: str = config.get("process.kg.er.paths.names.final"), + matches_path: str = config.get("process.kg.er.paths.names.matches"), raw_companies_path: str = os.path.join( config.get("process.kg.raw.output"), "companies.parquet" ), @@ -29,14 +29,14 @@ def evaluate_er_matches( This function: 1. Explodes resolved companies from blocks - 2. Creates companies_resolved.json/parquet files + 2. Creates companies_resolved.parquet files 3. Reports metrics on data reduction and UUID overlap 4. Validates source_uuids against ORIGINAL raw companies data 5. Removes invalid source_uuids and reports error percentage 6. For iteration 2+, tracks coverage against both original and previous iteration Args: - matches_path: Path to the final deduplicated companies file from ER final step + matches_path: Path to matches.jsonl from the ER match step raw_companies_path: Path to the ORIGINAL raw companies parquet file (iteration 0) output_path: Directory path to save evaluation results iteration: Iteration number (1, 2, 3, etc.) @@ -48,17 +48,16 @@ def evaluate_er_matches( local_mode=local_mode, ) - # Format matches_path for reading (should have {iteration} and {format} placeholders) - # Use JSON instead of Parquet because Parquet loses source_uuids - matches_json_path = matches_path.format(iteration=iteration, format="json") + # Format matches_path for reading (should have {iteration} placeholder) + matches_jsonl_path = matches_path.format(iteration=iteration) # Check if required input files exist - if not os.path.exists(matches_json_path): + if not os.path.exists(matches_jsonl_path): error_msg = ( - f"Final deduplicated companies file not found: {matches_json_path}\n\n" - f"The evaluation step requires output from the final deduplication step.\n" - f"Please run the final deduplication step first:\n" - f" abzu process er final --iteration {iteration}\n\n" + f"Matches file not found: {matches_jsonl_path}\n\n" + f"The evaluation step requires output from the match step.\n" + f"Please run the match step first:\n" + f" abzu process er match names --iteration {iteration}\n\n" f"Or run the complete pipeline:\n" f" abzu process er all names --iteration {iteration}" ) @@ -75,17 +74,22 @@ def evaluate_er_matches( logger.error(error_msg) raise FileNotFoundError(error_msg) - logger.info(f"Loading matches from {matches_json_path}") - # Use the Company schema to ensure correct types (especially match_skip_history as array) - company_schema = get_company_spark_schema() - matches_df: DataFrame = spark.read.schema(company_schema).json(matches_json_path) + logger.info(f"Loading matches from {matches_jsonl_path}") + # Read block records from matches.jsonl with explicit schema to enforce BAML types + blocks_df: DataFrame = spark.read.json(matches_jsonl_path, schema=get_matches_schema()) - # No need for JSON deserialization anymore since we're using PySpark to save - # The data is already in the correct format with proper struct arrays + # Explode resolved_companies from blocks to get individual companies + logger.info("Exploding resolved_companies from blocks...") + matches_df = blocks_df.select(F.explode("resolved_companies").alias("company")).select( + "company.*" + ) # Load the ORIGINAL raw companies (always the same, regardless of iteration) logger.info(f"Loading ORIGINAL raw companies from {raw_companies_path}") - original_raw_companies_df: DataFrame = spark.read.json(raw_companies_path) + if raw_companies_path.endswith(".parquet") or raw_companies_path.endswith(".parquet/"): + original_raw_companies_df: DataFrame = spark.read.parquet(raw_companies_path) + else: + original_raw_companies_df = spark.read.json(raw_companies_path) # For iteration 2+, also load the previous iteration's output for comparison previous_iteration_df: Optional[DataFrame] = None @@ -93,7 +97,7 @@ def evaluate_er_matches( prev_iteration = iteration - 1 # Check if output_path has {iteration} placeholder if "{iteration}" in output_path: - prev_iteration_path = output_path.format(iteration=prev_iteration, format="json") + prev_iteration_path = output_path.format(iteration=prev_iteration) else: # If no iteration placeholder, construct path based on current path # Replace current iteration with previous iteration in the path @@ -102,7 +106,7 @@ def evaluate_er_matches( prev_iteration_path = re.sub( f"iteration_{iteration}", f"iteration_{prev_iteration}", - output_path.format(format="json"), + output_path, ) # Check if the previous iteration output exists @@ -110,22 +114,24 @@ def evaluate_er_matches( logger.info( f"Loading previous iteration ({prev_iteration}) results from {prev_iteration_path}" ) - previous_iteration_df = spark.read.schema(company_schema).json(prev_iteration_path) + company_schema = get_company_spark_schema() + previous_iteration_df = spark.read.schema(company_schema).parquet(prev_iteration_path) else: logger.info( f"Previous iteration ({prev_iteration}) output not found at {prev_iteration_path}, skipping comparison" ) - total_blocks = matches_df.count() - logger.info(f"Loaded {total_blocks:,} blocks from matches") + total_blocks = blocks_df.count() + total_companies = matches_df.count() + logger.info(f"Loaded {total_blocks:,} blocks containing {total_companies:,} resolved companies") # Show sample of matches data if logger.isEnabledFor(logging.DEBUG): logger.info("Sample input data:") matches_df.show(3, truncate=False) - # Input is already exploded and deduplicated from final step - logger.info("Using deduplicated companies from final step...") + # Input is exploded resolved_companies from match step + logger.info("Using resolved companies from match step...") resolved_companies_df = matches_df # Split into BAML-processed vs skipped companies @@ -230,6 +236,12 @@ def evaluate_er_matches( unique_baml_processed = baml_processed_df.select("uuid").distinct().count() logger.info(f"BAML-processed companies (after matching): {unique_baml_processed:,} unique") + # Just plain deduplicate records + more_unique_baml_processed = baml_processed_df.distinct().count() + logger.info( + f"Deduplicated BAML-processed companies to ensure uniqueness: {more_unique_baml_processed:,} unique" + ) + # To calculate reduction, we need to know how many companies WENT INTO matching # This is: total original companies - skipped companies # Skipped companies are those with match_skip=True, which includes singletons @@ -241,7 +253,7 @@ def evaluate_er_matches( reduction_from_matching_pct = ( (reduction_from_matching / companies_that_went_into_matching) * 100 if companies_that_went_into_matching > 0 - else 0 + else 0.0 ) logger.info( f"Data reduction from matching: {reduction_from_matching:,} companies merged ({reduction_from_matching_pct:.2f}%)" @@ -252,7 +264,7 @@ def evaluate_er_matches( ids_dropped_pct = ( (ids_dropped_by_baml / companies_that_went_into_matching) * 100 if companies_that_went_into_matching > 0 - else 0 + else 0.0 ) logger.info( f"IDs dropped by BAML: {ids_dropped_by_baml:,} ({ids_dropped_pct:.2f}%) - recovered via UUID tracking" @@ -262,7 +274,7 @@ def evaluate_er_matches( total_output_companies = unique_baml_processed + skipped_records total_reduction = total_original_companies - total_output_companies total_reduction_pct = ( - (total_reduction / total_original_companies) * 100 if total_original_companies > 0 else 0 + (total_reduction / total_original_companies) * 100 if total_original_companies > 0 else 0.0 ) logger.info( f"Total reduction (original → output): {total_reduction:,} companies ({total_reduction_pct:.2f}%)" @@ -276,7 +288,7 @@ def evaluate_er_matches( overlap_with_original_pct = ( (overlapping_with_original / unique_baml_processed) * 100 if unique_baml_processed > 0 - else 0 + else 0.0 ) logger.info( f"UUID overlap with ORIGINAL (BAML-processed only): {overlapping_with_original:,} ({overlap_with_original_pct:.2f}%)" @@ -291,7 +303,7 @@ def evaluate_er_matches( overlap_with_prev_pct = ( (overlapping_with_prev / unique_baml_processed) * 100 if unique_baml_processed > 0 - else 0 + else 0.0 ) logger.info( f"UUID overlap with PREVIOUS iteration (BAML-processed only): {overlapping_with_prev:,} ({overlap_with_prev_pct:.2f}%) - should be 0%" @@ -319,7 +331,7 @@ def evaluate_er_matches( original_coverage_pct = ( (tracked_original_uuids / unique_original_companies) * 100 if unique_original_companies > 0 - else 0 + else 0.0 ) logger.info( f"Source UUID coverage of ORIGINAL: {tracked_original_uuids:,}/{unique_original_companies:,} ({original_coverage_pct:.2f}%)" @@ -331,7 +343,7 @@ def evaluate_er_matches( if previous_iteration_df is not None: tracked_prev_uuids = unique_source_uuids_df.intersect(prev_uuids).count() prev_coverage_pct = ( - (tracked_prev_uuids / unique_prev_companies) * 100 if unique_prev_companies > 0 else 0 + (tracked_prev_uuids / unique_prev_companies) * 100 if unique_prev_companies > 0 else 0.0 ) logger.info( f"Source UUID coverage of PREVIOUS iteration: {tracked_prev_uuids:,}/{unique_prev_companies:,} ({prev_coverage_pct:.2f}%)" @@ -344,10 +356,10 @@ def evaluate_er_matches( # Load UUIDs from ALL previous iterations (not just immediate previous) # This ensures we can validate source_uuids that chain through multiple iterations for prev_iter in range(1, iteration): - prev_iter_path = output_path.format(iteration=prev_iter, format="json") + prev_iter_path = output_path.format(iteration=prev_iter) if os.path.exists(prev_iter_path): logger.debug(f"Loading UUIDs from iteration {prev_iter} for validation") - prev_iter_df = spark.read.json(prev_iter_path) + prev_iter_df = spark.read.parquet(prev_iter_path) prev_iter_uuids = prev_iter_df.select("uuid").distinct() all_valid_uuids = all_valid_uuids.union(prev_iter_uuids).distinct() else: @@ -379,16 +391,15 @@ def evaluate_er_matches( # that aren't in our current dataset but are still valid cleaned_resolved_companies = resolved_companies_df - # Save companies_resolved file as JSON - # Format the output path with iteration and format - companies_resolved_json = output_path.format(iteration=iteration, format="json") + # Save companies_resolved file as Parquet + companies_resolved_parquet = output_path.format(iteration=iteration) - logger.info(f"Saving companies_resolved.json to {companies_resolved_json}") - cleaned_resolved_companies.coalesce(1).write.mode("overwrite").option( - "ignoreNullFields", "false" - ).json(companies_resolved_json) + logger.info(f"Saving companies_resolved.parquet to {companies_resolved_parquet}") + cleaned_resolved_companies.coalesce(1).write.mode("overwrite").parquet( + companies_resolved_parquet + ) - # Create and save evaluation metrics - both Parquet and single JSON file + # Create and save evaluation metrics as Parquet from pyspark.sql.types import ( DoubleType, IntegerType, @@ -454,13 +465,12 @@ def evaluate_er_matches( metrics_df = spark.createDataFrame(metrics_data, schema=metrics_schema) # Get the directory for metrics files - # Format the output_path first to get the actual directory with iteration number - formatted_output_path = output_path.format(iteration=iteration, format="json") + formatted_output_path = output_path.format(iteration=iteration) output_dir = os.path.dirname(formatted_output_path) - metrics_json_path = os.path.join(output_dir, "er_evaluation_metrics.json") + metrics_parquet_path = os.path.join(output_dir, "er_evaluation_metrics.parquet") - logger.info(f"Saving evaluation metrics (JSON) to {metrics_json_path}") - metrics_df.coalesce(1).write.mode("overwrite").json(metrics_json_path) + logger.info(f"Saving evaluation metrics to {metrics_parquet_path}") + metrics_df.coalesce(1).write.mode("overwrite").parquet(metrics_parquet_path) # Print final summary logger.info("\n" + "=" * 60) @@ -560,8 +570,8 @@ def evaluate_er_matches( logger.info(" └─ Resolved companies reusing original UUIDs (BUG!)") logger.info("") logger.info("OUTPUT FILES:") - logger.info(f" Resolved companies: {companies_resolved_json}") - logger.info(f" Evaluation metrics: {metrics_json_path}") + logger.info(f" Resolved companies: {companies_resolved_parquet}") + logger.info(f" Evaluation metrics: {metrics_parquet_path}") logger.info("=" * 60) # Don't stop the SparkSession - let the caller manage its lifecycle diff --git a/abzu/spark/er_final.py b/abzu/spark/er_final.py index a11a129..91fd00a 100644 --- a/abzu/spark/er_final.py +++ b/abzu/spark/er_final.py @@ -6,6 +6,7 @@ import pyspark.sql.functions as F from pyspark.sql import DataFrame, SparkSession +from abzu.config import config from abzu.er.match import match_entities from abzu.logs import get_logger from abzu.spark.config import get_spark_session @@ -13,14 +14,18 @@ BLOCK_FIELDS, build_udtf_return_type, get_company_fields_without_blocks, + get_matches_schema, normalize_company_dataframe, ) +from abzu.spark.utils import create_split_large_blocks_udtf logger = get_logger(__name__) +MAX_BLOCK_SIZE = config.get("process.kg.er.max_block_size", 50) + def build_uuid_blocks( - input_path: str, + matches_path: str, output_path: str, local_mode: Optional[bool] = None, max_block_size: int = 50, @@ -32,18 +37,26 @@ def build_uuid_blocks( as first_word and acronym blocking. Args: - input_path: Path to matches.json from previous matching step + matches_path: Path to matches.jsonl from previous matching step output_path: Path to save UUID blocks local_mode: Whether to run in local mode max_block_size: Maximum companies per block before splitting """ + # Use provided max_block_size or fall back to config + if max_block_size: + logger.info(f"Using provided max block size of {max_block_size}") + actual_max_block_size = max_block_size + else: + logger.info(f"Using default max block size of {MAX_BLOCK_SIZE}") + actual_max_block_size = MAX_BLOCK_SIZE + spark: SparkSession = get_spark_session( app_name="build_uuid_blocks", local_mode=local_mode, ) - logger.info(f"Loading resolved companies from {input_path}") - matches_df: DataFrame = spark.read.json(input_path) + logger.info(f"Loading resolved companies from {matches_path}") + matches_df: DataFrame = spark.read.json(matches_path, schema=get_matches_schema()) # Explode resolved_companies from blocks logger.info("Exploding resolved companies from blocks...") @@ -78,9 +91,7 @@ def build_uuid_blocks( empty_blocks = spark.createDataFrame( [], schema="block_key string, block_key_type string, companies array>" ) - empty_blocks.coalesce(1).write.mode("overwrite").option("ignoreNullFields", "false").json( - output_path - ) + empty_blocks.coalesce(1).write.mode("overwrite").parquet(output_path) return # Get company fields @@ -96,39 +107,18 @@ def build_uuid_blocks( ) .groupBy("block_key", "block_key_type") .agg(F.collect_list("company").alias("companies")) - .withColumn("company_count", F.size("companies")) + .withColumn("block_size", F.size("companies")) ) logger.info("UUID block size distribution:") - uuid_blocks_temp.groupBy("company_count").count().orderBy("company_count").show(20) + uuid_blocks_temp.groupBy("block_size").count().orderBy("block_size").show(20) - # Split large blocks + # Split large blocks using shared UDTF factory logger.info(f"Splitting blocks larger than {max_block_size}...") # Use centralized UDTF return type from schemas.py for consistency - # This ensures block_size field is always included (not company_count) udtf_return_type = build_udtf_return_type() - - @F.udtf(returnType=udtf_return_type) # type: ignore - class SplitLargeBlocks: - def eval( - self, - block_key: str, - block_key_type: str, - companies: list[dict], # type: ignore - block_size: int, - ): # type: ignore - if block_size <= max_block_size: - yield (block_key, block_key_type, companies, block_size) - else: - chunk_num = 1 - for i in range(0, len(companies), max_block_size): - chunk_companies = companies[i : i + max_block_size] - chunk_key = f"{block_key}_chunk_{chunk_num}" - yield (chunk_key, block_key_type, chunk_companies, len(chunk_companies)) - chunk_num += 1 - - # Register the UDTF for SQL use + SplitLargeBlocks = create_split_large_blocks_udtf(udtf_return_type, actual_max_block_size) spark.udtf.register("split_large_uuid_blocks", SplitLargeBlocks) # type: ignore # Create temp view and apply UDTF using SQL @@ -136,7 +126,7 @@ def eval( uuid_blocks = spark.sql( """ SELECT udtf_output.* FROM uuid_blocks_temp, - LATERAL split_large_uuid_blocks(block_key, block_key_type, companies, company_count) AS udtf_output + LATERAL split_large_uuid_blocks(block_key, block_key_type, companies, block_size) AS udtf_output """ ) @@ -145,157 +135,17 @@ def eval( # Save blocks with canonical BLOCK_FIELDS from schemas.py # The UDTF already returns block_size (not company_count) via build_udtf_return_type() - # Use ignoreNullFields=false to preserve all fields even when null (prevents schema drift) logger.info(f"Saving UUID blocks to {output_path}") - uuid_blocks.select(*BLOCK_FIELDS).coalesce(1).write.mode("overwrite").option( - "ignoreNullFields", "false" - ).json(output_path) + uuid_blocks.select(*BLOCK_FIELDS).coalesce(1).write.mode("overwrite").parquet(output_path) logger.info("UUID blocking complete!") -def build_name_blocks( - input_path: str, - output_path: str, - local_mode: Optional[bool] = None, - max_block_size: int = 50, -) -> None: - """ - Create name-based blocks from resolved companies for final deduplication. - - This treats the complete company name as a blocking key, catching duplicates - that have identical names but different UUIDs. - - Args: - input_path: Path to matches.json from previous matching step - output_path: Path to save name blocks - local_mode: Whether to run in local mode - max_block_size: Maximum companies per block before splitting - """ - spark: SparkSession = get_spark_session( - app_name="build_name_blocks", - local_mode=local_mode, - ) - - logger.info(f"Loading resolved companies from {input_path}") - matches_df: DataFrame = spark.read.json(input_path) - - # Explode resolved_companies from blocks - logger.info("Exploding resolved companies from blocks...") - companies_df_raw = matches_df.select(F.explode("resolved_companies").alias("company")).select( - "company.*" - ) - - # Normalize schema - logger.info("Normalizing company schema...") - companies_df = normalize_company_dataframe(companies_df_raw, preserve_extra_fields=True) - - total_companies = companies_df.count() - logger.info(f"Total resolved companies: {total_companies:,}") - - # Group by name (treating complete name as a blocking key) - # Normalize name: lowercase and trim whitespace - logger.info("Grouping companies by name...") - companies_with_normalized_name = companies_df.withColumn( - "normalized_name", F.lower(F.trim(F.col("name"))) - ) - - name_groups = ( - companies_with_normalized_name.groupBy("normalized_name") - .agg(F.count("*").alias("company_count")) - .filter(F.col("company_count") > F.lit(1)) # type: ignore[call-arg,operator] - ) - - duplicate_names = name_groups.count() - duplicate_companies = name_groups.agg(F.sum("company_count")).collect()[0][0] - - logger.info(f"Found {duplicate_names:,} names with duplicates") - logger.info(f"Total duplicate company records: {duplicate_companies:,}") - - if duplicate_names == 0: - logger.info("No duplicate names found - skipping name blocking") - # Save empty blocks file - empty_blocks = spark.createDataFrame( - [], schema="block_key string, block_key_type string, companies array>" - ) - empty_blocks.coalesce(1).write.mode("overwrite").option("ignoreNullFields", "false").json( - output_path - ) - return - - # Get company fields - company_fields = get_company_fields_without_blocks() - - # Create name blocks (same pattern as UUID blocking) - name_blocks_temp = ( - companies_with_normalized_name.join( - name_groups.select("normalized_name"), "normalized_name", "inner" - ) - .select( - F.col("normalized_name").alias("block_key"), - F.lit("name").alias("block_key_type"), - F.struct(*company_fields).alias("company"), - ) - .groupBy("block_key", "block_key_type") - .agg(F.collect_list("company").alias("companies")) - .withColumn("company_count", F.size("companies")) - ) - - logger.info("Name block size distribution:") - name_blocks_temp.groupBy("company_count").count().orderBy("company_count").show(20) - - # Split large blocks - logger.info(f"Splitting blocks larger than {max_block_size}...") - - # Use centralized UDTF return type from schemas.py for consistency - udtf_return_type = build_udtf_return_type() - - @F.udtf(returnType=udtf_return_type) # type: ignore - class SplitLargeNameBlocks: - def eval( - self, - block_key: str, - block_key_type: str, - companies: list[dict], # type: ignore - block_size: int, - ): # type: ignore - if block_size <= max_block_size: - yield (block_key, block_key_type, companies, block_size) - else: - chunk_num = 1 - for i in range(0, len(companies), max_block_size): - chunk_companies = companies[i : i + max_block_size] - chunk_key = f"{block_key}_chunk_{chunk_num}" - yield (chunk_key, block_key_type, chunk_companies, len(chunk_companies)) - chunk_num += 1 - - # Register the UDTF for SQL use - spark.udtf.register("split_large_name_blocks", SplitLargeNameBlocks) # type: ignore - - # Create temp view and apply UDTF using SQL - name_blocks_temp.createOrReplaceTempView("name_blocks_temp") - name_blocks = spark.sql( - """ - SELECT udtf_output.* FROM name_blocks_temp, - LATERAL split_large_name_blocks(block_key, block_key_type, companies, company_count) AS udtf_output - """ - ) - - final_block_count = name_blocks.count() - logger.info(f"Final name blocks after splitting: {final_block_count:,}") - - # Save blocks with canonical BLOCK_FIELDS from schemas.py - logger.info(f"Saving name blocks to {output_path}") - name_blocks.select(*BLOCK_FIELDS).coalesce(1).write.mode("overwrite").option( - "ignoreNullFields", "false" - ).json(output_path) - - logger.info("Name blocking complete!") - - def deduplicate_resolved_companies( matches_path: str, output_path: str, + uuid_blocks_path: str, + uuid_matches_path: str, iteration: int = 1, local_mode: Optional[bool] = None, batch_size: int = 10, @@ -309,7 +159,7 @@ def deduplicate_resolved_companies( 3. Output final deduplicated companies Args: - matches_path: Path to matches.json from previous matching step + matches_path: Path to matches.jsonl from previous matching step output_path: Path to save final deduplicated companies iteration: Iteration number local_mode: Whether to run in local mode @@ -326,9 +176,11 @@ def deduplicate_resolved_companies( logger.info(f"Starting final deduplication for iteration {iteration}") logger.info(f"Input: {matches_path}") logger.info(f"Output: {output_path}") + logger.info(f"UUID Blocks: {uuid_blocks_path}") + logger.info(f"UUID Matches: {uuid_matches_path}") # Read original matches to get input count - original_matches_df = spark.read.json(matches_path) + original_matches_df = spark.read.json(matches_path, schema=get_matches_schema()) original_companies_df = original_matches_df.select( F.explode("resolved_companies").alias("company") ).select("company.*") @@ -343,9 +195,8 @@ def deduplicate_resolved_companies( # ========== PHASE 1: UUID BLOCKING ========== logger.info("=== Phase 1: UUID Blocking ===") - uuid_blocks_path = output_path.replace("companies_final", "uuid_blocks") build_uuid_blocks( - input_path=matches_path, + matches_path=matches_path, output_path=uuid_blocks_path, local_mode=local_mode, max_block_size=50, @@ -353,7 +204,6 @@ def deduplicate_resolved_companies( # Match companies within UUID blocks logger.info("Matching companies within UUID blocks...") - uuid_matches_path = output_path.replace("companies_final", "uuid_matches") match_entities( blocks_path=uuid_blocks_path, @@ -364,11 +214,10 @@ def deduplicate_resolved_companies( ) # Apply UUID deduplication - # match_entities outputs a single JSON file, so check for that uuid_has_matches = os.path.exists(uuid_matches_path) if uuid_has_matches: - uuid_matches_df = spark.read.json(uuid_matches_path) + uuid_matches_df = spark.read.json(uuid_matches_path, schema=get_matches_schema()) # Get UUIDs that were in UUID blocks (from original_companies, not resolved) # These are the UUIDs we need to exclude from the left_anti join @@ -407,102 +256,22 @@ def deduplicate_resolved_companies( after_uuid_count = after_uuid_df.count() logger.info(f"Companies after UUID dedup: {after_uuid_count:,}") - # ========== PHASE 2: NAME BLOCKING ========== - logger.info("=== Phase 2: Name Blocking ===") - - # Save intermediate results for name blocking - intermediate_path = output_path.replace("companies_final", "after_uuid_dedup") - # Create a structure that build_name_blocks expects (matches.json format) - intermediate_df = ( - after_uuid_df.select(F.struct("*").alias("company")) - .groupBy() - .agg(F.collect_list("company").alias("resolved_companies")) - .withColumn("block_key", F.lit("intermediate")) - .withColumn("block_key_type", F.lit("intermediate")) - ) - intermediate_df.coalesce(1).write.mode("overwrite").option("ignoreNullFields", "false").json( - intermediate_path - ) - - name_blocks_path = output_path.replace("companies_final", "name_blocks") - build_name_blocks( - input_path=intermediate_path, - output_path=name_blocks_path, - local_mode=local_mode, - max_block_size=50, - ) - - # Match companies within name blocks - logger.info("Matching companies within name blocks...") - name_matches_path = output_path.replace("companies_final", "name_matches") - - match_entities( - blocks_path=name_blocks_path, - output_path=name_matches_path, - iteration=iteration, - batch_size=batch_size, - limit=None, - ) - - # Apply name deduplication - # match_entities outputs a single JSON file, so check for that - name_has_matches = os.path.exists(name_matches_path) - - if name_has_matches: - name_matches_df = spark.read.json(name_matches_path) - - # Get UUIDs that were in name blocks (from original_companies, not resolved) - # These are the UUIDs we need to exclude from the left_anti join - name_matched_uuids_df = ( - name_matches_df.select(F.explode("original_companies").alias("company")) - .select("company.uuid") - .distinct() - ) - - # Get name-deduplicated companies - name_deduplicated_df = name_matches_df.select( - F.explode("resolved_companies").alias("company") - ).select("company.*") - - name_deduplicated_df = normalize_company_dataframe( - name_deduplicated_df, preserve_extra_fields=True - ) - - # Get companies that were NOT in name blocks - unique_after_name_df = after_uuid_df.join(name_matched_uuids_df, on="uuid", how="left_anti") - - # Combine after name dedup - # Use unionByName to match columns by name instead of position, preventing schema misalignment - final_companies_df = name_deduplicated_df.unionByName( - unique_after_name_df, allowMissingColumns=True - ) - name_dedup_count = after_uuid_count - final_companies_df.count() - logger.info(f"Name deduplication merged {name_dedup_count:,} companies") - else: - logger.info("No name duplicates found") - final_companies_df = after_uuid_df - name_dedup_count = 0 - - output_count = final_companies_df.count() + output_count = after_uuid_count duplicate_count = input_count - output_count logger.info("=== Final Deduplication Complete ===") logger.info(f" Input: {input_count:,} companies") logger.info(f" UUID duplicates: {uuid_dedup_count:,} companies merged") - logger.info(f" Name duplicates: {name_dedup_count:,} companies merged") logger.info(f" Total merged: {duplicate_count:,} companies") logger.info(f" Output: {output_count:,} companies") # Save final deduplicated companies logger.info(f"Saving final deduplicated companies to {output_path}") - final_companies_df.coalesce(1).write.mode("overwrite").option("ignoreNullFields", "false").json( - output_path - ) + after_uuid_df.coalesce(1).write.mode("overwrite").parquet(output_path) return { "input_count": input_count, "uuid_duplicate_count": uuid_dedup_count, - "name_duplicate_count": name_dedup_count, "duplicate_count": duplicate_count, "output_count": output_count, } diff --git a/abzu/spark/refine_kg.py b/abzu/spark/refine_kg.py index 319cc19..63a5abf 100755 --- a/abzu/spark/refine_kg.py +++ b/abzu/spark/refine_kg.py @@ -131,9 +131,9 @@ def refine_knowledge_graph( F.array_distinct( F.transform( "products", - lambda x: F.when( - (F.upper(x) == x) & (F.length(x) > 1), F.upper(x) - ).otherwise(F.initcap(x)), + lambda x: F.when((F.upper(x) == x) & (F.length(x) > 1), F.upper(x)).otherwise( + F.initcap(x) + ), ) ), ) @@ -142,9 +142,9 @@ def refine_knowledge_graph( F.array_distinct( F.transform( "technologies", - lambda x: F.when( - (F.upper(x) == x) & (F.length(x) > 1), F.upper(x) - ).otherwise(F.initcap(x)), + lambda x: F.when((F.upper(x) == x) & (F.length(x) > 1), F.upper(x)).otherwise( + F.initcap(x) + ), ) ), ) diff --git a/abzu/spark/schemas.py b/abzu/spark/schemas.py index 950d5d8..cbf7578 100644 --- a/abzu/spark/schemas.py +++ b/abzu/spark/schemas.py @@ -6,10 +6,12 @@ from pyspark.sql import DataFrame from pyspark.sql.types import ( ArrayType, + BooleanType, DataType, IntegerType, LongType, MapType, + StringType, StructField, StructType, ) @@ -138,6 +140,10 @@ def normalize_company_dataframe(df: DataFrame, preserve_extra_fields: bool = Fal # Get expected fields from Company model expected_fields = list(Company.model_fields.keys()) + # Get the correct schema types for each field + company_schema = get_company_spark_schema() + field_types = {field.name: field.dataType for field in company_schema.fields} + # Get current DataFrame columns current_columns = df.columns @@ -159,8 +165,12 @@ def normalize_company_dataframe(df: DataFrame, preserve_extra_fields: bool = Fal if field in current_columns: select_list.append(F.col(field)) else: - # Add missing field as null with correct alias - select_list.append(F.lit(None).alias(field)) + # Add missing field as null with correct type from BAML schema + field_type = field_types.get(field) + if field_type: + select_list.append(F.lit(None).cast(field_type).alias(field)) + else: + select_list.append(F.lit(None).alias(field)) # Optionally preserve extra fields at the end if preserve_extra_fields: @@ -244,3 +254,27 @@ def validate_block_schema(columns: list[str]) -> None: f"This is likely a bug in the ER pipeline - blocks must include all fields " f"from the BAML CompanyList type." ) + + +def get_matches_schema() -> StructType: + """Get the Spark schema for reading matches.jsonl files. + + The matches file contains blocks with resolved_companies arrays. + This schema enforces correct BAML types when reading JSON. + + Returns: + StructType schema for matches JSON file + """ + company_schema = get_company_spark_schema() + + return StructType( + [ + StructField("block_key", StringType(), True), + StructField("block_key_type", StringType(), True), + StructField("original_companies", ArrayType(company_schema), True), + StructField("resolved_companies", ArrayType(company_schema), True), + StructField("original_count", LongType(), True), + StructField("resolved_count", LongType(), True), + StructField("was_resolved", BooleanType(), True), + ] + ) diff --git a/abzu/spark/utils.py b/abzu/spark/utils.py index 27d23fa..8f38dab 100644 --- a/abzu/spark/utils.py +++ b/abzu/spark/utils.py @@ -96,7 +96,7 @@ def create_uuid_schema(original_schema: T.StructType) -> T.StructType: # Modify products array to change manufacturer from long to string # and technologies array from array to array product_fields = [] - for prod_field in field.dataType.elementType.fields: + for prod_field in field.dataType.elementType.fields: # type: ignore[attr-defined] if prod_field.name == "manufacturer": # Change manufacturer from long to string (UUID) product_fields.append(T.StructField("manufacturer", T.StringType(), True)) @@ -112,7 +112,7 @@ def create_uuid_schema(original_schema: T.StructType) -> T.StructType: elif field.name == "technologies": # Modify technologies array to change developer from long to string tech_fields = [] - for tech_field in field.dataType.elementType.fields: + for tech_field in field.dataType.elementType.fields: # type: ignore[attr-defined] if tech_field.name == "developer": # Change developer from long to string (UUID) tech_fields.append(T.StructField("developer", T.StringType(), True)) @@ -123,7 +123,7 @@ def create_uuid_schema(original_schema: T.StructType) -> T.StructType: elif field.name == "relationships": # Modify relationships array to change company refs and arrays to strings rel_fields = [] - for rel_field in field.dataType.elementType.fields: + for rel_field in field.dataType.elementType.fields: # type: ignore[attr-defined] if rel_field.name in ["src_company", "dst_company"]: # Change company refs from long to string (UUID) rel_fields.append(T.StructField(rel_field.name, T.StringType(), True)) @@ -209,3 +209,55 @@ def update_entity_with_uuid( entity_dict["uuid"] = str(uuid.uuid4()) return entity_dict + + +def create_split_large_blocks_udtf( + udtf_return_type: str, + max_block_size: int, +) -> type: + """Create a SplitLargeBlocks UDTF class with the given return type and max block size. + + This factory function creates a PySpark UDTF that splits blocks larger than + max_block_size into smaller chunks. The UDTF is used for both regular ER blocking + and final UUID-based deduplication. + + Parameters + ---------- + udtf_return_type : str + The return type string for the UDTF, e.g., + "block_key: string, block_key_type: string, companies: array<...>, block_size: long" + max_block_size : int + Maximum number of companies per block before splitting + + Returns + ------- + type + A decorated UDTF class that can be registered with spark.udtf.register() + + Examples + -------- + >>> udtf_return_type = build_udtf_return_type() + >>> SplitLargeBlocks = create_split_large_blocks_udtf(udtf_return_type, 50) + >>> spark.udtf.register("split_large_blocks", SplitLargeBlocks) + """ + + @F.udtf(returnType=udtf_return_type) # type: ignore + class SplitLargeBlocks: + def eval( + self, + block_key: str, + block_key_type: str, + companies: list[dict[str, Any]], + block_size: int, + ): # type: ignore + if block_size <= max_block_size: + yield (block_key, block_key_type, companies, block_size) + else: + chunk_num = 1 + for i in range(0, len(companies), max_block_size): + chunk_companies = companies[i : i + max_block_size] + chunk_key = f"{block_key}_chunk_{chunk_num}" + yield (chunk_key, block_key_type, chunk_companies, len(chunk_companies)) + chunk_num += 1 + + return SplitLargeBlocks diff --git a/baml_src/add_tickers.baml b/baml_src/add_tickers.baml index be5b786..8e80f62 100644 --- a/baml_src/add_tickers.baml +++ b/baml_src/add_tickers.baml @@ -8,9 +8,9 @@ function AddTickersToCompanies(company_list: CompanyList, ticker_list: TickerLis // The prompt uses Jinja syntax. Change the models or this text and watch the prompt preview change! prompt #" - Examine the following sets of companies and tickers and match the tickers with the companies based on their names representing the same entity. Merge the matched records, adding a Ticker to each Company that matches. If the company name in the ticker differs from the company name, use the full corporate name. + Examine the following sets of companies and tickers and match the tickers with the companies based on their names representing the same entity. Merge the matched records, adding a Ticker to each Company that matches. If the company name in the ticker differs from the company name, use the full corporate name. Include the name of the exchange from the ticker record. - Return a set of companies with tickers merged into the ticker field. + Return a set of companies with tickers merged into the ticker field. Return the original company if no matching ticker is found. {# special macro to print the output instructions. #} {{ ctx.output_format(hoist_classes=["Company", "Ticker", "CompanyList"]) }} @@ -5204,4 +5204,817 @@ test BasicTest { ] } } +} +test RandomCompaniesTickerMatchTest { + functions [AddTickersToCompanies] + args { + company_list { + companies [ + { + id 0 + name "Atgenomix SeqsLab" + ticker null + description "Atgenomix SeqsLab is a Spark-native platform designed to address the challenges of health omics data analysis." + website_url null + headquarters_location null + revenue_usd null + employees null + founded_year null + ceo null + linkedin_url null + source_ids null + source_uuids null + } + { + id 1 + name "NTT" + ticker null + description "NTT is a data center operator." + website_url null + headquarters_location null + revenue_usd null + employees null + founded_year null + ceo null + linkedin_url null + source_ids null + source_uuids null + } + { + id 2 + name "Stack Infrastructure" + ticker null + description "A company that develops and operates data centers." + website_url null + headquarters_location null + revenue_usd null + employees null + founded_year null + ceo null + linkedin_url null + source_ids null + source_uuids null + } + { + id 3 + name "Zoomlion" + ticker null + description "Zoomlion is a company that showcases robotics innovations at World Robot Conference." + website_url null + headquarters_location null + revenue_usd null + employees null + founded_year null + ceo null + linkedin_url null + source_ids null + source_uuids null + } + { + id 4 + name "Terabeam" + ticker null + description "Terabeam is a company." + website_url null + headquarters_location null + revenue_usd null + employees null + founded_year null + ceo null + linkedin_url null + source_ids null + source_uuids null + } + { + id 5 + name "Samsung" + ticker null + description "Samsung is a multinational conglomerate company with a wide range of businesses, including electronics, semiconductors, and telecommunications." + website_url null + headquarters_location null + revenue_usd null + employees null + founded_year null + ceo null + linkedin_url null + source_ids null + source_uuids null + } + { + id 6 + name "Cipher Mining Inc." + ticker null + description "Cipher Mining Inc. is a company focused on cryptocurrency mining." + website_url null + headquarters_location null + revenue_usd null + employees null + founded_year null + ceo null + linkedin_url null + source_ids null + source_uuids null + } + { + id 7 + name "Prometheus Hyperscale" + ticker null + description "Prometheus Hyperscale is a data center firm." + website_url null + headquarters_location null + revenue_usd null + employees null + founded_year null + ceo null + linkedin_url null + source_ids null + source_uuids null + } + { + id 8 + name "Diversified" + ticker null + description "Company that produces Geo Week." + website_url null + headquarters_location null + revenue_usd null + employees null + founded_year null + ceo null + linkedin_url null + source_ids null + source_uuids null + } + { + id 9 + name "Flex" + ticker null + description "Flex is a company that announces new AI infrastructure platform to speed deployment by up to 30 percent." + website_url null + headquarters_location null + revenue_usd null + employees null + founded_year null + ceo null + linkedin_url null + source_ids null + source_uuids null + } + { + id 10 + name "NVIDIA" + ticker null + description "NVIDIA is a technology company that designs graphics processing units (GPUs)." + website_url null + headquarters_location null + revenue_usd null + employees null + founded_year null + ceo null + linkedin_url null + source_ids null + source_uuids null + } + { + id 11 + name "Cardinal Kinetic" + ticker null + description "Cardinal Kinetic is a company that develops motion control solutions." + website_url null + headquarters_location null + revenue_usd null + employees null + founded_year null + ceo null + linkedin_url null + source_ids null + source_uuids null + } + { + id 12 + name "MUFG" + ticker null + description "MUFG is a Japanese financial institution." + website_url null + headquarters_location null + revenue_usd null + employees null + founded_year null + ceo null + linkedin_url null + source_ids null + source_uuids null + } + { + id 13 + name "TSMC" + ticker null + description "Taiwan Semiconductor Manufacturing Company (TSMC) is a semiconductor foundry." + website_url null + headquarters_location null + revenue_usd null + employees null + founded_year null + ceo null + linkedin_url null + source_ids null + source_uuids null + } + { + id 14 + name "Equinix, Inc." + ticker null + description "Equinix, Inc. is a global digital infrastructure company that provides data center and interconnection services." + website_url null + headquarters_location null + revenue_usd null + employees null + founded_year null + ceo null + linkedin_url null + source_ids null + source_uuids null + } + { + id 15 + name "Cisco Systems, Inc." + ticker null + description "Cisco Systems, Inc. is a networking hardware and software company." + website_url null + headquarters_location null + revenue_usd null + employees null + founded_year null + ceo null + linkedin_url null + source_ids null + source_uuids null + } + { + id 16 + name "Murata Manufacturing Co., Ltd." + ticker null + description "Murata Manufacturing is a Japanese manufacturer of electronic components." + website_url null + headquarters_location null + revenue_usd null + employees null + founded_year null + ceo null + linkedin_url null + source_ids null + source_uuids null + } + { + id 17 + name "Sprint" + ticker null + description "Sprint Corporation was a telecommunications company providing wireless services and internet access." + website_url null + headquarters_location null + revenue_usd null + employees null + founded_year null + ceo null + linkedin_url null + source_ids null + source_uuids null + } + { + id 18 + name "CIMIC" + ticker null + description "CIMIC is a construction company." + website_url null + headquarters_location null + revenue_usd null + employees null + founded_year null + ceo null + linkedin_url null + source_ids null + source_uuids null + } + { + id 19 + name "Pelagian" + ticker null + description "Pelagian is a company." + website_url null + headquarters_location null + revenue_usd null + employees null + founded_year null + ceo null + linkedin_url null + source_ids null + source_uuids null + } + ] + } + ticker_list { + tickers [ + { + id 0 + symbol "MSFT" + exchange null + } + { + id 1 + symbol "NVDA" + exchange null + } + { + id 2 + symbol "AAPL" + exchange null + } + { + id 3 + symbol "AMZN" + exchange null + } + { + id 4 + symbol "GOOGL" + exchange null + } + { + id 5 + symbol "META" + exchange null + } + { + id 6 + symbol "TSLA" + exchange null + } + { + id 7 + symbol "AVGO" + exchange null + } + { + id 8 + symbol "BRK-B" + exchange null + } + { + id 9 + symbol "WMT" + exchange null + } + { + id 10 + symbol "JPM" + exchange null + } + { + id 11 + symbol "V" + exchange null + } + { + id 12 + symbol "LLY" + exchange null + } + { + id 13 + symbol "SPY" + exchange null + } + { + id 14 + symbol "MA" + exchange null + } + { + id 15 + symbol "NFLX" + exchange null + } + { + id 16 + symbol "COST" + exchange null + } + { + id 17 + symbol "ORCL" + exchange null + } + { + id 18 + symbol "XOM" + exchange null + } + { + id 19 + symbol "PG" + exchange null + } + { + id 20 + symbol "JNJ" + exchange null + } + { + id 21 + symbol "HD" + exchange null + } + { + id 22 + symbol "SAP" + exchange null + } + { + id 23 + symbol "BAC" + exchange null + } + { + id 24 + symbol "ABBV" + exchange null + } + { + id 25 + symbol "NVO" + exchange null + } + { + id 26 + symbol "KO" + exchange null + } + { + id 27 + symbol "PLTR" + exchange null + } + { + id 28 + symbol "ASML" + exchange null + } + { + id 29 + symbol "PM" + exchange null + } + { + id 30 + symbol "BABA" + exchange null + } + { + id 31 + symbol "TMUS" + exchange null + } + { + id 32 + symbol "UNH" + exchange null + } + { + id 33 + symbol "GE" + exchange null + } + { + id 34 + symbol "CRM" + exchange null + } + { + id 35 + symbol "TM" + exchange null + } + { + id 36 + symbol "CSCO" + exchange null + } + { + id 37 + symbol "WFC" + exchange null + } + { + id 38 + symbol "IBM" + exchange null + } + { + id 39 + symbol "CVX" + exchange null + } + { + id 40 + symbol "ABT" + exchange null + } + { + id 41 + symbol "NVS" + exchange null + } + { + id 42 + symbol "AZN" + exchange null + } + { + id 43 + symbol "MCD" + exchange null + } + { + id 44 + symbol "LIN" + exchange null + } + { + id 45 + symbol "INTU" + exchange null + } + { + id 46 + symbol "NOW" + exchange null + } + { + id 47 + symbol "HSBC" + exchange null + } + { + id 48 + symbol "AXP" + exchange null + } + { + id 49 + symbol "MS" + exchange null + } + { + id 50 + symbol "QQQ" + exchange null + } + { + id 51 + symbol "DIS" + exchange null + } + { + id 52 + symbol "T" + exchange null + } + { + id 53 + symbol "ISRG" + exchange null + } + { + id 54 + symbol "ACN" + exchange null + } + { + id 55 + symbol "SHEL" + exchange null + } + { + id 56 + symbol "MRK" + exchange null + } + { + id 57 + symbol "HDB" + exchange null + } + { + id 58 + symbol "FMX" + exchange null + } + { + id 59 + symbol "VZ" + exchange null + } + { + id 60 + symbol "GS" + exchange null + } + { + id 61 + symbol "RTX" + exchange null + } + { + id 62 + symbol "PEP" + exchange null + } + { + id 63 + symbol "RY" + exchange null + } + { + id 64 + symbol "BKNG" + exchange null + } + { + id 65 + symbol "AMD" + exchange null + } + { + id 66 + symbol "ADBE" + exchange null + } + { + id 67 + symbol "UBER" + exchange null + } + { + id 68 + symbol "BX" + exchange null + } + { + id 69 + symbol "PGR" + exchange null + } + { + id 70 + symbol "CAT" + exchange null + } + { + id 71 + symbol "TXN" + exchange null + } + { + id 72 + symbol "MUFG" + exchange null + } + { + id 73 + symbol "SONY" + exchange null + } + { + id 74 + symbol "SCHW" + exchange null + } + { + id 75 + symbol "QCOM" + exchange null + } + { + id 76 + symbol "SPGI" + exchange null + } + { + id 77 + symbol "UL" + exchange null + } + { + id 78 + symbol "BA" + exchange null + } + { + id 79 + symbol "BSX" + exchange null + } + { + id 80 + symbol "AMGN" + exchange null + } + { + id 81 + symbol "TMO" + exchange null + } + { + id 82 + symbol "BLK" + exchange null + } + { + id 83 + symbol "SYK" + exchange null + } + { + id 84 + symbol "HON" + exchange null + } + { + id 85 + symbol "NEE" + exchange null + } + { + id 86 + symbol "TJX" + exchange null + } + { + id 87 + symbol "C" + exchange null + } + { + id 88 + symbol "DE" + exchange null + } + { + id 89 + symbol "PDD" + exchange null + } + { + id 90 + symbol "GILD" + exchange null + } + { + id 91 + symbol "BUD" + exchange null + } + { + id 92 + symbol "SHOP" + exchange null + } + { + id 93 + symbol "DHR" + exchange null + } + { + id 94 + symbol "SPOT" + exchange null + } + { + id 95 + symbol "PFE" + exchange null + } + { + id 96 + symbol "UNP" + exchange null + } + { + id 97 + symbol "ADP" + exchange null + } + { + id 98 + symbol "ARM" + exchange null + } + { + id 99 + symbol "MELI" + exchange null + } + ] + } + } } \ No newline at end of file diff --git a/baml_src/article.baml b/baml_src/article.baml index 03813fc..0cc83aa 100644 --- a/baml_src/article.baml +++ b/baml_src/article.baml @@ -159,7 +159,7 @@ class Company { website_url string? headquarters_location string? - @description("City, state, country") + @description("City, state, country. Infer the country if only city is given.") jurisdiction string? @description("2-letter state or country where the company is incorporated") @@ -188,7 +188,8 @@ class Company { @description("Leave unaltered. Reason why this company was skipped (error_recovery or missing_in_match_output).") match_skip_history int[]? - @description("Leave unaltered. List of iteration numbers where this company was skipped.")} + @description("Leave unaltered. List of iteration numbers where this company was skipped.") +} class Product { id int @@ -256,6 +257,21 @@ enum RelationshipType { Licensing @description("One company licenses a technology to another company") Subsidiary @description("One company is owned by another company and operates independently") OEM @description("Original Equipment Manufacturer relationship") + + // New relationship types for 10-K + Competitor @description("Companies that compete in the same markets") + Customer @description("Company is a customer of another") + JointVenture @description("Companies formed a joint venture") + Litigation @description("Companies are in legal proceedings") + Regulatory @description("Regulatory relationship (oversight, approval, etc.)") + ResearchCollaboration @description("Companies collaborate on R&D") + DistributionAgreement @description("Distribution or channel partnership") + SupplyAgreement @description("Long-term supply agreement") + LicenseIn @description("Company licenses technology from another") + LicenseOut @description("Company licenses technology to another") + MaterialContract @description("Material contract between companies") + RevenueStream @description("Significant revenue stream from another company") + IntellectualProperty @description("Intellectual property relationship") } class Relationship { @@ -292,6 +308,18 @@ class Relationship { quarter string? @description("Actual or projected financial quarter of the deal in ISO-8601 format. This is the quarter of the year when a relationship took place or will take place.") + + contract_duration_years int? + @description("Contract term if applicable") + + segment_ids int[]? + @description("Business segment IDs where relationship applies") + + geographic_scope string? + @description("Geographic scope of relationship") + + is_material bool? + @description("Whether relationship is material to business") } class NewsSource { diff --git a/baml_src/clients.baml b/baml_src/clients.baml index a0f3358..871cfa6 100644 --- a/baml_src/clients.baml +++ b/baml_src/clients.baml @@ -1,32 +1,32 @@ // Learn more about clients at https://docs.boundaryml.com/docs/snippets/clients/overview -client O3Mini { - provider openai - options { - model "o3-mini" - api_key env.OPENAI_API_KEY - } -} +// client O3Mini { +// provider openai +// options { +// model "o3-mini" +// api_key env.OPENAI_API_KEY +// } +// } -client GPT4o { - provider openai - options { - model "gpt-4o" - api_key env.OPENAI_API_KEY - } -} +// client GPT4o { +// provider openai +// options { +// model "gpt-4o" +// api_key env.OPENAI_API_KEY +// } +// } -client Gemini25Flash { - provider google-ai - retry_policy Exponential - options { - model "gemini-2.5-flash" - api_key env.GEMINI_API_KEY - generationConfig { - temperature 0.0 - } - } -} +// client Gemini25Flash { +// provider google-ai +// retry_policy Exponential +// options { +// model "gemini-2.5-flash" +// api_key env.GEMINI_API_KEY +// generationConfig { +// temperature 0.0 +// } +// } +// } client Gemini20Flash { provider google-ai @@ -34,6 +34,10 @@ client Gemini20Flash { options { model "gemini-2.0-flash" api_key env.GEMINI_API_KEY + http { + time_to_first_token_timeout_ms 60000 + idle_timeout_ms 120000 + } generationConfig { temperature 0.0 } @@ -46,41 +50,45 @@ client Gemini25FlashLite { options { model "gemini-2.5-flash-lite" api_key env.GEMINI_API_KEY - generationConfig { - temperature 0.0 + http { + time_to_first_token_timeout_ms 60000 + idle_timeout_ms 120000 } - } -} - -client Gemini25Pro { - provider google-ai - retry_policy Exponential - options { - model "gemini-2.5-pro" - api_key env.GEMINI_API_KEY generationConfig { temperature 0.0 } } } -client GeminiFallback { - provider fallback - retry_policy Exponential - options { - strategy [Gemini25Pro, Gemini25Flash] - } -} +// client Gemini25Pro { +// provider google-ai +// retry_policy Exponential +// options { +// model "gemini-2.5-pro" +// api_key env.GEMINI_API_KEY +// generationConfig { +// temperature 0.0 +// } +// } +// } -// https://docs.boundaryml.com/docs/snippets/clients/fallback -client OpenAIFallback { - provider fallback - retry_policy Constant - options { - // This will try the clients in order until one succeeds - strategy [O3Mini, GPT4o] - } -} +// client GeminiFallback { +// provider fallback +// retry_policy Exponential +// options { +// strategy [Gemini25Pro, Gemini25Flash] +// } +// } + +// // https://docs.boundaryml.com/docs/snippets/clients/fallback +// client OpenAIFallback { +// provider fallback +// retry_policy Constant +// options { +// // This will try the clients in order until one succeeds +// strategy [O3Mini, GPT4o] +// } +// } // https://docs.boundaryml.com/docs/snippets/clients/retry retry_policy Constant { diff --git a/baml_src/final_er.baml b/baml_src/final_er.baml index 7fb46ee..fbfd9ff 100644 --- a/baml_src/final_er.baml +++ b/baml_src/final_er.baml @@ -62,30 +62,29 @@ test TwoDuplicatesTest { args { duplicate_group { uuid "test-uuid-123" - match_block_keys ["APPLE", "AAPL_ACRONYM"] + match_block_keys ["APPLE"] duplicate_companies [ { - id 1 + id 0 uuid "test-uuid-123" name "Apple Inc." ticker { - id 1 + id 0 uuid null - exchange "NASDAQ" + exchange "UQ" symbol "AAPL" } description "Technology company" founded_year 1976 - source_ids [1, 2] source_uuids ["source-1", "source-2"] match_skip_history null } { - id 2 + id 1 uuid "test-uuid-123" name "Apple" ticker { - id 2 + id 1 uuid null symbol "AAPL" } @@ -93,7 +92,6 @@ test TwoDuplicatesTest { headquarters_location "Cupertino, CA" revenue_usd 394328000000 employees 164000 - source_ids [3] source_uuids ["source-3", "source-4"] match_skip_history [1] } @@ -110,38 +108,36 @@ test ThreeDuplicatesWithNullsTest { match_block_keys ["NVIDIA", "NVDA_ACRONYM", "NVIDIA_COMBINED"] duplicate_companies [ { - id 10 + id 0 uuid "nvidia-uuid-456" name "Nvidia" ticker null description "GPU manufacturer" - source_ids null source_uuids ["doc-1"] match_skip_history null } { - id 11 + id 1 uuid "nvidia-uuid-456" name "NVIDIA Corp" ticker { - id 5 + id 0 uuid null symbol "NVDA" - exchange "NASDAQ" + exchange "UQ" } description null headquarters_location "Santa Clara, CA" founded_year 1993 - source_ids [10] source_uuids ["doc-2", "doc-3"] match_skip_history [1, 2] } { - id 12 + id 2 uuid "nvidia-uuid-456" name "Nvidia Corporation" ticker { - id 6 + id 1 uuid null symbol "NVDA" } @@ -149,7 +145,6 @@ test ThreeDuplicatesWithNullsTest { revenue_usd 60922000000 employees 29600 ceo "Jensen Huang" - source_ids [11, 12] source_uuids ["doc-4"] match_skip_history [2] } diff --git a/baml_src/form10k.baml b/baml_src/form10k.baml new file mode 100644 index 0000000..8d75f0a --- /dev/null +++ b/baml_src/form10k.baml @@ -0,0 +1,739 @@ +// Form 10-K BAML schema for extracting structured data from SEC annual reports +// This schema is designed for investment thesis validation via Text2Cypher queries +// Reuses Company, Ticker, Exchange, Nation, Product, Technology from article.baml + +// Import shared types from article.baml +// Company, Ticker, Exchange, Nation, Product, Technology, RelationshipType, Relationship + +// +// ============================================================================ +// ENUMERATIONS +// ============================================================================ +// + +enum RiskCategory { + Strategic @description("Risks related to business strategy, competitive position, market conditions") + Operational @description("Risks related to operations, supply chain, manufacturing, technology") + Financial @description("Risks related to financial condition, liquidity, capital, debt, currency") + Regulatory @description("Risks related to laws, regulations, compliance, government actions") + Legal @description("Risks related to litigation, intellectual property, contracts") + Cybersecurity @description("Risks related to data breaches, cyber attacks, IT systems") + Environmental @description("Risks related to climate change, environmental regulations, sustainability") + Macroeconomic @description("Risks related to economic conditions, inflation, interest rates, geopolitical events") + Reputational @description("Risks related to brand, public perception, ESG concerns") + TalentAndLabor @description("Risks related to workforce, key personnel, labor relations") +} + +enum RiskSeverity { + Critical @description("Could materially impair business viability") + High @description("Could significantly impact financial performance") + Medium @description("Could moderately affect operations or results") + Low @description("Minimal expected impact") +} + +enum CompetitivePosition { + Leader @description("Dominant market position, leading market share") + Challenger @description("Strong competitor actively pursuing market share") + Follower @description("Established player following market trends") + Niche @description("Specialized focus on specific market segment") + Emerging @description("New entrant with growth potential") +} + +enum SegmentType { + Geographic @description("Region or country-based segment") + ProductLine @description("Product or service category segment") + CustomerType @description("Customer category segment (enterprise, consumer, government)") + Channel @description("Distribution or sales channel segment") +} + +enum GovernanceRating { + Strong @description("Excellent governance practices") + Adequate @description("Meets standard governance requirements") + Weak @description("Governance concerns noted") +} + +enum AuditOpinionType { + Unqualified @description("Clean opinion, no material issues") + Qualified @description("Opinion with specific exceptions") + Adverse @description("Material misstatements exist") + Disclaimer @description("Unable to express opinion") +} + +enum ExecutiveRole { + CEO @description("Chief Executive Officer") + CFO @description("Chief Financial Officer") + COO @description("Chief Operating Officer") + CTO @description("Chief Technology Officer") + CIO @description("Chief Information Officer") + CISO @description("Chief Information Security Officer") + CLO @description("Chief Legal Officer / General Counsel") + CMO @description("Chief Marketing Officer") + CHRO @description("Chief Human Resources Officer") + President @description("President") + Chairman @description("Chairman of the Board") + ViceChairman @description("Vice Chairman") + Director @description("Board Director") + Other @description("Other executive position") +} + +enum BoardCommittee { + Audit @description("Audit Committee") + Compensation @description("Compensation Committee") + Nominating @description("Nominating and Governance Committee") + Risk @description("Risk Committee") + Technology @description("Technology Committee") + ESG @description("Environmental, Social, Governance Committee") + Finance @description("Finance Committee") + Other @description("Other Committee") +} + +enum AcquisitionType { + Merger @description("Full merger of two companies") + AssetPurchase @description("Purchase of specific assets") + StockPurchase @description("Purchase of controlling shares") + JointVenture @description("Formation of joint venture") + StrategicInvestment @description("Minority investment") + Spin @description("Spin-off or divestiture") +} + +enum RevenueRecognitionBasis { + PointInTime @description("Revenue recognized at a point in time") + OverTime @description("Revenue recognized over time as performance obligations are satisfied") + Subscription @description("Recurring subscription-based revenue") + Usage @description("Usage-based or consumption-based revenue") + Milestone @description("Milestone-based revenue recognition") +} + +// +// ============================================================================ +// ENTITY CLASSES +// ============================================================================ +// + +class Executive { + id int + @description("Unique identifier") + + company int + @description("Company id the executive is associated with.") + + name string + @description("Full name of the executive") + + role ExecutiveRole + @description("Primary role or title") + + title string? + @description("Official title if different from role") + + age int? + @description("Age of the executive") + + tenure_years int? + @description("Years in current position") + + biography string? + @description("Brief biography and relevant experience") + + total_compensation float? + @description("Total compensation in USD") + + stock_ownership_shares int? + @description("Number of shares owned") + + stock_ownership_percent float? + @description("Percentage of outstanding shares owned") + + committees BoardCommittee[]? + @description("Board committees served on") + + is_independent bool? + @description("Whether the director is independent") +} + +class RiskFactor { + id int + @description("Unique identifier") + + company int + @description("Company id the risk factor is associated with.") + + category RiskCategory + @description("Category of risk") + + title string + @description("Brief title or heading of the risk factor") + + description string + @description("Detailed description of the risk") + + severity RiskSeverity? + @description("Estimated severity of the risk") + + mitigation string? + @description("Company's mitigation strategy if mentioned") + + related_companies int[]? + @description("IDs of companies related to this risk (competitors, suppliers, etc.)") + + trend string? + @description("Whether risk is increasing, decreasing, or stable") + + is_new bool? + @description("Whether this is a newly disclosed risk factor") +} + +class BusinessSegment { + id int + @description("Unique identifier") + + company int + @description("Company id the business segment is associated with.") + + name string + @description("Name of the business segment") + + type SegmentType + @description("Type of segment classification") + + description string + @description("Description of segment operations") + + revenue float? + @description("Segment revenue in USD") + + revenue_percent float? + @description("Percentage of total revenue") + + operating_income float? + @description("Segment operating income in USD") + + operating_margin float? + @description("Operating margin percentage") + + assets float? + @description("Segment assets in USD") + + growth_rate float? + @description("Year-over-year revenue growth rate") + + key_products int[]? + @description("IDs of key products in this segment") + + geographic_regions string[]? + @description("Key geographic regions for this segment") +} + +class GeographicMarket { + id int + @description("Unique identifier") + + company int + @description("Company id the geographic market is associated with.") + + region string + @description("Region or country name") + + country Nation? + @description("Country details if applicable") + + revenue float? + @description("Revenue from this market in USD") + + revenue_percent float? + @description("Percentage of total revenue") + + growth_rate float? + @description("Year-over-year growth rate") + + employees int? + @description("Number of employees in this region") + + key_facilities int? + @description("Number of key facilities") + + regulatory_environment string? + @description("Description of regulatory environment") + + competitive_position CompetitivePosition? + @description("Company's competitive position in this market") +} + +class Competitor { + id int + @description("Unique identifier") + + company int + @description("Company id the competitor is competing with.") + + competitor int + @description("ID of the competitor company") + + segments int[]? + @description("IDs of business segments where they compete") + + competitive_position CompetitivePosition? + @description("Competitor's market position") + + threat_level string? + @description("Assessment of competitive threat") + + key_differentiators string? + @description("What differentiates the competitor") +} + +class RevenueStream { + id int + @description("Unique identifier") + + company int + @description("Company id the revenue stream is associated with.") + + name string + @description("Name of the revenue stream") + + description string + @description("Description of the revenue source") + + recognition_basis RevenueRecognitionBasis + @description("How revenue is recognized") + + revenue float? + @description("Revenue amount in USD") + + revenue_percent float? + @description("Percentage of total revenue") + + growth_rate float? + @description("Year-over-year growth rate") + + is_recurring bool? + @description("Whether revenue is recurring") + + deferred_revenue float? + @description("Deferred revenue balance in USD") +} + +class FinancialMetric { + id int + @description("Unique identifier") + + company int + @description("Company id the financial metric is associated with.") + + metric_name string + @description("Name of the metric") + + value float + @description("Value of the metric") + + unit string? + @description("Unit of measurement (USD, percent, ratio, etc.)") + + period string + @description("Fiscal period (e.g., FY2024, Q4 2024)") + + year_over_year_change float? + @description("Percentage change from prior year") + + description string? + @description("Context or notes about the metric") +} + +class GuidanceMetric { + id int + @description("Unique identifier") + + company int + @description("Company id the guidance metric is associated with.") + + metric_name string + @description("Name of the guidance metric") + + low_estimate float? + @description("Low end of guidance range") + + high_estimate float? + @description("High end of guidance range") + + point_estimate float? + @description("Point estimate if no range given") + + unit string? + @description("Unit of measurement") + + period string + @description("Period for guidance (e.g., FY2025, Q1 2025)") + + prior_guidance_low float? + @description("Prior low guidance if revised") + + prior_guidance_high float? + @description("Prior high guidance if revised") + + assumptions string? + @description("Key assumptions underlying guidance") +} + +class GovernanceItem { + id int + @description("Unique identifier") + + company int + @description("Company id the governance item is associated with.") + + item_type string + @description("Type: audit_fee, related_party_transaction, shareholder_proposal, etc.") + + description string + @description("Description of the governance item") + + amount float? + @description("Amount if applicable in USD") + + parties_involved int[]? + @description("IDs of related parties") + + outcome string? + @description("Outcome or resolution") +} + +// +// ============================================================================ +// TOP-LEVEL FORM 10-K CLASS +// ============================================================================ +// + +class Form10K { + // Document metadata + fiscal_year int + @description("Fiscal year of the report") + + fiscal_year_end string + @description("Fiscal year end date in ISO-8601 format") + + filing_date string? + @description("SEC filing date in ISO-8601 format") + + cik string? + @description("SEC Central Index Key") + + accession_number string? + @description("SEC accession number") + + // Filer company - the company this 10-K is about + filer Company + @description("The company filing this 10-K") + + // Part I - Business + business_description string + @description("Description of the company's business operations") + + industry string? + @description("Primary industry classification") + + sic_code string? + @description("Standard Industrial Classification code") + + naics_code string? + @description("North American Industry Classification System code") + + employees int? + @description("Total number of employees") + + business_segments BusinessSegment[]? + @description("Operating segments") + + geographic_markets GeographicMarket[]? + @description("Geographic market breakdown") + + products Product[]? + @description("Key products and services") + + technologies Technology[]? + @description("Key technologies") + + revenue_streams RevenueStream[]? + @description("Revenue stream breakdown") + + // Companies - all companies mentioned in the filing + companies Company[] + @description("All companies mentioned in the filing") + + // Competition + competitors Competitor[]? + @description("Key competitors") + + competitive_strengths string[]? + @description("Company's competitive strengths") + + competitive_weaknesses string[]? + @description("Company's competitive weaknesses") + + barriers_to_entry string[]? + @description("Industry barriers to entry") + + // Risk Factors (Part I, Item 1A) + risk_factors RiskFactor[] + @description("Risk factors disclosed in the filing") + + // Properties (Part I, Item 2) + properties_description string? + @description("Description of material properties") + + facilities_count int? + @description("Number of significant facilities") + + owned_square_feet int? + @description("Total owned property in square feet") + + leased_square_feet int? + @description("Total leased property in square feet") + + // Part II - Financial Information + // Key financial metrics + total_revenue float? + @description("Total revenue in USD") + + revenue_growth float? + @description("Revenue growth rate percentage") + + gross_profit float? + @description("Gross profit in USD") + + gross_margin float? + @description("Gross margin percentage") + + operating_income float? + @description("Operating income in USD") + + operating_margin float? + @description("Operating margin percentage") + + net_income float? + @description("Net income in USD") + + net_margin float? + @description("Net margin percentage") + + eps_basic float? + @description("Basic earnings per share") + + eps_diluted float? + @description("Diluted earnings per share") + + total_assets float? + @description("Total assets in USD") + + total_liabilities float? + @description("Total liabilities in USD") + + stockholders_equity float? + @description("Total stockholders' equity in USD") + + cash_and_equivalents float? + @description("Cash and cash equivalents in USD") + + total_debt float? + @description("Total debt in USD") + + free_cash_flow float? + @description("Free cash flow in USD") + + capex float? + @description("Capital expenditures in USD") + + rd_expense float? + @description("Research and development expense in USD") + + rd_percent_of_revenue float? + @description("R&D as percentage of revenue") + + // Additional financial metrics + financial_metrics FinancialMetric[]? + @description("Additional financial metrics") + + // Forward guidance + guidance_metrics GuidanceMetric[]? + @description("Forward-looking guidance") + + // Part III - Directors and Officers + executives Executive[]? + @description("Executive officers") + + board_members Executive[]? + @description("Board of Directors") + + audit_committee_financial_expert string? + @description("Name of audit committee financial expert") + + code_of_ethics_url string? + @description("URL to code of ethics") + + // Corporate governance + governance_items GovernanceItem[]? + @description("Corporate governance matters") + + audit_fees float? + @description("Audit fees paid in USD") + + audit_opinion AuditOpinionType? + @description("Type of audit opinion received") + + material_weakness bool? + @description("Whether material weakness in internal controls exists") + + // Part IV - Exhibits and Schedules + intellectual_property Technology[]? + @description("Key intellectual property") + + // Relationships + customers Company[]? + @description("Key customers") + + suppliers Company[]? + @description("Key suppliers") + + relationships Relationship[]? + @description("All inter-company relationships") + + // Regulatory matters + regulatory_bodies string[]? + @description("Key regulatory bodies overseeing the company") + + pending_regulatory_matters string[]? + @description("Pending regulatory approvals or investigations") + + // ESG and sustainability + environmental_initiatives string[]? + @description("Environmental initiatives mentioned") + + social_initiatives string[]? + @description("Social responsibility initiatives") + + governance_highlights string[]? + @description("Governance highlights") + + // MD&A highlights + mda_summary string? + @description("Summary of Management Discussion and Analysis") + + key_trends string[]? + @description("Key business trends identified") + + key_uncertainties string[]? + @description("Key uncertainties or challenges") + + // Ensure referential integrity for relationships + @@assert(referential_integrity, {{ + this.relationships is none or ( + this.relationships|map(attribute='src_company')| + select('in', this.companies|map(attribute='id'))|list|length == + this.relationships|length and + this.relationships|map(attribute='dst_company')| + select('in', this.companies|map(attribute='id'))|list|length == + this.relationships|length) + }}) +} + +// +// ============================================================================ +// EXTRACTION FUNCTION +// ============================================================================ +// + +function ExtractForm10K(filing_text: string) -> Form10K { + client Gemini20Flash + + prompt #" + You are an expert financial analyst extracting structured data from SEC Form 10-K filings. + Parse the following 10-K filing and extract comprehensive structured information for investment analysis. + + IMPORTANT GUIDELINES: + 1. Extract ALL companies mentioned - competitors, customers, suppliers, partners, subsidiaries + 2. Assign unique integer IDs to all entities (companies, products, technologies, etc.) + 3. Create relationships between companies whenever the filing describes their interactions + 4. Be thorough with risk factors - investors rely on complete risk disclosure + 5. Extract financial metrics with their exact values and periods + 6. Identify forward-looking guidance carefully + 7. For relationships, always specify both source and destination company IDs + 8. Skip fields that would be null unless they are required + + The filer company (the company this 10-K is about) should always have ID = 1. + + {# Output format instructions #} + {{ ctx.output_format(hoist_classes=[ + "Company", "Ticker", "Product", "Technology", "Executive", "RiskFactor", + "BusinessSegment", "GeographicMarket","RevenueStream", + "FinancialMetric", "GuidanceMetric", "GovernanceItem", + "Relationship" + ]) }} + + {{ _.role("user") }} + {{ filing_text }} + "# +} + +// +// ============================================================================ +// TEST CASE +// ============================================================================ +// + +test GoogleForm10KTest { + functions [ExtractForm10K] + args { + filing_text #" +UNITED STATES SECURITIES AND EXCHANGE COMMISSION +Form 10-K +ANNUAL REPORT PURSUANT TO SECTION 13 OR 15(d) OF THE SECURITIES EXCHANGE ACT OF 1934 +For the fiscal year ended December 31, 2024 +Commission File Number: 001-37580 + +ALPHABET INC. +1600 Amphitheatre Parkway +Mountain View, CA 94043 + +PART I + +Item 1. Business + +Alphabet Inc. is a holding company that provides products and platforms in various industries globally. We were incorporated in Delaware on October 2, 2015. Our portfolio of products and services includes Google Search, YouTube, Google Cloud, Android, Google Play, and Waymo. + +Our revenues are primarily generated from advertising on Google Search, YouTube, and the Google Display Network. Google Cloud provides enterprise cloud computing services. Other Bets includes Waymo (autonomous driving), Verily (life sciences), and other emerging businesses. + +Competition: We face significant competition from Microsoft Corporation, Amazon.com, Inc., Apple Inc., Meta Platforms, Inc., and other technology companies. In cloud computing, we compete with Amazon Web Services and Microsoft Azure. In digital advertising, we compete with Meta, Amazon, and Microsoft. + +Item 1A. Risk Factors + +We face intense competition in all of our businesses. If we do not continue to innovate and provide products and services that are useful to users, we may not remain competitive. + +Our revenues depend on advertising. Changes in online advertising markets, including from increased use of AI and ad-blocking technologies, could adversely affect our advertising revenues. + +We are subject to regulatory investigations and legal proceedings. The Department of Justice and various state attorneys general have filed antitrust lawsuits against us. + +Cybersecurity threats could disrupt our services. We process large amounts of user data, making us a target for cyberattacks. + +PART II + +Item 8. Financial Statements + +Total revenues: $350,018 million (2024), $307,394 million (2023) +Operating income: $94,500 million (2024), $84,293 million (2023) +Net income: $80,000 million (2024), $73,795 million (2023) +Total assets: $432,000 million +Total debt: $28,000 million +Cash and equivalents: $95,000 million +R&D expense: $45,000 million +Employees: 182,000 + +PART III + +Item 10. Directors, Executive Officers and Corporate Governance + +Sundar Pichai - Chief Executive Officer, age 52 +Ruth Porat - President and Chief Investment Officer, age 66 +John Hennessy - Chairman of the Board +"# + } +} diff --git a/baml_src/generators.baml b/baml_src/generators.baml index 8ec41e0..8542810 100644 --- a/baml_src/generators.baml +++ b/baml_src/generators.baml @@ -12,7 +12,7 @@ generator target { // The version of the BAML package you have installed (e.g. same version as your baml-py or @boundaryml/baml). // The BAML VSCode extension version should also match this version. - version "0.214.0" + version "0.215.0" // Valid values: "sync", "async" // This controls what `b.FunctionName()` will be (sync or async). default_client_mode async diff --git a/config.yml b/config.yml index 9d704fc..b16a639 100644 --- a/config.yml +++ b/config.yml @@ -129,7 +129,7 @@ process: - "${process.rss.output_dir}/processed_new-atlas.jsonl" - "${process.rss.output_dir}/processed_nvidia-blog.jsonl" - "${process.rss.output_dir}/processed_nvidia-corp.jsonl" - - "${process.rss.output_dir}/processed_planet-analog.jsonl" + # - "${process.rss.output_dir}/processed_planet-analog.jsonl" - "${process.rss.output_dir}/processed_robohub.jsonl" - "${process.rss.output_dir}/processed_robotics-tomorrow-news.jsonl" - "${process.rss.output_dir}/processed_robotics-tomorrow-robotics.jsonl" @@ -142,19 +142,32 @@ process: - "${process.rss.output_dir}/processed_wsj-tech.jsonl" # - "${chat.start.processed_articles}" output: "${base_dir}/knowledge_graph" - + + tickers: + url: "https://www.sec.gov/files/company_tickers.json" + cache: "${base_dir}/sec/company_tickers.json" + input: "${process.kg.raw.output}/companies.parquet" + output: "${process.kg.raw.output}/companies_with_tickers.jsonl" + company_batch_size: 20 + + wiki: + input: "${process.kg.tickers.output}" + output: "${process.kg.raw.output}/companies_with_wiki.jsonl" + concurrent: 5 + er: paths: - input: "${process.kg.raw.output}/companies.jsonl" + input: "${process.kg.raw.output}/companies.parquet" output: "${base_dir}/er/" - # Names workflow paths (supports iterations and formats) + # Names workflow paths (iteration subdirectories) names: - # Use iteration subdirectories for multi-round processing blocks_dir: "${base_dir}/er/iterations/{iteration}/" - blocks: "${base_dir}/er/iterations/{iteration}/all_blocks.{format}" - matches: "${base_dir}/er/iterations/{iteration}/matches.{format}" - final: "${base_dir}/er/iterations/{iteration}/companies_final.{format}" - eval: "${base_dir}/er/iterations/{iteration}/companies_resolved.{format}" + blocks: "${base_dir}/er/iterations/{iteration}/union_blocks.parquet" + uuid_blocks: "${base_dir}/er/iterations/{iteration}/uuid_blocks.parquet" + uuid_matches: "${base_dir}/er/iterations/{iteration}/uuid_matches.jsonl" + matches: "${base_dir}/er/iterations/{iteration}/matches.jsonl" + final: "${base_dir}/er/iterations/{iteration}/companies_final.parquet" + eval: "${base_dir}/er/iterations/{iteration}/companies_resolved.parquet" max_block_size: 50 iteration: 3 diff --git a/markdown/BLOCKING.md b/markdown/BLOCKING.md new file mode 100644 index 0000000..627b5da --- /dev/null +++ b/markdown/BLOCKING.md @@ -0,0 +1,47 @@ +# Semantic Entity Resolution Blocking + +When performing semantic entity resolution (SER) we use semantic clustering to reduce the number of comparisons needed to find matching entities in large datasets. This technique, known as blocking, groups similar entities together based on their semantic content, allowing for more efficient and accurate matching. + +## Blocking Strategies + +### Baseline: Name-Based Blocking + +Before implementing semantic blocking, we use simple name-based blocking strategies as a baseline. These are computationally cheap and effective for catching obvious duplicates: + +**First Word Blocking**: Extracts the first word of the normalized company name as the block key. + +- "Apple Inc" → `APPLE` +- "Apple Corporation" → `APPLE` +- "Microsoft Corporation" → `MICROSOFT` + +**Acronym Blocking**: Extracts uppercase letters from the company name as the block key. + +- "International Business Machines" → `IBM` +- "Apple Inc" → `AI` +- "NVIDIA Corporation" → `NC` + +**Combined Blocking**: Companies that appear in both first_word and acronym blocks are grouped together with higher confidence, as both strategies agree they should be compared. + +The union of all three block types (`union_blocks.parquet`) is used for matching. This approach ensures: + +- High recall by capturing matches through multiple strategies +- Prioritization of combined blocks (where both strategies agree) +- Manageable block sizes through chunking large blocks + +### Semantic Blocking + +Semantic clustering can use raw embeddings or fine-tuned embeddings (see https://github.com/Graphlet-AI/eridu) to create blocks of similar entities. The choice of embedding model can significantly impact the quality of the clusters and, consequently, the effectiveness of the blocking strategy. + +### What to block + +Modern embeddings are capable of blocking entire JSON records or individual fields within those records. The decision on what to block should be based on the specific use case and the nature of the data. For instance, blocking on company names and addresses may be effective for business entity resolution, while blocking on product descriptions may be more suitable for product matching tasks. + +### Block Size + +This is something that can be controlld through hyperparameters in the clustering algorithm. We need to keep these small to manage compute costs, but large enough to capture potential matches. + +## Multiple Blocking Strategies + +Implementing multiple blocking strategies (first word of name and acroym) as in 'abzu process er block names' creates a challenge introduced by using two name based blocking strategies (first word and acronym). Each strategy generates its own golden record in a block and we must then block on `uuid` or `name` to reduce records down to a single golden record. + +This is the purpose of the `abzu process er final` step, which uses `uuid` and `name` blocking to group records from the union of name based blocks into final blocks for matching. diff --git a/markdown/ER.md b/markdown/ER.md index a7f797a..50d2ba2b 100644 --- a/markdown/ER.md +++ b/markdown/ER.md @@ -1,273 +1,198 @@ -# September 10, 2025 - Abzu Capital - Entity Resolution Run through Two Iterations +# Entity Resolution (ER) Pipeline Specification -This file contains the results of two iterations of the entity resolution process for the Abzu Capital finance / technology knowledge graph. It shows each of the commands run and their output report at the end, as well as any relevant sampled data indicative of a problem or bug. Please read the debug output and reports that result from each command, compare the code for that command and the expected output with the actual output to determine any additional problems. Then figure out a plan for their resolution. +This document describes the entity resolution pipeline for resolving duplicate company entities in the Abzu Capital knowledge graph. -At the end there are notes and instructions on how to solve the problem. Take your time, read this file, inspect the relevant code, think hard, come up with a plan and add it to the end of this document under a heading `# Claude Code Plan`. +## Overview -You can use the 'pyspark-mcp' server to run Spark to analyze the Company records that appear during the first `abzu process er block names --iteration 1` command and the second iteration command `abzu process er block names --iteration 2` to determine what about the Company schema changed to guide your investigation and diagnosis. +The ER pipeline operates in iterations, progressively merging duplicate company records. Each iteration consists of three steps: -## Command: abzu process er clean +1. **Block** - Group potentially similar companies together using blocking keys +2. **Match** - Use an LLM to determine which companies in each block are duplicates +3. **Eval** - Evaluate the results and prepare input for the next iteration -Removed all data in data/er/iterations/1 and 2 +## Data Storage -## Command: abzu process kg raw +**Format**: All ER data is stored exclusively in Parquet format for efficient processing with Spark. -``` -2025-09-10 09:08:53,902 - abzu.spark.build_graph - INFO - Loaded 2,354 processed articles -25/09/10 09:08:54 WARN SparkStringUtils: Truncated the string representation of a plan since it was too large. This behavior can be adjusted by setting 'spark.sql.debug.maxToStringFields'. -2025-09-10 09:08:59,296 - abzu.spark.build_graph - INFO - 2,354 good articles. 0 bad articles. -2025-09-10 09:08:59,297 - abzu.spark.build_graph - INFO - Saved bad articles to data/knowledge_graph/bad_articles.jsonl, data/knowledge_graph/bad_articles.parquet, and data/knowledge_graph/bad_articles.csv -2025-09-10 09:08:59,297 - abzu.spark.build_graph - INFO - Replacing integer IDs with UUIDs for all entities... -2025-09-10 09:08:59,829 - abzu.spark.build_graph - INFO - Transformed 2,354 articles with UUID replacements -2025-09-10 09:08:59,829 - abzu.spark.build_graph - INFO - Extracting entities from documents ... -2025-09-10 09:08:59,829 - abzu.spark.build_graph - INFO - Extracting companies ... -2025-09-10 09:09:00,174 - abzu.spark.build_graph - INFO - After filtering nulls: 15,486 companies remain -2025-09-10 09:09:00,919 - abzu.spark.build_graph - INFO - Saved 15,486 companies to data/knowledge_graph/companies.parquet -2025-09-10 09:09:01,296 - abzu.spark.build_graph - INFO - Saved companies to data/knowledge_graph/companies.jsonl -2025-09-10 09:09:01,296 - abzu.spark.build_graph - INFO - Extracting products ... -2025-09-10 09:09:01,742 - abzu.spark.build_graph - INFO - Saved 6,242 products to data/knowledge_graph/products.parquet -2025-09-10 09:09:02,090 - abzu.spark.build_graph - INFO - Saved products to data/knowledge_graph/products.jsonl -2025-09-10 09:09:02,090 - abzu.spark.build_graph - INFO - Extracting technologies ... -2025-09-10 09:09:02,501 - abzu.spark.build_graph - INFO - Saved 10,978 technologies to data/knowledge_graph/technologies.parquet -2025-09-10 09:09:02,800 - abzu.spark.build_graph - INFO - Saved technologies to data/knowledge_graph/technologies.jsonl -2025-09-10 09:09:02,800 - abzu.spark.build_graph - INFO - Extracting tickers ... -2025-09-10 09:09:03,081 - abzu.spark.build_graph - INFO - Saved 1,702 tickers to data/knowledge_graph/tickers.parquet -2025-09-10 09:09:03,289 - abzu.spark.build_graph - INFO - Saved tickers to data/knowledge_graph/tickers.jsonl -2025-09-10 09:09:03,763 - abzu.spark.build_graph - INFO - Saved 4,962 relationships to data/knowledge_graph/relationships.parquet -2025-09-10 09:09:04,065 - abzu.spark.build_graph - INFO - Saved relationships to data/knowledge_graph/relationships.jsonl -2025-09-10 09:09:05,177 - abzu.spark.build_graph - INFO - Found 100.0% 4,962 valid relationships out of 4,962 -``` +**Location**: `data/er/iterations/{iteration}/` + +## Pipeline Steps + +### Step 1: Blocking (`abzu process er block names`) + +Creates blocks of potentially matching companies using multiple blocking strategies: + +- **First Word** - First word of normalized company name (e.g., "Apple" from "Apple Inc") +- **Acronym** - Uppercase letters from the name (e.g., "AI" from "Apple Inc") +- **Combined** - Companies that appear in both first_word and acronym blocks -## Command: abzu process er block names —iteration 1 # MAX_BLOCK_SIZE = 50 +**Output Files**: + +- `combined_blocks.parquet` - Companies appearing in both blocking strategies (highest confidence) +- `first_word_blocks.parquet` - Companies blocked by first word only +- `acronym_blocks.parquet` - Companies blocked by acronym only +- `union_blocks.parquet` - Union of all three block types (used for matching) + +**Block Schema**: ``` -2025-09-10 09:29:20,295 - abzu.spark.er_block - INFO - Unified all_blocks: 5553 blocks, 21251 companies -2025-09-10 09:29:22,558 - abzu.spark.er_block - INFO - ============================================================ -2025-09-10 09:29:22,558 - abzu.spark.er_block - INFO - ENTITY RESOLUTION BLOCKS CREATED -2025-09-10 09:29:22,558 - abzu.spark.er_block - INFO - ============================================================ -2025-09-10 09:29:22,558 - abzu.spark.er_block - INFO - Combined Blocks (overlapping keys): 2,601 blocks with 12,564 companies -2025-09-10 09:29:22,558 - abzu.spark.er_block - INFO - (Split from 2,522 original blocks) -2025-09-10 09:29:22,558 - abzu.spark.er_block - INFO - Saved to: data/er/iterations/1/combined_blocks.json and data/er/iterations/1/combined_blocks.parquet -2025-09-10 09:29:22,558 - abzu.spark.er_block - INFO - First Word Only Blocks: 1,546 blocks with 3,898 companies -2025-09-10 09:29:22,559 - abzu.spark.er_block - INFO - (Split from 1,536 original blocks) -2025-09-10 09:29:22,559 - abzu.spark.er_block - INFO - Saved to: data/er/iterations/1/first_word_blocks.json and data/er/iterations/1/first_word_blocks.parquet -2025-09-10 09:29:22,559 - abzu.spark.er_block - INFO - Acronym Only Blocks: 1,406 blocks with 4,789 companies -2025-09-10 09:29:22,559 - abzu.spark.er_block - INFO - (Split from 1,396 original blocks) -2025-09-10 09:29:22,559 - abzu.spark.er_block - INFO - Saved to: data/er/iterations/1/acronym_blocks.json and data/er/iterations/1/acronym_blocks.parquet -2025-09-10 09:29:22,559 - abzu.spark.er_block - INFO - Total Blocks: 5,553 blocks (all blocks ≤ 50 companies) -2025-09-10 09:29:22,559 - abzu.spark.er_block - INFO - ============================================================ +block_key: string +block_key_type: string ("combined", "first_word", "acronym") +companies: array +block_size: long ``` -## Command: abzu process er match names -b 30 --iteration 1 +**Options**: -``` -2025-09-10 09:36:26,135 - abzu.er.match - INFO - Saved 2200 resolved blocks to data/er/iterations/1/matches.parquet -2025-09-10 09:36:26,227 - abzu.er.match - INFO - -Summary: -2025-09-10 09:36:26,227 - abzu.er.match - INFO - Total blocks processed: 2200 -2025-09-10 09:36:26,227 - abzu.er.match - INFO - Successfully resolved: 2111 -2025-09-10 09:36:26,227 - abzu.er.match - INFO - Errors: 89 -2025-09-10 09:36:26,227 - abzu.er.match - INFO - Note: Resolved companies have new UUIDs; single-company blocks retain original UUIDs -2025-09-10 09:36:26,227 - abzu.er.match - INFO - Total companies before: 15678.0 -2025-09-10 09:36:26,227 - abzu.er.match - INFO - Total companies after: 4557.0 -2025-09-10 09:36:26,227 - abzu.er.match - INFO - Reduction: 11121.0 companies merged +- `--iteration N` - Iteration number (required) +- `-m, --max-block-size N` - Maximum companies per block (default: 50) + +**Example**: + +```bash +abzu process er block names --iteration 1 -m 50 ``` -## Command: abzu process er eval names --iteration 1 +### Step 2: Matching (`abzu process er match names`) + +Uses an LLM to resolve each block, determining which companies are duplicates and merging them. + +**Input**: `union_blocks.parquet` from blocking step + +**Output**: `matches.parquet` + +**Match Schema**: ``` -2025-09-10 09:45:21,098 - abzu.spark.er_eval - INFO - ENTITY RESOLUTION EVALUATION SUMMARY -2025-09-10 09:45:21,098 - abzu.spark.er_eval - INFO - ============================================================ -2025-09-10 09:45:21,098 - abzu.spark.er_eval - INFO - Raw companies: 15,486 total, 15,416 unique -2025-09-10 09:45:21,098 - abzu.spark.er_eval - INFO - Resolved companies: 6,915 total, 6,573 unique -2025-09-10 09:45:21,098 - abzu.spark.er_eval - INFO - Data reduction: 8,913 companies (57.56%) -2025-09-10 09:45:21,098 - abzu.spark.er_eval - INFO - New UUID verification: 2,016 UUIDs (13.08%) reuse raw UUIDs -2025-09-10 09:45:21,098 - abzu.spark.er_eval - INFO - Source UUID coverage: 11,981/15,416 (77.72%) -2025-09-10 09:45:21,098 - abzu.spark.er_eval - INFO - Source UUID validation: 15,524/15,541 valid (0.11% erroneous) -2025-09-10 09:45:21,098 - abzu.spark.er_eval - INFO - Files saved: -2025-09-10 09:45:21,098 - abzu.spark.er_eval - INFO - - data/er/iterations/1/companies_resolved.parquet -2025-09-10 09:45:21,098 - abzu.spark.er_eval - INFO - - data/er/iterations/1/companies_resolved.json -2025-09-10 09:45:21,098 - abzu.spark.er_eval - INFO - - data/er/iterations/{iteration}/er_evaluation_metrics.parquet -2025-09-10 09:45:21,098 - abzu.spark.er_eval - INFO - - data/er/iterations/{iteration}/er_evaluation_metrics.json -2025-09-10 09:45:21,098 - abzu.spark.er_eval - INFO - ============================================================ +block_key: string +block_key_type: string +resolved_companies: array +was_resolved: boolean +original_count: int +resolved_count: int ``` -## Command: abzu process er block names —iteration 2 +**Options**: +- `--iteration N` - Iteration number (required) +- `-b, --batch-size N` - Blocks to process in parallel (default: 50) +- `-n, --num-rows N` - Limit number of blocks to process (for testing) + +**Example**: + +```bash +abzu process er match names --iteration 1 -b 50 ``` -2025-09-10 10:30:23,951 - abzu.spark.er_block - INFO - Unified all_blocks: 3380 blocks, 11301 companies -2025-09-10 10:30:26,159 - abzu.spark.er_block - INFO - ============================================================ -2025-09-10 10:30:26,159 - abzu.spark.er_block - INFO - ENTITY RESOLUTION BLOCKS CREATED -2025-09-10 10:30:26,159 - abzu.spark.er_block - INFO - ============================================================ -2025-09-10 10:30:26,159 - abzu.spark.er_block - INFO - Combined Blocks (overlapping keys): 903 blocks with 4,334 companies -2025-09-10 10:30:26,159 - abzu.spark.er_block - INFO - (Split from 870 original blocks) -2025-09-10 10:30:26,159 - abzu.spark.er_block - INFO - Saved to: data/er/iterations/2/combined_blocks.json and data/er/iterations/2/combined_blocks.parquet -2025-09-10 10:30:26,159 - abzu.spark.er_block - INFO - First Word Only Blocks: 1,422 blocks with 3,279 companies -2025-09-10 10:30:26,159 - abzu.spark.er_block - INFO - (Split from 1,417 original blocks) -2025-09-10 10:30:26,159 - abzu.spark.er_block - INFO - Saved to: data/er/iterations/2/first_word_blocks.json and data/er/iterations/2/first_word_blocks.parquet -2025-09-10 10:30:26,159 - abzu.spark.er_block - INFO - Acronym Only Blocks: 1,055 blocks with 3,688 companies -2025-09-10 10:30:26,159 - abzu.spark.er_block - INFO - (Split from 1,052 original blocks) -2025-09-10 10:30:26,159 - abzu.spark.er_block - INFO - Saved to: data/er/iterations/2/acronym_blocks.json and data/er/iterations/2/acronym_blocks.parquet -2025-09-10 10:30:26,159 - abzu.spark.er_block - INFO - Total Blocks: 3,380 blocks (all blocks ≤ 50 companies) -2025-09-10 10:30:26,159 - abzu.spark.er_block - INFO - ============================================================ -``` -The records for the second iteration of blocking still don’t match up to the expected schema, as indicated by "AAPL" in the "url" field and a name of "combined". We have seen this behavior before. - -```json -{ - "block_key": "AAPL", - "block_key_type": "combined", - "companies": [ - { - "uuid": "f5374f7b-1d66-42f2-a1b6-4d9e0b3ea284", - "block_key": "AAPL", - "block_key_type": "combined", - "url": "AAPL", - "name": "combined", - "cik": "AAPL is likely referring to Apple Inc., a technology company that designs, develops, and sells consumer electronics, computer software, and online services. Apple Inc. is an American multinational technology company.", - "id": 7, - "posted_at": "AAPL", - "ticker": { - "symbol": "AAPL" - } - } - ], - "block_size": 1 -} +### Step 3: Evaluation (`abzu process er eval names`) + +Evaluates match results, computes metrics, and prepares resolved companies for the next iteration. + +**Input**: + +- `matches.parquet` from matching step +- Original raw companies from `data/knowledge_graph/companies.parquet` + +**Output**: + +- `companies_resolved.parquet` - Resolved companies for next iteration +- `er_evaluation_metrics.parquet` - Evaluation metrics + +**Metrics Computed**: + +- Raw company count +- Resolved company count +- Data reduction percentage +- Source UUID coverage (tracking original company provenance) +- Source UUID validation (error rate) + +**Example**: + +```bash +abzu process er eval names --iteration 1 ``` -These issues that arise during the second iteration blocking are due to a problem with the schema of the pyspark.sql.DataFrame we feed our UDTF on the second iteration, which is different than the first. See @UDTF.md and @abzu/spark/er_block.py lines 373 - 437: - -```python - @F.udtf( # type: ignore - returnType=( - "block_key: string, block_key_type: string, " - "companies: array,source_uuids:array," - "ticker:struct," - "website_url:string>>, " - "block_size: long" - ) - ) - class SplitLargeBlocks: - def eval(self, block_key: str, block_key_type: str, companies: list, block_size: int): - if block_size <= MAX_BLOCK_SIZE: - yield (block_key, block_key_type, companies, block_size) - else: - chunk_num = 1 - for i in range(0, len(companies), MAX_BLOCK_SIZE): - chunk_companies = companies[i : i + MAX_BLOCK_SIZE] - chunk_key = f"{block_key}_chunk_{chunk_num}" - yield (chunk_key, block_key_type, chunk_companies, len(chunk_companies)) - chunk_num += 1 - - # 2) Register the UDTF for SQL use - spark.udtf.register("split_large_blocks", SplitLargeBlocks) # type: ignore - - # 3) Create temp views for the DataFrames - combined_blocks.createOrReplaceTempView("combined_blocks_temp") - first_word_only_blocks.createOrReplaceTempView("first_word_blocks_temp") - acronym_only_blocks.createOrReplaceTempView("acronym_blocks_temp") - - # 4) Apply the UDTF using SQL with LATERAL syntax - only select UDTF output columns - combined_blocks_final = ( - spark.sql( - """ - SELECT udtf_output.* FROM combined_blocks_temp, - LATERAL split_large_blocks(block_key, block_key_type, companies, block_size) AS udtf_output - """ - ) - .orderBy(F.col("block_size")) - .cache() - ) - - first_word_blocks_final = ( - spark.sql( - """ - SELECT udtf_output.* FROM first_word_blocks_temp, - LATERAL split_large_blocks(block_key, block_key_type, companies, block_size) AS udtf_output - """ - ) - .orderBy(F.col("block_size")) - .cache() - ) - - acronym_blocks_final = ( - spark.sql( - """ - SELECT udtf_output.* FROM acronym_blocks_temp, - LATERAL split_large_blocks(block_key, block_key_type, companies, block_size) AS udtf_output - """ - ) - .orderBy(F.col("block_size")) - .cache() - ) +## Source UUID Tracking + +Throughout the ER pipeline, `source_uuids` tracks the provenance of merged companies: + +- First iteration: Each company's UUID is added to its own `source_uuids` +- Subsequent iterations: When companies merge, all their `source_uuids` are combined +- This allows tracing any resolved company back to its original source records + +## Multi-Iteration Workflow + +Run multiple iterations until convergence: + +```bash +# Iteration 1 +abzu process er block names --iteration 1 -m 50 +abzu process er match names --iteration 1 -b 50 +abzu process er eval names --iteration 1 + +# Iteration 2 (uses output from iteration 1) +abzu process er block names --iteration 2 -m 50 +abzu process er match names --iteration 2 -b 50 +abzu process er eval names --iteration 2 + +# Continue until minimal reduction is achieved... ``` -During the first iteration the schema is what this UDTF expects. During the second it is not. Our task is to fix the schema so that it is identical between the first and second runds. +## Cleanup + +To clean up ER output files for a fresh run: -## Command: abzu process er match names -b 20 --iteration 2 +```bash +# Clean all iterations +abzu process er clean +# Clean specific iteration +abzu process er clean --iteration 1 + +# Dry run (show what would be deleted) +abzu process er clean --dry-run ``` -2025-09-10 10:47:20,520 - abzu.er.match - INFO - Saved 1549 resolved blocks to data/er/iterations/2/matches.parquet -2025-09-10 10:47:20,568 - abzu.er.match - INFO - -Summary: -2025-09-10 10:47:20,568 - abzu.er.match - INFO - Total blocks processed: 1549 -2025-09-10 10:47:20,568 - abzu.er.match - INFO - Successfully resolved: 50 -2025-09-10 10:47:20,568 - abzu.er.match - INFO - Errors: 1499 -2025-09-10 10:47:20,568 - abzu.er.match - INFO - Note: Resolved companies have new UUIDs; single-company blocks retain original UUIDs -2025-09-10 10:47:20,568 - abzu.er.match - INFO - Total companies before: 102.0 -2025-09-10 10:47:20,568 - abzu.er.match - INFO - Total companies after: 54.0 -2025-09-10 10:47:20,568 - abzu.er.match - INFO - Reduction: 48.0 companies merged + +## Configuration + +ER paths are configured in `config.yml`: + +```yaml +process: + kg: + er: + paths: + input: "${process.kg.raw.output}/companies.parquet" + output: "${base_dir}/er/" + names: + blocks_dir: "${base_dir}/er/iterations/{iteration}/" + blocks: "${base_dir}/er/iterations/{iteration}/union_blocks.{format}" + matches: "${base_dir}/er/iterations/{iteration}/matches.{format}" + final: "${base_dir}/er/iterations/{iteration}/companies_final.{format}" + eval: "${base_dir}/er/iterations/{iteration}/companies_resolved.{format}" + max_block_size: 50 + iteration: 3 ``` -The records for the second iteration of matching still don’t match up to the expected - posted_at has name of ‘Google’ in it… - -```json -{ - "uuid": "c5dc3d06-4293-46cb-a7c7-a4817d57845e", - "block_key": "GOOGLE", - "block_key_type": "combined", - "url": "GOOGLE_chunk_6", - "name": "combined", - "description": null, - "ceo": null, - "cik": "A technology company known for its search engine, cloud computing, and artificial intelligence research.", - "employees": null, - "founded_year": null, - "headquarters_location": null, - "id": 103, - "jurisdiction": null, - "linkedin_url": null, - "posted_at": "Google", - "revenue_usd": null, - "source_ids": null, - "source_uuids": null, - "ticker": null, - "website_url": "" -} +## Testing + +Test one iteration of the full pipeline: + +```bash +# Block with small max size for testing +abzu process er block names --iteration 1 -m 30 + +# Match with limited rows for faster testing +abzu process er match names --iteration 1 -b 50 -n 100 + +# Evaluate +abzu process er eval names --iteration 1 ``` -## Command: abzu process er eval names --iteration 2 - -2025-09-10 11:19:30,226 - abzu.spark.er_eval - INFO - ENTITY RESOLUTION EVALUATION SUMMARY -2025-09-10 11:19:30,226 - abzu.spark.er_eval - INFO - ============================================================ -2025-09-10 11:19:30,226 - abzu.spark.er_eval - INFO - Raw companies: 15,486 total, 15,416 unique -2025-09-10 11:19:30,226 - abzu.spark.er_eval - INFO - Resolved companies: 9,901 total, 6,021 unique -2025-09-10 11:19:30,226 - abzu.spark.er_eval - INFO - Data reduction: 9,465 companies (61.12%) -2025-09-10 11:19:30,226 - abzu.spark.er_eval - INFO - New UUID verification: 2,016 UUIDs (13.08%) reuse raw UUIDs -2025-09-10 11:19:30,226 - abzu.spark.er_eval - INFO - Source UUID coverage: 234/15,416 (1.52%) -2025-09-10 11:19:30,226 - abzu.spark.er_eval - INFO - Source UUID validation: 0/1,980 valid (100.00% erroneous) -2025-09-10 11:19:30,226 - abzu.spark.er_eval - INFO - Files saved: -2025-09-10 11:19:30,226 - abzu.spark.er_eval - INFO - - data/er/iterations/2/companies_resolved.parquet -2025-09-10 11:19:30,226 - abzu.spark.er_eval - INFO - - data/er/iterations/2/companies_resolved.json -2025-09-10 11:19:30,226 - abzu.spark.er_eval - INFO - - data/er/iterations/{iteration}/er_evaluation_metrics.parquet -2025-09-10 11:19:30,226 - abzu.spark.er_eval - INFO - - data/er/iterations/{iteration}/er_evaluation_metrics.json -2025-09-10 11:19:30,226 - abzu.spark.er_eval - INFO - ============================================================ - -# Claude Code Plan - -Claude Code, put your plan to fix these issues here... +## Key Implementation Files + +- `abzu/spark/er_block.py` - Blocking logic +- `abzu/er/match.py` - LLM-based matching +- `abzu/spark/er_eval.py` - Evaluation and metrics +- `abzu/spark/er_final.py` - Final company consolidation +- `abzu/cli/process/er/` - CLI commands diff --git a/notebooks/graph.py b/notebooks/graph.py index 4a8235e..45c5c20 100644 --- a/notebooks/graph.py +++ b/notebooks/graph.py @@ -310,7 +310,7 @@ def _( height=800, ) ) - g.plot(G) + g.plot(G, validate=False, warn=True) return @@ -438,12 +438,7 @@ def _(FAVICON_URL, GRAPHISTRY_PARAMS, G_clean, graphistry): height=800, ) ) - g2.plot(G_clean) - return - - -@app.cell -def _(): + g2.plot(G_clean, validate=False, warn=True) return diff --git a/poetry.lock b/poetry.lock index 5203ac3..754c16c 100644 --- a/poetry.lock +++ b/poetry.lock @@ -384,6 +384,21 @@ files = [ {file = "attrs-25.4.0.tar.gz", hash = "sha256:16d5969b87f0859ef33a48b35d55ac1be6e42ae49d5e853b597db70c35c57e11"}, ] +[[package]] +name = "authlib" +version = "1.6.6" +description = "The ultimate Python library in building OAuth and OpenID Connect servers and clients." +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "authlib-1.6.6-py2.py3-none-any.whl", hash = "sha256:7d9e9bc535c13974313a87f53e8430eb6ea3d1cf6ae4f6efcd793f2e949143fd"}, + {file = "authlib-1.6.6.tar.gz", hash = "sha256:45770e8e056d0f283451d9996fbb59b70d45722b45d854d58f32878d0a40c38e"}, +] + +[package.dependencies] +cryptography = "*" + [[package]] name = "automat" version = "25.4.16" @@ -416,23 +431,43 @@ dev = ["backports.zoneinfo ; python_version < \"3.9\"", "freezegun (>=1.0,<2.0)" [[package]] name = "baml-py" -version = "0.214.0" +version = "0.215.0" description = "BAML python bindings (pyproject.toml)" optional = false python-versions = "*" groups = ["main"] files = [ - {file = "baml_py-0.214.0-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:6edf8fcf7b7fea9808634c075d9c178c6977c29ad833a63b3754b00ecd78a1cd"}, - {file = "baml_py-0.214.0-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:d341d2a686b6907eb37b43c55fc191ff0920549c3e5978db23ac72fb52d81ef1"}, - {file = "baml_py-0.214.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:35fa097a5d0d4d08308f7d6f1226264b9a110dd11f8b9d9c7e4e46c120d358e7"}, - {file = "baml_py-0.214.0-cp38-abi3-manylinux_2_24_aarch64.whl", hash = "sha256:53b13dee5851f54725b7f57d61207519d28bbd48e079d891910de7c329b15ef2"}, - {file = "baml_py-0.214.0-cp38-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:9c3aa6a1e236da034ab86b44b685c110aa0aed3202a316191e2554efcc9f1ed6"}, - {file = "baml_py-0.214.0-cp38-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:057b48b0319a36ac6d48fe80a293e30da9088f3f0735ba8014b9b5e504e7beee"}, - {file = "baml_py-0.214.0-cp38-abi3-win_amd64.whl", hash = "sha256:34ab27384264acbba1cbd179c074f584d9282af3f180b3b65f5dcd9ae9218f9c"}, - {file = "baml_py-0.214.0-cp38-abi3-win_arm64.whl", hash = "sha256:c02bdd5ae388e405239b9c0677808366db4ee5803323991d8405cf4250b6fe72"}, - {file = "baml_py-0.214.0-pp39-pypy39_pp73-manylinux_2_24_aarch64.whl", hash = "sha256:ecd7bdb45458917cdf508d54be83f0cc57226a320b112ef177b19e79f96a4a16"}, + {file = "baml_py-0.215.0-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:085fc9b1b8fb92b54c775423901ed2e37687a7cf96578fd6b428bb986833fae2"}, + {file = "baml_py-0.215.0-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:ccdba535391a7d6af7f267b7abf30f110a8aaada84c699122c3fc2b492aa667a"}, + {file = "baml_py-0.215.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:07e68d99e32b09692561f603bba0d9bf9c45be522e78dfe633a19f27fea87c0c"}, + {file = "baml_py-0.215.0-cp38-abi3-manylinux_2_24_aarch64.whl", hash = "sha256:b89dd513bd690f9e57c0e2dfd2e719185f2582655879b49a8b445e1db014b63c"}, + {file = "baml_py-0.215.0-cp38-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:c635dd4b5896360fa566191e912f5f858011dae16933f250e195ba265f2cb143"}, + {file = "baml_py-0.215.0-cp38-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:db94a4fb5f4639cb10f7826d85f1a6a0133e9be2888ccf68d53407922eca5d3a"}, + {file = "baml_py-0.215.0-cp38-abi3-win_amd64.whl", hash = "sha256:0b0ab57cf67a2503671a12091ea100c1f9f509ce593cb25e7badb758f3a63879"}, + {file = "baml_py-0.215.0-cp38-abi3-win_arm64.whl", hash = "sha256:c47c468dd46559321b55e614ca2e7613cc1dfe55738c62e5f9758a85cf30e5ac"}, + {file = "baml_py-0.215.0-pp39-pypy39_pp73-manylinux_2_24_aarch64.whl", hash = "sha256:8d5c10426953d6f1a0003b90a5d610a8a7ad71f7d205b167676efca332a067a0"}, +] + +[[package]] +name = "beartype" +version = "0.22.9" +description = "Unbearably fast near-real-time pure-Python runtime-static type-checker." +optional = false +python-versions = ">=3.10" +groups = ["main"] +files = [ + {file = "beartype-0.22.9-py3-none-any.whl", hash = "sha256:d16c9bbc61ea14637596c5f6fbff2ee99cbe3573e46a716401734ef50c3060c2"}, + {file = "beartype-0.22.9.tar.gz", hash = "sha256:8f82b54aa723a2848a56008d18875f91c1db02c32ef6a62319a002e3e25a975f"}, ] +[package.extras] +dev = ["autoapi (>=0.9.0)", "celery", "click", "coverage (>=5.5)", "docutils (>=0.22.0)", "equinox ; sys_platform == \"linux\" and python_version < \"3.15.0\"", "fastmcp ; python_version < \"3.14.0\"", "jax[cpu] ; sys_platform == \"linux\" and python_version < \"3.15.0\"", "jaxtyping ; sys_platform == \"linux\"", "langchain ; python_version < \"3.14.0\" and sys_platform != \"darwin\" and platform_python_implementation != \"PyPy\"", "mypy (>=0.800) ; platform_python_implementation != \"PyPy\"", "nuitka (>=1.2.6) ; sys_platform == \"linux\" and python_version < \"3.14.0\"", "numba ; python_version < \"3.14.0\"", "numpy ; python_version < \"3.15.0\" and sys_platform != \"darwin\" and platform_python_implementation != \"PyPy\"", "pandera (>=0.26.0) ; python_version < \"3.14.0\"", "poetry", "polars ; python_version < \"3.14.0\"", "pydata-sphinx-theme (<=0.7.2)", "pygments", "pyinstaller", "pyright (>=1.1.370)", "pytest (>=6.2.0)", "redis", "rich-click", "setuptools", "sphinx", "sphinx (>=4.2.0,<6.0.0)", "sphinxext-opengraph (>=0.7.5)", "sqlalchemy", "torch ; sys_platform == \"linux\" and python_version < \"3.14.0\"", "tox (>=3.20.1)", "typer", "typing-extensions (>=3.10.0.0)", "xarray ; python_version < \"3.15.0\""] +doc-ghp = ["mkdocs-material[imaging] (>=9.6.0)", "mkdocstrings-python (>=1.16.0)", "mkdocstrings-python-xref (>=1.16.0)"] +doc-rtd = ["autoapi (>=0.9.0)", "pydata-sphinx-theme (<=0.7.2)", "setuptools", "sphinx (>=4.2.0,<6.0.0)", "sphinxext-opengraph (>=0.7.5)"] +test = ["celery", "click", "coverage (>=5.5)", "docutils (>=0.22.0)", "equinox ; sys_platform == \"linux\" and python_version < \"3.15.0\"", "fastmcp ; python_version < \"3.14.0\"", "jax[cpu] ; sys_platform == \"linux\" and python_version < \"3.15.0\"", "jaxtyping ; sys_platform == \"linux\"", "langchain ; python_version < \"3.14.0\" and sys_platform != \"darwin\" and platform_python_implementation != \"PyPy\"", "mypy (>=0.800) ; platform_python_implementation != \"PyPy\"", "nuitka (>=1.2.6) ; sys_platform == \"linux\" and python_version < \"3.14.0\"", "numba ; python_version < \"3.14.0\"", "numpy ; python_version < \"3.15.0\" and sys_platform != \"darwin\" and platform_python_implementation != \"PyPy\"", "pandera (>=0.26.0) ; python_version < \"3.14.0\"", "poetry", "polars ; python_version < \"3.14.0\"", "pygments", "pyinstaller", "pyright (>=1.1.370)", "pytest (>=6.2.0)", "redis", "rich-click", "sphinx", "sqlalchemy", "torch ; sys_platform == \"linux\" and python_version < \"3.14.0\"", "tox (>=3.20.1)", "typer", "typing-extensions (>=3.10.0.0)", "xarray ; python_version < \"3.15.0\""] +test-tox = ["celery", "click", "docutils (>=0.22.0)", "equinox ; sys_platform == \"linux\" and python_version < \"3.15.0\"", "fastmcp ; python_version < \"3.14.0\"", "jax[cpu] ; sys_platform == \"linux\" and python_version < \"3.15.0\"", "jaxtyping ; sys_platform == \"linux\"", "langchain ; python_version < \"3.14.0\" and sys_platform != \"darwin\" and platform_python_implementation != \"PyPy\"", "mypy (>=0.800) ; platform_python_implementation != \"PyPy\"", "nuitka (>=1.2.6) ; sys_platform == \"linux\" and python_version < \"3.14.0\"", "numba ; python_version < \"3.14.0\"", "numpy ; python_version < \"3.15.0\" and sys_platform != \"darwin\" and platform_python_implementation != \"PyPy\"", "pandera (>=0.26.0) ; python_version < \"3.14.0\"", "poetry", "polars ; python_version < \"3.14.0\"", "pygments", "pyinstaller", "pyright (>=1.1.370)", "pytest (>=6.2.0)", "redis", "rich-click", "sphinx", "sqlalchemy", "torch ; sys_platform == \"linux\" and python_version < \"3.14.0\"", "typer", "typing-extensions (>=3.10.0.0)", "xarray ; python_version < \"3.15.0\""] +test-tox-coverage = ["coverage (>=5.5)"] + [[package]] name = "beautifulsoup4" version = "4.14.3" @@ -458,38 +493,39 @@ lxml = ["lxml"] [[package]] name = "black" -version = "25.11.0" +version = "25.12.0" description = "The uncompromising code formatter." optional = false -python-versions = ">=3.9" +python-versions = ">=3.10" groups = ["dev"] files = [ - {file = "black-25.11.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ec311e22458eec32a807f029b2646f661e6859c3f61bc6d9ffb67958779f392e"}, - {file = "black-25.11.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1032639c90208c15711334d681de2e24821af0575573db2810b0763bcd62e0f0"}, - {file = "black-25.11.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0c0f7c461df55cf32929b002335883946a4893d759f2df343389c4396f3b6b37"}, - {file = "black-25.11.0-cp310-cp310-win_amd64.whl", hash = "sha256:f9786c24d8e9bd5f20dc7a7f0cdd742644656987f6ea6947629306f937726c03"}, - {file = "black-25.11.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:895571922a35434a9d8ca67ef926da6bc9ad464522a5fe0db99b394ef1c0675a"}, - {file = "black-25.11.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:cb4f4b65d717062191bdec8e4a442539a8ea065e6af1c4f4d36f0cdb5f71e170"}, - {file = "black-25.11.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d81a44cbc7e4f73a9d6ae449ec2317ad81512d1e7dce7d57f6333fd6259737bc"}, - {file = "black-25.11.0-cp311-cp311-win_amd64.whl", hash = "sha256:7eebd4744dfe92ef1ee349dc532defbf012a88b087bb7ddd688ff59a447b080e"}, - {file = "black-25.11.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:80e7486ad3535636657aa180ad32a7d67d7c273a80e12f1b4bfa0823d54e8fac"}, - {file = "black-25.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6cced12b747c4c76bc09b4db057c319d8545307266f41aaee665540bc0e04e96"}, - {file = "black-25.11.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6cb2d54a39e0ef021d6c5eef442e10fd71fcb491be6413d083a320ee768329dd"}, - {file = "black-25.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:ae263af2f496940438e5be1a0c1020e13b09154f3af4df0835ea7f9fe7bfa409"}, - {file = "black-25.11.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0a1d40348b6621cc20d3d7530a5b8d67e9714906dfd7346338249ad9c6cedf2b"}, - {file = "black-25.11.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:51c65d7d60bb25429ea2bf0731c32b2a2442eb4bd3b2afcb47830f0b13e58bfd"}, - {file = "black-25.11.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:936c4dd07669269f40b497440159a221ee435e3fddcf668e0c05244a9be71993"}, - {file = "black-25.11.0-cp313-cp313-win_amd64.whl", hash = "sha256:f42c0ea7f59994490f4dccd64e6b2dd49ac57c7c84f38b8faab50f8759db245c"}, - {file = "black-25.11.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:35690a383f22dd3e468c85dc4b915217f87667ad9cce781d7b42678ce63c4170"}, - {file = "black-25.11.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:dae49ef7369c6caa1a1833fd5efb7c3024bb7e4499bf64833f65ad27791b1545"}, - {file = "black-25.11.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5bd4a22a0b37401c8e492e994bce79e614f91b14d9ea911f44f36e262195fdda"}, - {file = "black-25.11.0-cp314-cp314-win_amd64.whl", hash = "sha256:aa211411e94fdf86519996b7f5f05e71ba34835d8f0c0f03c00a26271da02664"}, - {file = "black-25.11.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a3bb5ce32daa9ff0605d73b6f19da0b0e6c1f8f2d75594db539fdfed722f2b06"}, - {file = "black-25.11.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9815ccee1e55717fe9a4b924cae1646ef7f54e0f990da39a34fc7b264fcf80a2"}, - {file = "black-25.11.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:92285c37b93a1698dcbc34581867b480f1ba3a7b92acf1fe0467b04d7a4da0dc"}, - {file = "black-25.11.0-cp39-cp39-win_amd64.whl", hash = "sha256:43945853a31099c7c0ff8dface53b4de56c41294fa6783c0441a8b1d9bf668bc"}, - {file = "black-25.11.0-py3-none-any.whl", hash = "sha256:e3f562da087791e96cefcd9dda058380a442ab322a02e222add53736451f604b"}, - {file = "black-25.11.0.tar.gz", hash = "sha256:9a323ac32f5dc75ce7470501b887250be5005a01602e931a15e45593f70f6e08"}, + {file = "black-25.12.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f85ba1ad15d446756b4ab5f3044731bf68b777f8f9ac9cdabd2425b97cd9c4e8"}, + {file = "black-25.12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:546eecfe9a3a6b46f9d69d8a642585a6eaf348bcbbc4d87a19635570e02d9f4a"}, + {file = "black-25.12.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:17dcc893da8d73d8f74a596f64b7c98ef5239c2cd2b053c0f25912c4494bf9ea"}, + {file = "black-25.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:09524b0e6af8ba7a3ffabdfc7a9922fb9adef60fed008c7cd2fc01f3048e6e6f"}, + {file = "black-25.12.0-cp310-cp310-win_arm64.whl", hash = "sha256:b162653ed89eb942758efeb29d5e333ca5bb90e5130216f8369857db5955a7da"}, + {file = "black-25.12.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d0cfa263e85caea2cff57d8f917f9f51adae8e20b610e2b23de35b5b11ce691a"}, + {file = "black-25.12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1a2f578ae20c19c50a382286ba78bfbeafdf788579b053d8e4980afb079ab9be"}, + {file = "black-25.12.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d3e1b65634b0e471d07ff86ec338819e2ef860689859ef4501ab7ac290431f9b"}, + {file = "black-25.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:a3fa71e3b8dd9f7c6ac4d818345237dfb4175ed3bf37cd5a581dbc4c034f1ec5"}, + {file = "black-25.12.0-cp311-cp311-win_arm64.whl", hash = "sha256:51e267458f7e650afed8445dc7edb3187143003d52a1b710c7321aef22aa9655"}, + {file = "black-25.12.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:31f96b7c98c1ddaeb07dc0f56c652e25bdedaac76d5b68a059d998b57c55594a"}, + {file = "black-25.12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:05dd459a19e218078a1f98178c13f861fe6a9a5f88fc969ca4d9b49eb1809783"}, + {file = "black-25.12.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c1f68c5eff61f226934be6b5b80296cf6939e5d2f0c2f7d543ea08b204bfaf59"}, + {file = "black-25.12.0-cp312-cp312-win_amd64.whl", hash = "sha256:274f940c147ddab4442d316b27f9e332ca586d39c85ecf59ebdea82cc9ee8892"}, + {file = "black-25.12.0-cp312-cp312-win_arm64.whl", hash = "sha256:169506ba91ef21e2e0591563deda7f00030cb466e747c4b09cb0a9dae5db2f43"}, + {file = "black-25.12.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a05ddeb656534c3e27a05a29196c962877c83fa5503db89e68857d1161ad08a5"}, + {file = "black-25.12.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9ec77439ef3e34896995503865a85732c94396edcc739f302c5673a2315e1e7f"}, + {file = "black-25.12.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e509c858adf63aa61d908061b52e580c40eae0dfa72415fa47ac01b12e29baf"}, + {file = "black-25.12.0-cp313-cp313-win_amd64.whl", hash = "sha256:252678f07f5bac4ff0d0e9b261fbb029fa530cfa206d0a636a34ab445ef8ca9d"}, + {file = "black-25.12.0-cp313-cp313-win_arm64.whl", hash = "sha256:bc5b1c09fe3c931ddd20ee548511c64ebf964ada7e6f0763d443947fd1c603ce"}, + {file = "black-25.12.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:0a0953b134f9335c2434864a643c842c44fba562155c738a2a37a4d61f00cad5"}, + {file = "black-25.12.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:2355bbb6c3b76062870942d8cc450d4f8ac71f9c93c40122762c8784df49543f"}, + {file = "black-25.12.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9678bd991cc793e81d19aeeae57966ee02909877cb65838ccffef24c3ebac08f"}, + {file = "black-25.12.0-cp314-cp314-win_amd64.whl", hash = "sha256:97596189949a8aad13ad12fcbb4ae89330039b96ad6742e6f6b45e75ad5cfd83"}, + {file = "black-25.12.0-cp314-cp314-win_arm64.whl", hash = "sha256:778285d9ea197f34704e3791ea9404cd6d07595745907dd2ce3da7a13627b29b"}, + {file = "black-25.12.0-py3-none-any.whl", hash = "sha256:48ceb36c16dbc84062740049eef990bb2ce07598272e673c17d1a7720c71c828"}, + {file = "black-25.12.0.tar.gz", hash = "sha256:8d3dd9cea14bff7ddc0eb243c811cdb1a011ebb4800a5f0335a01a68654796a7"}, ] [package.dependencies] @@ -529,18 +565,18 @@ css = ["tinycss2 (>=1.1.0,<1.5)"] [[package]] name = "boto3" -version = "1.42.4" +version = "1.42.12" description = "The AWS SDK for Python" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "boto3-1.42.4-py3-none-any.whl", hash = "sha256:0f4089e230d55f981d67376e48cefd41c3d58c7f694480f13288e6ff7b1fefbc"}, - {file = "boto3-1.42.4.tar.gz", hash = "sha256:65f0d98a3786ec729ba9b5f70448895b2d1d1f27949aa7af5cb4f39da341bbc4"}, + {file = "boto3-1.42.12-py3-none-any.whl", hash = "sha256:8112e1beb5978bb455ea4b41a9ef26fc408f6340d8ff69ef93dded4f80fd53e9"}, + {file = "boto3-1.42.12.tar.gz", hash = "sha256:649b134d25b278c24fcc8b3f94519de3884283b7848dc32f42b0ffdd9d19ce99"}, ] [package.dependencies] -botocore = ">=1.42.4,<1.43.0" +botocore = ">=1.42.12,<1.43.0" jmespath = ">=0.7.1,<2.0.0" s3transfer = ">=0.16.0,<0.17.0" @@ -549,14 +585,14 @@ crt = ["botocore[crt] (>=1.21.0,<2.0a0)"] [[package]] name = "botocore" -version = "1.42.4" +version = "1.42.12" description = "Low-level, data-driven core of boto 3." optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "botocore-1.42.4-py3-none-any.whl", hash = "sha256:c3b091fd33809f187824b6434e518b889514ded5164cb379358367c18e8b0d7d"}, - {file = "botocore-1.42.4.tar.gz", hash = "sha256:d4816023492b987a804f693c2d76fb751fdc8755d49933106d69e2489c4c0f98"}, + {file = "botocore-1.42.12-py3-none-any.whl", hash = "sha256:4f163880350f6d831857ce5d023875b7c6534be862e5affd9fcf82b8d1ab3537"}, + {file = "botocore-1.42.12.tar.gz", hash = "sha256:1f9f63c3d6bb1f768519da30d6018706443c5d8af5472274d183a4945f3d81f8"}, ] [package.dependencies] @@ -630,14 +666,14 @@ redis = ["redis (>=2.10.5)"] [[package]] name = "cachetools" -version = "6.2.2" +version = "6.2.4" description = "Extensible memoizing collections and decorators" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "cachetools-6.2.2-py3-none-any.whl", hash = "sha256:6c09c98183bf58560c97b2abfcedcbaf6a896a490f534b031b661d3723b45ace"}, - {file = "cachetools-6.2.2.tar.gz", hash = "sha256:8e6d266b25e539df852251cfd6f990b4bc3a141db73b939058d809ebd2590fc6"}, + {file = "cachetools-6.2.4-py3-none-any.whl", hash = "sha256:69a7a52634fed8b8bf6e24a050fb60bff1c9bd8f6d24572b99c32d4e71e62a51"}, + {file = "cachetools-6.2.4.tar.gz", hash = "sha256:82c5c05585e70b6ba2d3ae09ea60b79548872185d2f24ae1f2709d37299fd607"}, ] [[package]] @@ -887,15 +923,19 @@ files = [ [[package]] name = "cleanco" -version = "2.3" +version = "2.4-dev" description = "Python library to process company names" optional = false -python-versions = "*" +python-versions = "^3.8" groups = ["main"] -files = [ - {file = "cleanco-2.3-py3-none-any.whl", hash = "sha256:7ab4672cdfdabe2375a608686da86005b33df6f347ebf7849c9d0e2651f27e01"}, - {file = "cleanco-2.3.tar.gz", hash = "sha256:26b899b2be871be288408205686c5a1dd5f3ab085b709d041a77f3e3049d2961"}, -] +files = [] +develop = false + +[package.source] +type = "git" +url = "https://github.com/Graphlet-AI/cleanco.git" +reference = "rjurney/disco" +resolved_reference = "6439f9e792b3ecf2650ff68f5ee541f385726434" [[package]] name = "cleo" @@ -928,6 +968,18 @@ files = [ [package.dependencies] colorama = {version = "*", markers = "platform_system == \"Windows\""} +[[package]] +name = "cloudpickle" +version = "3.1.2" +description = "Pickler class to extend the standard pickle.Pickler functionality" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "cloudpickle-3.1.2-py3-none-any.whl", hash = "sha256:9acb47f6afd73f60dc1df93bb801b472f05ff42fa6c84167d25cb206be1fbf4a"}, + {file = "cloudpickle-3.1.2.tar.gz", hash = "sha256:7fda9eb655c9c230dab534f1983763de5835249750e85fbcef43aaa30a9a2414"}, +] + [[package]] name = "cloudscraper" version = "1.2.71" @@ -1218,6 +1270,33 @@ files = [ docs = ["ipython", "matplotlib", "numpydoc", "sphinx"] tests = ["pytest", "pytest-cov", "pytest-xdist"] +[[package]] +name = "cyclopts" +version = "4.4.0" +description = "Intuitive, easy CLIs based on type hints." +optional = false +python-versions = ">=3.10" +groups = ["main"] +files = [ + {file = "cyclopts-4.4.0-py3-none-any.whl", hash = "sha256:78ff95a5e52e738a1d0f01e5a3af48049c47748fa2c255f2629a4cef54dcf2b3"}, + {file = "cyclopts-4.4.0.tar.gz", hash = "sha256:16764f5a807696b61da7d19626f34d261cdffe33345e87a194cf3286db2bd9cc"}, +] + +[package.dependencies] +attrs = ">=23.1.0" +docstring-parser = ">=0.15,<4.0" +rich = ">=13.6.0" +rich-rst = ">=1.3.1,<2.0.0" + +[package.extras] +debug = ["ipdb (>=0.13.9)", "line-profiler (>=3.5.1)"] +dev = ["coverage[toml] (>=5.1)", "mkdocs (>=1.4.0)", "pre-commit (>=2.16.0)", "pydantic (>=2.11.2,<3.0.0)", "pytest (>=8.2.0)", "pytest-cov (>=3.0.0)", "pytest-mock (>=3.7.0)", "pyyaml (>=6.0.1)", "syrupy (>=4.0.0)", "toml (>=0.10.2,<1.0.0)", "trio (>=0.10.0)"] +docs = ["gitpython (>=3.1.31)", "myst-parser[linkify] (>=3.0.1,<5.0.0)", "sphinx (>=7.4.7,<8.2.0)", "sphinx-autodoc-typehints (>=1.25.2,<4.0.0)", "sphinx-copybutton (>=0.5,<1.0)", "sphinx-rtd-dark-mode (>=1.3.0,<2.0.0)", "sphinx-rtd-theme (>=3.0.0,<4.0.0)"] +mkdocs = ["markdown (>=3.3)", "mkdocs (>=1.4.0)", "pymdown-extensions (>=10.0)"] +toml = ["tomli (>=2.0.0) ; python_version < \"3.11\""] +trio = ["trio (>=0.10.0)"] +yaml = ["pyyaml (>=6.0.1)"] + [[package]] name = "dapr" version = "1.16.0" @@ -1256,42 +1335,42 @@ typing-inspect = ">=0.4.0,<1" [[package]] name = "debugpy" -version = "1.8.17" +version = "1.8.19" description = "An implementation of the Debug Adapter Protocol for Python" optional = false python-versions = ">=3.8" groups = ["dev"] files = [ - {file = "debugpy-1.8.17-cp310-cp310-macosx_15_0_x86_64.whl", hash = "sha256:c41d2ce8bbaddcc0009cc73f65318eedfa3dbc88a8298081deb05389f1ab5542"}, - {file = "debugpy-1.8.17-cp310-cp310-manylinux_2_34_x86_64.whl", hash = "sha256:1440fd514e1b815edd5861ca394786f90eb24960eb26d6f7200994333b1d79e3"}, - {file = "debugpy-1.8.17-cp310-cp310-win32.whl", hash = "sha256:3a32c0af575749083d7492dc79f6ab69f21b2d2ad4cd977a958a07d5865316e4"}, - {file = "debugpy-1.8.17-cp310-cp310-win_amd64.whl", hash = "sha256:a3aad0537cf4d9c1996434be68c6c9a6d233ac6f76c2a482c7803295b4e4f99a"}, - {file = "debugpy-1.8.17-cp311-cp311-macosx_15_0_universal2.whl", hash = "sha256:d3fce3f0e3de262a3b67e69916d001f3e767661c6e1ee42553009d445d1cd840"}, - {file = "debugpy-1.8.17-cp311-cp311-manylinux_2_34_x86_64.whl", hash = "sha256:c6bdf134457ae0cac6fb68205776be635d31174eeac9541e1d0c062165c6461f"}, - {file = "debugpy-1.8.17-cp311-cp311-win32.whl", hash = "sha256:e79a195f9e059edfe5d8bf6f3749b2599452d3e9380484cd261f6b7cd2c7c4da"}, - {file = "debugpy-1.8.17-cp311-cp311-win_amd64.whl", hash = "sha256:b532282ad4eca958b1b2d7dbcb2b7218e02cb934165859b918e3b6ba7772d3f4"}, - {file = "debugpy-1.8.17-cp312-cp312-macosx_15_0_universal2.whl", hash = "sha256:f14467edef672195c6f6b8e27ce5005313cb5d03c9239059bc7182b60c176e2d"}, - {file = "debugpy-1.8.17-cp312-cp312-manylinux_2_34_x86_64.whl", hash = "sha256:24693179ef9dfa20dca8605905a42b392be56d410c333af82f1c5dff807a64cc"}, - {file = "debugpy-1.8.17-cp312-cp312-win32.whl", hash = "sha256:6a4e9dacf2cbb60d2514ff7b04b4534b0139facbf2abdffe0639ddb6088e59cf"}, - {file = "debugpy-1.8.17-cp312-cp312-win_amd64.whl", hash = "sha256:e8f8f61c518952fb15f74a302e068b48d9c4691768ade433e4adeea961993464"}, - {file = "debugpy-1.8.17-cp313-cp313-macosx_15_0_universal2.whl", hash = "sha256:857c1dd5d70042502aef1c6d1c2801211f3ea7e56f75e9c335f434afb403e464"}, - {file = "debugpy-1.8.17-cp313-cp313-manylinux_2_34_x86_64.whl", hash = "sha256:3bea3b0b12f3946e098cce9b43c3c46e317b567f79570c3f43f0b96d00788088"}, - {file = "debugpy-1.8.17-cp313-cp313-win32.whl", hash = "sha256:e34ee844c2f17b18556b5bbe59e1e2ff4e86a00282d2a46edab73fd7f18f4a83"}, - {file = "debugpy-1.8.17-cp313-cp313-win_amd64.whl", hash = "sha256:6c5cd6f009ad4fca8e33e5238210dc1e5f42db07d4b6ab21ac7ffa904a196420"}, - {file = "debugpy-1.8.17-cp314-cp314-macosx_15_0_universal2.whl", hash = "sha256:045290c010bcd2d82bc97aa2daf6837443cd52f6328592698809b4549babcee1"}, - {file = "debugpy-1.8.17-cp314-cp314-manylinux_2_34_x86_64.whl", hash = "sha256:b69b6bd9dba6a03632534cdf67c760625760a215ae289f7489a452af1031fe1f"}, - {file = "debugpy-1.8.17-cp314-cp314-win32.whl", hash = "sha256:5c59b74aa5630f3a5194467100c3b3d1c77898f9ab27e3f7dc5d40fc2f122670"}, - {file = "debugpy-1.8.17-cp314-cp314-win_amd64.whl", hash = "sha256:893cba7bb0f55161de4365584b025f7064e1f88913551bcd23be3260b231429c"}, - {file = "debugpy-1.8.17-cp38-cp38-macosx_15_0_x86_64.whl", hash = "sha256:8deb4e31cd575c9f9370042876e078ca118117c1b5e1f22c32befcfbb6955f0c"}, - {file = "debugpy-1.8.17-cp38-cp38-manylinux_2_34_x86_64.whl", hash = "sha256:b75868b675949a96ab51abc114c7163f40ff0d8f7d6d5fd63f8932fd38e9c6d7"}, - {file = "debugpy-1.8.17-cp38-cp38-win32.whl", hash = "sha256:17e456da14848d618662354e1dccfd5e5fb75deec3d1d48dc0aa0baacda55860"}, - {file = "debugpy-1.8.17-cp38-cp38-win_amd64.whl", hash = "sha256:e851beb536a427b5df8aa7d0c7835b29a13812f41e46292ff80b2ef77327355a"}, - {file = "debugpy-1.8.17-cp39-cp39-macosx_15_0_x86_64.whl", hash = "sha256:f2ac8055a0c4a09b30b931100996ba49ef334c6947e7ae365cdd870416d7513e"}, - {file = "debugpy-1.8.17-cp39-cp39-manylinux_2_34_x86_64.whl", hash = "sha256:eaa85bce251feca8e4c87ce3b954aba84b8c645b90f0e6a515c00394a9f5c0e7"}, - {file = "debugpy-1.8.17-cp39-cp39-win32.whl", hash = "sha256:b13eea5587e44f27f6c48588b5ad56dcb74a4f3a5f89250443c94587f3eb2ea1"}, - {file = "debugpy-1.8.17-cp39-cp39-win_amd64.whl", hash = "sha256:bb1bbf92317e1f35afcf3ef0450219efb3afe00be79d8664b250ac0933b9015f"}, - {file = "debugpy-1.8.17-py2.py3-none-any.whl", hash = "sha256:60c7dca6571efe660ccb7a9508d73ca14b8796c4ed484c2002abba714226cfef"}, - {file = "debugpy-1.8.17.tar.gz", hash = "sha256:fd723b47a8c08892b1a16b2c6239a8b96637c62a59b94bb5dab4bac592a58a8e"}, + {file = "debugpy-1.8.19-cp310-cp310-macosx_15_0_x86_64.whl", hash = "sha256:fce6da15d73be5935b4438435c53adb512326a3e11e4f90793ea87cd9f018254"}, + {file = "debugpy-1.8.19-cp310-cp310-manylinux_2_34_x86_64.whl", hash = "sha256:e24b1652a1df1ab04d81e7ead446a91c226de704ff5dde6bd0a0dbaab07aa3f2"}, + {file = "debugpy-1.8.19-cp310-cp310-win32.whl", hash = "sha256:327cb28c3ad9e17bc925efc7f7018195fd4787c2fe4b7af1eec11f1d19bdec62"}, + {file = "debugpy-1.8.19-cp310-cp310-win_amd64.whl", hash = "sha256:b7dd275cf2c99e53adb9654f5ae015f70415bbe2bacbe24cfee30d54b6aa03c5"}, + {file = "debugpy-1.8.19-cp311-cp311-macosx_15_0_universal2.whl", hash = "sha256:c5dcfa21de1f735a4f7ced4556339a109aa0f618d366ede9da0a3600f2516d8b"}, + {file = "debugpy-1.8.19-cp311-cp311-manylinux_2_34_x86_64.whl", hash = "sha256:806d6800246244004625d5222d7765874ab2d22f3ba5f615416cf1342d61c488"}, + {file = "debugpy-1.8.19-cp311-cp311-win32.whl", hash = "sha256:783a519e6dfb1f3cd773a9bda592f4887a65040cb0c7bd38dde410f4e53c40d4"}, + {file = "debugpy-1.8.19-cp311-cp311-win_amd64.whl", hash = "sha256:14035cbdbb1fe4b642babcdcb5935c2da3b1067ac211c5c5a8fdc0bb31adbcaa"}, + {file = "debugpy-1.8.19-cp312-cp312-macosx_15_0_universal2.whl", hash = "sha256:bccb1540a49cde77edc7ce7d9d075c1dbeb2414751bc0048c7a11e1b597a4c2e"}, + {file = "debugpy-1.8.19-cp312-cp312-manylinux_2_34_x86_64.whl", hash = "sha256:e9c68d9a382ec754dc05ed1d1b4ed5bd824b9f7c1a8cd1083adb84b3c93501de"}, + {file = "debugpy-1.8.19-cp312-cp312-win32.whl", hash = "sha256:6599cab8a783d1496ae9984c52cb13b7c4a3bd06a8e6c33446832a5d97ce0bee"}, + {file = "debugpy-1.8.19-cp312-cp312-win_amd64.whl", hash = "sha256:66e3d2fd8f2035a8f111eb127fa508469dfa40928a89b460b41fd988684dc83d"}, + {file = "debugpy-1.8.19-cp313-cp313-macosx_15_0_universal2.whl", hash = "sha256:91e35db2672a0abaf325f4868fcac9c1674a0d9ad9bb8a8c849c03a5ebba3e6d"}, + {file = "debugpy-1.8.19-cp313-cp313-manylinux_2_34_x86_64.whl", hash = "sha256:85016a73ab84dea1c1f1dcd88ec692993bcbe4532d1b49ecb5f3c688ae50c606"}, + {file = "debugpy-1.8.19-cp313-cp313-win32.whl", hash = "sha256:b605f17e89ba0ecee994391194285fada89cee111cfcd29d6f2ee11cbdc40976"}, + {file = "debugpy-1.8.19-cp313-cp313-win_amd64.whl", hash = "sha256:c30639998a9f9cd9699b4b621942c0179a6527f083c72351f95c6ab1728d5b73"}, + {file = "debugpy-1.8.19-cp314-cp314-macosx_15_0_universal2.whl", hash = "sha256:1e8c4d1bd230067bf1bbcdbd6032e5a57068638eb28b9153d008ecde288152af"}, + {file = "debugpy-1.8.19-cp314-cp314-manylinux_2_34_x86_64.whl", hash = "sha256:d40c016c1f538dbf1762936e3aeb43a89b965069d9f60f9e39d35d9d25e6b809"}, + {file = "debugpy-1.8.19-cp314-cp314-win32.whl", hash = "sha256:0601708223fe1cd0e27c6cce67a899d92c7d68e73690211e6788a4b0e1903f5b"}, + {file = "debugpy-1.8.19-cp314-cp314-win_amd64.whl", hash = "sha256:8e19a725f5d486f20e53a1dde2ab8bb2c9607c40c00a42ab646def962b41125f"}, + {file = "debugpy-1.8.19-cp38-cp38-macosx_15_0_x86_64.whl", hash = "sha256:d9b6f633fd2865af2afba2beb0c1819b6ecd4aed1c8f90f5d1bbca3272306b10"}, + {file = "debugpy-1.8.19-cp38-cp38-manylinux_2_34_x86_64.whl", hash = "sha256:a21bfdea088f713df05fa246ba0520f6ba44dd7eaec224742f51987a6979a648"}, + {file = "debugpy-1.8.19-cp38-cp38-win32.whl", hash = "sha256:b1cb98e5325da3059ca24445fca48314bfddfdf65ce1b59ff07055e723f06bd2"}, + {file = "debugpy-1.8.19-cp38-cp38-win_amd64.whl", hash = "sha256:c9b9bf440141a36836bdbe4320a2b126bb38aafa85e1aed05d7bfbb0e2a278bf"}, + {file = "debugpy-1.8.19-cp39-cp39-macosx_15_0_x86_64.whl", hash = "sha256:c047177ab2d286451f242b855b650d313198c4a987140d4b35218b2855a64a4a"}, + {file = "debugpy-1.8.19-cp39-cp39-manylinux_2_34_x86_64.whl", hash = "sha256:4468de0c30012d367944f0eab4ecb8371736e8ef9522a465f61214f344c11183"}, + {file = "debugpy-1.8.19-cp39-cp39-win32.whl", hash = "sha256:7b62c0f015120ede25e5124a5f9d8a424e1208e3d96a36c89958f046ee21fff6"}, + {file = "debugpy-1.8.19-cp39-cp39-win_amd64.whl", hash = "sha256:76f566baaf7f3e06adbe67ffedccd2ee911d1e486f55931939ce3f0fe1090774"}, + {file = "debugpy-1.8.19-py2.py3-none-any.whl", hash = "sha256:360ffd231a780abbc414ba0f005dad409e71c78637efe8f2bd75837132a41d38"}, + {file = "debugpy-1.8.19.tar.gz", hash = "sha256:eea7e5987445ab0b5ed258093722d5ecb8bb72217c5c9b1e21f64efe23ddebdb"}, ] [[package]] @@ -1340,6 +1419,18 @@ speed = ["Brotli", "aiodns (>=1.1) ; sys_platform != \"win32\"", "cchardet (==2. test = ["coverage[toml]", "pytest", "pytest-asyncio", "pytest-cov", "pytest-mock", "typing-extensions (>=4.3,<5)", "tzdata ; sys_platform == \"win32\""] voice = ["PyNaCl (>=1.5.0,<1.6)"] +[[package]] +name = "diskcache" +version = "5.6.3" +description = "Disk Cache -- Disk and file backed persistent cache." +optional = false +python-versions = ">=3" +groups = ["main"] +files = [ + {file = "diskcache-5.6.3-py3-none-any.whl", hash = "sha256:5e31b2d5fbad117cc363ebaf6b689474db18a1f6438bc82358b024abd4c2ca19"}, + {file = "diskcache-5.6.3.tar.gz", hash = "sha256:2c3a3fa2743d8535d832ec61c2054a1641f41775aa7c556758a109941e33e4fc"}, +] + [[package]] name = "distlib" version = "0.4.0" @@ -1364,6 +1455,44 @@ files = [ {file = "distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed"}, ] +[[package]] +name = "dnspython" +version = "2.8.0" +description = "DNS toolkit" +optional = false +python-versions = ">=3.10" +groups = ["main"] +files = [ + {file = "dnspython-2.8.0-py3-none-any.whl", hash = "sha256:01d9bbc4a2d76bf0db7c1f729812ded6d912bd318d3b1cf81d30c0f845dbf3af"}, + {file = "dnspython-2.8.0.tar.gz", hash = "sha256:181d3c6996452cb1189c4046c61599b84a5a86e099562ffde77d26984ff26d0f"}, +] + +[package.extras] +dev = ["black (>=25.1.0)", "coverage (>=7.0)", "flake8 (>=7)", "hypercorn (>=0.17.0)", "mypy (>=1.17)", "pylint (>=3)", "pytest (>=8.4)", "pytest-cov (>=6.2.0)", "quart-trio (>=0.12.0)", "sphinx (>=8.2.0)", "sphinx-rtd-theme (>=3.0.0)", "twine (>=6.1.0)", "wheel (>=0.45.0)"] +dnssec = ["cryptography (>=45)"] +doh = ["h2 (>=4.2.0)", "httpcore (>=1.0.0)", "httpx (>=0.28.0)"] +doq = ["aioquic (>=1.2.0)"] +idna = ["idna (>=3.10)"] +trio = ["trio (>=0.30)"] +wmi = ["wmi (>=1.5.1) ; platform_system == \"Windows\""] + +[[package]] +name = "docstring-parser" +version = "0.17.0" +description = "Parse Python docstrings in reST, Google and Numpydoc format" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "docstring_parser-0.17.0-py3-none-any.whl", hash = "sha256:cf2569abd23dce8099b300f9b4fa8191e9582dda731fd533daf54c4551658708"}, + {file = "docstring_parser-0.17.0.tar.gz", hash = "sha256:583de4a309722b3315439bb31d64ba3eebada841f2e2cee23b99df001434c912"}, +] + +[package.extras] +dev = ["pre-commit (>=2.16.0) ; python_version >= \"3.9\"", "pydoctor (>=25.4.0)", "pytest"] +docs = ["pydoctor (>=25.4.0)"] +test = ["pytest"] + [[package]] name = "docutils" version = "0.22.3" @@ -1431,6 +1560,22 @@ paramiko = ["paramiko"] patiencediff = ["patiencediff"] pgp = ["gpg"] +[[package]] +name = "email-validator" +version = "2.3.0" +description = "A robust email address syntax and deliverability validation library." +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "email_validator-2.3.0-py3-none-any.whl", hash = "sha256:80f13f623413e6b197ae73bb10bf4eb0908faf509ad8362c5edeb0be7fd450b4"}, + {file = "email_validator-2.3.0.tar.gz", hash = "sha256:9fc05c37f2f6cf439ff414f8fc46d917929974a82244c20eb10231ba60c54426"}, +] + +[package.dependencies] +dnspython = ">=2.0.0" +idna = ">=2.0.0" + [[package]] name = "eval-type-backport" version = "0.3.1" @@ -1446,6 +1591,24 @@ files = [ [package.extras] tests = ["pytest"] +[[package]] +name = "exceptiongroup" +version = "1.3.1" +description = "Backport of PEP 654 (exception groups)" +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598"}, + {file = "exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219"}, +] + +[package.dependencies] +typing-extensions = {version = ">=4.6.0", markers = "python_version < \"3.13\""} + +[package.extras] +test = ["pytest (>=6)"] + [[package]] name = "execnet" version = "2.1.2" @@ -1476,6 +1639,31 @@ files = [ [package.extras] tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipython", "littleutils", "pytest", "rich ; python_version >= \"3.11\""] +[[package]] +name = "fakeredis" +version = "2.33.0" +description = "Python implementation of redis API, can be used for testing purposes." +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "fakeredis-2.33.0-py3-none-any.whl", hash = "sha256:de535f3f9ccde1c56672ab2fdd6a8efbc4f2619fc2f1acc87b8737177d71c965"}, + {file = "fakeredis-2.33.0.tar.gz", hash = "sha256:d7bc9a69d21df108a6451bbffee23b3eba432c21a654afc7ff2d295428ec5770"}, +] + +[package.dependencies] +lupa = {version = ">=2.1", optional = true, markers = "extra == \"lua\""} +redis = {version = ">=4.3", markers = "python_version > \"3.8\""} +sortedcontainers = ">=2" + +[package.extras] +bf = ["pyprobables (>=0.6)"] +cf = ["pyprobables (>=0.6)"] +json = ["jsonpath-ng (>=1.6)"] +lua = ["lupa (>=2.1)"] +probabilistic = ["pyprobables (>=0.6)"] +valkey = ["valkey (>=6) ; python_version >= \"3.8\""] + [[package]] name = "fasta2a" version = "0.2.9" @@ -1574,6 +1762,40 @@ files = [ [package.extras] devel = ["colorama", "json-spec", "jsonschema", "pylint", "pytest", "pytest-benchmark", "pytest-cache", "validictory"] +[[package]] +name = "fastmcp" +version = "2.14.1" +description = "The fast, Pythonic way to build MCP servers and clients." +optional = false +python-versions = ">=3.10" +groups = ["main"] +files = [ + {file = "fastmcp-2.14.1-py3-none-any.whl", hash = "sha256:fb3e365cc1d52573ab89caeba9944dd4b056149097be169bce428e011f0a57e5"}, + {file = "fastmcp-2.14.1.tar.gz", hash = "sha256:132725cbf77b68fa3c3d165eff0cfa47e40c1479457419e6a2cfda65bd84c8d6"}, +] + +[package.dependencies] +authlib = ">=1.6.5" +cyclopts = ">=4.0.0" +exceptiongroup = ">=1.2.2" +httpx = ">=0.28.1" +jsonschema-path = ">=0.3.4" +mcp = ">=1.24.0" +openapi-pydantic = ">=0.5.1" +platformdirs = ">=4.0.0" +py-key-value-aio = {version = ">=0.3.0,<0.4.0", extras = ["disk", "keyring", "memory"]} +pydantic = {version = ">=2.11.7", extras = ["email"]} +pydocket = ">=0.15.5" +pyperclip = ">=1.9.0" +python-dotenv = ">=1.1.0" +rich = ">=13.9.4" +uvicorn = ">=0.35" +websockets = ">=15.0.1" + +[package.extras] +anthropic = ["anthropic (>=0.40.0)"] +openai = ["openai (>=1.102.0)"] + [[package]] name = "feedparser" version = "6.0.12" @@ -1591,14 +1813,14 @@ sgmllib3k = "*" [[package]] name = "filelock" -version = "3.20.0" +version = "3.20.1" description = "A platform independent file lock." optional = false python-versions = ">=3.10" groups = ["main", "dev"] files = [ - {file = "filelock-3.20.0-py3-none-any.whl", hash = "sha256:339b4732ffda5cd79b13f4e2711a31b0365ce445d95d243bb996273d072546a2"}, - {file = "filelock-3.20.0.tar.gz", hash = "sha256:711e943b4ec6be42e1d4e6690b48dc175c822967466bb31c0c293f34334c13f4"}, + {file = "filelock-3.20.1-py3-none-any.whl", hash = "sha256:15d9e9a67306188a44baa72f569d2bfd803076269365fdea0934385da4dc361a"}, + {file = "filelock-3.20.1.tar.gz", hash = "sha256:b8360948b351b80f420878d8516519a2204b07aefcdcfd24912a5d33127f188c"}, ] [[package]] @@ -1653,62 +1875,62 @@ dev = ["Flit (>=3.4)", "pyTest (>=7)", "pyTest-cov (>=7) ; python_version >= \"3 [[package]] name = "fonttools" -version = "4.61.0" +version = "4.61.1" description = "Tools to manipulate font files" optional = false python-versions = ">=3.10" groups = ["main"] files = [ - {file = "fonttools-4.61.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:dc25a4a9c1225653e4431a9413d0381b1c62317b0f543bdcec24e1991f612f33"}, - {file = "fonttools-4.61.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b493c32d2555e9944ec1b911ea649ff8f01a649ad9cba6c118d6798e932b3f0"}, - {file = "fonttools-4.61.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ad751319dc532a79bdf628b8439af167181b4210a0cd28a8935ca615d9fdd727"}, - {file = "fonttools-4.61.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2de14557d113faa5fb519f7f29c3abe4d69c17fe6a5a2595cc8cda7338029219"}, - {file = "fonttools-4.61.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:59587bbe455dbdf75354a9dbca1697a35a8903e01fab4248d6b98a17032cee52"}, - {file = "fonttools-4.61.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:46cb3d9279f758ac0cf671dc3482da877104b65682679f01b246515db03dbb72"}, - {file = "fonttools-4.61.0-cp310-cp310-win32.whl", hash = "sha256:58b4f1b78dfbfe855bb8a6801b31b8cdcca0e2847ec769ad8e0b0b692832dd3b"}, - {file = "fonttools-4.61.0-cp310-cp310-win_amd64.whl", hash = "sha256:68704a8bbe0b61976262b255e90cde593dc0fe3676542d9b4d846bad2a890a76"}, - {file = "fonttools-4.61.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a32a16951cbf113d38f1dd8551b277b6e06e0f6f776fece0f99f746d739e1be3"}, - {file = "fonttools-4.61.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:328a9c227984bebaf69f3ac9062265f8f6acc7ddf2e4e344c63358579af0aa3d"}, - {file = "fonttools-4.61.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2f0bafc8a3b3749c69cc610e5aa3da832d39c2a37a68f03d18ec9a02ecaac04a"}, - {file = "fonttools-4.61.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b5ca59b7417d149cf24e4c1933c9f44b2957424fc03536f132346d5242e0ebe5"}, - {file = "fonttools-4.61.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:df8cbce85cf482eb01f4551edca978c719f099c623277bda8332e5dbe7dba09d"}, - {file = "fonttools-4.61.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7fb5b84f48a6a733ca3d7f41aa9551908ccabe8669ffe79586560abcc00a9cfd"}, - {file = "fonttools-4.61.0-cp311-cp311-win32.whl", hash = "sha256:787ef9dfd1ea9fe49573c272412ae5f479d78e671981819538143bec65863865"}, - {file = "fonttools-4.61.0-cp311-cp311-win_amd64.whl", hash = "sha256:14fafda386377b6131d9e448af42d0926bad47e038de0e5ba1d58c25d621f028"}, - {file = "fonttools-4.61.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e24a1565c4e57111ec7f4915f8981ecbb61adf66a55f378fdc00e206059fcfef"}, - {file = "fonttools-4.61.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:e2bfacb5351303cae9f072ccf3fc6ecb437a6f359c0606bae4b1ab6715201d87"}, - {file = "fonttools-4.61.0-cp312-cp312-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0bdcf2e29d65c26299cc3d502f4612365e8b90a939f46cd92d037b6cb7bb544a"}, - {file = "fonttools-4.61.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e6cd0d9051b8ddaf7385f99dd82ec2a058e2b46cf1f1961e68e1ff20fcbb61af"}, - {file = "fonttools-4.61.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e074bc07c31406f45c418e17c1722e83560f181d122c412fa9e815df0ff74810"}, - {file = "fonttools-4.61.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5a9b78da5d5faa17e63b2404b77feeae105c1b7e75f26020ab7a27b76e02039f"}, - {file = "fonttools-4.61.0-cp312-cp312-win32.whl", hash = "sha256:9821ed77bb676736b88fa87a737c97b6af06e8109667e625a4f00158540ce044"}, - {file = "fonttools-4.61.0-cp312-cp312-win_amd64.whl", hash = "sha256:0011d640afa61053bc6590f9a3394bd222de7cfde19346588beabac374e9d8ac"}, - {file = "fonttools-4.61.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba774b8cbd8754f54b8eb58124e8bd45f736b2743325ab1a5229698942b9b433"}, - {file = "fonttools-4.61.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c84b430616ed73ce46e9cafd0bf0800e366a3e02fb7e1ad7c1e214dbe3862b1f"}, - {file = "fonttools-4.61.0-cp313-cp313-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b2b734d8391afe3c682320840c8191de9bd24e7eb85768dd4dc06ed1b63dbb1b"}, - {file = "fonttools-4.61.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a5c5fff72bf31b0e558ed085e4fd7ed96eb85881404ecc39ed2a779e7cf724eb"}, - {file = "fonttools-4.61.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:14a290c5c93fcab76b7f451e6a4b7721b712d90b3b5ed6908f1abcf794e90d6d"}, - {file = "fonttools-4.61.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:13e3e20a5463bfeb77b3557d04b30bd6a96a6bb5c15c7b2e7908903e69d437a0"}, - {file = "fonttools-4.61.0-cp313-cp313-win32.whl", hash = "sha256:6781e7a4bb010be1cd69a29927b0305c86b843395f2613bdabe115f7d6ea7f34"}, - {file = "fonttools-4.61.0-cp313-cp313-win_amd64.whl", hash = "sha256:c53b47834ae41e8e4829171cc44fec0fdf125545a15f6da41776b926b9645a9a"}, - {file = "fonttools-4.61.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:96dfc9bc1f2302224e48e6ee37e656eddbab810b724b52e9d9c13a57a6abad01"}, - {file = "fonttools-4.61.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:3b2065d94e5d63aafc2591c8b6ccbdb511001d9619f1bca8ad39b745ebeb5efa"}, - {file = "fonttools-4.61.0-cp314-cp314-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:e0d87e81e4d869549585ba0beb3f033718501c1095004f5e6aef598d13ebc216"}, - {file = "fonttools-4.61.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1cfa2eb9bae650e58f0e8ad53c49d19a844d6034d6b259f30f197238abc1ccee"}, - {file = "fonttools-4.61.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:4238120002e68296d55e091411c09eab94e111c8ce64716d17df53fd0eb3bb3d"}, - {file = "fonttools-4.61.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b6ceac262cc62bec01b3bb59abccf41b24ef6580869e306a4e88b7e56bb4bdda"}, - {file = "fonttools-4.61.0-cp314-cp314-win32.whl", hash = "sha256:adbb4ecee1a779469a77377bbe490565effe8fce6fb2e6f95f064de58f8bac85"}, - {file = "fonttools-4.61.0-cp314-cp314-win_amd64.whl", hash = "sha256:02bdf8e04d1a70476564b8640380f04bb4ac74edc1fc71f1bacb840b3e398ee9"}, - {file = "fonttools-4.61.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:627216062d90ab0d98215176d8b9562c4dd5b61271d35f130bcd30f6a8aaa33a"}, - {file = "fonttools-4.61.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:7b446623c9cd5f14a59493818eaa80255eec2468c27d2c01b56e05357c263195"}, - {file = "fonttools-4.61.0-cp314-cp314t-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:70e2a0c0182ee75e493ef33061bfebf140ea57e035481d2f95aa03b66c7a0e05"}, - {file = "fonttools-4.61.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9064b0f55b947e929ac669af5311ab1f26f750214db6dd9a0c97e091e918f486"}, - {file = "fonttools-4.61.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:2cb5e45a824ce14b90510024d0d39dae51bd4fbb54c42a9334ea8c8cf4d95cbe"}, - {file = "fonttools-4.61.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6e5ca8c62efdec7972dfdfd454415c4db49b89aeaefaaacada432f3b7eea9866"}, - {file = "fonttools-4.61.0-cp314-cp314t-win32.whl", hash = "sha256:63c7125d31abe3e61d7bb917329b5543c5b3448db95f24081a13aaf064360fc8"}, - {file = "fonttools-4.61.0-cp314-cp314t-win_amd64.whl", hash = "sha256:67d841aa272be5500de7f447c40d1d8452783af33b4c3599899319f6ef9ad3c1"}, - {file = "fonttools-4.61.0-py3-none-any.whl", hash = "sha256:276f14c560e6f98d24ef7f5f44438e55ff5a67f78fa85236b218462c9f5d0635"}, - {file = "fonttools-4.61.0.tar.gz", hash = "sha256:ec520a1f0c7758d7a858a00f090c1745f6cde6a7c5e76fb70ea4044a15f712e7"}, + {file = "fonttools-4.61.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c7db70d57e5e1089a274cbb2b1fd635c9a24de809a231b154965d415d6c6d24"}, + {file = "fonttools-4.61.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5fe9fd43882620017add5eabb781ebfbc6998ee49b35bd7f8f79af1f9f99a958"}, + {file = "fonttools-4.61.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d8db08051fc9e7d8bc622f2112511b8107d8f27cd89e2f64ec45e9825e8288da"}, + {file = "fonttools-4.61.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a76d4cb80f41ba94a6691264be76435e5f72f2cb3cab0b092a6212855f71c2f6"}, + {file = "fonttools-4.61.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a13fc8aeb24bad755eea8f7f9d409438eb94e82cf86b08fe77a03fbc8f6a96b1"}, + {file = "fonttools-4.61.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b846a1fcf8beadeb9ea4f44ec5bdde393e2f1569e17d700bfc49cd69bde75881"}, + {file = "fonttools-4.61.1-cp310-cp310-win32.whl", hash = "sha256:78a7d3ab09dc47ac1a363a493e6112d8cabed7ba7caad5f54dbe2f08676d1b47"}, + {file = "fonttools-4.61.1-cp310-cp310-win_amd64.whl", hash = "sha256:eff1ac3cc66c2ac7cda1e64b4e2f3ffef474b7335f92fc3833fc632d595fcee6"}, + {file = "fonttools-4.61.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c6604b735bb12fef8e0efd5578c9fb5d3d8532d5001ea13a19cddf295673ee09"}, + {file = "fonttools-4.61.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5ce02f38a754f207f2f06557523cd39a06438ba3aafc0639c477ac409fc64e37"}, + {file = "fonttools-4.61.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:77efb033d8d7ff233385f30c62c7c79271c8885d5c9657d967ede124671bbdfb"}, + {file = "fonttools-4.61.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:75c1a6dfac6abd407634420c93864a1e274ebc1c7531346d9254c0d8f6ca00f9"}, + {file = "fonttools-4.61.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0de30bfe7745c0d1ffa2b0b7048fb7123ad0d71107e10ee090fa0b16b9452e87"}, + {file = "fonttools-4.61.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:58b0ee0ab5b1fc9921eccfe11d1435added19d6494dde14e323f25ad2bc30c56"}, + {file = "fonttools-4.61.1-cp311-cp311-win32.whl", hash = "sha256:f79b168428351d11e10c5aeb61a74e1851ec221081299f4cf56036a95431c43a"}, + {file = "fonttools-4.61.1-cp311-cp311-win_amd64.whl", hash = "sha256:fe2efccb324948a11dd09d22136fe2ac8a97d6c1347cf0b58a911dcd529f66b7"}, + {file = "fonttools-4.61.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:f3cb4a569029b9f291f88aafc927dd53683757e640081ca8c412781ea144565e"}, + {file = "fonttools-4.61.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:41a7170d042e8c0024703ed13b71893519a1a6d6e18e933e3ec7507a2c26a4b2"}, + {file = "fonttools-4.61.1-cp312-cp312-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:10d88e55330e092940584774ee5e8a6971b01fc2f4d3466a1d6c158230880796"}, + {file = "fonttools-4.61.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:15acc09befd16a0fb8a8f62bc147e1a82817542d72184acca9ce6e0aeda9fa6d"}, + {file = "fonttools-4.61.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e6bcdf33aec38d16508ce61fd81838f24c83c90a1d1b8c68982857038673d6b8"}, + {file = "fonttools-4.61.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5fade934607a523614726119164ff621e8c30e8fa1ffffbbd358662056ba69f0"}, + {file = "fonttools-4.61.1-cp312-cp312-win32.whl", hash = "sha256:75da8f28eff26defba42c52986de97b22106cb8f26515b7c22443ebc9c2d3261"}, + {file = "fonttools-4.61.1-cp312-cp312-win_amd64.whl", hash = "sha256:497c31ce314219888c0e2fce5ad9178ca83fe5230b01a5006726cdf3ac9f24d9"}, + {file = "fonttools-4.61.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8c56c488ab471628ff3bfa80964372fc13504ece601e0d97a78ee74126b2045c"}, + {file = "fonttools-4.61.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:dc492779501fa723b04d0ab1f5be046797fee17d27700476edc7ee9ae535a61e"}, + {file = "fonttools-4.61.1-cp313-cp313-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:64102ca87e84261419c3747a0d20f396eb024bdbeb04c2bfb37e2891f5fadcb5"}, + {file = "fonttools-4.61.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4c1b526c8d3f615a7b1867f38a9410849c8f4aef078535742198e942fba0e9bd"}, + {file = "fonttools-4.61.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:41ed4b5ec103bd306bb68f81dc166e77409e5209443e5773cb4ed837bcc9b0d3"}, + {file = "fonttools-4.61.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b501c862d4901792adaec7c25b1ecc749e2662543f68bb194c42ba18d6eec98d"}, + {file = "fonttools-4.61.1-cp313-cp313-win32.whl", hash = "sha256:4d7092bb38c53bbc78e9255a59158b150bcdc115a1e3b3ce0b5f267dc35dd63c"}, + {file = "fonttools-4.61.1-cp313-cp313-win_amd64.whl", hash = "sha256:21e7c8d76f62ab13c9472ccf74515ca5b9a761d1bde3265152a6dc58700d895b"}, + {file = "fonttools-4.61.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:fff4f534200a04b4a36e7ae3cb74493afe807b517a09e99cb4faa89a34ed6ecd"}, + {file = "fonttools-4.61.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:d9203500f7c63545b4ce3799319fe4d9feb1a1b89b28d3cb5abd11b9dd64147e"}, + {file = "fonttools-4.61.1-cp314-cp314-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fa646ecec9528bef693415c79a86e733c70a4965dd938e9a226b0fc64c9d2e6c"}, + {file = "fonttools-4.61.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:11f35ad7805edba3aac1a3710d104592df59f4b957e30108ae0ba6c10b11dd75"}, + {file = "fonttools-4.61.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b931ae8f62db78861b0ff1ac017851764602288575d65b8e8ff1963fed419063"}, + {file = "fonttools-4.61.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b148b56f5de675ee16d45e769e69f87623a4944f7443850bf9a9376e628a89d2"}, + {file = "fonttools-4.61.1-cp314-cp314-win32.whl", hash = "sha256:9b666a475a65f4e839d3d10473fad6d47e0a9db14a2f4a224029c5bfde58ad2c"}, + {file = "fonttools-4.61.1-cp314-cp314-win_amd64.whl", hash = "sha256:4f5686e1fe5fce75d82d93c47a438a25bf0d1319d2843a926f741140b2b16e0c"}, + {file = "fonttools-4.61.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:e76ce097e3c57c4bcb67c5aa24a0ecdbd9f74ea9219997a707a4061fbe2707aa"}, + {file = "fonttools-4.61.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:9cfef3ab326780c04d6646f68d4b4742aae222e8b8ea1d627c74e38afcbc9d91"}, + {file = "fonttools-4.61.1-cp314-cp314t-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:a75c301f96db737e1c5ed5fd7d77d9c34466de16095a266509e13da09751bd19"}, + {file = "fonttools-4.61.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:91669ccac46bbc1d09e9273546181919064e8df73488ea087dcac3e2968df9ba"}, + {file = "fonttools-4.61.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c33ab3ca9d3ccd581d58e989d67554e42d8d4ded94ab3ade3508455fe70e65f7"}, + {file = "fonttools-4.61.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:664c5a68ec406f6b1547946683008576ef8b38275608e1cee6c061828171c118"}, + {file = "fonttools-4.61.1-cp314-cp314t-win32.whl", hash = "sha256:aed04cabe26f30c1647ef0e8fbb207516fd40fe9472e9439695f5c6998e60ac5"}, + {file = "fonttools-4.61.1-cp314-cp314t-win_amd64.whl", hash = "sha256:2180f14c141d2f0f3da43f3a81bc8aa4684860f6b0e6f9e165a4831f24e6a23b"}, + {file = "fonttools-4.61.1-py3-none-any.whl", hash = "sha256:17d2bf5d541add43822bcf0c43d7d847b160c9bb01d15d5007d84e2217aaa371"}, + {file = "fonttools-4.61.1.tar.gz", hash = "sha256:6675329885c44657f826ef01d9e4fb33b9158e9d93c537d84ad8399539bc6f69"}, ] [package.extras] @@ -1952,14 +2174,14 @@ test = ["coverage[toml]", "ddt (>=1.1.1,!=1.4.3)", "mock ; python_version < \"3. [[package]] name = "google-auth" -version = "2.43.0" +version = "2.45.0" description = "Google Authentication Library" optional = false python-versions = ">=3.7" groups = ["main"] files = [ - {file = "google_auth-2.43.0-py2.py3-none-any.whl", hash = "sha256:af628ba6fa493f75c7e9dbe9373d148ca9f4399b5ea29976519e0a3848eddd16"}, - {file = "google_auth-2.43.0.tar.gz", hash = "sha256:88228eee5fc21b62a1b5fe773ca15e67778cb07dc8363adcb4a8827b52d81483"}, + {file = "google_auth-2.45.0-py2.py3-none-any.whl", hash = "sha256:82344e86dc00410ef5382d99be677c6043d72e502b625aa4f4afa0bdacca0f36"}, + {file = "google_auth-2.45.0.tar.gz", hash = "sha256:90d3f41b6b72ea72dd9811e765699ee491ab24139f34ebf1ca2b9cc0c38708f3"}, ] [package.dependencies] @@ -1970,6 +2192,7 @@ rsa = ">=3.1.4,<5" [package.extras] aiohttp = ["aiohttp (>=3.6.2,<4.0.0)", "requests (>=2.20.0,<3.0.0)"] +cryptography = ["cryptography (<39.0.0) ; python_version < \"3.8\"", "cryptography (>=38.0.3)"] enterprise-cert = ["cryptography", "pyopenssl"] pyjwt = ["cryptography (<39.0.0) ; python_version < \"3.8\"", "cryptography (>=38.0.3)", "pyjwt (>=2.0)"] pyopenssl = ["cryptography (<39.0.0) ; python_version < \"3.8\"", "cryptography (>=38.0.3)", "pyopenssl (>=20.0.0)"] @@ -1980,22 +2203,24 @@ urllib3 = ["packaging", "urllib3"] [[package]] name = "google-genai" -version = "1.53.0" +version = "1.56.0" description = "GenAI Python SDK" optional = false python-versions = ">=3.10" groups = ["main"] files = [ - {file = "google_genai-1.53.0-py3-none-any.whl", hash = "sha256:65a3f99e5c03c372d872cda7419f5940e723374bb12a2f3ffd5e3e56e8eb2094"}, - {file = "google_genai-1.53.0.tar.gz", hash = "sha256:938a26d22f3fd32c6eeeb4276ef204ef82884e63af9842ce3eac05ceb39cbd8d"}, + {file = "google_genai-1.56.0-py3-none-any.whl", hash = "sha256:9e6b11e0c105ead229368cb5849a480e4d0185519f8d9f538d61ecfcf193b052"}, + {file = "google_genai-1.56.0.tar.gz", hash = "sha256:0491af33c375f099777ae207d9621f044e27091fafad4c50e617eba32165e82f"}, ] [package.dependencies] anyio = ">=4.8.0,<5.0.0" -google-auth = {version = ">=2.14.1,<3.0.0", extras = ["requests"]} +distro = ">=1.7.0,<2" +google-auth = {version = ">=2.45.0,<3.0.0", extras = ["requests"]} httpx = ">=0.28.1,<1.0.0" pydantic = ">=2.9.0,<3.0.0" requests = ">=2.28.1,<3.0.0" +sniffio = "*" tenacity = ">=8.2.3,<9.2.0" typing-extensions = ">=4.11.0,<5.0.0" websockets = ">=13.0.0,<15.1.0" @@ -2039,13 +2264,15 @@ numpy = ">=1.7" [[package]] name = "graphistry" -version = "0.46.1+17.g643744ee" +version = "0.48.0" description = "A visual graph analytics library for extracting, transforming, displaying, and sharing big graphs with end-to-end GPU acceleration" optional = false python-versions = ">=3.8" groups = ["main"] -files = [] -develop = false +files = [ + {file = "graphistry-0.48.0-py3-none-any.whl", hash = "sha256:faab74f65abd58cea1fb952e609974d9db3f0730ece532a9e3225d543c341290"}, + {file = "graphistry-0.48.0.tar.gz", hash = "sha256:173c85b455d60a31594385ad3d5f55ce7fbd73c783a4a32f7c7fe57f3a22616d"}, +] [package.dependencies] numpy = "*" @@ -2059,11 +2286,11 @@ squarify = "*" typing-extensions = "*" [package.extras] -ai = ["faiss-cpu", "joblib", "scikit-learn", "scipy", "scipy", "sentence-transformers", "skrub", "torch", "umap-learn"] +ai = ["faiss-cpu", "joblib", "scikit-learn", "scipy", "sentence-transformers", "skrub", "torch", "umap-learn"] all = ["azure-identity", "azure-kusto-data", "cudf-cu12 (==24.12.*)", "cugraph-cu12 (==24.12.*)", "cuml-cu12 (==24.12.*)", "dask-cudf-cu12 (==24.12.*)", "dgl (==2.1.0)", "faiss-cpu", "google-cloud-spanner", "gremlinpython", "igraph", "ipython", "joblib", "neo4j", "neotime", "networkx (>=2.5)", "nx-cugraph-cu12 (==24.12.*)", "openpyxl (==3.1.0)", "pygraphviz", "scikit-learn", "scipy", "sentence-transformers", "skrub", "torch", "torch (==2.0.1)", "torchdata (==0.6.1)", "umap-learn", "xlrd"] bolt = ["neo4j", "neotime"] build = ["build"] -dev = ["Jinja2 (==3.1.4)", "azure-identity", "azure-kusto-data", "build", "docutils (==0.21.2)", "flake8 (>=5.0)", "google-cloud-spanner", "gremlinpython", "hypothesis", "igraph", "ipykernel (==6.29.5)", "ipython", "ipython", "ipython (==8.28)", "mock", "mypy", "myst-parser (==4.0.0)", "nbsphinx (==0.9.5)", "neo4j", "neotime", "networkx (>=2.5)", "numba (>=0.57.1)", "openpyxl (==3.1.0)", "pandas-stubs", "pygments (>2.10)", "pytest", "pytest-xdist", "rstcheck[sphinx] (>=6.0)", "sphinx (==8.0.2)", "sphinx-book-theme (==1.1.3)", "sphinx-copybutton (==0.5.2)", "types-requests", "types-tqdm", "xlrd"] +dev = ["Jinja2 (==3.1.4)", "azure-identity", "azure-kusto-data", "build", "docutils (==0.21.2)", "flake8 (>=5.0)", "google-cloud-spanner", "gremlinpython", "hypothesis", "igraph", "ipykernel (==6.29.5)", "ipython", "ipython (==8.28)", "mock", "mypy", "myst-parser (==4.0.0)", "nbsphinx (==0.9.5)", "neo4j", "neotime", "networkx (>=2.5)", "numba (>=0.57.1)", "openpyxl (==3.1.0)", "pandas-stubs", "pygments (>2.10)", "pytest", "pytest-xdist", "rstcheck[sphinx] (>=6.0)", "sphinx (==8.0.2)", "sphinx-book-theme (==1.1.3)", "sphinx-copybutton (==0.5.2)", "types-requests", "types-tqdm", "xlrd"] dgl-cpu = ["dgl (==2.1.0)", "torch (==2.0.1)", "torchdata (==0.6.1)"] dgl-gpu = ["dgl-cu12 (==2.4.0)", "torch (==2.4.1)"] docs = ["Jinja2 (==3.1.4)", "docutils (==0.21.2)", "ipykernel (==6.29.5)", "ipython (==8.28)", "myst-parser (==4.0.0)", "nbsphinx (==0.9.5)", "pygments (>2.10)", "rstcheck[sphinx] (>=6.0)", "sphinx (==8.0.2)", "sphinx-book-theme (==1.1.3)", "sphinx-copybutton (==0.5.2)"] @@ -2080,12 +2307,6 @@ test = ["flake8 (>=5.0)", "hypothesis", "ipython", "mock", "mypy", "pandas-stubs testai = ["numba (>=0.57.1)"] umap-learn = ["scikit-learn", "scipy", "skrub", "umap-learn"] -[package.source] -type = "git" -url = "https://github.com/graphistry/pygraphistry.git" -reference = "refs/pull/868/head" -resolved_reference = "643744eeceece0e6d5a84fe4d255988cc4fe8855" - [[package]] name = "greenlet" version = "3.3.0" @@ -2168,14 +2389,14 @@ pypi = ["pip (>=24.0)", "platformdirs (>=4.2)", "wheel (>=0.42)"] [[package]] name = "groq" -version = "0.37.1" +version = "1.0.0" description = "The official Python library for the groq API" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "groq-0.37.1-py3-none-any.whl", hash = "sha256:b49f8c8898c55eaec9f71f1342f3fcacc9560d67a08ce5f35fbfb84e8dacd3da"}, - {file = "groq-0.37.1.tar.gz", hash = "sha256:7353d6dfb60834fd7aacbb86af106e2dc2aeaff6d0edd65fb2fd0f16bd39314c"}, + {file = "groq-1.0.0-py3-none-any.whl", hash = "sha256:6e22bf92ffad988f01d2d4df7729add66b8fd5dbfb2154b5bbf3af245b72c731"}, + {file = "groq-1.0.0.tar.gz", hash = "sha256:66cb7bb729e6eb644daac7ce8efe945e99e4eb33657f733ee6f13059ef0c25a9"}, ] [package.dependencies] @@ -2302,7 +2523,7 @@ description = "Fast transfer of large files with the Hugging Face Hub." optional = false python-versions = ">=3.8" groups = ["main"] -markers = "platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"arm64\" or platform_machine == \"aarch64\"" +markers = "platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"arm64\" or platform_machine == \"aarch64\"" files = [ {file = "hf_xet-1.2.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:ceeefcd1b7aed4956ae8499e2199607765fbd1c60510752003b6cc0b8413b649"}, {file = "hf_xet-1.2.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b70218dd548e9840224df5638fdc94bd033552963cfa97f9170829381179c813"}, @@ -2392,42 +2613,39 @@ files = [ [[package]] name = "huggingface-hub" -version = "0.36.0" +version = "1.2.3" description = "Client library to download and publish models, datasets and other repos on the huggingface.co hub" optional = false -python-versions = ">=3.8.0" +python-versions = ">=3.9.0" groups = ["main"] files = [ - {file = "huggingface_hub-0.36.0-py3-none-any.whl", hash = "sha256:7bcc9ad17d5b3f07b57c78e79d527102d08313caa278a641993acddcb894548d"}, - {file = "huggingface_hub-0.36.0.tar.gz", hash = "sha256:47b3f0e2539c39bf5cde015d63b72ec49baff67b6931c3d97f3f84532e2b8d25"}, + {file = "huggingface_hub-1.2.3-py3-none-any.whl", hash = "sha256:c9b7a91a9eedaa2149cdc12bdd8f5a11780e10de1f1024718becf9e41e5a4642"}, + {file = "huggingface_hub-1.2.3.tar.gz", hash = "sha256:4ba57f17004fd27bb176a6b7107df579865d4cde015112db59184c51f5602ba7"}, ] [package.dependencies] filelock = "*" fsspec = ">=2023.5.0" -hf-xet = {version = ">=1.1.3,<2.0.0", markers = "platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"arm64\" or platform_machine == \"aarch64\""} +hf-xet = {version = ">=1.2.0,<2.0.0", markers = "platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"arm64\" or platform_machine == \"aarch64\""} +httpx = ">=0.23.0,<1" packaging = ">=20.9" pyyaml = ">=5.1" -requests = "*" +shellingham = "*" tqdm = ">=4.42.1" +typer-slim = "*" typing-extensions = ">=3.7.4.3" [package.extras] -all = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "authlib (>=1.3.2)", "fastapi", "gradio (>=4.0.0)", "httpx", "itsdangerous", "jedi", "libcst (>=1.4.0)", "mypy (==1.15.0) ; python_version >= \"3.9\"", "mypy (>=1.14.1,<1.15.0) ; python_version == \"3.8\"", "numpy", "pytest (>=8.1.1,<8.2.2)", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-mock", "pytest-rerunfailures (<16.0)", "pytest-vcr", "pytest-xdist", "ruff (>=0.9.0)", "soundfile", "ty", "types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)", "urllib3 (<2.0)"] -cli = ["InquirerPy (==0.3.4)"] -dev = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "authlib (>=1.3.2)", "fastapi", "gradio (>=4.0.0)", "httpx", "itsdangerous", "jedi", "libcst (>=1.4.0)", "mypy (==1.15.0) ; python_version >= \"3.9\"", "mypy (>=1.14.1,<1.15.0) ; python_version == \"3.8\"", "numpy", "pytest (>=8.1.1,<8.2.2)", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-mock", "pytest-rerunfailures (<16.0)", "pytest-vcr", "pytest-xdist", "ruff (>=0.9.0)", "soundfile", "ty", "types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)", "urllib3 (<2.0)"] +all = ["Jinja2", "Pillow", "authlib (>=1.3.2)", "fastapi", "fastapi", "httpx", "itsdangerous", "jedi", "libcst (>=1.4.0)", "mypy (==1.15.0)", "numpy", "pytest (>=8.4.2)", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-mock", "pytest-rerunfailures (<16.0)", "pytest-vcr", "pytest-xdist", "ruff (>=0.9.0)", "soundfile", "ty", "types-PyYAML", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)", "urllib3 (<2.0)"] +dev = ["Jinja2", "Pillow", "authlib (>=1.3.2)", "fastapi", "fastapi", "httpx", "itsdangerous", "jedi", "libcst (>=1.4.0)", "mypy (==1.15.0)", "numpy", "pytest (>=8.4.2)", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-mock", "pytest-rerunfailures (<16.0)", "pytest-vcr", "pytest-xdist", "ruff (>=0.9.0)", "soundfile", "ty", "types-PyYAML", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)", "urllib3 (<2.0)"] fastai = ["fastai (>=2.4)", "fastcore (>=1.3.27)", "toml"] -hf-transfer = ["hf-transfer (>=0.1.4)"] -hf-xet = ["hf-xet (>=1.1.2,<2.0.0)"] -inference = ["aiohttp"] -mcp = ["aiohttp", "mcp (>=1.8.0)", "typer"] +hf-xet = ["hf-xet (>=1.1.3,<2.0.0)"] +mcp = ["mcp (>=1.8.0)"] oauth = ["authlib (>=1.3.2)", "fastapi", "httpx", "itsdangerous"] -quality = ["libcst (>=1.4.0)", "mypy (==1.15.0) ; python_version >= \"3.9\"", "mypy (>=1.14.1,<1.15.0) ; python_version == \"3.8\"", "ruff (>=0.9.0)", "ty"] -tensorflow = ["graphviz", "pydot", "tensorflow"] -tensorflow-testing = ["keras (<3.0)", "tensorflow"] -testing = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "authlib (>=1.3.2)", "fastapi", "gradio (>=4.0.0)", "httpx", "itsdangerous", "jedi", "numpy", "pytest (>=8.1.1,<8.2.2)", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-mock", "pytest-rerunfailures (<16.0)", "pytest-vcr", "pytest-xdist", "soundfile", "urllib3 (<2.0)"] +quality = ["libcst (>=1.4.0)", "mypy (==1.15.0)", "ruff (>=0.9.0)", "ty"] +testing = ["Jinja2", "Pillow", "authlib (>=1.3.2)", "fastapi", "fastapi", "httpx", "itsdangerous", "jedi", "numpy", "pytest (>=8.4.2)", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-mock", "pytest-rerunfailures (<16.0)", "pytest-vcr", "pytest-xdist", "soundfile", "urllib3 (<2.0)"] torch = ["safetensors[torch]", "torch"] -typing = ["types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)"] +typing = ["types-PyYAML", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)"] [[package]] name = "hyperlink" @@ -2674,14 +2892,14 @@ colors = ["colorama (>=0.4.6)"] [[package]] name = "itemadapter" -version = "0.12.2" +version = "0.13.0" description = "Common interface for data container classes" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "itemadapter-0.12.2-py3-none-any.whl", hash = "sha256:17ff8acb169fb11dbed8af83e805c19c3b890bde4653761b4d3c1544142e04b6"}, - {file = "itemadapter-0.12.2.tar.gz", hash = "sha256:8e05c07cea966a7a8c4f096150ee2c91d9b4104a76f9afd029b235e1b564a61f"}, + {file = "itemadapter-0.13.0-py3-none-any.whl", hash = "sha256:6ddb8deef8fa96551f010cd0d90acb9be4e002f7e9431e39b6eea003dffe3b10"}, + {file = "itemadapter-0.13.0.tar.gz", hash = "sha256:bed9ef00e3bcb04f0c8478f11fdebe63ebf45bacffe802719dfb070e969d2062"}, ] [package.extras] @@ -2957,14 +3175,14 @@ files = [ [[package]] name = "joblib" -version = "1.5.2" +version = "1.5.3" description = "Lightweight pipelining with Python functions" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "joblib-1.5.2-py3-none-any.whl", hash = "sha256:4e1f0bdbb987e6d843c70cf43714cb276623def372df3c22fe5266b2670bc241"}, - {file = "joblib-1.5.2.tar.gz", hash = "sha256:3faa5c39054b2f03ca547da9b2f52fde67c06240c31853f306aea97f13647b55"}, + {file = "joblib-1.5.3-py3-none-any.whl", hash = "sha256:5fc3c5039fc5ca8c0276333a188bbd59d6b7ab37fe6632daa76bc7f9ec18e713"}, + {file = "joblib-1.5.3.tar.gz", hash = "sha256:8561a3269e6801106863fd0d6d84bb737be9e7631e33aaed3fb9ce5953688da3"}, ] [[package]] @@ -3040,6 +3258,24 @@ webcolors = {version = ">=24.6.0", optional = true, markers = "extra == \"format format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"] format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "rfc3987-syntax (>=1.1.0)", "uri-template", "webcolors (>=24.6.0)"] +[[package]] +name = "jsonschema-path" +version = "0.3.4" +description = "JSONSchema Spec with object-oriented paths" +optional = false +python-versions = "<4.0.0,>=3.8.0" +groups = ["main"] +files = [ + {file = "jsonschema_path-0.3.4-py3-none-any.whl", hash = "sha256:f502191fdc2b22050f9a81c9237be9d27145b9001c55842bece5e94e382e52f8"}, + {file = "jsonschema_path-0.3.4.tar.gz", hash = "sha256:8365356039f16cc65fddffafda5f58766e34bebab7d6d105616ab52bc4297001"}, +] + +[package.dependencies] +pathable = ">=0.4.1,<0.5.0" +PyYAML = ">=5.1" +referencing = "<0.37.0" +requests = ">=2.31.0,<3.0.0" + [[package]] name = "jsonschema-specifications" version = "2025.9.1" @@ -3096,26 +3332,26 @@ test = ["flake8 (==7)", "flake8-docstrings (==1.7)", "jupyterlab (>=4)", "mypy ( [[package]] name = "jupyter-client" -version = "8.6.3" +version = "8.7.0" description = "Jupyter protocol implementation and client libraries" optional = false -python-versions = ">=3.8" +python-versions = ">=3.10" groups = ["dev"] files = [ - {file = "jupyter_client-8.6.3-py3-none-any.whl", hash = "sha256:e8a19cc986cc45905ac3362915f410f3af85424b4c0905e94fa5f2cb08e8f23f"}, - {file = "jupyter_client-8.6.3.tar.gz", hash = "sha256:35b3a0947c4a6e9d589eb97d7d4cd5e90f910ee73101611f01283732bd6d9419"}, + {file = "jupyter_client-8.7.0-py3-none-any.whl", hash = "sha256:3671a94fd25e62f5f2f554f5e95389c2294d89822378a5f2dd24353e1494a9e0"}, + {file = "jupyter_client-8.7.0.tar.gz", hash = "sha256:3357212d9cbe01209e59190f67a3a7e1f387a4f4e88d1e0433ad84d7b262531d"}, ] [package.dependencies] -jupyter-core = ">=4.12,<5.0.dev0 || >=5.1.dev0" +jupyter-core = ">=5.1" python-dateutil = ">=2.8.2" -pyzmq = ">=23.0" -tornado = ">=6.2" +pyzmq = ">=25.0" +tornado = ">=6.4.1" traitlets = ">=5.3" [package.extras] docs = ["ipykernel", "myst-parser", "pydata-sphinx-theme", "sphinx (>=4)", "sphinx-autodoc-typehints", "sphinxcontrib-github-alt", "sphinxcontrib-spelling"] -test = ["coverage", "ipykernel (>=6.14)", "mypy", "paramiko ; sys_platform == \"win32\"", "pre-commit", "pytest (<8.2.0)", "pytest-cov", "pytest-jupyter[client] (>=0.4.1)", "pytest-timeout"] +test = ["anyio", "coverage", "ipykernel (>=6.14)", "mypy", "paramiko ; sys_platform == \"win32\"", "pre-commit", "pytest", "pytest-cov", "pytest-jupyter[client] (>=0.6.2)", "pytest-timeout"] [[package]] name = "jupyter-console" @@ -3280,14 +3516,14 @@ test = ["jupyter-server (>=2.0.0)", "pytest (>=7.0)", "pytest-jupyter[server] (> [[package]] name = "jupyterlab" -version = "4.5.0" +version = "4.5.1" description = "JupyterLab computational environment" optional = false python-versions = ">=3.9" groups = ["dev"] files = [ - {file = "jupyterlab-4.5.0-py3-none-any.whl", hash = "sha256:88e157c75c1afff64c7dc4b801ec471450b922a4eae4305211ddd40da8201c8a"}, - {file = "jupyterlab-4.5.0.tar.gz", hash = "sha256:aec33d6d8f1225b495ee2cf20f0514f45e6df8e360bdd7ac9bace0b7ac5177ea"}, + {file = "jupyterlab-4.5.1-py3-none-any.whl", hash = "sha256:31b059de96de0754ff1f2ce6279774b6aab8c34d7082e9752db58207c99bd514"}, + {file = "jupyterlab-4.5.1.tar.gz", hash = "sha256:09da1ddfbd9eec18b5101dbb8515612aa1e47443321fb99503725a88e93d20d9"}, ] [package.dependencies] @@ -3653,14 +3889,14 @@ langchain-core = ">=0.3.75,<2.0.0" [[package]] name = "langsmith" -version = "0.4.56" +version = "0.5.0" description = "Client library to connect to the LangSmith Observability and Evaluation Platform." optional = false python-versions = ">=3.10" groups = ["main"] files = [ - {file = "langsmith-0.4.56-py3-none-any.whl", hash = "sha256:f2c61d3f10210e78f16f77e3115f407d40f562ab00ac8c76927c7dd55b5c17b2"}, - {file = "langsmith-0.4.56.tar.gz", hash = "sha256:c3dc53509972689dbbc24f9ac92a095dcce00f76bb0db03ae385815945572540"}, + {file = "langsmith-0.5.0-py3-none-any.whl", hash = "sha256:a83750cb3dccb33148d4ffe005e3e03080fad13e01671efbb74c9a68813bfef8"}, + {file = "langsmith-0.5.0.tar.gz", hash = "sha256:5cadf1ddd30e838cf61679f4a776aaef638d4b02ffbceba9f73283caebd39e1b"}, ] [package.dependencies] @@ -3699,6 +3935,93 @@ interegular = ["interegular (>=0.3.1,<0.4.0)"] nearley = ["js2py"] regex = ["regex"] +[[package]] +name = "librt" +version = "0.7.4" +description = "Mypyc runtime library" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +markers = "platform_python_implementation != \"PyPy\"" +files = [ + {file = "librt-0.7.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:dc300cb5a5a01947b1ee8099233156fdccd5001739e5f596ecfbc0dab07b5a3b"}, + {file = "librt-0.7.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ee8d3323d921e0f6919918a97f9b5445a7dfe647270b2629ec1008aa676c0bc0"}, + {file = "librt-0.7.4-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:95cb80854a355b284c55f79674f6187cc9574df4dc362524e0cce98c89ee8331"}, + {file = "librt-0.7.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3ca1caedf8331d8ad6027f93b52d68ed8f8009f5c420c246a46fe9d3be06be0f"}, + {file = "librt-0.7.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c2a6f1236151e6fe1da289351b5b5bce49651c91554ecc7b70a947bced6fe212"}, + {file = "librt-0.7.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7766b57aeebaf3f1dac14fdd4a75c9a61f2ed56d8ebeefe4189db1cb9d2a3783"}, + {file = "librt-0.7.4-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:1c4c89fb01157dd0a3bfe9e75cd6253b0a1678922befcd664eca0772a4c6c979"}, + {file = "librt-0.7.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f7fa8beef580091c02b4fd26542de046b2abfe0aaefa02e8bcf68acb7618f2b3"}, + {file = "librt-0.7.4-cp310-cp310-win32.whl", hash = "sha256:543c42fa242faae0466fe72d297976f3c710a357a219b1efde3a0539a68a6997"}, + {file = "librt-0.7.4-cp310-cp310-win_amd64.whl", hash = "sha256:25cc40d8eb63f0a7ea4c8f49f524989b9df901969cb860a2bc0e4bad4b8cb8a8"}, + {file = "librt-0.7.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3485b9bb7dfa66167d5500ffdafdc35415b45f0da06c75eb7df131f3357b174a"}, + {file = "librt-0.7.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:188b4b1a770f7f95ea035d5bbb9d7367248fc9d12321deef78a269ebf46a5729"}, + {file = "librt-0.7.4-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:1b668b1c840183e4e38ed5a99f62fac44c3a3eef16870f7f17cfdfb8b47550ed"}, + {file = "librt-0.7.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0e8f864b521f6cfedb314d171630f827efee08f5c3462bcbc2244ab8e1768cd6"}, + {file = "librt-0.7.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4df7c9def4fc619a9c2ab402d73a0c5b53899abe090e0100323b13ccb5a3dd82"}, + {file = "librt-0.7.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:f79bc3595b6ed159a1bf0cdc70ed6ebec393a874565cab7088a219cca14da727"}, + {file = "librt-0.7.4-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:77772a4b8b5f77d47d883846928c36d730b6e612a6388c74cba33ad9eb149c11"}, + {file = "librt-0.7.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:064a286e6ab0b4c900e228ab4fa9cb3811b4b83d3e0cc5cd816b2d0f548cb61c"}, + {file = "librt-0.7.4-cp311-cp311-win32.whl", hash = "sha256:42da201c47c77b6cc91fc17e0e2b330154428d35d6024f3278aa2683e7e2daf2"}, + {file = "librt-0.7.4-cp311-cp311-win_amd64.whl", hash = "sha256:d31acb5886c16ae1711741f22504195af46edec8315fe69b77e477682a87a83e"}, + {file = "librt-0.7.4-cp311-cp311-win_arm64.whl", hash = "sha256:114722f35093da080a333b3834fff04ef43147577ed99dd4db574b03a5f7d170"}, + {file = "librt-0.7.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7dd3b5c37e0fb6666c27cf4e2c88ae43da904f2155c4cfc1e5a2fdce3b9fcf92"}, + {file = "librt-0.7.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a9c5de1928c486201b23ed0cc4ac92e6e07be5cd7f3abc57c88a9cf4f0f32108"}, + {file = "librt-0.7.4-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:078ae52ffb3f036396cc4aed558e5b61faedd504a3c1f62b8ae34bf95ae39d94"}, + {file = "librt-0.7.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ce58420e25097b2fc201aef9b9f6d65df1eb8438e51154e1a7feb8847e4a55ab"}, + {file = "librt-0.7.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b719c8730c02a606dc0e8413287e8e94ac2d32a51153b300baf1f62347858fba"}, + {file = "librt-0.7.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3749ef74c170809e6dee68addec9d2458700a8de703de081c888e92a8b015cf9"}, + {file = "librt-0.7.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b35c63f557653c05b5b1b6559a074dbabe0afee28ee2a05b6c9ba21ad0d16a74"}, + {file = "librt-0.7.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1ef704e01cb6ad39ad7af668d51677557ca7e5d377663286f0ee1b6b27c28e5f"}, + {file = "librt-0.7.4-cp312-cp312-win32.whl", hash = "sha256:c66c2b245926ec15188aead25d395091cb5c9df008d3b3207268cd65557d6286"}, + {file = "librt-0.7.4-cp312-cp312-win_amd64.whl", hash = "sha256:71a56f4671f7ff723451f26a6131754d7c1809e04e22ebfbac1db8c9e6767a20"}, + {file = "librt-0.7.4-cp312-cp312-win_arm64.whl", hash = "sha256:419eea245e7ec0fe664eb7e85e7ff97dcdb2513ca4f6b45a8ec4a3346904f95a"}, + {file = "librt-0.7.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d44a1b1ba44cbd2fc3cb77992bef6d6fdb1028849824e1dd5e4d746e1f7f7f0b"}, + {file = "librt-0.7.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c9cab4b3de1f55e6c30a84c8cee20e4d3b2476f4d547256694a1b0163da4fe32"}, + {file = "librt-0.7.4-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:2857c875f1edd1feef3c371fbf830a61b632fb4d1e57160bb1e6a3206e6abe67"}, + {file = "librt-0.7.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b370a77be0a16e1ad0270822c12c21462dc40496e891d3b0caf1617c8cc57e20"}, + {file = "librt-0.7.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d05acd46b9a52087bfc50c59dfdf96a2c480a601e8898a44821c7fd676598f74"}, + {file = "librt-0.7.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:70969229cb23d9c1a80e14225838d56e464dc71fa34c8342c954fc50e7516dee"}, + {file = "librt-0.7.4-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4450c354b89dbb266730893862dbff06006c9ed5b06b6016d529b2bf644fc681"}, + {file = "librt-0.7.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:adefe0d48ad35b90b6f361f6ff5a1bd95af80c17d18619c093c60a20e7a5b60c"}, + {file = "librt-0.7.4-cp313-cp313-win32.whl", hash = "sha256:21ea710e96c1e050635700695095962a22ea420d4b3755a25e4909f2172b4ff2"}, + {file = "librt-0.7.4-cp313-cp313-win_amd64.whl", hash = "sha256:772e18696cf5a64afee908662fbcb1f907460ddc851336ee3a848ef7684c8e1e"}, + {file = "librt-0.7.4-cp313-cp313-win_arm64.whl", hash = "sha256:52e34c6af84e12921748c8354aa6acf1912ca98ba60cdaa6920e34793f1a0788"}, + {file = "librt-0.7.4-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:4f1ee004942eaaed6e06c087d93ebc1c67e9a293e5f6b9b5da558df6bf23dc5d"}, + {file = "librt-0.7.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:d854c6dc0f689bad7ed452d2a3ecff58029d80612d336a45b62c35e917f42d23"}, + {file = "librt-0.7.4-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:a4f7339d9e445280f23d63dea842c0c77379c4a47471c538fc8feedab9d8d063"}, + {file = "librt-0.7.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:39003fc73f925e684f8521b2dbf34f61a5deb8a20a15dcf53e0d823190ce8848"}, + {file = "librt-0.7.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6bb15ee29d95875ad697d449fe6071b67f730f15a6961913a2b0205015ca0843"}, + {file = "librt-0.7.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:02a69369862099e37d00765583052a99d6a68af7e19b887e1b78fee0146b755a"}, + {file = "librt-0.7.4-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ec72342cc4d62f38b25a94e28b9efefce41839aecdecf5e9627473ed04b7be16"}, + {file = "librt-0.7.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:776dbb9bfa0fc5ce64234b446995d8d9f04badf64f544ca036bd6cff6f0732ce"}, + {file = "librt-0.7.4-cp314-cp314-win32.whl", hash = "sha256:0f8cac84196d0ffcadf8469d9ded4d4e3a8b1c666095c2a291e22bf58e1e8a9f"}, + {file = "librt-0.7.4-cp314-cp314-win_amd64.whl", hash = "sha256:037f5cb6fe5abe23f1dc058054d50e9699fcc90d0677eee4e4f74a8677636a1a"}, + {file = "librt-0.7.4-cp314-cp314-win_arm64.whl", hash = "sha256:a5deebb53d7a4d7e2e758a96befcd8edaaca0633ae71857995a0f16033289e44"}, + {file = "librt-0.7.4-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:b4c25312c7f4e6ab35ab16211bdf819e6e4eddcba3b2ea632fb51c9a2a97e105"}, + {file = "librt-0.7.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:618b7459bb392bdf373f2327e477597fff8f9e6a1878fffc1b711c013d1b0da4"}, + {file = "librt-0.7.4-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:1437c3f72a30c7047f16fd3e972ea58b90172c3c6ca309645c1c68984f05526a"}, + {file = "librt-0.7.4-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c96cb76f055b33308f6858b9b594618f1b46e147a4d03a4d7f0c449e304b9b95"}, + {file = "librt-0.7.4-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:28f990e6821204f516d09dc39966ef8b84556ffd648d5926c9a3f681e8de8906"}, + {file = "librt-0.7.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:bc4aebecc79781a1b77d7d4e7d9fe080385a439e198d993b557b60f9117addaf"}, + {file = "librt-0.7.4-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:022cc673e69283a42621dd453e2407cf1647e77f8bd857d7ad7499901e62376f"}, + {file = "librt-0.7.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:2b3ca211ae8ea540569e9c513da052699b7b06928dcda61247cb4f318122bdb5"}, + {file = "librt-0.7.4-cp314-cp314t-win32.whl", hash = "sha256:8a461f6456981d8c8e971ff5a55f2e34f4e60871e665d2f5fde23ee74dea4eeb"}, + {file = "librt-0.7.4-cp314-cp314t-win_amd64.whl", hash = "sha256:721a7b125a817d60bf4924e1eec2a7867bfcf64cfc333045de1df7a0629e4481"}, + {file = "librt-0.7.4-cp314-cp314t-win_arm64.whl", hash = "sha256:76b2ba71265c0102d11458879b4d53ccd0b32b0164d14deb8d2b598a018e502f"}, + {file = "librt-0.7.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6fc4aa67fedd827a601f97f0e61cc72711d0a9165f2c518e9a7c38fc1568b9ad"}, + {file = "librt-0.7.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e710c983d29d9cc4da29113b323647db286eaf384746344f4a233708cca1a82c"}, + {file = "librt-0.7.4-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:43a2515a33f2bc17b15f7fb49ff6426e49cb1d5b2539bc7f8126b9c5c7f37164"}, + {file = "librt-0.7.4-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0fd766bb9ace3498f6b93d32f30c0e7c8ce6b727fecbc84d28160e217bb66254"}, + {file = "librt-0.7.4-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ce1b44091355b68cffd16e2abac07c1cafa953fa935852d3a4dd8975044ca3bf"}, + {file = "librt-0.7.4-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:5a72b905420c4bb2c10c87b5c09fe6faf4a76d64730e3802feef255e43dfbf5a"}, + {file = "librt-0.7.4-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:07c4d7c9305e75a0edd3427b79c7bd1d019cd7eddaa7c89dbb10e0c7946bffbb"}, + {file = "librt-0.7.4-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:2e734c2c54423c6dcc77f58a8585ba83b9f72e422f9edf09cab1096d4a4bdc82"}, + {file = "librt-0.7.4-cp39-cp39-win32.whl", hash = "sha256:a34ae11315d4e26326aaf04e21ccd8d9b7de983635fba38d73e203a9c8e3fe3d"}, + {file = "librt-0.7.4-cp39-cp39-win_amd64.whl", hash = "sha256:7e4b5ffa1614ad4f32237d739699be444be28de95071bfa4e66a8da9fa777798"}, + {file = "librt-0.7.4.tar.gz", hash = "sha256:3871af56c59864d5fd21d1ac001eb2fb3b140d52ba0454720f2e4a19812404ba"}, +] + [[package]] name = "logfire-api" version = "4.16.0" @@ -3711,155 +4034,272 @@ files = [ {file = "logfire_api-4.16.0.tar.gz", hash = "sha256:0efa62f5e73abdea670b5e9384c841b544474207110a089536a0fa8704f9e386"}, ] +[[package]] +name = "loguru" +version = "0.7.3" +description = "Python logging made (stupidly) simple" +optional = false +python-versions = "<4.0,>=3.5" +groups = ["main"] +files = [ + {file = "loguru-0.7.3-py3-none-any.whl", hash = "sha256:31a33c10c8e1e10422bfd431aeb5d351c7cf7fa671e3c4df004162264b28220c"}, + {file = "loguru-0.7.3.tar.gz", hash = "sha256:19480589e77d47b8d85b2c827ad95d49bf31b0dcde16593892eb51dd18706eb6"}, +] + +[package.dependencies] +colorama = {version = ">=0.3.4", markers = "sys_platform == \"win32\""} +win32-setctime = {version = ">=1.0.0", markers = "sys_platform == \"win32\""} + +[package.extras] +dev = ["Sphinx (==8.1.3) ; python_version >= \"3.11\"", "build (==1.2.2) ; python_version >= \"3.11\"", "colorama (==0.4.5) ; python_version < \"3.8\"", "colorama (==0.4.6) ; python_version >= \"3.8\"", "exceptiongroup (==1.1.3) ; python_version >= \"3.7\" and python_version < \"3.11\"", "freezegun (==1.1.0) ; python_version < \"3.8\"", "freezegun (==1.5.0) ; python_version >= \"3.8\"", "mypy (==v0.910) ; python_version < \"3.6\"", "mypy (==v0.971) ; python_version == \"3.6\"", "mypy (==v1.13.0) ; python_version >= \"3.8\"", "mypy (==v1.4.1) ; python_version == \"3.7\"", "myst-parser (==4.0.0) ; python_version >= \"3.11\"", "pre-commit (==4.0.1) ; python_version >= \"3.9\"", "pytest (==6.1.2) ; python_version < \"3.8\"", "pytest (==8.3.2) ; python_version >= \"3.8\"", "pytest-cov (==2.12.1) ; python_version < \"3.8\"", "pytest-cov (==5.0.0) ; python_version == \"3.8\"", "pytest-cov (==6.0.0) ; python_version >= \"3.9\"", "pytest-mypy-plugins (==1.9.3) ; python_version >= \"3.6\" and python_version < \"3.8\"", "pytest-mypy-plugins (==3.1.0) ; python_version >= \"3.8\"", "sphinx-rtd-theme (==3.0.2) ; python_version >= \"3.11\"", "tox (==3.27.1) ; python_version < \"3.8\"", "tox (==4.23.2) ; python_version >= \"3.8\"", "twine (==6.0.1) ; python_version >= \"3.11\""] + [[package]] name = "loro" -version = "1.10.0" +version = "1.10.3" description = "Python bindings for [Loro](https://loro.dev)" optional = false python-versions = ">=3.8" groups = ["main"] files = [ - {file = "loro-1.10.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:42f6bc74223c8025ed77229472d9532133b6c288ca93c9f8918c68e84f88534f"}, - {file = "loro-1.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8c1a2c6eed4fcf5ccb644da0701bd2d5c667db6d7e04dab6f371324a78d90186"}, - {file = "loro-1.10.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eb46c00ac3a8331dbcfeca36406fb01971886260e4879b549717407dec29d65b"}, - {file = "loro-1.10.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9668a6af22672042fac2306c54471d8c08e011502fa5337f18ca0432d002ec86"}, - {file = "loro-1.10.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:30b490cdaca73cc491e19ac5ba25bd3a88150b0b6334f9f29074b44430836260"}, - {file = "loro-1.10.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3eeae69a9ba4f59320999beaf07f34f14657f00e7da322327beb1491ad6891fd"}, - {file = "loro-1.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a0c051a07d1faaa36e51b3af3897a3187e2a66a1b89aed4e676c8e217cd86aa"}, - {file = "loro-1.10.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8d642c7632237278c0ae5942d0a90e2530cbb4da337e357fe681a2dc5ccc539d"}, - {file = "loro-1.10.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b8285c868d6fd62a23459bd2c5cfe6ed0d260f8b0e373ff48b7ae03d7a556678"}, - {file = "loro-1.10.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:d8a639af0cd3dc2be2376d0cbef1bc836ac71aecd7a8abdef9b3701689af89b1"}, - {file = "loro-1.10.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:80143ee0ef62fa21b63b7dadd4cea63bfeddcd6f27d3ba91b0bd9f48fe04ac00"}, - {file = "loro-1.10.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:4db4ffb4526baaa8c7dd414b55f0a37f591aa0f63f426eb929ff2a3d7077b291"}, - {file = "loro-1.10.0-cp310-cp310-win32.whl", hash = "sha256:f57e0fdc8267c770ef65eebc4ff8e8813d8387288a1d301fc7044c9c9b48d3f5"}, - {file = "loro-1.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:9df57d95776ee8cb05d0eff54dd8eae99c5cbee5ac045d60059bbabd9765fcb9"}, - {file = "loro-1.10.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:33a02304cd4f9189bd9941d28ce40d611013084e67aabef5f49937dc31133049"}, - {file = "loro-1.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7ab51aa1c59706bf6b3f5df0b5d5e8ea908812db9670acba8498beca2270d8c5"}, - {file = "loro-1.10.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09f03dde1916e755f67ecce5f3754754c5c96835aafb8767559587bc82b94134"}, - {file = "loro-1.10.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2fe35429b3551e1ec6014aaac5677ed8b07f44f88440a62a71c015d93cd52413"}, - {file = "loro-1.10.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c21f290efcdd6f3372e0fb5b986316a007e658819265e66abad57046fc2424fe"}, - {file = "loro-1.10.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:774186f9add840518e9cc96c66bd4194b508cc878bb458c607189a321101bdd6"}, - {file = "loro-1.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85c17c975b6dc17202fff6fd4141df86416097eb3e328f6fd8cea8191114b04f"}, - {file = "loro-1.10.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cdd9fee688e88485294378b44072bd03e37d69a9c6b599c0f81fa6873c743622"}, - {file = "loro-1.10.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0ba4dcdd74166b14faec47314c55a3b734246d150733fcb7c09dfa1cc80d33a5"}, - {file = "loro-1.10.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:59fbbdb2d2e28c9e1c22a03a9e91a5b8c9f7bffa173bfef2b043f0dfc74d386a"}, - {file = "loro-1.10.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5719c805e8b975c5aa2c531802f4522b57f8b7ddba47d57ab81a8dfabffb3586"}, - {file = "loro-1.10.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:54eae7e46401a60caeea10eb2a61765dab5aee1dc7b13124497fdb04e9a6867d"}, - {file = "loro-1.10.0-cp311-cp311-win32.whl", hash = "sha256:1419a0d1081ceee24e2538a4b9612ea385fd2e985d6f0215dc44dec46ab7fa10"}, - {file = "loro-1.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:4a18a1f1cc7adca30f9748cfb9cce62f494a1254a2ec768ed9f627ed72eb338e"}, - {file = "loro-1.10.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:54829ab9363df9d486124d582f2ad78548c827ee5fd922185dfc3d22565b894a"}, - {file = "loro-1.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:44a16dbddfc570efe4dceb905cb4201a42ea3e2422e1e256d5984a2cb0ea973e"}, - {file = "loro-1.10.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5eea7720074f8066d509a1650fda614b3a38188154bc61071c2b347bb5c7be7e"}, - {file = "loro-1.10.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1da783f8a7b8d947af34e7315ea174f5ed389062934078584b376e7498f09236"}, - {file = "loro-1.10.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d16e2f1f7dea07390f8e35e9314fcf42cf73e6b632a99b62f744aea42a92b1f5"}, - {file = "loro-1.10.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2cfa4c51a53d9ab02b85a0e85f05d888db4b00dc58b87933fbb4e30795904735"}, - {file = "loro-1.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:219f0a9478f50087f88040f7addd140276eb7e64e8b31172d5fbdebaa19aa9cf"}, - {file = "loro-1.10.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:31c8fa88ac01af1a7ab046139505066859619a2aa8059587dfe32787e80976b0"}, - {file = "loro-1.10.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:086c29e4baa654dd76a20f1f94375fff75a2ae78ad4fdc8dfb33badaafbb20be"}, - {file = "loro-1.10.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:5ec50b0c469c7fa858844baf17782481407a9ae8621d201f6eaacf72c7a4d341"}, - {file = "loro-1.10.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:2564d2f9db1ad63c7949504a92b3f54524dc359dd33b3d514a3cdcb16fda1f67"}, - {file = "loro-1.10.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:cd125f03f8f65e7b0036267bcedddbce1885c3b8df0438cd5fcc63c8633f3f0b"}, - {file = "loro-1.10.0-cp312-cp312-win32.whl", hash = "sha256:dd1af5db17b03997db85732c5eda52e07485763915670a95f06d312a6881cc24"}, - {file = "loro-1.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:99a4a73e20da4371205558c95f0824ecc749179b40530a01883dc79e80524507"}, - {file = "loro-1.10.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:2fc145e9289607cdbeb7239c1512a2885f59fcd4f3f5bee5b469932e6b038b8a"}, - {file = "loro-1.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2edf1d0aa634e1d42bdb4b5d6f0676c2f41282a32de1cbe487d324e905e74af6"}, - {file = "loro-1.10.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5547841355820fc02fd75a7b3dea49074f8b68bbb460e217fa1f83be5f6feb9"}, - {file = "loro-1.10.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:90e7fbdc8f07d64659a349d46317be695ee8d16d5d2db5b188f65e4cf4beb174"}, - {file = "loro-1.10.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a5d7ec8e93f5fcb3e9a8e15de3380d82df405d0f688d9bc49baa15468ebbc48e"}, - {file = "loro-1.10.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1a93d4c762fd40a45639e914649dcc55074faac126306ccf30144fd1982dcf9b"}, - {file = "loro-1.10.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:637e3c8cc607cb2a2758a08e1170edd79ab1197f180765dd23a87148339e71fa"}, - {file = "loro-1.10.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8fa5b9bf207d60b2d274308163a8cdbce24f84da4f7f135c5285aa73e6c95f1a"}, - {file = "loro-1.10.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a81748cf4e531676dcdca64415bae153b8f1243c7b6a7d007fdbd8f7bbbed193"}, - {file = "loro-1.10.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:cd5520f4242b07eb15141d10e37c29db289b64ff320fe2fe33d5c16f275403d9"}, - {file = "loro-1.10.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ec09ff920d67d1521588432fe2e55338f34e2a3d1ae6dbe80f900ccf352acb0d"}, - {file = "loro-1.10.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:963c1548437da17bd35b36ca0327dcc600df7aee539b40b843d5e62b9a7e8850"}, - {file = "loro-1.10.0-cp313-cp313-win32.whl", hash = "sha256:71eda3367ec74b229a56e2184257fe35a8a1677c78c0694bd1b70d60c8ca80e3"}, - {file = "loro-1.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:f3e2e65ce0c951c94e79e099e7ca4d91800a658c1b0b7b2fe335ea097c11c145"}, - {file = "loro-1.10.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b522e7e1b2ef2d426bf48cf7f8d076203315be2f372016a3639d51abfc825071"}, - {file = "loro-1.10.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:693b5633de66862729ce4a9bfdb14eae76b9da5de830176fcd45f70755c66f46"}, - {file = "loro-1.10.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:00b90ce6a9ee26a985aad472dff4157fa848368e5f26d66670c03bf1ec1159a1"}, - {file = "loro-1.10.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ba282008c5c1768623990010c244a29d4fffddb31aed7f8f6d4504637ed6c776"}, - {file = "loro-1.10.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:fe03cbeac73af2eb9805840ac9e16c7fc4382cb4be78b1784d814faeb79210c6"}, - {file = "loro-1.10.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:f255ffc8dab77f86a8b18c40ea1beb3da1144a183acee523910d1024c2321252"}, - {file = "loro-1.10.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:07757bf9279d0d99bc0f411015f1058c1430d2fee6500593301a7c8b428e7835"}, - {file = "loro-1.10.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:0bd6b48247544463a6fc2a218ad93c68145c1b2e4c5365a5a2746e02e2aac0f7"}, - {file = "loro-1.10.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:7d6e8397db5345067af44a5a0daba7ea2f5711c20241638efdf0b9e254086b28"}, - {file = "loro-1.10.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:a45e9310fc732f4586c45eaa6edcec21d9302c18934c8b88220b7dcb8ef6fdda"}, - {file = "loro-1.10.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a60eabd6beb2a185d331affb637301628884111c91720b7506842a3f1920afec"}, - {file = "loro-1.10.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4bdf02beda2134505861f97bd5154684d25b9764ae8eb4476a61cfceddb2b48e"}, - {file = "loro-1.10.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8303d216c2bae9eee453c1f7da08a8a88f92d73cdc788fcd0b1ac318631da6e9"}, - {file = "loro-1.10.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:95d472d6fc45cbc33f221d5e5db2110ebb0460193017c4446b59b7fd6c005d78"}, - {file = "loro-1.10.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:370453c812ef896ebf8bbc2239fe80f216cb150cba137a3a5367b579ce321472"}, - {file = "loro-1.10.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a3182813a4ab6f7a7250384fd15e330d486ff3977776db4c6e376e2f84c8c0e3"}, - {file = "loro-1.10.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a05f0a877072ed196b2af5d463cbc6ed1881f3fdc6b0c0d17d34f9f799c32300"}, - {file = "loro-1.10.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:7e0d05e9a3ae7258e3ae30e1ddb73c3e332e93e4d31cad746a962e220c859b1f"}, - {file = "loro-1.10.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:3760364e797320686ee7af89d0842de6ade4f932a5c3dcc8df393d4200baaf26"}, - {file = "loro-1.10.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c9751bf0abf7577a8b84fab93c3bd0b660791af7f6c932ee7c4a64814c826a7f"}, - {file = "loro-1.10.0-cp314-cp314-win32.whl", hash = "sha256:13c9374384fd246e94068abc998df0e814400a2630c303e33c7f1847b18ddd06"}, - {file = "loro-1.10.0-cp314-cp314-win_amd64.whl", hash = "sha256:6f18b963a5d444a7e3619212a2d806593fcc652a7d4f4f49be5997602b1a020e"}, - {file = "loro-1.10.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce38a7418aa450251bf19aabf6b46c00536d616ecd56329f3bd3fe55408838ef"}, - {file = "loro-1.10.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0a41fb90d26e4dc8bcd90a24c9aac2ffb3b1927c99e589efe5acebad28a62aa2"}, - {file = "loro-1.10.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1227afcf3fe2f82d94e60bc2af6902db8c1d83e480ae8a3aaa153fca83ef1b5a"}, - {file = "loro-1.10.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e0bde1a70f67b9cbc183ca29363ecd4f82c9e8826353f13bd1f463c0f46ce01f"}, - {file = "loro-1.10.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:f0057615053895c27c064440c26247a9fae1e5fa537ddaeb5bc87d081a56a2e4"}, - {file = "loro-1.10.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:f2bbf037b3a807f0bdc6ce3e8020e43edc9819537de851273bfda48527147485"}, - {file = "loro-1.10.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:b3ce63abb3af51a2cf94516cd5bb1ed7d5ef7c650c60b6972141075b94e4707a"}, - {file = "loro-1.10.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:df4f66702b3bbc35072bb695c38bcba26025c8733313a24da5f2fc86bb535b45"}, - {file = "loro-1.10.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:f27c53b9083d17092991444f6d236a15cf1162c96ce2997b71eab697e499e41b"}, - {file = "loro-1.10.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f014ac70c3d074b7f2f35c66f0a14da85538525a7bf81e3a6ec0467fbae3abfa"}, - {file = "loro-1.10.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7bbb17202bde2e4c139dd27a0ba161933edfcb37669e97aff1385dce1d5ee68"}, - {file = "loro-1.10.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2c661485c7154e1073792d58a321cabeaf02850013fc81ff7e8e9435fa038105"}, - {file = "loro-1.10.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c97e26f00ebf09eb1aa949fed6ed85ce1c6e2bf5f83c58fc3d35febb5754d7e2"}, - {file = "loro-1.10.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48d71d8cb70ec27034dff2b873e8755b5bf7bb3193abab66b8acd028e8859c1f"}, - {file = "loro-1.10.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e66f9b221e84253484dbd34a45d6fef5d938bc5e9e418dbb920de5e64eaba0e"}, - {file = "loro-1.10.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b6cad463f89204854a2da0c4dd3c079d262b9abda4492b8b4797082bae52660a"}, - {file = "loro-1.10.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:9cf843c12e460b666cdf4bdeadea00ba2ca896980424270f54a3b4ec4d0bb297"}, - {file = "loro-1.10.0-cp38-cp38-musllinux_1_2_armv7l.whl", hash = "sha256:480b2fa3a4c1b61954d13965be38b76dec8270b82accf61e96758d00b7bb9410"}, - {file = "loro-1.10.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:e67f04320011ae997ddca06aeabe3bc48c0f76657ffa2289df210faab9206529"}, - {file = "loro-1.10.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:1562958f7abd7c15418f27aa468c25a4d6fa5ee3d3932b47c32427f5666e10db"}, - {file = "loro-1.10.0-cp38-cp38-win32.whl", hash = "sha256:264c5faccc235e772a53f30e2ab399d46915258f11637c1aeea17148e3402e0c"}, - {file = "loro-1.10.0-cp38-cp38-win_amd64.whl", hash = "sha256:b603fb2ef3121d4982598d085d7edd649e919a67838acd3e41f99fef7c770ec2"}, - {file = "loro-1.10.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:5a364b340ae88fea3d304ac0bd122909132f7e94ad38d5e7f40819db54d4026c"}, - {file = "loro-1.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:955824c7221552dbcc0505aa2ab928fc76383ac835aa03d65a3193406e9b338b"}, - {file = "loro-1.10.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:db29c0d8eb87c74f7c42c05b9f0a507a51ccc3e53a17c33bfff1752cf3b4db5a"}, - {file = "loro-1.10.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:de0147650cb3c47b210147a459f64076ad2f678ee861a837a2c0022ab98096f6"}, - {file = "loro-1.10.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fe89b273937dee70bbc652e1c8a4dff2a05454a63df761cd6c9bf269bfc67c49"}, - {file = "loro-1.10.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cb71b5621cf60a5fe3f2ce9681faf96ee7ad6bd5dbc5e7a45836dd941892cdbc"}, - {file = "loro-1.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7d8b205917c2a1fa4c15fca418e7712d961ca014e7549ca4cc9de5d922237903"}, - {file = "loro-1.10.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:db447cad3ab0f90d878cbfaa1b4d345ba6887e7a3e958d25b3e8982a70be09f1"}, - {file = "loro-1.10.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:6beafd4045cd51a4105dac068508852f1b46d5b6316c34d8d2529cf872fea468"}, - {file = "loro-1.10.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:650881c41c2a0b73454036f8604746841bd560aa81ad9370a662d8ec38829fb1"}, - {file = "loro-1.10.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:68f34b01f44d68ae91ccf117d2aa09c3abf977e88f6e38e84f87c6e50f6f233a"}, - {file = "loro-1.10.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:bd0dfaac71b9448c7b7663b4bcbcde9ef95fa1c51e4f455be457313cce15411f"}, - {file = "loro-1.10.0-cp39-cp39-win32.whl", hash = "sha256:db635d46e7f009981efb639783b6e6c6a0ea16c3347e090d5f21f9eab129a13f"}, - {file = "loro-1.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:b9fad5fec38e57a691824ec21de1bb2ecad364a2f2b5b032432bfa9765c64a00"}, - {file = "loro-1.10.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50dfb077bcdf43d0960601f71c2cc7eddf663763be0624f8f5a9d5d446831dd7"}, - {file = "loro-1.10.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0b44dcdaa522a9b1a1a424ea8f31560b7c807755044e546e85a88d97ac799fd8"}, - {file = "loro-1.10.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:19c969b840cf7bae4402d4437c09f4b7a238024dfeacd0d3f146f646b440e496"}, - {file = "loro-1.10.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:10840bd4f37dd5ac2f6d7aa87cc5056dd0ece8d3271bcb266689bbb02d2c5d25"}, - {file = "loro-1.10.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:7aaf63dd94920c6a966a4f3191a2a3f843200501f1ff4138a95a34f53695d623"}, - {file = "loro-1.10.0-pp310-pypy310_pp73-musllinux_1_2_armv7l.whl", hash = "sha256:f7ecc202a05d064be34a3aec008313bbe526d0f96a2369597522167c41094ae8"}, - {file = "loro-1.10.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:dc9c3aa3aa135fb8c70fb9d7246734bcf6fe0f0754fdb3d987cbc7fabc7962fe"}, - {file = "loro-1.10.0-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:7c55c07f24f23f05584a53ca5235fc33b7774e82b543985c83e982f2729bc93d"}, - {file = "loro-1.10.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e44c6b875e2da2e7a1d4ae22ca74f271e3e03253ba50b62dae007bbc529cb5b9"}, - {file = "loro-1.10.0-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:37a46fe5d9c37e85992e78c1d34e92c9a2e1d6a96072bd660a132916aa7943e8"}, - {file = "loro-1.10.0-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8a443f46bc4a78d4184393e1a6d8af3f16fe33d6ceb47a405c9db3366ee7e9e0"}, - {file = "loro-1.10.0-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6aa99df616cc47fed389cd158943571b387a2efffad94625377a986faf45b3c7"}, - {file = "loro-1.10.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be6e07a02374d568a6c64b31f33092494c4044f09c85dec6238621b03e5c76ca"}, - {file = "loro-1.10.0-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f29c71ae2c2cae5418aadfe91e7f21a4ba329c321735a702efac10241aded1ad"}, - {file = "loro-1.10.0-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:c8d731fd77c9aa0c84354629d207a2453d923aec9cfddeb91869a3884236ddd1"}, - {file = "loro-1.10.0-pp311-pypy311_pp73-musllinux_1_2_armv7l.whl", hash = "sha256:802ca5982bf76af2e258b7369bb2f436bd6970840de920a1abfbab67a82925ac"}, - {file = "loro-1.10.0-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:877a390ea7e071aca8b6b83f506411b26ea6d5d94cca51575af3a6c9cfa5c781"}, - {file = "loro-1.10.0-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:b42b71931e82d003e80853ee5260d75b052babfead2464332914c4929e5b7191"}, - {file = "loro-1.10.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b2166a04ee78419e82a47118e74da30773bb52e6f3d0b0ae99c5fd1617e4c73"}, - {file = "loro-1.10.0-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:dfa4b88055bd8afee50a975135c99ac8684328d64112631a779d641432efe84b"}, - {file = "loro-1.10.0-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:44787d0411aa2bb14838c2f2146098f45336039b67dd3012d010cf99d3698047"}, - {file = "loro-1.10.0-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:234aa86788a6a14b23dab3936b039bd8914d17de9e1519e2b991b02096a76c07"}, - {file = "loro-1.10.0-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:ec6bb8c6f8b0eaeb26b9c631610e427ad3db9eba914a88277c30bda370d57ff4"}, - {file = "loro-1.10.0-pp39-pypy39_pp73-musllinux_1_2_armv7l.whl", hash = "sha256:2ae174676bc2680467dd7864900f913d7da4105380bc0f34507c2dd0e696af6b"}, - {file = "loro-1.10.0-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:24ef8e7e7a783c6b922d215f14ecb642c1edb9944a021db1c401c42889ef7163"}, - {file = "loro-1.10.0-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:8cca2cfea64465a78ba1dda6f1f8e66ce0bbe337a2372d431df56ddb7d8d0739"}, - {file = "loro-1.10.0.tar.gz", hash = "sha256:0d205fee88e0373fb3dcbcdb224d0c2acabdf3fa0796e9af18ecbac699f98afb"}, + {file = "loro-1.10.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:7e7e3461439c57efaadfd364a5a504a849653cf408c97086033004dffb3f2857"}, + {file = "loro-1.10.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ed91dae34236f888c357b367d37b050ac4fa21ff30ab0231122f580ca87f46ba"}, + {file = "loro-1.10.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5d417a99bae161ecb1250f3272a80c87f2ae546dfb705cadac3ebbc623b7382"}, + {file = "loro-1.10.3-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4a9b821925c9051ee2653a519a99b1d2fc1177a4bac1f02b1f8eaec491f6d43b"}, + {file = "loro-1.10.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0ee8982a6b82660165e516932cda0e5fd7065023f35ae5e2d17562cf14969e87"}, + {file = "loro-1.10.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd391a27550dcf837c82d8ae4e420b4d3b16bdc5a698c3862540803a16bf52dd"}, + {file = "loro-1.10.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e74235d480c6e9b362c6f2265a7d28dd848e6a6142a3c9d0831b82cf3776efee"}, + {file = "loro-1.10.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:df7baf726db4e82f411f7a0454500047812f41bef9552109cb738b8f6ee89c9f"}, + {file = "loro-1.10.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:112d5eeaf76ca6dfbe811e6f6d18649ceeb7697626288ed1185bd1a7d4aae182"}, + {file = "loro-1.10.3-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:a2cbc231a07f11b82099b76386b1e5659687f4415d6f111699bbd4f291c945a4"}, + {file = "loro-1.10.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:dbf31ae00bae9c76a4429f73cec3fb3000f1b4d41603244793c660e17747ce1f"}, + {file = "loro-1.10.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:98d8855a94e2123dab0e40fb5ac7760edbb9b87cd4b29608327899874721ed0b"}, + {file = "loro-1.10.3-cp310-cp310-win32.whl", hash = "sha256:b539f86cf5e44ad7eefd05772ec637985fddd31137deadca508cd8f3bad211a9"}, + {file = "loro-1.10.3-cp310-cp310-win_amd64.whl", hash = "sha256:a5da9963be9a323424695c04d9be836577705077a359d1bb4cabd43963ed2600"}, + {file = "loro-1.10.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:7ee0e1c9a6d0e4a1df4f1847d3b31cef8088860c1193442f131936d084bd3fe1"}, + {file = "loro-1.10.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d7225471b29a892a10589d7cf59c70b0e4de502fa20da675e9aaa1060c7703ae"}, + {file = "loro-1.10.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc04a714e0a604e191279501fa4d2db3b39cee112275f31e87d95ecfbafdfb6c"}, + {file = "loro-1.10.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:375c888a4ddf758b034eb6ebd093348547d17364fae72aa7459d1358e4843b1f"}, + {file = "loro-1.10.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2020d9384a426e91a7d38c9d0befd42e8ad40557892ed50d47aad79f8d92b654"}, + {file = "loro-1.10.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:95afacd832dce152700c2bc643f7feb27d5611fc97b5141684b5831b22845380"}, + {file = "loro-1.10.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7c95868bcf6361d700e215f33a88b8f51d7bc3ae7bbe3d35998148932e23d3fa"}, + {file = "loro-1.10.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:68f5c7fad09d8937ef4b55e7dd4a0f9f175f026369b3f55a5b054d3513f6846d"}, + {file = "loro-1.10.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:740bb548139d71eccd6317f3df40a0dc5312e98bbb2be09a6e4aaddcaf764206"}, + {file = "loro-1.10.3-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:c756a6ee37ed851e9cf91e5fedbc68ca21e05969c4e2ec6531c15419a4649b58"}, + {file = "loro-1.10.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:3553390518e188c055b56bcbae76bf038329f9c3458cb1d69068c55b3f8f49f1"}, + {file = "loro-1.10.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0885388c0c2b53f5140229921bd64c7838827e3101a05d4d53346191ba76b15d"}, + {file = "loro-1.10.3-cp311-cp311-win32.whl", hash = "sha256:764b68c4ff0411399c9cf936d8b6db1161ec445388ff2944a25bbdeb2bbac15c"}, + {file = "loro-1.10.3-cp311-cp311-win_amd64.whl", hash = "sha256:9e583e6aabd6f9b2bdf3ff3f6e0de10c3f7f8ab9d4c05c01a9ecca309c969017"}, + {file = "loro-1.10.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:028948b48dcc5c2127f974dae4ad466ab69f0d1eeaf367a8145eb6501fb988f2"}, + {file = "loro-1.10.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5253b8f436d90412b373c583f22ac9539cfb495bf88f78d4bb41daafef0830b7"}, + {file = "loro-1.10.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:14be8a5539d49468c94d65742355dbe79745123d78bf769a23e53bf9b60dd46a"}, + {file = "loro-1.10.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:91b2b9139dfc5314a0197132a53b6673fddb63738380a522d12a05cec7ad76b4"}, + {file = "loro-1.10.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:247897288911c712ee7746965573299fc23ce091e94456da8da371e6adae30f4"}, + {file = "loro-1.10.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:835abc6025eb5b6a0fe22c808472affc95e9a661b212400cfd88ba186b0d304c"}, + {file = "loro-1.10.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e660853617fc29e71bb7b796e6f2c21f7722c215f593a89e95cd4d8d5a32aca0"}, + {file = "loro-1.10.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8059063cab57ca521012ed315a454784c20b0a86653e9014795e804e0a333659"}, + {file = "loro-1.10.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9748359343b5fd7019ab3c2d1d583a0c13c633a4dd21d75e50e3815ab479f493"}, + {file = "loro-1.10.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:def7c9c2e16ad5470c9c56f096ac649dd4cd42d5936a32bb0817509a92d82467"}, + {file = "loro-1.10.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:34b223fab58591a823f439d9a13d1a1ddac18dc4316866503c588ae8a9147cb1"}, + {file = "loro-1.10.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9d5fa4baceb248d771897b76d1426c7656176e82e770f6790940bc3e3812436d"}, + {file = "loro-1.10.3-cp312-cp312-win32.whl", hash = "sha256:f25ab769b84a5fbeb1f9a1111f5d28927eaeaa8f5d2d871e237f80eaca5c684e"}, + {file = "loro-1.10.3-cp312-cp312-win_amd64.whl", hash = "sha256:3b73b7a3a32e60c3424fc7deaf8b127af7580948e27d8bbe749e3f43508aa0a2"}, + {file = "loro-1.10.3-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:380ef692c5272e8b607be2ee6a8eef5113e65dc38e6739526c30e3db6abc3fbc"}, + {file = "loro-1.10.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ed966ce6ff1fb3787b3f6c4ed6dd036baa5fb738b84a466a5e764f2ab534ccc2"}, + {file = "loro-1.10.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4d7c8d2f3d88578fdf69845a9ae16fc5ea3ac54aa838a6bf43a24ce11908220"}, + {file = "loro-1.10.3-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:62283c345bfeedef19c8a6d029cd8830e5d2c20b5fb45975d8a70a8a30a7944b"}, + {file = "loro-1.10.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d1e7e6ae091179fa5f0fca1f8612fde20236ee0a678744bf51ff7d26103ea04f"}, + {file = "loro-1.10.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6abc6de4876aa205498cef52a002bc38662fbd8d742351ea0f535479208b8b1c"}, + {file = "loro-1.10.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:acbbfd24cf28a71bbdad8544852e9bbba0ba8535f8221f8859b2693555fa8356"}, + {file = "loro-1.10.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5faf4ebbe8ca39605024f16dbbbde354365f4e2dcfda82c753797461b504bbd3"}, + {file = "loro-1.10.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e049c21b292c4ff992b23a98812840735db84620721c10ae7f047a921202d090"}, + {file = "loro-1.10.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:20e8dacfb827c1f7ffb73e127029d7995a9ab2c3b7b7bc3ecc91d22ee32d78d0"}, + {file = "loro-1.10.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:1b743c1c4f93f5b4f0e12efbb352d26e9f80bcbf20f45d9c70f3d0b522f42060"}, + {file = "loro-1.10.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:446d67bc9e28036a5a5e03526d28a1559ef2a47b3ccad6b07820dae123cc3697"}, + {file = "loro-1.10.3-cp313-cp313-win32.whl", hash = "sha256:45d7d8ec683599897695bb714771baccabc1b4c4a412283cc39787c7a59f7ff0"}, + {file = "loro-1.10.3-cp313-cp313-win_amd64.whl", hash = "sha256:a42bf73b99b07fed11b65feb0a5362b33b19de098f2235848687f4c41204830e"}, + {file = "loro-1.10.3-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4866325b154aeebcd34be106c7597acf150c374481ac3c12035a1af715ac0f01"}, + {file = "loro-1.10.3-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ea7b8849660a28ce8cd90a82db4f76c23453836fcbc88f5767feaaf8739045e2"}, + {file = "loro-1.10.3-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9e82cdaf9a5892557d3167e07ed5093f87dfa31ef860a63b0eac6c0c2f435705"}, + {file = "loro-1.10.3-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c7ee99e5dc844fb20fca830906a0d721022ad1c37aad0b1a440c4ecb98d0c02f"}, + {file = "loro-1.10.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:153c297672ad98d0fe6ff8985decf1e64528ad1dd01ae1452bb83bdeb31f858f"}, + {file = "loro-1.10.3-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:0ed72f8c6a5f521252ee726954055339abba3fcf00404fb4b5c2da168f0cce79"}, + {file = "loro-1.10.3-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:f612ab17acdac16c0139e63ff45b33175ebfb22e61a60eb7929a4583389348d6"}, + {file = "loro-1.10.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f2741db05c79f3618c954bac90f4572d28c01c243884453f379e9a8738f93d81"}, + {file = "loro-1.10.3-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:623cf7df17626aa55bc6ca54e89177dbe71a5f1c293e102d6153f43991a1a041"}, + {file = "loro-1.10.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:d8e715d475f32a1462969aca27eeb3f998f309182978f55bc37ce5c515d92e90"}, + {file = "loro-1.10.3-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:61e012a80e8c9fe248b9d0a76e91664c9479a72d976eaeed78f87b15b5d1d732"}, + {file = "loro-1.10.3-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:686ece56756acbaf80c986848915e9126a29a06d7a62209747e3ef1efc0bd8f6"}, + {file = "loro-1.10.3-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3aa821c8871deca98f4605eb0c40fb26bcf82bd29c9e7fa33b183516c5395b11"}, + {file = "loro-1.10.3-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:507d34137adb4148f79e1da7f89a21a4aab18565621a5dc2b389773fe98ac25b"}, + {file = "loro-1.10.3-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:91d3b2e187ccfe2b14118a6e5617266fedcdf3435f6fa0a3db7b4afce8afa687"}, + {file = "loro-1.10.3-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c0016f834fd1626710081334400aed8494380b55ef131f7133d21c3bd22d892a"}, + {file = "loro-1.10.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:71c4275dca5a8a86219d60545d4f60e081b4af44b490ac912c0481906934bfc6"}, + {file = "loro-1.10.3-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:490f12571b2ed1a8eaf1edd3a7fffc55adac5010b1875fe1bb9e9af9a3907c38"}, + {file = "loro-1.10.3-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:a374a43cadaa48528a5411496481df9ae52bf01e513f4509e37d6c986f199c0e"}, + {file = "loro-1.10.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:1a93b2ee59f1fa8d98dd552211fd5693551893b34c1dd2ba0324806d6d14022f"}, + {file = "loro-1.10.3-cp314-cp314-win32.whl", hash = "sha256:baa863e3d869422e3320e822c0b1f87f5dc44cda903d1bd3b7a16f8413ce3d92"}, + {file = "loro-1.10.3-cp314-cp314-win_amd64.whl", hash = "sha256:f10ed3ca89485f942b8b2de796ed9783edb990e7e570605232de77489e9f3548"}, + {file = "loro-1.10.3-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2b4d049efb1953aebfc16fa0b445ff5a37d4d08a1ab93f3b5a577a454b7a5ded"}, + {file = "loro-1.10.3-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:56ecad7fbac58aa8bee52bb261a764aeef6c7b39c20f0d69e8fad908ab2ca7d8"}, + {file = "loro-1.10.3-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5d8d1be349d08b3a95592c6a17b80b1ea6aef892b1b8e2b93b540062d04e34e0"}, + {file = "loro-1.10.3-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1ec0a0b9bc4e32c46f14710062ec5b536c72110318aaf85632a4f8b37e9a470a"}, + {file = "loro-1.10.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c5d4437987f7a4a4ff5927f39d0f43ded5b34295dfb0a3c8e150687e25c3d6b8"}, + {file = "loro-1.10.3-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:86d4f0c631ca274ad2fa2c0bdb8e1e141882d94339b7284a8bef5bf73fa6957d"}, + {file = "loro-1.10.3-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:15e03084ff1b472e14623183ed6e1e43e0f717c2112697beda5e69b5bd0ff236"}, + {file = "loro-1.10.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:42d6a5ce5bc518eaa682413e82d597299650eeb03e8bc39341752d6e0d22503e"}, + {file = "loro-1.10.3-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:76a7cfe05f82ce366cd3c833827d52b8bd1d052edd08efa10bfe23e14ce0aaa2"}, + {file = "loro-1.10.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f5797d8a51f42622ffad0996338ed1035048f565916ea6ee75684de77e015172"}, + {file = "loro-1.10.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76fe43743efb6c21a40ef082714cb8dce47405dbf343c3914f415e6f4b307b80"}, + {file = "loro-1.10.3-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:19de3b1278e0abb96af61bec57e17c68bae60845d80d7847b3ee73116b1642a0"}, + {file = "loro-1.10.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f3a3ae2b92529ec003705145a4cc556052bcf00b07399fe96d6ede2c5920264b"}, + {file = "loro-1.10.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:99945cb199f8bfcf64ecedc69e14f7017b65802b66dbe0c9e0b57f45d76f8732"}, + {file = "loro-1.10.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b45f27c0e5ed504deae6b611e16fb2b0012d773c4007a2403f9b50ad6f836b26"}, + {file = "loro-1.10.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c929a85bc2199ffb9c0f4c25f5d68b112fbc2477721e1a5a74b37e72b92be2b1"}, + {file = "loro-1.10.3-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:18380d407be87d93c58389fce591804a67bc76bb5ff0dc3198160b9ad85e3047"}, + {file = "loro-1.10.3-cp38-cp38-musllinux_1_2_armv7l.whl", hash = "sha256:ea01999078f66280c023b6c95d16f5ac03c0e15a4b99813700bf0ed49d6f0d36"}, + {file = "loro-1.10.3-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:1eef5ef59757967baf13ad4d1b999af8724637d5f26f39b10a7248d076fc0979"}, + {file = "loro-1.10.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:52a46b2939f47e944900462d952ae41bded1a86c7c7d4eeadd8de25f5fe3fcba"}, + {file = "loro-1.10.3-cp38-cp38-win32.whl", hash = "sha256:e3bc9c2a4eb5e3769b35c67bebd394f2f5bb186e3c2a2e17a9e43622adee9e4a"}, + {file = "loro-1.10.3-cp38-cp38-win_amd64.whl", hash = "sha256:aee091f5859c865fffcfb29f36ab3b20bd35c9ab681c5ffa3f46443313c8deb0"}, + {file = "loro-1.10.3-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:e5b450835d884d0e40918252d0eae9b769d85598530c024eb286f581622d5f34"}, + {file = "loro-1.10.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f8e7924f9434d82cb98b40d3c2947d3335ff1d93267de7bb7389bd4b4f6eb156"}, + {file = "loro-1.10.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f83ca75c8f46ab05d64eb671d95c0d829df0813e8914fc7e46495ac1578535b1"}, + {file = "loro-1.10.3-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c104f5d00b480a6f471289b8e2163a16e96c1823cc59305a2dd69ad93406cbc9"}, + {file = "loro-1.10.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce07bfb3dbd97a7657c5b26db8ef12d084c41fd451e685e6531827f90c72babd"}, + {file = "loro-1.10.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:451b009010178fd7122ea2d69f13ea0af58b0c0c9da807a7d15e6da44f7957bb"}, + {file = "loro-1.10.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2695577dd6ef705f9c73c82df28a9ea1c7543eae0be2ca1661dce5540b9617c8"}, + {file = "loro-1.10.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ffe9f838f02ff33b4b586441a308927afa487268c0be95854af9f417a7b713d3"}, + {file = "loro-1.10.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:755fe048c8f13827220987c0f899beaff01305ac0d86f82528a10626924c13d7"}, + {file = "loro-1.10.3-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:2423673cbf7fc613d08e018e450028ea8b5e2787a926b25dbda6f94e4c5267fd"}, + {file = "loro-1.10.3-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:667a1982f91709cc67c872189525af4f563b654ce79684d38d82e8c49508588d"}, + {file = "loro-1.10.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:19ce30d8f08e17e1b01d4d62bca95a11f352b99b3215fc5afcd9c3ec9cf2b116"}, + {file = "loro-1.10.3-cp39-cp39-win32.whl", hash = "sha256:95ab7ba4bde202e00b60cb29ac6717af8982d16a872c1795229b612a0a9b673b"}, + {file = "loro-1.10.3-cp39-cp39-win_amd64.whl", hash = "sha256:e8ce4d88d29af10059a75d2dd6157ff12eef0fa1e273e24ba25a63bb7d168908"}, + {file = "loro-1.10.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55214615c1cb9f727a5278f5e57b9660743e7d095e08899e8936f174a45471b9"}, + {file = "loro-1.10.3-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:10591fa32dc628f770da472beac7544d2ba16a3a22d590211364331c5871b9f6"}, + {file = "loro-1.10.3-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f18df6892097603e5bd2e149384d4bcb996be8a3b6ba10d3da74bce39e1d5093"}, + {file = "loro-1.10.3-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0a8911b8cd97652a04e22481dd90b3c8d286f12c8d8286a4e34a655835dd6506"}, + {file = "loro-1.10.3-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:73d5737c95bccf725950555c51374e5823c9be16bfc5496d8c1fafb2bb04690f"}, + {file = "loro-1.10.3-pp310-pypy310_pp73-musllinux_1_2_armv7l.whl", hash = "sha256:fa875a691556daaedb639dc920ee9c3743745eea2aa4c7fd914841e31b92c556"}, + {file = "loro-1.10.3-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:e7ddfd247fa3ae3c05d38019fb1424a903ea98e5730a10105081f5f7dc08f9c1"}, + {file = "loro-1.10.3-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:033a456647d487d61af82ea96aff95a789a3776441ea8af86556f2877867530d"}, + {file = "loro-1.10.3-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:16ca42e991589ea300b59da9e98940d5ddda76275fe4363b1f1e079d244403a1"}, + {file = "loro-1.10.3-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b9ca16dae359397aa7772891bb3967939ffda8da26e0b392d331b506e16afc78"}, + {file = "loro-1.10.3-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d87cfc0a6e119c1c8cfa93078f5d012e557c6b75edcd0977da58ec46d28dc242"}, + {file = "loro-1.10.3-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4541ed987306c51e718f51196fd2b2d05e87b323da5d850b37900d2e8ac6aae6"}, + {file = "loro-1.10.3-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce0b0a500e08b190038380d4593efcb33c98ed4282cc8347ca6ce55d05cbdf6e"}, + {file = "loro-1.10.3-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:987dbcb42b4b8d2c799660a6d8942e53ae346f51d51c9ad7ef5d7e640422fe4a"}, + {file = "loro-1.10.3-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:f876d477cb38c6c623c4ccb5dc4b7041dbeff04167bf9c19fa461d57a3a1b916"}, + {file = "loro-1.10.3-pp311-pypy311_pp73-musllinux_1_2_armv7l.whl", hash = "sha256:641c8445bd1e4181b5b28b75a0bc544ef51f065b15746e8714f90e2e029b5202"}, + {file = "loro-1.10.3-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:a6ab6244472402b8d1f4f77e5210efa44dfa4914423cafcfcbd09232ea8bbff0"}, + {file = "loro-1.10.3-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:ae4c765671ee7d7618962ec11cb3bb471965d9b88c075166fe383263235d58d6"}, + {file = "loro-1.10.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67502e6ce57966cccd79fe8374fd4b19777b4dd29aad4961cd5bebd02e7d7da5"}, + {file = "loro-1.10.3-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:18431306bbc822b9bc6135c5d6cf3536b956348726b4bb76bc2f400613077de8"}, + {file = "loro-1.10.3-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6383aafa7a9c133e037e3956f00082f2a503d6e5a450bb125bb9f440a81d19f3"}, + {file = "loro-1.10.3-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2cd8fdabe8850876815bcac6ba4bd1444254d6424d81e639fd6dc554ed9f5656"}, + {file = "loro-1.10.3-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:dccc66b17932ffb4f63a1131199062d49a9be2ff4dbf5692034aa7ceec6ba371"}, + {file = "loro-1.10.3-pp39-pypy39_pp73-musllinux_1_2_armv7l.whl", hash = "sha256:0003b9217729a335d16ff2ec8066cf350d074e6b29691d670696d4f042497f71"}, + {file = "loro-1.10.3-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:2ba6f9e89b2636d02b9401491a4a5cbff05ca1c97ee87a28a58de5f550715ef2"}, + {file = "loro-1.10.3-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:d1aa73eaf4471b4ded7bf32eabceed9fc115c65fa3aefeb425ed0f7ad0289032"}, + {file = "loro-1.10.3.tar.gz", hash = "sha256:68184ab1c2ab94af6ad4aaba416d22f579cabee0b26cbb09a1f67858207bbce8"}, +] + +[[package]] +name = "lupa" +version = "2.6" +description = "Python wrapper around Lua and LuaJIT" +optional = false +python-versions = "*" +groups = ["main"] +files = [ + {file = "lupa-2.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6b3dabda836317e63c5ad052826e156610f356a04b3003dfa0dbe66b5d54d671"}, + {file = "lupa-2.6-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:8726d1c123bbe9fbb974ce29825e94121824e66003038ff4532c14cc2ed0c51c"}, + {file = "lupa-2.6-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:f4e159e7d814171199b246f9235ca8961f6461ea8c1165ab428afa13c9289a94"}, + {file = "lupa-2.6-cp310-cp310-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:202160e80dbfddfb79316692a563d843b767e0f6787bbd1c455f9d54052efa6c"}, + {file = "lupa-2.6-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5deede7c5b36ab64f869dae4831720428b67955b0bb186c8349cf6ea121c852b"}, + {file = "lupa-2.6-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:86f04901f920bbf7c0cac56807dc9597e42347123e6f1f3ca920f15f54188ce5"}, + {file = "lupa-2.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:6deef8f851d6afb965c84849aa5b8c38856942df54597a811ce0369ced678610"}, + {file = "lupa-2.6-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:21f2b5549681c2a13b1170a26159d30875d367d28f0247b81ca347222c755038"}, + {file = "lupa-2.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:66eea57630eab5e6f49fdc5d7811c0a2a41f2011be4ea56a087ea76112011eb7"}, + {file = "lupa-2.6-cp310-cp310-win32.whl", hash = "sha256:60a403de8cab262a4fe813085dd77010effa6e2eb1886db2181df803140533b1"}, + {file = "lupa-2.6-cp310-cp310-win_amd64.whl", hash = "sha256:e4656a39d93dfa947cf3db56dc16c7916cb0cc8024acd3a952071263f675df64"}, + {file = "lupa-2.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6d988c0f9331b9f2a5a55186701a25444ab10a1432a1021ee58011499ecbbdd5"}, + {file = "lupa-2.6-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:ebe1bbf48259382c72a6fe363dea61a0fd6fe19eab95e2ae881e20f3654587bf"}, + {file = "lupa-2.6-cp311-cp311-macosx_11_0_x86_64.whl", hash = "sha256:a8fcee258487cf77cdd41560046843bb38c2e18989cd19671dd1e2596f798306"}, + {file = "lupa-2.6-cp311-cp311-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:561a8e3be800827884e767a694727ed8482d066e0d6edfcbf423b05e63b05535"}, + {file = "lupa-2.6-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:af880a62d47991cae78b8e9905c008cbfdc4a3a9723a66310c2634fc7644578c"}, + {file = "lupa-2.6-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:80b22923aa4023c86c0097b235615f89d469a0c4eee0489699c494d3367c4c85"}, + {file = "lupa-2.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:153d2cc6b643f7efb9cfc0c6bb55ec784d5bac1a3660cfc5b958a7b8f38f4a75"}, + {file = "lupa-2.6-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:3fa8777e16f3ded50b72967dc17e23f5a08e4f1e2c9456aff2ebdb57f5b2869f"}, + {file = "lupa-2.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:8dbdcbe818c02a2f56f5ab5ce2de374dab03e84b25266cfbaef237829bc09b3f"}, + {file = "lupa-2.6-cp311-cp311-win32.whl", hash = "sha256:defaf188fde8f7a1e5ce3a5e6d945e533b8b8d547c11e43b96c9b7fe527f56dc"}, + {file = "lupa-2.6-cp311-cp311-win_amd64.whl", hash = "sha256:9505ae600b5c14f3e17e70f87f88d333717f60411faca1ddc6f3e61dce85fa9e"}, + {file = "lupa-2.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:47ce718817ef1cc0c40d87c3d5ae56a800d61af00fbc0fad1ca9be12df2f3b56"}, + {file = "lupa-2.6-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:7aba985b15b101495aa4b07112cdc08baa0c545390d560ad5cfde2e9e34f4d58"}, + {file = "lupa-2.6-cp312-cp312-macosx_11_0_x86_64.whl", hash = "sha256:b766f62f95b2739f2248977d29b0722e589dcf4f0ccfa827ccbd29f0148bd2e5"}, + {file = "lupa-2.6-cp312-cp312-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:00a934c23331f94cb51760097ebfab14b005d55a6b30a2b480e3c53dd2fa290d"}, + {file = "lupa-2.6-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:21de9f38bd475303e34a042b7081aabdf50bd9bafd36ce4faea2f90fd9f15c31"}, + {file = "lupa-2.6-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cf3bda96d3fc41237e964a69c23647d50d4e28421111360274d4799832c560e9"}, + {file = "lupa-2.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5a76ead245da54801a81053794aa3975f213221f6542d14ec4b859ee2e7e0323"}, + {file = "lupa-2.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:8dd0861741caa20886ddbda0a121d8e52fb9b5bb153d82fa9bba796962bf30e8"}, + {file = "lupa-2.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:239e63948b0b23023f81d9a19a395e768ed3da6a299f84e7963b8f813f6e3f9c"}, + {file = "lupa-2.6-cp312-cp312-win32.whl", hash = "sha256:325894e1099499e7a6f9c351147661a2011887603c71086d36fe0f964d52d1ce"}, + {file = "lupa-2.6-cp312-cp312-win_amd64.whl", hash = "sha256:c735a1ce8ee60edb0fe71d665f1e6b7c55c6021f1d340eb8c865952c602cd36f"}, + {file = "lupa-2.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:663a6e58a0f60e7d212017d6678639ac8df0119bc13c2145029dcba084391310"}, + {file = "lupa-2.6-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:d1f5afda5c20b1f3217a80e9bc1b77037f8a6eb11612fd3ada19065303c8f380"}, + {file = "lupa-2.6-cp313-cp313-macosx_11_0_x86_64.whl", hash = "sha256:26f2b3c085fe76e9119e48c1013c1cccdc1f51585d456858290475aa38e7089e"}, + {file = "lupa-2.6-cp313-cp313-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:60d2f902c7b96fb8ab98493dcff315e7bb4d0b44dc9dd76eb37de575025d5685"}, + {file = "lupa-2.6-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a02d25dee3a3250967c36590128d9220ae02f2eda166a24279da0b481519cbff"}, + {file = "lupa-2.6-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6eae1ee16b886b8914ff292dbefbf2f48abfbdee94b33a88d1d5475e02423203"}, + {file = "lupa-2.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b0edd5073a4ee74ab36f74fe61450148e6044f3952b8d21248581f3c5d1a58be"}, + {file = "lupa-2.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:0c53ee9f22a8a17e7d4266ad48e86f43771951797042dd51d1494aaa4f5f3f0a"}, + {file = "lupa-2.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:de7c0f157a9064a400d828789191a96da7f4ce889969a588b87ec80de9b14772"}, + {file = "lupa-2.6-cp313-cp313-win32.whl", hash = "sha256:ee9523941ae0a87b5b703417720c5d78f72d2f5bc23883a2ea80a949a3ed9e75"}, + {file = "lupa-2.6-cp313-cp313-win_amd64.whl", hash = "sha256:b1335a5835b0a25ebdbc75cf0bda195e54d133e4d994877ef025e218c2e59db9"}, + {file = "lupa-2.6-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:dcb6d0a3264873e1653bc188499f48c1fb4b41a779e315eba45256cfe7bc33c1"}, + {file = "lupa-2.6-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:a37e01f2128f8c36106726cb9d360bac087d58c54b4522b033cc5691c584db18"}, + {file = "lupa-2.6-cp314-cp314-macosx_11_0_x86_64.whl", hash = "sha256:458bd7e9ff3c150b245b0fcfbb9bd2593d1152ea7f0a7b91c1d185846da033fe"}, + {file = "lupa-2.6-cp314-cp314-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:052ee82cac5206a02df77119c325339acbc09f5ce66967f66a2e12a0f3211cad"}, + {file = "lupa-2.6-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96594eca3c87dd07938009e95e591e43d554c1dbd0385be03c100367141db5a8"}, + {file = "lupa-2.6-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e8faddd9d198688c8884091173a088a8e920ecc96cda2ffed576a23574c4b3f6"}, + {file = "lupa-2.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:daebb3a6b58095c917e76ba727ab37b27477fb926957c825205fbda431552134"}, + {file = "lupa-2.6-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:f3154e68972befe0f81564e37d8142b5d5d79931a18309226a04ec92487d4ea3"}, + {file = "lupa-2.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e4dadf77b9fedc0bfa53417cc28dc2278a26d4cbd95c29f8927ad4d8fe0a7ef9"}, + {file = "lupa-2.6-cp314-cp314-win32.whl", hash = "sha256:cb34169c6fa3bab3e8ac58ca21b8a7102f6a94b6a5d08d3636312f3f02fafd8f"}, + {file = "lupa-2.6-cp314-cp314-win_amd64.whl", hash = "sha256:b74f944fe46c421e25d0f8692aef1e842192f6f7f68034201382ac440ef9ea67"}, + {file = "lupa-2.6-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0e21b716408a21ab65723f8841cf7f2f37a844b7a965eeabb785e27fca4099cf"}, + {file = "lupa-2.6-cp314-cp314t-macosx_11_0_universal2.whl", hash = "sha256:589db872a141bfff828340079bbdf3e9a31f2689f4ca0d88f97d9e8c2eae6142"}, + {file = "lupa-2.6-cp314-cp314t-macosx_11_0_x86_64.whl", hash = "sha256:cd852a91a4a9d4dcbb9a58100f820a75a425703ec3e3f049055f60b8533b7953"}, + {file = "lupa-2.6-cp314-cp314t-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:0334753be028358922415ca97a64a3048e4ed155413fc4eaf87dd0a7e2752983"}, + {file = "lupa-2.6-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:661d895cd38c87658a34780fac54a690ec036ead743e41b74c3fb81a9e65a6aa"}, + {file = "lupa-2.6-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6aa58454ccc13878cc177c62529a2056be734da16369e451987ff92784994ca7"}, + {file = "lupa-2.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:1425017264e470c98022bba8cff5bd46d054a827f5df6b80274f9cc71dafd24f"}, + {file = "lupa-2.6-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:224af0532d216e3105f0a127410f12320f7c5f1aa0300bdf9646b8d9afb0048c"}, + {file = "lupa-2.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9abb98d5a8fd27c8285302e82199f0e56e463066f88f619d6594a450bf269d80"}, + {file = "lupa-2.6-cp314-cp314t-win32.whl", hash = "sha256:1849efeba7a8f6fb8aa2c13790bee988fd242ae404bd459509640eeea3d1e291"}, + {file = "lupa-2.6-cp314-cp314t-win_amd64.whl", hash = "sha256:fc1498d1a4fc028bc521c26d0fad4ca00ed63b952e32fb95949bda76a04bad52"}, + {file = "lupa-2.6-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9591700991e333b70dd92b48f152eb4731b8b24af671a9f6f721b74d68ed4499"}, + {file = "lupa-2.6-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:ef8dfa7fe08bc3f4591411b8945bbeb15af8512c3e7ad5e9b1e3a9036cdbbce7"}, + {file = "lupa-2.6-cp38-cp38-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:728c466e91174dad238f8a9c1cbdb8e69ffe559df85f87ee76edac3395300949"}, + {file = "lupa-2.6-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c781170bc7134704ae317a66204d30688b41d3e471e17e659987ea4947e11f20"}, + {file = "lupa-2.6-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:241f4ddab33b9a686fc76667241bebc39a06b74ec40d79ec222f5add9000fe57"}, + {file = "lupa-2.6-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:c17f6b6193ced33cc7ca0b2b08b319a1b3501b014a3a3f9999c01cafc04c40f5"}, + {file = "lupa-2.6-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:fa6c1379e83d4104065c151736250a09f3a99e368423c7a20f9c59b15945e9fc"}, + {file = "lupa-2.6-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:aef1a8bc10c50695e1a33a07dbef803b93eb97fc150fdb19858d704a603a67dd"}, + {file = "lupa-2.6-cp38-cp38-win32.whl", hash = "sha256:10c191bc1d5565e4360d884bea58320975ddb33270cdf9a9f55d1a1efe79aa03"}, + {file = "lupa-2.6-cp38-cp38-win_amd64.whl", hash = "sha256:05681f8ffb41f0c7fbb9ca859cc3a7e4006e9c6350d25358b535c5295c6a9928"}, + {file = "lupa-2.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8897dc6c3249786b2cdf2f83324febb436193d4581b6a71dea49f77bf8b19bb0"}, + {file = "lupa-2.6-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:4446396ca3830be0c106c70db4b4f622c37b2d447874c07952cafb9c57949a4a"}, + {file = "lupa-2.6-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:5826e687c89995a6eaafeae242071ba16448eec1a9ee8e17ed48551b5d1e21c2"}, + {file = "lupa-2.6-cp39-cp39-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:5871935cb36d1d22f9c04ac0db75c06751bd95edcfa0d9309f732de908e297a9"}, + {file = "lupa-2.6-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:43eb6e43ea8512d0d65b995d36dd9d77aa02598035e25b84c23a1b58700c9fb2"}, + {file = "lupa-2.6-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:559714053018d9885cc8c36a33c5b7eb9aad30fb6357719cac3ce4dc6b39157e"}, + {file = "lupa-2.6-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:57ac88a00ce59bd9d4ddcd4fca8e02564765725f5068786b011c9d1be3de20c5"}, + {file = "lupa-2.6-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:b683fbd867c2e54c44a686361b75eee7e7a790da55afdbe89f1f23b106de0274"}, + {file = "lupa-2.6-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:d2f656903a2ed2e074bf2b7d300968028dfa327a45b055be8e3b51ef0b82f9bf"}, + {file = "lupa-2.6-cp39-cp39-win32.whl", hash = "sha256:bf28f68ae231b72008523ab5ac23835ba0f76e0e99ec38b59766080a84eb596a"}, + {file = "lupa-2.6-cp39-cp39-win_amd64.whl", hash = "sha256:b4b2e9b3795a9897cf6cfcc58d08210fdc0d13ab47c9a0e13858c68932d8353c"}, + {file = "lupa-2.6.tar.gz", hash = "sha256:9a770a6e89576be3447668d7ced312cd6fd41d3c13c2462c9dc2c2ab570e45d9"}, ] [[package]] @@ -4281,67 +4721,67 @@ tests = ["pytest", "simplejson"] [[package]] name = "matplotlib" -version = "3.10.7" +version = "3.10.8" description = "Python plotting package" optional = false python-versions = ">=3.10" groups = ["main"] files = [ - {file = "matplotlib-3.10.7-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:7ac81eee3b7c266dd92cee1cd658407b16c57eed08c7421fa354ed68234de380"}, - {file = "matplotlib-3.10.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:667ecd5d8d37813a845053d8f5bf110b534c3c9f30e69ebd25d4701385935a6d"}, - {file = "matplotlib-3.10.7-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cc1c51b846aca49a5a8b44fbba6a92d583a35c64590ad9e1e950dc88940a4297"}, - {file = "matplotlib-3.10.7-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a11c2e9e72e7de09b7b72e62f3df23317c888299c875e2b778abf1eda8c0a42"}, - {file = "matplotlib-3.10.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f19410b486fdd139885ace124e57f938c1e6a3210ea13dd29cab58f5d4bc12c7"}, - {file = "matplotlib-3.10.7-cp310-cp310-win_amd64.whl", hash = "sha256:b498e9e4022f93de2d5a37615200ca01297ceebbb56fe4c833f46862a490f9e3"}, - {file = "matplotlib-3.10.7-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:53b492410a6cd66c7a471de6c924f6ede976e963c0f3097a3b7abfadddc67d0a"}, - {file = "matplotlib-3.10.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d9749313deb729f08207718d29c86246beb2ea3fdba753595b55901dee5d2fd6"}, - {file = "matplotlib-3.10.7-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2222c7ba2cbde7fe63032769f6eb7e83ab3227f47d997a8453377709b7fe3a5a"}, - {file = "matplotlib-3.10.7-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e91f61a064c92c307c5a9dc8c05dc9f8a68f0a3be199d9a002a0622e13f874a1"}, - {file = "matplotlib-3.10.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6f1851eab59ca082c95df5a500106bad73672645625e04538b3ad0f69471ffcc"}, - {file = "matplotlib-3.10.7-cp311-cp311-win_amd64.whl", hash = "sha256:6516ce375109c60ceec579e699524e9d504cd7578506f01150f7a6bc174a775e"}, - {file = "matplotlib-3.10.7-cp311-cp311-win_arm64.whl", hash = "sha256:b172db79759f5f9bc13ef1c3ef8b9ee7b37b0247f987fbbbdaa15e4f87fd46a9"}, - {file = "matplotlib-3.10.7-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7a0edb7209e21840e8361e91ea84ea676658aa93edd5f8762793dec77a4a6748"}, - {file = "matplotlib-3.10.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c380371d3c23e0eadf8ebff114445b9f970aff2010198d498d4ab4c3b41eea4f"}, - {file = "matplotlib-3.10.7-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d5f256d49fea31f40f166a5e3131235a5d2f4b7f44520b1cf0baf1ce568ccff0"}, - {file = "matplotlib-3.10.7-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:11ae579ac83cdf3fb72573bb89f70e0534de05266728740d478f0f818983c695"}, - {file = "matplotlib-3.10.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4c14b6acd16cddc3569a2d515cfdd81c7a68ac5639b76548cfc1a9e48b20eb65"}, - {file = "matplotlib-3.10.7-cp312-cp312-win_amd64.whl", hash = "sha256:0d8c32b7ea6fb80b1aeff5a2ceb3fb9778e2759e899d9beff75584714afcc5ee"}, - {file = "matplotlib-3.10.7-cp312-cp312-win_arm64.whl", hash = "sha256:5f3f6d315dcc176ba7ca6e74c7768fb7e4cf566c49cb143f6bc257b62e634ed8"}, - {file = "matplotlib-3.10.7-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1d9d3713a237970569156cfb4de7533b7c4eacdd61789726f444f96a0d28f57f"}, - {file = "matplotlib-3.10.7-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:37a1fea41153dd6ee061d21ab69c9cf2cf543160b1b85d89cd3d2e2a7902ca4c"}, - {file = "matplotlib-3.10.7-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b3c4ea4948d93c9c29dc01c0c23eef66f2101bf75158c291b88de6525c55c3d1"}, - {file = "matplotlib-3.10.7-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:22df30ffaa89f6643206cf13877191c63a50e8f800b038bc39bee9d2d4957632"}, - {file = "matplotlib-3.10.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b69676845a0a66f9da30e87f48be36734d6748024b525ec4710be40194282c84"}, - {file = "matplotlib-3.10.7-cp313-cp313-win_amd64.whl", hash = "sha256:744991e0cc863dd669c8dc9136ca4e6e0082be2070b9d793cbd64bec872a6815"}, - {file = "matplotlib-3.10.7-cp313-cp313-win_arm64.whl", hash = "sha256:fba2974df0bf8ce3c995fa84b79cde38326e0f7b5409e7a3a481c1141340bcf7"}, - {file = "matplotlib-3.10.7-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:932c55d1fa7af4423422cb6a492a31cbcbdbe68fd1a9a3f545aa5e7a143b5355"}, - {file = "matplotlib-3.10.7-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5e38c2d581d62ee729a6e144c47a71b3f42fb4187508dbbf4fe71d5612c3433b"}, - {file = "matplotlib-3.10.7-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:786656bb13c237bbcebcd402f65f44dd61ead60ee3deb045af429d889c8dbc67"}, - {file = "matplotlib-3.10.7-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:09d7945a70ea43bf9248f4b6582734c2fe726723204a76eca233f24cffc7ef67"}, - {file = "matplotlib-3.10.7-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:d0b181e9fa8daf1d9f2d4c547527b167cb8838fc587deabca7b5c01f97199e84"}, - {file = "matplotlib-3.10.7-cp313-cp313t-win_amd64.whl", hash = "sha256:31963603041634ce1a96053047b40961f7a29eb8f9a62e80cc2c0427aa1d22a2"}, - {file = "matplotlib-3.10.7-cp313-cp313t-win_arm64.whl", hash = "sha256:aebed7b50aa6ac698c90f60f854b47e48cd2252b30510e7a1feddaf5a3f72cbf"}, - {file = "matplotlib-3.10.7-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:d883460c43e8c6b173fef244a2341f7f7c0e9725c7fe68306e8e44ed9c8fb100"}, - {file = "matplotlib-3.10.7-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:07124afcf7a6504eafcb8ce94091c5898bbdd351519a1beb5c45f7a38c67e77f"}, - {file = "matplotlib-3.10.7-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c17398b709a6cce3d9fdb1595c33e356d91c098cd9486cb2cc21ea2ea418e715"}, - {file = "matplotlib-3.10.7-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7146d64f561498764561e9cd0ed64fcf582e570fc519e6f521e2d0cfd43365e1"}, - {file = "matplotlib-3.10.7-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:90ad854c0a435da3104c01e2c6f0028d7e719b690998a2333d7218db80950722"}, - {file = "matplotlib-3.10.7-cp314-cp314-win_amd64.whl", hash = "sha256:4645fc5d9d20ffa3a39361fcdbcec731382763b623b72627806bf251b6388866"}, - {file = "matplotlib-3.10.7-cp314-cp314-win_arm64.whl", hash = "sha256:9257be2f2a03415f9105c486d304a321168e61ad450f6153d77c69504ad764bb"}, - {file = "matplotlib-3.10.7-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1e4bbad66c177a8fdfa53972e5ef8be72a5f27e6a607cec0d8579abd0f3102b1"}, - {file = "matplotlib-3.10.7-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:d8eb7194b084b12feb19142262165832fc6ee879b945491d1c3d4660748020c4"}, - {file = "matplotlib-3.10.7-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b4d41379b05528091f00e1728004f9a8d7191260f3862178b88e8fd770206318"}, - {file = "matplotlib-3.10.7-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4a74f79fafb2e177f240579bc83f0b60f82cc47d2f1d260f422a0627207008ca"}, - {file = "matplotlib-3.10.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:702590829c30aada1e8cef0568ddbffa77ca747b4d6e36c6d173f66e301f89cc"}, - {file = "matplotlib-3.10.7-cp314-cp314t-win_amd64.whl", hash = "sha256:f79d5de970fc90cd5591f60053aecfce1fcd736e0303d9f0bf86be649fa68fb8"}, - {file = "matplotlib-3.10.7-cp314-cp314t-win_arm64.whl", hash = "sha256:cb783436e47fcf82064baca52ce748af71725d0352e1d31564cbe9c95df92b9c"}, - {file = "matplotlib-3.10.7-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:5c09cf8f2793f81368f49f118b6f9f937456362bee282eac575cca7f84cda537"}, - {file = "matplotlib-3.10.7-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:de66744b2bb88d5cd27e80dfc2ec9f0517d0a46d204ff98fe9e5f2864eb67657"}, - {file = "matplotlib-3.10.7-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:53cc80662dd197ece414dd5b66e07370201515a3eaf52e7c518c68c16814773b"}, - {file = "matplotlib-3.10.7-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:15112bcbaef211bd663fa935ec33313b948e214454d949b723998a43357b17b0"}, - {file = "matplotlib-3.10.7-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d2a959c640cdeecdd2ec3136e8ea0441da59bcaf58d67e9c590740addba2cb68"}, - {file = "matplotlib-3.10.7-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3886e47f64611046bc1db523a09dd0a0a6bed6081e6f90e13806dd1d1d1b5e91"}, - {file = "matplotlib-3.10.7.tar.gz", hash = "sha256:a06ba7e2a2ef9131c79c49e63dad355d2d878413a0376c1727c8b9335ff731c7"}, + {file = "matplotlib-3.10.8-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:00270d217d6b20d14b584c521f810d60c5c78406dc289859776550df837dcda7"}, + {file = "matplotlib-3.10.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:37b3c1cc42aa184b3f738cfa18c1c1d72fd496d85467a6cf7b807936d39aa656"}, + {file = "matplotlib-3.10.8-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ee40c27c795bda6a5292e9cff9890189d32f7e3a0bf04e0e3c9430c4a00c37df"}, + {file = "matplotlib-3.10.8-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a48f2b74020919552ea25d222d5cc6af9ca3f4eb43a93e14d068457f545c2a17"}, + {file = "matplotlib-3.10.8-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f254d118d14a7f99d616271d6c3c27922c092dac11112670b157798b89bf4933"}, + {file = "matplotlib-3.10.8-cp310-cp310-win_amd64.whl", hash = "sha256:f9b587c9c7274c1613a30afabf65a272114cd6cdbe67b3406f818c79d7ab2e2a"}, + {file = "matplotlib-3.10.8-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:6be43b667360fef5c754dda5d25a32e6307a03c204f3c0fc5468b78fa87b4160"}, + {file = "matplotlib-3.10.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a2b336e2d91a3d7006864e0990c83b216fcdca64b5a6484912902cef87313d78"}, + {file = "matplotlib-3.10.8-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:efb30e3baaea72ce5928e32bab719ab4770099079d66726a62b11b1ef7273be4"}, + {file = "matplotlib-3.10.8-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d56a1efd5bfd61486c8bc968fa18734464556f0fb8e51690f4ac25d85cbbbbc2"}, + {file = "matplotlib-3.10.8-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:238b7ce5717600615c895050239ec955d91f321c209dd110db988500558e70d6"}, + {file = "matplotlib-3.10.8-cp311-cp311-win_amd64.whl", hash = "sha256:18821ace09c763ec93aef5eeff087ee493a24051936d7b9ebcad9662f66501f9"}, + {file = "matplotlib-3.10.8-cp311-cp311-win_arm64.whl", hash = "sha256:bab485bcf8b1c7d2060b4fcb6fc368a9e6f4cd754c9c2fea281f4be21df394a2"}, + {file = "matplotlib-3.10.8-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:64fcc24778ca0404ce0cb7b6b77ae1f4c7231cdd60e6778f999ee05cbd581b9a"}, + {file = "matplotlib-3.10.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b9a5ca4ac220a0cdd1ba6bcba3608547117d30468fefce49bb26f55c1a3d5c58"}, + {file = "matplotlib-3.10.8-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3ab4aabc72de4ff77b3ec33a6d78a68227bf1123465887f9905ba79184a1cc04"}, + {file = "matplotlib-3.10.8-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:24d50994d8c5816ddc35411e50a86ab05f575e2530c02752e02538122613371f"}, + {file = "matplotlib-3.10.8-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:99eefd13c0dc3b3c1b4d561c1169e65fe47aab7b8158754d7c084088e2329466"}, + {file = "matplotlib-3.10.8-cp312-cp312-win_amd64.whl", hash = "sha256:dd80ecb295460a5d9d260df63c43f4afbdd832d725a531f008dad1664f458adf"}, + {file = "matplotlib-3.10.8-cp312-cp312-win_arm64.whl", hash = "sha256:3c624e43ed56313651bc18a47f838b60d7b8032ed348911c54906b130b20071b"}, + {file = "matplotlib-3.10.8-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:3f2e409836d7f5ac2f1c013110a4d50b9f7edc26328c108915f9075d7d7a91b6"}, + {file = "matplotlib-3.10.8-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:56271f3dac49a88d7fca5060f004d9d22b865f743a12a23b1e937a0be4818ee1"}, + {file = "matplotlib-3.10.8-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a0a7f52498f72f13d4a25ea70f35f4cb60642b466cbb0a9be951b5bc3f45a486"}, + {file = "matplotlib-3.10.8-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:646d95230efb9ca614a7a594d4fcacde0ac61d25e37dd51710b36477594963ce"}, + {file = "matplotlib-3.10.8-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f89c151aab2e2e23cb3fe0acad1e8b82841fd265379c4cecd0f3fcb34c15e0f6"}, + {file = "matplotlib-3.10.8-cp313-cp313-win_amd64.whl", hash = "sha256:e8ea3e2d4066083e264e75c829078f9e149fa119d27e19acd503de65e0b13149"}, + {file = "matplotlib-3.10.8-cp313-cp313-win_arm64.whl", hash = "sha256:c108a1d6fa78a50646029cb6d49808ff0fc1330fda87fa6f6250c6b5369b6645"}, + {file = "matplotlib-3.10.8-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:ad3d9833a64cf48cc4300f2b406c3d0f4f4724a91c0bd5640678a6ba7c102077"}, + {file = "matplotlib-3.10.8-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:eb3823f11823deade26ce3b9f40dcb4a213da7a670013929f31d5f5ed1055b22"}, + {file = "matplotlib-3.10.8-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d9050fee89a89ed57b4fb2c1bfac9a3d0c57a0d55aed95949eedbc42070fea39"}, + {file = "matplotlib-3.10.8-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b44d07310e404ba95f8c25aa5536f154c0a8ec473303535949e52eb71d0a1565"}, + {file = "matplotlib-3.10.8-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:0a33deb84c15ede243aead39f77e990469fff93ad1521163305095b77b72ce4a"}, + {file = "matplotlib-3.10.8-cp313-cp313t-win_amd64.whl", hash = "sha256:3a48a78d2786784cc2413e57397981fb45c79e968d99656706018d6e62e57958"}, + {file = "matplotlib-3.10.8-cp313-cp313t-win_arm64.whl", hash = "sha256:15d30132718972c2c074cd14638c7f4592bd98719e2308bccea40e0538bc0cb5"}, + {file = "matplotlib-3.10.8-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:b53285e65d4fa4c86399979e956235deb900be5baa7fc1218ea67fbfaeaadd6f"}, + {file = "matplotlib-3.10.8-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:32f8dce744be5569bebe789e46727946041199030db8aeb2954d26013a0eb26b"}, + {file = "matplotlib-3.10.8-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4cf267add95b1c88300d96ca837833d4112756045364f5c734a2276038dae27d"}, + {file = "matplotlib-3.10.8-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2cf5bd12cecf46908f286d7838b2abc6c91cda506c0445b8223a7c19a00df008"}, + {file = "matplotlib-3.10.8-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:41703cc95688f2516b480f7f339d8851a6035f18e100ee6a32bc0b8536a12a9c"}, + {file = "matplotlib-3.10.8-cp314-cp314-win_amd64.whl", hash = "sha256:83d282364ea9f3e52363da262ce32a09dfe241e4080dcedda3c0db059d3c1f11"}, + {file = "matplotlib-3.10.8-cp314-cp314-win_arm64.whl", hash = "sha256:2c1998e92cd5999e295a731bcb2911c75f597d937341f3030cc24ef2733d78a8"}, + {file = "matplotlib-3.10.8-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:b5a2b97dbdc7d4f353ebf343744f1d1f1cca8aa8bfddb4262fcf4306c3761d50"}, + {file = "matplotlib-3.10.8-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:3f5c3e4da343bba819f0234186b9004faba952cc420fbc522dc4e103c1985908"}, + {file = "matplotlib-3.10.8-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f62550b9a30afde8c1c3ae450e5eb547d579dd69b25c2fc7a1c67f934c1717a"}, + {file = "matplotlib-3.10.8-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:495672de149445ec1b772ff2c9ede9b769e3cb4f0d0aa7fa730d7f59e2d4e1c1"}, + {file = "matplotlib-3.10.8-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:595ba4d8fe983b88f0eec8c26a241e16d6376fe1979086232f481f8f3f67494c"}, + {file = "matplotlib-3.10.8-cp314-cp314t-win_amd64.whl", hash = "sha256:25d380fe8b1dc32cf8f0b1b448470a77afb195438bafdf1d858bfb876f3edf7b"}, + {file = "matplotlib-3.10.8-cp314-cp314t-win_arm64.whl", hash = "sha256:113bb52413ea508ce954a02c10ffd0d565f9c3bc7f2eddc27dfe1731e71c7b5f"}, + {file = "matplotlib-3.10.8-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:f97aeb209c3d2511443f8797e3e5a569aebb040d4f8bc79aa3ee78a8fb9e3dd8"}, + {file = "matplotlib-3.10.8-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:fb061f596dad3a0f52b60dc6a5dec4a0c300dec41e058a7efe09256188d170b7"}, + {file = "matplotlib-3.10.8-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:12d90df9183093fcd479f4172ac26b322b1248b15729cb57f42f71f24c7e37a3"}, + {file = "matplotlib-3.10.8-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:6da7c2ce169267d0d066adcf63758f0604aa6c3eebf67458930f9d9b79ad1db1"}, + {file = "matplotlib-3.10.8-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:9153c3292705be9f9c64498a8872118540c3f4123d1a1c840172edf262c8be4a"}, + {file = "matplotlib-3.10.8-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1ae029229a57cd1e8fe542485f27e7ca7b23aa9e8944ddb4985d0bc444f1eca2"}, + {file = "matplotlib-3.10.8.tar.gz", hash = "sha256:2299372c19d56bcd35cf05a2738308758d32b9eaed2371898d8f5bd33f084aa3"}, ] [package.dependencies] @@ -4390,14 +4830,14 @@ files = [ [[package]] name = "mcp" -version = "1.23.1" +version = "1.24.0" description = "Model Context Protocol SDK" optional = false python-versions = ">=3.10" groups = ["main"] files = [ - {file = "mcp-1.23.1-py3-none-any.whl", hash = "sha256:3ce897fcc20a41bd50b4c58d3aa88085f11f505dcc0eaed48930012d34c731d8"}, - {file = "mcp-1.23.1.tar.gz", hash = "sha256:7403e053e8e2283b1e6ae631423cb54736933fea70b32422152e6064556cd298"}, + {file = "mcp-1.24.0-py3-none-any.whl", hash = "sha256:db130e103cc50ddc3dffc928382f33ba3eaef0b711f7a87c05e7ded65b1ca062"}, + {file = "mcp-1.24.0.tar.gz", hash = "sha256:aeaad134664ce56f2721d1abf300666a1e8348563f4d3baff361c3b652448efc"}, ] [package.dependencies] @@ -4779,6 +5219,67 @@ files = [ {file = "mwparserfromhell-0.7.2.tar.gz", hash = "sha256:f4193072e9ea93b9e88f772f60a02125c0602d32890d0bbdcb275ed58c8b3763"}, ] +[[package]] +name = "mypy" +version = "1.19.1" +description = "Optional static typing for Python" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "mypy-1.19.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5f05aa3d375b385734388e844bc01733bd33c644ab48e9684faa54e5389775ec"}, + {file = "mypy-1.19.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:022ea7279374af1a5d78dfcab853fe6a536eebfda4b59deab53cd21f6cd9f00b"}, + {file = "mypy-1.19.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee4c11e460685c3e0c64a4c5de82ae143622410950d6be863303a1c4ba0e36d6"}, + {file = "mypy-1.19.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de759aafbae8763283b2ee5869c7255391fbc4de3ff171f8f030b5ec48381b74"}, + {file = "mypy-1.19.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ab43590f9cd5108f41aacf9fca31841142c786827a74ab7cc8a2eacb634e09a1"}, + {file = "mypy-1.19.1-cp310-cp310-win_amd64.whl", hash = "sha256:2899753e2f61e571b3971747e302d5f420c3fd09650e1951e99f823bc3089dac"}, + {file = "mypy-1.19.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d8dfc6ab58ca7dda47d9237349157500468e404b17213d44fc1cb77bce532288"}, + {file = "mypy-1.19.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e3f276d8493c3c97930e354b2595a44a21348b320d859fb4a2b9f66da9ed27ab"}, + {file = "mypy-1.19.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2abb24cf3f17864770d18d673c85235ba52456b36a06b6afc1e07c1fdcd3d0e6"}, + {file = "mypy-1.19.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a009ffa5a621762d0c926a078c2d639104becab69e79538a494bcccb62cc0331"}, + {file = "mypy-1.19.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f7cee03c9a2e2ee26ec07479f38ea9c884e301d42c6d43a19d20fb014e3ba925"}, + {file = "mypy-1.19.1-cp311-cp311-win_amd64.whl", hash = "sha256:4b84a7a18f41e167f7995200a1d07a4a6810e89d29859df936f1c3923d263042"}, + {file = "mypy-1.19.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a8174a03289288c1f6c46d55cef02379b478bfbc8e358e02047487cad44c6ca1"}, + {file = "mypy-1.19.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ffcebe56eb09ff0c0885e750036a095e23793ba6c2e894e7e63f6d89ad51f22e"}, + {file = "mypy-1.19.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b64d987153888790bcdb03a6473d321820597ab8dd9243b27a92153c4fa50fd2"}, + {file = "mypy-1.19.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c35d298c2c4bba75feb2195655dfea8124d855dfd7343bf8b8c055421eaf0cf8"}, + {file = "mypy-1.19.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:34c81968774648ab5ac09c29a375fdede03ba253f8f8287847bd480782f73a6a"}, + {file = "mypy-1.19.1-cp312-cp312-win_amd64.whl", hash = "sha256:b10e7c2cd7870ba4ad9b2d8a6102eb5ffc1f16ca35e3de6bfa390c1113029d13"}, + {file = "mypy-1.19.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e3157c7594ff2ef1634ee058aafc56a82db665c9438fd41b390f3bde1ab12250"}, + {file = "mypy-1.19.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bdb12f69bcc02700c2b47e070238f42cb87f18c0bc1fc4cdb4fb2bc5fd7a3b8b"}, + {file = "mypy-1.19.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f859fb09d9583a985be9a493d5cfc5515b56b08f7447759a0c5deaf68d80506e"}, + {file = "mypy-1.19.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c9a6538e0415310aad77cb94004ca6482330fece18036b5f360b62c45814c4ef"}, + {file = "mypy-1.19.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:da4869fc5e7f62a88f3fe0b5c919d1d9f7ea3cef92d3689de2823fd27e40aa75"}, + {file = "mypy-1.19.1-cp313-cp313-win_amd64.whl", hash = "sha256:016f2246209095e8eda7538944daa1d60e1e8134d98983b9fc1e92c1fc0cb8dd"}, + {file = "mypy-1.19.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:06e6170bd5836770e8104c8fdd58e5e725cfeb309f0a6c681a811f557e97eac1"}, + {file = "mypy-1.19.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:804bd67b8054a85447c8954215a906d6eff9cabeabe493fb6334b24f4bfff718"}, + {file = "mypy-1.19.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:21761006a7f497cb0d4de3d8ef4ca70532256688b0523eee02baf9eec895e27b"}, + {file = "mypy-1.19.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:28902ee51f12e0f19e1e16fbe2f8f06b6637f482c459dd393efddd0ec7f82045"}, + {file = "mypy-1.19.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:481daf36a4c443332e2ae9c137dfee878fcea781a2e3f895d54bd3002a900957"}, + {file = "mypy-1.19.1-cp314-cp314-win_amd64.whl", hash = "sha256:8bb5c6f6d043655e055be9b542aa5f3bdd30e4f3589163e85f93f3640060509f"}, + {file = "mypy-1.19.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7bcfc336a03a1aaa26dfce9fff3e287a3ba99872a157561cbfcebe67c13308e3"}, + {file = "mypy-1.19.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b7951a701c07ea584c4fe327834b92a30825514c868b1f69c30445093fdd9d5a"}, + {file = "mypy-1.19.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b13cfdd6c87fc3efb69ea4ec18ef79c74c3f98b4e5498ca9b85ab3b2c2329a67"}, + {file = "mypy-1.19.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4f28f99c824ecebcdaa2e55d82953e38ff60ee5ec938476796636b86afa3956e"}, + {file = "mypy-1.19.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c608937067d2fc5a4dd1a5ce92fd9e1398691b8c5d012d66e1ddd430e9244376"}, + {file = "mypy-1.19.1-cp39-cp39-win_amd64.whl", hash = "sha256:409088884802d511ee52ca067707b90c883426bd95514e8cfda8281dc2effe24"}, + {file = "mypy-1.19.1-py3-none-any.whl", hash = "sha256:f1235f5ea01b7db5468d53ece6aaddf1ad0b88d9e7462b86ef96fe04995d7247"}, + {file = "mypy-1.19.1.tar.gz", hash = "sha256:19d88bb05303fe63f71dd2c6270daca27cb9401c4ca8255fe50d1d920e0eb9ba"}, +] + +[package.dependencies] +librt = {version = ">=0.6.2", markers = "platform_python_implementation != \"PyPy\""} +mypy_extensions = ">=1.0.0" +pathspec = ">=0.9.0" +typing_extensions = ">=4.6.0" + +[package.extras] +dmypy = ["psutil (>=4.0)"] +faster-cache = ["orjson"] +install-types = ["pip"] +mypyc = ["setuptools (>=50)"] +reports = ["lxml"] + [[package]] name = "mypy-extensions" version = "1.1.0" @@ -4793,14 +5294,14 @@ files = [ [[package]] name = "narwhals" -version = "2.13.0" +version = "2.14.0" description = "Extremely lightweight compatibility layer between dataframe libraries" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "narwhals-2.13.0-py3-none-any.whl", hash = "sha256:9b795523c179ca78204e3be53726da374168f906e38de2ff174c2363baaaf481"}, - {file = "narwhals-2.13.0.tar.gz", hash = "sha256:ee94c97f4cf7cfeebbeca8d274784df8b3d7fd3f955ce418af998d405576fdd9"}, + {file = "narwhals-2.14.0-py3-none-any.whl", hash = "sha256:b56796c9a00179bd757d15282c540024e1d5c910b19b8c9944d836566c030acf"}, + {file = "narwhals-2.14.0.tar.gz", hash = "sha256:98be155c3599db4d5c211e565c3190c398c87e7bf5b3cdb157dece67641946e0"}, ] [package.extras] @@ -4939,14 +5440,14 @@ files = [ [[package]] name = "networkx" -version = "3.6" +version = "3.6.1" description = "Python package for creating and manipulating graphs and networks" optional = false -python-versions = ">=3.11" +python-versions = "!=3.14.1,>=3.11" groups = ["main"] files = [ - {file = "networkx-3.6-py3-none-any.whl", hash = "sha256:cdb395b105806062473d3be36458d8f1459a4e4b98e236a66c3a48996e07684f"}, - {file = "networkx-3.6.tar.gz", hash = "sha256:285276002ad1f7f7da0f7b42f004bcba70d381e936559166363707fdad3d72ad"}, + {file = "networkx-3.6.1-py3-none-any.whl", hash = "sha256:d47fbf302e7d9cbbb9e2555a0d267983d2aa476bac30e90dfbe5669bd57f3762"}, + {file = "networkx-3.6.1.tar.gz", hash = "sha256:26b7c357accc0c8cde558ad486283728b65b6a95d85ee1cd66bafab4c8168509"}, ] [package.extras] @@ -4987,19 +5488,19 @@ files = [ [[package]] name = "notebook" -version = "7.5.0" +version = "7.5.1" description = "Jupyter Notebook - A web-based notebook environment for interactive computing" optional = false python-versions = ">=3.9" groups = ["dev"] files = [ - {file = "notebook-7.5.0-py3-none-any.whl", hash = "sha256:3300262d52905ca271bd50b22617681d95f08a8360d099e097726e6d2efb5811"}, - {file = "notebook-7.5.0.tar.gz", hash = "sha256:3b27eaf9913033c28dde92d02139414c608992e1df4b969c843219acf2ff95e4"}, + {file = "notebook-7.5.1-py3-none-any.whl", hash = "sha256:f4e2451c19910c33b88709b84537e11f6368c1cdff1aa0c43db701aea535dd44"}, + {file = "notebook-7.5.1.tar.gz", hash = "sha256:b2fb4cef4d47d08c33aecce1c6c6e84be05436fbd791f88fce8df9fbca088b75"}, ] [package.dependencies] jupyter-server = ">=2.4.0,<3" -jupyterlab = ">=4.5.0rc0,<4.6" +jupyterlab = ">=4.5.1,<4.6" jupyterlab-server = ">=2.28.0,<3" notebook-shim = ">=0.2,<0.3" tornado = ">=6.2.0" @@ -5360,22 +5861,105 @@ datalib = ["numpy (>=1)", "pandas (>=1.2.3)", "pandas-stubs (>=1.1.0.11)"] realtime = ["websockets (>=13,<16)"] voice-helpers = ["numpy (>=2.0.2)", "sounddevice (>=0.5.1)"] +[[package]] +name = "openapi-pydantic" +version = "0.5.1" +description = "Pydantic OpenAPI schema implementation" +optional = false +python-versions = "<4.0,>=3.8" +groups = ["main"] +files = [ + {file = "openapi_pydantic-0.5.1-py3-none-any.whl", hash = "sha256:a3a09ef4586f5bd760a8df7f43028b60cafb6d9f61de2acba9574766255ab146"}, + {file = "openapi_pydantic-0.5.1.tar.gz", hash = "sha256:ff6835af6bde7a459fb93eb93bb92b8749b754fc6e51b2f1590a19dc3005ee0d"}, +] + +[package.dependencies] +pydantic = ">=1.8" + [[package]] name = "opentelemetry-api" -version = "1.39.0" +version = "1.39.1" description = "OpenTelemetry Python API" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "opentelemetry_api-1.39.0-py3-none-any.whl", hash = "sha256:3c3b3ca5c5687b1b5b37e5c5027ff68eacea8675241b29f13110a8ffbb8f0459"}, - {file = "opentelemetry_api-1.39.0.tar.gz", hash = "sha256:6130644268c5ac6bdffaf660ce878f10906b3e789f7e2daa5e169b047a2933b9"}, + {file = "opentelemetry_api-1.39.1-py3-none-any.whl", hash = "sha256:2edd8463432a7f8443edce90972169b195e7d6a05500cd29e6d13898187c9950"}, + {file = "opentelemetry_api-1.39.1.tar.gz", hash = "sha256:fbde8c80e1b937a2c61f20347e91c0c18a1940cecf012d62e65a7caf08967c9c"}, ] [package.dependencies] importlib-metadata = ">=6.0,<8.8.0" typing-extensions = ">=4.5.0" +[[package]] +name = "opentelemetry-exporter-prometheus" +version = "0.60b1" +description = "Prometheus Metric Exporter for OpenTelemetry" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "opentelemetry_exporter_prometheus-0.60b1-py3-none-any.whl", hash = "sha256:49f59178de4f4590e3cef0b8b95cf6e071aae70e1f060566df5546fad773b8fd"}, + {file = "opentelemetry_exporter_prometheus-0.60b1.tar.gz", hash = "sha256:a4011b46906323f71724649d301b4dc188aaa068852e814f4df38cc76eac616b"}, +] + +[package.dependencies] +opentelemetry-api = ">=1.12,<2.0" +opentelemetry-sdk = ">=1.39.1,<1.40.0" +prometheus-client = ">=0.5.0,<1.0.0" + +[[package]] +name = "opentelemetry-instrumentation" +version = "0.60b1" +description = "Instrumentation Tools & Auto Instrumentation for OpenTelemetry Python" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "opentelemetry_instrumentation-0.60b1-py3-none-any.whl", hash = "sha256:04480db952b48fb1ed0073f822f0ee26012b7be7c3eac1a3793122737c78632d"}, + {file = "opentelemetry_instrumentation-0.60b1.tar.gz", hash = "sha256:57ddc7974c6eb35865af0426d1a17132b88b2ed8586897fee187fd5b8944bd6a"}, +] + +[package.dependencies] +opentelemetry-api = ">=1.4,<2.0" +opentelemetry-semantic-conventions = "0.60b1" +packaging = ">=18.0" +wrapt = ">=1.0.0,<2.0.0" + +[[package]] +name = "opentelemetry-sdk" +version = "1.39.1" +description = "OpenTelemetry Python SDK" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "opentelemetry_sdk-1.39.1-py3-none-any.whl", hash = "sha256:4d5482c478513ecb0a5d938dcc61394e647066e0cc2676bee9f3af3f3f45f01c"}, + {file = "opentelemetry_sdk-1.39.1.tar.gz", hash = "sha256:cf4d4563caf7bff906c9f7967e2be22d0d6b349b908be0d90fb21c8e9c995cc6"}, +] + +[package.dependencies] +opentelemetry-api = "1.39.1" +opentelemetry-semantic-conventions = "0.60b1" +typing-extensions = ">=4.5.0" + +[[package]] +name = "opentelemetry-semantic-conventions" +version = "0.60b1" +description = "OpenTelemetry Semantic Conventions" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "opentelemetry_semantic_conventions-0.60b1-py3-none-any.whl", hash = "sha256:9fa8c8b0c110da289809292b0591220d3a7b53c1526a23021e977d68597893fb"}, + {file = "opentelemetry_semantic_conventions-0.60b1.tar.gz", hash = "sha256:87c228b5a0669b748c76d76df6c364c369c28f1c465e50f661e39737e84bc953"}, +] + +[package.dependencies] +opentelemetry-api = "1.39.1" +typing-extensions = ">=4.5.0" + [[package]] name = "orjson" version = "3.11.5" @@ -5657,6 +6241,18 @@ files = [ qa = ["flake8 (==5.0.4)", "mypy (==0.971)", "types-setuptools (==67.2.0.1)"] testing = ["docopt", "pytest"] +[[package]] +name = "pathable" +version = "0.4.4" +description = "Object-oriented paths" +optional = false +python-versions = "<4.0.0,>=3.7.0" +groups = ["main"] +files = [ + {file = "pathable-0.4.4-py3-none-any.whl", hash = "sha256:5ae9e94793b6ef5a4cbe0a7ce9dbbefc1eec38df253763fd0aeeacf2762dbbc2"}, + {file = "pathable-0.4.4.tar.gz", hash = "sha256:6905a3cd17804edfac7875b5f6c9142a218c7caef78693c2dbbbfbac186d88b2"}, +] + [[package]] name = "pathspec" version = "0.12.1" @@ -5669,16 +6265,33 @@ files = [ {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, ] +[[package]] +name = "pathvalidate" +version = "3.3.1" +description = "pathvalidate is a Python library to sanitize/validate a string such as filenames/file-paths/etc." +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "pathvalidate-3.3.1-py3-none-any.whl", hash = "sha256:5263baab691f8e1af96092fa5137ee17df5bdfbd6cff1fcac4d6ef4bc2e1735f"}, + {file = "pathvalidate-3.3.1.tar.gz", hash = "sha256:b18c07212bfead624345bb8e1d6141cdcf15a39736994ea0b94035ad2b1ba177"}, +] + +[package.extras] +docs = ["Sphinx (>=2.4)", "sphinx_rtd_theme (>=1.2.2)", "urllib3 (<2)"] +readme = ["path (>=13,<18)", "readmemaker (>=1.2.0)"] +test = ["Faker (>=1.0.8)", "allpairspy (>=2)", "click (>=6.2)", "pytest (>=6.0.1)", "pytest-md-report (>=0.6.2)"] + [[package]] name = "pbs-installer" -version = "2025.12.5" +version = "2025.12.17" description = "Installer for Python Build Standalone" optional = false python-versions = ">=3.8" groups = ["dev"] files = [ - {file = "pbs_installer-2025.12.5-py3-none-any.whl", hash = "sha256:1cc1977bbb8367bb3ce03a7954f58d6e765e184fd873b73674076f9e8788cd08"}, - {file = "pbs_installer-2025.12.5.tar.gz", hash = "sha256:3d03347d73f4a58ddfbe26473b622065476633e57de44fb025527c8852e4efa7"}, + {file = "pbs_installer-2025.12.17-py3-none-any.whl", hash = "sha256:1a899ac5af9ca4c59a7a7944ec3fcf7ad7e40d5684b12eadcfbeee7c59d44123"}, + {file = "pbs_installer-2025.12.17.tar.gz", hash = "sha256:cf32043fadd168c17a1b18c1c3f801090281bd5c9ce101e2deb7e0e51c8279dd"}, ] [package.dependencies] @@ -5849,20 +6462,20 @@ type = ["mypy (>=1.18.2)"] [[package]] name = "playwright" -version = "1.56.0" +version = "1.57.0" description = "A high-level API to automate web browsers" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "playwright-1.56.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:b33eb89c516cbc6723f2e3523bada4a4eb0984a9c411325c02d7016a5d625e9c"}, - {file = "playwright-1.56.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:b228b3395212b9472a4ee5f1afe40d376eef9568eb039fcb3e563de8f4f4657b"}, - {file = "playwright-1.56.0-py3-none-macosx_11_0_universal2.whl", hash = "sha256:0ef7e6fd653267798a8a968ff7aa2dcac14398b7dd7440ef57524e01e0fbbd65"}, - {file = "playwright-1.56.0-py3-none-manylinux1_x86_64.whl", hash = "sha256:404be089b49d94bc4c1fe0dfb07664bda5ffe87789034a03bffb884489bdfb5c"}, - {file = "playwright-1.56.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:64cda7cf4e51c0d35dab55190841bfcdfb5871685ec22cb722cd0ad2df183e34"}, - {file = "playwright-1.56.0-py3-none-win32.whl", hash = "sha256:d87b79bcb082092d916a332c27ec9732e0418c319755d235d93cc6be13bdd721"}, - {file = "playwright-1.56.0-py3-none-win_amd64.whl", hash = "sha256:3c7fc49bb9e673489bf2622855f9486d41c5101bbed964638552b864c4591f94"}, - {file = "playwright-1.56.0-py3-none-win_arm64.whl", hash = "sha256:2745490ae8dd58d27e5ea4d9aa28402e8e2991eb84fb4b2fd5fbde2106716f6f"}, + {file = "playwright-1.57.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:9351c1ac3dfd9b3820fe7fc4340d96c0d3736bb68097b9b7a69bd45d25e9370c"}, + {file = "playwright-1.57.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:a4a9d65027bce48eeba842408bcc1421502dfd7e41e28d207e94260fa93ca67e"}, + {file = "playwright-1.57.0-py3-none-macosx_11_0_universal2.whl", hash = "sha256:99104771abc4eafee48f47dac2369e0015516dc1ce8c409807d2dd440828b9a4"}, + {file = "playwright-1.57.0-py3-none-manylinux1_x86_64.whl", hash = "sha256:284ed5a706b7c389a06caa431b2f0ba9ac4130113c3a779767dda758c2497bb1"}, + {file = "playwright-1.57.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38a1bae6c0a07839cdeaddbc0756b3b2b85e476c07945f64ece08f1f956a86f1"}, + {file = "playwright-1.57.0-py3-none-win32.whl", hash = "sha256:1dd93b265688da46e91ecb0606d36f777f8eadcf7fbef12f6426b20bf0c9137c"}, + {file = "playwright-1.57.0-py3-none-win_amd64.whl", hash = "sha256:6caefb08ed2c6f29d33b8088d05d09376946e49a73be19271c8cd5384b82b14c"}, + {file = "playwright-1.57.0-py3-none-win_arm64.whl", hash = "sha256:5f065f5a133dbc15e6e7c71e7bc04f258195755b1c32a432b792e28338c8335e"}, ] [package.dependencies] @@ -5996,14 +6609,14 @@ test = ["betamax (>=0.8,<0.9)", "pytest (>=2.7.3)", "urllib3 (==1.26.*)"] [[package]] name = "pre-commit" -version = "4.5.0" +version = "4.5.1" description = "A framework for managing and maintaining multi-language pre-commit hooks." optional = false python-versions = ">=3.10" groups = ["dev"] files = [ - {file = "pre_commit-4.5.0-py2.py3-none-any.whl", hash = "sha256:25e2ce09595174d9c97860a95609f9f852c0614ba602de3561e267547f2335e1"}, - {file = "pre_commit-4.5.0.tar.gz", hash = "sha256:dc5a065e932b19fc1d4c653c6939068fe54325af8e741e74e88db4d28a4dd66b"}, + {file = "pre_commit-4.5.1-py2.py3-none-any.whl", hash = "sha256:3b3afd891e97337708c1674210f8eba659b52a38ea5f822ff142d10786221f77"}, + {file = "pre_commit-4.5.1.tar.gz", hash = "sha256:eb545fcff725875197837263e977ea257a402056661f09dae08e4b149b030a61"}, ] [package.dependencies] @@ -6019,7 +6632,7 @@ version = "0.23.1" description = "Python client for the Prometheus monitoring system." optional = false python-versions = ">=3.9" -groups = ["dev"] +groups = ["main", "dev"] files = [ {file = "prometheus_client-0.23.1-py3-none-any.whl", hash = "sha256:dd1913e6e76b59cfe44e7a4b83e01afc9873c1bdfd2ed8739f1e76aeca115f99"}, {file = "prometheus_client-0.23.1.tar.gz", hash = "sha256:6ae8f9081eaaaf153a2e959d2e6c4f4fb57b12ef76c8c7980202f1e57b48b2ce"}, @@ -6268,6 +6881,61 @@ files = [ [package.extras] tests = ["pytest"] +[[package]] +name = "py-key-value-aio" +version = "0.3.0" +description = "Async Key-Value" +optional = false +python-versions = ">=3.10" +groups = ["main"] +files = [ + {file = "py_key_value_aio-0.3.0-py3-none-any.whl", hash = "sha256:1c781915766078bfd608daa769fefb97e65d1d73746a3dfb640460e322071b64"}, + {file = "py_key_value_aio-0.3.0.tar.gz", hash = "sha256:858e852fcf6d696d231266da66042d3355a7f9871650415feef9fca7a6cd4155"}, +] + +[package.dependencies] +beartype = ">=0.20.0" +cachetools = {version = ">=5.0.0", optional = true, markers = "extra == \"memory\""} +diskcache = {version = ">=5.0.0", optional = true, markers = "extra == \"disk\""} +keyring = {version = ">=25.6.0", optional = true, markers = "extra == \"keyring\""} +pathvalidate = {version = ">=3.3.1", optional = true, markers = "extra == \"disk\""} +py-key-value-shared = "0.3.0" +redis = {version = ">=4.3.0", optional = true, markers = "extra == \"redis\""} + +[package.extras] +disk = ["diskcache (>=5.0.0)", "pathvalidate (>=3.3.1)"] +duckdb = ["duckdb (>=1.1.1)", "pytz (>=2025.2)"] +dynamodb = ["aioboto3 (>=13.3.0)", "types-aiobotocore-dynamodb (>=2.16.0)"] +elasticsearch = ["aiohttp (>=3.12)", "elasticsearch (>=8.0.0)"] +filetree = ["aiofile (>=3.5.0)", "anyio (>=4.4.0)"] +keyring = ["keyring (>=25.6.0)"] +keyring-linux = ["dbus-python (>=1.4.0)", "keyring (>=25.6.0)"] +memcached = ["aiomcache (>=0.8.0)"] +memory = ["cachetools (>=5.0.0)"] +mongodb = ["pymongo (>=4.0.0)"] +pydantic = ["pydantic (>=2.11.9)"] +redis = ["redis (>=4.3.0)"] +rocksdb = ["rocksdict (>=0.3.2) ; python_full_version < \"3.12.0\"", "rocksdict (>=0.3.24) ; python_full_version >= \"3.12.0\""] +valkey = ["valkey-glide (>=2.1.0)"] +vault = ["hvac (>=2.3.0)", "types-hvac (>=2.3.0)"] +wrappers-encryption = ["cryptography (>=45.0.0)"] + +[[package]] +name = "py-key-value-shared" +version = "0.3.0" +description = "Shared Key-Value" +optional = false +python-versions = ">=3.10" +groups = ["main"] +files = [ + {file = "py_key_value_shared-0.3.0-py3-none-any.whl", hash = "sha256:5b0efba7ebca08bb158b1e93afc2f07d30b8f40c2fc12ce24a4c0d84f42f9298"}, + {file = "py_key_value_shared-0.3.0.tar.gz", hash = "sha256:8fdd786cf96c3e900102945f92aa1473138ebe960ef49da1c833790160c28a4b"}, +] + +[package.dependencies] +beartype = ">=0.20.0" +typing-extensions = ">=4.15.0" + [[package]] name = "py4j" version = "0.10.9.7" @@ -6401,6 +7069,7 @@ files = [ [package.dependencies] annotated-types = ">=0.6.0" +email-validator = {version = ">=2.0.0", optional = true, markers = "extra == \"email\""} pydantic-core = "2.41.5" typing-extensions = ">=4.14.1" typing-inspection = ">=0.4.2" @@ -6696,6 +7365,32 @@ files = [ [package.extras] dev = ["tox"] +[[package]] +name = "pydocket" +version = "0.16.1" +description = "A distributed background task system for Python functions" +optional = false +python-versions = ">=3.10" +groups = ["main"] +files = [ + {file = "pydocket-0.16.1-py3-none-any.whl", hash = "sha256:bc6ccf7e91164761def854b4014101abf23c3cc2fb7d0fa2c4e07ea3bf6a1826"}, + {file = "pydocket-0.16.1.tar.gz", hash = "sha256:8663cb6dc801d8b8d703541fb665f4099c84f4d10d8f3fd441e208b080aa4826"}, +] + +[package.dependencies] +cloudpickle = ">=3.1.1" +fakeredis = {version = ">=2.32.1", extras = ["lua"]} +opentelemetry-api = ">=1.33.0" +opentelemetry-exporter-prometheus = ">=0.60b0" +opentelemetry-instrumentation = ">=0.60b0" +prometheus-client = ">=0.21.1" +py-key-value-aio = {version = ">=0.3.0", extras = ["memory", "redis"]} +python-json-logger = ">=2.0.7" +redis = ">=5" +rich = ">=13.9.4" +typer = ">=0.15.1" +typing-extensions = ">=4.12.0" + [[package]] name = "pyee" version = "13.0.0" @@ -6764,14 +7459,14 @@ tests = ["coverage[toml] (==5.0.4)", "pytest (>=6.0.0,<7.0.0)"] [[package]] name = "pymdown-extensions" -version = "10.18" +version = "10.19.1" description = "Extension pack for Python Markdown." optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "pymdown_extensions-10.18-py3-none-any.whl", hash = "sha256:090bca72be43f7d3186374e23c782899dbef9dc153ef24c59dcd3c346f9ffcae"}, - {file = "pymdown_extensions-10.18.tar.gz", hash = "sha256:20252abe6367354b24191431617a072ee6be9f68c5afcc74ea5573508a61f9e5"}, + {file = "pymdown_extensions-10.19.1-py3-none-any.whl", hash = "sha256:e8698a66055b1dc0dca2a7f2c9d0ea6f5faa7834a9c432e3535ab96c0c4e509b"}, + {file = "pymdown_extensions-10.19.1.tar.gz", hash = "sha256:4969c691009a389fb1f9712dd8e7bd70dcc418d15a0faf70acb5117d022f7de8"}, ] [package.dependencies] @@ -6860,6 +7555,18 @@ files = [ [package.extras] diagrams = ["jinja2", "railroad-diagrams"] +[[package]] +name = "pyperclip" +version = "1.11.0" +description = "A cross-platform clipboard module for Python. (Only handles plain text for now.)" +optional = false +python-versions = "*" +groups = ["main"] +files = [ + {file = "pyperclip-1.11.0-py3-none-any.whl", hash = "sha256:299403e9ff44581cb9ba2ffeed69c7aa96a008622ad0c46cb575ca75b5b84273"}, + {file = "pyperclip-1.11.0.tar.gz", hash = "sha256:244035963e4428530d9e3a6101a1ef97209c6825edab1567beac148ccc1db1b6"}, +] + [[package]] name = "pyproject-hooks" version = "1.2.0" @@ -6884,6 +7591,22 @@ files = [ {file = "PyPyDispatcher-2.1.2.tar.gz", hash = "sha256:b6bec5dfcff9d2535bca2b23c80eae367b1ac250a645106948d315fcfa9130f2"}, ] +[[package]] +name = "pyrate-limiter" +version = "3.9.0" +description = "Python Rate-Limiter using Leaky-Bucket Algorithm" +optional = false +python-versions = "<4.0,>=3.8" +groups = ["main"] +files = [ + {file = "pyrate_limiter-3.9.0-py3-none-any.whl", hash = "sha256:77357840c8cf97a36d67005d4e090787043f54000c12c2b414ff65657653e378"}, + {file = "pyrate_limiter-3.9.0.tar.gz", hash = "sha256:6b882e2c77cda07a241d3730975daea4258344b39c878f1dd8849df73f70b0ce"}, +] + +[package.extras] +all = ["filelock (>=3.0)", "psycopg[pool] (>=3.1.18,<4.0.0)", "redis (>=5.0.0,<6.0.0)"] +docs = ["furo (>=2022.3.4,<2023.0.0)", "myst-parser (>=0.17)", "sphinx (>=4.3.0,<5.0.0)", "sphinx-autodoc-typehints (>=1.17,<2.0)", "sphinx-copybutton (>=0.5)", "sphinxcontrib-apidoc (>=0.3,<0.4)"] + [[package]] name = "pyspark" version = "3.5.7" @@ -6905,6 +7628,25 @@ mllib = ["numpy (>=1.15,<2)"] pandas-on-spark = ["numpy (>=1.15,<2)", "pandas (>=1.0.5)", "pyarrow (>=4.0.0)"] sql = ["numpy (>=1.15,<2)", "pandas (>=1.0.5)", "pyarrow (>=4.0.0)"] +[[package]] +name = "pyspark-mcp" +version = "0.0.3" +description = "Minimal PySpark MCP server inspired by LakeSail" +optional = false +python-versions = "<4.0,>=3.11" +groups = ["main"] +files = [ + {file = "pyspark_mcp-0.0.3-py3-none-any.whl", hash = "sha256:e2f95a9a3451d8da1760c7baec0de4b78052974971bfec0ab6312a7fc61b91b6"}, + {file = "pyspark_mcp-0.0.3.tar.gz", hash = "sha256:ce47eb0cd838ab70aaf0775fa946c05109d35b1428548f426dbca9e4312fb2be"}, +] + +[package.dependencies] +click = ">=8.0" +fastmcp = ">=2.10.6" +loguru = "*" +pandas = ">=2.3.2,<3.0.0" +pyspark = ">=3.5" + [[package]] name = "pytest" version = "8.4.2" @@ -7004,7 +7746,7 @@ version = "4.0.0" description = "JSON Log Formatter for the Python Logging Package" optional = false python-versions = ">=3.8" -groups = ["dev"] +groups = ["main", "dev"] files = [ {file = "python_json_logger-4.0.0-py3-none-any.whl", hash = "sha256:af09c9daf6a813aa4cc7180395f50f2a9e5fa056034c9953aec92e381c5ba1e2"}, {file = "python_json_logger-4.0.0.tar.gz", hash = "sha256:f58e68eb46e1faed27e0f574a55a0455eecd7b8a5b88b85a784519ba3cff047f"}, @@ -7015,14 +7757,14 @@ dev = ["backports.zoneinfo ; python_version < \"3.9\"", "black", "build", "freez [[package]] name = "python-multipart" -version = "0.0.20" +version = "0.0.21" description = "A streaming multipart parser for Python" optional = false -python-versions = ">=3.8" +python-versions = ">=3.10" groups = ["main"] files = [ - {file = "python_multipart-0.0.20-py3-none-any.whl", hash = "sha256:8a62d3a8335e06589fe01f2a3e178cdcc632f3fbe0d492ad9ee0ec35aab1f104"}, - {file = "python_multipart-0.0.20.tar.gz", hash = "sha256:8dd0cab45b8e23064ae09147625994d090fa46f5b0d1e13af944c331a7fa9d13"}, + {file = "python_multipart-0.0.21-py3-none-any.whl", hash = "sha256:cf7a6713e01c87aa35387f4774e812c4361150938d20d232800f75ffcf266090"}, + {file = "python_multipart-0.0.21.tar.gz", hash = "sha256:7137ebd4d3bbf70ea1622998f902b97a29434a9e8dc40eb203bbcf7c2a2cba92"}, ] [[package]] @@ -7412,16 +8154,34 @@ files = [ [package.extras] all = ["numpy"] +[[package]] +name = "redis" +version = "7.1.0" +description = "Python client for Redis database and key-value store" +optional = false +python-versions = ">=3.10" +groups = ["main"] +files = [ + {file = "redis-7.1.0-py3-none-any.whl", hash = "sha256:23c52b208f92b56103e17c5d06bdc1a6c2c0b3106583985a76a18f83b265de2b"}, + {file = "redis-7.1.0.tar.gz", hash = "sha256:b1cc3cfa5a2cb9c2ab3ba700864fb0ad75617b41f01352ce5779dabf6d5f9c3c"}, +] + +[package.extras] +circuit-breaker = ["pybreaker (>=1.4.0)"] +hiredis = ["hiredis (>=3.2.0)"] +jwt = ["pyjwt (>=2.9.0)"] +ocsp = ["cryptography (>=36.0.1)", "pyopenssl (>=20.0.1)", "requests (>=2.31.0)"] + [[package]] name = "referencing" -version = "0.37.0" +version = "0.36.2" description = "JSON Referencing + Python" optional = false -python-versions = ">=3.10" +python-versions = ">=3.9" groups = ["main", "dev"] files = [ - {file = "referencing-0.37.0-py3-none-any.whl", hash = "sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231"}, - {file = "referencing-0.37.0.tar.gz", hash = "sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8"}, + {file = "referencing-0.36.2-py3-none-any.whl", hash = "sha256:e8699adbbf8b5c7de96d8ffa0eb5c158b3beafce084968e2ea8bb08c6794dcd0"}, + {file = "referencing-0.36.2.tar.gz", hash = "sha256:df2e89862cd09deabbdba16944cc3f10feb6b3e6f18e902f7cc25609a34775aa"}, ] [package.dependencies] @@ -7670,6 +8430,25 @@ pygments = ">=2.13.0,<3.0.0" [package.extras] jupyter = ["ipywidgets (>=7.5.1,<9)"] +[[package]] +name = "rich-rst" +version = "1.3.2" +description = "A beautiful reStructuredText renderer for rich" +optional = false +python-versions = "*" +groups = ["main"] +files = [ + {file = "rich_rst-1.3.2-py3-none-any.whl", hash = "sha256:a99b4907cbe118cf9d18b0b44de272efa61f15117c61e39ebdc431baf5df722a"}, + {file = "rich_rst-1.3.2.tar.gz", hash = "sha256:a1196fdddf1e364b02ec68a05e8ff8f6914fee10fbca2e6b6735f166bb0da8d4"}, +] + +[package.dependencies] +docutils = "*" +rich = ">=12.0.0" + +[package.extras] +docs = ["sphinx"] + [[package]] name = "rpds-py" version = "0.30.0" @@ -7877,59 +8656,65 @@ torch = ["packaging", "safetensors[numpy]", "torch (>=1.10)"] [[package]] name = "scikit-learn" -version = "1.7.2" +version = "1.8.0" description = "A set of python modules for machine learning and data mining" optional = false -python-versions = ">=3.10" +python-versions = ">=3.11" groups = ["main"] files = [ - {file = "scikit_learn-1.7.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b33579c10a3081d076ab403df4a4190da4f4432d443521674637677dc91e61f"}, - {file = "scikit_learn-1.7.2-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:36749fb62b3d961b1ce4fedf08fa57a1986cd409eff2d783bca5d4b9b5fce51c"}, - {file = "scikit_learn-1.7.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7a58814265dfc52b3295b1900cfb5701589d30a8bb026c7540f1e9d3499d5ec8"}, - {file = "scikit_learn-1.7.2-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a847fea807e278f821a0406ca01e387f97653e284ecbd9750e3ee7c90347f18"}, - {file = "scikit_learn-1.7.2-cp310-cp310-win_amd64.whl", hash = "sha256:ca250e6836d10e6f402436d6463d6c0e4d8e0234cfb6a9a47835bd392b852ce5"}, - {file = "scikit_learn-1.7.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c7509693451651cd7361d30ce4e86a1347493554f172b1c72a39300fa2aea79e"}, - {file = "scikit_learn-1.7.2-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:0486c8f827c2e7b64837c731c8feff72c0bd2b998067a8a9cbc10643c31f0fe1"}, - {file = "scikit_learn-1.7.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:89877e19a80c7b11a2891a27c21c4894fb18e2c2e077815bcade10d34287b20d"}, - {file = "scikit_learn-1.7.2-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8da8bf89d4d79aaec192d2bda62f9b56ae4e5b4ef93b6a56b5de4977e375c1f1"}, - {file = "scikit_learn-1.7.2-cp311-cp311-win_amd64.whl", hash = "sha256:9b7ed8d58725030568523e937c43e56bc01cadb478fc43c042a9aca1dacb3ba1"}, - {file = "scikit_learn-1.7.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:8d91a97fa2b706943822398ab943cde71858a50245e31bc71dba62aab1d60a96"}, - {file = "scikit_learn-1.7.2-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:acbc0f5fd2edd3432a22c69bed78e837c70cf896cd7993d71d51ba6708507476"}, - {file = "scikit_learn-1.7.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e5bf3d930aee75a65478df91ac1225ff89cd28e9ac7bd1196853a9229b6adb0b"}, - {file = "scikit_learn-1.7.2-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b4d6e9deed1a47aca9fe2f267ab8e8fe82ee20b4526b2c0cd9e135cea10feb44"}, - {file = "scikit_learn-1.7.2-cp312-cp312-win_amd64.whl", hash = "sha256:6088aa475f0785e01bcf8529f55280a3d7d298679f50c0bb70a2364a82d0b290"}, - {file = "scikit_learn-1.7.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0b7dacaa05e5d76759fb071558a8b5130f4845166d88654a0f9bdf3eb57851b7"}, - {file = "scikit_learn-1.7.2-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:abebbd61ad9e1deed54cca45caea8ad5f79e1b93173dece40bb8e0c658dbe6fe"}, - {file = "scikit_learn-1.7.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:502c18e39849c0ea1a5d681af1dbcf15f6cce601aebb657aabbfe84133c1907f"}, - {file = "scikit_learn-1.7.2-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7a4c328a71785382fe3fe676a9ecf2c86189249beff90bf85e22bdb7efaf9ae0"}, - {file = "scikit_learn-1.7.2-cp313-cp313-win_amd64.whl", hash = "sha256:63a9afd6f7b229aad94618c01c252ce9e6fa97918c5ca19c9a17a087d819440c"}, - {file = "scikit_learn-1.7.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:9acb6c5e867447b4e1390930e3944a005e2cb115922e693c08a323421a6966e8"}, - {file = "scikit_learn-1.7.2-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:2a41e2a0ef45063e654152ec9d8bcfc39f7afce35b08902bfe290c2498a67a6a"}, - {file = "scikit_learn-1.7.2-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:98335fb98509b73385b3ab2bd0639b1f610541d3988ee675c670371d6a87aa7c"}, - {file = "scikit_learn-1.7.2-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:191e5550980d45449126e23ed1d5e9e24b2c68329ee1f691a3987476e115e09c"}, - {file = "scikit_learn-1.7.2-cp313-cp313t-win_amd64.whl", hash = "sha256:57dc4deb1d3762c75d685507fbd0bc17160144b2f2ba4ccea5dc285ab0d0e973"}, - {file = "scikit_learn-1.7.2-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fa8f63940e29c82d1e67a45d5297bdebbcb585f5a5a50c4914cc2e852ab77f33"}, - {file = "scikit_learn-1.7.2-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:f95dc55b7902b91331fa4e5845dd5bde0580c9cd9612b1b2791b7e80c3d32615"}, - {file = "scikit_learn-1.7.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9656e4a53e54578ad10a434dc1f993330568cfee176dff07112b8785fb413106"}, - {file = "scikit_learn-1.7.2-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96dc05a854add0e50d3f47a1ef21a10a595016da5b007c7d9cd9d0bffd1fcc61"}, - {file = "scikit_learn-1.7.2-cp314-cp314-win_amd64.whl", hash = "sha256:bb24510ed3f9f61476181e4db51ce801e2ba37541def12dc9333b946fc7a9cf8"}, - {file = "scikit_learn-1.7.2.tar.gz", hash = "sha256:20e9e49ecd130598f1ca38a1d85090e1a600147b9c02fa6f15d69cb53d968fda"}, -] - -[package.dependencies] -joblib = ">=1.2.0" -numpy = ">=1.22.0" -scipy = ">=1.8.0" -threadpoolctl = ">=3.1.0" - -[package.extras] -benchmark = ["matplotlib (>=3.5.0)", "memory_profiler (>=0.57.0)", "pandas (>=1.4.0)"] -build = ["cython (>=3.0.10)", "meson-python (>=0.17.1)", "numpy (>=1.22.0)", "scipy (>=1.8.0)"] -docs = ["Pillow (>=8.4.0)", "matplotlib (>=3.5.0)", "memory_profiler (>=0.57.0)", "numpydoc (>=1.2.0)", "pandas (>=1.4.0)", "plotly (>=5.14.0)", "polars (>=0.20.30)", "pooch (>=1.6.0)", "pydata-sphinx-theme (>=0.15.3)", "scikit-image (>=0.19.0)", "seaborn (>=0.9.0)", "sphinx (>=7.3.7)", "sphinx-copybutton (>=0.5.2)", "sphinx-design (>=0.5.0)", "sphinx-design (>=0.6.0)", "sphinx-gallery (>=0.17.1)", "sphinx-prompt (>=1.4.0)", "sphinx-remove-toctrees (>=1.0.0.post1)", "sphinxcontrib-sass (>=0.3.4)", "sphinxext-opengraph (>=0.9.1)", "towncrier (>=24.8.0)"] -examples = ["matplotlib (>=3.5.0)", "pandas (>=1.4.0)", "plotly (>=5.14.0)", "pooch (>=1.6.0)", "scikit-image (>=0.19.0)", "seaborn (>=0.9.0)"] -install = ["joblib (>=1.2.0)", "numpy (>=1.22.0)", "scipy (>=1.8.0)", "threadpoolctl (>=3.1.0)"] + {file = "scikit_learn-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:146b4d36f800c013d267b29168813f7a03a43ecd2895d04861f1240b564421da"}, + {file = "scikit_learn-1.8.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:f984ca4b14914e6b4094c5d52a32ea16b49832c03bd17a110f004db3c223e8e1"}, + {file = "scikit_learn-1.8.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5e30adb87f0cc81c7690a84f7932dd66be5bac57cfe16b91cb9151683a4a2d3b"}, + {file = "scikit_learn-1.8.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ada8121bcb4dac28d930febc791a69f7cb1673c8495e5eee274190b73a4559c1"}, + {file = "scikit_learn-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:c57b1b610bd1f40ba43970e11ce62821c2e6569e4d74023db19c6b26f246cb3b"}, + {file = "scikit_learn-1.8.0-cp311-cp311-win_arm64.whl", hash = "sha256:2838551e011a64e3053ad7618dda9310175f7515f1742fa2d756f7c874c05961"}, + {file = "scikit_learn-1.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5fb63362b5a7ddab88e52b6dbb47dac3fd7dafeee740dc6c8d8a446ddedade8e"}, + {file = "scikit_learn-1.8.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:5025ce924beccb28298246e589c691fe1b8c1c96507e6d27d12c5fadd85bfd76"}, + {file = "scikit_learn-1.8.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4496bb2cf7a43ce1a2d7524a79e40bc5da45cf598dbf9545b7e8316ccba47bb4"}, + {file = "scikit_learn-1.8.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a0bcfe4d0d14aec44921545fd2af2338c7471de9cb701f1da4c9d85906ab847a"}, + {file = "scikit_learn-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:35c007dedb2ffe38fe3ee7d201ebac4a2deccd2408e8621d53067733e3c74809"}, + {file = "scikit_learn-1.8.0-cp312-cp312-win_arm64.whl", hash = "sha256:8c497fff237d7b4e07e9ef1a640887fa4fb765647f86fbe00f969ff6280ce2bb"}, + {file = "scikit_learn-1.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0d6ae97234d5d7079dc0040990a6f7aeb97cb7fa7e8945f1999a429b23569e0a"}, + {file = "scikit_learn-1.8.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:edec98c5e7c128328124a029bceb09eda2d526997780fef8d65e9a69eead963e"}, + {file = "scikit_learn-1.8.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:74b66d8689d52ed04c271e1329f0c61635bcaf5b926db9b12d58914cdc01fe57"}, + {file = "scikit_learn-1.8.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8fdf95767f989b0cfedb85f7ed8ca215d4be728031f56ff5a519ee1e3276dc2e"}, + {file = "scikit_learn-1.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:2de443b9373b3b615aec1bb57f9baa6bb3a9bd093f1269ba95c17d870422b271"}, + {file = "scikit_learn-1.8.0-cp313-cp313-win_arm64.whl", hash = "sha256:eddde82a035681427cbedded4e6eff5e57fa59216c2e3e90b10b19ab1d0a65c3"}, + {file = "scikit_learn-1.8.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:7cc267b6108f0a1499a734167282c00c4ebf61328566b55ef262d48e9849c735"}, + {file = "scikit_learn-1.8.0-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:fe1c011a640a9f0791146011dfd3c7d9669785f9fed2b2a5f9e207536cf5c2fd"}, + {file = "scikit_learn-1.8.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:72358cce49465d140cc4e7792015bb1f0296a9742d5622c67e31399b75468b9e"}, + {file = "scikit_learn-1.8.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:80832434a6cc114f5219211eec13dcbc16c2bac0e31ef64c6d346cde3cf054cb"}, + {file = "scikit_learn-1.8.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ee787491dbfe082d9c3013f01f5991658b0f38aa8177e4cd4bf434c58f551702"}, + {file = "scikit_learn-1.8.0-cp313-cp313t-win_arm64.whl", hash = "sha256:bf97c10a3f5a7543f9b88cbf488d33d175e9146115a451ae34568597ba33dcde"}, + {file = "scikit_learn-1.8.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:c22a2da7a198c28dd1a6e1136f19c830beab7fdca5b3e5c8bba8394f8a5c45b3"}, + {file = "scikit_learn-1.8.0-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:6b595b07a03069a2b1740dc08c2299993850ea81cce4fe19b2421e0c970de6b7"}, + {file = "scikit_learn-1.8.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:29ffc74089f3d5e87dfca4c2c8450f88bdc61b0fc6ed5d267f3988f19a1309f6"}, + {file = "scikit_learn-1.8.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fb65db5d7531bccf3a4f6bec3462223bea71384e2cda41da0f10b7c292b9e7c4"}, + {file = "scikit_learn-1.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:56079a99c20d230e873ea40753102102734c5953366972a71d5cb39a32bc40c6"}, + {file = "scikit_learn-1.8.0-cp314-cp314-win_arm64.whl", hash = "sha256:3bad7565bc9cf37ce19a7c0d107742b320c1285df7aab1a6e2d28780df167242"}, + {file = "scikit_learn-1.8.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:4511be56637e46c25721e83d1a9cea9614e7badc7040c4d573d75fbe257d6fd7"}, + {file = "scikit_learn-1.8.0-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:a69525355a641bf8ef136a7fa447672fb54fe8d60cab5538d9eb7c6438543fb9"}, + {file = "scikit_learn-1.8.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c2656924ec73e5939c76ac4c8b026fc203b83d8900362eb2599d8aee80e4880f"}, + {file = "scikit_learn-1.8.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:15fc3b5d19cc2be65404786857f2e13c70c83dd4782676dd6814e3b89dc8f5b9"}, + {file = "scikit_learn-1.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:00d6f1d66fbcf4eba6e356e1420d33cc06c70a45bb1363cd6f6a8e4ebbbdece2"}, + {file = "scikit_learn-1.8.0-cp314-cp314t-win_arm64.whl", hash = "sha256:f28dd15c6bb0b66ba09728cf09fd8736c304be29409bd8445a080c1280619e8c"}, + {file = "scikit_learn-1.8.0.tar.gz", hash = "sha256:9bccbb3b40e3de10351f8f5068e105d0f4083b1a65fa07b6634fbc401a6287fd"}, +] + +[package.dependencies] +joblib = ">=1.3.0" +numpy = ">=1.24.1" +scipy = ">=1.10.0" +threadpoolctl = ">=3.2.0" + +[package.extras] +benchmark = ["matplotlib (>=3.6.1)", "memory_profiler (>=0.57.0)", "pandas (>=1.5.0)"] +build = ["cython (>=3.1.2)", "meson-python (>=0.17.1)", "numpy (>=1.24.1)", "scipy (>=1.10.0)"] +docs = ["Pillow (>=10.1.0)", "matplotlib (>=3.6.1)", "memory_profiler (>=0.57.0)", "numpydoc (>=1.2.0)", "pandas (>=1.5.0)", "plotly (>=5.18.0)", "polars (>=0.20.30)", "pooch (>=1.8.0)", "pydata-sphinx-theme (>=0.15.3)", "scikit-image (>=0.22.0)", "seaborn (>=0.13.0)", "sphinx (>=7.3.7)", "sphinx-copybutton (>=0.5.2)", "sphinx-design (>=0.6.0)", "sphinx-gallery (>=0.17.1)", "sphinx-prompt (>=1.4.0)", "sphinx-remove-toctrees (>=1.0.0.post1)", "sphinxcontrib-sass (>=0.3.4)", "sphinxext-opengraph (>=0.9.1)", "towncrier (>=24.8.0)"] +examples = ["matplotlib (>=3.6.1)", "pandas (>=1.5.0)", "plotly (>=5.18.0)", "pooch (>=1.8.0)", "scikit-image (>=0.22.0)", "seaborn (>=0.13.0)"] +install = ["joblib (>=1.3.0)", "numpy (>=1.24.1)", "scipy (>=1.10.0)", "threadpoolctl (>=3.2.0)"] maintenance = ["conda-lock (==3.0.1)"] -tests = ["matplotlib (>=3.5.0)", "mypy (>=1.15)", "numpydoc (>=1.2.0)", "pandas (>=1.4.0)", "polars (>=0.20.30)", "pooch (>=1.6.0)", "pyamg (>=4.2.1)", "pyarrow (>=12.0.0)", "pytest (>=7.1.2)", "pytest-cov (>=2.9.0)", "ruff (>=0.11.7)", "scikit-image (>=0.19.0)"] +tests = ["matplotlib (>=3.6.1)", "mypy (>=1.15)", "numpydoc (>=1.2.0)", "pandas (>=1.5.0)", "polars (>=0.20.30)", "pooch (>=1.8.0)", "pyamg (>=5.0.0)", "pyarrow (>=12.0.0)", "pytest (>=7.1.2)", "pytest-cov (>=2.9.0)", "ruff (>=0.11.7)"] [[package]] name = "scipy" @@ -8064,6 +8849,26 @@ dev = ["flake8", "flit", "mypy", "pandas-stubs", "pre-commit", "pytest", "pytest docs = ["ipykernel", "nbconvert", "numpydoc", "pydata_sphinx_theme (==0.10.0rc2)", "pyyaml", "sphinx (<6.0.0)", "sphinx-copybutton", "sphinx-design", "sphinx-issues"] stats = ["scipy (>=1.7)", "statsmodels (>=0.12)"] +[[package]] +name = "sec-edgar-api" +version = "1.1.0" +description = "Unofficial SEC EDGAR API wrapper for Python" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "sec_edgar_api-1.1.0-py3-none-any.whl", hash = "sha256:1a07499b5235d42ce5cd347e1974ece9b21a497a6ae8397a9f1a9ae6540c9b48"}, + {file = "sec_edgar_api-1.1.0.tar.gz", hash = "sha256:edee783aa6c29e4499f04c059a9e71c9aa7af4274750a9609ba12103f82c49ab"}, +] + +[package.dependencies] +pyrate-limiter = ">=3.1.0" +requests = "*" + +[package.extras] +doc = ["sphinx", "sphinx-autodoc-typehints"] +test = ["pytest", "pytest-cov"] + [[package]] name = "secretstorage" version = "3.5.0" @@ -8100,43 +8905,43 @@ win32 = ["pywin32 ; sys_platform == \"win32\""] [[package]] name = "sentence-transformers" -version = "5.1.2" +version = "5.2.0" description = "Embeddings, Retrieval, and Reranking" optional = false -python-versions = ">=3.9" +python-versions = ">=3.10" groups = ["main"] files = [ - {file = "sentence_transformers-5.1.2-py3-none-any.whl", hash = "sha256:724ce0ea62200f413f1a5059712aff66495bc4e815a1493f7f9bca242414c333"}, - {file = "sentence_transformers-5.1.2.tar.gz", hash = "sha256:0f6c8bd916a78dc65b366feb8d22fd885efdb37432e7630020d113233af2b856"}, + {file = "sentence_transformers-5.2.0-py3-none-any.whl", hash = "sha256:aa57180f053687d29b08206766ae7db549be5074f61849def7b17bf0b8025ca2"}, + {file = "sentence_transformers-5.2.0.tar.gz", hash = "sha256:acaeb38717de689f3dab45d5e5a02ebe2f75960a4764ea35fea65f58a4d3019f"}, ] [package.dependencies] huggingface-hub = ">=0.20.0" -Pillow = "*" scikit-learn = "*" scipy = "*" torch = ">=1.11.0" tqdm = "*" -transformers = ">=4.41.0,<5.0.0" +transformers = ">=4.41.0,<6.0.0" typing_extensions = ">=4.5.0" [package.extras] -dev = ["accelerate (>=0.20.3)", "datasets", "peft", "pre-commit", "pytest", "pytest-cov"] -onnx = ["optimum[onnxruntime] (>=1.23.1)"] -onnx-gpu = ["optimum[onnxruntime-gpu] (>=1.23.1)"] -openvino = ["optimum-intel[openvino] (>=1.20.0)"] +dev = ["Pillow", "accelerate (>=0.20.3)", "datasets", "peft", "pre-commit", "pytest", "pytest-cov"] +image = ["Pillow"] +onnx = ["optimum-onnx[onnxruntime]"] +onnx-gpu = ["optimum-onnx[onnxruntime-gpu]"] +openvino = ["optimum-intel[openvino]"] train = ["accelerate (>=0.20.3)", "datasets"] [[package]] name = "sentry-sdk" -version = "2.47.0" +version = "2.48.0" description = "Python client for Sentry (https://sentry.io)" optional = false python-versions = ">=3.6" groups = ["main"] files = [ - {file = "sentry_sdk-2.47.0-py2.py3-none-any.whl", hash = "sha256:d72f8c61025b7d1d9e52510d03a6247b280094a327dd900d987717a4fce93412"}, - {file = "sentry_sdk-2.47.0.tar.gz", hash = "sha256:8218891d5e41b4ea8d61d2aed62ed10c80e39d9f2959d6f939efbf056857e050"}, + {file = "sentry_sdk-2.48.0-py2.py3-none-any.whl", hash = "sha256:6b12ac256769d41825d9b7518444e57fa35b5642df4c7c5e322af4d2c8721172"}, + {file = "sentry_sdk-2.48.0.tar.gz", hash = "sha256:5213190977ff7fdff8a58b722fb807f8d5524a80488626ebeda1b5676c0c1473"}, ] [package.dependencies] @@ -8253,7 +9058,7 @@ version = "1.5.4" description = "Tool to Detect Surrounding Shell" optional = false python-versions = ">=3.7" -groups = ["dev"] +groups = ["main", "dev"] files = [ {file = "shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686"}, {file = "shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de"}, @@ -8295,6 +9100,18 @@ files = [ {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, ] +[[package]] +name = "sortedcontainers" +version = "2.4.0" +description = "Sorted Containers -- Sorted List, Sorted Dict, Sorted Set" +optional = false +python-versions = "*" +groups = ["main"] +files = [ + {file = "sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0"}, + {file = "sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88"}, +] + [[package]] name = "soupsieve" version = "2.8" @@ -8335,69 +9152,64 @@ resolved_reference = "ff1d38d595b2c62700ccba6c10dc613832fe368c" [[package]] name = "sqlalchemy" -version = "2.0.44" +version = "2.0.45" description = "Database Abstraction Library" optional = false python-versions = ">=3.7" groups = ["main"] files = [ - {file = "SQLAlchemy-2.0.44-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:471733aabb2e4848d609141a9e9d56a427c0a038f4abf65dd19d7a21fd563632"}, - {file = "SQLAlchemy-2.0.44-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48bf7d383a35e668b984c805470518b635d48b95a3c57cb03f37eaa3551b5f9f"}, - {file = "SQLAlchemy-2.0.44-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bf4bb6b3d6228fcf3a71b50231199fb94d2dd2611b66d33be0578ea3e6c2726"}, - {file = "SQLAlchemy-2.0.44-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:e998cf7c29473bd077704cea3577d23123094311f59bdc4af551923b168332b1"}, - {file = "SQLAlchemy-2.0.44-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:ebac3f0b5732014a126b43c2b7567f2f0e0afea7d9119a3378bde46d3dcad88e"}, - {file = "SQLAlchemy-2.0.44-cp37-cp37m-win32.whl", hash = "sha256:3255d821ee91bdf824795e936642bbf43a4c7cedf5d1aed8d24524e66843aa74"}, - {file = "SQLAlchemy-2.0.44-cp37-cp37m-win_amd64.whl", hash = "sha256:78e6c137ba35476adb5432103ae1534f2f5295605201d946a4198a0dea4b38e7"}, - {file = "sqlalchemy-2.0.44-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7c77f3080674fc529b1bd99489378c7f63fcb4ba7f8322b79732e0258f0ea3ce"}, - {file = "sqlalchemy-2.0.44-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4c26ef74ba842d61635b0152763d057c8d48215d5be9bb8b7604116a059e9985"}, - {file = "sqlalchemy-2.0.44-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4a172b31785e2f00780eccab00bc240ccdbfdb8345f1e6063175b3ff12ad1b0"}, - {file = "sqlalchemy-2.0.44-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9480c0740aabd8cb29c329b422fb65358049840b34aba0adf63162371d2a96e"}, - {file = "sqlalchemy-2.0.44-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:17835885016b9e4d0135720160db3095dc78c583e7b902b6be799fb21035e749"}, - {file = "sqlalchemy-2.0.44-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cbe4f85f50c656d753890f39468fcd8190c5f08282caf19219f684225bfd5fd2"}, - {file = "sqlalchemy-2.0.44-cp310-cp310-win32.whl", hash = "sha256:2fcc4901a86ed81dc76703f3b93ff881e08761c63263c46991081fd7f034b165"}, - {file = "sqlalchemy-2.0.44-cp310-cp310-win_amd64.whl", hash = "sha256:9919e77403a483ab81e3423151e8ffc9dd992c20d2603bf17e4a8161111e55f5"}, - {file = "sqlalchemy-2.0.44-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0fe3917059c7ab2ee3f35e77757062b1bea10a0b6ca633c58391e3f3c6c488dd"}, - {file = "sqlalchemy-2.0.44-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:de4387a354ff230bc979b46b2207af841dc8bf29847b6c7dbe60af186d97aefa"}, - {file = "sqlalchemy-2.0.44-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c3678a0fb72c8a6a29422b2732fe423db3ce119c34421b5f9955873eb9b62c1e"}, - {file = "sqlalchemy-2.0.44-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3cf6872a23601672d61a68f390e44703442639a12ee9dd5a88bbce52a695e46e"}, - {file = "sqlalchemy-2.0.44-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:329aa42d1be9929603f406186630135be1e7a42569540577ba2c69952b7cf399"}, - {file = "sqlalchemy-2.0.44-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:70e03833faca7166e6a9927fbee7c27e6ecde436774cd0b24bbcc96353bce06b"}, - {file = "sqlalchemy-2.0.44-cp311-cp311-win32.whl", hash = "sha256:253e2f29843fb303eca6b2fc645aca91fa7aa0aa70b38b6950da92d44ff267f3"}, - {file = "sqlalchemy-2.0.44-cp311-cp311-win_amd64.whl", hash = "sha256:7a8694107eb4308a13b425ca8c0e67112f8134c846b6e1f722698708741215d5"}, - {file = "sqlalchemy-2.0.44-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:72fea91746b5890f9e5e0997f16cbf3d53550580d76355ba2d998311b17b2250"}, - {file = "sqlalchemy-2.0.44-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:585c0c852a891450edbb1eaca8648408a3cc125f18cf433941fa6babcc359e29"}, - {file = "sqlalchemy-2.0.44-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b94843a102efa9ac68a7a30cd46df3ff1ed9c658100d30a725d10d9c60a2f44"}, - {file = "sqlalchemy-2.0.44-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:119dc41e7a7defcefc57189cfa0e61b1bf9c228211aba432b53fb71ef367fda1"}, - {file = "sqlalchemy-2.0.44-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0765e318ee9179b3718c4fd7ba35c434f4dd20332fbc6857a5e8df17719c24d7"}, - {file = "sqlalchemy-2.0.44-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2e7b5b079055e02d06a4308d0481658e4f06bc7ef211567edc8f7d5dce52018d"}, - {file = "sqlalchemy-2.0.44-cp312-cp312-win32.whl", hash = "sha256:846541e58b9a81cce7dee8329f352c318de25aa2f2bbe1e31587eb1f057448b4"}, - {file = "sqlalchemy-2.0.44-cp312-cp312-win_amd64.whl", hash = "sha256:7cbcb47fd66ab294703e1644f78971f6f2f1126424d2b300678f419aa73c7b6e"}, - {file = "sqlalchemy-2.0.44-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ff486e183d151e51b1d694c7aa1695747599bb00b9f5f604092b54b74c64a8e1"}, - {file = "sqlalchemy-2.0.44-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0b1af8392eb27b372ddb783b317dea0f650241cea5bd29199b22235299ca2e45"}, - {file = "sqlalchemy-2.0.44-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2b61188657e3a2b9ac4e8f04d6cf8e51046e28175f79464c67f2fd35bceb0976"}, - {file = "sqlalchemy-2.0.44-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b87e7b91a5d5973dda5f00cd61ef72ad75a1db73a386b62877d4875a8840959c"}, - {file = "sqlalchemy-2.0.44-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:15f3326f7f0b2bfe406ee562e17f43f36e16167af99c4c0df61db668de20002d"}, - {file = "sqlalchemy-2.0.44-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1e77faf6ff919aa8cd63f1c4e561cac1d9a454a191bb864d5dd5e545935e5a40"}, - {file = "sqlalchemy-2.0.44-cp313-cp313-win32.whl", hash = "sha256:ee51625c2d51f8baadf2829fae817ad0b66b140573939dd69284d2ba3553ae73"}, - {file = "sqlalchemy-2.0.44-cp313-cp313-win_amd64.whl", hash = "sha256:c1c80faaee1a6c3428cecf40d16a2365bcf56c424c92c2b6f0f9ad204b899e9e"}, - {file = "sqlalchemy-2.0.44-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2fc44e5965ea46909a416fff0af48a219faefd5773ab79e5f8a5fcd5d62b2667"}, - {file = "sqlalchemy-2.0.44-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:dc8b3850d2a601ca2320d081874033684e246d28e1c5e89db0864077cfc8f5a9"}, - {file = "sqlalchemy-2.0.44-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d733dec0614bb8f4bcb7c8af88172b974f685a31dc3a65cca0527e3120de5606"}, - {file = "sqlalchemy-2.0.44-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22be14009339b8bc16d6b9dc8780bacaba3402aa7581658e246114abbd2236e3"}, - {file = "sqlalchemy-2.0.44-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:357bade0e46064f88f2c3a99808233e67b0051cdddf82992379559322dfeb183"}, - {file = "sqlalchemy-2.0.44-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:4848395d932e93c1595e59a8672aa7400e8922c39bb9b0668ed99ac6fa867822"}, - {file = "sqlalchemy-2.0.44-cp38-cp38-win32.whl", hash = "sha256:2f19644f27c76f07e10603580a47278abb2a70311136a7f8fd27dc2e096b9013"}, - {file = "sqlalchemy-2.0.44-cp38-cp38-win_amd64.whl", hash = "sha256:1df4763760d1de0dfc8192cc96d8aa293eb1a44f8f7a5fbe74caf1b551905c5e"}, - {file = "sqlalchemy-2.0.44-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f7027414f2b88992877573ab780c19ecb54d3a536bef3397933573d6b5068be4"}, - {file = "sqlalchemy-2.0.44-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3fe166c7d00912e8c10d3a9a0ce105569a31a3d0db1a6e82c4e0f4bf16d5eca9"}, - {file = "sqlalchemy-2.0.44-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3caef1ff89b1caefc28f0368b3bde21a7e3e630c2eddac16abd9e47bd27cc36a"}, - {file = "sqlalchemy-2.0.44-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc2856d24afa44295735e72f3c75d6ee7fdd4336d8d3a8f3d44de7aa6b766df2"}, - {file = "sqlalchemy-2.0.44-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:11bac86b0deada30b6b5f93382712ff0e911fe8d31cb9bf46e6b149ae175eff0"}, - {file = "sqlalchemy-2.0.44-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:4d18cd0e9a0f37c9f4088e50e3839fcb69a380a0ec957408e0b57cff08ee0a26"}, - {file = "sqlalchemy-2.0.44-cp39-cp39-win32.whl", hash = "sha256:9e9018544ab07614d591a26c1bd4293ddf40752cc435caf69196740516af7100"}, - {file = "sqlalchemy-2.0.44-cp39-cp39-win_amd64.whl", hash = "sha256:8e0e4e66fd80f277a8c3de016a81a554e76ccf6b8d881ee0b53200305a8433f6"}, - {file = "sqlalchemy-2.0.44-py3-none-any.whl", hash = "sha256:19de7ca1246fbef9f9d1bff8f1ab25641569df226364a0e40457dc5457c54b05"}, - {file = "sqlalchemy-2.0.44.tar.gz", hash = "sha256:0ae7454e1ab1d780aee69fd2aae7d6b8670a581d8847f2d1e0f7ddfbf47e5a22"}, + {file = "sqlalchemy-2.0.45-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c64772786d9eee72d4d3784c28f0a636af5b0a29f3fe26ff11f55efe90c0bd85"}, + {file = "sqlalchemy-2.0.45-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7ae64ebf7657395824a19bca98ab10eb9a3ecb026bf09524014f1bb81cb598d4"}, + {file = "sqlalchemy-2.0.45-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f02325709d1b1a1489f23a39b318e175a171497374149eae74d612634b234c0"}, + {file = "sqlalchemy-2.0.45-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d2c3684fca8a05f0ac1d9a21c1f4a266983a7ea9180efb80ffeb03861ecd01a0"}, + {file = "sqlalchemy-2.0.45-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:040f6f0545b3b7da6b9317fc3e922c9a98fc7243b2a1b39f78390fc0942f7826"}, + {file = "sqlalchemy-2.0.45-cp310-cp310-win32.whl", hash = "sha256:830d434d609fe7bfa47c425c445a8b37929f140a7a44cdaf77f6d34df3a7296a"}, + {file = "sqlalchemy-2.0.45-cp310-cp310-win_amd64.whl", hash = "sha256:0209d9753671b0da74da2cfbb9ecf9c02f72a759e4b018b3ab35f244c91842c7"}, + {file = "sqlalchemy-2.0.45-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2e90a344c644a4fa871eb01809c32096487928bd2038bf10f3e4515cb688cc56"}, + {file = "sqlalchemy-2.0.45-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b8c8b41b97fba5f62349aa285654230296829672fc9939cd7f35aab246d1c08b"}, + {file = "sqlalchemy-2.0.45-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:12c694ed6468333a090d2f60950e4250b928f457e4962389553d6ba5fe9951ac"}, + {file = "sqlalchemy-2.0.45-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:f7d27a1d977a1cfef38a0e2e1ca86f09c4212666ce34e6ae542f3ed0a33bc606"}, + {file = "sqlalchemy-2.0.45-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d62e47f5d8a50099b17e2bfc1b0c7d7ecd8ba6b46b1507b58cc4f05eefc3bb1c"}, + {file = "sqlalchemy-2.0.45-cp311-cp311-win32.whl", hash = "sha256:3c5f76216e7b85770d5bb5130ddd11ee89f4d52b11783674a662c7dd57018177"}, + {file = "sqlalchemy-2.0.45-cp311-cp311-win_amd64.whl", hash = "sha256:a15b98adb7f277316f2c276c090259129ee4afca783495e212048daf846654b2"}, + {file = "sqlalchemy-2.0.45-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b3ee2aac15169fb0d45822983631466d60b762085bc4535cd39e66bea362df5f"}, + {file = "sqlalchemy-2.0.45-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba547ac0b361ab4f1608afbc8432db669bd0819b3e12e29fb5fa9529a8bba81d"}, + {file = "sqlalchemy-2.0.45-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:215f0528b914e5c75ef2559f69dca86878a3beeb0c1be7279d77f18e8d180ed4"}, + {file = "sqlalchemy-2.0.45-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:107029bf4f43d076d4011f1afb74f7c3e2ea029ec82eb23d8527d5e909e97aa6"}, + {file = "sqlalchemy-2.0.45-cp312-cp312-win32.whl", hash = "sha256:0c9f6ada57b58420a2c0277ff853abe40b9e9449f8d7d231763c6bc30f5c4953"}, + {file = "sqlalchemy-2.0.45-cp312-cp312-win_amd64.whl", hash = "sha256:8defe5737c6d2179c7997242d6473587c3beb52e557f5ef0187277009f73e5e1"}, + {file = "sqlalchemy-2.0.45-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fe187fc31a54d7fd90352f34e8c008cf3ad5d064d08fedd3de2e8df83eb4a1cf"}, + {file = "sqlalchemy-2.0.45-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:672c45cae53ba88e0dad74b9027dddd09ef6f441e927786b05bec75d949fbb2e"}, + {file = "sqlalchemy-2.0.45-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:470daea2c1ce73910f08caf10575676a37159a6d16c4da33d0033546bddebc9b"}, + {file = "sqlalchemy-2.0.45-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9c6378449e0940476577047150fd09e242529b761dc887c9808a9a937fe990c8"}, + {file = "sqlalchemy-2.0.45-cp313-cp313-win32.whl", hash = "sha256:4b6bec67ca45bc166c8729910bd2a87f1c0407ee955df110d78948f5b5827e8a"}, + {file = "sqlalchemy-2.0.45-cp313-cp313-win_amd64.whl", hash = "sha256:afbf47dc4de31fa38fd491f3705cac5307d21d4bb828a4f020ee59af412744ee"}, + {file = "sqlalchemy-2.0.45-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:83d7009f40ce619d483d26ac1b757dfe3167b39921379a8bd1b596cf02dab4a6"}, + {file = "sqlalchemy-2.0.45-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:d8a2ca754e5415cde2b656c27900b19d50ba076aa05ce66e2207623d3fe41f5a"}, + {file = "sqlalchemy-2.0.45-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7f46ec744e7f51275582e6a24326e10c49fbdd3fc99103e01376841213028774"}, + {file = "sqlalchemy-2.0.45-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:883c600c345123c033c2f6caca18def08f1f7f4c3ebeb591a63b6fceffc95cce"}, + {file = "sqlalchemy-2.0.45-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:2c0b74aa79e2deade948fe8593654c8ef4228c44ba862bb7c9585c8e0db90f33"}, + {file = "sqlalchemy-2.0.45-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:8a420169cef179d4c9064365f42d779f1e5895ad26ca0c8b4c0233920973db74"}, + {file = "sqlalchemy-2.0.45-cp314-cp314-win32.whl", hash = "sha256:e50dcb81a5dfe4b7b4a4aa8f338116d127cb209559124f3694c70d6cd072b68f"}, + {file = "sqlalchemy-2.0.45-cp314-cp314-win_amd64.whl", hash = "sha256:4748601c8ea959e37e03d13dcda4a44837afcd1b21338e637f7c935b8da06177"}, + {file = "sqlalchemy-2.0.45-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cd337d3526ec5298f67d6a30bbbe4ed7e5e68862f0bf6dd21d289f8d37b7d60b"}, + {file = "sqlalchemy-2.0.45-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9a62b446b7d86a3909abbcd1cd3cc550a832f99c2bc37c5b22e1925438b9367b"}, + {file = "sqlalchemy-2.0.45-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5964f832431b7cdfaaa22a660b4c7eb1dfcd6ed41375f67fd3e3440fd95cb3cc"}, + {file = "sqlalchemy-2.0.45-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee580ab50e748208754ae8980cec79ec205983d8cf8b3f7c39067f3d9f2c8e22"}, + {file = "sqlalchemy-2.0.45-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:13e27397a7810163440c6bfed6b3fe46f1bfb2486eb540315a819abd2c004128"}, + {file = "sqlalchemy-2.0.45-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:ed3635353e55d28e7f4a95c8eda98a5cdc0a0b40b528433fbd41a9ae88f55b3d"}, + {file = "sqlalchemy-2.0.45-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:db6834900338fb13a9123307f0c2cbb1f890a8656fcd5e5448ae3ad5bbe8d312"}, + {file = "sqlalchemy-2.0.45-cp38-cp38-win32.whl", hash = "sha256:1d8b4a7a8c9b537509d56d5cd10ecdcfbb95912d72480c8861524efecc6a3fff"}, + {file = "sqlalchemy-2.0.45-cp38-cp38-win_amd64.whl", hash = "sha256:ebd300afd2b62679203435f596b2601adafe546cb7282d5a0cd3ed99e423720f"}, + {file = "sqlalchemy-2.0.45-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d29b2b99d527dbc66dd87c3c3248a5dd789d974a507f4653c969999fc7c1191b"}, + {file = "sqlalchemy-2.0.45-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:59a8b8bd9c6bedf81ad07c8bd5543eedca55fe9b8780b2b628d495ba55f8db1e"}, + {file = "sqlalchemy-2.0.45-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fd93c6f5d65f254ceabe97548c709e073d6da9883343adaa51bf1a913ce93f8e"}, + {file = "sqlalchemy-2.0.45-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:6d0beadc2535157070c9c17ecf25ecec31e13c229a8f69196d7590bde8082bf1"}, + {file = "sqlalchemy-2.0.45-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:e057f928ffe9c9b246a55b469c133b98a426297e1772ad24ce9f0c47d123bd5b"}, + {file = "sqlalchemy-2.0.45-cp39-cp39-win32.whl", hash = "sha256:c1c2091b1489435ff85728fafeb990f073e64f6f5e81d5cd53059773e8521eb6"}, + {file = "sqlalchemy-2.0.45-cp39-cp39-win_amd64.whl", hash = "sha256:56ead1f8dfb91a54a28cd1d072c74b3d635bcffbd25e50786533b822d4f2cde2"}, + {file = "sqlalchemy-2.0.45-py3-none-any.whl", hash = "sha256:5225a288e4c8cc2308dbdd874edad6e7d0fd38eac1e9e5f23503425c8eee20d0"}, + {file = "sqlalchemy-2.0.45.tar.gz", hash = "sha256:1632a4bda8d2d25703fdad6363058d882541bdaaee0e5e3ddfa0cd3229efce88"}, ] [package.dependencies] @@ -8443,22 +9255,23 @@ files = [ [[package]] name = "sse-starlette" -version = "3.0.3" +version = "3.0.4" description = "SSE plugin for Starlette" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "sse_starlette-3.0.3-py3-none-any.whl", hash = "sha256:af5bf5a6f3933df1d9c7f8539633dc8444ca6a97ab2e2a7cd3b6e431ac03a431"}, - {file = "sse_starlette-3.0.3.tar.gz", hash = "sha256:88cfb08747e16200ea990c8ca876b03910a23b547ab3bd764c0d8eb81019b971"}, + {file = "sse_starlette-3.0.4-py3-none-any.whl", hash = "sha256:32c80ef0d04506ced4b0b6ab8fe300925edc37d26f666afb1874c754895f5dc3"}, + {file = "sse_starlette-3.0.4.tar.gz", hash = "sha256:5e34286862e96ead0eb70f5ddd0bd21ab1f6473a8f44419dd267f431611383dd"}, ] [package.dependencies] anyio = ">=4.7.0" +starlette = ">=0.49.1" [package.extras] daphne = ["daphne (>=4.2.0)"] -examples = ["aiosqlite (>=0.21.0)", "fastapi (>=0.115.12)", "sqlalchemy[asyncio] (>=2.0.41)", "starlette (>=0.49.1)", "uvicorn (>=0.34.0)"] +examples = ["aiosqlite (>=0.21.0)", "fastapi (>=0.115.12)", "sqlalchemy[asyncio] (>=2.0.41)", "uvicorn (>=0.34.0)"] granian = ["granian (>=2.3.1)"] uvicorn = ["uvicorn (>=0.34.0)"] @@ -8748,24 +9561,24 @@ pyyaml = ["pyyaml"] [[package]] name = "tornado" -version = "6.5.2" +version = "6.5.4" description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." optional = false python-versions = ">=3.9" groups = ["dev"] files = [ - {file = "tornado-6.5.2-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:2436822940d37cde62771cff8774f4f00b3c8024fe482e16ca8387b8a2724db6"}, - {file = "tornado-6.5.2-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:583a52c7aa94ee046854ba81d9ebb6c81ec0fd30386d96f7640c96dad45a03ef"}, - {file = "tornado-6.5.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b0fe179f28d597deab2842b86ed4060deec7388f1fd9c1b4a41adf8af058907e"}, - {file = "tornado-6.5.2-cp39-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b186e85d1e3536d69583d2298423744740986018e393d0321df7340e71898882"}, - {file = "tornado-6.5.2-cp39-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e792706668c87709709c18b353da1f7662317b563ff69f00bab83595940c7108"}, - {file = "tornado-6.5.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:06ceb1300fd70cb20e43b1ad8aaee0266e69e7ced38fa910ad2e03285009ce7c"}, - {file = "tornado-6.5.2-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:74db443e0f5251be86cbf37929f84d8c20c27a355dd452a5cfa2aada0d001ec4"}, - {file = "tornado-6.5.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b5e735ab2889d7ed33b32a459cac490eda71a1ba6857b0118de476ab6c366c04"}, - {file = "tornado-6.5.2-cp39-abi3-win32.whl", hash = "sha256:c6f29e94d9b37a95013bb669616352ddb82e3bfe8326fccee50583caebc8a5f0"}, - {file = "tornado-6.5.2-cp39-abi3-win_amd64.whl", hash = "sha256:e56a5af51cc30dd2cae649429af65ca2f6571da29504a07995175df14c18f35f"}, - {file = "tornado-6.5.2-cp39-abi3-win_arm64.whl", hash = "sha256:d6c33dc3672e3a1f3618eb63b7ef4683a7688e7b9e6e8f0d9aa5726360a004af"}, - {file = "tornado-6.5.2.tar.gz", hash = "sha256:ab53c8f9a0fa351e2c0741284e06c7a45da86afb544133201c5cc8578eb076a0"}, + {file = "tornado-6.5.4-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:d6241c1a16b1c9e4cc28148b1cda97dd1c6cb4fb7068ac1bedc610768dff0ba9"}, + {file = "tornado-6.5.4-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:2d50f63dda1d2cac3ae1fa23d254e16b5e38153758470e9956cbc3d813d40843"}, + {file = "tornado-6.5.4-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1cf66105dc6acb5af613c054955b8137e34a03698aa53272dbda4afe252be17"}, + {file = "tornado-6.5.4-cp39-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50ff0a58b0dc97939d29da29cd624da010e7f804746621c78d14b80238669335"}, + {file = "tornado-6.5.4-cp39-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e5fb5e04efa54cf0baabdd10061eb4148e0be137166146fff835745f59ab9f7f"}, + {file = "tornado-6.5.4-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9c86b1643b33a4cd415f8d0fe53045f913bf07b4a3ef646b735a6a86047dda84"}, + {file = "tornado-6.5.4-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:6eb82872335a53dd063a4f10917b3efd28270b56a33db69009606a0312660a6f"}, + {file = "tornado-6.5.4-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:6076d5dda368c9328ff41ab5d9dd3608e695e8225d1cd0fd1e006f05da3635a8"}, + {file = "tornado-6.5.4-cp39-abi3-win32.whl", hash = "sha256:1768110f2411d5cd281bac0a090f707223ce77fd110424361092859e089b38d1"}, + {file = "tornado-6.5.4-cp39-abi3-win_amd64.whl", hash = "sha256:fa07d31e0cd85c60713f2b995da613588aa03e1303d75705dca6af8babc18ddc"}, + {file = "tornado-6.5.4-cp39-abi3-win_arm64.whl", hash = "sha256:053e6e16701eb6cbe641f308f4c1a9541f91b6261991160391bfc342e8a551a1"}, + {file = "tornado-6.5.4.tar.gz", hash = "sha256:a22fa9047405d03260b483980635f0b041989d8bcc9a313f8fe18b411d84b1d7"}, ] [[package]] @@ -8808,19 +9621,19 @@ test = ["argcomplete (>=3.0.3)", "mypy (>=1.7.0)", "pre-commit", "pytest (>=7.0, [[package]] name = "transformers" -version = "4.57.3" -description = "State-of-the-art Machine Learning for JAX, PyTorch and TensorFlow" +version = "5.0.0rc1" +description = "Transformers: the model-definition framework for state-of-the-art machine learning models in text, vision, audio, and multimodal models, for both inference and training." optional = false -python-versions = ">=3.9.0" +python-versions = ">=3.10.0" groups = ["main"] files = [ - {file = "transformers-4.57.3-py3-none-any.whl", hash = "sha256:c77d353a4851b1880191603d36acb313411d3577f6e2897814f333841f7003f4"}, - {file = "transformers-4.57.3.tar.gz", hash = "sha256:df4945029aaddd7c09eec5cad851f30662f8bd1746721b34cc031d70c65afebc"}, + {file = "transformers-5.0.0rc1-py3-none-any.whl", hash = "sha256:8b9604700769872cab4280dbcde201f557e93f72ee5a85c4592275ab4f15d330"}, + {file = "transformers-5.0.0rc1.tar.gz", hash = "sha256:1fdde557b96ef8ea277c45b8e0d558f1e167fe28a98593f4c4aec0277e335821"}, ] [package.dependencies] filelock = "*" -huggingface-hub = ">=0.34.0,<1.0" +huggingface-hub = ">=1.2.1,<2.0" numpy = ">=1.17" packaging = ">=20.0" pyyaml = ">=5.1" @@ -8829,32 +9642,28 @@ requests = "*" safetensors = ">=0.4.3" tokenizers = ">=0.22.0,<=0.23.0" tqdm = ">=4.27" +typer-slim = "*" [package.extras] -accelerate = ["accelerate (>=0.26.0)"] -all = ["Pillow (>=10.0.1,<=15.0)", "Pillow (>=10.0.1,<=15.0)", "accelerate (>=0.26.0)", "accelerate (>=0.26.0)", "av", "codecarbon (>=2.8.1)", "flax (>=0.4.1,<=0.7.0)", "jax (>=0.4.1,<=0.4.13)", "jaxlib (>=0.4.1,<=0.4.13)", "jinja2 (>=3.1.0)", "kenlm", "keras-nlp (>=0.3.1,<0.14.0)", "kernels (>=0.6.1,<=0.9)", "librosa", "mistral-common[opencv] (>=1.6.3)", "num2words", "onnxconverter-common", "optax (>=0.0.8,<=0.1.4)", "optuna", "phonemizer", "protobuf", "pyctcdecode (>=0.4.0)", "ray[tune] (>=2.7.0)", "scipy (<1.13.0)", "sentencepiece (>=0.1.91,!=0.1.92)", "tensorflow (>2.9,<2.16)", "tensorflow-text (<2.16)", "tf2onnx", "timm (!=1.0.18,<=1.0.19)", "tokenizers (>=0.22.0,<=0.23.0)", "torch (>=2.2)", "torchaudio", "torchvision"] +accelerate = ["accelerate (>=1.1.0)"] +all = ["Pillow (>=10.0.1,<=15.0)", "Pillow (>=10.0.1,<=15.0)", "accelerate (>=1.1.0)", "accelerate (>=1.1.0)", "av", "codecarbon (>=2.8.1)", "jinja2 (>=3.1.0)", "jmespath (>=1.0.1)", "kenlm", "kernels (>=0.10.2,<0.11)", "librosa", "mistral-common[opencv] (>=1.6.3)", "num2words", "optuna", "phonemizer", "protobuf", "pyctcdecode (>=0.4.0)", "ray[tune] (>=2.7.0)", "sentencepiece (>=0.1.91,!=0.1.92)", "timm (!=1.0.18,<=1.0.19)", "tokenizers (>=0.22.0,<=0.23.0)", "torch (>=2.2)", "torchaudio", "torchvision"] audio = ["kenlm", "librosa", "phonemizer", "pyctcdecode (>=0.4.0)"] benchmark = ["optimum-benchmark (>=0.3.0)"] -chat-template = ["jinja2 (>=3.1.0)"] +chat-template = ["jinja2 (>=3.1.0)", "jmespath (>=1.0.1)"] codecarbon = ["codecarbon (>=2.8.1)"] -deepspeed = ["accelerate (>=0.26.0)", "deepspeed (>=0.9.3)"] -deepspeed-testing = ["GitPython (<3.1.19)", "accelerate (>=0.26.0)", "accelerate (>=0.26.0)", "beautifulsoup4", "cookiecutter (==1.7.3)", "datasets (>=2.15.0)", "datasets (>=2.15.0)", "deepspeed (>=0.9.3)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "fastapi", "libcst", "mistral-common[opencv] (>=1.6.3)", "nltk (<=3.8.1)", "openai (>=1.98.0)", "optuna", "parameterized (>=0.9)", "protobuf", "psutil", "pydantic (>=2)", "pydantic (>=2)", "pytest (>=7.2.0)", "pytest-asyncio", "pytest-order", "pytest-rerunfailures (<16.0)", "pytest-rich", "pytest-timeout", "pytest-xdist", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "ruff (==0.13.1)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "sentencepiece (>=0.1.91,!=0.1.92)", "sentencepiece (>=0.1.91,!=0.1.92)", "starlette", "tensorboard", "timeout-decorator", "torch (>=2.2)", "uvicorn"] -dev = ["GitPython (<3.1.19)", "GitPython (<3.1.19)", "Pillow (>=10.0.1,<=15.0)", "Pillow (>=10.0.1,<=15.0)", "accelerate (>=0.26.0)", "accelerate (>=0.26.0)", "accelerate (>=0.26.0)", "av", "beautifulsoup4", "codecarbon (>=2.8.1)", "cookiecutter (==1.7.3)", "cookiecutter (==1.7.3)", "datasets (>=2.15.0)", "datasets (>=2.15.0)", "datasets (>=2.15.0)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "fastapi", "flax (>=0.4.1,<=0.7.0)", "fugashi (>=1.0)", "ipadic (>=1.0.0,<2.0)", "jax (>=0.4.1,<=0.4.13)", "jaxlib (>=0.4.1,<=0.4.13)", "jinja2 (>=3.1.0)", "kenlm", "keras-nlp (>=0.3.1,<0.14.0)", "kernels (>=0.6.1,<=0.9)", "libcst", "libcst", "librosa", "mistral-common[opencv] (>=1.6.3)", "mistral-common[opencv] (>=1.6.3)", "nltk (<=3.8.1)", "num2words", "onnxconverter-common", "openai (>=1.98.0)", "optax (>=0.0.8,<=0.1.4)", "optuna", "pandas (<2.3.0)", "parameterized (>=0.9)", "phonemizer", "protobuf", "psutil", "pyctcdecode (>=0.4.0)", "pydantic (>=2)", "pydantic (>=2)", "pytest (>=7.2.0)", "pytest-asyncio", "pytest-order", "pytest-rerunfailures (<16.0)", "pytest-rich", "pytest-timeout", "pytest-xdist", "ray[tune] (>=2.7.0)", "rhoknp (>=1.1.0,<1.3.1)", "rich", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "ruff (==0.13.1)", "ruff (==0.13.1)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "scikit-learn", "scipy (<1.13.0)", "sentencepiece (>=0.1.91,!=0.1.92)", "sentencepiece (>=0.1.91,!=0.1.92)", "starlette", "sudachidict_core (>=20220729)", "sudachipy (>=0.6.6)", "tensorboard", "tensorflow (>2.9,<2.16)", "tensorflow-text (<2.16)", "tf2onnx", "timeout-decorator", "timm (!=1.0.18,<=1.0.19)", "tokenizers (>=0.22.0,<=0.23.0)", "torch (>=2.2)", "torch (>=2.2)", "torchaudio", "torchvision", "unidic (>=1.0.2)", "unidic_lite (>=1.0.7)", "urllib3 (<2.0.0)", "uvicorn"] -dev-tensorflow = ["GitPython (<3.1.19)", "GitPython (<3.1.19)", "Pillow (>=10.0.1,<=15.0)", "accelerate (>=0.26.0)", "beautifulsoup4", "cookiecutter (==1.7.3)", "cookiecutter (==1.7.3)", "datasets (>=2.15.0)", "datasets (>=2.15.0)", "datasets (>=2.15.0)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "fastapi", "kenlm", "keras-nlp (>=0.3.1,<0.14.0)", "libcst", "libcst", "librosa", "mistral-common[opencv] (>=1.6.3)", "nltk (<=3.8.1)", "onnxconverter-common", "onnxconverter-common", "onnxruntime (>=1.4.0)", "onnxruntime-tools (>=1.4.2)", "openai (>=1.98.0)", "pandas (<2.3.0)", "parameterized (>=0.9)", "phonemizer", "protobuf", "psutil", "pyctcdecode (>=0.4.0)", "pydantic (>=2)", "pydantic (>=2)", "pytest (>=7.2.0)", "pytest-asyncio", "pytest-order", "pytest-rerunfailures (<16.0)", "pytest-rich", "pytest-timeout", "pytest-xdist", "rich", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "ruff (==0.13.1)", "ruff (==0.13.1)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "scikit-learn", "sentencepiece (>=0.1.91,!=0.1.92)", "sentencepiece (>=0.1.91,!=0.1.92)", "starlette", "tensorboard", "tensorflow (>2.9,<2.16)", "tensorflow-text (<2.16)", "tf2onnx", "tf2onnx", "timeout-decorator", "tokenizers (>=0.22.0,<=0.23.0)", "torch (>=2.2)", "urllib3 (<2.0.0)", "uvicorn"] -dev-torch = ["GitPython (<3.1.19)", "GitPython (<3.1.19)", "Pillow (>=10.0.1,<=15.0)", "Pillow (>=10.0.1,<=15.0)", "accelerate (>=0.26.0)", "accelerate (>=0.26.0)", "beautifulsoup4", "codecarbon (>=2.8.1)", "cookiecutter (==1.7.3)", "cookiecutter (==1.7.3)", "datasets (>=2.15.0)", "datasets (>=2.15.0)", "datasets (>=2.15.0)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "fastapi", "fugashi (>=1.0)", "ipadic (>=1.0.0,<2.0)", "kenlm", "kernels (>=0.6.1,<=0.9)", "libcst", "libcst", "librosa", "mistral-common[opencv] (>=1.6.3)", "nltk (<=3.8.1)", "num2words", "onnxruntime (>=1.4.0)", "onnxruntime-tools (>=1.4.2)", "openai (>=1.98.0)", "optuna", "pandas (<2.3.0)", "parameterized (>=0.9)", "phonemizer", "protobuf", "psutil", "pyctcdecode (>=0.4.0)", "pydantic (>=2)", "pydantic (>=2)", "pytest (>=7.2.0)", "pytest-asyncio", "pytest-order", "pytest-rerunfailures (<16.0)", "pytest-rich", "pytest-timeout", "pytest-xdist", "ray[tune] (>=2.7.0)", "rhoknp (>=1.1.0,<1.3.1)", "rich", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "ruff (==0.13.1)", "ruff (==0.13.1)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "scikit-learn", "sentencepiece (>=0.1.91,!=0.1.92)", "sentencepiece (>=0.1.91,!=0.1.92)", "starlette", "sudachidict_core (>=20220729)", "sudachipy (>=0.6.6)", "tensorboard", "timeout-decorator", "timm (!=1.0.18,<=1.0.19)", "tokenizers (>=0.22.0,<=0.23.0)", "torch (>=2.2)", "torch (>=2.2)", "torchaudio", "torchvision", "unidic (>=1.0.2)", "unidic_lite (>=1.0.7)", "urllib3 (<2.0.0)", "uvicorn"] -flax = ["flax (>=0.4.1,<=0.7.0)", "jax (>=0.4.1,<=0.4.13)", "jaxlib (>=0.4.1,<=0.4.13)", "optax (>=0.0.8,<=0.1.4)", "scipy (<1.13.0)"] -flax-speech = ["kenlm", "librosa", "phonemizer", "pyctcdecode (>=0.4.0)"] +deepspeed = ["accelerate (>=1.1.0)", "deepspeed (>=0.9.3)"] +deepspeed-testing = ["GitPython (<3.1.19)", "accelerate (>=1.1.0)", "accelerate (>=1.1.0)", "beautifulsoup4", "cookiecutter (==1.7.3)", "datasets (>=2.15.0)", "datasets (>=2.15.0)", "deepspeed (>=0.9.3)", "dill (<0.3.5)", "evaluate (>=0.4.6)", "faiss-cpu", "fastapi", "libcst", "mistral-common[opencv] (>=1.6.3)", "nltk (<=3.8.1)", "openai (>=1.98.0)", "optuna", "parameterized (>=0.9)", "protobuf", "psutil", "pydantic (>=2)", "pydantic (>=2)", "pytest (>=7.2.0,<9.0.0)", "pytest-asyncio (>=1.2.0)", "pytest-order", "pytest-rerunfailures (<16.0)", "pytest-rich", "pytest-timeout", "pytest-xdist", "rich", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "ruff (==0.13.1)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "sentencepiece (>=0.1.91,!=0.1.92)", "sentencepiece (>=0.1.91,!=0.1.92)", "starlette", "tensorboard", "timeout-decorator", "torch (>=2.2)", "uvicorn"] +dev = ["GitPython (<3.1.19)", "GitPython (<3.1.19)", "Pillow (>=10.0.1,<=15.0)", "Pillow (>=10.0.1,<=15.0)", "accelerate (>=1.1.0)", "accelerate (>=1.1.0)", "accelerate (>=1.1.0)", "av", "beautifulsoup4", "codecarbon (>=2.8.1)", "cookiecutter (==1.7.3)", "cookiecutter (==1.7.3)", "datasets (>=2.15.0)", "datasets (>=2.15.0)", "datasets (>=2.15.0)", "dill (<0.3.5)", "evaluate (>=0.4.6)", "faiss-cpu", "fastapi", "fugashi (>=1.0)", "ipadic (>=1.0.0,<2.0)", "jinja2 (>=3.1.0)", "jmespath (>=1.0.1)", "kenlm", "kernels (>=0.10.2,<0.11)", "libcst", "libcst", "librosa", "mistral-common[opencv] (>=1.6.3)", "mistral-common[opencv] (>=1.6.3)", "nltk (<=3.8.1)", "num2words", "openai (>=1.98.0)", "optuna", "pandas (<2.3.0)", "parameterized (>=0.9)", "phonemizer", "protobuf", "psutil", "pyctcdecode (>=0.4.0)", "pydantic (>=2)", "pydantic (>=2)", "pytest (>=7.2.0,<9.0.0)", "pytest-asyncio (>=1.2.0)", "pytest-order", "pytest-rerunfailures (<16.0)", "pytest-rich", "pytest-timeout", "pytest-xdist", "ray[tune] (>=2.7.0)", "rhoknp (>=1.1.0,<1.3.1)", "rich", "rich", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "ruff (==0.13.1)", "ruff (==0.13.1)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "scikit-learn", "sentencepiece (>=0.1.91,!=0.1.92)", "sentencepiece (>=0.1.91,!=0.1.92)", "starlette", "sudachidict_core (>=20220729)", "sudachipy (>=0.6.6)", "tensorboard", "timeout-decorator", "timm (!=1.0.18,<=1.0.19)", "tokenizers (>=0.22.0,<=0.23.0)", "torch (>=2.2)", "torch (>=2.2)", "torchaudio", "torchvision", "unidic (>=1.0.2)", "unidic_lite (>=1.0.7)", "urllib3 (<2.0.0)", "uvicorn"] +dev-torch = ["GitPython (<3.1.19)", "GitPython (<3.1.19)", "Pillow (>=10.0.1,<=15.0)", "Pillow (>=10.0.1,<=15.0)", "accelerate (>=1.1.0)", "accelerate (>=1.1.0)", "beautifulsoup4", "codecarbon (>=2.8.1)", "cookiecutter (==1.7.3)", "cookiecutter (==1.7.3)", "datasets (>=2.15.0)", "datasets (>=2.15.0)", "datasets (>=2.15.0)", "dill (<0.3.5)", "evaluate (>=0.4.6)", "faiss-cpu", "fastapi", "fugashi (>=1.0)", "ipadic (>=1.0.0,<2.0)", "kenlm", "kernels (>=0.10.2,<0.11)", "libcst", "libcst", "librosa", "mistral-common[opencv] (>=1.6.3)", "nltk (<=3.8.1)", "num2words", "openai (>=1.98.0)", "optuna", "pandas (<2.3.0)", "parameterized (>=0.9)", "phonemizer", "protobuf", "psutil", "pyctcdecode (>=0.4.0)", "pydantic (>=2)", "pydantic (>=2)", "pytest (>=7.2.0,<9.0.0)", "pytest-asyncio (>=1.2.0)", "pytest-order", "pytest-rerunfailures (<16.0)", "pytest-rich", "pytest-timeout", "pytest-xdist", "ray[tune] (>=2.7.0)", "rhoknp (>=1.1.0,<1.3.1)", "rich", "rich", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "ruff (==0.13.1)", "ruff (==0.13.1)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "scikit-learn", "sentencepiece (>=0.1.91,!=0.1.92)", "sentencepiece (>=0.1.91,!=0.1.92)", "starlette", "sudachidict_core (>=20220729)", "sudachipy (>=0.6.6)", "tensorboard", "timeout-decorator", "timm (!=1.0.18,<=1.0.19)", "tokenizers (>=0.22.0,<=0.23.0)", "torch (>=2.2)", "torch (>=2.2)", "torchaudio", "torchvision", "unidic (>=1.0.2)", "unidic_lite (>=1.0.7)", "urllib3 (<2.0.0)", "uvicorn"] ftfy = ["ftfy"] hf-xet = ["hf_xet"] -hub-kernels = ["kernels (>=0.6.1,<=0.9)"] -integrations = ["kernels (>=0.6.1,<=0.9)", "optuna", "ray[tune] (>=2.7.0)"] +hub-kernels = ["kernels (>=0.10.2,<0.11)"] +integrations = ["kernels (>=0.10.2,<0.11)", "optuna", "ray[tune] (>=2.7.0)"] ja = ["fugashi (>=1.0)", "ipadic (>=1.0.0,<2.0)", "rhoknp (>=1.1.0,<1.3.1)", "sudachidict_core (>=20220729)", "sudachipy (>=0.6.6)", "unidic (>=1.0.2)", "unidic_lite (>=1.0.7)"] mistral-common = ["mistral-common[opencv] (>=1.6.3)"] modelcreation = ["cookiecutter (==1.7.3)"] natten = ["natten (>=0.14.6,<0.15.0)"] num2words = ["num2words"] -onnx = ["onnxconverter-common", "onnxruntime (>=1.4.0)", "onnxruntime-tools (>=1.4.2)", "tf2onnx"] -onnxruntime = ["onnxruntime (>=1.4.0)", "onnxruntime-tools (>=1.4.2)"] open-telemetry = ["opentelemetry-api", "opentelemetry-exporter-otlp", "opentelemetry-sdk"] optuna = ["optuna"] quality = ["GitPython (<3.1.19)", "datasets (>=2.15.0)", "libcst", "pandas (<2.3.0)", "rich", "ruff (==0.13.1)", "urllib3 (<2.0.0)"] @@ -8863,21 +9672,17 @@ retrieval = ["datasets (>=2.15.0)", "faiss-cpu"] ruff = ["ruff (==0.13.1)"] sagemaker = ["sagemaker (>=2.31.0)"] sentencepiece = ["protobuf", "sentencepiece (>=0.1.91,!=0.1.92)"] -serving = ["accelerate (>=0.26.0)", "fastapi", "openai (>=1.98.0)", "pydantic (>=2)", "starlette", "torch (>=2.2)", "uvicorn"] -sigopt = ["sigopt"] +serving = ["accelerate (>=1.1.0)", "fastapi", "openai (>=1.98.0)", "pydantic (>=2)", "rich", "starlette", "torch (>=2.2)", "uvicorn"] sklearn = ["scikit-learn"] speech = ["kenlm", "librosa", "phonemizer", "pyctcdecode (>=0.4.0)", "torchaudio"] -testing = ["GitPython (<3.1.19)", "accelerate (>=0.26.0)", "beautifulsoup4", "cookiecutter (==1.7.3)", "datasets (>=2.15.0)", "datasets (>=2.15.0)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "fastapi", "libcst", "mistral-common[opencv] (>=1.6.3)", "nltk (<=3.8.1)", "openai (>=1.98.0)", "parameterized (>=0.9)", "psutil", "pydantic (>=2)", "pydantic (>=2)", "pytest (>=7.2.0)", "pytest-asyncio", "pytest-order", "pytest-rerunfailures (<16.0)", "pytest-rich", "pytest-timeout", "pytest-xdist", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "ruff (==0.13.1)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "sentencepiece (>=0.1.91,!=0.1.92)", "starlette", "tensorboard", "timeout-decorator", "torch (>=2.2)", "uvicorn"] -tf = ["keras-nlp (>=0.3.1,<0.14.0)", "onnxconverter-common", "tensorflow (>2.9,<2.16)", "tensorflow-text (<2.16)", "tf2onnx"] -tf-cpu = ["keras (>2.9,<2.16)", "keras-nlp (>=0.3.1,<0.14.0)", "onnxconverter-common", "tensorflow-cpu (>2.9,<2.16)", "tensorflow-probability (<0.24)", "tensorflow-text (<2.16)", "tf2onnx"] -tf-speech = ["kenlm", "librosa", "phonemizer", "pyctcdecode (>=0.4.0)"] +testing = ["GitPython (<3.1.19)", "accelerate (>=1.1.0)", "beautifulsoup4", "cookiecutter (==1.7.3)", "datasets (>=2.15.0)", "datasets (>=2.15.0)", "dill (<0.3.5)", "evaluate (>=0.4.6)", "faiss-cpu", "fastapi", "libcst", "mistral-common[opencv] (>=1.6.3)", "nltk (<=3.8.1)", "openai (>=1.98.0)", "parameterized (>=0.9)", "psutil", "pydantic (>=2)", "pydantic (>=2)", "pytest (>=7.2.0,<9.0.0)", "pytest-asyncio (>=1.2.0)", "pytest-order", "pytest-rerunfailures (<16.0)", "pytest-rich", "pytest-timeout", "pytest-xdist", "rich", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "ruff (==0.13.1)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "sentencepiece (>=0.1.91,!=0.1.92)", "starlette", "tensorboard", "timeout-decorator", "torch (>=2.2)", "uvicorn"] tiktoken = ["blobfile", "tiktoken"] timm = ["timm (!=1.0.18,<=1.0.19)"] tokenizers = ["tokenizers (>=0.22.0,<=0.23.0)"] -torch = ["accelerate (>=0.26.0)", "torch (>=2.2)"] +torch = ["accelerate (>=1.1.0)", "torch (>=2.2)"] torch-speech = ["kenlm", "librosa", "phonemizer", "pyctcdecode (>=0.4.0)", "torchaudio"] torch-vision = ["Pillow (>=10.0.1,<=15.0)", "torchvision"] -torchhub = ["filelock", "huggingface-hub (>=0.34.0,<1.0)", "importlib_metadata", "numpy (>=1.17)", "packaging (>=20.0)", "protobuf", "regex (!=2019.12.17)", "requests", "sentencepiece (>=0.1.91,!=0.1.92)", "tokenizers (>=0.22.0,<=0.23.0)", "torch (>=2.2)", "tqdm (>=4.27)"] +torchhub = ["filelock", "huggingface-hub (>=1.2.1,<2.0)", "importlib_metadata", "numpy (>=1.17)", "packaging (>=20.0)", "protobuf", "regex (!=2019.12.17)", "requests", "sentencepiece (>=0.1.91,!=0.1.92)", "tokenizers (>=0.22.0,<=0.23.0)", "torch (>=2.2)", "tqdm (>=4.27)"] video = ["av"] vision = ["Pillow (>=10.0.1,<=15.0)"] @@ -8960,6 +9765,43 @@ tls = ["idna (>=2.4)", "pyopenssl (>=21.0.0)", "service-identity (>=18.1.0)"] websocket = ["wsproto"] windows-platform = ["appdirs (>=1.4.0)", "appdirs (>=1.4.0)", "bcrypt (>=3.1.3)", "bcrypt (>=3.1.3)", "cryptography (>=3.3)", "cryptography (>=3.3)", "cython-test-exception-raiser (>=1.0.2,<2)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.2,<5.0)", "h2 (>=3.2,<5.0)", "httpx[http2] (>=0.27)", "httpx[http2] (>=0.27)", "hypothesis (>=6.56)", "hypothesis (>=6.56)", "idna (>=2.4)", "idna (>=2.4)", "priority (>=1.1.0,<2.0)", "priority (>=1.1.0,<2.0)", "pyhamcrest (>=2)", "pyhamcrest (>=2)", "pyopenssl (>=21.0.0)", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "pyserial (>=3.0)", "pywin32 (!=226)", "pywin32 (!=226)", "pywin32 (!=226) ; platform_system == \"Windows\"", "pywin32 (!=226) ; platform_system == \"Windows\"", "service-identity (>=18.1.0)", "service-identity (>=18.1.0)", "twisted-iocpsupport (>=1.0.2)", "twisted-iocpsupport (>=1.0.2)", "wsproto", "wsproto"] +[[package]] +name = "typer" +version = "0.20.1" +description = "Typer, build great CLIs. Easy to code. Based on Python type hints." +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "typer-0.20.1-py3-none-any.whl", hash = "sha256:4b3bde918a67c8e03d861aa02deca90a95bbac572e71b1b9be56ff49affdb5a8"}, + {file = "typer-0.20.1.tar.gz", hash = "sha256:68585eb1b01203689c4199bc440d6be616f0851e9f0eb41e4a778845c5a0fd5b"}, +] + +[package.dependencies] +click = ">=8.0.0" +rich = ">=10.11.0" +shellingham = ">=1.3.0" +typing-extensions = ">=3.7.4.3" + +[[package]] +name = "typer-slim" +version = "0.20.0" +description = "Typer, build great CLIs. Easy to code. Based on Python type hints." +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "typer_slim-0.20.0-py3-none-any.whl", hash = "sha256:f42a9b7571a12b97dddf364745d29f12221865acef7a2680065f9bb29c7dc89d"}, + {file = "typer_slim-0.20.0.tar.gz", hash = "sha256:9fc6607b3c6c20f5c33ea9590cbeb17848667c51feee27d9e314a579ab07d1a3"}, +] + +[package.dependencies] +click = ">=8.0.0" +typing-extensions = ">=3.7.4.3" + +[package.extras] +standard = ["rich (>=10.11.0)", "shellingham (>=1.3.0)"] + [[package]] name = "types-beautifulsoup4" version = "4.12.0.20250516" @@ -9086,14 +9928,14 @@ typing-extensions = ">=4.12.0" [[package]] name = "tzdata" -version = "2025.2" +version = "2025.3" description = "Provider of IANA time zone data" optional = false python-versions = ">=2" groups = ["main", "dev"] files = [ - {file = "tzdata-2025.2-py2.py3-none-any.whl", hash = "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8"}, - {file = "tzdata-2025.2.tar.gz", hash = "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9"}, + {file = "tzdata-2025.3-py2.py3-none-any.whl", hash = "sha256:06a47e5700f3081aab02b2e513160914ff0694bce9947d6b76ebd6bf57cfc5d1"}, + {file = "tzdata-2025.3.tar.gz", hash = "sha256:de39c2ca5dc7b0344f2eba86f49d614019d29f060fc4ebc8a417896a620b56a7"}, ] [[package]] @@ -9133,14 +9975,14 @@ dev = ["flake8", "flake8-annotations", "flake8-bandit", "flake8-bugbear", "flake [[package]] name = "urllib3" -version = "2.6.0" +version = "2.6.2" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.9" groups = ["main", "dev"] files = [ - {file = "urllib3-2.6.0-py3-none-any.whl", hash = "sha256:c90f7a39f716c572c4e3e58509581ebd83f9b59cced005b7db7ad2d22b0db99f"}, - {file = "urllib3-2.6.0.tar.gz", hash = "sha256:cb9bcef5a4b345d5da5d145dc3e30834f58e8018828cbc724d30b4cb7d4d49f1"}, + {file = "urllib3-2.6.2-py3-none-any.whl", hash = "sha256:ec21cddfe7724fc7cb4ba4bea7aa8e2ef36f607a4bab81aa6ce42a13dc3f03dd"}, + {file = "urllib3-2.6.2.tar.gz", hash = "sha256:016f9c98bb7e98085cb2b4b17b87d2c702975664e4f060c6532e64d1c1a5e797"}, ] [package.extras] @@ -9437,6 +10279,113 @@ files = [ beautifulsoup4 = "*" requests = ">=2.0.0,<3.0.0" +[[package]] +name = "win32-setctime" +version = "1.2.0" +description = "A small Python utility to set file creation time on Windows" +optional = false +python-versions = ">=3.5" +groups = ["main"] +markers = "sys_platform == \"win32\"" +files = [ + {file = "win32_setctime-1.2.0-py3-none-any.whl", hash = "sha256:95d644c4e708aba81dc3704a116d8cbc974d70b3bdb8be1d150e36be6e9d1390"}, + {file = "win32_setctime-1.2.0.tar.gz", hash = "sha256:ae1fdf948f5640aae05c511ade119313fb6a30d7eabe25fef9764dca5873c4c0"}, +] + +[package.extras] +dev = ["black (>=19.3b0) ; python_version >= \"3.6\"", "pytest (>=4.6.2)"] + +[[package]] +name = "wrapt" +version = "1.17.3" +description = "Module for decorators, wrappers and monkey patching." +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "wrapt-1.17.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:88bbae4d40d5a46142e70d58bf664a89b6b4befaea7b2ecc14e03cedb8e06c04"}, + {file = "wrapt-1.17.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e6b13af258d6a9ad602d57d889f83b9d5543acd471eee12eb51f5b01f8eb1bc2"}, + {file = "wrapt-1.17.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd341868a4b6714a5962c1af0bd44f7c404ef78720c7de4892901e540417111c"}, + {file = "wrapt-1.17.3-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f9b2601381be482f70e5d1051a5965c25fb3625455a2bf520b5a077b22afb775"}, + {file = "wrapt-1.17.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:343e44b2a8e60e06a7e0d29c1671a0d9951f59174f3709962b5143f60a2a98bd"}, + {file = "wrapt-1.17.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:33486899acd2d7d3066156b03465b949da3fd41a5da6e394ec49d271baefcf05"}, + {file = "wrapt-1.17.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e6f40a8aa5a92f150bdb3e1c44b7e98fb7113955b2e5394122fa5532fec4b418"}, + {file = "wrapt-1.17.3-cp310-cp310-win32.whl", hash = "sha256:a36692b8491d30a8c75f1dfee65bef119d6f39ea84ee04d9f9311f83c5ad9390"}, + {file = "wrapt-1.17.3-cp310-cp310-win_amd64.whl", hash = "sha256:afd964fd43b10c12213574db492cb8f73b2f0826c8df07a68288f8f19af2ebe6"}, + {file = "wrapt-1.17.3-cp310-cp310-win_arm64.whl", hash = "sha256:af338aa93554be859173c39c85243970dc6a289fa907402289eeae7543e1ae18"}, + {file = "wrapt-1.17.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:273a736c4645e63ac582c60a56b0acb529ef07f78e08dc6bfadf6a46b19c0da7"}, + {file = "wrapt-1.17.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5531d911795e3f935a9c23eb1c8c03c211661a5060aab167065896bbf62a5f85"}, + {file = "wrapt-1.17.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0610b46293c59a3adbae3dee552b648b984176f8562ee0dba099a56cfbe4df1f"}, + {file = "wrapt-1.17.3-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b32888aad8b6e68f83a8fdccbf3165f5469702a7544472bdf41f582970ed3311"}, + {file = "wrapt-1.17.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8cccf4f81371f257440c88faed6b74f1053eef90807b77e31ca057b2db74edb1"}, + {file = "wrapt-1.17.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8a210b158a34164de8bb68b0e7780041a903d7b00c87e906fb69928bf7890d5"}, + {file = "wrapt-1.17.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:79573c24a46ce11aab457b472efd8d125e5a51da2d1d24387666cd85f54c05b2"}, + {file = "wrapt-1.17.3-cp311-cp311-win32.whl", hash = "sha256:c31eebe420a9a5d2887b13000b043ff6ca27c452a9a22fa71f35f118e8d4bf89"}, + {file = "wrapt-1.17.3-cp311-cp311-win_amd64.whl", hash = "sha256:0b1831115c97f0663cb77aa27d381237e73ad4f721391a9bfb2fe8bc25fa6e77"}, + {file = "wrapt-1.17.3-cp311-cp311-win_arm64.whl", hash = "sha256:5a7b3c1ee8265eb4c8f1b7d29943f195c00673f5ab60c192eba2d4a7eae5f46a"}, + {file = "wrapt-1.17.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ab232e7fdb44cdfbf55fc3afa31bcdb0d8980b9b95c38b6405df2acb672af0e0"}, + {file = "wrapt-1.17.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9baa544e6acc91130e926e8c802a17f3b16fbea0fd441b5a60f5cf2cc5c3deba"}, + {file = "wrapt-1.17.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6b538e31eca1a7ea4605e44f81a48aa24c4632a277431a6ed3f328835901f4fd"}, + {file = "wrapt-1.17.3-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:042ec3bb8f319c147b1301f2393bc19dba6e176b7da446853406d041c36c7828"}, + {file = "wrapt-1.17.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3af60380ba0b7b5aeb329bc4e402acd25bd877e98b3727b0135cb5c2efdaefe9"}, + {file = "wrapt-1.17.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0b02e424deef65c9f7326d8c19220a2c9040c51dc165cddb732f16198c168396"}, + {file = "wrapt-1.17.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:74afa28374a3c3a11b3b5e5fca0ae03bef8450d6aa3ab3a1e2c30e3a75d023dc"}, + {file = "wrapt-1.17.3-cp312-cp312-win32.whl", hash = "sha256:4da9f45279fff3543c371d5ababc57a0384f70be244de7759c85a7f989cb4ebe"}, + {file = "wrapt-1.17.3-cp312-cp312-win_amd64.whl", hash = "sha256:e71d5c6ebac14875668a1e90baf2ea0ef5b7ac7918355850c0908ae82bcb297c"}, + {file = "wrapt-1.17.3-cp312-cp312-win_arm64.whl", hash = "sha256:604d076c55e2fdd4c1c03d06dc1a31b95130010517b5019db15365ec4a405fc6"}, + {file = "wrapt-1.17.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a47681378a0439215912ef542c45a783484d4dd82bac412b71e59cf9c0e1cea0"}, + {file = "wrapt-1.17.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:54a30837587c6ee3cd1a4d1c2ec5d24e77984d44e2f34547e2323ddb4e22eb77"}, + {file = "wrapt-1.17.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:16ecf15d6af39246fe33e507105d67e4b81d8f8d2c6598ff7e3ca1b8a37213f7"}, + {file = "wrapt-1.17.3-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6fd1ad24dc235e4ab88cda009e19bf347aabb975e44fd5c2fb22a3f6e4141277"}, + {file = "wrapt-1.17.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ed61b7c2d49cee3c027372df5809a59d60cf1b6c2f81ee980a091f3afed6a2d"}, + {file = "wrapt-1.17.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:423ed5420ad5f5529db9ce89eac09c8a2f97da18eb1c870237e84c5a5c2d60aa"}, + {file = "wrapt-1.17.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e01375f275f010fcbf7f643b4279896d04e571889b8a5b3f848423d91bf07050"}, + {file = "wrapt-1.17.3-cp313-cp313-win32.whl", hash = "sha256:53e5e39ff71b3fc484df8a522c933ea2b7cdd0d5d15ae82e5b23fde87d44cbd8"}, + {file = "wrapt-1.17.3-cp313-cp313-win_amd64.whl", hash = "sha256:1f0b2f40cf341ee8cc1a97d51ff50dddb9fcc73241b9143ec74b30fc4f44f6cb"}, + {file = "wrapt-1.17.3-cp313-cp313-win_arm64.whl", hash = "sha256:7425ac3c54430f5fc5e7b6f41d41e704db073309acfc09305816bc6a0b26bb16"}, + {file = "wrapt-1.17.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:cf30f6e3c077c8e6a9a7809c94551203c8843e74ba0c960f4a98cd80d4665d39"}, + {file = "wrapt-1.17.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:e228514a06843cae89621384cfe3a80418f3c04aadf8a3b14e46a7be704e4235"}, + {file = "wrapt-1.17.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:5ea5eb3c0c071862997d6f3e02af1d055f381b1d25b286b9d6644b79db77657c"}, + {file = "wrapt-1.17.3-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:281262213373b6d5e4bb4353bc36d1ba4084e6d6b5d242863721ef2bf2c2930b"}, + {file = "wrapt-1.17.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dc4a8d2b25efb6681ecacad42fca8859f88092d8732b170de6a5dddd80a1c8fa"}, + {file = "wrapt-1.17.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:373342dd05b1d07d752cecbec0c41817231f29f3a89aa8b8843f7b95992ed0c7"}, + {file = "wrapt-1.17.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d40770d7c0fd5cbed9d84b2c3f2e156431a12c9a37dc6284060fb4bec0b7ffd4"}, + {file = "wrapt-1.17.3-cp314-cp314-win32.whl", hash = "sha256:fbd3c8319de8e1dc79d346929cd71d523622da527cca14e0c1d257e31c2b8b10"}, + {file = "wrapt-1.17.3-cp314-cp314-win_amd64.whl", hash = "sha256:e1a4120ae5705f673727d3253de3ed0e016f7cd78dc463db1b31e2463e1f3cf6"}, + {file = "wrapt-1.17.3-cp314-cp314-win_arm64.whl", hash = "sha256:507553480670cab08a800b9463bdb881b2edeed77dc677b0a5915e6106e91a58"}, + {file = "wrapt-1.17.3-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:ed7c635ae45cfbc1a7371f708727bf74690daedc49b4dba310590ca0bd28aa8a"}, + {file = "wrapt-1.17.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:249f88ed15503f6492a71f01442abddd73856a0032ae860de6d75ca62eed8067"}, + {file = "wrapt-1.17.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5a03a38adec8066d5a37bea22f2ba6bbf39fcdefbe2d91419ab864c3fb515454"}, + {file = "wrapt-1.17.3-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:5d4478d72eb61c36e5b446e375bbc49ed002430d17cdec3cecb36993398e1a9e"}, + {file = "wrapt-1.17.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:223db574bb38637e8230eb14b185565023ab624474df94d2af18f1cdb625216f"}, + {file = "wrapt-1.17.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e405adefb53a435f01efa7ccdec012c016b5a1d3f35459990afc39b6be4d5056"}, + {file = "wrapt-1.17.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:88547535b787a6c9ce4086917b6e1d291aa8ed914fdd3a838b3539dc95c12804"}, + {file = "wrapt-1.17.3-cp314-cp314t-win32.whl", hash = "sha256:41b1d2bc74c2cac6f9074df52b2efbef2b30bdfe5f40cb78f8ca22963bc62977"}, + {file = "wrapt-1.17.3-cp314-cp314t-win_amd64.whl", hash = "sha256:73d496de46cd2cdbdbcce4ae4bcdb4afb6a11234a1df9c085249d55166b95116"}, + {file = "wrapt-1.17.3-cp314-cp314t-win_arm64.whl", hash = "sha256:f38e60678850c42461d4202739f9bf1e3a737c7ad283638251e79cc49effb6b6"}, + {file = "wrapt-1.17.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:70d86fa5197b8947a2fa70260b48e400bf2ccacdcab97bb7de47e3d1e6312225"}, + {file = "wrapt-1.17.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:df7d30371a2accfe4013e90445f6388c570f103d61019b6b7c57e0265250072a"}, + {file = "wrapt-1.17.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:caea3e9c79d5f0d2c6d9ab96111601797ea5da8e6d0723f77eabb0d4068d2b2f"}, + {file = "wrapt-1.17.3-cp38-cp38-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:758895b01d546812d1f42204bd443b8c433c44d090248bf22689df673ccafe00"}, + {file = "wrapt-1.17.3-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:02b551d101f31694fc785e58e0720ef7d9a10c4e62c1c9358ce6f63f23e30a56"}, + {file = "wrapt-1.17.3-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:656873859b3b50eeebe6db8b1455e99d90c26ab058db8e427046dbc35c3140a5"}, + {file = "wrapt-1.17.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:a9a2203361a6e6404f80b99234fe7fb37d1fc73487b5a78dc1aa5b97201e0f22"}, + {file = "wrapt-1.17.3-cp38-cp38-win32.whl", hash = "sha256:55cbbc356c2842f39bcc553cf695932e8b30e30e797f961860afb308e6b1bb7c"}, + {file = "wrapt-1.17.3-cp38-cp38-win_amd64.whl", hash = "sha256:ad85e269fe54d506b240d2d7b9f5f2057c2aa9a2ea5b32c66f8902f768117ed2"}, + {file = "wrapt-1.17.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:30ce38e66630599e1193798285706903110d4f057aab3168a34b7fdc85569afc"}, + {file = "wrapt-1.17.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:65d1d00fbfb3ea5f20add88bbc0f815150dbbde3b026e6c24759466c8b5a9ef9"}, + {file = "wrapt-1.17.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a7c06742645f914f26c7f1fa47b8bc4c91d222f76ee20116c43d5ef0912bba2d"}, + {file = "wrapt-1.17.3-cp39-cp39-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7e18f01b0c3e4a07fe6dfdb00e29049ba17eadbc5e7609a2a3a4af83ab7d710a"}, + {file = "wrapt-1.17.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0f5f51a6466667a5a356e6381d362d259125b57f059103dd9fdc8c0cf1d14139"}, + {file = "wrapt-1.17.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:59923aa12d0157f6b82d686c3fd8e1166fa8cdfb3e17b42ce3b6147ff81528df"}, + {file = "wrapt-1.17.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:46acc57b331e0b3bcb3e1ca3b421d65637915cfcd65eb783cb2f78a511193f9b"}, + {file = "wrapt-1.17.3-cp39-cp39-win32.whl", hash = "sha256:3e62d15d3cfa26e3d0788094de7b64efa75f3a53875cdbccdf78547aed547a81"}, + {file = "wrapt-1.17.3-cp39-cp39-win_amd64.whl", hash = "sha256:1f23fa283f51c890eda8e34e4937079114c74b4c81d2b2f1f1d94948f5cc3d7f"}, + {file = "wrapt-1.17.3-cp39-cp39-win_arm64.whl", hash = "sha256:24c2ed34dc222ed754247a2702b1e1e89fdbaa4016f324b4b8f1a802d4ffe87f"}, + {file = "wrapt-1.17.3-py3-none-any.whl", hash = "sha256:7171ae35d2c33d326ac19dd8facb1e82e5fd04ef8c6c0e394d7af55a55051c22"}, + {file = "wrapt-1.17.3.tar.gz", hash = "sha256:f66eb08feaa410fe4eebd17f2a2c8e2e46d3476e9f8c783daa8e09e0faa666d0"}, +] + [[package]] name = "xattr" version = "1.3.0" @@ -9827,25 +10776,7 @@ files = [ [package.extras] cffi = ["cffi (>=1.17,<2.0) ; platform_python_implementation != \"PyPy\" and python_version < \"3.14\"", "cffi (>=2.0.0b) ; platform_python_implementation != \"PyPy\" and python_version >= \"3.14\""] -[[package]] -name = "zuban" -version = "0.0.24" -description = "Zuban - The Zuban Language Server" -optional = false -python-versions = ">=3.7" -groups = ["dev"] -files = [ - {file = "zuban-0.0.24-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:5d50085686be69c3cde97d58a83c56b16a6a9cb97206a6707243dd49fa1b78b9"}, - {file = "zuban-0.0.24-py3-none-macosx_11_0_arm64.whl", hash = "sha256:d53c9f954905396d7841b4cdb2aff43a079695d10bf93bf614ad353f7556fab0"}, - {file = "zuban-0.0.24-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28e76feed159631c4733a9297d8b86cae124e754c6d68c615994ceeb4d966780"}, - {file = "zuban-0.0.24-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8cff03602aec1f1b125316685183911260c6927211b80b598ac9766db50c4aa0"}, - {file = "zuban-0.0.24-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44d476970b20778700fa4ae56112226b2ed4c192bba6c43f70c305c4d8bb15b6"}, - {file = "zuban-0.0.24-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:845b8b58adce6ad90012e79fa2b4515f8748b486c4e4c36a06eb85c0798e5ed0"}, - {file = "zuban-0.0.24-py3-none-win32.whl", hash = "sha256:7542e78770e93c8a9c29320b00e3169e6c8dcb8a9957b1150377541ee6ef0177"}, - {file = "zuban-0.0.24-py3-none-win_amd64.whl", hash = "sha256:ef88eb0932ce00adcfbc4d0c1c9dccaf233aec55029a407e335e59e3727137a8"}, -] - [metadata] lock-version = "2.1" python-versions = "3.12.*" -content-hash = "f50784950477b4ca970a364574990980c3b492be15e9b04705773edb9ee03dbb" +content-hash = "3a2111c8c2898964edb64eeaf405fa55b0d4e7683dbeeaed1b541c89cbe20fe4" diff --git a/pyproject.toml b/pyproject.toml index c712bfc..8207776 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,19 +18,19 @@ abzu = "abzu.cli.click_cli:main" [tool.poetry.dependencies] python = "3.12.*" scrapy = "^2.12.0" -baml-py = "0.214.0" +baml-py = ">=0.215.0" pyspark = "^3.5.5" python-dateutil = "^2.9.0.post0" pydantic = "^2.11.3" pandas = "^2.2.3" -graphistry = {git = "https://github.com/graphistry/pygraphistry.git", rev = "refs/pull/868/head"} +graphistry = ">=0.48.0" pyarrow = "^19.0.1" seaborn = "^0.13.2" networkx = "^3.4.2" matplotlib = "^3.10.1" graphframes-latest = "^0.8.3" requests = "^2.32.3" -cleanco = "^2.2.0" +cleanco = {git = "https://github.com/Graphlet-AI/cleanco.git", rev = "rjurney/disco"} click = "^8.1.8" langchain = "^0.3.24" langchain-community = "^0.3.23" @@ -58,6 +58,8 @@ stop-words = "^2018.7.23" playwright = "^1.54.0" sparkdantic = {git = "https://github.com/Graphlet-AI/sparkdantic.git", rev = "fix-forward-references"} wandb = "0.23.0" +sec-edgar-api = "^1.1.0" +pyspark-mcp = ">=0.0.3" [tool.poetry.group.dev.dependencies] flake8 = "^7.2.0" @@ -78,7 +80,7 @@ types-beautifulsoup4 = "^4.12.0.20250204" pytest-asyncio = "^1.0.0" types-pyyaml = "^6.0.12.20250822" pytest-xdist = "^3.8.0" -zuban = "^0.0.24" +mypy = "^1.19.1" [build-system] requires = ["poetry-core"] @@ -101,36 +103,20 @@ target-version = ["py311"] profile = "black" src_paths = ["abzu", "tests"] -[tool.zuban] +[tool.mypy] python_version = "3.12" files = ["abzu"] exclude = ["abzu/baml_client", "tests"] -warn_return_any = true -warn_unused_configs = true -disallow_untyped_defs = true -disallow_any_generics = true -check_untyped_defs = true ignore_missing_imports = true -warn_unreachable = false -[[tool.zuban.overrides]] -module = "abzu.baml_client" -ignore_errors = true -ignore_missing_imports = true - -[[tool.zuban.overrides]] +[[tool.mypy.overrides]] module = "abzu.baml_client.*" ignore_errors = true -[[tool.zuban.overrides]] +[[tool.mypy.overrides]] module = "discord.*" ignore_missing_imports = true -[[tool.zuban.overrides]] -module = "notebooks.*" -ignore_errors = true -ignore_missing_imports = true - [tool.flake8] max-line-length = 100 docstring-convention = "numpy" diff --git a/tests/test_domain_suffix_removal.py b/tests/test_domain_suffix_removal.py index bb17dfd..c533774 100644 --- a/tests/test_domain_suffix_removal.py +++ b/tests/test_domain_suffix_removal.py @@ -10,7 +10,7 @@ def spark(): """Create a SparkSession for testing.""" spark = ( - SparkSession.builder.master("local[1]") + SparkSession.builder.master("local[1]") # type: ignore[attr-defined] .appName("test_domain_suffix") .config("spark.sql.shuffle.partitions", "1") .config("spark.ui.enabled", "false") @@ -92,9 +92,10 @@ def test_remove_domain_suffix_edge_cases(): def test_get_first_word_with_periods(spark): """Test that get_first_word preserves legitimate periods.""" - from abzu.spark.er_block import get_first_word from pyspark.sql import functions as F + from abzu.spark.er_block import get_first_word + # Create test DataFrame test_data = [ ("St. Jude Medical",), @@ -117,9 +118,10 @@ def test_get_first_word_with_periods(spark): def test_get_acronym_with_periods(spark): """Test that get_acronym handles periods correctly.""" - from abzu.spark.er_block import get_acronym from pyspark.sql import functions as F + from abzu.spark.er_block import get_acronym + # Create test DataFrame test_data = [ ("St. Jude Medical",), diff --git a/tests/test_er_block_udtf.py b/tests/test_er_block_udtf.py index 9b6efa9..3c90b09 100644 --- a/tests/test_er_block_udtf.py +++ b/tests/test_er_block_udtf.py @@ -2,6 +2,7 @@ import json import os +from typing import Any import pytest from pyspark.sql import SparkSession @@ -12,10 +13,10 @@ @pytest.fixture(scope="module") -def spark(): +def spark() -> SparkSession: # type: ignore """Create a SparkSession for testing.""" spark = ( - SparkSession.builder.master("local[1]") + SparkSession.builder.master("local[1]") # type: ignore .appName("test_er_block_udtf") .config("spark.sql.shuffle.partitions", "1") .config("spark.ui.enabled", "false") @@ -26,7 +27,7 @@ def spark(): spark.stop() -def create_raw_companies_data(): +def create_raw_companies_data() -> list[dict[str, Any]]: """Create sample raw company data (first iteration input).""" return [ { @@ -160,29 +161,29 @@ def test_er_blocking_first_iteration(spark, tmp_path): build_blocks(input_path=input_path, output_path=output_path, local_mode=True, stop_spark=False) # Check output files exist - assert os.path.exists(os.path.join(output_path, "all_blocks.parquet")) + assert os.path.exists(os.path.join(output_path, "union_blocks.parquet")) assert os.path.exists(os.path.join(output_path, "combined_blocks.parquet")) # Load and validate blocks - all_blocks = spark.read.parquet(os.path.join(output_path, "all_blocks.parquet")) + union_blocks = spark.read.parquet(os.path.join(output_path, "union_blocks.parquet")) # Check schema - assert "block_key" in all_blocks.columns - assert "block_key_type" in all_blocks.columns - assert "companies" in all_blocks.columns - assert "block_size" in all_blocks.columns + assert "block_key" in union_blocks.columns + assert "block_key_type" in union_blocks.columns + assert "companies" in union_blocks.columns + assert "block_size" in union_blocks.columns # Check we have blocks - assert all_blocks.count() > 0 + assert union_blocks.count() > 0 # Check company struct doesn't have block fields - companies_schema = all_blocks.schema["companies"].dataType.elementType + companies_schema = union_blocks.schema["companies"].dataType.elementType field_names = [field.name for field in companies_schema.fields] assert "block_key" not in field_names assert "block_key_type" not in field_names # Verify data integrity - sample_block = all_blocks.filter(F.col("block_key") == "APPLE").collect() + sample_block = union_blocks.filter(F.col("block_key") == "APPLE").collect() if sample_block: companies = sample_block[0]["companies"] assert len(companies) > 0 @@ -208,27 +209,27 @@ def test_er_blocking_second_iteration(spark, tmp_path): build_blocks(input_path=input_path, output_path=output_path, local_mode=True, stop_spark=False) # Check output files exist - assert os.path.exists(os.path.join(output_path, "all_blocks.parquet")) + assert os.path.exists(os.path.join(output_path, "union_blocks.parquet")) # Load and validate blocks - all_blocks = spark.read.parquet(os.path.join(output_path, "all_blocks.parquet")) + union_blocks = spark.read.parquet(os.path.join(output_path, "union_blocks.parquet")) # Check we have blocks - assert all_blocks.count() > 0 + assert union_blocks.count() > 0 # Check company struct doesn't have contamination from block fields - companies_schema = all_blocks.schema["companies"].dataType.elementType + companies_schema = union_blocks.schema["companies"].dataType.elementType field_names = [field.name for field in companies_schema.fields] assert "block_key" not in field_names assert "block_key_type" not in field_names # Load the JSON to verify field mapping is correct - all_blocks_json_path = os.path.join(output_path, "all_blocks.json") + union_blocks_json_path = os.path.join(output_path, "union_blocks.json") # Find the first JSON file in the directory - json_files = [f for f in os.listdir(all_blocks_json_path) if f.endswith(".json")] + json_files = [f for f in os.listdir(union_blocks_json_path) if f.endswith(".json")] if json_files: - json_file_path = os.path.join(all_blocks_json_path, json_files[0]) + json_file_path = os.path.join(union_blocks_json_path, json_files[0]) # Read first line of JSON with open(json_file_path) as f: @@ -273,8 +274,8 @@ def test_schema_consistency_across_iterations(spark, tmp_path): build_blocks(resolved_path, resolved_output, local_mode=True, stop_spark=False) # Load results - raw_blocks = spark.read.parquet(os.path.join(raw_output, "all_blocks.parquet")) - resolved_blocks = spark.read.parquet(os.path.join(resolved_output, "all_blocks.parquet")) + raw_blocks = spark.read.parquet(os.path.join(raw_output, "union_blocks.parquet")) + resolved_blocks = spark.read.parquet(os.path.join(resolved_output, "union_blocks.parquet")) # Compare schemas raw_company_schema = raw_blocks.schema["companies"].dataType.elementType diff --git a/tests/test_source_uuid_tracking.py b/tests/test_source_uuid_tracking.py index c6f4bb1..ac2835e 100644 --- a/tests/test_source_uuid_tracking.py +++ b/tests/test_source_uuid_tracking.py @@ -159,19 +159,19 @@ def run_single_iteration( # Load blocks - handle partitioned JSON output blocks_data = [] - # Try all_blocks.json first (might be a directory with part files) - all_blocks_path = os.path.join(blocks_path, "all_blocks.json") - if os.path.isdir(all_blocks_path): + # Try union_blocks.json first (might be a directory with part files) + union_blocks_path = os.path.join(blocks_path, "union_blocks.json") + if os.path.isdir(union_blocks_path): # Load all part-*.json files from the directory import glob - part_files = glob.glob(os.path.join(all_blocks_path, "part-*.json")) + part_files = glob.glob(os.path.join(union_blocks_path, "part-*.json")) for part_file in sorted(part_files): with open(part_file, "r") as f: blocks_data.extend([json.loads(line) for line in f]) - elif os.path.isfile(all_blocks_path): + elif os.path.isfile(union_blocks_path): # Single file - with open(all_blocks_path, "r") as f: + with open(union_blocks_path, "r") as f: blocks_data = [json.loads(line) for line in f] else: # Try alternative location - combined_blocks.json @@ -299,7 +299,7 @@ def run_single_iteration( ] ) - matches_df = spark.createDataFrame(matched_blocks, schema=matches_schema) # type: ignore[type-var] + matches_df = spark.createDataFrame(matched_blocks, schema=matches_schema) # type: ignore[arg-type] matches_df.write.mode("overwrite").parquet(matches_parquet_path) # Step 3: Evaluation diff --git a/tests/test_walmart_amazon.py b/tests/test_walmart_amazon.py index 24bd8eb..9b37dda 100644 --- a/tests/test_walmart_amazon.py +++ b/tests/test_walmart_amazon.py @@ -1,8 +1,6 @@ """Tests for the walmart_amazon download CLI command.""" -import io import zipfile -from pathlib import Path from unittest.mock import Mock, patch import pandas as pd @@ -134,9 +132,7 @@ def test_walmart_amazon_http_error(tmp_path): with patch("abzu.cli.download.walmart_amazon.requests.get") as mock_get: # Simulate an HTTP error mock_response = Mock() - mock_response.raise_for_status.side_effect = requests.exceptions.HTTPError( - "404 Not Found" - ) + mock_response.raise_for_status.side_effect = requests.exceptions.HTTPError("404 Not Found") mock_get.return_value = mock_response result = runner.invoke(walmart_amazon, ["-o", str(output_dir)])