diff --git a/.github/actions/setup-environment/action.yml b/.github/actions/setup-environment/action.yml
index 299e831e9..b0225eadc 100644
--- a/.github/actions/setup-environment/action.yml
+++ b/.github/actions/setup-environment/action.yml
@@ -9,7 +9,7 @@ runs:
using: "composite"
steps:
- name: Install UV
- uses: astral-sh/setup-uv@v5.3
+ uses: astral-sh/setup-uv@v5.4
id: setup-uv
with:
enable-cache: true
diff --git a/.github/workflows/bot-pr-checks.yml b/.github/workflows/bot-pr-checks.yml
new file mode 100644
index 000000000..95ab19e26
--- /dev/null
+++ b/.github/workflows/bot-pr-checks.yml
@@ -0,0 +1,50 @@
+name: Bot PR Checks
+
+on:
+ pull_request_target:
+ types: [ opened, synchronize, reopened, labeled ]
+ branches:
+ - "develop"
+
+jobs:
+ bot-check:
+ runs-on: ubuntu-latest
+ if: contains(github.triggering_actor, '[bot]')
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+ ref: ${{ github.event.pull_request.head.sha }}
+
+ - name: Setup environment
+ uses: ./.github/actions/setup-environment
+
+ - name: Run tests
+ timeout-minutes: 5
+ run: |
+ uv run pytest \
+ -n auto \
+ --cov src \
+ --timeout 15 \
+ -o junit_suite_name="bot-unit-tests" \
+ tests/unit
+
+ - uses: ./.github/actions/report
+ with:
+ flag: bot-unit-tests
+ codecov_token: ${{ secrets.CODECOV_TOKEN }}
+
+ # Add a job to handle the Build & Release workflow for bot PRs
+ bot-build:
+ runs-on: ubuntu-latest
+ if: contains(github.triggering_actor, '[bot]')
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+ ref: ${{ github.event.pull_request.head.sha }}
+
+ - name: Skip build for bot PRs
+ run: echo "Skipping build for bot PRs"
diff --git a/.github/workflows/mypy.yml b/.github/workflows/mypy.yml
index 4d3c5c9e8..4662ff63d 100644
--- a/.github/workflows/mypy.yml
+++ b/.github/workflows/mypy.yml
@@ -1,7 +1,7 @@
name: Mypy Checks
on:
- pull_request:
+ pull_request_target:
branches:
- "develop"
@@ -19,6 +19,7 @@ jobs:
uses: actions/checkout@v4
with:
fetch-depth: 0
+ ref: ${{ github.event.pull_request.head.sha }}
- name: Setup environment
uses: ./.github/actions/setup-environment
diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml
index 153fe1d9b..c58ff3786 100644
--- a/.github/workflows/pre-commit.yml
+++ b/.github/workflows/pre-commit.yml
@@ -1,7 +1,7 @@
name: pre-commit
on:
- pull_request:
+ pull_request_target:
branches:
- "develop"
push:
@@ -21,6 +21,7 @@ jobs:
with:
fetch-depth: 0
token: ${{ env.REPO_SCOPED_TOKEN || github.token }}
+ ref: ${{ github.event.pull_request.head.sha }}
- name: Setup environment
uses: ./.github/actions/setup-environment
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 7876de525..3751a14c9 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -25,8 +25,26 @@ permissions:
contents: read
jobs:
+ # Add a check to skip the build for bot PRs
+ check-bot:
+ runs-on: ubuntu-latest
+ outputs:
+ is_bot: ${{ steps.check-bot.outputs.is_bot }}
+ steps:
+ - name: Check if user is bot
+ id: check-bot
+ run: |
+ if [[ "${{ github.triggering_actor }}" == *"[bot]" ]]; then
+ echo "is_bot=true" >> $GITHUB_OUTPUT
+ else
+ echo "is_bot=false" >> $GITHUB_OUTPUT
+ fi
+
build:
name: Build 3.${{ matrix.python }} ${{ matrix.os }}
+ needs: check-bot
+ # Skip this job if the PR is from a bot
+ if: needs.check-bot.outputs.is_bot != 'true'
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
@@ -55,7 +73,7 @@ jobs:
repository: ${{ github.event.pull_request.head.repo.full_name || github.event.repository.full_name }}
- name: Install UV
- uses: astral-sh/setup-uv@v5.3
+ uses: astral-sh/setup-uv@v5.4
id: setup-uv
with:
enable-cache: false
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 9b923bf53..06b7396d4 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -14,7 +14,17 @@ jobs:
access-check:
runs-on: ubuntu-latest
steps:
+ - name: Check if user is bot
+ id: check-bot
+ run: |
+ if [[ "${{ github.triggering_actor }}" == *"[bot]" ]]; then
+ echo "is_bot=true" >> $GITHUB_OUTPUT
+ else
+ echo "is_bot=false" >> $GITHUB_OUTPUT
+ fi
+
- uses: actions-cool/check-user-permission@v2
+ if: steps.check-bot.outputs.is_bot != 'true'
with:
require: write
username: ${{ github.triggering_actor }}
@@ -32,15 +42,20 @@ jobs:
- name: Setup environment
uses: ./.github/actions/setup-environment
- - name: Run ATS and Tests
- uses: ./.github/actions/run-ats
- timeout-minutes: 15
+ - name: Test with pytest
+ timeout-minutes: 5
+ run: |
+ uv run pytest \
+ -n auto \
+ --cov src \
+ --timeout 15 \
+ -o junit_suite_name="${{github.job}}" \
+ tests/unit
+
+ - uses: ./.github/actions/report
with:
- default_tests: "tests/unit"
- codecov_static_token: ${{ secrets.CODECOV_STATIC_TOKEN }}
+ flag: unit-tests
codecov_token: ${{ secrets.CODECOV_TOKEN }}
- collect_args: "--timeout 15"
- codecov_flags: unit-tests
codemod-tests:
needs: access-check
diff --git a/README.md b/README.md
index f9e94756b..985a7f196 100644
--- a/README.md
+++ b/README.md
@@ -1,117 +1,114 @@
-
-
-
-
-
-
-
-
-
- Scriptable interface to a powerful, multi-lingual language server.
-
-
-
-
-[](https://pypi.org/project/codegen/)
-[](https://docs.codegen.com)
-[](https://community.codegen.com)
-[](https://github.com/codegen-sh/codegen-sdk/tree/develop?tab=Apache-2.0-1-ov-file)
-[](https://x.com/codegen)
-
-
-
-
-
-[Codegen](https://docs.codegen.com) is a python library for manipulating codebases.
-
-```python
-from codegen import Codebase
-
-# Codegen builds a complete graph connecting
-# functions, classes, imports and their relationships
-codebase = Codebase("./")
-
-# Work with code without dealing with syntax trees or parsing
-for function in codebase.functions:
- # Comprehensive static analysis for references, dependencies, etc.
- if not function.usages:
- # Auto-handles references and imports to maintain correctness
- function.move_to_file("deprecated.py")
+# Comprehensive Codebase Analyzer
+
+A powerful static code analysis system that provides extensive information about your codebase using the Codegen SDK.
+
+## Features
+
+This analyzer provides comprehensive analysis of your codebase, including:
+
+### 1. Codebase Structure Analysis
+- File Statistics (count, language, size)
+- Symbol Tree Analysis
+- Import/Export Analysis
+- Module Organization
+
+### 2. Symbol-Level Analysis
+- Function Analysis (parameters, return types, complexity)
+- Class Analysis (methods, attributes, inheritance)
+- Variable Analysis
+- Type Analysis
+
+### 3. Dependency and Flow Analysis
+- Call Graph Generation
+- Data Flow Analysis
+- Control Flow Analysis
+- Symbol Usage Analysis
+
+### 4. Code Quality Analysis
+- Unused Code Detection
+- Code Duplication Analysis
+- Complexity Metrics
+- Style and Convention Analysis
+
+### 5. Visualization Capabilities
+- Dependency Graphs
+- Call Graphs
+- Symbol Trees
+- Heat Maps
+
+### 6. Language-Specific Analysis
+- Python-Specific Analysis
+- TypeScript-Specific Analysis
+
+### 7. Code Metrics
+- Monthly Commits
+- Cyclomatic Complexity
+- Halstead Volume
+- Maintainability Index
+
+## Installation
+
+1. Clone the repository:
+```bash
+git clone https://github.com/yourusername/codebase-analyzer.git
+cd codebase-analyzer
```
-Write code that transforms code. Codegen combines the parsing power of [Tree-sitter](https://tree-sitter.github.io/tree-sitter/) with the graph algorithms of [rustworkx](https://github.com/Qiskit/rustworkx) to enable scriptable, multi-language code manipulation at scale.
-
-## Installation and Usage
-
-We support
-
-- Running Codegen in Python 3.12 - 3.13 (recommended: Python 3.13+)
-- macOS and Linux
- - macOS is supported
- - Linux is supported on x86_64 and aarch64 with glibc 2.34+
- - Windows is supported via WSL. See [here](https://docs.codegen.com/building-with-codegen/codegen-with-wsl) for more details.
-- Python, Typescript, Javascript and React codebases
-
-```
-# Install inside existing project
-uv pip install codegen
-
-# Install global CLI
-uv tool install codegen --python 3.13
-
-# Create a codemod for a given repo
-cd path/to/repo
-codegen init
-codegen create test-function
-
-# Run the codemod
-codegen run test-function
-
-# Create an isolated venv with codegen => open jupyter
-codegen notebook
+2. Install dependencies:
+```bash
+pip install -r requirements.txt
```
## Usage
-See [Getting Started](https://docs.codegen.com/introduction/getting-started) for a full tutorial.
+### Analyzing a Repository
-```
-from codegen import Codebase
-```
-
-## Troubleshooting
+```bash
+# Analyze from URL
+python codebase_analyzer.py --repo-url https://github.com/username/repo
-Having issues? Here are some common problems and their solutions:
+# Analyze local repository
+python codebase_analyzer.py --repo-path /path/to/repo
-- **I'm hitting an UV error related to `[[ packages ]]`**: This means you're likely using an outdated version of UV. Try updating to the latest version with: `uv self update`.
-- **I'm hitting an error about `No module named 'codegen.sdk.extensions.utils'`**: The compiled cython extensions are out of sync. Update them with `uv sync --reinstall-package codegen`.
-- **I'm hitting a `RecursionError: maximum recursion depth exceeded` error while parsing my codebase**: If you are using python 3.12, try upgrading to 3.13. If you are already on 3.13, try upping the recursion limit with `sys.setrecursionlimit(10000)`.
+# Specify language
+python codebase_analyzer.py --repo-url https://github.com/username/repo --language python
-If you run into additional issues not listed here, please [join our slack community](https://community.codegen.com) and we'll help you out!
-
-## Resources
+# Analyze specific categories
+python codebase_analyzer.py --repo-url https://github.com/username/repo --categories codebase_structure code_quality
+```
-- [Docs](https://docs.codegen.com)
-- [Getting Started](https://docs.codegen.com/introduction/getting-started)
-- [Contributing](CONTRIBUTING.md)
-- [Contact Us](https://codegen.com/contact)
+### Output Formats
-## Why Codegen?
+```bash
+# Output as JSON
+python codebase_analyzer.py --repo-url https://github.com/username/repo --output-format json --output-file analysis.json
-Software development is fundamentally programmatic. Refactoring a codebase, enforcing patterns, or analyzing control flow - these are all operations that can (and should) be expressed as programs themselves.
+# Generate HTML report
+python codebase_analyzer.py --repo-url https://github.com/username/repo --output-format html --output-file report.html
-We built Codegen backwards from real-world refactors performed on enterprise codebases. Instead of starting with theoretical abstractions, we focused on creating APIs that match how developers actually think about code changes:
+# Print to console (default)
+python codebase_analyzer.py --repo-url https://github.com/username/repo --output-format console
+```
-- **Natural mental model**: Write transforms that read like your thought process - "move this function", "rename this variable", "add this parameter". No more wrestling with ASTs or manual import management.
+## Available Analysis Categories
-- **Battle-tested on complex codebases**: Handle Python, TypeScript, and React codebases with millions of lines of code.
+- `codebase_structure`: File statistics, symbol tree, import/export analysis, module organization
+- `symbol_level`: Function, class, variable, and type analysis
+- `dependency_flow`: Call graphs, data flow, control flow, symbol usage
+- `code_quality`: Unused code, duplication, complexity, style
+- `visualization`: Dependency graphs, call graphs, symbol trees, heat maps
+- `language_specific`: Language-specific analysis features
+- `code_metrics`: Commits, complexity, volume, maintainability
-- **Built for advanced intelligences**: As AI developers become more sophisticated, they need expressive yet precise tools to manipulate code. Codegen provides a programmatic interface that both humans and AI can use to express complex transformations through code itself.
+## Requirements
-## Contributing
+- Python 3.8+
+- Codegen SDK
+- NetworkX
+- Matplotlib
+- Rich
-Please see our [Contributing Guide](CONTRIBUTING.md) for instructions on how to set up the development environment and submit contributions.
+## License
-## Enterprise
+MIT
-For more information on enterprise engagements, please [contact us](https://codegen.com/contact) or [request a demo](https://codegen.com/request-demo).
diff --git a/codegen-examples/examples/deep_code_research/run.py b/codegen-examples/examples/deep_code_research/run.py
index 1172590bb..314d4f0cf 100644
--- a/codegen-examples/examples/deep_code_research/run.py
+++ b/codegen-examples/examples/deep_code_research/run.py
@@ -11,7 +11,7 @@
from codegen.extensions.langchain.tools import (
ListDirectoryTool,
RevealSymbolTool,
- SearchTool,
+ RipGrepTool,
SemanticSearchTool,
ViewFileTool,
)
@@ -100,7 +100,7 @@ def research(repo_name: Optional[str] = None, query: Optional[str] = None, threa
tools = [
ViewFileTool(codebase),
ListDirectoryTool(codebase),
- SearchTool(codebase),
+ RipGrepTool(codebase),
SemanticSearchTool(codebase),
RevealSymbolTool(codebase),
]
diff --git a/codegen-examples/examples/langchain_agent/README.md b/codegen-examples/examples/langchain_agent/README.md
index 113610302..4744cdec3 100644
--- a/codegen-examples/examples/langchain_agent/README.md
+++ b/codegen-examples/examples/langchain_agent/README.md
@@ -57,7 +57,7 @@ The agent comes with several built-in tools for code operations:
- `ViewFileTool`: View file contents and metadata
- `ListDirectoryTool`: List directory contents
-- `SearchTool`: Search code using regex
+- `RipGrepTool`: Search code using ripgrep
- `EditFileTool`: Edit file contents
- `CreateFileTool`: Create new files
- `DeleteFileTool`: Delete files
diff --git a/codegen-examples/examples/langchain_agent/run.py b/codegen-examples/examples/langchain_agent/run.py
index 5c6891889..30de9ed49 100644
--- a/codegen-examples/examples/langchain_agent/run.py
+++ b/codegen-examples/examples/langchain_agent/run.py
@@ -1,6 +1,9 @@
"""Demo implementation of an agent with Codegen tools."""
from codegen import Codebase
+from codegen.extensions.langchain.graph import create_react_agent
+from codegen.extensions.langchain.llm import LLM
+from codegen.extensions.langchain.prompts import REASONER_SYSTEM_MESSAGE
from codegen.extensions.langchain.tools import (
CommitTool,
CreateFileTool,
@@ -10,18 +13,13 @@
MoveSymbolTool,
RenameFileTool,
RevealSymbolTool,
- SearchTool,
+ RipGrepTool,
SemanticEditTool,
ViewFileTool,
)
-
-from codegen.extensions.langchain.llm import LLM
-from codegen.extensions.langchain.prompts import REASONER_SYSTEM_MESSAGE
-
+from langchain_core.messages import SystemMessage
from langgraph.checkpoint.memory import MemorySaver
from langgraph.graph.graph import CompiledGraph
-from codegen.extensions.langchain.graph import create_react_agent
-from langchain_core.messages import SystemMessage
def create_codebase_agent(
@@ -57,7 +55,7 @@ def create_codebase_agent(
tools = [
ViewFileTool(codebase),
ListDirectoryTool(codebase),
- SearchTool(codebase),
+ RipGrepTool(codebase),
EditFileTool(codebase),
CreateFileTool(codebase),
DeleteFileTool(codebase),
diff --git a/codegen-examples/examples/swebench_agent_run/.env.db.template b/codegen-examples/examples/swebench_agent_run/.env.db.template
new file mode 100644
index 000000000..e29dbd721
--- /dev/null
+++ b/codegen-examples/examples/swebench_agent_run/.env.db.template
@@ -0,0 +1,5 @@
+POSTGRES_HOST="localhost"
+POSTGRES_DATABASE="swebench"
+POSTGRES_USER="swebench"
+POSTGRES_PASSWORD="swebench"
+POSTGRES_PORT="5432"
diff --git a/codegen-examples/examples/swebench_agent_run/README.md b/codegen-examples/examples/swebench_agent_run/README.md
index ddbf86814..711d395e7 100644
--- a/codegen-examples/examples/swebench_agent_run/README.md
+++ b/codegen-examples/examples/swebench_agent_run/README.md
@@ -1,38 +1,94 @@
-# INSTRUCTIONS
+# SWE-bench Agent Runner
-1. Create a `.env` file in the `swebench_agent_run` directory (codegen-examples/examples/swebench_agent_run) and add your API keys.
+Tool for running and evaluating model fixes using SWE-bench.
-1. cd into the `codegen-examples/examples/swebench_agent_run` directory
+## Setup
-1. Create a `.venv` with `uv venv` and activate it with `source .venv/bin/activate`
+1. Using the `.env.template` reference, create a `.env` file in the project root and add your API keys:
-1. Install the dependencies with `uv pip install .`
+ ```env
+ OPENAI_API_KEY=your_key_here
+ MODAL_TOKEN_ID=your_token_id
+ MODAL_TOKEN_SECRET=your_token_secret
+ ```
-1. Install the codegen dependencies with `uv add codegen`
+1. Create and activate a virtual environment:
-- Note: If you'd like to install the dependencies using the global environment, use `uv pip install -e ../../../` instead of `uv pip install .`. This will allow you to test modifications to the codegen codebase. You will need to run `uv pip install -e ../../../` each time you make changes to the codebase.
+ ```bash
+ uv venv
+ source .venv/bin/activate
+ ```
-6. Ensure that you have a modal account and profile set up. If you don't have one, you can create one at https://modal.com/
+1. Install the package:
-1. Activate the appropriate modal profile `python -m modal profile activate `
+ ```bash
+ # Basic installation
+ uv pip install -e .
-1. Launch the modal app with `python -m modal deploy --env= entry_point.py`
+ # With metrics support
+ uv pip install -e ".[metrics]"
-1. Run the evaluation with `python -m run_eval` with the desired options:
+ # With development tools
+ uv pip install -e ".[dev]"
-- ```bash
- $ python run_eval.py --help
- Usage: run_eval.py [OPTIONS]
+ # Install everything
+ uv pip install -e ".[all]"
+ ```
- Options:
- --use-existing-preds TEXT The run ID of the existing predictions to
- use.
+1. Set up Modal:
+
+ - Create an account at https://modal.com/ if you don't have one
+ - Activate your Modal profile:
+ ```bash
+ python -m modal profile activate
+ ```
+
+## Usage
+
+The package provides two main command-line tools:
+
+### Testing SWE CodeAgent
+
+Run the agent on a specific repository:
+
+```bash
+# Using the installed command
+swe-agent --repo pallets/flask --prompt "Analyze the URL routing system"
+
+# Options
+swe-agent --help
+Options:
+ --agent-class [DefaultAgent|CustomAgent] Agent class to use
+ --repo TEXT Repository to analyze (owner/repo)
+ --prompt TEXT Prompt for the agent
+ --help Show this message and exit
+```
+
+### Running SWE-Bench Eval
+
+Deploy modal app
+
+```bash
+./deploy.sh
+```
+
+Run evaluations on model fixes:
+
+```bash
+# Using the installed command
+swe-eval --dataset lite --length 10
+
+# Options
+swe-eval --help
+Options:
+ --use-existing-preds TEXT Run ID of existing predictions
--dataset [lite|full|verified|lite_small|lite_medium|lite_large]
- The dataset to use.
- --length INTEGER The number of examples to process.
- --instance-id TEXT The instance ID of the example to process.
- --repo TEXT The repo to use.
+ --length INTEGER Number of examples to process
+ --instance-id TEXT Specific instance ID to process
+ --repo TEXT Specific repo to evaluate
+ --local Run evaluation locally
--instance-ids LIST_OF_STRINGS The instance IDs of the examples to process.
Example: --instance-ids ,,...
- --help Show this message and exit.
- ```
+ --push-metrics Push results to metrics database (Requires additional database environment variables)
+ --help Show this message and exit
+```
diff --git a/codegen-examples/examples/swebench_agent_run/agent_cli.py b/codegen-examples/examples/swebench_agent_run/agent_cli.py
new file mode 100644
index 000000000..223cea4cb
--- /dev/null
+++ b/codegen-examples/examples/swebench_agent_run/agent_cli.py
@@ -0,0 +1,55 @@
+import click
+import modal
+from codegen import CodeAgent, Codebase
+
+image = modal.Image.debian_slim(python_version="3.13").apt_install("git").pip_install("codegen")
+
+app = modal.App(
+ name="codegen-examples",
+ image=image,
+ secrets=[modal.Secret.from_dotenv()],
+)
+
+
+@app.function()
+def run_agent(repo_name: str, prompt: str) -> bool:
+ codebase = Codebase.from_repo(repo_full_name=repo_name)
+ agent = CodeAgent(codebase)
+ return agent.run(prompt=prompt)
+
+
+@click.command()
+@click.option(
+ "--repo",
+ type=str,
+ default="pallets/flask",
+ help="The repository to analyze (format: owner/repo)",
+)
+@click.option(
+ "--prompt",
+ type=str,
+ default="Tell me about the codebase and the files in it.",
+ help="The prompt to send to the agent",
+)
+def main(repo: str, prompt: str):
+ """Run a codegen agent on a GitHub repository."""
+ # Import agent class dynamically based on name
+
+ click.echo(f"Running on {repo}")
+ click.echo(f"Prompt: {prompt}")
+
+ try:
+ with app.run():
+ result = run_agent.remote(repo, prompt)
+ if result:
+ click.echo("✅ Analysis completed successfully:")
+ click.echo(result)
+ else:
+ click.echo("❌ Analysis failed")
+ except Exception as e:
+ click.echo(f"❌ Error: {str(e)}", err=True)
+ raise click.Abort()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/codegen-examples/examples/swebench_agent_run/deploy.sh b/codegen-examples/examples/swebench_agent_run/deploy.sh
new file mode 100755
index 000000000..a1a681fb3
--- /dev/null
+++ b/codegen-examples/examples/swebench_agent_run/deploy.sh
@@ -0,0 +1,3 @@
+#! /bin/bash
+
+uv run modal deploy swebench_agent_run/modal_harness/entry_point.py
diff --git a/codegen-examples/examples/swebench_agent_run/entry_point.py b/codegen-examples/examples/swebench_agent_run/entry_point.py
deleted file mode 100644
index a364aaa19..000000000
--- a/codegen-examples/examples/swebench_agent_run/entry_point.py
+++ /dev/null
@@ -1,19 +0,0 @@
-from codegen.extensions.swebench.utils import SweBenchExample
-from codegen.extensions.swebench.harness import run_agent_on_entry
-import modal
-
-image = (
- modal.Image.debian_slim(python_version="3.13")
- .apt_install(["git", "ripgrep"])
- .pip_install("fastapi[standard]")
- .copy_local_dir("../../../", "/root/codegen", ignore=[".venv", "**/.venv", "tests", "**/tests"])
- .run_commands("pip install -e /root/codegen")
-)
-
-app = modal.App(name="swebench-agent-run", image=image, secrets=[modal.Secret.from_dotenv()])
-
-
-@app.function(timeout=43200)
-async def run_agent_modal(entry: SweBenchExample, run_id: str, model: str):
- """Modal function to process a single example from the SWE-bench dataset."""
- return run_agent_on_entry(entry, run_id=run_id, model=model)
diff --git a/codegen-examples/examples/swebench_agent_run/eval_cli.py b/codegen-examples/examples/swebench_agent_run/eval_cli.py
new file mode 100644
index 000000000..3258235bc
--- /dev/null
+++ b/codegen-examples/examples/swebench_agent_run/eval_cli.py
@@ -0,0 +1,360 @@
+import json
+import traceback
+import uuid
+from dataclasses import asdict, dataclass
+from datetime import datetime
+from pathlib import Path
+from typing import Any, ClassVar, Dict, List, Optional, Tuple
+
+import click
+import modal
+from codegen.extensions.swebench.harness import run_agent_on_entry
+from codegen.extensions.swebench.utils import (
+ SWEBenchDataset,
+ SweBenchExample,
+ get_swe_bench_examples,
+)
+from codegen.sdk.core.codebase import Codebase
+
+from swebench_agent_run.constants import DATASET_DICT
+from swebench_agent_run.report import generate_report
+from swebench_agent_run.utils import track_batches
+
+# Constants
+PREDS_DNAME = Path(__file__).parent / "predictions"
+LOG_DIR = Path(__file__).parent / "logs"
+
+# Modal function setup
+run_agent_modal = modal.Function.from_name(
+ app_name="swebench-agent-run",
+ name="run_agent_modal",
+)
+
+
+# Type aliases
+@dataclass
+class ErrorInfo:
+ error_type: str
+ error_message: str
+ traceback: str
+ modal_error_code: Optional[str] = None
+ modal_error_details: Optional[dict] = None
+
+ def format_error(self, example_id: str = "") -> Dict[str, Any]:
+ """Format error information into a structured dictionary."""
+ error_dict = {
+ "error_context": "Processing error"
+ if not example_id
+ else f"Error processing {example_id}",
+ "error_details": {
+ "type": self.error_type,
+ "message": self.error_message,
+ "traceback": self.traceback.split("\n"), # Split for better JSON formatting
+ },
+ }
+
+ if self.modal_error_code or self.modal_error_details:
+ error_dict["error_details"]["modal_specific"] = {
+ "error_code": self.modal_error_code,
+ "error_details": self.modal_error_details,
+ }
+
+ return error_dict
+
+
+@dataclass
+class ProcessingResult:
+ instance_id: str
+ status: Optional[str] = None
+ error_info: Optional[ErrorInfo] = None
+ result: Optional[dict] = None
+
+ ERROR_STATUS: ClassVar[str] = "error" # Class constant for error status
+
+ @classmethod
+ def create_error(cls, instance_id: str, error_info: ErrorInfo) -> "ProcessingResult":
+ """Create a ProcessingResult instance for an error case."""
+ return cls(instance_id=instance_id, status=cls.ERROR_STATUS, error_info=error_info)
+
+
+def create_error_info(error: Exception, example_id: str = "") -> ErrorInfo:
+ """Create standardized error information."""
+ traceback_str = (
+ "".join(traceback.format_exception(type(error), error, error.__traceback__))
+ if hasattr(error, "__traceback__")
+ else traceback.format_exc()
+ )
+
+ error_info = ErrorInfo(
+ error_type=type(error).__name__,
+ error_message=str(error),
+ traceback=traceback_str,
+ )
+
+ if isinstance(error, modal.exception.Error):
+ error_info.modal_error_code = getattr(error, "code", None)
+ error_info.modal_error_details = getattr(error, "details", None)
+
+ # Print formatted error as JSON
+ print(json.dumps(error_info.format_error(example_id), indent=2))
+
+ return error_info
+
+
+def process_modal(
+ examples: list[SweBenchExample],
+ model: str,
+ run_id: str,
+) -> List[ProcessingResult]:
+ """Process examples using Modal's parallel execution."""
+ results: List[ProcessingResult] = []
+
+ try:
+ batch_results = run_agent_modal.starmap(
+ [(ex, run_id, model) for ex in examples],
+ )
+
+ for example, result in zip(examples, batch_results):
+ if isinstance(result, Exception):
+ error_info = create_error_info(result, example.instance_id)
+ results.append(ProcessingResult.create_error(example.instance_id, error_info))
+ elif result is None:
+ print(f"Warning: Null result for {example.instance_id}")
+ results.append(
+ ProcessingResult.create_error(
+ example.instance_id,
+ ErrorInfo(
+ error_type="NullResult",
+ error_message="Process returned None",
+ ),
+ )
+ )
+ else:
+ results.append(ProcessingResult(instance_id=example.instance_id, result=result))
+
+ except Exception as e:
+ error_info = create_error_info(e)
+ # Mark all examples as failed
+ results.extend(
+ [ProcessingResult.create_error(example.instance_id, error_info) for example in examples]
+ )
+
+ return results
+
+
+def process_batch_local(
+ examples: list[SweBenchExample],
+ batch_size: int = 10,
+ codebases: dict[str, Codebase] = {},
+ model: str = "claude-3-7-sonnet-latest",
+ run_id: str | None = None,
+) -> List[ProcessingResult]:
+ """Process examples in local batches."""
+ results: List[ProcessingResult] = []
+
+ for _, batch in track_batches(examples, batch_size, desc="Processing examples"):
+ for example in batch:
+ try:
+ result = run_agent_on_entry(
+ example,
+ model=model,
+ codebase=codebases.get(example.instance_id),
+ run_id=run_id,
+ )
+ results.append(ProcessingResult(instance_id=example.instance_id, result=result))
+ except Exception as e:
+ error_info = create_error_info(e, example.instance_id)
+ results.append(ProcessingResult.create_error(example.instance_id, error_info))
+
+ return results
+
+
+def save_results(
+ results: List[ProcessingResult], predictions_dir: Path, timestamp: str
+) -> Tuple[Path, dict]:
+ """Save individual results and create summary."""
+ # Save individual results
+ for result in results:
+ output_file = predictions_dir / f"{result.instance_id}.json"
+ output_file.parent.mkdir(exist_ok=True, parents=True)
+ with open(output_file, "w") as f:
+ # Convert dataclass to dict for JSON serialization
+ json.dump(asdict(result), f, indent=4)
+
+ # Create and save summary
+ summary = {
+ "timestamp": timestamp,
+ "total_examples": len(results),
+ "successful": len([r for r in results if not r.status]), # No status means success
+ "failed": len([r for r in results if r.status == ProcessingResult.ERROR_STATUS]),
+ "error_types": {},
+ "results": [asdict(r) for r in results], # Convert all results to dict
+ }
+
+ # Collect error statistics
+ for result in results:
+ if result.status == ProcessingResult.ERROR_STATUS and result.error_info:
+ error_type = result.error_info.error_type
+ summary["error_types"][error_type] = summary["error_types"].get(error_type, 0) + 1
+
+ summary_file = predictions_dir / f"summary_{timestamp}.json"
+ with open(summary_file, "w") as f:
+ json.dump(summary, f, indent=4)
+
+ return summary_file, summary
+
+
+def print_summary(summary: dict, predictions_dir: Path, summary_file: Path) -> None:
+ """Print processing summary information."""
+ print("\nProcessing complete!")
+ print(f"Results saved to: {predictions_dir}")
+ print(f"Summary saved to: {summary_file}")
+ print(f"Successful: {summary['successful']}/{summary['total_examples']}")
+ print(f"Failed: {summary['failed']}/{summary['total_examples']}")
+
+ if summary["error_types"]:
+ print("\nError type distribution:")
+ for error_type, count in summary["error_types"].items():
+ print(f" {error_type}: {count}")
+
+
+def run_eval(
+ use_existing_preds: Optional[str],
+ dataset_enum: SWEBenchDataset,
+ length: int,
+ instance_id: Optional[str] = None,
+ local: bool = False,
+ codebases: Dict[str, Codebase] = {},
+ repo: Optional[str] = None,
+ model: str = "claude-3-7-sonnet-latest",
+ instance_ids: list[str] | None = None,
+) -> Tuple[Path, Path, SWEBenchDataset, str]:
+ """Main evaluation function."""
+ run_id = use_existing_preds or str(uuid.uuid4())
+ print(f"Run ID: {run_id}")
+
+ predictions_dir = PREDS_DNAME / f"results_{run_id}"
+
+ examples = get_swe_bench_examples(
+ dataset=dataset_enum,
+ length=length,
+ instance_id=instance_id,
+ repo=repo,
+ instance_ids=instance_ids or [],
+ )
+ print(
+ "Examples:\n" + "\n".join(f"{e.instance_id} - {e.repo} - {e.base_commit}" for e in examples)
+ )
+
+ try:
+ if use_existing_preds is None:
+ print(f"Repo: {repo}")
+ print(
+ f"Examples:\n{'\n'.join([f'{e.instance_id} - {e.repo} - {e.base_commit}' for e in examples])}"
+ )
+ print(f"Processing {len(examples)} examples...")
+
+ predictions_dir.mkdir(exist_ok=True, parents=True)
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
+
+ results = (
+ process_batch_local(
+ examples,
+ codebases=codebases,
+ model=model,
+ run_id=run_id,
+ )
+ if local
+ else process_modal(examples, model=model, run_id=run_id)
+ )
+ summary_file, summary = save_results(results, predictions_dir, timestamp)
+ print_summary(summary, predictions_dir, summary_file)
+
+ return predictions_dir, LOG_DIR, dataset_enum, run_id
+ except Exception:
+ traceback.print_exc()
+ raise
+
+
+def list_of_strings(value: str) -> list[str]:
+ if value == "":
+ return []
+ return value.split(",")
+
+
+@click.command()
+@click.option(
+ "--use-existing-preds",
+ help="The run ID of the existing predictions to use.",
+ type=str,
+ default=None,
+)
+@click.option(
+ "--dataset",
+ help="The dataset to use.",
+ type=click.Choice(["lite", "full", "verified"]),
+ default="lite",
+)
+@click.option("--length", help="The number of examples to process.", type=int, default=10)
+@click.option(
+ "--instance-id",
+ help="The instance ID of the example to process.",
+ type=str,
+ default=None,
+)
+@click.option("--local", help="Run the evaluation locally.", is_flag=True, default=False)
+@click.option("--push-metrics", help="Push metrics to the database.", is_flag=True, default=False)
+@click.option("--repo", help="The repo to use.", type=str, default=None)
+@click.option("--model", help="The model to use.", type=str, default="claude-3-7-sonnet-latest")
+@click.option(
+ "--instance-ids",
+ help="The instance IDs of the examples to process. Example: --instance-ids ,,...",
+ type=list_of_strings,
+ default="",
+)
+def main(
+ use_existing_preds: Optional[str],
+ dataset: str,
+ length: int,
+ instance_id: Optional[str],
+ local: bool,
+ repo: Optional[str],
+ model: str,
+ push_metrics: bool,
+ instance_ids: list[str],
+) -> None:
+ """Command-line interface for running evaluations."""
+ print(f"Repo: {repo}")
+ result = run_eval(
+ use_existing_preds=use_existing_preds,
+ dataset_enum=DATASET_DICT[dataset],
+ length=length,
+ instance_id=instance_id,
+ local=local,
+ repo=repo,
+ model=model,
+ instance_ids=instance_ids,
+ )
+
+ generate_report(*result)
+
+ evaluation_result_file = Path(f"results.{result[3]}.json")
+
+ if push_metrics:
+ if not evaluation_result_file.exists() and use_existing_preds is None:
+ print("Evaluation was not run - no metrics were pushed")
+ return
+
+ try:
+ from swebench_agent_run.metrics import (
+ write_report_to_db, # delay import because of extras
+ )
+
+ write_report_to_db(str(evaluation_result_file.resolve()))
+ except Exception:
+ print("Error writing report to db")
+ traceback.print_exc()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/codegen-examples/examples/swebench_agent_run/local_run.ipynb b/codegen-examples/examples/swebench_agent_run/local_run.ipynb
index 54d845c98..f2f73c922 100644
--- a/codegen-examples/examples/swebench_agent_run/local_run.ipynb
+++ b/codegen-examples/examples/swebench_agent_run/local_run.ipynb
@@ -32,7 +32,7 @@
"metadata": {},
"outputs": [],
"source": [
- "await run_eval(use_existing_preds=None, dataset=\"lite\", length=20, repo=\"django/django\", num_workers=10, model=\"claude-3-7-sonnet-latest\")"
+ "await run_eval(use_existing_preds=None, dataset=\"lite\", length=5, repo=\"django/django\", num_workers=10, model=\"claude-3-7-sonnet-latest\")"
]
},
{
diff --git a/codegen-examples/examples/swebench_agent_run/pyproject.toml b/codegen-examples/examples/swebench_agent_run/pyproject.toml
index fc612d4b1..640e252b5 100644
--- a/codegen-examples/examples/swebench_agent_run/pyproject.toml
+++ b/codegen-examples/examples/swebench_agent_run/pyproject.toml
@@ -1,10 +1,45 @@
[project]
name = "swebench-agent-run"
version = "0.1.0"
-description = "Add your description here"
+description = "SWE-bench agent runner for evaluating model fixes"
readme = "README.md"
requires-python = ">=3.12, <3.14"
-dependencies = ["modal>=0.73.25"]
+dependencies = [
+ "modal>=0.73.25",
+ "tqdm>=4.66.0",
+ "click>=8.1.0",
+ "codegen",
+ "swebench>=3.0.15",
+ "tenacity>=9.0.0",
+]
-[tool.setuptools]
-py-modules = ["entry_point", "run_eval"]
+[project.optional-dependencies]
+metrics = ["psycopg2-binary"]
+dev = ["ruff", "mypy"]
+all = ["swebench-agent-run[metrics,dev]"]
+
+[project.scripts]
+swe-agent = "agent_cli:main"
+swe-eval = "eval_cli:main"
+modal-deploy = "modal_harness:deploy"
+
+[tool.ruff]
+line-length = 100
+target-version = "py312"
+
+
+[tool.mypy]
+python_version = "3.12"
+strict = true
+warn_return_any = true
+warn_unused_configs = true
+
+[build-system]
+requires = ["hatchling"]
+build-backend = "hatchling.build"
+
+[tool.hatch.metadata]
+allow-direct-references = true
+
+[tool.uv.sources]
+codegen = { path = "../../../" }
diff --git a/codegen-examples/examples/swebench_agent_run/run_eval.py b/codegen-examples/examples/swebench_agent_run/run_eval.py
deleted file mode 100644
index 98d300855..000000000
--- a/codegen-examples/examples/swebench_agent_run/run_eval.py
+++ /dev/null
@@ -1,385 +0,0 @@
-import asyncio
-import json
-import traceback
-from pathlib import Path
-import uuid
-import modal
-import click
-import time
-from codegen.extensions.swebench.enums import SWEBenchDataset, SWEBenchLiteSubset
-from constants import DATASET_DICT
-from codegen.extensions.swebench.harness import run_agent_on_entry
-from codegen.extensions.swebench.utils import SweBenchExample, get_swe_bench_examples
-from codegen.extensions.swebench.report import generate_report
-from codegen.sdk.core.codebase import Codebase
-
-PREDS_DNAME = Path(__file__).parent / "predictions"
-LOG_DIR = Path(__file__).parent / "logs"
-
-run_agent_modal = modal.Function.from_name(app_name="swebench-agent-run", name="run_agent_modal")
-
-
-async def process_batch_modal(examples: list[SweBenchExample], run_id: str, model: str, num_workers=5, min_workers=1, max_retries=3):
- """Process a batch of examples concurrently using a queue system with incremental worker scaling.
-
- Args:
- examples: List of SweBenchExample objects to process
- num_workers: Initial number of examples to process concurrently
- min_workers: Minimum number of concurrent workers to maintain
- max_retries: Maximum number of retries for failed requests
- """
- results = {}
- queue = asyncio.Queue()
-
- # Shared state for worker management
- state = {
- "active_workers": num_workers,
- "success_streak": 0,
- "last_scaling_time": time.time(),
- "scaling_cooldown": 0, # seconds between scaling operations
- "worker_tasks": [],
- "running": True,
- }
-
- # Use a lock to protect shared state during adjustments
- state_lock = asyncio.Lock()
-
- # Initialize the queue with (example, attempt) tuples
- for example in examples:
- await queue.put((example, 0)) # 0 represents first attempt
-
- async def scale_down_worker(task_to_cancel=None):
- """Remove a single worker when rate limiting is detected"""
- async with state_lock:
- # Only scale if cooldown period has passed and we're above min_workers
- current_time = time.time()
- if current_time - state["last_scaling_time"] < state["scaling_cooldown"] or state["active_workers"] <= min_workers:
- return False
-
- # Reset success streak when scaling down
- state["success_streak"] = 0
- state["last_scaling_time"] = current_time
-
- # If a specific task was provided, cancel it
- if task_to_cancel and task_to_cancel in state["worker_tasks"]:
- print(f"Rate limiting detected! Removing 1 worker, going from {state['active_workers']} to {state['active_workers'] - 1}")
- state["worker_tasks"].remove(task_to_cancel)
- task_to_cancel.cancel()
- state["active_workers"] -= 1
- return True
-
- # Otherwise, cancel the most recently added worker
- elif state["worker_tasks"]:
- print(f"Rate limiting detected! Removing 1 worker, going from {state['active_workers']} to {state['active_workers'] - 1}")
- task = state["worker_tasks"].pop()
- task.cancel()
- state["active_workers"] -= 1
- return True
-
- return False
-
- async def scale_up_worker():
- """Add a single worker when operations have been consistently successful"""
- async with state_lock:
- # Only scale if cooldown period has passed and we're below num_workers
- current_time = time.time()
- if current_time - state["last_scaling_time"] < state["scaling_cooldown"] or state["active_workers"] >= num_workers:
- return False
-
- # Add a worker after a streak of successful operations
- if state["success_streak"] >= 5:
- print(f"Operations succeeding! Adding 1 worker, going from {state['active_workers']} to {state['active_workers'] + 1}")
-
- # Create new worker
- if state["running"]:
- new_task = asyncio.create_task(worker())
- state["worker_tasks"].append(new_task)
- state["active_workers"] += 1
- state["success_streak"] = 0
- state["last_scaling_time"] = current_time
- return True
-
- return False
-
- async def is_rate_limit_error(error):
- """Determine if an error is due to rate limiting"""
- # Check for common rate limit error patterns
- if isinstance(error, modal.exception.Error):
- error_msg = str(error).lower()
- rate_limit_indicators = ["rate limit", "too many requests", "429", "throttle", "quota exceeded", "capacity", "limit exceeded"]
- return any(indicator in error_msg for indicator in rate_limit_indicators)
- return False
-
- async def process_example(example, attempt, current_task):
- try:
- result = await run_agent_modal.remote.aio(example, run_id=run_id, model=model)
-
- if result is None:
- print(f"Warning: Null result for {example.instance_id}")
- return {"status": "error", "instance_id": example.instance_id, "error_info": {"error_type": "NullResult", "error_message": "Process returned None"}}
-
- # Increment success streak and potentially scale up
- async with state_lock:
- state["success_streak"] += 1
-
- if state["success_streak"] % 5 == 0: # Check after every 5 successes
- await scale_up_worker()
-
- return result
-
- except Exception as e:
- error_type = type(e).__name__
- error_info = {
- "error_type": error_type,
- "error_message": str(e),
- "traceback": traceback.format_exception(type(e), e, e.__traceback__),
- }
-
- if isinstance(e, modal.exception.Error):
- error_info["modal_error_code"] = getattr(e, "code", None)
- error_info["modal_error_details"] = getattr(e, "details", None)
-
- print(f"Error processing {example.instance_id} (attempt {attempt + 1}):")
- print(f"Type: {error_type}")
- print(f"Message: {str(e)}")
-
- # Check if this is a rate limit error
- if await is_rate_limit_error(e):
- print(f"Rate limit detected on task for {example.instance_id}")
-
- # Scale down by removing this specific worker
- scaled_down = await scale_down_worker(current_task)
-
- # If we're removing this worker, we need to requeue the task for another worker
- if scaled_down:
- # Requeue this example with the same attempt count (not incrementing)
- await queue.put((example, attempt))
- return None
-
- # Otherwise add a small delay before retrying
- await asyncio.sleep(2 * (attempt + 1)) # Exponential backoff
-
- if attempt < max_retries:
- await queue.put((example, attempt + 1))
- return None
-
- return {"status": "error", "instance_id": example.instance_id, "error_info": error_info}
-
- async def worker():
- # Store this task reference to allow targeted cancellation
- current_task = asyncio.current_task()
-
- while state["running"]:
- try:
- # Use a timeout to allow worker to check if it should exit
- try:
- example, attempt = await asyncio.wait_for(queue.get(), timeout=1.0)
- except asyncio.TimeoutError:
- continue
-
- if example.instance_id in results:
- queue.task_done()
- continue
- print(f"Processing example {example.instance_id}")
- process_result = await process_example(example, attempt, current_task)
-
- # If we're still processing this task (not requeued due to rate limiting)
- if process_result is not None:
- results[example.instance_id] = {"instance_id": example.instance_id, **process_result}
- print(f"Processed example {example.instance_id}")
- queue.task_done()
-
- # If None is returned, the task was requeued due to rate limiting
- # and this worker is being shut down, so exit the loop
- else:
- print(f"Task for {example.instance_id} has been requeued")
- queue.task_done()
- if current_task not in state["worker_tasks"]:
- break
-
- except asyncio.CancelledError:
- # Handle graceful cancellation
- print("Worker task cancelled")
- break
- except Exception as e:
- print(f"Worker error: {str(e)}")
- traceback.print_exc()
- queue.task_done()
-
- # Start initial workers
- state["worker_tasks"] = [asyncio.create_task(worker()) for _ in range(num_workers)]
-
- # Wait for queue to be fully processed
- await queue.join()
-
- # Mark as not running and cancel remaining workers
- state["running"] = False
- for w in state["worker_tasks"]:
- w.cancel()
-
- # Wait for all workers to be cancelled
- await asyncio.gather(*state["worker_tasks"], return_exceptions=True)
-
- # Return results in the same order as input examples
- return [results.get(example.instance_id, {"instance_id": example.instance_id, "status": "missing"}) for example in examples]
-
-
-def process_batch_local(examples: list[SweBenchExample], model: str, num_workers=5, codebases: dict[str, Codebase] = {}, run_id: str | None = None):
- """Process a batch of examples synchronously.
-
- Args:
- examples: List of SweBenchExample objects to process
- num_workers: Number of examples to process in each batch.
- Default is 10 to avoid overwhelming the system.
- """
- results = []
-
- # Process examples in batches
- for i in range(0, len(examples), num_workers):
- batch = examples[i : i + num_workers]
- print(f"Processing batch {i // num_workers + 1}/{len(examples) // num_workers + 1} (examples {i + 1}-{min(i + num_workers, len(examples))})")
-
- # Process each example in the batch
- for example in batch:
- try:
- # Run the agent locally instead of using modal
- if codebases and example.instance_id in codebases:
- result = run_agent_on_entry(example, model=model, codebase=codebases[example.instance_id], run_id=run_id)
- else:
- result = run_agent_on_entry(example, model=model, run_id=run_id)
- results.append(result)
-
- except Exception as e:
- error_type = type(e).__name__
- error_info = {
- "error_type": error_type,
- "error_message": str(e),
- "traceback": traceback.format_exc(),
- }
-
- print(f"Error processing {example.instance_id}:")
- print(f"Type: {error_type}")
- print(f"Message: {str(e)}")
- print("Traceback:")
- print(error_info["traceback"])
-
- results.append({"instance_id": example.instance_id, "status": "error", "error_info": error_info})
-
- return results
-
-
-async def run_eval(
- dataset: str,
- use_existing_preds: str | None = None,
- length: int | None = None,
- instance_id: str | None = None,
- local: bool = False,
- codebases: dict[str, Codebase] = {},
- repo: str | None = None,
- num_workers: int = 2,
- model: str = "claude-3-7-sonnet-latest",
- instance_ids: list[str] = [],
-):
- run_id = use_existing_preds or str(uuid.uuid4())
- print(f"Run ID: {run_id}")
- predictions_dir = PREDS_DNAME / f"results_{run_id}"
-
- dataset_enum = DATASET_DICT[dataset]
- examples = get_swe_bench_examples(dataset=dataset_enum, length=length, instance_id=instance_id, repo=repo, instance_ids=instance_ids)
-
- try:
- if use_existing_preds is None:
- print(f"Repo: {repo}")
- print(f"Examples:\n{'\n'.join([f'{e.instance_id} - {e.repo} - {e.base_commit}' for e in examples])}")
- print(f"Processing {len(examples)} examples...")
- # Create output directory if it doesn't exist
- predictions_dir.mkdir(exist_ok=True, parents=True)
-
- # Create a timestamp for this run
- timestamp = time.strftime("%Y-%m-%d %H:%M %Z", time.localtime(time.time()))
-
- # Process all examples in parallel batches
- if local:
- results = process_batch_local(examples, model=model, codebases=codebases, run_id=run_id)
- else:
- results = await process_batch_modal(examples, model=model, run_id=run_id, num_workers=num_workers)
-
- # Save individual results
- for result in results:
- if result and "instance_id" in result:
- instance_id = result["instance_id"]
- output_file = predictions_dir / f"{instance_id}.json"
- output_file.parent.mkdir(exist_ok=True, parents=True)
- with open(output_file, "w") as f:
- json.dump(result, f, indent=4)
-
- # Save summary file
- summary_file = predictions_dir / f"summary_{timestamp}.json"
- summary = {
- "timestamp": timestamp,
- "total_examples": len(examples),
- "successful": len([r for r in results if r and "status" not in r]),
- "failed": len([r for r in results if r and "status" in r and r["status"] == "error"]),
- "error_types": {},
- "results": results,
- }
-
- # Collect error statistics
- for result in results:
- if result and "status" in result and result["status"] == "error":
- error_type = result.get("error_info", {}).get("error_type", "Unknown")
- summary["error_types"][error_type] = summary["error_types"].get(error_type, 0) + 1
-
- with open(summary_file, "w") as f:
- json.dump(summary, f, indent=4)
-
- print("\nProcessing complete!")
- print(f"Results saved to: {predictions_dir}")
- print(f"Summary saved to: {summary_file}")
- print(f"Successful: {summary['successful']}/{summary['total_examples']}")
- print(f"Failed: {summary['failed']}/{summary['total_examples']}")
- if summary["error_types"]:
- print("\nError type distribution:")
- for error_type, count in summary["error_types"].items():
- print(f" {error_type}: {count}")
-
- if isinstance(dataset_enum, SWEBenchLiteSubset):
- dataset_enum = SWEBenchDataset.LITE
- # Generate Report on Modal
- generate_report(predictions_dir, LOG_DIR, dataset_enum, run_id)
- except Exception:
- print("Fatal error in run_eval:")
- traceback.print_exc()
- raise
-
-
-def list_of_strings(value: str) -> list[str]:
- if value == "":
- return []
- return value.split(",")
-
-
-@click.command()
-@click.option("--dataset", help="The dataset to use.", type=click.Choice(["lite", "full", "verified", "lite_small", "lite_medium", "lite_large"]), default="lite")
-@click.option("--use-existing-preds", help="The run ID of the existing predictions to use.", type=str, default=None)
-@click.option("--length", help="The number of examples to process.", type=int, default=None)
-@click.option("--instance-id", help="The instance ID of the example to process.", type=str, default=None)
-@click.option("--local", help="Run the evaluation locally.", is_flag=True, default=False)
-@click.option("--repo", help="The repo to use.", type=str, default=None)
-@click.option(
- "--num-workers", help="The number of workers to use. This is the number of examples that will be processed concurrently. A large number may lead to rate limiting issues.", type=int, default=5
-)
-@click.option("--model", help="The model to use.", type=str, default="claude-3-7-sonnet-latest")
-@click.option("--instance-ids", help="The instance IDs of the examples to process. Example: --instance-ids ,,...", type=list_of_strings, default="")
-def run_eval_command(dataset, use_existing_preds, length, instance_id, local, repo, num_workers, model, instance_ids):
- print(f"Repo: {repo}")
- print(f"Model: {model}")
- asyncio.run(
- run_eval(
- dataset=dataset, use_existing_preds=use_existing_preds, length=length, instance_id=instance_id, local=local, repo=repo, num_workers=num_workers, model=model, instance_ids=instance_ids
- )
- )
-
-
-if __name__ == "__main__":
- run_eval_command()
diff --git a/codegen-examples/examples/swebench_agent_run/swebench_agent_run/__init__.py b/codegen-examples/examples/swebench_agent_run/swebench_agent_run/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/codegen-examples/examples/swebench_agent_run/constants.py b/codegen-examples/examples/swebench_agent_run/swebench_agent_run/constants.py
similarity index 100%
rename from codegen-examples/examples/swebench_agent_run/constants.py
rename to codegen-examples/examples/swebench_agent_run/swebench_agent_run/constants.py
diff --git a/codegen-examples/examples/swebench_agent_run/swebench_agent_run/metrics.py b/codegen-examples/examples/swebench_agent_run/swebench_agent_run/metrics.py
new file mode 100644
index 000000000..4052604d7
--- /dev/null
+++ b/codegen-examples/examples/swebench_agent_run/swebench_agent_run/metrics.py
@@ -0,0 +1,69 @@
+import json
+import os
+from importlib.metadata import version
+from pathlib import Path
+
+import psycopg2
+from dotenv import load_dotenv
+
+
+def write_report_to_db(report_file: str):
+ path = Path(__file__).parent.parent / ".env.db"
+ if not path.exists():
+ raise FileNotFoundError(f"DB credentials not found: {path}")
+ load_dotenv(str(path.resolve()))
+
+ postgres_host = os.getenv("POSTGRESQL_HOST")
+ postgres_database = os.getenv("POSTGRESQL_DATABASE")
+ postgres_user = os.getenv("POSTGRESQL_USER")
+ postgres_password = os.getenv("POSTGRESQL_PASSWORD")
+ postgres_port = os.getenv("POSTGRESQL_PORT")
+
+ try:
+ codegen_version = version("codegen")
+ except Exception:
+ codegen_version = "dev"
+
+ with open(report_file) as f:
+ report = json.load(f)
+
+ # Establish connection
+
+ conn = psycopg2.connect(
+ host=postgres_host,
+ database=postgres_database,
+ user=postgres_user,
+ password=postgres_password,
+ port=postgres_port,
+ )
+
+ # Create a cursor
+ cur = conn.cursor()
+
+ try:
+ # Single row insert
+ cur.execute(
+ "INSERT INTO swebench_output (codegen_version, submitted, completed_instances, resolved_instances, unresolved_instances, empty_patches, error_instances) VALUES (%s, %s, %s, %s, %s, %s, %s)",
+ (
+ codegen_version,
+ report["submitted_instances"],
+ report["completed_instances"],
+ report["resolved_instances"],
+ report["unresolved_instances"],
+ report["empty_patch_instances"],
+ report["error_instances"],
+ ),
+ )
+
+ # Commit the transaction
+ conn.commit()
+
+ except Exception as e:
+ # Rollback in case of error
+ conn.rollback()
+ print(f"Error: {e}")
+
+ finally:
+ # Close cursor and connection
+ cur.close()
+ conn.close()
diff --git a/codegen-examples/examples/swebench_agent_run/swebench_agent_run/modal_harness/__init__.py b/codegen-examples/examples/swebench_agent_run/swebench_agent_run/modal_harness/__init__.py
new file mode 100644
index 000000000..e26435103
--- /dev/null
+++ b/codegen-examples/examples/swebench_agent_run/swebench_agent_run/modal_harness/__init__.py
@@ -0,0 +1,3 @@
+from .entry_point import patched_swebench_eval
+
+__all__ = ["patched_swebench_eval"]
diff --git a/codegen-examples/examples/swebench_agent_run/swebench_agent_run/modal_harness/entry_point.py b/codegen-examples/examples/swebench_agent_run/swebench_agent_run/modal_harness/entry_point.py
new file mode 100644
index 000000000..d044af28f
--- /dev/null
+++ b/codegen-examples/examples/swebench_agent_run/swebench_agent_run/modal_harness/entry_point.py
@@ -0,0 +1,334 @@
+"""Largely copied from swebench/harness/modal_eval/run_evaluation_modal.py
+
+Points of difference:
+ - We added CGModalSandboxRuntime class that is used to populate the sandbox with the snapshot.
+ - We are adding custom post-processing of the TestOutput in run_instances_modal
+"""
+
+import json
+import time
+import traceback
+from contextlib import nullcontext
+from typing import TYPE_CHECKING
+from unittest.mock import patch
+
+import modal as modal_lib
+import tenacity
+from swebench.harness.constants import (
+ APPLY_PATCH_FAIL,
+ APPLY_PATCH_PASS,
+)
+from swebench.harness.docker_build import setup_logger
+from swebench.harness.grading import get_eval_report
+from swebench.harness.modal_eval.run_evaluation_modal import (
+ LOCAL_SANDBOX_ENTRYPOINT_PATH,
+ REMOTE_SANDBOX_ENTRYPOINT_PATH,
+ ModalSandboxRuntime,
+ TestOutput,
+ get_log_dir,
+ swebench_image,
+)
+from swebench.harness.run_evaluation import main
+from swebench.harness.test_spec.test_spec import TestSpec
+from swebench.harness.utils import EvaluationError
+
+if TYPE_CHECKING:
+ from codegen.extensions.swebench.utils import SweBenchExample
+
+image = (
+ modal_lib.Image.debian_slim(python_version="3.13")
+ .apt_install(["git", "ripgrep"])
+ .add_local_dir(
+ "../../../",
+ "/root/codegen",
+ ignore=[
+ "__pycache__",
+ "**/__pycache__",
+ ".venv",
+ "**/.venv",
+ "tests",
+ "**/tests",
+ "codegen-on-oss/",
+ "codegen-examples/",
+ "build/",
+ ".vscode/",
+ ".codegen/",
+ ".github/",
+ ".architecture/",
+ "docs/",
+ "*cache/",
+ ],
+ copy=True,
+ )
+ .add_local_dir(
+ ".",
+ "/root/swebench_agent_run",
+ ignore=[
+ "__pycache__",
+ "**/__pycache__",
+ ".venv",
+ "**/.venv",
+ ".env*",
+ ],
+ copy=True,
+ )
+ .run_commands(
+ "pip install -e /root/codegen",
+ "rm -r /root/codegen/.git",
+ "pip install -e /root/swebench_agent_run",
+ )
+)
+
+app = modal_lib.App(
+ name="swebench-agent-run", image=image, secrets=[modal_lib.Secret.from_dotenv()]
+)
+
+
+class ShouldRetry(Exception):
+ pass
+
+
+@app.function(timeout=43200, max_containers=10)
+async def run_agent_modal(entry: "SweBenchExample", run_id: str, model: str):
+ from codegen.extensions.swebench.harness import run_agent_on_entry
+
+ """Modal function to process a single example from the SWE-bench dataset."""
+ for attempt in tenacity.Retrying(
+ wait=tenacity.wait_exponential_jitter(max=600),
+ retry=tenacity.retry_if_exception_type(ShouldRetry),
+ ):
+ with attempt:
+ try:
+ return run_agent_on_entry(entry, run_id=run_id, model=model)
+ except Exception as e:
+ if any(
+ msg in str(e).lower()
+ for msg in (
+ "rate limit",
+ "too many requests",
+ "429",
+ "throttle",
+ "quota exceeded",
+ "capacity",
+ "limit exceeded",
+ )
+ ):
+ raise ShouldRetry() from e
+ else:
+ raise e
+
+
+@app.function(
+ image=swebench_image.add_local_file(
+ LOCAL_SANDBOX_ENTRYPOINT_PATH, REMOTE_SANDBOX_ENTRYPOINT_PATH, copy=True
+ ).add_local_python_source("eval_cli", "swebench_agent_run", copy=True),
+ timeout=120 * 60, # Much larger than default timeout to account for image build time
+)
+def run_instance_modal(
+ test_spec: TestSpec,
+ pred: dict,
+ run_id: str,
+ timeout: int | None = None,
+) -> TestOutput:
+ """Run a single instance with the given prediction.
+
+ Args:
+ test_spec (TestSpec): TestSpec instance
+ pred (dict): Prediction w/ model_name_or_path, model_patch, instance_id
+ run_id (str): Run ID
+ timeout (int): Timeout for running tests
+ """
+ instance_id = test_spec.instance_id
+ log_dir = get_log_dir(pred, run_id, instance_id)
+ log_dir.mkdir(parents=True, exist_ok=True)
+
+ log_file = log_dir / "run_instance.log"
+
+ logger = setup_logger(instance_id, log_file, add_stdout=True)
+
+ try:
+ runner = ModalSandboxRuntime(test_spec, timeout)
+ except Exception as e:
+ print(f"Error creating sandbox: {e}")
+ raise EvaluationError(
+ instance_id,
+ f"Error creating sandbox: {e}",
+ logger,
+ ) from e
+
+ patch_diff = pred.get("model_patch", "")
+
+ try:
+ patch_file = "/tmp/patch.diff"
+ runner.write_file(patch_file, patch_diff)
+
+ apply_patch_output, returncode = runner.exec(
+ "cd /testbed && git apply -v /tmp/patch.diff",
+ )
+
+ if returncode != 0:
+ logger.info("Failed to apply patch to container, trying again...")
+
+ apply_patch_output, returncode = runner.exec(
+ "cd /testbed && patch --batch --fuzz=5 -p1 -i /tmp/patch.diff",
+ )
+
+ if returncode != 0:
+ logger.info(f"{APPLY_PATCH_FAIL}:\n{apply_patch_output}")
+ raise EvaluationError(
+ instance_id,
+ f"{APPLY_PATCH_FAIL}:\n{apply_patch_output}",
+ logger,
+ )
+ else:
+ logger.info(f"{APPLY_PATCH_PASS}:\n{apply_patch_output}")
+ else:
+ logger.info(f"{APPLY_PATCH_PASS}:\n{apply_patch_output}")
+
+ # Get git diff before running eval script
+ git_diff_output_before, returncode = runner.exec(
+ "cd /testbed && git diff",
+ )
+ logger.info(f"Git diff before:\n{git_diff_output_before}")
+
+ eval_file = "/root/eval.sh"
+ eval_script = test_spec.eval_script
+ # django hack
+ eval_script = eval_script.replace("locale-gen", "locale-gen en_US.UTF-8")
+ runner.write_file(eval_file, eval_script)
+
+ start_time = time.time()
+
+ run_command = "cd /testbed"
+ # pylint hack
+ if "pylint" in test_spec.instance_id:
+ run_command += " && PYTHONPATH="
+ # increase recursion limit for testing
+ run_command += " && python3 -c 'import sys; sys.setrecursionlimit(10000)'"
+ # run eval script
+ run_command += " && /bin/bash /root/eval.sh"
+ test_output, returncode = runner.exec(run_command)
+
+ total_runtime = time.time() - start_time
+
+ test_output_path = log_dir / "test_output.txt"
+ logger.info(f"Test runtime: {total_runtime:_.2f} seconds")
+ with open(test_output_path, "w") as f:
+ f.write(test_output)
+ logger.info(f"Test output for {instance_id} written to {test_output_path}")
+ print(f"Test output for {instance_id} written to {test_output_path}")
+
+ # Get git diff after running eval script
+ git_diff_output_after, returncode = runner.exec("cd /testbed && git diff")
+
+ # Check if git diff changed after running eval script
+ logger.info(f"Git diff after:\n{git_diff_output_after}")
+ if git_diff_output_after != git_diff_output_before:
+ logger.info("Git diff changed after running eval script")
+
+ # Get report from test output
+ logger.info(f"Grading answer for {instance_id}...")
+ report = get_eval_report(
+ test_spec=test_spec,
+ prediction=pred,
+ test_log_path=test_output_path,
+ include_tests_status=True,
+ )
+ logger.info(
+ f"report: {report}\nResult for {instance_id}: resolved: {report[instance_id]['resolved']}"
+ )
+
+ return TestOutput(
+ instance_id=instance_id,
+ test_output=test_output,
+ report_json_str=json.dumps(report, indent=4),
+ run_instance_log=log_file.read_text(),
+ patch_diff=patch_diff,
+ log_dir=log_dir,
+ errored=False,
+ )
+ except modal_lib.exception.SandboxTimeoutError as e:
+ raise EvaluationError(
+ instance_id,
+ f"Test timed out after {timeout} seconds.",
+ logger,
+ ) from e
+ except EvaluationError:
+ error_msg = traceback.format_exc()
+ logger.info(error_msg)
+ return TestOutput(
+ instance_id=instance_id,
+ test_output="",
+ report_json_str="",
+ run_instance_log=log_file.read_text(),
+ patch_diff=patch_diff,
+ log_dir=log_dir,
+ errored=True,
+ )
+ except Exception as e:
+ error_msg = f"Error in evaluating model for {instance_id}: {e}\n{traceback.format_exc()}\nCheck ({logger.log_file}) for more information."
+ logger.exception(error_msg)
+ return TestOutput(
+ instance_id=instance_id,
+ test_output="",
+ report_json_str="",
+ run_instance_log=log_file.read_text(),
+ patch_diff=patch_diff,
+ log_dir=log_dir,
+ errored=True,
+ )
+
+
+def patched_swebench_eval( # Defaults from swebench harness
+ predictions_path, # Required argument
+ run_id, # Required argument
+ dataset_name="princeton-nlp/SWE-bench_Lite",
+ split="test",
+ instance_ids=None,
+ max_workers=4,
+ open_file_limit=4096,
+ timeout=1800,
+ force_rebuild=False,
+ cache_level="env",
+ clean=False,
+ namespace="swebench",
+ instance_image_tag="latest",
+ rewrite_reports=False,
+ report_dir=".",
+ modal=False,
+ **kwargs,
+):
+ with (
+ patch(
+ "swebench.harness.modal_eval.run_evaluation_modal.run_instance_modal",
+ modal_lib.Function.from_name(
+ app_name="swebench-agent-run",
+ name="run_instance_modal",
+ ),
+ ),
+ patch(
+ "swebench.harness.modal_eval.run_evaluation_modal.app",
+ app,
+ ),
+ ):
+ # Don't want swebench to run app.run() again
+ app.run = nullcontext
+ return main(
+ dataset_name=dataset_name,
+ split=split,
+ instance_ids=instance_ids,
+ predictions_path=predictions_path,
+ max_workers=max_workers,
+ force_rebuild=force_rebuild,
+ cache_level=cache_level,
+ clean=clean,
+ open_file_limit=open_file_limit,
+ run_id=run_id,
+ timeout=timeout,
+ namespace=namespace,
+ rewrite_reports=rewrite_reports,
+ modal=modal,
+ instance_image_tag=instance_image_tag,
+ report_dir=report_dir,
+ **kwargs,
+ )
diff --git a/codegen-examples/examples/swebench_agent_run/swebench_agent_run/modal_harness/sandbox.py b/codegen-examples/examples/swebench_agent_run/swebench_agent_run/modal_harness/sandbox.py
new file mode 100644
index 000000000..25664d1f5
--- /dev/null
+++ b/codegen-examples/examples/swebench_agent_run/swebench_agent_run/modal_harness/sandbox.py
@@ -0,0 +1,97 @@
+import io
+import json
+from collections import defaultdict
+
+import modal as modal_lib
+from swebench.harness.constants import (
+ SWEbenchInstance,
+)
+from swebench.harness.modal_eval.run_evaluation_modal import (
+ ModalSandboxRuntime,
+)
+from swebench.harness.test_spec.test_spec import make_test_spec
+
+
+class SnapshotManager:
+ def get_snapshot_uid(self, example: SWEbenchInstance) -> str:
+ msg = "Not implemented"
+ raise NotImplementedError(msg)
+
+ def save_snapshot_uid(self, example: SWEbenchInstance, snapshot_uid: str) -> None:
+ msg = "Not implemented"
+ raise NotImplementedError(msg)
+
+
+class VolumeSnapshotManager(SnapshotManager):
+ def __init__(self, volume_name: str = "swebench-agent-snapshot-volume"):
+ self.snapshot_volume = modal_lib.Volume.from_name(volume_name, create_if_missing=True)
+ self.snapshot_meta_file_path: str = "/root/snapshot_meta.json"
+
+ def get_snapshot_uid(self, example: SWEbenchInstance) -> str:
+ snapshot_meta = self.read_snapshot_meta()
+ return snapshot_meta[example.repo][example.environment_setup_commit]
+
+ def save_snapshot_uid(self, example: SWEbenchInstance, snapshot_uid: str) -> None:
+ snapshot_meta = self.read_snapshot_meta()
+ snapshot_meta[example.repo][example.environment_setup_commit] = snapshot_uid
+ with self.snapshot_volume.batch_upload() as upload:
+ upload.put_file(
+ io.BytesIO(json.dumps(snapshot_meta).encode("utf-8")),
+ self.snapshot_meta_file_path,
+ )
+ self.snapshot_volume.commit()
+
+ def read_snapshot_meta(self) -> dict[str, dict[str, str]]:
+ bytes_io = io.BytesIO()
+ try:
+ self.snapshot_volume.read_file_into_fileobj(self.snapshot_meta_file_path, bytes_io)
+ snapshot_meta = json.loads(bytes_io.getvalue().decode("utf-8"))
+ except FileNotFoundError:
+ snapshot_meta = {}
+ return defaultdict(lambda: defaultdict(lambda: None), snapshot_meta)
+
+
+class ModalDictSnapshotManager(SnapshotManager):
+ def __init__(self, name: str = "swebench-agent-snapshot-dict"):
+ self.snapshot_dict = modal_lib.Dict.from_name(name, create_if_missing=True)
+
+ def get_snapshot_uid(self, example: SWEbenchInstance) -> str | None:
+ try:
+ return self.snapshot_dict[(example.repo, example.environment_setup_commit)]
+ except KeyError:
+ return None
+
+ def save_snapshot_uid(self, example: SWEbenchInstance, snapshot_uid: str) -> None:
+ self.snapshot_dict[(example.repo, example.environment_setup_commit)] = snapshot_uid
+
+
+class CGModalSandboxRuntime(ModalSandboxRuntime):
+ def __init__(
+ self,
+ example: SWEbenchInstance,
+ timeout: int | None = None,
+ verbose: bool = True,
+ ):
+ self.example = example
+ self.snapshot_manager = ModalDictSnapshotManager()
+ self.test_spec = make_test_spec(example)
+ self.sandbox = self._get_sandbox(timeout)
+ self.verbose = verbose
+ self._stream_tasks = []
+
+ # Hack for pylint
+ self.write_file("/sys/fs/cgroup/cpu/cpu.shares", "2048")
+
+ @property
+ def image(self) -> modal_lib.Image:
+ return ModalSandboxRuntime.get_instance_image(self.test_spec)
+
+ def _get_sandbox(self, timeout: int | None = None):
+ """Populate sandbox ourselves"""
+ uid = self.snapshot_manager.get_snapshot_uid(self.example)
+ if uid is None:
+ sandbox = super()._get_sandbox(timeout)
+ snapshot = sandbox._experimental_snapshot()
+ self.snapshot_manager.save_snapshot_uid(self.example, snapshot.object_id)
+ else:
+ return modal_lib.Sandbox._experimental_from_snapshot(uid)
diff --git a/src/codegen/extensions/swebench/report.py b/codegen-examples/examples/swebench_agent_run/swebench_agent_run/report.py
similarity index 78%
rename from src/codegen/extensions/swebench/report.py
rename to codegen-examples/examples/swebench_agent_run/swebench_agent_run/report.py
index f8100e36d..580bc805a 100755
--- a/src/codegen/extensions/swebench/report.py
+++ b/codegen-examples/examples/swebench_agent_run/swebench_agent_run/report.py
@@ -1,31 +1,15 @@
#!/usr/bin/env python
import json
-import subprocess
from collections import defaultdict
from pathlib import Path
from codegen.extensions.swebench.enums import SWEBenchDataset
from codegen.extensions.swebench.tests import remove_patches_to_tests
-NUM_EVAL_PROCS = 5
-
-
-def run_evals(predictions_jsonl, logs_dir: Path, dataset: SWEBenchDataset, run_id: str):
- """Run the evaluations on the predictions on modal."""
- run_evals_cmd = f"""
-python -m swebench.harness.run_evaluation
- --predictions_path {predictions_jsonl}
- --run_id {run_id}
- --dataset_name {dataset.value}
- --cache_level instance
- --report_dir {logs_dir}
- --modal true
-"""
- run_evals_cmd = " ".join([line.strip() for line in run_evals_cmd.split() if line.strip()])
- print("Running evaluation command:", run_evals_cmd)
+from .modal_harness import patched_swebench_eval
- subprocess.run(run_evals_cmd.split(), check=True)
+NUM_EVAL_PROCS = 5
def get_report(predictions_jsonl, logs_dir: Path):
@@ -87,31 +71,32 @@ def update_pred_json(predictions, report, predictions_dir: Path):
return predictions
-def preds_to_jsonl(predictions, predictions_dir: Path):
- dname = predictions_dir
-
- predictions_jsonl = str(dname / "all_preds.jsonl")
+def preds_to_jsonl(predictions, predictions_jsonl: Path):
print(f"Creating JSONL file: {predictions_jsonl}")
# Use a default model name since it's not in the predictions
model_name = "results"
with open(predictions_jsonl, "w") as fh:
- for inst, pred in predictions.items():
+ for pred in predictions.values():
minimal_pred = {
"model_name_or_path": model_name, # Use default model name
- "model_patch": remove_patches_to_tests(pred["model_patch"]) if "model_patch" in pred else pred.get("patch", ""),
+ "model_patch": remove_patches_to_tests(
+ pred.get("result", {}).get("model_patch", "")
+ ),
"instance_id": pred["instance_id"],
}
fh.write(json.dumps(minimal_pred) + "\n")
return predictions_jsonl
-def generate_report(predictions_dir: Path, logs_dir: Path, dataset: SWEBenchDataset, run_id: str):
+def generate_report(
+ predictions_dir: Path, logs_dir: Path, dataset: SWEBenchDataset, run_id: str
+) -> str | None:
# Automatically find all JSON files in predictions/results
if not predictions_dir.exists():
print(f"Directory does not exist: {predictions_dir}")
- return 1
+ return None
predictions_jsonl = predictions_dir / "all_preds.jsonl"
existing_preds = predictions_jsonl.exists()
@@ -128,6 +113,7 @@ def generate_report(predictions_dir: Path, logs_dir: Path, dataset: SWEBenchData
except json.JSONDecodeError:
print(f"Error reading JSON from {file_path}")
continue
+
if not existing_preds:
if not predictions:
print("No valid predictions found")
@@ -135,15 +121,21 @@ def generate_report(predictions_dir: Path, logs_dir: Path, dataset: SWEBenchData
print(f"Successfully loaded {len(predictions)} predictions")
- predictions_jsonl = preds_to_jsonl(predictions, predictions_dir)
+ predictions_jsonl = preds_to_jsonl(predictions, predictions_jsonl)
# Setup log directory
log_dir = logs_dir / "results"
log_dir.mkdir(exist_ok=True, parents=True)
print(f"Using log directory: {log_dir}")
- # Run evaluations
- run_evals(predictions_jsonl, logs_dir, dataset, run_id)
+ evaluation_result_file = patched_swebench_eval(
+ str(predictions_jsonl),
+ run_id,
+ dataset_name=dataset.value,
+ cache_level="instance",
+ report_dir=logs_dir,
+ modal=True,
+ )
# Get and display report
report = get_report(predictions_jsonl, logs_dir)
@@ -151,4 +143,4 @@ def generate_report(predictions_dir: Path, logs_dir: Path, dataset: SWEBenchData
# Update prediction JSONs with results
predictions = update_pred_json(predictions, report, predictions_dir)
- return 0
+ return evaluation_result_file
diff --git a/codegen-examples/examples/swebench_agent_run/swebench_agent_run/utils.py b/codegen-examples/examples/swebench_agent_run/swebench_agent_run/utils.py
new file mode 100644
index 000000000..64ed1609b
--- /dev/null
+++ b/codegen-examples/examples/swebench_agent_run/swebench_agent_run/utils.py
@@ -0,0 +1,28 @@
+from itertools import batched
+from typing import Iterator, List, TypeVar
+
+from tqdm import tqdm
+
+T = TypeVar("T")
+
+
+def track_batches(
+ items: List[T], batch_size: int, desc: str = "Processing"
+) -> Iterator[tuple[int, List[T]]]:
+ """
+ Track batch progress with tqdm.
+ Returns tuples of (batch_number, batch_items).
+ """
+ total_items = len(items)
+ total_batches = (total_items + batch_size - 1) // batch_size
+
+ with tqdm(
+ total=total_items,
+ desc=desc,
+ unit="examples",
+ bar_format="{l_bar}{bar}| {n_fmt}/{total_fmt} examples [{elapsed}<{remaining}, {rate_fmt}]",
+ ) as pbar:
+ for batch_num, batch in enumerate(batched(items, batch_size), 1):
+ pbar.set_postfix({"batch": f"{batch_num}/{total_batches}", "batch_size": len(batch)})
+ yield batch_num, batch
+ pbar.update(len(batch))
diff --git a/codegen-examples/examples/swebench_agent_run/test.py b/codegen-examples/examples/swebench_agent_run/test.py
deleted file mode 100644
index fb6e4eb5a..000000000
--- a/codegen-examples/examples/swebench_agent_run/test.py
+++ /dev/null
@@ -1,14 +0,0 @@
-from codegen import Codebase
-import modal
-
-image = modal.Image.debian_slim(python_version="3.13").apt_install("git").pip_install("fastapi[standard]").run_commands("pip install codegen")
-
-app = modal.App(name="codegen-examples", image=image, secrets=[modal.Secret.from_dotenv()])
-
-
-@app.function()
-def run_agent(AgentClass):
- codebase = Codebase.from_repo(repo_full_name="pallets/flask")
- agent = AgentClass(codebase)
- agent.run(prompt="Tell me about the codebase and the files in it.")
- return True
diff --git a/codegen-examples/examples/swebench_agent_run/uv.lock b/codegen-examples/examples/swebench_agent_run/uv.lock
new file mode 100644
index 000000000..d383e93de
--- /dev/null
+++ b/codegen-examples/examples/swebench_agent_run/uv.lock
@@ -0,0 +1,3675 @@
+version = 1
+requires-python = ">=3.12, <3.14"
+resolution-markers = [
+ "python_full_version >= '3.12.4'",
+ "python_full_version < '3.12.4'",
+]
+
+[[package]]
+name = "aiohappyeyeballs"
+version = "2.4.8"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/de/7c/79a15272e88d2563c9d63599fa59f05778975f35b255bf8f90c8b12b4ada/aiohappyeyeballs-2.4.8.tar.gz", hash = "sha256:19728772cb12263077982d2f55453babd8bec6a052a926cd5c0c42796da8bf62", size = 22337 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/52/0e/b187e2bb3eeb2644515109657c4474d65a84e7123de249bf1e8467d04a65/aiohappyeyeballs-2.4.8-py3-none-any.whl", hash = "sha256:6cac4f5dd6e34a9644e69cf9021ef679e4394f54e58a183056d12009e42ea9e3", size = 15005 },
+]
+
+[[package]]
+name = "aiohttp"
+version = "3.11.13"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "aiohappyeyeballs" },
+ { name = "aiosignal" },
+ { name = "attrs" },
+ { name = "frozenlist" },
+ { name = "multidict" },
+ { name = "propcache" },
+ { name = "yarl" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/b3/3f/c4a667d184c69667b8f16e0704127efc5f1e60577df429382b4d95fd381e/aiohttp-3.11.13.tar.gz", hash = "sha256:8ce789231404ca8fff7f693cdce398abf6d90fd5dae2b1847477196c243b1fbb", size = 7674284 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/9a/a9/6657664a55f78db8767e396cc9723782ed3311eb57704b0a5dacfa731916/aiohttp-3.11.13-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:2eabb269dc3852537d57589b36d7f7362e57d1ece308842ef44d9830d2dc3c90", size = 705054 },
+ { url = "https://files.pythonhosted.org/packages/3b/06/f7df1fe062d16422f70af5065b76264f40b382605cf7477fa70553a9c9c1/aiohttp-3.11.13-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7b77ee42addbb1c36d35aca55e8cc6d0958f8419e458bb70888d8c69a4ca833d", size = 464440 },
+ { url = "https://files.pythonhosted.org/packages/22/3a/8773ea866735754004d9f79e501fe988bdd56cfac7fdecbc8de17fc093eb/aiohttp-3.11.13-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55789e93c5ed71832e7fac868167276beadf9877b85697020c46e9a75471f55f", size = 456394 },
+ { url = "https://files.pythonhosted.org/packages/7f/61/8e2f2af2327e8e475a2b0890f15ef0bbfd117e321cce1e1ed210df81bbac/aiohttp-3.11.13-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c929f9a7249a11e4aa5c157091cfad7f49cc6b13f4eecf9b747104befd9f56f2", size = 1682752 },
+ { url = "https://files.pythonhosted.org/packages/24/ed/84fce816bc8da39aa3f6c1196fe26e47065fea882b1a67a808282029c079/aiohttp-3.11.13-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d33851d85537bbf0f6291ddc97926a754c8f041af759e0aa0230fe939168852b", size = 1737375 },
+ { url = "https://files.pythonhosted.org/packages/d9/de/35a5ba9e3d21ebfda1ebbe66f6cc5cbb4d3ff9bd6a03e5e8a788954f8f27/aiohttp-3.11.13-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9229d8613bd8401182868fe95688f7581673e1c18ff78855671a4b8284f47bcb", size = 1793660 },
+ { url = "https://files.pythonhosted.org/packages/ff/fe/0f650a8c7c72c8a07edf8ab164786f936668acd71786dd5885fc4b1ca563/aiohttp-3.11.13-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:669dd33f028e54fe4c96576f406ebb242ba534dd3a981ce009961bf49960f117", size = 1692233 },
+ { url = "https://files.pythonhosted.org/packages/a8/20/185378b3483f968c6303aafe1e33b0da0d902db40731b2b2b2680a631131/aiohttp-3.11.13-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7c1b20a1ace54af7db1f95af85da530fe97407d9063b7aaf9ce6a32f44730778", size = 1619708 },
+ { url = "https://files.pythonhosted.org/packages/a4/f9/d9c181750980b17e1e13e522d7e82a8d08d3d28a2249f99207ef5d8d738f/aiohttp-3.11.13-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5724cc77f4e648362ebbb49bdecb9e2b86d9b172c68a295263fa072e679ee69d", size = 1641802 },
+ { url = "https://files.pythonhosted.org/packages/50/c7/1cb46b72b1788710343b6e59eaab9642bd2422f2d87ede18b1996e0aed8f/aiohttp-3.11.13-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:aa36c35e94ecdb478246dd60db12aba57cfcd0abcad43c927a8876f25734d496", size = 1684678 },
+ { url = "https://files.pythonhosted.org/packages/71/87/89b979391de840c5d7c34e78e1148cc731b8aafa84b6a51d02f44b4c66e2/aiohttp-3.11.13-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:9b5b37c863ad5b0892cc7a4ceb1e435e5e6acd3f2f8d3e11fa56f08d3c67b820", size = 1646921 },
+ { url = "https://files.pythonhosted.org/packages/a7/db/a463700ac85b72f8cf68093e988538faaf4e865e3150aa165cf80ee29d6e/aiohttp-3.11.13-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:e06cf4852ce8c4442a59bae5a3ea01162b8fcb49ab438d8548b8dc79375dad8a", size = 1702493 },
+ { url = "https://files.pythonhosted.org/packages/b8/32/1084e65da3adfb08c7e1b3e94f3e4ded8bd707dee265a412bc377b7cd000/aiohttp-3.11.13-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5194143927e494616e335d074e77a5dac7cd353a04755330c9adc984ac5a628e", size = 1735004 },
+ { url = "https://files.pythonhosted.org/packages/a0/bb/a634cbdd97ce5d05c2054a9a35bfc32792d7e4f69d600ad7e820571d095b/aiohttp-3.11.13-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:afcb6b275c2d2ba5d8418bf30a9654fa978b4f819c2e8db6311b3525c86fe637", size = 1694964 },
+ { url = "https://files.pythonhosted.org/packages/fd/cf/7d29db4e5c28ec316e5d2ac9ac9df0e2e278e9ea910e5c4205b9b64c2c42/aiohttp-3.11.13-cp312-cp312-win32.whl", hash = "sha256:7104d5b3943c6351d1ad7027d90bdd0ea002903e9f610735ac99df3b81f102ee", size = 411746 },
+ { url = "https://files.pythonhosted.org/packages/65/a9/13e69ad4fd62104ebd94617f9f2be58231b50bb1e6bac114f024303ac23b/aiohttp-3.11.13-cp312-cp312-win_amd64.whl", hash = "sha256:47dc018b1b220c48089b5b9382fbab94db35bef2fa192995be22cbad3c5730c8", size = 438078 },
+ { url = "https://files.pythonhosted.org/packages/87/dc/7d58d33cec693f1ddf407d4ab975445f5cb507af95600f137b81683a18d8/aiohttp-3.11.13-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:9862d077b9ffa015dbe3ce6c081bdf35135948cb89116e26667dd183550833d1", size = 698372 },
+ { url = "https://files.pythonhosted.org/packages/84/e7/5d88514c9e24fbc8dd6117350a8ec4a9314f4adae6e89fe32e3e639b0c37/aiohttp-3.11.13-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:fbfef0666ae9e07abfa2c54c212ac18a1f63e13e0760a769f70b5717742f3ece", size = 461057 },
+ { url = "https://files.pythonhosted.org/packages/96/1a/8143c48a929fa00c6324f85660cb0f47a55ed9385f0c1b72d4b8043acf8e/aiohttp-3.11.13-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:93a1f7d857c4fcf7cabb1178058182c789b30d85de379e04f64c15b7e88d66fb", size = 453340 },
+ { url = "https://files.pythonhosted.org/packages/2f/1c/b8010e4d65c5860d62681088e5376f3c0a940c5e3ca8989cae36ce8c3ea8/aiohttp-3.11.13-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba40b7ae0f81c7029583a338853f6607b6d83a341a3dcde8bed1ea58a3af1df9", size = 1665561 },
+ { url = "https://files.pythonhosted.org/packages/19/ed/a68c3ab2f92fdc17dfc2096117d1cfaa7f7bdded2a57bacbf767b104165b/aiohttp-3.11.13-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b5b95787335c483cd5f29577f42bbe027a412c5431f2f80a749c80d040f7ca9f", size = 1718335 },
+ { url = "https://files.pythonhosted.org/packages/27/4f/3a0b6160ce663b8ebdb65d1eedff60900cd7108838c914d25952fe2b909f/aiohttp-3.11.13-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a7d474c5c1f0b9405c1565fafdc4429fa7d986ccbec7ce55bc6a330f36409cad", size = 1775522 },
+ { url = "https://files.pythonhosted.org/packages/0b/58/9da09291e19696c452e7224c1ce8c6d23a291fe8cd5c6b247b51bcda07db/aiohttp-3.11.13-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e83fb1991e9d8982b3b36aea1e7ad27ea0ce18c14d054c7a404d68b0319eebb", size = 1677566 },
+ { url = "https://files.pythonhosted.org/packages/3d/18/6184f2bf8bbe397acbbbaa449937d61c20a6b85765f48e5eddc6d84957fe/aiohttp-3.11.13-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4586a68730bd2f2b04a83e83f79d271d8ed13763f64b75920f18a3a677b9a7f0", size = 1603590 },
+ { url = "https://files.pythonhosted.org/packages/04/94/91e0d1ca0793012ccd927e835540aa38cca98bdce2389256ab813ebd64a3/aiohttp-3.11.13-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9fe4eb0e7f50cdb99b26250d9328faef30b1175a5dbcfd6d0578d18456bac567", size = 1618688 },
+ { url = "https://files.pythonhosted.org/packages/71/85/d13c3ea2e48a10b43668305d4903838834c3d4112e5229177fbcc23a56cd/aiohttp-3.11.13-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:2a8a6bc19818ac3e5596310ace5aa50d918e1ebdcc204dc96e2f4d505d51740c", size = 1658053 },
+ { url = "https://files.pythonhosted.org/packages/12/6a/3242a35100de23c1e8d9e05e8605e10f34268dee91b00d9d1e278c58eb80/aiohttp-3.11.13-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:7f27eec42f6c3c1df09cfc1f6786308f8b525b8efaaf6d6bd76c1f52c6511f6a", size = 1616917 },
+ { url = "https://files.pythonhosted.org/packages/f5/b3/3f99b6f0a9a79590a7ba5655dbde8408c685aa462247378c977603464d0a/aiohttp-3.11.13-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:2a4a13dfbb23977a51853b419141cd0a9b9573ab8d3a1455c6e63561387b52ff", size = 1685872 },
+ { url = "https://files.pythonhosted.org/packages/8a/2e/99672181751f280a85e24fcb9a2c2469e8b1a0de1746b7b5c45d1eb9a999/aiohttp-3.11.13-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:02876bf2f69b062584965507b07bc06903c2dc93c57a554b64e012d636952654", size = 1715719 },
+ { url = "https://files.pythonhosted.org/packages/7a/cd/68030356eb9a7d57b3e2823c8a852709d437abb0fbff41a61ebc351b7625/aiohttp-3.11.13-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b992778d95b60a21c4d8d4a5f15aaab2bd3c3e16466a72d7f9bfd86e8cea0d4b", size = 1673166 },
+ { url = "https://files.pythonhosted.org/packages/03/61/425397a9a2839c609d09fdb53d940472f316a2dbeaa77a35b2628dae6284/aiohttp-3.11.13-cp313-cp313-win32.whl", hash = "sha256:507ab05d90586dacb4f26a001c3abf912eb719d05635cbfad930bdbeb469b36c", size = 410615 },
+ { url = "https://files.pythonhosted.org/packages/9c/54/ebb815bc0fe057d8e7a11c086c479e972e827082f39aeebc6019dd4f0862/aiohttp-3.11.13-cp313-cp313-win_amd64.whl", hash = "sha256:5ceb81a4db2decdfa087381b5fc5847aa448244f973e5da232610304e199e7b2", size = 436452 },
+]
+
+[[package]]
+name = "aiosignal"
+version = "1.3.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "frozenlist" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/ba/b5/6d55e80f6d8a08ce22b982eafa278d823b541c925f11ee774b0b9c43473d/aiosignal-1.3.2.tar.gz", hash = "sha256:a8c255c66fafb1e499c9351d0bf32ff2d8a0321595ebac3b93713656d2436f54", size = 19424 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/ec/6a/bc7e17a3e87a2985d3e8f4da4cd0f481060eb78fb08596c42be62c90a4d9/aiosignal-1.3.2-py2.py3-none-any.whl", hash = "sha256:45cde58e409a301715980c2b01d0c28bdde3770d8290b5eb2173759d9acb31a5", size = 7597 },
+]
+
+[[package]]
+name = "alabaster"
+version = "1.0.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/a6/f8/d9c74d0daf3f742840fd818d69cfae176fa332022fd44e3469487d5a9420/alabaster-1.0.0.tar.gz", hash = "sha256:c00dca57bca26fa62a6d7d0a9fcce65f3e026e9bfe33e9c538fd3fbb2144fd9e", size = 24210 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/7e/b3/6b4067be973ae96ba0d615946e314c5ae35f9f993eca561b356540bb0c2b/alabaster-1.0.0-py3-none-any.whl", hash = "sha256:fc6786402dc3fcb2de3cabd5fe455a2db534b371124f1f21de8731783dec828b", size = 13929 },
+]
+
+[[package]]
+name = "annotated-types"
+version = "0.7.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643 },
+]
+
+[[package]]
+name = "anthropic"
+version = "0.49.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "anyio" },
+ { name = "distro" },
+ { name = "httpx" },
+ { name = "jiter" },
+ { name = "pydantic" },
+ { name = "sniffio" },
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/86/e3/a88c8494ce4d1a88252b9e053607e885f9b14d0a32273d47b727cbee4228/anthropic-0.49.0.tar.gz", hash = "sha256:c09e885b0f674b9119b4f296d8508907f6cff0009bc20d5cf6b35936c40b4398", size = 210016 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/76/74/5d90ad14d55fbe3f9c474fdcb6e34b4bed99e3be8efac98734a5ddce88c1/anthropic-0.49.0-py3-none-any.whl", hash = "sha256:bbc17ad4e7094988d2fa86b87753ded8dce12498f4b85fe5810f208f454a8375", size = 243368 },
+]
+
+[[package]]
+name = "anyio"
+version = "4.8.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "idna" },
+ { name = "sniffio" },
+ { name = "typing-extensions", marker = "python_full_version < '3.13'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/a3/73/199a98fc2dae33535d6b8e8e6ec01f8c1d76c9adb096c6b7d64823038cde/anyio-4.8.0.tar.gz", hash = "sha256:1d9fe889df5212298c0c0723fa20479d1b94883a2df44bd3897aa91083316f7a", size = 181126 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/46/eb/e7f063ad1fec6b3178a3cd82d1a3c4de82cccf283fc42746168188e1cdd5/anyio-4.8.0-py3-none-any.whl", hash = "sha256:b5011f270ab5eb0abf13385f851315585cc37ef330dd88e27ec3d34d651fd47a", size = 96041 },
+]
+
+[[package]]
+name = "argcomplete"
+version = "3.5.3"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/0c/be/6c23d80cb966fb8f83fb1ebfb988351ae6b0554d0c3a613ee4531c026597/argcomplete-3.5.3.tar.gz", hash = "sha256:c12bf50eded8aebb298c7b7da7a5ff3ee24dffd9f5281867dfe1424b58c55392", size = 72999 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c4/08/2a4db06ec3d203124c967fc89295e85a202e5cbbcdc08fd6a64b65217d1e/argcomplete-3.5.3-py3-none-any.whl", hash = "sha256:2ab2c4a215c59fd6caaff41a869480a23e8f6a5f910b266c1808037f4e375b61", size = 43569 },
+]
+
+[[package]]
+name = "astor"
+version = "0.8.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/5a/21/75b771132fee241dfe601d39ade629548a9626d1d39f333fde31bc46febe/astor-0.8.1.tar.gz", hash = "sha256:6a6effda93f4e1ce9f618779b2dd1d9d84f1e32812c23a29b3fff6fd7f63fa5e", size = 35090 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c3/88/97eef84f48fa04fbd6750e62dcceafba6c63c81b7ac1420856c8dcc0a3f9/astor-0.8.1-py2.py3-none-any.whl", hash = "sha256:070a54e890cefb5b3739d19f30f5a5ec840ffc9c50ffa7d23cc9fc1a38ebbfc5", size = 27488 },
+]
+
+[[package]]
+name = "attrs"
+version = "25.1.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/49/7c/fdf464bcc51d23881d110abd74b512a42b3d5d376a55a831b44c603ae17f/attrs-25.1.0.tar.gz", hash = "sha256:1c97078a80c814273a76b2a298a932eb681c87415c11dee0a6921de7f1b02c3e", size = 810562 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/fc/30/d4986a882011f9df997a55e6becd864812ccfcd821d64aac8570ee39f719/attrs-25.1.0-py3-none-any.whl", hash = "sha256:c75a69e28a550a7e93789579c22aa26b0f5b83b75dc4e08fe092980051e1090a", size = 63152 },
+]
+
+[[package]]
+name = "babel"
+version = "2.17.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/7d/6b/d52e42361e1aa00709585ecc30b3f9684b3ab62530771402248b1b1d6240/babel-2.17.0.tar.gz", hash = "sha256:0c54cffb19f690cdcc52a3b50bcbf71e07a808d1c80d549f2459b9d2cf0afb9d", size = 9951852 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/b7/b8/3fe70c75fe32afc4bb507f75563d39bc5642255d1d94f1f23604725780bf/babel-2.17.0-py3-none-any.whl", hash = "sha256:4d0b53093fdfb4b21c92b5213dba5a1b23885afa8383709427046b21c366e5f2", size = 10182537 },
+]
+
+[[package]]
+name = "beautifulsoup4"
+version = "4.13.3"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "soupsieve" },
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/f0/3c/adaf39ce1fb4afdd21b611e3d530b183bb7759c9b673d60db0e347fd4439/beautifulsoup4-4.13.3.tar.gz", hash = "sha256:1bd32405dacc920b42b83ba01644747ed77456a65760e285fbc47633ceddaf8b", size = 619516 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/f9/49/6abb616eb3cbab6a7cca303dc02fdf3836de2e0b834bf966a7f5271a34d8/beautifulsoup4-4.13.3-py3-none-any.whl", hash = "sha256:99045d7d3f08f91f0d656bc9b7efbae189426cd913d830294a15eefa0ea4df16", size = 186015 },
+]
+
+[[package]]
+name = "black"
+version = "25.1.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "click" },
+ { name = "mypy-extensions" },
+ { name = "packaging" },
+ { name = "pathspec" },
+ { name = "platformdirs" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/94/49/26a7b0f3f35da4b5a65f081943b7bcd22d7002f5f0fb8098ec1ff21cb6ef/black-25.1.0.tar.gz", hash = "sha256:33496d5cd1222ad73391352b4ae8da15253c5de89b93a80b3e2c8d9a19ec2666", size = 649449 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/83/71/3fe4741df7adf015ad8dfa082dd36c94ca86bb21f25608eb247b4afb15b2/black-25.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4b60580e829091e6f9238c848ea6750efed72140b91b048770b64e74fe04908b", size = 1650988 },
+ { url = "https://files.pythonhosted.org/packages/13/f3/89aac8a83d73937ccd39bbe8fc6ac8860c11cfa0af5b1c96d081facac844/black-25.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1e2978f6df243b155ef5fa7e558a43037c3079093ed5d10fd84c43900f2d8ecc", size = 1453985 },
+ { url = "https://files.pythonhosted.org/packages/6f/22/b99efca33f1f3a1d2552c714b1e1b5ae92efac6c43e790ad539a163d1754/black-25.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3b48735872ec535027d979e8dcb20bf4f70b5ac75a8ea99f127c106a7d7aba9f", size = 1783816 },
+ { url = "https://files.pythonhosted.org/packages/18/7e/a27c3ad3822b6f2e0e00d63d58ff6299a99a5b3aee69fa77cd4b0076b261/black-25.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:ea0213189960bda9cf99be5b8c8ce66bb054af5e9e861249cd23471bd7b0b3ba", size = 1440860 },
+ { url = "https://files.pythonhosted.org/packages/98/87/0edf98916640efa5d0696e1abb0a8357b52e69e82322628f25bf14d263d1/black-25.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8f0b18a02996a836cc9c9c78e5babec10930862827b1b724ddfe98ccf2f2fe4f", size = 1650673 },
+ { url = "https://files.pythonhosted.org/packages/52/e5/f7bf17207cf87fa6e9b676576749c6b6ed0d70f179a3d812c997870291c3/black-25.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:afebb7098bfbc70037a053b91ae8437c3857482d3a690fefc03e9ff7aa9a5fd3", size = 1453190 },
+ { url = "https://files.pythonhosted.org/packages/e3/ee/adda3d46d4a9120772fae6de454c8495603c37c4c3b9c60f25b1ab6401fe/black-25.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:030b9759066a4ee5e5aca28c3c77f9c64789cdd4de8ac1df642c40b708be6171", size = 1782926 },
+ { url = "https://files.pythonhosted.org/packages/cc/64/94eb5f45dcb997d2082f097a3944cfc7fe87e071907f677e80788a2d7b7a/black-25.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:a22f402b410566e2d1c950708c77ebf5ebd5d0d88a6a2e87c86d9fb48afa0d18", size = 1442613 },
+ { url = "https://files.pythonhosted.org/packages/09/71/54e999902aed72baf26bca0d50781b01838251a462612966e9fc4891eadd/black-25.1.0-py3-none-any.whl", hash = "sha256:95e8176dae143ba9097f351d174fdaf0ccd29efb414b362ae3fd72bf0f710717", size = 207646 },
+]
+
+[[package]]
+name = "certifi"
+version = "2025.1.31"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/1c/ab/c9f1e32b7b1bf505bf26f0ef697775960db7932abeb7b516de930ba2705f/certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651", size = 167577 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/38/fc/bce832fd4fd99766c04d1ee0eead6b0ec6486fb100ae5e74c1d91292b982/certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe", size = 166393 },
+]
+
+[[package]]
+name = "cffi"
+version = "1.17.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "pycparser" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/fc/97/c783634659c2920c3fc70419e3af40972dbaf758daa229a7d6ea6135c90d/cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824", size = 516621 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/5a/84/e94227139ee5fb4d600a7a4927f322e1d4aea6fdc50bd3fca8493caba23f/cffi-1.17.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4", size = 183178 },
+ { url = "https://files.pythonhosted.org/packages/da/ee/fb72c2b48656111c4ef27f0f91da355e130a923473bf5ee75c5643d00cca/cffi-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c", size = 178840 },
+ { url = "https://files.pythonhosted.org/packages/cc/b6/db007700f67d151abadf508cbfd6a1884f57eab90b1bb985c4c8c02b0f28/cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36", size = 454803 },
+ { url = "https://files.pythonhosted.org/packages/1a/df/f8d151540d8c200eb1c6fba8cd0dfd40904f1b0682ea705c36e6c2e97ab3/cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5", size = 478850 },
+ { url = "https://files.pythonhosted.org/packages/28/c0/b31116332a547fd2677ae5b78a2ef662dfc8023d67f41b2a83f7c2aa78b1/cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff", size = 485729 },
+ { url = "https://files.pythonhosted.org/packages/91/2b/9a1ddfa5c7f13cab007a2c9cc295b70fbbda7cb10a286aa6810338e60ea1/cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99", size = 471256 },
+ { url = "https://files.pythonhosted.org/packages/b2/d5/da47df7004cb17e4955df6a43d14b3b4ae77737dff8bf7f8f333196717bf/cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93", size = 479424 },
+ { url = "https://files.pythonhosted.org/packages/0b/ac/2a28bcf513e93a219c8a4e8e125534f4f6db03e3179ba1c45e949b76212c/cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3", size = 484568 },
+ { url = "https://files.pythonhosted.org/packages/d4/38/ca8a4f639065f14ae0f1d9751e70447a261f1a30fa7547a828ae08142465/cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8", size = 488736 },
+ { url = "https://files.pythonhosted.org/packages/86/c5/28b2d6f799ec0bdecf44dced2ec5ed43e0eb63097b0f58c293583b406582/cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65", size = 172448 },
+ { url = "https://files.pythonhosted.org/packages/50/b9/db34c4755a7bd1cb2d1603ac3863f22bcecbd1ba29e5ee841a4bc510b294/cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903", size = 181976 },
+ { url = "https://files.pythonhosted.org/packages/8d/f8/dd6c246b148639254dad4d6803eb6a54e8c85c6e11ec9df2cffa87571dbe/cffi-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e", size = 182989 },
+ { url = "https://files.pythonhosted.org/packages/8b/f1/672d303ddf17c24fc83afd712316fda78dc6fce1cd53011b839483e1ecc8/cffi-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2", size = 178802 },
+ { url = "https://files.pythonhosted.org/packages/0e/2d/eab2e858a91fdff70533cab61dcff4a1f55ec60425832ddfdc9cd36bc8af/cffi-1.17.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3", size = 454792 },
+ { url = "https://files.pythonhosted.org/packages/75/b2/fbaec7c4455c604e29388d55599b99ebcc250a60050610fadde58932b7ee/cffi-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683", size = 478893 },
+ { url = "https://files.pythonhosted.org/packages/4f/b7/6e4a2162178bf1935c336d4da8a9352cccab4d3a5d7914065490f08c0690/cffi-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5", size = 485810 },
+ { url = "https://files.pythonhosted.org/packages/c7/8a/1d0e4a9c26e54746dc08c2c6c037889124d4f59dffd853a659fa545f1b40/cffi-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4", size = 471200 },
+ { url = "https://files.pythonhosted.org/packages/26/9f/1aab65a6c0db35f43c4d1b4f580e8df53914310afc10ae0397d29d697af4/cffi-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd", size = 479447 },
+ { url = "https://files.pythonhosted.org/packages/5f/e4/fb8b3dd8dc0e98edf1135ff067ae070bb32ef9d509d6cb0f538cd6f7483f/cffi-1.17.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed", size = 484358 },
+ { url = "https://files.pythonhosted.org/packages/f1/47/d7145bf2dc04684935d57d67dff9d6d795b2ba2796806bb109864be3a151/cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9", size = 488469 },
+ { url = "https://files.pythonhosted.org/packages/bf/ee/f94057fa6426481d663b88637a9a10e859e492c73d0384514a17d78ee205/cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d", size = 172475 },
+ { url = "https://files.pythonhosted.org/packages/7c/fc/6a8cb64e5f0324877d503c854da15d76c1e50eb722e320b15345c4d0c6de/cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a", size = 182009 },
+]
+
+[[package]]
+name = "cfgv"
+version = "3.4.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/11/74/539e56497d9bd1d484fd863dd69cbbfa653cd2aa27abfe35653494d85e94/cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560", size = 7114 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c5/55/51844dd50c4fc7a33b653bfaba4c2456f06955289ca770a5dbd5fd267374/cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9", size = 7249 },
+]
+
+[[package]]
+name = "chardet"
+version = "5.2.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/f3/0d/f7b6ab21ec75897ed80c17d79b15951a719226b9fababf1e40ea74d69079/chardet-5.2.0.tar.gz", hash = "sha256:1b3b6ff479a8c414bc3fa2c0852995695c4a026dcd6d0633b2dd092ca39c1cf7", size = 2069618 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/38/6f/f5fbc992a329ee4e0f288c1fe0e2ad9485ed064cac731ed2fe47dcc38cbf/chardet-5.2.0-py3-none-any.whl", hash = "sha256:e1cf59446890a00105fe7b7912492ea04b6e6f06d4b742b2c788469e34c82970", size = 199385 },
+]
+
+[[package]]
+name = "charset-normalizer"
+version = "3.4.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/16/b0/572805e227f01586461c80e0fd25d65a2115599cc9dad142fee4b747c357/charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3", size = 123188 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/0a/9a/dd1e1cdceb841925b7798369a09279bd1cf183cef0f9ddf15a3a6502ee45/charset_normalizer-3.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545", size = 196105 },
+ { url = "https://files.pythonhosted.org/packages/d3/8c/90bfabf8c4809ecb648f39794cf2a84ff2e7d2a6cf159fe68d9a26160467/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7", size = 140404 },
+ { url = "https://files.pythonhosted.org/packages/ad/8f/e410d57c721945ea3b4f1a04b74f70ce8fa800d393d72899f0a40526401f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757", size = 150423 },
+ { url = "https://files.pythonhosted.org/packages/f0/b8/e6825e25deb691ff98cf5c9072ee0605dc2acfca98af70c2d1b1bc75190d/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa", size = 143184 },
+ { url = "https://files.pythonhosted.org/packages/3e/a2/513f6cbe752421f16d969e32f3583762bfd583848b763913ddab8d9bfd4f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d", size = 145268 },
+ { url = "https://files.pythonhosted.org/packages/74/94/8a5277664f27c3c438546f3eb53b33f5b19568eb7424736bdc440a88a31f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616", size = 147601 },
+ { url = "https://files.pythonhosted.org/packages/7c/5f/6d352c51ee763623a98e31194823518e09bfa48be2a7e8383cf691bbb3d0/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b", size = 141098 },
+ { url = "https://files.pythonhosted.org/packages/78/d4/f5704cb629ba5ab16d1d3d741396aec6dc3ca2b67757c45b0599bb010478/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d", size = 149520 },
+ { url = "https://files.pythonhosted.org/packages/c5/96/64120b1d02b81785f222b976c0fb79a35875457fa9bb40827678e54d1bc8/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a", size = 152852 },
+ { url = "https://files.pythonhosted.org/packages/84/c9/98e3732278a99f47d487fd3468bc60b882920cef29d1fa6ca460a1fdf4e6/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9", size = 150488 },
+ { url = "https://files.pythonhosted.org/packages/13/0e/9c8d4cb99c98c1007cc11eda969ebfe837bbbd0acdb4736d228ccaabcd22/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1", size = 146192 },
+ { url = "https://files.pythonhosted.org/packages/b2/21/2b6b5b860781a0b49427309cb8670785aa543fb2178de875b87b9cc97746/charset_normalizer-3.4.1-cp312-cp312-win32.whl", hash = "sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35", size = 95550 },
+ { url = "https://files.pythonhosted.org/packages/21/5b/1b390b03b1d16c7e382b561c5329f83cc06623916aab983e8ab9239c7d5c/charset_normalizer-3.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f", size = 102785 },
+ { url = "https://files.pythonhosted.org/packages/38/94/ce8e6f63d18049672c76d07d119304e1e2d7c6098f0841b51c666e9f44a0/charset_normalizer-3.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda", size = 195698 },
+ { url = "https://files.pythonhosted.org/packages/24/2e/dfdd9770664aae179a96561cc6952ff08f9a8cd09a908f259a9dfa063568/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313", size = 140162 },
+ { url = "https://files.pythonhosted.org/packages/24/4e/f646b9093cff8fc86f2d60af2de4dc17c759de9d554f130b140ea4738ca6/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9", size = 150263 },
+ { url = "https://files.pythonhosted.org/packages/5e/67/2937f8d548c3ef6e2f9aab0f6e21001056f692d43282b165e7c56023e6dd/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b", size = 142966 },
+ { url = "https://files.pythonhosted.org/packages/52/ed/b7f4f07de100bdb95c1756d3a4d17b90c1a3c53715c1a476f8738058e0fa/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11", size = 144992 },
+ { url = "https://files.pythonhosted.org/packages/96/2c/d49710a6dbcd3776265f4c923bb73ebe83933dfbaa841c5da850fe0fd20b/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f", size = 147162 },
+ { url = "https://files.pythonhosted.org/packages/b4/41/35ff1f9a6bd380303dea55e44c4933b4cc3c4850988927d4082ada230273/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd", size = 140972 },
+ { url = "https://files.pythonhosted.org/packages/fb/43/c6a0b685fe6910d08ba971f62cd9c3e862a85770395ba5d9cad4fede33ab/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2", size = 149095 },
+ { url = "https://files.pythonhosted.org/packages/4c/ff/a9a504662452e2d2878512115638966e75633519ec11f25fca3d2049a94a/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886", size = 152668 },
+ { url = "https://files.pythonhosted.org/packages/6c/71/189996b6d9a4b932564701628af5cee6716733e9165af1d5e1b285c530ed/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601", size = 150073 },
+ { url = "https://files.pythonhosted.org/packages/e4/93/946a86ce20790e11312c87c75ba68d5f6ad2208cfb52b2d6a2c32840d922/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd", size = 145732 },
+ { url = "https://files.pythonhosted.org/packages/cd/e5/131d2fb1b0dddafc37be4f3a2fa79aa4c037368be9423061dccadfd90091/charset_normalizer-3.4.1-cp313-cp313-win32.whl", hash = "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407", size = 95391 },
+ { url = "https://files.pythonhosted.org/packages/27/f2/4f9a69cc7712b9b5ad8fdb87039fd89abba997ad5cbe690d1835d40405b0/charset_normalizer-3.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971", size = 102702 },
+ { url = "https://files.pythonhosted.org/packages/0e/f6/65ecc6878a89bb1c23a086ea335ad4bf21a588990c3f535a227b9eea9108/charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85", size = 49767 },
+]
+
+[[package]]
+name = "click"
+version = "8.1.8"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "colorama", marker = "sys_platform == 'win32'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188 },
+]
+
+[[package]]
+name = "click-option-group"
+version = "0.5.6"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "click" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/e7/b8/91054601a2e05fd9060cb1baf56be5b24145817b059e078669e1099529c7/click-option-group-0.5.6.tar.gz", hash = "sha256:97d06703873518cc5038509443742b25069a3c7562d1ea72ff08bfadde1ce777", size = 16517 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/af/75/81ea958bc0f7e410257cb2a42531b93a7695a31930cde87192c010a52c50/click_option_group-0.5.6-py3-none-any.whl", hash = "sha256:38a26d963ee3ad93332ddf782f9259c5bdfe405e73408d943ef5e7d0c3767ec7", size = 12467 },
+]
+
+[[package]]
+name = "codegen"
+source = { directory = "../../../" }
+dependencies = [
+ { name = "anthropic" },
+ { name = "astor" },
+ { name = "click" },
+ { name = "codegen-sdk-pink" },
+ { name = "codeowners" },
+ { name = "colorlog" },
+ { name = "dataclasses-json" },
+ { name = "datamodel-code-generator" },
+ { name = "datasets" },
+ { name = "dicttoxml" },
+ { name = "docker" },
+ { name = "docstring-parser" },
+ { name = "fastapi", extra = ["standard"] },
+ { name = "gitpython" },
+ { name = "giturlparse" },
+ { name = "hatch-vcs" },
+ { name = "hatchling" },
+ { name = "httpx" },
+ { name = "humanize" },
+ { name = "langchain", extra = ["openai"] },
+ { name = "langchain-anthropic" },
+ { name = "langchain-core" },
+ { name = "langchain-openai" },
+ { name = "langchain-xai" },
+ { name = "langgraph" },
+ { name = "langgraph-prebuilt" },
+ { name = "langsmith" },
+ { name = "lazy-object-proxy" },
+ { name = "lox" },
+ { name = "mcp", extra = ["cli"] },
+ { name = "mini-racer" },
+ { name = "modal" },
+ { name = "neo4j" },
+ { name = "networkx" },
+ { name = "numpy" },
+ { name = "openai" },
+ { name = "packaging" },
+ { name = "pip" },
+ { name = "plotly" },
+ { name = "psutil" },
+ { name = "pydantic" },
+ { name = "pydantic-core" },
+ { name = "pydantic-settings" },
+ { name = "pygit2" },
+ { name = "pygithub" },
+ { name = "pyinstrument" },
+ { name = "pyjson5" },
+ { name = "pyright" },
+ { name = "pytest-snapshot" },
+ { name = "python-dotenv" },
+ { name = "python-levenshtein" },
+ { name = "python-semantic-release" },
+ { name = "requests" },
+ { name = "rich" },
+ { name = "rich-click" },
+ { name = "rustworkx" },
+ { name = "sentry-sdk" },
+ { name = "slack-sdk" },
+ { name = "starlette" },
+ { name = "tabulate" },
+ { name = "termcolor" },
+ { name = "tiktoken" },
+ { name = "tomlkit" },
+ { name = "tqdm" },
+ { name = "tree-sitter" },
+ { name = "tree-sitter-javascript" },
+ { name = "tree-sitter-python" },
+ { name = "tree-sitter-typescript" },
+ { name = "typing-extensions" },
+ { name = "unidiff" },
+ { name = "urllib3" },
+ { name = "uvicorn", extra = ["standard"] },
+ { name = "watchfiles" },
+ { name = "wrapt" },
+ { name = "xmltodict" },
+]
+
+[package.metadata]
+requires-dist = [
+ { name = "anthropic" },
+ { name = "astor", specifier = ">=0.8.1,<1.0.0" },
+ { name = "attrs", marker = "extra == 'lsp'", specifier = ">=25.1.0" },
+ { name = "click", specifier = ">=8.1.7" },
+ { name = "codegen-sdk-pink", specifier = ">=0.1.0" },
+ { name = "codeowners", specifier = ">=0.6.0,<1.0.0" },
+ { name = "colorlog", specifier = ">=6.9.0" },
+ { name = "dataclasses-json", specifier = ">=0.6.4,<1.0.0" },
+ { name = "datamodel-code-generator", specifier = ">=0.26.5" },
+ { name = "datasets" },
+ { name = "dicttoxml", specifier = ">=1.7.16,<2.0.0" },
+ { name = "docker", specifier = ">=6.1.3" },
+ { name = "docstring-parser", specifier = ">=0.16,<1.0" },
+ { name = "fastapi", extras = ["standard"], specifier = ">=0.115.2,<1.0.0" },
+ { name = "gitpython", specifier = "==3.1.44" },
+ { name = "giturlparse" },
+ { name = "hatch-vcs", specifier = ">=0.4.0" },
+ { name = "hatchling", specifier = ">=1.25.0" },
+ { name = "httpx", specifier = ">=0.28.1" },
+ { name = "humanize", specifier = ">=4.10.0,<5.0.0" },
+ { name = "langchain", extras = ["openai"] },
+ { name = "langchain-anthropic", specifier = ">=0.3.7" },
+ { name = "langchain-core" },
+ { name = "langchain-openai" },
+ { name = "langchain-xai", specifier = ">=0.2.1" },
+ { name = "langgraph" },
+ { name = "langgraph-prebuilt" },
+ { name = "langsmith" },
+ { name = "lazy-object-proxy", specifier = ">=0.0.0" },
+ { name = "lox", specifier = ">=0.12.0" },
+ { name = "lsprotocol", marker = "extra == 'lsp'", specifier = "==2024.0.0b1" },
+ { name = "mcp", extras = ["cli"] },
+ { name = "mini-racer", specifier = ">=0.12.4" },
+ { name = "modal", specifier = ">=0.73.45" },
+ { name = "neo4j" },
+ { name = "networkx", specifier = ">=3.4.1" },
+ { name = "numpy", specifier = ">=2.2.2" },
+ { name = "openai", specifier = "==1.66.3" },
+ { name = "packaging", specifier = ">=24.2" },
+ { name = "pip", specifier = ">=24.3.1" },
+ { name = "plotly", specifier = ">=5.24.0,<7.0.0" },
+ { name = "psutil", specifier = ">=5.8.0" },
+ { name = "pydantic", specifier = ">=2.9.2,<3.0.0" },
+ { name = "pydantic-core", specifier = ">=2.23.4" },
+ { name = "pydantic-settings", specifier = ">=2.0.0" },
+ { name = "pygit2", specifier = ">=1.16.0" },
+ { name = "pygithub", specifier = "==2.6.1" },
+ { name = "pygls", marker = "extra == 'lsp'", specifier = ">=2.0.0a2" },
+ { name = "pyinstrument", specifier = ">=5.0.0" },
+ { name = "pyjson5", specifier = "==1.6.8" },
+ { name = "pyright", specifier = ">=1.1.372,<2.0.0" },
+ { name = "pytest-snapshot", specifier = ">=0.9.0" },
+ { name = "python-dotenv", specifier = ">=1.0.1" },
+ { name = "python-levenshtein", specifier = ">=0.25.1,<1.0.0" },
+ { name = "python-semantic-release" },
+ { name = "requests", specifier = ">=2.32.3" },
+ { name = "rich", specifier = ">=13.7.1,<14.0.0" },
+ { name = "rich-click", specifier = ">=1.8.5" },
+ { name = "rustworkx", specifier = ">=0.15.1" },
+ { name = "sentry-sdk", specifier = "==2.22.0" },
+ { name = "slack-sdk" },
+ { name = "starlette", specifier = ">=0.16.0,<1.0.0" },
+ { name = "tabulate", specifier = ">=0.9.0,<1.0.0" },
+ { name = "termcolor", specifier = ">=2.4.0" },
+ { name = "tiktoken", specifier = ">=0.5.1,<1.0.0" },
+ { name = "tomlkit", specifier = ">=0.13.2" },
+ { name = "tqdm", specifier = ">=4.67.1" },
+ { name = "tree-sitter", specifier = ">=0.23.1" },
+ { name = "tree-sitter-javascript", specifier = ">=0.23.1" },
+ { name = "tree-sitter-python", specifier = ">=0.23.4" },
+ { name = "tree-sitter-typescript", specifier = ">=0.23.2" },
+ { name = "types-networkx", marker = "extra == 'types'", specifier = ">=3.2.1.20240918" },
+ { name = "types-requests", marker = "extra == 'types'", specifier = ">=2.32.0.20241016" },
+ { name = "types-tabulate", marker = "extra == 'types'", specifier = ">=0.9.0.20240106" },
+ { name = "types-toml", marker = "extra == 'types'", specifier = ">=0.10.8.20240310" },
+ { name = "typing-extensions", specifier = ">=4.12.2" },
+ { name = "unidiff", specifier = ">=0.7.5" },
+ { name = "urllib3", specifier = ">=2.0.0" },
+ { name = "uvicorn", extras = ["standard"], specifier = ">=0.30.0" },
+ { name = "watchfiles", specifier = ">=1.0.0,<1.1.0" },
+ { name = "wrapt", specifier = ">=1.16.0,<2.0.0" },
+ { name = "xmltodict", specifier = ">=0.13.0,<1.0.0" },
+]
+
+[package.metadata.requires-dev]
+dev = [
+ { name = "austin-dist", specifier = ">=3.7.0" },
+ { name = "austin-python", specifier = ">=1.7.1" },
+ { name = "autoflake", specifier = ">=2.3.1" },
+ { name = "black", specifier = ">=24.8.0" },
+ { name = "braintrust", specifier = ">=0.0.160" },
+ { name = "cibuildwheel", extras = ["uv"], specifier = ">=2.22.0" },
+ { name = "coverage", specifier = ">=7.6.1,<8.0.0" },
+ { name = "cython", specifier = ">=3.0.11" },
+ { name = "deptry", specifier = ">=0.22.0" },
+ { name = "emoji", specifier = ">=2.14.0" },
+ { name = "filelock", specifier = ">=3.15.4,<4.0.0" },
+ { name = "httpx", specifier = ">=0.28.1,<0.28.2" },
+ { name = "inflection", specifier = ">=0.5.1,<1.0.0" },
+ { name = "isort", specifier = ">=5.13.2" },
+ { name = "jsbeautifier", specifier = ">=1.15.1,<2.0.0" },
+ { name = "jupyterlab", specifier = ">=4.3.5" },
+ { name = "loguru", specifier = ">=0.7.3" },
+ { name = "modal", specifier = ">=0.73.25" },
+ { name = "mypy", extras = ["mypyc", "faster-cache"], specifier = ">=1.13.0" },
+ { name = "pre-commit", specifier = ">=4.0.1" },
+ { name = "pre-commit-uv", specifier = ">=4.1.4" },
+ { name = "pytest", specifier = ">=8.3.3" },
+ { name = "pytest-asyncio", specifier = ">=0.21.1,<1.0.0" },
+ { name = "pytest-benchmark", extras = ["histogram"], specifier = ">=5.1.0" },
+ { name = "pytest-cov", specifier = ">=6.0.0,<6.0.1" },
+ { name = "pytest-lsp", specifier = ">=1.0.0b1" },
+ { name = "pytest-mock", specifier = ">=3.14.0,<4.0.0" },
+ { name = "pytest-timeout", specifier = ">=2.3.1" },
+ { name = "pytest-xdist", specifier = ">=3.6.1,<4.0.0" },
+ { name = "ruff", specifier = ">=0.6.8" },
+ { name = "ruff-lsp", specifier = ">=0.0.55,<1.0.0" },
+ { name = "sybil", extras = ["pytest"], specifier = ">=9.0.0" },
+ { name = "typer", specifier = ">=0.12.5" },
+ { name = "uv", specifier = ">=0.4.25" },
+]
+
+[[package]]
+name = "codegen-sdk-pink"
+version = "0.1.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/1d/da/0e968f5bd8d839ec30b58b681ba30781d5eb1b33a95d771e4b31f3a7cf08/codegen_sdk_pink-0.1.0.tar.gz", hash = "sha256:3be5c2caf47f40ec541cdd04558d8ddfb816ede7d7334e4a62ab3f6130f86afb", size = 322299 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/98/4c/6321af0699207ab63b750e82589f2c4d8726956da9413e30a42c7ea59641/codegen_sdk_pink-0.1.0-cp311-abi3-macosx_10_12_x86_64.whl", hash = "sha256:03f71cd48cd7547faf8233b90f01f4c41b750b4195a83a6a1b6427bee24a45a4", size = 5749136 },
+ { url = "https://files.pythonhosted.org/packages/c2/d0/39b35e45ce5683dace3e4b8c44e51a6471177708e5b3285fc1d764270ba1/codegen_sdk_pink-0.1.0-cp311-abi3-macosx_11_0_arm64.whl", hash = "sha256:c4872286a1328ec546798268ab9ff3bf368c223178fecf45903cf0c667290471", size = 5807261 },
+ { url = "https://files.pythonhosted.org/packages/db/19/5aff61ba06d877f385b206a8da88c87c77f6b7cd68f0aec7b8b16813e1a9/codegen_sdk_pink-0.1.0-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:64943be3bed917d506ece1e0b5492effaa500712c5109a3937266d440ee8bb53", size = 6387801 },
+ { url = "https://files.pythonhosted.org/packages/5e/e4/6a8f7b12b20ab4cd61b833f32bbc1f7c8c86ca7332364f01f08881a4a5e2/codegen_sdk_pink-0.1.0-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:345deecefa2de455dcf1fb2bdf5ad2e71e74476b4212b1bd51f57e6904c1d7e9", size = 6231083 },
+ { url = "https://files.pythonhosted.org/packages/0d/c3/b0f7106308e278b6774275c891bb82c08e04c41f1e9abf6bdf56757cc123/codegen_sdk_pink-0.1.0-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:7c5bcf0ad41644ac980590a37178f231ba275a75ce946dcfc31fa39330c098da", size = 6543302 },
+ { url = "https://files.pythonhosted.org/packages/e0/42/fedf5eec26a06d83de5cfb39fc7072261b72311b70d5fbbd4a75deec2457/codegen_sdk_pink-0.1.0-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b3ee15469ad58d0222dfa0ba5950cd0eb7b8b7c607912d1845950096ddcb7aad", size = 6682410 },
+ { url = "https://files.pythonhosted.org/packages/38/fc/b1479140f579bcd6bdc090e71033484fcfd3bbc76aa779906a322cb33834/codegen_sdk_pink-0.1.0-cp311-abi3-win_amd64.whl", hash = "sha256:10b9b00070b5561df80dd269524f106e44e222d1ab9a93f6cf6ca3565c0aa0f9", size = 4305666 },
+]
+
+[[package]]
+name = "codeowners"
+version = "0.7.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/75/66/ddba64473b0ce0b2c30cd0e1e32d923839834ed91948ad92bad23b2eadeb/codeowners-0.7.0.tar.gz", hash = "sha256:a842647b20968c14da6066e4de4fffac4fd7c1c30de9cfa8b2fc8f534b3d9f48", size = 7706 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/92/d1/4091c351ac4de65fa22da912bdb395011e6dc8e630f070348b7b3fdd885d/codeowners-0.7.0-py3-none-any.whl", hash = "sha256:0df5cd47299f984ba2e120dc4a0a7be68b528d53016ff39d06e86f85e33c7fc2", size = 8718 },
+]
+
+[[package]]
+name = "colorama"
+version = "0.4.6"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 },
+]
+
+[[package]]
+name = "colorlog"
+version = "6.9.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "colorama", marker = "sys_platform == 'win32'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/d3/7a/359f4d5df2353f26172b3cc39ea32daa39af8de522205f512f458923e677/colorlog-6.9.0.tar.gz", hash = "sha256:bfba54a1b93b94f54e1f4fe48395725a3d92fd2a4af702f6bd70946bdc0c6ac2", size = 16624 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/e3/51/9b208e85196941db2f0654ad0357ca6388ab3ed67efdbfc799f35d1f83aa/colorlog-6.9.0-py3-none-any.whl", hash = "sha256:5906e71acd67cb07a71e779c47c4bcb45fb8c2993eebe9e5adcd6a6f1b283eff", size = 11424 },
+]
+
+[[package]]
+name = "cryptography"
+version = "44.0.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "cffi", marker = "platform_python_implementation != 'PyPy'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/cd/25/4ce80c78963834b8a9fd1cc1266be5ed8d1840785c0f2e1b73b8d128d505/cryptography-44.0.2.tar.gz", hash = "sha256:c63454aa261a0cf0c5b4718349629793e9e634993538db841165b3df74f37ec0", size = 710807 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/92/ef/83e632cfa801b221570c5f58c0369db6fa6cef7d9ff859feab1aae1a8a0f/cryptography-44.0.2-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:efcfe97d1b3c79e486554efddeb8f6f53a4cdd4cf6086642784fa31fc384e1d7", size = 6676361 },
+ { url = "https://files.pythonhosted.org/packages/30/ec/7ea7c1e4c8fc8329506b46c6c4a52e2f20318425d48e0fe597977c71dbce/cryptography-44.0.2-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29ecec49f3ba3f3849362854b7253a9f59799e3763b0c9d0826259a88efa02f1", size = 3952350 },
+ { url = "https://files.pythonhosted.org/packages/27/61/72e3afdb3c5ac510330feba4fc1faa0fe62e070592d6ad00c40bb69165e5/cryptography-44.0.2-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc821e161ae88bfe8088d11bb39caf2916562e0a2dc7b6d56714a48b784ef0bb", size = 4166572 },
+ { url = "https://files.pythonhosted.org/packages/26/e4/ba680f0b35ed4a07d87f9e98f3ebccb05091f3bf6b5a478b943253b3bbd5/cryptography-44.0.2-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:3c00b6b757b32ce0f62c574b78b939afab9eecaf597c4d624caca4f9e71e7843", size = 3958124 },
+ { url = "https://files.pythonhosted.org/packages/9c/e8/44ae3e68c8b6d1cbc59040288056df2ad7f7f03bbcaca6b503c737ab8e73/cryptography-44.0.2-cp37-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7bdcd82189759aba3816d1f729ce42ffded1ac304c151d0a8e89b9996ab863d5", size = 3678122 },
+ { url = "https://files.pythonhosted.org/packages/27/7b/664ea5e0d1eab511a10e480baf1c5d3e681c7d91718f60e149cec09edf01/cryptography-44.0.2-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:4973da6ca3db4405c54cd0b26d328be54c7747e89e284fcff166132eb7bccc9c", size = 4191831 },
+ { url = "https://files.pythonhosted.org/packages/2a/07/79554a9c40eb11345e1861f46f845fa71c9e25bf66d132e123d9feb8e7f9/cryptography-44.0.2-cp37-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:4e389622b6927d8133f314949a9812972711a111d577a5d1f4bee5e58736b80a", size = 3960583 },
+ { url = "https://files.pythonhosted.org/packages/bb/6d/858e356a49a4f0b591bd6789d821427de18432212e137290b6d8a817e9bf/cryptography-44.0.2-cp37-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:f514ef4cd14bb6fb484b4a60203e912cfcb64f2ab139e88c2274511514bf7308", size = 4191753 },
+ { url = "https://files.pythonhosted.org/packages/b2/80/62df41ba4916067fa6b125aa8c14d7e9181773f0d5d0bd4dcef580d8b7c6/cryptography-44.0.2-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:1bc312dfb7a6e5d66082c87c34c8a62176e684b6fe3d90fcfe1568de675e6688", size = 4079550 },
+ { url = "https://files.pythonhosted.org/packages/f3/cd/2558cc08f7b1bb40683f99ff4327f8dcfc7de3affc669e9065e14824511b/cryptography-44.0.2-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:3b721b8b4d948b218c88cb8c45a01793483821e709afe5f622861fc6182b20a7", size = 4298367 },
+ { url = "https://files.pythonhosted.org/packages/71/59/94ccc74788945bc3bd4cf355d19867e8057ff5fdbcac781b1ff95b700fb1/cryptography-44.0.2-cp37-abi3-win32.whl", hash = "sha256:51e4de3af4ec3899d6d178a8c005226491c27c4ba84101bfb59c901e10ca9f79", size = 2772843 },
+ { url = "https://files.pythonhosted.org/packages/ca/2c/0d0bbaf61ba05acb32f0841853cfa33ebb7a9ab3d9ed8bb004bd39f2da6a/cryptography-44.0.2-cp37-abi3-win_amd64.whl", hash = "sha256:c505d61b6176aaf982c5717ce04e87da5abc9a36a5b39ac03905c4aafe8de7aa", size = 3209057 },
+ { url = "https://files.pythonhosted.org/packages/9e/be/7a26142e6d0f7683d8a382dd963745e65db895a79a280a30525ec92be890/cryptography-44.0.2-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:8e0ddd63e6bf1161800592c71ac794d3fb8001f2caebe0966e77c5234fa9efc3", size = 6677789 },
+ { url = "https://files.pythonhosted.org/packages/06/88/638865be7198a84a7713950b1db7343391c6066a20e614f8fa286eb178ed/cryptography-44.0.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:81276f0ea79a208d961c433a947029e1a15948966658cf6710bbabb60fcc2639", size = 3951919 },
+ { url = "https://files.pythonhosted.org/packages/d7/fc/99fe639bcdf58561dfad1faa8a7369d1dc13f20acd78371bb97a01613585/cryptography-44.0.2-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a1e657c0f4ea2a23304ee3f964db058c9e9e635cc7019c4aa21c330755ef6fd", size = 4167812 },
+ { url = "https://files.pythonhosted.org/packages/53/7b/aafe60210ec93d5d7f552592a28192e51d3c6b6be449e7fd0a91399b5d07/cryptography-44.0.2-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:6210c05941994290f3f7f175a4a57dbbb2afd9273657614c506d5976db061181", size = 3958571 },
+ { url = "https://files.pythonhosted.org/packages/16/32/051f7ce79ad5a6ef5e26a92b37f172ee2d6e1cce09931646eef8de1e9827/cryptography-44.0.2-cp39-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d1c3572526997b36f245a96a2b1713bf79ce99b271bbcf084beb6b9b075f29ea", size = 3679832 },
+ { url = "https://files.pythonhosted.org/packages/78/2b/999b2a1e1ba2206f2d3bca267d68f350beb2b048a41ea827e08ce7260098/cryptography-44.0.2-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:b042d2a275c8cee83a4b7ae30c45a15e6a4baa65a179a0ec2d78ebb90e4f6699", size = 4193719 },
+ { url = "https://files.pythonhosted.org/packages/72/97/430e56e39a1356e8e8f10f723211a0e256e11895ef1a135f30d7d40f2540/cryptography-44.0.2-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:d03806036b4f89e3b13b6218fefea8d5312e450935b1a2d55f0524e2ed7c59d9", size = 3960852 },
+ { url = "https://files.pythonhosted.org/packages/89/33/c1cf182c152e1d262cac56850939530c05ca6c8d149aa0dcee490b417e99/cryptography-44.0.2-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:c7362add18b416b69d58c910caa217f980c5ef39b23a38a0880dfd87bdf8cd23", size = 4193906 },
+ { url = "https://files.pythonhosted.org/packages/e1/99/87cf26d4f125380dc674233971069bc28d19b07f7755b29861570e513650/cryptography-44.0.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:8cadc6e3b5a1f144a039ea08a0bdb03a2a92e19c46be3285123d32029f40a922", size = 4081572 },
+ { url = "https://files.pythonhosted.org/packages/b3/9f/6a3e0391957cc0c5f84aef9fbdd763035f2b52e998a53f99345e3ac69312/cryptography-44.0.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:6f101b1f780f7fc613d040ca4bdf835c6ef3b00e9bd7125a4255ec574c7916e4", size = 4298631 },
+ { url = "https://files.pythonhosted.org/packages/e2/a5/5bc097adb4b6d22a24dea53c51f37e480aaec3465285c253098642696423/cryptography-44.0.2-cp39-abi3-win32.whl", hash = "sha256:3dc62975e31617badc19a906481deacdeb80b4bb454394b4098e3f2525a488c5", size = 2773792 },
+ { url = "https://files.pythonhosted.org/packages/33/cf/1f7649b8b9a3543e042d3f348e398a061923ac05b507f3f4d95f11938aa9/cryptography-44.0.2-cp39-abi3-win_amd64.whl", hash = "sha256:5f6f90b72d8ccadb9c6e311c775c8305381db88374c65fa1a68250aa8a9cb3a6", size = 3210957 },
+]
+
+[[package]]
+name = "dataclasses-json"
+version = "0.6.7"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "marshmallow" },
+ { name = "typing-inspect" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/64/a4/f71d9cf3a5ac257c993b5ca3f93df5f7fb395c725e7f1e6479d2514173c3/dataclasses_json-0.6.7.tar.gz", hash = "sha256:b6b3e528266ea45b9535223bc53ca645f5208833c29229e847b3f26a1cc55fc0", size = 32227 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c3/be/d0d44e092656fe7a06b55e6103cbce807cdbdee17884a5367c68c9860853/dataclasses_json-0.6.7-py3-none-any.whl", hash = "sha256:0dbf33f26c8d5305befd61b39d2b3414e8a407bedc2834dea9b8d642666fb40a", size = 28686 },
+]
+
+[[package]]
+name = "datamodel-code-generator"
+version = "0.28.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "argcomplete" },
+ { name = "black" },
+ { name = "genson" },
+ { name = "inflect" },
+ { name = "isort" },
+ { name = "jinja2" },
+ { name = "packaging" },
+ { name = "pydantic" },
+ { name = "pyyaml" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/25/5f/74fac9f7262e7763eaf56bbcd64c31f712f68135f2c758bc02d15876c543/datamodel_code_generator-0.28.2.tar.gz", hash = "sha256:5f16fe4d6acee79c1366f9ee68016eeec544fc0a2fec25ce47d35f7b7767e0fe", size = 435017 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/78/a0/5ce4d9495355507dfb6093192d1762f745c3e824be6377fc3df8539f06dc/datamodel_code_generator-0.28.2-py3-none-any.whl", hash = "sha256:a2c425386c3f836c618ae276be57e460df323ac78f911b1b12d927ddffd70e73", size = 115645 },
+]
+
+[[package]]
+name = "datasets"
+version = "3.3.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "aiohttp" },
+ { name = "dill" },
+ { name = "filelock" },
+ { name = "fsspec", extra = ["http"] },
+ { name = "huggingface-hub" },
+ { name = "multiprocess" },
+ { name = "numpy" },
+ { name = "packaging" },
+ { name = "pandas" },
+ { name = "pyarrow" },
+ { name = "pyyaml" },
+ { name = "requests" },
+ { name = "tqdm" },
+ { name = "xxhash" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/73/0c/dc3d172104e78e68f7a60386664adbf61db5d10c2246b31ddad06c2d1cb3/datasets-3.3.2.tar.gz", hash = "sha256:20901a97da870fb80b407ccc45f034a7ac99accd07da897ed42f11641bdb8c6e", size = 564352 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/4c/37/22ef7675bef4ffe9577b937ddca2e22791534cbbe11c30714972a91532dc/datasets-3.3.2-py3-none-any.whl", hash = "sha256:fdaf3d5d70242621210b044e9b9b15a56e908bfc3e9d077bcf5605ac390f70bd", size = 485360 },
+]
+
+[[package]]
+name = "deprecated"
+version = "1.2.18"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "wrapt" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/98/97/06afe62762c9a8a86af0cfb7bfdab22a43ad17138b07af5b1a58442690a2/deprecated-1.2.18.tar.gz", hash = "sha256:422b6f6d859da6f2ef57857761bfb392480502a64c3028ca9bbe86085d72115d", size = 2928744 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/6e/c6/ac0b6c1e2d138f1002bcf799d330bd6d85084fece321e662a14223794041/Deprecated-1.2.18-py2.py3-none-any.whl", hash = "sha256:bd5011788200372a32418f888e326a09ff80d0214bd961147cfed01b5c018eec", size = 9998 },
+]
+
+[[package]]
+name = "dicttoxml"
+version = "1.7.16"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/ee/c9/3132427f9e64d572688e6a1cbe3d542d1a03f676b81fb600f3d1fd7d2ec5/dicttoxml-1.7.16.tar.gz", hash = "sha256:6f36ce644881db5cd8940bee9b7cb3f3f6b7b327ba8a67d83d3e2caa0538bf9d", size = 39314 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/09/40/9d521973cae7f7ef8b1f0d0e28a3db0f851c1f1dca45d4c2ed5360bb7246/dicttoxml-1.7.16-py3-none-any.whl", hash = "sha256:8677671496d0d38e66c7179f82a7e9059f94887777955dc71b0ac602ee637c26", size = 24155 },
+]
+
+[[package]]
+name = "dill"
+version = "0.3.8"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/17/4d/ac7ffa80c69ea1df30a8aa11b3578692a5118e7cd1aa157e3ef73b092d15/dill-0.3.8.tar.gz", hash = "sha256:3ebe3c479ad625c4553aca177444d89b486b1d84982eeacded644afc0cf797ca", size = 184847 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c9/7a/cef76fd8438a42f96db64ddaa85280485a9c395e7df3db8158cfec1eee34/dill-0.3.8-py3-none-any.whl", hash = "sha256:c36ca9ffb54365bdd2f8eb3eff7d2a21237f8452b57ace88b1ac615b7e815bd7", size = 116252 },
+]
+
+[[package]]
+name = "distlib"
+version = "0.3.9"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/0d/dd/1bec4c5ddb504ca60fc29472f3d27e8d4da1257a854e1d96742f15c1d02d/distlib-0.3.9.tar.gz", hash = "sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403", size = 613923 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/91/a1/cf2472db20f7ce4a6be1253a81cfdf85ad9c7885ffbed7047fb72c24cf87/distlib-0.3.9-py2.py3-none-any.whl", hash = "sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87", size = 468973 },
+]
+
+[[package]]
+name = "distro"
+version = "1.9.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/fc/f8/98eea607f65de6527f8a2e8885fc8015d3e6f5775df186e443e0964a11c3/distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed", size = 60722 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277 },
+]
+
+[[package]]
+name = "dnspython"
+version = "2.7.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/b5/4a/263763cb2ba3816dd94b08ad3a33d5fdae34ecb856678773cc40a3605829/dnspython-2.7.0.tar.gz", hash = "sha256:ce9c432eda0dc91cf618a5cedf1a4e142651196bbcd2c80e89ed5a907e5cfaf1", size = 345197 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/68/1b/e0a87d256e40e8c888847551b20a017a6b98139178505dc7ffb96f04e954/dnspython-2.7.0-py3-none-any.whl", hash = "sha256:b4c34b7d10b51bcc3a5071e7b8dee77939f1e878477eeecc965e9835f63c6c86", size = 313632 },
+]
+
+[[package]]
+name = "docker"
+version = "7.1.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "pywin32", marker = "sys_platform == 'win32'" },
+ { name = "requests" },
+ { name = "urllib3" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/91/9b/4a2ea29aeba62471211598dac5d96825bb49348fa07e906ea930394a83ce/docker-7.1.0.tar.gz", hash = "sha256:ad8c70e6e3f8926cb8a92619b832b4ea5299e2831c14284663184e200546fa6c", size = 117834 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/e3/26/57c6fb270950d476074c087527a558ccb6f4436657314bfb6cdf484114c4/docker-7.1.0-py3-none-any.whl", hash = "sha256:c96b93b7f0a746f9e77d325bcfb87422a3d8bd4f03136ae8a85b37f1898d5fc0", size = 147774 },
+]
+
+[[package]]
+name = "docstring-parser"
+version = "0.16"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/08/12/9c22a58c0b1e29271051222d8906257616da84135af9ed167c9e28f85cb3/docstring_parser-0.16.tar.gz", hash = "sha256:538beabd0af1e2db0146b6bd3caa526c35a34d61af9fd2887f3a8a27a739aa6e", size = 26565 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/d5/7c/e9fcff7623954d86bdc17782036cbf715ecab1bec4847c008557affe1ca8/docstring_parser-0.16-py3-none-any.whl", hash = "sha256:bf0a1387354d3691d102edef7ec124f219ef639982d096e26e3b60aeffa90637", size = 36533 },
+]
+
+[[package]]
+name = "docutils"
+version = "0.21.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/ae/ed/aefcc8cd0ba62a0560c3c18c33925362d46c6075480bfa4df87b28e169a9/docutils-0.21.2.tar.gz", hash = "sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f", size = 2204444 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/8f/d7/9322c609343d929e75e7e5e6255e614fcc67572cfd083959cdef3b7aad79/docutils-0.21.2-py3-none-any.whl", hash = "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2", size = 587408 },
+]
+
+[[package]]
+name = "dotty-dict"
+version = "1.3.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/6a/ab/88d67f02024700b48cd8232579ad1316aa9df2272c63049c27cc094229d6/dotty_dict-1.3.1.tar.gz", hash = "sha256:4b016e03b8ae265539757a53eba24b9bfda506fb94fbce0bee843c6f05541a15", size = 7699 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/1a/91/e0d457ee03ec33d79ee2cd8d212debb1bc21dfb99728ae35efdb5832dc22/dotty_dict-1.3.1-py3-none-any.whl", hash = "sha256:5022d234d9922f13aa711b4950372a06a6d64cb6d6db9ba43d0ba133ebfce31f", size = 7014 },
+]
+
+[[package]]
+name = "email-validator"
+version = "2.2.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "dnspython" },
+ { name = "idna" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/48/ce/13508a1ec3f8bb981ae4ca79ea40384becc868bfae97fd1c942bb3a001b1/email_validator-2.2.0.tar.gz", hash = "sha256:cb690f344c617a714f22e66ae771445a1ceb46821152df8e165c5f9a364582b7", size = 48967 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/d7/ee/bf0adb559ad3c786f12bcbc9296b3f5675f529199bef03e2df281fa1fadb/email_validator-2.2.0-py3-none-any.whl", hash = "sha256:561977c2d73ce3611850a06fa56b414621e0c8faa9d66f2611407d87465da631", size = 33521 },
+]
+
+[[package]]
+name = "fastapi"
+version = "0.115.11"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "pydantic" },
+ { name = "starlette" },
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/b5/28/c5d26e5860df807241909a961a37d45e10533acef95fc368066c7dd186cd/fastapi-0.115.11.tar.gz", hash = "sha256:cc81f03f688678b92600a65a5e618b93592c65005db37157147204d8924bf94f", size = 294441 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/b3/5d/4d8bbb94f0dbc22732350c06965e40740f4a92ca560e90bb566f4f73af41/fastapi-0.115.11-py3-none-any.whl", hash = "sha256:32e1541b7b74602e4ef4a0260ecaf3aadf9d4f19590bba3e1bf2ac4666aa2c64", size = 94926 },
+]
+
+[package.optional-dependencies]
+standard = [
+ { name = "email-validator" },
+ { name = "fastapi-cli", extra = ["standard"] },
+ { name = "httpx" },
+ { name = "jinja2" },
+ { name = "python-multipart" },
+ { name = "uvicorn", extra = ["standard"] },
+]
+
+[[package]]
+name = "fastapi-cli"
+version = "0.0.7"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "rich-toolkit" },
+ { name = "typer" },
+ { name = "uvicorn", extra = ["standard"] },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/fe/73/82a5831fbbf8ed75905bacf5b2d9d3dfd6f04d6968b29fe6f72a5ae9ceb1/fastapi_cli-0.0.7.tar.gz", hash = "sha256:02b3b65956f526412515907a0793c9094abd4bfb5457b389f645b0ea6ba3605e", size = 16753 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/a1/e6/5daefc851b514ce2287d8f5d358ae4341089185f78f3217a69d0ce3a390c/fastapi_cli-0.0.7-py3-none-any.whl", hash = "sha256:d549368ff584b2804336c61f192d86ddea080c11255f375959627911944804f4", size = 10705 },
+]
+
+[package.optional-dependencies]
+standard = [
+ { name = "uvicorn", extra = ["standard"] },
+]
+
+[[package]]
+name = "fastcore"
+version = "1.7.29"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "packaging" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/a1/a6/f457241a8a5c42b80ef50b96e7cc515dd93bdb9ea273133004bbc8a6aa96/fastcore-1.7.29.tar.gz", hash = "sha256:e7e734cbe58805a22c205341c6671de562a8abba54b13eeb24cdb4486d066e31", size = 80514 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/d7/3a/a0b1c764426622287c9b6547d4ea637c406bc884141814df4a5ebab3ab9b/fastcore-1.7.29-py3-none-any.whl", hash = "sha256:76fd4815eabbed704faca3abfea4b7e1f98b6351ba6c869a2d405f37bc4b0074", size = 84208 },
+]
+
+[[package]]
+name = "filelock"
+version = "3.17.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/dc/9c/0b15fb47b464e1b663b1acd1253a062aa5feecb07d4e597daea542ebd2b5/filelock-3.17.0.tar.gz", hash = "sha256:ee4e77401ef576ebb38cd7f13b9b28893194acc20a8e68e18730ba9c0e54660e", size = 18027 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/89/ec/00d68c4ddfedfe64159999e5f8a98fb8442729a63e2077eb9dcd89623d27/filelock-3.17.0-py3-none-any.whl", hash = "sha256:533dc2f7ba78dc2f0f531fc6c4940addf7b70a481e269a5a3b93be94ffbe8338", size = 16164 },
+]
+
+[[package]]
+name = "frozenlist"
+version = "1.5.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/8f/ed/0f4cec13a93c02c47ec32d81d11c0c1efbadf4a471e3f3ce7cad366cbbd3/frozenlist-1.5.0.tar.gz", hash = "sha256:81d5af29e61b9c8348e876d442253723928dce6433e0e76cd925cd83f1b4b817", size = 39930 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/79/73/fa6d1a96ab7fd6e6d1c3500700963eab46813847f01ef0ccbaa726181dd5/frozenlist-1.5.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:31115ba75889723431aa9a4e77d5f398f5cf976eea3bdf61749731f62d4a4a21", size = 94026 },
+ { url = "https://files.pythonhosted.org/packages/ab/04/ea8bf62c8868b8eada363f20ff1b647cf2e93377a7b284d36062d21d81d1/frozenlist-1.5.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7437601c4d89d070eac8323f121fcf25f88674627505334654fd027b091db09d", size = 54150 },
+ { url = "https://files.pythonhosted.org/packages/d0/9a/8e479b482a6f2070b26bda572c5e6889bb3ba48977e81beea35b5ae13ece/frozenlist-1.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7948140d9f8ece1745be806f2bfdf390127cf1a763b925c4a805c603df5e697e", size = 51927 },
+ { url = "https://files.pythonhosted.org/packages/e3/12/2aad87deb08a4e7ccfb33600871bbe8f0e08cb6d8224371387f3303654d7/frozenlist-1.5.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:feeb64bc9bcc6b45c6311c9e9b99406660a9c05ca8a5b30d14a78555088b0b3a", size = 282647 },
+ { url = "https://files.pythonhosted.org/packages/77/f2/07f06b05d8a427ea0060a9cef6e63405ea9e0d761846b95ef3fb3be57111/frozenlist-1.5.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:683173d371daad49cffb8309779e886e59c2f369430ad28fe715f66d08d4ab1a", size = 289052 },
+ { url = "https://files.pythonhosted.org/packages/bd/9f/8bf45a2f1cd4aa401acd271b077989c9267ae8463e7c8b1eb0d3f561b65e/frozenlist-1.5.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7d57d8f702221405a9d9b40f9da8ac2e4a1a8b5285aac6100f3393675f0a85ee", size = 291719 },
+ { url = "https://files.pythonhosted.org/packages/41/d1/1f20fd05a6c42d3868709b7604c9f15538a29e4f734c694c6bcfc3d3b935/frozenlist-1.5.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:30c72000fbcc35b129cb09956836c7d7abf78ab5416595e4857d1cae8d6251a6", size = 267433 },
+ { url = "https://files.pythonhosted.org/packages/af/f2/64b73a9bb86f5a89fb55450e97cd5c1f84a862d4ff90d9fd1a73ab0f64a5/frozenlist-1.5.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:000a77d6034fbad9b6bb880f7ec073027908f1b40254b5d6f26210d2dab1240e", size = 283591 },
+ { url = "https://files.pythonhosted.org/packages/29/e2/ffbb1fae55a791fd6c2938dd9ea779509c977435ba3940b9f2e8dc9d5316/frozenlist-1.5.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5d7f5a50342475962eb18b740f3beecc685a15b52c91f7d975257e13e029eca9", size = 273249 },
+ { url = "https://files.pythonhosted.org/packages/2e/6e/008136a30798bb63618a114b9321b5971172a5abddff44a100c7edc5ad4f/frozenlist-1.5.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:87f724d055eb4785d9be84e9ebf0f24e392ddfad00b3fe036e43f489fafc9039", size = 271075 },
+ { url = "https://files.pythonhosted.org/packages/ae/f0/4e71e54a026b06724cec9b6c54f0b13a4e9e298cc8db0f82ec70e151f5ce/frozenlist-1.5.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:6e9080bb2fb195a046e5177f10d9d82b8a204c0736a97a153c2466127de87784", size = 285398 },
+ { url = "https://files.pythonhosted.org/packages/4d/36/70ec246851478b1c0b59f11ef8ade9c482ff447c1363c2bd5fad45098b12/frozenlist-1.5.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9b93d7aaa36c966fa42efcaf716e6b3900438632a626fb09c049f6a2f09fc631", size = 294445 },
+ { url = "https://files.pythonhosted.org/packages/37/e0/47f87544055b3349b633a03c4d94b405956cf2437f4ab46d0928b74b7526/frozenlist-1.5.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:52ef692a4bc60a6dd57f507429636c2af8b6046db8b31b18dac02cbc8f507f7f", size = 280569 },
+ { url = "https://files.pythonhosted.org/packages/f9/7c/490133c160fb6b84ed374c266f42800e33b50c3bbab1652764e6e1fc498a/frozenlist-1.5.0-cp312-cp312-win32.whl", hash = "sha256:29d94c256679247b33a3dc96cce0f93cbc69c23bf75ff715919332fdbb6a32b8", size = 44721 },
+ { url = "https://files.pythonhosted.org/packages/b1/56/4e45136ffc6bdbfa68c29ca56ef53783ef4c2fd395f7cbf99a2624aa9aaa/frozenlist-1.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:8969190d709e7c48ea386db202d708eb94bdb29207a1f269bab1196ce0dcca1f", size = 51329 },
+ { url = "https://files.pythonhosted.org/packages/da/3b/915f0bca8a7ea04483622e84a9bd90033bab54bdf485479556c74fd5eaf5/frozenlist-1.5.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:7a1a048f9215c90973402e26c01d1cff8a209e1f1b53f72b95c13db61b00f953", size = 91538 },
+ { url = "https://files.pythonhosted.org/packages/c7/d1/a7c98aad7e44afe5306a2b068434a5830f1470675f0e715abb86eb15f15b/frozenlist-1.5.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:dd47a5181ce5fcb463b5d9e17ecfdb02b678cca31280639255ce9d0e5aa67af0", size = 52849 },
+ { url = "https://files.pythonhosted.org/packages/3a/c8/76f23bf9ab15d5f760eb48701909645f686f9c64fbb8982674c241fbef14/frozenlist-1.5.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1431d60b36d15cda188ea222033eec8e0eab488f39a272461f2e6d9e1a8e63c2", size = 50583 },
+ { url = "https://files.pythonhosted.org/packages/1f/22/462a3dd093d11df623179d7754a3b3269de3b42de2808cddef50ee0f4f48/frozenlist-1.5.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6482a5851f5d72767fbd0e507e80737f9c8646ae7fd303def99bfe813f76cf7f", size = 265636 },
+ { url = "https://files.pythonhosted.org/packages/80/cf/e075e407fc2ae7328155a1cd7e22f932773c8073c1fc78016607d19cc3e5/frozenlist-1.5.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:44c49271a937625619e862baacbd037a7ef86dd1ee215afc298a417ff3270608", size = 270214 },
+ { url = "https://files.pythonhosted.org/packages/a1/58/0642d061d5de779f39c50cbb00df49682832923f3d2ebfb0fedf02d05f7f/frozenlist-1.5.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:12f78f98c2f1c2429d42e6a485f433722b0061d5c0b0139efa64f396efb5886b", size = 273905 },
+ { url = "https://files.pythonhosted.org/packages/ab/66/3fe0f5f8f2add5b4ab7aa4e199f767fd3b55da26e3ca4ce2cc36698e50c4/frozenlist-1.5.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ce3aa154c452d2467487765e3adc730a8c153af77ad84096bc19ce19a2400840", size = 250542 },
+ { url = "https://files.pythonhosted.org/packages/f6/b8/260791bde9198c87a465224e0e2bb62c4e716f5d198fc3a1dacc4895dbd1/frozenlist-1.5.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9b7dc0c4338e6b8b091e8faf0db3168a37101943e687f373dce00959583f7439", size = 267026 },
+ { url = "https://files.pythonhosted.org/packages/2e/a4/3d24f88c527f08f8d44ade24eaee83b2627793fa62fa07cbb7ff7a2f7d42/frozenlist-1.5.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:45e0896250900b5aa25180f9aec243e84e92ac84bd4a74d9ad4138ef3f5c97de", size = 257690 },
+ { url = "https://files.pythonhosted.org/packages/de/9a/d311d660420b2beeff3459b6626f2ab4fb236d07afbdac034a4371fe696e/frozenlist-1.5.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:561eb1c9579d495fddb6da8959fd2a1fca2c6d060d4113f5844b433fc02f2641", size = 253893 },
+ { url = "https://files.pythonhosted.org/packages/c6/23/e491aadc25b56eabd0f18c53bb19f3cdc6de30b2129ee0bc39cd387cd560/frozenlist-1.5.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:df6e2f325bfee1f49f81aaac97d2aa757c7646534a06f8f577ce184afe2f0a9e", size = 267006 },
+ { url = "https://files.pythonhosted.org/packages/08/c4/ab918ce636a35fb974d13d666dcbe03969592aeca6c3ab3835acff01f79c/frozenlist-1.5.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:140228863501b44b809fb39ec56b5d4071f4d0aa6d216c19cbb08b8c5a7eadb9", size = 276157 },
+ { url = "https://files.pythonhosted.org/packages/c0/29/3b7a0bbbbe5a34833ba26f686aabfe982924adbdcafdc294a7a129c31688/frozenlist-1.5.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7707a25d6a77f5d27ea7dc7d1fc608aa0a478193823f88511ef5e6b8a48f9d03", size = 264642 },
+ { url = "https://files.pythonhosted.org/packages/ab/42/0595b3dbffc2e82d7fe658c12d5a5bafcd7516c6bf2d1d1feb5387caa9c1/frozenlist-1.5.0-cp313-cp313-win32.whl", hash = "sha256:31a9ac2b38ab9b5a8933b693db4939764ad3f299fcaa931a3e605bc3460e693c", size = 44914 },
+ { url = "https://files.pythonhosted.org/packages/17/c4/b7db1206a3fea44bf3b838ca61deb6f74424a8a5db1dd53ecb21da669be6/frozenlist-1.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:11aabdd62b8b9c4b84081a3c246506d1cddd2dd93ff0ad53ede5defec7886b28", size = 51167 },
+ { url = "https://files.pythonhosted.org/packages/c6/c8/a5be5b7550c10858fcf9b0ea054baccab474da77d37f1e828ce043a3a5d4/frozenlist-1.5.0-py3-none-any.whl", hash = "sha256:d994863bba198a4a518b467bb971c56e1db3f180a25c6cf7bb1949c267f748c3", size = 11901 },
+]
+
+[[package]]
+name = "fsspec"
+version = "2024.12.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/ee/11/de70dee31455c546fbc88301971ec03c328f3d1138cfba14263f651e9551/fsspec-2024.12.0.tar.gz", hash = "sha256:670700c977ed2fb51e0d9f9253177ed20cbde4a3e5c0283cc5385b5870c8533f", size = 291600 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/de/86/5486b0188d08aa643e127774a99bac51ffa6cf343e3deb0583956dca5b22/fsspec-2024.12.0-py3-none-any.whl", hash = "sha256:b520aed47ad9804237ff878b504267a3b0b441e97508bd6d2d8774e3db85cee2", size = 183862 },
+]
+
+[package.optional-dependencies]
+http = [
+ { name = "aiohttp" },
+]
+
+[[package]]
+name = "genson"
+version = "1.3.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/c5/cf/2303c8ad276dcf5ee2ad6cf69c4338fd86ef0f471a5207b069adf7a393cf/genson-1.3.0.tar.gz", hash = "sha256:e02db9ac2e3fd29e65b5286f7135762e2cd8a986537c075b06fc5f1517308e37", size = 34919 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/f8/5c/e226de133afd8bb267ec27eead9ae3d784b95b39a287ed404caab39a5f50/genson-1.3.0-py3-none-any.whl", hash = "sha256:468feccd00274cc7e4c09e84b08704270ba8d95232aa280f65b986139cec67f7", size = 21470 },
+]
+
+[[package]]
+name = "ghapi"
+version = "1.0.6"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "fastcore" },
+ { name = "packaging" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/f8/88/97e6b0c94885db3530d04ccab7016c606dcaf08bf0581ced1193b9668d06/ghapi-1.0.6.tar.gz", hash = "sha256:64fdd9f06d8e3373065c42c2a03e067e2bbb9ca18b583cd6e38a28aaad0224f6", size = 65518 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/4c/ad/f7204c0c38175f300621af7880737ca6379dd633e9b7d1c0a8fc2748f0dc/ghapi-1.0.6-py3-none-any.whl", hash = "sha256:b3d96bf18fcaa2cb7131bad9de2948e2a1c2bb226377a25826f6c80950c57854", size = 62391 },
+]
+
+[[package]]
+name = "gitdb"
+version = "4.0.12"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "smmap" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/72/94/63b0fc47eb32792c7ba1fe1b694daec9a63620db1e313033d18140c2320a/gitdb-4.0.12.tar.gz", hash = "sha256:5ef71f855d191a3326fcfbc0d5da835f26b13fbcba60c32c21091c349ffdb571", size = 394684 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl", hash = "sha256:67073e15955400952c6565cc3e707c554a4eea2e428946f7a4c162fab9bd9bcf", size = 62794 },
+]
+
+[[package]]
+name = "gitpython"
+version = "3.1.44"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "gitdb" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/c0/89/37df0b71473153574a5cdef8f242de422a0f5d26d7a9e231e6f169b4ad14/gitpython-3.1.44.tar.gz", hash = "sha256:c87e30b26253bf5418b01b0660f818967f3c503193838337fe5e573331249269", size = 214196 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/1d/9a/4114a9057db2f1462d5c8f8390ab7383925fe1ac012eaa42402ad65c2963/GitPython-3.1.44-py3-none-any.whl", hash = "sha256:9e0e10cda9bed1ee64bc9a6de50e7e38a9c9943241cd7f585f6df3ed28011110", size = 207599 },
+]
+
+[[package]]
+name = "giturlparse"
+version = "0.12.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/37/5f/543dc54c82842376139748226e5aa61eb95093992f63dd495af9c6b4f076/giturlparse-0.12.0.tar.gz", hash = "sha256:c0fff7c21acc435491b1779566e038757a205c1ffdcb47e4f81ea52ad8c3859a", size = 14907 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/dd/94/c6ff3388b8e3225a014e55aed957188639aa0966443e0408d38f0c9614a7/giturlparse-0.12.0-py2.py3-none-any.whl", hash = "sha256:412b74f2855f1da2fefa89fd8dde62df48476077a72fc19b62039554d27360eb", size = 15752 },
+]
+
+[[package]]
+name = "greenlet"
+version = "3.1.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/2f/ff/df5fede753cc10f6a5be0931204ea30c35fa2f2ea7a35b25bdaf4fe40e46/greenlet-3.1.1.tar.gz", hash = "sha256:4ce3ac6cdb6adf7946475d7ef31777c26d94bccc377e070a7986bd2d5c515467", size = 186022 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/7d/ec/bad1ac26764d26aa1353216fcbfa4670050f66d445448aafa227f8b16e80/greenlet-3.1.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:4afe7ea89de619adc868e087b4d2359282058479d7cfb94970adf4b55284574d", size = 274260 },
+ { url = "https://files.pythonhosted.org/packages/66/d4/c8c04958870f482459ab5956c2942c4ec35cac7fe245527f1039837c17a9/greenlet-3.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f406b22b7c9a9b4f8aa9d2ab13d6ae0ac3e85c9a809bd590ad53fed2bf70dc79", size = 649064 },
+ { url = "https://files.pythonhosted.org/packages/51/41/467b12a8c7c1303d20abcca145db2be4e6cd50a951fa30af48b6ec607581/greenlet-3.1.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c3a701fe5a9695b238503ce5bbe8218e03c3bcccf7e204e455e7462d770268aa", size = 663420 },
+ { url = "https://files.pythonhosted.org/packages/27/8f/2a93cd9b1e7107d5c7b3b7816eeadcac2ebcaf6d6513df9abaf0334777f6/greenlet-3.1.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2846930c65b47d70b9d178e89c7e1a69c95c1f68ea5aa0a58646b7a96df12441", size = 658035 },
+ { url = "https://files.pythonhosted.org/packages/57/5c/7c6f50cb12be092e1dccb2599be5a942c3416dbcfb76efcf54b3f8be4d8d/greenlet-3.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99cfaa2110534e2cf3ba31a7abcac9d328d1d9f1b95beede58294a60348fba36", size = 660105 },
+ { url = "https://files.pythonhosted.org/packages/f1/66/033e58a50fd9ec9df00a8671c74f1f3a320564c6415a4ed82a1c651654ba/greenlet-3.1.1-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1443279c19fca463fc33e65ef2a935a5b09bb90f978beab37729e1c3c6c25fe9", size = 613077 },
+ { url = "https://files.pythonhosted.org/packages/19/c5/36384a06f748044d06bdd8776e231fadf92fc896bd12cb1c9f5a1bda9578/greenlet-3.1.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b7cede291382a78f7bb5f04a529cb18e068dd29e0fb27376074b6d0317bf4dd0", size = 1135975 },
+ { url = "https://files.pythonhosted.org/packages/38/f9/c0a0eb61bdf808d23266ecf1d63309f0e1471f284300ce6dac0ae1231881/greenlet-3.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:23f20bb60ae298d7d8656c6ec6db134bca379ecefadb0b19ce6f19d1f232a942", size = 1163955 },
+ { url = "https://files.pythonhosted.org/packages/43/21/a5d9df1d21514883333fc86584c07c2b49ba7c602e670b174bd73cfc9c7f/greenlet-3.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:7124e16b4c55d417577c2077be379514321916d5790fa287c9ed6f23bd2ffd01", size = 299655 },
+ { url = "https://files.pythonhosted.org/packages/f3/57/0db4940cd7bb461365ca8d6fd53e68254c9dbbcc2b452e69d0d41f10a85e/greenlet-3.1.1-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:05175c27cb459dcfc05d026c4232f9de8913ed006d42713cb8a5137bd49375f1", size = 272990 },
+ { url = "https://files.pythonhosted.org/packages/1c/ec/423d113c9f74e5e402e175b157203e9102feeb7088cee844d735b28ef963/greenlet-3.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:935e943ec47c4afab8965954bf49bfa639c05d4ccf9ef6e924188f762145c0ff", size = 649175 },
+ { url = "https://files.pythonhosted.org/packages/a9/46/ddbd2db9ff209186b7b7c621d1432e2f21714adc988703dbdd0e65155c77/greenlet-3.1.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:667a9706c970cb552ede35aee17339a18e8f2a87a51fba2ed39ceeeb1004798a", size = 663425 },
+ { url = "https://files.pythonhosted.org/packages/bc/f9/9c82d6b2b04aa37e38e74f0c429aece5eeb02bab6e3b98e7db89b23d94c6/greenlet-3.1.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b8a678974d1f3aa55f6cc34dc480169d58f2e6d8958895d68845fa4ab566509e", size = 657736 },
+ { url = "https://files.pythonhosted.org/packages/d9/42/b87bc2a81e3a62c3de2b0d550bf91a86939442b7ff85abb94eec3fc0e6aa/greenlet-3.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efc0f674aa41b92da8c49e0346318c6075d734994c3c4e4430b1c3f853e498e4", size = 660347 },
+ { url = "https://files.pythonhosted.org/packages/37/fa/71599c3fd06336cdc3eac52e6871cfebab4d9d70674a9a9e7a482c318e99/greenlet-3.1.1-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0153404a4bb921f0ff1abeb5ce8a5131da56b953eda6e14b88dc6bbc04d2049e", size = 615583 },
+ { url = "https://files.pythonhosted.org/packages/4e/96/e9ef85de031703ee7a4483489b40cf307f93c1824a02e903106f2ea315fe/greenlet-3.1.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:275f72decf9932639c1c6dd1013a1bc266438eb32710016a1c742df5da6e60a1", size = 1133039 },
+ { url = "https://files.pythonhosted.org/packages/87/76/b2b6362accd69f2d1889db61a18c94bc743e961e3cab344c2effaa4b4a25/greenlet-3.1.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:c4aab7f6381f38a4b42f269057aee279ab0fc7bf2e929e3d4abfae97b682a12c", size = 1160716 },
+ { url = "https://files.pythonhosted.org/packages/1f/1b/54336d876186920e185066d8c3024ad55f21d7cc3683c856127ddb7b13ce/greenlet-3.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:b42703b1cf69f2aa1df7d1030b9d77d3e584a70755674d60e710f0af570f3761", size = 299490 },
+ { url = "https://files.pythonhosted.org/packages/5f/17/bea55bf36990e1638a2af5ba10c1640273ef20f627962cf97107f1e5d637/greenlet-3.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1695e76146579f8c06c1509c7ce4dfe0706f49c6831a817ac04eebb2fd02011", size = 643731 },
+ { url = "https://files.pythonhosted.org/packages/78/d2/aa3d2157f9ab742a08e0fd8f77d4699f37c22adfbfeb0c610a186b5f75e0/greenlet-3.1.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7876452af029456b3f3549b696bb36a06db7c90747740c5302f74a9e9fa14b13", size = 649304 },
+ { url = "https://files.pythonhosted.org/packages/f1/8e/d0aeffe69e53ccff5a28fa86f07ad1d2d2d6537a9506229431a2a02e2f15/greenlet-3.1.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4ead44c85f8ab905852d3de8d86f6f8baf77109f9da589cb4fa142bd3b57b475", size = 646537 },
+ { url = "https://files.pythonhosted.org/packages/05/79/e15408220bbb989469c8871062c97c6c9136770657ba779711b90870d867/greenlet-3.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8320f64b777d00dd7ccdade271eaf0cad6636343293a25074cc5566160e4de7b", size = 642506 },
+ { url = "https://files.pythonhosted.org/packages/18/87/470e01a940307796f1d25f8167b551a968540fbe0551c0ebb853cb527dd6/greenlet-3.1.1-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6510bf84a6b643dabba74d3049ead221257603a253d0a9873f55f6a59a65f822", size = 602753 },
+ { url = "https://files.pythonhosted.org/packages/e2/72/576815ba674eddc3c25028238f74d7b8068902b3968cbe456771b166455e/greenlet-3.1.1-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:04b013dc07c96f83134b1e99888e7a79979f1a247e2a9f59697fa14b5862ed01", size = 1122731 },
+ { url = "https://files.pythonhosted.org/packages/ac/38/08cc303ddddc4b3d7c628c3039a61a3aae36c241ed01393d00c2fd663473/greenlet-3.1.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:411f015496fec93c1c8cd4e5238da364e1da7a124bcb293f085bf2860c32c6f6", size = 1142112 },
+]
+
+[[package]]
+name = "grpclib"
+version = "0.4.7"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "h2" },
+ { name = "multidict" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/79/b9/55936e462a5925190d7427e880b3033601d1effd13809b483d13a926061a/grpclib-0.4.7.tar.gz", hash = "sha256:2988ef57c02b22b7a2e8e961792c41ccf97efc2ace91ae7a5b0de03c363823c3", size = 61254 }
+
+[[package]]
+name = "h11"
+version = "0.14.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/f5/38/3af3d3633a34a3316095b39c8e8fb4853a28a536e55d347bd8d8e9a14b03/h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d", size = 100418 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/95/04/ff642e65ad6b90db43e668d70ffb6736436c7ce41fcc549f4e9472234127/h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761", size = 58259 },
+]
+
+[[package]]
+name = "h2"
+version = "4.2.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "hpack" },
+ { name = "hyperframe" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/1b/38/d7f80fd13e6582fb8e0df8c9a653dcc02b03ca34f4d72f34869298c5baf8/h2-4.2.0.tar.gz", hash = "sha256:c8a52129695e88b1a0578d8d2cc6842bbd79128ac685463b887ee278126ad01f", size = 2150682 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/d0/9e/984486f2d0a0bd2b024bf4bc1c62688fcafa9e61991f041fb0e2def4a982/h2-4.2.0-py3-none-any.whl", hash = "sha256:479a53ad425bb29af087f3458a61d30780bc818e4ebcf01f0b536ba916462ed0", size = 60957 },
+]
+
+[[package]]
+name = "hatch-vcs"
+version = "0.4.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "hatchling" },
+ { name = "setuptools-scm" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/f5/c9/54bb4fa27b4e4a014ef3bb17710cdf692b3aa2cbc7953da885f1bf7e06ea/hatch_vcs-0.4.0.tar.gz", hash = "sha256:093810748fe01db0d451fabcf2c1ac2688caefd232d4ede967090b1c1b07d9f7", size = 10917 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/82/0f/6cbd9976160bc334add63bc2e7a58b1433a31b34b7cda6c5de6dd983d9a7/hatch_vcs-0.4.0-py3-none-any.whl", hash = "sha256:b8a2b6bee54cf6f9fc93762db73890017ae59c9081d1038a41f16235ceaf8b2c", size = 8412 },
+]
+
+[[package]]
+name = "hatchling"
+version = "1.27.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "packaging" },
+ { name = "pathspec" },
+ { name = "pluggy" },
+ { name = "trove-classifiers" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/8f/8a/cc1debe3514da292094f1c3a700e4ca25442489731ef7c0814358816bb03/hatchling-1.27.0.tar.gz", hash = "sha256:971c296d9819abb3811112fc52c7a9751c8d381898f36533bb16f9791e941fd6", size = 54983 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/08/e7/ae38d7a6dfba0533684e0b2136817d667588ae3ec984c1a4e5df5eb88482/hatchling-1.27.0-py3-none-any.whl", hash = "sha256:d3a2f3567c4f926ea39849cdf924c7e99e6686c9c8e288ae1037c8fa2a5d937b", size = 75794 },
+]
+
+[[package]]
+name = "hpack"
+version = "4.1.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/2c/48/71de9ed269fdae9c8057e5a4c0aa7402e8bb16f2c6e90b3aa53327b113f8/hpack-4.1.0.tar.gz", hash = "sha256:ec5eca154f7056aa06f196a557655c5b009b382873ac8d1e66e79e87535f1dca", size = 51276 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/07/c6/80c95b1b2b94682a72cbdbfb85b81ae2daffa4291fbfa1b1464502ede10d/hpack-4.1.0-py3-none-any.whl", hash = "sha256:157ac792668d995c657d93111f46b4535ed114f0c9c8d672271bbec7eae1b496", size = 34357 },
+]
+
+[[package]]
+name = "httpcore"
+version = "1.0.7"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "certifi" },
+ { name = "h11" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/6a/41/d7d0a89eb493922c37d343b607bc1b5da7f5be7e383740b4753ad8943e90/httpcore-1.0.7.tar.gz", hash = "sha256:8551cb62a169ec7162ac7be8d4817d561f60e08eaa485234898414bb5a8a0b4c", size = 85196 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/87/f5/72347bc88306acb359581ac4d52f23c0ef445b57157adedb9aee0cd689d2/httpcore-1.0.7-py3-none-any.whl", hash = "sha256:a3fff8f43dc260d5bd363d9f9cf1830fa3a458b332856f34282de498ed420edd", size = 78551 },
+]
+
+[[package]]
+name = "httptools"
+version = "0.6.4"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/a7/9a/ce5e1f7e131522e6d3426e8e7a490b3a01f39a6696602e1c4f33f9e94277/httptools-0.6.4.tar.gz", hash = "sha256:4e93eee4add6493b59a5c514da98c939b244fce4a0d8879cd3f466562f4b7d5c", size = 240639 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/bb/0e/d0b71465c66b9185f90a091ab36389a7352985fe857e352801c39d6127c8/httptools-0.6.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:df017d6c780287d5c80601dafa31f17bddb170232d85c066604d8558683711a2", size = 200683 },
+ { url = "https://files.pythonhosted.org/packages/e2/b8/412a9bb28d0a8988de3296e01efa0bd62068b33856cdda47fe1b5e890954/httptools-0.6.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:85071a1e8c2d051b507161f6c3e26155b5c790e4e28d7f236422dbacc2a9cc44", size = 104337 },
+ { url = "https://files.pythonhosted.org/packages/9b/01/6fb20be3196ffdc8eeec4e653bc2a275eca7f36634c86302242c4fbb2760/httptools-0.6.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69422b7f458c5af875922cdb5bd586cc1f1033295aa9ff63ee196a87519ac8e1", size = 508796 },
+ { url = "https://files.pythonhosted.org/packages/f7/d8/b644c44acc1368938317d76ac991c9bba1166311880bcc0ac297cb9d6bd7/httptools-0.6.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:16e603a3bff50db08cd578d54f07032ca1631450ceb972c2f834c2b860c28ea2", size = 510837 },
+ { url = "https://files.pythonhosted.org/packages/52/d8/254d16a31d543073a0e57f1c329ca7378d8924e7e292eda72d0064987486/httptools-0.6.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ec4f178901fa1834d4a060320d2f3abc5c9e39766953d038f1458cb885f47e81", size = 485289 },
+ { url = "https://files.pythonhosted.org/packages/5f/3c/4aee161b4b7a971660b8be71a92c24d6c64372c1ab3ae7f366b3680df20f/httptools-0.6.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f9eb89ecf8b290f2e293325c646a211ff1c2493222798bb80a530c5e7502494f", size = 489779 },
+ { url = "https://files.pythonhosted.org/packages/12/b7/5cae71a8868e555f3f67a50ee7f673ce36eac970f029c0c5e9d584352961/httptools-0.6.4-cp312-cp312-win_amd64.whl", hash = "sha256:db78cb9ca56b59b016e64b6031eda5653be0589dba2b1b43453f6e8b405a0970", size = 88634 },
+ { url = "https://files.pythonhosted.org/packages/94/a3/9fe9ad23fd35f7de6b91eeb60848986058bd8b5a5c1e256f5860a160cc3e/httptools-0.6.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ade273d7e767d5fae13fa637f4d53b6e961fb7fd93c7797562663f0171c26660", size = 197214 },
+ { url = "https://files.pythonhosted.org/packages/ea/d9/82d5e68bab783b632023f2fa31db20bebb4e89dfc4d2293945fd68484ee4/httptools-0.6.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:856f4bc0478ae143bad54a4242fccb1f3f86a6e1be5548fecfd4102061b3a083", size = 102431 },
+ { url = "https://files.pythonhosted.org/packages/96/c1/cb499655cbdbfb57b577734fde02f6fa0bbc3fe9fb4d87b742b512908dff/httptools-0.6.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:322d20ea9cdd1fa98bd6a74b77e2ec5b818abdc3d36695ab402a0de8ef2865a3", size = 473121 },
+ { url = "https://files.pythonhosted.org/packages/af/71/ee32fd358f8a3bb199b03261f10921716990808a675d8160b5383487a317/httptools-0.6.4-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4d87b29bd4486c0093fc64dea80231f7c7f7eb4dc70ae394d70a495ab8436071", size = 473805 },
+ { url = "https://files.pythonhosted.org/packages/8a/0a/0d4df132bfca1507114198b766f1737d57580c9ad1cf93c1ff673e3387be/httptools-0.6.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:342dd6946aa6bda4b8f18c734576106b8a31f2fe31492881a9a160ec84ff4bd5", size = 448858 },
+ { url = "https://files.pythonhosted.org/packages/1e/6a/787004fdef2cabea27bad1073bf6a33f2437b4dbd3b6fb4a9d71172b1c7c/httptools-0.6.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4b36913ba52008249223042dca46e69967985fb4051951f94357ea681e1f5dc0", size = 452042 },
+ { url = "https://files.pythonhosted.org/packages/4d/dc/7decab5c404d1d2cdc1bb330b1bf70e83d6af0396fd4fc76fc60c0d522bf/httptools-0.6.4-cp313-cp313-win_amd64.whl", hash = "sha256:28908df1b9bb8187393d5b5db91435ccc9c8e891657f9cbb42a2541b44c82fc8", size = 87682 },
+]
+
+[[package]]
+name = "httpx"
+version = "0.28.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "anyio" },
+ { name = "certifi" },
+ { name = "httpcore" },
+ { name = "idna" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517 },
+]
+
+[[package]]
+name = "httpx-sse"
+version = "0.4.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/4c/60/8f4281fa9bbf3c8034fd54c0e7412e66edbab6bc74c4996bd616f8d0406e/httpx-sse-0.4.0.tar.gz", hash = "sha256:1e81a3a3070ce322add1d3529ed42eb5f70817f45ed6ec915ab753f961139721", size = 12624 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/e1/9b/a181f281f65d776426002f330c31849b86b31fc9d848db62e16f03ff739f/httpx_sse-0.4.0-py3-none-any.whl", hash = "sha256:f329af6eae57eaa2bdfd962b42524764af68075ea87370a2de920af5341e318f", size = 7819 },
+]
+
+[[package]]
+name = "huggingface-hub"
+version = "0.29.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "filelock" },
+ { name = "fsspec" },
+ { name = "packaging" },
+ { name = "pyyaml" },
+ { name = "requests" },
+ { name = "tqdm" },
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/22/37/797d6476f13e5ef6af5fc48a5d641d32b39c37e166ccf40c3714c5854a85/huggingface_hub-0.29.1.tar.gz", hash = "sha256:9524eae42077b8ff4fc459ceb7a514eca1c1232b775276b009709fe2a084f250", size = 389776 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/ae/05/75b90de9093de0aadafc868bb2fa7c57651fd8f45384adf39bd77f63980d/huggingface_hub-0.29.1-py3-none-any.whl", hash = "sha256:352f69caf16566c7b6de84b54a822f6238e17ddd8ae3da4f8f2272aea5b198d5", size = 468049 },
+]
+
+[[package]]
+name = "humanize"
+version = "4.12.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/5b/8c/4f2f0784d08a383b5de3d3b1d65a6f204cc5dc487621c91c550388d756af/humanize-4.12.1.tar.gz", hash = "sha256:1338ba97415c96556758a6e2f65977ed406dddf4620d4c6db9bbdfd07f0f1232", size = 80827 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/32/30/5ef5994b090398f9284d2662f56853e5183ae2cb5d8e3db67e4f4cfea407/humanize-4.12.1-py3-none-any.whl", hash = "sha256:86014ca5c52675dffa1d404491952f1f5bf03b07c175a51891a343daebf01fea", size = 127409 },
+]
+
+[[package]]
+name = "hyperframe"
+version = "6.1.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/02/e7/94f8232d4a74cc99514c13a9f995811485a6903d48e5d952771ef6322e30/hyperframe-6.1.0.tar.gz", hash = "sha256:f630908a00854a7adeabd6382b43923a4c4cd4b821fcb527e6ab9e15382a3b08", size = 26566 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/48/30/47d0bf6072f7252e6521f3447ccfa40b421b6824517f82854703d0f5a98b/hyperframe-6.1.0-py3-none-any.whl", hash = "sha256:b03380493a519fce58ea5af42e4a42317bf9bd425596f7a0835ffce80f1a42e5", size = 13007 },
+]
+
+[[package]]
+name = "identify"
+version = "2.6.8"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/f9/fa/5eb460539e6f5252a7c5a931b53426e49258cde17e3d50685031c300a8fd/identify-2.6.8.tar.gz", hash = "sha256:61491417ea2c0c5c670484fd8abbb34de34cdae1e5f39a73ee65e48e4bb663fc", size = 99249 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/78/8c/4bfcab2d8286473b8d83ea742716f4b79290172e75f91142bc1534b05b9a/identify-2.6.8-py2.py3-none-any.whl", hash = "sha256:83657f0f766a3c8d0eaea16d4ef42494b39b34629a4b3192a9d020d349b3e255", size = 99109 },
+]
+
+[[package]]
+name = "idna"
+version = "3.10"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 },
+]
+
+[[package]]
+name = "imagesize"
+version = "1.4.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/a7/84/62473fb57d61e31fef6e36d64a179c8781605429fd927b5dd608c997be31/imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a", size = 1280026 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/ff/62/85c4c919272577931d407be5ba5d71c20f0b616d31a0befe0ae45bb79abd/imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b", size = 8769 },
+]
+
+[[package]]
+name = "importlib-resources"
+version = "6.5.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/cf/8c/f834fbf984f691b4f7ff60f50b514cc3de5cc08abfc3295564dd89c5e2e7/importlib_resources-6.5.2.tar.gz", hash = "sha256:185f87adef5bcc288449d98fb4fba07cea78bc036455dd44c5fc4a2fe78fed2c", size = 44693 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/a4/ed/1f1afb2e9e7f38a545d628f864d562a5ae64fe6f7a10e28ffb9b185b4e89/importlib_resources-6.5.2-py3-none-any.whl", hash = "sha256:789cfdc3ed28c78b67a06acb8126751ced69a3d5f79c095a98298cd8a760ccec", size = 37461 },
+]
+
+[[package]]
+name = "inflect"
+version = "5.6.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/cb/db/cae5d8524c4b5e574c281895b212062f3b06d0e14186904ed71c538b4e90/inflect-5.6.2.tar.gz", hash = "sha256:aadc7ed73928f5e014129794bbac03058cca35d0a973a5fc4eb45c7fa26005f9", size = 69378 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/df/d8/3e1a32d305215166f5c32652c473aa766bd7809cd10b34c544dbc31facb5/inflect-5.6.2-py3-none-any.whl", hash = "sha256:b45d91a4a28a4e617ff1821117439b06eaa86e2a4573154af0149e9be6687238", size = 33704 },
+]
+
+[[package]]
+name = "iniconfig"
+version = "2.0.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", size = 4646 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892 },
+]
+
+[[package]]
+name = "isort"
+version = "6.0.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/b8/21/1e2a441f74a653a144224d7d21afe8f4169e6c7c20bb13aec3a2dc3815e0/isort-6.0.1.tar.gz", hash = "sha256:1cb5df28dfbc742e490c5e41bad6da41b805b0a8be7bc93cd0fb2a8a890ac450", size = 821955 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c1/11/114d0a5f4dabbdcedc1125dee0888514c3c3b16d3e9facad87ed96fad97c/isort-6.0.1-py3-none-any.whl", hash = "sha256:2dc5d7f65c9678d94c88dfc29161a320eec67328bc97aad576874cb4be1e9615", size = 94186 },
+]
+
+[[package]]
+name = "jinja2"
+version = "3.1.5"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "markupsafe" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/af/92/b3130cbbf5591acf9ade8708c365f3238046ac7cb8ccba6e81abccb0ccff/jinja2-3.1.5.tar.gz", hash = "sha256:8fefff8dc3034e27bb80d67c671eb8a9bc424c0ef4c0826edbff304cceff43bb", size = 244674 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/bd/0f/2ba5fbcd631e3e88689309dbe978c5769e883e4b84ebfe7da30b43275c5a/jinja2-3.1.5-py3-none-any.whl", hash = "sha256:aba0f4dc9ed8013c424088f68a5c226f7d6097ed89b246d7749c2ec4175c6adb", size = 134596 },
+]
+
+[[package]]
+name = "jiter"
+version = "0.8.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/f8/70/90bc7bd3932e651486861df5c8ffea4ca7c77d28e8532ddefe2abc561a53/jiter-0.8.2.tar.gz", hash = "sha256:cd73d3e740666d0e639f678adb176fad25c1bcbdae88d8d7b857e1783bb4212d", size = 163007 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/a1/17/c8747af8ea4e045f57d6cfd6fc180752cab9bc3de0e8a0c9ca4e8af333b1/jiter-0.8.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:e6ec2be506e7d6f9527dae9ff4b7f54e68ea44a0ef6b098256ddf895218a2f8f", size = 302027 },
+ { url = "https://files.pythonhosted.org/packages/3c/c1/6da849640cd35a41e91085723b76acc818d4b7d92b0b6e5111736ce1dd10/jiter-0.8.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:76e324da7b5da060287c54f2fabd3db5f76468006c811831f051942bf68c9d44", size = 310326 },
+ { url = "https://files.pythonhosted.org/packages/06/99/a2bf660d8ccffee9ad7ed46b4f860d2108a148d0ea36043fd16f4dc37e94/jiter-0.8.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:180a8aea058f7535d1c84183c0362c710f4750bef66630c05f40c93c2b152a0f", size = 334242 },
+ { url = "https://files.pythonhosted.org/packages/a7/5f/cea1c17864828731f11427b9d1ab7f24764dbd9aaf4648a7f851164d2718/jiter-0.8.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:025337859077b41548bdcbabe38698bcd93cfe10b06ff66617a48ff92c9aec60", size = 356654 },
+ { url = "https://files.pythonhosted.org/packages/e9/13/62774b7e5e7f5d5043efe1d0f94ead66e6d0f894ae010adb56b3f788de71/jiter-0.8.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ecff0dc14f409599bbcafa7e470c00b80f17abc14d1405d38ab02e4b42e55b57", size = 379967 },
+ { url = "https://files.pythonhosted.org/packages/ec/fb/096b34c553bb0bd3f2289d5013dcad6074948b8d55212aa13a10d44c5326/jiter-0.8.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ffd9fee7d0775ebaba131f7ca2e2d83839a62ad65e8e02fe2bd8fc975cedeb9e", size = 389252 },
+ { url = "https://files.pythonhosted.org/packages/17/61/beea645c0bf398ced8b199e377b61eb999d8e46e053bb285c91c3d3eaab0/jiter-0.8.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14601dcac4889e0a1c75ccf6a0e4baf70dbc75041e51bcf8d0e9274519df6887", size = 345490 },
+ { url = "https://files.pythonhosted.org/packages/d5/df/834aa17ad5dcc3cf0118821da0a0cf1589ea7db9832589278553640366bc/jiter-0.8.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:92249669925bc1c54fcd2ec73f70f2c1d6a817928480ee1c65af5f6b81cdf12d", size = 376991 },
+ { url = "https://files.pythonhosted.org/packages/67/80/87d140399d382fb4ea5b3d56e7ecaa4efdca17cd7411ff904c1517855314/jiter-0.8.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e725edd0929fa79f8349ab4ec7f81c714df51dc4e991539a578e5018fa4a7152", size = 510822 },
+ { url = "https://files.pythonhosted.org/packages/5c/37/3394bb47bac1ad2cb0465601f86828a0518d07828a650722e55268cdb7e6/jiter-0.8.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:bf55846c7b7a680eebaf9c3c48d630e1bf51bdf76c68a5f654b8524335b0ad29", size = 503730 },
+ { url = "https://files.pythonhosted.org/packages/f9/e2/253fc1fa59103bb4e3aa0665d6ceb1818df1cd7bf3eb492c4dad229b1cd4/jiter-0.8.2-cp312-cp312-win32.whl", hash = "sha256:7efe4853ecd3d6110301665a5178b9856be7e2a9485f49d91aa4d737ad2ae49e", size = 203375 },
+ { url = "https://files.pythonhosted.org/packages/41/69/6d4bbe66b3b3b4507e47aa1dd5d075919ad242b4b1115b3f80eecd443687/jiter-0.8.2-cp312-cp312-win_amd64.whl", hash = "sha256:83c0efd80b29695058d0fd2fa8a556490dbce9804eac3e281f373bbc99045f6c", size = 204740 },
+ { url = "https://files.pythonhosted.org/packages/6c/b0/bfa1f6f2c956b948802ef5a021281978bf53b7a6ca54bb126fd88a5d014e/jiter-0.8.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:ca1f08b8e43dc3bd0594c992fb1fd2f7ce87f7bf0d44358198d6da8034afdf84", size = 301190 },
+ { url = "https://files.pythonhosted.org/packages/a4/8f/396ddb4e292b5ea57e45ade5dc48229556b9044bad29a3b4b2dddeaedd52/jiter-0.8.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5672a86d55416ccd214c778efccf3266b84f87b89063b582167d803246354be4", size = 309334 },
+ { url = "https://files.pythonhosted.org/packages/7f/68/805978f2f446fa6362ba0cc2e4489b945695940656edd844e110a61c98f8/jiter-0.8.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:58dc9bc9767a1101f4e5e22db1b652161a225874d66f0e5cb8e2c7d1c438b587", size = 333918 },
+ { url = "https://files.pythonhosted.org/packages/b3/99/0f71f7be667c33403fa9706e5b50583ae5106d96fab997fa7e2f38ee8347/jiter-0.8.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:37b2998606d6dadbb5ccda959a33d6a5e853252d921fec1792fc902351bb4e2c", size = 356057 },
+ { url = "https://files.pythonhosted.org/packages/8d/50/a82796e421a22b699ee4d2ce527e5bcb29471a2351cbdc931819d941a167/jiter-0.8.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4ab9a87f3784eb0e098f84a32670cfe4a79cb6512fd8f42ae3d0709f06405d18", size = 379790 },
+ { url = "https://files.pythonhosted.org/packages/3c/31/10fb012b00f6d83342ca9e2c9618869ab449f1aa78c8f1b2193a6b49647c/jiter-0.8.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:79aec8172b9e3c6d05fd4b219d5de1ac616bd8da934107325a6c0d0e866a21b6", size = 388285 },
+ { url = "https://files.pythonhosted.org/packages/c8/81/f15ebf7de57be488aa22944bf4274962aca8092e4f7817f92ffa50d3ee46/jiter-0.8.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:711e408732d4e9a0208008e5892c2966b485c783cd2d9a681f3eb147cf36c7ef", size = 344764 },
+ { url = "https://files.pythonhosted.org/packages/b3/e8/0cae550d72b48829ba653eb348cdc25f3f06f8a62363723702ec18e7be9c/jiter-0.8.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:653cf462db4e8c41995e33d865965e79641ef45369d8a11f54cd30888b7e6ff1", size = 376620 },
+ { url = "https://files.pythonhosted.org/packages/b8/50/e5478ff9d82534a944c03b63bc217c5f37019d4a34d288db0f079b13c10b/jiter-0.8.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:9c63eaef32b7bebac8ebebf4dabebdbc6769a09c127294db6babee38e9f405b9", size = 510402 },
+ { url = "https://files.pythonhosted.org/packages/8e/1e/3de48bbebbc8f7025bd454cedc8c62378c0e32dd483dece5f4a814a5cb55/jiter-0.8.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:eb21aaa9a200d0a80dacc7a81038d2e476ffe473ffdd9c91eb745d623561de05", size = 503018 },
+ { url = "https://files.pythonhosted.org/packages/d5/cd/d5a5501d72a11fe3e5fd65c78c884e5164eefe80077680533919be22d3a3/jiter-0.8.2-cp313-cp313-win32.whl", hash = "sha256:789361ed945d8d42850f919342a8665d2dc79e7e44ca1c97cc786966a21f627a", size = 203190 },
+ { url = "https://files.pythonhosted.org/packages/51/bf/e5ca301245ba951447e3ad677a02a64a8845b185de2603dabd83e1e4b9c6/jiter-0.8.2-cp313-cp313-win_amd64.whl", hash = "sha256:ab7f43235d71e03b941c1630f4b6e3055d46b6cb8728a17663eaac9d8e83a865", size = 203551 },
+ { url = "https://files.pythonhosted.org/packages/2f/3c/71a491952c37b87d127790dd7a0b1ebea0514c6b6ad30085b16bbe00aee6/jiter-0.8.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b426f72cd77da3fec300ed3bc990895e2dd6b49e3bfe6c438592a3ba660e41ca", size = 308347 },
+ { url = "https://files.pythonhosted.org/packages/a0/4c/c02408042e6a7605ec063daed138e07b982fdb98467deaaf1c90950cf2c6/jiter-0.8.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b2dd880785088ff2ad21ffee205e58a8c1ddabc63612444ae41e5e4b321b39c0", size = 342875 },
+ { url = "https://files.pythonhosted.org/packages/91/61/c80ef80ed8a0a21158e289ef70dac01e351d929a1c30cb0f49be60772547/jiter-0.8.2-cp313-cp313t-win_amd64.whl", hash = "sha256:3ac9f578c46f22405ff7f8b1f5848fb753cc4b8377fbec8470a7dc3997ca7566", size = 202374 },
+]
+
+[[package]]
+name = "jsonpatch"
+version = "1.33"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "jsonpointer" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/42/78/18813351fe5d63acad16aec57f94ec2b70a09e53ca98145589e185423873/jsonpatch-1.33.tar.gz", hash = "sha256:9fcd4009c41e6d12348b4a0ff2563ba56a2923a7dfee731d004e212e1ee5030c", size = 21699 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/73/07/02e16ed01e04a374e644b575638ec7987ae846d25ad97bcc9945a3ee4b0e/jsonpatch-1.33-py2.py3-none-any.whl", hash = "sha256:0ae28c0cd062bbd8b8ecc26d7d164fbbea9652a1a3693f3b956c1eae5145dade", size = 12898 },
+]
+
+[[package]]
+name = "jsonpointer"
+version = "3.0.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/6a/0a/eebeb1fa92507ea94016a2a790b93c2ae41a7e18778f85471dc54475ed25/jsonpointer-3.0.0.tar.gz", hash = "sha256:2b2d729f2091522d61c3b31f82e11870f60b68f43fbc705cb76bf4b832af59ef", size = 9114 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/71/92/5e77f98553e9e75130c78900d000368476aed74276eb8ae8796f65f00918/jsonpointer-3.0.0-py2.py3-none-any.whl", hash = "sha256:13e088adc14fca8b6aa8177c044e12701e6ad4b28ff10e65f2267a90109c9942", size = 7595 },
+]
+
+[[package]]
+name = "langchain"
+version = "0.3.20"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "langchain-core" },
+ { name = "langchain-text-splitters" },
+ { name = "langsmith" },
+ { name = "pydantic" },
+ { name = "pyyaml" },
+ { name = "requests" },
+ { name = "sqlalchemy" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/2a/b0/5121cdd19cf99e684043f4eae528c893f56bd25e7711d4de89f27832a5f3/langchain-0.3.20.tar.gz", hash = "sha256:edcc3241703e1f6557ef5a5c35cd56f9ccc25ff12e38b4829c66d94971737a93", size = 10225276 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/b5/d4/afe8174838bdd3baba5d6a19e9f3af4c54c5db1ab4d66ef0b650c6157919/langchain-0.3.20-py3-none-any.whl", hash = "sha256:273287f8e61ffdf7e811cf8799e6a71e9381325b8625fd6618900faba79cfdd0", size = 1011577 },
+]
+
+[package.optional-dependencies]
+openai = [
+ { name = "langchain-openai" },
+]
+
+[[package]]
+name = "langchain-anthropic"
+version = "0.3.9"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "anthropic" },
+ { name = "langchain-core" },
+ { name = "pydantic" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/be/0a/7ccb79c41575b04266fc4def50f41d0a4689361421d82a14350d9d5e783e/langchain_anthropic-0.3.9.tar.gz", hash = "sha256:e8012d7986ad1d8412df6914c56f3c0d2797f231766a03bb1ad22cc7023e6e1d", size = 42205 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/b9/27/258565b4a487fca7db363ea95765e6f1f00c23baa83dc4ec19a009213658/langchain_anthropic-0.3.9-py3-none-any.whl", hash = "sha256:adbbfaf3ce9798d46fb43d6fc01105630238f375dc6043d35d0aafab61fdbb71", size = 24414 },
+]
+
+[[package]]
+name = "langchain-core"
+version = "0.3.41"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "jsonpatch" },
+ { name = "langsmith" },
+ { name = "packaging" },
+ { name = "pydantic" },
+ { name = "pyyaml" },
+ { name = "tenacity" },
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/2b/0a/aa5167a1a46094024b8fe50917e37f1df5bcd0034adb25452e121dae60e6/langchain_core-0.3.41.tar.gz", hash = "sha256:d3ee9f3616ebbe7943470ade23d4a04e1729b1512c0ec55a4a07bd2ac64dedb4", size = 528826 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/bc/a6/551de93e02b1ef4ec031f6e1c0ff31a70790096c1e7066168a7693e4efe5/langchain_core-0.3.41-py3-none-any.whl", hash = "sha256:1a27cca5333bae7597de4004fb634b5f3e71667a3da6493b94ce83bcf15a23bd", size = 415149 },
+]
+
+[[package]]
+name = "langchain-openai"
+version = "0.3.7"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "langchain-core" },
+ { name = "openai" },
+ { name = "tiktoken" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/8e/3c/08add067e46409d3e881933155f546edb08644e5e4e2360ff22c6a2104a8/langchain_openai-0.3.7.tar.gz", hash = "sha256:b8b51a3aaa1cc3bda060651ea41145f7728219e8a7150b5404fb1e8446de9cef", size = 256488 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/36/0e/816c5293eda67600d374bb8484a9adab873c9096489f6f91634581919f35/langchain_openai-0.3.7-py3-none-any.whl", hash = "sha256:0aefc7bdf8e7398d41e09c4313cace816df6438f2aa93d34f79523487310f0da", size = 55254 },
+]
+
+[[package]]
+name = "langchain-text-splitters"
+version = "0.3.6"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "langchain-core" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/0d/33/89912a07c63e4e818f9b0c8d52e4f9d600c97beca8a91db8c9dae6a1b28f/langchain_text_splitters-0.3.6.tar.gz", hash = "sha256:c537972f4b7c07451df431353a538019ad9dadff7a1073ea363946cea97e1bee", size = 40545 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/4c/f8/6b82af988e65af9697f6a2f25373fb173fd32d48b62772a8773c5184c870/langchain_text_splitters-0.3.6-py3-none-any.whl", hash = "sha256:e5d7b850f6c14259ea930be4a964a65fa95d9df7e1dbdd8bad8416db72292f4e", size = 31197 },
+]
+
+[[package]]
+name = "langchain-xai"
+version = "0.2.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "aiohttp" },
+ { name = "langchain-core" },
+ { name = "langchain-openai" },
+ { name = "requests" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/a3/94/a633bf1b4bbf66e4516f4188adc1174480c465ae12fb98f06c3e23c98519/langchain_xai-0.2.1.tar.gz", hash = "sha256:143a6f52be7617b5e5c68ab10c9b7df90914f54a6b3098566ce22b5d8fd89da5", size = 7788 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/7b/88/d8050e610fadabf97c1745d24f0987b3e53b72fca63c8038ab1e0c103da9/langchain_xai-0.2.1-py3-none-any.whl", hash = "sha256:87228125cb15131663979d627210fca47dcd6b9a28462e8b5fee47f73bbed9f4", size = 6263 },
+]
+
+[[package]]
+name = "langgraph"
+version = "0.3.5"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "langchain-core" },
+ { name = "langgraph-checkpoint" },
+ { name = "langgraph-prebuilt" },
+ { name = "langgraph-sdk" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/4e/fa/b1ecc95a2464bc7dbe5e67fbd21096013829119899c33236090b98c75508/langgraph-0.3.5.tar.gz", hash = "sha256:7c0d8e61aa02578b41036c9f7a599ccba2562d269f66ef76bacbba47a99a7eca", size = 114020 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/a4/5f/1e1d9173b5c41eff54f88d9f4ee82c38eb4928120ab6a21a68a78d1c499e/langgraph-0.3.5-py3-none-any.whl", hash = "sha256:be313ec300633c857873ea3e44aece4dd7d0b11f131d385108b359d377a85bf7", size = 131527 },
+]
+
+[[package]]
+name = "langgraph-checkpoint"
+version = "2.0.16"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "langchain-core" },
+ { name = "msgpack" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/01/66/5d4a2013a84c511be289bb4a5ef91cbaad28c091b6b366fdb79710a1458b/langgraph_checkpoint-2.0.16.tar.gz", hash = "sha256:49ba8cfa12b2aae845ccc3b1fbd1d7a8d3a6c4a2e387ab3a92fca40dd3d4baa5", size = 34206 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/7c/63/03bc3dd304ead45b53313cab8727329e1d139a2d220f2d030c72242c860e/langgraph_checkpoint-2.0.16-py3-none-any.whl", hash = "sha256:dfab51076a6eddb5f9e146cfe1b977e3dd6419168b2afa23ff3f4e47973bf06f", size = 38291 },
+]
+
+[[package]]
+name = "langgraph-prebuilt"
+version = "0.1.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "langchain-core" },
+ { name = "langgraph-checkpoint" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/22/15/848593ccace12e4f8b80cc0b159b0ba1da17605e1eecbda5f37d891748a3/langgraph_prebuilt-0.1.1.tar.gz", hash = "sha256:420a748ff93842f2b1a345a0c1ca3939d2bc7a2d46c20e9a9a0d8f148152cc47", size = 23257 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/3c/62/a424fdb892f578fa88b2ff4df0bfdebdc8b89501dacb8ca3b480305cbfef/langgraph_prebuilt-0.1.1-py3-none-any.whl", hash = "sha256:148a9558a36ec7e83cc6512f3521425c862b0463251ae0242ade52a448c54e78", size = 24622 },
+]
+
+[[package]]
+name = "langgraph-sdk"
+version = "0.1.53"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "httpx" },
+ { name = "orjson" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/39/b2/a261cfbf91a4499396ba0993cf5601076301dd22883d3c0901e905253917/langgraph_sdk-0.1.53.tar.gz", hash = "sha256:12906ed965905fa27e0c28d9fa07dc6fd89e6895ff321ff049fdf3965d057cc4", size = 42369 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/fc/97/3492a07b454cc74bf49938e83f0a95c608a8bc5c3dda338091d3c66e3ec5/langgraph_sdk-0.1.53-py3-none-any.whl", hash = "sha256:4fab62caad73661ffe4c3ababedcd0d7bfaaba986bee4416b9c28948458a3af5", size = 45441 },
+]
+
+[[package]]
+name = "langsmith"
+version = "0.3.11"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "httpx" },
+ { name = "orjson", marker = "platform_python_implementation != 'PyPy'" },
+ { name = "packaging" },
+ { name = "pydantic" },
+ { name = "requests" },
+ { name = "requests-toolbelt" },
+ { name = "zstandard" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/ea/34/c4c0eddad03e00457cd6be1a88c288cd4419da8d368d8f519a29abe5392c/langsmith-0.3.11.tar.gz", hash = "sha256:ddf29d24352e99de79c9618aaf95679214324e146c5d3d9475a7ddd2870018b1", size = 323815 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/ff/68/514ffa62860202a5a0a3acbf5c05017ef9df38d4437d2cb44a3cf93d617b/langsmith-0.3.11-py3-none-any.whl", hash = "sha256:0cca22737ef07d3b038a437c141deda37e00add56022582680188b681bec095e", size = 335265 },
+]
+
+[[package]]
+name = "lazy-object-proxy"
+version = "1.10.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/2c/f0/f02e2d150d581a294efded4020094a371bbab42423fe78625ac18854d89b/lazy-object-proxy-1.10.0.tar.gz", hash = "sha256:78247b6d45f43a52ef35c25b5581459e85117225408a4128a3daf8bf9648ac69", size = 43271 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/d0/5d/768a7f2ccebb29604def61842fd54f6f5f75c79e366ee8748dda84de0b13/lazy_object_proxy-1.10.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e98c8af98d5707dcdecc9ab0863c0ea6e88545d42ca7c3feffb6b4d1e370c7ba", size = 27560 },
+ { url = "https://files.pythonhosted.org/packages/b3/ce/f369815549dbfa4bebed541fa4e1561d69e4f268a1f6f77da886df182dab/lazy_object_proxy-1.10.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:952c81d415b9b80ea261d2372d2a4a2332a3890c2b83e0535f263ddfe43f0d43", size = 72403 },
+ { url = "https://files.pythonhosted.org/packages/44/46/3771e0a4315044aa7b67da892b2fb1f59dfcf0eaff2c8967b2a0a85d5896/lazy_object_proxy-1.10.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80b39d3a151309efc8cc48675918891b865bdf742a8616a337cb0090791a0de9", size = 72401 },
+ { url = "https://files.pythonhosted.org/packages/81/39/84ce4740718e1c700bd04d3457ac92b2e9ce76529911583e7a2bf4d96eb2/lazy_object_proxy-1.10.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e221060b701e2aa2ea991542900dd13907a5c90fa80e199dbf5a03359019e7a3", size = 75375 },
+ { url = "https://files.pythonhosted.org/packages/86/3b/d6b65da2b864822324745c0a73fe7fd86c67ccea54173682c3081d7adea8/lazy_object_proxy-1.10.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:92f09ff65ecff3108e56526f9e2481b8116c0b9e1425325e13245abfd79bdb1b", size = 75466 },
+ { url = "https://files.pythonhosted.org/packages/f5/33/467a093bf004a70022cb410c590d937134bba2faa17bf9dc42a48f49af35/lazy_object_proxy-1.10.0-cp312-cp312-win32.whl", hash = "sha256:3ad54b9ddbe20ae9f7c1b29e52f123120772b06dbb18ec6be9101369d63a4074", size = 25914 },
+ { url = "https://files.pythonhosted.org/packages/77/ce/7956dc5ac2f8b62291b798c8363c81810e22a9effe469629d297d087e350/lazy_object_proxy-1.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:127a789c75151db6af398b8972178afe6bda7d6f68730c057fbbc2e96b08d282", size = 27525 },
+ { url = "https://files.pythonhosted.org/packages/31/8b/94dc8d58704ab87b39faed6f2fc0090b9d90e2e2aa2bbec35c79f3d2a054/lazy_object_proxy-1.10.0-pp310.pp311.pp312.pp38.pp39-none-any.whl", hash = "sha256:80fa48bd89c8f2f456fc0765c11c23bf5af827febacd2f523ca5bc1893fcc09d", size = 16405 },
+]
+
+[[package]]
+name = "levenshtein"
+version = "0.27.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "rapidfuzz" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/7e/b3/b5f8011483ba9083a0bc74c4d58705e9cf465fbe55c948a1b1357d0a2aa8/levenshtein-0.27.1.tar.gz", hash = "sha256:3e18b73564cfc846eec94dd13fab6cb006b5d2e0cc56bad1fd7d5585881302e3", size = 382571 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/0d/73/84a7126b9e6441c2547f1fbfd65f3c15c387d1fc04e0dd1d025a12107771/levenshtein-0.27.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:25fb540d8c55d1dc7bdc59b7de518ea5ed9df92eb2077e74bcb9bb6de7b06f69", size = 173953 },
+ { url = "https://files.pythonhosted.org/packages/8f/5c/06c01870c0cf336f9f29397bbfbfbbfd3a59918868716e7bb15828e89367/levenshtein-0.27.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f09cfab6387e9c908c7b37961c045e8e10eb9b7ec4a700367f8e080ee803a562", size = 156399 },
+ { url = "https://files.pythonhosted.org/packages/c7/4a/c1d3f27ec8b3fff5a96617251bf3f61c67972869ac0a0419558fc3e2cbe6/levenshtein-0.27.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dafa29c0e616f322b574e0b2aeb5b1ff2f8d9a1a6550f22321f3bd9bb81036e3", size = 151061 },
+ { url = "https://files.pythonhosted.org/packages/4d/8f/2521081e9a265891edf46aa30e1b59c1f347a452aed4c33baafbec5216fa/levenshtein-0.27.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:be7a7642ea64392fa1e6ef7968c2e50ef2152c60948f95d0793361ed97cf8a6f", size = 183119 },
+ { url = "https://files.pythonhosted.org/packages/1f/a0/a63e3bce6376127596d04be7f57e672d2f3d5f540265b1e30b9dd9b3c5a9/levenshtein-0.27.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:060b48c45ed54bcea9582ce79c6365b20a1a7473767e0b3d6be712fa3a22929c", size = 185352 },
+ { url = "https://files.pythonhosted.org/packages/17/8c/8352e992063952b38fb61d49bad8d193a4a713e7eeceb3ae74b719d7863d/levenshtein-0.27.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:712f562c5e64dd0398d3570fe99f8fbb88acec7cc431f101cb66c9d22d74c542", size = 159879 },
+ { url = "https://files.pythonhosted.org/packages/69/b4/564866e2038acf47c3de3e9292fc7fc7cc18d2593fedb04f001c22ac6e15/levenshtein-0.27.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a6141ad65cab49aa4527a3342d76c30c48adb2393b6cdfeca65caae8d25cb4b8", size = 245005 },
+ { url = "https://files.pythonhosted.org/packages/ba/f9/7367f87e3a6eed282f3654ec61a174b4d1b78a7a73f2cecb91f0ab675153/levenshtein-0.27.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:799b8d73cda3265331116f62932f553804eae16c706ceb35aaf16fc2a704791b", size = 1116865 },
+ { url = "https://files.pythonhosted.org/packages/f5/02/b5b3bfb4b4cd430e9d110bad2466200d51c6061dae7c5a64e36047c8c831/levenshtein-0.27.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:ec99871d98e517e1cc4a15659c62d6ea63ee5a2d72c5ddbebd7bae8b9e2670c8", size = 1401723 },
+ { url = "https://files.pythonhosted.org/packages/ef/69/b93bccd093b3f06a99e67e11ebd6e100324735dc2834958ba5852a1b9fed/levenshtein-0.27.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:8799164e1f83588dbdde07f728ea80796ea72196ea23484d78d891470241b222", size = 1226276 },
+ { url = "https://files.pythonhosted.org/packages/ab/32/37dd1bc5ce866c136716619e6f7081d7078d7dd1c1da7025603dcfd9cf5f/levenshtein-0.27.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:583943813898326516ab451a83f734c6f07488cda5c361676150d3e3e8b47927", size = 1420132 },
+ { url = "https://files.pythonhosted.org/packages/4b/08/f3bc828dd9f0f8433b26f37c4fceab303186ad7b9b70819f2ccb493d99fc/levenshtein-0.27.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5bb22956af44bb4eade93546bf95be610c8939b9a9d4d28b2dfa94abf454fed7", size = 1189144 },
+ { url = "https://files.pythonhosted.org/packages/2d/54/5ecd89066cf579223d504abe3ac37ba11f63b01a19fd12591083acc00eb6/levenshtein-0.27.1-cp312-cp312-win32.whl", hash = "sha256:d9099ed1bcfa7ccc5540e8ad27b5dc6f23d16addcbe21fdd82af6440f4ed2b6d", size = 88279 },
+ { url = "https://files.pythonhosted.org/packages/53/79/4f8fabcc5aca9305b494d1d6c7a98482e90a855e0050ae9ff5d7bf4ab2c6/levenshtein-0.27.1-cp312-cp312-win_amd64.whl", hash = "sha256:7f071ecdb50aa6c15fd8ae5bcb67e9da46ba1df7bba7c6bf6803a54c7a41fd96", size = 100659 },
+ { url = "https://files.pythonhosted.org/packages/cb/81/f8e4c0f571c2aac2e0c56a6e0e41b679937a2b7013e79415e4aef555cff0/levenshtein-0.27.1-cp312-cp312-win_arm64.whl", hash = "sha256:83b9033a984ccace7703f35b688f3907d55490182fd39b33a8e434d7b2e249e6", size = 88168 },
+ { url = "https://files.pythonhosted.org/packages/c6/d3/30485fb9aee848542ee2d01aba85106a7f5da982ebeeffc619f70ea593c7/levenshtein-0.27.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ab00c2cae2889166afb7e1af64af2d4e8c1b126f3902d13ef3740df00e54032d", size = 173397 },
+ { url = "https://files.pythonhosted.org/packages/df/9f/40a81c54cfe74b22737710e654bd25ad934a675f737b60b24f84099540e0/levenshtein-0.27.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c27e00bc7527e282f7c437817081df8da4eb7054e7ef9055b851fa3947896560", size = 155787 },
+ { url = "https://files.pythonhosted.org/packages/df/98/915f4e24e21982b6eca2c0203546c160f4a83853fa6a2ac6e2b208a54afc/levenshtein-0.27.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5b07de42bfc051136cc8e7f1e7ba2cb73666aa0429930f4218efabfdc5837ad", size = 150013 },
+ { url = "https://files.pythonhosted.org/packages/80/93/9b0773107580416b9de14bf6a12bd1dd2b2964f7a9f6fb0e40723e1f0572/levenshtein-0.27.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fb11ad3c9dae3063405aa50d9c96923722ab17bb606c776b6817d70b51fd7e07", size = 181234 },
+ { url = "https://files.pythonhosted.org/packages/91/b1/3cd4f69af32d40de14808142cc743af3a1b737b25571bd5e8d2f46b885e0/levenshtein-0.27.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c5986fb46cb0c063305fd45b0a79924abf2959a6d984bbac2b511d3ab259f3f", size = 183697 },
+ { url = "https://files.pythonhosted.org/packages/bb/65/b691e502c6463f6965b7e0d8d84224c188aa35b53fbc85853c72a0e436c9/levenshtein-0.27.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75191e469269ddef2859bc64c4a8cfd6c9e063302766b5cb7e1e67f38cc7051a", size = 159964 },
+ { url = "https://files.pythonhosted.org/packages/0f/c0/89a922a47306a475fb6d8f2ab08668f143d3dc7dea4c39d09e46746e031c/levenshtein-0.27.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:51b3a7b2266933babc04e4d9821a495142eebd6ef709f90e24bc532b52b81385", size = 244759 },
+ { url = "https://files.pythonhosted.org/packages/b4/93/30283c6e69a6556b02e0507c88535df9613179f7b44bc49cdb4bc5e889a3/levenshtein-0.27.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bbac509794afc3e2a9e73284c9e3d0aab5b1d928643f42b172969c3eefa1f2a3", size = 1115955 },
+ { url = "https://files.pythonhosted.org/packages/0b/cf/7e19ea2c23671db02fbbe5a5a4aeafd1d471ee573a6251ae17008458c434/levenshtein-0.27.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:8d68714785178347ecb272b94e85cbf7e638165895c4dd17ab57e7742d8872ec", size = 1400921 },
+ { url = "https://files.pythonhosted.org/packages/e3/f7/fb42bfe2f3b46ef91f0fc6fa217b44dbeb4ef8c72a9c1917bbbe1cafc0f8/levenshtein-0.27.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:8ee74ee31a5ab8f61cd6c6c6e9ade4488dde1285f3c12207afc018393c9b8d14", size = 1225037 },
+ { url = "https://files.pythonhosted.org/packages/74/25/c86f8874ac7b0632b172d0d1622ed3ab9608a7f8fe85d41d632b16f5948e/levenshtein-0.27.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:f2441b6365453ec89640b85344afd3d602b0d9972840b693508074c613486ce7", size = 1420601 },
+ { url = "https://files.pythonhosted.org/packages/20/fe/ebfbaadcd90ea7dfde987ae95b5c11dc27c2c5d55a2c4ccbbe4e18a8af7b/levenshtein-0.27.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a9be39640a46d8a0f9be729e641651d16a62b2c07d3f4468c36e1cc66b0183b9", size = 1188241 },
+ { url = "https://files.pythonhosted.org/packages/2e/1a/aa6b07316e10781a6c5a5a8308f9bdc22213dc3911b959daa6d7ff654fc6/levenshtein-0.27.1-cp313-cp313-win32.whl", hash = "sha256:a520af67d976761eb6580e7c026a07eb8f74f910f17ce60e98d6e492a1f126c7", size = 88103 },
+ { url = "https://files.pythonhosted.org/packages/9d/7b/9bbfd417f80f1047a28d0ea56a9b38b9853ba913b84dd5998785c5f98541/levenshtein-0.27.1-cp313-cp313-win_amd64.whl", hash = "sha256:7dd60aa49c2d8d23e0ef6452c8329029f5d092f386a177e3385d315cabb78f2a", size = 100579 },
+ { url = "https://files.pythonhosted.org/packages/8b/01/5f3ff775db7340aa378b250e2a31e6b4b038809a24ff0a3636ef20c7ca31/levenshtein-0.27.1-cp313-cp313-win_arm64.whl", hash = "sha256:149cd4f0baf5884ac5df625b7b0d281721b15de00f447080e38f5188106e1167", size = 87933 },
+]
+
+[[package]]
+name = "lox"
+version = "0.12.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "pathos" },
+ { name = "sphinx-rtd-theme" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/0f/b5/2bfa8da2a1dd6647c3ea0b8d7ae366bbb36b49f9f3858a253199daacb860/lox-0.12.0.tar.gz", hash = "sha256:cc7d5f867afb4dc7c2bce7bd6e90f4665c6df492863f35ff63229300b7219977", size = 37579 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/02/9a/cc790ca4b853821b76acb5944d32036590a789e5f3b9e4f10a8962bcfda5/lox-0.12.0-py2.py3-none-any.whl", hash = "sha256:ac0a392662f3a75cc9097655d26169d5e3564e2670431fd9884a7a09a09f6921", size = 25372 },
+]
+
+[[package]]
+name = "markdown-it-py"
+version = "3.0.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "mdurl" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528 },
+]
+
+[[package]]
+name = "markupsafe"
+version = "3.0.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/22/09/d1f21434c97fc42f09d290cbb6350d44eb12f09cc62c9476effdb33a18aa/MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf", size = 14274 },
+ { url = "https://files.pythonhosted.org/packages/6b/b0/18f76bba336fa5aecf79d45dcd6c806c280ec44538b3c13671d49099fdd0/MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225", size = 12348 },
+ { url = "https://files.pythonhosted.org/packages/e0/25/dd5c0f6ac1311e9b40f4af06c78efde0f3b5cbf02502f8ef9501294c425b/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028", size = 24149 },
+ { url = "https://files.pythonhosted.org/packages/f3/f0/89e7aadfb3749d0f52234a0c8c7867877876e0a20b60e2188e9850794c17/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8", size = 23118 },
+ { url = "https://files.pythonhosted.org/packages/d5/da/f2eeb64c723f5e3777bc081da884b414671982008c47dcc1873d81f625b6/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c", size = 22993 },
+ { url = "https://files.pythonhosted.org/packages/da/0e/1f32af846df486dce7c227fe0f2398dc7e2e51d4a370508281f3c1c5cddc/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557", size = 24178 },
+ { url = "https://files.pythonhosted.org/packages/c4/f6/bb3ca0532de8086cbff5f06d137064c8410d10779c4c127e0e47d17c0b71/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22", size = 23319 },
+ { url = "https://files.pythonhosted.org/packages/a2/82/8be4c96ffee03c5b4a034e60a31294daf481e12c7c43ab8e34a1453ee48b/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48", size = 23352 },
+ { url = "https://files.pythonhosted.org/packages/51/ae/97827349d3fcffee7e184bdf7f41cd6b88d9919c80f0263ba7acd1bbcb18/MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30", size = 15097 },
+ { url = "https://files.pythonhosted.org/packages/c1/80/a61f99dc3a936413c3ee4e1eecac96c0da5ed07ad56fd975f1a9da5bc630/MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87", size = 15601 },
+ { url = "https://files.pythonhosted.org/packages/83/0e/67eb10a7ecc77a0c2bbe2b0235765b98d164d81600746914bebada795e97/MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", size = 14274 },
+ { url = "https://files.pythonhosted.org/packages/2b/6d/9409f3684d3335375d04e5f05744dfe7e9f120062c9857df4ab490a1031a/MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", size = 12352 },
+ { url = "https://files.pythonhosted.org/packages/d2/f5/6eadfcd3885ea85fe2a7c128315cc1bb7241e1987443d78c8fe712d03091/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", size = 24122 },
+ { url = "https://files.pythonhosted.org/packages/0c/91/96cf928db8236f1bfab6ce15ad070dfdd02ed88261c2afafd4b43575e9e9/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", size = 23085 },
+ { url = "https://files.pythonhosted.org/packages/c2/cf/c9d56af24d56ea04daae7ac0940232d31d5a8354f2b457c6d856b2057d69/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79", size = 22978 },
+ { url = "https://files.pythonhosted.org/packages/2a/9f/8619835cd6a711d6272d62abb78c033bda638fdc54c4e7f4272cf1c0962b/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", size = 24208 },
+ { url = "https://files.pythonhosted.org/packages/f9/bf/176950a1792b2cd2102b8ffeb5133e1ed984547b75db47c25a67d3359f77/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca", size = 23357 },
+ { url = "https://files.pythonhosted.org/packages/ce/4f/9a02c1d335caabe5c4efb90e1b6e8ee944aa245c1aaaab8e8a618987d816/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", size = 23344 },
+ { url = "https://files.pythonhosted.org/packages/ee/55/c271b57db36f748f0e04a759ace9f8f759ccf22b4960c270c78a394f58be/MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", size = 15101 },
+ { url = "https://files.pythonhosted.org/packages/29/88/07df22d2dd4df40aba9f3e402e6dc1b8ee86297dddbad4872bd5e7b0094f/MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", size = 15603 },
+ { url = "https://files.pythonhosted.org/packages/62/6a/8b89d24db2d32d433dffcd6a8779159da109842434f1dd2f6e71f32f738c/MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", size = 14510 },
+ { url = "https://files.pythonhosted.org/packages/7a/06/a10f955f70a2e5a9bf78d11a161029d278eeacbd35ef806c3fd17b13060d/MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", size = 12486 },
+ { url = "https://files.pythonhosted.org/packages/34/cf/65d4a571869a1a9078198ca28f39fba5fbb910f952f9dbc5220afff9f5e6/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", size = 25480 },
+ { url = "https://files.pythonhosted.org/packages/0c/e3/90e9651924c430b885468b56b3d597cabf6d72be4b24a0acd1fa0e12af67/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", size = 23914 },
+ { url = "https://files.pythonhosted.org/packages/66/8c/6c7cf61f95d63bb866db39085150df1f2a5bd3335298f14a66b48e92659c/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe", size = 23796 },
+ { url = "https://files.pythonhosted.org/packages/bb/35/cbe9238ec3f47ac9a7c8b3df7a808e7cb50fe149dc7039f5f454b3fba218/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", size = 25473 },
+ { url = "https://files.pythonhosted.org/packages/e6/32/7621a4382488aa283cc05e8984a9c219abad3bca087be9ec77e89939ded9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a", size = 24114 },
+ { url = "https://files.pythonhosted.org/packages/0d/80/0985960e4b89922cb5a0bac0ed39c5b96cbc1a536a99f30e8c220a996ed9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", size = 24098 },
+ { url = "https://files.pythonhosted.org/packages/82/78/fedb03c7d5380df2427038ec8d973587e90561b2d90cd472ce9254cf348b/MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", size = 15208 },
+ { url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739 },
+]
+
+[[package]]
+name = "marshmallow"
+version = "3.26.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "packaging" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/ab/5e/5e53d26b42ab75491cda89b871dab9e97c840bf12c63ec58a1919710cd06/marshmallow-3.26.1.tar.gz", hash = "sha256:e6d8affb6cb61d39d26402096dc0aee12d5a26d490a121f118d2e81dc0719dc6", size = 221825 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/34/75/51952c7b2d3873b44a0028b1bd26a25078c18f92f256608e8d1dc61b39fd/marshmallow-3.26.1-py3-none-any.whl", hash = "sha256:3350409f20a70a7e4e11a27661187b77cdcaeb20abca41c1454fe33636bea09c", size = 50878 },
+]
+
+[[package]]
+name = "mcp"
+version = "1.3.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "anyio" },
+ { name = "httpx" },
+ { name = "httpx-sse" },
+ { name = "pydantic" },
+ { name = "pydantic-settings" },
+ { name = "sse-starlette" },
+ { name = "starlette" },
+ { name = "uvicorn" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/6b/b6/81e5f2490290351fc97bf46c24ff935128cb7d34d68e3987b522f26f7ada/mcp-1.3.0.tar.gz", hash = "sha256:f409ae4482ce9d53e7ac03f3f7808bcab735bdfc0fba937453782efb43882d45", size = 150235 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/d0/d2/a9e87b506b2094f5aa9becc1af5178842701b27217fa43877353da2577e3/mcp-1.3.0-py3-none-any.whl", hash = "sha256:2829d67ce339a249f803f22eba5e90385eafcac45c94b00cab6cef7e8f217211", size = 70672 },
+]
+
+[package.optional-dependencies]
+cli = [
+ { name = "python-dotenv" },
+ { name = "typer" },
+]
+
+[[package]]
+name = "mdurl"
+version = "0.1.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 },
+]
+
+[[package]]
+name = "mini-racer"
+version = "0.12.4"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/8c/2d/e051f58e17117b1b8b11a7d17622c1528fa9002c553943c6b677c1b412da/mini_racer-0.12.4.tar.gz", hash = "sha256:84c67553ce9f3736d4c617d8a3f882949d37a46cfb47fe11dab33dd6704e62a4", size = 447529 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/71/fe/1452b6c74cae9e8cd7b6a16d8b1ef08bba4dd0ed373a95f3b401c2e712ea/mini_racer-0.12.4-py3-none-macosx_10_9_x86_64.whl", hash = "sha256:bce8a3cee946575a352f5e65335903bc148da42c036d0c738ac67e931600e455", size = 15701219 },
+ { url = "https://files.pythonhosted.org/packages/99/ae/c22478eff26e6136341e6b40d34f8d285f910ca4d2e2a0ca4703ef87be79/mini_racer-0.12.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:56c832e6ac2db6a304d1e8e80030615297aafbc6940f64f3479af4ba16abccd5", size = 14566436 },
+ { url = "https://files.pythonhosted.org/packages/44/89/f062aa116b14fcace91f0af86a37605f0ba7c07a01c8101b5ea104d489b1/mini_racer-0.12.4-py3-none-manylinux_2_31_aarch64.whl", hash = "sha256:b82c4bd2976e280ed0a72c9c2de01b13f18ccfbe6f4892cbc22aae04410fac3c", size = 14931664 },
+ { url = "https://files.pythonhosted.org/packages/9c/a1/09122c88a0dd0a2141b0ea068d70f5d31acd0015d6f3157b8efd3ff7e026/mini_racer-0.12.4-py3-none-manylinux_2_31_x86_64.whl", hash = "sha256:69a1c44d02a9069b881684cef15a2d747fe0743df29eadc881fda7002aae5fd2", size = 14955238 },
+ { url = "https://files.pythonhosted.org/packages/6c/3b/826e41f92631560e5c6ca2aa4ef9005bdccf9290c1e7ddebe05e0a3b8c7c/mini_racer-0.12.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:499dbc267dfe60e954bc1b6c3787f7b10fc41fe1975853c9a6ddb55eb83dc4d9", size = 15211136 },
+ { url = "https://files.pythonhosted.org/packages/e5/37/15b30316630d1f63b025f058dc92efa75931a37315c34ca07f80be2cc405/mini_racer-0.12.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:231f949f5787d18351939f1fe59e5a6fe134bccb5ecf8f836b9beab69d91c8d9", size = 15128684 },
+ { url = "https://files.pythonhosted.org/packages/5c/0e/a9943f90b4a8a6d3849b81a00a00d2db128d876365385af382a0e2caf191/mini_racer-0.12.4-py3-none-win_amd64.whl", hash = "sha256:9446e3bd6a4eb9fbedf1861326f7476080995a31c9b69308acef17e5b7ecaa1b", size = 13674040 },
+]
+
+[[package]]
+name = "modal"
+version = "0.73.87"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "aiohttp" },
+ { name = "certifi" },
+ { name = "click" },
+ { name = "fastapi" },
+ { name = "grpclib" },
+ { name = "protobuf" },
+ { name = "rich" },
+ { name = "synchronicity" },
+ { name = "toml" },
+ { name = "typer" },
+ { name = "types-certifi" },
+ { name = "types-toml" },
+ { name = "typing-extensions" },
+ { name = "watchfiles" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/ea/60/5e8bdc689d0a966f72fa523fd8d0c335893c68a036c932be26d2d52f00b9/modal-0.73.87.tar.gz", hash = "sha256:07052bebfe043b411d4ce7fcac1a69b3c7840d19cda3f2320d4bad3c2bfcd7a5", size = 469486 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/28/94/13dccb2a54c44bd5c566f12f478de2d16a8d2c416d6c0c39505f05c5f838/modal-0.73.87-py3-none-any.whl", hash = "sha256:8a372003cbac173b9d28a7a583eece9cd9b083653be258fe266ff04e17b13c09", size = 535780 },
+]
+
+[[package]]
+name = "msgpack"
+version = "1.1.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/cb/d0/7555686ae7ff5731205df1012ede15dd9d927f6227ea151e901c7406af4f/msgpack-1.1.0.tar.gz", hash = "sha256:dd432ccc2c72b914e4cb77afce64aab761c1137cc698be3984eee260bcb2896e", size = 167260 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/e1/d6/716b7ca1dbde63290d2973d22bbef1b5032ca634c3ff4384a958ec3f093a/msgpack-1.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:d46cf9e3705ea9485687aa4001a76e44748b609d260af21c4ceea7f2212a501d", size = 152421 },
+ { url = "https://files.pythonhosted.org/packages/70/da/5312b067f6773429cec2f8f08b021c06af416bba340c912c2ec778539ed6/msgpack-1.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5dbad74103df937e1325cc4bfeaf57713be0b4f15e1c2da43ccdd836393e2ea2", size = 85277 },
+ { url = "https://files.pythonhosted.org/packages/28/51/da7f3ae4462e8bb98af0d5bdf2707f1b8c65a0d4f496e46b6afb06cbc286/msgpack-1.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:58dfc47f8b102da61e8949708b3eafc3504509a5728f8b4ddef84bd9e16ad420", size = 82222 },
+ { url = "https://files.pythonhosted.org/packages/33/af/dc95c4b2a49cff17ce47611ca9ba218198806cad7796c0b01d1e332c86bb/msgpack-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4676e5be1b472909b2ee6356ff425ebedf5142427842aa06b4dfd5117d1ca8a2", size = 392971 },
+ { url = "https://files.pythonhosted.org/packages/f1/54/65af8de681fa8255402c80eda2a501ba467921d5a7a028c9c22a2c2eedb5/msgpack-1.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17fb65dd0bec285907f68b15734a993ad3fc94332b5bb21b0435846228de1f39", size = 401403 },
+ { url = "https://files.pythonhosted.org/packages/97/8c/e333690777bd33919ab7024269dc3c41c76ef5137b211d776fbb404bfead/msgpack-1.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a51abd48c6d8ac89e0cfd4fe177c61481aca2d5e7ba42044fd218cfd8ea9899f", size = 385356 },
+ { url = "https://files.pythonhosted.org/packages/57/52/406795ba478dc1c890559dd4e89280fa86506608a28ccf3a72fbf45df9f5/msgpack-1.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2137773500afa5494a61b1208619e3871f75f27b03bcfca7b3a7023284140247", size = 383028 },
+ { url = "https://files.pythonhosted.org/packages/e7/69/053b6549bf90a3acadcd8232eae03e2fefc87f066a5b9fbb37e2e608859f/msgpack-1.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:398b713459fea610861c8a7b62a6fec1882759f308ae0795b5413ff6a160cf3c", size = 391100 },
+ { url = "https://files.pythonhosted.org/packages/23/f0/d4101d4da054f04274995ddc4086c2715d9b93111eb9ed49686c0f7ccc8a/msgpack-1.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:06f5fd2f6bb2a7914922d935d3b8bb4a7fff3a9a91cfce6d06c13bc42bec975b", size = 394254 },
+ { url = "https://files.pythonhosted.org/packages/1c/12/cf07458f35d0d775ff3a2dc5559fa2e1fcd06c46f1ef510e594ebefdca01/msgpack-1.1.0-cp312-cp312-win32.whl", hash = "sha256:ad33e8400e4ec17ba782f7b9cf868977d867ed784a1f5f2ab46e7ba53b6e1e1b", size = 69085 },
+ { url = "https://files.pythonhosted.org/packages/73/80/2708a4641f7d553a63bc934a3eb7214806b5b39d200133ca7f7afb0a53e8/msgpack-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:115a7af8ee9e8cddc10f87636767857e7e3717b7a2e97379dc2054712693e90f", size = 75347 },
+ { url = "https://files.pythonhosted.org/packages/c8/b0/380f5f639543a4ac413e969109978feb1f3c66e931068f91ab6ab0f8be00/msgpack-1.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:071603e2f0771c45ad9bc65719291c568d4edf120b44eb36324dcb02a13bfddf", size = 151142 },
+ { url = "https://files.pythonhosted.org/packages/c8/ee/be57e9702400a6cb2606883d55b05784fada898dfc7fd12608ab1fdb054e/msgpack-1.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0f92a83b84e7c0749e3f12821949d79485971f087604178026085f60ce109330", size = 84523 },
+ { url = "https://files.pythonhosted.org/packages/7e/3a/2919f63acca3c119565449681ad08a2f84b2171ddfcff1dba6959db2cceb/msgpack-1.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4a1964df7b81285d00a84da4e70cb1383f2e665e0f1f2a7027e683956d04b734", size = 81556 },
+ { url = "https://files.pythonhosted.org/packages/7c/43/a11113d9e5c1498c145a8925768ea2d5fce7cbab15c99cda655aa09947ed/msgpack-1.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59caf6a4ed0d164055ccff8fe31eddc0ebc07cf7326a2aaa0dbf7a4001cd823e", size = 392105 },
+ { url = "https://files.pythonhosted.org/packages/2d/7b/2c1d74ca6c94f70a1add74a8393a0138172207dc5de6fc6269483519d048/msgpack-1.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0907e1a7119b337971a689153665764adc34e89175f9a34793307d9def08e6ca", size = 399979 },
+ { url = "https://files.pythonhosted.org/packages/82/8c/cf64ae518c7b8efc763ca1f1348a96f0e37150061e777a8ea5430b413a74/msgpack-1.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:65553c9b6da8166e819a6aa90ad15288599b340f91d18f60b2061f402b9a4915", size = 383816 },
+ { url = "https://files.pythonhosted.org/packages/69/86/a847ef7a0f5ef3fa94ae20f52a4cacf596a4e4a010197fbcc27744eb9a83/msgpack-1.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7a946a8992941fea80ed4beae6bff74ffd7ee129a90b4dd5cf9c476a30e9708d", size = 380973 },
+ { url = "https://files.pythonhosted.org/packages/aa/90/c74cf6e1126faa93185d3b830ee97246ecc4fe12cf9d2d31318ee4246994/msgpack-1.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4b51405e36e075193bc051315dbf29168d6141ae2500ba8cd80a522964e31434", size = 387435 },
+ { url = "https://files.pythonhosted.org/packages/7a/40/631c238f1f338eb09f4acb0f34ab5862c4e9d7eda11c1b685471a4c5ea37/msgpack-1.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b4c01941fd2ff87c2a934ee6055bda4ed353a7846b8d4f341c428109e9fcde8c", size = 399082 },
+ { url = "https://files.pythonhosted.org/packages/e9/1b/fa8a952be252a1555ed39f97c06778e3aeb9123aa4cccc0fd2acd0b4e315/msgpack-1.1.0-cp313-cp313-win32.whl", hash = "sha256:7c9a35ce2c2573bada929e0b7b3576de647b0defbd25f5139dcdaba0ae35a4cc", size = 69037 },
+ { url = "https://files.pythonhosted.org/packages/b6/bc/8bd826dd03e022153bfa1766dcdec4976d6c818865ed54223d71f07862b3/msgpack-1.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:bce7d9e614a04d0883af0b3d4d501171fbfca038f12c77fa838d9f198147a23f", size = 75140 },
+]
+
+[[package]]
+name = "multidict"
+version = "6.1.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/d6/be/504b89a5e9ca731cd47487e91c469064f8ae5af93b7259758dcfc2b9c848/multidict-6.1.0.tar.gz", hash = "sha256:22ae2ebf9b0c69d206c003e2f6a914ea33f0a932d4aa16f236afc049d9958f4a", size = 64002 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/fd/16/92057c74ba3b96d5e211b553895cd6dc7cc4d1e43d9ab8fafc727681ef71/multidict-6.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:b04772ed465fa3cc947db808fa306d79b43e896beb677a56fb2347ca1a49c1fa", size = 48713 },
+ { url = "https://files.pythonhosted.org/packages/94/3d/37d1b8893ae79716179540b89fc6a0ee56b4a65fcc0d63535c6f5d96f217/multidict-6.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6180c0ae073bddeb5a97a38c03f30c233e0a4d39cd86166251617d1bbd0af436", size = 29516 },
+ { url = "https://files.pythonhosted.org/packages/a2/12/adb6b3200c363062f805275b4c1e656be2b3681aada66c80129932ff0bae/multidict-6.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:071120490b47aa997cca00666923a83f02c7fbb44f71cf7f136df753f7fa8761", size = 29557 },
+ { url = "https://files.pythonhosted.org/packages/47/e9/604bb05e6e5bce1e6a5cf80a474e0f072e80d8ac105f1b994a53e0b28c42/multidict-6.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50b3a2710631848991d0bf7de077502e8994c804bb805aeb2925a981de58ec2e", size = 130170 },
+ { url = "https://files.pythonhosted.org/packages/7e/13/9efa50801785eccbf7086b3c83b71a4fb501a4d43549c2f2f80b8787d69f/multidict-6.1.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b58c621844d55e71c1b7f7c498ce5aa6985d743a1a59034c57a905b3f153c1ef", size = 134836 },
+ { url = "https://files.pythonhosted.org/packages/bf/0f/93808b765192780d117814a6dfcc2e75de6dcc610009ad408b8814dca3ba/multidict-6.1.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55b6d90641869892caa9ca42ff913f7ff1c5ece06474fbd32fb2cf6834726c95", size = 133475 },
+ { url = "https://files.pythonhosted.org/packages/d3/c8/529101d7176fe7dfe1d99604e48d69c5dfdcadb4f06561f465c8ef12b4df/multidict-6.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b820514bfc0b98a30e3d85462084779900347e4d49267f747ff54060cc33925", size = 131049 },
+ { url = "https://files.pythonhosted.org/packages/ca/0c/fc85b439014d5a58063e19c3a158a889deec399d47b5269a0f3b6a2e28bc/multidict-6.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10a9b09aba0c5b48c53761b7c720aaaf7cf236d5fe394cd399c7ba662d5f9966", size = 120370 },
+ { url = "https://files.pythonhosted.org/packages/db/46/d4416eb20176492d2258fbd47b4abe729ff3b6e9c829ea4236f93c865089/multidict-6.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1e16bf3e5fc9f44632affb159d30a437bfe286ce9e02754759be5536b169b305", size = 125178 },
+ { url = "https://files.pythonhosted.org/packages/5b/46/73697ad7ec521df7de5531a32780bbfd908ded0643cbe457f981a701457c/multidict-6.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:76f364861c3bfc98cbbcbd402d83454ed9e01a5224bb3a28bf70002a230f73e2", size = 119567 },
+ { url = "https://files.pythonhosted.org/packages/cd/ed/51f060e2cb0e7635329fa6ff930aa5cffa17f4c7f5c6c3ddc3500708e2f2/multidict-6.1.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:820c661588bd01a0aa62a1283f20d2be4281b086f80dad9e955e690c75fb54a2", size = 129822 },
+ { url = "https://files.pythonhosted.org/packages/df/9e/ee7d1954b1331da3eddea0c4e08d9142da5f14b1321c7301f5014f49d492/multidict-6.1.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:0e5f362e895bc5b9e67fe6e4ded2492d8124bdf817827f33c5b46c2fe3ffaca6", size = 128656 },
+ { url = "https://files.pythonhosted.org/packages/77/00/8538f11e3356b5d95fa4b024aa566cde7a38aa7a5f08f4912b32a037c5dc/multidict-6.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3ec660d19bbc671e3a6443325f07263be452c453ac9e512f5eb935e7d4ac28b3", size = 125360 },
+ { url = "https://files.pythonhosted.org/packages/be/05/5d334c1f2462d43fec2363cd00b1c44c93a78c3925d952e9a71caf662e96/multidict-6.1.0-cp312-cp312-win32.whl", hash = "sha256:58130ecf8f7b8112cdb841486404f1282b9c86ccb30d3519faf301b2e5659133", size = 26382 },
+ { url = "https://files.pythonhosted.org/packages/a3/bf/f332a13486b1ed0496d624bcc7e8357bb8053823e8cd4b9a18edc1d97e73/multidict-6.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:188215fc0aafb8e03341995e7c4797860181562380f81ed0a87ff455b70bf1f1", size = 28529 },
+ { url = "https://files.pythonhosted.org/packages/22/67/1c7c0f39fe069aa4e5d794f323be24bf4d33d62d2a348acdb7991f8f30db/multidict-6.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:d569388c381b24671589335a3be6e1d45546c2988c2ebe30fdcada8457a31008", size = 48771 },
+ { url = "https://files.pythonhosted.org/packages/3c/25/c186ee7b212bdf0df2519eacfb1981a017bda34392c67542c274651daf23/multidict-6.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:052e10d2d37810b99cc170b785945421141bf7bb7d2f8799d431e7db229c385f", size = 29533 },
+ { url = "https://files.pythonhosted.org/packages/67/5e/04575fd837e0958e324ca035b339cea174554f6f641d3fb2b4f2e7ff44a2/multidict-6.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f90c822a402cb865e396a504f9fc8173ef34212a342d92e362ca498cad308e28", size = 29595 },
+ { url = "https://files.pythonhosted.org/packages/d3/b2/e56388f86663810c07cfe4a3c3d87227f3811eeb2d08450b9e5d19d78876/multidict-6.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b225d95519a5bf73860323e633a664b0d85ad3d5bede6d30d95b35d4dfe8805b", size = 130094 },
+ { url = "https://files.pythonhosted.org/packages/6c/ee/30ae9b4186a644d284543d55d491fbd4239b015d36b23fea43b4c94f7052/multidict-6.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:23bfd518810af7de1116313ebd9092cb9aa629beb12f6ed631ad53356ed6b86c", size = 134876 },
+ { url = "https://files.pythonhosted.org/packages/84/c7/70461c13ba8ce3c779503c70ec9d0345ae84de04521c1f45a04d5f48943d/multidict-6.1.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c09fcfdccdd0b57867577b719c69e347a436b86cd83747f179dbf0cc0d4c1f3", size = 133500 },
+ { url = "https://files.pythonhosted.org/packages/4a/9f/002af221253f10f99959561123fae676148dd730e2daa2cd053846a58507/multidict-6.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf6bea52ec97e95560af5ae576bdac3aa3aae0b6758c6efa115236d9e07dae44", size = 131099 },
+ { url = "https://files.pythonhosted.org/packages/82/42/d1c7a7301d52af79d88548a97e297f9d99c961ad76bbe6f67442bb77f097/multidict-6.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57feec87371dbb3520da6192213c7d6fc892d5589a93db548331954de8248fd2", size = 120403 },
+ { url = "https://files.pythonhosted.org/packages/68/f3/471985c2c7ac707547553e8f37cff5158030d36bdec4414cb825fbaa5327/multidict-6.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0c3f390dc53279cbc8ba976e5f8035eab997829066756d811616b652b00a23a3", size = 125348 },
+ { url = "https://files.pythonhosted.org/packages/67/2c/e6df05c77e0e433c214ec1d21ddd203d9a4770a1f2866a8ca40a545869a0/multidict-6.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:59bfeae4b25ec05b34f1956eaa1cb38032282cd4dfabc5056d0a1ec4d696d3aa", size = 119673 },
+ { url = "https://files.pythonhosted.org/packages/c5/cd/bc8608fff06239c9fb333f9db7743a1b2eafe98c2666c9a196e867a3a0a4/multidict-6.1.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:b2f59caeaf7632cc633b5cf6fc449372b83bbdf0da4ae04d5be36118e46cc0aa", size = 129927 },
+ { url = "https://files.pythonhosted.org/packages/44/8e/281b69b7bc84fc963a44dc6e0bbcc7150e517b91df368a27834299a526ac/multidict-6.1.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:37bb93b2178e02b7b618893990941900fd25b6b9ac0fa49931a40aecdf083fe4", size = 128711 },
+ { url = "https://files.pythonhosted.org/packages/12/a4/63e7cd38ed29dd9f1881d5119f272c898ca92536cdb53ffe0843197f6c85/multidict-6.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4e9f48f58c2c523d5a06faea47866cd35b32655c46b443f163d08c6d0ddb17d6", size = 125519 },
+ { url = "https://files.pythonhosted.org/packages/38/e0/4f5855037a72cd8a7a2f60a3952d9aa45feedb37ae7831642102604e8a37/multidict-6.1.0-cp313-cp313-win32.whl", hash = "sha256:3a37ffb35399029b45c6cc33640a92bef403c9fd388acce75cdc88f58bd19a81", size = 26426 },
+ { url = "https://files.pythonhosted.org/packages/7e/a5/17ee3a4db1e310b7405f5d25834460073a8ccd86198ce044dfaf69eac073/multidict-6.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:e9aa71e15d9d9beaad2c6b9319edcdc0a49a43ef5c0a4c8265ca9ee7d6c67774", size = 28531 },
+ { url = "https://files.pythonhosted.org/packages/99/b7/b9e70fde2c0f0c9af4cc5277782a89b66d35948ea3369ec9f598358c3ac5/multidict-6.1.0-py3-none-any.whl", hash = "sha256:48e171e52d1c4d33888e529b999e5900356b9ae588c2f09a52dcefb158b27506", size = 10051 },
+]
+
+[[package]]
+name = "multiprocess"
+version = "0.70.16"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "dill" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/b5/ae/04f39c5d0d0def03247c2893d6f2b83c136bf3320a2154d7b8858f2ba72d/multiprocess-0.70.16.tar.gz", hash = "sha256:161af703d4652a0e1410be6abccecde4a7ddffd19341be0a7011b94aeb171ac1", size = 1772603 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/bc/f7/7ec7fddc92e50714ea3745631f79bd9c96424cb2702632521028e57d3a36/multiprocess-0.70.16-py310-none-any.whl", hash = "sha256:c4a9944c67bd49f823687463660a2d6daae94c289adff97e0f9d696ba6371d02", size = 134824 },
+ { url = "https://files.pythonhosted.org/packages/50/15/b56e50e8debaf439f44befec5b2af11db85f6e0f344c3113ae0be0593a91/multiprocess-0.70.16-py311-none-any.whl", hash = "sha256:af4cabb0dac72abfb1e794fa7855c325fd2b55a10a44628a3c1ad3311c04127a", size = 143519 },
+ { url = "https://files.pythonhosted.org/packages/0a/7d/a988f258104dcd2ccf1ed40fdc97e26c4ac351eeaf81d76e266c52d84e2f/multiprocess-0.70.16-py312-none-any.whl", hash = "sha256:fc0544c531920dde3b00c29863377f87e1632601092ea2daca74e4beb40faa2e", size = 146741 },
+ { url = "https://files.pythonhosted.org/packages/ea/89/38df130f2c799090c978b366cfdf5b96d08de5b29a4a293df7f7429fa50b/multiprocess-0.70.16-py38-none-any.whl", hash = "sha256:a71d82033454891091a226dfc319d0cfa8019a4e888ef9ca910372a446de4435", size = 132628 },
+ { url = "https://files.pythonhosted.org/packages/da/d9/f7f9379981e39b8c2511c9e0326d212accacb82f12fbfdc1aa2ce2a7b2b6/multiprocess-0.70.16-py39-none-any.whl", hash = "sha256:a0bafd3ae1b732eac64be2e72038231c1ba97724b60b09400d68f229fcc2fbf3", size = 133351 },
+]
+
+[[package]]
+name = "mypy"
+version = "1.15.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "mypy-extensions" },
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/ce/43/d5e49a86afa64bd3839ea0d5b9c7103487007d728e1293f52525d6d5486a/mypy-1.15.0.tar.gz", hash = "sha256:404534629d51d3efea5c800ee7c42b72a6554d6c400e6a79eafe15d11341fd43", size = 3239717 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/98/3a/03c74331c5eb8bd025734e04c9840532226775c47a2c39b56a0c8d4f128d/mypy-1.15.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:aea39e0583d05124836ea645f412e88a5c7d0fd77a6d694b60d9b6b2d9f184fd", size = 10793981 },
+ { url = "https://files.pythonhosted.org/packages/f0/1a/41759b18f2cfd568848a37c89030aeb03534411eef981df621d8fad08a1d/mypy-1.15.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2f2147ab812b75e5b5499b01ade1f4a81489a147c01585cda36019102538615f", size = 9749175 },
+ { url = "https://files.pythonhosted.org/packages/12/7e/873481abf1ef112c582db832740f4c11b2bfa510e829d6da29b0ab8c3f9c/mypy-1.15.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ce436f4c6d218a070048ed6a44c0bbb10cd2cc5e272b29e7845f6a2f57ee4464", size = 11455675 },
+ { url = "https://files.pythonhosted.org/packages/b3/d0/92ae4cde706923a2d3f2d6c39629134063ff64b9dedca9c1388363da072d/mypy-1.15.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8023ff13985661b50a5928fc7a5ca15f3d1affb41e5f0a9952cb68ef090b31ee", size = 12410020 },
+ { url = "https://files.pythonhosted.org/packages/46/8b/df49974b337cce35f828ba6fda228152d6db45fed4c86ba56ffe442434fd/mypy-1.15.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1124a18bc11a6a62887e3e137f37f53fbae476dc36c185d549d4f837a2a6a14e", size = 12498582 },
+ { url = "https://files.pythonhosted.org/packages/13/50/da5203fcf6c53044a0b699939f31075c45ae8a4cadf538a9069b165c1050/mypy-1.15.0-cp312-cp312-win_amd64.whl", hash = "sha256:171a9ca9a40cd1843abeca0e405bc1940cd9b305eaeea2dda769ba096932bb22", size = 9366614 },
+ { url = "https://files.pythonhosted.org/packages/6a/9b/fd2e05d6ffff24d912f150b87db9e364fa8282045c875654ce7e32fffa66/mypy-1.15.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:93faf3fdb04768d44bf28693293f3904bbb555d076b781ad2530214ee53e3445", size = 10788592 },
+ { url = "https://files.pythonhosted.org/packages/74/37/b246d711c28a03ead1fd906bbc7106659aed7c089d55fe40dd58db812628/mypy-1.15.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:811aeccadfb730024c5d3e326b2fbe9249bb7413553f15499a4050f7c30e801d", size = 9753611 },
+ { url = "https://files.pythonhosted.org/packages/a6/ac/395808a92e10cfdac8003c3de9a2ab6dc7cde6c0d2a4df3df1b815ffd067/mypy-1.15.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:98b7b9b9aedb65fe628c62a6dc57f6d5088ef2dfca37903a7d9ee374d03acca5", size = 11438443 },
+ { url = "https://files.pythonhosted.org/packages/d2/8b/801aa06445d2de3895f59e476f38f3f8d610ef5d6908245f07d002676cbf/mypy-1.15.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c43a7682e24b4f576d93072216bf56eeff70d9140241f9edec0c104d0c515036", size = 12402541 },
+ { url = "https://files.pythonhosted.org/packages/c7/67/5a4268782eb77344cc613a4cf23540928e41f018a9a1ec4c6882baf20ab8/mypy-1.15.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:baefc32840a9f00babd83251560e0ae1573e2f9d1b067719479bfb0e987c6357", size = 12494348 },
+ { url = "https://files.pythonhosted.org/packages/83/3e/57bb447f7bbbfaabf1712d96f9df142624a386d98fb026a761532526057e/mypy-1.15.0-cp313-cp313-win_amd64.whl", hash = "sha256:b9378e2c00146c44793c98b8d5a61039a048e31f429fb0eb546d93f4b000bedf", size = 9373648 },
+ { url = "https://files.pythonhosted.org/packages/09/4e/a7d65c7322c510de2c409ff3828b03354a7c43f5a8ed458a7a131b41c7b9/mypy-1.15.0-py3-none-any.whl", hash = "sha256:5469affef548bd1895d86d3bf10ce2b44e33d86923c29e4d675b3e323437ea3e", size = 2221777 },
+]
+
+[[package]]
+name = "mypy-extensions"
+version = "1.0.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/98/a4/1ab47638b92648243faf97a5aeb6ea83059cc3624972ab6b8d2316078d3f/mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782", size = 4433 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/2a/e2/5d3f6ada4297caebe1a2add3b126fe800c96f56dbe5d1988a2cbe0b267aa/mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d", size = 4695 },
+]
+
+[[package]]
+name = "narwhals"
+version = "1.29.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/e6/f7/caa23ebc4aed3ef2314441c44e1d842e701adc6af57587ffda9263c03b6e/narwhals-1.29.0.tar.gz", hash = "sha256:1021c345d56c66ff0cc8e6d03ca8c543d01ffc411630973a5cb69ee86824d823", size = 248349 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/ee/f6/1fcd6b3d0e21d9b75e71ae68fbc92bbb9b9b1f4f33dd81c61d8f53378b30/narwhals-1.29.0-py3-none-any.whl", hash = "sha256:653aa8e5eb435816e7b50c8def17e7e5e3324c2ffd8a3eec03fef85792e9cf5e", size = 305214 },
+]
+
+[[package]]
+name = "neo4j"
+version = "5.28.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "pytz" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/4b/20/733dac16f7cedc80b23093415822c9763302519cba0e7c8bcdb5c01fc512/neo4j-5.28.1.tar.gz", hash = "sha256:ae8e37a1d895099062c75bc359b2cce62099baac7be768d0eba7180c1298e214", size = 231094 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/6a/57/94225fe5e9dabdc0ff60c88cbfcedf11277f4b34e7ab1373d3e62dbdd207/neo4j-5.28.1-py3-none-any.whl", hash = "sha256:6755ef9e5f4e14b403aef1138fb6315b120631a0075c138b5ddb2a06b87b09fd", size = 312258 },
+]
+
+[[package]]
+name = "networkx"
+version = "3.4.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/fd/1d/06475e1cd5264c0b870ea2cc6fdb3e37177c1e565c43f56ff17a10e3937f/networkx-3.4.2.tar.gz", hash = "sha256:307c3669428c5362aab27c8a1260aa8f47c4e91d3891f48be0141738d8d053e1", size = 2151368 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/b9/54/dd730b32ea14ea797530a4479b2ed46a6fb250f682a9cfb997e968bf0261/networkx-3.4.2-py3-none-any.whl", hash = "sha256:df5d4365b724cf81b8c6a7312509d0c22386097011ad1abe274afd5e9d3bbc5f", size = 1723263 },
+]
+
+[[package]]
+name = "nodeenv"
+version = "1.9.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/43/16/fc88b08840de0e0a72a2f9d8c6bae36be573e475a6326ae854bcc549fc45/nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f", size = 47437 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9", size = 22314 },
+]
+
+[[package]]
+name = "numpy"
+version = "2.2.3"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/fb/90/8956572f5c4ae52201fdec7ba2044b2c882832dcec7d5d0922c9e9acf2de/numpy-2.2.3.tar.gz", hash = "sha256:dbdc15f0c81611925f382dfa97b3bd0bc2c1ce19d4fe50482cb0ddc12ba30020", size = 20262700 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/43/ec/43628dcf98466e087812142eec6d1c1a6c6bdfdad30a0aa07b872dc01f6f/numpy-2.2.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:12c045f43b1d2915eca6b880a7f4a256f59d62df4f044788c8ba67709412128d", size = 20929458 },
+ { url = "https://files.pythonhosted.org/packages/9b/c0/2f4225073e99a5c12350954949ed19b5d4a738f541d33e6f7439e33e98e4/numpy-2.2.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:87eed225fd415bbae787f93a457af7f5990b92a334e346f72070bf569b9c9c95", size = 14115299 },
+ { url = "https://files.pythonhosted.org/packages/ca/fa/d2c5575d9c734a7376cc1592fae50257ec95d061b27ee3dbdb0b3b551eb2/numpy-2.2.3-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:712a64103d97c404e87d4d7c47fb0c7ff9acccc625ca2002848e0d53288b90ea", size = 5145723 },
+ { url = "https://files.pythonhosted.org/packages/eb/dc/023dad5b268a7895e58e791f28dc1c60eb7b6c06fcbc2af8538ad069d5f3/numpy-2.2.3-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:a5ae282abe60a2db0fd407072aff4599c279bcd6e9a2475500fc35b00a57c532", size = 6678797 },
+ { url = "https://files.pythonhosted.org/packages/3f/19/bcd641ccf19ac25abb6fb1dcd7744840c11f9d62519d7057b6ab2096eb60/numpy-2.2.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5266de33d4c3420973cf9ae3b98b54a2a6d53a559310e3236c4b2b06b9c07d4e", size = 14067362 },
+ { url = "https://files.pythonhosted.org/packages/39/04/78d2e7402fb479d893953fb78fa7045f7deb635ec095b6b4f0260223091a/numpy-2.2.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b787adbf04b0db1967798dba8da1af07e387908ed1553a0d6e74c084d1ceafe", size = 16116679 },
+ { url = "https://files.pythonhosted.org/packages/d0/a1/e90f7aa66512be3150cb9d27f3d9995db330ad1b2046474a13b7040dfd92/numpy-2.2.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:34c1b7e83f94f3b564b35f480f5652a47007dd91f7c839f404d03279cc8dd021", size = 15264272 },
+ { url = "https://files.pythonhosted.org/packages/dc/b6/50bd027cca494de4fa1fc7bf1662983d0ba5f256fa0ece2c376b5eb9b3f0/numpy-2.2.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4d8335b5f1b6e2bce120d55fb17064b0262ff29b459e8493d1785c18ae2553b8", size = 17880549 },
+ { url = "https://files.pythonhosted.org/packages/96/30/f7bf4acb5f8db10a96f73896bdeed7a63373137b131ca18bd3dab889db3b/numpy-2.2.3-cp312-cp312-win32.whl", hash = "sha256:4d9828d25fb246bedd31e04c9e75714a4087211ac348cb39c8c5f99dbb6683fe", size = 6293394 },
+ { url = "https://files.pythonhosted.org/packages/42/6e/55580a538116d16ae7c9aa17d4edd56e83f42126cb1dfe7a684da7925d2c/numpy-2.2.3-cp312-cp312-win_amd64.whl", hash = "sha256:83807d445817326b4bcdaaaf8e8e9f1753da04341eceec705c001ff342002e5d", size = 12626357 },
+ { url = "https://files.pythonhosted.org/packages/0e/8b/88b98ed534d6a03ba8cddb316950fe80842885709b58501233c29dfa24a9/numpy-2.2.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7bfdb06b395385ea9b91bf55c1adf1b297c9fdb531552845ff1d3ea6e40d5aba", size = 20916001 },
+ { url = "https://files.pythonhosted.org/packages/d9/b4/def6ec32c725cc5fbd8bdf8af80f616acf075fe752d8a23e895da8c67b70/numpy-2.2.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:23c9f4edbf4c065fddb10a4f6e8b6a244342d95966a48820c614891e5059bb50", size = 14130721 },
+ { url = "https://files.pythonhosted.org/packages/20/60/70af0acc86495b25b672d403e12cb25448d79a2b9658f4fc45e845c397a8/numpy-2.2.3-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:a0c03b6be48aaf92525cccf393265e02773be8fd9551a2f9adbe7db1fa2b60f1", size = 5130999 },
+ { url = "https://files.pythonhosted.org/packages/2e/69/d96c006fb73c9a47bcb3611417cf178049aae159afae47c48bd66df9c536/numpy-2.2.3-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:2376e317111daa0a6739e50f7ee2a6353f768489102308b0d98fcf4a04f7f3b5", size = 6665299 },
+ { url = "https://files.pythonhosted.org/packages/5a/3f/d8a877b6e48103733ac224ffa26b30887dc9944ff95dffdfa6c4ce3d7df3/numpy-2.2.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8fb62fe3d206d72fe1cfe31c4a1106ad2b136fcc1606093aeab314f02930fdf2", size = 14064096 },
+ { url = "https://files.pythonhosted.org/packages/e4/43/619c2c7a0665aafc80efca465ddb1f260287266bdbdce517396f2f145d49/numpy-2.2.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:52659ad2534427dffcc36aac76bebdd02b67e3b7a619ac67543bc9bfe6b7cdb1", size = 16114758 },
+ { url = "https://files.pythonhosted.org/packages/d9/79/ee4fe4f60967ccd3897aa71ae14cdee9e3c097e3256975cc9575d393cb42/numpy-2.2.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1b416af7d0ed3271cad0f0a0d0bee0911ed7eba23e66f8424d9f3dfcdcae1304", size = 15259880 },
+ { url = "https://files.pythonhosted.org/packages/fb/c8/8b55cf05db6d85b7a7d414b3d1bd5a740706df00bfa0824a08bf041e52ee/numpy-2.2.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1402da8e0f435991983d0a9708b779f95a8c98c6b18a171b9f1be09005e64d9d", size = 17876721 },
+ { url = "https://files.pythonhosted.org/packages/21/d6/b4c2f0564b7dcc413117b0ffbb818d837e4b29996b9234e38b2025ed24e7/numpy-2.2.3-cp313-cp313-win32.whl", hash = "sha256:136553f123ee2951bfcfbc264acd34a2fc2f29d7cdf610ce7daf672b6fbaa693", size = 6290195 },
+ { url = "https://files.pythonhosted.org/packages/97/e7/7d55a86719d0de7a6a597949f3febefb1009435b79ba510ff32f05a8c1d7/numpy-2.2.3-cp313-cp313-win_amd64.whl", hash = "sha256:5b732c8beef1d7bc2d9e476dbba20aaff6167bf205ad9aa8d30913859e82884b", size = 12619013 },
+ { url = "https://files.pythonhosted.org/packages/a6/1f/0b863d5528b9048fd486a56e0b97c18bf705e88736c8cea7239012119a54/numpy-2.2.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:435e7a933b9fda8126130b046975a968cc2d833b505475e588339e09f7672890", size = 20944621 },
+ { url = "https://files.pythonhosted.org/packages/aa/99/b478c384f7a0a2e0736177aafc97dc9152fc036a3fdb13f5a3ab225f1494/numpy-2.2.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:7678556eeb0152cbd1522b684dcd215250885993dd00adb93679ec3c0e6e091c", size = 14142502 },
+ { url = "https://files.pythonhosted.org/packages/fb/61/2d9a694a0f9cd0a839501d362de2a18de75e3004576a3008e56bdd60fcdb/numpy-2.2.3-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:2e8da03bd561504d9b20e7a12340870dfc206c64ea59b4cfee9fceb95070ee94", size = 5176293 },
+ { url = "https://files.pythonhosted.org/packages/33/35/51e94011b23e753fa33f891f601e5c1c9a3d515448659b06df9d40c0aa6e/numpy-2.2.3-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:c9aa4496fd0e17e3843399f533d62857cef5900facf93e735ef65aa4bbc90ef0", size = 6691874 },
+ { url = "https://files.pythonhosted.org/packages/ff/cf/06e37619aad98a9d03bd8d65b8e3041c3a639be0f5f6b0a0e2da544538d4/numpy-2.2.3-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4ca91d61a4bf61b0f2228f24bbfa6a9facd5f8af03759fe2a655c50ae2c6610", size = 14036826 },
+ { url = "https://files.pythonhosted.org/packages/0c/93/5d7d19955abd4d6099ef4a8ee006f9ce258166c38af259f9e5558a172e3e/numpy-2.2.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:deaa09cd492e24fd9b15296844c0ad1b3c976da7907e1c1ed3a0ad21dded6f76", size = 16096567 },
+ { url = "https://files.pythonhosted.org/packages/af/53/d1c599acf7732d81f46a93621dab6aa8daad914b502a7a115b3f17288ab2/numpy-2.2.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:246535e2f7496b7ac85deffe932896a3577be7af8fb7eebe7146444680297e9a", size = 15242514 },
+ { url = "https://files.pythonhosted.org/packages/53/43/c0f5411c7b3ea90adf341d05ace762dad8cb9819ef26093e27b15dd121ac/numpy-2.2.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:daf43a3d1ea699402c5a850e5313680ac355b4adc9770cd5cfc2940e7861f1bf", size = 17872920 },
+ { url = "https://files.pythonhosted.org/packages/5b/57/6dbdd45ab277aff62021cafa1e15f9644a52f5b5fc840bc7591b4079fb58/numpy-2.2.3-cp313-cp313t-win32.whl", hash = "sha256:cf802eef1f0134afb81fef94020351be4fe1d6681aadf9c5e862af6602af64ef", size = 6346584 },
+ { url = "https://files.pythonhosted.org/packages/97/9b/484f7d04b537d0a1202a5ba81c6f53f1846ae6c63c2127f8df869ed31342/numpy-2.2.3-cp313-cp313t-win_amd64.whl", hash = "sha256:aee2512827ceb6d7f517c8b85aa5d3923afe8fc7a57d028cffcd522f1c6fd082", size = 12706784 },
+]
+
+[[package]]
+name = "openai"
+version = "1.66.3"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "anyio" },
+ { name = "distro" },
+ { name = "httpx" },
+ { name = "jiter" },
+ { name = "pydantic" },
+ { name = "sniffio" },
+ { name = "tqdm" },
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/a3/77/5172104ca1df35ed2ed8fb26dbc787f721c39498fc51d666c4db07756a0c/openai-1.66.3.tar.gz", hash = "sha256:8dde3aebe2d081258d4159c4cb27bdc13b5bb3f7ea2201d9bd940b9a89faf0c9", size = 397244 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/78/5a/e20182f7b6171642d759c548daa0ba20a1d3ac10d2bd0a13fd75704a9ac3/openai-1.66.3-py3-none-any.whl", hash = "sha256:a427c920f727711877ab17c11b95f1230b27767ba7a01e5b66102945141ceca9", size = 567400 },
+]
+
+[[package]]
+name = "orjson"
+version = "3.10.15"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/ae/f9/5dea21763eeff8c1590076918a446ea3d6140743e0e36f58f369928ed0f4/orjson-3.10.15.tar.gz", hash = "sha256:05ca7fe452a2e9d8d9d706a2984c95b9c2ebc5db417ce0b7a49b91d50642a23e", size = 5282482 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/66/85/22fe737188905a71afcc4bf7cc4c79cd7f5bbe9ed1fe0aac4ce4c33edc30/orjson-3.10.15-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:9d11c0714fc85bfcf36ada1179400862da3288fc785c30e8297844c867d7505a", size = 249504 },
+ { url = "https://files.pythonhosted.org/packages/48/b7/2622b29f3afebe938a0a9037e184660379797d5fd5234e5998345d7a5b43/orjson-3.10.15-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dba5a1e85d554e3897fa9fe6fbcff2ed32d55008973ec9a2b992bd9a65d2352d", size = 125080 },
+ { url = "https://files.pythonhosted.org/packages/ce/8f/0b72a48f4403d0b88b2a41450c535b3e8989e8a2d7800659a967efc7c115/orjson-3.10.15-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7723ad949a0ea502df656948ddd8b392780a5beaa4c3b5f97e525191b102fff0", size = 150121 },
+ { url = "https://files.pythonhosted.org/packages/06/ec/acb1a20cd49edb2000be5a0404cd43e3c8aad219f376ac8c60b870518c03/orjson-3.10.15-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6fd9bc64421e9fe9bd88039e7ce8e58d4fead67ca88e3a4014b143cec7684fd4", size = 139796 },
+ { url = "https://files.pythonhosted.org/packages/33/e1/f7840a2ea852114b23a52a1c0b2bea0a1ea22236efbcdb876402d799c423/orjson-3.10.15-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dadba0e7b6594216c214ef7894c4bd5f08d7c0135f4dd0145600be4fbcc16767", size = 154636 },
+ { url = "https://files.pythonhosted.org/packages/fa/da/31543337febd043b8fa80a3b67de627669b88c7b128d9ad4cc2ece005b7a/orjson-3.10.15-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b48f59114fe318f33bbaee8ebeda696d8ccc94c9e90bc27dbe72153094e26f41", size = 130621 },
+ { url = "https://files.pythonhosted.org/packages/ed/78/66115dc9afbc22496530d2139f2f4455698be444c7c2475cb48f657cefc9/orjson-3.10.15-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:035fb83585e0f15e076759b6fedaf0abb460d1765b6a36f48018a52858443514", size = 138516 },
+ { url = "https://files.pythonhosted.org/packages/22/84/cd4f5fb5427ffcf823140957a47503076184cb1ce15bcc1165125c26c46c/orjson-3.10.15-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d13b7fe322d75bf84464b075eafd8e7dd9eae05649aa2a5354cfa32f43c59f17", size = 130762 },
+ { url = "https://files.pythonhosted.org/packages/93/1f/67596b711ba9f56dd75d73b60089c5c92057f1130bb3a25a0f53fb9a583b/orjson-3.10.15-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:7066b74f9f259849629e0d04db6609db4cf5b973248f455ba5d3bd58a4daaa5b", size = 414700 },
+ { url = "https://files.pythonhosted.org/packages/7c/0c/6a3b3271b46443d90efb713c3e4fe83fa8cd71cda0d11a0f69a03f437c6e/orjson-3.10.15-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:88dc3f65a026bd3175eb157fea994fca6ac7c4c8579fc5a86fc2114ad05705b7", size = 141077 },
+ { url = "https://files.pythonhosted.org/packages/3b/9b/33c58e0bfc788995eccd0d525ecd6b84b40d7ed182dd0751cd4c1322ac62/orjson-3.10.15-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b342567e5465bd99faa559507fe45e33fc76b9fb868a63f1642c6bc0735ad02a", size = 129898 },
+ { url = "https://files.pythonhosted.org/packages/01/c1/d577ecd2e9fa393366a1ea0a9267f6510d86e6c4bb1cdfb9877104cac44c/orjson-3.10.15-cp312-cp312-win32.whl", hash = "sha256:0a4f27ea5617828e6b58922fdbec67b0aa4bb844e2d363b9244c47fa2180e665", size = 142566 },
+ { url = "https://files.pythonhosted.org/packages/ed/eb/a85317ee1732d1034b92d56f89f1de4d7bf7904f5c8fb9dcdd5b1c83917f/orjson-3.10.15-cp312-cp312-win_amd64.whl", hash = "sha256:ef5b87e7aa9545ddadd2309efe6824bd3dd64ac101c15dae0f2f597911d46eaa", size = 133732 },
+ { url = "https://files.pythonhosted.org/packages/06/10/fe7d60b8da538e8d3d3721f08c1b7bff0491e8fa4dd3bf11a17e34f4730e/orjson-3.10.15-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:bae0e6ec2b7ba6895198cd981b7cca95d1487d0147c8ed751e5632ad16f031a6", size = 249399 },
+ { url = "https://files.pythonhosted.org/packages/6b/83/52c356fd3a61abd829ae7e4366a6fe8e8863c825a60d7ac5156067516edf/orjson-3.10.15-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f93ce145b2db1252dd86af37d4165b6faa83072b46e3995ecc95d4b2301b725a", size = 125044 },
+ { url = "https://files.pythonhosted.org/packages/55/b2/d06d5901408e7ded1a74c7c20d70e3a127057a6d21355f50c90c0f337913/orjson-3.10.15-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7c203f6f969210128af3acae0ef9ea6aab9782939f45f6fe02d05958fe761ef9", size = 150066 },
+ { url = "https://files.pythonhosted.org/packages/75/8c/60c3106e08dc593a861755781c7c675a566445cc39558677d505878d879f/orjson-3.10.15-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8918719572d662e18b8af66aef699d8c21072e54b6c82a3f8f6404c1f5ccd5e0", size = 139737 },
+ { url = "https://files.pythonhosted.org/packages/6a/8c/ae00d7d0ab8a4490b1efeb01ad4ab2f1982e69cc82490bf8093407718ff5/orjson-3.10.15-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f71eae9651465dff70aa80db92586ad5b92df46a9373ee55252109bb6b703307", size = 154804 },
+ { url = "https://files.pythonhosted.org/packages/22/86/65dc69bd88b6dd254535310e97bc518aa50a39ef9c5a2a5d518e7a223710/orjson-3.10.15-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e117eb299a35f2634e25ed120c37c641398826c2f5a3d3cc39f5993b96171b9e", size = 130583 },
+ { url = "https://files.pythonhosted.org/packages/bb/00/6fe01ededb05d52be42fabb13d93a36e51f1fd9be173bd95707d11a8a860/orjson-3.10.15-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:13242f12d295e83c2955756a574ddd6741c81e5b99f2bef8ed8d53e47a01e4b7", size = 138465 },
+ { url = "https://files.pythonhosted.org/packages/db/2f/4cc151c4b471b0cdc8cb29d3eadbce5007eb0475d26fa26ed123dca93b33/orjson-3.10.15-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7946922ada8f3e0b7b958cc3eb22cfcf6c0df83d1fe5521b4a100103e3fa84c8", size = 130742 },
+ { url = "https://files.pythonhosted.org/packages/9f/13/8a6109e4b477c518498ca37963d9c0eb1508b259725553fb53d53b20e2ea/orjson-3.10.15-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:b7155eb1623347f0f22c38c9abdd738b287e39b9982e1da227503387b81b34ca", size = 414669 },
+ { url = "https://files.pythonhosted.org/packages/22/7b/1d229d6d24644ed4d0a803de1b0e2df832032d5beda7346831c78191b5b2/orjson-3.10.15-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:208beedfa807c922da4e81061dafa9c8489c6328934ca2a562efa707e049e561", size = 141043 },
+ { url = "https://files.pythonhosted.org/packages/cc/d3/6dc91156cf12ed86bed383bcb942d84d23304a1e57b7ab030bf60ea130d6/orjson-3.10.15-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eca81f83b1b8c07449e1d6ff7074e82e3fd6777e588f1a6632127f286a968825", size = 129826 },
+ { url = "https://files.pythonhosted.org/packages/b3/38/c47c25b86f6996f1343be721b6ea4367bc1c8bc0fc3f6bbcd995d18cb19d/orjson-3.10.15-cp313-cp313-win32.whl", hash = "sha256:c03cd6eea1bd3b949d0d007c8d57049aa2b39bd49f58b4b2af571a5d3833d890", size = 142542 },
+ { url = "https://files.pythonhosted.org/packages/27/f1/1d7ec15b20f8ce9300bc850de1e059132b88990e46cd0ccac29cbf11e4f9/orjson-3.10.15-cp313-cp313-win_amd64.whl", hash = "sha256:fd56a26a04f6ba5fb2045b0acc487a63162a958ed837648c5781e1fe3316cfbf", size = 133444 },
+]
+
+[[package]]
+name = "packaging"
+version = "24.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/d0/63/68dbb6eb2de9cb10ee4c9c14a0148804425e13c4fb20d61cce69f53106da/packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f", size = 163950 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451 },
+]
+
+[[package]]
+name = "pandas"
+version = "2.2.3"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "numpy" },
+ { name = "python-dateutil" },
+ { name = "pytz" },
+ { name = "tzdata" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/9c/d6/9f8431bacc2e19dca897724cd097b1bb224a6ad5433784a44b587c7c13af/pandas-2.2.3.tar.gz", hash = "sha256:4f18ba62b61d7e192368b84517265a99b4d7ee8912f8708660fb4a366cc82667", size = 4399213 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/17/a3/fb2734118db0af37ea7433f57f722c0a56687e14b14690edff0cdb4b7e58/pandas-2.2.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b1d432e8d08679a40e2a6d8b2f9770a5c21793a6f9f47fdd52c5ce1948a5a8a9", size = 12529893 },
+ { url = "https://files.pythonhosted.org/packages/e1/0c/ad295fd74bfac85358fd579e271cded3ac969de81f62dd0142c426b9da91/pandas-2.2.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a5a1595fe639f5988ba6a8e5bc9649af3baf26df3998a0abe56c02609392e0a4", size = 11363475 },
+ { url = "https://files.pythonhosted.org/packages/c6/2a/4bba3f03f7d07207481fed47f5b35f556c7441acddc368ec43d6643c5777/pandas-2.2.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5de54125a92bb4d1c051c0659e6fcb75256bf799a732a87184e5ea503965bce3", size = 15188645 },
+ { url = "https://files.pythonhosted.org/packages/38/f8/d8fddee9ed0d0c0f4a2132c1dfcf0e3e53265055da8df952a53e7eaf178c/pandas-2.2.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fffb8ae78d8af97f849404f21411c95062db1496aeb3e56f146f0355c9989319", size = 12739445 },
+ { url = "https://files.pythonhosted.org/packages/20/e8/45a05d9c39d2cea61ab175dbe6a2de1d05b679e8de2011da4ee190d7e748/pandas-2.2.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6dfcb5ee8d4d50c06a51c2fffa6cff6272098ad6540aed1a76d15fb9318194d8", size = 16359235 },
+ { url = "https://files.pythonhosted.org/packages/1d/99/617d07a6a5e429ff90c90da64d428516605a1ec7d7bea494235e1c3882de/pandas-2.2.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:062309c1b9ea12a50e8ce661145c6aab431b1e99530d3cd60640e255778bd43a", size = 14056756 },
+ { url = "https://files.pythonhosted.org/packages/29/d4/1244ab8edf173a10fd601f7e13b9566c1b525c4f365d6bee918e68381889/pandas-2.2.3-cp312-cp312-win_amd64.whl", hash = "sha256:59ef3764d0fe818125a5097d2ae867ca3fa64df032331b7e0917cf5d7bf66b13", size = 11504248 },
+ { url = "https://files.pythonhosted.org/packages/64/22/3b8f4e0ed70644e85cfdcd57454686b9057c6c38d2f74fe4b8bc2527214a/pandas-2.2.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f00d1345d84d8c86a63e476bb4955e46458b304b9575dcf71102b5c705320015", size = 12477643 },
+ { url = "https://files.pythonhosted.org/packages/e4/93/b3f5d1838500e22c8d793625da672f3eec046b1a99257666c94446969282/pandas-2.2.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3508d914817e153ad359d7e069d752cdd736a247c322d932eb89e6bc84217f28", size = 11281573 },
+ { url = "https://files.pythonhosted.org/packages/f5/94/6c79b07f0e5aab1dcfa35a75f4817f5c4f677931d4234afcd75f0e6a66ca/pandas-2.2.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:22a9d949bfc9a502d320aa04e5d02feab689d61da4e7764b62c30b991c42c5f0", size = 15196085 },
+ { url = "https://files.pythonhosted.org/packages/e8/31/aa8da88ca0eadbabd0a639788a6da13bb2ff6edbbb9f29aa786450a30a91/pandas-2.2.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3a255b2c19987fbbe62a9dfd6cff7ff2aa9ccab3fc75218fd4b7530f01efa24", size = 12711809 },
+ { url = "https://files.pythonhosted.org/packages/ee/7c/c6dbdb0cb2a4344cacfb8de1c5808ca885b2e4dcfde8008266608f9372af/pandas-2.2.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:800250ecdadb6d9c78eae4990da62743b857b470883fa27f652db8bdde7f6659", size = 16356316 },
+ { url = "https://files.pythonhosted.org/packages/57/b7/8b757e7d92023b832869fa8881a992696a0bfe2e26f72c9ae9f255988d42/pandas-2.2.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6374c452ff3ec675a8f46fd9ab25c4ad0ba590b71cf0656f8b6daa5202bca3fb", size = 14022055 },
+ { url = "https://files.pythonhosted.org/packages/3b/bc/4b18e2b8c002572c5a441a64826252ce5da2aa738855747247a971988043/pandas-2.2.3-cp313-cp313-win_amd64.whl", hash = "sha256:61c5ad4043f791b61dd4752191d9f07f0ae412515d59ba8f005832a532f8736d", size = 11481175 },
+ { url = "https://files.pythonhosted.org/packages/76/a3/a5d88146815e972d40d19247b2c162e88213ef51c7c25993942c39dbf41d/pandas-2.2.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:3b71f27954685ee685317063bf13c7709a7ba74fc996b84fc6821c59b0f06468", size = 12615650 },
+ { url = "https://files.pythonhosted.org/packages/9c/8c/f0fd18f6140ddafc0c24122c8a964e48294acc579d47def376fef12bcb4a/pandas-2.2.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:38cf8125c40dae9d5acc10fa66af8ea6fdf760b2714ee482ca691fc66e6fcb18", size = 11290177 },
+ { url = "https://files.pythonhosted.org/packages/ed/f9/e995754eab9c0f14c6777401f7eece0943840b7a9fc932221c19d1abee9f/pandas-2.2.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ba96630bc17c875161df3818780af30e43be9b166ce51c9a18c1feae342906c2", size = 14651526 },
+ { url = "https://files.pythonhosted.org/packages/25/b0/98d6ae2e1abac4f35230aa756005e8654649d305df9a28b16b9ae4353bff/pandas-2.2.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1db71525a1538b30142094edb9adc10be3f3e176748cd7acc2240c2f2e5aa3a4", size = 11871013 },
+ { url = "https://files.pythonhosted.org/packages/cc/57/0f72a10f9db6a4628744c8e8f0df4e6e21de01212c7c981d31e50ffc8328/pandas-2.2.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:15c0e1e02e93116177d29ff83e8b1619c93ddc9c49083f237d4312337a61165d", size = 15711620 },
+ { url = "https://files.pythonhosted.org/packages/ab/5f/b38085618b950b79d2d9164a711c52b10aefc0ae6833b96f626b7021b2ed/pandas-2.2.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:ad5b65698ab28ed8d7f18790a0dc58005c7629f227be9ecc1072aa74c0c1d43a", size = 13098436 },
+]
+
+[[package]]
+name = "pathos"
+version = "0.3.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "dill" },
+ { name = "multiprocess" },
+ { name = "pox" },
+ { name = "ppft" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/be/99/7fcb91495e40735958a576b9bde930cc402d594e9ad5277bdc9b6326e1c8/pathos-0.3.2.tar.gz", hash = "sha256:4f2a42bc1e10ccf0fe71961e7145fc1437018b6b21bd93b2446abc3983e49a7a", size = 166506 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/f4/7f/cea34872c000d17972dad998575d14656d7c6bcf1a08a8d66d73c1ef2cca/pathos-0.3.2-py3-none-any.whl", hash = "sha256:d669275e6eb4b3fbcd2846d7a6d1bba315fe23add0c614445ba1408d8b38bafe", size = 82075 },
+]
+
+[[package]]
+name = "pathspec"
+version = "0.12.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191 },
+]
+
+[[package]]
+name = "pip"
+version = "25.0.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/70/53/b309b4a497b09655cb7e07088966881a57d082f48ac3cb54ea729fd2c6cf/pip-25.0.1.tar.gz", hash = "sha256:88f96547ea48b940a3a385494e181e29fb8637898f88d88737c5049780f196ea", size = 1950850 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c9/bc/b7db44f5f39f9d0494071bddae6880eb645970366d0a200022a1a93d57f5/pip-25.0.1-py3-none-any.whl", hash = "sha256:c46efd13b6aa8279f33f2864459c8ce587ea6a1a59ee20de055868d8f7688f7f", size = 1841526 },
+]
+
+[[package]]
+name = "platformdirs"
+version = "4.3.6"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/13/fc/128cc9cb8f03208bdbf93d3aa862e16d376844a14f9a0ce5cf4507372de4/platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907", size = 21302 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/3c/a6/bc1012356d8ece4d66dd75c4b9fc6c1f6650ddd5991e421177d9f8f671be/platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb", size = 18439 },
+]
+
+[[package]]
+name = "plotly"
+version = "6.0.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "narwhals" },
+ { name = "packaging" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/9c/80/761c14012d6daf18e12b6d1e4f6b218e999bcceb694d7a9b180154f9e4db/plotly-6.0.0.tar.gz", hash = "sha256:c4aad38b8c3d65e4a5e7dd308b084143b9025c2cc9d5317fc1f1d30958db87d3", size = 8111782 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/0e/77/a946f38b57fb88e736c71fbdd737a1aebd27b532bda0779c137f357cf5fc/plotly-6.0.0-py3-none-any.whl", hash = "sha256:f708871c3a9349a68791ff943a5781b1ec04de7769ea69068adcd9202e57653a", size = 14805949 },
+]
+
+[[package]]
+name = "pluggy"
+version = "1.5.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/96/2d/02d4312c973c6050a18b314a5ad0b3210edb65a906f868e31c111dede4a6/pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", size = 67955 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556 },
+]
+
+[[package]]
+name = "pox"
+version = "0.3.5"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/2e/0d/f2eb94b4d1358a60f3539a6abcbbd757fbcb78538fe8d4cfa49850356ccf/pox-0.3.5.tar.gz", hash = "sha256:8120ee4c94e950e6e0483e050a4f0e56076e590ba0a9add19524c254bd23c2d1", size = 119452 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/1d/4c/490d8f7825f38fa77bff188c568163f222d01f6c6d76f574429135edfc49/pox-0.3.5-py3-none-any.whl", hash = "sha256:9e82bcc9e578b43e80a99cad80f0d8f44f4d424f0ee4ee8d4db27260a6aa365a", size = 29492 },
+]
+
+[[package]]
+name = "ppft"
+version = "1.7.6.9"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/2b/06/305532df3e1b0c601f60854b6e080991835809d077934cf41976d0f224ce/ppft-1.7.6.9.tar.gz", hash = "sha256:73161c67474ea9d81d04bcdad166d399cff3f084d5d2dc21ebdd46c075bbc265", size = 136395 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/02/b3/45a04dabc39d93ad4836d99625e7c5350257b48e9ae2c5b701f3d5da6960/ppft-1.7.6.9-py3-none-any.whl", hash = "sha256:dab36548db5ca3055067fbe6b1a17db5fee29f3c366c579a9a27cebb52ed96f0", size = 56792 },
+]
+
+[[package]]
+name = "pre-commit"
+version = "4.1.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "cfgv" },
+ { name = "identify" },
+ { name = "nodeenv" },
+ { name = "pyyaml" },
+ { name = "virtualenv" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/2a/13/b62d075317d8686071eb843f0bb1f195eb332f48869d3c31a4c6f1e063ac/pre_commit-4.1.0.tar.gz", hash = "sha256:ae3f018575a588e30dfddfab9a05448bfbd6b73d78709617b5a2b853549716d4", size = 193330 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/43/b3/df14c580d82b9627d173ceea305ba898dca135feb360b6d84019d0803d3b/pre_commit-4.1.0-py2.py3-none-any.whl", hash = "sha256:d29e7cb346295bcc1cc75fc3e92e343495e3ea0196c9ec6ba53f49f10ab6ae7b", size = 220560 },
+]
+
+[[package]]
+name = "propcache"
+version = "0.3.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/92/76/f941e63d55c0293ff7829dd21e7cf1147e90a526756869a9070f287a68c9/propcache-0.3.0.tar.gz", hash = "sha256:a8fd93de4e1d278046345f49e2238cdb298589325849b2645d4a94c53faeffc5", size = 42722 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/8d/2c/921f15dc365796ec23975b322b0078eae72995c7b4d49eba554c6a308d70/propcache-0.3.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e53d19c2bf7d0d1e6998a7e693c7e87300dd971808e6618964621ccd0e01fe4e", size = 79867 },
+ { url = "https://files.pythonhosted.org/packages/11/a5/4a6cc1a559d1f2fb57ea22edc4245158cdffae92f7f92afcee2913f84417/propcache-0.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a61a68d630e812b67b5bf097ab84e2cd79b48c792857dc10ba8a223f5b06a2af", size = 46109 },
+ { url = "https://files.pythonhosted.org/packages/e1/6d/28bfd3af3a567ad7d667348e7f46a520bda958229c4d545ba138a044232f/propcache-0.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fb91d20fa2d3b13deea98a690534697742029f4fb83673a3501ae6e3746508b5", size = 45635 },
+ { url = "https://files.pythonhosted.org/packages/73/20/d75b42eaffe5075eac2f4e168f6393d21c664c91225288811d85451b2578/propcache-0.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67054e47c01b7b349b94ed0840ccae075449503cf1fdd0a1fdd98ab5ddc2667b", size = 242159 },
+ { url = "https://files.pythonhosted.org/packages/a5/fb/4b537dd92f9fd4be68042ec51c9d23885ca5fafe51ec24c58d9401034e5f/propcache-0.3.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:997e7b8f173a391987df40f3b52c423e5850be6f6df0dcfb5376365440b56667", size = 248163 },
+ { url = "https://files.pythonhosted.org/packages/e7/af/8a9db04ac596d531ca0ef7dde518feaadfcdabef7b17d6a5ec59ee3effc2/propcache-0.3.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d663fd71491dde7dfdfc899d13a067a94198e90695b4321084c6e450743b8c7", size = 248794 },
+ { url = "https://files.pythonhosted.org/packages/9d/c4/ecfc988879c0fd9db03228725b662d76cf484b6b46f7e92fee94e4b52490/propcache-0.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8884ba1a0fe7210b775106b25850f5e5a9dc3c840d1ae9924ee6ea2eb3acbfe7", size = 243912 },
+ { url = "https://files.pythonhosted.org/packages/04/a2/298dd27184faa8b7d91cc43488b578db218b3cc85b54d912ed27b8c5597a/propcache-0.3.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aa806bbc13eac1ab6291ed21ecd2dd426063ca5417dd507e6be58de20e58dfcf", size = 229402 },
+ { url = "https://files.pythonhosted.org/packages/be/0d/efe7fec316ca92dbf4bc4a9ba49ca889c43ca6d48ab1d6fa99fc94e5bb98/propcache-0.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6f4d7a7c0aff92e8354cceca6fe223973ddf08401047920df0fcb24be2bd5138", size = 226896 },
+ { url = "https://files.pythonhosted.org/packages/60/63/72404380ae1d9c96d96e165aa02c66c2aae6072d067fc4713da5cde96762/propcache-0.3.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:9be90eebc9842a93ef8335291f57b3b7488ac24f70df96a6034a13cb58e6ff86", size = 221447 },
+ { url = "https://files.pythonhosted.org/packages/9d/18/b8392cab6e0964b67a30a8f4dadeaff64dc7022b5a34bb1d004ea99646f4/propcache-0.3.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:bf15fc0b45914d9d1b706f7c9c4f66f2b7b053e9517e40123e137e8ca8958b3d", size = 222440 },
+ { url = "https://files.pythonhosted.org/packages/6f/be/105d9ceda0f97eff8c06bac1673448b2db2a497444de3646464d3f5dc881/propcache-0.3.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5a16167118677d94bb48bfcd91e420088854eb0737b76ec374b91498fb77a70e", size = 234104 },
+ { url = "https://files.pythonhosted.org/packages/cb/c9/f09a4ec394cfcce4053d8b2a04d622b5f22d21ba9bb70edd0cad061fa77b/propcache-0.3.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:41de3da5458edd5678b0f6ff66691507f9885f5fe6a0fb99a5d10d10c0fd2d64", size = 239086 },
+ { url = "https://files.pythonhosted.org/packages/ea/aa/96f7f9ed6def82db67c972bdb7bd9f28b95d7d98f7e2abaf144c284bf609/propcache-0.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:728af36011bb5d344c4fe4af79cfe186729efb649d2f8b395d1572fb088a996c", size = 230991 },
+ { url = "https://files.pythonhosted.org/packages/5a/11/bee5439de1307d06fad176f7143fec906e499c33d7aff863ea8428b8e98b/propcache-0.3.0-cp312-cp312-win32.whl", hash = "sha256:6b5b7fd6ee7b54e01759f2044f936dcf7dea6e7585f35490f7ca0420fe723c0d", size = 40337 },
+ { url = "https://files.pythonhosted.org/packages/e4/17/e5789a54a0455a61cb9efc4ca6071829d992220c2998a27c59aeba749f6f/propcache-0.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:2d15bc27163cd4df433e75f546b9ac31c1ba7b0b128bfb1b90df19082466ff57", size = 44404 },
+ { url = "https://files.pythonhosted.org/packages/3a/0f/a79dd23a0efd6ee01ab0dc9750d8479b343bfd0c73560d59d271eb6a99d4/propcache-0.3.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a2b9bf8c79b660d0ca1ad95e587818c30ccdb11f787657458d6f26a1ea18c568", size = 77287 },
+ { url = "https://files.pythonhosted.org/packages/b8/51/76675703c90de38ac75adb8deceb3f3ad99b67ff02a0fa5d067757971ab8/propcache-0.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b0c1a133d42c6fc1f5fbcf5c91331657a1ff822e87989bf4a6e2e39b818d0ee9", size = 44923 },
+ { url = "https://files.pythonhosted.org/packages/01/9b/fd5ddbee66cf7686e73c516227c2fd9bf471dbfed0f48329d095ea1228d3/propcache-0.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bb2f144c6d98bb5cbc94adeb0447cfd4c0f991341baa68eee3f3b0c9c0e83767", size = 44325 },
+ { url = "https://files.pythonhosted.org/packages/13/1c/6961f11eb215a683b34b903b82bde486c606516c1466bf1fa67f26906d51/propcache-0.3.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1323cd04d6e92150bcc79d0174ce347ed4b349d748b9358fd2e497b121e03c8", size = 225116 },
+ { url = "https://files.pythonhosted.org/packages/ef/ea/f8410c40abcb2e40dffe9adeed017898c930974650a63e5c79b886aa9f73/propcache-0.3.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b812b3cb6caacd072276ac0492d249f210006c57726b6484a1e1805b3cfeea0", size = 229905 },
+ { url = "https://files.pythonhosted.org/packages/ef/5a/a9bf90894001468bf8e6ea293bb00626cc9ef10f8eb7996e9ec29345c7ed/propcache-0.3.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:742840d1d0438eb7ea4280f3347598f507a199a35a08294afdcc560c3739989d", size = 233221 },
+ { url = "https://files.pythonhosted.org/packages/dd/ce/fffdddd9725b690b01d345c1156b4c2cc6dca09ab5c23a6d07b8f37d6e2f/propcache-0.3.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7c6e7e4f9167fddc438cd653d826f2222222564daed4116a02a184b464d3ef05", size = 227627 },
+ { url = "https://files.pythonhosted.org/packages/58/ae/45c89a5994a334735a3032b48e8e4a98c05d9536ddee0719913dc27da548/propcache-0.3.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a94ffc66738da99232ddffcf7910e0f69e2bbe3a0802e54426dbf0714e1c2ffe", size = 214217 },
+ { url = "https://files.pythonhosted.org/packages/01/84/bc60188c3290ff8f5f4a92b9ca2d93a62e449c8daf6fd11ad517ad136926/propcache-0.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3c6ec957025bf32b15cbc6b67afe233c65b30005e4c55fe5768e4bb518d712f1", size = 212921 },
+ { url = "https://files.pythonhosted.org/packages/14/b3/39d60224048feef7a96edabb8217dc3f75415457e5ebbef6814f8b2a27b5/propcache-0.3.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:549722908de62aa0b47a78b90531c022fa6e139f9166be634f667ff45632cc92", size = 208200 },
+ { url = "https://files.pythonhosted.org/packages/9d/b3/0a6720b86791251273fff8a01bc8e628bc70903513bd456f86cde1e1ef84/propcache-0.3.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:5d62c4f6706bff5d8a52fd51fec6069bef69e7202ed481486c0bc3874912c787", size = 208400 },
+ { url = "https://files.pythonhosted.org/packages/e9/4f/bb470f3e687790547e2e78105fb411f54e0cdde0d74106ccadd2521c6572/propcache-0.3.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:24c04f8fbf60094c531667b8207acbae54146661657a1b1be6d3ca7773b7a545", size = 218116 },
+ { url = "https://files.pythonhosted.org/packages/34/71/277f7f9add469698ac9724c199bfe06f85b199542121a71f65a80423d62a/propcache-0.3.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:7c5f5290799a3f6539cc5e6f474c3e5c5fbeba74a5e1e5be75587746a940d51e", size = 222911 },
+ { url = "https://files.pythonhosted.org/packages/92/e3/a7b9782aef5a2fc765b1d97da9ec7aed2f25a4e985703608e73232205e3f/propcache-0.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4fa0e7c9c3cf7c276d4f6ab9af8adddc127d04e0fcabede315904d2ff76db626", size = 216563 },
+ { url = "https://files.pythonhosted.org/packages/ab/76/0583ca2c551aa08ffcff87b2c6849c8f01c1f6fb815a5226f0c5c202173e/propcache-0.3.0-cp313-cp313-win32.whl", hash = "sha256:ee0bd3a7b2e184e88d25c9baa6a9dc609ba25b76daae942edfb14499ac7ec374", size = 39763 },
+ { url = "https://files.pythonhosted.org/packages/80/ec/c6a84f9a36f608379b95f0e786c111d5465926f8c62f12be8cdadb02b15c/propcache-0.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:1c8f7d896a16da9455f882870a507567d4f58c53504dc2d4b1e1d386dfe4588a", size = 43650 },
+ { url = "https://files.pythonhosted.org/packages/ee/95/7d32e3560f5bf83fc2f2a4c1b0c181d327d53d5f85ebd045ab89d4d97763/propcache-0.3.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:e560fd75aaf3e5693b91bcaddd8b314f4d57e99aef8a6c6dc692f935cc1e6bbf", size = 82140 },
+ { url = "https://files.pythonhosted.org/packages/86/89/752388f12e6027a5e63f5d075f15291ded48e2d8311314fff039da5a9b11/propcache-0.3.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:65a37714b8ad9aba5780325228598a5b16c47ba0f8aeb3dc0514701e4413d7c0", size = 47296 },
+ { url = "https://files.pythonhosted.org/packages/1b/4c/b55c98d586c69180d3048984a57a5ea238bdeeccf82dbfcd598e935e10bb/propcache-0.3.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:07700939b2cbd67bfb3b76a12e1412405d71019df00ca5697ce75e5ef789d829", size = 46724 },
+ { url = "https://files.pythonhosted.org/packages/0f/b6/67451a437aed90c4e951e320b5b3d7eb584ade1d5592f6e5e8f678030989/propcache-0.3.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7c0fdbdf6983526e269e5a8d53b7ae3622dd6998468821d660d0daf72779aefa", size = 291499 },
+ { url = "https://files.pythonhosted.org/packages/ee/ff/e4179facd21515b24737e1e26e02615dfb5ed29416eed4cf5bc6ac5ce5fb/propcache-0.3.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:794c3dd744fad478b6232289c866c25406ecdfc47e294618bdf1697e69bd64a6", size = 293911 },
+ { url = "https://files.pythonhosted.org/packages/76/8d/94a8585992a064a23bd54f56c5e58c3b8bf0c0a06ae10e56f2353ae16c3d/propcache-0.3.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4544699674faf66fb6b4473a1518ae4999c1b614f0b8297b1cef96bac25381db", size = 293301 },
+ { url = "https://files.pythonhosted.org/packages/b0/b8/2c860c92b4134f68c7716c6f30a0d723973f881c32a6d7a24c4ddca05fdf/propcache-0.3.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fddb8870bdb83456a489ab67c6b3040a8d5a55069aa6f72f9d872235fbc52f54", size = 281947 },
+ { url = "https://files.pythonhosted.org/packages/cd/72/b564be7411b525d11757b713c757c21cd4dc13b6569c3b2b8f6d3c96fd5e/propcache-0.3.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f857034dc68d5ceb30fb60afb6ff2103087aea10a01b613985610e007053a121", size = 268072 },
+ { url = "https://files.pythonhosted.org/packages/37/68/d94649e399e8d7fc051e5a4f2334efc567993525af083db145a70690a121/propcache-0.3.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:02df07041e0820cacc8f739510078f2aadcfd3fc57eaeeb16d5ded85c872c89e", size = 275190 },
+ { url = "https://files.pythonhosted.org/packages/d8/3c/446e125f5bbbc1922964dd67cb541c01cdb678d811297b79a4ff6accc843/propcache-0.3.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:f47d52fd9b2ac418c4890aad2f6d21a6b96183c98021f0a48497a904199f006e", size = 254145 },
+ { url = "https://files.pythonhosted.org/packages/f4/80/fd3f741483dc8e59f7ba7e05eaa0f4e11677d7db2077522b92ff80117a2a/propcache-0.3.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:9ff4e9ecb6e4b363430edf2c6e50173a63e0820e549918adef70515f87ced19a", size = 257163 },
+ { url = "https://files.pythonhosted.org/packages/dc/cf/6292b5ce6ed0017e6a89024a827292122cc41b6259b30ada0c6732288513/propcache-0.3.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:ecc2920630283e0783c22e2ac94427f8cca29a04cfdf331467d4f661f4072dac", size = 280249 },
+ { url = "https://files.pythonhosted.org/packages/e8/f0/fd9b8247b449fe02a4f96538b979997e229af516d7462b006392badc59a1/propcache-0.3.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:c441c841e82c5ba7a85ad25986014be8d7849c3cfbdb6004541873505929a74e", size = 288741 },
+ { url = "https://files.pythonhosted.org/packages/64/71/cf831fdc2617f86cfd7f414cfc487d018e722dac8acc098366ce9bba0941/propcache-0.3.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6c929916cbdb540d3407c66f19f73387f43e7c12fa318a66f64ac99da601bcdf", size = 277061 },
+ { url = "https://files.pythonhosted.org/packages/42/78/9432542a35d944abeca9e02927a0de38cd7a298466d8ffa171536e2381c3/propcache-0.3.0-cp313-cp313t-win32.whl", hash = "sha256:0c3e893c4464ebd751b44ae76c12c5f5c1e4f6cbd6fbf67e3783cd93ad221863", size = 42252 },
+ { url = "https://files.pythonhosted.org/packages/6f/45/960365f4f8978f48ebb56b1127adf33a49f2e69ecd46ac1f46d6cf78a79d/propcache-0.3.0-cp313-cp313t-win_amd64.whl", hash = "sha256:75e872573220d1ee2305b35c9813626e620768248425f58798413e9c39741f46", size = 46425 },
+ { url = "https://files.pythonhosted.org/packages/b5/35/6c4c6fc8774a9e3629cd750dc24a7a4fb090a25ccd5c3246d127b70f9e22/propcache-0.3.0-py3-none-any.whl", hash = "sha256:67dda3c7325691c2081510e92c561f465ba61b975f481735aefdfc845d2cd043", size = 12101 },
+]
+
+[[package]]
+name = "protobuf"
+version = "5.29.3"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/f7/d1/e0a911544ca9993e0f17ce6d3cc0932752356c1b0a834397f28e63479344/protobuf-5.29.3.tar.gz", hash = "sha256:5da0f41edaf117bde316404bad1a486cb4ededf8e4a54891296f648e8e076620", size = 424945 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/dc/7a/1e38f3cafa022f477ca0f57a1f49962f21ad25850c3ca0acd3b9d0091518/protobuf-5.29.3-cp310-abi3-win32.whl", hash = "sha256:3ea51771449e1035f26069c4c7fd51fba990d07bc55ba80701c78f886bf9c888", size = 422708 },
+ { url = "https://files.pythonhosted.org/packages/61/fa/aae8e10512b83de633f2646506a6d835b151edf4b30d18d73afd01447253/protobuf-5.29.3-cp310-abi3-win_amd64.whl", hash = "sha256:a4fa6f80816a9a0678429e84973f2f98cbc218cca434abe8db2ad0bffc98503a", size = 434508 },
+ { url = "https://files.pythonhosted.org/packages/dd/04/3eaedc2ba17a088961d0e3bd396eac764450f431621b58a04ce898acd126/protobuf-5.29.3-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:a8434404bbf139aa9e1300dbf989667a83d42ddda9153d8ab76e0d5dcaca484e", size = 417825 },
+ { url = "https://files.pythonhosted.org/packages/4f/06/7c467744d23c3979ce250397e26d8ad8eeb2bea7b18ca12ad58313c1b8d5/protobuf-5.29.3-cp38-abi3-manylinux2014_aarch64.whl", hash = "sha256:daaf63f70f25e8689c072cfad4334ca0ac1d1e05a92fc15c54eb9cf23c3efd84", size = 319573 },
+ { url = "https://files.pythonhosted.org/packages/a8/45/2ebbde52ad2be18d3675b6bee50e68cd73c9e0654de77d595540b5129df8/protobuf-5.29.3-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:c027e08a08be10b67c06bf2370b99c811c466398c357e615ca88c91c07f0910f", size = 319672 },
+ { url = "https://files.pythonhosted.org/packages/fd/b2/ab07b09e0f6d143dfb839693aa05765257bceaa13d03bf1a696b78323e7a/protobuf-5.29.3-py3-none-any.whl", hash = "sha256:0a18ed4a24198528f2333802eb075e59dea9d679ab7a6c5efb017a59004d849f", size = 172550 },
+]
+
+[[package]]
+name = "psutil"
+version = "7.0.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/2a/80/336820c1ad9286a4ded7e845b2eccfcb27851ab8ac6abece774a6ff4d3de/psutil-7.0.0.tar.gz", hash = "sha256:7be9c3eba38beccb6495ea33afd982a44074b78f28c434a1f51cc07fd315c456", size = 497003 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/ed/e6/2d26234410f8b8abdbf891c9da62bee396583f713fb9f3325a4760875d22/psutil-7.0.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:101d71dc322e3cffd7cea0650b09b3d08b8e7c4109dd6809fe452dfd00e58b25", size = 238051 },
+ { url = "https://files.pythonhosted.org/packages/04/8b/30f930733afe425e3cbfc0e1468a30a18942350c1a8816acfade80c005c4/psutil-7.0.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:39db632f6bb862eeccf56660871433e111b6ea58f2caea825571951d4b6aa3da", size = 239535 },
+ { url = "https://files.pythonhosted.org/packages/2a/ed/d362e84620dd22876b55389248e522338ed1bf134a5edd3b8231d7207f6d/psutil-7.0.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1fcee592b4c6f146991ca55919ea3d1f8926497a713ed7faaf8225e174581e91", size = 275004 },
+ { url = "https://files.pythonhosted.org/packages/bf/b9/b0eb3f3cbcb734d930fdf839431606844a825b23eaf9a6ab371edac8162c/psutil-7.0.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b1388a4f6875d7e2aff5c4ca1cc16c545ed41dd8bb596cefea80111db353a34", size = 277986 },
+ { url = "https://files.pythonhosted.org/packages/eb/a2/709e0fe2f093556c17fbafda93ac032257242cabcc7ff3369e2cb76a97aa/psutil-7.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5f098451abc2828f7dc6b58d44b532b22f2088f4999a937557b603ce72b1993", size = 279544 },
+ { url = "https://files.pythonhosted.org/packages/50/e6/eecf58810b9d12e6427369784efe814a1eec0f492084ce8eb8f4d89d6d61/psutil-7.0.0-cp37-abi3-win32.whl", hash = "sha256:ba3fcef7523064a6c9da440fc4d6bd07da93ac726b5733c29027d7dc95b39d99", size = 241053 },
+ { url = "https://files.pythonhosted.org/packages/50/1b/6921afe68c74868b4c9fa424dad3be35b095e16687989ebbb50ce4fceb7c/psutil-7.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:4cf3d4eb1aa9b348dec30105c55cd9b7d4629285735a102beb4441e38db90553", size = 244885 },
+]
+
+[[package]]
+name = "psycopg2-binary"
+version = "2.9.10"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/cb/0e/bdc8274dc0585090b4e3432267d7be4dfbfd8971c0fa59167c711105a6bf/psycopg2-binary-2.9.10.tar.gz", hash = "sha256:4b3df0e6990aa98acda57d983942eff13d824135fe2250e6522edaa782a06de2", size = 385764 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/49/7d/465cc9795cf76f6d329efdafca74693714556ea3891813701ac1fee87545/psycopg2_binary-2.9.10-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:880845dfe1f85d9d5f7c412efea7a08946a46894537e4e5d091732eb1d34d9a0", size = 3044771 },
+ { url = "https://files.pythonhosted.org/packages/8b/31/6d225b7b641a1a2148e3ed65e1aa74fc86ba3fee850545e27be9e1de893d/psycopg2_binary-2.9.10-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:9440fa522a79356aaa482aa4ba500b65f28e5d0e63b801abf6aa152a29bd842a", size = 3275336 },
+ { url = "https://files.pythonhosted.org/packages/30/b7/a68c2b4bff1cbb1728e3ec864b2d92327c77ad52edcd27922535a8366f68/psycopg2_binary-2.9.10-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e3923c1d9870c49a2d44f795df0c889a22380d36ef92440ff618ec315757e539", size = 2851637 },
+ { url = "https://files.pythonhosted.org/packages/0b/b1/cfedc0e0e6f9ad61f8657fd173b2f831ce261c02a08c0b09c652b127d813/psycopg2_binary-2.9.10-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7b2c956c028ea5de47ff3a8d6b3cc3330ab45cf0b7c3da35a2d6ff8420896526", size = 3082097 },
+ { url = "https://files.pythonhosted.org/packages/18/ed/0a8e4153c9b769f59c02fb5e7914f20f0b2483a19dae7bf2db54b743d0d0/psycopg2_binary-2.9.10-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f758ed67cab30b9a8d2833609513ce4d3bd027641673d4ebc9c067e4d208eec1", size = 3264776 },
+ { url = "https://files.pythonhosted.org/packages/10/db/d09da68c6a0cdab41566b74e0a6068a425f077169bed0946559b7348ebe9/psycopg2_binary-2.9.10-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8cd9b4f2cfab88ed4a9106192de509464b75a906462fb846b936eabe45c2063e", size = 3020968 },
+ { url = "https://files.pythonhosted.org/packages/94/28/4d6f8c255f0dfffb410db2b3f9ac5218d959a66c715c34cac31081e19b95/psycopg2_binary-2.9.10-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6dc08420625b5a20b53551c50deae6e231e6371194fa0651dbe0fb206452ae1f", size = 2872334 },
+ { url = "https://files.pythonhosted.org/packages/05/f7/20d7bf796593c4fea95e12119d6cc384ff1f6141a24fbb7df5a668d29d29/psycopg2_binary-2.9.10-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:d7cd730dfa7c36dbe8724426bf5612798734bff2d3c3857f36f2733f5bfc7c00", size = 2822722 },
+ { url = "https://files.pythonhosted.org/packages/4d/e4/0c407ae919ef626dbdb32835a03b6737013c3cc7240169843965cada2bdf/psycopg2_binary-2.9.10-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:155e69561d54d02b3c3209545fb08938e27889ff5a10c19de8d23eb5a41be8a5", size = 2920132 },
+ { url = "https://files.pythonhosted.org/packages/2d/70/aa69c9f69cf09a01da224909ff6ce8b68faeef476f00f7ec377e8f03be70/psycopg2_binary-2.9.10-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c3cc28a6fd5a4a26224007712e79b81dbaee2ffb90ff406256158ec4d7b52b47", size = 2959312 },
+ { url = "https://files.pythonhosted.org/packages/d3/bd/213e59854fafe87ba47814bf413ace0dcee33a89c8c8c814faca6bc7cf3c/psycopg2_binary-2.9.10-cp312-cp312-win32.whl", hash = "sha256:ec8a77f521a17506a24a5f626cb2aee7850f9b69a0afe704586f63a464f3cd64", size = 1025191 },
+ { url = "https://files.pythonhosted.org/packages/92/29/06261ea000e2dc1e22907dbbc483a1093665509ea586b29b8986a0e56733/psycopg2_binary-2.9.10-cp312-cp312-win_amd64.whl", hash = "sha256:18c5ee682b9c6dd3696dad6e54cc7ff3a1a9020df6a5c0f861ef8bfd338c3ca0", size = 1164031 },
+ { url = "https://files.pythonhosted.org/packages/3e/30/d41d3ba765609c0763505d565c4d12d8f3c79793f0d0f044ff5a28bf395b/psycopg2_binary-2.9.10-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:26540d4a9a4e2b096f1ff9cce51253d0504dca5a85872c7f7be23be5a53eb18d", size = 3044699 },
+ { url = "https://files.pythonhosted.org/packages/35/44/257ddadec7ef04536ba71af6bc6a75ec05c5343004a7ec93006bee66c0bc/psycopg2_binary-2.9.10-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:e217ce4d37667df0bc1c397fdcd8de5e81018ef305aed9415c3b093faaeb10fb", size = 3275245 },
+ { url = "https://files.pythonhosted.org/packages/1b/11/48ea1cd11de67f9efd7262085588790a95d9dfcd9b8a687d46caf7305c1a/psycopg2_binary-2.9.10-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:245159e7ab20a71d989da00f280ca57da7641fa2cdcf71749c193cea540a74f7", size = 2851631 },
+ { url = "https://files.pythonhosted.org/packages/62/e0/62ce5ee650e6c86719d621a761fe4bc846ab9eff8c1f12b1ed5741bf1c9b/psycopg2_binary-2.9.10-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c4ded1a24b20021ebe677b7b08ad10bf09aac197d6943bfe6fec70ac4e4690d", size = 3082140 },
+ { url = "https://files.pythonhosted.org/packages/27/ce/63f946c098611f7be234c0dd7cb1ad68b0b5744d34f68062bb3c5aa510c8/psycopg2_binary-2.9.10-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3abb691ff9e57d4a93355f60d4f4c1dd2d68326c968e7db17ea96df3c023ef73", size = 3264762 },
+ { url = "https://files.pythonhosted.org/packages/43/25/c603cd81402e69edf7daa59b1602bd41eb9859e2824b8c0855d748366ac9/psycopg2_binary-2.9.10-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8608c078134f0b3cbd9f89b34bd60a943b23fd33cc5f065e8d5f840061bd0673", size = 3020967 },
+ { url = "https://files.pythonhosted.org/packages/5f/d6/8708d8c6fca531057fa170cdde8df870e8b6a9b136e82b361c65e42b841e/psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:230eeae2d71594103cd5b93fd29d1ace6420d0b86f4778739cb1a5a32f607d1f", size = 2872326 },
+ { url = "https://files.pythonhosted.org/packages/ce/ac/5b1ea50fc08a9df82de7e1771537557f07c2632231bbab652c7e22597908/psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:bb89f0a835bcfc1d42ccd5f41f04870c1b936d8507c6df12b7737febc40f0909", size = 2822712 },
+ { url = "https://files.pythonhosted.org/packages/c4/fc/504d4503b2abc4570fac3ca56eb8fed5e437bf9c9ef13f36b6621db8ef00/psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f0c2d907a1e102526dd2986df638343388b94c33860ff3bbe1384130828714b1", size = 2920155 },
+ { url = "https://files.pythonhosted.org/packages/b2/d1/323581e9273ad2c0dbd1902f3fb50c441da86e894b6e25a73c3fda32c57e/psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f8157bed2f51db683f31306aa497311b560f2265998122abe1dce6428bd86567", size = 2959356 },
+ { url = "https://files.pythonhosted.org/packages/08/50/d13ea0a054189ae1bc21af1d85b6f8bb9bbc5572991055d70ad9006fe2d6/psycopg2_binary-2.9.10-cp313-cp313-win_amd64.whl", hash = "sha256:27422aa5f11fbcd9b18da48373eb67081243662f9b46e6fd07c3eb46e4535142", size = 2569224 },
+]
+
+[[package]]
+name = "pyarrow"
+version = "19.0.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/7f/09/a9046344212690f0632b9c709f9bf18506522feb333c894d0de81d62341a/pyarrow-19.0.1.tar.gz", hash = "sha256:3bf266b485df66a400f282ac0b6d1b500b9d2ae73314a153dbe97d6d5cc8a99e", size = 1129437 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/78/b4/94e828704b050e723f67d67c3535cf7076c7432cd4cf046e4bb3b96a9c9d/pyarrow-19.0.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:80b2ad2b193e7d19e81008a96e313fbd53157945c7be9ac65f44f8937a55427b", size = 30670749 },
+ { url = "https://files.pythonhosted.org/packages/7e/3b/4692965e04bb1df55e2c314c4296f1eb12b4f3052d4cf43d29e076aedf66/pyarrow-19.0.1-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:ee8dec072569f43835932a3b10c55973593abc00936c202707a4ad06af7cb294", size = 32128007 },
+ { url = "https://files.pythonhosted.org/packages/22/f7/2239af706252c6582a5635c35caa17cb4d401cd74a87821ef702e3888957/pyarrow-19.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d5d1ec7ec5324b98887bdc006f4d2ce534e10e60f7ad995e7875ffa0ff9cb14", size = 41144566 },
+ { url = "https://files.pythonhosted.org/packages/fb/e3/c9661b2b2849cfefddd9fd65b64e093594b231b472de08ff658f76c732b2/pyarrow-19.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3ad4c0eb4e2a9aeb990af6c09e6fa0b195c8c0e7b272ecc8d4d2b6574809d34", size = 42202991 },
+ { url = "https://files.pythonhosted.org/packages/fe/4f/a2c0ed309167ef436674782dfee4a124570ba64299c551e38d3fdaf0a17b/pyarrow-19.0.1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:d383591f3dcbe545f6cc62daaef9c7cdfe0dff0fb9e1c8121101cabe9098cfa6", size = 40507986 },
+ { url = "https://files.pythonhosted.org/packages/27/2e/29bb28a7102a6f71026a9d70d1d61df926887e36ec797f2e6acfd2dd3867/pyarrow-19.0.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:b4c4156a625f1e35d6c0b2132635a237708944eb41df5fbe7d50f20d20c17832", size = 42087026 },
+ { url = "https://files.pythonhosted.org/packages/16/33/2a67c0f783251106aeeee516f4806161e7b481f7d744d0d643d2f30230a5/pyarrow-19.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:5bd1618ae5e5476b7654c7b55a6364ae87686d4724538c24185bbb2952679960", size = 25250108 },
+ { url = "https://files.pythonhosted.org/packages/2b/8d/275c58d4b00781bd36579501a259eacc5c6dfb369be4ddeb672ceb551d2d/pyarrow-19.0.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:e45274b20e524ae5c39d7fc1ca2aa923aab494776d2d4b316b49ec7572ca324c", size = 30653552 },
+ { url = "https://files.pythonhosted.org/packages/a0/9e/e6aca5cc4ef0c7aec5f8db93feb0bde08dbad8c56b9014216205d271101b/pyarrow-19.0.1-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:d9dedeaf19097a143ed6da37f04f4051aba353c95ef507764d344229b2b740ae", size = 32103413 },
+ { url = "https://files.pythonhosted.org/packages/6a/fa/a7033f66e5d4f1308c7eb0dfcd2ccd70f881724eb6fd1776657fdf65458f/pyarrow-19.0.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ebfb5171bb5f4a52319344ebbbecc731af3f021e49318c74f33d520d31ae0c4", size = 41134869 },
+ { url = "https://files.pythonhosted.org/packages/2d/92/34d2569be8e7abdc9d145c98dc410db0071ac579b92ebc30da35f500d630/pyarrow-19.0.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2a21d39fbdb948857f67eacb5bbaaf36802de044ec36fbef7a1c8f0dd3a4ab2", size = 42192626 },
+ { url = "https://files.pythonhosted.org/packages/0a/1f/80c617b1084fc833804dc3309aa9d8daacd46f9ec8d736df733f15aebe2c/pyarrow-19.0.1-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:99bc1bec6d234359743b01e70d4310d0ab240c3d6b0da7e2a93663b0158616f6", size = 40496708 },
+ { url = "https://files.pythonhosted.org/packages/e6/90/83698fcecf939a611c8d9a78e38e7fed7792dcc4317e29e72cf8135526fb/pyarrow-19.0.1-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:1b93ef2c93e77c442c979b0d596af45e4665d8b96da598db145b0fec014b9136", size = 42075728 },
+ { url = "https://files.pythonhosted.org/packages/40/49/2325f5c9e7a1c125c01ba0c509d400b152c972a47958768e4e35e04d13d8/pyarrow-19.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:d9d46e06846a41ba906ab25302cf0fd522f81aa2a85a71021826f34639ad31ef", size = 25242568 },
+ { url = "https://files.pythonhosted.org/packages/3f/72/135088d995a759d4d916ec4824cb19e066585b4909ebad4ab196177aa825/pyarrow-19.0.1-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:c0fe3dbbf054a00d1f162fda94ce236a899ca01123a798c561ba307ca38af5f0", size = 30702371 },
+ { url = "https://files.pythonhosted.org/packages/2e/01/00beeebd33d6bac701f20816a29d2018eba463616bbc07397fdf99ac4ce3/pyarrow-19.0.1-cp313-cp313t-macosx_12_0_x86_64.whl", hash = "sha256:96606c3ba57944d128e8a8399da4812f56c7f61de8c647e3470b417f795d0ef9", size = 32116046 },
+ { url = "https://files.pythonhosted.org/packages/1f/c9/23b1ea718dfe967cbd986d16cf2a31fe59d015874258baae16d7ea0ccabc/pyarrow-19.0.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f04d49a6b64cf24719c080b3c2029a3a5b16417fd5fd7c4041f94233af732f3", size = 41091183 },
+ { url = "https://files.pythonhosted.org/packages/3a/d4/b4a3aa781a2c715520aa8ab4fe2e7fa49d33a1d4e71c8fc6ab7b5de7a3f8/pyarrow-19.0.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a9137cf7e1640dce4c190551ee69d478f7121b5c6f323553b319cac936395f6", size = 42171896 },
+ { url = "https://files.pythonhosted.org/packages/23/1b/716d4cd5a3cbc387c6e6745d2704c4b46654ba2668260d25c402626c5ddb/pyarrow-19.0.1-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:7c1bca1897c28013db5e4c83944a2ab53231f541b9e0c3f4791206d0c0de389a", size = 40464851 },
+ { url = "https://files.pythonhosted.org/packages/ed/bd/54907846383dcc7ee28772d7e646f6c34276a17da740002a5cefe90f04f7/pyarrow-19.0.1-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:58d9397b2e273ef76264b45531e9d552d8ec8a6688b7390b5be44c02a37aade8", size = 42085744 },
+]
+
+[[package]]
+name = "pycparser"
+version = "2.22"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/1d/b2/31537cf4b1ca988837256c910a668b553fceb8f069bedc4b1c826024b52c/pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6", size = 172736 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/13/a3/a812df4e2dd5696d1f351d58b8fe16a405b234ad2886a0dab9183fb78109/pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc", size = 117552 },
+]
+
+[[package]]
+name = "pydantic"
+version = "2.10.6"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "annotated-types" },
+ { name = "pydantic-core" },
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/b7/ae/d5220c5c52b158b1de7ca89fc5edb72f304a70a4c540c84c8844bf4008de/pydantic-2.10.6.tar.gz", hash = "sha256:ca5daa827cce33de7a42be142548b0096bf05a7e7b365aebfa5f8eeec7128236", size = 761681 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/f4/3c/8cc1cc84deffa6e25d2d0c688ebb80635dfdbf1dbea3e30c541c8cf4d860/pydantic-2.10.6-py3-none-any.whl", hash = "sha256:427d664bf0b8a2b34ff5dd0f5a18df00591adcee7198fbd71981054cef37b584", size = 431696 },
+]
+
+[[package]]
+name = "pydantic-core"
+version = "2.27.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/fc/01/f3e5ac5e7c25833db5eb555f7b7ab24cd6f8c322d3a3ad2d67a952dc0abc/pydantic_core-2.27.2.tar.gz", hash = "sha256:eb026e5a4c1fee05726072337ff51d1efb6f59090b7da90d30ea58625b1ffb39", size = 413443 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/d6/74/51c8a5482ca447871c93e142d9d4a92ead74de6c8dc5e66733e22c9bba89/pydantic_core-2.27.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9e0c8cfefa0ef83b4da9588448b6d8d2a2bf1a53c3f1ae5fca39eb3061e2f0b0", size = 1893127 },
+ { url = "https://files.pythonhosted.org/packages/d3/f3/c97e80721735868313c58b89d2de85fa80fe8dfeeed84dc51598b92a135e/pydantic_core-2.27.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:83097677b8e3bd7eaa6775720ec8e0405f1575015a463285a92bfdfe254529ef", size = 1811340 },
+ { url = "https://files.pythonhosted.org/packages/9e/91/840ec1375e686dbae1bd80a9e46c26a1e0083e1186abc610efa3d9a36180/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:172fce187655fece0c90d90a678424b013f8fbb0ca8b036ac266749c09438cb7", size = 1822900 },
+ { url = "https://files.pythonhosted.org/packages/f6/31/4240bc96025035500c18adc149aa6ffdf1a0062a4b525c932065ceb4d868/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:519f29f5213271eeeeb3093f662ba2fd512b91c5f188f3bb7b27bc5973816934", size = 1869177 },
+ { url = "https://files.pythonhosted.org/packages/fa/20/02fbaadb7808be578317015c462655c317a77a7c8f0ef274bc016a784c54/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:05e3a55d124407fffba0dd6b0c0cd056d10e983ceb4e5dbd10dda135c31071d6", size = 2038046 },
+ { url = "https://files.pythonhosted.org/packages/06/86/7f306b904e6c9eccf0668248b3f272090e49c275bc488a7b88b0823444a4/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c3ed807c7b91de05e63930188f19e921d1fe90de6b4f5cd43ee7fcc3525cb8c", size = 2685386 },
+ { url = "https://files.pythonhosted.org/packages/8d/f0/49129b27c43396581a635d8710dae54a791b17dfc50c70164866bbf865e3/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fb4aadc0b9a0c063206846d603b92030eb6f03069151a625667f982887153e2", size = 1997060 },
+ { url = "https://files.pythonhosted.org/packages/0d/0f/943b4af7cd416c477fd40b187036c4f89b416a33d3cc0ab7b82708a667aa/pydantic_core-2.27.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:28ccb213807e037460326424ceb8b5245acb88f32f3d2777427476e1b32c48c4", size = 2004870 },
+ { url = "https://files.pythonhosted.org/packages/35/40/aea70b5b1a63911c53a4c8117c0a828d6790483f858041f47bab0b779f44/pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:de3cd1899e2c279b140adde9357c4495ed9d47131b4a4eaff9052f23398076b3", size = 1999822 },
+ { url = "https://files.pythonhosted.org/packages/f2/b3/807b94fd337d58effc5498fd1a7a4d9d59af4133e83e32ae39a96fddec9d/pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:220f892729375e2d736b97d0e51466252ad84c51857d4d15f5e9692f9ef12be4", size = 2130364 },
+ { url = "https://files.pythonhosted.org/packages/fc/df/791c827cd4ee6efd59248dca9369fb35e80a9484462c33c6649a8d02b565/pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a0fcd29cd6b4e74fe8ddd2c90330fd8edf2e30cb52acda47f06dd615ae72da57", size = 2158303 },
+ { url = "https://files.pythonhosted.org/packages/9b/67/4e197c300976af185b7cef4c02203e175fb127e414125916bf1128b639a9/pydantic_core-2.27.2-cp312-cp312-win32.whl", hash = "sha256:1e2cb691ed9834cd6a8be61228471d0a503731abfb42f82458ff27be7b2186fc", size = 1834064 },
+ { url = "https://files.pythonhosted.org/packages/1f/ea/cd7209a889163b8dcca139fe32b9687dd05249161a3edda62860430457a5/pydantic_core-2.27.2-cp312-cp312-win_amd64.whl", hash = "sha256:cc3f1a99a4f4f9dd1de4fe0312c114e740b5ddead65bb4102884b384c15d8bc9", size = 1989046 },
+ { url = "https://files.pythonhosted.org/packages/bc/49/c54baab2f4658c26ac633d798dab66b4c3a9bbf47cff5284e9c182f4137a/pydantic_core-2.27.2-cp312-cp312-win_arm64.whl", hash = "sha256:3911ac9284cd8a1792d3cb26a2da18f3ca26c6908cc434a18f730dc0db7bfa3b", size = 1885092 },
+ { url = "https://files.pythonhosted.org/packages/41/b1/9bc383f48f8002f99104e3acff6cba1231b29ef76cfa45d1506a5cad1f84/pydantic_core-2.27.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7d14bd329640e63852364c306f4d23eb744e0f8193148d4044dd3dacdaacbd8b", size = 1892709 },
+ { url = "https://files.pythonhosted.org/packages/10/6c/e62b8657b834f3eb2961b49ec8e301eb99946245e70bf42c8817350cbefc/pydantic_core-2.27.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:82f91663004eb8ed30ff478d77c4d1179b3563df6cdb15c0817cd1cdaf34d154", size = 1811273 },
+ { url = "https://files.pythonhosted.org/packages/ba/15/52cfe49c8c986e081b863b102d6b859d9defc63446b642ccbbb3742bf371/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71b24c7d61131bb83df10cc7e687433609963a944ccf45190cfc21e0887b08c9", size = 1823027 },
+ { url = "https://files.pythonhosted.org/packages/b1/1c/b6f402cfc18ec0024120602bdbcebc7bdd5b856528c013bd4d13865ca473/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fa8e459d4954f608fa26116118bb67f56b93b209c39b008277ace29937453dc9", size = 1868888 },
+ { url = "https://files.pythonhosted.org/packages/bd/7b/8cb75b66ac37bc2975a3b7de99f3c6f355fcc4d89820b61dffa8f1e81677/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce8918cbebc8da707ba805b7fd0b382816858728ae7fe19a942080c24e5b7cd1", size = 2037738 },
+ { url = "https://files.pythonhosted.org/packages/c8/f1/786d8fe78970a06f61df22cba58e365ce304bf9b9f46cc71c8c424e0c334/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eda3f5c2a021bbc5d976107bb302e0131351c2ba54343f8a496dc8783d3d3a6a", size = 2685138 },
+ { url = "https://files.pythonhosted.org/packages/a6/74/d12b2cd841d8724dc8ffb13fc5cef86566a53ed358103150209ecd5d1999/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd8086fa684c4775c27f03f062cbb9eaa6e17f064307e86b21b9e0abc9c0f02e", size = 1997025 },
+ { url = "https://files.pythonhosted.org/packages/a0/6e/940bcd631bc4d9a06c9539b51f070b66e8f370ed0933f392db6ff350d873/pydantic_core-2.27.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8d9b3388db186ba0c099a6d20f0604a44eabdeef1777ddd94786cdae158729e4", size = 2004633 },
+ { url = "https://files.pythonhosted.org/packages/50/cc/a46b34f1708d82498c227d5d80ce615b2dd502ddcfd8376fc14a36655af1/pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7a66efda2387de898c8f38c0cf7f14fca0b51a8ef0b24bfea5849f1b3c95af27", size = 1999404 },
+ { url = "https://files.pythonhosted.org/packages/ca/2d/c365cfa930ed23bc58c41463bae347d1005537dc8db79e998af8ba28d35e/pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:18a101c168e4e092ab40dbc2503bdc0f62010e95d292b27827871dc85450d7ee", size = 2130130 },
+ { url = "https://files.pythonhosted.org/packages/f4/d7/eb64d015c350b7cdb371145b54d96c919d4db516817f31cd1c650cae3b21/pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ba5dd002f88b78a4215ed2f8ddbdf85e8513382820ba15ad5ad8955ce0ca19a1", size = 2157946 },
+ { url = "https://files.pythonhosted.org/packages/a4/99/bddde3ddde76c03b65dfd5a66ab436c4e58ffc42927d4ff1198ffbf96f5f/pydantic_core-2.27.2-cp313-cp313-win32.whl", hash = "sha256:1ebaf1d0481914d004a573394f4be3a7616334be70261007e47c2a6fe7e50130", size = 1834387 },
+ { url = "https://files.pythonhosted.org/packages/71/47/82b5e846e01b26ac6f1893d3c5f9f3a2eb6ba79be26eef0b759b4fe72946/pydantic_core-2.27.2-cp313-cp313-win_amd64.whl", hash = "sha256:953101387ecf2f5652883208769a79e48db18c6df442568a0b5ccd8c2723abee", size = 1990453 },
+ { url = "https://files.pythonhosted.org/packages/51/b2/b2b50d5ecf21acf870190ae5d093602d95f66c9c31f9d5de6062eb329ad1/pydantic_core-2.27.2-cp313-cp313-win_arm64.whl", hash = "sha256:ac4dbfd1691affb8f48c2c13241a2e3b60ff23247cbcf981759c768b6633cf8b", size = 1885186 },
+]
+
+[[package]]
+name = "pydantic-settings"
+version = "2.8.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "pydantic" },
+ { name = "python-dotenv" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/88/82/c79424d7d8c29b994fb01d277da57b0a9b09cc03c3ff875f9bd8a86b2145/pydantic_settings-2.8.1.tar.gz", hash = "sha256:d5c663dfbe9db9d5e1c646b2e161da12f0d734d422ee56f567d0ea2cee4e8585", size = 83550 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/0b/53/a64f03044927dc47aafe029c42a5b7aabc38dfb813475e0e1bf71c4a59d0/pydantic_settings-2.8.1-py3-none-any.whl", hash = "sha256:81942d5ac3d905f7f3ee1a70df5dfb62d5569c12f51a5a647defc1c3d9ee2e9c", size = 30839 },
+]
+
+[[package]]
+name = "pygit2"
+version = "1.17.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "cffi" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/b7/ea/17aa8ca38750f1ba69511ceeb41d29961f90eb2e0a242b668c70311efd4e/pygit2-1.17.0.tar.gz", hash = "sha256:fa2bc050b2c2d3e73b54d6d541c792178561a344f07e409f532d5bb97ac7b894", size = 769002 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/ee/53/8286256d077a0a38837c4ceee73a3c2b2d6caed3ec86e8bf7b32580e5ed0/pygit2-1.17.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:f7224d89a7dda7290e458393941e500c8682f375f41e6d80ee423958a5d4013d", size = 5465330 },
+ { url = "https://files.pythonhosted.org/packages/dd/a0/060ebb435d2590c1188ad6bc7ea0d5f0561e09a13db02baec8252b507390/pygit2-1.17.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9ae1967b0c8a2438b3b0e4a63307b5c22c80024a2f09b28d14dfde0001fed8dc", size = 5683366 },
+ { url = "https://files.pythonhosted.org/packages/21/92/fedc77806ff06b502a82ddbb857a5749429ce7bf638e3007b82bd10b4244/pygit2-1.17.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:507343fa142a82028c8448c2626317dc19885985aba8ea27d381777ac484eefb", size = 5645689 },
+ { url = "https://files.pythonhosted.org/packages/14/a9/3405b991f3264163e3d93c16b43929e0e765e559ca83f8697008c7f65587/pygit2-1.17.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bc04917a680591c6e801df912d7fb722c253b5ac68178ff37b5666dafd06999", size = 5457766 },
+ { url = "https://files.pythonhosted.org/packages/71/bb/40c37e00994727efb1a68bfd1f0b505207ec066ef8004b7e258210f230cc/pygit2-1.17.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7bb1b623cbd16962c3a1ec7f8e1012fa224c9e9642758c65e8e656ecc7ff1574", size = 5400609 },
+ { url = "https://files.pythonhosted.org/packages/db/55/7781d8997632ebfe2682a8f80668710eb4bc8c99a80e0691243b020f7391/pygit2-1.17.0-cp312-cp312-win32.whl", hash = "sha256:3029331ddf56a6908547278ab4c354b2d6932eb6a53be81e0093adc98a0ae540", size = 1219823 },
+ { url = "https://files.pythonhosted.org/packages/7c/73/166aae3a12a0c5252619df37a033c8a3c9756a6af4e49640769492d14893/pygit2-1.17.0-cp312-cp312-win_amd64.whl", hash = "sha256:1011236bab7317b82e6cbc3dff4be8467923b1dcf2ffe28bf2e64805dcb37749", size = 1305143 },
+ { url = "https://files.pythonhosted.org/packages/3d/09/d79f99cc25b895a891eab10697fecde3c2552fdfd467b9b72b388f9a1ad9/pygit2-1.17.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ce938e7a4fdfc816ffceb62babad65fb62e1a5ad261e880b9a072e8da144ccca", size = 5465211 },
+ { url = "https://files.pythonhosted.org/packages/a6/85/74e786da47ee2face731fb892fe87c04ae257d3b5136966f8f839727d130/pygit2-1.17.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:61ff2c8b0fc96fdf45a7a5239cc262b0293a5171f68d67eea239a42c3b2226cb", size = 5687159 },
+ { url = "https://files.pythonhosted.org/packages/58/61/b502b240ba91a3dec58e4936eb85c4c17d682dfb4872c197c2212fc13bc1/pygit2-1.17.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8101aa723c292892ba46303b19487a9fb0de50d9e30f4c1c2a76e3383b6e4b6d", size = 5649303 },
+ { url = "https://files.pythonhosted.org/packages/5a/33/e359c7c938df5b1cef2acb4dcf72cb153677f2185db8bfd0bb06a7ab96f9/pygit2-1.17.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36e3e9225e3f01bb6a2d4589c126900bbc571cd0876ca9c01372a6e3d3693c0e", size = 5461433 },
+ { url = "https://files.pythonhosted.org/packages/98/8e/6885fd4ce98aedb84fe4459a3c85f3b866577aec9343becfca4a0e50a1eb/pygit2-1.17.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:614cfddbf048900da19b016787f153d44ea9fd7ef80f9e03a77024aa1555d5f4", size = 5402395 },
+ { url = "https://files.pythonhosted.org/packages/9f/62/51b84a6c80742e73ecd562f45234c6ef23e833864583bc759d8c6770f493/pygit2-1.17.0-cp313-cp313-win32.whl", hash = "sha256:1391762153af9715ed1d0586e3f207c518f03f5874e1f5b8e398697d006a0a82", size = 1219803 },
+ { url = "https://files.pythonhosted.org/packages/7d/69/8dfe160c7166cec689d985e6efb52198c2c2fd5b722196e4beb920f9f460/pygit2-1.17.0-cp313-cp313-win_amd64.whl", hash = "sha256:d677d6fb85c426c5f5f8409bdc5a2e391016c99f73b97779b284c4ad25aa75fa", size = 1305156 },
+]
+
+[[package]]
+name = "pygithub"
+version = "2.6.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "deprecated" },
+ { name = "pyjwt", extra = ["crypto"] },
+ { name = "pynacl" },
+ { name = "requests" },
+ { name = "typing-extensions" },
+ { name = "urllib3" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/c0/88/e08ab18dc74b2916f48703ed1a797d57cb64eca0e23b0a9254e13cfe3911/pygithub-2.6.1.tar.gz", hash = "sha256:b5c035392991cca63959e9453286b41b54d83bf2de2daa7d7ff7e4312cebf3bf", size = 3659473 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/ac/fc/a444cd19ccc8c4946a512f3827ed0b3565c88488719d800d54a75d541c0b/PyGithub-2.6.1-py3-none-any.whl", hash = "sha256:6f2fa6d076ccae475f9fc392cc6cdbd54db985d4f69b8833a28397de75ed6ca3", size = 410451 },
+]
+
+[[package]]
+name = "pygments"
+version = "2.19.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/7c/2d/c3338d48ea6cc0feb8446d8e6937e1408088a72a39937982cc6111d17f84/pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f", size = 4968581 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/8a/0b/9fcc47d19c48b59121088dd6da2488a49d5f72dacf8262e2790a1d2c7d15/pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c", size = 1225293 },
+]
+
+[[package]]
+name = "pyinstrument"
+version = "5.0.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/64/6e/85c2722e40cab4fd9df6bbe68a0d032e237cf8cfada71e5f067e4e433214/pyinstrument-5.0.1.tar.gz", hash = "sha256:f4fd0754d02959c113a4b1ebed02f4627b6e2c138719ddf43244fd95f201c8c9", size = 263162 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/e1/09/696e29364503393c5bd0471f1c396d41820167b3f496bf8b128dc981f30d/pyinstrument-5.0.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:cfd7b7dc56501a1f30aa059cc2f1746ece6258a841d2e4609882581f9c17f824", size = 128903 },
+ { url = "https://files.pythonhosted.org/packages/b5/dd/36d1641414eb0ab3fb50815de8d927b74924a9bfb1e409c53e9aad4a16de/pyinstrument-5.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fe1f33178a2b0ddb3c6d2321406228bdad41286774e65314d511dcf4a71b83e4", size = 121440 },
+ { url = "https://files.pythonhosted.org/packages/9e/3f/05196fb514735aceef9a9439f56bcaa5ccb8b440685aa4f13fdb9e925182/pyinstrument-5.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0519d02dee55a87afcf6d787f8d8f5a16d2b89f7ba9533064a986a2d31f27340", size = 144783 },
+ { url = "https://files.pythonhosted.org/packages/73/4b/1b041b974e7e465ca311e712beb8be0bc9cf769bcfc6660b1b2ba630c27c/pyinstrument-5.0.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2f59ed9ac9466ff9b30eb7285160fa794aa3f8ce2bcf58a94142f945882d28ab", size = 143717 },
+ { url = "https://files.pythonhosted.org/packages/4a/dc/3fa73e2dde1588b6281e494a14c183a27e1a67db7401fddf9c528fb8e1a9/pyinstrument-5.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cbf3114d332e499ba35ca4aedc1ef95bc6fb15c8d819729b5c0aeb35c8b64dd2", size = 145082 },
+ { url = "https://files.pythonhosted.org/packages/91/24/b86d4273cc524a4f334a610a1c4b157146c808d8935e85d44dff3a6b75ee/pyinstrument-5.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:20f8054e85dd710f5a8c4d6b738867366ceef89671db09c87690ba1b5c66bd67", size = 144737 },
+ { url = "https://files.pythonhosted.org/packages/3c/39/6025a71082122bfbfee4eac6649635e4c688954bdf306bcd3629457c49b2/pyinstrument-5.0.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:63e8d75ffa50c3cf6d980844efce0334659e934dcc3832bad08c23c171c545ff", size = 144488 },
+ { url = "https://files.pythonhosted.org/packages/da/ce/679b0e9a278004defc93c277c3f81b456389dd530f89e28a45bd9dae203e/pyinstrument-5.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a3ca9c8540051513dd633de9d7eac9fee2eda50b78b6eedeaa7e5a7be66026b5", size = 144895 },
+ { url = "https://files.pythonhosted.org/packages/58/d8/cf80bb278e2a071325e4fb244127eb68dce9d0520d20c1fda75414f119ee/pyinstrument-5.0.1-cp312-cp312-win32.whl", hash = "sha256:b549d910b846757ffbf74d94528d1a694a3848a6cfc6a6cab2ce697ee71e4548", size = 123027 },
+ { url = "https://files.pythonhosted.org/packages/39/49/9251fe641d242d4c0dc49178b064f22da1c542d80e4040561428a9f8dd1c/pyinstrument-5.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:86f20b680223697a8ac5c061fb40a63d3ee519c7dfb1097627bd4480711216d9", size = 123818 },
+ { url = "https://files.pythonhosted.org/packages/0f/ae/f8f84ecd0dc2c4f0d84920cb4ffdbea52a66e4b4abc2110f18879b57f538/pyinstrument-5.0.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f5065639dfedc3b8e537161f9aaa8c550c8717c935a962e9bf1e843bf0e8791f", size = 128900 },
+ { url = "https://files.pythonhosted.org/packages/23/2f/b742c46d86d4c1f74ec0819f091bbc2fad0bab786584a18d89d9178802f1/pyinstrument-5.0.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b5d20802b0c2bd1ddb95b2e96ebd3e9757dbab1e935792c2629166f1eb267bb2", size = 121445 },
+ { url = "https://files.pythonhosted.org/packages/d9/e0/297dc8454ed437aec0fbdc3cc1a6a5fdf6701935b91dd31caf38c5e3ff92/pyinstrument-5.0.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6e6f5655d580429e7992c37757cc5f6e74ca81b0f2768b833d9711631a8cb2f7", size = 144904 },
+ { url = "https://files.pythonhosted.org/packages/8b/df/e4faff09fdbad7e685ceb0f96066d434fc8350382acf8df47577653f702b/pyinstrument-5.0.1-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b4c8c9ad93f62f0bf2ddc7fb6fce3a91c008d422873824e01c5e5e83467fd1fb", size = 143801 },
+ { url = "https://files.pythonhosted.org/packages/b1/63/ed2955d980bbebf17155119e2687ac15e170b6221c4bb5f5c37f41323fe5/pyinstrument-5.0.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db15d1854b360182d242da8de89761a0ffb885eea61cb8652e40b5b9a4ef44bc", size = 145204 },
+ { url = "https://files.pythonhosted.org/packages/c4/18/31b8dcdade9767afc7a36a313d8cf9c5690b662e9755fe7bd0523125e06f/pyinstrument-5.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c803f7b880394b7bba5939ff8a59d6962589e9a0140fc33c3a6a345c58846106", size = 144881 },
+ { url = "https://files.pythonhosted.org/packages/1f/14/cd19894eb03dd28093f564e8bcf7ae4edc8e315ce962c8155cf795fc0784/pyinstrument-5.0.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:84e37ffabcf26fe820d354a1f7e9fc26949f953addab89b590c5000b3ffa60d0", size = 144643 },
+ { url = "https://files.pythonhosted.org/packages/80/54/3dd08f5a869d3b654ff7e4e4c9d2b34f8de73fb0f2f792fac5024a312e0f/pyinstrument-5.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a0d23d3763ec95da0beb390c2f7df7cbe36ea62b6a4d5b89c4eaab81c1c649cf", size = 145070 },
+ { url = "https://files.pythonhosted.org/packages/5d/dc/ac8e798235a1dbccefc1b204a16709cef36f02c07587763ba8eb510fc8bc/pyinstrument-5.0.1-cp313-cp313-win32.whl", hash = "sha256:967f84bd82f14425543a983956ff9cfcf1e3762755ffcec8cd835c6be22a7a0a", size = 123030 },
+ { url = "https://files.pythonhosted.org/packages/52/59/adcb3e85c9105c59382723a67f682012aa7f49027e270e721f2d59f63fcf/pyinstrument-5.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:70b16b5915534d8df40dcf04a7cc78d3290464c06fa358a4bc324280af4c74e0", size = 123825 },
+]
+
+[[package]]
+name = "pyjson5"
+version = "1.6.8"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/8e/27/76ff4f9c71b353b8171fe9a8bda20612b7b12f9728d619a5c6df1e279bce/pyjson5-1.6.8.tar.gz", hash = "sha256:b3ecee050a8a4b03cc4f1a7e9a0c478be757b46578fda1ea0f16ac8a24ba8e7a", size = 300019 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/ff/3a/0ed2cdfdb67eaaa73dc28686eebee1805bd7edfa0e8f85cc0f0a7d71641e/pyjson5-1.6.8-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d7b4a4b36a8748011c7586d4bba3eb403d82bdb62605e7478f2c8b11c7e01711", size = 327150 },
+ { url = "https://files.pythonhosted.org/packages/60/60/c9e84e3b2520f7b67412173c7d17d98ab24fbef874bcfcf51eb83622fa9a/pyjson5-1.6.8-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9ee2f077cf05daa9aaf3c750b63cce5b5671cf8fa848b29beaf1030a08d94fda", size = 173668 },
+ { url = "https://files.pythonhosted.org/packages/ae/dd/4c9569654dc42c42d2a029e77e4371687bfb6f9f4afda6f1c8adda5d655d/pyjson5-1.6.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2bbfdeeb531f79730899ef674d80dd6b6bc7c29fe3789660115f0ba66eef834f", size = 162740 },
+ { url = "https://files.pythonhosted.org/packages/fb/6f/976aed9c5fe81cafda04bb470196c790fec78bfc057ea0a8a5e84ef4671e/pyjson5-1.6.8-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8fe8ba077a6ef01e6493696c27455eeae64e39ff4bd71a1a7bb66af40be7232c", size = 174476 },
+ { url = "https://files.pythonhosted.org/packages/da/8b/ab7fcfe3c07ecd1d71dec2b1062755950d8e211808f602ff60cf31264820/pyjson5-1.6.8-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:701db0660e434fae000e5d4d49efc0b80fbeedf938cbcc8b6d72c229d395feca", size = 177611 },
+ { url = "https://files.pythonhosted.org/packages/6a/64/8e52e7950da4855adbcbffa4a89864685995b692802a768ea31675e2c5c7/pyjson5-1.6.8-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:515c89e7063100bcc7c67292559bdd926da19b59fe00281e9dd2fa83f30747f1", size = 195618 },
+ { url = "https://files.pythonhosted.org/packages/dd/1a/957fea06a1e6ba34767411f2a4c6a926b32f5181a16e5505de9aca85847f/pyjson5-1.6.8-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d622733cf671c8104a2936b3ff589903fa4e2fec5db4e2679297219446d944a7", size = 175521 },
+ { url = "https://files.pythonhosted.org/packages/dc/7d/cc11b4283a6f255bea76458d663d1d41de396bc50100f2f7af603dbe6d65/pyjson5-1.6.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a4577a18545f3f4461df46d3d38d85659b16a77ca8975289ef6f21e1c228f7bf", size = 185277 },
+ { url = "https://files.pythonhosted.org/packages/94/21/5187cc7105934e7ac1dfbfabd33bc517618f62a78c7357544f53653bf373/pyjson5-1.6.8-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b0cd98871646bfb2236cfdc0ae87f8ae8f1f631133b99fef5e74307248c4ae8d", size = 196515 },
+ { url = "https://files.pythonhosted.org/packages/6d/05/2f4943349dd6814f3f24ce515ef06864f9d0351b20d69c978dd66c07fa1f/pyjson5-1.6.8-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5a379911161545aa57bd6cd97f249cabcfe5990688f4dff9a8f328f5f6f231d3", size = 1119222 },
+ { url = "https://files.pythonhosted.org/packages/40/62/1d78786fbd998937849e9364dc034f68fd43fa1e619dbfc71a0b57e50031/pyjson5-1.6.8-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:24c6206f508c169034fd851eb87af3aec893d2eca3bf14df65eecc520da16883", size = 997285 },
+ { url = "https://files.pythonhosted.org/packages/ad/3a/c57b9724b471e61d38123eef69eed09b6ec7fd2a144f56e49c96b11a7458/pyjson5-1.6.8-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:fd21ce9dd4733347b6a426f4f943dd20547befbd6ef502b7480944c84a1425a3", size = 1276952 },
+ { url = "https://files.pythonhosted.org/packages/db/fa/81257989504d1442d272e86e03b9d1c4b7e355e0034c0d6c51f1ac5e3229/pyjson5-1.6.8-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:7a11d3cd6114de90364c24876f1cd47dcecaffb47184ffffb01eb585c8810f4b", size = 1229440 },
+ { url = "https://files.pythonhosted.org/packages/89/88/8d63d86d871bd60ec43030509ea58e216a635fdf723290071e159689e4e2/pyjson5-1.6.8-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:4a58185b9ac3adfed0adf539be7293d76fe0f7c515b6f9982b225c8084027255", size = 1318444 },
+ { url = "https://files.pythonhosted.org/packages/e4/59/1a89268f650c9d8ef73f97ff9adeab1e0f40b8bf09d82fac840e26f8154d/pyjson5-1.6.8-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f4724dcb646c2d40ad45d5aa7a5af86d54dc38c78e27b795418ecca23248bb", size = 1177145 },
+ { url = "https://files.pythonhosted.org/packages/e1/45/cc1967749b08a701ddeb743cd432a9a6ddbff188a1b1294d061823d22993/pyjson5-1.6.8-cp312-cp312-win32.whl", hash = "sha256:cc414b6ab28ed75d761c825f1150c19dd9a8f9b2268ee6af0173d148f018a8c5", size = 127509 },
+ { url = "https://files.pythonhosted.org/packages/d6/07/430e3a960daf322e7f4b82515ec64d6f2febccdeba31a421c2daab8a1786/pyjson5-1.6.8-cp312-cp312-win_amd64.whl", hash = "sha256:3fd513eaffba7b72d56bd5b26a92e2edb3694602adcaf3414a9f7d6c4c5d9be7", size = 143885 },
+ { url = "https://files.pythonhosted.org/packages/74/17/1a2002b6ee6b6bd7abba860afa7c8f76f6cde88a8493f7db6e14b5681fcb/pyjson5-1.6.8-cp312-cp312-win_arm64.whl", hash = "sha256:f8d5a208b8954758c75f8e8ae28d195bac3fae24ce9b51f6261b401e4ccce116", size = 127142 },
+ { url = "https://files.pythonhosted.org/packages/ee/e1/2d85c838a9a702f6d4134cbccc85f8811f96f0889ca0f642dd4e1cecae66/pyjson5-1.6.8-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:681e52df0705056dc39cf7d7bec4161e2769437fdf89f55084a4b060e9bbbfc9", size = 325120 },
+ { url = "https://files.pythonhosted.org/packages/42/43/3b2a26ca84573209616675d63ffe559a6e8b73488d6c11e4a45f0204fc3e/pyjson5-1.6.8-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1550dc70199401056f80acfc503da36de2df70dd4364a0efb654ffe7e9246ac6", size = 172648 },
+ { url = "https://files.pythonhosted.org/packages/9d/cd/ad93170f8b7934b13e5a340daed934e7a8591e5d08abf3f50ab144a2663d/pyjson5-1.6.8-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:77005662014809a7b8b78f984131a3751295ff102f4c62b452bbdac946360166", size = 161830 },
+ { url = "https://files.pythonhosted.org/packages/21/d3/dffd61a6b17680f39d5aaea24297ddf13d03064fb9ab5987de4bb619bd79/pyjson5-1.6.8-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:65f2922cc8fd6b1e9cc8ff7e5fe975f7bf111c03eb06ed9b2ee793e6870d3212", size = 173697 },
+ { url = "https://files.pythonhosted.org/packages/b8/72/9566b6ec24c11293d2bb91be24492afaf9e339781057b355129a7d262050/pyjson5-1.6.8-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d83e0bc87d94baa39703c1d7139c5ce7ff025a53a34251762128713a294cf147", size = 177518 },
+ { url = "https://files.pythonhosted.org/packages/4b/2c/e615aca4b7e8f1c3b4d5520b8ec6b808a5320e19be8ccd6828b016e46b77/pyjson5-1.6.8-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:72fa22291149e8731c4bbc225cf75a41a049a54903018ca670c849658c1edc04", size = 193327 },
+ { url = "https://files.pythonhosted.org/packages/62/64/f06dec3ec3c7501d5a969d9aec1403898b70a2817225db749c8219203229/pyjson5-1.6.8-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3948742ff2d2f222ab87cc77d8c6ce8a9ef063fe2904f8fa88309611a128147a", size = 174453 },
+ { url = "https://files.pythonhosted.org/packages/d4/ca/f5b147b8a186e37a9339290dd9c8271aae94eab0307169124ec83c74aa99/pyjson5-1.6.8-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94e1b9d219f40bebbb6285840b094eca523481cf199cd46154044dae333d492d", size = 184161 },
+ { url = "https://files.pythonhosted.org/packages/1e/9d/7e7d2eaef592e350e8988a68b4d38f358894a1fb05237b6aef5cd25fea8a/pyjson5-1.6.8-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2dea723f88e89dba1d4a6542c5527cac7ecff6755291ad2eb60e1c2f578bb69f", size = 195307 },
+ { url = "https://files.pythonhosted.org/packages/51/c1/1538a2064599e6e77b96e5a58dc212d0fabf18442363a0224f5fdc31a51e/pyjson5-1.6.8-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:06b857a5a36f2bad52267d1a57a880cd62c3b0d3f3a719ab8599a1d5465e2417", size = 1121719 },
+ { url = "https://files.pythonhosted.org/packages/21/36/4af2c28aa6a0a9c2f839d2f63613605c11d0294d5a8dadcf65cc6b7e4f5c/pyjson5-1.6.8-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:aebdd4c5a878f125fea8b192244b1e64532561a315725502eee8d7629598882f", size = 995812 },
+ { url = "https://files.pythonhosted.org/packages/55/63/1c7c7797113aee8fd6bbebf56ac2603681635dd7bab73bd14d5ad34b48d1/pyjson5-1.6.8-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:10688e75fd9f18e34dddd111cafd87cca6727837469b8bfb61f2d2685490f976", size = 1279088 },
+ { url = "https://files.pythonhosted.org/packages/b4/c1/1121519c37ce70e4d1d4e5f714f5e0121313b79421ba8495a130cdad5d1e/pyjson5-1.6.8-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:e3aee51ef5feb4409ff36713f70251265b04c18c8322bc91d2578759225e918d", size = 1229957 },
+ { url = "https://files.pythonhosted.org/packages/84/39/3618b8e0dbc53233afd99c867d0f4fa7d8cc36489949d18dc833e692f7f3/pyjson5-1.6.8-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:5e7f5b92460dc69ce27814d4ab546e3bae84b9b2e26f29701ad7fab637e6bf2f", size = 1318799 },
+ { url = "https://files.pythonhosted.org/packages/90/ae/353ce74183d884b56407d29ebc3aab63d23ca7dfb9e9a75208737a917e11/pyjson5-1.6.8-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b77c94296cd0763bc2d7d276cb53dbc97edeacfbc50c02103521d586ca91ff37", size = 1180476 },
+ { url = "https://files.pythonhosted.org/packages/8c/df/f8afe0318b0b628a8c8abce57ffccb7afd0df9aab08bb08f4c2de5008854/pyjson5-1.6.8-cp313-cp313-win32.whl", hash = "sha256:260b6f2d7148f5fa23d817b82e9960a75a44678116d6a5513bed4e88d6697343", size = 127415 },
+ { url = "https://files.pythonhosted.org/packages/67/d9/9bd17bc0c99d2d917900114d548414f609ea81947e58f6525068d673fc77/pyjson5-1.6.8-cp313-cp313-win_amd64.whl", hash = "sha256:fe03568ca61050f00c951501d70aaf68064ab5fecb3d84961ce743102cc81036", size = 143519 },
+ { url = "https://files.pythonhosted.org/packages/ee/6d/8f35cab314cab3b67681ec072e7acb6432bee3ebc45dcf11fd8b6535cb57/pyjson5-1.6.8-cp313-cp313-win_arm64.whl", hash = "sha256:f984d06902b2096206d15bcbc6f0c75c024de295294ca04c8c11aedc871e2da0", size = 126843 },
+]
+
+[[package]]
+name = "pyjwt"
+version = "2.10.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/e7/46/bd74733ff231675599650d3e47f361794b22ef3e3770998dda30d3b63726/pyjwt-2.10.1.tar.gz", hash = "sha256:3cc5772eb20009233caf06e9d8a0577824723b44e6648ee0a2aedb6cf9381953", size = 87785 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/61/ad/689f02752eeec26aed679477e80e632ef1b682313be70793d798c1d5fc8f/PyJWT-2.10.1-py3-none-any.whl", hash = "sha256:dcdd193e30abefd5debf142f9adfcdd2b58004e644f25406ffaebd50bd98dacb", size = 22997 },
+]
+
+[package.optional-dependencies]
+crypto = [
+ { name = "cryptography" },
+]
+
+[[package]]
+name = "pynacl"
+version = "1.5.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "cffi" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/a7/22/27582568be639dfe22ddb3902225f91f2f17ceff88ce80e4db396c8986da/PyNaCl-1.5.0.tar.gz", hash = "sha256:8ac7448f09ab85811607bdd21ec2464495ac8b7c66d146bf545b0f08fb9220ba", size = 3392854 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/ce/75/0b8ede18506041c0bf23ac4d8e2971b4161cd6ce630b177d0a08eb0d8857/PyNaCl-1.5.0-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:401002a4aaa07c9414132aaed7f6836ff98f59277a234704ff66878c2ee4a0d1", size = 349920 },
+ { url = "https://files.pythonhosted.org/packages/59/bb/fddf10acd09637327a97ef89d2a9d621328850a72f1fdc8c08bdf72e385f/PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:52cb72a79269189d4e0dc537556f4740f7f0a9ec41c1322598799b0bdad4ef92", size = 601722 },
+ { url = "https://files.pythonhosted.org/packages/5d/70/87a065c37cca41a75f2ce113a5a2c2aa7533be648b184ade58971b5f7ccc/PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a36d4a9dda1f19ce6e03c9a784a2921a4b726b02e1c736600ca9c22029474394", size = 680087 },
+ { url = "https://files.pythonhosted.org/packages/ee/87/f1bb6a595f14a327e8285b9eb54d41fef76c585a0edef0a45f6fc95de125/PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:0c84947a22519e013607c9be43706dd42513f9e6ae5d39d3613ca1e142fba44d", size = 856678 },
+ { url = "https://files.pythonhosted.org/packages/66/28/ca86676b69bf9f90e710571b67450508484388bfce09acf8a46f0b8c785f/PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06b8f6fa7f5de8d5d2f7573fe8c863c051225a27b61e6860fd047b1775807858", size = 1133660 },
+ { url = "https://files.pythonhosted.org/packages/3d/85/c262db650e86812585e2bc59e497a8f59948a005325a11bbbc9ecd3fe26b/PyNaCl-1.5.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:a422368fc821589c228f4c49438a368831cb5bbc0eab5ebe1d7fac9dded6567b", size = 663824 },
+ { url = "https://files.pythonhosted.org/packages/fd/1a/cc308a884bd299b651f1633acb978e8596c71c33ca85e9dc9fa33a5399b9/PyNaCl-1.5.0-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:61f642bf2378713e2c2e1de73444a3778e5f0a38be6fee0fe532fe30060282ff", size = 1117912 },
+ { url = "https://files.pythonhosted.org/packages/25/2d/b7df6ddb0c2a33afdb358f8af6ea3b8c4d1196ca45497dd37a56f0c122be/PyNaCl-1.5.0-cp36-abi3-win32.whl", hash = "sha256:e46dae94e34b085175f8abb3b0aaa7da40767865ac82c928eeb9e57e1ea8a543", size = 204624 },
+ { url = "https://files.pythonhosted.org/packages/5e/22/d3db169895faaf3e2eda892f005f433a62db2decbcfbc2f61e6517adfa87/PyNaCl-1.5.0-cp36-abi3-win_amd64.whl", hash = "sha256:20f42270d27e1b6a29f54032090b972d97f0a1b0948cc52392041ef7831fee93", size = 212141 },
+]
+
+[[package]]
+name = "pyright"
+version = "1.1.396"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "nodeenv" },
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/bd/73/f20cb1dea1bdc1774e7f860fb69dc0718c7d8dea854a345faec845eb086a/pyright-1.1.396.tar.gz", hash = "sha256:142901f5908f5a0895be3d3befcc18bedcdb8cc1798deecaec86ef7233a29b03", size = 3814400 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/80/be/ecb7cfb42d242b7ee764b52e6ff4782beeec00e3b943a3ec832b281f9da6/pyright-1.1.396-py3-none-any.whl", hash = "sha256:c635e473095b9138c471abccca22b9fedbe63858e0b40d4fc4b67da041891844", size = 5689355 },
+]
+
+[[package]]
+name = "pytest"
+version = "8.3.5"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "colorama", marker = "sys_platform == 'win32'" },
+ { name = "iniconfig" },
+ { name = "packaging" },
+ { name = "pluggy" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/ae/3c/c9d525a414d506893f0cd8a8d0de7706446213181570cdbd766691164e40/pytest-8.3.5.tar.gz", hash = "sha256:f4efe70cc14e511565ac476b57c279e12a855b11f48f212af1080ef2263d3845", size = 1450891 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/30/3d/64ad57c803f1fa1e963a7946b6e0fea4a70df53c1a7fed304586539c2bac/pytest-8.3.5-py3-none-any.whl", hash = "sha256:c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820", size = 343634 },
+]
+
+[[package]]
+name = "pytest-snapshot"
+version = "0.9.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "pytest" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/9b/7b/ab8f1fc1e687218aa66acec1c3674d9c443f6a2dc8cb6a50f464548ffa34/pytest-snapshot-0.9.0.tar.gz", hash = "sha256:c7013c3abc3e860f9feff899f8b4debe3708650d8d8242a61bf2625ff64db7f3", size = 19877 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/cb/29/518f32faf6edad9f56d6e0107217f7de6b79f297a47170414a2bd4be7f01/pytest_snapshot-0.9.0-py3-none-any.whl", hash = "sha256:4b9fe1c21c868fe53a545e4e3184d36bc1c88946e3f5c1d9dd676962a9b3d4ab", size = 10715 },
+]
+
+[[package]]
+name = "python-dateutil"
+version = "2.9.0.post0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "six" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892 },
+]
+
+[[package]]
+name = "python-dotenv"
+version = "1.0.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/bc/57/e84d88dfe0aec03b7a2d4327012c1627ab5f03652216c63d49846d7a6c58/python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca", size = 39115 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/6a/3e/b68c118422ec867fa7ab88444e1274aa40681c606d59ac27de5a5588f082/python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a", size = 19863 },
+]
+
+[[package]]
+name = "python-gitlab"
+version = "4.13.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "requests" },
+ { name = "requests-toolbelt" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/c4/ea/e2cde926d63526935c1df259177371a195089b631d67a577fe5c39fbc7e1/python_gitlab-4.13.0.tar.gz", hash = "sha256:576bfb0901faca0c6b2d1ff2592e02944a6ec3e086c3129fb43c2a0df56a1c67", size = 484996 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/6b/5e/5fb4dcae9f5af5463c16952823d446ca449cce920efe8669871f600f0ab9/python_gitlab-4.13.0-py3-none-any.whl", hash = "sha256:8299a054fb571da16e1a8c1868fff01f34ac41ea1410c713a4647b3bbb2aa279", size = 145254 },
+]
+
+[[package]]
+name = "python-levenshtein"
+version = "0.27.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "levenshtein" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/13/f6/d865a565b7eeef4b5f9a18accafb03d5730c712420fc84a3a40555f7ea6b/python_levenshtein-0.27.1.tar.gz", hash = "sha256:3a5314a011016d373d309a68e875fd029caaa692ad3f32e78319299648045f11", size = 12326 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/2a/95/8c8fd923b0a702388da4f9e0368f490d123cc5224279e6a083984304a15e/python_levenshtein-0.27.1-py3-none-any.whl", hash = "sha256:e1a4bc2a70284b2ebc4c505646142fecd0f831e49aa04ed972995895aec57396", size = 9426 },
+]
+
+[[package]]
+name = "python-multipart"
+version = "0.0.20"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/f3/87/f44d7c9f274c7ee665a29b885ec97089ec5dc034c7f3fafa03da9e39a09e/python_multipart-0.0.20.tar.gz", hash = "sha256:8dd0cab45b8e23064ae09147625994d090fa46f5b0d1e13af944c331a7fa9d13", size = 37158 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/45/58/38b5afbc1a800eeea951b9285d3912613f2603bdf897a4ab0f4bd7f405fc/python_multipart-0.0.20-py3-none-any.whl", hash = "sha256:8a62d3a8335e06589fe01f2a3e178cdcc632f3fbe0d492ad9ee0ec35aab1f104", size = 24546 },
+]
+
+[[package]]
+name = "python-semantic-release"
+version = "9.21.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "click" },
+ { name = "click-option-group" },
+ { name = "deprecated" },
+ { name = "dotty-dict" },
+ { name = "gitpython" },
+ { name = "importlib-resources" },
+ { name = "jinja2" },
+ { name = "pydantic" },
+ { name = "python-gitlab" },
+ { name = "requests" },
+ { name = "rich" },
+ { name = "shellingham" },
+ { name = "tomlkit" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/01/21/d64b81fa9e7326b8c25765ecf0e0f1458dd098a94a9e80d0e6671c827880/python_semantic_release-9.21.0.tar.gz", hash = "sha256:d8673d25cab2acdfeb34f791e271bb8a02ecc63650c5aa5c03d520ddf0cbe887", size = 307256 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/10/75/24ad6ed3832e4616ea9d97fe9644d5efb98c9014f25cd6c83e8dc10ef574/python_semantic_release-9.21.0-py3-none-any.whl", hash = "sha256:1ecf9753283835f1c6cda4702e419d9702863a51b03fa11955429139234f063c", size = 132564 },
+]
+
+[[package]]
+name = "pytz"
+version = "2025.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/5f/57/df1c9157c8d5a05117e455d66fd7cf6dbc46974f832b1058ed4856785d8a/pytz-2025.1.tar.gz", hash = "sha256:c2db42be2a2518b28e65f9207c4d05e6ff547d1efa4086469ef855e4ab70178e", size = 319617 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/eb/38/ac33370d784287baa1c3d538978b5e2ea064d4c1b93ffbd12826c190dd10/pytz-2025.1-py2.py3-none-any.whl", hash = "sha256:89dd22dca55b46eac6eda23b2d72721bf1bdfef212645d81513ef5d03038de57", size = 507930 },
+]
+
+[[package]]
+name = "pywin32"
+version = "308"
+source = { registry = "https://pypi.org/simple" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/00/7c/d00d6bdd96de4344e06c4afbf218bc86b54436a94c01c71a8701f613aa56/pywin32-308-cp312-cp312-win32.whl", hash = "sha256:587f3e19696f4bf96fde9d8a57cec74a57021ad5f204c9e627e15c33ff568897", size = 5939729 },
+ { url = "https://files.pythonhosted.org/packages/21/27/0c8811fbc3ca188f93b5354e7c286eb91f80a53afa4e11007ef661afa746/pywin32-308-cp312-cp312-win_amd64.whl", hash = "sha256:00b3e11ef09ede56c6a43c71f2d31857cf7c54b0ab6e78ac659497abd2834f47", size = 6543015 },
+ { url = "https://files.pythonhosted.org/packages/9d/0f/d40f8373608caed2255781a3ad9a51d03a594a1248cd632d6a298daca693/pywin32-308-cp312-cp312-win_arm64.whl", hash = "sha256:9b4de86c8d909aed15b7011182c8cab38c8850de36e6afb1f0db22b8959e3091", size = 7976033 },
+ { url = "https://files.pythonhosted.org/packages/a9/a4/aa562d8935e3df5e49c161b427a3a2efad2ed4e9cf81c3de636f1fdddfd0/pywin32-308-cp313-cp313-win32.whl", hash = "sha256:1c44539a37a5b7b21d02ab34e6a4d314e0788f1690d65b48e9b0b89f31abbbed", size = 5938579 },
+ { url = "https://files.pythonhosted.org/packages/c7/50/b0efb8bb66210da67a53ab95fd7a98826a97ee21f1d22949863e6d588b22/pywin32-308-cp313-cp313-win_amd64.whl", hash = "sha256:fd380990e792eaf6827fcb7e187b2b4b1cede0585e3d0c9e84201ec27b9905e4", size = 6542056 },
+ { url = "https://files.pythonhosted.org/packages/26/df/2b63e3e4f2df0224f8aaf6d131f54fe4e8c96400eb9df563e2aae2e1a1f9/pywin32-308-cp313-cp313-win_arm64.whl", hash = "sha256:ef313c46d4c18dfb82a2431e3051ac8f112ccee1a34f29c263c583c568db63cd", size = 7974986 },
+]
+
+[[package]]
+name = "pyyaml"
+version = "6.0.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", size = 183873 },
+ { url = "https://files.pythonhosted.org/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", size = 173302 },
+ { url = "https://files.pythonhosted.org/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", size = 739154 },
+ { url = "https://files.pythonhosted.org/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", size = 766223 },
+ { url = "https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", size = 767542 },
+ { url = "https://files.pythonhosted.org/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", size = 731164 },
+ { url = "https://files.pythonhosted.org/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611 },
+ { url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591 },
+ { url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338 },
+ { url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309 },
+ { url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679 },
+ { url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428 },
+ { url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361 },
+ { url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523 },
+ { url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660 },
+ { url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597 },
+ { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527 },
+ { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446 },
+]
+
+[[package]]
+name = "rapidfuzz"
+version = "3.12.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/f9/be/8dff25a6157dfbde9867720b1282157fe7b809e085130bb89d7655c62186/rapidfuzz-3.12.2.tar.gz", hash = "sha256:b0ba1ccc22fff782e7152a3d3d0caca44ec4e32dc48ba01c560b8593965b5aa3", size = 57907839 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/a7/d2/e071753227c9e9f7f3550b983f30565f6e994581529815fa5a8879e7cd10/rapidfuzz-3.12.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:1d982a651253ffe8434d9934ff0c1089111d60502228464721a2a4587435e159", size = 1944403 },
+ { url = "https://files.pythonhosted.org/packages/aa/d1/4a10d21cc97aa36f4019af24382b5b4dc5ea6444499883c1c1286c6089ba/rapidfuzz-3.12.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:02e6466caa0222d5233b1f05640873671cd99549a5c5ba4c29151634a1e56080", size = 1430287 },
+ { url = "https://files.pythonhosted.org/packages/6a/2d/76d39ab0beeb884d432096fe288c41850e37608e0145264081d0cb809f3c/rapidfuzz-3.12.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e956b3f053e474abae69ac693a52742109d860ac2375fe88e9387d3277f4c96c", size = 1403693 },
+ { url = "https://files.pythonhosted.org/packages/85/1a/719b0f6498c003627e4b83b841bdcd48b11de8a9908a9051c4d2a0bc2245/rapidfuzz-3.12.2-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2dee7d740a2d5418d4f964f39ab8d89923e6b945850db833e798a1969b19542a", size = 5555878 },
+ { url = "https://files.pythonhosted.org/packages/af/48/14d952a73254b4b0e517141acd27979bd23948adaf197f6ca2dc722fde6a/rapidfuzz-3.12.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a057cdb0401e42c84b6516c9b1635f7aedd5e430c6e388bd5f6bcd1d6a0686bb", size = 1655301 },
+ { url = "https://files.pythonhosted.org/packages/db/3f/b093e154e9752325d7459aa6dca43b7acbcaffa05133507e2403676e3e75/rapidfuzz-3.12.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dccf8d4fb5b86d39c581a59463c596b1d09df976da26ff04ae219604223d502f", size = 1678069 },
+ { url = "https://files.pythonhosted.org/packages/d6/7e/88853ecae5b5456eb1a1d8a01cbd534e25b671735d5d974609cbae082542/rapidfuzz-3.12.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21d5b3793c6f5aecca595cd24164bf9d3c559e315ec684f912146fc4e769e367", size = 3137119 },
+ { url = "https://files.pythonhosted.org/packages/4d/d2/b1f809b815aaf682ddac9c57929149f740b90feeb4f8da2f535c196de821/rapidfuzz-3.12.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:46a616c0e13cff2de1761b011e0b14bb73b110182f009223f1453d505c9a975c", size = 2491639 },
+ { url = "https://files.pythonhosted.org/packages/61/e4/a908d7b8db6e52ba2f80f6f0d0709ef9fdedb767db4307084331742b67f0/rapidfuzz-3.12.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:19fa5bc4301a1ee55400d4a38a8ecf9522b0391fc31e6da5f4d68513fe5c0026", size = 7821561 },
+ { url = "https://files.pythonhosted.org/packages/f3/83/0250c49deefff15c46f5e590d8ee6abbd0f056e20b85994db55c16ac6ead/rapidfuzz-3.12.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:544a47190a0d25971658a9365dba7095397b4ce3e897f7dd0a77ca2cf6fa984e", size = 2874048 },
+ { url = "https://files.pythonhosted.org/packages/6c/3f/8d433d964c6e476476ee53eae5fa77b9f16b38d312eb1571e9099a6a3b12/rapidfuzz-3.12.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:f21af27c5e001f0ba1b88c36a0936437dfe034c452548d998891c21125eb640f", size = 3522801 },
+ { url = "https://files.pythonhosted.org/packages/82/85/4931bfa41ef837b1544838e46e0556640d18114b3da9cf05e10defff00ae/rapidfuzz-3.12.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b63170d9db00629b5b3f2862114d8d6ee19127eaba0eee43762d62a25817dbe0", size = 4567304 },
+ { url = "https://files.pythonhosted.org/packages/b1/fe/fdae322869885115dd19a38c1da71b73a8832aa77757c93f460743d4f54c/rapidfuzz-3.12.2-cp312-cp312-win32.whl", hash = "sha256:6c7152d77b2eb6bfac7baa11f2a9c45fd5a2d848dbb310acd0953b3b789d95c9", size = 1845332 },
+ { url = "https://files.pythonhosted.org/packages/ca/a4/2ccebda5fb8a266d163d57a42c2a6ef6f91815df5d89cf38c12e8aa6ed0b/rapidfuzz-3.12.2-cp312-cp312-win_amd64.whl", hash = "sha256:1a314d170ee272ac87579f25a6cf8d16a031e1f7a7b07663434b41a1473bc501", size = 1617926 },
+ { url = "https://files.pythonhosted.org/packages/a5/bc/aa8a4dc4ebff966dd039cce017c614cfd202049b4d1a2daafee7d018521b/rapidfuzz-3.12.2-cp312-cp312-win_arm64.whl", hash = "sha256:d41e8231326e94fd07c4d8f424f6bed08fead6f5e6688d1e6e787f1443ae7631", size = 864737 },
+ { url = "https://files.pythonhosted.org/packages/96/59/2ea3b5bb82798eae73d6ee892264ebfe42727626c1f0e96c77120f0d5cf6/rapidfuzz-3.12.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:941f31038dba5d3dedcfcceba81d61570ad457c873a24ceb13f4f44fcb574260", size = 1936870 },
+ { url = "https://files.pythonhosted.org/packages/54/85/4e486bf9ea05e771ad231731305ed701db1339157f630b76b246ce29cf71/rapidfuzz-3.12.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:fe2dfc454ee51ba168a67b1e92b72aad251e45a074972cef13340bbad2fd9438", size = 1424231 },
+ { url = "https://files.pythonhosted.org/packages/dc/60/aeea3eed402c40a8cf055d554678769fbee0dd95c22f04546070a22bb90e/rapidfuzz-3.12.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78fafaf7f5a48ee35ccd7928339080a0136e27cf97396de45259eca1d331b714", size = 1398055 },
+ { url = "https://files.pythonhosted.org/packages/33/6b/757106f4c21fe3f20ce13ba3df560da60e52fe0dc390fd22bf613761669c/rapidfuzz-3.12.2-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e0c7989ff32c077bb8fd53253fd6ca569d1bfebc80b17557e60750e6909ba4fe", size = 5526188 },
+ { url = "https://files.pythonhosted.org/packages/1e/a2/7c680cdc5532746dba67ecf302eed975252657094e50ae334fa9268352e8/rapidfuzz-3.12.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:96fa00bc105caa34b6cd93dca14a29243a3a7f0c336e4dcd36348d38511e15ac", size = 1648483 },
+ { url = "https://files.pythonhosted.org/packages/f6/b0/ce942a1448b1a75d64af230dd746dede502224dd29ca9001665bbfd4bee6/rapidfuzz-3.12.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bccfb30c668620c5bc3490f2dc7d7da1cca0ead5a9da8b755e2e02e2ef0dff14", size = 1676076 },
+ { url = "https://files.pythonhosted.org/packages/ba/71/81f77b08333200be6984b6cdf2bdfd7cfca4943f16b478a2f7838cba8d66/rapidfuzz-3.12.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2f9b0adc3d894beb51f5022f64717b6114a6fabaca83d77e93ac7675911c8cc5", size = 3114169 },
+ { url = "https://files.pythonhosted.org/packages/01/16/f3f34b207fdc8c61a33f9d2d61fc96b62c7dadca88bda1df1be4b94afb0b/rapidfuzz-3.12.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:32691aa59577f42864d5535cb6225d0f47e2c7bff59cf4556e5171e96af68cc1", size = 2485317 },
+ { url = "https://files.pythonhosted.org/packages/b2/a6/b954f0766f644eb8dd8df44703e024ab4f5f15a8f8f5ea969963dd036f50/rapidfuzz-3.12.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:758b10380ad34c1f51753a070d7bb278001b5e6fcf544121c6df93170952d705", size = 7844495 },
+ { url = "https://files.pythonhosted.org/packages/fb/8f/1dc604d05e07150a02b56a8ffc47df75ce316c65467259622c9edf098451/rapidfuzz-3.12.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:50a9c54c0147b468363119132d514c5024fbad1ed8af12bd8bd411b0119f9208", size = 2873242 },
+ { url = "https://files.pythonhosted.org/packages/78/a9/9c649ace4b7f885e0a5fdcd1f33b057ebd83ecc2837693e6659bd944a2bb/rapidfuzz-3.12.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:e3ceb87c11d2d0fbe8559bb795b0c0604b84cfc8bb7b8720b5c16e9e31e00f41", size = 3519124 },
+ { url = "https://files.pythonhosted.org/packages/f5/81/ce0b774e540a2e22ec802e383131d7ead18347197304d584c4ccf7b8861a/rapidfuzz-3.12.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f7c9a003002434889255ff5676ca0f8934a478065ab5e702f75dc42639505bba", size = 4557831 },
+ { url = "https://files.pythonhosted.org/packages/13/28/7bf0ee8d35efa7ab14e83d1795cdfd54833aa0428b6f87e987893136c372/rapidfuzz-3.12.2-cp313-cp313-win32.whl", hash = "sha256:cf165a76870cd875567941cf861dfd361a0a6e6a56b936c5d30042ddc9def090", size = 1842802 },
+ { url = "https://files.pythonhosted.org/packages/ef/7e/792d609484776c8a40e1695ebd28b62196be9f8347b785b9104604dc7268/rapidfuzz-3.12.2-cp313-cp313-win_amd64.whl", hash = "sha256:55bcc003541f5f16ec0a73bf6de758161973f9e8d75161954380738dd147f9f2", size = 1615808 },
+ { url = "https://files.pythonhosted.org/packages/4b/43/ca3d1018b392f49131843648e10b08ace23afe8dad3bee5f136e4346b7cd/rapidfuzz-3.12.2-cp313-cp313-win_arm64.whl", hash = "sha256:69f6ecdf1452139f2b947d0c169a605de578efdb72cbb2373cb0a94edca1fd34", size = 863535 },
+]
+
+[[package]]
+name = "regex"
+version = "2024.11.6"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/8e/5f/bd69653fbfb76cf8604468d3b4ec4c403197144c7bfe0e6a5fc9e02a07cb/regex-2024.11.6.tar.gz", hash = "sha256:7ab159b063c52a0333c884e4679f8d7a85112ee3078fe3d9004b2dd875585519", size = 399494 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/ba/30/9a87ce8336b172cc232a0db89a3af97929d06c11ceaa19d97d84fa90a8f8/regex-2024.11.6-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:52fb28f528778f184f870b7cf8f225f5eef0a8f6e3778529bdd40c7b3920796a", size = 483781 },
+ { url = "https://files.pythonhosted.org/packages/01/e8/00008ad4ff4be8b1844786ba6636035f7ef926db5686e4c0f98093612add/regex-2024.11.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fdd6028445d2460f33136c55eeb1f601ab06d74cb3347132e1c24250187500d9", size = 288455 },
+ { url = "https://files.pythonhosted.org/packages/60/85/cebcc0aff603ea0a201667b203f13ba75d9fc8668fab917ac5b2de3967bc/regex-2024.11.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:805e6b60c54bf766b251e94526ebad60b7de0c70f70a4e6210ee2891acb70bf2", size = 284759 },
+ { url = "https://files.pythonhosted.org/packages/94/2b/701a4b0585cb05472a4da28ee28fdfe155f3638f5e1ec92306d924e5faf0/regex-2024.11.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b85c2530be953a890eaffde05485238f07029600e8f098cdf1848d414a8b45e4", size = 794976 },
+ { url = "https://files.pythonhosted.org/packages/4b/bf/fa87e563bf5fee75db8915f7352e1887b1249126a1be4813837f5dbec965/regex-2024.11.6-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bb26437975da7dc36b7efad18aa9dd4ea569d2357ae6b783bf1118dabd9ea577", size = 833077 },
+ { url = "https://files.pythonhosted.org/packages/a1/56/7295e6bad94b047f4d0834e4779491b81216583c00c288252ef625c01d23/regex-2024.11.6-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:abfa5080c374a76a251ba60683242bc17eeb2c9818d0d30117b4486be10c59d3", size = 823160 },
+ { url = "https://files.pythonhosted.org/packages/fb/13/e3b075031a738c9598c51cfbc4c7879e26729c53aa9cca59211c44235314/regex-2024.11.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b7fa6606c2881c1db9479b0eaa11ed5dfa11c8d60a474ff0e095099f39d98e", size = 796896 },
+ { url = "https://files.pythonhosted.org/packages/24/56/0b3f1b66d592be6efec23a795b37732682520b47c53da5a32c33ed7d84e3/regex-2024.11.6-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0c32f75920cf99fe6b6c539c399a4a128452eaf1af27f39bce8909c9a3fd8cbe", size = 783997 },
+ { url = "https://files.pythonhosted.org/packages/f9/a1/eb378dada8b91c0e4c5f08ffb56f25fcae47bf52ad18f9b2f33b83e6d498/regex-2024.11.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:982e6d21414e78e1f51cf595d7f321dcd14de1f2881c5dc6a6e23bbbbd68435e", size = 781725 },
+ { url = "https://files.pythonhosted.org/packages/83/f2/033e7dec0cfd6dda93390089864732a3409246ffe8b042e9554afa9bff4e/regex-2024.11.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a7c2155f790e2fb448faed6dd241386719802296ec588a8b9051c1f5c481bc29", size = 789481 },
+ { url = "https://files.pythonhosted.org/packages/83/23/15d4552ea28990a74e7696780c438aadd73a20318c47e527b47a4a5a596d/regex-2024.11.6-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:149f5008d286636e48cd0b1dd65018548944e495b0265b45e1bffecce1ef7f39", size = 852896 },
+ { url = "https://files.pythonhosted.org/packages/e3/39/ed4416bc90deedbfdada2568b2cb0bc1fdb98efe11f5378d9892b2a88f8f/regex-2024.11.6-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:e5364a4502efca094731680e80009632ad6624084aff9a23ce8c8c6820de3e51", size = 860138 },
+ { url = "https://files.pythonhosted.org/packages/93/2d/dd56bb76bd8e95bbce684326302f287455b56242a4f9c61f1bc76e28360e/regex-2024.11.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0a86e7eeca091c09e021db8eb72d54751e527fa47b8d5787caf96d9831bd02ad", size = 787692 },
+ { url = "https://files.pythonhosted.org/packages/0b/55/31877a249ab7a5156758246b9c59539abbeba22461b7d8adc9e8475ff73e/regex-2024.11.6-cp312-cp312-win32.whl", hash = "sha256:32f9a4c643baad4efa81d549c2aadefaeba12249b2adc5af541759237eee1c54", size = 262135 },
+ { url = "https://files.pythonhosted.org/packages/38/ec/ad2d7de49a600cdb8dd78434a1aeffe28b9d6fc42eb36afab4a27ad23384/regex-2024.11.6-cp312-cp312-win_amd64.whl", hash = "sha256:a93c194e2df18f7d264092dc8539b8ffb86b45b899ab976aa15d48214138e81b", size = 273567 },
+ { url = "https://files.pythonhosted.org/packages/90/73/bcb0e36614601016552fa9344544a3a2ae1809dc1401b100eab02e772e1f/regex-2024.11.6-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a6ba92c0bcdf96cbf43a12c717eae4bc98325ca3730f6b130ffa2e3c3c723d84", size = 483525 },
+ { url = "https://files.pythonhosted.org/packages/0f/3f/f1a082a46b31e25291d830b369b6b0c5576a6f7fb89d3053a354c24b8a83/regex-2024.11.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:525eab0b789891ac3be914d36893bdf972d483fe66551f79d3e27146191a37d4", size = 288324 },
+ { url = "https://files.pythonhosted.org/packages/09/c9/4e68181a4a652fb3ef5099e077faf4fd2a694ea6e0f806a7737aff9e758a/regex-2024.11.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:086a27a0b4ca227941700e0b31425e7a28ef1ae8e5e05a33826e17e47fbfdba0", size = 284617 },
+ { url = "https://files.pythonhosted.org/packages/fc/fd/37868b75eaf63843165f1d2122ca6cb94bfc0271e4428cf58c0616786dce/regex-2024.11.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bde01f35767c4a7899b7eb6e823b125a64de314a8ee9791367c9a34d56af18d0", size = 795023 },
+ { url = "https://files.pythonhosted.org/packages/c4/7c/d4cd9c528502a3dedb5c13c146e7a7a539a3853dc20209c8e75d9ba9d1b2/regex-2024.11.6-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b583904576650166b3d920d2bcce13971f6f9e9a396c673187f49811b2769dc7", size = 833072 },
+ { url = "https://files.pythonhosted.org/packages/4f/db/46f563a08f969159c5a0f0e722260568425363bea43bb7ae370becb66a67/regex-2024.11.6-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c4de13f06a0d54fa0d5ab1b7138bfa0d883220965a29616e3ea61b35d5f5fc7", size = 823130 },
+ { url = "https://files.pythonhosted.org/packages/db/60/1eeca2074f5b87df394fccaa432ae3fc06c9c9bfa97c5051aed70e6e00c2/regex-2024.11.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3cde6e9f2580eb1665965ce9bf17ff4952f34f5b126beb509fee8f4e994f143c", size = 796857 },
+ { url = "https://files.pythonhosted.org/packages/10/db/ac718a08fcee981554d2f7bb8402f1faa7e868c1345c16ab1ebec54b0d7b/regex-2024.11.6-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0d7f453dca13f40a02b79636a339c5b62b670141e63efd511d3f8f73fba162b3", size = 784006 },
+ { url = "https://files.pythonhosted.org/packages/c2/41/7da3fe70216cea93144bf12da2b87367590bcf07db97604edeea55dac9ad/regex-2024.11.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:59dfe1ed21aea057a65c6b586afd2a945de04fc7db3de0a6e3ed5397ad491b07", size = 781650 },
+ { url = "https://files.pythonhosted.org/packages/a7/d5/880921ee4eec393a4752e6ab9f0fe28009435417c3102fc413f3fe81c4e5/regex-2024.11.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b97c1e0bd37c5cd7902e65f410779d39eeda155800b65fc4d04cc432efa9bc6e", size = 789545 },
+ { url = "https://files.pythonhosted.org/packages/dc/96/53770115e507081122beca8899ab7f5ae28ae790bfcc82b5e38976df6a77/regex-2024.11.6-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f9d1e379028e0fc2ae3654bac3cbbef81bf3fd571272a42d56c24007979bafb6", size = 853045 },
+ { url = "https://files.pythonhosted.org/packages/31/d3/1372add5251cc2d44b451bd94f43b2ec78e15a6e82bff6a290ef9fd8f00a/regex-2024.11.6-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:13291b39131e2d002a7940fb176e120bec5145f3aeb7621be6534e46251912c4", size = 860182 },
+ { url = "https://files.pythonhosted.org/packages/ed/e3/c446a64984ea9f69982ba1a69d4658d5014bc7a0ea468a07e1a1265db6e2/regex-2024.11.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4f51f88c126370dcec4908576c5a627220da6c09d0bff31cfa89f2523843316d", size = 787733 },
+ { url = "https://files.pythonhosted.org/packages/2b/f1/e40c8373e3480e4f29f2692bd21b3e05f296d3afebc7e5dcf21b9756ca1c/regex-2024.11.6-cp313-cp313-win32.whl", hash = "sha256:63b13cfd72e9601125027202cad74995ab26921d8cd935c25f09c630436348ff", size = 262122 },
+ { url = "https://files.pythonhosted.org/packages/45/94/bc295babb3062a731f52621cdc992d123111282e291abaf23faa413443ea/regex-2024.11.6-cp313-cp313-win_amd64.whl", hash = "sha256:2b3361af3198667e99927da8b84c1b010752fa4b1115ee30beaa332cabc3ef1a", size = 273545 },
+]
+
+[[package]]
+name = "requests"
+version = "2.32.3"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "certifi" },
+ { name = "charset-normalizer" },
+ { name = "idna" },
+ { name = "urllib3" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928 },
+]
+
+[[package]]
+name = "requests-toolbelt"
+version = "1.0.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "requests" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/f3/61/d7545dafb7ac2230c70d38d31cbfe4cc64f7144dc41f6e4e4b78ecd9f5bb/requests-toolbelt-1.0.0.tar.gz", hash = "sha256:7681a0a3d047012b5bdc0ee37d7f8f07ebe76ab08caeccfc3921ce23c88d5bc6", size = 206888 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/3f/51/d4db610ef29373b879047326cbf6fa98b6c1969d6f6dc423279de2b1be2c/requests_toolbelt-1.0.0-py2.py3-none-any.whl", hash = "sha256:cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06", size = 54481 },
+]
+
+[[package]]
+name = "rich"
+version = "13.9.4"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "markdown-it-py" },
+ { name = "pygments" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/ab/3a/0316b28d0761c6734d6bc14e770d85506c986c85ffb239e688eeaab2c2bc/rich-13.9.4.tar.gz", hash = "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098", size = 223149 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/19/71/39c7c0d87f8d4e6c020a393182060eaefeeae6c01dab6a84ec346f2567df/rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90", size = 242424 },
+]
+
+[[package]]
+name = "rich-click"
+version = "1.8.6"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "click" },
+ { name = "rich" },
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/ea/e3/ff1c715b673ec9e01f4482d8d0edfd9adf891f3630d83e695b38337a3889/rich_click-1.8.6.tar.gz", hash = "sha256:8a2448fd80e3d4e16fcb3815bfbc19be9bae75c9bb6aedf637901e45f3555752", size = 38247 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/7e/09/c20b04b6c9cf273995753f226ca51656e00f8a37f1e723f8c713b93b2ad4/rich_click-1.8.6-py3-none-any.whl", hash = "sha256:55fb571bad7d3d69ac43ca45f05b44616fd019616161b1815ff053567b9a8e22", size = 35076 },
+]
+
+[[package]]
+name = "rich-toolkit"
+version = "0.13.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "click" },
+ { name = "rich" },
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/5b/8a/71cfbf6bf6257ea785d1f030c22468f763eea1b3e5417620f2ba9abd6dca/rich_toolkit-0.13.2.tar.gz", hash = "sha256:fea92557530de7c28f121cbed572ad93d9e0ddc60c3ca643f1b831f2f56b95d3", size = 72288 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/7e/1b/1c2f43af46456050b27810a7a013af8a7e12bc545a0cdc00eb0df55eb769/rich_toolkit-0.13.2-py3-none-any.whl", hash = "sha256:f3f6c583e5283298a2f7dbd3c65aca18b7f818ad96174113ab5bec0b0e35ed61", size = 13566 },
+]
+
+[[package]]
+name = "roman-numerals-py"
+version = "3.1.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/30/76/48fd56d17c5bdbdf65609abbc67288728a98ed4c02919428d4f52d23b24b/roman_numerals_py-3.1.0.tar.gz", hash = "sha256:be4bf804f083a4ce001b5eb7e3c0862479d10f94c936f6c4e5f250aa5ff5bd2d", size = 9017 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/53/97/d2cbbaa10c9b826af0e10fdf836e1bf344d9f0abb873ebc34d1f49642d3f/roman_numerals_py-3.1.0-py3-none-any.whl", hash = "sha256:9da2ad2fb670bcf24e81070ceb3be72f6c11c440d73bd579fbeca1e9f330954c", size = 7742 },
+]
+
+[[package]]
+name = "ruff"
+version = "0.9.9"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/6f/c3/418441a8170e8d53d05c0b9dad69760dbc7b8a12c10dbe6db1e1205d2377/ruff-0.9.9.tar.gz", hash = "sha256:0062ed13f22173e85f8f7056f9a24016e692efeea8704d1a5e8011b8aa850933", size = 3717448 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/bc/c3/2c4afa9ba467555d074b146d9aed0633a56ccdb900839fb008295d037b89/ruff-0.9.9-py3-none-linux_armv6l.whl", hash = "sha256:628abb5ea10345e53dff55b167595a159d3e174d6720bf19761f5e467e68d367", size = 10027252 },
+ { url = "https://files.pythonhosted.org/packages/33/d1/439e58487cf9eac26378332e25e7d5ade4b800ce1eec7dc2cfc9b0d7ca96/ruff-0.9.9-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b6cd1428e834b35d7493354723543b28cc11dc14d1ce19b685f6e68e07c05ec7", size = 10840721 },
+ { url = "https://files.pythonhosted.org/packages/50/44/fead822c38281ba0122f1b76b460488a175a9bd48b130650a6fb6dbcbcf9/ruff-0.9.9-py3-none-macosx_11_0_arm64.whl", hash = "sha256:5ee162652869120ad260670706f3cd36cd3f32b0c651f02b6da142652c54941d", size = 10161439 },
+ { url = "https://files.pythonhosted.org/packages/11/ae/d404a2ab8e61ddf6342e09cc6b7f7846cce6b243e45c2007dbe0ca928a5d/ruff-0.9.9-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3aa0f6b75082c9be1ec5a1db78c6d4b02e2375c3068438241dc19c7c306cc61a", size = 10336264 },
+ { url = "https://files.pythonhosted.org/packages/6a/4e/7c268aa7d84cd709fb6f046b8972313142cffb40dfff1d2515c5e6288d54/ruff-0.9.9-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:584cc66e89fb5f80f84b05133dd677a17cdd86901d6479712c96597a3f28e7fe", size = 9908774 },
+ { url = "https://files.pythonhosted.org/packages/cc/26/c618a878367ef1b76270fd027ca93692657d3f6122b84ba48911ef5f2edc/ruff-0.9.9-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abf3369325761a35aba75cd5c55ba1b5eb17d772f12ab168fbfac54be85cf18c", size = 11428127 },
+ { url = "https://files.pythonhosted.org/packages/d7/9a/c5588a93d9bfed29f565baf193fe802fa676a0c837938137ea6cf0576d8c/ruff-0.9.9-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:3403a53a32a90ce929aa2f758542aca9234befa133e29f4933dcef28a24317be", size = 12133187 },
+ { url = "https://files.pythonhosted.org/packages/3e/ff/e7980a7704a60905ed7e156a8d73f604c846d9bd87deda9cabfa6cba073a/ruff-0.9.9-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:18454e7fa4e4d72cffe28a37cf6a73cb2594f81ec9f4eca31a0aaa9ccdfb1590", size = 11602937 },
+ { url = "https://files.pythonhosted.org/packages/24/78/3690444ad9e3cab5c11abe56554c35f005b51d1d118b429765249095269f/ruff-0.9.9-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fadfe2c88724c9617339f62319ed40dcdadadf2888d5afb88bf3adee7b35bfb", size = 13771698 },
+ { url = "https://files.pythonhosted.org/packages/6e/bf/e477c2faf86abe3988e0b5fd22a7f3520e820b2ee335131aca2e16120038/ruff-0.9.9-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6df104d08c442a1aabcfd254279b8cc1e2cbf41a605aa3e26610ba1ec4acf0b0", size = 11249026 },
+ { url = "https://files.pythonhosted.org/packages/f7/82/cdaffd59e5a8cb5b14c408c73d7a555a577cf6645faaf83e52fe99521715/ruff-0.9.9-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:d7c62939daf5b2a15af48abbd23bea1efdd38c312d6e7c4cedf5a24e03207e17", size = 10220432 },
+ { url = "https://files.pythonhosted.org/packages/fe/a4/2507d0026225efa5d4412b6e294dfe54725a78652a5c7e29e6bd0fc492f3/ruff-0.9.9-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:9494ba82a37a4b81b6a798076e4a3251c13243fc37967e998efe4cce58c8a8d1", size = 9874602 },
+ { url = "https://files.pythonhosted.org/packages/d5/be/f3aab1813846b476c4bcffe052d232244979c3cd99d751c17afb530ca8e4/ruff-0.9.9-py3-none-musllinux_1_2_i686.whl", hash = "sha256:4efd7a96ed6d36ef011ae798bf794c5501a514be369296c672dab7921087fa57", size = 10851212 },
+ { url = "https://files.pythonhosted.org/packages/8b/45/8e5fd559bea0d2f57c4e12bf197a2fade2fac465aa518284f157dfbca92b/ruff-0.9.9-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:ab90a7944c5a1296f3ecb08d1cbf8c2da34c7e68114b1271a431a3ad30cb660e", size = 11327490 },
+ { url = "https://files.pythonhosted.org/packages/42/55/e6c90f13880aeef327746052907e7e930681f26a164fe130ddac28b08269/ruff-0.9.9-py3-none-win32.whl", hash = "sha256:6b4c376d929c25ecd6d87e182a230fa4377b8e5125a4ff52d506ee8c087153c1", size = 10227912 },
+ { url = "https://files.pythonhosted.org/packages/35/b2/da925693cb82a1208aa34966c0f36cb222baca94e729dd22a587bc22d0f3/ruff-0.9.9-py3-none-win_amd64.whl", hash = "sha256:837982ea24091d4c1700ddb2f63b7070e5baec508e43b01de013dc7eff974ff1", size = 11355632 },
+ { url = "https://files.pythonhosted.org/packages/31/d8/de873d1c1b020d668d8ec9855d390764cb90cf8f6486c0983da52be8b7b7/ruff-0.9.9-py3-none-win_arm64.whl", hash = "sha256:3ac78f127517209fe6d96ab00f3ba97cafe38718b23b1db3e96d8b2d39e37ddf", size = 10435860 },
+]
+
+[[package]]
+name = "rustworkx"
+version = "0.16.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "numpy" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/a5/c4/6d6ef39e57610d54c5f106dc3dece9eebce8b9d52d561ae092e3aede1b66/rustworkx-0.16.0.tar.gz", hash = "sha256:9f0dcb83f38d5ca2c3a683eb9b6951c8aec3262fbfe5141946a7ee5ba37e0bb6", size = 349524 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/f8/70/36f5916aee41ffe4f604ad75742eb1bb1b849fb568e010555f9d159cd93e/rustworkx-0.16.0-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:476a6c67b0142acd941691943750cc6737a48372304489969c2b62d30aaf4c27", size = 2141999 },
+ { url = "https://files.pythonhosted.org/packages/94/47/7e7c37fb73efcc87be6414b235534605c4008a4cdbd92a61db23b878eecd/rustworkx-0.16.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:bef2ef42870f806af93979b457e240f6dfa4f867ca33965c620f3a804409ed3a", size = 1940309 },
+ { url = "https://files.pythonhosted.org/packages/c6/42/a6d6b3137be55ef1d887becdf6b64b0917c7d437bd483065a88500a55603/rustworkx-0.16.0-cp39-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0db3a73bf68b3e66c08322a2fc95d3aa663d037d9b4e49c3509da4898d3529cc", size = 2195350 },
+ { url = "https://files.pythonhosted.org/packages/59/d2/1bc99df831c132c4b7420a85ce9150e065f4c993798f31b6a4229f238398/rustworkx-0.16.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f12a13d7486234fa2a84746d5e41f436bf9df43548043e7a232f48804ff8c61", size = 1971689 },
+ { url = "https://files.pythonhosted.org/packages/b5/3b/1125e7eb834f4408bcec3cee79947efd504c715fb7ab1876f8cd4bbca497/rustworkx-0.16.0-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:89efd5c3a4653ddacc55ca39f28b261d43deec7d678f8f8fc6b76b5087f1dfea", size = 3297342 },
+ { url = "https://files.pythonhosted.org/packages/4f/e2/e21187b255c6211d71db0d08a44fc16771038b2af41712d66c408d9bec16/rustworkx-0.16.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec0c12aac8c54910ace20ac6ada4b890cd39f95f69100514715f8ad7af9041e4", size = 2110107 },
+ { url = "https://files.pythonhosted.org/packages/3c/79/e3fcff21f31253ea85ef196bf2fcabad7802b11468f7d3a5d592cd0ac789/rustworkx-0.16.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:d650e39fc1a1534335f7517358ebfc3478bb235428463cfcd7c5750d50377b33", size = 2007544 },
+ { url = "https://files.pythonhosted.org/packages/67/04/741ed09c2b0dc0f360f85270c1179ed433785372ac9ab6ab26d3dd3ae02d/rustworkx-0.16.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:293180b83509ee9bff4c3af7ccc1024f6528d61b65d0cb7320bd31924f10cb71", size = 2172787 },
+ { url = "https://files.pythonhosted.org/packages/6d/fd/9c71e90f8cde76fed95dbc1e7d019977b89a29492f49ded232c6fad3055f/rustworkx-0.16.0-cp39-abi3-win32.whl", hash = "sha256:040c4368729cf502f756a3b0ff5f1c6915fc389f74dcc6afc6c3833688c97c01", size = 1840183 },
+ { url = "https://files.pythonhosted.org/packages/3e/79/9bdd52d2a33d468c81c1827de1b588080cb055d1d3561b194ab7bf2635b5/rustworkx-0.16.0-cp39-abi3-win_amd64.whl", hash = "sha256:905df608843c32fa45ac023687769fe13056edf7584474c801d5c50705d76e9b", size = 1953559 },
+]
+
+[[package]]
+name = "sentry-sdk"
+version = "2.22.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "certifi" },
+ { name = "urllib3" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/81/b6/662988ecd2345bf6c3a5c306a9a3590852742eff91d0a78a143398b816f3/sentry_sdk-2.22.0.tar.gz", hash = "sha256:b4bf43bb38f547c84b2eadcefbe389b36ef75f3f38253d7a74d6b928c07ae944", size = 303539 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/12/7f/0e4459173e9671ba5f75a48dda2442bcc48a12c79e54e5789381c8c6a9bc/sentry_sdk-2.22.0-py2.py3-none-any.whl", hash = "sha256:3d791d631a6c97aad4da7074081a57073126c69487560c6f8bffcf586461de66", size = 325815 },
+]
+
+[[package]]
+name = "setuptools"
+version = "75.8.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/d1/53/43d99d7687e8cdef5ab5f9ec5eaf2c0423c2b35133a2b7e7bc276fc32b21/setuptools-75.8.2.tar.gz", hash = "sha256:4880473a969e5f23f2a2be3646b2dfd84af9028716d398e46192f84bc36900d2", size = 1344083 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/a9/38/7d7362e031bd6dc121e5081d8cb6aa6f6fedf2b67bf889962134c6da4705/setuptools-75.8.2-py3-none-any.whl", hash = "sha256:558e47c15f1811c1fa7adbd0096669bf76c1d3f433f58324df69f3f5ecac4e8f", size = 1229385 },
+]
+
+[[package]]
+name = "setuptools-scm"
+version = "8.2.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "packaging" },
+ { name = "setuptools" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/4b/bd/c5d16dd95900567e09744af92119da7abc5f447320d53ec1d9415ec30263/setuptools_scm-8.2.0.tar.gz", hash = "sha256:a18396a1bc0219c974d1a74612b11f9dce0d5bd8b1dc55c65f6ac7fd609e8c28", size = 77572 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/10/7c/5a9799042320242c383c4485a2771a37d49e8ce2312ca647653d2fd1a7a4/setuptools_scm-8.2.0-py3-none-any.whl", hash = "sha256:136e2b1d393d709d2bcf26f275b8dec06c48b811154167b0fd6bb002aad17d6d", size = 43944 },
+]
+
+[[package]]
+name = "shellingham"
+version = "1.5.4"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755 },
+]
+
+[[package]]
+name = "sigtools"
+version = "4.0.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "attrs" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/5f/db/669ca14166814da187b3087b908ca924cf83f5b504fe23b3859a3ef67d4f/sigtools-4.0.1.tar.gz", hash = "sha256:4b8e135a9cd4d2ea00da670c093372d74e672ba3abb87f4c98d8e73dea54445c", size = 71910 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/1f/91/853dbf6ec096197dba9cd5fd0c836c5fc19142038b7db60ebe6332b1bab1/sigtools-4.0.1-py2.py3-none-any.whl", hash = "sha256:d216b4cf920bbab0fce636ddc429ed8463a5b533d9e1492acb45a2a1bc36ac6c", size = 76419 },
+]
+
+[[package]]
+name = "six"
+version = "1.17.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050 },
+]
+
+[[package]]
+name = "slack-sdk"
+version = "3.34.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/6e/ff/6eb67fd5bd179fa804dbd859d88d872d3ae343955e63a319a73a132d406f/slack_sdk-3.34.0.tar.gz", hash = "sha256:ff61db7012160eed742285ea91f11c72b7a38a6500a7f6c5335662b4bc6b853d", size = 233629 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/25/2d/8724ef191cb64907de1e4e4436462955501e00f859a53d0aa794d0d060ff/slack_sdk-3.34.0-py2.py3-none-any.whl", hash = "sha256:c61f57f310d85be83466db5a98ab6ae3bb2e5587437b54fa0daa8fae6a0feffa", size = 292480 },
+]
+
+[[package]]
+name = "smmap"
+version = "5.0.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/44/cd/a040c4b3119bbe532e5b0732286f805445375489fceaec1f48306068ee3b/smmap-5.0.2.tar.gz", hash = "sha256:26ea65a03958fa0c8a1c7e8c7a58fdc77221b8910f6be2131affade476898ad5", size = 22329 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/04/be/d09147ad1ec7934636ad912901c5fd7667e1c858e19d355237db0d0cd5e4/smmap-5.0.2-py3-none-any.whl", hash = "sha256:b30115f0def7d7531d22a0fb6502488d879e75b260a9db4d0819cfb25403af5e", size = 24303 },
+]
+
+[[package]]
+name = "sniffio"
+version = "1.3.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235 },
+]
+
+[[package]]
+name = "snowballstemmer"
+version = "2.2.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/44/7b/af302bebf22c749c56c9c3e8ae13190b5b5db37a33d9068652e8f73b7089/snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1", size = 86699 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/ed/dc/c02e01294f7265e63a7315fe086dd1df7dacb9f840a804da846b96d01b96/snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a", size = 93002 },
+]
+
+[[package]]
+name = "soupsieve"
+version = "2.6"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/d7/ce/fbaeed4f9fb8b2daa961f90591662df6a86c1abf25c548329a86920aedfb/soupsieve-2.6.tar.gz", hash = "sha256:e2e68417777af359ec65daac1057404a3c8a5455bb8abc36f1a9866ab1a51abb", size = 101569 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/d1/c2/fe97d779f3ef3b15f05c94a2f1e3d21732574ed441687474db9d342a7315/soupsieve-2.6-py3-none-any.whl", hash = "sha256:e72c4ff06e4fb6e4b5a9f0f55fe6e81514581fca1515028625d0f299c602ccc9", size = 36186 },
+]
+
+[[package]]
+name = "sphinx"
+version = "8.2.3"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "alabaster" },
+ { name = "babel" },
+ { name = "colorama", marker = "sys_platform == 'win32'" },
+ { name = "docutils" },
+ { name = "imagesize" },
+ { name = "jinja2" },
+ { name = "packaging" },
+ { name = "pygments" },
+ { name = "requests" },
+ { name = "roman-numerals-py" },
+ { name = "snowballstemmer" },
+ { name = "sphinxcontrib-applehelp" },
+ { name = "sphinxcontrib-devhelp" },
+ { name = "sphinxcontrib-htmlhelp" },
+ { name = "sphinxcontrib-jsmath" },
+ { name = "sphinxcontrib-qthelp" },
+ { name = "sphinxcontrib-serializinghtml" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/38/ad/4360e50ed56cb483667b8e6dadf2d3fda62359593faabbe749a27c4eaca6/sphinx-8.2.3.tar.gz", hash = "sha256:398ad29dee7f63a75888314e9424d40f52ce5a6a87ae88e7071e80af296ec348", size = 8321876 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/31/53/136e9eca6e0b9dc0e1962e2c908fbea2e5ac000c2a2fbd9a35797958c48b/sphinx-8.2.3-py3-none-any.whl", hash = "sha256:4405915165f13521d875a8c29c8970800a0141c14cc5416a38feca4ea5d9b9c3", size = 3589741 },
+]
+
+[[package]]
+name = "sphinx-rtd-theme"
+version = "3.0.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "docutils" },
+ { name = "sphinx" },
+ { name = "sphinxcontrib-jquery" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/91/44/c97faec644d29a5ceddd3020ae2edffa69e7d00054a8c7a6021e82f20335/sphinx_rtd_theme-3.0.2.tar.gz", hash = "sha256:b7457bc25dda723b20b086a670b9953c859eab60a2a03ee8eb2bb23e176e5f85", size = 7620463 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/85/77/46e3bac77b82b4df5bb5b61f2de98637724f246b4966cfc34bc5895d852a/sphinx_rtd_theme-3.0.2-py2.py3-none-any.whl", hash = "sha256:422ccc750c3a3a311de4ae327e82affdaf59eb695ba4936538552f3b00f4ee13", size = 7655561 },
+]
+
+[[package]]
+name = "sphinxcontrib-applehelp"
+version = "2.0.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/ba/6e/b837e84a1a704953c62ef8776d45c3e8d759876b4a84fe14eba2859106fe/sphinxcontrib_applehelp-2.0.0.tar.gz", hash = "sha256:2f29ef331735ce958efa4734873f084941970894c6090408b079c61b2e1c06d1", size = 20053 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/5d/85/9ebeae2f76e9e77b952f4b274c27238156eae7979c5421fba91a28f4970d/sphinxcontrib_applehelp-2.0.0-py3-none-any.whl", hash = "sha256:4cd3f0ec4ac5dd9c17ec65e9ab272c9b867ea77425228e68ecf08d6b28ddbdb5", size = 119300 },
+]
+
+[[package]]
+name = "sphinxcontrib-devhelp"
+version = "2.0.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/f6/d2/5beee64d3e4e747f316bae86b55943f51e82bb86ecd325883ef65741e7da/sphinxcontrib_devhelp-2.0.0.tar.gz", hash = "sha256:411f5d96d445d1d73bb5d52133377b4248ec79db5c793ce7dbe59e074b4dd1ad", size = 12967 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/35/7a/987e583882f985fe4d7323774889ec58049171828b58c2217e7f79cdf44e/sphinxcontrib_devhelp-2.0.0-py3-none-any.whl", hash = "sha256:aefb8b83854e4b0998877524d1029fd3e6879210422ee3780459e28a1f03a8a2", size = 82530 },
+]
+
+[[package]]
+name = "sphinxcontrib-htmlhelp"
+version = "2.1.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/43/93/983afd9aa001e5201eab16b5a444ed5b9b0a7a010541e0ddfbbfd0b2470c/sphinxcontrib_htmlhelp-2.1.0.tar.gz", hash = "sha256:c9e2916ace8aad64cc13a0d233ee22317f2b9025b9cf3295249fa985cc7082e9", size = 22617 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/0a/7b/18a8c0bcec9182c05a0b3ec2a776bba4ead82750a55ff798e8d406dae604/sphinxcontrib_htmlhelp-2.1.0-py3-none-any.whl", hash = "sha256:166759820b47002d22914d64a075ce08f4c46818e17cfc9470a9786b759b19f8", size = 98705 },
+]
+
+[[package]]
+name = "sphinxcontrib-jquery"
+version = "4.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "sphinx" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/de/f3/aa67467e051df70a6330fe7770894b3e4f09436dea6881ae0b4f3d87cad8/sphinxcontrib-jquery-4.1.tar.gz", hash = "sha256:1620739f04e36a2c779f1a131a2dfd49b2fd07351bf1968ced074365933abc7a", size = 122331 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/76/85/749bd22d1a68db7291c89e2ebca53f4306c3f205853cf31e9de279034c3c/sphinxcontrib_jquery-4.1-py2.py3-none-any.whl", hash = "sha256:f936030d7d0147dd026a4f2b5a57343d233f1fc7b363f68b3d4f1cb0993878ae", size = 121104 },
+]
+
+[[package]]
+name = "sphinxcontrib-jsmath"
+version = "1.0.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/b2/e8/9ed3830aeed71f17c026a07a5097edcf44b692850ef215b161b8ad875729/sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8", size = 5787 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c2/42/4c8646762ee83602e3fb3fbe774c2fac12f317deb0b5dbeeedd2d3ba4b77/sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178", size = 5071 },
+]
+
+[[package]]
+name = "sphinxcontrib-qthelp"
+version = "2.0.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/68/bc/9104308fc285eb3e0b31b67688235db556cd5b0ef31d96f30e45f2e51cae/sphinxcontrib_qthelp-2.0.0.tar.gz", hash = "sha256:4fe7d0ac8fc171045be623aba3e2a8f613f8682731f9153bb2e40ece16b9bbab", size = 17165 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/27/83/859ecdd180cacc13b1f7e857abf8582a64552ea7a061057a6c716e790fce/sphinxcontrib_qthelp-2.0.0-py3-none-any.whl", hash = "sha256:b18a828cdba941ccd6ee8445dbe72ffa3ef8cbe7505d8cd1fa0d42d3f2d5f3eb", size = 88743 },
+]
+
+[[package]]
+name = "sphinxcontrib-serializinghtml"
+version = "2.0.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/3b/44/6716b257b0aa6bfd51a1b31665d1c205fb12cb5ad56de752dfa15657de2f/sphinxcontrib_serializinghtml-2.0.0.tar.gz", hash = "sha256:e9d912827f872c029017a53f0ef2180b327c3f7fd23c87229f7a8e8b70031d4d", size = 16080 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/52/a7/d2782e4e3f77c8450f727ba74a8f12756d5ba823d81b941f1b04da9d033a/sphinxcontrib_serializinghtml-2.0.0-py3-none-any.whl", hash = "sha256:6e2cb0eef194e10c27ec0023bfeb25badbbb5868244cf5bc5bdc04e4464bf331", size = 92072 },
+]
+
+[[package]]
+name = "sqlalchemy"
+version = "2.0.38"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "greenlet", marker = "platform_machine == 'AMD64' or platform_machine == 'WIN32' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'ppc64le' or platform_machine == 'win32' or platform_machine == 'x86_64'" },
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/e4/08/9a90962ea72acd532bda71249a626344d855c4032603924b1b547694b837/sqlalchemy-2.0.38.tar.gz", hash = "sha256:e5a4d82bdb4bf1ac1285a68eab02d253ab73355d9f0fe725a97e1e0fa689decb", size = 9634782 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/5a/f8/6d0424af1442c989b655a7b5f608bc2ae5e4f94cdf6df9f6054f629dc587/SQLAlchemy-2.0.38-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:12d5b06a1f3aeccf295a5843c86835033797fea292c60e72b07bcb5d820e6dd3", size = 2104927 },
+ { url = "https://files.pythonhosted.org/packages/25/80/fc06e65fca0a19533e2bfab633a5633ed8b6ee0b9c8d580acf84609ce4da/SQLAlchemy-2.0.38-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e036549ad14f2b414c725349cce0772ea34a7ab008e9cd67f9084e4f371d1f32", size = 2095317 },
+ { url = "https://files.pythonhosted.org/packages/98/2d/5d66605f76b8e344813237dc160a01f03b987201e974b46056a7fb94a874/SQLAlchemy-2.0.38-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ee3bee874cb1fadee2ff2b79fc9fc808aa638670f28b2145074538d4a6a5028e", size = 3244735 },
+ { url = "https://files.pythonhosted.org/packages/73/8d/b0539e8dce90861efc38fea3eefb15a5d0cfeacf818614762e77a9f192f9/SQLAlchemy-2.0.38-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e185ea07a99ce8b8edfc788c586c538c4b1351007e614ceb708fd01b095ef33e", size = 3255581 },
+ { url = "https://files.pythonhosted.org/packages/ac/a5/94e1e44bf5bdffd1782807fcc072542b110b950f0be53f49e68b5f5eca1b/SQLAlchemy-2.0.38-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b79ee64d01d05a5476d5cceb3c27b5535e6bb84ee0f872ba60d9a8cd4d0e6579", size = 3190877 },
+ { url = "https://files.pythonhosted.org/packages/91/13/f08b09996dce945aec029c64f61c13b4788541ac588d9288e31e0d3d8850/SQLAlchemy-2.0.38-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:afd776cf1ebfc7f9aa42a09cf19feadb40a26366802d86c1fba080d8e5e74bdd", size = 3217485 },
+ { url = "https://files.pythonhosted.org/packages/13/8f/8cfe2ba5ba6d8090f4de0e658330c53be6b7bf430a8df1b141c2b180dcdf/SQLAlchemy-2.0.38-cp312-cp312-win32.whl", hash = "sha256:a5645cd45f56895cfe3ca3459aed9ff2d3f9aaa29ff7edf557fa7a23515a3725", size = 2075254 },
+ { url = "https://files.pythonhosted.org/packages/c2/5c/e3c77fae41862be1da966ca98eec7fbc07cdd0b00f8b3e1ef2a13eaa6cca/SQLAlchemy-2.0.38-cp312-cp312-win_amd64.whl", hash = "sha256:1052723e6cd95312f6a6eff9a279fd41bbae67633415373fdac3c430eca3425d", size = 2100865 },
+ { url = "https://files.pythonhosted.org/packages/21/77/caa875a1f5a8a8980b564cc0e6fee1bc992d62d29101252561d0a5e9719c/SQLAlchemy-2.0.38-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ecef029b69843b82048c5b347d8e6049356aa24ed644006c9a9d7098c3bd3bfd", size = 2100201 },
+ { url = "https://files.pythonhosted.org/packages/f4/ec/94bb036ec78bf9a20f8010c807105da9152dd84f72e8c51681ad2f30b3fd/SQLAlchemy-2.0.38-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9c8bcad7fc12f0cc5896d8e10fdf703c45bd487294a986903fe032c72201596b", size = 2090678 },
+ { url = "https://files.pythonhosted.org/packages/7b/61/63ff1893f146e34d3934c0860209fdd3925c25ee064330e6c2152bacc335/SQLAlchemy-2.0.38-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a0ef3f98175d77180ffdc623d38e9f1736e8d86b6ba70bff182a7e68bed7727", size = 3177107 },
+ { url = "https://files.pythonhosted.org/packages/a9/4f/b933bea41a602b5f274065cc824fae25780ed38664d735575192490a021b/SQLAlchemy-2.0.38-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8b0ac78898c50e2574e9f938d2e5caa8fe187d7a5b69b65faa1ea4648925b096", size = 3190435 },
+ { url = "https://files.pythonhosted.org/packages/f5/23/9e654b4059e385988de08c5d3b38a369ea042f4c4d7c8902376fd737096a/SQLAlchemy-2.0.38-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9eb4fa13c8c7a2404b6a8e3772c17a55b1ba18bc711e25e4d6c0c9f5f541b02a", size = 3123648 },
+ { url = "https://files.pythonhosted.org/packages/83/59/94c6d804e76ebc6412a08d2b086a8cb3e5a056cd61508e18ddaf3ec70100/SQLAlchemy-2.0.38-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5dba1cdb8f319084f5b00d41207b2079822aa8d6a4667c0f369fce85e34b0c86", size = 3151789 },
+ { url = "https://files.pythonhosted.org/packages/b2/27/17f143013aabbe1256dce19061eafdce0b0142465ce32168cdb9a18c04b1/SQLAlchemy-2.0.38-cp313-cp313-win32.whl", hash = "sha256:eae27ad7580529a427cfdd52c87abb2dfb15ce2b7a3e0fc29fbb63e2ed6f8120", size = 2073023 },
+ { url = "https://files.pythonhosted.org/packages/e2/3e/259404b03c3ed2e7eee4c179e001a07d9b61070334be91124cf4ad32eec7/SQLAlchemy-2.0.38-cp313-cp313-win_amd64.whl", hash = "sha256:b335a7c958bc945e10c522c069cd6e5804f4ff20f9a744dd38e748eb602cbbda", size = 2096908 },
+ { url = "https://files.pythonhosted.org/packages/aa/e4/592120713a314621c692211eba034d09becaf6bc8848fabc1dc2a54d8c16/SQLAlchemy-2.0.38-py3-none-any.whl", hash = "sha256:63178c675d4c80def39f1febd625a6333f44c0ba269edd8a468b156394b27753", size = 1896347 },
+]
+
+[[package]]
+name = "sse-starlette"
+version = "2.2.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "anyio" },
+ { name = "starlette" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/71/a4/80d2a11af59fe75b48230846989e93979c892d3a20016b42bb44edb9e398/sse_starlette-2.2.1.tar.gz", hash = "sha256:54470d5f19274aeed6b2d473430b08b4b379ea851d953b11d7f1c4a2c118b419", size = 17376 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/d9/e0/5b8bd393f27f4a62461c5cf2479c75a2cc2ffa330976f9f00f5f6e4f50eb/sse_starlette-2.2.1-py3-none-any.whl", hash = "sha256:6410a3d3ba0c89e7675d4c273a301d64649c03a5ef1ca101f10b47f895fd0e99", size = 10120 },
+]
+
+[[package]]
+name = "starlette"
+version = "0.46.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "anyio" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/44/b6/fb9a32e3c5d59b1e383c357534c63c2d3caa6f25bf3c59dd89d296ecbaec/starlette-0.46.0.tar.gz", hash = "sha256:b359e4567456b28d473d0193f34c0de0ed49710d75ef183a74a5ce0499324f50", size = 2575568 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/41/94/8af675a62e3c91c2dee47cf92e602cfac86e8767b1a1ac3caf1b327c2ab0/starlette-0.46.0-py3-none-any.whl", hash = "sha256:913f0798bd90ba90a9156383bcf1350a17d6259451d0d8ee27fc0cf2db609038", size = 71991 },
+]
+
+[[package]]
+name = "swebench"
+version = "3.0.15"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "beautifulsoup4" },
+ { name = "chardet" },
+ { name = "datasets" },
+ { name = "docker" },
+ { name = "ghapi" },
+ { name = "gitpython" },
+ { name = "modal" },
+ { name = "pre-commit" },
+ { name = "python-dotenv" },
+ { name = "requests" },
+ { name = "rich" },
+ { name = "tenacity" },
+ { name = "tqdm" },
+ { name = "unidiff" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/a8/05/c163c2ee93f306110b27ddcdc7800ca1932c7489a35973e11c113d64d767/swebench-3.0.15.tar.gz", hash = "sha256:24e734fbcce34082665a25719075e6899382b7135103dd8c6cc09a6e23789101", size = 108523 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/62/6c/febe6bb4398e03aa48d50c555b36d2ac26b2e6d3c427ff9dba499b2557a2/swebench-3.0.15-py3-none-any.whl", hash = "sha256:dd694356f9c155a55d3d2e113fe58446f7385eea0574230af5e2504426f8b85b", size = 125151 },
+]
+
+[[package]]
+name = "swebench-agent-run"
+version = "0.1.0"
+source = { editable = "." }
+dependencies = [
+ { name = "click" },
+ { name = "codegen" },
+ { name = "modal" },
+ { name = "swebench" },
+ { name = "tenacity" },
+ { name = "tqdm" },
+]
+
+[package.optional-dependencies]
+all = [
+ { name = "mypy" },
+ { name = "psycopg2-binary" },
+ { name = "ruff" },
+]
+dev = [
+ { name = "mypy" },
+ { name = "ruff" },
+]
+metrics = [
+ { name = "psycopg2-binary" },
+]
+
+[package.metadata]
+requires-dist = [
+ { name = "click", specifier = ">=8.1.0" },
+ { name = "codegen", directory = "../../../" },
+ { name = "modal", specifier = ">=0.73.25" },
+ { name = "mypy", marker = "extra == 'dev'" },
+ { name = "psycopg2-binary", marker = "extra == 'metrics'" },
+ { name = "ruff", marker = "extra == 'dev'" },
+ { name = "swebench", specifier = ">=3.0.15" },
+ { name = "swebench-agent-run", extras = ["metrics", "dev"], marker = "extra == 'all'" },
+ { name = "tenacity", specifier = ">=9.0.0" },
+ { name = "tqdm", specifier = ">=4.66.0" },
+]
+
+[[package]]
+name = "synchronicity"
+version = "0.9.11"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "sigtools" },
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/b5/52/f34a9ab6d514e0808d0f572affb360411d596b3439107318c00889277dd6/synchronicity-0.9.11.tar.gz", hash = "sha256:cb5dbbcb43d637e516ae50db05a776da51a705d1e1a9c0e301f6049afc3c2cae", size = 50323 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/f2/d5/7675cd9b8e18f05b9ea261acad5d197fcb8027d2a65b1a750427ec084593/synchronicity-0.9.11-py3-none-any.whl", hash = "sha256:231129654d2f56b1aa148e85ebd8545231be135771f6d2196d414175b1594ef6", size = 36827 },
+]
+
+[[package]]
+name = "tabulate"
+version = "0.9.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/ec/fe/802052aecb21e3797b8f7902564ab6ea0d60ff8ca23952079064155d1ae1/tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c", size = 81090 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/40/44/4a5f08c96eb108af5cb50b41f76142f0afa346dfa99d5296fe7202a11854/tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f", size = 35252 },
+]
+
+[[package]]
+name = "tenacity"
+version = "9.0.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/cd/94/91fccdb4b8110642462e653d5dcb27e7b674742ad68efd146367da7bdb10/tenacity-9.0.0.tar.gz", hash = "sha256:807f37ca97d62aa361264d497b0e31e92b8027044942bfa756160d908320d73b", size = 47421 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/b6/cb/b86984bed139586d01532a587464b5805f12e397594f19f931c4c2fbfa61/tenacity-9.0.0-py3-none-any.whl", hash = "sha256:93de0c98785b27fcf659856aa9f54bfbd399e29969b0621bc7f762bd441b4539", size = 28169 },
+]
+
+[[package]]
+name = "termcolor"
+version = "2.5.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/37/72/88311445fd44c455c7d553e61f95412cf89054308a1aa2434ab835075fc5/termcolor-2.5.0.tar.gz", hash = "sha256:998d8d27da6d48442e8e1f016119076b690d962507531df4890fcd2db2ef8a6f", size = 13057 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/7f/be/df630c387a0a054815d60be6a97eb4e8f17385d5d6fe660e1c02750062b4/termcolor-2.5.0-py3-none-any.whl", hash = "sha256:37b17b5fc1e604945c2642c872a3764b5d547a48009871aea3edd3afa180afb8", size = 7755 },
+]
+
+[[package]]
+name = "tiktoken"
+version = "0.9.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "regex" },
+ { name = "requests" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/ea/cf/756fedf6981e82897f2d570dd25fa597eb3f4459068ae0572d7e888cfd6f/tiktoken-0.9.0.tar.gz", hash = "sha256:d02a5ca6a938e0490e1ff957bc48c8b078c88cb83977be1625b1fd8aac792c5d", size = 35991 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/cf/e5/21ff33ecfa2101c1bb0f9b6df750553bd873b7fb532ce2cb276ff40b197f/tiktoken-0.9.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:e88f121c1c22b726649ce67c089b90ddda8b9662545a8aeb03cfef15967ddd03", size = 1065073 },
+ { url = "https://files.pythonhosted.org/packages/8e/03/a95e7b4863ee9ceec1c55983e4cc9558bcfd8f4f80e19c4f8a99642f697d/tiktoken-0.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a6600660f2f72369acb13a57fb3e212434ed38b045fd8cc6cdd74947b4b5d210", size = 1008075 },
+ { url = "https://files.pythonhosted.org/packages/40/10/1305bb02a561595088235a513ec73e50b32e74364fef4de519da69bc8010/tiktoken-0.9.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:95e811743b5dfa74f4b227927ed86cbc57cad4df859cb3b643be797914e41794", size = 1140754 },
+ { url = "https://files.pythonhosted.org/packages/1b/40/da42522018ca496432ffd02793c3a72a739ac04c3794a4914570c9bb2925/tiktoken-0.9.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99376e1370d59bcf6935c933cb9ba64adc29033b7e73f5f7569f3aad86552b22", size = 1196678 },
+ { url = "https://files.pythonhosted.org/packages/5c/41/1e59dddaae270ba20187ceb8aa52c75b24ffc09f547233991d5fd822838b/tiktoken-0.9.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:badb947c32739fb6ddde173e14885fb3de4d32ab9d8c591cbd013c22b4c31dd2", size = 1259283 },
+ { url = "https://files.pythonhosted.org/packages/5b/64/b16003419a1d7728d0d8c0d56a4c24325e7b10a21a9dd1fc0f7115c02f0a/tiktoken-0.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:5a62d7a25225bafed786a524c1b9f0910a1128f4232615bf3f8257a73aaa3b16", size = 894897 },
+ { url = "https://files.pythonhosted.org/packages/7a/11/09d936d37f49f4f494ffe660af44acd2d99eb2429d60a57c71318af214e0/tiktoken-0.9.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2b0e8e05a26eda1249e824156d537015480af7ae222ccb798e5234ae0285dbdb", size = 1064919 },
+ { url = "https://files.pythonhosted.org/packages/80/0e/f38ba35713edb8d4197ae602e80837d574244ced7fb1b6070b31c29816e0/tiktoken-0.9.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:27d457f096f87685195eea0165a1807fae87b97b2161fe8c9b1df5bd74ca6f63", size = 1007877 },
+ { url = "https://files.pythonhosted.org/packages/fe/82/9197f77421e2a01373e27a79dd36efdd99e6b4115746ecc553318ecafbf0/tiktoken-0.9.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cf8ded49cddf825390e36dd1ad35cd49589e8161fdcb52aa25f0583e90a3e01", size = 1140095 },
+ { url = "https://files.pythonhosted.org/packages/f2/bb/4513da71cac187383541facd0291c4572b03ec23c561de5811781bbd988f/tiktoken-0.9.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc156cb314119a8bb9748257a2eaebd5cc0753b6cb491d26694ed42fc7cb3139", size = 1195649 },
+ { url = "https://files.pythonhosted.org/packages/fa/5c/74e4c137530dd8504e97e3a41729b1103a4ac29036cbfd3250b11fd29451/tiktoken-0.9.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:cd69372e8c9dd761f0ab873112aba55a0e3e506332dd9f7522ca466e817b1b7a", size = 1258465 },
+ { url = "https://files.pythonhosted.org/packages/de/a8/8f499c179ec900783ffe133e9aab10044481679bb9aad78436d239eee716/tiktoken-0.9.0-cp313-cp313-win_amd64.whl", hash = "sha256:5ea0edb6f83dc56d794723286215918c1cde03712cbbafa0348b33448faf5b95", size = 894669 },
+]
+
+[[package]]
+name = "toml"
+version = "0.10.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f", size = 22253 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", size = 16588 },
+]
+
+[[package]]
+name = "tomlkit"
+version = "0.13.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/b1/09/a439bec5888f00a54b8b9f05fa94d7f901d6735ef4e55dcec9bc37b5d8fa/tomlkit-0.13.2.tar.gz", hash = "sha256:fff5fe59a87295b278abd31bec92c15d9bc4a06885ab12bcea52c71119392e79", size = 192885 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/f9/b6/a447b5e4ec71e13871be01ba81f5dfc9d0af7e473da256ff46bc0e24026f/tomlkit-0.13.2-py3-none-any.whl", hash = "sha256:7a974427f6e119197f670fbbbeae7bef749a6c14e793db934baefc1b5f03efde", size = 37955 },
+]
+
+[[package]]
+name = "tqdm"
+version = "4.67.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "colorama", marker = "sys_platform == 'win32'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/a8/4b/29b4ef32e036bb34e4ab51796dd745cdba7ed47ad142a9f4a1eb8e0c744d/tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2", size = 169737 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", size = 78540 },
+]
+
+[[package]]
+name = "tree-sitter"
+version = "0.24.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/a7/a2/698b9d31d08ad5558f8bfbfe3a0781bd4b1f284e89bde3ad18e05101a892/tree-sitter-0.24.0.tar.gz", hash = "sha256:abd95af65ca2f4f7eca356343391ed669e764f37748b5352946f00f7fc78e734", size = 168304 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/e9/57/3a590f287b5aa60c07d5545953912be3d252481bf5e178f750db75572bff/tree_sitter-0.24.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:14beeff5f11e223c37be7d5d119819880601a80d0399abe8c738ae2288804afc", size = 140788 },
+ { url = "https://files.pythonhosted.org/packages/61/0b/fc289e0cba7dbe77c6655a4dd949cd23c663fd62a8b4d8f02f97e28d7fe5/tree_sitter-0.24.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:26a5b130f70d5925d67b47db314da209063664585a2fd36fa69e0717738efaf4", size = 133945 },
+ { url = "https://files.pythonhosted.org/packages/86/d7/80767238308a137e0b5b5c947aa243e3c1e3e430e6d0d5ae94b9a9ffd1a2/tree_sitter-0.24.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5fc5c3c26d83c9d0ecb4fc4304fba35f034b7761d35286b936c1db1217558b4e", size = 564819 },
+ { url = "https://files.pythonhosted.org/packages/bf/b3/6c5574f4b937b836601f5fb556b24804b0a6341f2eb42f40c0e6464339f4/tree_sitter-0.24.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:772e1bd8c0931c866b848d0369b32218ac97c24b04790ec4b0e409901945dd8e", size = 579303 },
+ { url = "https://files.pythonhosted.org/packages/0a/f4/bd0ddf9abe242ea67cca18a64810f8af230fc1ea74b28bb702e838ccd874/tree_sitter-0.24.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:24a8dd03b0d6b8812425f3b84d2f4763322684e38baf74e5bb766128b5633dc7", size = 581054 },
+ { url = "https://files.pythonhosted.org/packages/8c/1c/ff23fa4931b6ef1bbeac461b904ca7e49eaec7e7e5398584e3eef836ec96/tree_sitter-0.24.0-cp312-cp312-win_amd64.whl", hash = "sha256:f9e8b1605ab60ed43803100f067eed71b0b0e6c1fb9860a262727dbfbbb74751", size = 120221 },
+ { url = "https://files.pythonhosted.org/packages/b2/2a/9979c626f303177b7612a802237d0533155bf1e425ff6f73cc40f25453e2/tree_sitter-0.24.0-cp312-cp312-win_arm64.whl", hash = "sha256:f733a83d8355fc95561582b66bbea92ffd365c5d7a665bc9ebd25e049c2b2abb", size = 108234 },
+ { url = "https://files.pythonhosted.org/packages/61/cd/2348339c85803330ce38cee1c6cbbfa78a656b34ff58606ebaf5c9e83bd0/tree_sitter-0.24.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0d4a6416ed421c4210f0ca405a4834d5ccfbb8ad6692d4d74f7773ef68f92071", size = 140781 },
+ { url = "https://files.pythonhosted.org/packages/8b/a3/1ea9d8b64e8dcfcc0051028a9c84a630301290995cd6e947bf88267ef7b1/tree_sitter-0.24.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e0992d483677e71d5c5d37f30dfb2e3afec2f932a9c53eec4fca13869b788c6c", size = 133928 },
+ { url = "https://files.pythonhosted.org/packages/fe/ae/55c1055609c9428a4aedf4b164400ab9adb0b1bf1538b51f4b3748a6c983/tree_sitter-0.24.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:57277a12fbcefb1c8b206186068d456c600dbfbc3fd6c76968ee22614c5cd5ad", size = 564497 },
+ { url = "https://files.pythonhosted.org/packages/ce/d0/f2ffcd04882c5aa28d205a787353130cbf84b2b8a977fd211bdc3b399ae3/tree_sitter-0.24.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d25fa22766d63f73716c6fec1a31ee5cf904aa429484256bd5fdf5259051ed74", size = 578917 },
+ { url = "https://files.pythonhosted.org/packages/af/82/aebe78ea23a2b3a79324993d4915f3093ad1af43d7c2208ee90be9273273/tree_sitter-0.24.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7d5d9537507e1c8c5fa9935b34f320bfec4114d675e028f3ad94f11cf9db37b9", size = 581148 },
+ { url = "https://files.pythonhosted.org/packages/a1/b4/6b0291a590c2b0417cfdb64ccb8ea242f270a46ed429c641fbc2bfab77e0/tree_sitter-0.24.0-cp313-cp313-win_amd64.whl", hash = "sha256:f58bb4956917715ec4d5a28681829a8dad5c342cafd4aea269f9132a83ca9b34", size = 120207 },
+ { url = "https://files.pythonhosted.org/packages/a8/18/542fd844b75272630229c9939b03f7db232c71a9d82aadc59c596319ea6a/tree_sitter-0.24.0-cp313-cp313-win_arm64.whl", hash = "sha256:23641bd25dcd4bb0b6fa91b8fb3f46cc9f1c9f475efe4d536d3f1f688d1b84c8", size = 108232 },
+]
+
+[[package]]
+name = "tree-sitter-javascript"
+version = "0.23.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/cd/dc/1c55c33cc6bbe754359b330534cf9f261c1b9b2c26ddf23aef3c5fa67759/tree_sitter_javascript-0.23.1.tar.gz", hash = "sha256:b2059ce8b150162cda05a457ca3920450adbf915119c04b8c67b5241cd7fcfed", size = 110058 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/20/d3/c67d7d49967344b51208ad19f105233be1afdf07d3dcb35b471900265227/tree_sitter_javascript-0.23.1-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:6ca583dad4bd79d3053c310b9f7208cd597fd85f9947e4ab2294658bb5c11e35", size = 59333 },
+ { url = "https://files.pythonhosted.org/packages/a5/db/ea0ee1547679d1750e80a0c4bc60b3520b166eeaf048764cfdd1ba3fd5e5/tree_sitter_javascript-0.23.1-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:94100e491a6a247aa4d14caf61230c171b6376c863039b6d9cd71255c2d815ec", size = 61071 },
+ { url = "https://files.pythonhosted.org/packages/67/6e/07c4857e08be37bfb55bfb269863df8ec908b2f6a3f1893cd852b893ecab/tree_sitter_javascript-0.23.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5a6bc1055b061c5055ec58f39ee9b2e9efb8e6e0ae970838af74da0afb811f0a", size = 96999 },
+ { url = "https://files.pythonhosted.org/packages/5f/f5/4de730afe8b9422845bc2064020a8a8f49ebd1695c04261c38d1b3e3edec/tree_sitter_javascript-0.23.1-cp39-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:056dc04fb6b24293f8c5fec43c14e7e16ba2075b3009c643abf8c85edc4c7c3c", size = 94020 },
+ { url = "https://files.pythonhosted.org/packages/77/0a/f980520da86c4eff8392867840a945578ef43372c9d4a37922baa6b121fe/tree_sitter_javascript-0.23.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a11ca1c0f736da42967586b568dff8a465ee148a986c15ebdc9382806e0ce871", size = 92927 },
+ { url = "https://files.pythonhosted.org/packages/ff/5c/36a98d512aa1d1082409d6b7eda5d26b820bd4477a54100ad9f62212bc55/tree_sitter_javascript-0.23.1-cp39-abi3-win_amd64.whl", hash = "sha256:041fa22b34250ea6eb313d33104d5303f79504cb259d374d691e38bbdc49145b", size = 58824 },
+ { url = "https://files.pythonhosted.org/packages/dc/79/ceb21988e6de615355a63eebcf806cd2a0fe875bec27b429d58b63e7fb5f/tree_sitter_javascript-0.23.1-cp39-abi3-win_arm64.whl", hash = "sha256:eb28130cd2fb30d702d614cbf61ef44d1c7f6869e7d864a9cc17111e370be8f7", size = 57027 },
+]
+
+[[package]]
+name = "tree-sitter-python"
+version = "0.23.6"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/1c/30/6766433b31be476fda6569a3a374c2220e45ffee0bff75460038a57bf23b/tree_sitter_python-0.23.6.tar.gz", hash = "sha256:354bfa0a2f9217431764a631516f85173e9711af2c13dbd796a8815acfe505d9", size = 155868 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/ab/67/577a02acae5f776007c924ca86ef14c19c12e71de0aa9d2a036f3c248e7b/tree_sitter_python-0.23.6-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:28fbec8f74eeb2b30292d97715e60fac9ccf8a8091ce19b9d93e9b580ed280fb", size = 74361 },
+ { url = "https://files.pythonhosted.org/packages/d2/a6/194b3625a7245c532ad418130d63077ce6cd241152524152f533e4d6edb0/tree_sitter_python-0.23.6-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:680b710051b144fedf61c95197db0094f2245e82551bf7f0c501356333571f7a", size = 76436 },
+ { url = "https://files.pythonhosted.org/packages/d0/62/1da112689d6d282920e62c40e67ab39ea56463b0e7167bfc5e81818a770e/tree_sitter_python-0.23.6-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8a9dcef55507b6567207e8ee0a6b053d0688019b47ff7f26edc1764b7f4dc0a4", size = 112060 },
+ { url = "https://files.pythonhosted.org/packages/5d/62/c9358584c96e38318d69b6704653684fd8467601f7b74e88aa44f4e6903f/tree_sitter_python-0.23.6-cp39-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:29dacdc0cd2f64e55e61d96c6906533ebb2791972bec988450c46cce60092f5d", size = 112338 },
+ { url = "https://files.pythonhosted.org/packages/1a/58/c5e61add45e34fb8ecbf057c500bae9d96ed7c9ca36edb7985da8ae45526/tree_sitter_python-0.23.6-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:7e048733c36f564b379831689006801feb267d8194f9e793fbb395ef1723335d", size = 109382 },
+ { url = "https://files.pythonhosted.org/packages/e9/f3/9b30893cae9b3811fe652dc6f90aaadfda12ae0b2757f5722fc7266f423c/tree_sitter_python-0.23.6-cp39-abi3-win_amd64.whl", hash = "sha256:a24027248399fb41594b696f929f9956828ae7cc85596d9f775e6c239cd0c2be", size = 75904 },
+ { url = "https://files.pythonhosted.org/packages/87/cb/ce35a65f83a47b510d8a2f1eddf3bdbb0d57aabc87351c8788caf3309f76/tree_sitter_python-0.23.6-cp39-abi3-win_arm64.whl", hash = "sha256:71334371bd73d5fe080aed39fbff49ed8efb9506edebe16795b0c7567ed6a272", size = 73649 },
+]
+
+[[package]]
+name = "tree-sitter-typescript"
+version = "0.23.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/1e/fc/bb52958f7e399250aee093751e9373a6311cadbe76b6e0d109b853757f35/tree_sitter_typescript-0.23.2.tar.gz", hash = "sha256:7b167b5827c882261cb7a50dfa0fb567975f9b315e87ed87ad0a0a3aedb3834d", size = 773053 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/28/95/4c00680866280e008e81dd621fd4d3f54aa3dad1b76b857a19da1b2cc426/tree_sitter_typescript-0.23.2-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:3cd752d70d8e5371fdac6a9a4df9d8924b63b6998d268586f7d374c9fba2a478", size = 286677 },
+ { url = "https://files.pythonhosted.org/packages/8f/2f/1f36fda564518d84593f2740d5905ac127d590baf5c5753cef2a88a89c15/tree_sitter_typescript-0.23.2-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:c7cc1b0ff5d91bac863b0e38b1578d5505e718156c9db577c8baea2557f66de8", size = 302008 },
+ { url = "https://files.pythonhosted.org/packages/96/2d/975c2dad292aa9994f982eb0b69cc6fda0223e4b6c4ea714550477d8ec3a/tree_sitter_typescript-0.23.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b1eed5b0b3a8134e86126b00b743d667ec27c63fc9de1b7bb23168803879e31", size = 351987 },
+ { url = "https://files.pythonhosted.org/packages/49/d1/a71c36da6e2b8a4ed5e2970819b86ef13ba77ac40d9e333cb17df6a2c5db/tree_sitter_typescript-0.23.2-cp39-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e96d36b85bcacdeb8ff5c2618d75593ef12ebaf1b4eace3477e2bdb2abb1752c", size = 344960 },
+ { url = "https://files.pythonhosted.org/packages/7f/cb/f57b149d7beed1a85b8266d0c60ebe4c46e79c9ba56bc17b898e17daf88e/tree_sitter_typescript-0.23.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:8d4f0f9bcb61ad7b7509d49a1565ff2cc363863644a234e1e0fe10960e55aea0", size = 340245 },
+ { url = "https://files.pythonhosted.org/packages/8b/ab/dd84f0e2337296a5f09749f7b5483215d75c8fa9e33738522e5ed81f7254/tree_sitter_typescript-0.23.2-cp39-abi3-win_amd64.whl", hash = "sha256:3f730b66396bc3e11811e4465c41ee45d9e9edd6de355a58bbbc49fa770da8f9", size = 278015 },
+ { url = "https://files.pythonhosted.org/packages/9f/e4/81f9a935789233cf412a0ed5fe04c883841d2c8fb0b7e075958a35c65032/tree_sitter_typescript-0.23.2-cp39-abi3-win_arm64.whl", hash = "sha256:05db58f70b95ef0ea126db5560f3775692f609589ed6f8dd0af84b7f19f1cbb7", size = 274052 },
+]
+
+[[package]]
+name = "trove-classifiers"
+version = "2025.3.3.18"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/08/e9/eb59303bac7aca949c4a4b0fa03a9b270be165d303a84cf2733d35a840ce/trove_classifiers-2025.3.3.18.tar.gz", hash = "sha256:3ffcfa90a428adfde1a5d90e3aa1b87fe474c5dbdbf5ccbca74ed69ba83c5ca7", size = 16239 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/ef/bf/44195f3d9c3c4fe4cccf1c261c80d50781b9e8a0a6febf084c09c66740ff/trove_classifiers-2025.3.3.18-py3-none-any.whl", hash = "sha256:215630da61cf8757c373f81b602fc1283ec5a691cf12c5f9f96f11d6ad5fc7f2", size = 13629 },
+]
+
+[[package]]
+name = "typer"
+version = "0.15.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "click" },
+ { name = "rich" },
+ { name = "shellingham" },
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/8b/6f/3991f0f1c7fcb2df31aef28e0594d8d54b05393a0e4e34c65e475c2a5d41/typer-0.15.2.tar.gz", hash = "sha256:ab2fab47533a813c49fe1f16b1a370fd5819099c00b119e0633df65f22144ba5", size = 100711 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/7f/fc/5b29fea8cee020515ca82cc68e3b8e1e34bb19a3535ad854cac9257b414c/typer-0.15.2-py3-none-any.whl", hash = "sha256:46a499c6107d645a9c13f7ee46c5d5096cae6f5fc57dd11eccbbb9ae3e44ddfc", size = 45061 },
+]
+
+[[package]]
+name = "types-certifi"
+version = "2021.10.8.3"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/52/68/943c3aeaf14624712a0357c4a67814dba5cea36d194f5c764dad7959a00c/types-certifi-2021.10.8.3.tar.gz", hash = "sha256:72cf7798d165bc0b76e1c10dd1ea3097c7063c42c21d664523b928e88b554a4f", size = 2095 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/b5/63/2463d89481e811f007b0e1cd0a91e52e141b47f9de724d20db7b861dcfec/types_certifi-2021.10.8.3-py3-none-any.whl", hash = "sha256:b2d1e325e69f71f7c78e5943d410e650b4707bb0ef32e4ddf3da37f54176e88a", size = 2136 },
+]
+
+[[package]]
+name = "types-toml"
+version = "0.10.8.20240310"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/86/47/3e4c75042792bff8e90d7991aa5c51812cc668828cc6cce711e97f63a607/types-toml-0.10.8.20240310.tar.gz", hash = "sha256:3d41501302972436a6b8b239c850b26689657e25281b48ff0ec06345b8830331", size = 4392 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/da/a2/d32ab58c0b216912638b140ab2170ee4b8644067c293b170e19fba340ccc/types_toml-0.10.8.20240310-py3-none-any.whl", hash = "sha256:627b47775d25fa29977d9c70dc0cbab3f314f32c8d8d0c012f2ef5de7aaec05d", size = 4777 },
+]
+
+[[package]]
+name = "typing-extensions"
+version = "4.12.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/df/db/f35a00659bc03fec321ba8bce9420de607a1d37f8342eee1863174c69557/typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8", size = 85321 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/26/9f/ad63fc0248c5379346306f8668cda6e2e2e9c95e01216d2b8ffd9ff037d0/typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", size = 37438 },
+]
+
+[[package]]
+name = "typing-inspect"
+version = "0.9.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "mypy-extensions" },
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/dc/74/1789779d91f1961fa9438e9a8710cdae6bd138c80d7303996933d117264a/typing_inspect-0.9.0.tar.gz", hash = "sha256:b23fc42ff6f6ef6954e4852c1fb512cdd18dbea03134f91f856a95ccc9461f78", size = 13825 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/65/f3/107a22063bf27bdccf2024833d3445f4eea42b2e598abfbd46f6a63b6cb0/typing_inspect-0.9.0-py3-none-any.whl", hash = "sha256:9ee6fc59062311ef8547596ab6b955e1b8aa46242d854bfc78f4f6b0eff35f9f", size = 8827 },
+]
+
+[[package]]
+name = "tzdata"
+version = "2025.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/43/0f/fa4723f22942480be4ca9527bbde8d43f6c3f2fe8412f00e7f5f6746bc8b/tzdata-2025.1.tar.gz", hash = "sha256:24894909e88cdb28bd1636c6887801df64cb485bd593f2fd83ef29075a81d694", size = 194950 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/0f/dd/84f10e23edd882c6f968c21c2434fe67bd4a528967067515feca9e611e5e/tzdata-2025.1-py2.py3-none-any.whl", hash = "sha256:7e127113816800496f027041c570f50bcd464a020098a3b6b199517772303639", size = 346762 },
+]
+
+[[package]]
+name = "unidiff"
+version = "0.7.5"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/a3/48/81be0ac96e423a877754153699731ef439fd7b80b4c8b5425c94ed079ebd/unidiff-0.7.5.tar.gz", hash = "sha256:2e5f0162052248946b9f0970a40e9e124236bf86c82b70821143a6fc1dea2574", size = 20931 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/8a/54/57c411a6e8f7bd7848c8b66e4dcaffa586bf4c02e63f2280db0327a4e6eb/unidiff-0.7.5-py2.py3-none-any.whl", hash = "sha256:c93bf2265cc1ba2a520e415ab05da587370bc2a3ae9e0414329f54f0c2fc09e8", size = 14386 },
+]
+
+[[package]]
+name = "urllib3"
+version = "2.3.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/aa/63/e53da845320b757bf29ef6a9062f5c669fe997973f966045cb019c3f4b66/urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d", size = 307268 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c8/19/4ec628951a74043532ca2cf5d97b7b14863931476d117c471e8e2b1eb39f/urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df", size = 128369 },
+]
+
+[[package]]
+name = "uvicorn"
+version = "0.34.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "click" },
+ { name = "h11" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/4b/4d/938bd85e5bf2edeec766267a5015ad969730bb91e31b44021dfe8b22df6c/uvicorn-0.34.0.tar.gz", hash = "sha256:404051050cd7e905de2c9a7e61790943440b3416f49cb409f965d9dcd0fa73e9", size = 76568 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/61/14/33a3a1352cfa71812a3a21e8c9bfb83f60b0011f5e36f2b1399d51928209/uvicorn-0.34.0-py3-none-any.whl", hash = "sha256:023dc038422502fa28a09c7a30bf2b6991512da7dcdb8fd35fe57cfc154126f4", size = 62315 },
+]
+
+[package.optional-dependencies]
+standard = [
+ { name = "colorama", marker = "sys_platform == 'win32'" },
+ { name = "httptools" },
+ { name = "python-dotenv" },
+ { name = "pyyaml" },
+ { name = "uvloop", marker = "platform_python_implementation != 'PyPy' and sys_platform != 'cygwin' and sys_platform != 'win32'" },
+ { name = "watchfiles" },
+ { name = "websockets" },
+]
+
+[[package]]
+name = "uvloop"
+version = "0.21.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/af/c0/854216d09d33c543f12a44b393c402e89a920b1a0a7dc634c42de91b9cf6/uvloop-0.21.0.tar.gz", hash = "sha256:3bf12b0fda68447806a7ad847bfa591613177275d35b6724b1ee573faa3704e3", size = 2492741 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/8c/4c/03f93178830dc7ce8b4cdee1d36770d2f5ebb6f3d37d354e061eefc73545/uvloop-0.21.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:359ec2c888397b9e592a889c4d72ba3d6befba8b2bb01743f72fffbde663b59c", size = 1471284 },
+ { url = "https://files.pythonhosted.org/packages/43/3e/92c03f4d05e50f09251bd8b2b2b584a2a7f8fe600008bcc4523337abe676/uvloop-0.21.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f7089d2dc73179ce5ac255bdf37c236a9f914b264825fdaacaded6990a7fb4c2", size = 821349 },
+ { url = "https://files.pythonhosted.org/packages/a6/ef/a02ec5da49909dbbfb1fd205a9a1ac4e88ea92dcae885e7c961847cd51e2/uvloop-0.21.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:baa4dcdbd9ae0a372f2167a207cd98c9f9a1ea1188a8a526431eef2f8116cc8d", size = 4580089 },
+ { url = "https://files.pythonhosted.org/packages/06/a7/b4e6a19925c900be9f98bec0a75e6e8f79bb53bdeb891916609ab3958967/uvloop-0.21.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86975dca1c773a2c9864f4c52c5a55631038e387b47eaf56210f873887b6c8dc", size = 4693770 },
+ { url = "https://files.pythonhosted.org/packages/ce/0c/f07435a18a4b94ce6bd0677d8319cd3de61f3a9eeb1e5f8ab4e8b5edfcb3/uvloop-0.21.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:461d9ae6660fbbafedd07559c6a2e57cd553b34b0065b6550685f6653a98c1cb", size = 4451321 },
+ { url = "https://files.pythonhosted.org/packages/8f/eb/f7032be105877bcf924709c97b1bf3b90255b4ec251f9340cef912559f28/uvloop-0.21.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:183aef7c8730e54c9a3ee3227464daed66e37ba13040bb3f350bc2ddc040f22f", size = 4659022 },
+ { url = "https://files.pythonhosted.org/packages/3f/8d/2cbef610ca21539f0f36e2b34da49302029e7c9f09acef0b1c3b5839412b/uvloop-0.21.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:bfd55dfcc2a512316e65f16e503e9e450cab148ef11df4e4e679b5e8253a5281", size = 1468123 },
+ { url = "https://files.pythonhosted.org/packages/93/0d/b0038d5a469f94ed8f2b2fce2434a18396d8fbfb5da85a0a9781ebbdec14/uvloop-0.21.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:787ae31ad8a2856fc4e7c095341cccc7209bd657d0e71ad0dc2ea83c4a6fa8af", size = 819325 },
+ { url = "https://files.pythonhosted.org/packages/50/94/0a687f39e78c4c1e02e3272c6b2ccdb4e0085fda3b8352fecd0410ccf915/uvloop-0.21.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ee4d4ef48036ff6e5cfffb09dd192c7a5027153948d85b8da7ff705065bacc6", size = 4582806 },
+ { url = "https://files.pythonhosted.org/packages/d2/19/f5b78616566ea68edd42aacaf645adbf71fbd83fc52281fba555dc27e3f1/uvloop-0.21.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3df876acd7ec037a3d005b3ab85a7e4110422e4d9c1571d4fc89b0fc41b6816", size = 4701068 },
+ { url = "https://files.pythonhosted.org/packages/47/57/66f061ee118f413cd22a656de622925097170b9380b30091b78ea0c6ea75/uvloop-0.21.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bd53ecc9a0f3d87ab847503c2e1552b690362e005ab54e8a48ba97da3924c0dc", size = 4454428 },
+ { url = "https://files.pythonhosted.org/packages/63/9a/0962b05b308494e3202d3f794a6e85abe471fe3cafdbcf95c2e8c713aabd/uvloop-0.21.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a5c39f217ab3c663dc699c04cbd50c13813e31d917642d459fdcec07555cc553", size = 4660018 },
+]
+
+[[package]]
+name = "virtualenv"
+version = "20.29.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "distlib" },
+ { name = "filelock" },
+ { name = "platformdirs" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/f1/88/dacc875dd54a8acadb4bcbfd4e3e86df8be75527116c91d8f9784f5e9cab/virtualenv-20.29.2.tar.gz", hash = "sha256:fdaabebf6d03b5ba83ae0a02cfe96f48a716f4fae556461d180825866f75b728", size = 4320272 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/93/fa/849483d56773ae29740ae70043ad88e068f98a6401aa819b5d6bee604683/virtualenv-20.29.2-py3-none-any.whl", hash = "sha256:febddfc3d1ea571bdb1dc0f98d7b45d24def7428214d4fb73cc486c9568cce6a", size = 4301478 },
+]
+
+[[package]]
+name = "watchfiles"
+version = "1.0.4"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "anyio" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/f5/26/c705fc77d0a9ecdb9b66f1e2976d95b81df3cae518967431e7dbf9b5e219/watchfiles-1.0.4.tar.gz", hash = "sha256:6ba473efd11062d73e4f00c2b730255f9c1bdd73cd5f9fe5b5da8dbd4a717205", size = 94625 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/5b/1a/8f4d9a1461709756ace48c98f07772bc6d4519b1e48b5fa24a4061216256/watchfiles-1.0.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:229e6ec880eca20e0ba2f7e2249c85bae1999d330161f45c78d160832e026ee2", size = 391345 },
+ { url = "https://files.pythonhosted.org/packages/bc/d2/6750b7b3527b1cdaa33731438432e7238a6c6c40a9924049e4cebfa40805/watchfiles-1.0.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5717021b199e8353782dce03bd8a8f64438832b84e2885c4a645f9723bf656d9", size = 381515 },
+ { url = "https://files.pythonhosted.org/packages/4e/17/80500e42363deef1e4b4818729ed939aaddc56f82f4e72b2508729dd3c6b/watchfiles-1.0.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0799ae68dfa95136dde7c472525700bd48777875a4abb2ee454e3ab18e9fc712", size = 449767 },
+ { url = "https://files.pythonhosted.org/packages/10/37/1427fa4cfa09adbe04b1e97bced19a29a3462cc64c78630787b613a23f18/watchfiles-1.0.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:43b168bba889886b62edb0397cab5b6490ffb656ee2fcb22dec8bfeb371a9e12", size = 455677 },
+ { url = "https://files.pythonhosted.org/packages/c5/7a/39e9397f3a19cb549a7d380412fd9e507d4854eddc0700bfad10ef6d4dba/watchfiles-1.0.4-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fb2c46e275fbb9f0c92e7654b231543c7bbfa1df07cdc4b99fa73bedfde5c844", size = 482219 },
+ { url = "https://files.pythonhosted.org/packages/45/2d/7113931a77e2ea4436cad0c1690c09a40a7f31d366f79c6f0a5bc7a4f6d5/watchfiles-1.0.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:857f5fc3aa027ff5e57047da93f96e908a35fe602d24f5e5d8ce64bf1f2fc733", size = 518830 },
+ { url = "https://files.pythonhosted.org/packages/f9/1b/50733b1980fa81ef3c70388a546481ae5fa4c2080040100cd7bf3bf7b321/watchfiles-1.0.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55ccfd27c497b228581e2838d4386301227fc0cb47f5a12923ec2fe4f97b95af", size = 497997 },
+ { url = "https://files.pythonhosted.org/packages/2b/b4/9396cc61b948ef18943e7c85ecfa64cf940c88977d882da57147f62b34b1/watchfiles-1.0.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c11ea22304d17d4385067588123658e9f23159225a27b983f343fcffc3e796a", size = 452249 },
+ { url = "https://files.pythonhosted.org/packages/fb/69/0c65a5a29e057ad0dc691c2fa6c23b2983c7dabaa190ba553b29ac84c3cc/watchfiles-1.0.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:74cb3ca19a740be4caa18f238298b9d472c850f7b2ed89f396c00a4c97e2d9ff", size = 614412 },
+ { url = "https://files.pythonhosted.org/packages/7f/b9/319fcba6eba5fad34327d7ce16a6b163b39741016b1996f4a3c96b8dd0e1/watchfiles-1.0.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c7cce76c138a91e720d1df54014a047e680b652336e1b73b8e3ff3158e05061e", size = 611982 },
+ { url = "https://files.pythonhosted.org/packages/f1/47/143c92418e30cb9348a4387bfa149c8e0e404a7c5b0585d46d2f7031b4b9/watchfiles-1.0.4-cp312-cp312-win32.whl", hash = "sha256:b045c800d55bc7e2cadd47f45a97c7b29f70f08a7c2fa13241905010a5493f94", size = 271822 },
+ { url = "https://files.pythonhosted.org/packages/ea/94/b0165481bff99a64b29e46e07ac2e0df9f7a957ef13bec4ceab8515f44e3/watchfiles-1.0.4-cp312-cp312-win_amd64.whl", hash = "sha256:c2acfa49dd0ad0bf2a9c0bb9a985af02e89345a7189be1efc6baa085e0f72d7c", size = 285441 },
+ { url = "https://files.pythonhosted.org/packages/11/de/09fe56317d582742d7ca8c2ca7b52a85927ebb50678d9b0fa8194658f536/watchfiles-1.0.4-cp312-cp312-win_arm64.whl", hash = "sha256:22bb55a7c9e564e763ea06c7acea24fc5d2ee5dfc5dafc5cfbedfe58505e9f90", size = 277141 },
+ { url = "https://files.pythonhosted.org/packages/08/98/f03efabec64b5b1fa58c0daab25c68ef815b0f320e54adcacd0d6847c339/watchfiles-1.0.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:8012bd820c380c3d3db8435e8cf7592260257b378b649154a7948a663b5f84e9", size = 390954 },
+ { url = "https://files.pythonhosted.org/packages/16/09/4dd49ba0a32a45813debe5fb3897955541351ee8142f586303b271a02b40/watchfiles-1.0.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:aa216f87594f951c17511efe5912808dfcc4befa464ab17c98d387830ce07b60", size = 381133 },
+ { url = "https://files.pythonhosted.org/packages/76/59/5aa6fc93553cd8d8ee75c6247763d77c02631aed21551a97d94998bf1dae/watchfiles-1.0.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62c9953cf85529c05b24705639ffa390f78c26449e15ec34d5339e8108c7c407", size = 449516 },
+ { url = "https://files.pythonhosted.org/packages/4c/aa/df4b6fe14b6317290b91335b23c96b488d365d65549587434817e06895ea/watchfiles-1.0.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7cf684aa9bba4cd95ecb62c822a56de54e3ae0598c1a7f2065d51e24637a3c5d", size = 454820 },
+ { url = "https://files.pythonhosted.org/packages/5e/71/185f8672f1094ce48af33252c73e39b48be93b761273872d9312087245f6/watchfiles-1.0.4-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f44a39aee3cbb9b825285ff979ab887a25c5d336e5ec3574f1506a4671556a8d", size = 481550 },
+ { url = "https://files.pythonhosted.org/packages/85/d7/50ebba2c426ef1a5cb17f02158222911a2e005d401caf5d911bfca58f4c4/watchfiles-1.0.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a38320582736922be8c865d46520c043bff350956dfc9fbaee3b2df4e1740a4b", size = 518647 },
+ { url = "https://files.pythonhosted.org/packages/f0/7a/4c009342e393c545d68987e8010b937f72f47937731225b2b29b7231428f/watchfiles-1.0.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:39f4914548b818540ef21fd22447a63e7be6e24b43a70f7642d21f1e73371590", size = 497547 },
+ { url = "https://files.pythonhosted.org/packages/0f/7c/1cf50b35412d5c72d63b2bf9a4fffee2e1549a245924960dd087eb6a6de4/watchfiles-1.0.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f12969a3765909cf5dc1e50b2436eb2c0e676a3c75773ab8cc3aa6175c16e902", size = 452179 },
+ { url = "https://files.pythonhosted.org/packages/d6/a9/3db1410e1c1413735a9a472380e4f431ad9a9e81711cda2aaf02b7f62693/watchfiles-1.0.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:0986902677a1a5e6212d0c49b319aad9cc48da4bd967f86a11bde96ad9676ca1", size = 614125 },
+ { url = "https://files.pythonhosted.org/packages/f2/e1/0025d365cf6248c4d1ee4c3d2e3d373bdd3f6aff78ba4298f97b4fad2740/watchfiles-1.0.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:308ac265c56f936636e3b0e3f59e059a40003c655228c131e1ad439957592303", size = 611911 },
+ { url = "https://files.pythonhosted.org/packages/55/55/035838277d8c98fc8c917ac9beeb0cd6c59d675dc2421df5f9fcf44a0070/watchfiles-1.0.4-cp313-cp313-win32.whl", hash = "sha256:aee397456a29b492c20fda2d8961e1ffb266223625346ace14e4b6d861ba9c80", size = 271152 },
+ { url = "https://files.pythonhosted.org/packages/f0/e5/96b8e55271685ddbadc50ce8bc53aa2dff278fb7ac4c2e473df890def2dc/watchfiles-1.0.4-cp313-cp313-win_amd64.whl", hash = "sha256:d6097538b0ae5c1b88c3b55afa245a66793a8fec7ada6755322e465fb1a0e8cc", size = 285216 },
+]
+
+[[package]]
+name = "websockets"
+version = "15.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/2e/7a/8bc4d15af7ff30f7ba34f9a172063bfcee9f5001d7cef04bee800a658f33/websockets-15.0.tar.gz", hash = "sha256:ca36151289a15b39d8d683fd8b7abbe26fc50be311066c5f8dcf3cb8cee107ab", size = 175574 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/22/1e/92c4547d7b2a93f848aedaf37e9054111bc00dc11bff4385ca3f80dbb412/websockets-15.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:cccc18077acd34c8072578394ec79563664b1c205f7a86a62e94fafc7b59001f", size = 174709 },
+ { url = "https://files.pythonhosted.org/packages/9f/37/eae4830a28061ba552516d84478686b637cd9e57d6a90b45ad69e89cb0af/websockets-15.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d4c22992e24f12de340ca5f824121a5b3e1a37ad4360b4e1aaf15e9d1c42582d", size = 172372 },
+ { url = "https://files.pythonhosted.org/packages/46/2f/b409f8b8aa9328d5a47f7a301a43319d540d70cf036d1e6443675978a988/websockets-15.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1206432cc6c644f6fc03374b264c5ff805d980311563202ed7fef91a38906276", size = 172607 },
+ { url = "https://files.pythonhosted.org/packages/d6/81/d7e2e4542d4b4df849b0110df1b1f94f2647b71ab4b65d672090931ad2bb/websockets-15.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d3cc75ef3e17490042c47e0523aee1bcc4eacd2482796107fd59dd1100a44bc", size = 182422 },
+ { url = "https://files.pythonhosted.org/packages/b6/91/3b303160938d123eea97f58be363f7dbec76e8c59d587e07b5bc257dd584/websockets-15.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b89504227a5311610e4be16071465885a0a3d6b0e82e305ef46d9b064ce5fb72", size = 181362 },
+ { url = "https://files.pythonhosted.org/packages/f2/8b/df6807f1ca339c567aba9a7ab03bfdb9a833f625e8d2b4fc7529e4c701de/websockets-15.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56e3efe356416bc67a8e093607315951d76910f03d2b3ad49c4ade9207bf710d", size = 181787 },
+ { url = "https://files.pythonhosted.org/packages/21/37/e6d3d5ebb0ebcaf98ae84904205c9dcaf3e0fe93e65000b9f08631ed7309/websockets-15.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0f2205cdb444a42a7919690238fb5979a05439b9dbb73dd47c863d39640d85ab", size = 182058 },
+ { url = "https://files.pythonhosted.org/packages/c9/df/6aca296f2be4c638ad20908bb3d7c94ce7afc8d9b4b2b0780d1fc59b359c/websockets-15.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:aea01f40995fa0945c020228ab919b8dfc93fc8a9f2d3d705ab5b793f32d9e99", size = 181434 },
+ { url = "https://files.pythonhosted.org/packages/88/f1/75717a982bab39bbe63c83f9df0e7753e5c98bab907eb4fb5d97fe5c8c11/websockets-15.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a9f8e33747b1332db11cf7fcf4a9512bef9748cb5eb4d3f7fbc8c30d75dc6ffc", size = 181431 },
+ { url = "https://files.pythonhosted.org/packages/e7/15/cee9e63ed9ac5bfc1a3ae8fc6c02c41745023c21eed622eef142d8fdd749/websockets-15.0-cp312-cp312-win32.whl", hash = "sha256:32e02a2d83f4954aa8c17e03fe8ec6962432c39aca4be7e8ee346b05a3476904", size = 175678 },
+ { url = "https://files.pythonhosted.org/packages/4e/00/993974c60f40faabb725d4dbae8b072ef73b4c4454bd261d3b1d34ace41f/websockets-15.0-cp312-cp312-win_amd64.whl", hash = "sha256:ffc02b159b65c05f2ed9ec176b715b66918a674bd4daed48a9a7a590dd4be1aa", size = 176119 },
+ { url = "https://files.pythonhosted.org/packages/12/23/be28dc1023707ac51768f848d28a946443041a348ee3a54abdf9f6283372/websockets-15.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:d2244d8ab24374bed366f9ff206e2619345f9cd7fe79aad5225f53faac28b6b1", size = 174714 },
+ { url = "https://files.pythonhosted.org/packages/8f/ff/02b5e9fbb078e7666bf3d25c18c69b499747a12f3e7f2776063ef3fb7061/websockets-15.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:3a302241fbe825a3e4fe07666a2ab513edfdc6d43ce24b79691b45115273b5e7", size = 172374 },
+ { url = "https://files.pythonhosted.org/packages/8e/61/901c8d4698e0477eff4c3c664d53f898b601fa83af4ce81946650ec2a4cb/websockets-15.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:10552fed076757a70ba2c18edcbc601c7637b30cdfe8c24b65171e824c7d6081", size = 172605 },
+ { url = "https://files.pythonhosted.org/packages/d2/4b/dc47601a80dff317aecf8da7b4ab278d11d3494b2c373b493e4887561f90/websockets-15.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c53f97032b87a406044a1c33d1e9290cc38b117a8062e8a8b285175d7e2f99c9", size = 182380 },
+ { url = "https://files.pythonhosted.org/packages/83/f7/b155d2b38f05ed47a0b8de1c9ea245fcd7fc625d89f35a37eccba34b42de/websockets-15.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1caf951110ca757b8ad9c4974f5cac7b8413004d2f29707e4d03a65d54cedf2b", size = 181325 },
+ { url = "https://files.pythonhosted.org/packages/d3/ff/040a20c01c294695cac0e361caf86f33347acc38f164f6d2be1d3e007d9f/websockets-15.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8bf1ab71f9f23b0a1d52ec1682a3907e0c208c12fef9c3e99d2b80166b17905f", size = 181763 },
+ { url = "https://files.pythonhosted.org/packages/cb/6a/af23e93678fda8341ac8775e85123425e45c608389d3514863c702896ea5/websockets-15.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bfcd3acc1a81f106abac6afd42327d2cf1e77ec905ae11dc1d9142a006a496b6", size = 182097 },
+ { url = "https://files.pythonhosted.org/packages/7e/3e/1069e159c30129dc03c01513b5830237e576f47cedb888777dd885cae583/websockets-15.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:c8c5c8e1bac05ef3c23722e591ef4f688f528235e2480f157a9cfe0a19081375", size = 181485 },
+ { url = "https://files.pythonhosted.org/packages/9a/a7/c91c47103f1cd941b576bbc452601e9e01f67d5c9be3e0a9abe726491ab5/websockets-15.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:86bfb52a9cfbcc09aba2b71388b0a20ea5c52b6517c0b2e316222435a8cdab72", size = 181466 },
+ { url = "https://files.pythonhosted.org/packages/16/32/a4ca6e3d56c24aac46b0cf5c03b841379f6409d07fc2044b244f90f54105/websockets-15.0-cp313-cp313-win32.whl", hash = "sha256:26ba70fed190708551c19a360f9d7eca8e8c0f615d19a574292b7229e0ae324c", size = 175673 },
+ { url = "https://files.pythonhosted.org/packages/c0/31/25a417a23e985b61ffa5544f9facfe4a118cb64d664c886f1244a8baeca5/websockets-15.0-cp313-cp313-win_amd64.whl", hash = "sha256:ae721bcc8e69846af00b7a77a220614d9b2ec57d25017a6bbde3a99473e41ce8", size = 176115 },
+ { url = "https://files.pythonhosted.org/packages/e8/b2/31eec524b53f01cd8343f10a8e429730c52c1849941d1f530f8253b6d934/websockets-15.0-py3-none-any.whl", hash = "sha256:51ffd53c53c4442415b613497a34ba0aa7b99ac07f1e4a62db5dcd640ae6c3c3", size = 169023 },
+]
+
+[[package]]
+name = "wrapt"
+version = "1.17.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/c3/fc/e91cc220803d7bc4db93fb02facd8461c37364151b8494762cc88b0fbcef/wrapt-1.17.2.tar.gz", hash = "sha256:41388e9d4d1522446fe79d3213196bd9e3b301a336965b9e27ca2788ebd122f3", size = 55531 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/a1/bd/ab55f849fd1f9a58ed7ea47f5559ff09741b25f00c191231f9f059c83949/wrapt-1.17.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d5e2439eecc762cd85e7bd37161d4714aa03a33c5ba884e26c81559817ca0925", size = 53799 },
+ { url = "https://files.pythonhosted.org/packages/53/18/75ddc64c3f63988f5a1d7e10fb204ffe5762bc663f8023f18ecaf31a332e/wrapt-1.17.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3fc7cb4c1c744f8c05cd5f9438a3caa6ab94ce8344e952d7c45a8ed59dd88392", size = 38821 },
+ { url = "https://files.pythonhosted.org/packages/48/2a/97928387d6ed1c1ebbfd4efc4133a0633546bec8481a2dd5ec961313a1c7/wrapt-1.17.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8fdbdb757d5390f7c675e558fd3186d590973244fab0c5fe63d373ade3e99d40", size = 38919 },
+ { url = "https://files.pythonhosted.org/packages/73/54/3bfe5a1febbbccb7a2f77de47b989c0b85ed3a6a41614b104204a788c20e/wrapt-1.17.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5bb1d0dbf99411f3d871deb6faa9aabb9d4e744d67dcaaa05399af89d847a91d", size = 88721 },
+ { url = "https://files.pythonhosted.org/packages/25/cb/7262bc1b0300b4b64af50c2720ef958c2c1917525238d661c3e9a2b71b7b/wrapt-1.17.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d18a4865f46b8579d44e4fe1e2bcbc6472ad83d98e22a26c963d46e4c125ef0b", size = 80899 },
+ { url = "https://files.pythonhosted.org/packages/2a/5a/04cde32b07a7431d4ed0553a76fdb7a61270e78c5fd5a603e190ac389f14/wrapt-1.17.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc570b5f14a79734437cb7b0500376b6b791153314986074486e0b0fa8d71d98", size = 89222 },
+ { url = "https://files.pythonhosted.org/packages/09/28/2e45a4f4771fcfb109e244d5dbe54259e970362a311b67a965555ba65026/wrapt-1.17.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6d9187b01bebc3875bac9b087948a2bccefe464a7d8f627cf6e48b1bbae30f82", size = 86707 },
+ { url = "https://files.pythonhosted.org/packages/c6/d2/dcb56bf5f32fcd4bd9aacc77b50a539abdd5b6536872413fd3f428b21bed/wrapt-1.17.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:9e8659775f1adf02eb1e6f109751268e493c73716ca5761f8acb695e52a756ae", size = 79685 },
+ { url = "https://files.pythonhosted.org/packages/80/4e/eb8b353e36711347893f502ce91c770b0b0929f8f0bed2670a6856e667a9/wrapt-1.17.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e8b2816ebef96d83657b56306152a93909a83f23994f4b30ad4573b00bd11bb9", size = 87567 },
+ { url = "https://files.pythonhosted.org/packages/17/27/4fe749a54e7fae6e7146f1c7d914d28ef599dacd4416566c055564080fe2/wrapt-1.17.2-cp312-cp312-win32.whl", hash = "sha256:468090021f391fe0056ad3e807e3d9034e0fd01adcd3bdfba977b6fdf4213ea9", size = 36672 },
+ { url = "https://files.pythonhosted.org/packages/15/06/1dbf478ea45c03e78a6a8c4be4fdc3c3bddea5c8de8a93bc971415e47f0f/wrapt-1.17.2-cp312-cp312-win_amd64.whl", hash = "sha256:ec89ed91f2fa8e3f52ae53cd3cf640d6feff92ba90d62236a81e4e563ac0e991", size = 38865 },
+ { url = "https://files.pythonhosted.org/packages/ce/b9/0ffd557a92f3b11d4c5d5e0c5e4ad057bd9eb8586615cdaf901409920b14/wrapt-1.17.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6ed6ffac43aecfe6d86ec5b74b06a5be33d5bb9243d055141e8cabb12aa08125", size = 53800 },
+ { url = "https://files.pythonhosted.org/packages/c0/ef/8be90a0b7e73c32e550c73cfb2fa09db62234227ece47b0e80a05073b375/wrapt-1.17.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:35621ae4c00e056adb0009f8e86e28eb4a41a4bfa8f9bfa9fca7d343fe94f998", size = 38824 },
+ { url = "https://files.pythonhosted.org/packages/36/89/0aae34c10fe524cce30fe5fc433210376bce94cf74d05b0d68344c8ba46e/wrapt-1.17.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a604bf7a053f8362d27eb9fefd2097f82600b856d5abe996d623babd067b1ab5", size = 38920 },
+ { url = "https://files.pythonhosted.org/packages/3b/24/11c4510de906d77e0cfb5197f1b1445d4fec42c9a39ea853d482698ac681/wrapt-1.17.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5cbabee4f083b6b4cd282f5b817a867cf0b1028c54d445b7ec7cfe6505057cf8", size = 88690 },
+ { url = "https://files.pythonhosted.org/packages/71/d7/cfcf842291267bf455b3e266c0c29dcb675b5540ee8b50ba1699abf3af45/wrapt-1.17.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49703ce2ddc220df165bd2962f8e03b84c89fee2d65e1c24a7defff6f988f4d6", size = 80861 },
+ { url = "https://files.pythonhosted.org/packages/d5/66/5d973e9f3e7370fd686fb47a9af3319418ed925c27d72ce16b791231576d/wrapt-1.17.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8112e52c5822fc4253f3901b676c55ddf288614dc7011634e2719718eaa187dc", size = 89174 },
+ { url = "https://files.pythonhosted.org/packages/a7/d3/8e17bb70f6ae25dabc1aaf990f86824e4fd98ee9cadf197054e068500d27/wrapt-1.17.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9fee687dce376205d9a494e9c121e27183b2a3df18037f89d69bd7b35bcf59e2", size = 86721 },
+ { url = "https://files.pythonhosted.org/packages/6f/54/f170dfb278fe1c30d0ff864513cff526d624ab8de3254b20abb9cffedc24/wrapt-1.17.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:18983c537e04d11cf027fbb60a1e8dfd5190e2b60cc27bc0808e653e7b218d1b", size = 79763 },
+ { url = "https://files.pythonhosted.org/packages/4a/98/de07243751f1c4a9b15c76019250210dd3486ce098c3d80d5f729cba029c/wrapt-1.17.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:703919b1633412ab54bcf920ab388735832fdcb9f9a00ae49387f0fe67dad504", size = 87585 },
+ { url = "https://files.pythonhosted.org/packages/f9/f0/13925f4bd6548013038cdeb11ee2cbd4e37c30f8bfd5db9e5a2a370d6e20/wrapt-1.17.2-cp313-cp313-win32.whl", hash = "sha256:abbb9e76177c35d4e8568e58650aa6926040d6a9f6f03435b7a522bf1c487f9a", size = 36676 },
+ { url = "https://files.pythonhosted.org/packages/bf/ae/743f16ef8c2e3628df3ddfd652b7d4c555d12c84b53f3d8218498f4ade9b/wrapt-1.17.2-cp313-cp313-win_amd64.whl", hash = "sha256:69606d7bb691b50a4240ce6b22ebb319c1cfb164e5f6569835058196e0f3a845", size = 38871 },
+ { url = "https://files.pythonhosted.org/packages/3d/bc/30f903f891a82d402ffb5fda27ec1d621cc97cb74c16fea0b6141f1d4e87/wrapt-1.17.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:4a721d3c943dae44f8e243b380cb645a709ba5bd35d3ad27bc2ed947e9c68192", size = 56312 },
+ { url = "https://files.pythonhosted.org/packages/8a/04/c97273eb491b5f1c918857cd26f314b74fc9b29224521f5b83f872253725/wrapt-1.17.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:766d8bbefcb9e00c3ac3b000d9acc51f1b399513f44d77dfe0eb026ad7c9a19b", size = 40062 },
+ { url = "https://files.pythonhosted.org/packages/4e/ca/3b7afa1eae3a9e7fefe499db9b96813f41828b9fdb016ee836c4c379dadb/wrapt-1.17.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e496a8ce2c256da1eb98bd15803a79bee00fc351f5dfb9ea82594a3f058309e0", size = 40155 },
+ { url = "https://files.pythonhosted.org/packages/89/be/7c1baed43290775cb9030c774bc53c860db140397047cc49aedaf0a15477/wrapt-1.17.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40d615e4fe22f4ad3528448c193b218e077656ca9ccb22ce2cb20db730f8d306", size = 113471 },
+ { url = "https://files.pythonhosted.org/packages/32/98/4ed894cf012b6d6aae5f5cc974006bdeb92f0241775addad3f8cd6ab71c8/wrapt-1.17.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a5aaeff38654462bc4b09023918b7f21790efb807f54c000a39d41d69cf552cb", size = 101208 },
+ { url = "https://files.pythonhosted.org/packages/ea/fd/0c30f2301ca94e655e5e057012e83284ce8c545df7661a78d8bfca2fac7a/wrapt-1.17.2-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a7d15bbd2bc99e92e39f49a04653062ee6085c0e18b3b7512a4f2fe91f2d681", size = 109339 },
+ { url = "https://files.pythonhosted.org/packages/75/56/05d000de894c4cfcb84bcd6b1df6214297b8089a7bd324c21a4765e49b14/wrapt-1.17.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:e3890b508a23299083e065f435a492b5435eba6e304a7114d2f919d400888cc6", size = 110232 },
+ { url = "https://files.pythonhosted.org/packages/53/f8/c3f6b2cf9b9277fb0813418e1503e68414cd036b3b099c823379c9575e6d/wrapt-1.17.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:8c8b293cd65ad716d13d8dd3624e42e5a19cc2a2f1acc74b30c2c13f15cb61a6", size = 100476 },
+ { url = "https://files.pythonhosted.org/packages/a7/b1/0bb11e29aa5139d90b770ebbfa167267b1fc548d2302c30c8f7572851738/wrapt-1.17.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4c82b8785d98cdd9fed4cac84d765d234ed3251bd6afe34cb7ac523cb93e8b4f", size = 106377 },
+ { url = "https://files.pythonhosted.org/packages/6a/e1/0122853035b40b3f333bbb25f1939fc1045e21dd518f7f0922b60c156f7c/wrapt-1.17.2-cp313-cp313t-win32.whl", hash = "sha256:13e6afb7fe71fe7485a4550a8844cc9ffbe263c0f1a1eea569bc7091d4898555", size = 37986 },
+ { url = "https://files.pythonhosted.org/packages/09/5e/1655cf481e079c1f22d0cabdd4e51733679932718dc23bf2db175f329b76/wrapt-1.17.2-cp313-cp313t-win_amd64.whl", hash = "sha256:eaf675418ed6b3b31c7a989fd007fa7c3be66ce14e5c3b27336383604c9da85c", size = 40750 },
+ { url = "https://files.pythonhosted.org/packages/2d/82/f56956041adef78f849db6b289b282e72b55ab8045a75abad81898c28d19/wrapt-1.17.2-py3-none-any.whl", hash = "sha256:b18f2d1533a71f069c7f82d524a52599053d4c7166e9dd374ae2136b7f40f7c8", size = 23594 },
+]
+
+[[package]]
+name = "xmltodict"
+version = "0.14.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/50/05/51dcca9a9bf5e1bce52582683ce50980bcadbc4fa5143b9f2b19ab99958f/xmltodict-0.14.2.tar.gz", hash = "sha256:201e7c28bb210e374999d1dde6382923ab0ed1a8a5faeece48ab525b7810a553", size = 51942 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/d6/45/fc303eb433e8a2a271739c98e953728422fa61a3c1f36077a49e395c972e/xmltodict-0.14.2-py2.py3-none-any.whl", hash = "sha256:20cc7d723ed729276e808f26fb6b3599f786cbc37e06c65e192ba77c40f20aac", size = 9981 },
+]
+
+[[package]]
+name = "xxhash"
+version = "3.5.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/00/5e/d6e5258d69df8b4ed8c83b6664f2b47d30d2dec551a29ad72a6c69eafd31/xxhash-3.5.0.tar.gz", hash = "sha256:84f2caddf951c9cbf8dc2e22a89d4ccf5d86391ac6418fe81e3c67d0cf60b45f", size = 84241 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/07/0e/1bfce2502c57d7e2e787600b31c83535af83746885aa1a5f153d8c8059d6/xxhash-3.5.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:14470ace8bd3b5d51318782cd94e6f94431974f16cb3b8dc15d52f3b69df8e00", size = 31969 },
+ { url = "https://files.pythonhosted.org/packages/3f/d6/8ca450d6fe5b71ce521b4e5db69622383d039e2b253e9b2f24f93265b52c/xxhash-3.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:59aa1203de1cb96dbeab595ded0ad0c0056bb2245ae11fac11c0ceea861382b9", size = 30787 },
+ { url = "https://files.pythonhosted.org/packages/5b/84/de7c89bc6ef63d750159086a6ada6416cc4349eab23f76ab870407178b93/xxhash-3.5.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:08424f6648526076e28fae6ea2806c0a7d504b9ef05ae61d196d571e5c879c84", size = 220959 },
+ { url = "https://files.pythonhosted.org/packages/fe/86/51258d3e8a8545ff26468c977101964c14d56a8a37f5835bc0082426c672/xxhash-3.5.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:61a1ff00674879725b194695e17f23d3248998b843eb5e933007ca743310f793", size = 200006 },
+ { url = "https://files.pythonhosted.org/packages/02/0a/96973bd325412feccf23cf3680fd2246aebf4b789122f938d5557c54a6b2/xxhash-3.5.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f2f2c61bee5844d41c3eb015ac652a0229e901074951ae48581d58bfb2ba01be", size = 428326 },
+ { url = "https://files.pythonhosted.org/packages/11/a7/81dba5010f7e733de88af9555725146fc133be97ce36533867f4c7e75066/xxhash-3.5.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d32a592cac88d18cc09a89172e1c32d7f2a6e516c3dfde1b9adb90ab5df54a6", size = 194380 },
+ { url = "https://files.pythonhosted.org/packages/fb/7d/f29006ab398a173f4501c0e4977ba288f1c621d878ec217b4ff516810c04/xxhash-3.5.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:70dabf941dede727cca579e8c205e61121afc9b28516752fd65724be1355cc90", size = 207934 },
+ { url = "https://files.pythonhosted.org/packages/8a/6e/6e88b8f24612510e73d4d70d9b0c7dff62a2e78451b9f0d042a5462c8d03/xxhash-3.5.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e5d0ddaca65ecca9c10dcf01730165fd858533d0be84c75c327487c37a906a27", size = 216301 },
+ { url = "https://files.pythonhosted.org/packages/af/51/7862f4fa4b75a25c3b4163c8a873f070532fe5f2d3f9b3fc869c8337a398/xxhash-3.5.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3e5b5e16c5a480fe5f59f56c30abdeba09ffd75da8d13f6b9b6fd224d0b4d0a2", size = 203351 },
+ { url = "https://files.pythonhosted.org/packages/22/61/8d6a40f288f791cf79ed5bb113159abf0c81d6efb86e734334f698eb4c59/xxhash-3.5.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:149b7914451eb154b3dfaa721315117ea1dac2cc55a01bfbd4df7c68c5dd683d", size = 210294 },
+ { url = "https://files.pythonhosted.org/packages/17/02/215c4698955762d45a8158117190261b2dbefe9ae7e5b906768c09d8bc74/xxhash-3.5.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:eade977f5c96c677035ff39c56ac74d851b1cca7d607ab3d8f23c6b859379cab", size = 414674 },
+ { url = "https://files.pythonhosted.org/packages/31/5c/b7a8db8a3237cff3d535261325d95de509f6a8ae439a5a7a4ffcff478189/xxhash-3.5.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fa9f547bd98f5553d03160967866a71056a60960be00356a15ecc44efb40ba8e", size = 192022 },
+ { url = "https://files.pythonhosted.org/packages/78/e3/dd76659b2811b3fd06892a8beb850e1996b63e9235af5a86ea348f053e9e/xxhash-3.5.0-cp312-cp312-win32.whl", hash = "sha256:f7b58d1fd3551b8c80a971199543379be1cee3d0d409e1f6d8b01c1a2eebf1f8", size = 30170 },
+ { url = "https://files.pythonhosted.org/packages/d9/6b/1c443fe6cfeb4ad1dcf231cdec96eb94fb43d6498b4469ed8b51f8b59a37/xxhash-3.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:fa0cafd3a2af231b4e113fba24a65d7922af91aeb23774a8b78228e6cd785e3e", size = 30040 },
+ { url = "https://files.pythonhosted.org/packages/0f/eb/04405305f290173acc0350eba6d2f1a794b57925df0398861a20fbafa415/xxhash-3.5.0-cp312-cp312-win_arm64.whl", hash = "sha256:586886c7e89cb9828bcd8a5686b12e161368e0064d040e225e72607b43858ba2", size = 26796 },
+ { url = "https://files.pythonhosted.org/packages/c9/b8/e4b3ad92d249be5c83fa72916c9091b0965cb0faeff05d9a0a3870ae6bff/xxhash-3.5.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:37889a0d13b0b7d739cfc128b1c902f04e32de17b33d74b637ad42f1c55101f6", size = 31795 },
+ { url = "https://files.pythonhosted.org/packages/fc/d8/b3627a0aebfbfa4c12a41e22af3742cf08c8ea84f5cc3367b5de2d039cce/xxhash-3.5.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:97a662338797c660178e682f3bc180277b9569a59abfb5925e8620fba00b9fc5", size = 30792 },
+ { url = "https://files.pythonhosted.org/packages/c3/cc/762312960691da989c7cd0545cb120ba2a4148741c6ba458aa723c00a3f8/xxhash-3.5.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7f85e0108d51092bdda90672476c7d909c04ada6923c14ff9d913c4f7dc8a3bc", size = 220950 },
+ { url = "https://files.pythonhosted.org/packages/fe/e9/cc266f1042c3c13750e86a535496b58beb12bf8c50a915c336136f6168dc/xxhash-3.5.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cd2fd827b0ba763ac919440042302315c564fdb797294d86e8cdd4578e3bc7f3", size = 199980 },
+ { url = "https://files.pythonhosted.org/packages/bf/85/a836cd0dc5cc20376de26b346858d0ac9656f8f730998ca4324921a010b9/xxhash-3.5.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:82085c2abec437abebf457c1d12fccb30cc8b3774a0814872511f0f0562c768c", size = 428324 },
+ { url = "https://files.pythonhosted.org/packages/b4/0e/15c243775342ce840b9ba34aceace06a1148fa1630cd8ca269e3223987f5/xxhash-3.5.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:07fda5de378626e502b42b311b049848c2ef38784d0d67b6f30bb5008642f8eb", size = 194370 },
+ { url = "https://files.pythonhosted.org/packages/87/a1/b028bb02636dfdc190da01951d0703b3d904301ed0ef6094d948983bef0e/xxhash-3.5.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c279f0d2b34ef15f922b77966640ade58b4ccdfef1c4d94b20f2a364617a493f", size = 207911 },
+ { url = "https://files.pythonhosted.org/packages/80/d5/73c73b03fc0ac73dacf069fdf6036c9abad82de0a47549e9912c955ab449/xxhash-3.5.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:89e66ceed67b213dec5a773e2f7a9e8c58f64daeb38c7859d8815d2c89f39ad7", size = 216352 },
+ { url = "https://files.pythonhosted.org/packages/b6/2a/5043dba5ddbe35b4fe6ea0a111280ad9c3d4ba477dd0f2d1fe1129bda9d0/xxhash-3.5.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:bcd51708a633410737111e998ceb3b45d3dbc98c0931f743d9bb0a209033a326", size = 203410 },
+ { url = "https://files.pythonhosted.org/packages/a2/b2/9a8ded888b7b190aed75b484eb5c853ddd48aa2896e7b59bbfbce442f0a1/xxhash-3.5.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3ff2c0a34eae7df88c868be53a8dd56fbdf592109e21d4bfa092a27b0bf4a7bf", size = 210322 },
+ { url = "https://files.pythonhosted.org/packages/98/62/440083fafbc917bf3e4b67c2ade621920dd905517e85631c10aac955c1d2/xxhash-3.5.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:4e28503dccc7d32e0b9817aa0cbfc1f45f563b2c995b7a66c4c8a0d232e840c7", size = 414725 },
+ { url = "https://files.pythonhosted.org/packages/75/db/009206f7076ad60a517e016bb0058381d96a007ce3f79fa91d3010f49cc2/xxhash-3.5.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a6c50017518329ed65a9e4829154626f008916d36295b6a3ba336e2458824c8c", size = 192070 },
+ { url = "https://files.pythonhosted.org/packages/1f/6d/c61e0668943a034abc3a569cdc5aeae37d686d9da7e39cf2ed621d533e36/xxhash-3.5.0-cp313-cp313-win32.whl", hash = "sha256:53a068fe70301ec30d868ece566ac90d873e3bb059cf83c32e76012c889b8637", size = 30172 },
+ { url = "https://files.pythonhosted.org/packages/96/14/8416dce965f35e3d24722cdf79361ae154fa23e2ab730e5323aa98d7919e/xxhash-3.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:80babcc30e7a1a484eab952d76a4f4673ff601f54d5142c26826502740e70b43", size = 30041 },
+ { url = "https://files.pythonhosted.org/packages/27/ee/518b72faa2073f5aa8e3262408d284892cb79cf2754ba0c3a5870645ef73/xxhash-3.5.0-cp313-cp313-win_arm64.whl", hash = "sha256:4811336f1ce11cac89dcbd18f3a25c527c16311709a89313c3acaf771def2d4b", size = 26801 },
+]
+
+[[package]]
+name = "yarl"
+version = "1.18.3"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "idna" },
+ { name = "multidict" },
+ { name = "propcache" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/b7/9d/4b94a8e6d2b51b599516a5cb88e5bc99b4d8d4583e468057eaa29d5f0918/yarl-1.18.3.tar.gz", hash = "sha256:ac1801c45cbf77b6c99242eeff4fffb5e4e73a800b5c4ad4fc0be5def634d2e1", size = 181062 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/33/85/bd2e2729752ff4c77338e0102914897512e92496375e079ce0150a6dc306/yarl-1.18.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1dd4bdd05407ced96fed3d7f25dbbf88d2ffb045a0db60dbc247f5b3c5c25d50", size = 142644 },
+ { url = "https://files.pythonhosted.org/packages/ff/74/1178322cc0f10288d7eefa6e4a85d8d2e28187ccab13d5b844e8b5d7c88d/yarl-1.18.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7c33dd1931a95e5d9a772d0ac5e44cac8957eaf58e3c8da8c1414de7dd27c576", size = 94962 },
+ { url = "https://files.pythonhosted.org/packages/be/75/79c6acc0261e2c2ae8a1c41cf12265e91628c8c58ae91f5ff59e29c0787f/yarl-1.18.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:25b411eddcfd56a2f0cd6a384e9f4f7aa3efee14b188de13048c25b5e91f1640", size = 92795 },
+ { url = "https://files.pythonhosted.org/packages/6b/32/927b2d67a412c31199e83fefdce6e645247b4fb164aa1ecb35a0f9eb2058/yarl-1.18.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:436c4fc0a4d66b2badc6c5fc5ef4e47bb10e4fd9bf0c79524ac719a01f3607c2", size = 332368 },
+ { url = "https://files.pythonhosted.org/packages/19/e5/859fca07169d6eceeaa4fde1997c91d8abde4e9a7c018e371640c2da2b71/yarl-1.18.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e35ef8683211db69ffe129a25d5634319a677570ab6b2eba4afa860f54eeaf75", size = 342314 },
+ { url = "https://files.pythonhosted.org/packages/08/75/76b63ccd91c9e03ab213ef27ae6add2e3400e77e5cdddf8ed2dbc36e3f21/yarl-1.18.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:84b2deecba4a3f1a398df819151eb72d29bfeb3b69abb145a00ddc8d30094512", size = 341987 },
+ { url = "https://files.pythonhosted.org/packages/1a/e1/a097d5755d3ea8479a42856f51d97eeff7a3a7160593332d98f2709b3580/yarl-1.18.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00e5a1fea0fd4f5bfa7440a47eff01d9822a65b4488f7cff83155a0f31a2ecba", size = 336914 },
+ { url = "https://files.pythonhosted.org/packages/0b/42/e1b4d0e396b7987feceebe565286c27bc085bf07d61a59508cdaf2d45e63/yarl-1.18.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d0e883008013c0e4aef84dcfe2a0b172c4d23c2669412cf5b3371003941f72bb", size = 325765 },
+ { url = "https://files.pythonhosted.org/packages/7e/18/03a5834ccc9177f97ca1bbb245b93c13e58e8225276f01eedc4cc98ab820/yarl-1.18.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5a3f356548e34a70b0172d8890006c37be92995f62d95a07b4a42e90fba54272", size = 344444 },
+ { url = "https://files.pythonhosted.org/packages/c8/03/a713633bdde0640b0472aa197b5b86e90fbc4c5bc05b727b714cd8a40e6d/yarl-1.18.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:ccd17349166b1bee6e529b4add61727d3f55edb7babbe4069b5764c9587a8cc6", size = 340760 },
+ { url = "https://files.pythonhosted.org/packages/eb/99/f6567e3f3bbad8fd101886ea0276c68ecb86a2b58be0f64077396cd4b95e/yarl-1.18.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b958ddd075ddba5b09bb0be8a6d9906d2ce933aee81100db289badbeb966f54e", size = 346484 },
+ { url = "https://files.pythonhosted.org/packages/8e/a9/84717c896b2fc6cb15bd4eecd64e34a2f0a9fd6669e69170c73a8b46795a/yarl-1.18.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c7d79f7d9aabd6011004e33b22bc13056a3e3fb54794d138af57f5ee9d9032cb", size = 359864 },
+ { url = "https://files.pythonhosted.org/packages/1e/2e/d0f5f1bef7ee93ed17e739ec8dbcb47794af891f7d165fa6014517b48169/yarl-1.18.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:4891ed92157e5430874dad17b15eb1fda57627710756c27422200c52d8a4e393", size = 364537 },
+ { url = "https://files.pythonhosted.org/packages/97/8a/568d07c5d4964da5b02621a517532adb8ec5ba181ad1687191fffeda0ab6/yarl-1.18.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ce1af883b94304f493698b00d0f006d56aea98aeb49d75ec7d98cd4a777e9285", size = 357861 },
+ { url = "https://files.pythonhosted.org/packages/7d/e3/924c3f64b6b3077889df9a1ece1ed8947e7b61b0a933f2ec93041990a677/yarl-1.18.3-cp312-cp312-win32.whl", hash = "sha256:f91c4803173928a25e1a55b943c81f55b8872f0018be83e3ad4938adffb77dd2", size = 84097 },
+ { url = "https://files.pythonhosted.org/packages/34/45/0e055320daaabfc169b21ff6174567b2c910c45617b0d79c68d7ab349b02/yarl-1.18.3-cp312-cp312-win_amd64.whl", hash = "sha256:7e2ee16578af3b52ac2f334c3b1f92262f47e02cc6193c598502bd46f5cd1477", size = 90399 },
+ { url = "https://files.pythonhosted.org/packages/30/c7/c790513d5328a8390be8f47be5d52e141f78b66c6c48f48d241ca6bd5265/yarl-1.18.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:90adb47ad432332d4f0bc28f83a5963f426ce9a1a8809f5e584e704b82685dcb", size = 140789 },
+ { url = "https://files.pythonhosted.org/packages/30/aa/a2f84e93554a578463e2edaaf2300faa61c8701f0898725842c704ba5444/yarl-1.18.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:913829534200eb0f789d45349e55203a091f45c37a2674678744ae52fae23efa", size = 94144 },
+ { url = "https://files.pythonhosted.org/packages/c6/fc/d68d8f83714b221a85ce7866832cba36d7c04a68fa6a960b908c2c84f325/yarl-1.18.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ef9f7768395923c3039055c14334ba4d926f3baf7b776c923c93d80195624782", size = 91974 },
+ { url = "https://files.pythonhosted.org/packages/56/4e/d2563d8323a7e9a414b5b25341b3942af5902a2263d36d20fb17c40411e2/yarl-1.18.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88a19f62ff30117e706ebc9090b8ecc79aeb77d0b1f5ec10d2d27a12bc9f66d0", size = 333587 },
+ { url = "https://files.pythonhosted.org/packages/25/c9/cfec0bc0cac8d054be223e9f2c7909d3e8442a856af9dbce7e3442a8ec8d/yarl-1.18.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e17c9361d46a4d5addf777c6dd5eab0715a7684c2f11b88c67ac37edfba6c482", size = 344386 },
+ { url = "https://files.pythonhosted.org/packages/ab/5d/4c532190113b25f1364d25f4c319322e86232d69175b91f27e3ebc2caf9a/yarl-1.18.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1a74a13a4c857a84a845505fd2d68e54826a2cd01935a96efb1e9d86c728e186", size = 345421 },
+ { url = "https://files.pythonhosted.org/packages/23/d1/6cdd1632da013aa6ba18cee4d750d953104a5e7aac44e249d9410a972bf5/yarl-1.18.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:41f7ce59d6ee7741af71d82020346af364949314ed3d87553763a2df1829cc58", size = 339384 },
+ { url = "https://files.pythonhosted.org/packages/9a/c4/6b3c39bec352e441bd30f432cda6ba51681ab19bb8abe023f0d19777aad1/yarl-1.18.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f52a265001d830bc425f82ca9eabda94a64a4d753b07d623a9f2863fde532b53", size = 326689 },
+ { url = "https://files.pythonhosted.org/packages/23/30/07fb088f2eefdc0aa4fc1af4e3ca4eb1a3aadd1ce7d866d74c0f124e6a85/yarl-1.18.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:82123d0c954dc58db301f5021a01854a85bf1f3bb7d12ae0c01afc414a882ca2", size = 345453 },
+ { url = "https://files.pythonhosted.org/packages/63/09/d54befb48f9cd8eec43797f624ec37783a0266855f4930a91e3d5c7717f8/yarl-1.18.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:2ec9bbba33b2d00999af4631a3397d1fd78290c48e2a3e52d8dd72db3a067ac8", size = 341872 },
+ { url = "https://files.pythonhosted.org/packages/91/26/fd0ef9bf29dd906a84b59f0cd1281e65b0c3e08c6aa94b57f7d11f593518/yarl-1.18.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:fbd6748e8ab9b41171bb95c6142faf068f5ef1511935a0aa07025438dd9a9bc1", size = 347497 },
+ { url = "https://files.pythonhosted.org/packages/d9/b5/14ac7a256d0511b2ac168d50d4b7d744aea1c1aa20c79f620d1059aab8b2/yarl-1.18.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:877d209b6aebeb5b16c42cbb377f5f94d9e556626b1bfff66d7b0d115be88d0a", size = 359981 },
+ { url = "https://files.pythonhosted.org/packages/ca/b3/d493221ad5cbd18bc07e642894030437e405e1413c4236dd5db6e46bcec9/yarl-1.18.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:b464c4ab4bfcb41e3bfd3f1c26600d038376c2de3297760dfe064d2cb7ea8e10", size = 366229 },
+ { url = "https://files.pythonhosted.org/packages/04/56/6a3e2a5d9152c56c346df9b8fb8edd2c8888b1e03f96324d457e5cf06d34/yarl-1.18.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8d39d351e7faf01483cc7ff7c0213c412e38e5a340238826be7e0e4da450fdc8", size = 360383 },
+ { url = "https://files.pythonhosted.org/packages/fd/b7/4b3c7c7913a278d445cc6284e59b2e62fa25e72758f888b7a7a39eb8423f/yarl-1.18.3-cp313-cp313-win32.whl", hash = "sha256:61ee62ead9b68b9123ec24bc866cbef297dd266175d53296e2db5e7f797f902d", size = 310152 },
+ { url = "https://files.pythonhosted.org/packages/f5/d5/688db678e987c3e0fb17867970700b92603cadf36c56e5fb08f23e822a0c/yarl-1.18.3-cp313-cp313-win_amd64.whl", hash = "sha256:578e281c393af575879990861823ef19d66e2b1d0098414855dd367e234f5b3c", size = 315723 },
+ { url = "https://files.pythonhosted.org/packages/f5/4b/a06e0ec3d155924f77835ed2d167ebd3b211a7b0853da1cf8d8414d784ef/yarl-1.18.3-py3-none-any.whl", hash = "sha256:b57f4f58099328dfb26c6a771d09fb20dbbae81d20cfb66141251ea063bd101b", size = 45109 },
+]
+
+[[package]]
+name = "zstandard"
+version = "0.23.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "cffi", marker = "platform_python_implementation == 'PyPy'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/ed/f6/2ac0287b442160a89d726b17a9184a4c615bb5237db763791a7fd16d9df1/zstandard-0.23.0.tar.gz", hash = "sha256:b2d8c62d08e7255f68f7a740bae85b3c9b8e5466baa9cbf7f57f1cde0ac6bc09", size = 681701 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/7b/83/f23338c963bd9de687d47bf32efe9fd30164e722ba27fb59df33e6b1719b/zstandard-0.23.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b4567955a6bc1b20e9c31612e615af6b53733491aeaa19a6b3b37f3b65477094", size = 788713 },
+ { url = "https://files.pythonhosted.org/packages/5b/b3/1a028f6750fd9227ee0b937a278a434ab7f7fdc3066c3173f64366fe2466/zstandard-0.23.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1e172f57cd78c20f13a3415cc8dfe24bf388614324d25539146594c16d78fcc8", size = 633459 },
+ { url = "https://files.pythonhosted.org/packages/26/af/36d89aae0c1f95a0a98e50711bc5d92c144939efc1f81a2fcd3e78d7f4c1/zstandard-0.23.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b0e166f698c5a3e914947388c162be2583e0c638a4703fc6a543e23a88dea3c1", size = 4945707 },
+ { url = "https://files.pythonhosted.org/packages/cd/2e/2051f5c772f4dfc0aae3741d5fc72c3dcfe3aaeb461cc231668a4db1ce14/zstandard-0.23.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:12a289832e520c6bd4dcaad68e944b86da3bad0d339ef7989fb7e88f92e96072", size = 5306545 },
+ { url = "https://files.pythonhosted.org/packages/0a/9e/a11c97b087f89cab030fa71206963090d2fecd8eb83e67bb8f3ffb84c024/zstandard-0.23.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d50d31bfedd53a928fed6707b15a8dbeef011bb6366297cc435accc888b27c20", size = 5337533 },
+ { url = "https://files.pythonhosted.org/packages/fc/79/edeb217c57fe1bf16d890aa91a1c2c96b28c07b46afed54a5dcf310c3f6f/zstandard-0.23.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72c68dda124a1a138340fb62fa21b9bf4848437d9ca60bd35db36f2d3345f373", size = 5436510 },
+ { url = "https://files.pythonhosted.org/packages/81/4f/c21383d97cb7a422ddf1ae824b53ce4b51063d0eeb2afa757eb40804a8ef/zstandard-0.23.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:53dd9d5e3d29f95acd5de6802e909ada8d8d8cfa37a3ac64836f3bc4bc5512db", size = 4859973 },
+ { url = "https://files.pythonhosted.org/packages/ab/15/08d22e87753304405ccac8be2493a495f529edd81d39a0870621462276ef/zstandard-0.23.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:6a41c120c3dbc0d81a8e8adc73312d668cd34acd7725f036992b1b72d22c1772", size = 4936968 },
+ { url = "https://files.pythonhosted.org/packages/eb/fa/f3670a597949fe7dcf38119a39f7da49a8a84a6f0b1a2e46b2f71a0ab83f/zstandard-0.23.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:40b33d93c6eddf02d2c19f5773196068d875c41ca25730e8288e9b672897c105", size = 5467179 },
+ { url = "https://files.pythonhosted.org/packages/4e/a9/dad2ab22020211e380adc477a1dbf9f109b1f8d94c614944843e20dc2a99/zstandard-0.23.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9206649ec587e6b02bd124fb7799b86cddec350f6f6c14bc82a2b70183e708ba", size = 4848577 },
+ { url = "https://files.pythonhosted.org/packages/08/03/dd28b4484b0770f1e23478413e01bee476ae8227bbc81561f9c329e12564/zstandard-0.23.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:76e79bc28a65f467e0409098fa2c4376931fd3207fbeb6b956c7c476d53746dd", size = 4693899 },
+ { url = "https://files.pythonhosted.org/packages/2b/64/3da7497eb635d025841e958bcd66a86117ae320c3b14b0ae86e9e8627518/zstandard-0.23.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:66b689c107857eceabf2cf3d3fc699c3c0fe8ccd18df2219d978c0283e4c508a", size = 5199964 },
+ { url = "https://files.pythonhosted.org/packages/43/a4/d82decbab158a0e8a6ebb7fc98bc4d903266bce85b6e9aaedea1d288338c/zstandard-0.23.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9c236e635582742fee16603042553d276cca506e824fa2e6489db04039521e90", size = 5655398 },
+ { url = "https://files.pythonhosted.org/packages/f2/61/ac78a1263bc83a5cf29e7458b77a568eda5a8f81980691bbc6eb6a0d45cc/zstandard-0.23.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a8fffdbd9d1408006baaf02f1068d7dd1f016c6bcb7538682622c556e7b68e35", size = 5191313 },
+ { url = "https://files.pythonhosted.org/packages/e7/54/967c478314e16af5baf849b6ee9d6ea724ae5b100eb506011f045d3d4e16/zstandard-0.23.0-cp312-cp312-win32.whl", hash = "sha256:dc1d33abb8a0d754ea4763bad944fd965d3d95b5baef6b121c0c9013eaf1907d", size = 430877 },
+ { url = "https://files.pythonhosted.org/packages/75/37/872d74bd7739639c4553bf94c84af7d54d8211b626b352bc57f0fd8d1e3f/zstandard-0.23.0-cp312-cp312-win_amd64.whl", hash = "sha256:64585e1dba664dc67c7cdabd56c1e5685233fbb1fc1966cfba2a340ec0dfff7b", size = 495595 },
+ { url = "https://files.pythonhosted.org/packages/80/f1/8386f3f7c10261fe85fbc2c012fdb3d4db793b921c9abcc995d8da1b7a80/zstandard-0.23.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:576856e8594e6649aee06ddbfc738fec6a834f7c85bf7cadd1c53d4a58186ef9", size = 788975 },
+ { url = "https://files.pythonhosted.org/packages/16/e8/cbf01077550b3e5dc86089035ff8f6fbbb312bc0983757c2d1117ebba242/zstandard-0.23.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:38302b78a850ff82656beaddeb0bb989a0322a8bbb1bf1ab10c17506681d772a", size = 633448 },
+ { url = "https://files.pythonhosted.org/packages/06/27/4a1b4c267c29a464a161aeb2589aff212b4db653a1d96bffe3598f3f0d22/zstandard-0.23.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d2240ddc86b74966c34554c49d00eaafa8200a18d3a5b6ffbf7da63b11d74ee2", size = 4945269 },
+ { url = "https://files.pythonhosted.org/packages/7c/64/d99261cc57afd9ae65b707e38045ed8269fbdae73544fd2e4a4d50d0ed83/zstandard-0.23.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2ef230a8fd217a2015bc91b74f6b3b7d6522ba48be29ad4ea0ca3a3775bf7dd5", size = 5306228 },
+ { url = "https://files.pythonhosted.org/packages/7a/cf/27b74c6f22541f0263016a0fd6369b1b7818941de639215c84e4e94b2a1c/zstandard-0.23.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:774d45b1fac1461f48698a9d4b5fa19a69d47ece02fa469825b442263f04021f", size = 5336891 },
+ { url = "https://files.pythonhosted.org/packages/fa/18/89ac62eac46b69948bf35fcd90d37103f38722968e2981f752d69081ec4d/zstandard-0.23.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f77fa49079891a4aab203d0b1744acc85577ed16d767b52fc089d83faf8d8ed", size = 5436310 },
+ { url = "https://files.pythonhosted.org/packages/a8/a8/5ca5328ee568a873f5118d5b5f70d1f36c6387716efe2e369010289a5738/zstandard-0.23.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ac184f87ff521f4840e6ea0b10c0ec90c6b1dcd0bad2f1e4a9a1b4fa177982ea", size = 4859912 },
+ { url = "https://files.pythonhosted.org/packages/ea/ca/3781059c95fd0868658b1cf0440edd832b942f84ae60685d0cfdb808bca1/zstandard-0.23.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c363b53e257246a954ebc7c488304b5592b9c53fbe74d03bc1c64dda153fb847", size = 4936946 },
+ { url = "https://files.pythonhosted.org/packages/ce/11/41a58986f809532742c2b832c53b74ba0e0a5dae7e8ab4642bf5876f35de/zstandard-0.23.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:e7792606d606c8df5277c32ccb58f29b9b8603bf83b48639b7aedf6df4fe8171", size = 5466994 },
+ { url = "https://files.pythonhosted.org/packages/83/e3/97d84fe95edd38d7053af05159465d298c8b20cebe9ccb3d26783faa9094/zstandard-0.23.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a0817825b900fcd43ac5d05b8b3079937073d2b1ff9cf89427590718b70dd840", size = 4848681 },
+ { url = "https://files.pythonhosted.org/packages/6e/99/cb1e63e931de15c88af26085e3f2d9af9ce53ccafac73b6e48418fd5a6e6/zstandard-0.23.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:9da6bc32faac9a293ddfdcb9108d4b20416219461e4ec64dfea8383cac186690", size = 4694239 },
+ { url = "https://files.pythonhosted.org/packages/ab/50/b1e703016eebbc6501fc92f34db7b1c68e54e567ef39e6e59cf5fb6f2ec0/zstandard-0.23.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:fd7699e8fd9969f455ef2926221e0233f81a2542921471382e77a9e2f2b57f4b", size = 5200149 },
+ { url = "https://files.pythonhosted.org/packages/aa/e0/932388630aaba70197c78bdb10cce2c91fae01a7e553b76ce85471aec690/zstandard-0.23.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:d477ed829077cd945b01fc3115edd132c47e6540ddcd96ca169facff28173057", size = 5655392 },
+ { url = "https://files.pythonhosted.org/packages/02/90/2633473864f67a15526324b007a9f96c96f56d5f32ef2a56cc12f9548723/zstandard-0.23.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa6ce8b52c5987b3e34d5674b0ab529a4602b632ebab0a93b07bfb4dfc8f8a33", size = 5191299 },
+ { url = "https://files.pythonhosted.org/packages/b0/4c/315ca5c32da7e2dc3455f3b2caee5c8c2246074a61aac6ec3378a97b7136/zstandard-0.23.0-cp313-cp313-win32.whl", hash = "sha256:a9b07268d0c3ca5c170a385a0ab9fb7fdd9f5fd866be004c4ea39e44edce47dd", size = 430862 },
+ { url = "https://files.pythonhosted.org/packages/a2/bf/c6aaba098e2d04781e8f4f7c0ba3c7aa73d00e4c436bcc0cf059a66691d1/zstandard-0.23.0-cp313-cp313-win_amd64.whl", hash = "sha256:f3513916e8c645d0610815c257cbfd3242adfd5c4cfa78be514e5a3ebb42a41b", size = 495578 },
+]
diff --git a/codegen-examples/pyproject.toml b/codegen-examples/pyproject.toml
index 80fb4aff8..11ec96b52 100644
--- a/codegen-examples/pyproject.toml
+++ b/codegen-examples/pyproject.toml
@@ -31,8 +31,6 @@ dev-dependencies = [
"deptry>=0.22.0",
]
-[tool.uv.workspace]
-members = ["examples/swebench_agent_run"]
[tool.pre-commit-uv]
requirements = ["strict-requirements"]
diff --git a/codegen-examples/uv.lock b/codegen-examples/uv.lock
index 138a09454..7c30ff898 100644
--- a/codegen-examples/uv.lock
+++ b/codegen-examples/uv.lock
@@ -1,5 +1,4 @@
version = 1
-revision = 1
requires-python = ">=3.12, <3.14"
resolution-markers = [
"python_full_version >= '3.12.4'",
@@ -488,6 +487,128 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/5e/06/5ec9ae8bedb4a590e939a9064c7fee805e620a5e578f1bbf10dfd35c86d0/codegen-0.51.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_34_x86_64.whl", hash = "sha256:d3bdb1d29b4f910a041245bbc1df4c535827fe95986993991e824d172bd7f009", size = 2232761 },
]
+
+[package.metadata]
+requires-dist = [
+ { name = "astor", specifier = ">=0.8.1,<1.0.0" },
+ { name = "attrs", marker = "extra == 'lsp'", specifier = ">=25.1.0" },
+ { name = "click", specifier = ">=8.1.7" },
+ { name = "codeowners", specifier = ">=0.6.0,<1.0.0" },
+ { name = "colorlog", specifier = ">=6.9.0" },
+ { name = "dataclasses-json", specifier = ">=0.6.4,<1.0.0" },
+ { name = "datamodel-code-generator", specifier = ">=0.26.5" },
+ { name = "datasets" },
+ { name = "dicttoxml", specifier = ">=1.7.16,<2.0.0" },
+ { name = "docker", specifier = ">=6.1.3" },
+ { name = "docstring-parser", specifier = ">=0.16,<1.0" },
+ { name = "fastapi", extras = ["standard"], specifier = ">=0.115.2,<1.0.0" },
+ { name = "gitpython", specifier = "==3.1.44" },
+ { name = "giturlparse" },
+ { name = "hatch-vcs", specifier = ">=0.4.0" },
+ { name = "hatchling", specifier = ">=1.25.0" },
+ { name = "httpx", specifier = ">=0.28.1" },
+ { name = "humanize", specifier = ">=4.10.0,<5.0.0" },
+ { name = "langchain", extras = ["openai"] },
+ { name = "langchain-anthropic", specifier = ">=0.3.7" },
+ { name = "langchain-core" },
+ { name = "langchain-openai" },
+ { name = "langgraph" },
+ { name = "langgraph-prebuilt" },
+ { name = "langsmith" },
+ { name = "lazy-object-proxy", specifier = ">=0.0.0" },
+ { name = "lox", specifier = ">=0.12.0" },
+ { name = "lsprotocol", marker = "extra == 'lsp'", specifier = "==2024.0.0b1" },
+ { name = "mcp", extras = ["cli"] },
+ { name = "mini-racer", specifier = ">=0.12.4" },
+ { name = "modal", specifier = ">=0.73.45" },
+ { name = "neo4j" },
+ { name = "networkx", specifier = ">=3.4.1" },
+ { name = "numpy", specifier = ">=2.2.2" },
+ { name = "openai", specifier = "==1.65.2" },
+ { name = "packaging", specifier = ">=24.2" },
+ { name = "pip", specifier = ">=24.3.1" },
+ { name = "plotly", specifier = ">=5.24.0,<7.0.0" },
+ { name = "psutil", specifier = ">=5.8.0" },
+ { name = "pydantic", specifier = ">=2.9.2,<3.0.0" },
+ { name = "pydantic-core", specifier = ">=2.23.4" },
+ { name = "pydantic-settings", specifier = ">=2.0.0" },
+ { name = "pygit2", specifier = ">=1.16.0" },
+ { name = "pygithub", specifier = "==2.6.1" },
+ { name = "pygls", marker = "extra == 'lsp'", specifier = ">=2.0.0a2" },
+ { name = "pyinstrument", specifier = ">=5.0.0" },
+ { name = "pyjson5", specifier = "==1.6.8" },
+ { name = "pyright", specifier = ">=1.1.372,<2.0.0" },
+ { name = "pytest-snapshot", specifier = ">=0.9.0" },
+ { name = "python-dotenv", specifier = ">=1.0.1" },
+ { name = "python-levenshtein", specifier = ">=0.25.1,<1.0.0" },
+ { name = "python-semantic-release" },
+ { name = "requests", specifier = ">=2.32.3" },
+ { name = "rich", specifier = ">=13.7.1,<14.0.0" },
+ { name = "rich-click", specifier = ">=1.8.5" },
+ { name = "rustworkx", specifier = ">=0.15.1" },
+ { name = "sentry-sdk", specifier = "==2.22.0" },
+ { name = "slack-sdk" },
+ { name = "starlette", specifier = ">=0.16.0,<1.0.0" },
+ { name = "tabulate", specifier = ">=0.9.0,<1.0.0" },
+ { name = "termcolor", specifier = ">=2.4.0" },
+ { name = "tiktoken", specifier = ">=0.5.1,<1.0.0" },
+ { name = "tomlkit", specifier = ">=0.13.2" },
+ { name = "tqdm", specifier = ">=4.67.1" },
+ { name = "tree-sitter", specifier = ">=0.23.1" },
+ { name = "tree-sitter-javascript", specifier = ">=0.23.1" },
+ { name = "tree-sitter-python", specifier = ">=0.23.4" },
+ { name = "tree-sitter-typescript", specifier = ">=0.23.2" },
+ { name = "types-networkx", marker = "extra == 'types'", specifier = ">=3.2.1.20240918" },
+ { name = "types-requests", marker = "extra == 'types'", specifier = ">=2.32.0.20241016" },
+ { name = "types-tabulate", marker = "extra == 'types'", specifier = ">=0.9.0.20240106" },
+ { name = "types-toml", marker = "extra == 'types'", specifier = ">=0.10.8.20240310" },
+ { name = "typing-extensions", specifier = ">=4.12.2" },
+ { name = "unidiff", specifier = ">=0.7.5" },
+ { name = "urllib3", specifier = ">=2.0.0" },
+ { name = "uvicorn", extras = ["standard"], specifier = ">=0.30.0" },
+ { name = "watchfiles", specifier = ">=1.0.0,<1.1.0" },
+ { name = "wrapt", specifier = ">=1.16.0,<2.0.0" },
+ { name = "xmltodict", specifier = ">=0.13.0,<1.0.0" },
+]
+
+[package.metadata.requires-dev]
+dev = [
+ { name = "austin-dist", specifier = ">=3.7.0" },
+ { name = "austin-python", specifier = ">=1.7.1" },
+ { name = "autoflake", specifier = ">=2.3.1" },
+ { name = "black", specifier = ">=24.8.0" },
+ { name = "braintrust", specifier = ">=0.0.160" },
+ { name = "cibuildwheel", extras = ["uv"], specifier = ">=2.22.0" },
+ { name = "coverage", specifier = ">=7.6.1,<8.0.0" },
+ { name = "cython", specifier = ">=3.0.11" },
+ { name = "deptry", specifier = ">=0.22.0" },
+ { name = "emoji", specifier = ">=2.14.0" },
+ { name = "filelock", specifier = ">=3.15.4,<4.0.0" },
+ { name = "httpx", specifier = ">=0.28.1,<0.28.2" },
+ { name = "inflection", specifier = ">=0.5.1,<1.0.0" },
+ { name = "isort", specifier = ">=5.13.2" },
+ { name = "jsbeautifier", specifier = ">=1.15.1,<2.0.0" },
+ { name = "jupyterlab", specifier = ">=4.3.5" },
+ { name = "loguru", specifier = ">=0.7.3" },
+ { name = "modal", specifier = ">=0.73.25" },
+ { name = "mypy", extras = ["mypyc", "faster-cache"], specifier = ">=1.13.0" },
+ { name = "pre-commit", specifier = ">=4.0.1" },
+ { name = "pre-commit-uv", specifier = ">=4.1.4" },
+ { name = "pytest", specifier = ">=8.3.3" },
+ { name = "pytest-asyncio", specifier = ">=0.21.1,<1.0.0" },
+ { name = "pytest-benchmark", extras = ["histogram"], specifier = ">=5.1.0" },
+ { name = "pytest-cov", specifier = ">=6.0.0,<6.0.1" },
+ { name = "pytest-lsp", specifier = ">=1.0.0b1" },
+ { name = "pytest-mock", specifier = ">=3.14.0,<4.0.0" },
+ { name = "pytest-timeout", specifier = ">=2.3.1" },
+ { name = "pytest-xdist", specifier = ">=3.6.1,<4.0.0" },
+ { name = "ruff", specifier = ">=0.6.8" },
+ { name = "ruff-lsp", specifier = ">=0.0.55,<1.0.0" },
+ { name = "sybil", extras = ["pytest"], specifier = ">=9.0.0" },
+ { name = "typer", specifier = ">=0.12.5" },
+ { name = "uv", specifier = ">=0.4.25" },
+]
+
[[package]]
name = "codegen-examples"
version = "0.0.0"
@@ -2085,6 +2206,31 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/da/d9/f7f9379981e39b8c2511c9e0326d212accacb82f12fbfdc1aa2ce2a7b2b6/multiprocess-0.70.16-py39-none-any.whl", hash = "sha256:a0bafd3ae1b732eac64be2e72038231c1ba97724b60b09400d68f229fcc2fbf3", size = 133351 },
]
+[[package]]
+name = "mypy"
+version = "1.15.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "mypy-extensions" },
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/ce/43/d5e49a86afa64bd3839ea0d5b9c7103487007d728e1293f52525d6d5486a/mypy-1.15.0.tar.gz", hash = "sha256:404534629d51d3efea5c800ee7c42b72a6554d6c400e6a79eafe15d11341fd43", size = 3239717 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/98/3a/03c74331c5eb8bd025734e04c9840532226775c47a2c39b56a0c8d4f128d/mypy-1.15.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:aea39e0583d05124836ea645f412e88a5c7d0fd77a6d694b60d9b6b2d9f184fd", size = 10793981 },
+ { url = "https://files.pythonhosted.org/packages/f0/1a/41759b18f2cfd568848a37c89030aeb03534411eef981df621d8fad08a1d/mypy-1.15.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2f2147ab812b75e5b5499b01ade1f4a81489a147c01585cda36019102538615f", size = 9749175 },
+ { url = "https://files.pythonhosted.org/packages/12/7e/873481abf1ef112c582db832740f4c11b2bfa510e829d6da29b0ab8c3f9c/mypy-1.15.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ce436f4c6d218a070048ed6a44c0bbb10cd2cc5e272b29e7845f6a2f57ee4464", size = 11455675 },
+ { url = "https://files.pythonhosted.org/packages/b3/d0/92ae4cde706923a2d3f2d6c39629134063ff64b9dedca9c1388363da072d/mypy-1.15.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8023ff13985661b50a5928fc7a5ca15f3d1affb41e5f0a9952cb68ef090b31ee", size = 12410020 },
+ { url = "https://files.pythonhosted.org/packages/46/8b/df49974b337cce35f828ba6fda228152d6db45fed4c86ba56ffe442434fd/mypy-1.15.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1124a18bc11a6a62887e3e137f37f53fbae476dc36c185d549d4f837a2a6a14e", size = 12498582 },
+ { url = "https://files.pythonhosted.org/packages/13/50/da5203fcf6c53044a0b699939f31075c45ae8a4cadf538a9069b165c1050/mypy-1.15.0-cp312-cp312-win_amd64.whl", hash = "sha256:171a9ca9a40cd1843abeca0e405bc1940cd9b305eaeea2dda769ba096932bb22", size = 9366614 },
+ { url = "https://files.pythonhosted.org/packages/6a/9b/fd2e05d6ffff24d912f150b87db9e364fa8282045c875654ce7e32fffa66/mypy-1.15.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:93faf3fdb04768d44bf28693293f3904bbb555d076b781ad2530214ee53e3445", size = 10788592 },
+ { url = "https://files.pythonhosted.org/packages/74/37/b246d711c28a03ead1fd906bbc7106659aed7c089d55fe40dd58db812628/mypy-1.15.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:811aeccadfb730024c5d3e326b2fbe9249bb7413553f15499a4050f7c30e801d", size = 9753611 },
+ { url = "https://files.pythonhosted.org/packages/a6/ac/395808a92e10cfdac8003c3de9a2ab6dc7cde6c0d2a4df3df1b815ffd067/mypy-1.15.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:98b7b9b9aedb65fe628c62a6dc57f6d5088ef2dfca37903a7d9ee374d03acca5", size = 11438443 },
+ { url = "https://files.pythonhosted.org/packages/d2/8b/801aa06445d2de3895f59e476f38f3f8d610ef5d6908245f07d002676cbf/mypy-1.15.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c43a7682e24b4f576d93072216bf56eeff70d9140241f9edec0c104d0c515036", size = 12402541 },
+ { url = "https://files.pythonhosted.org/packages/c7/67/5a4268782eb77344cc613a4cf23540928e41f018a9a1ec4c6882baf20ab8/mypy-1.15.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:baefc32840a9f00babd83251560e0ae1573e2f9d1b067719479bfb0e987c6357", size = 12494348 },
+ { url = "https://files.pythonhosted.org/packages/83/3e/57bb447f7bbbfaabf1712d96f9df142624a386d98fb026a761532526057e/mypy-1.15.0-cp313-cp313-win_amd64.whl", hash = "sha256:b9378e2c00146c44793c98b8d5a61039a048e31f429fb0eb546d93f4b000bedf", size = 9373648 },
+ { url = "https://files.pythonhosted.org/packages/09/4e/a7d65c7322c510de2c409ff3828b03354a7c43f5a8ed458a7a131b41c7b9/mypy-1.15.0-py3-none-any.whl", hash = "sha256:5469affef548bd1895d86d3bf10ce2b44e33d86923c29e4d675b3e323437ea3e", size = 2221777 },
+]
+
[[package]]
name = "mypy-extensions"
version = "1.0.0"
@@ -2600,6 +2746,37 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/50/1b/6921afe68c74868b4c9fa424dad3be35b095e16687989ebbb50ce4fceb7c/psutil-7.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:4cf3d4eb1aa9b348dec30105c55cd9b7d4629285735a102beb4441e38db90553", size = 244885 },
]
+[[package]]
+name = "psycopg2-binary"
+version = "2.9.10"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/cb/0e/bdc8274dc0585090b4e3432267d7be4dfbfd8971c0fa59167c711105a6bf/psycopg2-binary-2.9.10.tar.gz", hash = "sha256:4b3df0e6990aa98acda57d983942eff13d824135fe2250e6522edaa782a06de2", size = 385764 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/49/7d/465cc9795cf76f6d329efdafca74693714556ea3891813701ac1fee87545/psycopg2_binary-2.9.10-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:880845dfe1f85d9d5f7c412efea7a08946a46894537e4e5d091732eb1d34d9a0", size = 3044771 },
+ { url = "https://files.pythonhosted.org/packages/8b/31/6d225b7b641a1a2148e3ed65e1aa74fc86ba3fee850545e27be9e1de893d/psycopg2_binary-2.9.10-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:9440fa522a79356aaa482aa4ba500b65f28e5d0e63b801abf6aa152a29bd842a", size = 3275336 },
+ { url = "https://files.pythonhosted.org/packages/30/b7/a68c2b4bff1cbb1728e3ec864b2d92327c77ad52edcd27922535a8366f68/psycopg2_binary-2.9.10-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e3923c1d9870c49a2d44f795df0c889a22380d36ef92440ff618ec315757e539", size = 2851637 },
+ { url = "https://files.pythonhosted.org/packages/0b/b1/cfedc0e0e6f9ad61f8657fd173b2f831ce261c02a08c0b09c652b127d813/psycopg2_binary-2.9.10-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7b2c956c028ea5de47ff3a8d6b3cc3330ab45cf0b7c3da35a2d6ff8420896526", size = 3082097 },
+ { url = "https://files.pythonhosted.org/packages/18/ed/0a8e4153c9b769f59c02fb5e7914f20f0b2483a19dae7bf2db54b743d0d0/psycopg2_binary-2.9.10-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f758ed67cab30b9a8d2833609513ce4d3bd027641673d4ebc9c067e4d208eec1", size = 3264776 },
+ { url = "https://files.pythonhosted.org/packages/10/db/d09da68c6a0cdab41566b74e0a6068a425f077169bed0946559b7348ebe9/psycopg2_binary-2.9.10-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8cd9b4f2cfab88ed4a9106192de509464b75a906462fb846b936eabe45c2063e", size = 3020968 },
+ { url = "https://files.pythonhosted.org/packages/94/28/4d6f8c255f0dfffb410db2b3f9ac5218d959a66c715c34cac31081e19b95/psycopg2_binary-2.9.10-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6dc08420625b5a20b53551c50deae6e231e6371194fa0651dbe0fb206452ae1f", size = 2872334 },
+ { url = "https://files.pythonhosted.org/packages/05/f7/20d7bf796593c4fea95e12119d6cc384ff1f6141a24fbb7df5a668d29d29/psycopg2_binary-2.9.10-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:d7cd730dfa7c36dbe8724426bf5612798734bff2d3c3857f36f2733f5bfc7c00", size = 2822722 },
+ { url = "https://files.pythonhosted.org/packages/4d/e4/0c407ae919ef626dbdb32835a03b6737013c3cc7240169843965cada2bdf/psycopg2_binary-2.9.10-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:155e69561d54d02b3c3209545fb08938e27889ff5a10c19de8d23eb5a41be8a5", size = 2920132 },
+ { url = "https://files.pythonhosted.org/packages/2d/70/aa69c9f69cf09a01da224909ff6ce8b68faeef476f00f7ec377e8f03be70/psycopg2_binary-2.9.10-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c3cc28a6fd5a4a26224007712e79b81dbaee2ffb90ff406256158ec4d7b52b47", size = 2959312 },
+ { url = "https://files.pythonhosted.org/packages/d3/bd/213e59854fafe87ba47814bf413ace0dcee33a89c8c8c814faca6bc7cf3c/psycopg2_binary-2.9.10-cp312-cp312-win32.whl", hash = "sha256:ec8a77f521a17506a24a5f626cb2aee7850f9b69a0afe704586f63a464f3cd64", size = 1025191 },
+ { url = "https://files.pythonhosted.org/packages/92/29/06261ea000e2dc1e22907dbbc483a1093665509ea586b29b8986a0e56733/psycopg2_binary-2.9.10-cp312-cp312-win_amd64.whl", hash = "sha256:18c5ee682b9c6dd3696dad6e54cc7ff3a1a9020df6a5c0f861ef8bfd338c3ca0", size = 1164031 },
+ { url = "https://files.pythonhosted.org/packages/3e/30/d41d3ba765609c0763505d565c4d12d8f3c79793f0d0f044ff5a28bf395b/psycopg2_binary-2.9.10-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:26540d4a9a4e2b096f1ff9cce51253d0504dca5a85872c7f7be23be5a53eb18d", size = 3044699 },
+ { url = "https://files.pythonhosted.org/packages/35/44/257ddadec7ef04536ba71af6bc6a75ec05c5343004a7ec93006bee66c0bc/psycopg2_binary-2.9.10-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:e217ce4d37667df0bc1c397fdcd8de5e81018ef305aed9415c3b093faaeb10fb", size = 3275245 },
+ { url = "https://files.pythonhosted.org/packages/1b/11/48ea1cd11de67f9efd7262085588790a95d9dfcd9b8a687d46caf7305c1a/psycopg2_binary-2.9.10-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:245159e7ab20a71d989da00f280ca57da7641fa2cdcf71749c193cea540a74f7", size = 2851631 },
+ { url = "https://files.pythonhosted.org/packages/62/e0/62ce5ee650e6c86719d621a761fe4bc846ab9eff8c1f12b1ed5741bf1c9b/psycopg2_binary-2.9.10-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c4ded1a24b20021ebe677b7b08ad10bf09aac197d6943bfe6fec70ac4e4690d", size = 3082140 },
+ { url = "https://files.pythonhosted.org/packages/27/ce/63f946c098611f7be234c0dd7cb1ad68b0b5744d34f68062bb3c5aa510c8/psycopg2_binary-2.9.10-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3abb691ff9e57d4a93355f60d4f4c1dd2d68326c968e7db17ea96df3c023ef73", size = 3264762 },
+ { url = "https://files.pythonhosted.org/packages/43/25/c603cd81402e69edf7daa59b1602bd41eb9859e2824b8c0855d748366ac9/psycopg2_binary-2.9.10-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8608c078134f0b3cbd9f89b34bd60a943b23fd33cc5f065e8d5f840061bd0673", size = 3020967 },
+ { url = "https://files.pythonhosted.org/packages/5f/d6/8708d8c6fca531057fa170cdde8df870e8b6a9b136e82b361c65e42b841e/psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:230eeae2d71594103cd5b93fd29d1ace6420d0b86f4778739cb1a5a32f607d1f", size = 2872326 },
+ { url = "https://files.pythonhosted.org/packages/ce/ac/5b1ea50fc08a9df82de7e1771537557f07c2632231bbab652c7e22597908/psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:bb89f0a835bcfc1d42ccd5f41f04870c1b936d8507c6df12b7737febc40f0909", size = 2822712 },
+ { url = "https://files.pythonhosted.org/packages/c4/fc/504d4503b2abc4570fac3ca56eb8fed5e437bf9c9ef13f36b6621db8ef00/psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f0c2d907a1e102526dd2986df638343388b94c33860ff3bbe1384130828714b1", size = 2920155 },
+ { url = "https://files.pythonhosted.org/packages/b2/d1/323581e9273ad2c0dbd1902f3fb50c441da86e894b6e25a73c3fda32c57e/psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f8157bed2f51db683f31306aa497311b560f2265998122abe1dce6428bd86567", size = 2959356 },
+ { url = "https://files.pythonhosted.org/packages/08/50/d13ea0a054189ae1bc21af1d85b6f8bb9bbc5572991055d70ad9006fe2d6/psycopg2_binary-2.9.10-cp313-cp313-win_amd64.whl", hash = "sha256:27422aa5f11fbcd9b18da48373eb67081243662f9b46e6fd07c3eb46e4535142", size = 2569224 },
+]
+
[[package]]
name = "ptyprocess"
version = "0.7.0"
@@ -3345,6 +3522,31 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/68/15/6d22d07e063ce5e9bfbd96db9ec2fbb4693591b4503e3a76996639474d02/rpds_py-0.23.1-cp313-cp313t-win_amd64.whl", hash = "sha256:d6f6512a90bd5cd9030a6237f5346f046c6f0e40af98657568fa45695d4de59d", size = 235415 },
]
+[[package]]
+name = "ruff"
+version = "0.9.9"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/6f/c3/418441a8170e8d53d05c0b9dad69760dbc7b8a12c10dbe6db1e1205d2377/ruff-0.9.9.tar.gz", hash = "sha256:0062ed13f22173e85f8f7056f9a24016e692efeea8704d1a5e8011b8aa850933", size = 3717448 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/bc/c3/2c4afa9ba467555d074b146d9aed0633a56ccdb900839fb008295d037b89/ruff-0.9.9-py3-none-linux_armv6l.whl", hash = "sha256:628abb5ea10345e53dff55b167595a159d3e174d6720bf19761f5e467e68d367", size = 10027252 },
+ { url = "https://files.pythonhosted.org/packages/33/d1/439e58487cf9eac26378332e25e7d5ade4b800ce1eec7dc2cfc9b0d7ca96/ruff-0.9.9-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b6cd1428e834b35d7493354723543b28cc11dc14d1ce19b685f6e68e07c05ec7", size = 10840721 },
+ { url = "https://files.pythonhosted.org/packages/50/44/fead822c38281ba0122f1b76b460488a175a9bd48b130650a6fb6dbcbcf9/ruff-0.9.9-py3-none-macosx_11_0_arm64.whl", hash = "sha256:5ee162652869120ad260670706f3cd36cd3f32b0c651f02b6da142652c54941d", size = 10161439 },
+ { url = "https://files.pythonhosted.org/packages/11/ae/d404a2ab8e61ddf6342e09cc6b7f7846cce6b243e45c2007dbe0ca928a5d/ruff-0.9.9-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3aa0f6b75082c9be1ec5a1db78c6d4b02e2375c3068438241dc19c7c306cc61a", size = 10336264 },
+ { url = "https://files.pythonhosted.org/packages/6a/4e/7c268aa7d84cd709fb6f046b8972313142cffb40dfff1d2515c5e6288d54/ruff-0.9.9-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:584cc66e89fb5f80f84b05133dd677a17cdd86901d6479712c96597a3f28e7fe", size = 9908774 },
+ { url = "https://files.pythonhosted.org/packages/cc/26/c618a878367ef1b76270fd027ca93692657d3f6122b84ba48911ef5f2edc/ruff-0.9.9-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abf3369325761a35aba75cd5c55ba1b5eb17d772f12ab168fbfac54be85cf18c", size = 11428127 },
+ { url = "https://files.pythonhosted.org/packages/d7/9a/c5588a93d9bfed29f565baf193fe802fa676a0c837938137ea6cf0576d8c/ruff-0.9.9-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:3403a53a32a90ce929aa2f758542aca9234befa133e29f4933dcef28a24317be", size = 12133187 },
+ { url = "https://files.pythonhosted.org/packages/3e/ff/e7980a7704a60905ed7e156a8d73f604c846d9bd87deda9cabfa6cba073a/ruff-0.9.9-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:18454e7fa4e4d72cffe28a37cf6a73cb2594f81ec9f4eca31a0aaa9ccdfb1590", size = 11602937 },
+ { url = "https://files.pythonhosted.org/packages/24/78/3690444ad9e3cab5c11abe56554c35f005b51d1d118b429765249095269f/ruff-0.9.9-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fadfe2c88724c9617339f62319ed40dcdadadf2888d5afb88bf3adee7b35bfb", size = 13771698 },
+ { url = "https://files.pythonhosted.org/packages/6e/bf/e477c2faf86abe3988e0b5fd22a7f3520e820b2ee335131aca2e16120038/ruff-0.9.9-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6df104d08c442a1aabcfd254279b8cc1e2cbf41a605aa3e26610ba1ec4acf0b0", size = 11249026 },
+ { url = "https://files.pythonhosted.org/packages/f7/82/cdaffd59e5a8cb5b14c408c73d7a555a577cf6645faaf83e52fe99521715/ruff-0.9.9-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:d7c62939daf5b2a15af48abbd23bea1efdd38c312d6e7c4cedf5a24e03207e17", size = 10220432 },
+ { url = "https://files.pythonhosted.org/packages/fe/a4/2507d0026225efa5d4412b6e294dfe54725a78652a5c7e29e6bd0fc492f3/ruff-0.9.9-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:9494ba82a37a4b81b6a798076e4a3251c13243fc37967e998efe4cce58c8a8d1", size = 9874602 },
+ { url = "https://files.pythonhosted.org/packages/d5/be/f3aab1813846b476c4bcffe052d232244979c3cd99d751c17afb530ca8e4/ruff-0.9.9-py3-none-musllinux_1_2_i686.whl", hash = "sha256:4efd7a96ed6d36ef011ae798bf794c5501a514be369296c672dab7921087fa57", size = 10851212 },
+ { url = "https://files.pythonhosted.org/packages/8b/45/8e5fd559bea0d2f57c4e12bf197a2fade2fac465aa518284f157dfbca92b/ruff-0.9.9-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:ab90a7944c5a1296f3ecb08d1cbf8c2da34c7e68114b1271a431a3ad30cb660e", size = 11327490 },
+ { url = "https://files.pythonhosted.org/packages/42/55/e6c90f13880aeef327746052907e7e930681f26a164fe130ddac28b08269/ruff-0.9.9-py3-none-win32.whl", hash = "sha256:6b4c376d929c25ecd6d87e182a230fa4377b8e5125a4ff52d506ee8c087153c1", size = 10227912 },
+ { url = "https://files.pythonhosted.org/packages/35/b2/da925693cb82a1208aa34966c0f36cb222baca94e729dd22a587bc22d0f3/ruff-0.9.9-py3-none-win_amd64.whl", hash = "sha256:837982ea24091d4c1700ddb2f63b7070e5baec508e43b01de013dc7eff974ff1", size = 11355632 },
+ { url = "https://files.pythonhosted.org/packages/31/d8/de873d1c1b020d668d8ec9855d390764cb90cf8f6486c0983da52be8b7b7/ruff-0.9.9-py3-none-win_arm64.whl", hash = "sha256:3ac78f127517209fe6d96ab00f3ba97cafe38718b23b1db3e96d8b2d39e37ddf", size = 10435860 },
+]
+
[[package]]
name = "rustworkx"
version = "0.16.0"
@@ -3547,13 +3749,39 @@ wheels = [
[[package]]
name = "swebench-agent-run"
version = "0.1.0"
-source = { virtual = "examples/swebench_agent_run" }
+source = { editable = "examples/swebench_agent_run" }
dependencies = [
+ { name = "click" },
+ { name = "codegen" },
{ name = "modal" },
+ { name = "tqdm" },
+]
+
+[package.optional-dependencies]
+all = [
+ { name = "mypy" },
+ { name = "psycopg2-binary" },
+ { name = "ruff" },
+]
+dev = [
+ { name = "mypy" },
+ { name = "ruff" },
+]
+metrics = [
+ { name = "psycopg2-binary" },
]
[package.metadata]
-requires-dist = [{ name = "modal", specifier = ">=0.73.25" }]
+requires-dist = [
+ { name = "click", specifier = ">=8.1.0" },
+ { name = "codegen", directory = "../" },
+ { name = "modal", specifier = ">=0.73.25" },
+ { name = "mypy", marker = "extra == 'dev'" },
+ { name = "psycopg2-binary", marker = "extra == 'metrics'" },
+ { name = "ruff", marker = "extra == 'dev'" },
+ { name = "swebench-agent-run", extras = ["metrics", "dev"], marker = "extra == 'all'" },
+ { name = "tqdm", specifier = ">=4.66.0" },
+]
[[package]]
name = "synchronicity"
diff --git a/codegen-on-oss/.dockerignore b/codegen-on-oss/.dockerignore
new file mode 100644
index 000000000..7b435ab1e
--- /dev/null
+++ b/codegen-on-oss/.dockerignore
@@ -0,0 +1,5 @@
+.git/
+repositories/
+.venv/
+.vscode/
+output/
diff --git a/codegen-on-oss/.gitignore b/codegen-on-oss/.gitignore
new file mode 100644
index 000000000..780eabf11
--- /dev/null
+++ b/codegen-on-oss/.gitignore
@@ -0,0 +1,140 @@
+docs/source
+
+# From https://raw.githubusercontent.com/github/gitignore/main/Python.gitignore
+
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+share/python-wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+MANIFEST
+
+# PyInstaller
+# Usually these files are written by a python script from a template
+# before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.nox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*.cover
+*.py,cover
+.hypothesis/
+.pytest_cache/
+cover/
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+local_settings.py
+db.sqlite3
+db.sqlite3-journal
+
+# Flask stuff:
+instance/
+.webassets-cache
+
+# Scrapy stuff:
+.scrapy
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+.pybuilder/
+target/
+
+# Jupyter Notebook
+.ipynb_checkpoints
+
+# IPython
+profile_default/
+ipython_config.py
+
+# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
+__pypackages__/
+
+# Celery stuff
+celerybeat-schedule
+celerybeat.pid
+
+# SageMath parsed files
+*.sage.py
+
+# Environments
+.env
+.venv
+env/
+venv/
+ENV/
+env.bak/
+venv.bak/
+
+# Spyder project settings
+.spyderproject
+.spyproject
+
+# Rope project settings
+.ropeproject
+
+# mkdocs documentation
+/site
+
+# mypy
+.mypy_cache/
+.dmypy.json
+dmypy.json
+
+# Pyre type checker
+.pyre/
+
+# pytype static type analyzer
+.pytype/
+
+# Cython debug symbols
+cython_debug/
+
+# Vscode config files
+.vscode/
+
+# PyCharm
+# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
+# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
+# and can be added to the global gitignore or merged into this file. For a more nuclear
+# option (not recommended) you can uncomment the following to ignore the entire idea folder.
+#.idea/
diff --git a/codegen-on-oss/Dockerfile b/codegen-on-oss/Dockerfile
new file mode 100644
index 000000000..458758a84
--- /dev/null
+++ b/codegen-on-oss/Dockerfile
@@ -0,0 +1,34 @@
+# Install uv
+FROM python:3.12-slim AS installer
+COPY --from=ghcr.io/astral-sh/uv:latest /uv /bin/uv
+
+# Change the working directory to the `app` directory
+WORKDIR /app
+
+# Copy the lockfile and `pyproject.toml` into the image
+COPY uv.lock /app/uv.lock
+COPY pyproject.toml /app/pyproject.toml
+
+# Install dependencies
+RUN apt-get update && apt-get install -y git \
+ && uv sync --frozen --no-install-project \
+ && apt-get clean \
+ && rm -rf /var/lib/apt/lists/*
+
+
+FROM python:3.12-slim
+
+ENV PATH="/venv/bin:/app/scripts:$PATH"
+# Copy the project into the image
+COPY --from=installer /app/.venv/ /venv
+
+RUN apt-get update && apt-get install -y postgresql-client \
+ && rm -rf /var/lib/apt/lists/* \
+ && apt-get clean
+
+
+WORKDIR /app
+
+COPY . .
+
+CMD ["python", "modal_run.py"]
diff --git a/codegen-on-oss/Makefile b/codegen-on-oss/Makefile
new file mode 100644
index 000000000..dba86014c
--- /dev/null
+++ b/codegen-on-oss/Makefile
@@ -0,0 +1,38 @@
+.PHONY: install
+install: ## Install the virtual environment and install the pre-commit hooks
+ @echo "🚀 Creating virtual environment using uv"
+ @uv sync
+ @uv run pre-commit install
+
+.PHONY: check
+check: ## Run code quality tools.
+ @echo "🚀 Checking lock file consistency with 'pyproject.toml'"
+ @uv lock --locked
+ @echo "🚀 Linting code: Running pre-commit"
+ @uv run pre-commit run -a
+ @echo "🚀 Static type checking: Running mypy"
+ @uv run mypy
+ @echo "🚀 Checking for obsolete dependencies: Running deptry"
+ @uv run deptry .
+
+.PHONY: test
+test: ## Test the code with pytest
+ @echo "🚀 Testing code: Running pytest"
+ @uv run python -m pytest --cov --cov-config=pyproject.toml --cov-report=xml
+
+.PHONY: build
+build: clean-build ## Build wheel file
+ @echo "🚀 Creating wheel file"
+ @uvx --from build pyproject-build --installer uv
+
+.PHONY: clean-build
+clean-build: ## Clean build artifacts
+ @echo "🚀 Removing build artifacts"
+ @uv run python -c "import shutil; import os; shutil.rmtree('dist') if os.path.exists('dist') else None"
+
+.PHONY: help
+help:
+ @uv run python -c "import re; \
+ [[print(f'\033[36m{m[0]:<20}\033[0m {m[1]}') for m in re.findall(r'^([a-zA-Z_-]+):.*?## (.*)$$', open(makefile).read(), re.M)] for makefile in ('$(MAKEFILE_LIST)').strip().split()]"
+
+.DEFAULT_GOAL := help
diff --git a/codegen-on-oss/README.md b/codegen-on-oss/README.md
new file mode 100644
index 000000000..a7700eb77
--- /dev/null
+++ b/codegen-on-oss/README.md
@@ -0,0 +1,337 @@
+# Overview
+
+The **Codegen on OSS** package provides a modular pipeline that:
+
+- **Collects repository URLs** from different sources (e.g., CSV files or GitHub searches).
+- **Parses repositories** using the codegen tool.
+- **Profiles performance** and logs metrics for each parsing run.
+- **Logs errors** to help pinpoint parsing failures or performance bottlenecks.
+
+______________________________________________________________________
+
+## Package Structure
+
+The package is composed of several modules:
+
+- `sources`
+
+ - Defines the Repository source classes and settings. Settings are all configurable via environment variables
+
+ - Github Source
+
+ ```python
+ class GithubSettings(SourceSettings):
+ language: Literal["python", "typescript"] = "python"
+ heuristic: Literal[
+ "stars",
+ "forks",
+ "updated",
+ # "watchers",
+ # "contributors",
+ # "commit_activity",
+ # "issues",
+ # "dependency",
+ ] = "stars"
+ github_token: str | None = None
+ ```
+
+ - The three options available now are the three supported by the Github API.
+ - Future Work Additional options will require different strategies
+
+ - CSV Source
+
+ - Simply reads repo URLs from CSV
+
+- `cache`
+
+ - Currently only specifies the cache directory. It is used for caching git repositories pulled by the pipeline `--force-pull` can be used to re-pull from the remote.
+
+- `cli`
+
+ - Built with Click, the CLI provides two main commands:
+ - `run-one`: Parses a single repository specified by URL.
+ - `run`: Iterates over repositories obtained from a selected source and parses each one.
+
+- **`metrics`**
+
+ - Provides profiling tools to measure performance during the parse:
+ - `MetricsProfiler`: A context manager that creates a profiling session.
+ - `MetricsProfile`: Represents a "span" or a "run" of a specific repository. Records step-by-step metrics (clock duration, CPU time, memory usage) and writes them to a CSV file specified by `--output-path`
+
+- **`parser`**
+
+ Contains the `CodegenParser` class that orchestrates the parsing process:
+
+ - Clones the repository (or forces a pull if specified).
+ - Initializes a `Codebase` (from the codegen tool).
+ - Runs post-initialization validation.
+ - Integrates with the `MetricsProfiler` to log measurements at key steps.
+
+______________________________________________________________________
+
+## Getting Started
+
+1. **Configure the Repository Source**
+
+ Decide whether you want to read from a CSV file or query GitHub:
+
+ - For CSV, ensure that your CSV file (default: `input.csv`) exists and contains repository URLs in its first column \[`repo_url`\] and commit hash \[`commit_hash`\] (or empty) in the second column.
+ - For GitHub, configure your desired settings (e.g., `language`, `heuristic`, and optionally a GitHub token) via environment variables (`GITHUB_` prefix)
+
+1. **Run the Parser**
+
+ Use the CLI to start parsing:
+
+ - To parse one repository:
+
+ ```bash
+ uv run cgparse run-one --help
+ ```
+
+ - To parse multiple repositories from a source:
+
+ ```bash
+ uv run cgparse run --help
+ ```
+
+1. **Review Metrics and Logs**
+
+ After parsing, check the CSV (default: `metrics.csv` ) to review performance measurements per repository. Error logs are written to the specified error output file (default: `errors.log`)
+
+______________________________________________________________________
+
+## Running on Modal
+
+```shell
+$ uv run modal run modal_run.py
+```
+
+Codegen runs this parser on modal using the CSV source file `input.csv` tracked in this repository.
+
+### Modal Configuration
+
+- **Compute Resources**: Allocates 4 CPUs and 16GB of memory.
+- **Secrets & Volumes**: Uses secrets (for bucket credentials) and mounts a volume for caching repositories.
+- **Image Setup**: Builds on a Debian slim image with Python 3.12, installs required packages (`uv` and `git` )
+- **Environment Configuration**: Environment variables (e.g., GitHub settings) are injected at runtime.
+
+The function `parse_repo_on_modal` performs the following steps:
+
+1. **Environment Setup**: Updates environment variables and configures logging using Loguru.
+1. **Source Initialization**: Creates a repository source based on the provided type (e.g., GitHub).
+1. **Metrics Profiling**: Instantiates `MetricsProfiler` to capture and log performance data.
+1. **Repository Parsing**: Iterates over repository URLs and parses each using the `CodegenParser`.
+1. **Error Handling**: Logs any exceptions encountered during parsing.
+1. **Result Upload**: Uses the `BucketStore` class to upload the configuration, logs, and metrics to an S3 bucket.
+
+### Bucket Storage
+
+**Bucket (public):** [codegen-oss-parse](https://s3.amazonaws.com/codegen-oss-parse/)
+
+The results of each run are saved under the version of `codegen` lib that the run installed and the source type it was run with. Within this prefix:
+
+- Source Settings
+ - `https://s3.amazonaws.com/codegen-oss-parse/{version}/{source}/config.json`
+- Metrics
+ - `https://s3.amazonaws.com/codegen-oss-parse/{version}/{source}/metrics.csv`
+- Logs
+ - `https://s3.amazonaws.com/codegen-oss-parse/{version}/{source}/output.logs`
+
+______________________________________________________________________
+
+### Running it yourself
+
+You can also run `modal_run.py` yourself. It is designed to be run via Modal for cloud-based parsing. It offers additional configuration methods:
+
+```shell
+$ uv run modal run modal_run.py
+```
+
+- **CSV and Repository Volumes:**
+ The script defines two Modal volumes:
+
+ - `codegen-oss-input-volume`: For uploading and reloading CSV inputs.
+ - `codegen-oss-repo-volume`: For caching repository data during parsing.
+ The repository and input volume names are configurable via environment variables (`CODEGEN_MODAL_REPO_VOLUME` and `CODEGEN_MODAL_INPUT_VOLUME`).
+
+- **Secrets Handling:**
+ The script loads various credentials via Modal secrets. It first checks for a pre-configured Modal secret (`codegen-oss-bucket-credentials` configurable via environment variable `CODEGEN_MODAL_SECRET_NAME`) and falls back to dynamically created Modal secret from local `.env` or environment variables if not found.
+
+- **Entrypoint Parameters:**
+ The main function supports multiple source types:
+
+ - **csv:** Uploads a CSV file (`--csv-file input.csv`) for batch processing.
+ - **single:** Parses a single repository specified by its URL (`--single-url "https://github.com/codegen-sh/codegen-sdk.git"`) and an optional commit hash (`--single-commit ...`)
+ - **github:** Uses GitHub settings, language (`--github-language python`) and heuristic (`--github-heuristic stars`) to query for top repositories.
+
+- **Result Storage:**
+ Upon completion, logs and metrics are automatically uploaded to the S3 bucket specified by the environment variable `BUCKET_NAME` (default: `codegen-oss-parse`). This allows for centralized storage and easy retrieval of run outputs. The AWS Credentials provided in the secret are used for this operation.
+
+______________________________________________________________________
+
+## Extensibility
+
+**Adding New Sources:**
+
+You can define additional repository sources by subclassing `RepoSource` and providing a corresponding settings class. Make sure to set the `source_type` and register your new source by following the pattern established in `CSVInputSource` or `GithubSource`.
+
+**Improving Testing:**
+
+The detailed metrics collected can help you understand where parsing failures occur or where performance lags. Use these insights to improve error handling and optimize the codegen parsing logic.
+
+**Containerization and Automation:**
+
+There is a Dockerfile that can be used to create an image capable of running the parse tests. Runtime environment variables can be used to configure the run and output.
+
+**Input & Configuration**
+
+Explore a better CLI for providing options to the Modal run.
+
+______________________________________________________________________
+
+## Example Log Output
+
+```shell
+[codegen-on-oss*] codegen/codegen-on-oss/$ uv run cgparse run --source csv
+ 21:32:36 INFO Cloning repository https://github.com/JohnSnowLabs/spark-nlp.git
+ 21:36:57 INFO {
+ "profile_name": "https://github.com/JohnSnowLabs/spark-nlp.git",
+ "step": "codebase_init",
+ "delta_time": 7.186550649999845,
+ "cumulative_time": 7.186550649999845,
+ "cpu_time": 180.3553702,
+ "memory_usage": 567525376,
+ "memory_delta": 317095936,
+ "error": null
+}
+ 21:36:58 INFO {
+ "profile_name": "https://github.com/JohnSnowLabs/spark-nlp.git",
+ "step": "post_init_validation",
+ "delta_time": 0.5465090990001045,
+ "cumulative_time": 7.733059748999949,
+ "cpu_time": 180.9174761,
+ "memory_usage": 569249792,
+ "memory_delta": 1724416,
+ "error": null
+}
+ 21:36:58 ERROR Repository: https://github.com/JohnSnowLabs/spark-nlp.git
+Traceback (most recent call last):
+
+ File "/home/codegen/codegen/codegen-on-oss/.venv/bin/cgparse", line 10, in
+ sys.exit(cli())
+ │ │ └
+ │ └
+ └
+ File "/home/codegen/codegen/codegen-on-oss/.venv/lib/python3.12/site-packages/click/core.py", line 1161, in __call__
+ return self.main(*args, **kwargs)
+ │ │ │ └ {}
+ │ │ └ ()
+ │ └
+ └
+ File "/home/codegen/codegen/codegen-on-oss/.venv/lib/python3.12/site-packages/click/core.py", line 1082, in main
+ rv = self.invoke(ctx)
+ │ │ └
+ │ └
+ └
+ File "/home/codegen/codegen/codegen-on-oss/.venv/lib/python3.12/site-packages/click/core.py", line 1697, in invoke
+ return _process_result(sub_ctx.command.invoke(sub_ctx))
+ │ │ │ │ └
+ │ │ │ └
+ │ │ └
+ │ └
+ └ ._process_result at 0x7f466597fb00>
+ File "/home/codegen/codegen/codegen-on-oss/.venv/lib/python3.12/site-packages/click/core.py", line 1443, in invoke
+ return ctx.invoke(self.callback, **ctx.params)
+ │ │ │ │ │ └ {'source': 'csv', 'output_path': 'metrics.csv', 'error_output_path': 'errors.log', 'cache_dir': PosixPath('/home/.cache...
+ │ │ │ │ └
+ │ │ │ └
+ │ │ └
+ │ └
+ └
+ File "/home/codegen/codegen/codegen-on-oss/.venv/lib/python3.12/site-packages/click/core.py", line 788, in invoke
+ return __callback(*args, **kwargs)
+ │ └ {'source': 'csv', 'output_path': 'metrics.csv', 'error_output_path': 'errors.log', 'cache_dir': PosixPath('/home/.cache...
+ └ ()
+
+ File "/home/codegen/codegen/codegen-on-oss/codegen_on_oss/cli.py", line 121, in run
+ parser.parse(repo_url)
+ │ │ └ 'https://github.com/JohnSnowLabs/spark-nlp.git'
+ │ └
+ └
+
+ File "/home/codegen/codegen/codegen-on-oss/codegen_on_oss/parser.py", line 52, in parse
+ with self.metrics_profiler.start_profiler(
+ │ │ └
+ │ └
+ └
+
+ File "/home/.local/share/uv/python/cpython-3.12.6-linux-x86_64-gnu/lib/python3.12/contextlib.py", line 158, in __exit__
+ self.gen.throw(value)
+ │ │ │ └ ParseRunError()
+ │ │ └
+ │ └
+ └
+
+> File "/home/codegen/codegen/codegen-on-oss/codegen_on_oss/metrics.py", line 41, in start_profiler
+ yield profile
+ └
+
+ File "/home/codegen/codegen/codegen-on-oss/codegen_on_oss/parser.py", line 64, in parse
+ raise ParseRunError(validation_status)
+ │ └
+ └
+
+codegen_on_oss.parser.ParseRunError: LOW_IMPORT_RESOLUTION_RATE
+ 21:36:58 INFO {
+ "profile_name": "https://github.com/JohnSnowLabs/spark-nlp.git",
+ "step": "TOTAL",
+ "delta_time": 7.740976418000173,
+ "cumulative_time": 7.740976418000173,
+ "cpu_time": 180.9221699,
+ "memory_usage": 569249792,
+ "memory_delta": 0,
+ "error": "LOW_IMPORT_RESOLUTION_RATE"
+}
+ 21:36:58 INFO Cloning repository https://github.com/Lightning-AI/lightning.git
+ 21:37:53 INFO {
+ "profile_name": "https://github.com/Lightning-AI/lightning.git",
+ "step": "codebase_init",
+ "delta_time": 24.256577352999557,
+ "cumulative_time": 24.256577352999557,
+ "cpu_time": 211.3604081,
+ "memory_usage": 1535971328,
+ "memory_delta": 966184960,
+ "error": null
+}
+ 21:37:53 INFO {
+ "profile_name": "https://github.com/Lightning-AI/lightning.git",
+ "step": "post_init_validation",
+ "delta_time": 0.137609629000508,
+ "cumulative_time": 24.394186982000065,
+ "cpu_time": 211.5082702,
+ "memory_usage": 1536241664,
+ "memory_delta": 270336,
+ "error": null
+}
+ 21:37:53 INFO {
+ "profile_name": "https://github.com/Lightning-AI/lightning.git",
+ "step": "TOTAL",
+ "delta_time": 24.394700584999555,
+ "cumulative_time": 24.394700584999555,
+ "cpu_time": 211.5088282,
+ "memory_usage": 1536241664,
+ "memory_delta": 0,
+ "error": null
+}
+```
+
+## Example Metrics Output
+
+| profile_name | step | delta_time | cumulative_time | cpu_time | memory_usage | memory_delta | error |
+| ---------------------- | -------------------- | ------------------ | ------------------ | ----------- | ------------ | ------------ | -------------------------- |
+| JohnSnowLabs/spark-nlp | codebase_init | 7.186550649999845 | 7.186550649999845 | 180.3553702 | 567525376 | 317095936 | |
+| JohnSnowLabs/spark-nlp | post_init_validation | 0.5465090990001045 | 7.733059748999949 | 180.9174761 | 569249792 | 1724416 | |
+| JohnSnowLabs/spark-nlp | TOTAL | 7.740976418000173 | 7.740976418000173 | 180.9221699 | 569249792 | 0 | LOW_IMPORT_RESOLUTION_RATE |
+| Lightning-AI/lightning | codebase_init | 24.256577352999557 | 24.256577352999557 | 211.3604081 | 1535971328 | 966184960 | |
+| Lightning-AI/lightning | post_init_validation | 0.137609629000508 | 24.394186982000065 | 211.5082702 | 1536241664 | 270336 | |
+| Lightning-AI/lightning | TOTAL | 24.394700584999555 | 24.394700584999555 | 211.5088282 | 1536241664 | 0 | |
diff --git a/codegen-on-oss/codecov.yaml b/codegen-on-oss/codecov.yaml
new file mode 100644
index 000000000..058cfb765
--- /dev/null
+++ b/codegen-on-oss/codecov.yaml
@@ -0,0 +1,9 @@
+coverage:
+ range: 70..100
+ round: down
+ precision: 1
+ status:
+ project:
+ default:
+ target: 90%
+ threshold: 0.5%
diff --git a/codegen-on-oss/codegen_modal_deploy.py b/codegen-on-oss/codegen_modal_deploy.py
new file mode 100644
index 000000000..a0fa03539
--- /dev/null
+++ b/codegen-on-oss/codegen_modal_deploy.py
@@ -0,0 +1,76 @@
+import sys
+from pathlib import Path
+
+import modal
+from loguru import logger
+
+from codegen_on_oss.cache import cachedir
+from codegen_on_oss.metrics import MetricsProfiler
+from codegen_on_oss.outputs.sql_output import ParseMetricsSQLOutput
+from codegen_on_oss.parser import CodegenParser
+
+app = modal.App("codegen-oss-parse")
+
+
+codegen_repo_volume = modal.Volume.from_name(
+ "codegen-oss-repo-volume",
+ create_if_missing=True,
+)
+
+
+aws_secrets = modal.Secret.from_name(
+ "codegen-oss-parse-secrets",
+)
+
+
+@app.function(
+ name="parse_repo",
+ concurrency_limit=10,
+ cpu=4,
+ memory=16384,
+ timeout=3600 * 8,
+ secrets=[aws_secrets],
+ volumes={
+ str(cachedir.absolute()): codegen_repo_volume,
+ },
+ proxy=modal.Proxy.from_name("codegen-parse-proxy"),
+ image=modal.Image.debian_slim(python_version="3.13")
+ .pip_install("uv")
+ .apt_install("git") # required by codegen sdk
+ .env({"PATH": "/app/.venv/bin:$PATH"})
+ .workdir("/app")
+ .add_local_file("uv.lock", remote_path="/app/uv.lock", copy=True)
+ .add_local_file("pyproject.toml", remote_path="/app/pyproject.toml", copy=True)
+ .run_commands("uv sync --frozen --no-install-project --extra sql")
+ .add_local_python_source("codegen_on_oss", copy=True),
+ # .add_local_python_source("codegen_on_oss"),
+ # .add_local_dir("codegen_on_oss", remote_path="/app/codegen_on_oss"),
+)
+def parse_repo(
+ repo_url: str,
+ commit_hash: str | None,
+ language: str | None = None,
+):
+ """
+ Parse repositories on Modal.
+
+ Args:
+ repo_url: The URL of the repository to parse.
+ commit_hash: The commit hash of the repository to parse.
+ """
+ logger.add(sys.stdout, format="{time: HH:mm:ss} {level} {message}", level="DEBUG")
+
+ output = ParseMetricsSQLOutput(
+ modal_function_call_id=modal.current_function_call_id()
+ )
+ metrics_profiler = MetricsProfiler(output)
+ parser = CodegenParser(Path(cachedir) / "repositories", metrics_profiler)
+ # Refresh any updating repo data from other instances
+ codegen_repo_volume.reload()
+ try:
+ parser.parse(repo_url, language, commit_hash)
+ except Exception as e:
+ logger.exception(f"Error parsing repository {repo_url}: {e}")
+ finally:
+ # Commit any cache changes to the repo volume
+ codegen_repo_volume.commit()
diff --git a/codegen-on-oss/codegen_modal_run.py b/codegen-on-oss/codegen_modal_run.py
new file mode 100644
index 000000000..ab0ad8ecb
--- /dev/null
+++ b/codegen-on-oss/codegen_modal_run.py
@@ -0,0 +1,29 @@
+import modal
+
+from codegen_on_oss.sources import GithubSettings, GithubSource
+
+app = modal.App("codegen-oss-parse")
+
+
+@app.local_entrypoint()
+def main(
+ languages: str = "python,typescript",
+ heuristic: str = "stars",
+ num_repos: int = 100,
+):
+ """
+ Main entrypoint for the parse app.
+ """
+ parse_repo_on_modal_fn = modal.Function.from_name("codegen-oss-parse", "parse_repo")
+ for language in languages.split(","):
+ repo_source = GithubSource(
+ GithubSettings(
+ language=language.strip(), heuristic=heuristic, num_repos=num_repos
+ )
+ )
+ for repo_url, commit_hash in repo_source:
+ parse_repo_on_modal_fn.spawn(
+ repo_url=repo_url,
+ commit_hash=commit_hash,
+ language=language,
+ )
diff --git a/codegen-on-oss/codegen_on_oss/__init__.py b/codegen-on-oss/codegen_on_oss/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/codegen-on-oss/codegen_on_oss/bucket_store.py b/codegen-on-oss/codegen_on_oss/bucket_store.py
new file mode 100644
index 000000000..f068fd691
--- /dev/null
+++ b/codegen-on-oss/codegen_on_oss/bucket_store.py
@@ -0,0 +1,26 @@
+from datetime import datetime
+from importlib.metadata import version
+from typing import TYPE_CHECKING
+
+from boto3 import client
+
+if TYPE_CHECKING:
+ from types_boto3_s3 import S3Client
+
+
+class BucketStore:
+ s3_client: "S3Client"
+
+ def __init__(self, bucket_name: str):
+ self.bucket_name = bucket_name
+ self.s3_client = client("s3")
+ self.key_prefix: str = str(version("codegen"))
+
+ def upload_file(self, local_path: str, remote_path: str) -> str:
+ key = f"{self.key_prefix}/{datetime.now().strftime('%Y-%m-%d-%H-%M-%S')}/{remote_path}"
+ self.s3_client.upload_file(
+ local_path,
+ self.bucket_name,
+ key,
+ )
+ return key
diff --git a/codegen-on-oss/codegen_on_oss/cache.py b/codegen-on-oss/codegen_on_oss/cache.py
new file mode 100644
index 000000000..6f1346a98
--- /dev/null
+++ b/codegen-on-oss/codegen_on_oss/cache.py
@@ -0,0 +1,5 @@
+from pathlib import Path
+
+from platformdirs import user_cache_dir
+
+cachedir = Path(user_cache_dir("codegen-on-oss", "codegen"))
diff --git a/codegen-on-oss/codegen_on_oss/cli.py b/codegen-on-oss/codegen_on_oss/cli.py
new file mode 100644
index 000000000..c1807d13e
--- /dev/null
+++ b/codegen-on-oss/codegen_on_oss/cli.py
@@ -0,0 +1,128 @@
+import sys
+from pathlib import Path
+
+import click
+from loguru import logger
+
+from codegen_on_oss.cache import cachedir
+from codegen_on_oss.metrics import MetricsProfiler
+from codegen_on_oss.outputs.csv_output import CSVOutput
+from codegen_on_oss.parser import CodegenParser
+from codegen_on_oss.sources import RepoSource, all_sources
+
+logger.remove(0)
+
+
+@click.group()
+def cli():
+ pass
+
+
+@cli.command(name="run-one")
+@click.argument("url", type=str)
+@click.option(
+ "--cache-dir",
+ type=click.Path(dir_okay=True),
+ help="Cache directory",
+ default=cachedir,
+)
+@click.option(
+ "--output-path",
+ type=click.Path(dir_okay=True),
+ help="Output path",
+ default="metrics.csv",
+)
+@click.option(
+ "--commit-hash",
+ type=str,
+ help="Commit hash to parse",
+)
+@click.option(
+ "--error-output-path",
+ type=click.Path(dir_okay=True),
+ help="Error output path",
+ default=cachedir / "errors.log",
+)
+@click.option(
+ "--debug",
+ is_flag=True,
+ help="Debug mode",
+)
+def run_one(
+ url: str,
+ cache_dir: str | Path = str(cachedir),
+ output_path: str = "metrics.csv",
+ commit_hash: str | None = None,
+ error_output_path: Path = str(cachedir / "errors.log"),
+ debug: bool = False,
+):
+ """
+ Parse a repository with codegen
+ """
+ logger.add(error_output_path, level="ERROR")
+ logger.add(sys.stdout, level="DEBUG" if debug else "INFO")
+ output = CSVOutput(MetricsProfiler.fields(), output_path)
+ metrics_profiler = MetricsProfiler(output)
+
+ parser = CodegenParser(Path(cache_dir) / "repositories", metrics_profiler)
+ parser.parse(url, commit_hash)
+
+
+@cli.command()
+@click.option(
+ "--source",
+ type=click.Choice(list(all_sources.keys())),
+ default="csv",
+)
+@click.option(
+ "--output-path",
+ type=click.Path(dir_okay=True),
+ help="Output path",
+ default="metrics.csv",
+)
+@click.option(
+ "--error-output-path",
+ type=click.Path(dir_okay=True),
+ help="Error output path",
+ default="errors.log",
+)
+@click.option(
+ "--cache-dir",
+ type=click.Path(dir_okay=True),
+ help="Cache directory",
+ default=cachedir,
+)
+@click.option(
+ "--debug",
+ is_flag=True,
+ help="Debug mode",
+)
+def run(
+ source: str,
+ output_path: str,
+ error_output_path: str,
+ cache_dir: str,
+ debug: bool,
+):
+ """
+ Run codegen parsing pipeline on repositories from a given repository source.
+ """
+ logger.add(
+ error_output_path, format="{time: HH:mm:ss} {level} {message}", level="ERROR"
+ )
+ logger.add(
+ sys.stdout,
+ format="{time: HH:mm:ss} {level} {message}",
+ level="DEBUG" if debug else "INFO",
+ )
+
+ repo_source = RepoSource.from_source_type(source)
+ output = CSVOutput(MetricsProfiler.fields(), output_path)
+ metrics_profiler = MetricsProfiler(output)
+ parser = CodegenParser(Path(cache_dir) / "repositories", metrics_profiler)
+ for repo_url, commit_hash in repo_source:
+ parser.parse(repo_url, commit_hash)
+
+
+if __name__ == "__main__":
+ cli()
diff --git a/codegen-on-oss/codegen_on_oss/error_analyzer.py b/codegen-on-oss/codegen_on_oss/error_analyzer.py
new file mode 100644
index 000000000..2c3355d18
--- /dev/null
+++ b/codegen-on-oss/codegen_on_oss/error_analyzer.py
@@ -0,0 +1,1994 @@
+#!/usr/bin/env python3
+"""
+Comprehensive Codebase Analyzer
+
+This module provides a complete static code analysis system using the Codegen SDK.
+It analyzes a codebase and provides extensive information about its structure,
+dependencies, code quality, and more.
+"""
+
+import os
+import sys
+import json
+import time
+import logging
+import argparse
+import tempfile
+import datetime
+import re
+import math
+import networkx as nx
+from pathlib import Path
+from typing import Dict, List, Set, Tuple, Any, Optional, Union, Callable
+from collections import Counter, defaultdict
+import matplotlib.pyplot as plt
+from rich.console import Console
+from rich.table import Table
+from rich.progress import Progress, SpinnerColumn, TextColumn, BarColumn, TimeElapsedColumn
+
+try:
+ from codegen.sdk.core.codebase import Codebase
+ from codegen.configs.models.codebase import CodebaseConfig
+ from codegen.configs.models.secrets import SecretsConfig
+ from codegen.shared.enums.programming_language import ProgrammingLanguage
+except ImportError:
+ print("Codegen SDK not found. Please install it first.")
+ sys.exit(1)
+
+# Configure logging
+logging.basicConfig(
+ level=logging.INFO,
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
+ handlers=[logging.StreamHandler()]
+)
+logger = logging.getLogger(__name__)
+
+# Constants
+METRICS_CATEGORIES = {
+ "codebase_structure": [
+ "get_file_count",
+ "get_files_by_language",
+ "get_file_size_distribution",
+ "get_directory_structure",
+ "get_symbol_count",
+ "get_symbol_type_distribution",
+ "get_symbol_hierarchy",
+ "get_top_level_vs_nested_symbols",
+ "get_import_dependency_map",
+ "get_external_vs_internal_dependencies",
+ "get_circular_imports",
+ "get_unused_imports",
+ "get_module_coupling_metrics",
+ "get_module_cohesion_analysis",
+ "get_package_structure",
+ "get_module_dependency_graph",
+ ],
+ "symbol_level": [
+ "get_function_parameter_analysis",
+ "get_return_type_analysis",
+ "get_function_complexity_metrics",
+ "get_call_site_tracking",
+ "get_async_function_detection",
+ "get_function_overload_analysis",
+ "get_inheritance_hierarchy",
+ "get_method_analysis",
+ "get_attribute_analysis",
+ "get_constructor_analysis",
+ "get_interface_implementation_verification",
+ "get_access_modifier_usage",
+ "get_type_inference",
+ "get_usage_tracking",
+ "get_scope_analysis",
+ "get_constant_vs_mutable_usage",
+ "get_global_variable_detection",
+ "get_type_alias_resolution",
+ "get_generic_type_usage",
+ "get_type_consistency_checking",
+ "get_union_intersection_type_analysis",
+ ],
+ "dependency_flow": [
+ "get_function_call_relationships",
+ "get_call_hierarchy_visualization",
+ "get_entry_point_analysis",
+ "get_dead_code_detection",
+ "get_variable_usage_tracking",
+ "get_data_transformation_paths",
+ "get_input_output_parameter_analysis",
+ "get_conditional_branch_analysis",
+ "get_loop_structure_analysis",
+ "get_exception_handling_paths",
+ "get_return_statement_analysis",
+ "get_symbol_reference_tracking",
+ "get_usage_frequency_metrics",
+ "get_cross_file_symbol_usage",
+ ],
+ "code_quality": [
+ "get_unused_functions",
+ "get_unused_classes",
+ "get_unused_variables",
+ "get_unused_imports",
+ "get_similar_function_detection",
+ "get_repeated_code_patterns",
+ "get_refactoring_opportunities",
+ "get_cyclomatic_complexity",
+ "get_cognitive_complexity",
+ "get_nesting_depth_analysis",
+ "get_function_size_metrics",
+ "get_naming_convention_consistency",
+ "get_comment_coverage",
+ "get_documentation_completeness",
+ "get_code_formatting_consistency",
+ ],
+ "visualization": [
+ "get_module_dependency_visualization",
+ "get_symbol_dependency_visualization",
+ "get_import_relationship_graphs",
+ "get_function_call_visualization",
+ "get_call_hierarchy_trees",
+ "get_entry_point_flow_diagrams",
+ "get_class_hierarchy_visualization",
+ "get_symbol_relationship_diagrams",
+ "get_package_structure_visualization",
+ "get_code_complexity_heat_maps",
+ "get_usage_frequency_visualization",
+ "get_change_frequency_analysis",
+ ],
+ "language_specific": [
+ "get_decorator_usage_analysis",
+ "get_dynamic_attribute_access_detection",
+ "get_type_hint_coverage",
+ "get_magic_method_usage",
+ "get_interface_implementation_verification",
+ "get_type_definition_completeness",
+ "get_jsx_tsx_component_analysis",
+ "get_type_narrowing_pattern_detection",
+ ],
+ "code_metrics": [
+ "get_monthly_commits",
+ "calculate_cyclomatic_complexity",
+ "cc_rank",
+ "get_operators_and_operands",
+ "calculate_halstead_volume",
+ "count_lines",
+ "calculate_maintainability_index",
+ "get_maintainability_rank",
+ ]
+}
+
+class CodebaseAnalyzer:
+ """
+ Comprehensive codebase analyzer using Codegen SDK.
+
+ This class provides methods to analyze a codebase and extract detailed information
+ about its structure, dependencies, code quality, and more.
+ """
+
+ def __init__(self, repo_url: str = None, repo_path: str = None, language: str = None):
+ """
+ Initialize the CodebaseAnalyzer.
+
+ Args:
+ repo_url: URL of the repository to analyze
+ repo_path: Local path to the repository to analyze
+ language: Programming language of the codebase (auto-detected if not provided)
+ """
+ self.repo_url = repo_url
+ self.repo_path = repo_path
+ self.language = language
+ self.codebase = None
+ self.console = Console()
+ self.results = {}
+
+ # Initialize the codebase
+ if repo_url:
+ self._init_from_url(repo_url, language)
+ elif repo_path:
+ self._init_from_path(repo_path, language)
+
+ def _init_from_url(self, repo_url: str, language: str = None):
+ """Initialize codebase from a repository URL."""
+ try:
+ # Extract owner and repo name from URL
+ if repo_url.endswith('.git'):
+ repo_url = repo_url[:-4]
+
+ parts = repo_url.rstrip('/').split('/')
+ repo_name = parts[-1]
+ owner = parts[-2]
+ repo_full_name = f"{owner}/{repo_name}"
+
+ # Create a temporary directory for cloning
+ tmp_dir = tempfile.mkdtemp(prefix="codebase_analyzer_")
+
+ # Configure the codebase
+ config = CodebaseConfig(
+ debug=False,
+ allow_external=True,
+ py_resolve_syspath=True,
+ )
+
+ secrets = SecretsConfig()
+
+ # Initialize the codebase
+ self.console.print(f"[bold green]Initializing codebase from {repo_url}...[/bold green]")
+
+ prog_lang = None
+ if language:
+ prog_lang = ProgrammingLanguage(language.upper())
+
+ self.codebase = Codebase.from_github(
+ repo_full_name=repo_full_name,
+ tmp_dir=tmp_dir,
+ language=prog_lang,
+ config=config,
+ secrets=secrets,
+ full_history=True
+ )
+
+ self.console.print(f"[bold green]Successfully initialized codebase from {repo_url}[/bold green]")
+
+ except Exception as e:
+ self.console.print(f"[bold red]Error initializing codebase from URL: {e}[/bold red]")
+ raise
+
+ def _init_from_path(self, repo_path: str, language: str = None):
+ """Initialize codebase from a local repository path."""
+ try:
+ # Configure the codebase
+ config = CodebaseConfig(
+ debug=False,
+ allow_external=True,
+ py_resolve_syspath=True,
+ )
+
+ secrets = SecretsConfig()
+
+ # Initialize the codebase
+ self.console.print(f"[bold green]Initializing codebase from {repo_path}...[/bold green]")
+
+ prog_lang = None
+ if language:
+ prog_lang = ProgrammingLanguage(language.upper())
+
+ self.codebase = Codebase(
+ repo_path=repo_path,
+ language=prog_lang,
+ config=config,
+ secrets=secrets
+ )
+
+ self.console.print(f"[bold green]Successfully initialized codebase from {repo_path}[/bold green]")
+
+ except Exception as e:
+ self.console.print(f"[bold red]Error initializing codebase from path: {e}[/bold red]")
+ raise
+
+ def analyze(self, categories: List[str] = None, output_format: str = "json", output_file: str = None):
+ """
+ Perform a comprehensive analysis of the codebase.
+
+ Args:
+ categories: List of categories to analyze. If None, all categories are analyzed.
+ output_format: Format of the output (json, html, console)
+ output_file: Path to the output file
+
+ Returns:
+ Dict containing the analysis results
+ """
+ if not self.codebase:
+ raise ValueError("Codebase not initialized. Please initialize the codebase first.")
+
+ # If no categories specified, analyze all
+ if not categories:
+ categories = list(METRICS_CATEGORIES.keys())
+
+ # Initialize results dictionary
+ self.results = {
+ "metadata": {
+ "repo_name": self.codebase.ctx.repo_name,
+ "analysis_time": datetime.datetime.now().isoformat(),
+ "language": str(self.codebase.ctx.programming_language),
+ },
+ "categories": {}
+ }
+
+ # Analyze each category
+ with Progress(
+ SpinnerColumn(),
+ TextColumn("[bold blue]{task.description}"),
+ BarColumn(),
+ TextColumn("[bold green]{task.completed}/{task.total}"),
+ TimeElapsedColumn(),
+ ) as progress:
+ task = progress.add_task("[bold green]Analyzing codebase...", total=len(categories))
+
+ for category in categories:
+ if category not in METRICS_CATEGORIES:
+ self.console.print(f"[bold yellow]Warning: Unknown category '{category}'. Skipping.[/bold yellow]")
+ progress.update(task, advance=1)
+ continue
+
+ self.console.print(f"[bold blue]Analyzing {category}...[/bold blue]")
+
+ # Get the metrics for this category
+ metrics = METRICS_CATEGORIES[category]
+ category_results = {}
+
+ # Run each metric
+ for metric in metrics:
+ try:
+ method = getattr(self, metric, None)
+ if method and callable(method):
+ result = method()
+ category_results[metric] = result
+ else:
+ category_results[metric] = {"error": f"Method {metric} not implemented"}
+ except Exception as e:
+ category_results[metric] = {"error": str(e)}
+
+ # Add the results to the main results dictionary
+ self.results["categories"][category] = category_results
+
+ progress.update(task, advance=1)
+
+ # Output the results
+ if output_format == "json":
+ if output_file:
+ with open(output_file, 'w') as f:
+ json.dump(self.results, f, indent=2)
+ self.console.print(f"[bold green]Results saved to {output_file}[/bold green]")
+ else:
+ return self.results
+ elif output_format == "html":
+ self._generate_html_report(output_file)
+ elif output_format == "console":
+ self._print_console_report()
+
+ return self.results
+
+ #
+ # Codebase Structure Analysis Methods
+ #
+
+ def get_file_count(self) -> Dict[str, int]:
+ """Get the total number of files in the codebase."""
+ files = list(self.codebase.files)
+ return {
+ "total_files": len(files),
+ "source_files": len([f for f in files if not f.is_binary])
+ }
+
+ def get_files_by_language(self) -> Dict[str, int]:
+ """Get the distribution of files by language/extension."""
+ files = list(self.codebase.files)
+ extensions = {}
+
+ for file in files:
+ if file.is_binary:
+ continue
+
+ ext = file.extension
+ if not ext:
+ ext = "(no extension)"
+
+ if ext in extensions:
+ extensions[ext] += 1
+ else:
+ extensions[ext] = 1
+
+ return extensions
+
+ def get_file_size_distribution(self) -> Dict[str, int]:
+ """Get the distribution of file sizes."""
+ files = list(self.codebase.files)
+ size_ranges = {
+ "small (< 1KB)": 0,
+ "medium (1KB - 10KB)": 0,
+ "large (10KB - 100KB)": 0,
+ "very large (> 100KB)": 0
+ }
+
+ for file in files:
+ if file.is_binary:
+ continue
+
+ size = len(file.content)
+
+ if size < 1024:
+ size_ranges["small (< 1KB)"] += 1
+ elif size < 10240:
+ size_ranges["medium (1KB - 10KB)"] += 1
+ elif size < 102400:
+ size_ranges["large (10KB - 100KB)"] += 1
+ else:
+ size_ranges["very large (> 100KB)"] += 1
+
+ return size_ranges
+
+ def get_directory_structure(self) -> Dict[str, Any]:
+ """Get the directory structure of the codebase."""
+ directories = {}
+
+ for directory in self.codebase.directories:
+ path = str(directory.path)
+ parent_path = str(directory.path.parent) if directory.path.parent != self.codebase.repo_path else "/"
+
+ if parent_path not in directories:
+ directories[parent_path] = []
+
+ directories[parent_path].append({
+ "name": directory.path.name,
+ "path": path,
+ "files": len(directory.files),
+ "subdirectories": len(directory.subdirectories)
+ })
+
+ return directories
+
+ def get_symbol_count(self) -> Dict[str, int]:
+ """Get the total count of symbols in the codebase."""
+ return {
+ "total_symbols": len(list(self.codebase.symbols)),
+ "classes": len(list(self.codebase.classes)),
+ "functions": len(list(self.codebase.functions)),
+ "global_vars": len(list(self.codebase.global_vars)),
+ "interfaces": len(list(self.codebase.interfaces))
+ }
+
+ def get_symbol_type_distribution(self) -> Dict[str, int]:
+ """Get the distribution of symbol types."""
+ symbols = list(self.codebase.symbols)
+ distribution = {}
+
+ for symbol in symbols:
+ symbol_type = str(symbol.symbol_type)
+
+ if symbol_type in distribution:
+ distribution[symbol_type] += 1
+ else:
+ distribution[symbol_type] = 1
+
+ return distribution
+
+ def get_symbol_hierarchy(self) -> Dict[str, Any]:
+ """Get the hierarchy of symbols in the codebase."""
+ classes = list(self.codebase.classes)
+ hierarchy = {}
+
+ for cls in classes:
+ class_name = cls.name
+ parent_classes = []
+
+ # Get parent classes if available
+ if hasattr(cls, "parent_class_names"):
+ parent_classes = cls.parent_class_names
+
+ hierarchy[class_name] = {
+ "parent_classes": parent_classes,
+ "methods": [method.name for method in cls.methods],
+ "attributes": [attr.name for attr in cls.attributes] if hasattr(cls, "attributes") else []
+ }
+
+ return hierarchy
+
+ def get_top_level_vs_nested_symbols(self) -> Dict[str, int]:
+ """Get the count of top-level vs nested symbols."""
+ symbols = list(self.codebase.symbols)
+ top_level = 0
+ nested = 0
+
+ for symbol in symbols:
+ if hasattr(symbol, "is_top_level") and symbol.is_top_level:
+ top_level += 1
+ else:
+ nested += 1
+
+ return {
+ "top_level": top_level,
+ "nested": nested
+ }
+
+ def get_import_dependency_map(self) -> Dict[str, List[str]]:
+ """Get a map of import dependencies."""
+ files = list(self.codebase.files)
+ dependency_map = {}
+
+ for file in files:
+ if file.is_binary:
+ continue
+
+ file_path = file.file_path
+ imports = []
+
+ for imp in file.imports:
+ if hasattr(imp, "imported_symbol") and imp.imported_symbol:
+ imported_symbol = imp.imported_symbol
+ if hasattr(imported_symbol, "file") and imported_symbol.file:
+ imports.append(imported_symbol.file.file_path)
+
+ dependency_map[file_path] = imports
+
+ return dependency_map
+
+ def get_external_vs_internal_dependencies(self) -> Dict[str, int]:
+ """Get the count of external vs internal dependencies."""
+ files = list(self.codebase.files)
+ internal = 0
+ external = 0
+
+ for file in files:
+ if file.is_binary:
+ continue
+
+ for imp in file.imports:
+ if hasattr(imp, "imported_symbol") and imp.imported_symbol:
+ imported_symbol = imp.imported_symbol
+ if hasattr(imported_symbol, "file") and imported_symbol.file:
+ internal += 1
+ else:
+ external += 1
+ else:
+ external += 1
+
+ return {
+ "internal": internal,
+ "external": external
+ }
+
+ def get_circular_imports(self) -> List[List[str]]:
+ """Detect circular imports in the codebase."""
+ files = list(self.codebase.files)
+ dependency_map = {}
+
+ # Build dependency graph
+ for file in files:
+ if file.is_binary:
+ continue
+
+ file_path = file.file_path
+ imports = []
+
+ for imp in file.imports:
+ if hasattr(imp, "imported_symbol") and imp.imported_symbol:
+ imported_symbol = imp.imported_symbol
+ if hasattr(imported_symbol, "file") and imported_symbol.file:
+ imports.append(imported_symbol.file.file_path)
+
+ dependency_map[file_path] = imports
+
+ # Create a directed graph
+ G = nx.DiGraph()
+
+ # Add nodes and edges
+ for file_path, imports in dependency_map.items():
+ G.add_node(file_path)
+ for imp in imports:
+ G.add_edge(file_path, imp)
+
+ # Find cycles
+ cycles = list(nx.simple_cycles(G))
+
+ return cycles
+
+ def get_unused_imports(self) -> List[Dict[str, str]]:
+ """Get a list of unused imports."""
+ files = list(self.codebase.files)
+ unused_imports = []
+
+ for file in files:
+ if file.is_binary:
+ continue
+
+ for imp in file.imports:
+ if hasattr(imp, "usages") and len(imp.usages) == 0:
+ unused_imports.append({
+ "file": file.file_path,
+ "import": imp.source
+ })
+
+ return unused_imports
+
+ def get_module_coupling_metrics(self) -> Dict[str, float]:
+ """Calculate module coupling metrics."""
+ files = list(self.codebase.files)
+ dependency_map = {}
+
+ # Build dependency graph
+ for file in files:
+ if file.is_binary:
+ continue
+
+ file_path = file.file_path
+ imports = []
+
+ for imp in file.imports:
+ if hasattr(imp, "imported_symbol") and imp.imported_symbol:
+ imported_symbol = imp.imported_symbol
+ if hasattr(imported_symbol, "file") and imported_symbol.file:
+ imports.append(imported_symbol.file.file_path)
+
+ dependency_map[file_path] = imports
+
+ # Calculate metrics
+ total_files = len(dependency_map)
+ total_dependencies = sum(len(deps) for deps in dependency_map.values())
+
+ if total_files == 0:
+ return {
+ "average_dependencies_per_file": 0,
+ "max_dependencies": 0,
+ "coupling_factor": 0
+ }
+
+ max_dependencies = max(len(deps) for deps in dependency_map.values()) if dependency_map else 0
+ coupling_factor = total_dependencies / (total_files * (total_files - 1)) if total_files > 1 else 0
+
+ return {
+ "average_dependencies_per_file": total_dependencies / total_files,
+ "max_dependencies": max_dependencies,
+ "coupling_factor": coupling_factor
+ }
+
+ def get_module_cohesion_analysis(self) -> Dict[str, float]:
+ """Analyze module cohesion."""
+ files = list(self.codebase.files)
+ cohesion_metrics = {}
+
+ for file in files:
+ if file.is_binary:
+ continue
+
+ symbols = list(file.symbols)
+ total_symbols = len(symbols)
+
+ if total_symbols <= 1:
+ continue
+
+ # Count internal references
+ internal_refs = 0
+
+ for symbol in symbols:
+ if hasattr(symbol, "symbol_usages"):
+ for usage in symbol.symbol_usages:
+ if hasattr(usage, "file") and usage.file == file:
+ internal_refs += 1
+
+ max_possible_refs = total_symbols * (total_symbols - 1)
+ cohesion = internal_refs / max_possible_refs if max_possible_refs > 0 else 0
+
+ cohesion_metrics[file.file_path] = cohesion
+
+ # Calculate average cohesion
+ if cohesion_metrics:
+ avg_cohesion = sum(cohesion_metrics.values()) / len(cohesion_metrics)
+ else:
+ avg_cohesion = 0
+
+ return {
+ "average_cohesion": avg_cohesion,
+ "file_cohesion": cohesion_metrics
+ }
+
+ def get_package_structure(self) -> Dict[str, Any]:
+ """Get the package structure of the codebase."""
+ directories = {}
+
+ for directory in self.codebase.directories:
+ path = str(directory.path)
+ parent_path = str(directory.path.parent) if directory.path.parent != self.codebase.repo_path else "/"
+
+ if parent_path not in directories:
+ directories[parent_path] = []
+
+ # Check if this is a package (has __init__.py)
+ is_package = any(f.name == "__init__.py" for f in directory.files)
+
+ directories[parent_path].append({
+ "name": directory.path.name,
+ "path": path,
+ "is_package": is_package,
+ "files": len(directory.files),
+ "subdirectories": len(directory.subdirectories)
+ })
+
+ return directories
+
+ def get_module_dependency_graph(self) -> Dict[str, List[str]]:
+ """Get the module dependency graph."""
+ files = list(self.codebase.files)
+ dependency_graph = {}
+
+ for file in files:
+ if file.is_binary:
+ continue
+
+ file_path = file.file_path
+ imports = []
+
+ for imp in file.imports:
+ if hasattr(imp, "imported_symbol") and imp.imported_symbol:
+ imported_symbol = imp.imported_symbol
+ if hasattr(imported_symbol, "file") and imported_symbol.file:
+ imports.append(imported_symbol.file.file_path)
+
+ dependency_graph[file_path] = imports
+
+ return dependency_graph
+
+ #
+ # Symbol-Level Analysis Methods
+ #
+
+ def get_function_parameter_analysis(self) -> Dict[str, Any]:
+ """Analyze function parameters."""
+ functions = list(self.codebase.functions)
+ parameter_stats = {
+ "total_parameters": 0,
+ "avg_parameters_per_function": 0,
+ "functions_with_no_parameters": 0,
+ "functions_with_many_parameters": 0, # > 5 parameters
+ "parameter_type_coverage": 0,
+ "functions_with_default_params": 0
+ }
+
+ if not functions:
+ return parameter_stats
+
+ total_params = 0
+ functions_with_types = 0
+ functions_with_defaults = 0
+
+ for func in functions:
+ params = func.parameters
+ param_count = len(params)
+ total_params += param_count
+
+ if param_count == 0:
+ parameter_stats["functions_with_no_parameters"] += 1
+ elif param_count > 5:
+ parameter_stats["functions_with_many_parameters"] += 1
+
+ # Check for type annotations
+ has_type_annotations = all(hasattr(p, "type") and p.type for p in params)
+ if has_type_annotations:
+ functions_with_types += 1
+
+ # Check for default values
+ has_defaults = any(hasattr(p, "default") and p.default for p in params)
+ if has_defaults:
+ functions_with_defaults += 1
+
+ parameter_stats["total_parameters"] = total_params
+ parameter_stats["avg_parameters_per_function"] = total_params / len(functions)
+ parameter_stats["parameter_type_coverage"] = functions_with_types / len(functions) if functions else 0
+ parameter_stats["functions_with_default_params"] = functions_with_defaults
+
+ return parameter_stats
+
+ def get_return_type_analysis(self) -> Dict[str, Any]:
+ """Analyze function return types."""
+ functions = list(self.codebase.functions)
+ return_type_stats = {
+ "functions_with_return_type": 0,
+ "return_type_coverage": 0,
+ "common_return_types": {}
+ }
+
+ if not functions:
+ return return_type_stats
+
+ functions_with_return_type = 0
+ return_types = {}
+
+ for func in functions:
+ if hasattr(func, "return_type") and func.return_type:
+ functions_with_return_type += 1
+
+ return_type = str(func.return_type.source) if hasattr(func.return_type, "source") else str(func.return_type)
+
+ if return_type in return_types:
+ return_types[return_type] += 1
+ else:
+ return_types[return_type] = 1
+
+ return_type_stats["functions_with_return_type"] = functions_with_return_type
+ return_type_stats["return_type_coverage"] = functions_with_return_type / len(functions)
+
+ # Get the most common return types
+ sorted_types = sorted(return_types.items(), key=lambda x: x[1], reverse=True)
+ return_type_stats["common_return_types"] = dict(sorted_types[:10]) # Top 10 return types
+
+ return return_type_stats
+
+ def get_function_complexity_metrics(self) -> Dict[str, Any]:
+ """Calculate function complexity metrics."""
+ functions = list(self.codebase.functions)
+ complexity_metrics = {
+ "avg_function_length": 0,
+ "max_function_length": 0,
+ "functions_by_complexity": {
+ "simple": 0, # < 10 lines
+ "moderate": 0, # 10-30 lines
+ "complex": 0, # 30-100 lines
+ "very_complex": 0 # > 100 lines
+ }
+ }
+
+ if not functions:
+ return complexity_metrics
+
+ total_length = 0
+ max_length = 0
+
+ for func in functions:
+ # Calculate function length in lines
+ func_source = func.source
+ func_lines = func_source.count('\n') + 1
+
+ total_length += func_lines
+ max_length = max(max_length, func_lines)
+
+ # Categorize by complexity
+ if func_lines < 10:
+ complexity_metrics["functions_by_complexity"]["simple"] += 1
+ elif func_lines < 30:
+ complexity_metrics["functions_by_complexity"]["moderate"] += 1
+ elif func_lines < 100:
+ complexity_metrics["functions_by_complexity"]["complex"] += 1
+ else:
+ complexity_metrics["functions_by_complexity"]["very_complex"] += 1
+
+ complexity_metrics["avg_function_length"] = total_length / len(functions)
+ complexity_metrics["max_function_length"] = max_length
+
+ return complexity_metrics
+
+ def get_call_site_tracking(self) -> Dict[str, Any]:
+ """Track function call sites."""
+ functions = list(self.codebase.functions)
+ call_site_stats = {
+ "functions_with_no_calls": 0,
+ "functions_with_many_calls": 0, # > 10 calls
+ "avg_call_sites_per_function": 0,
+ "most_called_functions": []
+ }
+
+ if not functions:
+ return call_site_stats
+
+ function_calls = {}
+ total_calls = 0
+
+ for func in functions:
+ if hasattr(func, "call_sites"):
+ call_count = len(func.call_sites)
+ total_calls += call_count
+
+ if call_count == 0:
+ call_site_stats["functions_with_no_calls"] += 1
+ elif call_count > 10:
+ call_site_stats["functions_with_many_calls"] += 1
+
+ function_calls[func.name] = call_count
+
+ call_site_stats["avg_call_sites_per_function"] = total_calls / len(functions)
+
+ # Get the most called functions
+ sorted_functions = sorted(function_calls.items(), key=lambda x: x[1], reverse=True)
+ call_site_stats["most_called_functions"] = [{"name": name, "calls": calls} for name, calls in sorted_functions[:10]]
+
+ return call_site_stats
+
+ def get_async_function_detection(self) -> Dict[str, Any]:
+ """Detect async functions."""
+ functions = list(self.codebase.functions)
+ async_stats = {
+ "total_async_functions": 0,
+ "async_function_percentage": 0,
+ "async_functions": []
+ }
+
+ if not functions:
+ return async_stats
+
+ async_functions = []
+
+ for func in functions:
+ if hasattr(func, "is_async") and func.is_async:
+ async_functions.append({
+ "name": func.name,
+ "file": func.file.file_path if hasattr(func, "file") else "Unknown"
+ })
+
+ async_stats["total_async_functions"] = len(async_functions)
+ async_stats["async_function_percentage"] = len(async_functions) / len(functions)
+ async_stats["async_functions"] = async_functions
+
+ return async_stats
+
+ def get_function_overload_analysis(self) -> Dict[str, Any]:
+ """Analyze function overloads."""
+ functions = list(self.codebase.functions)
+ overload_stats = {
+ "total_overloaded_functions": 0,
+ "overloaded_function_percentage": 0,
+ "overloaded_functions": []
+ }
+
+ if not functions:
+ return overload_stats
+
+ overloaded_functions = []
+ function_names = {}
+
+ for func in functions:
+ name = func.name
+
+ if name in function_names:
+ function_names[name].append(func)
+ else:
+ function_names[name] = [func]
+
+ for name, funcs in function_names.items():
+ if len(funcs) > 1:
+ overloaded_functions.append({
+ "name": name,
+ "overloads": len(funcs),
+ "file": funcs[0].file.file_path if hasattr(funcs[0], "file") else "Unknown"
+ })
+
+ overload_stats["total_overloaded_functions"] = len(overloaded_functions)
+ overload_stats["overloaded_function_percentage"] = len(overloaded_functions) / len(function_names) if function_names else 0
+ overload_stats["overloaded_functions"] = overloaded_functions
+
+ return overload_stats
+
+ def get_inheritance_hierarchy(self) -> Dict[str, Any]:
+ """Get the inheritance hierarchy of classes."""
+ classes = list(self.codebase.classes)
+ hierarchy = {}
+
+ for cls in classes:
+ class_name = cls.name
+ parent_classes = []
+
+ # Get parent classes if available
+ if hasattr(cls, "parent_class_names"):
+ parent_classes = cls.parent_class_names
+
+ hierarchy[class_name] = {
+ "parent_classes": parent_classes,
+ "file": cls.file.file_path if hasattr(cls, "file") else "Unknown"
+ }
+
+ # Build inheritance tree
+ inheritance_tree = {}
+
+ for class_name, info in hierarchy.items():
+ if not info["parent_classes"]:
+ if class_name not in inheritance_tree:
+ inheritance_tree[class_name] = []
+ else:
+ for parent in info["parent_classes"]:
+ if parent not in inheritance_tree:
+ inheritance_tree[parent] = []
+ inheritance_tree[parent].append(class_name)
+
+ return {
+ "class_hierarchy": hierarchy,
+ "inheritance_tree": inheritance_tree
+ }
+
+ def get_method_analysis(self) -> Dict[str, Any]:
+ """Analyze class methods."""
+ classes = list(self.codebase.classes)
+ method_stats = {
+ "total_methods": 0,
+ "avg_methods_per_class": 0,
+ "classes_with_no_methods": 0,
+ "classes_with_many_methods": 0, # > 10 methods
+ "method_types": {
+ "instance": 0,
+ "static": 0,
+ "class": 0,
+ "property": 0
+ }
+ }
+
+ if not classes:
+ return method_stats
+
+ total_methods = 0
+
+ for cls in classes:
+ methods = cls.methods if hasattr(cls, "methods") else []
+ method_count = len(methods)
+ total_methods += method_count
+
+ if method_count == 0:
+ method_stats["classes_with_no_methods"] += 1
+ elif method_count > 10:
+ method_stats["classes_with_many_methods"] += 1
+
+ # Analyze method types
+ for method in methods:
+ if hasattr(method, "is_static") and method.is_static:
+ method_stats["method_types"]["static"] += 1
+ elif hasattr(method, "is_class_method") and method.is_class_method:
+ method_stats["method_types"]["class"] += 1
+ elif hasattr(method, "is_property") and method.is_property:
+ method_stats["method_types"]["property"] += 1
+ else:
+ method_stats["method_types"]["instance"] += 1
+
+ method_stats["total_methods"] = total_methods
+ method_stats["avg_methods_per_class"] = total_methods / len(classes) if classes else 0
+
+ return method_stats
+
+ def get_attribute_analysis(self) -> Dict[str, Any]:
+ """Analyze class attributes."""
+ classes = list(self.codebase.classes)
+ attribute_stats = {
+ "total_attributes": 0,
+ "avg_attributes_per_class": 0,
+ "classes_with_no_attributes": 0,
+ "classes_with_many_attributes": 0, # > 10 attributes
+ "attribute_types": {}
+ }
+
+ if not classes:
+ return attribute_stats
+
+ total_attributes = 0
+ attribute_types = {}
+
+ for cls in classes:
+ attributes = cls.attributes if hasattr(cls, "attributes") else []
+ attr_count = len(attributes)
+ total_attributes += attr_count
+
+ if attr_count == 0:
+ attribute_stats["classes_with_no_attributes"] += 1
+ elif attr_count > 10:
+ attribute_stats["classes_with_many_attributes"] += 1
+
+ # Analyze attribute types
+ for attr in attributes:
+ if hasattr(attr, "type") and attr.type:
+ attr_type = str(attr.type.source) if hasattr(attr.type, "source") else str(attr.type)
+
+ if attr_type in attribute_types:
+ attribute_types[attr_type] += 1
+ else:
+ attribute_types[attr_type] = 1
+
+ attribute_stats["total_attributes"] = total_attributes
+ attribute_stats["avg_attributes_per_class"] = total_attributes / len(classes) if classes else 0
+ attribute_stats["attribute_types"] = attribute_types
+
+ return attribute_stats
+
+ def get_constructor_analysis(self) -> Dict[str, Any]:
+ """Analyze class constructors."""
+ classes = list(self.codebase.classes)
+ constructor_stats = {
+ "classes_with_constructor": 0,
+ "constructor_percentage": 0,
+ "avg_constructor_params": 0
+ }
+
+ if not classes:
+ return constructor_stats
+
+ classes_with_constructor = 0
+ total_constructor_params = 0
+
+ for cls in classes:
+ constructor = None
+
+ # Find constructor
+ for method in cls.methods:
+ if hasattr(method, "is_constructor") and method.is_constructor:
+ constructor = method
+ break
+
+ if constructor:
+ classes_with_constructor += 1
+ param_count = len(constructor.parameters) if hasattr(constructor, "parameters") else 0
+ total_constructor_params += param_count
+
+ constructor_stats["classes_with_constructor"] = classes_with_constructor
+ constructor_stats["constructor_percentage"] = classes_with_constructor / len(classes)
+ constructor_stats["avg_constructor_params"] = total_constructor_params / classes_with_constructor if classes_with_constructor else 0
+
+ return constructor_stats
+
+ def get_interface_implementation_verification(self) -> Dict[str, Any]:
+ """Verify interface implementations."""
+ classes = list(self.codebase.classes)
+ interfaces = list(self.codebase.interfaces)
+ implementation_stats = {
+ "total_interfaces": len(interfaces),
+ "classes_implementing_interfaces": 0,
+ "interface_implementations": {}
+ }
+
+ if not interfaces or not classes:
+ return implementation_stats
+
+ # Map interfaces to implementing classes
+ interface_implementations = {}
+
+ for interface in interfaces:
+ interface_name = interface.name
+ implementing_classes = []
+
+ for cls in classes:
+ if hasattr(cls, "parent_class_names") and interface_name in cls.parent_class_names:
+ implementing_classes.append(cls.name)
+
+ interface_implementations[interface_name] = implementing_classes
+
+ # Count classes implementing interfaces
+ classes_implementing = set()
+ for implementers in interface_implementations.values():
+ classes_implementing.update(implementers)
+
+ implementation_stats["classes_implementing_interfaces"] = len(classes_implementing)
+ implementation_stats["interface_implementations"] = interface_implementations
+
+ return implementation_stats
+
+ def get_access_modifier_usage(self) -> Dict[str, Any]:
+ """Analyze access modifier usage."""
+ symbols = list(self.codebase.symbols)
+ access_stats = {
+ "public": 0,
+ "private": 0,
+ "protected": 0,
+ "internal": 0,
+ "unknown": 0
+ }
+
+ for symbol in symbols:
+ if hasattr(symbol, "is_private") and symbol.is_private:
+ access_stats["private"] += 1
+ elif hasattr(symbol, "is_protected") and symbol.is_protected:
+ access_stats["protected"] += 1
+ elif hasattr(symbol, "is_internal") and symbol.is_internal:
+ access_stats["internal"] += 1
+ elif hasattr(symbol, "is_public") and symbol.is_public:
+ access_stats["public"] += 1
+ else:
+ access_stats["unknown"] += 1
+
+ return access_stats
+
+ #
+ # Code Quality Analysis Methods
+ #
+
+ def get_unused_functions(self) -> List[Dict[str, str]]:
+ """Get a list of unused functions."""
+ functions = list(self.codebase.functions)
+ unused_functions = []
+
+ for func in functions:
+ if hasattr(func, "call_sites") and len(func.call_sites) == 0:
+ # Skip special methods like __init__, __str__, etc.
+ if hasattr(func, "is_magic") and func.is_magic:
+ continue
+
+ # Skip entry points and main functions
+ if func.name in ["main", "__main__"]:
+ continue
+
+ unused_functions.append({
+ "name": func.name,
+ "file": func.file.file_path if hasattr(func, "file") else "Unknown"
+ })
+
+ return unused_functions
+
+ def get_unused_classes(self) -> List[Dict[str, str]]:
+ """Get a list of unused classes."""
+ classes = list(self.codebase.classes)
+ unused_classes = []
+
+ for cls in classes:
+ if hasattr(cls, "symbol_usages") and len(cls.symbol_usages) == 0:
+ unused_classes.append({
+ "name": cls.name,
+ "file": cls.file.file_path if hasattr(cls, "file") else "Unknown"
+ })
+
+ return unused_classes
+
+ def get_unused_variables(self) -> List[Dict[str, str]]:
+ """Get a list of unused variables."""
+ global_vars = list(self.codebase.global_vars)
+ unused_vars = []
+
+ for var in global_vars:
+ if hasattr(var, "symbol_usages") and len(var.symbol_usages) == 0:
+ unused_vars.append({
+ "name": var.name,
+ "file": var.file.file_path if hasattr(var, "file") else "Unknown"
+ })
+
+ return unused_vars
+
+ def get_unused_imports(self) -> List[Dict[str, str]]:
+ """Get a list of unused imports."""
+ files = list(self.codebase.files)
+ unused_imports = []
+
+ for file in files:
+ if file.is_binary:
+ continue
+
+ for imp in file.imports:
+ if hasattr(imp, "usages") and len(imp.usages) == 0:
+ unused_imports.append({
+ "file": file.file_path,
+ "import": imp.source
+ })
+
+ return unused_imports
+
+ def get_similar_function_detection(self) -> List[Dict[str, Any]]:
+ """Detect similar functions."""
+ functions = list(self.codebase.functions)
+ similar_functions = []
+
+ # Group functions by name
+ function_groups = {}
+
+ for func in functions:
+ name = func.name
+
+ if name in function_groups:
+ function_groups[name].append(func)
+ else:
+ function_groups[name] = [func]
+
+ # Find similar functions
+ for name, funcs in function_groups.items():
+ if len(funcs) > 1:
+ similar_functions.append({
+ "name": name,
+ "count": len(funcs),
+ "files": [func.file.file_path if hasattr(func, "file") else "Unknown" for func in funcs]
+ })
+
+ return similar_functions
+
+ def get_repeated_code_patterns(self) -> Dict[str, Any]:
+ """Detect repeated code patterns."""
+ functions = list(self.codebase.functions)
+
+ # This is a simplified implementation that looks for functions with similar structure
+ # A more advanced implementation would use code clone detection algorithms
+
+ # Group functions by length (in lines)
+ functions_by_length = {}
+
+ for func in functions:
+ func_source = func.source
+ func_lines = func_source.count('\n') + 1
+
+ if func_lines in functions_by_length:
+ functions_by_length[func_lines].append(func)
+ else:
+ functions_by_length[func_lines] = [func]
+
+ # Find potential code clones (functions with same length)
+ potential_clones = {}
+
+ for length, funcs in functions_by_length.items():
+ if len(funcs) > 1:
+ potential_clones[length] = [func.name for func in funcs]
+
+ return {
+ "potential_code_clones": potential_clones
+ }
+
+ def get_refactoring_opportunities(self) -> Dict[str, Any]:
+ """Identify refactoring opportunities."""
+ refactoring_opportunities = {
+ "long_functions": [],
+ "large_classes": [],
+ "high_coupling_files": [],
+ "low_cohesion_files": []
+ }
+
+ # Find long functions
+ functions = list(self.codebase.functions)
+ for func in functions:
+ func_source = func.source
+ func_lines = func_source.count('\n') + 1
+
+ if func_lines > 50: # Threshold for long functions
+ refactoring_opportunities["long_functions"].append({
+ "name": func.name,
+ "file": func.file.file_path if hasattr(func, "file") else "Unknown",
+ "lines": func_lines
+ })
+
+ # Find large classes
+ classes = list(self.codebase.classes)
+ for cls in classes:
+ methods = cls.methods if hasattr(cls, "methods") else []
+ attributes = cls.attributes if hasattr(cls, "attributes") else []
+
+ if len(methods) + len(attributes) > 20: # Threshold for large classes
+ refactoring_opportunities["large_classes"].append({
+ "name": cls.name,
+ "file": cls.file.file_path if hasattr(cls, "file") else "Unknown",
+ "methods": len(methods),
+ "attributes": len(attributes)
+ })
+
+ # Find high coupling files
+ files = list(self.codebase.files)
+ for file in files:
+ if file.is_binary:
+ continue
+
+ imports = file.imports
+ if len(imports) > 15: # Threshold for high coupling
+ refactoring_opportunities["high_coupling_files"].append({
+ "file": file.file_path,
+ "imports": len(imports)
+ })
+
+ # Find low cohesion files
+ cohesion_metrics = self.get_module_cohesion_analysis()
+ file_cohesion = cohesion_metrics.get("file_cohesion", {})
+
+ for file_path, cohesion in file_cohesion.items():
+ if cohesion < 0.3: # Threshold for low cohesion
+ refactoring_opportunities["low_cohesion_files"].append({
+ "file": file_path,
+ "cohesion": cohesion
+ })
+
+ return refactoring_opportunities
+
+ def calculate_cyclomatic_complexity(self) -> Dict[str, Any]:
+ """Calculate cyclomatic complexity for functions."""
+ functions = list(self.codebase.functions)
+ complexity_results = {
+ "avg_complexity": 0,
+ "max_complexity": 0,
+ "complexity_distribution": {
+ "low": 0, # 1-5
+ "moderate": 0, # 6-10
+ "high": 0, # 11-20
+ "very_high": 0 # > 20
+ },
+ "complex_functions": []
+ }
+
+ if not functions:
+ return complexity_results
+
+ total_complexity = 0
+ max_complexity = 0
+ complex_functions = []
+
+ for func in functions:
+ # A simple approximation of cyclomatic complexity
+ # In a real implementation, we would parse the AST and count decision points
+ source = func.source
+
+ # Count decision points
+ if_count = source.count("if ") + source.count("elif ")
+ for_count = source.count("for ")
+ while_count = source.count("while ")
+ case_count = source.count("case ") + source.count("switch ") + source.count("match ")
+ catch_count = source.count("catch ") + source.count("except ")
+ and_count = source.count(" && ") + source.count(" and ")
+ or_count = source.count(" || ") + source.count(" or ")
+
+ # Calculate complexity
+ complexity = 1 + if_count + for_count + while_count + case_count + catch_count + and_count + or_count
+
+ total_complexity += complexity
+ max_complexity = max(max_complexity, complexity)
+
+ # Categorize complexity
+ if complexity <= 5:
+ complexity_results["complexity_distribution"]["low"] += 1
+ elif complexity <= 10:
+ complexity_results["complexity_distribution"]["moderate"] += 1
+ elif complexity <= 20:
+ complexity_results["complexity_distribution"]["high"] += 1
+ else:
+ complexity_results["complexity_distribution"]["very_high"] += 1
+
+ # Track complex functions
+ if complexity > 10:
+ complex_functions.append({
+ "name": func.name,
+ "file": func.file.file_path if hasattr(func, "file") else "Unknown",
+ "complexity": complexity
+ })
+
+ complexity_results["avg_complexity"] = total_complexity / len(functions)
+ complexity_results["max_complexity"] = max_complexity
+ complexity_results["complex_functions"] = sorted(complex_functions, key=lambda x: x["complexity"], reverse=True)[:10] # Top 10 most complex
+
+ return complexity_results
+
+ def cc_rank(self) -> Dict[str, str]:
+ """Rank the codebase based on cyclomatic complexity."""
+ complexity_results = self.calculate_cyclomatic_complexity()
+ avg_complexity = complexity_results["avg_complexity"]
+
+ if avg_complexity < 5:
+ rank = "A"
+ description = "Excellent: Low complexity, highly maintainable code"
+ elif avg_complexity < 10:
+ rank = "B"
+ description = "Good: Moderate complexity, maintainable code"
+ elif avg_complexity < 15:
+ rank = "C"
+ description = "Fair: Moderate to high complexity, some maintenance challenges"
+ elif avg_complexity < 20:
+ rank = "D"
+ description = "Poor: High complexity, difficult to maintain"
+ else:
+ rank = "F"
+ description = "Very Poor: Very high complexity, extremely difficult to maintain"
+
+ return {
+ "rank": rank,
+ "description": description,
+ "avg_complexity": avg_complexity
+ }
+
+ def get_operators_and_operands(self) -> Dict[str, Any]:
+ """Get operators and operands for Halstead metrics."""
+ files = list(self.codebase.files)
+
+ # Define common operators
+ operators = ["+", "-", "*", "/", "%", "=", "==", "!=", "<", ">", "<=", ">=",
+ "&&", "||", "!", "&", "|", "^", "~", "<<", ">>", "++", "--",
+ "+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>="]
+
+ # Count operators and operands
+ operator_count = {}
+ operand_count = {}
+
+ for file in files:
+ if file.is_binary:
+ continue
+
+ content = file.content
+
+ # Count operators
+ for op in operators:
+ count = content.count(op)
+ if count > 0:
+ if op in operator_count:
+ operator_count[op] += count
+ else:
+ operator_count[op] = count
+
+ # Simplified operand counting (this is a rough approximation)
+ # In a real implementation, we would parse the AST and extract identifiers
+ words = re.findall(r'\b[a-zA-Z_][a-zA-Z0-9_]*\b', content)
+ for word in words:
+ if word not in ["if", "else", "for", "while", "return", "break", "continue",
+ "class", "def", "function", "import", "from", "as", "try",
+ "except", "finally", "with", "in", "is", "not", "and", "or"]:
+ if word in operand_count:
+ operand_count[word] += 1
+ else:
+ operand_count[word] = 1
+
+ return {
+ "unique_operators": len(operator_count),
+ "total_operators": sum(operator_count.values()),
+ "unique_operands": len(operand_count),
+ "total_operands": sum(operand_count.values()),
+ "top_operators": dict(sorted(operator_count.items(), key=lambda x: x[1], reverse=True)[:10]),
+ "top_operands": dict(sorted(operand_count.items(), key=lambda x: x[1], reverse=True)[:10])
+ }
+
+ def calculate_halstead_volume(self) -> Dict[str, float]:
+ """Calculate Halstead volume metrics."""
+ operators_and_operands = self.get_operators_and_operands()
+
+ n1 = operators_and_operands["unique_operators"]
+ n2 = operators_and_operands["unique_operands"]
+ N1 = operators_and_operands["total_operators"]
+ N2 = operators_and_operands["total_operands"]
+
+ # Calculate Halstead metrics
+ vocabulary = n1 + n2
+ length = N1 + N2
+ volume = length * math.log2(vocabulary) if vocabulary > 0 else 0
+ difficulty = (n1 / 2) * (N2 / n2) if n2 > 0 else 0
+ effort = volume * difficulty
+ time = effort / 18 # Time in seconds (18 is a constant from empirical studies)
+ bugs = volume / 3000 # Estimated bugs (3000 is a constant from empirical studies)
+
+ return {
+ "vocabulary": vocabulary,
+ "length": length,
+ "volume": volume,
+ "difficulty": difficulty,
+ "effort": effort,
+ "time": time, # in seconds
+ "bugs": bugs
+ }
+
+ def count_lines(self) -> Dict[str, int]:
+ """Count lines of code."""
+ files = list(self.codebase.files)
+
+ total_lines = 0
+ code_lines = 0
+ comment_lines = 0
+ blank_lines = 0
+
+ for file in files:
+ if file.is_binary:
+ continue
+
+ content = file.content
+ lines = content.split('\n')
+
+ total_lines += len(lines)
+
+ for line in lines:
+ line = line.strip()
+
+ if not line:
+ blank_lines += 1
+ elif line.startswith('#') or line.startswith('//') or line.startswith('/*') or line.startswith('*'):
+ comment_lines += 1
+ else:
+ code_lines += 1
+
+ return {
+ "total_lines": total_lines,
+ "code_lines": code_lines,
+ "comment_lines": comment_lines,
+ "blank_lines": blank_lines,
+ "comment_ratio": comment_lines / code_lines if code_lines > 0 else 0
+ }
+
+ def calculate_maintainability_index(self) -> Dict[str, float]:
+ """Calculate maintainability index."""
+ halstead = self.calculate_halstead_volume()
+ complexity = self.calculate_cyclomatic_complexity()
+ lines = self.count_lines()
+
+ # Calculate maintainability index
+ # MI = 171 - 5.2 * ln(V) - 0.23 * CC - 16.2 * ln(LOC)
+ volume = halstead["volume"]
+ avg_complexity = complexity["avg_complexity"]
+ loc = lines["code_lines"]
+
+ mi = 171 - 5.2 * math.log(volume) - 0.23 * avg_complexity - 16.2 * math.log(loc) if volume > 0 and loc > 0 else 0
+
+ # Normalize to 0-100 scale
+ normalized_mi = max(0, min(100, mi * 100 / 171))
+
+ return {
+ "maintainability_index": mi,
+ "normalized_maintainability_index": normalized_mi
+ }
+
+ def get_maintainability_rank(self) -> Dict[str, str]:
+ """Rank the codebase based on maintainability index."""
+ mi = self.calculate_maintainability_index()["normalized_maintainability_index"]
+
+ if mi >= 85:
+ rank = "A"
+ description = "Highly maintainable"
+ elif mi >= 65:
+ rank = "B"
+ description = "Maintainable"
+ elif mi >= 40:
+ rank = "C"
+ description = "Moderately maintainable"
+ elif mi >= 20:
+ rank = "D"
+ description = "Difficult to maintain"
+ else:
+ rank = "F"
+ description = "Very difficult to maintain"
+
+ return {
+ "rank": rank,
+ "description": description,
+ "maintainability_index": mi
+ }
+
+ def get_cognitive_complexity(self) -> Dict[str, Any]:
+ """Calculate cognitive complexity for functions."""
+ functions = list(self.codebase.functions)
+ complexity_results = {
+ "avg_complexity": 0,
+ "max_complexity": 0,
+ "complexity_distribution": {
+ "low": 0, # 0-5
+ "moderate": 0, # 6-10
+ "high": 0, # 11-20
+ "very_high": 0 # > 20
+ },
+ "complex_functions": []
+ }
+
+ if not functions:
+ return complexity_results
+
+ total_complexity = 0
+ max_complexity = 0
+ complex_functions = []
+
+ for func in functions:
+ # A simple approximation of cognitive complexity
+ # In a real implementation, we would parse the AST and analyze control flow
+ source = func.source
+
+ # Count decision points with nesting
+ nesting_level = 0
+ cognitive_complexity = 0
+
+ lines = source.split('\n')
+ for line in lines:
+ line = line.strip()
+
+ # Increase nesting level
+ if re.search(r'\b(if|for|while|switch|case|catch|try)\b', line):
+ cognitive_complexity += 1 + nesting_level
+ nesting_level += 1
+
+ # Decrease nesting level
+ if line.startswith('}') or line.endswith(':'):
+ nesting_level = max(0, nesting_level - 1)
+
+ # Add complexity for boolean operators
+ cognitive_complexity += line.count(" && ") + line.count(" and ")
+ cognitive_complexity += line.count(" || ") + line.count(" or ")
+
+ # Add complexity for jumps
+ if re.search(r'\b(break|continue|goto|return)\b', line):
+ cognitive_complexity += 1
+
+ total_complexity += cognitive_complexity
+ max_complexity = max(max_complexity, cognitive_complexity)
+
+ # Categorize complexity
+ if cognitive_complexity <= 5:
+ complexity_results["complexity_distribution"]["low"] += 1
+ elif cognitive_complexity <= 10:
+ complexity_results["complexity_distribution"]["moderate"] += 1
+ elif cognitive_complexity <= 20:
+ complexity_results["complexity_distribution"]["high"] += 1
+ else:
+ complexity_results["complexity_distribution"]["very_high"] += 1
+
+ # Track complex functions
+ if cognitive_complexity > 10:
+ complex_functions.append({
+ "name": func.name,
+ "file": func.file.file_path if hasattr(func, "file") else "Unknown",
+ "complexity": cognitive_complexity
+ })
+
+ complexity_results["avg_complexity"] = total_complexity / len(functions)
+ complexity_results["max_complexity"] = max_complexity
+ complexity_results["complex_functions"] = sorted(complex_functions, key=lambda x: x["complexity"], reverse=True)[:10] # Top 10 most complex
+
+ return complexity_results
+
+ def get_nesting_depth_analysis(self) -> Dict[str, Any]:
+ """Analyze nesting depth in functions."""
+ functions = list(self.codebase.functions)
+ nesting_results = {
+ "avg_max_nesting": 0,
+ "max_nesting": 0,
+ "nesting_distribution": {
+ "low": 0, # 0-2
+ "moderate": 0, # 3-4
+ "high": 0, # 5-6
+ "very_high": 0 # > 6
+ },
+ "deeply_nested_functions": []
+ }
+
+ if not functions:
+ return nesting_results
+
+ total_max_nesting = 0
+ max_nesting_overall = 0
+ deeply_nested_functions = []
+
+ for func in functions:
+ source = func.source
+ lines = source.split('\n')
+
+ max_nesting = 0
+ current_nesting = 0
+
+ for line in lines:
+ line = line.strip()
+
+ # Increase nesting level
+ if re.search(r'\b(if|for|while|switch|case|catch|try)\b', line) and not line.startswith('}'):
+ current_nesting += 1
+ max_nesting = max(max_nesting, current_nesting)
+
+ # Decrease nesting level
+ if line.startswith('}'):
+ current_nesting = max(0, current_nesting - 1)
+
+ total_max_nesting += max_nesting
+ max_nesting_overall = max(max_nesting_overall, max_nesting)
+
+ # Categorize nesting
+ if max_nesting <= 2:
+ nesting_results["nesting_distribution"]["low"] += 1
+ elif max_nesting <= 4:
+ nesting_results["nesting_distribution"]["moderate"] += 1
+ elif max_nesting <= 6:
+ nesting_results["nesting_distribution"]["high"] += 1
+ else:
+ nesting_results["nesting_distribution"]["very_high"] += 1
+
+ # Track deeply nested functions
+ if max_nesting > 4:
+ deeply_nested_functions.append({
+ "name": func.name,
+ "file": func.file.file_path if hasattr(func, "file") else "Unknown",
+ "max_nesting": max_nesting
+ })
+
+ nesting_results["avg_max_nesting"] = total_max_nesting / len(functions)
+ nesting_results["max_nesting"] = max_nesting_overall
+ nesting_results["deeply_nested_functions"] = sorted(deeply_nested_functions, key=lambda x: x["max_nesting"], reverse=True)[:10] # Top 10 most nested
+
+ return nesting_results
+
+ def get_function_size_metrics(self) -> Dict[str, Any]:
+ """Get function size metrics."""
+ functions = list(self.codebase.functions)
+ size_metrics = {
+ "avg_function_length": 0,
+ "max_function_length": 0,
+ "function_size_distribution": {
+ "small": 0, # < 10 lines
+ "medium": 0, # 10-30 lines
+ "large": 0, # 30-100 lines
+ "very_large": 0 # > 100 lines
+ },
+ "largest_functions": []
+ }
+
+ if not functions:
+ return size_metrics
+
+ total_length = 0
+ max_length = 0
+ largest_functions = []
+
+ for func in functions:
+ func_source = func.source
+ func_lines = func_source.count('\n') + 1
+
+ total_length += func_lines
+ max_length = max(max_length, func_lines)
+
+ # Categorize by size
+ if func_lines < 10:
+ size_metrics["function_size_distribution"]["small"] += 1
+ elif func_lines < 30:
+ size_metrics["function_size_distribution"]["medium"] += 1
+ elif func_lines < 100:
+ size_metrics["function_size_distribution"]["large"] += 1
+ else:
+ size_metrics["function_size_distribution"]["very_large"] += 1
+
+ # Track large functions
+ if func_lines > 30:
+ largest_functions.append({
+ "name": func.name,
+ "file": func.file.file_path if hasattr(func, "file") else "Unknown",
+ "lines": func_lines
+ })
+
+ size_metrics["avg_function_length"] = total_length / len(functions)
+ size_metrics["max_function_length"] = max_length
+ size_metrics["largest_functions"] = sorted(largest_functions, key=lambda x: x["lines"], reverse=True)[:10] # Top 10 largest
+
+ return size_metrics
+
+ #
+ # Visualization and Output Methods
+ #
+
+ def _generate_html_report(self, output_file: str) -> None:
+ """Generate an HTML report of the analysis results."""
+ if not output_file:
+ output_file = "codebase_analysis_report.html"
+
+ # Simple HTML template
+ html = f"""
+
+
+
+ Codebase Analysis Report
+
+
+
+ Codebase Analysis Report
+
+
Metadata
+
Repository: {self.results["metadata"]["repo_name"]}
+
Analysis Time: {self.results["metadata"]["analysis_time"]}
+
Language: {self.results["metadata"]["language"]}
+
+ """
+
+ # Add each category
+ for category, metrics in self.results["categories"].items():
+ html += f"""
+
+
{category.replace("_", " ").title()}
+ """
+
+ for metric_name, metric_value in metrics.items():
+ html += f"""
+
+
{metric_name.replace("_", " ").title()}
+
{json.dumps(metric_value, indent=2)}
+
+ """
+
+ html += "
"
+
+ html += """
+
+
+ """
+
+ with open(output_file, "w") as f:
+ f.write(html)
+
+ self.console.print(f"[bold green]HTML report saved to {output_file}[/bold green]")
+
+ def _print_console_report(self) -> None:
+ """Print a summary report to the console."""
+ self.console.print(f"[bold blue]Codebase Analysis Report for {self.results['metadata']['repo_name']}[/bold blue]")
+ self.console.print(f"[bold]Analysis Time:[/bold] {self.results['metadata']['analysis_time']}")
+ self.console.print(f"[bold]Language:[/bold] {self.results['metadata']['language']}")
+
+ for category, metrics in self.results["categories"].items():
+ self.console.print(f"\n[bold green]{category.replace('_', ' ').title()}[/bold green]")
+
+ for metric_name, metric_value in metrics.items():
+ self.console.print(f"[bold]{metric_name.replace('_', ' ').title()}:[/bold]")
+
+ if isinstance(metric_value, dict):
+ table = Table(show_header=True)
+ table.add_column("Key")
+ table.add_column("Value")
+
+ for k, v in metric_value.items():
+ if isinstance(v, dict):
+ table.add_row(k, str(v))
+ else:
+ table.add_row(str(k), str(v))
+
+ self.console.print(table)
+ elif isinstance(metric_value, list):
+ if len(metric_value) > 0 and isinstance(metric_value[0], dict):
+ if len(metric_value) > 0:
+ table = Table(show_header=True)
+ for key in metric_value[0].keys():
+ table.add_column(key)
+
+ for item in metric_value[:10]: # Show only first 10 items
+ table.add_row(*[str(v) for v in item.values()])
+
+ self.console.print(table)
+ if len(metric_value) > 10:
+ self.console.print(f"... and {len(metric_value) - 10} more items")
+ else:
+ self.console.print(str(metric_value))
+ else:
+ self.console.print(str(metric_value))
+
+ def get_monthly_commits(self) -> Dict[str, int]:
+ """Get the number of commits per month."""
+ try:
+ # Get commit history
+ commits = list(self.codebase.github.repo.get_commits())
+
+ # Group commits by month
+ commits_by_month = {}
+
+ for commit in commits:
+ date = commit.commit.author.date
+ month_key = f"{date.year}-{date.month:02d}"
+
+ if month_key in commits_by_month:
+ commits_by_month[month_key] += 1
+ else:
+ commits_by_month[month_key] = 1
+
+ # Sort by month
+ sorted_commits = dict(sorted(commits_by_month.items()))
+
+ return sorted_commits
+ except Exception as e:
+ return {"error": str(e)}
+
+
+def main():
+ """Main entry point for the codebase analyzer."""
+ parser = argparse.ArgumentParser(description="Comprehensive Codebase Analyzer")
+
+ # Repository source
+ source_group = parser.add_mutually_exclusive_group(required=True)
+ source_group.add_argument("--repo-url", help="URL of the repository to analyze")
+ source_group.add_argument("--repo-path", help="Local path to the repository to analyze")
+
+ # Analysis options
+ parser.add_argument("--language", help="Programming language of the codebase (auto-detected if not provided)")
+ parser.add_argument("--categories", nargs="+", help="Categories to analyze (default: all)")
+
+ # Output options
+ parser.add_argument("--output-format", choices=["json", "html", "console"], default="console", help="Output format")
+ parser.add_argument("--output-file", help="Path to the output file")
+
+ args = parser.parse_args()
+
+ try:
+ # Initialize the analyzer
+ analyzer = CodebaseAnalyzer(
+ repo_url=args.repo_url,
+ repo_path=args.repo_path,
+ language=args.language
+ )
+
+ # Perform the analysis
+ results = analyzer.analyze(
+ categories=args.categories,
+ output_format=args.output_format,
+ output_file=args.output_file
+ )
+
+ # Print success message
+ if args.output_format == "json" and args.output_file:
+ print(f"Analysis results saved to {args.output_file}")
+ elif args.output_format == "html":
+ print(f"HTML report saved to {args.output_file or 'codebase_analysis_report.html'}")
+
+ except Exception as e:
+ print(f"Error: {e}")
+ import traceback
+ traceback.print_exc()
+ sys.exit(1)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/codegen-on-oss/codegen_on_oss/errors.py b/codegen-on-oss/codegen_on_oss/errors.py
new file mode 100644
index 000000000..7e00dc085
--- /dev/null
+++ b/codegen-on-oss/codegen_on_oss/errors.py
@@ -0,0 +1,6 @@
+class ParseRunError(Exception):
+ pass
+
+
+class PostValidationError(ParseRunError):
+ pass
diff --git a/codegen-on-oss/codegen_on_oss/metrics.py b/codegen-on-oss/codegen_on_oss/metrics.py
new file mode 100644
index 000000000..d77b4e686
--- /dev/null
+++ b/codegen-on-oss/codegen_on_oss/metrics.py
@@ -0,0 +1,198 @@
+import json
+import os
+import time
+from collections.abc import Generator
+from contextlib import contextmanager
+from importlib.metadata import version
+from typing import TYPE_CHECKING, Any
+
+import psutil
+
+from codegen_on_oss.errors import ParseRunError
+from codegen_on_oss.outputs.base import BaseOutput
+
+if TYPE_CHECKING:
+ # Logger only available in type checking context.
+ from loguru import Logger # type: ignore[attr-defined]
+
+
+codegen_version = str(version("codegen"))
+
+
+class MetricsProfiler:
+ """
+ A helper to record performance metrics across multiple profiles and write them to a CSV.
+
+ Usage:
+
+ metrics_profiler = MetricsProfiler(output_path="metrics.csv")
+
+ with metrics_profiler.start_profiler(name="profile_1", language="python") as profile:
+ # Some code block...
+ profile.measure("step 1")
+ # More code...
+ profile.measure("step 2")
+
+ # The CSV "metrics.csv" now contains the measurements for profile_1.
+ """
+
+ def __init__(self, output: BaseOutput):
+ self.output = output
+
+ @contextmanager
+ def start_profiler(
+ self, name: str, revision: str, language: str | None, logger: "Logger"
+ ) -> Generator["MetricsProfile", None, None]:
+ """
+ Starts a new profiling session for a given profile name.
+ Returns a MetricsProfile instance that you can use to mark measurements.
+ """
+ profile = MetricsProfile(name, revision, language, self.output, logger)
+ error_msg: str | None = None
+ try:
+ yield profile
+ except ParseRunError as e:
+ logger.error(f"Repository: {name} {e.args[0]}") # noqa: TRY400
+ error_msg = e.args[0]
+ except Exception as e:
+ logger.exception(f"Repository: {name}")
+ error_msg = f"Unhandled Exception {type(e)}"
+
+ finally:
+ profile.finish(error=error_msg)
+
+ @classmethod
+ def fields(cls) -> list[str]:
+ return [
+ "repo",
+ "revision",
+ "language",
+ "action",
+ "codegen_version",
+ "delta_time",
+ "cumulative_time",
+ "cpu_time",
+ "memory_usage",
+ "memory_delta",
+ "error",
+ ]
+
+
+class MetricsProfile:
+ """
+ Context-managed profile that records measurements at each call to `measure()`.
+ It tracks the wall-clock duration, CPU time, and memory usage (with delta) at the time of the call.
+ Upon exiting the context, it also writes all collected metrics, including the total time,
+ to a CSV file.
+ """
+
+ if TYPE_CHECKING:
+ logger: "Logger"
+ measurements: list[dict[str, Any]]
+
+ def __init__(
+ self,
+ name: str,
+ revision: str,
+ language: str,
+ output: BaseOutput,
+ logger: "Logger",
+ ):
+ self.name = name
+ self.revision = revision
+ self.language = language
+ self.output = output
+ self.logger = logger
+
+ # Capture initial metrics.
+ self.start_time = time.perf_counter()
+ self.start_cpu = time.process_time()
+ self.start_mem = int(
+ psutil.Process(os.getpid()).memory_info().rss / (1024 * 1024)
+ )
+
+ # For delta calculations, store the last measurement values.
+ self.last_measure_time = self.start_time
+ self.last_measure_mem = self.start_mem
+
+ def reset_checkpoint(self):
+ # Update last measurement time and memory for the next delta.
+ self.last_measure_time = time.perf_counter()
+ self.last_measure_mem = self.start_mem
+
+ def measure(self, action_name: str):
+ """
+ Records a measurement for the given step. The measurement includes:
+ - Delta wall-clock time since the last measurement or the start,
+ - Cumulative wall-clock time since the start,
+ - The current CPU usage of the process (using time.process_time()),
+ - The current memory usage (RSS in bytes),
+ - The memory delta (difference from the previous measurement).
+ """
+ current_time = time.perf_counter()
+ current_cpu = float(time.process_time())
+ current_mem = int(psutil.Process(os.getpid()).memory_info().rss / (1024 * 1024))
+
+ # Calculate time deltas.
+ delta_time = current_time - self.last_measure_time
+ cumulative_time = current_time - self.start_time
+
+ # Calculate memory delta.
+ memory_delta = current_mem - self.last_measure_mem
+
+ # Record the measurement.
+ measurement = {
+ "repo": self.name,
+ "revision": self.revision,
+ "codegen_version": codegen_version,
+ "action": action_name,
+ "language": self.language,
+ "delta_time": delta_time,
+ "cumulative_time": cumulative_time,
+ "cpu_time": current_cpu, # CPU usage at this point.
+ "memory_usage": current_mem,
+ "memory_delta": memory_delta,
+ "error": None,
+ }
+ self.write_output(measurement)
+
+ # Update last measurement time and memory for the next delta.
+ self.last_measure_time = current_time
+ self.last_measure_mem = current_mem
+
+ def finish(self, error: str | None = None):
+ """
+ Called automatically when the profiling context is exited.
+ This method records a final measurement (for the total duration) and
+ writes all collected metrics to the CSV file.
+ """
+ finish_time = time.perf_counter()
+ finish_cpu = float(time.process_time())
+ finish_mem = int(psutil.Process(os.getpid()).memory_info().rss / (1024 * 1024))
+
+ total_duration = finish_time - self.start_time
+
+ # Calculate final memory delta.
+ memory_delta = finish_mem - self.last_measure_mem
+
+ # Record the overall profile measurement.
+ self.write_output({
+ "repo": self.name,
+ "revision": self.revision,
+ "codegen_version": codegen_version,
+ "language": self.language,
+ "action": "total_parse",
+ "delta_time": total_duration,
+ "cumulative_time": total_duration,
+ "cpu_time": finish_cpu,
+ "memory_usage": finish_mem,
+ "memory_delta": memory_delta,
+ "error": error,
+ })
+
+ def write_output(self, measurement: dict[str, Any]):
+ """
+ Writes all measurements to the CSV file using CSVOutput.
+ """
+ self.logger.info(json.dumps(measurement, indent=4))
+ self.output.write_output(measurement)
diff --git a/codegen-on-oss/codegen_on_oss/outputs/__init__.py b/codegen-on-oss/codegen_on_oss/outputs/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/codegen-on-oss/codegen_on_oss/outputs/base.py b/codegen-on-oss/codegen_on_oss/outputs/base.py
new file mode 100644
index 000000000..d984fe45e
--- /dev/null
+++ b/codegen-on-oss/codegen_on_oss/outputs/base.py
@@ -0,0 +1,16 @@
+import json
+from typing import Any
+
+from loguru import logger
+
+
+class BaseOutput:
+ """
+ BaseOutput is a class that defines the interface for all output classes.
+ """
+
+ def __init__(self, fields: list[str]):
+ self.fields = fields
+
+ def write_output(self, value: dict[str, Any]):
+ logger.info(json.dumps(value, indent=4))
diff --git a/codegen-on-oss/codegen_on_oss/outputs/csv_output.py b/codegen-on-oss/codegen_on_oss/outputs/csv_output.py
new file mode 100644
index 000000000..09bcce82f
--- /dev/null
+++ b/codegen-on-oss/codegen_on_oss/outputs/csv_output.py
@@ -0,0 +1,30 @@
+import csv
+import os
+from pathlib import Path
+from typing import Any
+
+from codegen_on_oss.outputs.base import BaseOutput
+
+
+class CSVOutput(BaseOutput):
+ """
+ CSVOutput is a class that writes output to a CSV file.
+ """
+
+ def __init__(self, fields: list[str], output_path: str):
+ super().__init__(fields)
+ self.output_path = output_path
+
+ def write_output(self, value: dict[str, Any]):
+ """
+ Writes a dictionary to a CSV file. If the file does not exist, it creates it and writes headers; otherwise, it appends.
+ """
+ file_exists = os.path.isfile(self.output_path)
+ if not file_exists:
+ Path(self.output_path).parent.mkdir(parents=True, exist_ok=True)
+
+ with open(self.output_path, mode="a", newline="") as csv_file:
+ writer = csv.DictWriter(csv_file, fieldnames=self.fields)
+ if not file_exists:
+ writer.writeheader()
+ writer.writerow(value)
diff --git a/codegen-on-oss/codegen_on_oss/outputs/sql_output.py b/codegen-on-oss/codegen_on_oss/outputs/sql_output.py
new file mode 100644
index 000000000..e3fae129b
--- /dev/null
+++ b/codegen-on-oss/codegen_on_oss/outputs/sql_output.py
@@ -0,0 +1,150 @@
+from typing import Any
+
+from pydantic import computed_field
+from pydantic_settings import BaseSettings, SettingsConfigDict
+from sqlalchemy import Float, Integer, String, UniqueConstraint
+from sqlalchemy.dialects.postgresql import insert
+from sqlalchemy.engine import create_engine
+from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, sessionmaker
+
+from .base import BaseOutput
+
+
+class Base(DeclarativeBase):
+ pass
+
+
+class SQLSettings(BaseSettings):
+ model_config = SettingsConfigDict(env_prefix="POSTGRESQL_")
+ host: str = "localhost"
+ port: int = 5432
+ user: str = "postgres"
+ password: str = "postgres" # noqa: S105
+ database: str = "postgres"
+ dialect: str = "postgresql"
+
+ @computed_field
+ def url(self) -> str:
+ return f"{self.dialect}://{self.user}:{self.password}@{self.host}:{self.port}/{self.database}"
+
+
+def get_session_maker(settings: SQLSettings):
+ engine = create_engine(settings.url)
+ return sessionmaker(bind=engine)
+
+
+class ParseMetrics(Base):
+ __tablename__ = "parse_metrics"
+
+ id: Mapped[int] = mapped_column(Integer, primary_key=True)
+ repo: Mapped[str] = mapped_column(String, index=True)
+ revision: Mapped[str] = mapped_column(String, index=True)
+ language: Mapped[str] = mapped_column(String, index=True)
+ action: Mapped[str] = mapped_column(String, index=True)
+ codegen_version: Mapped[str] = mapped_column(String, index=True)
+ delta_time: Mapped[float] = mapped_column(Float, index=True)
+ cumulative_time: Mapped[float] = mapped_column(Float, index=True)
+ cpu_time: Mapped[float] = mapped_column(Float, index=True)
+ memory_usage: Mapped[int] = mapped_column(Integer, index=True)
+ memory_delta: Mapped[int] = mapped_column(Integer, index=True)
+ error: Mapped[str] = mapped_column(String, index=True)
+ modal_function_call_id: Mapped[str] = mapped_column(String)
+
+ __table_args__ = (
+ UniqueConstraint(
+ "repo",
+ "revision",
+ "action",
+ "codegen_version",
+ name="uq_repo_revision_action_codegen_version",
+ ),
+ )
+
+
+class SWEBenchResult(Base):
+ __tablename__ = "swebench_output"
+
+ id: Mapped[int] = mapped_column(primary_key=True)
+ codegen_version: Mapped[str] = mapped_column(index=True)
+ submitted: Mapped[int]
+ completed_instances: Mapped[int]
+ resolved_instances: Mapped[int]
+ unresolved_instances: Mapped[int]
+ empty_patches: Mapped[int]
+ error_instances: Mapped[int]
+
+
+class ParseMetricsSQLOutput(BaseOutput):
+ extras: dict[str, Any]
+
+ def __init__(self, modal_function_call_id: str):
+ super().__init__(
+ fields=[
+ "repo",
+ "revision",
+ "action",
+ "codegen_version",
+ "delta_time",
+ "cumulative_time",
+ "cpu_time",
+ "memory_usage",
+ "memory_delta",
+ "error",
+ "modal_function_call_id",
+ ]
+ )
+ self.modal_function_call_id = modal_function_call_id
+ settings = SQLSettings()
+ self.session_maker = get_session_maker(settings)
+
+ def write_output(self, value: dict[str, Any]):
+ with self.session_maker() as session:
+ stmt = insert(ParseMetrics).values(
+ **value, modal_function_call_id=self.modal_function_call_id
+ )
+ stmt = stmt.on_conflict_do_update(
+ index_elements=[
+ ParseMetrics.repo,
+ ParseMetrics.revision,
+ ParseMetrics.action,
+ ParseMetrics.codegen_version,
+ ],
+ set_={
+ k: v
+ for k, v in value.items()
+ if k
+ not in (
+ "repo",
+ "revision",
+ "action",
+ "codegen_version",
+ "id",
+ )
+ },
+ )
+ session.execute(stmt)
+ session.commit()
+
+
+class SWEBenchSQLOutput(BaseOutput):
+ def __init__(self, modal_function_call_id: str):
+ self.modal_function_call_id = modal_function_call_id
+ settings = SQLSettings()
+ self.session_maker = get_session_maker(settings)
+ super().__init__(
+ fields=[
+ "instance_id",
+ "modal_function_call_id",
+ "errored",
+ "output",
+ "report",
+ ]
+ )
+
+ def write_output(self, value: dict[str, Any]):
+ with self.session_maker() as session:
+ stmt = insert(SWEBenchResult).values(
+ **value, modal_function_call_id=self.modal_function_call_id
+ )
+ session.execute(stmt)
+ session.commit()
diff --git a/codegen-on-oss/codegen_on_oss/parser.py b/codegen-on-oss/codegen_on_oss/parser.py
new file mode 100644
index 000000000..46ee45277
--- /dev/null
+++ b/codegen-on-oss/codegen_on_oss/parser.py
@@ -0,0 +1,86 @@
+import gc
+import sys
+from pathlib import Path
+from typing import TYPE_CHECKING
+from urllib.parse import urlparse
+
+from codegen import Codebase
+from codegen.sdk.codebase.validation import (
+ PostInitValidationStatus,
+ post_init_validation,
+)
+from codegen.sdk.extensions.utils import uncache_all
+from loguru import logger
+
+from codegen_on_oss.errors import PostValidationError
+from codegen_on_oss.metrics import MetricsProfiler
+
+if TYPE_CHECKING:
+ from codegen.sdk.codebase.config import ProjectConfig
+
+
+class CodegenParser:
+ if TYPE_CHECKING:
+ repo_dir: Path
+ metrics_profiler: MetricsProfiler
+
+ def __init__(self, repo_dir: Path, metrics_profiler: MetricsProfiler):
+ self.repo_dir = repo_dir
+ self.repo_dir.mkdir(parents=True, exist_ok=True)
+ self.metrics_profiler = metrics_profiler
+ sys.setrecursionlimit(10000000)
+
+ def parse(
+ self, url: str, language: str | None = None, commit_hash: str | None = None
+ ):
+ """
+ Parse the repository at the given URL. MetricsProfiler is used to profile the parse and
+ post_init_validation.
+
+ Args:
+ url (str): The URL of the repository to parse.
+ commit_hash (str | None): The commit hash to parse. If None, the head commit will be used.
+
+ """
+ repo_name = urlparse(url).path.removeprefix("/").removesuffix(".git")
+ repo_dest_path = Path(*repo_name.split("/"))
+ repo_dest_path = self.repo_dir / repo_dest_path
+ repo_logger = logger.bind(repo_name=repo_name)
+
+ self.gc()
+
+ with self.metrics_profiler.start_profiler(
+ name=repo_name, revision=commit_hash, language=language, logger=repo_logger
+ ) as profile:
+ # Awkward design here is due to adapting to using Codebase.from_repo() and parsing done in __init__.
+ # May want to consider __init__ with parsed state from a separate input handling / parser class.
+ class ProfiledCodebase(Codebase):
+ def __init__(self, *args, projects: "list[ProjectConfig]", **kwargs):
+ # Since Codebase is performing git ops, we need to extract commit if it wasn't explicitly provided.
+ profile.revision = (
+ profile.revision
+ or projects[
+ 0
+ ].repo_operator.head_commit # assume projects is not empty
+ )
+ # from_repo would have performed any repo initialization necessary
+ # It could pull or use cached
+ profile.reset_checkpoint()
+ super().__init__(*args, projects=projects, **kwargs)
+ profile.language = profile.language or str(self.language).lower()
+ profile.measure("codebase_parse")
+ validation_status = post_init_validation(self)
+
+ profile.measure("post_init_validation")
+ if validation_status is PostInitValidationStatus.SUCCESS:
+ return
+ else:
+ raise PostValidationError(validation_status)
+
+ ProfiledCodebase.from_repo(
+ repo_name, tmp_dir=str(self.repo_dir.absolute()), commit=commit_hash
+ )
+
+ def gc(self):
+ uncache_all()
+ gc.collect()
diff --git a/codegen-on-oss/codegen_on_oss/sources/__init__.py b/codegen-on-oss/codegen_on_oss/sources/__init__.py
new file mode 100644
index 000000000..3b5cbffd6
--- /dev/null
+++ b/codegen-on-oss/codegen_on_oss/sources/__init__.py
@@ -0,0 +1,16 @@
+from .base import RepoSource, SourceSettings, all_sources
+from .csv_source import CSVInputSettings, CSVInputSource
+from .github_source import GithubSettings, GithubSource
+from .single_source import SingleSettings, SingleSource
+
+__all__ = [
+ "CSVInputSettings",
+ "CSVInputSource",
+ "GithubSettings",
+ "GithubSource",
+ "RepoSource",
+ "SingleSettings",
+ "SingleSource",
+ "SourceSettings",
+ "all_sources",
+]
diff --git a/codegen-on-oss/codegen_on_oss/sources/base.py b/codegen-on-oss/codegen_on_oss/sources/base.py
new file mode 100644
index 000000000..48c6e89fd
--- /dev/null
+++ b/codegen-on-oss/codegen_on_oss/sources/base.py
@@ -0,0 +1,65 @@
+from collections.abc import Iterator
+from typing import TYPE_CHECKING, ClassVar, Generic, TypeVar
+
+from pydantic_settings import BaseSettings, SettingsConfigDict
+
+
+class SourceSettings(BaseSettings):
+ """
+ SourceSettings is a class that contains the settings for a source.
+ """
+
+ model_config = SettingsConfigDict(env_prefix="SOURCE_")
+ num_repos: int = 50
+
+
+SettingsType = TypeVar("SettingsType", bound=SourceSettings)
+
+all_sources: dict[str, type["RepoSource"]] = {}
+
+
+class DuplicateSource(ValueError):
+ """
+ DuplicateSource is an error that occurs when a source type is defined twice.
+ """
+
+ def __init__(self, source_type: str) -> None:
+ super().__init__(f"Source type {source_type} already exists")
+
+
+class RepoSource(Generic[SettingsType]):
+ """
+ RepoSource is a class that contains the configuration for a source.
+ """
+
+ source_type: ClassVar[str]
+ settings_cls: ClassVar[type[SourceSettings]]
+
+ if TYPE_CHECKING:
+ settings: SourceSettings
+
+ def __init_subclass__(cls) -> None:
+ if not hasattr(cls, "source_type"):
+ raise NotImplementedError("source_type must be defined")
+
+ if not hasattr(cls, "settings_cls"):
+ raise NotImplementedError("settings_cls must be defined")
+
+ if cls.source_type in all_sources:
+ raise DuplicateSource(cls.source_type)
+ all_sources[cls.source_type] = cls
+
+ def __init__(self, settings: SourceSettings | None = None) -> None:
+ self.settings = settings or self.settings_cls()
+
+ @classmethod
+ def from_source_type(
+ cls, source_type: str, settings: SourceSettings | None = None
+ ) -> "RepoSource":
+ return all_sources[source_type](settings)
+
+ def __iter__(self) -> Iterator[tuple[str, str | None]]:
+ """
+ Yields URL and optional commit hash of repositories.
+ """
+ raise NotImplementedError
diff --git a/codegen-on-oss/codegen_on_oss/sources/csv_source.py b/codegen-on-oss/codegen_on_oss/sources/csv_source.py
new file mode 100644
index 000000000..14c003ac3
--- /dev/null
+++ b/codegen-on-oss/codegen_on_oss/sources/csv_source.py
@@ -0,0 +1,39 @@
+import csv
+from collections.abc import Iterator
+from pathlib import Path
+
+from pydantic import field_validator
+
+from .base import RepoSource, SourceSettings
+
+
+class CSVInputSettings(SourceSettings, env_prefix="CSV_"):
+ """
+ CSVInputSettings is a class that contains the settings for a CSVInputSource.
+ """
+
+ file_path: Path = Path("input.csv")
+
+ @field_validator("file_path", mode="after")
+ def validate_file_path(cls, v):
+ if not v.exists():
+ msg = f"File {v} does not exist"
+ raise ValueError(msg)
+ return v
+
+
+class CSVInputSource(RepoSource):
+ """
+ CSVInputSource is a source that reads URLs from a CSV file.
+ """
+
+ source_type = "csv"
+ settings_cls = CSVInputSettings
+
+ def __iter__(self) -> Iterator[tuple[str, str | None]]:
+ with open(self.settings.file_path) as f:
+ reader = csv.DictReader(f, fieldnames=["url", "commit_hash"])
+ next(reader)
+
+ for row in reader:
+ yield row["url"], row.get("commit_hash") or None
diff --git a/codegen-on-oss/codegen_on_oss/sources/github_source.py b/codegen-on-oss/codegen_on_oss/sources/github_source.py
new file mode 100644
index 000000000..7c9f3b3a6
--- /dev/null
+++ b/codegen-on-oss/codegen_on_oss/sources/github_source.py
@@ -0,0 +1,59 @@
+from collections.abc import Iterator
+from typing import TYPE_CHECKING, ClassVar, Literal
+
+from github import Auth, Github # nosemgrep
+
+from .base import RepoSource, SourceSettings
+
+
+class GithubSettings(SourceSettings, env_prefix="GITHUB_"):
+ """
+ Settings for the Github source.
+ """
+
+ language: Literal["python", "typescript"] = "python"
+ heuristic: Literal[
+ "stars",
+ "forks",
+ "updated",
+ # "watchers",
+ # "contributors",
+ # "commit_activity",
+ # "issues",
+ # "dependency",
+ ] = "stars"
+ token: str | None = None
+ num_repos: int = 50
+
+
+class GithubSource(RepoSource[GithubSettings]):
+ """
+ Source for Github repositories via Github Search API
+ """
+
+ if TYPE_CHECKING:
+ github_client: Github
+ settings: GithubSettings
+
+ source_type: ClassVar[str] = "github"
+ settings_cls: ClassVar[type[GithubSettings]] = GithubSettings
+
+ def __init__(self, *args, **kwargs) -> None:
+ super().__init__(*args, **kwargs)
+ if self.settings.token is None:
+ self.github_client = Github()
+ else:
+ self.github_client = Github(auth=Auth.Token(self.settings.token))
+
+ def __iter__(self) -> Iterator[tuple[str, str | None]]:
+ repositories = self.github_client.search_repositories(
+ query=f"language:{self.settings.language}",
+ sort=self.settings.heuristic,
+ order="desc",
+ )
+
+ for idx, repository in enumerate(repositories):
+ if idx >= self.settings.num_repos:
+ break
+ commit = repository.get_commits()[0]
+ yield repository.clone_url, commit.sha
diff --git a/codegen-on-oss/codegen_on_oss/sources/single_source.py b/codegen-on-oss/codegen_on_oss/sources/single_source.py
new file mode 100644
index 000000000..f017b51fe
--- /dev/null
+++ b/codegen-on-oss/codegen_on_oss/sources/single_source.py
@@ -0,0 +1,31 @@
+from collections.abc import Iterator
+from typing import TYPE_CHECKING, ClassVar
+
+from github import Github # nosemgrep
+
+from .base import RepoSource, SourceSettings
+
+
+class SingleSettings(SourceSettings, env_prefix="SINGLE_"):
+ """
+ Settings for the Single source.
+ """
+
+ url: str
+ commit: str | None = None
+
+
+class SingleSource(RepoSource[SingleSettings]):
+ """
+ Source for a single repository.
+ """
+
+ if TYPE_CHECKING:
+ github_client: Github
+ settings: SingleSettings
+
+ source_type: ClassVar[str] = "single"
+ settings_cls: ClassVar[type[SingleSettings]] = SingleSettings
+
+ def __iter__(self) -> Iterator[tuple[str, str | None]]:
+ yield self.settings.url, self.settings.commit
diff --git a/codegen-on-oss/docker-compose.yaml b/codegen-on-oss/docker-compose.yaml
new file mode 100644
index 000000000..6abf28f70
--- /dev/null
+++ b/codegen-on-oss/docker-compose.yaml
@@ -0,0 +1,9 @@
+services:
+ codegen-benchmark:
+ image: codegen-benchmark
+ build: .
+ env_file:
+ - .env
+ command: ["tail", "-f", "/dev/null"]
+ volumes:
+ - .:/app
diff --git a/codegen-on-oss/input.csv b/codegen-on-oss/input.csv
new file mode 100644
index 000000000..f1c4e9719
--- /dev/null
+++ b/codegen-on-oss/input.csv
@@ -0,0 +1,2 @@
+repo_url, commit_hash
+https://github.com/JohnSnowLabs/spark-nlp.git,7d2bed7647bec3878362a4b7ebbe43befeabe2ba
diff --git a/codegen-on-oss/modal_run.py b/codegen-on-oss/modal_run.py
new file mode 100644
index 000000000..26c87f8f1
--- /dev/null
+++ b/codegen-on-oss/modal_run.py
@@ -0,0 +1,151 @@
+import os
+import sys
+from pathlib import Path
+
+import modal
+from loguru import logger
+
+from codegen_on_oss.bucket_store import BucketStore
+from codegen_on_oss.cache import cachedir
+from codegen_on_oss.metrics import MetricsProfiler
+from codegen_on_oss.parser import CodegenParser
+from codegen_on_oss.sources import RepoSource
+
+parse_app = modal.App("codegen-oss-parse")
+
+
+codegen_repo_volume = modal.Volume.from_name(
+ os.getenv("CODEGEN_MODAL_REPO_VOLUME", "codegen-oss-repo-volume"),
+ create_if_missing=True,
+)
+
+
+codegen_input_volume = modal.Volume.from_name(
+ os.getenv("CODEGEN_MODAL_INPUT_VOLUME", "codegen-oss-input-volume"),
+ create_if_missing=True,
+)
+
+try:
+ aws_secrets = modal.Secret.from_name(
+ os.getenv("CODEGEN_MODAL_SECRET_NAME", "codegen-oss-bucket-credentials")
+ )
+except modal.exception.NotFoundError:
+ if Path(".env").exists():
+ aws_secrets = modal.Secret.from_dotenv()
+ else:
+ aws_secrets = modal.Secret.from_dict({
+ "AWS_ACCESS_KEY_ID": os.getenv("AWS_ACCESS_KEY_ID"),
+ "AWS_SECRET_ACCESS_KEY": os.getenv("AWS_SECRET_ACCESS_KEY"),
+ "BUCKET_NAME": os.getenv("BUCKET_NAME"),
+ "GITHUB_TOKEN": os.getenv("GITHUB_TOKEN"),
+ })
+
+
+@parse_app.function(
+ cpu=4,
+ memory=16384,
+ timeout=3600 * 8,
+ secrets=[aws_secrets],
+ volumes={
+ str(cachedir.absolute()): codegen_repo_volume,
+ "/app/inputs": codegen_input_volume,
+ },
+ image=modal.Image.debian_slim(python_version="3.13")
+ .pip_install("uv")
+ .apt_install("git") # required by codegen sdk
+ .workdir("/app")
+ .add_local_file("uv.lock", remote_path="/app/uv.lock", copy=True)
+ .add_local_file("pyproject.toml", remote_path="/app/pyproject.toml", copy=True)
+ .run_commands("uv sync --frozen --no-install-project")
+ .env({"PATH": "/app/.venv/bin:$PATH"})
+ .add_local_python_source("codegen_on_oss")
+ .add_local_dir("codegen_on_oss", remote_path="/app/codegen_on_oss"),
+)
+def parse_repo_on_modal(
+ source: str,
+ env: dict[str, str],
+ log_output_path: str = "output.logs",
+ metrics_output_path: str = "metrics.csv",
+):
+ """
+ Parse repositories on Modal.
+
+ Args:
+ source: The source of the repositories to parse.
+ env: The environment variables to use.
+ log_output_path: The path to the log file.
+ metrics_output_path: The path to the metrics file.
+ """
+ os.environ.update(env)
+
+ logger.add(
+ log_output_path,
+ format="{time: HH:mm:ss} {level} {message}",
+ level="INFO",
+ )
+ logger.add(sys.stdout, format="{time: HH:mm:ss} {level} {message}", level="DEBUG")
+
+ repo_source = RepoSource.from_source_type(source)
+ metrics_profiler = MetricsProfiler(metrics_output_path)
+
+ parser = CodegenParser(Path(cachedir) / "repositories", metrics_profiler)
+ for repo_url, commit_hash in repo_source:
+ # Refresh any updating repo data from other instances
+ codegen_repo_volume.reload()
+ try:
+ parser.parse(repo_url, commit_hash)
+ except Exception as e:
+ logger.exception(f"Error parsing repository {repo_url}: {e}")
+ finally:
+ # Commit any cache changes to the repo volume
+ codegen_repo_volume.commit()
+
+ store = BucketStore(bucket_name=os.getenv("BUCKET_NAME", "codegen-oss-parse"))
+ log_key = store.upload_file(log_output_path, "output.logs")
+ metrics_key = store.upload_file(metrics_output_path, "metrics.csv")
+
+ logger.info(f"Uploaded logs to {log_key} in bucket {store.bucket_name}")
+ logger.info(f"Uploaded metrics to {metrics_key} in bucket {store.bucket_name}")
+
+
+@parse_app.local_entrypoint()
+def main(
+ source: str = "csv",
+ csv_file: str = "input.csv",
+ single_url: str = "https://github.com/codegen-sh/codegen-sdk.git",
+ single_commit: str | None = None,
+ github_language: str = "python",
+ github_heuristic: str = "stars",
+ github_num_repos: int = 50,
+):
+ """
+ Main entrypoint for the parse app.
+ """
+
+ match source:
+ case "csv":
+ input_path = Path(csv_file).relative_to(".")
+ with codegen_input_volume.batch_upload(force=True) as b:
+ b.put_file(csv_file, input_path)
+
+ env = {
+ "CSV_FILE_PATH": f"/app/inputs/{input_path}",
+ }
+ case "single":
+ env = {"SINGLE_URL": single_url}
+ if single_commit:
+ env["SINGLE_COMMIT"] = single_commit
+ case "github":
+ env = {
+ "GITHUB_LANGUAGE": github_language,
+ "GITHUB_HEURISTIC": github_heuristic,
+ "GITHUB_NUM_REPOS": str(github_num_repos),
+ }
+ case _:
+ msg = f"Invalid source: {source}"
+ raise ValueError(msg)
+
+ return parse_repo_on_modal.remote(
+ source=source,
+ env=env,
+ )
diff --git a/codegen-on-oss/pyproject.toml b/codegen-on-oss/pyproject.toml
new file mode 100644
index 000000000..b4227c454
--- /dev/null
+++ b/codegen-on-oss/pyproject.toml
@@ -0,0 +1,125 @@
+[project]
+name = "codegen-on-oss"
+version = "0.0.1"
+description = "Testing codegen parsing on popular OSS repositories"
+authors = [{ name = "Chris Lee", email = "clee@codegen.com" }]
+readme = "README.md"
+keywords = ['python']
+requires-python = ">=3.12,<4.0"
+classifiers = [
+ "Intended Audience :: Developers",
+ "Programming Language :: Python",
+ "Programming Language :: Python :: 3",
+ "Programming Language :: Python :: 3.12",
+ "Programming Language :: Python :: 3.13",
+ "Topic :: Software Development :: Libraries :: Python Modules",
+]
+dependencies = [
+ "boto3>=1.36.21",
+ "click>=8.1.8",
+ "codegen>=0.6.2",
+ "loguru>=0.7.3",
+ "modal>=0.73.51",
+ "pydantic-settings>=2.7.1",
+ "pygithub>=2.5.0",
+]
+
+[project.urls]
+Repository = "https://github.com/codegen-sh/codegen-on-oss"
+
+[dependency-groups]
+dev = [
+ "pytest>=7.2.0",
+ "pre-commit>=2.20.0",
+ "tox-uv>=1.11.3",
+ "deptry>=0.22.0",
+ "mypy>=0.991",
+ "pytest-cov>=4.0.0",
+ "ruff>=0.9.2",
+ "types-boto3[s3]>=1.36.21",
+]
+
+[project.scripts]
+cgparse = "codegen_on_oss.cli:cli"
+
+[project.optional-dependencies]
+sql = ["alembic>=1.14.1", "psycopg2-binary>=2.9.10", "sqlalchemy>=2.0.38"]
+
+[build-system]
+requires = ["hatchling"]
+build-backend = "hatchling.build"
+
+[tool.setuptools]
+py-modules = ["codegen_on_oss"]
+
+[tool.mypy]
+files = ["codegen_on_oss"]
+disallow_untyped_defs = true
+disallow_any_unimported = true
+no_implicit_optional = true
+check_untyped_defs = true
+warn_return_any = true
+warn_unused_ignores = true
+show_error_codes = true
+
+[tool.pytest.ini_options]
+testpaths = ["tests"]
+
+[tool.ruff]
+target-version = "py312"
+line-length = 88
+fix = true
+
+[tool.ruff.lint]
+select = [
+ # flake8-2020
+ "YTT",
+ # flake8-bandit
+ "S",
+ # flake8-bugbear
+ "B",
+ # flake8-builtins
+ "A",
+ # flake8-comprehensions
+ "C4",
+ # flake8-debugger
+ "T10",
+ # flake8-simplify
+ "SIM",
+ # isort
+ "I",
+ # mccabe
+ "C90",
+ # pycodestyle
+ "E",
+ "W",
+ # pyflakes
+ "F",
+ # pygrep-hooks
+ "PGH",
+ # pyupgrade
+ "UP",
+ # ruff
+ "RUF",
+ # tryceratops
+ "TRY",
+]
+ignore = [
+ # LineTooLong
+ "E501",
+ # DoNotAssignLambda
+ "E731",
+]
+
+[tool.ruff.lint.per-file-ignores]
+"tests/*" = ["S101"]
+
+[tool.ruff.format]
+preview = true
+
+[tool.coverage.report]
+skip_empty = true
+
+[tool.coverage.run]
+branch = true
+source = ["codegen_on_oss"]
diff --git a/codegen-on-oss/scripts/create_db.py b/codegen-on-oss/scripts/create_db.py
new file mode 100644
index 000000000..7b6179f06
--- /dev/null
+++ b/codegen-on-oss/scripts/create_db.py
@@ -0,0 +1,18 @@
+from pydantic_settings import SettingsConfigDict
+
+from codegen_on_oss.outputs.sql_output import Base, SQLSettings, get_session_maker
+
+
+class DotEnvSQLSettings(SQLSettings):
+ model_config = SettingsConfigDict(
+ env_file=".env",
+ env_prefix="POSTGRESQL_",
+ extra="ignore",
+ )
+
+
+settings = DotEnvSQLSettings()
+session_maker = get_session_maker(settings)
+
+with session_maker() as session:
+ Base.metadata.create_all(bind=session.bind)
diff --git a/codegen-on-oss/scripts/db b/codegen-on-oss/scripts/db
new file mode 100755
index 000000000..2adf25c4f
--- /dev/null
+++ b/codegen-on-oss/scripts/db
@@ -0,0 +1,7 @@
+#! /bin/bash
+
+# Load environment variables from .env file
+source .env
+
+# Connect to PostgreSQL using the environment variables
+PGPASSWORD=$POSTGRESQL_PASSWORD psql -h $POSTGRESQL_HOST -U ${POSTGRESQL_USER:-postgres} -d ${POSTGRESQL_DATABASE:-postgres}
diff --git a/codegen-on-oss/tests/test_cli.py b/codegen-on-oss/tests/test_cli.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/codegen-on-oss/tox.ini b/codegen-on-oss/tox.ini
new file mode 100644
index 000000000..a75c913ad
--- /dev/null
+++ b/codegen-on-oss/tox.ini
@@ -0,0 +1,19 @@
+[tox]
+skipsdist = true
+envlist = py39, py310, py311, py312, py313
+
+[gh-actions]
+python =
+ 3.9: py39
+ 3.10: py310
+ 3.11: py311
+ 3.12: py312
+ 3.13: py313
+
+[testenv]
+passenv = PYTHON_VERSION
+allowlist_externals = uv
+commands =
+ uv sync --python {envpython}
+ uv run python -m pytest --doctest-modules tests --cov --cov-config=pyproject.toml --cov-report=xml
+ mypy
diff --git a/codegen-on-oss/uv.lock b/codegen-on-oss/uv.lock
new file mode 100644
index 000000000..748b5d8bf
--- /dev/null
+++ b/codegen-on-oss/uv.lock
@@ -0,0 +1,3221 @@
+version = 1
+requires-python = ">=3.12, <4.0"
+resolution-markers = [
+ "python_full_version >= '3.12.4'",
+ "python_full_version < '3.12.4'",
+]
+
+[[package]]
+name = "aiohappyeyeballs"
+version = "2.4.6"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/08/07/508f9ebba367fc3370162e53a3cfd12f5652ad79f0e0bfdf9f9847c6f159/aiohappyeyeballs-2.4.6.tar.gz", hash = "sha256:9b05052f9042985d32ecbe4b59a77ae19c006a78f1344d7fdad69d28ded3d0b0", size = 21726 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/44/4c/03fb05f56551828ec67ceb3665e5dc51638042d204983a03b0a1541475b6/aiohappyeyeballs-2.4.6-py3-none-any.whl", hash = "sha256:147ec992cf873d74f5062644332c539fcd42956dc69453fe5204195e560517e1", size = 14543 },
+]
+
+[[package]]
+name = "aiohttp"
+version = "3.11.12"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "aiohappyeyeballs" },
+ { name = "aiosignal" },
+ { name = "attrs" },
+ { name = "frozenlist" },
+ { name = "multidict" },
+ { name = "propcache" },
+ { name = "yarl" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/37/4b/952d49c73084fb790cb5c6ead50848c8e96b4980ad806cf4d2ad341eaa03/aiohttp-3.11.12.tar.gz", hash = "sha256:7603ca26d75b1b86160ce1bbe2787a0b706e592af5b2504e12caa88a217767b0", size = 7673175 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/4d/d0/94346961acb476569fca9a644cc6f9a02f97ef75961a6b8d2b35279b8d1f/aiohttp-3.11.12-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e392804a38353900c3fd8b7cacbea5132888f7129f8e241915e90b85f00e3250", size = 704837 },
+ { url = "https://files.pythonhosted.org/packages/a9/af/05c503f1cc8f97621f199ef4b8db65fb88b8bc74a26ab2adb74789507ad3/aiohttp-3.11.12-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:8fa1510b96c08aaad49303ab11f8803787c99222288f310a62f493faf883ede1", size = 464218 },
+ { url = "https://files.pythonhosted.org/packages/f2/48/b9949eb645b9bd699153a2ec48751b985e352ab3fed9d98c8115de305508/aiohttp-3.11.12-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:dc065a4285307607df3f3686363e7f8bdd0d8ab35f12226362a847731516e42c", size = 456166 },
+ { url = "https://files.pythonhosted.org/packages/14/fb/980981807baecb6f54bdd38beb1bd271d9a3a786e19a978871584d026dcf/aiohttp-3.11.12-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cddb31f8474695cd61fc9455c644fc1606c164b93bff2490390d90464b4655df", size = 1682528 },
+ { url = "https://files.pythonhosted.org/packages/90/cb/77b1445e0a716914e6197b0698b7a3640590da6c692437920c586764d05b/aiohttp-3.11.12-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9dec0000d2d8621d8015c293e24589d46fa218637d820894cb7356c77eca3259", size = 1737154 },
+ { url = "https://files.pythonhosted.org/packages/ff/24/d6fb1f4cede9ccbe98e4def6f3ed1e1efcb658871bbf29f4863ec646bf38/aiohttp-3.11.12-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e3552fe98e90fdf5918c04769f338a87fa4f00f3b28830ea9b78b1bdc6140e0d", size = 1793435 },
+ { url = "https://files.pythonhosted.org/packages/17/e2/9f744cee0861af673dc271a3351f59ebd5415928e20080ab85be25641471/aiohttp-3.11.12-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6dfe7f984f28a8ae94ff3a7953cd9678550dbd2a1f9bda5dd9c5ae627744c78e", size = 1692010 },
+ { url = "https://files.pythonhosted.org/packages/90/c4/4a1235c1df544223eb57ba553ce03bc706bdd065e53918767f7fa1ff99e0/aiohttp-3.11.12-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a481a574af914b6e84624412666cbfbe531a05667ca197804ecc19c97b8ab1b0", size = 1619481 },
+ { url = "https://files.pythonhosted.org/packages/60/70/cf12d402a94a33abda86dd136eb749b14c8eb9fec1e16adc310e25b20033/aiohttp-3.11.12-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1987770fb4887560363b0e1a9b75aa303e447433c41284d3af2840a2f226d6e0", size = 1641578 },
+ { url = "https://files.pythonhosted.org/packages/1b/25/7211973fda1f5e833fcfd98ccb7f9ce4fbfc0074e3e70c0157a751d00db8/aiohttp-3.11.12-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:a4ac6a0f0f6402854adca4e3259a623f5c82ec3f0c049374133bcb243132baf9", size = 1684463 },
+ { url = "https://files.pythonhosted.org/packages/93/60/b5905b4d0693f6018b26afa9f2221fefc0dcbd3773fe2dff1a20fb5727f1/aiohttp-3.11.12-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c96a43822f1f9f69cc5c3706af33239489a6294be486a0447fb71380070d4d5f", size = 1646691 },
+ { url = "https://files.pythonhosted.org/packages/b4/fc/ba1b14d6fdcd38df0b7c04640794b3683e949ea10937c8a58c14d697e93f/aiohttp-3.11.12-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:a5e69046f83c0d3cb8f0d5bd9b8838271b1bc898e01562a04398e160953e8eb9", size = 1702269 },
+ { url = "https://files.pythonhosted.org/packages/5e/39/18c13c6f658b2ba9cc1e0c6fb2d02f98fd653ad2addcdf938193d51a9c53/aiohttp-3.11.12-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:68d54234c8d76d8ef74744f9f9fc6324f1508129e23da8883771cdbb5818cbef", size = 1734782 },
+ { url = "https://files.pythonhosted.org/packages/9f/d2/ccc190023020e342419b265861877cd8ffb75bec37b7ddd8521dd2c6deb8/aiohttp-3.11.12-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c9fd9dcf9c91affe71654ef77426f5cf8489305e1c66ed4816f5a21874b094b9", size = 1694740 },
+ { url = "https://files.pythonhosted.org/packages/3f/54/186805bcada64ea90ea909311ffedcd74369bfc6e880d39d2473314daa36/aiohttp-3.11.12-cp312-cp312-win32.whl", hash = "sha256:0ed49efcd0dc1611378beadbd97beb5d9ca8fe48579fc04a6ed0844072261b6a", size = 411530 },
+ { url = "https://files.pythonhosted.org/packages/3d/63/5eca549d34d141bcd9de50d4e59b913f3641559460c739d5e215693cb54a/aiohttp-3.11.12-cp312-cp312-win_amd64.whl", hash = "sha256:54775858c7f2f214476773ce785a19ee81d1294a6bedc5cc17225355aab74802", size = 437860 },
+ { url = "https://files.pythonhosted.org/packages/c3/9b/cea185d4b543ae08ee478373e16653722c19fcda10d2d0646f300ce10791/aiohttp-3.11.12-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:413ad794dccb19453e2b97c2375f2ca3cdf34dc50d18cc2693bd5aed7d16f4b9", size = 698148 },
+ { url = "https://files.pythonhosted.org/packages/91/5c/80d47fe7749fde584d1404a68ade29bcd7e58db8fa11fa38e8d90d77e447/aiohttp-3.11.12-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4a93d28ed4b4b39e6f46fd240896c29b686b75e39cc6992692e3922ff6982b4c", size = 460831 },
+ { url = "https://files.pythonhosted.org/packages/8e/f9/de568f8a8ca6b061d157c50272620c53168d6e3eeddae78dbb0f7db981eb/aiohttp-3.11.12-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d589264dbba3b16e8951b6f145d1e6b883094075283dafcab4cdd564a9e353a0", size = 453122 },
+ { url = "https://files.pythonhosted.org/packages/8b/fd/b775970a047543bbc1d0f66725ba72acef788028fce215dc959fd15a8200/aiohttp-3.11.12-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e5148ca8955affdfeb864aca158ecae11030e952b25b3ae15d4e2b5ba299bad2", size = 1665336 },
+ { url = "https://files.pythonhosted.org/packages/82/9b/aff01d4f9716245a1b2965f02044e4474fadd2bcfe63cf249ca788541886/aiohttp-3.11.12-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:525410e0790aab036492eeea913858989c4cb070ff373ec3bc322d700bdf47c1", size = 1718111 },
+ { url = "https://files.pythonhosted.org/packages/e0/a9/166fd2d8b2cc64f08104aa614fad30eee506b563154081bf88ce729bc665/aiohttp-3.11.12-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9bd8695be2c80b665ae3f05cb584093a1e59c35ecb7d794d1edd96e8cc9201d7", size = 1775293 },
+ { url = "https://files.pythonhosted.org/packages/13/c5/0d3c89bd9e36288f10dc246f42518ce8e1c333f27636ac78df091c86bb4a/aiohttp-3.11.12-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0203433121484b32646a5f5ea93ae86f3d9559d7243f07e8c0eab5ff8e3f70e", size = 1677338 },
+ { url = "https://files.pythonhosted.org/packages/72/b2/017db2833ef537be284f64ead78725984db8a39276c1a9a07c5c7526e238/aiohttp-3.11.12-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40cd36749a1035c34ba8d8aaf221b91ca3d111532e5ccb5fa8c3703ab1b967ed", size = 1603365 },
+ { url = "https://files.pythonhosted.org/packages/fc/72/b66c96a106ec7e791e29988c222141dd1219d7793ffb01e72245399e08d2/aiohttp-3.11.12-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a7442662afebbf7b4c6d28cb7aab9e9ce3a5df055fc4116cc7228192ad6cb484", size = 1618464 },
+ { url = "https://files.pythonhosted.org/packages/3f/50/e68a40f267b46a603bab569d48d57f23508801614e05b3369898c5b2910a/aiohttp-3.11.12-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:8a2fb742ef378284a50766e985804bd6adb5adb5aa781100b09befdbfa757b65", size = 1657827 },
+ { url = "https://files.pythonhosted.org/packages/c5/1d/aafbcdb1773d0ba7c20793ebeedfaba1f3f7462f6fc251f24983ed738aa7/aiohttp-3.11.12-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2cee3b117a8d13ab98b38d5b6bdcd040cfb4181068d05ce0c474ec9db5f3c5bb", size = 1616700 },
+ { url = "https://files.pythonhosted.org/packages/b0/5e/6cd9724a2932f36e2a6b742436a36d64784322cfb3406ca773f903bb9a70/aiohttp-3.11.12-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f6a19bcab7fbd8f8649d6595624856635159a6527861b9cdc3447af288a00c00", size = 1685643 },
+ { url = "https://files.pythonhosted.org/packages/8b/38/ea6c91d5c767fd45a18151675a07c710ca018b30aa876a9f35b32fa59761/aiohttp-3.11.12-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:e4cecdb52aaa9994fbed6b81d4568427b6002f0a91c322697a4bfcc2b2363f5a", size = 1715487 },
+ { url = "https://files.pythonhosted.org/packages/8e/24/e9edbcb7d1d93c02e055490348df6f955d675e85a028c33babdcaeda0853/aiohttp-3.11.12-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:30f546358dfa0953db92ba620101fefc81574f87b2346556b90b5f3ef16e55ce", size = 1672948 },
+ { url = "https://files.pythonhosted.org/packages/25/be/0b1fb737268e003198f25c3a68c2135e76e4754bf399a879b27bd508a003/aiohttp-3.11.12-cp313-cp313-win32.whl", hash = "sha256:ce1bb21fc7d753b5f8a5d5a4bae99566386b15e716ebdb410154c16c91494d7f", size = 410396 },
+ { url = "https://files.pythonhosted.org/packages/68/fd/677def96a75057b0a26446b62f8fbb084435b20a7d270c99539c26573bfd/aiohttp-3.11.12-cp313-cp313-win_amd64.whl", hash = "sha256:f7914ab70d2ee8ab91c13e5402122edbc77821c66d2758abb53aabe87f013287", size = 436234 },
+]
+
+[[package]]
+name = "aiosignal"
+version = "1.3.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "frozenlist" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/ba/b5/6d55e80f6d8a08ce22b982eafa278d823b541c925f11ee774b0b9c43473d/aiosignal-1.3.2.tar.gz", hash = "sha256:a8c255c66fafb1e499c9351d0bf32ff2d8a0321595ebac3b93713656d2436f54", size = 19424 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/ec/6a/bc7e17a3e87a2985d3e8f4da4cd0f481060eb78fb08596c42be62c90a4d9/aiosignal-1.3.2-py2.py3-none-any.whl", hash = "sha256:45cde58e409a301715980c2b01d0c28bdde3770d8290b5eb2173759d9acb31a5", size = 7597 },
+]
+
+[[package]]
+name = "alembic"
+version = "1.14.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "mako" },
+ { name = "sqlalchemy" },
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/99/09/f844822e4e847a3f0bd41797f93c4674cd4d2462a3f6c459aa528cdf786e/alembic-1.14.1.tar.gz", hash = "sha256:496e888245a53adf1498fcab31713a469c65836f8de76e01399aa1c3e90dd213", size = 1918219 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/54/7e/ac0991d1745f7d755fc1cd381b3990a45b404b4d008fc75e2a983516fbfe/alembic-1.14.1-py3-none-any.whl", hash = "sha256:1acdd7a3a478e208b0503cd73614d5e4c6efafa4e73518bb60e4f2846a37b1c5", size = 233565 },
+]
+
+[[package]]
+name = "annotated-types"
+version = "0.7.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643 },
+]
+
+[[package]]
+name = "anthropic"
+version = "0.23.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "anyio" },
+ { name = "distro" },
+ { name = "httpx" },
+ { name = "pydantic" },
+ { name = "sniffio" },
+ { name = "tokenizers" },
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/1b/2d/7be8f53faba0ca14ea20d31ebc53a2a27a8ab76672d993c12198b69dda39/anthropic-0.23.1.tar.gz", hash = "sha256:9325103702cbc96bb09d1b58c36bde75c726f6a01029fb4d85f41ebba07e9066", size = 823288 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/f8/99/6716253f156fac232d2979020f1fb7c93f7ba4daafca4e8872e83dbe378a/anthropic-0.23.1-py3-none-any.whl", hash = "sha256:6dc5779dae83a5834864f4a4af0166c972b70f4cb8fd2765e1558282cc6d6242", size = 869140 },
+]
+
+[[package]]
+name = "anyio"
+version = "4.8.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "idna" },
+ { name = "sniffio" },
+ { name = "typing-extensions", marker = "python_full_version < '3.13'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/a3/73/199a98fc2dae33535d6b8e8e6ec01f8c1d76c9adb096c6b7d64823038cde/anyio-4.8.0.tar.gz", hash = "sha256:1d9fe889df5212298c0c0723fa20479d1b94883a2df44bd3897aa91083316f7a", size = 181126 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/46/eb/e7f063ad1fec6b3178a3cd82d1a3c4de82cccf283fc42746168188e1cdd5/anyio-4.8.0-py3-none-any.whl", hash = "sha256:b5011f270ab5eb0abf13385f851315585cc37ef330dd88e27ec3d34d651fd47a", size = 96041 },
+]
+
+[[package]]
+name = "argcomplete"
+version = "3.5.3"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/0c/be/6c23d80cb966fb8f83fb1ebfb988351ae6b0554d0c3a613ee4531c026597/argcomplete-3.5.3.tar.gz", hash = "sha256:c12bf50eded8aebb298c7b7da7a5ff3ee24dffd9f5281867dfe1424b58c55392", size = 72999 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c4/08/2a4db06ec3d203124c967fc89295e85a202e5cbbcdc08fd6a64b65217d1e/argcomplete-3.5.3-py3-none-any.whl", hash = "sha256:2ab2c4a215c59fd6caaff41a869480a23e8f6a5f910b266c1808037f4e375b61", size = 43569 },
+]
+
+[[package]]
+name = "astor"
+version = "0.8.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/5a/21/75b771132fee241dfe601d39ade629548a9626d1d39f333fde31bc46febe/astor-0.8.1.tar.gz", hash = "sha256:6a6effda93f4e1ce9f618779b2dd1d9d84f1e32812c23a29b3fff6fd7f63fa5e", size = 35090 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c3/88/97eef84f48fa04fbd6750e62dcceafba6c63c81b7ac1420856c8dcc0a3f9/astor-0.8.1-py2.py3-none-any.whl", hash = "sha256:070a54e890cefb5b3739d19f30f5a5ec840ffc9c50ffa7d23cc9fc1a38ebbfc5", size = 27488 },
+]
+
+[[package]]
+name = "attrs"
+version = "25.1.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/49/7c/fdf464bcc51d23881d110abd74b512a42b3d5d376a55a831b44c603ae17f/attrs-25.1.0.tar.gz", hash = "sha256:1c97078a80c814273a76b2a298a932eb681c87415c11dee0a6921de7f1b02c3e", size = 810562 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/fc/30/d4986a882011f9df997a55e6becd864812ccfcd821d64aac8570ee39f719/attrs-25.1.0-py3-none-any.whl", hash = "sha256:c75a69e28a550a7e93789579c22aa26b0f5b83b75dc4e08fe092980051e1090a", size = 63152 },
+]
+
+[[package]]
+name = "backoff"
+version = "2.2.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/47/d7/5bbeb12c44d7c4f2fb5b56abce497eb5ed9f34d85701de869acedd602619/backoff-2.2.1.tar.gz", hash = "sha256:03f829f5bb1923180821643f8753b0502c3b682293992485b0eef2807afa5cba", size = 17001 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/df/73/b6e24bd22e6720ca8ee9a85a0c4a2971af8497d8f3193fa05390cbd46e09/backoff-2.2.1-py3-none-any.whl", hash = "sha256:63579f9a0628e06278f7e47b7d7d5b6ce20dc65c5e96a6f3ca99a6adca0396e8", size = 15148 },
+]
+
+[[package]]
+name = "black"
+version = "25.1.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "click" },
+ { name = "mypy-extensions" },
+ { name = "packaging" },
+ { name = "pathspec" },
+ { name = "platformdirs" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/94/49/26a7b0f3f35da4b5a65f081943b7bcd22d7002f5f0fb8098ec1ff21cb6ef/black-25.1.0.tar.gz", hash = "sha256:33496d5cd1222ad73391352b4ae8da15253c5de89b93a80b3e2c8d9a19ec2666", size = 649449 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/83/71/3fe4741df7adf015ad8dfa082dd36c94ca86bb21f25608eb247b4afb15b2/black-25.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4b60580e829091e6f9238c848ea6750efed72140b91b048770b64e74fe04908b", size = 1650988 },
+ { url = "https://files.pythonhosted.org/packages/13/f3/89aac8a83d73937ccd39bbe8fc6ac8860c11cfa0af5b1c96d081facac844/black-25.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1e2978f6df243b155ef5fa7e558a43037c3079093ed5d10fd84c43900f2d8ecc", size = 1453985 },
+ { url = "https://files.pythonhosted.org/packages/6f/22/b99efca33f1f3a1d2552c714b1e1b5ae92efac6c43e790ad539a163d1754/black-25.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3b48735872ec535027d979e8dcb20bf4f70b5ac75a8ea99f127c106a7d7aba9f", size = 1783816 },
+ { url = "https://files.pythonhosted.org/packages/18/7e/a27c3ad3822b6f2e0e00d63d58ff6299a99a5b3aee69fa77cd4b0076b261/black-25.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:ea0213189960bda9cf99be5b8c8ce66bb054af5e9e861249cd23471bd7b0b3ba", size = 1440860 },
+ { url = "https://files.pythonhosted.org/packages/98/87/0edf98916640efa5d0696e1abb0a8357b52e69e82322628f25bf14d263d1/black-25.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8f0b18a02996a836cc9c9c78e5babec10930862827b1b724ddfe98ccf2f2fe4f", size = 1650673 },
+ { url = "https://files.pythonhosted.org/packages/52/e5/f7bf17207cf87fa6e9b676576749c6b6ed0d70f179a3d812c997870291c3/black-25.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:afebb7098bfbc70037a053b91ae8437c3857482d3a690fefc03e9ff7aa9a5fd3", size = 1453190 },
+ { url = "https://files.pythonhosted.org/packages/e3/ee/adda3d46d4a9120772fae6de454c8495603c37c4c3b9c60f25b1ab6401fe/black-25.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:030b9759066a4ee5e5aca28c3c77f9c64789cdd4de8ac1df642c40b708be6171", size = 1782926 },
+ { url = "https://files.pythonhosted.org/packages/cc/64/94eb5f45dcb997d2082f097a3944cfc7fe87e071907f677e80788a2d7b7a/black-25.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:a22f402b410566e2d1c950708c77ebf5ebd5d0d88a6a2e87c86d9fb48afa0d18", size = 1442613 },
+ { url = "https://files.pythonhosted.org/packages/09/71/54e999902aed72baf26bca0d50781b01838251a462612966e9fc4891eadd/black-25.1.0-py3-none-any.whl", hash = "sha256:95e8176dae143ba9097f351d174fdaf0ccd29efb414b362ae3fd72bf0f710717", size = 207646 },
+]
+
+[[package]]
+name = "boto3"
+version = "1.36.21"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "botocore" },
+ { name = "jmespath" },
+ { name = "s3transfer" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/af/cb/745ca9a661be42f3dc0c5b6ea4d3182d9dd5dfd4204aad4910af20775a26/boto3-1.36.21.tar.gz", hash = "sha256:41eb2b73eb612d300e629e3328b83f1ffea0fc6633e75c241a72a76746c1db26", size = 110999 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/39/99/7f5c7a16e205e19089e7f0d8716e9d1a5207bf4736f82a7d0c602bd0a40c/boto3-1.36.21-py3-none-any.whl", hash = "sha256:f94faa7cf932d781f474d87f8b4c14a033af95ac1460136b40d75e7a30086ef0", size = 139179 },
+]
+
+[[package]]
+name = "botocore"
+version = "1.36.21"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "jmespath" },
+ { name = "python-dateutil" },
+ { name = "urllib3" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/69/9f/17b7610f2bfc5ccba6d2395f1cc856dd3e7e50f0088fc22949e56ae9f569/botocore-1.36.21.tar.gz", hash = "sha256:da746240e2ad64fd4997f7f3664a0a8e303d18075fc1d473727cb6375080ea16", size = 13523380 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/7b/b4/8f1dc71437d12a61ca1daac534bc32fa6ccf207011eab7465d8c8a46dc06/botocore-1.36.21-py3-none-any.whl", hash = "sha256:24a7052e792639dc2726001bd474cd0aaa959c1e18ddd92c17f3adc6efa1b132", size = 13352864 },
+]
+
+[[package]]
+name = "botocore-stubs"
+version = "1.36.21"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "types-awscrt" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/ea/c1/84516c57f19f27dbd91f93d639ecaac06500241265b6617d6041683390dd/botocore_stubs-1.36.21.tar.gz", hash = "sha256:b49520a71c47bb56dfb4dafea751c40e0fefcd7070fef7ea7f0d54cc917d82aa", size = 41284 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/53/e6/c28c43390fad09b999b3edf0dad82e948c17f7af723c5cc4119a5554493c/botocore_stubs-1.36.21-py3-none-any.whl", hash = "sha256:3dec608cde59eb9357139efa45d954cae692a1497645f7f132eeda0af13df25b", size = 64107 },
+]
+
+[[package]]
+name = "cachetools"
+version = "5.5.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/d9/74/57df1ab0ce6bc5f6fa868e08de20df8ac58f9c44330c7671ad922d2bbeae/cachetools-5.5.1.tar.gz", hash = "sha256:70f238fbba50383ef62e55c6aff6d9673175fe59f7c6782c7a0b9e38f4a9df95", size = 28044 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/ec/4e/de4ff18bcf55857ba18d3a4bd48c8a9fde6bb0980c9d20b263f05387fd88/cachetools-5.5.1-py3-none-any.whl", hash = "sha256:b76651fdc3b24ead3c648bbdeeb940c1b04d365b38b4af66788f9ec4a81d42bb", size = 9530 },
+]
+
+[[package]]
+name = "certifi"
+version = "2025.1.31"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/1c/ab/c9f1e32b7b1bf505bf26f0ef697775960db7932abeb7b516de930ba2705f/certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651", size = 167577 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/38/fc/bce832fd4fd99766c04d1ee0eead6b0ec6486fb100ae5e74c1d91292b982/certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe", size = 166393 },
+]
+
+[[package]]
+name = "cffi"
+version = "1.17.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "pycparser" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/fc/97/c783634659c2920c3fc70419e3af40972dbaf758daa229a7d6ea6135c90d/cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824", size = 516621 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/5a/84/e94227139ee5fb4d600a7a4927f322e1d4aea6fdc50bd3fca8493caba23f/cffi-1.17.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4", size = 183178 },
+ { url = "https://files.pythonhosted.org/packages/da/ee/fb72c2b48656111c4ef27f0f91da355e130a923473bf5ee75c5643d00cca/cffi-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c", size = 178840 },
+ { url = "https://files.pythonhosted.org/packages/cc/b6/db007700f67d151abadf508cbfd6a1884f57eab90b1bb985c4c8c02b0f28/cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36", size = 454803 },
+ { url = "https://files.pythonhosted.org/packages/1a/df/f8d151540d8c200eb1c6fba8cd0dfd40904f1b0682ea705c36e6c2e97ab3/cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5", size = 478850 },
+ { url = "https://files.pythonhosted.org/packages/28/c0/b31116332a547fd2677ae5b78a2ef662dfc8023d67f41b2a83f7c2aa78b1/cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff", size = 485729 },
+ { url = "https://files.pythonhosted.org/packages/91/2b/9a1ddfa5c7f13cab007a2c9cc295b70fbbda7cb10a286aa6810338e60ea1/cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99", size = 471256 },
+ { url = "https://files.pythonhosted.org/packages/b2/d5/da47df7004cb17e4955df6a43d14b3b4ae77737dff8bf7f8f333196717bf/cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93", size = 479424 },
+ { url = "https://files.pythonhosted.org/packages/0b/ac/2a28bcf513e93a219c8a4e8e125534f4f6db03e3179ba1c45e949b76212c/cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3", size = 484568 },
+ { url = "https://files.pythonhosted.org/packages/d4/38/ca8a4f639065f14ae0f1d9751e70447a261f1a30fa7547a828ae08142465/cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8", size = 488736 },
+ { url = "https://files.pythonhosted.org/packages/86/c5/28b2d6f799ec0bdecf44dced2ec5ed43e0eb63097b0f58c293583b406582/cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65", size = 172448 },
+ { url = "https://files.pythonhosted.org/packages/50/b9/db34c4755a7bd1cb2d1603ac3863f22bcecbd1ba29e5ee841a4bc510b294/cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903", size = 181976 },
+ { url = "https://files.pythonhosted.org/packages/8d/f8/dd6c246b148639254dad4d6803eb6a54e8c85c6e11ec9df2cffa87571dbe/cffi-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e", size = 182989 },
+ { url = "https://files.pythonhosted.org/packages/8b/f1/672d303ddf17c24fc83afd712316fda78dc6fce1cd53011b839483e1ecc8/cffi-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2", size = 178802 },
+ { url = "https://files.pythonhosted.org/packages/0e/2d/eab2e858a91fdff70533cab61dcff4a1f55ec60425832ddfdc9cd36bc8af/cffi-1.17.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3", size = 454792 },
+ { url = "https://files.pythonhosted.org/packages/75/b2/fbaec7c4455c604e29388d55599b99ebcc250a60050610fadde58932b7ee/cffi-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683", size = 478893 },
+ { url = "https://files.pythonhosted.org/packages/4f/b7/6e4a2162178bf1935c336d4da8a9352cccab4d3a5d7914065490f08c0690/cffi-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5", size = 485810 },
+ { url = "https://files.pythonhosted.org/packages/c7/8a/1d0e4a9c26e54746dc08c2c6c037889124d4f59dffd853a659fa545f1b40/cffi-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4", size = 471200 },
+ { url = "https://files.pythonhosted.org/packages/26/9f/1aab65a6c0db35f43c4d1b4f580e8df53914310afc10ae0397d29d697af4/cffi-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd", size = 479447 },
+ { url = "https://files.pythonhosted.org/packages/5f/e4/fb8b3dd8dc0e98edf1135ff067ae070bb32ef9d509d6cb0f538cd6f7483f/cffi-1.17.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed", size = 484358 },
+ { url = "https://files.pythonhosted.org/packages/f1/47/d7145bf2dc04684935d57d67dff9d6d795b2ba2796806bb109864be3a151/cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9", size = 488469 },
+ { url = "https://files.pythonhosted.org/packages/bf/ee/f94057fa6426481d663b88637a9a10e859e492c73d0384514a17d78ee205/cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d", size = 172475 },
+ { url = "https://files.pythonhosted.org/packages/7c/fc/6a8cb64e5f0324877d503c854da15d76c1e50eb722e320b15345c4d0c6de/cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a", size = 182009 },
+]
+
+[[package]]
+name = "cfgv"
+version = "3.4.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/11/74/539e56497d9bd1d484fd863dd69cbbfa653cd2aa27abfe35653494d85e94/cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560", size = 7114 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c5/55/51844dd50c4fc7a33b653bfaba4c2456f06955289ca770a5dbd5fd267374/cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9", size = 7249 },
+]
+
+[[package]]
+name = "chardet"
+version = "5.2.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/f3/0d/f7b6ab21ec75897ed80c17d79b15951a719226b9fababf1e40ea74d69079/chardet-5.2.0.tar.gz", hash = "sha256:1b3b6ff479a8c414bc3fa2c0852995695c4a026dcd6d0633b2dd092ca39c1cf7", size = 2069618 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/38/6f/f5fbc992a329ee4e0f288c1fe0e2ad9485ed064cac731ed2fe47dcc38cbf/chardet-5.2.0-py3-none-any.whl", hash = "sha256:e1cf59446890a00105fe7b7912492ea04b6e6f06d4b742b2c788469e34c82970", size = 199385 },
+]
+
+[[package]]
+name = "charset-normalizer"
+version = "3.4.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/16/b0/572805e227f01586461c80e0fd25d65a2115599cc9dad142fee4b747c357/charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3", size = 123188 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/0a/9a/dd1e1cdceb841925b7798369a09279bd1cf183cef0f9ddf15a3a6502ee45/charset_normalizer-3.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545", size = 196105 },
+ { url = "https://files.pythonhosted.org/packages/d3/8c/90bfabf8c4809ecb648f39794cf2a84ff2e7d2a6cf159fe68d9a26160467/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7", size = 140404 },
+ { url = "https://files.pythonhosted.org/packages/ad/8f/e410d57c721945ea3b4f1a04b74f70ce8fa800d393d72899f0a40526401f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757", size = 150423 },
+ { url = "https://files.pythonhosted.org/packages/f0/b8/e6825e25deb691ff98cf5c9072ee0605dc2acfca98af70c2d1b1bc75190d/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa", size = 143184 },
+ { url = "https://files.pythonhosted.org/packages/3e/a2/513f6cbe752421f16d969e32f3583762bfd583848b763913ddab8d9bfd4f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d", size = 145268 },
+ { url = "https://files.pythonhosted.org/packages/74/94/8a5277664f27c3c438546f3eb53b33f5b19568eb7424736bdc440a88a31f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616", size = 147601 },
+ { url = "https://files.pythonhosted.org/packages/7c/5f/6d352c51ee763623a98e31194823518e09bfa48be2a7e8383cf691bbb3d0/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b", size = 141098 },
+ { url = "https://files.pythonhosted.org/packages/78/d4/f5704cb629ba5ab16d1d3d741396aec6dc3ca2b67757c45b0599bb010478/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d", size = 149520 },
+ { url = "https://files.pythonhosted.org/packages/c5/96/64120b1d02b81785f222b976c0fb79a35875457fa9bb40827678e54d1bc8/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a", size = 152852 },
+ { url = "https://files.pythonhosted.org/packages/84/c9/98e3732278a99f47d487fd3468bc60b882920cef29d1fa6ca460a1fdf4e6/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9", size = 150488 },
+ { url = "https://files.pythonhosted.org/packages/13/0e/9c8d4cb99c98c1007cc11eda969ebfe837bbbd0acdb4736d228ccaabcd22/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1", size = 146192 },
+ { url = "https://files.pythonhosted.org/packages/b2/21/2b6b5b860781a0b49427309cb8670785aa543fb2178de875b87b9cc97746/charset_normalizer-3.4.1-cp312-cp312-win32.whl", hash = "sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35", size = 95550 },
+ { url = "https://files.pythonhosted.org/packages/21/5b/1b390b03b1d16c7e382b561c5329f83cc06623916aab983e8ab9239c7d5c/charset_normalizer-3.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f", size = 102785 },
+ { url = "https://files.pythonhosted.org/packages/38/94/ce8e6f63d18049672c76d07d119304e1e2d7c6098f0841b51c666e9f44a0/charset_normalizer-3.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda", size = 195698 },
+ { url = "https://files.pythonhosted.org/packages/24/2e/dfdd9770664aae179a96561cc6952ff08f9a8cd09a908f259a9dfa063568/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313", size = 140162 },
+ { url = "https://files.pythonhosted.org/packages/24/4e/f646b9093cff8fc86f2d60af2de4dc17c759de9d554f130b140ea4738ca6/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9", size = 150263 },
+ { url = "https://files.pythonhosted.org/packages/5e/67/2937f8d548c3ef6e2f9aab0f6e21001056f692d43282b165e7c56023e6dd/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b", size = 142966 },
+ { url = "https://files.pythonhosted.org/packages/52/ed/b7f4f07de100bdb95c1756d3a4d17b90c1a3c53715c1a476f8738058e0fa/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11", size = 144992 },
+ { url = "https://files.pythonhosted.org/packages/96/2c/d49710a6dbcd3776265f4c923bb73ebe83933dfbaa841c5da850fe0fd20b/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f", size = 147162 },
+ { url = "https://files.pythonhosted.org/packages/b4/41/35ff1f9a6bd380303dea55e44c4933b4cc3c4850988927d4082ada230273/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd", size = 140972 },
+ { url = "https://files.pythonhosted.org/packages/fb/43/c6a0b685fe6910d08ba971f62cd9c3e862a85770395ba5d9cad4fede33ab/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2", size = 149095 },
+ { url = "https://files.pythonhosted.org/packages/4c/ff/a9a504662452e2d2878512115638966e75633519ec11f25fca3d2049a94a/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886", size = 152668 },
+ { url = "https://files.pythonhosted.org/packages/6c/71/189996b6d9a4b932564701628af5cee6716733e9165af1d5e1b285c530ed/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601", size = 150073 },
+ { url = "https://files.pythonhosted.org/packages/e4/93/946a86ce20790e11312c87c75ba68d5f6ad2208cfb52b2d6a2c32840d922/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd", size = 145732 },
+ { url = "https://files.pythonhosted.org/packages/cd/e5/131d2fb1b0dddafc37be4f3a2fa79aa4c037368be9423061dccadfd90091/charset_normalizer-3.4.1-cp313-cp313-win32.whl", hash = "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407", size = 95391 },
+ { url = "https://files.pythonhosted.org/packages/27/f2/4f9a69cc7712b9b5ad8fdb87039fd89abba997ad5cbe690d1835d40405b0/charset_normalizer-3.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971", size = 102702 },
+ { url = "https://files.pythonhosted.org/packages/0e/f6/65ecc6878a89bb1c23a086ea335ad4bf21a588990c3f535a227b9eea9108/charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85", size = 49767 },
+]
+
+[[package]]
+name = "click"
+version = "8.1.8"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "colorama", marker = "sys_platform == 'win32'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188 },
+]
+
+[[package]]
+name = "click-option-group"
+version = "0.5.6"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "click" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/e7/b8/91054601a2e05fd9060cb1baf56be5b24145817b059e078669e1099529c7/click-option-group-0.5.6.tar.gz", hash = "sha256:97d06703873518cc5038509443742b25069a3c7562d1ea72ff08bfadde1ce777", size = 16517 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/af/75/81ea958bc0f7e410257cb2a42531b93a7695a31930cde87192c010a52c50/click_option_group-0.5.6-py3-none-any.whl", hash = "sha256:38a26d963ee3ad93332ddf782f9259c5bdfe405e73408d943ef5e7d0c3767ec7", size = 12467 },
+]
+
+[[package]]
+name = "codegen"
+version = "0.6.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "anthropic" },
+ { name = "astor" },
+ { name = "backoff" },
+ { name = "click" },
+ { name = "codeowners" },
+ { name = "dataclasses-json" },
+ { name = "datamodel-code-generator" },
+ { name = "dicttoxml" },
+ { name = "docstring-parser" },
+ { name = "fastapi", extra = ["standard"] },
+ { name = "gitpython" },
+ { name = "giturlparse" },
+ { name = "hatch-vcs" },
+ { name = "hatchling" },
+ { name = "humanize" },
+ { name = "langchain", extra = ["openai"] },
+ { name = "langchain-core" },
+ { name = "langchain-openai" },
+ { name = "lazy-object-proxy" },
+ { name = "mini-racer" },
+ { name = "networkx" },
+ { name = "numpy" },
+ { name = "openai" },
+ { name = "pip" },
+ { name = "plotly" },
+ { name = "psutil" },
+ { name = "pydantic" },
+ { name = "pydantic-core" },
+ { name = "pydantic-settings" },
+ { name = "pygit2" },
+ { name = "pygithub" },
+ { name = "pyinstrument" },
+ { name = "pyjson5" },
+ { name = "pyright" },
+ { name = "pytest-snapshot" },
+ { name = "python-dotenv" },
+ { name = "python-levenshtein" },
+ { name = "python-semantic-release" },
+ { name = "requests" },
+ { name = "rich" },
+ { name = "rich-click" },
+ { name = "rustworkx" },
+ { name = "sentry-sdk" },
+ { name = "starlette" },
+ { name = "tabulate" },
+ { name = "tenacity" },
+ { name = "termcolor" },
+ { name = "tiktoken" },
+ { name = "toml" },
+ { name = "tomlkit" },
+ { name = "tqdm" },
+ { name = "tree-sitter" },
+ { name = "tree-sitter-javascript" },
+ { name = "tree-sitter-python" },
+ { name = "tree-sitter-typescript" },
+ { name = "typing-extensions" },
+ { name = "unidiff" },
+ { name = "uvicorn", extra = ["standard"] },
+ { name = "watchfiles" },
+ { name = "wrapt" },
+ { name = "xmltodict" },
+]
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/72/81/89f094e03de4880dcb49bf06a021db17a0c67fd512bd71927cbbd9ae06ee/codegen-0.6.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ea206446a3d87f013f8a1d39a4b1df05ff6edc09989ce9f8626a24334639cea1", size = 989506 },
+ { url = "https://files.pythonhosted.org/packages/15/27/8b698d01363cb3782feec414d354b15ce4707515f0d35b67f7d5e088fbd4/codegen-0.6.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3304029f2cc57f87d85161fb7e63689f235bed1be5a08141553ee75db5945f64", size = 980917 },
+ { url = "https://files.pythonhosted.org/packages/b5/36/d598c692948880849e222dcd3363b13fb7803f67a79bb1b01ff9f2026d80/codegen-0.6.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_34_aarch64.whl", hash = "sha256:ff4a59461fb770daf78ce5a177ac59fa61728c37f9fdbd8851cc4664741d2396", size = 1963295 },
+ { url = "https://files.pythonhosted.org/packages/af/89/ce34bfcf34319a1fc5593b84f4a2bd6e6c0a876f651936c37eef771ee215/codegen-0.6.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_34_x86_64.whl", hash = "sha256:62605ef8930d9f8a74ee2cde270a620fcf4af013f4db1c8b5e4aaa3a46b4ef97", size = 2009400 },
+ { url = "https://files.pythonhosted.org/packages/01/d8/c72792de6859f92fbf20486a93284a66105663536f43f3b8324292b41f62/codegen-0.6.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:3dca7d09c10b4ccb11f6cb5b4b294624a8ef4430204f334368a9386855e38aab", size = 985428 },
+ { url = "https://files.pythonhosted.org/packages/c6/45/ad0295853a6877725a2293ddfa26b86da36ebba2bbc6efb481129415790d/codegen-0.6.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:39b4639772c257339aa41247c0c9eea7c07e58e187bdf6d62ac4b032be01e854", size = 977301 },
+ { url = "https://files.pythonhosted.org/packages/cd/3d/8753a7ba2fe00fcedb38c360f6ee7269438a06dfff19d128f568e129b21f/codegen-0.6.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_34_aarch64.whl", hash = "sha256:f0200beee0253b0f0b08349a185aafc54f21b3d1f331ab18839e19a476a40e81", size = 1956498 },
+ { url = "https://files.pythonhosted.org/packages/8f/b4/8895fec93cb49cd307f65b291eb14d21ac60b07c3e738477da854b8c1e13/codegen-0.6.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_34_x86_64.whl", hash = "sha256:07b5105e8431b340cf33d670405369bfa693d95a14147184be8be2860892da73", size = 2000872 },
+]
+
+[[package]]
+name = "codegen-on-oss"
+version = "0.0.1"
+source = { editable = "." }
+dependencies = [
+ { name = "boto3" },
+ { name = "click" },
+ { name = "codegen" },
+ { name = "loguru" },
+ { name = "modal" },
+ { name = "pydantic-settings" },
+ { name = "pygithub" },
+]
+
+[package.optional-dependencies]
+sql = [
+ { name = "alembic" },
+ { name = "psycopg2-binary" },
+ { name = "sqlalchemy" },
+]
+
+[package.dev-dependencies]
+dev = [
+ { name = "deptry" },
+ { name = "mypy" },
+ { name = "pre-commit" },
+ { name = "pytest" },
+ { name = "pytest-cov" },
+ { name = "ruff" },
+ { name = "tox-uv" },
+ { name = "types-boto3", extra = ["s3"] },
+]
+
+[package.metadata]
+requires-dist = [
+ { name = "alembic", marker = "extra == 'sql'", specifier = ">=1.14.1" },
+ { name = "boto3", specifier = ">=1.36.21" },
+ { name = "click", specifier = ">=8.1.8" },
+ { name = "codegen", specifier = ">=0.6.2" },
+ { name = "loguru", specifier = ">=0.7.3" },
+ { name = "modal", specifier = ">=0.73.51" },
+ { name = "psycopg2-binary", marker = "extra == 'sql'", specifier = ">=2.9.10" },
+ { name = "pydantic-settings", specifier = ">=2.7.1" },
+ { name = "pygithub", specifier = ">=2.5.0" },
+ { name = "sqlalchemy", marker = "extra == 'sql'", specifier = ">=2.0.38" },
+]
+
+[package.metadata.requires-dev]
+dev = [
+ { name = "deptry", specifier = ">=0.22.0" },
+ { name = "mypy", specifier = ">=0.991" },
+ { name = "pre-commit", specifier = ">=2.20.0" },
+ { name = "pytest", specifier = ">=7.2.0" },
+ { name = "pytest-cov", specifier = ">=4.0.0" },
+ { name = "ruff", specifier = ">=0.9.2" },
+ { name = "tox-uv", specifier = ">=1.11.3" },
+ { name = "types-boto3", extras = ["s3"], specifier = ">=1.36.21" },
+]
+
+[[package]]
+name = "codeowners"
+version = "0.7.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/75/66/ddba64473b0ce0b2c30cd0e1e32d923839834ed91948ad92bad23b2eadeb/codeowners-0.7.0.tar.gz", hash = "sha256:a842647b20968c14da6066e4de4fffac4fd7c1c30de9cfa8b2fc8f534b3d9f48", size = 7706 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/92/d1/4091c351ac4de65fa22da912bdb395011e6dc8e630f070348b7b3fdd885d/codeowners-0.7.0-py3-none-any.whl", hash = "sha256:0df5cd47299f984ba2e120dc4a0a7be68b528d53016ff39d06e86f85e33c7fc2", size = 8718 },
+]
+
+[[package]]
+name = "colorama"
+version = "0.4.6"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 },
+]
+
+[[package]]
+name = "coverage"
+version = "7.6.11"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/89/4e/38141d42af7452f4b7c5d3d7442a8018de34754ef52eb9a400768bc8d59e/coverage-7.6.11.tar.gz", hash = "sha256:e642e6a46a04e992ebfdabed79e46f478ec60e2c528e1e1a074d63800eda4286", size = 805460 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/65/83/cf3d6ac06bd02e1fb7fc6609d7a3be799328a94938dd2a64cf091989b8ce/coverage-7.6.11-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:dbb1a822fd858d9853333a7c95d4e70dde9a79e65893138ce32c2ec6457d7a36", size = 208543 },
+ { url = "https://files.pythonhosted.org/packages/e7/e1/b1448995072ab033898758179e208afa924f4625ea4524ec868fafbae77d/coverage-7.6.11-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:61c834cbb80946d6ebfddd9b393a4c46bec92fcc0fa069321fcb8049117f76ea", size = 208805 },
+ { url = "https://files.pythonhosted.org/packages/80/22/11ae7726086bf16ad35ecd1ebf31c0c709647b2618977bc088003bd38808/coverage-7.6.11-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a46d56e99a31d858d6912d31ffa4ede6a325c86af13139539beefca10a1234ce", size = 239768 },
+ { url = "https://files.pythonhosted.org/packages/7d/68/717286bda6530f39f3ac16899dac1855a71921aca5ee565484269326c979/coverage-7.6.11-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5b48db06f53d1864fea6dbd855e6d51d41c0f06c212c3004511c0bdc6847b297", size = 242023 },
+ { url = "https://files.pythonhosted.org/packages/93/57/4b028c7c882411d9ca3f12cd4223ceeb5cb39f84bb91c4fb21a06440cbd9/coverage-7.6.11-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b6ff5be3b1853e0862da9d349fe87f869f68e63a25f7c37ce1130b321140f963", size = 239610 },
+ { url = "https://files.pythonhosted.org/packages/44/88/720c9eba316406f243670237306bcdb8e269e4d0e12b191a697f66369404/coverage-7.6.11-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:be05bde21d5e6eefbc3a6de6b9bee2b47894b8945342e8663192809c4d1f08ce", size = 241212 },
+ { url = "https://files.pythonhosted.org/packages/1d/ae/a09edf77bd535d597de13679262845f5cb6ff1fab37a3065640fb3d5e6e8/coverage-7.6.11-cp312-cp312-win32.whl", hash = "sha256:e3b746fa0ffc5b6b8856529de487da8b9aeb4fb394bb58de6502ef45f3434f12", size = 211186 },
+ { url = "https://files.pythonhosted.org/packages/80/5d/63ad5e3f1421504194da0228d259a3913884830999d1297b5e16b59bcb0f/coverage-7.6.11-cp312-cp312-win_amd64.whl", hash = "sha256:ac476e6d0128fb7919b3fae726de72b28b5c9644cb4b579e4a523d693187c551", size = 211974 },
+ { url = "https://files.pythonhosted.org/packages/8b/83/096a4954b686212b4e8d3ef14e01370e111b44972370fcc26169e3b32757/coverage-7.6.11-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c86f4c7a6d1a54a24d804d9684d96e36a62d3ef7c0d7745ae2ea39e3e0293251", size = 208568 },
+ { url = "https://files.pythonhosted.org/packages/bc/78/74f5f1545b06524a3c9c36be339fa1ebbc17eef182c961fbed91cd0805e1/coverage-7.6.11-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7eb0504bb307401fd08bc5163a351df301438b3beb88a4fa044681295bbefc67", size = 208839 },
+ { url = "https://files.pythonhosted.org/packages/6a/4b/df3433cbb9a91cb3f5ea8301bef312a8e77587881e2dea93f2d58683908e/coverage-7.6.11-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca95d40900cf614e07f00cee8c2fad0371df03ca4d7a80161d84be2ec132b7a4", size = 242383 },
+ { url = "https://files.pythonhosted.org/packages/40/22/681a1b724866f12b96bf46d178e0d5df557bb9c3da43aa2a8be67a4be65e/coverage-7.6.11-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:db4b1a69976b1b02acda15937538a1d3fe10b185f9d99920b17a740a0a102e06", size = 239424 },
+ { url = "https://files.pythonhosted.org/packages/29/08/978e14dca15fec135b13246cd5cbbedc6506d8102854f4bdde73038efaa3/coverage-7.6.11-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4cf96beb05d004e4c51cd846fcdf9eee9eb2681518524b66b2e7610507944c2f", size = 241440 },
+ { url = "https://files.pythonhosted.org/packages/a6/34/39fc8ad65d6381d1e8278f9042ff4e201a2cb52092d705d7a02ffc8ccc1b/coverage-7.6.11-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:08e5fb93576a6b054d3d326242af5ef93daaac9bb52bc25f12ccbc3fa94227cd", size = 241076 },
+ { url = "https://files.pythonhosted.org/packages/13/6b/392fa652391bf6751766921a7b29f576a3de1db78b8d48e1f438ce0121b4/coverage-7.6.11-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:25575cd5a7d2acc46b42711e8aff826027c0e4f80fb38028a74f31ac22aae69d", size = 239186 },
+ { url = "https://files.pythonhosted.org/packages/3d/ad/6c0edcd7ee9b7ceddcfda45aeea2b84ef017d19bde27fe3de51deab6468a/coverage-7.6.11-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8fa4fffd90ee92f62ff7404b4801b59e8ea8502e19c9bf2d3241ce745b52926c", size = 240928 },
+ { url = "https://files.pythonhosted.org/packages/e7/7c/f4f38aa65aad6d2f0ec3ba2a1d50a06f4c8c2d3516761d4eaff332ec14d7/coverage-7.6.11-cp313-cp313-win32.whl", hash = "sha256:0d03c9452d9d1ccfe5d3a5df0427705022a49b356ac212d529762eaea5ef97b4", size = 211211 },
+ { url = "https://files.pythonhosted.org/packages/c1/c1/2003bf96e799e5414be7aac2dae14bcc463067f7d8d40d69e33a82c352e6/coverage-7.6.11-cp313-cp313-win_amd64.whl", hash = "sha256:fd2fffc8ce8692ce540103dff26279d2af22d424516ddebe2d7e4d6dbb3816b2", size = 211995 },
+ { url = "https://files.pythonhosted.org/packages/e3/7c/8c71cf43a68d09772408182177394d1f3aafe8ec45c88bd0702efc9e5640/coverage-7.6.11-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:5e7ac966ab110bd94ee844f2643f196d78fde1cd2450399116d3efdd706e19f5", size = 209408 },
+ { url = "https://files.pythonhosted.org/packages/17/74/25a3f0e9745cab1120a641240074eb9e77d3278e9b2e6b53d4ba5b6ae1f0/coverage-7.6.11-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:6ba27a0375c5ef4d2a7712f829265102decd5ff78b96d342ac2fa555742c4f4f", size = 209629 },
+ { url = "https://files.pythonhosted.org/packages/f6/e4/22d61ef97964ec28246a8487fa117568b7ef225913de43621b86ad6d2446/coverage-7.6.11-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2778be4f574b39ec9dcd9e5e13644f770351ee0990a0ecd27e364aba95af89b", size = 253884 },
+ { url = "https://files.pythonhosted.org/packages/44/3b/c272005a36f28374c76d4cef63e4ff1824b33eb6970ce2cea2c5293a8119/coverage-7.6.11-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5edc16712187139ab635a2e644cc41fc239bc6d245b16124045743130455c652", size = 249592 },
+ { url = "https://files.pythonhosted.org/packages/cf/4f/d9daa13ebad04a22e9f48a8619aa27380961fefc20e15e5bf3f7d6325fd1/coverage-7.6.11-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:df6ff122a0a10a30121d9f0cb3fbd03a6fe05861e4ec47adb9f25e9245aabc19", size = 251928 },
+ { url = "https://files.pythonhosted.org/packages/a7/52/42b5b3bde8b0fbc268fc8809b775caffb1ebc51555d04ad979e824b84f9a/coverage-7.6.11-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:ff562952f15eff27247a4c4b03e45ce8a82e3fb197de6a7c54080f9d4ba07845", size = 251431 },
+ { url = "https://files.pythonhosted.org/packages/ef/0e/efb47cd1a2279acc1c05966a441f1658564ec81fa331a9420aef54997bfc/coverage-7.6.11-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:4f21e3617f48d683f30cf2a6c8b739c838e600cb1454fe6b2eb486ac2bce8fbd", size = 249089 },
+ { url = "https://files.pythonhosted.org/packages/ea/65/bd348b3d0da43ad6a2e70c3bd9bffde2ef680c2987a2ea8b19f189a83cae/coverage-7.6.11-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6d60577673ba48d8ae8e362e61fd4ad1a640293ffe8991d11c86f195479100b7", size = 250526 },
+ { url = "https://files.pythonhosted.org/packages/f8/b8/b2ba25ebda1f3e149d679b0468eda846cfba5d48f8c2f9e0b565c0cdbb91/coverage-7.6.11-cp313-cp313t-win32.whl", hash = "sha256:13100f98497086b359bf56fc035a762c674de8ef526daa389ac8932cb9bff1e0", size = 211929 },
+ { url = "https://files.pythonhosted.org/packages/0a/97/ad0cc489eddd0ffdb1b873a39182834d6119d8e1f6ee5ce760345a573971/coverage-7.6.11-cp313-cp313t-win_amd64.whl", hash = "sha256:2c81e53782043b323bd34c7de711ed9b4673414eb517eaf35af92185b873839c", size = 213138 },
+ { url = "https://files.pythonhosted.org/packages/24/f3/63cd48409a519d4f6cf79abc6c89103a8eabc5c93e496f40779269dba0c0/coverage-7.6.11-py3-none-any.whl", hash = "sha256:f0f334ae844675420164175bf32b04e18a81fe57ad8eb7e0cfd4689d681ffed7", size = 200446 },
+]
+
+[[package]]
+name = "cryptography"
+version = "44.0.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "cffi", marker = "platform_python_implementation != 'PyPy'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/91/4c/45dfa6829acffa344e3967d6006ee4ae8be57af746ae2eba1c431949b32c/cryptography-44.0.0.tar.gz", hash = "sha256:cd4e834f340b4293430701e772ec543b0fbe6c2dea510a5286fe0acabe153a02", size = 710657 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/55/09/8cc67f9b84730ad330b3b72cf867150744bf07ff113cda21a15a1c6d2c7c/cryptography-44.0.0-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:84111ad4ff3f6253820e6d3e58be2cc2a00adb29335d4cacb5ab4d4d34f2a123", size = 6541833 },
+ { url = "https://files.pythonhosted.org/packages/7e/5b/3759e30a103144e29632e7cb72aec28cedc79e514b2ea8896bb17163c19b/cryptography-44.0.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b15492a11f9e1b62ba9d73c210e2416724633167de94607ec6069ef724fad092", size = 3922710 },
+ { url = "https://files.pythonhosted.org/packages/5f/58/3b14bf39f1a0cfd679e753e8647ada56cddbf5acebffe7db90e184c76168/cryptography-44.0.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:831c3c4d0774e488fdc83a1923b49b9957d33287de923d58ebd3cec47a0ae43f", size = 4137546 },
+ { url = "https://files.pythonhosted.org/packages/98/65/13d9e76ca19b0ba5603d71ac8424b5694415b348e719db277b5edc985ff5/cryptography-44.0.0-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:761817a3377ef15ac23cd7834715081791d4ec77f9297ee694ca1ee9c2c7e5eb", size = 3915420 },
+ { url = "https://files.pythonhosted.org/packages/b1/07/40fe09ce96b91fc9276a9ad272832ead0fddedcba87f1190372af8e3039c/cryptography-44.0.0-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3c672a53c0fb4725a29c303be906d3c1fa99c32f58abe008a82705f9ee96f40b", size = 4154498 },
+ { url = "https://files.pythonhosted.org/packages/75/ea/af65619c800ec0a7e4034207aec543acdf248d9bffba0533342d1bd435e1/cryptography-44.0.0-cp37-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:4ac4c9f37eba52cb6fbeaf5b59c152ea976726b865bd4cf87883a7e7006cc543", size = 3932569 },
+ { url = "https://files.pythonhosted.org/packages/c7/af/d1deb0c04d59612e3d5e54203159e284d3e7a6921e565bb0eeb6269bdd8a/cryptography-44.0.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ed3534eb1090483c96178fcb0f8893719d96d5274dfde98aa6add34614e97c8e", size = 4016721 },
+ { url = "https://files.pythonhosted.org/packages/bd/69/7ca326c55698d0688db867795134bdfac87136b80ef373aaa42b225d6dd5/cryptography-44.0.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:f3f6fdfa89ee2d9d496e2c087cebef9d4fcbb0ad63c40e821b39f74bf48d9c5e", size = 4240915 },
+ { url = "https://files.pythonhosted.org/packages/ef/d4/cae11bf68c0f981e0413906c6dd03ae7fa864347ed5fac40021df1ef467c/cryptography-44.0.0-cp37-abi3-win32.whl", hash = "sha256:eb33480f1bad5b78233b0ad3e1b0be21e8ef1da745d8d2aecbb20671658b9053", size = 2757925 },
+ { url = "https://files.pythonhosted.org/packages/64/b1/50d7739254d2002acae64eed4fc43b24ac0cc44bf0a0d388d1ca06ec5bb1/cryptography-44.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:abc998e0c0eee3c8a1904221d3f67dcfa76422b23620173e28c11d3e626c21bd", size = 3202055 },
+ { url = "https://files.pythonhosted.org/packages/11/18/61e52a3d28fc1514a43b0ac291177acd1b4de00e9301aaf7ef867076ff8a/cryptography-44.0.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:660cb7312a08bc38be15b696462fa7cc7cd85c3ed9c576e81f4dc4d8b2b31591", size = 6542801 },
+ { url = "https://files.pythonhosted.org/packages/1a/07/5f165b6c65696ef75601b781a280fc3b33f1e0cd6aa5a92d9fb96c410e97/cryptography-44.0.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1923cb251c04be85eec9fda837661c67c1049063305d6be5721643c22dd4e2b7", size = 3922613 },
+ { url = "https://files.pythonhosted.org/packages/28/34/6b3ac1d80fc174812486561cf25194338151780f27e438526f9c64e16869/cryptography-44.0.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:404fdc66ee5f83a1388be54300ae978b2efd538018de18556dde92575e05defc", size = 4137925 },
+ { url = "https://files.pythonhosted.org/packages/d0/c7/c656eb08fd22255d21bc3129625ed9cd5ee305f33752ef2278711b3fa98b/cryptography-44.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:c5eb858beed7835e5ad1faba59e865109f3e52b3783b9ac21e7e47dc5554e289", size = 3915417 },
+ { url = "https://files.pythonhosted.org/packages/ef/82/72403624f197af0db6bac4e58153bc9ac0e6020e57234115db9596eee85d/cryptography-44.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f53c2c87e0fb4b0c00fa9571082a057e37690a8f12233306161c8f4b819960b7", size = 4155160 },
+ { url = "https://files.pythonhosted.org/packages/a2/cd/2f3c440913d4329ade49b146d74f2e9766422e1732613f57097fea61f344/cryptography-44.0.0-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:9e6fc8a08e116fb7c7dd1f040074c9d7b51d74a8ea40d4df2fc7aa08b76b9e6c", size = 3932331 },
+ { url = "https://files.pythonhosted.org/packages/7f/df/8be88797f0a1cca6e255189a57bb49237402b1880d6e8721690c5603ac23/cryptography-44.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:d2436114e46b36d00f8b72ff57e598978b37399d2786fd39793c36c6d5cb1c64", size = 4017372 },
+ { url = "https://files.pythonhosted.org/packages/af/36/5ccc376f025a834e72b8e52e18746b927f34e4520487098e283a719c205e/cryptography-44.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a01956ddfa0a6790d594f5b34fc1bfa6098aca434696a03cfdbe469b8ed79285", size = 4239657 },
+ { url = "https://files.pythonhosted.org/packages/46/b0/f4f7d0d0bcfbc8dd6296c1449be326d04217c57afb8b2594f017eed95533/cryptography-44.0.0-cp39-abi3-win32.whl", hash = "sha256:eca27345e1214d1b9f9490d200f9db5a874479be914199194e746c893788d417", size = 2758672 },
+ { url = "https://files.pythonhosted.org/packages/97/9b/443270b9210f13f6ef240eff73fd32e02d381e7103969dc66ce8e89ee901/cryptography-44.0.0-cp39-abi3-win_amd64.whl", hash = "sha256:708ee5f1bafe76d041b53a4f95eb28cdeb8d18da17e597d46d7833ee59b97ede", size = 3202071 },
+]
+
+[[package]]
+name = "dataclasses-json"
+version = "0.6.7"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "marshmallow" },
+ { name = "typing-inspect" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/64/a4/f71d9cf3a5ac257c993b5ca3f93df5f7fb395c725e7f1e6479d2514173c3/dataclasses_json-0.6.7.tar.gz", hash = "sha256:b6b3e528266ea45b9535223bc53ca645f5208833c29229e847b3f26a1cc55fc0", size = 32227 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c3/be/d0d44e092656fe7a06b55e6103cbce807cdbdee17884a5367c68c9860853/dataclasses_json-0.6.7-py3-none-any.whl", hash = "sha256:0dbf33f26c8d5305befd61b39d2b3414e8a407bedc2834dea9b8d642666fb40a", size = 28686 },
+]
+
+[[package]]
+name = "datamodel-code-generator"
+version = "0.27.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "argcomplete" },
+ { name = "black" },
+ { name = "genson" },
+ { name = "inflect" },
+ { name = "isort" },
+ { name = "jinja2" },
+ { name = "packaging" },
+ { name = "pydantic" },
+ { name = "pyyaml" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/8c/49/9cb4f868856304dd4e2fc0795d848889a7c9c6f2539165ad24977cef0da3/datamodel_code_generator-0.27.2.tar.gz", hash = "sha256:1a7655f5fd3a61329b57534904f5c40dd850850e420696fd946ec7a4f59c32b8", size = 436345 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/73/a0/678f10ecc40f1cce3c170246c3dd1b86735867d2844eb9f4596abf187dac/datamodel_code_generator-0.27.2-py3-none-any.whl", hash = "sha256:efcbfbe6a1488d3411fc588b1ce1af5f854f5107810b1cc9026a6d6333a7c4d8", size = 115483 },
+]
+
+[[package]]
+name = "deprecated"
+version = "1.2.18"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "wrapt" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/98/97/06afe62762c9a8a86af0cfb7bfdab22a43ad17138b07af5b1a58442690a2/deprecated-1.2.18.tar.gz", hash = "sha256:422b6f6d859da6f2ef57857761bfb392480502a64c3028ca9bbe86085d72115d", size = 2928744 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/6e/c6/ac0b6c1e2d138f1002bcf799d330bd6d85084fece321e662a14223794041/Deprecated-1.2.18-py2.py3-none-any.whl", hash = "sha256:bd5011788200372a32418f888e326a09ff80d0214bd961147cfed01b5c018eec", size = 9998 },
+]
+
+[[package]]
+name = "deptry"
+version = "0.23.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "click" },
+ { name = "colorama", marker = "sys_platform == 'win32'" },
+ { name = "packaging" },
+ { name = "requirements-parser" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/52/7e/75a1990a7244a3d3c5364353ac76f1173aa568a67793199d09f995b66c29/deptry-0.23.0.tar.gz", hash = "sha256:4915a3590ccf38ad7a9176aee376745aa9de121f50f8da8fb9ccec87fa93e676", size = 200920 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/d6/85/a8b77c8a87e7c9e81ce8437d752879b5281fd8a0b8a114c6d393f980aa72/deptry-0.23.0-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:1f2a6817a37d76e8f6b667381b7caf6ea3e6d6c18b5be24d36c625f387c79852", size = 1756706 },
+ { url = "https://files.pythonhosted.org/packages/53/bf/26c58af1467df6e889c6b969c27dad2c67b8bd625320d9db7d70277a222f/deptry-0.23.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:9601b64cc0aed42687fdd5c912d5f1e90d7f7333fb589b14e35bfdfebae866f3", size = 1657001 },
+ { url = "https://files.pythonhosted.org/packages/ae/7d/b0bd6a50ec3f87b0a5ed3bff64ac2bd5bd8d3205e570bc5bc3170f26a01f/deptry-0.23.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e6172b2205f6e84bcc9df25226693d4deb9576a6f746c2ace828f6d13401d357", size = 1754607 },
+ { url = "https://files.pythonhosted.org/packages/e6/1b/79b1213bb9b58b0bcc200867cd6d64cd76ec4b9c5cdb76f95c3e6ee7b92e/deptry-0.23.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1cfa4b3a46ee8a026eaa38e4b9ba43fe6036a07fe16bf0a663cb611b939f6af8", size = 1831961 },
+ { url = "https://files.pythonhosted.org/packages/09/d6/607004f20637987d437f420f3dad4d6f1a87a4a83380ab60220397ee8fbe/deptry-0.23.0-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:9d03cc99a61c348df92074a50e0a71b28f264f0edbf686084ca90e6fd44e3abe", size = 1932126 },
+ { url = "https://files.pythonhosted.org/packages/ff/ff/6fff20bf2632727af55dc3a24a6f5634dcdf34fd785402a55207ba49d9cc/deptry-0.23.0-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:9a46f78098f145100dc582a59af8548b26cdfa16cf0fbd85d2d44645e724cb6a", size = 2004755 },
+ { url = "https://files.pythonhosted.org/packages/41/30/1b6217bdccf2144d4c3e78f89b2a84db82478b2449599c2d3b4b21a89043/deptry-0.23.0-cp39-abi3-win_amd64.whl", hash = "sha256:d53e803b280791d89a051b6183d9dc40411200e22a8ab7e6c32c6b169822a664", size = 1606944 },
+ { url = "https://files.pythonhosted.org/packages/28/ab/47398041d11b19aa9db28f28cf076dbe42aba3e16d67d3e7911330e3a304/deptry-0.23.0-cp39-abi3-win_arm64.whl", hash = "sha256:da7678624f4626d839c8c03675452cefc59d6cf57d25c84a9711dae514719279", size = 1518394 },
+]
+
+[[package]]
+name = "dicttoxml"
+version = "1.7.16"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/ee/c9/3132427f9e64d572688e6a1cbe3d542d1a03f676b81fb600f3d1fd7d2ec5/dicttoxml-1.7.16.tar.gz", hash = "sha256:6f36ce644881db5cd8940bee9b7cb3f3f6b7b327ba8a67d83d3e2caa0538bf9d", size = 39314 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/09/40/9d521973cae7f7ef8b1f0d0e28a3db0f851c1f1dca45d4c2ed5360bb7246/dicttoxml-1.7.16-py3-none-any.whl", hash = "sha256:8677671496d0d38e66c7179f82a7e9059f94887777955dc71b0ac602ee637c26", size = 24155 },
+]
+
+[[package]]
+name = "distlib"
+version = "0.3.9"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/0d/dd/1bec4c5ddb504ca60fc29472f3d27e8d4da1257a854e1d96742f15c1d02d/distlib-0.3.9.tar.gz", hash = "sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403", size = 613923 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/91/a1/cf2472db20f7ce4a6be1253a81cfdf85ad9c7885ffbed7047fb72c24cf87/distlib-0.3.9-py2.py3-none-any.whl", hash = "sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87", size = 468973 },
+]
+
+[[package]]
+name = "distro"
+version = "1.9.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/fc/f8/98eea607f65de6527f8a2e8885fc8015d3e6f5775df186e443e0964a11c3/distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed", size = 60722 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277 },
+]
+
+[[package]]
+name = "dnspython"
+version = "2.7.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/b5/4a/263763cb2ba3816dd94b08ad3a33d5fdae34ecb856678773cc40a3605829/dnspython-2.7.0.tar.gz", hash = "sha256:ce9c432eda0dc91cf618a5cedf1a4e142651196bbcd2c80e89ed5a907e5cfaf1", size = 345197 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/68/1b/e0a87d256e40e8c888847551b20a017a6b98139178505dc7ffb96f04e954/dnspython-2.7.0-py3-none-any.whl", hash = "sha256:b4c34b7d10b51bcc3a5071e7b8dee77939f1e878477eeecc965e9835f63c6c86", size = 313632 },
+]
+
+[[package]]
+name = "docstring-parser"
+version = "0.16"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/08/12/9c22a58c0b1e29271051222d8906257616da84135af9ed167c9e28f85cb3/docstring_parser-0.16.tar.gz", hash = "sha256:538beabd0af1e2db0146b6bd3caa526c35a34d61af9fd2887f3a8a27a739aa6e", size = 26565 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/d5/7c/e9fcff7623954d86bdc17782036cbf715ecab1bec4847c008557affe1ca8/docstring_parser-0.16-py3-none-any.whl", hash = "sha256:bf0a1387354d3691d102edef7ec124f219ef639982d096e26e3b60aeffa90637", size = 36533 },
+]
+
+[[package]]
+name = "dotty-dict"
+version = "1.3.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/6a/ab/88d67f02024700b48cd8232579ad1316aa9df2272c63049c27cc094229d6/dotty_dict-1.3.1.tar.gz", hash = "sha256:4b016e03b8ae265539757a53eba24b9bfda506fb94fbce0bee843c6f05541a15", size = 7699 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/1a/91/e0d457ee03ec33d79ee2cd8d212debb1bc21dfb99728ae35efdb5832dc22/dotty_dict-1.3.1-py3-none-any.whl", hash = "sha256:5022d234d9922f13aa711b4950372a06a6d64cb6d6db9ba43d0ba133ebfce31f", size = 7014 },
+]
+
+[[package]]
+name = "email-validator"
+version = "2.2.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "dnspython" },
+ { name = "idna" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/48/ce/13508a1ec3f8bb981ae4ca79ea40384becc868bfae97fd1c942bb3a001b1/email_validator-2.2.0.tar.gz", hash = "sha256:cb690f344c617a714f22e66ae771445a1ceb46821152df8e165c5f9a364582b7", size = 48967 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/d7/ee/bf0adb559ad3c786f12bcbc9296b3f5675f529199bef03e2df281fa1fadb/email_validator-2.2.0-py3-none-any.whl", hash = "sha256:561977c2d73ce3611850a06fa56b414621e0c8faa9d66f2611407d87465da631", size = 33521 },
+]
+
+[[package]]
+name = "fastapi"
+version = "0.115.8"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "pydantic" },
+ { name = "starlette" },
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/a2/b2/5a5dc4affdb6661dea100324e19a7721d5dc524b464fe8e366c093fd7d87/fastapi-0.115.8.tar.gz", hash = "sha256:0ce9111231720190473e222cdf0f07f7206ad7e53ea02beb1d2dc36e2f0741e9", size = 295403 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/8f/7d/2d6ce181d7a5f51dedb8c06206cbf0ec026a99bf145edd309f9e17c3282f/fastapi-0.115.8-py3-none-any.whl", hash = "sha256:753a96dd7e036b34eeef8babdfcfe3f28ff79648f86551eb36bfc1b0bf4a8cbf", size = 94814 },
+]
+
+[package.optional-dependencies]
+standard = [
+ { name = "email-validator" },
+ { name = "fastapi-cli", extra = ["standard"] },
+ { name = "httpx" },
+ { name = "jinja2" },
+ { name = "python-multipart" },
+ { name = "uvicorn", extra = ["standard"] },
+]
+
+[[package]]
+name = "fastapi-cli"
+version = "0.0.7"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "rich-toolkit" },
+ { name = "typer" },
+ { name = "uvicorn", extra = ["standard"] },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/fe/73/82a5831fbbf8ed75905bacf5b2d9d3dfd6f04d6968b29fe6f72a5ae9ceb1/fastapi_cli-0.0.7.tar.gz", hash = "sha256:02b3b65956f526412515907a0793c9094abd4bfb5457b389f645b0ea6ba3605e", size = 16753 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/a1/e6/5daefc851b514ce2287d8f5d358ae4341089185f78f3217a69d0ce3a390c/fastapi_cli-0.0.7-py3-none-any.whl", hash = "sha256:d549368ff584b2804336c61f192d86ddea080c11255f375959627911944804f4", size = 10705 },
+]
+
+[package.optional-dependencies]
+standard = [
+ { name = "uvicorn", extra = ["standard"] },
+]
+
+[[package]]
+name = "filelock"
+version = "3.17.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/dc/9c/0b15fb47b464e1b663b1acd1253a062aa5feecb07d4e597daea542ebd2b5/filelock-3.17.0.tar.gz", hash = "sha256:ee4e77401ef576ebb38cd7f13b9b28893194acc20a8e68e18730ba9c0e54660e", size = 18027 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/89/ec/00d68c4ddfedfe64159999e5f8a98fb8442729a63e2077eb9dcd89623d27/filelock-3.17.0-py3-none-any.whl", hash = "sha256:533dc2f7ba78dc2f0f531fc6c4940addf7b70a481e269a5a3b93be94ffbe8338", size = 16164 },
+]
+
+[[package]]
+name = "frozenlist"
+version = "1.5.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/8f/ed/0f4cec13a93c02c47ec32d81d11c0c1efbadf4a471e3f3ce7cad366cbbd3/frozenlist-1.5.0.tar.gz", hash = "sha256:81d5af29e61b9c8348e876d442253723928dce6433e0e76cd925cd83f1b4b817", size = 39930 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/79/73/fa6d1a96ab7fd6e6d1c3500700963eab46813847f01ef0ccbaa726181dd5/frozenlist-1.5.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:31115ba75889723431aa9a4e77d5f398f5cf976eea3bdf61749731f62d4a4a21", size = 94026 },
+ { url = "https://files.pythonhosted.org/packages/ab/04/ea8bf62c8868b8eada363f20ff1b647cf2e93377a7b284d36062d21d81d1/frozenlist-1.5.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7437601c4d89d070eac8323f121fcf25f88674627505334654fd027b091db09d", size = 54150 },
+ { url = "https://files.pythonhosted.org/packages/d0/9a/8e479b482a6f2070b26bda572c5e6889bb3ba48977e81beea35b5ae13ece/frozenlist-1.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7948140d9f8ece1745be806f2bfdf390127cf1a763b925c4a805c603df5e697e", size = 51927 },
+ { url = "https://files.pythonhosted.org/packages/e3/12/2aad87deb08a4e7ccfb33600871bbe8f0e08cb6d8224371387f3303654d7/frozenlist-1.5.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:feeb64bc9bcc6b45c6311c9e9b99406660a9c05ca8a5b30d14a78555088b0b3a", size = 282647 },
+ { url = "https://files.pythonhosted.org/packages/77/f2/07f06b05d8a427ea0060a9cef6e63405ea9e0d761846b95ef3fb3be57111/frozenlist-1.5.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:683173d371daad49cffb8309779e886e59c2f369430ad28fe715f66d08d4ab1a", size = 289052 },
+ { url = "https://files.pythonhosted.org/packages/bd/9f/8bf45a2f1cd4aa401acd271b077989c9267ae8463e7c8b1eb0d3f561b65e/frozenlist-1.5.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7d57d8f702221405a9d9b40f9da8ac2e4a1a8b5285aac6100f3393675f0a85ee", size = 291719 },
+ { url = "https://files.pythonhosted.org/packages/41/d1/1f20fd05a6c42d3868709b7604c9f15538a29e4f734c694c6bcfc3d3b935/frozenlist-1.5.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:30c72000fbcc35b129cb09956836c7d7abf78ab5416595e4857d1cae8d6251a6", size = 267433 },
+ { url = "https://files.pythonhosted.org/packages/af/f2/64b73a9bb86f5a89fb55450e97cd5c1f84a862d4ff90d9fd1a73ab0f64a5/frozenlist-1.5.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:000a77d6034fbad9b6bb880f7ec073027908f1b40254b5d6f26210d2dab1240e", size = 283591 },
+ { url = "https://files.pythonhosted.org/packages/29/e2/ffbb1fae55a791fd6c2938dd9ea779509c977435ba3940b9f2e8dc9d5316/frozenlist-1.5.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5d7f5a50342475962eb18b740f3beecc685a15b52c91f7d975257e13e029eca9", size = 273249 },
+ { url = "https://files.pythonhosted.org/packages/2e/6e/008136a30798bb63618a114b9321b5971172a5abddff44a100c7edc5ad4f/frozenlist-1.5.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:87f724d055eb4785d9be84e9ebf0f24e392ddfad00b3fe036e43f489fafc9039", size = 271075 },
+ { url = "https://files.pythonhosted.org/packages/ae/f0/4e71e54a026b06724cec9b6c54f0b13a4e9e298cc8db0f82ec70e151f5ce/frozenlist-1.5.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:6e9080bb2fb195a046e5177f10d9d82b8a204c0736a97a153c2466127de87784", size = 285398 },
+ { url = "https://files.pythonhosted.org/packages/4d/36/70ec246851478b1c0b59f11ef8ade9c482ff447c1363c2bd5fad45098b12/frozenlist-1.5.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9b93d7aaa36c966fa42efcaf716e6b3900438632a626fb09c049f6a2f09fc631", size = 294445 },
+ { url = "https://files.pythonhosted.org/packages/37/e0/47f87544055b3349b633a03c4d94b405956cf2437f4ab46d0928b74b7526/frozenlist-1.5.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:52ef692a4bc60a6dd57f507429636c2af8b6046db8b31b18dac02cbc8f507f7f", size = 280569 },
+ { url = "https://files.pythonhosted.org/packages/f9/7c/490133c160fb6b84ed374c266f42800e33b50c3bbab1652764e6e1fc498a/frozenlist-1.5.0-cp312-cp312-win32.whl", hash = "sha256:29d94c256679247b33a3dc96cce0f93cbc69c23bf75ff715919332fdbb6a32b8", size = 44721 },
+ { url = "https://files.pythonhosted.org/packages/b1/56/4e45136ffc6bdbfa68c29ca56ef53783ef4c2fd395f7cbf99a2624aa9aaa/frozenlist-1.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:8969190d709e7c48ea386db202d708eb94bdb29207a1f269bab1196ce0dcca1f", size = 51329 },
+ { url = "https://files.pythonhosted.org/packages/da/3b/915f0bca8a7ea04483622e84a9bd90033bab54bdf485479556c74fd5eaf5/frozenlist-1.5.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:7a1a048f9215c90973402e26c01d1cff8a209e1f1b53f72b95c13db61b00f953", size = 91538 },
+ { url = "https://files.pythonhosted.org/packages/c7/d1/a7c98aad7e44afe5306a2b068434a5830f1470675f0e715abb86eb15f15b/frozenlist-1.5.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:dd47a5181ce5fcb463b5d9e17ecfdb02b678cca31280639255ce9d0e5aa67af0", size = 52849 },
+ { url = "https://files.pythonhosted.org/packages/3a/c8/76f23bf9ab15d5f760eb48701909645f686f9c64fbb8982674c241fbef14/frozenlist-1.5.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1431d60b36d15cda188ea222033eec8e0eab488f39a272461f2e6d9e1a8e63c2", size = 50583 },
+ { url = "https://files.pythonhosted.org/packages/1f/22/462a3dd093d11df623179d7754a3b3269de3b42de2808cddef50ee0f4f48/frozenlist-1.5.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6482a5851f5d72767fbd0e507e80737f9c8646ae7fd303def99bfe813f76cf7f", size = 265636 },
+ { url = "https://files.pythonhosted.org/packages/80/cf/e075e407fc2ae7328155a1cd7e22f932773c8073c1fc78016607d19cc3e5/frozenlist-1.5.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:44c49271a937625619e862baacbd037a7ef86dd1ee215afc298a417ff3270608", size = 270214 },
+ { url = "https://files.pythonhosted.org/packages/a1/58/0642d061d5de779f39c50cbb00df49682832923f3d2ebfb0fedf02d05f7f/frozenlist-1.5.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:12f78f98c2f1c2429d42e6a485f433722b0061d5c0b0139efa64f396efb5886b", size = 273905 },
+ { url = "https://files.pythonhosted.org/packages/ab/66/3fe0f5f8f2add5b4ab7aa4e199f767fd3b55da26e3ca4ce2cc36698e50c4/frozenlist-1.5.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ce3aa154c452d2467487765e3adc730a8c153af77ad84096bc19ce19a2400840", size = 250542 },
+ { url = "https://files.pythonhosted.org/packages/f6/b8/260791bde9198c87a465224e0e2bb62c4e716f5d198fc3a1dacc4895dbd1/frozenlist-1.5.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9b7dc0c4338e6b8b091e8faf0db3168a37101943e687f373dce00959583f7439", size = 267026 },
+ { url = "https://files.pythonhosted.org/packages/2e/a4/3d24f88c527f08f8d44ade24eaee83b2627793fa62fa07cbb7ff7a2f7d42/frozenlist-1.5.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:45e0896250900b5aa25180f9aec243e84e92ac84bd4a74d9ad4138ef3f5c97de", size = 257690 },
+ { url = "https://files.pythonhosted.org/packages/de/9a/d311d660420b2beeff3459b6626f2ab4fb236d07afbdac034a4371fe696e/frozenlist-1.5.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:561eb1c9579d495fddb6da8959fd2a1fca2c6d060d4113f5844b433fc02f2641", size = 253893 },
+ { url = "https://files.pythonhosted.org/packages/c6/23/e491aadc25b56eabd0f18c53bb19f3cdc6de30b2129ee0bc39cd387cd560/frozenlist-1.5.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:df6e2f325bfee1f49f81aaac97d2aa757c7646534a06f8f577ce184afe2f0a9e", size = 267006 },
+ { url = "https://files.pythonhosted.org/packages/08/c4/ab918ce636a35fb974d13d666dcbe03969592aeca6c3ab3835acff01f79c/frozenlist-1.5.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:140228863501b44b809fb39ec56b5d4071f4d0aa6d216c19cbb08b8c5a7eadb9", size = 276157 },
+ { url = "https://files.pythonhosted.org/packages/c0/29/3b7a0bbbbe5a34833ba26f686aabfe982924adbdcafdc294a7a129c31688/frozenlist-1.5.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7707a25d6a77f5d27ea7dc7d1fc608aa0a478193823f88511ef5e6b8a48f9d03", size = 264642 },
+ { url = "https://files.pythonhosted.org/packages/ab/42/0595b3dbffc2e82d7fe658c12d5a5bafcd7516c6bf2d1d1feb5387caa9c1/frozenlist-1.5.0-cp313-cp313-win32.whl", hash = "sha256:31a9ac2b38ab9b5a8933b693db4939764ad3f299fcaa931a3e605bc3460e693c", size = 44914 },
+ { url = "https://files.pythonhosted.org/packages/17/c4/b7db1206a3fea44bf3b838ca61deb6f74424a8a5db1dd53ecb21da669be6/frozenlist-1.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:11aabdd62b8b9c4b84081a3c246506d1cddd2dd93ff0ad53ede5defec7886b28", size = 51167 },
+ { url = "https://files.pythonhosted.org/packages/c6/c8/a5be5b7550c10858fcf9b0ea054baccab474da77d37f1e828ce043a3a5d4/frozenlist-1.5.0-py3-none-any.whl", hash = "sha256:d994863bba198a4a518b467bb971c56e1db3f180a25c6cf7bb1949c267f748c3", size = 11901 },
+]
+
+[[package]]
+name = "fsspec"
+version = "2025.2.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/b5/79/68612ed99700e6413de42895aa725463e821a6b3be75c87fcce1b4af4c70/fsspec-2025.2.0.tar.gz", hash = "sha256:1c24b16eaa0a1798afa0337aa0db9b256718ab2a89c425371f5628d22c3b6afd", size = 292283 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/e2/94/758680531a00d06e471ef649e4ec2ed6bf185356a7f9fbfbb7368a40bd49/fsspec-2025.2.0-py3-none-any.whl", hash = "sha256:9de2ad9ce1f85e1931858535bc882543171d197001a0a5eb2ddc04f1781ab95b", size = 184484 },
+]
+
+[[package]]
+name = "genson"
+version = "1.3.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/c5/cf/2303c8ad276dcf5ee2ad6cf69c4338fd86ef0f471a5207b069adf7a393cf/genson-1.3.0.tar.gz", hash = "sha256:e02db9ac2e3fd29e65b5286f7135762e2cd8a986537c075b06fc5f1517308e37", size = 34919 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/f8/5c/e226de133afd8bb267ec27eead9ae3d784b95b39a287ed404caab39a5f50/genson-1.3.0-py3-none-any.whl", hash = "sha256:468feccd00274cc7e4c09e84b08704270ba8d95232aa280f65b986139cec67f7", size = 21470 },
+]
+
+[[package]]
+name = "gitdb"
+version = "4.0.12"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "smmap" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/72/94/63b0fc47eb32792c7ba1fe1b694daec9a63620db1e313033d18140c2320a/gitdb-4.0.12.tar.gz", hash = "sha256:5ef71f855d191a3326fcfbc0d5da835f26b13fbcba60c32c21091c349ffdb571", size = 394684 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl", hash = "sha256:67073e15955400952c6565cc3e707c554a4eea2e428946f7a4c162fab9bd9bcf", size = 62794 },
+]
+
+[[package]]
+name = "gitpython"
+version = "3.1.44"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "gitdb" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/c0/89/37df0b71473153574a5cdef8f242de422a0f5d26d7a9e231e6f169b4ad14/gitpython-3.1.44.tar.gz", hash = "sha256:c87e30b26253bf5418b01b0660f818967f3c503193838337fe5e573331249269", size = 214196 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/1d/9a/4114a9057db2f1462d5c8f8390ab7383925fe1ac012eaa42402ad65c2963/GitPython-3.1.44-py3-none-any.whl", hash = "sha256:9e0e10cda9bed1ee64bc9a6de50e7e38a9c9943241cd7f585f6df3ed28011110", size = 207599 },
+]
+
+[[package]]
+name = "giturlparse"
+version = "0.12.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/37/5f/543dc54c82842376139748226e5aa61eb95093992f63dd495af9c6b4f076/giturlparse-0.12.0.tar.gz", hash = "sha256:c0fff7c21acc435491b1779566e038757a205c1ffdcb47e4f81ea52ad8c3859a", size = 14907 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/dd/94/c6ff3388b8e3225a014e55aed957188639aa0966443e0408d38f0c9614a7/giturlparse-0.12.0-py2.py3-none-any.whl", hash = "sha256:412b74f2855f1da2fefa89fd8dde62df48476077a72fc19b62039554d27360eb", size = 15752 },
+]
+
+[[package]]
+name = "greenlet"
+version = "3.1.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/2f/ff/df5fede753cc10f6a5be0931204ea30c35fa2f2ea7a35b25bdaf4fe40e46/greenlet-3.1.1.tar.gz", hash = "sha256:4ce3ac6cdb6adf7946475d7ef31777c26d94bccc377e070a7986bd2d5c515467", size = 186022 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/7d/ec/bad1ac26764d26aa1353216fcbfa4670050f66d445448aafa227f8b16e80/greenlet-3.1.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:4afe7ea89de619adc868e087b4d2359282058479d7cfb94970adf4b55284574d", size = 274260 },
+ { url = "https://files.pythonhosted.org/packages/66/d4/c8c04958870f482459ab5956c2942c4ec35cac7fe245527f1039837c17a9/greenlet-3.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f406b22b7c9a9b4f8aa9d2ab13d6ae0ac3e85c9a809bd590ad53fed2bf70dc79", size = 649064 },
+ { url = "https://files.pythonhosted.org/packages/51/41/467b12a8c7c1303d20abcca145db2be4e6cd50a951fa30af48b6ec607581/greenlet-3.1.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c3a701fe5a9695b238503ce5bbe8218e03c3bcccf7e204e455e7462d770268aa", size = 663420 },
+ { url = "https://files.pythonhosted.org/packages/27/8f/2a93cd9b1e7107d5c7b3b7816eeadcac2ebcaf6d6513df9abaf0334777f6/greenlet-3.1.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2846930c65b47d70b9d178e89c7e1a69c95c1f68ea5aa0a58646b7a96df12441", size = 658035 },
+ { url = "https://files.pythonhosted.org/packages/57/5c/7c6f50cb12be092e1dccb2599be5a942c3416dbcfb76efcf54b3f8be4d8d/greenlet-3.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99cfaa2110534e2cf3ba31a7abcac9d328d1d9f1b95beede58294a60348fba36", size = 660105 },
+ { url = "https://files.pythonhosted.org/packages/f1/66/033e58a50fd9ec9df00a8671c74f1f3a320564c6415a4ed82a1c651654ba/greenlet-3.1.1-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1443279c19fca463fc33e65ef2a935a5b09bb90f978beab37729e1c3c6c25fe9", size = 613077 },
+ { url = "https://files.pythonhosted.org/packages/19/c5/36384a06f748044d06bdd8776e231fadf92fc896bd12cb1c9f5a1bda9578/greenlet-3.1.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b7cede291382a78f7bb5f04a529cb18e068dd29e0fb27376074b6d0317bf4dd0", size = 1135975 },
+ { url = "https://files.pythonhosted.org/packages/38/f9/c0a0eb61bdf808d23266ecf1d63309f0e1471f284300ce6dac0ae1231881/greenlet-3.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:23f20bb60ae298d7d8656c6ec6db134bca379ecefadb0b19ce6f19d1f232a942", size = 1163955 },
+ { url = "https://files.pythonhosted.org/packages/43/21/a5d9df1d21514883333fc86584c07c2b49ba7c602e670b174bd73cfc9c7f/greenlet-3.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:7124e16b4c55d417577c2077be379514321916d5790fa287c9ed6f23bd2ffd01", size = 299655 },
+ { url = "https://files.pythonhosted.org/packages/f3/57/0db4940cd7bb461365ca8d6fd53e68254c9dbbcc2b452e69d0d41f10a85e/greenlet-3.1.1-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:05175c27cb459dcfc05d026c4232f9de8913ed006d42713cb8a5137bd49375f1", size = 272990 },
+ { url = "https://files.pythonhosted.org/packages/1c/ec/423d113c9f74e5e402e175b157203e9102feeb7088cee844d735b28ef963/greenlet-3.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:935e943ec47c4afab8965954bf49bfa639c05d4ccf9ef6e924188f762145c0ff", size = 649175 },
+ { url = "https://files.pythonhosted.org/packages/a9/46/ddbd2db9ff209186b7b7c621d1432e2f21714adc988703dbdd0e65155c77/greenlet-3.1.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:667a9706c970cb552ede35aee17339a18e8f2a87a51fba2ed39ceeeb1004798a", size = 663425 },
+ { url = "https://files.pythonhosted.org/packages/bc/f9/9c82d6b2b04aa37e38e74f0c429aece5eeb02bab6e3b98e7db89b23d94c6/greenlet-3.1.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b8a678974d1f3aa55f6cc34dc480169d58f2e6d8958895d68845fa4ab566509e", size = 657736 },
+ { url = "https://files.pythonhosted.org/packages/d9/42/b87bc2a81e3a62c3de2b0d550bf91a86939442b7ff85abb94eec3fc0e6aa/greenlet-3.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efc0f674aa41b92da8c49e0346318c6075d734994c3c4e4430b1c3f853e498e4", size = 660347 },
+ { url = "https://files.pythonhosted.org/packages/37/fa/71599c3fd06336cdc3eac52e6871cfebab4d9d70674a9a9e7a482c318e99/greenlet-3.1.1-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0153404a4bb921f0ff1abeb5ce8a5131da56b953eda6e14b88dc6bbc04d2049e", size = 615583 },
+ { url = "https://files.pythonhosted.org/packages/4e/96/e9ef85de031703ee7a4483489b40cf307f93c1824a02e903106f2ea315fe/greenlet-3.1.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:275f72decf9932639c1c6dd1013a1bc266438eb32710016a1c742df5da6e60a1", size = 1133039 },
+ { url = "https://files.pythonhosted.org/packages/87/76/b2b6362accd69f2d1889db61a18c94bc743e961e3cab344c2effaa4b4a25/greenlet-3.1.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:c4aab7f6381f38a4b42f269057aee279ab0fc7bf2e929e3d4abfae97b682a12c", size = 1160716 },
+ { url = "https://files.pythonhosted.org/packages/1f/1b/54336d876186920e185066d8c3024ad55f21d7cc3683c856127ddb7b13ce/greenlet-3.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:b42703b1cf69f2aa1df7d1030b9d77d3e584a70755674d60e710f0af570f3761", size = 299490 },
+ { url = "https://files.pythonhosted.org/packages/5f/17/bea55bf36990e1638a2af5ba10c1640273ef20f627962cf97107f1e5d637/greenlet-3.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1695e76146579f8c06c1509c7ce4dfe0706f49c6831a817ac04eebb2fd02011", size = 643731 },
+ { url = "https://files.pythonhosted.org/packages/78/d2/aa3d2157f9ab742a08e0fd8f77d4699f37c22adfbfeb0c610a186b5f75e0/greenlet-3.1.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7876452af029456b3f3549b696bb36a06db7c90747740c5302f74a9e9fa14b13", size = 649304 },
+ { url = "https://files.pythonhosted.org/packages/f1/8e/d0aeffe69e53ccff5a28fa86f07ad1d2d2d6537a9506229431a2a02e2f15/greenlet-3.1.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4ead44c85f8ab905852d3de8d86f6f8baf77109f9da589cb4fa142bd3b57b475", size = 646537 },
+ { url = "https://files.pythonhosted.org/packages/05/79/e15408220bbb989469c8871062c97c6c9136770657ba779711b90870d867/greenlet-3.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8320f64b777d00dd7ccdade271eaf0cad6636343293a25074cc5566160e4de7b", size = 642506 },
+ { url = "https://files.pythonhosted.org/packages/18/87/470e01a940307796f1d25f8167b551a968540fbe0551c0ebb853cb527dd6/greenlet-3.1.1-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6510bf84a6b643dabba74d3049ead221257603a253d0a9873f55f6a59a65f822", size = 602753 },
+ { url = "https://files.pythonhosted.org/packages/e2/72/576815ba674eddc3c25028238f74d7b8068902b3968cbe456771b166455e/greenlet-3.1.1-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:04b013dc07c96f83134b1e99888e7a79979f1a247e2a9f59697fa14b5862ed01", size = 1122731 },
+ { url = "https://files.pythonhosted.org/packages/ac/38/08cc303ddddc4b3d7c628c3039a61a3aae36c241ed01393d00c2fd663473/greenlet-3.1.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:411f015496fec93c1c8cd4e5238da364e1da7a124bcb293f085bf2860c32c6f6", size = 1142112 },
+]
+
+[[package]]
+name = "grpclib"
+version = "0.4.7"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "h2" },
+ { name = "multidict" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/79/b9/55936e462a5925190d7427e880b3033601d1effd13809b483d13a926061a/grpclib-0.4.7.tar.gz", hash = "sha256:2988ef57c02b22b7a2e8e961792c41ccf97efc2ace91ae7a5b0de03c363823c3", size = 61254 }
+
+[[package]]
+name = "h11"
+version = "0.14.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/f5/38/3af3d3633a34a3316095b39c8e8fb4853a28a536e55d347bd8d8e9a14b03/h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d", size = 100418 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/95/04/ff642e65ad6b90db43e668d70ffb6736436c7ce41fcc549f4e9472234127/h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761", size = 58259 },
+]
+
+[[package]]
+name = "h2"
+version = "4.2.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "hpack" },
+ { name = "hyperframe" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/1b/38/d7f80fd13e6582fb8e0df8c9a653dcc02b03ca34f4d72f34869298c5baf8/h2-4.2.0.tar.gz", hash = "sha256:c8a52129695e88b1a0578d8d2cc6842bbd79128ac685463b887ee278126ad01f", size = 2150682 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/d0/9e/984486f2d0a0bd2b024bf4bc1c62688fcafa9e61991f041fb0e2def4a982/h2-4.2.0-py3-none-any.whl", hash = "sha256:479a53ad425bb29af087f3458a61d30780bc818e4ebcf01f0b536ba916462ed0", size = 60957 },
+]
+
+[[package]]
+name = "hatch-vcs"
+version = "0.4.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "hatchling" },
+ { name = "setuptools-scm" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/f5/c9/54bb4fa27b4e4a014ef3bb17710cdf692b3aa2cbc7953da885f1bf7e06ea/hatch_vcs-0.4.0.tar.gz", hash = "sha256:093810748fe01db0d451fabcf2c1ac2688caefd232d4ede967090b1c1b07d9f7", size = 10917 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/82/0f/6cbd9976160bc334add63bc2e7a58b1433a31b34b7cda6c5de6dd983d9a7/hatch_vcs-0.4.0-py3-none-any.whl", hash = "sha256:b8a2b6bee54cf6f9fc93762db73890017ae59c9081d1038a41f16235ceaf8b2c", size = 8412 },
+]
+
+[[package]]
+name = "hatchling"
+version = "1.27.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "packaging" },
+ { name = "pathspec" },
+ { name = "pluggy" },
+ { name = "trove-classifiers" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/8f/8a/cc1debe3514da292094f1c3a700e4ca25442489731ef7c0814358816bb03/hatchling-1.27.0.tar.gz", hash = "sha256:971c296d9819abb3811112fc52c7a9751c8d381898f36533bb16f9791e941fd6", size = 54983 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/08/e7/ae38d7a6dfba0533684e0b2136817d667588ae3ec984c1a4e5df5eb88482/hatchling-1.27.0-py3-none-any.whl", hash = "sha256:d3a2f3567c4f926ea39849cdf924c7e99e6686c9c8e288ae1037c8fa2a5d937b", size = 75794 },
+]
+
+[[package]]
+name = "hpack"
+version = "4.1.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/2c/48/71de9ed269fdae9c8057e5a4c0aa7402e8bb16f2c6e90b3aa53327b113f8/hpack-4.1.0.tar.gz", hash = "sha256:ec5eca154f7056aa06f196a557655c5b009b382873ac8d1e66e79e87535f1dca", size = 51276 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/07/c6/80c95b1b2b94682a72cbdbfb85b81ae2daffa4291fbfa1b1464502ede10d/hpack-4.1.0-py3-none-any.whl", hash = "sha256:157ac792668d995c657d93111f46b4535ed114f0c9c8d672271bbec7eae1b496", size = 34357 },
+]
+
+[[package]]
+name = "httpcore"
+version = "1.0.7"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "certifi" },
+ { name = "h11" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/6a/41/d7d0a89eb493922c37d343b607bc1b5da7f5be7e383740b4753ad8943e90/httpcore-1.0.7.tar.gz", hash = "sha256:8551cb62a169ec7162ac7be8d4817d561f60e08eaa485234898414bb5a8a0b4c", size = 85196 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/87/f5/72347bc88306acb359581ac4d52f23c0ef445b57157adedb9aee0cd689d2/httpcore-1.0.7-py3-none-any.whl", hash = "sha256:a3fff8f43dc260d5bd363d9f9cf1830fa3a458b332856f34282de498ed420edd", size = 78551 },
+]
+
+[[package]]
+name = "httptools"
+version = "0.6.4"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/a7/9a/ce5e1f7e131522e6d3426e8e7a490b3a01f39a6696602e1c4f33f9e94277/httptools-0.6.4.tar.gz", hash = "sha256:4e93eee4add6493b59a5c514da98c939b244fce4a0d8879cd3f466562f4b7d5c", size = 240639 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/bb/0e/d0b71465c66b9185f90a091ab36389a7352985fe857e352801c39d6127c8/httptools-0.6.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:df017d6c780287d5c80601dafa31f17bddb170232d85c066604d8558683711a2", size = 200683 },
+ { url = "https://files.pythonhosted.org/packages/e2/b8/412a9bb28d0a8988de3296e01efa0bd62068b33856cdda47fe1b5e890954/httptools-0.6.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:85071a1e8c2d051b507161f6c3e26155b5c790e4e28d7f236422dbacc2a9cc44", size = 104337 },
+ { url = "https://files.pythonhosted.org/packages/9b/01/6fb20be3196ffdc8eeec4e653bc2a275eca7f36634c86302242c4fbb2760/httptools-0.6.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69422b7f458c5af875922cdb5bd586cc1f1033295aa9ff63ee196a87519ac8e1", size = 508796 },
+ { url = "https://files.pythonhosted.org/packages/f7/d8/b644c44acc1368938317d76ac991c9bba1166311880bcc0ac297cb9d6bd7/httptools-0.6.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:16e603a3bff50db08cd578d54f07032ca1631450ceb972c2f834c2b860c28ea2", size = 510837 },
+ { url = "https://files.pythonhosted.org/packages/52/d8/254d16a31d543073a0e57f1c329ca7378d8924e7e292eda72d0064987486/httptools-0.6.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ec4f178901fa1834d4a060320d2f3abc5c9e39766953d038f1458cb885f47e81", size = 485289 },
+ { url = "https://files.pythonhosted.org/packages/5f/3c/4aee161b4b7a971660b8be71a92c24d6c64372c1ab3ae7f366b3680df20f/httptools-0.6.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f9eb89ecf8b290f2e293325c646a211ff1c2493222798bb80a530c5e7502494f", size = 489779 },
+ { url = "https://files.pythonhosted.org/packages/12/b7/5cae71a8868e555f3f67a50ee7f673ce36eac970f029c0c5e9d584352961/httptools-0.6.4-cp312-cp312-win_amd64.whl", hash = "sha256:db78cb9ca56b59b016e64b6031eda5653be0589dba2b1b43453f6e8b405a0970", size = 88634 },
+ { url = "https://files.pythonhosted.org/packages/94/a3/9fe9ad23fd35f7de6b91eeb60848986058bd8b5a5c1e256f5860a160cc3e/httptools-0.6.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ade273d7e767d5fae13fa637f4d53b6e961fb7fd93c7797562663f0171c26660", size = 197214 },
+ { url = "https://files.pythonhosted.org/packages/ea/d9/82d5e68bab783b632023f2fa31db20bebb4e89dfc4d2293945fd68484ee4/httptools-0.6.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:856f4bc0478ae143bad54a4242fccb1f3f86a6e1be5548fecfd4102061b3a083", size = 102431 },
+ { url = "https://files.pythonhosted.org/packages/96/c1/cb499655cbdbfb57b577734fde02f6fa0bbc3fe9fb4d87b742b512908dff/httptools-0.6.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:322d20ea9cdd1fa98bd6a74b77e2ec5b818abdc3d36695ab402a0de8ef2865a3", size = 473121 },
+ { url = "https://files.pythonhosted.org/packages/af/71/ee32fd358f8a3bb199b03261f10921716990808a675d8160b5383487a317/httptools-0.6.4-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4d87b29bd4486c0093fc64dea80231f7c7f7eb4dc70ae394d70a495ab8436071", size = 473805 },
+ { url = "https://files.pythonhosted.org/packages/8a/0a/0d4df132bfca1507114198b766f1737d57580c9ad1cf93c1ff673e3387be/httptools-0.6.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:342dd6946aa6bda4b8f18c734576106b8a31f2fe31492881a9a160ec84ff4bd5", size = 448858 },
+ { url = "https://files.pythonhosted.org/packages/1e/6a/787004fdef2cabea27bad1073bf6a33f2437b4dbd3b6fb4a9d71172b1c7c/httptools-0.6.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4b36913ba52008249223042dca46e69967985fb4051951f94357ea681e1f5dc0", size = 452042 },
+ { url = "https://files.pythonhosted.org/packages/4d/dc/7decab5c404d1d2cdc1bb330b1bf70e83d6af0396fd4fc76fc60c0d522bf/httptools-0.6.4-cp313-cp313-win_amd64.whl", hash = "sha256:28908df1b9bb8187393d5b5db91435ccc9c8e891657f9cbb42a2541b44c82fc8", size = 87682 },
+]
+
+[[package]]
+name = "httpx"
+version = "0.28.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "anyio" },
+ { name = "certifi" },
+ { name = "httpcore" },
+ { name = "idna" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517 },
+]
+
+[[package]]
+name = "huggingface-hub"
+version = "0.28.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "filelock" },
+ { name = "fsspec" },
+ { name = "packaging" },
+ { name = "pyyaml" },
+ { name = "requests" },
+ { name = "tqdm" },
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/e7/ce/a734204aaae6c35a22f9956ebcd8d8708ae5b842e15d6f42bd6f49e634a4/huggingface_hub-0.28.1.tar.gz", hash = "sha256:893471090c98e3b6efbdfdacafe4052b20b84d59866fb6f54c33d9af18c303ae", size = 387074 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/ea/da/6c2bea5327b640920267d3bf2c9fc114cfbd0a5de234d81cda80cc9e33c8/huggingface_hub-0.28.1-py3-none-any.whl", hash = "sha256:aa6b9a3ffdae939b72c464dbb0d7f99f56e649b55c3d52406f49e0a5a620c0a7", size = 464068 },
+]
+
+[[package]]
+name = "humanize"
+version = "4.11.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/6a/40/64a912b9330786df25e58127194d4a5a7441f818b400b155e748a270f924/humanize-4.11.0.tar.gz", hash = "sha256:e66f36020a2d5a974c504bd2555cf770621dbdbb6d82f94a6857c0b1ea2608be", size = 80374 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/92/75/4bc3e242ad13f2e6c12e0b0401ab2c5e5c6f0d7da37ec69bc808e24e0ccb/humanize-4.11.0-py3-none-any.whl", hash = "sha256:b53caaec8532bcb2fff70c8826f904c35943f8cecaca29d272d9df38092736c0", size = 128055 },
+]
+
+[[package]]
+name = "hyperframe"
+version = "6.1.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/02/e7/94f8232d4a74cc99514c13a9f995811485a6903d48e5d952771ef6322e30/hyperframe-6.1.0.tar.gz", hash = "sha256:f630908a00854a7adeabd6382b43923a4c4cd4b821fcb527e6ab9e15382a3b08", size = 26566 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/48/30/47d0bf6072f7252e6521f3447ccfa40b421b6824517f82854703d0f5a98b/hyperframe-6.1.0-py3-none-any.whl", hash = "sha256:b03380493a519fce58ea5af42e4a42317bf9bd425596f7a0835ffce80f1a42e5", size = 13007 },
+]
+
+[[package]]
+name = "identify"
+version = "2.6.7"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/83/d1/524aa3350f78bcd714d148ade6133d67d6b7de2cdbae7d99039c024c9a25/identify-2.6.7.tar.gz", hash = "sha256:3fa266b42eba321ee0b2bb0936a6a6b9e36a1351cbb69055b3082f4193035684", size = 99260 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/03/00/1fd4a117c6c93f2dcc5b7edaeaf53ea45332ef966429be566ca16c2beb94/identify-2.6.7-py2.py3-none-any.whl", hash = "sha256:155931cb617a401807b09ecec6635d6c692d180090a1cedca8ef7d58ba5b6aa0", size = 99097 },
+]
+
+[[package]]
+name = "idna"
+version = "3.10"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 },
+]
+
+[[package]]
+name = "importlib-resources"
+version = "6.5.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/cf/8c/f834fbf984f691b4f7ff60f50b514cc3de5cc08abfc3295564dd89c5e2e7/importlib_resources-6.5.2.tar.gz", hash = "sha256:185f87adef5bcc288449d98fb4fba07cea78bc036455dd44c5fc4a2fe78fed2c", size = 44693 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/a4/ed/1f1afb2e9e7f38a545d628f864d562a5ae64fe6f7a10e28ffb9b185b4e89/importlib_resources-6.5.2-py3-none-any.whl", hash = "sha256:789cfdc3ed28c78b67a06acb8126751ced69a3d5f79c095a98298cd8a760ccec", size = 37461 },
+]
+
+[[package]]
+name = "inflect"
+version = "5.6.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/cb/db/cae5d8524c4b5e574c281895b212062f3b06d0e14186904ed71c538b4e90/inflect-5.6.2.tar.gz", hash = "sha256:aadc7ed73928f5e014129794bbac03058cca35d0a973a5fc4eb45c7fa26005f9", size = 69378 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/df/d8/3e1a32d305215166f5c32652c473aa766bd7809cd10b34c544dbc31facb5/inflect-5.6.2-py3-none-any.whl", hash = "sha256:b45d91a4a28a4e617ff1821117439b06eaa86e2a4573154af0149e9be6687238", size = 33704 },
+]
+
+[[package]]
+name = "iniconfig"
+version = "2.0.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", size = 4646 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892 },
+]
+
+[[package]]
+name = "isort"
+version = "6.0.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/1c/28/b382d1656ac0ee4cef4bf579b13f9c6c813bff8a5cb5996669592c8c75fa/isort-6.0.0.tar.gz", hash = "sha256:75d9d8a1438a9432a7d7b54f2d3b45cad9a4a0fdba43617d9873379704a8bdf1", size = 828356 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/76/c7/d6017f09ae5b1206fbe531f7af3b6dac1f67aedcbd2e79f3b386c27955d6/isort-6.0.0-py3-none-any.whl", hash = "sha256:567954102bb47bb12e0fae62606570faacddd441e45683968c8d1734fb1af892", size = 94053 },
+]
+
+[[package]]
+name = "jinja2"
+version = "3.1.5"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "markupsafe" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/af/92/b3130cbbf5591acf9ade8708c365f3238046ac7cb8ccba6e81abccb0ccff/jinja2-3.1.5.tar.gz", hash = "sha256:8fefff8dc3034e27bb80d67c671eb8a9bc424c0ef4c0826edbff304cceff43bb", size = 244674 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/bd/0f/2ba5fbcd631e3e88689309dbe978c5769e883e4b84ebfe7da30b43275c5a/jinja2-3.1.5-py3-none-any.whl", hash = "sha256:aba0f4dc9ed8013c424088f68a5c226f7d6097ed89b246d7749c2ec4175c6adb", size = 134596 },
+]
+
+[[package]]
+name = "jiter"
+version = "0.8.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/f8/70/90bc7bd3932e651486861df5c8ffea4ca7c77d28e8532ddefe2abc561a53/jiter-0.8.2.tar.gz", hash = "sha256:cd73d3e740666d0e639f678adb176fad25c1bcbdae88d8d7b857e1783bb4212d", size = 163007 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/a1/17/c8747af8ea4e045f57d6cfd6fc180752cab9bc3de0e8a0c9ca4e8af333b1/jiter-0.8.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:e6ec2be506e7d6f9527dae9ff4b7f54e68ea44a0ef6b098256ddf895218a2f8f", size = 302027 },
+ { url = "https://files.pythonhosted.org/packages/3c/c1/6da849640cd35a41e91085723b76acc818d4b7d92b0b6e5111736ce1dd10/jiter-0.8.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:76e324da7b5da060287c54f2fabd3db5f76468006c811831f051942bf68c9d44", size = 310326 },
+ { url = "https://files.pythonhosted.org/packages/06/99/a2bf660d8ccffee9ad7ed46b4f860d2108a148d0ea36043fd16f4dc37e94/jiter-0.8.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:180a8aea058f7535d1c84183c0362c710f4750bef66630c05f40c93c2b152a0f", size = 334242 },
+ { url = "https://files.pythonhosted.org/packages/a7/5f/cea1c17864828731f11427b9d1ab7f24764dbd9aaf4648a7f851164d2718/jiter-0.8.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:025337859077b41548bdcbabe38698bcd93cfe10b06ff66617a48ff92c9aec60", size = 356654 },
+ { url = "https://files.pythonhosted.org/packages/e9/13/62774b7e5e7f5d5043efe1d0f94ead66e6d0f894ae010adb56b3f788de71/jiter-0.8.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ecff0dc14f409599bbcafa7e470c00b80f17abc14d1405d38ab02e4b42e55b57", size = 379967 },
+ { url = "https://files.pythonhosted.org/packages/ec/fb/096b34c553bb0bd3f2289d5013dcad6074948b8d55212aa13a10d44c5326/jiter-0.8.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ffd9fee7d0775ebaba131f7ca2e2d83839a62ad65e8e02fe2bd8fc975cedeb9e", size = 389252 },
+ { url = "https://files.pythonhosted.org/packages/17/61/beea645c0bf398ced8b199e377b61eb999d8e46e053bb285c91c3d3eaab0/jiter-0.8.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14601dcac4889e0a1c75ccf6a0e4baf70dbc75041e51bcf8d0e9274519df6887", size = 345490 },
+ { url = "https://files.pythonhosted.org/packages/d5/df/834aa17ad5dcc3cf0118821da0a0cf1589ea7db9832589278553640366bc/jiter-0.8.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:92249669925bc1c54fcd2ec73f70f2c1d6a817928480ee1c65af5f6b81cdf12d", size = 376991 },
+ { url = "https://files.pythonhosted.org/packages/67/80/87d140399d382fb4ea5b3d56e7ecaa4efdca17cd7411ff904c1517855314/jiter-0.8.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e725edd0929fa79f8349ab4ec7f81c714df51dc4e991539a578e5018fa4a7152", size = 510822 },
+ { url = "https://files.pythonhosted.org/packages/5c/37/3394bb47bac1ad2cb0465601f86828a0518d07828a650722e55268cdb7e6/jiter-0.8.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:bf55846c7b7a680eebaf9c3c48d630e1bf51bdf76c68a5f654b8524335b0ad29", size = 503730 },
+ { url = "https://files.pythonhosted.org/packages/f9/e2/253fc1fa59103bb4e3aa0665d6ceb1818df1cd7bf3eb492c4dad229b1cd4/jiter-0.8.2-cp312-cp312-win32.whl", hash = "sha256:7efe4853ecd3d6110301665a5178b9856be7e2a9485f49d91aa4d737ad2ae49e", size = 203375 },
+ { url = "https://files.pythonhosted.org/packages/41/69/6d4bbe66b3b3b4507e47aa1dd5d075919ad242b4b1115b3f80eecd443687/jiter-0.8.2-cp312-cp312-win_amd64.whl", hash = "sha256:83c0efd80b29695058d0fd2fa8a556490dbce9804eac3e281f373bbc99045f6c", size = 204740 },
+ { url = "https://files.pythonhosted.org/packages/6c/b0/bfa1f6f2c956b948802ef5a021281978bf53b7a6ca54bb126fd88a5d014e/jiter-0.8.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:ca1f08b8e43dc3bd0594c992fb1fd2f7ce87f7bf0d44358198d6da8034afdf84", size = 301190 },
+ { url = "https://files.pythonhosted.org/packages/a4/8f/396ddb4e292b5ea57e45ade5dc48229556b9044bad29a3b4b2dddeaedd52/jiter-0.8.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5672a86d55416ccd214c778efccf3266b84f87b89063b582167d803246354be4", size = 309334 },
+ { url = "https://files.pythonhosted.org/packages/7f/68/805978f2f446fa6362ba0cc2e4489b945695940656edd844e110a61c98f8/jiter-0.8.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:58dc9bc9767a1101f4e5e22db1b652161a225874d66f0e5cb8e2c7d1c438b587", size = 333918 },
+ { url = "https://files.pythonhosted.org/packages/b3/99/0f71f7be667c33403fa9706e5b50583ae5106d96fab997fa7e2f38ee8347/jiter-0.8.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:37b2998606d6dadbb5ccda959a33d6a5e853252d921fec1792fc902351bb4e2c", size = 356057 },
+ { url = "https://files.pythonhosted.org/packages/8d/50/a82796e421a22b699ee4d2ce527e5bcb29471a2351cbdc931819d941a167/jiter-0.8.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4ab9a87f3784eb0e098f84a32670cfe4a79cb6512fd8f42ae3d0709f06405d18", size = 379790 },
+ { url = "https://files.pythonhosted.org/packages/3c/31/10fb012b00f6d83342ca9e2c9618869ab449f1aa78c8f1b2193a6b49647c/jiter-0.8.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:79aec8172b9e3c6d05fd4b219d5de1ac616bd8da934107325a6c0d0e866a21b6", size = 388285 },
+ { url = "https://files.pythonhosted.org/packages/c8/81/f15ebf7de57be488aa22944bf4274962aca8092e4f7817f92ffa50d3ee46/jiter-0.8.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:711e408732d4e9a0208008e5892c2966b485c783cd2d9a681f3eb147cf36c7ef", size = 344764 },
+ { url = "https://files.pythonhosted.org/packages/b3/e8/0cae550d72b48829ba653eb348cdc25f3f06f8a62363723702ec18e7be9c/jiter-0.8.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:653cf462db4e8c41995e33d865965e79641ef45369d8a11f54cd30888b7e6ff1", size = 376620 },
+ { url = "https://files.pythonhosted.org/packages/b8/50/e5478ff9d82534a944c03b63bc217c5f37019d4a34d288db0f079b13c10b/jiter-0.8.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:9c63eaef32b7bebac8ebebf4dabebdbc6769a09c127294db6babee38e9f405b9", size = 510402 },
+ { url = "https://files.pythonhosted.org/packages/8e/1e/3de48bbebbc8f7025bd454cedc8c62378c0e32dd483dece5f4a814a5cb55/jiter-0.8.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:eb21aaa9a200d0a80dacc7a81038d2e476ffe473ffdd9c91eb745d623561de05", size = 503018 },
+ { url = "https://files.pythonhosted.org/packages/d5/cd/d5a5501d72a11fe3e5fd65c78c884e5164eefe80077680533919be22d3a3/jiter-0.8.2-cp313-cp313-win32.whl", hash = "sha256:789361ed945d8d42850f919342a8665d2dc79e7e44ca1c97cc786966a21f627a", size = 203190 },
+ { url = "https://files.pythonhosted.org/packages/51/bf/e5ca301245ba951447e3ad677a02a64a8845b185de2603dabd83e1e4b9c6/jiter-0.8.2-cp313-cp313-win_amd64.whl", hash = "sha256:ab7f43235d71e03b941c1630f4b6e3055d46b6cb8728a17663eaac9d8e83a865", size = 203551 },
+ { url = "https://files.pythonhosted.org/packages/2f/3c/71a491952c37b87d127790dd7a0b1ebea0514c6b6ad30085b16bbe00aee6/jiter-0.8.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b426f72cd77da3fec300ed3bc990895e2dd6b49e3bfe6c438592a3ba660e41ca", size = 308347 },
+ { url = "https://files.pythonhosted.org/packages/a0/4c/c02408042e6a7605ec063daed138e07b982fdb98467deaaf1c90950cf2c6/jiter-0.8.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b2dd880785088ff2ad21ffee205e58a8c1ddabc63612444ae41e5e4b321b39c0", size = 342875 },
+ { url = "https://files.pythonhosted.org/packages/91/61/c80ef80ed8a0a21158e289ef70dac01e351d929a1c30cb0f49be60772547/jiter-0.8.2-cp313-cp313t-win_amd64.whl", hash = "sha256:3ac9f578c46f22405ff7f8b1f5848fb753cc4b8377fbec8470a7dc3997ca7566", size = 202374 },
+]
+
+[[package]]
+name = "jmespath"
+version = "1.0.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/00/2a/e867e8531cf3e36b41201936b7fa7ba7b5702dbef42922193f05c8976cd6/jmespath-1.0.1.tar.gz", hash = "sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe", size = 25843 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/31/b4/b9b800c45527aadd64d5b442f9b932b00648617eb5d63d2c7a6587b7cafc/jmespath-1.0.1-py3-none-any.whl", hash = "sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980", size = 20256 },
+]
+
+[[package]]
+name = "jsonpatch"
+version = "1.33"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "jsonpointer" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/42/78/18813351fe5d63acad16aec57f94ec2b70a09e53ca98145589e185423873/jsonpatch-1.33.tar.gz", hash = "sha256:9fcd4009c41e6d12348b4a0ff2563ba56a2923a7dfee731d004e212e1ee5030c", size = 21699 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/73/07/02e16ed01e04a374e644b575638ec7987ae846d25ad97bcc9945a3ee4b0e/jsonpatch-1.33-py2.py3-none-any.whl", hash = "sha256:0ae28c0cd062bbd8b8ecc26d7d164fbbea9652a1a3693f3b956c1eae5145dade", size = 12898 },
+]
+
+[[package]]
+name = "jsonpointer"
+version = "3.0.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/6a/0a/eebeb1fa92507ea94016a2a790b93c2ae41a7e18778f85471dc54475ed25/jsonpointer-3.0.0.tar.gz", hash = "sha256:2b2d729f2091522d61c3b31f82e11870f60b68f43fbc705cb76bf4b832af59ef", size = 9114 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/71/92/5e77f98553e9e75130c78900d000368476aed74276eb8ae8796f65f00918/jsonpointer-3.0.0-py2.py3-none-any.whl", hash = "sha256:13e088adc14fca8b6aa8177c044e12701e6ad4b28ff10e65f2267a90109c9942", size = 7595 },
+]
+
+[[package]]
+name = "langchain"
+version = "0.3.18"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "aiohttp" },
+ { name = "langchain-core" },
+ { name = "langchain-text-splitters" },
+ { name = "langsmith" },
+ { name = "numpy" },
+ { name = "pydantic" },
+ { name = "pyyaml" },
+ { name = "requests" },
+ { name = "sqlalchemy" },
+ { name = "tenacity" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/87/23/612d99c74889f672fe349f43a458a42e449650ebd57073b9e96e0b6b2253/langchain-0.3.18.tar.gz", hash = "sha256:311ac227a995545ff7c3f74c7767930c5349edef0b39f19d3105b86d39316b69", size = 10223807 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/93/83/a4b41a1cf8b22fd708104d50edf98b720aa28647d3083d83b8348927a786/langchain-0.3.18-py3-none-any.whl", hash = "sha256:1a6e629f02a25962aa5b16932e8f073248104a66804ed5af1f78618ad7c1d38d", size = 1010321 },
+]
+
+[package.optional-dependencies]
+openai = [
+ { name = "langchain-openai" },
+]
+
+[[package]]
+name = "langchain-core"
+version = "0.3.34"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "jsonpatch" },
+ { name = "langsmith" },
+ { name = "packaging" },
+ { name = "pydantic" },
+ { name = "pyyaml" },
+ { name = "tenacity" },
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/b9/c8/a4394a5bdfc820f539bd6983b1408964723ed43ce8cfafbcc7cada69c015/langchain_core-0.3.34.tar.gz", hash = "sha256:26504cf1e8e6c310adad907b890d4e3c147581cfa7434114f6dc1134fe4bc6d3", size = 524756 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/9f/65/27a586c8871a0632d747059eb97855b49ac6dea12b263a79f6c1b4f18b99/langchain_core-0.3.34-py3-none-any.whl", hash = "sha256:a057ebeddd2158d3be14bde341b25640ddf958b6989bd6e47160396f5a8202ae", size = 412955 },
+]
+
+[[package]]
+name = "langchain-openai"
+version = "0.3.4"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "langchain-core" },
+ { name = "openai" },
+ { name = "tiktoken" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/91/a4/1270f7bad6ba0b032f8364b2fdffaa7d044bb9c6d8238ec52494a996689c/langchain_openai-0.3.4.tar.gz", hash = "sha256:c6645745a1d1bf19f21ea6fa473a746bd464053ff57ce563215e6165a0c4b9f1", size = 255126 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/a3/24/a57c061a6738b89f44aa48d756945b011867cedba9a94d48729def22155c/langchain_openai-0.3.4-py3-none-any.whl", hash = "sha256:58d0c014620eb92f4f46ff9daf584c2a7794896b1379eb85ad7be8d9f3493b61", size = 54713 },
+]
+
+[[package]]
+name = "langchain-text-splitters"
+version = "0.3.6"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "langchain-core" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/0d/33/89912a07c63e4e818f9b0c8d52e4f9d600c97beca8a91db8c9dae6a1b28f/langchain_text_splitters-0.3.6.tar.gz", hash = "sha256:c537972f4b7c07451df431353a538019ad9dadff7a1073ea363946cea97e1bee", size = 40545 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/4c/f8/6b82af988e65af9697f6a2f25373fb173fd32d48b62772a8773c5184c870/langchain_text_splitters-0.3.6-py3-none-any.whl", hash = "sha256:e5d7b850f6c14259ea930be4a964a65fa95d9df7e1dbdd8bad8416db72292f4e", size = 31197 },
+]
+
+[[package]]
+name = "langsmith"
+version = "0.3.8"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "httpx" },
+ { name = "orjson", marker = "platform_python_implementation != 'PyPy'" },
+ { name = "pydantic" },
+ { name = "requests" },
+ { name = "requests-toolbelt" },
+ { name = "zstandard" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/d8/1a/974b66a9e7c43f41bec067e1f393a296803aee48fafcf183941c31295b59/langsmith-0.3.8.tar.gz", hash = "sha256:97f9bebe0b7cb0a4f278e6ff30ae7d5ededff3883b014442ec6d7d575b02a0f1", size = 321394 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/8b/e4/5380e8229c442e406404977d2ec71a9db6a3e6a89fce7791c6ad7cd2bdbe/langsmith-0.3.8-py3-none-any.whl", hash = "sha256:fbb9dd97b0f090219447fca9362698d07abaeda1da85aa7cc6ec6517b36581b1", size = 332800 },
+]
+
+[[package]]
+name = "lazy-object-proxy"
+version = "1.10.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/2c/f0/f02e2d150d581a294efded4020094a371bbab42423fe78625ac18854d89b/lazy-object-proxy-1.10.0.tar.gz", hash = "sha256:78247b6d45f43a52ef35c25b5581459e85117225408a4128a3daf8bf9648ac69", size = 43271 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/d0/5d/768a7f2ccebb29604def61842fd54f6f5f75c79e366ee8748dda84de0b13/lazy_object_proxy-1.10.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e98c8af98d5707dcdecc9ab0863c0ea6e88545d42ca7c3feffb6b4d1e370c7ba", size = 27560 },
+ { url = "https://files.pythonhosted.org/packages/b3/ce/f369815549dbfa4bebed541fa4e1561d69e4f268a1f6f77da886df182dab/lazy_object_proxy-1.10.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:952c81d415b9b80ea261d2372d2a4a2332a3890c2b83e0535f263ddfe43f0d43", size = 72403 },
+ { url = "https://files.pythonhosted.org/packages/44/46/3771e0a4315044aa7b67da892b2fb1f59dfcf0eaff2c8967b2a0a85d5896/lazy_object_proxy-1.10.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80b39d3a151309efc8cc48675918891b865bdf742a8616a337cb0090791a0de9", size = 72401 },
+ { url = "https://files.pythonhosted.org/packages/81/39/84ce4740718e1c700bd04d3457ac92b2e9ce76529911583e7a2bf4d96eb2/lazy_object_proxy-1.10.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e221060b701e2aa2ea991542900dd13907a5c90fa80e199dbf5a03359019e7a3", size = 75375 },
+ { url = "https://files.pythonhosted.org/packages/86/3b/d6b65da2b864822324745c0a73fe7fd86c67ccea54173682c3081d7adea8/lazy_object_proxy-1.10.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:92f09ff65ecff3108e56526f9e2481b8116c0b9e1425325e13245abfd79bdb1b", size = 75466 },
+ { url = "https://files.pythonhosted.org/packages/f5/33/467a093bf004a70022cb410c590d937134bba2faa17bf9dc42a48f49af35/lazy_object_proxy-1.10.0-cp312-cp312-win32.whl", hash = "sha256:3ad54b9ddbe20ae9f7c1b29e52f123120772b06dbb18ec6be9101369d63a4074", size = 25914 },
+ { url = "https://files.pythonhosted.org/packages/77/ce/7956dc5ac2f8b62291b798c8363c81810e22a9effe469629d297d087e350/lazy_object_proxy-1.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:127a789c75151db6af398b8972178afe6bda7d6f68730c057fbbc2e96b08d282", size = 27525 },
+ { url = "https://files.pythonhosted.org/packages/31/8b/94dc8d58704ab87b39faed6f2fc0090b9d90e2e2aa2bbec35c79f3d2a054/lazy_object_proxy-1.10.0-pp310.pp311.pp312.pp38.pp39-none-any.whl", hash = "sha256:80fa48bd89c8f2f456fc0765c11c23bf5af827febacd2f523ca5bc1893fcc09d", size = 16405 },
+]
+
+[[package]]
+name = "levenshtein"
+version = "0.26.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "rapidfuzz" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/97/e6/79807d3b59a67dd78bb77072ca6a28d8db0935161fecf935e6c38c5f6825/levenshtein-0.26.1.tar.gz", hash = "sha256:0d19ba22330d50609b2349021ec3cf7d905c6fe21195a2d0d876a146e7ed2575", size = 374307 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/4c/53/3685ee7fbe9b8eb4b82d8045255e59dd6943f94e8091697ef3808e7ecf63/levenshtein-0.26.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cc741ca406d3704dc331a69c04b061fc952509a069b79cab8287413f434684bd", size = 176447 },
+ { url = "https://files.pythonhosted.org/packages/82/7f/7d6fe9b76bd030200f8f9b162f3de862d597804d292af292ec3ce9ae8bee/levenshtein-0.26.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:821ace3b4e1c2e02b43cf5dc61aac2ea43bdb39837ac890919c225a2c3f2fea4", size = 157589 },
+ { url = "https://files.pythonhosted.org/packages/bc/d3/44539e952df93c5d88a95a0edff34af38e4f87330a76e8335bfe2c0f31bf/levenshtein-0.26.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f92694c9396f55d4c91087efacf81297bef152893806fc54c289fc0254b45384", size = 153306 },
+ { url = "https://files.pythonhosted.org/packages/ba/fe/21443c0c50824314e2d2ce7e1e9cd11d21b3643f3c14da156b15b4d399c7/levenshtein-0.26.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:51ba374de7a1797d04a14a4f0ad3602d2d71fef4206bb20a6baaa6b6a502da58", size = 184409 },
+ { url = "https://files.pythonhosted.org/packages/f0/7b/c95066c64bb18628cf7488e0dd6aec2b7cbda307d93ba9ede68a21af2a7b/levenshtein-0.26.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f7aa5c3327dda4ef952769bacec09c09ff5bf426e07fdc94478c37955681885b", size = 193134 },
+ { url = "https://files.pythonhosted.org/packages/36/22/5f9760b135bdefb8cf8d663890756136754db03214f929b73185dfa33f05/levenshtein-0.26.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:33e2517e8d3c221de2d1183f400aed64211fcfc77077b291ed9f3bb64f141cdc", size = 162266 },
+ { url = "https://files.pythonhosted.org/packages/11/50/6b1a5f3600caae40db0928f6775d7efc62c13dec2407d3d540bc4afdb72c/levenshtein-0.26.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9092b622765c7649dd1d8af0f43354723dd6f4e570ac079ffd90b41033957438", size = 246339 },
+ { url = "https://files.pythonhosted.org/packages/26/eb/ede282fcb495570898b39a0d2f21bbc9be5587d604c93a518ece80f3e7dc/levenshtein-0.26.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:fc16796c85d7d8b259881d59cc8b5e22e940901928c2ff6924b2c967924e8a0b", size = 1077937 },
+ { url = "https://files.pythonhosted.org/packages/35/41/eebe1c4a75f592d9bdc3c2595418f083bcad747e0aec52a1a9ffaae93f5c/levenshtein-0.26.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e4370733967f5994ceeed8dc211089bedd45832ee688cecea17bfd35a9eb22b9", size = 1330607 },
+ { url = "https://files.pythonhosted.org/packages/12/8e/4d34b1857adfd69c2a72d84bca1b8538d4cfaaf6fddd8599573f4281a9d1/levenshtein-0.26.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:3535ecfd88c9b283976b5bc61265855f59bba361881e92ed2b5367b6990c93fe", size = 1197505 },
+ { url = "https://files.pythonhosted.org/packages/c0/7b/6afcda1b0a0622cedaa4f7a5b3507c2384a7358fc051ccf619e5d2453bf2/levenshtein-0.26.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:90236e93d98bdfd708883a6767826fafd976dac8af8fc4a0fb423d4fa08e1bf0", size = 1352832 },
+ { url = "https://files.pythonhosted.org/packages/21/5e/0ed4e7b5c820b6bc40e2c391633292c3666400339042a3d306f0dc8fdcb4/levenshtein-0.26.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:04b7cabb82edf566b1579b3ed60aac0eec116655af75a3c551fee8754ffce2ea", size = 1135970 },
+ { url = "https://files.pythonhosted.org/packages/c9/91/3ff1abacb58642749dfd130ad855370e01b9c7aeaa73801964361f6e355f/levenshtein-0.26.1-cp312-cp312-win32.whl", hash = "sha256:ae382af8c76f6d2a040c0d9ca978baf461702ceb3f79a0a3f6da8d596a484c5b", size = 87599 },
+ { url = "https://files.pythonhosted.org/packages/7d/f9/727f3ba7843a3fb2a0f3db825358beea2a52bc96258874ee80cb2e5ecabb/levenshtein-0.26.1-cp312-cp312-win_amd64.whl", hash = "sha256:fd091209798cfdce53746f5769987b4108fe941c54fb2e058c016ffc47872918", size = 98809 },
+ { url = "https://files.pythonhosted.org/packages/d4/f4/f87f19222d279dbac429b9bc7ccae271d900fd9c48a581b8bc180ba6cd09/levenshtein-0.26.1-cp312-cp312-win_arm64.whl", hash = "sha256:7e82f2ea44a81ad6b30d92a110e04cd3c8c7c6034b629aca30a3067fa174ae89", size = 88227 },
+ { url = "https://files.pythonhosted.org/packages/7e/d6/b4b522b94d7b387c023d22944590befc0ac6b766ac6d197afd879ddd77fc/levenshtein-0.26.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:790374a9f5d2cbdb30ee780403a62e59bef51453ac020668c1564d1e43438f0e", size = 175836 },
+ { url = "https://files.pythonhosted.org/packages/25/76/06d1e26a8e6d0de68ef4a157dd57f6b342413c03550309e4aa095a453b28/levenshtein-0.26.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7b05c0415c386d00efda83d48db9db68edd02878d6dbc6df01194f12062be1bb", size = 157036 },
+ { url = "https://files.pythonhosted.org/packages/7e/23/21209a9e96b878aede3bea104533866762ba621e36fc344aa080db5feb02/levenshtein-0.26.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c3114586032361722ddededf28401ce5baf1cf617f9f49fb86b8766a45a423ff", size = 153326 },
+ { url = "https://files.pythonhosted.org/packages/06/38/9fc68685fffd8863b13864552eba8f3eb6a82a4dc558bf2c6553c2347d6c/levenshtein-0.26.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2532f8a13b68bf09f152d906f118a88da2063da22f44c90e904b142b0a53d534", size = 183693 },
+ { url = "https://files.pythonhosted.org/packages/f6/82/ccd7bdd7d431329da025e649c63b731df44f8cf31b957e269ae1c1dc9a8e/levenshtein-0.26.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:219c30be6aa734bf927188d1208b7d78d202a3eb017b1c5f01ab2034d2d4ccca", size = 190581 },
+ { url = "https://files.pythonhosted.org/packages/6e/c5/57f90b4aea1f89f853872b27a5a5dbce37b89ffeae42c02060b3e82038b2/levenshtein-0.26.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:397e245e77f87836308bd56305bba630010cd8298c34c4c44bd94990cdb3b7b1", size = 162446 },
+ { url = "https://files.pythonhosted.org/packages/fc/da/df6acca738921f896ce2d178821be866b43a583f85e2d1de63a4f8f78080/levenshtein-0.26.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aeff6ea3576f72e26901544c6c55c72a7b79b9983b6f913cba0e9edbf2f87a97", size = 247123 },
+ { url = "https://files.pythonhosted.org/packages/22/fb/f44a4c0d7784ccd32e4166714fea61e50f62b232162ae16332f45cb55ab2/levenshtein-0.26.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a19862e3539a697df722a08793994e334cd12791e8144851e8a1dee95a17ff63", size = 1077437 },
+ { url = "https://files.pythonhosted.org/packages/f0/5e/d9b9e7daa13cc7e2184a3c2422bb847f05d354ce15ba113b20d83e9ab366/levenshtein-0.26.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:dc3b5a64f57c3c078d58b1e447f7d68cad7ae1b23abe689215d03fc434f8f176", size = 1330362 },
+ { url = "https://files.pythonhosted.org/packages/bf/67/480d85bb516798014a6849be0225b246f35df4b54499c348c9c9e311f936/levenshtein-0.26.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:bb6c7347424a91317c5e1b68041677e4c8ed3e7823b5bbaedb95bffb3c3497ea", size = 1198721 },
+ { url = "https://files.pythonhosted.org/packages/9a/7d/889ff7d86903b6545665655627113d263c88c6d596c68fb09a640ee4f0a7/levenshtein-0.26.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:b817376de4195a207cc0e4ca37754c0e1e1078c2a2d35a6ae502afde87212f9e", size = 1351820 },
+ { url = "https://files.pythonhosted.org/packages/b9/29/cd42273150f08c200ed2d1879486d73502ee35265f162a77952f101d93a0/levenshtein-0.26.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7b50c3620ff47c9887debbb4c154aaaac3e46be7fc2e5789ee8dbe128bce6a17", size = 1135747 },
+ { url = "https://files.pythonhosted.org/packages/1d/90/cbcfa3dd86023e82036662a19fec2fcb48782d3f9fa322d44dc898d95a5d/levenshtein-0.26.1-cp313-cp313-win32.whl", hash = "sha256:9fb859da90262eb474c190b3ca1e61dee83add022c676520f5c05fdd60df902a", size = 87318 },
+ { url = "https://files.pythonhosted.org/packages/83/73/372edebc79fd09a8b2382cf1244d279ada5b795124f1e1c4fc73d9fbb00f/levenshtein-0.26.1-cp313-cp313-win_amd64.whl", hash = "sha256:8adcc90e3a5bfb0a463581d85e599d950fe3c2938ac6247b29388b64997f6e2d", size = 98418 },
+ { url = "https://files.pythonhosted.org/packages/b2/6d/f0160ea5a7bb7a62b3b3d56e9fc5024b440cb59555a90be2347abf2e7888/levenshtein-0.26.1-cp313-cp313-win_arm64.whl", hash = "sha256:c2599407e029865dc66d210b8804c7768cbdbf60f061d993bb488d5242b0b73e", size = 87792 },
+]
+
+[[package]]
+name = "loguru"
+version = "0.7.3"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "colorama", marker = "sys_platform == 'win32'" },
+ { name = "win32-setctime", marker = "sys_platform == 'win32'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/3a/05/a1dae3dffd1116099471c643b8924f5aa6524411dc6c63fdae648c4f1aca/loguru-0.7.3.tar.gz", hash = "sha256:19480589e77d47b8d85b2c827ad95d49bf31b0dcde16593892eb51dd18706eb6", size = 63559 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/0c/29/0348de65b8cc732daa3e33e67806420b2ae89bdce2b04af740289c5c6c8c/loguru-0.7.3-py3-none-any.whl", hash = "sha256:31a33c10c8e1e10422bfd431aeb5d351c7cf7fa671e3c4df004162264b28220c", size = 61595 },
+]
+
+[[package]]
+name = "mako"
+version = "1.3.9"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "markupsafe" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/62/4f/ddb1965901bc388958db9f0c991255b2c469349a741ae8c9cd8a562d70a6/mako-1.3.9.tar.gz", hash = "sha256:b5d65ff3462870feec922dbccf38f6efb44e5714d7b593a656be86663d8600ac", size = 392195 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/cd/83/de0a49e7de540513f53ab5d2e105321dedeb08a8f5850f0208decf4390ec/Mako-1.3.9-py3-none-any.whl", hash = "sha256:95920acccb578427a9aa38e37a186b1e43156c87260d7ba18ca63aa4c7cbd3a1", size = 78456 },
+]
+
+[[package]]
+name = "markdown-it-py"
+version = "3.0.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "mdurl" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528 },
+]
+
+[[package]]
+name = "markupsafe"
+version = "3.0.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/22/09/d1f21434c97fc42f09d290cbb6350d44eb12f09cc62c9476effdb33a18aa/MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf", size = 14274 },
+ { url = "https://files.pythonhosted.org/packages/6b/b0/18f76bba336fa5aecf79d45dcd6c806c280ec44538b3c13671d49099fdd0/MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225", size = 12348 },
+ { url = "https://files.pythonhosted.org/packages/e0/25/dd5c0f6ac1311e9b40f4af06c78efde0f3b5cbf02502f8ef9501294c425b/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028", size = 24149 },
+ { url = "https://files.pythonhosted.org/packages/f3/f0/89e7aadfb3749d0f52234a0c8c7867877876e0a20b60e2188e9850794c17/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8", size = 23118 },
+ { url = "https://files.pythonhosted.org/packages/d5/da/f2eeb64c723f5e3777bc081da884b414671982008c47dcc1873d81f625b6/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c", size = 22993 },
+ { url = "https://files.pythonhosted.org/packages/da/0e/1f32af846df486dce7c227fe0f2398dc7e2e51d4a370508281f3c1c5cddc/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557", size = 24178 },
+ { url = "https://files.pythonhosted.org/packages/c4/f6/bb3ca0532de8086cbff5f06d137064c8410d10779c4c127e0e47d17c0b71/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22", size = 23319 },
+ { url = "https://files.pythonhosted.org/packages/a2/82/8be4c96ffee03c5b4a034e60a31294daf481e12c7c43ab8e34a1453ee48b/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48", size = 23352 },
+ { url = "https://files.pythonhosted.org/packages/51/ae/97827349d3fcffee7e184bdf7f41cd6b88d9919c80f0263ba7acd1bbcb18/MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30", size = 15097 },
+ { url = "https://files.pythonhosted.org/packages/c1/80/a61f99dc3a936413c3ee4e1eecac96c0da5ed07ad56fd975f1a9da5bc630/MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87", size = 15601 },
+ { url = "https://files.pythonhosted.org/packages/83/0e/67eb10a7ecc77a0c2bbe2b0235765b98d164d81600746914bebada795e97/MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", size = 14274 },
+ { url = "https://files.pythonhosted.org/packages/2b/6d/9409f3684d3335375d04e5f05744dfe7e9f120062c9857df4ab490a1031a/MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", size = 12352 },
+ { url = "https://files.pythonhosted.org/packages/d2/f5/6eadfcd3885ea85fe2a7c128315cc1bb7241e1987443d78c8fe712d03091/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", size = 24122 },
+ { url = "https://files.pythonhosted.org/packages/0c/91/96cf928db8236f1bfab6ce15ad070dfdd02ed88261c2afafd4b43575e9e9/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", size = 23085 },
+ { url = "https://files.pythonhosted.org/packages/c2/cf/c9d56af24d56ea04daae7ac0940232d31d5a8354f2b457c6d856b2057d69/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79", size = 22978 },
+ { url = "https://files.pythonhosted.org/packages/2a/9f/8619835cd6a711d6272d62abb78c033bda638fdc54c4e7f4272cf1c0962b/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", size = 24208 },
+ { url = "https://files.pythonhosted.org/packages/f9/bf/176950a1792b2cd2102b8ffeb5133e1ed984547b75db47c25a67d3359f77/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca", size = 23357 },
+ { url = "https://files.pythonhosted.org/packages/ce/4f/9a02c1d335caabe5c4efb90e1b6e8ee944aa245c1aaaab8e8a618987d816/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", size = 23344 },
+ { url = "https://files.pythonhosted.org/packages/ee/55/c271b57db36f748f0e04a759ace9f8f759ccf22b4960c270c78a394f58be/MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", size = 15101 },
+ { url = "https://files.pythonhosted.org/packages/29/88/07df22d2dd4df40aba9f3e402e6dc1b8ee86297dddbad4872bd5e7b0094f/MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", size = 15603 },
+ { url = "https://files.pythonhosted.org/packages/62/6a/8b89d24db2d32d433dffcd6a8779159da109842434f1dd2f6e71f32f738c/MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", size = 14510 },
+ { url = "https://files.pythonhosted.org/packages/7a/06/a10f955f70a2e5a9bf78d11a161029d278eeacbd35ef806c3fd17b13060d/MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", size = 12486 },
+ { url = "https://files.pythonhosted.org/packages/34/cf/65d4a571869a1a9078198ca28f39fba5fbb910f952f9dbc5220afff9f5e6/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", size = 25480 },
+ { url = "https://files.pythonhosted.org/packages/0c/e3/90e9651924c430b885468b56b3d597cabf6d72be4b24a0acd1fa0e12af67/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", size = 23914 },
+ { url = "https://files.pythonhosted.org/packages/66/8c/6c7cf61f95d63bb866db39085150df1f2a5bd3335298f14a66b48e92659c/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe", size = 23796 },
+ { url = "https://files.pythonhosted.org/packages/bb/35/cbe9238ec3f47ac9a7c8b3df7a808e7cb50fe149dc7039f5f454b3fba218/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", size = 25473 },
+ { url = "https://files.pythonhosted.org/packages/e6/32/7621a4382488aa283cc05e8984a9c219abad3bca087be9ec77e89939ded9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a", size = 24114 },
+ { url = "https://files.pythonhosted.org/packages/0d/80/0985960e4b89922cb5a0bac0ed39c5b96cbc1a536a99f30e8c220a996ed9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", size = 24098 },
+ { url = "https://files.pythonhosted.org/packages/82/78/fedb03c7d5380df2427038ec8d973587e90561b2d90cd472ce9254cf348b/MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", size = 15208 },
+ { url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739 },
+]
+
+[[package]]
+name = "marshmallow"
+version = "3.26.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "packaging" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/ab/5e/5e53d26b42ab75491cda89b871dab9e97c840bf12c63ec58a1919710cd06/marshmallow-3.26.1.tar.gz", hash = "sha256:e6d8affb6cb61d39d26402096dc0aee12d5a26d490a121f118d2e81dc0719dc6", size = 221825 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/34/75/51952c7b2d3873b44a0028b1bd26a25078c18f92f256608e8d1dc61b39fd/marshmallow-3.26.1-py3-none-any.whl", hash = "sha256:3350409f20a70a7e4e11a27661187b77cdcaeb20abca41c1454fe33636bea09c", size = 50878 },
+]
+
+[[package]]
+name = "mdurl"
+version = "0.1.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 },
+]
+
+[[package]]
+name = "mini-racer"
+version = "0.12.4"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/8c/2d/e051f58e17117b1b8b11a7d17622c1528fa9002c553943c6b677c1b412da/mini_racer-0.12.4.tar.gz", hash = "sha256:84c67553ce9f3736d4c617d8a3f882949d37a46cfb47fe11dab33dd6704e62a4", size = 447529 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/71/fe/1452b6c74cae9e8cd7b6a16d8b1ef08bba4dd0ed373a95f3b401c2e712ea/mini_racer-0.12.4-py3-none-macosx_10_9_x86_64.whl", hash = "sha256:bce8a3cee946575a352f5e65335903bc148da42c036d0c738ac67e931600e455", size = 15701219 },
+ { url = "https://files.pythonhosted.org/packages/99/ae/c22478eff26e6136341e6b40d34f8d285f910ca4d2e2a0ca4703ef87be79/mini_racer-0.12.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:56c832e6ac2db6a304d1e8e80030615297aafbc6940f64f3479af4ba16abccd5", size = 14566436 },
+ { url = "https://files.pythonhosted.org/packages/44/89/f062aa116b14fcace91f0af86a37605f0ba7c07a01c8101b5ea104d489b1/mini_racer-0.12.4-py3-none-manylinux_2_31_aarch64.whl", hash = "sha256:b82c4bd2976e280ed0a72c9c2de01b13f18ccfbe6f4892cbc22aae04410fac3c", size = 14931664 },
+ { url = "https://files.pythonhosted.org/packages/9c/a1/09122c88a0dd0a2141b0ea068d70f5d31acd0015d6f3157b8efd3ff7e026/mini_racer-0.12.4-py3-none-manylinux_2_31_x86_64.whl", hash = "sha256:69a1c44d02a9069b881684cef15a2d747fe0743df29eadc881fda7002aae5fd2", size = 14955238 },
+ { url = "https://files.pythonhosted.org/packages/6c/3b/826e41f92631560e5c6ca2aa4ef9005bdccf9290c1e7ddebe05e0a3b8c7c/mini_racer-0.12.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:499dbc267dfe60e954bc1b6c3787f7b10fc41fe1975853c9a6ddb55eb83dc4d9", size = 15211136 },
+ { url = "https://files.pythonhosted.org/packages/e5/37/15b30316630d1f63b025f058dc92efa75931a37315c34ca07f80be2cc405/mini_racer-0.12.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:231f949f5787d18351939f1fe59e5a6fe134bccb5ecf8f836b9beab69d91c8d9", size = 15128684 },
+ { url = "https://files.pythonhosted.org/packages/5c/0e/a9943f90b4a8a6d3849b81a00a00d2db128d876365385af382a0e2caf191/mini_racer-0.12.4-py3-none-win_amd64.whl", hash = "sha256:9446e3bd6a4eb9fbedf1861326f7476080995a31c9b69308acef17e5b7ecaa1b", size = 13674040 },
+]
+
+[[package]]
+name = "modal"
+version = "0.73.51"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "aiohttp" },
+ { name = "certifi" },
+ { name = "click" },
+ { name = "fastapi" },
+ { name = "grpclib" },
+ { name = "protobuf" },
+ { name = "rich" },
+ { name = "synchronicity" },
+ { name = "toml" },
+ { name = "typer" },
+ { name = "types-certifi" },
+ { name = "types-toml" },
+ { name = "typing-extensions" },
+ { name = "watchfiles" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/3c/d0/ef9322bc8fc653e1b24422287b108ca9a0cd489b59691b77082c4ee6a840/modal-0.73.51.tar.gz", hash = "sha256:497d115ae92b46b65f0b8d2391465e327cd67f05ef11aa3cbc5f74f184cbefae", size = 468049 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c0/a3/57bccda40048ad4feae34f60ee7a88b57f5d7e0162c7bba51f7c16d90b85/modal-0.73.51-py3-none-any.whl", hash = "sha256:fb173b405ed139666657580a2ffee313004b84643585052bdfa7447acf2df599", size = 534085 },
+]
+
+[[package]]
+name = "multidict"
+version = "6.1.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/d6/be/504b89a5e9ca731cd47487e91c469064f8ae5af93b7259758dcfc2b9c848/multidict-6.1.0.tar.gz", hash = "sha256:22ae2ebf9b0c69d206c003e2f6a914ea33f0a932d4aa16f236afc049d9958f4a", size = 64002 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/fd/16/92057c74ba3b96d5e211b553895cd6dc7cc4d1e43d9ab8fafc727681ef71/multidict-6.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:b04772ed465fa3cc947db808fa306d79b43e896beb677a56fb2347ca1a49c1fa", size = 48713 },
+ { url = "https://files.pythonhosted.org/packages/94/3d/37d1b8893ae79716179540b89fc6a0ee56b4a65fcc0d63535c6f5d96f217/multidict-6.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6180c0ae073bddeb5a97a38c03f30c233e0a4d39cd86166251617d1bbd0af436", size = 29516 },
+ { url = "https://files.pythonhosted.org/packages/a2/12/adb6b3200c363062f805275b4c1e656be2b3681aada66c80129932ff0bae/multidict-6.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:071120490b47aa997cca00666923a83f02c7fbb44f71cf7f136df753f7fa8761", size = 29557 },
+ { url = "https://files.pythonhosted.org/packages/47/e9/604bb05e6e5bce1e6a5cf80a474e0f072e80d8ac105f1b994a53e0b28c42/multidict-6.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50b3a2710631848991d0bf7de077502e8994c804bb805aeb2925a981de58ec2e", size = 130170 },
+ { url = "https://files.pythonhosted.org/packages/7e/13/9efa50801785eccbf7086b3c83b71a4fb501a4d43549c2f2f80b8787d69f/multidict-6.1.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b58c621844d55e71c1b7f7c498ce5aa6985d743a1a59034c57a905b3f153c1ef", size = 134836 },
+ { url = "https://files.pythonhosted.org/packages/bf/0f/93808b765192780d117814a6dfcc2e75de6dcc610009ad408b8814dca3ba/multidict-6.1.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55b6d90641869892caa9ca42ff913f7ff1c5ece06474fbd32fb2cf6834726c95", size = 133475 },
+ { url = "https://files.pythonhosted.org/packages/d3/c8/529101d7176fe7dfe1d99604e48d69c5dfdcadb4f06561f465c8ef12b4df/multidict-6.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b820514bfc0b98a30e3d85462084779900347e4d49267f747ff54060cc33925", size = 131049 },
+ { url = "https://files.pythonhosted.org/packages/ca/0c/fc85b439014d5a58063e19c3a158a889deec399d47b5269a0f3b6a2e28bc/multidict-6.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10a9b09aba0c5b48c53761b7c720aaaf7cf236d5fe394cd399c7ba662d5f9966", size = 120370 },
+ { url = "https://files.pythonhosted.org/packages/db/46/d4416eb20176492d2258fbd47b4abe729ff3b6e9c829ea4236f93c865089/multidict-6.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1e16bf3e5fc9f44632affb159d30a437bfe286ce9e02754759be5536b169b305", size = 125178 },
+ { url = "https://files.pythonhosted.org/packages/5b/46/73697ad7ec521df7de5531a32780bbfd908ded0643cbe457f981a701457c/multidict-6.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:76f364861c3bfc98cbbcbd402d83454ed9e01a5224bb3a28bf70002a230f73e2", size = 119567 },
+ { url = "https://files.pythonhosted.org/packages/cd/ed/51f060e2cb0e7635329fa6ff930aa5cffa17f4c7f5c6c3ddc3500708e2f2/multidict-6.1.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:820c661588bd01a0aa62a1283f20d2be4281b086f80dad9e955e690c75fb54a2", size = 129822 },
+ { url = "https://files.pythonhosted.org/packages/df/9e/ee7d1954b1331da3eddea0c4e08d9142da5f14b1321c7301f5014f49d492/multidict-6.1.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:0e5f362e895bc5b9e67fe6e4ded2492d8124bdf817827f33c5b46c2fe3ffaca6", size = 128656 },
+ { url = "https://files.pythonhosted.org/packages/77/00/8538f11e3356b5d95fa4b024aa566cde7a38aa7a5f08f4912b32a037c5dc/multidict-6.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3ec660d19bbc671e3a6443325f07263be452c453ac9e512f5eb935e7d4ac28b3", size = 125360 },
+ { url = "https://files.pythonhosted.org/packages/be/05/5d334c1f2462d43fec2363cd00b1c44c93a78c3925d952e9a71caf662e96/multidict-6.1.0-cp312-cp312-win32.whl", hash = "sha256:58130ecf8f7b8112cdb841486404f1282b9c86ccb30d3519faf301b2e5659133", size = 26382 },
+ { url = "https://files.pythonhosted.org/packages/a3/bf/f332a13486b1ed0496d624bcc7e8357bb8053823e8cd4b9a18edc1d97e73/multidict-6.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:188215fc0aafb8e03341995e7c4797860181562380f81ed0a87ff455b70bf1f1", size = 28529 },
+ { url = "https://files.pythonhosted.org/packages/22/67/1c7c0f39fe069aa4e5d794f323be24bf4d33d62d2a348acdb7991f8f30db/multidict-6.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:d569388c381b24671589335a3be6e1d45546c2988c2ebe30fdcada8457a31008", size = 48771 },
+ { url = "https://files.pythonhosted.org/packages/3c/25/c186ee7b212bdf0df2519eacfb1981a017bda34392c67542c274651daf23/multidict-6.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:052e10d2d37810b99cc170b785945421141bf7bb7d2f8799d431e7db229c385f", size = 29533 },
+ { url = "https://files.pythonhosted.org/packages/67/5e/04575fd837e0958e324ca035b339cea174554f6f641d3fb2b4f2e7ff44a2/multidict-6.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f90c822a402cb865e396a504f9fc8173ef34212a342d92e362ca498cad308e28", size = 29595 },
+ { url = "https://files.pythonhosted.org/packages/d3/b2/e56388f86663810c07cfe4a3c3d87227f3811eeb2d08450b9e5d19d78876/multidict-6.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b225d95519a5bf73860323e633a664b0d85ad3d5bede6d30d95b35d4dfe8805b", size = 130094 },
+ { url = "https://files.pythonhosted.org/packages/6c/ee/30ae9b4186a644d284543d55d491fbd4239b015d36b23fea43b4c94f7052/multidict-6.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:23bfd518810af7de1116313ebd9092cb9aa629beb12f6ed631ad53356ed6b86c", size = 134876 },
+ { url = "https://files.pythonhosted.org/packages/84/c7/70461c13ba8ce3c779503c70ec9d0345ae84de04521c1f45a04d5f48943d/multidict-6.1.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c09fcfdccdd0b57867577b719c69e347a436b86cd83747f179dbf0cc0d4c1f3", size = 133500 },
+ { url = "https://files.pythonhosted.org/packages/4a/9f/002af221253f10f99959561123fae676148dd730e2daa2cd053846a58507/multidict-6.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf6bea52ec97e95560af5ae576bdac3aa3aae0b6758c6efa115236d9e07dae44", size = 131099 },
+ { url = "https://files.pythonhosted.org/packages/82/42/d1c7a7301d52af79d88548a97e297f9d99c961ad76bbe6f67442bb77f097/multidict-6.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57feec87371dbb3520da6192213c7d6fc892d5589a93db548331954de8248fd2", size = 120403 },
+ { url = "https://files.pythonhosted.org/packages/68/f3/471985c2c7ac707547553e8f37cff5158030d36bdec4414cb825fbaa5327/multidict-6.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0c3f390dc53279cbc8ba976e5f8035eab997829066756d811616b652b00a23a3", size = 125348 },
+ { url = "https://files.pythonhosted.org/packages/67/2c/e6df05c77e0e433c214ec1d21ddd203d9a4770a1f2866a8ca40a545869a0/multidict-6.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:59bfeae4b25ec05b34f1956eaa1cb38032282cd4dfabc5056d0a1ec4d696d3aa", size = 119673 },
+ { url = "https://files.pythonhosted.org/packages/c5/cd/bc8608fff06239c9fb333f9db7743a1b2eafe98c2666c9a196e867a3a0a4/multidict-6.1.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:b2f59caeaf7632cc633b5cf6fc449372b83bbdf0da4ae04d5be36118e46cc0aa", size = 129927 },
+ { url = "https://files.pythonhosted.org/packages/44/8e/281b69b7bc84fc963a44dc6e0bbcc7150e517b91df368a27834299a526ac/multidict-6.1.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:37bb93b2178e02b7b618893990941900fd25b6b9ac0fa49931a40aecdf083fe4", size = 128711 },
+ { url = "https://files.pythonhosted.org/packages/12/a4/63e7cd38ed29dd9f1881d5119f272c898ca92536cdb53ffe0843197f6c85/multidict-6.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4e9f48f58c2c523d5a06faea47866cd35b32655c46b443f163d08c6d0ddb17d6", size = 125519 },
+ { url = "https://files.pythonhosted.org/packages/38/e0/4f5855037a72cd8a7a2f60a3952d9aa45feedb37ae7831642102604e8a37/multidict-6.1.0-cp313-cp313-win32.whl", hash = "sha256:3a37ffb35399029b45c6cc33640a92bef403c9fd388acce75cdc88f58bd19a81", size = 26426 },
+ { url = "https://files.pythonhosted.org/packages/7e/a5/17ee3a4db1e310b7405f5d25834460073a8ccd86198ce044dfaf69eac073/multidict-6.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:e9aa71e15d9d9beaad2c6b9319edcdc0a49a43ef5c0a4c8265ca9ee7d6c67774", size = 28531 },
+ { url = "https://files.pythonhosted.org/packages/99/b7/b9e70fde2c0f0c9af4cc5277782a89b66d35948ea3369ec9f598358c3ac5/multidict-6.1.0-py3-none-any.whl", hash = "sha256:48e171e52d1c4d33888e529b999e5900356b9ae588c2f09a52dcefb158b27506", size = 10051 },
+]
+
+[[package]]
+name = "mypy"
+version = "1.15.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "mypy-extensions" },
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/ce/43/d5e49a86afa64bd3839ea0d5b9c7103487007d728e1293f52525d6d5486a/mypy-1.15.0.tar.gz", hash = "sha256:404534629d51d3efea5c800ee7c42b72a6554d6c400e6a79eafe15d11341fd43", size = 3239717 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/98/3a/03c74331c5eb8bd025734e04c9840532226775c47a2c39b56a0c8d4f128d/mypy-1.15.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:aea39e0583d05124836ea645f412e88a5c7d0fd77a6d694b60d9b6b2d9f184fd", size = 10793981 },
+ { url = "https://files.pythonhosted.org/packages/f0/1a/41759b18f2cfd568848a37c89030aeb03534411eef981df621d8fad08a1d/mypy-1.15.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2f2147ab812b75e5b5499b01ade1f4a81489a147c01585cda36019102538615f", size = 9749175 },
+ { url = "https://files.pythonhosted.org/packages/12/7e/873481abf1ef112c582db832740f4c11b2bfa510e829d6da29b0ab8c3f9c/mypy-1.15.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ce436f4c6d218a070048ed6a44c0bbb10cd2cc5e272b29e7845f6a2f57ee4464", size = 11455675 },
+ { url = "https://files.pythonhosted.org/packages/b3/d0/92ae4cde706923a2d3f2d6c39629134063ff64b9dedca9c1388363da072d/mypy-1.15.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8023ff13985661b50a5928fc7a5ca15f3d1affb41e5f0a9952cb68ef090b31ee", size = 12410020 },
+ { url = "https://files.pythonhosted.org/packages/46/8b/df49974b337cce35f828ba6fda228152d6db45fed4c86ba56ffe442434fd/mypy-1.15.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1124a18bc11a6a62887e3e137f37f53fbae476dc36c185d549d4f837a2a6a14e", size = 12498582 },
+ { url = "https://files.pythonhosted.org/packages/13/50/da5203fcf6c53044a0b699939f31075c45ae8a4cadf538a9069b165c1050/mypy-1.15.0-cp312-cp312-win_amd64.whl", hash = "sha256:171a9ca9a40cd1843abeca0e405bc1940cd9b305eaeea2dda769ba096932bb22", size = 9366614 },
+ { url = "https://files.pythonhosted.org/packages/6a/9b/fd2e05d6ffff24d912f150b87db9e364fa8282045c875654ce7e32fffa66/mypy-1.15.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:93faf3fdb04768d44bf28693293f3904bbb555d076b781ad2530214ee53e3445", size = 10788592 },
+ { url = "https://files.pythonhosted.org/packages/74/37/b246d711c28a03ead1fd906bbc7106659aed7c089d55fe40dd58db812628/mypy-1.15.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:811aeccadfb730024c5d3e326b2fbe9249bb7413553f15499a4050f7c30e801d", size = 9753611 },
+ { url = "https://files.pythonhosted.org/packages/a6/ac/395808a92e10cfdac8003c3de9a2ab6dc7cde6c0d2a4df3df1b815ffd067/mypy-1.15.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:98b7b9b9aedb65fe628c62a6dc57f6d5088ef2dfca37903a7d9ee374d03acca5", size = 11438443 },
+ { url = "https://files.pythonhosted.org/packages/d2/8b/801aa06445d2de3895f59e476f38f3f8d610ef5d6908245f07d002676cbf/mypy-1.15.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c43a7682e24b4f576d93072216bf56eeff70d9140241f9edec0c104d0c515036", size = 12402541 },
+ { url = "https://files.pythonhosted.org/packages/c7/67/5a4268782eb77344cc613a4cf23540928e41f018a9a1ec4c6882baf20ab8/mypy-1.15.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:baefc32840a9f00babd83251560e0ae1573e2f9d1b067719479bfb0e987c6357", size = 12494348 },
+ { url = "https://files.pythonhosted.org/packages/83/3e/57bb447f7bbbfaabf1712d96f9df142624a386d98fb026a761532526057e/mypy-1.15.0-cp313-cp313-win_amd64.whl", hash = "sha256:b9378e2c00146c44793c98b8d5a61039a048e31f429fb0eb546d93f4b000bedf", size = 9373648 },
+ { url = "https://files.pythonhosted.org/packages/09/4e/a7d65c7322c510de2c409ff3828b03354a7c43f5a8ed458a7a131b41c7b9/mypy-1.15.0-py3-none-any.whl", hash = "sha256:5469affef548bd1895d86d3bf10ce2b44e33d86923c29e4d675b3e323437ea3e", size = 2221777 },
+]
+
+[[package]]
+name = "mypy-extensions"
+version = "1.0.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/98/a4/1ab47638b92648243faf97a5aeb6ea83059cc3624972ab6b8d2316078d3f/mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782", size = 4433 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/2a/e2/5d3f6ada4297caebe1a2add3b126fe800c96f56dbe5d1988a2cbe0b267aa/mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d", size = 4695 },
+]
+
+[[package]]
+name = "narwhals"
+version = "1.26.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/18/6f/75929abaac73088fe34c788ecb40db20252174bcd00b8612381aebb954ee/narwhals-1.26.0.tar.gz", hash = "sha256:b9d7605bf1d97a9d87783a69748c39150964e2a1ab0e5a6fef3e59e56772639e", size = 248933 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/15/fc/420680ad8b0cf81372eee7a213a7b7173ec5a628f0d5b2426047fe55c3b3/narwhals-1.26.0-py3-none-any.whl", hash = "sha256:4af8bbdea9e45638bb9a981568a8dfa880e40eb7dcf740d19fd32aea79223c6f", size = 306574 },
+]
+
+[[package]]
+name = "networkx"
+version = "3.4.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/fd/1d/06475e1cd5264c0b870ea2cc6fdb3e37177c1e565c43f56ff17a10e3937f/networkx-3.4.2.tar.gz", hash = "sha256:307c3669428c5362aab27c8a1260aa8f47c4e91d3891f48be0141738d8d053e1", size = 2151368 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/b9/54/dd730b32ea14ea797530a4479b2ed46a6fb250f682a9cfb997e968bf0261/networkx-3.4.2-py3-none-any.whl", hash = "sha256:df5d4365b724cf81b8c6a7312509d0c22386097011ad1abe274afd5e9d3bbc5f", size = 1723263 },
+]
+
+[[package]]
+name = "nodeenv"
+version = "1.9.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/43/16/fc88b08840de0e0a72a2f9d8c6bae36be573e475a6326ae854bcc549fc45/nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f", size = 47437 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9", size = 22314 },
+]
+
+[[package]]
+name = "numpy"
+version = "2.2.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/ec/d0/c12ddfd3a02274be06ffc71f3efc6d0e457b0409c4481596881e748cb264/numpy-2.2.2.tar.gz", hash = "sha256:ed6906f61834d687738d25988ae117683705636936cc605be0bb208b23df4d8f", size = 20233295 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/0c/e6/847d15770ab7a01e807bdfcd4ead5bdae57c0092b7dc83878171b6af97bb/numpy-2.2.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ac9bea18d6d58a995fac1b2cb4488e17eceeac413af014b1dd26170b766d8467", size = 20912636 },
+ { url = "https://files.pythonhosted.org/packages/d1/af/f83580891577b13bd7e261416120e036d0d8fb508c8a43a73e38928b794b/numpy-2.2.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:23ae9f0c2d889b7b2d88a3791f6c09e2ef827c2446f1c4a3e3e76328ee4afd9a", size = 14098403 },
+ { url = "https://files.pythonhosted.org/packages/2b/86/d019fb60a9d0f1d4cf04b014fe88a9135090adfadcc31c1fadbb071d7fa7/numpy-2.2.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:3074634ea4d6df66be04f6728ee1d173cfded75d002c75fac79503a880bf3825", size = 5128938 },
+ { url = "https://files.pythonhosted.org/packages/7a/1b/50985edb6f1ec495a1c36452e860476f5b7ecdc3fc59ea89ccad3c4926c5/numpy-2.2.2-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:8ec0636d3f7d68520afc6ac2dc4b8341ddb725039de042faf0e311599f54eb37", size = 6661937 },
+ { url = "https://files.pythonhosted.org/packages/f4/1b/17efd94cad1b9d605c3f8907fb06bcffc4ce4d1d14d46b95316cccccf2b9/numpy-2.2.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2ffbb1acd69fdf8e89dd60ef6182ca90a743620957afb7066385a7bbe88dc748", size = 14049518 },
+ { url = "https://files.pythonhosted.org/packages/5b/73/65d2f0b698df1731e851e3295eb29a5ab8aa06f763f7e4188647a809578d/numpy-2.2.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0349b025e15ea9d05c3d63f9657707a4e1d471128a3b1d876c095f328f8ff7f0", size = 16099146 },
+ { url = "https://files.pythonhosted.org/packages/d5/69/308f55c0e19d4b5057b5df286c5433822e3c8039ede06d4051d96f1c2c4e/numpy-2.2.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:463247edcee4a5537841d5350bc87fe8e92d7dd0e8c71c995d2c6eecb8208278", size = 15246336 },
+ { url = "https://files.pythonhosted.org/packages/f0/d8/d8d333ad0d8518d077a21aeea7b7c826eff766a2b1ce1194dea95ca0bacf/numpy-2.2.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9dd47ff0cb2a656ad69c38da850df3454da88ee9a6fde0ba79acceee0e79daba", size = 17863507 },
+ { url = "https://files.pythonhosted.org/packages/82/6e/0b84ad3103ffc16d6673e63b5acbe7901b2af96c2837174c6318c98e27ab/numpy-2.2.2-cp312-cp312-win32.whl", hash = "sha256:4525b88c11906d5ab1b0ec1f290996c0020dd318af8b49acaa46f198b1ffc283", size = 6276491 },
+ { url = "https://files.pythonhosted.org/packages/fc/84/7f801a42a67b9772a883223a0a1e12069a14626c81a732bd70aac57aebc1/numpy-2.2.2-cp312-cp312-win_amd64.whl", hash = "sha256:5acea83b801e98541619af398cc0109ff48016955cc0818f478ee9ef1c5c3dcb", size = 12616372 },
+ { url = "https://files.pythonhosted.org/packages/e1/fe/df5624001f4f5c3e0b78e9017bfab7fdc18a8d3b3d3161da3d64924dd659/numpy-2.2.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b208cfd4f5fe34e1535c08983a1a6803fdbc7a1e86cf13dd0c61de0b51a0aadc", size = 20899188 },
+ { url = "https://files.pythonhosted.org/packages/a9/80/d349c3b5ed66bd3cb0214be60c27e32b90a506946857b866838adbe84040/numpy-2.2.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d0bbe7dd86dca64854f4b6ce2ea5c60b51e36dfd597300057cf473d3615f2369", size = 14113972 },
+ { url = "https://files.pythonhosted.org/packages/9d/50/949ec9cbb28c4b751edfa64503f0913cbfa8d795b4a251e7980f13a8a655/numpy-2.2.2-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:22ea3bb552ade325530e72a0c557cdf2dea8914d3a5e1fecf58fa5dbcc6f43cd", size = 5114294 },
+ { url = "https://files.pythonhosted.org/packages/8d/f3/399c15629d5a0c68ef2aa7621d430b2be22034f01dd7f3c65a9c9666c445/numpy-2.2.2-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:128c41c085cab8a85dc29e66ed88c05613dccf6bc28b3866cd16050a2f5448be", size = 6648426 },
+ { url = "https://files.pythonhosted.org/packages/2c/03/c72474c13772e30e1bc2e558cdffd9123c7872b731263d5648b5c49dd459/numpy-2.2.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:250c16b277e3b809ac20d1f590716597481061b514223c7badb7a0f9993c7f84", size = 14045990 },
+ { url = "https://files.pythonhosted.org/packages/83/9c/96a9ab62274ffafb023f8ee08c88d3d31ee74ca58869f859db6845494fa6/numpy-2.2.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0c8854b09bc4de7b041148d8550d3bd712b5c21ff6a8ed308085f190235d7ff", size = 16096614 },
+ { url = "https://files.pythonhosted.org/packages/d5/34/cd0a735534c29bec7093544b3a509febc9b0df77718a9b41ffb0809c9f46/numpy-2.2.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b6fb9c32a91ec32a689ec6410def76443e3c750e7cfc3fb2206b985ffb2b85f0", size = 15242123 },
+ { url = "https://files.pythonhosted.org/packages/5e/6d/541717a554a8f56fa75e91886d9b79ade2e595918690eb5d0d3dbd3accb9/numpy-2.2.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:57b4012e04cc12b78590a334907e01b3a85efb2107df2b8733ff1ed05fce71de", size = 17859160 },
+ { url = "https://files.pythonhosted.org/packages/b9/a5/fbf1f2b54adab31510728edd06a05c1b30839f37cf8c9747cb85831aaf1b/numpy-2.2.2-cp313-cp313-win32.whl", hash = "sha256:4dbd80e453bd34bd003b16bd802fac70ad76bd463f81f0c518d1245b1c55e3d9", size = 6273337 },
+ { url = "https://files.pythonhosted.org/packages/56/e5/01106b9291ef1d680f82bc47d0c5b5e26dfed15b0754928e8f856c82c881/numpy-2.2.2-cp313-cp313-win_amd64.whl", hash = "sha256:5a8c863ceacae696aff37d1fd636121f1a512117652e5dfb86031c8d84836369", size = 12609010 },
+ { url = "https://files.pythonhosted.org/packages/9f/30/f23d9876de0f08dceb707c4dcf7f8dd7588266745029debb12a3cdd40be6/numpy-2.2.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:b3482cb7b3325faa5f6bc179649406058253d91ceda359c104dac0ad320e1391", size = 20924451 },
+ { url = "https://files.pythonhosted.org/packages/6a/ec/6ea85b2da9d5dfa1dbb4cb3c76587fc8ddcae580cb1262303ab21c0926c4/numpy-2.2.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:9491100aba630910489c1d0158034e1c9a6546f0b1340f716d522dc103788e39", size = 14122390 },
+ { url = "https://files.pythonhosted.org/packages/68/05/bfbdf490414a7dbaf65b10c78bc243f312c4553234b6d91c94eb7c4b53c2/numpy-2.2.2-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:41184c416143defa34cc8eb9d070b0a5ba4f13a0fa96a709e20584638254b317", size = 5156590 },
+ { url = "https://files.pythonhosted.org/packages/f7/ec/fe2e91b2642b9d6544518388a441bcd65c904cea38d9ff998e2e8ebf808e/numpy-2.2.2-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:7dca87ca328f5ea7dafc907c5ec100d187911f94825f8700caac0b3f4c384b49", size = 6671958 },
+ { url = "https://files.pythonhosted.org/packages/b1/6f/6531a78e182f194d33ee17e59d67d03d0d5a1ce7f6be7343787828d1bd4a/numpy-2.2.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0bc61b307655d1a7f9f4b043628b9f2b721e80839914ede634e3d485913e1fb2", size = 14019950 },
+ { url = "https://files.pythonhosted.org/packages/e1/fb/13c58591d0b6294a08cc40fcc6b9552d239d773d520858ae27f39997f2ae/numpy-2.2.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fad446ad0bc886855ddf5909cbf8cb5d0faa637aaa6277fb4b19ade134ab3c7", size = 16079759 },
+ { url = "https://files.pythonhosted.org/packages/2c/f2/f2f8edd62abb4b289f65a7f6d1f3650273af00b91b7267a2431be7f1aec6/numpy-2.2.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:149d1113ac15005652e8d0d3f6fd599360e1a708a4f98e43c9c77834a28238cb", size = 15226139 },
+ { url = "https://files.pythonhosted.org/packages/aa/29/14a177f1a90b8ad8a592ca32124ac06af5eff32889874e53a308f850290f/numpy-2.2.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:106397dbbb1896f99e044efc90360d098b3335060375c26aa89c0d8a97c5f648", size = 17856316 },
+ { url = "https://files.pythonhosted.org/packages/95/03/242ae8d7b97f4e0e4ab8dd51231465fb23ed5e802680d629149722e3faf1/numpy-2.2.2-cp313-cp313t-win32.whl", hash = "sha256:0eec19f8af947a61e968d5429f0bd92fec46d92b0008d0a6685b40d6adf8a4f4", size = 6329134 },
+ { url = "https://files.pythonhosted.org/packages/80/94/cd9e9b04012c015cb6320ab3bf43bc615e248dddfeb163728e800a5d96f0/numpy-2.2.2-cp313-cp313t-win_amd64.whl", hash = "sha256:97b974d3ba0fb4612b77ed35d7627490e8e3dff56ab41454d9e8b23448940576", size = 12696208 },
+]
+
+[[package]]
+name = "openai"
+version = "1.61.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "anyio" },
+ { name = "distro" },
+ { name = "httpx" },
+ { name = "jiter" },
+ { name = "pydantic" },
+ { name = "sniffio" },
+ { name = "tqdm" },
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/d9/cf/61e71ce64cf0a38f029da0f9a5f10c9fa0e69a7a977b537126dac50adfea/openai-1.61.1.tar.gz", hash = "sha256:ce1851507218209961f89f3520e06726c0aa7d0512386f0f977e3ac3e4f2472e", size = 350784 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/9a/b6/2e2a011b2dc27a6711376808b4cd8c922c476ea0f1420b39892117fa8563/openai-1.61.1-py3-none-any.whl", hash = "sha256:72b0826240ce26026ac2cd17951691f046e5be82ad122d20a8e1b30ca18bd11e", size = 463126 },
+]
+
+[[package]]
+name = "orjson"
+version = "3.10.15"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/ae/f9/5dea21763eeff8c1590076918a446ea3d6140743e0e36f58f369928ed0f4/orjson-3.10.15.tar.gz", hash = "sha256:05ca7fe452a2e9d8d9d706a2984c95b9c2ebc5db417ce0b7a49b91d50642a23e", size = 5282482 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/66/85/22fe737188905a71afcc4bf7cc4c79cd7f5bbe9ed1fe0aac4ce4c33edc30/orjson-3.10.15-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:9d11c0714fc85bfcf36ada1179400862da3288fc785c30e8297844c867d7505a", size = 249504 },
+ { url = "https://files.pythonhosted.org/packages/48/b7/2622b29f3afebe938a0a9037e184660379797d5fd5234e5998345d7a5b43/orjson-3.10.15-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dba5a1e85d554e3897fa9fe6fbcff2ed32d55008973ec9a2b992bd9a65d2352d", size = 125080 },
+ { url = "https://files.pythonhosted.org/packages/ce/8f/0b72a48f4403d0b88b2a41450c535b3e8989e8a2d7800659a967efc7c115/orjson-3.10.15-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7723ad949a0ea502df656948ddd8b392780a5beaa4c3b5f97e525191b102fff0", size = 150121 },
+ { url = "https://files.pythonhosted.org/packages/06/ec/acb1a20cd49edb2000be5a0404cd43e3c8aad219f376ac8c60b870518c03/orjson-3.10.15-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6fd9bc64421e9fe9bd88039e7ce8e58d4fead67ca88e3a4014b143cec7684fd4", size = 139796 },
+ { url = "https://files.pythonhosted.org/packages/33/e1/f7840a2ea852114b23a52a1c0b2bea0a1ea22236efbcdb876402d799c423/orjson-3.10.15-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dadba0e7b6594216c214ef7894c4bd5f08d7c0135f4dd0145600be4fbcc16767", size = 154636 },
+ { url = "https://files.pythonhosted.org/packages/fa/da/31543337febd043b8fa80a3b67de627669b88c7b128d9ad4cc2ece005b7a/orjson-3.10.15-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b48f59114fe318f33bbaee8ebeda696d8ccc94c9e90bc27dbe72153094e26f41", size = 130621 },
+ { url = "https://files.pythonhosted.org/packages/ed/78/66115dc9afbc22496530d2139f2f4455698be444c7c2475cb48f657cefc9/orjson-3.10.15-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:035fb83585e0f15e076759b6fedaf0abb460d1765b6a36f48018a52858443514", size = 138516 },
+ { url = "https://files.pythonhosted.org/packages/22/84/cd4f5fb5427ffcf823140957a47503076184cb1ce15bcc1165125c26c46c/orjson-3.10.15-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d13b7fe322d75bf84464b075eafd8e7dd9eae05649aa2a5354cfa32f43c59f17", size = 130762 },
+ { url = "https://files.pythonhosted.org/packages/93/1f/67596b711ba9f56dd75d73b60089c5c92057f1130bb3a25a0f53fb9a583b/orjson-3.10.15-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:7066b74f9f259849629e0d04db6609db4cf5b973248f455ba5d3bd58a4daaa5b", size = 414700 },
+ { url = "https://files.pythonhosted.org/packages/7c/0c/6a3b3271b46443d90efb713c3e4fe83fa8cd71cda0d11a0f69a03f437c6e/orjson-3.10.15-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:88dc3f65a026bd3175eb157fea994fca6ac7c4c8579fc5a86fc2114ad05705b7", size = 141077 },
+ { url = "https://files.pythonhosted.org/packages/3b/9b/33c58e0bfc788995eccd0d525ecd6b84b40d7ed182dd0751cd4c1322ac62/orjson-3.10.15-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b342567e5465bd99faa559507fe45e33fc76b9fb868a63f1642c6bc0735ad02a", size = 129898 },
+ { url = "https://files.pythonhosted.org/packages/01/c1/d577ecd2e9fa393366a1ea0a9267f6510d86e6c4bb1cdfb9877104cac44c/orjson-3.10.15-cp312-cp312-win32.whl", hash = "sha256:0a4f27ea5617828e6b58922fdbec67b0aa4bb844e2d363b9244c47fa2180e665", size = 142566 },
+ { url = "https://files.pythonhosted.org/packages/ed/eb/a85317ee1732d1034b92d56f89f1de4d7bf7904f5c8fb9dcdd5b1c83917f/orjson-3.10.15-cp312-cp312-win_amd64.whl", hash = "sha256:ef5b87e7aa9545ddadd2309efe6824bd3dd64ac101c15dae0f2f597911d46eaa", size = 133732 },
+ { url = "https://files.pythonhosted.org/packages/06/10/fe7d60b8da538e8d3d3721f08c1b7bff0491e8fa4dd3bf11a17e34f4730e/orjson-3.10.15-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:bae0e6ec2b7ba6895198cd981b7cca95d1487d0147c8ed751e5632ad16f031a6", size = 249399 },
+ { url = "https://files.pythonhosted.org/packages/6b/83/52c356fd3a61abd829ae7e4366a6fe8e8863c825a60d7ac5156067516edf/orjson-3.10.15-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f93ce145b2db1252dd86af37d4165b6faa83072b46e3995ecc95d4b2301b725a", size = 125044 },
+ { url = "https://files.pythonhosted.org/packages/55/b2/d06d5901408e7ded1a74c7c20d70e3a127057a6d21355f50c90c0f337913/orjson-3.10.15-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7c203f6f969210128af3acae0ef9ea6aab9782939f45f6fe02d05958fe761ef9", size = 150066 },
+ { url = "https://files.pythonhosted.org/packages/75/8c/60c3106e08dc593a861755781c7c675a566445cc39558677d505878d879f/orjson-3.10.15-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8918719572d662e18b8af66aef699d8c21072e54b6c82a3f8f6404c1f5ccd5e0", size = 139737 },
+ { url = "https://files.pythonhosted.org/packages/6a/8c/ae00d7d0ab8a4490b1efeb01ad4ab2f1982e69cc82490bf8093407718ff5/orjson-3.10.15-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f71eae9651465dff70aa80db92586ad5b92df46a9373ee55252109bb6b703307", size = 154804 },
+ { url = "https://files.pythonhosted.org/packages/22/86/65dc69bd88b6dd254535310e97bc518aa50a39ef9c5a2a5d518e7a223710/orjson-3.10.15-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e117eb299a35f2634e25ed120c37c641398826c2f5a3d3cc39f5993b96171b9e", size = 130583 },
+ { url = "https://files.pythonhosted.org/packages/bb/00/6fe01ededb05d52be42fabb13d93a36e51f1fd9be173bd95707d11a8a860/orjson-3.10.15-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:13242f12d295e83c2955756a574ddd6741c81e5b99f2bef8ed8d53e47a01e4b7", size = 138465 },
+ { url = "https://files.pythonhosted.org/packages/db/2f/4cc151c4b471b0cdc8cb29d3eadbce5007eb0475d26fa26ed123dca93b33/orjson-3.10.15-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7946922ada8f3e0b7b958cc3eb22cfcf6c0df83d1fe5521b4a100103e3fa84c8", size = 130742 },
+ { url = "https://files.pythonhosted.org/packages/9f/13/8a6109e4b477c518498ca37963d9c0eb1508b259725553fb53d53b20e2ea/orjson-3.10.15-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:b7155eb1623347f0f22c38c9abdd738b287e39b9982e1da227503387b81b34ca", size = 414669 },
+ { url = "https://files.pythonhosted.org/packages/22/7b/1d229d6d24644ed4d0a803de1b0e2df832032d5beda7346831c78191b5b2/orjson-3.10.15-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:208beedfa807c922da4e81061dafa9c8489c6328934ca2a562efa707e049e561", size = 141043 },
+ { url = "https://files.pythonhosted.org/packages/cc/d3/6dc91156cf12ed86bed383bcb942d84d23304a1e57b7ab030bf60ea130d6/orjson-3.10.15-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eca81f83b1b8c07449e1d6ff7074e82e3fd6777e588f1a6632127f286a968825", size = 129826 },
+ { url = "https://files.pythonhosted.org/packages/b3/38/c47c25b86f6996f1343be721b6ea4367bc1c8bc0fc3f6bbcd995d18cb19d/orjson-3.10.15-cp313-cp313-win32.whl", hash = "sha256:c03cd6eea1bd3b949d0d007c8d57049aa2b39bd49f58b4b2af571a5d3833d890", size = 142542 },
+ { url = "https://files.pythonhosted.org/packages/27/f1/1d7ec15b20f8ce9300bc850de1e059132b88990e46cd0ccac29cbf11e4f9/orjson-3.10.15-cp313-cp313-win_amd64.whl", hash = "sha256:fd56a26a04f6ba5fb2045b0acc487a63162a958ed837648c5781e1fe3316cfbf", size = 133444 },
+]
+
+[[package]]
+name = "packaging"
+version = "24.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/d0/63/68dbb6eb2de9cb10ee4c9c14a0148804425e13c4fb20d61cce69f53106da/packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f", size = 163950 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451 },
+]
+
+[[package]]
+name = "pathspec"
+version = "0.12.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191 },
+]
+
+[[package]]
+name = "pip"
+version = "25.0.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/70/53/b309b4a497b09655cb7e07088966881a57d082f48ac3cb54ea729fd2c6cf/pip-25.0.1.tar.gz", hash = "sha256:88f96547ea48b940a3a385494e181e29fb8637898f88d88737c5049780f196ea", size = 1950850 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c9/bc/b7db44f5f39f9d0494071bddae6880eb645970366d0a200022a1a93d57f5/pip-25.0.1-py3-none-any.whl", hash = "sha256:c46efd13b6aa8279f33f2864459c8ce587ea6a1a59ee20de055868d8f7688f7f", size = 1841526 },
+]
+
+[[package]]
+name = "platformdirs"
+version = "4.3.6"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/13/fc/128cc9cb8f03208bdbf93d3aa862e16d376844a14f9a0ce5cf4507372de4/platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907", size = 21302 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/3c/a6/bc1012356d8ece4d66dd75c4b9fc6c1f6650ddd5991e421177d9f8f671be/platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb", size = 18439 },
+]
+
+[[package]]
+name = "plotly"
+version = "6.0.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "narwhals" },
+ { name = "packaging" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/9c/80/761c14012d6daf18e12b6d1e4f6b218e999bcceb694d7a9b180154f9e4db/plotly-6.0.0.tar.gz", hash = "sha256:c4aad38b8c3d65e4a5e7dd308b084143b9025c2cc9d5317fc1f1d30958db87d3", size = 8111782 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/0e/77/a946f38b57fb88e736c71fbdd737a1aebd27b532bda0779c137f357cf5fc/plotly-6.0.0-py3-none-any.whl", hash = "sha256:f708871c3a9349a68791ff943a5781b1ec04de7769ea69068adcd9202e57653a", size = 14805949 },
+]
+
+[[package]]
+name = "pluggy"
+version = "1.5.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/96/2d/02d4312c973c6050a18b314a5ad0b3210edb65a906f868e31c111dede4a6/pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", size = 67955 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556 },
+]
+
+[[package]]
+name = "pre-commit"
+version = "4.1.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "cfgv" },
+ { name = "identify" },
+ { name = "nodeenv" },
+ { name = "pyyaml" },
+ { name = "virtualenv" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/2a/13/b62d075317d8686071eb843f0bb1f195eb332f48869d3c31a4c6f1e063ac/pre_commit-4.1.0.tar.gz", hash = "sha256:ae3f018575a588e30dfddfab9a05448bfbd6b73d78709617b5a2b853549716d4", size = 193330 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/43/b3/df14c580d82b9627d173ceea305ba898dca135feb360b6d84019d0803d3b/pre_commit-4.1.0-py2.py3-none-any.whl", hash = "sha256:d29e7cb346295bcc1cc75fc3e92e343495e3ea0196c9ec6ba53f49f10ab6ae7b", size = 220560 },
+]
+
+[[package]]
+name = "propcache"
+version = "0.2.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/20/c8/2a13f78d82211490855b2fb303b6721348d0787fdd9a12ac46d99d3acde1/propcache-0.2.1.tar.gz", hash = "sha256:3f77ce728b19cb537714499928fe800c3dda29e8d9428778fc7c186da4c09a64", size = 41735 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/4c/28/1d205fe49be8b1b4df4c50024e62480a442b1a7b818e734308bb0d17e7fb/propcache-0.2.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:081a430aa8d5e8876c6909b67bd2d937bfd531b0382d3fdedb82612c618bc41a", size = 79588 },
+ { url = "https://files.pythonhosted.org/packages/21/ee/fc4d893f8d81cd4971affef2a6cb542b36617cd1d8ce56b406112cb80bf7/propcache-0.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d2ccec9ac47cf4e04897619c0e0c1a48c54a71bdf045117d3a26f80d38ab1fb0", size = 45825 },
+ { url = "https://files.pythonhosted.org/packages/4a/de/bbe712f94d088da1d237c35d735f675e494a816fd6f54e9db2f61ef4d03f/propcache-0.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:14d86fe14b7e04fa306e0c43cdbeebe6b2c2156a0c9ce56b815faacc193e320d", size = 45357 },
+ { url = "https://files.pythonhosted.org/packages/7f/14/7ae06a6cf2a2f1cb382586d5a99efe66b0b3d0c6f9ac2f759e6f7af9d7cf/propcache-0.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:049324ee97bb67285b49632132db351b41e77833678432be52bdd0289c0e05e4", size = 241869 },
+ { url = "https://files.pythonhosted.org/packages/cc/59/227a78be960b54a41124e639e2c39e8807ac0c751c735a900e21315f8c2b/propcache-0.2.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1cd9a1d071158de1cc1c71a26014dcdfa7dd3d5f4f88c298c7f90ad6f27bb46d", size = 247884 },
+ { url = "https://files.pythonhosted.org/packages/84/58/f62b4ffaedf88dc1b17f04d57d8536601e4e030feb26617228ef930c3279/propcache-0.2.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98110aa363f1bb4c073e8dcfaefd3a5cea0f0834c2aab23dda657e4dab2f53b5", size = 248486 },
+ { url = "https://files.pythonhosted.org/packages/1c/07/ebe102777a830bca91bbb93e3479cd34c2ca5d0361b83be9dbd93104865e/propcache-0.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:647894f5ae99c4cf6bb82a1bb3a796f6e06af3caa3d32e26d2350d0e3e3faf24", size = 243649 },
+ { url = "https://files.pythonhosted.org/packages/ed/bc/4f7aba7f08f520376c4bb6a20b9a981a581b7f2e385fa0ec9f789bb2d362/propcache-0.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bfd3223c15bebe26518d58ccf9a39b93948d3dcb3e57a20480dfdd315356baff", size = 229103 },
+ { url = "https://files.pythonhosted.org/packages/fe/d5/04ac9cd4e51a57a96f78795e03c5a0ddb8f23ec098b86f92de028d7f2a6b/propcache-0.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d71264a80f3fcf512eb4f18f59423fe82d6e346ee97b90625f283df56aee103f", size = 226607 },
+ { url = "https://files.pythonhosted.org/packages/e3/f0/24060d959ea41d7a7cc7fdbf68b31852331aabda914a0c63bdb0e22e96d6/propcache-0.2.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:e73091191e4280403bde6c9a52a6999d69cdfde498f1fdf629105247599b57ec", size = 221153 },
+ { url = "https://files.pythonhosted.org/packages/77/a7/3ac76045a077b3e4de4859a0753010765e45749bdf53bd02bc4d372da1a0/propcache-0.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3935bfa5fede35fb202c4b569bb9c042f337ca4ff7bd540a0aa5e37131659348", size = 222151 },
+ { url = "https://files.pythonhosted.org/packages/e7/af/5e29da6f80cebab3f5a4dcd2a3240e7f56f2c4abf51cbfcc99be34e17f0b/propcache-0.2.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:f508b0491767bb1f2b87fdfacaba5f7eddc2f867740ec69ece6d1946d29029a6", size = 233812 },
+ { url = "https://files.pythonhosted.org/packages/8c/89/ebe3ad52642cc5509eaa453e9f4b94b374d81bae3265c59d5c2d98efa1b4/propcache-0.2.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:1672137af7c46662a1c2be1e8dc78cb6d224319aaa40271c9257d886be4363a6", size = 238829 },
+ { url = "https://files.pythonhosted.org/packages/e9/2f/6b32f273fa02e978b7577159eae7471b3cfb88b48563b1c2578b2d7ca0bb/propcache-0.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b74c261802d3d2b85c9df2dfb2fa81b6f90deeef63c2db9f0e029a3cac50b518", size = 230704 },
+ { url = "https://files.pythonhosted.org/packages/5c/2e/f40ae6ff5624a5f77edd7b8359b208b5455ea113f68309e2b00a2e1426b6/propcache-0.2.1-cp312-cp312-win32.whl", hash = "sha256:d09c333d36c1409d56a9d29b3a1b800a42c76a57a5a8907eacdbce3f18768246", size = 40050 },
+ { url = "https://files.pythonhosted.org/packages/3b/77/a92c3ef994e47180862b9d7d11e37624fb1c00a16d61faf55115d970628b/propcache-0.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:c214999039d4f2a5b2073ac506bba279945233da8c786e490d411dfc30f855c1", size = 44117 },
+ { url = "https://files.pythonhosted.org/packages/0f/2a/329e0547cf2def8857157f9477669043e75524cc3e6251cef332b3ff256f/propcache-0.2.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aca405706e0b0a44cc6bfd41fbe89919a6a56999157f6de7e182a990c36e37bc", size = 77002 },
+ { url = "https://files.pythonhosted.org/packages/12/2d/c4df5415e2382f840dc2ecbca0eeb2293024bc28e57a80392f2012b4708c/propcache-0.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:12d1083f001ace206fe34b6bdc2cb94be66d57a850866f0b908972f90996b3e9", size = 44639 },
+ { url = "https://files.pythonhosted.org/packages/d0/5a/21aaa4ea2f326edaa4e240959ac8b8386ea31dedfdaa636a3544d9e7a408/propcache-0.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d93f3307ad32a27bda2e88ec81134b823c240aa3abb55821a8da553eed8d9439", size = 44049 },
+ { url = "https://files.pythonhosted.org/packages/4e/3e/021b6cd86c0acc90d74784ccbb66808b0bd36067a1bf3e2deb0f3845f618/propcache-0.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba278acf14471d36316159c94a802933d10b6a1e117b8554fe0d0d9b75c9d536", size = 224819 },
+ { url = "https://files.pythonhosted.org/packages/3c/57/c2fdeed1b3b8918b1770a133ba5c43ad3d78e18285b0c06364861ef5cc38/propcache-0.2.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4e6281aedfca15301c41f74d7005e6e3f4ca143584ba696ac69df4f02f40d629", size = 229625 },
+ { url = "https://files.pythonhosted.org/packages/9d/81/70d4ff57bf2877b5780b466471bebf5892f851a7e2ca0ae7ffd728220281/propcache-0.2.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5b750a8e5a1262434fb1517ddf64b5de58327f1adc3524a5e44c2ca43305eb0b", size = 232934 },
+ { url = "https://files.pythonhosted.org/packages/3c/b9/bb51ea95d73b3fb4100cb95adbd4e1acaf2cbb1fd1083f5468eeb4a099a8/propcache-0.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf72af5e0fb40e9babf594308911436c8efde3cb5e75b6f206c34ad18be5c052", size = 227361 },
+ { url = "https://files.pythonhosted.org/packages/f1/20/3c6d696cd6fd70b29445960cc803b1851a1131e7a2e4ee261ee48e002bcd/propcache-0.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b2d0a12018b04f4cb820781ec0dffb5f7c7c1d2a5cd22bff7fb055a2cb19ebce", size = 213904 },
+ { url = "https://files.pythonhosted.org/packages/a1/cb/1593bfc5ac6d40c010fa823f128056d6bc25b667f5393781e37d62f12005/propcache-0.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e800776a79a5aabdb17dcc2346a7d66d0777e942e4cd251defeb084762ecd17d", size = 212632 },
+ { url = "https://files.pythonhosted.org/packages/6d/5c/e95617e222be14a34c709442a0ec179f3207f8a2b900273720501a70ec5e/propcache-0.2.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:4160d9283bd382fa6c0c2b5e017acc95bc183570cd70968b9202ad6d8fc48dce", size = 207897 },
+ { url = "https://files.pythonhosted.org/packages/8e/3b/56c5ab3dc00f6375fbcdeefdede5adf9bee94f1fab04adc8db118f0f9e25/propcache-0.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:30b43e74f1359353341a7adb783c8f1b1c676367b011709f466f42fda2045e95", size = 208118 },
+ { url = "https://files.pythonhosted.org/packages/86/25/d7ef738323fbc6ebcbce33eb2a19c5e07a89a3df2fded206065bd5e868a9/propcache-0.2.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:58791550b27d5488b1bb52bc96328456095d96206a250d28d874fafe11b3dfaf", size = 217851 },
+ { url = "https://files.pythonhosted.org/packages/b3/77/763e6cef1852cf1ba740590364ec50309b89d1c818e3256d3929eb92fabf/propcache-0.2.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:0f022d381747f0dfe27e99d928e31bc51a18b65bb9e481ae0af1380a6725dd1f", size = 222630 },
+ { url = "https://files.pythonhosted.org/packages/4f/e9/0f86be33602089c701696fbed8d8c4c07b6ee9605c5b7536fd27ed540c5b/propcache-0.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:297878dc9d0a334358f9b608b56d02e72899f3b8499fc6044133f0d319e2ec30", size = 216269 },
+ { url = "https://files.pythonhosted.org/packages/cc/02/5ac83217d522394b6a2e81a2e888167e7ca629ef6569a3f09852d6dcb01a/propcache-0.2.1-cp313-cp313-win32.whl", hash = "sha256:ddfab44e4489bd79bda09d84c430677fc7f0a4939a73d2bba3073036f487a0a6", size = 39472 },
+ { url = "https://files.pythonhosted.org/packages/f4/33/d6f5420252a36034bc8a3a01171bc55b4bff5df50d1c63d9caa50693662f/propcache-0.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:556fc6c10989f19a179e4321e5d678db8eb2924131e64652a51fe83e4c3db0e1", size = 43363 },
+ { url = "https://files.pythonhosted.org/packages/41/b6/c5319caea262f4821995dca2107483b94a3345d4607ad797c76cb9c36bcc/propcache-0.2.1-py3-none-any.whl", hash = "sha256:52277518d6aae65536e9cea52d4e7fd2f7a66f4aa2d30ed3f2fcea620ace3c54", size = 11818 },
+]
+
+[[package]]
+name = "protobuf"
+version = "5.29.3"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/f7/d1/e0a911544ca9993e0f17ce6d3cc0932752356c1b0a834397f28e63479344/protobuf-5.29.3.tar.gz", hash = "sha256:5da0f41edaf117bde316404bad1a486cb4ededf8e4a54891296f648e8e076620", size = 424945 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/dc/7a/1e38f3cafa022f477ca0f57a1f49962f21ad25850c3ca0acd3b9d0091518/protobuf-5.29.3-cp310-abi3-win32.whl", hash = "sha256:3ea51771449e1035f26069c4c7fd51fba990d07bc55ba80701c78f886bf9c888", size = 422708 },
+ { url = "https://files.pythonhosted.org/packages/61/fa/aae8e10512b83de633f2646506a6d835b151edf4b30d18d73afd01447253/protobuf-5.29.3-cp310-abi3-win_amd64.whl", hash = "sha256:a4fa6f80816a9a0678429e84973f2f98cbc218cca434abe8db2ad0bffc98503a", size = 434508 },
+ { url = "https://files.pythonhosted.org/packages/dd/04/3eaedc2ba17a088961d0e3bd396eac764450f431621b58a04ce898acd126/protobuf-5.29.3-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:a8434404bbf139aa9e1300dbf989667a83d42ddda9153d8ab76e0d5dcaca484e", size = 417825 },
+ { url = "https://files.pythonhosted.org/packages/4f/06/7c467744d23c3979ce250397e26d8ad8eeb2bea7b18ca12ad58313c1b8d5/protobuf-5.29.3-cp38-abi3-manylinux2014_aarch64.whl", hash = "sha256:daaf63f70f25e8689c072cfad4334ca0ac1d1e05a92fc15c54eb9cf23c3efd84", size = 319573 },
+ { url = "https://files.pythonhosted.org/packages/a8/45/2ebbde52ad2be18d3675b6bee50e68cd73c9e0654de77d595540b5129df8/protobuf-5.29.3-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:c027e08a08be10b67c06bf2370b99c811c466398c357e615ca88c91c07f0910f", size = 319672 },
+ { url = "https://files.pythonhosted.org/packages/fd/b2/ab07b09e0f6d143dfb839693aa05765257bceaa13d03bf1a696b78323e7a/protobuf-5.29.3-py3-none-any.whl", hash = "sha256:0a18ed4a24198528f2333802eb075e59dea9d679ab7a6c5efb017a59004d849f", size = 172550 },
+]
+
+[[package]]
+name = "psutil"
+version = "6.1.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/1f/5a/07871137bb752428aa4b659f910b399ba6f291156bdea939be3e96cae7cb/psutil-6.1.1.tar.gz", hash = "sha256:cf8496728c18f2d0b45198f06895be52f36611711746b7f30c464b422b50e2f5", size = 508502 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/61/99/ca79d302be46f7bdd8321089762dd4476ee725fce16fc2b2e1dbba8cac17/psutil-6.1.1-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:fc0ed7fe2231a444fc219b9c42d0376e0a9a1a72f16c5cfa0f68d19f1a0663e8", size = 247511 },
+ { url = "https://files.pythonhosted.org/packages/0b/6b/73dbde0dd38f3782905d4587049b9be64d76671042fdcaf60e2430c6796d/psutil-6.1.1-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:0bdd4eab935276290ad3cb718e9809412895ca6b5b334f5a9111ee6d9aff9377", size = 248985 },
+ { url = "https://files.pythonhosted.org/packages/17/38/c319d31a1d3f88c5b79c68b3116c129e5133f1822157dd6da34043e32ed6/psutil-6.1.1-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b6e06c20c05fe95a3d7302d74e7097756d4ba1247975ad6905441ae1b5b66003", size = 284488 },
+ { url = "https://files.pythonhosted.org/packages/9c/39/0f88a830a1c8a3aba27fededc642da37613c57cbff143412e3536f89784f/psutil-6.1.1-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:97f7cb9921fbec4904f522d972f0c0e1f4fabbdd4e0287813b21215074a0f160", size = 287477 },
+ { url = "https://files.pythonhosted.org/packages/47/da/99f4345d4ddf2845cb5b5bd0d93d554e84542d116934fde07a0c50bd4e9f/psutil-6.1.1-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:33431e84fee02bc84ea36d9e2c4a6d395d479c9dd9bba2376c1f6ee8f3a4e0b3", size = 289017 },
+ { url = "https://files.pythonhosted.org/packages/38/53/bd755c2896f4461fd4f36fa6a6dcb66a88a9e4b9fd4e5b66a77cf9d4a584/psutil-6.1.1-cp37-abi3-win32.whl", hash = "sha256:eaa912e0b11848c4d9279a93d7e2783df352b082f40111e078388701fd479e53", size = 250602 },
+ { url = "https://files.pythonhosted.org/packages/7b/d7/7831438e6c3ebbfa6e01a927127a6cb42ad3ab844247f3c5b96bea25d73d/psutil-6.1.1-cp37-abi3-win_amd64.whl", hash = "sha256:f35cfccb065fff93529d2afb4a2e89e363fe63ca1e4a5da22b603a85833c2649", size = 254444 },
+]
+
+[[package]]
+name = "psycopg2-binary"
+version = "2.9.10"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/cb/0e/bdc8274dc0585090b4e3432267d7be4dfbfd8971c0fa59167c711105a6bf/psycopg2-binary-2.9.10.tar.gz", hash = "sha256:4b3df0e6990aa98acda57d983942eff13d824135fe2250e6522edaa782a06de2", size = 385764 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/49/7d/465cc9795cf76f6d329efdafca74693714556ea3891813701ac1fee87545/psycopg2_binary-2.9.10-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:880845dfe1f85d9d5f7c412efea7a08946a46894537e4e5d091732eb1d34d9a0", size = 3044771 },
+ { url = "https://files.pythonhosted.org/packages/8b/31/6d225b7b641a1a2148e3ed65e1aa74fc86ba3fee850545e27be9e1de893d/psycopg2_binary-2.9.10-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:9440fa522a79356aaa482aa4ba500b65f28e5d0e63b801abf6aa152a29bd842a", size = 3275336 },
+ { url = "https://files.pythonhosted.org/packages/30/b7/a68c2b4bff1cbb1728e3ec864b2d92327c77ad52edcd27922535a8366f68/psycopg2_binary-2.9.10-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e3923c1d9870c49a2d44f795df0c889a22380d36ef92440ff618ec315757e539", size = 2851637 },
+ { url = "https://files.pythonhosted.org/packages/0b/b1/cfedc0e0e6f9ad61f8657fd173b2f831ce261c02a08c0b09c652b127d813/psycopg2_binary-2.9.10-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7b2c956c028ea5de47ff3a8d6b3cc3330ab45cf0b7c3da35a2d6ff8420896526", size = 3082097 },
+ { url = "https://files.pythonhosted.org/packages/18/ed/0a8e4153c9b769f59c02fb5e7914f20f0b2483a19dae7bf2db54b743d0d0/psycopg2_binary-2.9.10-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f758ed67cab30b9a8d2833609513ce4d3bd027641673d4ebc9c067e4d208eec1", size = 3264776 },
+ { url = "https://files.pythonhosted.org/packages/10/db/d09da68c6a0cdab41566b74e0a6068a425f077169bed0946559b7348ebe9/psycopg2_binary-2.9.10-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8cd9b4f2cfab88ed4a9106192de509464b75a906462fb846b936eabe45c2063e", size = 3020968 },
+ { url = "https://files.pythonhosted.org/packages/94/28/4d6f8c255f0dfffb410db2b3f9ac5218d959a66c715c34cac31081e19b95/psycopg2_binary-2.9.10-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6dc08420625b5a20b53551c50deae6e231e6371194fa0651dbe0fb206452ae1f", size = 2872334 },
+ { url = "https://files.pythonhosted.org/packages/05/f7/20d7bf796593c4fea95e12119d6cc384ff1f6141a24fbb7df5a668d29d29/psycopg2_binary-2.9.10-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:d7cd730dfa7c36dbe8724426bf5612798734bff2d3c3857f36f2733f5bfc7c00", size = 2822722 },
+ { url = "https://files.pythonhosted.org/packages/4d/e4/0c407ae919ef626dbdb32835a03b6737013c3cc7240169843965cada2bdf/psycopg2_binary-2.9.10-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:155e69561d54d02b3c3209545fb08938e27889ff5a10c19de8d23eb5a41be8a5", size = 2920132 },
+ { url = "https://files.pythonhosted.org/packages/2d/70/aa69c9f69cf09a01da224909ff6ce8b68faeef476f00f7ec377e8f03be70/psycopg2_binary-2.9.10-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c3cc28a6fd5a4a26224007712e79b81dbaee2ffb90ff406256158ec4d7b52b47", size = 2959312 },
+ { url = "https://files.pythonhosted.org/packages/d3/bd/213e59854fafe87ba47814bf413ace0dcee33a89c8c8c814faca6bc7cf3c/psycopg2_binary-2.9.10-cp312-cp312-win32.whl", hash = "sha256:ec8a77f521a17506a24a5f626cb2aee7850f9b69a0afe704586f63a464f3cd64", size = 1025191 },
+ { url = "https://files.pythonhosted.org/packages/92/29/06261ea000e2dc1e22907dbbc483a1093665509ea586b29b8986a0e56733/psycopg2_binary-2.9.10-cp312-cp312-win_amd64.whl", hash = "sha256:18c5ee682b9c6dd3696dad6e54cc7ff3a1a9020df6a5c0f861ef8bfd338c3ca0", size = 1164031 },
+ { url = "https://files.pythonhosted.org/packages/3e/30/d41d3ba765609c0763505d565c4d12d8f3c79793f0d0f044ff5a28bf395b/psycopg2_binary-2.9.10-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:26540d4a9a4e2b096f1ff9cce51253d0504dca5a85872c7f7be23be5a53eb18d", size = 3044699 },
+ { url = "https://files.pythonhosted.org/packages/35/44/257ddadec7ef04536ba71af6bc6a75ec05c5343004a7ec93006bee66c0bc/psycopg2_binary-2.9.10-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:e217ce4d37667df0bc1c397fdcd8de5e81018ef305aed9415c3b093faaeb10fb", size = 3275245 },
+ { url = "https://files.pythonhosted.org/packages/1b/11/48ea1cd11de67f9efd7262085588790a95d9dfcd9b8a687d46caf7305c1a/psycopg2_binary-2.9.10-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:245159e7ab20a71d989da00f280ca57da7641fa2cdcf71749c193cea540a74f7", size = 2851631 },
+ { url = "https://files.pythonhosted.org/packages/62/e0/62ce5ee650e6c86719d621a761fe4bc846ab9eff8c1f12b1ed5741bf1c9b/psycopg2_binary-2.9.10-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c4ded1a24b20021ebe677b7b08ad10bf09aac197d6943bfe6fec70ac4e4690d", size = 3082140 },
+ { url = "https://files.pythonhosted.org/packages/27/ce/63f946c098611f7be234c0dd7cb1ad68b0b5744d34f68062bb3c5aa510c8/psycopg2_binary-2.9.10-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3abb691ff9e57d4a93355f60d4f4c1dd2d68326c968e7db17ea96df3c023ef73", size = 3264762 },
+ { url = "https://files.pythonhosted.org/packages/43/25/c603cd81402e69edf7daa59b1602bd41eb9859e2824b8c0855d748366ac9/psycopg2_binary-2.9.10-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8608c078134f0b3cbd9f89b34bd60a943b23fd33cc5f065e8d5f840061bd0673", size = 3020967 },
+ { url = "https://files.pythonhosted.org/packages/5f/d6/8708d8c6fca531057fa170cdde8df870e8b6a9b136e82b361c65e42b841e/psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:230eeae2d71594103cd5b93fd29d1ace6420d0b86f4778739cb1a5a32f607d1f", size = 2872326 },
+ { url = "https://files.pythonhosted.org/packages/ce/ac/5b1ea50fc08a9df82de7e1771537557f07c2632231bbab652c7e22597908/psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:bb89f0a835bcfc1d42ccd5f41f04870c1b936d8507c6df12b7737febc40f0909", size = 2822712 },
+ { url = "https://files.pythonhosted.org/packages/c4/fc/504d4503b2abc4570fac3ca56eb8fed5e437bf9c9ef13f36b6621db8ef00/psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f0c2d907a1e102526dd2986df638343388b94c33860ff3bbe1384130828714b1", size = 2920155 },
+ { url = "https://files.pythonhosted.org/packages/b2/d1/323581e9273ad2c0dbd1902f3fb50c441da86e894b6e25a73c3fda32c57e/psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f8157bed2f51db683f31306aa497311b560f2265998122abe1dce6428bd86567", size = 2959356 },
+ { url = "https://files.pythonhosted.org/packages/08/50/d13ea0a054189ae1bc21af1d85b6f8bb9bbc5572991055d70ad9006fe2d6/psycopg2_binary-2.9.10-cp313-cp313-win_amd64.whl", hash = "sha256:27422aa5f11fbcd9b18da48373eb67081243662f9b46e6fd07c3eb46e4535142", size = 2569224 },
+]
+
+[[package]]
+name = "pycparser"
+version = "2.22"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/1d/b2/31537cf4b1ca988837256c910a668b553fceb8f069bedc4b1c826024b52c/pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6", size = 172736 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/13/a3/a812df4e2dd5696d1f351d58b8fe16a405b234ad2886a0dab9183fb78109/pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc", size = 117552 },
+]
+
+[[package]]
+name = "pydantic"
+version = "2.10.6"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "annotated-types" },
+ { name = "pydantic-core" },
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/b7/ae/d5220c5c52b158b1de7ca89fc5edb72f304a70a4c540c84c8844bf4008de/pydantic-2.10.6.tar.gz", hash = "sha256:ca5daa827cce33de7a42be142548b0096bf05a7e7b365aebfa5f8eeec7128236", size = 761681 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/f4/3c/8cc1cc84deffa6e25d2d0c688ebb80635dfdbf1dbea3e30c541c8cf4d860/pydantic-2.10.6-py3-none-any.whl", hash = "sha256:427d664bf0b8a2b34ff5dd0f5a18df00591adcee7198fbd71981054cef37b584", size = 431696 },
+]
+
+[[package]]
+name = "pydantic-core"
+version = "2.27.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/fc/01/f3e5ac5e7c25833db5eb555f7b7ab24cd6f8c322d3a3ad2d67a952dc0abc/pydantic_core-2.27.2.tar.gz", hash = "sha256:eb026e5a4c1fee05726072337ff51d1efb6f59090b7da90d30ea58625b1ffb39", size = 413443 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/d6/74/51c8a5482ca447871c93e142d9d4a92ead74de6c8dc5e66733e22c9bba89/pydantic_core-2.27.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9e0c8cfefa0ef83b4da9588448b6d8d2a2bf1a53c3f1ae5fca39eb3061e2f0b0", size = 1893127 },
+ { url = "https://files.pythonhosted.org/packages/d3/f3/c97e80721735868313c58b89d2de85fa80fe8dfeeed84dc51598b92a135e/pydantic_core-2.27.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:83097677b8e3bd7eaa6775720ec8e0405f1575015a463285a92bfdfe254529ef", size = 1811340 },
+ { url = "https://files.pythonhosted.org/packages/9e/91/840ec1375e686dbae1bd80a9e46c26a1e0083e1186abc610efa3d9a36180/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:172fce187655fece0c90d90a678424b013f8fbb0ca8b036ac266749c09438cb7", size = 1822900 },
+ { url = "https://files.pythonhosted.org/packages/f6/31/4240bc96025035500c18adc149aa6ffdf1a0062a4b525c932065ceb4d868/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:519f29f5213271eeeeb3093f662ba2fd512b91c5f188f3bb7b27bc5973816934", size = 1869177 },
+ { url = "https://files.pythonhosted.org/packages/fa/20/02fbaadb7808be578317015c462655c317a77a7c8f0ef274bc016a784c54/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:05e3a55d124407fffba0dd6b0c0cd056d10e983ceb4e5dbd10dda135c31071d6", size = 2038046 },
+ { url = "https://files.pythonhosted.org/packages/06/86/7f306b904e6c9eccf0668248b3f272090e49c275bc488a7b88b0823444a4/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c3ed807c7b91de05e63930188f19e921d1fe90de6b4f5cd43ee7fcc3525cb8c", size = 2685386 },
+ { url = "https://files.pythonhosted.org/packages/8d/f0/49129b27c43396581a635d8710dae54a791b17dfc50c70164866bbf865e3/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fb4aadc0b9a0c063206846d603b92030eb6f03069151a625667f982887153e2", size = 1997060 },
+ { url = "https://files.pythonhosted.org/packages/0d/0f/943b4af7cd416c477fd40b187036c4f89b416a33d3cc0ab7b82708a667aa/pydantic_core-2.27.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:28ccb213807e037460326424ceb8b5245acb88f32f3d2777427476e1b32c48c4", size = 2004870 },
+ { url = "https://files.pythonhosted.org/packages/35/40/aea70b5b1a63911c53a4c8117c0a828d6790483f858041f47bab0b779f44/pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:de3cd1899e2c279b140adde9357c4495ed9d47131b4a4eaff9052f23398076b3", size = 1999822 },
+ { url = "https://files.pythonhosted.org/packages/f2/b3/807b94fd337d58effc5498fd1a7a4d9d59af4133e83e32ae39a96fddec9d/pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:220f892729375e2d736b97d0e51466252ad84c51857d4d15f5e9692f9ef12be4", size = 2130364 },
+ { url = "https://files.pythonhosted.org/packages/fc/df/791c827cd4ee6efd59248dca9369fb35e80a9484462c33c6649a8d02b565/pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a0fcd29cd6b4e74fe8ddd2c90330fd8edf2e30cb52acda47f06dd615ae72da57", size = 2158303 },
+ { url = "https://files.pythonhosted.org/packages/9b/67/4e197c300976af185b7cef4c02203e175fb127e414125916bf1128b639a9/pydantic_core-2.27.2-cp312-cp312-win32.whl", hash = "sha256:1e2cb691ed9834cd6a8be61228471d0a503731abfb42f82458ff27be7b2186fc", size = 1834064 },
+ { url = "https://files.pythonhosted.org/packages/1f/ea/cd7209a889163b8dcca139fe32b9687dd05249161a3edda62860430457a5/pydantic_core-2.27.2-cp312-cp312-win_amd64.whl", hash = "sha256:cc3f1a99a4f4f9dd1de4fe0312c114e740b5ddead65bb4102884b384c15d8bc9", size = 1989046 },
+ { url = "https://files.pythonhosted.org/packages/bc/49/c54baab2f4658c26ac633d798dab66b4c3a9bbf47cff5284e9c182f4137a/pydantic_core-2.27.2-cp312-cp312-win_arm64.whl", hash = "sha256:3911ac9284cd8a1792d3cb26a2da18f3ca26c6908cc434a18f730dc0db7bfa3b", size = 1885092 },
+ { url = "https://files.pythonhosted.org/packages/41/b1/9bc383f48f8002f99104e3acff6cba1231b29ef76cfa45d1506a5cad1f84/pydantic_core-2.27.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7d14bd329640e63852364c306f4d23eb744e0f8193148d4044dd3dacdaacbd8b", size = 1892709 },
+ { url = "https://files.pythonhosted.org/packages/10/6c/e62b8657b834f3eb2961b49ec8e301eb99946245e70bf42c8817350cbefc/pydantic_core-2.27.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:82f91663004eb8ed30ff478d77c4d1179b3563df6cdb15c0817cd1cdaf34d154", size = 1811273 },
+ { url = "https://files.pythonhosted.org/packages/ba/15/52cfe49c8c986e081b863b102d6b859d9defc63446b642ccbbb3742bf371/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71b24c7d61131bb83df10cc7e687433609963a944ccf45190cfc21e0887b08c9", size = 1823027 },
+ { url = "https://files.pythonhosted.org/packages/b1/1c/b6f402cfc18ec0024120602bdbcebc7bdd5b856528c013bd4d13865ca473/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fa8e459d4954f608fa26116118bb67f56b93b209c39b008277ace29937453dc9", size = 1868888 },
+ { url = "https://files.pythonhosted.org/packages/bd/7b/8cb75b66ac37bc2975a3b7de99f3c6f355fcc4d89820b61dffa8f1e81677/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce8918cbebc8da707ba805b7fd0b382816858728ae7fe19a942080c24e5b7cd1", size = 2037738 },
+ { url = "https://files.pythonhosted.org/packages/c8/f1/786d8fe78970a06f61df22cba58e365ce304bf9b9f46cc71c8c424e0c334/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eda3f5c2a021bbc5d976107bb302e0131351c2ba54343f8a496dc8783d3d3a6a", size = 2685138 },
+ { url = "https://files.pythonhosted.org/packages/a6/74/d12b2cd841d8724dc8ffb13fc5cef86566a53ed358103150209ecd5d1999/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd8086fa684c4775c27f03f062cbb9eaa6e17f064307e86b21b9e0abc9c0f02e", size = 1997025 },
+ { url = "https://files.pythonhosted.org/packages/a0/6e/940bcd631bc4d9a06c9539b51f070b66e8f370ed0933f392db6ff350d873/pydantic_core-2.27.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8d9b3388db186ba0c099a6d20f0604a44eabdeef1777ddd94786cdae158729e4", size = 2004633 },
+ { url = "https://files.pythonhosted.org/packages/50/cc/a46b34f1708d82498c227d5d80ce615b2dd502ddcfd8376fc14a36655af1/pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7a66efda2387de898c8f38c0cf7f14fca0b51a8ef0b24bfea5849f1b3c95af27", size = 1999404 },
+ { url = "https://files.pythonhosted.org/packages/ca/2d/c365cfa930ed23bc58c41463bae347d1005537dc8db79e998af8ba28d35e/pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:18a101c168e4e092ab40dbc2503bdc0f62010e95d292b27827871dc85450d7ee", size = 2130130 },
+ { url = "https://files.pythonhosted.org/packages/f4/d7/eb64d015c350b7cdb371145b54d96c919d4db516817f31cd1c650cae3b21/pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ba5dd002f88b78a4215ed2f8ddbdf85e8513382820ba15ad5ad8955ce0ca19a1", size = 2157946 },
+ { url = "https://files.pythonhosted.org/packages/a4/99/bddde3ddde76c03b65dfd5a66ab436c4e58ffc42927d4ff1198ffbf96f5f/pydantic_core-2.27.2-cp313-cp313-win32.whl", hash = "sha256:1ebaf1d0481914d004a573394f4be3a7616334be70261007e47c2a6fe7e50130", size = 1834387 },
+ { url = "https://files.pythonhosted.org/packages/71/47/82b5e846e01b26ac6f1893d3c5f9f3a2eb6ba79be26eef0b759b4fe72946/pydantic_core-2.27.2-cp313-cp313-win_amd64.whl", hash = "sha256:953101387ecf2f5652883208769a79e48db18c6df442568a0b5ccd8c2723abee", size = 1990453 },
+ { url = "https://files.pythonhosted.org/packages/51/b2/b2b50d5ecf21acf870190ae5d093602d95f66c9c31f9d5de6062eb329ad1/pydantic_core-2.27.2-cp313-cp313-win_arm64.whl", hash = "sha256:ac4dbfd1691affb8f48c2c13241a2e3b60ff23247cbcf981759c768b6633cf8b", size = 1885186 },
+]
+
+[[package]]
+name = "pydantic-settings"
+version = "2.7.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "pydantic" },
+ { name = "python-dotenv" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/73/7b/c58a586cd7d9ac66d2ee4ba60ca2d241fa837c02bca9bea80a9a8c3d22a9/pydantic_settings-2.7.1.tar.gz", hash = "sha256:10c9caad35e64bfb3c2fbf70a078c0e25cc92499782e5200747f942a065dec93", size = 79920 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/b4/46/93416fdae86d40879714f72956ac14df9c7b76f7d41a4d68aa9f71a0028b/pydantic_settings-2.7.1-py3-none-any.whl", hash = "sha256:590be9e6e24d06db33a4262829edef682500ef008565a969c73d39d5f8bfb3fd", size = 29718 },
+]
+
+[[package]]
+name = "pygit2"
+version = "1.17.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "cffi" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/b7/ea/17aa8ca38750f1ba69511ceeb41d29961f90eb2e0a242b668c70311efd4e/pygit2-1.17.0.tar.gz", hash = "sha256:fa2bc050b2c2d3e73b54d6d541c792178561a344f07e409f532d5bb97ac7b894", size = 769002 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/ee/53/8286256d077a0a38837c4ceee73a3c2b2d6caed3ec86e8bf7b32580e5ed0/pygit2-1.17.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:f7224d89a7dda7290e458393941e500c8682f375f41e6d80ee423958a5d4013d", size = 5465330 },
+ { url = "https://files.pythonhosted.org/packages/dd/a0/060ebb435d2590c1188ad6bc7ea0d5f0561e09a13db02baec8252b507390/pygit2-1.17.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9ae1967b0c8a2438b3b0e4a63307b5c22c80024a2f09b28d14dfde0001fed8dc", size = 5683366 },
+ { url = "https://files.pythonhosted.org/packages/21/92/fedc77806ff06b502a82ddbb857a5749429ce7bf638e3007b82bd10b4244/pygit2-1.17.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:507343fa142a82028c8448c2626317dc19885985aba8ea27d381777ac484eefb", size = 5645689 },
+ { url = "https://files.pythonhosted.org/packages/14/a9/3405b991f3264163e3d93c16b43929e0e765e559ca83f8697008c7f65587/pygit2-1.17.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bc04917a680591c6e801df912d7fb722c253b5ac68178ff37b5666dafd06999", size = 5457766 },
+ { url = "https://files.pythonhosted.org/packages/71/bb/40c37e00994727efb1a68bfd1f0b505207ec066ef8004b7e258210f230cc/pygit2-1.17.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7bb1b623cbd16962c3a1ec7f8e1012fa224c9e9642758c65e8e656ecc7ff1574", size = 5400609 },
+ { url = "https://files.pythonhosted.org/packages/db/55/7781d8997632ebfe2682a8f80668710eb4bc8c99a80e0691243b020f7391/pygit2-1.17.0-cp312-cp312-win32.whl", hash = "sha256:3029331ddf56a6908547278ab4c354b2d6932eb6a53be81e0093adc98a0ae540", size = 1219823 },
+ { url = "https://files.pythonhosted.org/packages/7c/73/166aae3a12a0c5252619df37a033c8a3c9756a6af4e49640769492d14893/pygit2-1.17.0-cp312-cp312-win_amd64.whl", hash = "sha256:1011236bab7317b82e6cbc3dff4be8467923b1dcf2ffe28bf2e64805dcb37749", size = 1305143 },
+ { url = "https://files.pythonhosted.org/packages/3d/09/d79f99cc25b895a891eab10697fecde3c2552fdfd467b9b72b388f9a1ad9/pygit2-1.17.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ce938e7a4fdfc816ffceb62babad65fb62e1a5ad261e880b9a072e8da144ccca", size = 5465211 },
+ { url = "https://files.pythonhosted.org/packages/a6/85/74e786da47ee2face731fb892fe87c04ae257d3b5136966f8f839727d130/pygit2-1.17.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:61ff2c8b0fc96fdf45a7a5239cc262b0293a5171f68d67eea239a42c3b2226cb", size = 5687159 },
+ { url = "https://files.pythonhosted.org/packages/58/61/b502b240ba91a3dec58e4936eb85c4c17d682dfb4872c197c2212fc13bc1/pygit2-1.17.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8101aa723c292892ba46303b19487a9fb0de50d9e30f4c1c2a76e3383b6e4b6d", size = 5649303 },
+ { url = "https://files.pythonhosted.org/packages/5a/33/e359c7c938df5b1cef2acb4dcf72cb153677f2185db8bfd0bb06a7ab96f9/pygit2-1.17.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36e3e9225e3f01bb6a2d4589c126900bbc571cd0876ca9c01372a6e3d3693c0e", size = 5461433 },
+ { url = "https://files.pythonhosted.org/packages/98/8e/6885fd4ce98aedb84fe4459a3c85f3b866577aec9343becfca4a0e50a1eb/pygit2-1.17.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:614cfddbf048900da19b016787f153d44ea9fd7ef80f9e03a77024aa1555d5f4", size = 5402395 },
+ { url = "https://files.pythonhosted.org/packages/9f/62/51b84a6c80742e73ecd562f45234c6ef23e833864583bc759d8c6770f493/pygit2-1.17.0-cp313-cp313-win32.whl", hash = "sha256:1391762153af9715ed1d0586e3f207c518f03f5874e1f5b8e398697d006a0a82", size = 1219803 },
+ { url = "https://files.pythonhosted.org/packages/7d/69/8dfe160c7166cec689d985e6efb52198c2c2fd5b722196e4beb920f9f460/pygit2-1.17.0-cp313-cp313-win_amd64.whl", hash = "sha256:d677d6fb85c426c5f5f8409bdc5a2e391016c99f73b97779b284c4ad25aa75fa", size = 1305156 },
+]
+
+[[package]]
+name = "pygithub"
+version = "2.5.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "deprecated" },
+ { name = "pyjwt", extra = ["crypto"] },
+ { name = "pynacl" },
+ { name = "requests" },
+ { name = "typing-extensions" },
+ { name = "urllib3" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/16/ce/aa91d30040d9552c274e7ea8bd10a977600d508d579a4bb262b95eccf961/pygithub-2.5.0.tar.gz", hash = "sha256:e1613ac508a9be710920d26eb18b1905ebd9926aa49398e88151c1b526aad3cf", size = 3552804 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/37/05/bfbdbbc5d8aafd8dae9b3b6877edca561fccd8528ef5edc4e7b6d23721b5/PyGithub-2.5.0-py3-none-any.whl", hash = "sha256:b0b635999a658ab8e08720bdd3318893ff20e2275f6446fcf35bf3f44f2c0fd2", size = 375935 },
+]
+
+[[package]]
+name = "pygments"
+version = "2.19.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/7c/2d/c3338d48ea6cc0feb8446d8e6937e1408088a72a39937982cc6111d17f84/pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f", size = 4968581 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/8a/0b/9fcc47d19c48b59121088dd6da2488a49d5f72dacf8262e2790a1d2c7d15/pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c", size = 1225293 },
+]
+
+[[package]]
+name = "pyinstrument"
+version = "5.0.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/64/6e/85c2722e40cab4fd9df6bbe68a0d032e237cf8cfada71e5f067e4e433214/pyinstrument-5.0.1.tar.gz", hash = "sha256:f4fd0754d02959c113a4b1ebed02f4627b6e2c138719ddf43244fd95f201c8c9", size = 263162 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/e1/09/696e29364503393c5bd0471f1c396d41820167b3f496bf8b128dc981f30d/pyinstrument-5.0.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:cfd7b7dc56501a1f30aa059cc2f1746ece6258a841d2e4609882581f9c17f824", size = 128903 },
+ { url = "https://files.pythonhosted.org/packages/b5/dd/36d1641414eb0ab3fb50815de8d927b74924a9bfb1e409c53e9aad4a16de/pyinstrument-5.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fe1f33178a2b0ddb3c6d2321406228bdad41286774e65314d511dcf4a71b83e4", size = 121440 },
+ { url = "https://files.pythonhosted.org/packages/9e/3f/05196fb514735aceef9a9439f56bcaa5ccb8b440685aa4f13fdb9e925182/pyinstrument-5.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0519d02dee55a87afcf6d787f8d8f5a16d2b89f7ba9533064a986a2d31f27340", size = 144783 },
+ { url = "https://files.pythonhosted.org/packages/73/4b/1b041b974e7e465ca311e712beb8be0bc9cf769bcfc6660b1b2ba630c27c/pyinstrument-5.0.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2f59ed9ac9466ff9b30eb7285160fa794aa3f8ce2bcf58a94142f945882d28ab", size = 143717 },
+ { url = "https://files.pythonhosted.org/packages/4a/dc/3fa73e2dde1588b6281e494a14c183a27e1a67db7401fddf9c528fb8e1a9/pyinstrument-5.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cbf3114d332e499ba35ca4aedc1ef95bc6fb15c8d819729b5c0aeb35c8b64dd2", size = 145082 },
+ { url = "https://files.pythonhosted.org/packages/91/24/b86d4273cc524a4f334a610a1c4b157146c808d8935e85d44dff3a6b75ee/pyinstrument-5.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:20f8054e85dd710f5a8c4d6b738867366ceef89671db09c87690ba1b5c66bd67", size = 144737 },
+ { url = "https://files.pythonhosted.org/packages/3c/39/6025a71082122bfbfee4eac6649635e4c688954bdf306bcd3629457c49b2/pyinstrument-5.0.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:63e8d75ffa50c3cf6d980844efce0334659e934dcc3832bad08c23c171c545ff", size = 144488 },
+ { url = "https://files.pythonhosted.org/packages/da/ce/679b0e9a278004defc93c277c3f81b456389dd530f89e28a45bd9dae203e/pyinstrument-5.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a3ca9c8540051513dd633de9d7eac9fee2eda50b78b6eedeaa7e5a7be66026b5", size = 144895 },
+ { url = "https://files.pythonhosted.org/packages/58/d8/cf80bb278e2a071325e4fb244127eb68dce9d0520d20c1fda75414f119ee/pyinstrument-5.0.1-cp312-cp312-win32.whl", hash = "sha256:b549d910b846757ffbf74d94528d1a694a3848a6cfc6a6cab2ce697ee71e4548", size = 123027 },
+ { url = "https://files.pythonhosted.org/packages/39/49/9251fe641d242d4c0dc49178b064f22da1c542d80e4040561428a9f8dd1c/pyinstrument-5.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:86f20b680223697a8ac5c061fb40a63d3ee519c7dfb1097627bd4480711216d9", size = 123818 },
+ { url = "https://files.pythonhosted.org/packages/0f/ae/f8f84ecd0dc2c4f0d84920cb4ffdbea52a66e4b4abc2110f18879b57f538/pyinstrument-5.0.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f5065639dfedc3b8e537161f9aaa8c550c8717c935a962e9bf1e843bf0e8791f", size = 128900 },
+ { url = "https://files.pythonhosted.org/packages/23/2f/b742c46d86d4c1f74ec0819f091bbc2fad0bab786584a18d89d9178802f1/pyinstrument-5.0.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b5d20802b0c2bd1ddb95b2e96ebd3e9757dbab1e935792c2629166f1eb267bb2", size = 121445 },
+ { url = "https://files.pythonhosted.org/packages/d9/e0/297dc8454ed437aec0fbdc3cc1a6a5fdf6701935b91dd31caf38c5e3ff92/pyinstrument-5.0.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6e6f5655d580429e7992c37757cc5f6e74ca81b0f2768b833d9711631a8cb2f7", size = 144904 },
+ { url = "https://files.pythonhosted.org/packages/8b/df/e4faff09fdbad7e685ceb0f96066d434fc8350382acf8df47577653f702b/pyinstrument-5.0.1-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b4c8c9ad93f62f0bf2ddc7fb6fce3a91c008d422873824e01c5e5e83467fd1fb", size = 143801 },
+ { url = "https://files.pythonhosted.org/packages/b1/63/ed2955d980bbebf17155119e2687ac15e170b6221c4bb5f5c37f41323fe5/pyinstrument-5.0.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db15d1854b360182d242da8de89761a0ffb885eea61cb8652e40b5b9a4ef44bc", size = 145204 },
+ { url = "https://files.pythonhosted.org/packages/c4/18/31b8dcdade9767afc7a36a313d8cf9c5690b662e9755fe7bd0523125e06f/pyinstrument-5.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c803f7b880394b7bba5939ff8a59d6962589e9a0140fc33c3a6a345c58846106", size = 144881 },
+ { url = "https://files.pythonhosted.org/packages/1f/14/cd19894eb03dd28093f564e8bcf7ae4edc8e315ce962c8155cf795fc0784/pyinstrument-5.0.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:84e37ffabcf26fe820d354a1f7e9fc26949f953addab89b590c5000b3ffa60d0", size = 144643 },
+ { url = "https://files.pythonhosted.org/packages/80/54/3dd08f5a869d3b654ff7e4e4c9d2b34f8de73fb0f2f792fac5024a312e0f/pyinstrument-5.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a0d23d3763ec95da0beb390c2f7df7cbe36ea62b6a4d5b89c4eaab81c1c649cf", size = 145070 },
+ { url = "https://files.pythonhosted.org/packages/5d/dc/ac8e798235a1dbccefc1b204a16709cef36f02c07587763ba8eb510fc8bc/pyinstrument-5.0.1-cp313-cp313-win32.whl", hash = "sha256:967f84bd82f14425543a983956ff9cfcf1e3762755ffcec8cd835c6be22a7a0a", size = 123030 },
+ { url = "https://files.pythonhosted.org/packages/52/59/adcb3e85c9105c59382723a67f682012aa7f49027e270e721f2d59f63fcf/pyinstrument-5.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:70b16b5915534d8df40dcf04a7cc78d3290464c06fa358a4bc324280af4c74e0", size = 123825 },
+]
+
+[[package]]
+name = "pyjson5"
+version = "1.6.8"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/8e/27/76ff4f9c71b353b8171fe9a8bda20612b7b12f9728d619a5c6df1e279bce/pyjson5-1.6.8.tar.gz", hash = "sha256:b3ecee050a8a4b03cc4f1a7e9a0c478be757b46578fda1ea0f16ac8a24ba8e7a", size = 300019 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/ff/3a/0ed2cdfdb67eaaa73dc28686eebee1805bd7edfa0e8f85cc0f0a7d71641e/pyjson5-1.6.8-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d7b4a4b36a8748011c7586d4bba3eb403d82bdb62605e7478f2c8b11c7e01711", size = 327150 },
+ { url = "https://files.pythonhosted.org/packages/60/60/c9e84e3b2520f7b67412173c7d17d98ab24fbef874bcfcf51eb83622fa9a/pyjson5-1.6.8-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9ee2f077cf05daa9aaf3c750b63cce5b5671cf8fa848b29beaf1030a08d94fda", size = 173668 },
+ { url = "https://files.pythonhosted.org/packages/ae/dd/4c9569654dc42c42d2a029e77e4371687bfb6f9f4afda6f1c8adda5d655d/pyjson5-1.6.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2bbfdeeb531f79730899ef674d80dd6b6bc7c29fe3789660115f0ba66eef834f", size = 162740 },
+ { url = "https://files.pythonhosted.org/packages/fb/6f/976aed9c5fe81cafda04bb470196c790fec78bfc057ea0a8a5e84ef4671e/pyjson5-1.6.8-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8fe8ba077a6ef01e6493696c27455eeae64e39ff4bd71a1a7bb66af40be7232c", size = 174476 },
+ { url = "https://files.pythonhosted.org/packages/da/8b/ab7fcfe3c07ecd1d71dec2b1062755950d8e211808f602ff60cf31264820/pyjson5-1.6.8-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:701db0660e434fae000e5d4d49efc0b80fbeedf938cbcc8b6d72c229d395feca", size = 177611 },
+ { url = "https://files.pythonhosted.org/packages/6a/64/8e52e7950da4855adbcbffa4a89864685995b692802a768ea31675e2c5c7/pyjson5-1.6.8-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:515c89e7063100bcc7c67292559bdd926da19b59fe00281e9dd2fa83f30747f1", size = 195618 },
+ { url = "https://files.pythonhosted.org/packages/dd/1a/957fea06a1e6ba34767411f2a4c6a926b32f5181a16e5505de9aca85847f/pyjson5-1.6.8-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d622733cf671c8104a2936b3ff589903fa4e2fec5db4e2679297219446d944a7", size = 175521 },
+ { url = "https://files.pythonhosted.org/packages/dc/7d/cc11b4283a6f255bea76458d663d1d41de396bc50100f2f7af603dbe6d65/pyjson5-1.6.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a4577a18545f3f4461df46d3d38d85659b16a77ca8975289ef6f21e1c228f7bf", size = 185277 },
+ { url = "https://files.pythonhosted.org/packages/94/21/5187cc7105934e7ac1dfbfabd33bc517618f62a78c7357544f53653bf373/pyjson5-1.6.8-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b0cd98871646bfb2236cfdc0ae87f8ae8f1f631133b99fef5e74307248c4ae8d", size = 196515 },
+ { url = "https://files.pythonhosted.org/packages/6d/05/2f4943349dd6814f3f24ce515ef06864f9d0351b20d69c978dd66c07fa1f/pyjson5-1.6.8-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5a379911161545aa57bd6cd97f249cabcfe5990688f4dff9a8f328f5f6f231d3", size = 1119222 },
+ { url = "https://files.pythonhosted.org/packages/40/62/1d78786fbd998937849e9364dc034f68fd43fa1e619dbfc71a0b57e50031/pyjson5-1.6.8-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:24c6206f508c169034fd851eb87af3aec893d2eca3bf14df65eecc520da16883", size = 997285 },
+ { url = "https://files.pythonhosted.org/packages/ad/3a/c57b9724b471e61d38123eef69eed09b6ec7fd2a144f56e49c96b11a7458/pyjson5-1.6.8-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:fd21ce9dd4733347b6a426f4f943dd20547befbd6ef502b7480944c84a1425a3", size = 1276952 },
+ { url = "https://files.pythonhosted.org/packages/db/fa/81257989504d1442d272e86e03b9d1c4b7e355e0034c0d6c51f1ac5e3229/pyjson5-1.6.8-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:7a11d3cd6114de90364c24876f1cd47dcecaffb47184ffffb01eb585c8810f4b", size = 1229440 },
+ { url = "https://files.pythonhosted.org/packages/89/88/8d63d86d871bd60ec43030509ea58e216a635fdf723290071e159689e4e2/pyjson5-1.6.8-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:4a58185b9ac3adfed0adf539be7293d76fe0f7c515b6f9982b225c8084027255", size = 1318444 },
+ { url = "https://files.pythonhosted.org/packages/e4/59/1a89268f650c9d8ef73f97ff9adeab1e0f40b8bf09d82fac840e26f8154d/pyjson5-1.6.8-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f4724dcb646c2d40ad45d5aa7a5af86d54dc38c78e27b795418ecca23248bb", size = 1177145 },
+ { url = "https://files.pythonhosted.org/packages/e1/45/cc1967749b08a701ddeb743cd432a9a6ddbff188a1b1294d061823d22993/pyjson5-1.6.8-cp312-cp312-win32.whl", hash = "sha256:cc414b6ab28ed75d761c825f1150c19dd9a8f9b2268ee6af0173d148f018a8c5", size = 127509 },
+ { url = "https://files.pythonhosted.org/packages/d6/07/430e3a960daf322e7f4b82515ec64d6f2febccdeba31a421c2daab8a1786/pyjson5-1.6.8-cp312-cp312-win_amd64.whl", hash = "sha256:3fd513eaffba7b72d56bd5b26a92e2edb3694602adcaf3414a9f7d6c4c5d9be7", size = 143885 },
+ { url = "https://files.pythonhosted.org/packages/74/17/1a2002b6ee6b6bd7abba860afa7c8f76f6cde88a8493f7db6e14b5681fcb/pyjson5-1.6.8-cp312-cp312-win_arm64.whl", hash = "sha256:f8d5a208b8954758c75f8e8ae28d195bac3fae24ce9b51f6261b401e4ccce116", size = 127142 },
+ { url = "https://files.pythonhosted.org/packages/ee/e1/2d85c838a9a702f6d4134cbccc85f8811f96f0889ca0f642dd4e1cecae66/pyjson5-1.6.8-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:681e52df0705056dc39cf7d7bec4161e2769437fdf89f55084a4b060e9bbbfc9", size = 325120 },
+ { url = "https://files.pythonhosted.org/packages/42/43/3b2a26ca84573209616675d63ffe559a6e8b73488d6c11e4a45f0204fc3e/pyjson5-1.6.8-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1550dc70199401056f80acfc503da36de2df70dd4364a0efb654ffe7e9246ac6", size = 172648 },
+ { url = "https://files.pythonhosted.org/packages/9d/cd/ad93170f8b7934b13e5a340daed934e7a8591e5d08abf3f50ab144a2663d/pyjson5-1.6.8-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:77005662014809a7b8b78f984131a3751295ff102f4c62b452bbdac946360166", size = 161830 },
+ { url = "https://files.pythonhosted.org/packages/21/d3/dffd61a6b17680f39d5aaea24297ddf13d03064fb9ab5987de4bb619bd79/pyjson5-1.6.8-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:65f2922cc8fd6b1e9cc8ff7e5fe975f7bf111c03eb06ed9b2ee793e6870d3212", size = 173697 },
+ { url = "https://files.pythonhosted.org/packages/b8/72/9566b6ec24c11293d2bb91be24492afaf9e339781057b355129a7d262050/pyjson5-1.6.8-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d83e0bc87d94baa39703c1d7139c5ce7ff025a53a34251762128713a294cf147", size = 177518 },
+ { url = "https://files.pythonhosted.org/packages/4b/2c/e615aca4b7e8f1c3b4d5520b8ec6b808a5320e19be8ccd6828b016e46b77/pyjson5-1.6.8-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:72fa22291149e8731c4bbc225cf75a41a049a54903018ca670c849658c1edc04", size = 193327 },
+ { url = "https://files.pythonhosted.org/packages/62/64/f06dec3ec3c7501d5a969d9aec1403898b70a2817225db749c8219203229/pyjson5-1.6.8-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3948742ff2d2f222ab87cc77d8c6ce8a9ef063fe2904f8fa88309611a128147a", size = 174453 },
+ { url = "https://files.pythonhosted.org/packages/d4/ca/f5b147b8a186e37a9339290dd9c8271aae94eab0307169124ec83c74aa99/pyjson5-1.6.8-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94e1b9d219f40bebbb6285840b094eca523481cf199cd46154044dae333d492d", size = 184161 },
+ { url = "https://files.pythonhosted.org/packages/1e/9d/7e7d2eaef592e350e8988a68b4d38f358894a1fb05237b6aef5cd25fea8a/pyjson5-1.6.8-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2dea723f88e89dba1d4a6542c5527cac7ecff6755291ad2eb60e1c2f578bb69f", size = 195307 },
+ { url = "https://files.pythonhosted.org/packages/51/c1/1538a2064599e6e77b96e5a58dc212d0fabf18442363a0224f5fdc31a51e/pyjson5-1.6.8-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:06b857a5a36f2bad52267d1a57a880cd62c3b0d3f3a719ab8599a1d5465e2417", size = 1121719 },
+ { url = "https://files.pythonhosted.org/packages/21/36/4af2c28aa6a0a9c2f839d2f63613605c11d0294d5a8dadcf65cc6b7e4f5c/pyjson5-1.6.8-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:aebdd4c5a878f125fea8b192244b1e64532561a315725502eee8d7629598882f", size = 995812 },
+ { url = "https://files.pythonhosted.org/packages/55/63/1c7c7797113aee8fd6bbebf56ac2603681635dd7bab73bd14d5ad34b48d1/pyjson5-1.6.8-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:10688e75fd9f18e34dddd111cafd87cca6727837469b8bfb61f2d2685490f976", size = 1279088 },
+ { url = "https://files.pythonhosted.org/packages/b4/c1/1121519c37ce70e4d1d4e5f714f5e0121313b79421ba8495a130cdad5d1e/pyjson5-1.6.8-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:e3aee51ef5feb4409ff36713f70251265b04c18c8322bc91d2578759225e918d", size = 1229957 },
+ { url = "https://files.pythonhosted.org/packages/84/39/3618b8e0dbc53233afd99c867d0f4fa7d8cc36489949d18dc833e692f7f3/pyjson5-1.6.8-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:5e7f5b92460dc69ce27814d4ab546e3bae84b9b2e26f29701ad7fab637e6bf2f", size = 1318799 },
+ { url = "https://files.pythonhosted.org/packages/90/ae/353ce74183d884b56407d29ebc3aab63d23ca7dfb9e9a75208737a917e11/pyjson5-1.6.8-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b77c94296cd0763bc2d7d276cb53dbc97edeacfbc50c02103521d586ca91ff37", size = 1180476 },
+ { url = "https://files.pythonhosted.org/packages/8c/df/f8afe0318b0b628a8c8abce57ffccb7afd0df9aab08bb08f4c2de5008854/pyjson5-1.6.8-cp313-cp313-win32.whl", hash = "sha256:260b6f2d7148f5fa23d817b82e9960a75a44678116d6a5513bed4e88d6697343", size = 127415 },
+ { url = "https://files.pythonhosted.org/packages/67/d9/9bd17bc0c99d2d917900114d548414f609ea81947e58f6525068d673fc77/pyjson5-1.6.8-cp313-cp313-win_amd64.whl", hash = "sha256:fe03568ca61050f00c951501d70aaf68064ab5fecb3d84961ce743102cc81036", size = 143519 },
+ { url = "https://files.pythonhosted.org/packages/ee/6d/8f35cab314cab3b67681ec072e7acb6432bee3ebc45dcf11fd8b6535cb57/pyjson5-1.6.8-cp313-cp313-win_arm64.whl", hash = "sha256:f984d06902b2096206d15bcbc6f0c75c024de295294ca04c8c11aedc871e2da0", size = 126843 },
+]
+
+[[package]]
+name = "pyjwt"
+version = "2.10.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/e7/46/bd74733ff231675599650d3e47f361794b22ef3e3770998dda30d3b63726/pyjwt-2.10.1.tar.gz", hash = "sha256:3cc5772eb20009233caf06e9d8a0577824723b44e6648ee0a2aedb6cf9381953", size = 87785 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/61/ad/689f02752eeec26aed679477e80e632ef1b682313be70793d798c1d5fc8f/PyJWT-2.10.1-py3-none-any.whl", hash = "sha256:dcdd193e30abefd5debf142f9adfcdd2b58004e644f25406ffaebd50bd98dacb", size = 22997 },
+]
+
+[package.optional-dependencies]
+crypto = [
+ { name = "cryptography" },
+]
+
+[[package]]
+name = "pynacl"
+version = "1.5.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "cffi" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/a7/22/27582568be639dfe22ddb3902225f91f2f17ceff88ce80e4db396c8986da/PyNaCl-1.5.0.tar.gz", hash = "sha256:8ac7448f09ab85811607bdd21ec2464495ac8b7c66d146bf545b0f08fb9220ba", size = 3392854 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/ce/75/0b8ede18506041c0bf23ac4d8e2971b4161cd6ce630b177d0a08eb0d8857/PyNaCl-1.5.0-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:401002a4aaa07c9414132aaed7f6836ff98f59277a234704ff66878c2ee4a0d1", size = 349920 },
+ { url = "https://files.pythonhosted.org/packages/59/bb/fddf10acd09637327a97ef89d2a9d621328850a72f1fdc8c08bdf72e385f/PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:52cb72a79269189d4e0dc537556f4740f7f0a9ec41c1322598799b0bdad4ef92", size = 601722 },
+ { url = "https://files.pythonhosted.org/packages/5d/70/87a065c37cca41a75f2ce113a5a2c2aa7533be648b184ade58971b5f7ccc/PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a36d4a9dda1f19ce6e03c9a784a2921a4b726b02e1c736600ca9c22029474394", size = 680087 },
+ { url = "https://files.pythonhosted.org/packages/ee/87/f1bb6a595f14a327e8285b9eb54d41fef76c585a0edef0a45f6fc95de125/PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:0c84947a22519e013607c9be43706dd42513f9e6ae5d39d3613ca1e142fba44d", size = 856678 },
+ { url = "https://files.pythonhosted.org/packages/66/28/ca86676b69bf9f90e710571b67450508484388bfce09acf8a46f0b8c785f/PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06b8f6fa7f5de8d5d2f7573fe8c863c051225a27b61e6860fd047b1775807858", size = 1133660 },
+ { url = "https://files.pythonhosted.org/packages/3d/85/c262db650e86812585e2bc59e497a8f59948a005325a11bbbc9ecd3fe26b/PyNaCl-1.5.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:a422368fc821589c228f4c49438a368831cb5bbc0eab5ebe1d7fac9dded6567b", size = 663824 },
+ { url = "https://files.pythonhosted.org/packages/fd/1a/cc308a884bd299b651f1633acb978e8596c71c33ca85e9dc9fa33a5399b9/PyNaCl-1.5.0-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:61f642bf2378713e2c2e1de73444a3778e5f0a38be6fee0fe532fe30060282ff", size = 1117912 },
+ { url = "https://files.pythonhosted.org/packages/25/2d/b7df6ddb0c2a33afdb358f8af6ea3b8c4d1196ca45497dd37a56f0c122be/PyNaCl-1.5.0-cp36-abi3-win32.whl", hash = "sha256:e46dae94e34b085175f8abb3b0aaa7da40767865ac82c928eeb9e57e1ea8a543", size = 204624 },
+ { url = "https://files.pythonhosted.org/packages/5e/22/d3db169895faaf3e2eda892f005f433a62db2decbcfbc2f61e6517adfa87/PyNaCl-1.5.0-cp36-abi3-win_amd64.whl", hash = "sha256:20f42270d27e1b6a29f54032090b972d97f0a1b0948cc52392041ef7831fee93", size = 212141 },
+]
+
+[[package]]
+name = "pyproject-api"
+version = "1.9.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "packaging" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/7e/66/fdc17e94486836eda4ba7113c0db9ac7e2f4eea1b968ee09de2fe75e391b/pyproject_api-1.9.0.tar.gz", hash = "sha256:7e8a9854b2dfb49454fae421cb86af43efbb2b2454e5646ffb7623540321ae6e", size = 22714 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/b0/1d/92b7c765df46f454889d9610292b0ccab15362be3119b9a624458455e8d5/pyproject_api-1.9.0-py3-none-any.whl", hash = "sha256:326df9d68dea22d9d98b5243c46e3ca3161b07a1b9b18e213d1e24fd0e605766", size = 13131 },
+]
+
+[[package]]
+name = "pyright"
+version = "1.1.393"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "nodeenv" },
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/f4/c1/aede6c74e664ab103673e4f1b7fd3d058fef32276be5c43572f4067d4a8e/pyright-1.1.393.tar.gz", hash = "sha256:aeeb7ff4e0364775ef416a80111613f91a05c8e01e58ecfefc370ca0db7aed9c", size = 3790430 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/92/47/f0dd0f8afce13d92e406421ecac6df0990daee84335fc36717678577d3e0/pyright-1.1.393-py3-none-any.whl", hash = "sha256:8320629bb7a44ca90944ba599390162bf59307f3d9fb6e27da3b7011b8c17ae5", size = 5646057 },
+]
+
+[[package]]
+name = "pytest"
+version = "8.3.4"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "colorama", marker = "sys_platform == 'win32'" },
+ { name = "iniconfig" },
+ { name = "packaging" },
+ { name = "pluggy" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/05/35/30e0d83068951d90a01852cb1cef56e5d8a09d20c7f511634cc2f7e0372a/pytest-8.3.4.tar.gz", hash = "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761", size = 1445919 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/11/92/76a1c94d3afee238333bc0a42b82935dd8f9cf8ce9e336ff87ee14d9e1cf/pytest-8.3.4-py3-none-any.whl", hash = "sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6", size = 343083 },
+]
+
+[[package]]
+name = "pytest-cov"
+version = "6.0.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "coverage" },
+ { name = "pytest" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/be/45/9b538de8cef30e17c7b45ef42f538a94889ed6a16f2387a6c89e73220651/pytest-cov-6.0.0.tar.gz", hash = "sha256:fde0b595ca248bb8e2d76f020b465f3b107c9632e6a1d1705f17834c89dcadc0", size = 66945 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/36/3b/48e79f2cd6a61dbbd4807b4ed46cb564b4fd50a76166b1c4ea5c1d9e2371/pytest_cov-6.0.0-py3-none-any.whl", hash = "sha256:eee6f1b9e61008bd34975a4d5bab25801eb31898b032dd55addc93e96fcaaa35", size = 22949 },
+]
+
+[[package]]
+name = "pytest-snapshot"
+version = "0.9.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "pytest" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/9b/7b/ab8f1fc1e687218aa66acec1c3674d9c443f6a2dc8cb6a50f464548ffa34/pytest-snapshot-0.9.0.tar.gz", hash = "sha256:c7013c3abc3e860f9feff899f8b4debe3708650d8d8242a61bf2625ff64db7f3", size = 19877 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/cb/29/518f32faf6edad9f56d6e0107217f7de6b79f297a47170414a2bd4be7f01/pytest_snapshot-0.9.0-py3-none-any.whl", hash = "sha256:4b9fe1c21c868fe53a545e4e3184d36bc1c88946e3f5c1d9dd676962a9b3d4ab", size = 10715 },
+]
+
+[[package]]
+name = "python-dateutil"
+version = "2.9.0.post0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "six" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892 },
+]
+
+[[package]]
+name = "python-dotenv"
+version = "1.0.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/bc/57/e84d88dfe0aec03b7a2d4327012c1627ab5f03652216c63d49846d7a6c58/python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca", size = 39115 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/6a/3e/b68c118422ec867fa7ab88444e1274aa40681c606d59ac27de5a5588f082/python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a", size = 19863 },
+]
+
+[[package]]
+name = "python-gitlab"
+version = "4.13.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "requests" },
+ { name = "requests-toolbelt" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/c4/ea/e2cde926d63526935c1df259177371a195089b631d67a577fe5c39fbc7e1/python_gitlab-4.13.0.tar.gz", hash = "sha256:576bfb0901faca0c6b2d1ff2592e02944a6ec3e086c3129fb43c2a0df56a1c67", size = 484996 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/6b/5e/5fb4dcae9f5af5463c16952823d446ca449cce920efe8669871f600f0ab9/python_gitlab-4.13.0-py3-none-any.whl", hash = "sha256:8299a054fb571da16e1a8c1868fff01f34ac41ea1410c713a4647b3bbb2aa279", size = 145254 },
+]
+
+[[package]]
+name = "python-levenshtein"
+version = "0.26.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "levenshtein" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/31/72/58d77cb80b3c130d94f53a8204ffad9acfddb925b2fb5818ff9af0b3c832/python_levenshtein-0.26.1.tar.gz", hash = "sha256:24ba578e28058ebb4afa2700057e1678d7adf27e43cd1f17700c09a9009d5d3a", size = 12276 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/0f/d7/03e0453719ed89724664f781f0255949408118093dbf77a2aa2a1198b38e/python_Levenshtein-0.26.1-py3-none-any.whl", hash = "sha256:8ef5e529dd640fb00f05ee62d998d2ee862f19566b641ace775d5ae16167b2ef", size = 9426 },
+]
+
+[[package]]
+name = "python-multipart"
+version = "0.0.20"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/f3/87/f44d7c9f274c7ee665a29b885ec97089ec5dc034c7f3fafa03da9e39a09e/python_multipart-0.0.20.tar.gz", hash = "sha256:8dd0cab45b8e23064ae09147625994d090fa46f5b0d1e13af944c331a7fa9d13", size = 37158 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/45/58/38b5afbc1a800eeea951b9285d3912613f2603bdf897a4ab0f4bd7f405fc/python_multipart-0.0.20-py3-none-any.whl", hash = "sha256:8a62d3a8335e06589fe01f2a3e178cdcc632f3fbe0d492ad9ee0ec35aab1f104", size = 24546 },
+]
+
+[[package]]
+name = "python-semantic-release"
+version = "9.19.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "click" },
+ { name = "click-option-group" },
+ { name = "dotty-dict" },
+ { name = "gitpython" },
+ { name = "importlib-resources" },
+ { name = "jinja2" },
+ { name = "pydantic" },
+ { name = "python-gitlab" },
+ { name = "requests" },
+ { name = "rich" },
+ { name = "shellingham" },
+ { name = "tomlkit" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/78/60/ca6f63f302325093137afc1b83bba60c0e717a51977f7ba65bf3dab33949/python_semantic_release-9.19.0.tar.gz", hash = "sha256:6b5a560ce263258c1f2918f6124bb92f8efcf5e8cadbf2b7ced9f0cb5a6e8566", size = 299801 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/4f/2c/1d4b13166c4629e0001406a9eb90adcaccacff325aab33b37a615da4cf83/python_semantic_release-9.19.0-py3-none-any.whl", hash = "sha256:711edd1650fc59008209ba5058660306e2e365d64f3d03fc51d5de27badf6cfa", size = 127132 },
+]
+
+[[package]]
+name = "pyyaml"
+version = "6.0.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", size = 183873 },
+ { url = "https://files.pythonhosted.org/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", size = 173302 },
+ { url = "https://files.pythonhosted.org/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", size = 739154 },
+ { url = "https://files.pythonhosted.org/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", size = 766223 },
+ { url = "https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", size = 767542 },
+ { url = "https://files.pythonhosted.org/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", size = 731164 },
+ { url = "https://files.pythonhosted.org/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611 },
+ { url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591 },
+ { url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338 },
+ { url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309 },
+ { url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679 },
+ { url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428 },
+ { url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361 },
+ { url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523 },
+ { url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660 },
+ { url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597 },
+ { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527 },
+ { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446 },
+]
+
+[[package]]
+name = "rapidfuzz"
+version = "3.12.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/c9/df/c300ead8c2962f54ad87872e6372a6836f0181a7f20b433c987bd106bfce/rapidfuzz-3.12.1.tar.gz", hash = "sha256:6a98bbca18b4a37adddf2d8201856441c26e9c981d8895491b5bc857b5f780eb", size = 57907552 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/1a/20/6049061411df87f2814a2677db0f15e673bb9795bfeff57dc9708121374d/rapidfuzz-3.12.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f6235b57ae3faa3f85cb3f90c9fee49b21bd671b76e90fc99e8ca2bdf0b5e4a3", size = 1944328 },
+ { url = "https://files.pythonhosted.org/packages/25/73/199383c4c21ae3b4b6ea6951c6896ab38e9dc96942462fa01f9d3fb047da/rapidfuzz-3.12.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:af4585e5812632c357fee5ab781c29f00cd06bea58f8882ff244cc4906ba6c9e", size = 1430203 },
+ { url = "https://files.pythonhosted.org/packages/7b/51/77ebaeec5413c53c3e6d8b800f2b979551adbed7b5efa094d1fad5c5b751/rapidfuzz-3.12.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5942dc4460e5030c5f9e1d4c9383de2f3564a2503fe25e13e89021bcbfea2f44", size = 1403662 },
+ { url = "https://files.pythonhosted.org/packages/54/06/1fadd2704db0a7eecf78de812e2f4fab37c4ae105a5ce4578c9fc66bb0c5/rapidfuzz-3.12.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0b31ab59e1a0df5afc21f3109b6cfd77b34040dbf54f1bad3989f885cfae1e60", size = 5555849 },
+ { url = "https://files.pythonhosted.org/packages/19/45/da128c3952bd09cef2935df58db5273fc4eb67f04a69dcbf9e25af9e4432/rapidfuzz-3.12.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:97c885a7a480b21164f57a706418c9bbc9a496ec6da087e554424358cadde445", size = 1655273 },
+ { url = "https://files.pythonhosted.org/packages/03/ee/bf2b2a95b5af4e6d36105dd9284dc5335fdcc7f0326186d4ab0b5aa4721e/rapidfuzz-3.12.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d844c0587d969ce36fbf4b7cbf0860380ffeafc9ac5e17a7cbe8abf528d07bb", size = 1678041 },
+ { url = "https://files.pythonhosted.org/packages/7f/4f/36ea4d7f306a23e30ea1a6cabf545d2a794e8ca9603d2ee48384314cde3a/rapidfuzz-3.12.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a93c95dce8917bf428064c64024de43ffd34ec5949dd4425780c72bd41f9d969", size = 3137099 },
+ { url = "https://files.pythonhosted.org/packages/70/ef/48195d94b018e7340a60c9a642ab0081bf9dc64fb0bd01dfafd93757d2a2/rapidfuzz-3.12.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:834f6113d538af358f39296604a1953e55f8eeffc20cb4caf82250edbb8bf679", size = 2307388 },
+ { url = "https://files.pythonhosted.org/packages/e5/cd/53d5dbc4791df3e1a8640fc4ad5e328ebb040cc01c10c66f891aa6b83ed5/rapidfuzz-3.12.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a940aa71a7f37d7f0daac186066bf6668d4d3b7e7ef464cb50bc7ba89eae1f51", size = 6906504 },
+ { url = "https://files.pythonhosted.org/packages/1b/99/c27e7db1d49cfd77780cb73978f81092682c2bdbc6de75363df6aaa086d6/rapidfuzz-3.12.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:ec9eaf73501c9a7de2c6938cb3050392e2ee0c5ca3921482acf01476b85a7226", size = 2684757 },
+ { url = "https://files.pythonhosted.org/packages/02/8c/2474d6282fdd4aae386a6b16272e544a3f9ea2dcdcf2f3b0b286549bc3d5/rapidfuzz-3.12.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:3c5ec360694ac14bfaeb6aea95737cf1a6cf805b5fe8ea7fd28814706c7fa838", size = 3229940 },
+ { url = "https://files.pythonhosted.org/packages/ac/27/95d5a8ebe5fcc5462dd0fd265553c8a2ec4a770e079afabcff978442bcb3/rapidfuzz-3.12.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6b5e176524653ac46f1802bdd273a4b44a5f8d0054ed5013a8e8a4b72f254599", size = 4148489 },
+ { url = "https://files.pythonhosted.org/packages/8d/2c/e509bc24b6514de4d6f2c5480201568e1d9a3c7e4692cc969ef899227ba5/rapidfuzz-3.12.1-cp312-cp312-win32.whl", hash = "sha256:6f463c6f1c42ec90e45d12a6379e18eddd5cdf74138804d8215619b6f4d31cea", size = 1834110 },
+ { url = "https://files.pythonhosted.org/packages/cc/ab/900b8d57090b30269258e3ae31752ec9c31042cd58660fcc96d50728487d/rapidfuzz-3.12.1-cp312-cp312-win_amd64.whl", hash = "sha256:b894fa2b30cd6498a29e5c470cb01c6ea898540b7e048a0342775a5000531334", size = 1612461 },
+ { url = "https://files.pythonhosted.org/packages/a0/df/3f51a0a277185b3f28b2941e071aff62908a6b81527efc67a643bcb59fb8/rapidfuzz-3.12.1-cp312-cp312-win_arm64.whl", hash = "sha256:43bb17056c5d1332f517b888c4e57846c4b5f936ed304917eeb5c9ac85d940d4", size = 864251 },
+ { url = "https://files.pythonhosted.org/packages/62/d2/ceebc2446d1f3d3f2cae2597116982e50c2eed9ff2f5a322a51736981405/rapidfuzz-3.12.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:97f824c15bc6933a31d6e3cbfa90188ba0e5043cf2b6dd342c2b90ee8b3fd47c", size = 1936794 },
+ { url = "https://files.pythonhosted.org/packages/88/38/37f7ea800aa959a4f7a63477fc9ad7f3cd024e46bfadce5d23420af6c7e5/rapidfuzz-3.12.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a973b3f5cabf931029a3ae4a0f72e3222e53d412ea85fc37ddc49e1774f00fbf", size = 1424155 },
+ { url = "https://files.pythonhosted.org/packages/3f/14/409d0aa84430451488177fcc5cba8babcdf5a45cee772a2a265b9b5f4c7e/rapidfuzz-3.12.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df7880e012228722dec1be02b9ef3898ed023388b8a24d6fa8213d7581932510", size = 1398013 },
+ { url = "https://files.pythonhosted.org/packages/4b/2c/601e3ad0bbe61e65f99e72c8cefed9713606cf4b297cc4c3876051db7722/rapidfuzz-3.12.1-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9c78582f50e75e6c2bc38c791ed291cb89cf26a3148c47860c1a04d6e5379c8e", size = 5526157 },
+ { url = "https://files.pythonhosted.org/packages/97/ce/deb7b00ce6e06713fc4df81336402b7fa062f2393c8a47401c228ee906c3/rapidfuzz-3.12.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2d7d9e6a04d8344b0198c96394c28874086888d0a2b2f605f30d1b27b9377b7d", size = 1648446 },
+ { url = "https://files.pythonhosted.org/packages/ec/6f/2b8eae1748a022290815999594b438dbc1e072c38c76178ea996920a6253/rapidfuzz-3.12.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5620001fd4d6644a2f56880388179cc8f3767670f0670160fcb97c3b46c828af", size = 1676038 },
+ { url = "https://files.pythonhosted.org/packages/b9/6c/5c831197aca7148ed85c86bbe940e66073fea0fa97f30307bb5850ed8858/rapidfuzz-3.12.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0666ab4c52e500af7ba5cc17389f5d15c0cdad06412c80312088519fdc25686d", size = 3114137 },
+ { url = "https://files.pythonhosted.org/packages/fc/f2/d66ac185eeb0ee3fc0fe208dab1e72feece2c883bc0ab2097570a8159a7b/rapidfuzz-3.12.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:27b4d440fa50b50c515a91a01ee17e8ede719dca06eef4c0cccf1a111a4cfad3", size = 2305754 },
+ { url = "https://files.pythonhosted.org/packages/6c/61/9bf74d7ea9bebc7a1bed707591617bba7901fce414d346a7c5532ef02dbd/rapidfuzz-3.12.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:83dccfd5a754f2a0e8555b23dde31f0f7920601bfa807aa76829391ea81e7c67", size = 6901746 },
+ { url = "https://files.pythonhosted.org/packages/81/73/d8dddf73e168f723ef21272e8abb7d34d9244da395eb90ed5a617f870678/rapidfuzz-3.12.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:b572b634740e047c53743ed27a1bb3b4f93cf4abbac258cd7af377b2c4a9ba5b", size = 2673947 },
+ { url = "https://files.pythonhosted.org/packages/2e/31/3c473cea7d76af162819a5b84f5e7bdcf53b9e19568fc37cfbdab4f4512a/rapidfuzz-3.12.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:7fa7b81fb52902d5f78dac42b3d6c835a6633b01ddf9b202a3ca8443be4b2d6a", size = 3233070 },
+ { url = "https://files.pythonhosted.org/packages/c0/b7/73227dcbf8586f0ca4a77be2720311367288e2db142ae00a1404f42e712d/rapidfuzz-3.12.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b1d4fbff980cb6baef4ee675963c081f7b5d6580a105d6a4962b20f1f880e1fb", size = 4146828 },
+ { url = "https://files.pythonhosted.org/packages/3a/c8/fea749c662e268d348a77501995b51ac95cdc3624f3f95ba261f30b000ff/rapidfuzz-3.12.1-cp313-cp313-win32.whl", hash = "sha256:3fe8da12ea77271097b303fa7624cfaf5afd90261002314e3b0047d36f4afd8d", size = 1831797 },
+ { url = "https://files.pythonhosted.org/packages/66/18/11052be5984d9972eb04a52e2931e19e95b2e87731d179f60b79707b7efd/rapidfuzz-3.12.1-cp313-cp313-win_amd64.whl", hash = "sha256:6f7e92fc7d2a7f02e1e01fe4f539324dfab80f27cb70a30dd63a95445566946b", size = 1610169 },
+ { url = "https://files.pythonhosted.org/packages/db/c1/66427c618f000298edbd24e46dd3dd2d3fa441a602701ba6a260d41dd62b/rapidfuzz-3.12.1-cp313-cp313-win_arm64.whl", hash = "sha256:e31be53d7f4905a6a038296d8b773a79da9ee9f0cd19af9490c5c5a22e37d2e5", size = 863036 },
+]
+
+[[package]]
+name = "regex"
+version = "2024.11.6"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/8e/5f/bd69653fbfb76cf8604468d3b4ec4c403197144c7bfe0e6a5fc9e02a07cb/regex-2024.11.6.tar.gz", hash = "sha256:7ab159b063c52a0333c884e4679f8d7a85112ee3078fe3d9004b2dd875585519", size = 399494 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/ba/30/9a87ce8336b172cc232a0db89a3af97929d06c11ceaa19d97d84fa90a8f8/regex-2024.11.6-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:52fb28f528778f184f870b7cf8f225f5eef0a8f6e3778529bdd40c7b3920796a", size = 483781 },
+ { url = "https://files.pythonhosted.org/packages/01/e8/00008ad4ff4be8b1844786ba6636035f7ef926db5686e4c0f98093612add/regex-2024.11.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fdd6028445d2460f33136c55eeb1f601ab06d74cb3347132e1c24250187500d9", size = 288455 },
+ { url = "https://files.pythonhosted.org/packages/60/85/cebcc0aff603ea0a201667b203f13ba75d9fc8668fab917ac5b2de3967bc/regex-2024.11.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:805e6b60c54bf766b251e94526ebad60b7de0c70f70a4e6210ee2891acb70bf2", size = 284759 },
+ { url = "https://files.pythonhosted.org/packages/94/2b/701a4b0585cb05472a4da28ee28fdfe155f3638f5e1ec92306d924e5faf0/regex-2024.11.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b85c2530be953a890eaffde05485238f07029600e8f098cdf1848d414a8b45e4", size = 794976 },
+ { url = "https://files.pythonhosted.org/packages/4b/bf/fa87e563bf5fee75db8915f7352e1887b1249126a1be4813837f5dbec965/regex-2024.11.6-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bb26437975da7dc36b7efad18aa9dd4ea569d2357ae6b783bf1118dabd9ea577", size = 833077 },
+ { url = "https://files.pythonhosted.org/packages/a1/56/7295e6bad94b047f4d0834e4779491b81216583c00c288252ef625c01d23/regex-2024.11.6-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:abfa5080c374a76a251ba60683242bc17eeb2c9818d0d30117b4486be10c59d3", size = 823160 },
+ { url = "https://files.pythonhosted.org/packages/fb/13/e3b075031a738c9598c51cfbc4c7879e26729c53aa9cca59211c44235314/regex-2024.11.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b7fa6606c2881c1db9479b0eaa11ed5dfa11c8d60a474ff0e095099f39d98e", size = 796896 },
+ { url = "https://files.pythonhosted.org/packages/24/56/0b3f1b66d592be6efec23a795b37732682520b47c53da5a32c33ed7d84e3/regex-2024.11.6-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0c32f75920cf99fe6b6c539c399a4a128452eaf1af27f39bce8909c9a3fd8cbe", size = 783997 },
+ { url = "https://files.pythonhosted.org/packages/f9/a1/eb378dada8b91c0e4c5f08ffb56f25fcae47bf52ad18f9b2f33b83e6d498/regex-2024.11.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:982e6d21414e78e1f51cf595d7f321dcd14de1f2881c5dc6a6e23bbbbd68435e", size = 781725 },
+ { url = "https://files.pythonhosted.org/packages/83/f2/033e7dec0cfd6dda93390089864732a3409246ffe8b042e9554afa9bff4e/regex-2024.11.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a7c2155f790e2fb448faed6dd241386719802296ec588a8b9051c1f5c481bc29", size = 789481 },
+ { url = "https://files.pythonhosted.org/packages/83/23/15d4552ea28990a74e7696780c438aadd73a20318c47e527b47a4a5a596d/regex-2024.11.6-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:149f5008d286636e48cd0b1dd65018548944e495b0265b45e1bffecce1ef7f39", size = 852896 },
+ { url = "https://files.pythonhosted.org/packages/e3/39/ed4416bc90deedbfdada2568b2cb0bc1fdb98efe11f5378d9892b2a88f8f/regex-2024.11.6-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:e5364a4502efca094731680e80009632ad6624084aff9a23ce8c8c6820de3e51", size = 860138 },
+ { url = "https://files.pythonhosted.org/packages/93/2d/dd56bb76bd8e95bbce684326302f287455b56242a4f9c61f1bc76e28360e/regex-2024.11.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0a86e7eeca091c09e021db8eb72d54751e527fa47b8d5787caf96d9831bd02ad", size = 787692 },
+ { url = "https://files.pythonhosted.org/packages/0b/55/31877a249ab7a5156758246b9c59539abbeba22461b7d8adc9e8475ff73e/regex-2024.11.6-cp312-cp312-win32.whl", hash = "sha256:32f9a4c643baad4efa81d549c2aadefaeba12249b2adc5af541759237eee1c54", size = 262135 },
+ { url = "https://files.pythonhosted.org/packages/38/ec/ad2d7de49a600cdb8dd78434a1aeffe28b9d6fc42eb36afab4a27ad23384/regex-2024.11.6-cp312-cp312-win_amd64.whl", hash = "sha256:a93c194e2df18f7d264092dc8539b8ffb86b45b899ab976aa15d48214138e81b", size = 273567 },
+ { url = "https://files.pythonhosted.org/packages/90/73/bcb0e36614601016552fa9344544a3a2ae1809dc1401b100eab02e772e1f/regex-2024.11.6-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a6ba92c0bcdf96cbf43a12c717eae4bc98325ca3730f6b130ffa2e3c3c723d84", size = 483525 },
+ { url = "https://files.pythonhosted.org/packages/0f/3f/f1a082a46b31e25291d830b369b6b0c5576a6f7fb89d3053a354c24b8a83/regex-2024.11.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:525eab0b789891ac3be914d36893bdf972d483fe66551f79d3e27146191a37d4", size = 288324 },
+ { url = "https://files.pythonhosted.org/packages/09/c9/4e68181a4a652fb3ef5099e077faf4fd2a694ea6e0f806a7737aff9e758a/regex-2024.11.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:086a27a0b4ca227941700e0b31425e7a28ef1ae8e5e05a33826e17e47fbfdba0", size = 284617 },
+ { url = "https://files.pythonhosted.org/packages/fc/fd/37868b75eaf63843165f1d2122ca6cb94bfc0271e4428cf58c0616786dce/regex-2024.11.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bde01f35767c4a7899b7eb6e823b125a64de314a8ee9791367c9a34d56af18d0", size = 795023 },
+ { url = "https://files.pythonhosted.org/packages/c4/7c/d4cd9c528502a3dedb5c13c146e7a7a539a3853dc20209c8e75d9ba9d1b2/regex-2024.11.6-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b583904576650166b3d920d2bcce13971f6f9e9a396c673187f49811b2769dc7", size = 833072 },
+ { url = "https://files.pythonhosted.org/packages/4f/db/46f563a08f969159c5a0f0e722260568425363bea43bb7ae370becb66a67/regex-2024.11.6-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c4de13f06a0d54fa0d5ab1b7138bfa0d883220965a29616e3ea61b35d5f5fc7", size = 823130 },
+ { url = "https://files.pythonhosted.org/packages/db/60/1eeca2074f5b87df394fccaa432ae3fc06c9c9bfa97c5051aed70e6e00c2/regex-2024.11.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3cde6e9f2580eb1665965ce9bf17ff4952f34f5b126beb509fee8f4e994f143c", size = 796857 },
+ { url = "https://files.pythonhosted.org/packages/10/db/ac718a08fcee981554d2f7bb8402f1faa7e868c1345c16ab1ebec54b0d7b/regex-2024.11.6-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0d7f453dca13f40a02b79636a339c5b62b670141e63efd511d3f8f73fba162b3", size = 784006 },
+ { url = "https://files.pythonhosted.org/packages/c2/41/7da3fe70216cea93144bf12da2b87367590bcf07db97604edeea55dac9ad/regex-2024.11.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:59dfe1ed21aea057a65c6b586afd2a945de04fc7db3de0a6e3ed5397ad491b07", size = 781650 },
+ { url = "https://files.pythonhosted.org/packages/a7/d5/880921ee4eec393a4752e6ab9f0fe28009435417c3102fc413f3fe81c4e5/regex-2024.11.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b97c1e0bd37c5cd7902e65f410779d39eeda155800b65fc4d04cc432efa9bc6e", size = 789545 },
+ { url = "https://files.pythonhosted.org/packages/dc/96/53770115e507081122beca8899ab7f5ae28ae790bfcc82b5e38976df6a77/regex-2024.11.6-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f9d1e379028e0fc2ae3654bac3cbbef81bf3fd571272a42d56c24007979bafb6", size = 853045 },
+ { url = "https://files.pythonhosted.org/packages/31/d3/1372add5251cc2d44b451bd94f43b2ec78e15a6e82bff6a290ef9fd8f00a/regex-2024.11.6-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:13291b39131e2d002a7940fb176e120bec5145f3aeb7621be6534e46251912c4", size = 860182 },
+ { url = "https://files.pythonhosted.org/packages/ed/e3/c446a64984ea9f69982ba1a69d4658d5014bc7a0ea468a07e1a1265db6e2/regex-2024.11.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4f51f88c126370dcec4908576c5a627220da6c09d0bff31cfa89f2523843316d", size = 787733 },
+ { url = "https://files.pythonhosted.org/packages/2b/f1/e40c8373e3480e4f29f2692bd21b3e05f296d3afebc7e5dcf21b9756ca1c/regex-2024.11.6-cp313-cp313-win32.whl", hash = "sha256:63b13cfd72e9601125027202cad74995ab26921d8cd935c25f09c630436348ff", size = 262122 },
+ { url = "https://files.pythonhosted.org/packages/45/94/bc295babb3062a731f52621cdc992d123111282e291abaf23faa413443ea/regex-2024.11.6-cp313-cp313-win_amd64.whl", hash = "sha256:2b3361af3198667e99927da8b84c1b010752fa4b1115ee30beaa332cabc3ef1a", size = 273545 },
+]
+
+[[package]]
+name = "requests"
+version = "2.32.3"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "certifi" },
+ { name = "charset-normalizer" },
+ { name = "idna" },
+ { name = "urllib3" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928 },
+]
+
+[[package]]
+name = "requests-toolbelt"
+version = "1.0.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "requests" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/f3/61/d7545dafb7ac2230c70d38d31cbfe4cc64f7144dc41f6e4e4b78ecd9f5bb/requests-toolbelt-1.0.0.tar.gz", hash = "sha256:7681a0a3d047012b5bdc0ee37d7f8f07ebe76ab08caeccfc3921ce23c88d5bc6", size = 206888 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/3f/51/d4db610ef29373b879047326cbf6fa98b6c1969d6f6dc423279de2b1be2c/requests_toolbelt-1.0.0-py2.py3-none-any.whl", hash = "sha256:cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06", size = 54481 },
+]
+
+[[package]]
+name = "requirements-parser"
+version = "0.11.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "packaging" },
+ { name = "types-setuptools" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/05/70/80ed53ebd21853855aad552d4ed6c4934df62cd32fe9a3669fcdef59429c/requirements_parser-0.11.0.tar.gz", hash = "sha256:35f36dc969d14830bf459803da84f314dc3d17c802592e9e970f63d0359e5920", size = 23663 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/88/33/190393a7d36872e237cbc99e6c44d9a078a1ba7b406462fe6eafd5a28e04/requirements_parser-0.11.0-py3-none-any.whl", hash = "sha256:50379eb50311834386c2568263ae5225d7b9d0867fb55cf4ecc93959de2c2684", size = 14800 },
+]
+
+[[package]]
+name = "rich"
+version = "13.9.4"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "markdown-it-py" },
+ { name = "pygments" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/ab/3a/0316b28d0761c6734d6bc14e770d85506c986c85ffb239e688eeaab2c2bc/rich-13.9.4.tar.gz", hash = "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098", size = 223149 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/19/71/39c7c0d87f8d4e6c020a393182060eaefeeae6c01dab6a84ec346f2567df/rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90", size = 242424 },
+]
+
+[[package]]
+name = "rich-click"
+version = "1.8.5"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "click" },
+ { name = "rich" },
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/9a/31/103501e85e885e3e202c087fa612cfe450693210372766552ce1ab5b57b9/rich_click-1.8.5.tar.gz", hash = "sha256:a3eebe81da1c9da3c32f3810017c79bd687ff1b3fa35bfc9d8a3338797f1d1a1", size = 38229 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/aa/0b/e2de98c538c0ee9336211d260f88b7e69affab44969750aaca0b48a697c8/rich_click-1.8.5-py3-none-any.whl", hash = "sha256:0fab7bb5b66c15da17c210b4104277cd45f3653a7322e0098820a169880baee0", size = 35081 },
+]
+
+[[package]]
+name = "rich-toolkit"
+version = "0.13.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "click" },
+ { name = "rich" },
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/5b/8a/71cfbf6bf6257ea785d1f030c22468f763eea1b3e5417620f2ba9abd6dca/rich_toolkit-0.13.2.tar.gz", hash = "sha256:fea92557530de7c28f121cbed572ad93d9e0ddc60c3ca643f1b831f2f56b95d3", size = 72288 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/7e/1b/1c2f43af46456050b27810a7a013af8a7e12bc545a0cdc00eb0df55eb769/rich_toolkit-0.13.2-py3-none-any.whl", hash = "sha256:f3f6c583e5283298a2f7dbd3c65aca18b7f818ad96174113ab5bec0b0e35ed61", size = 13566 },
+]
+
+[[package]]
+name = "ruff"
+version = "0.9.6"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/2a/e1/e265aba384343dd8ddd3083f5e33536cd17e1566c41453a5517b5dd443be/ruff-0.9.6.tar.gz", hash = "sha256:81761592f72b620ec8fa1068a6fd00e98a5ebee342a3642efd84454f3031dca9", size = 3639454 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/76/e3/3d2c022e687e18cf5d93d6bfa2722d46afc64eaa438c7fbbdd603b3597be/ruff-0.9.6-py3-none-linux_armv6l.whl", hash = "sha256:2f218f356dd2d995839f1941322ff021c72a492c470f0b26a34f844c29cdf5ba", size = 11714128 },
+ { url = "https://files.pythonhosted.org/packages/e1/22/aff073b70f95c052e5c58153cba735748c9e70107a77d03420d7850710a0/ruff-0.9.6-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b908ff4df65dad7b251c9968a2e4560836d8f5487c2f0cc238321ed951ea0504", size = 11682539 },
+ { url = "https://files.pythonhosted.org/packages/75/a7/f5b7390afd98a7918582a3d256cd3e78ba0a26165a467c1820084587cbf9/ruff-0.9.6-py3-none-macosx_11_0_arm64.whl", hash = "sha256:b109c0ad2ececf42e75fa99dc4043ff72a357436bb171900714a9ea581ddef83", size = 11132512 },
+ { url = "https://files.pythonhosted.org/packages/a6/e3/45de13ef65047fea2e33f7e573d848206e15c715e5cd56095589a7733d04/ruff-0.9.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1de4367cca3dac99bcbd15c161404e849bb0bfd543664db39232648dc00112dc", size = 11929275 },
+ { url = "https://files.pythonhosted.org/packages/7d/f2/23d04cd6c43b2e641ab961ade8d0b5edb212ecebd112506188c91f2a6e6c/ruff-0.9.6-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac3ee4d7c2c92ddfdaedf0bf31b2b176fa7aa8950efc454628d477394d35638b", size = 11466502 },
+ { url = "https://files.pythonhosted.org/packages/b5/6f/3a8cf166f2d7f1627dd2201e6cbc4cb81f8b7d58099348f0c1ff7b733792/ruff-0.9.6-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5dc1edd1775270e6aa2386119aea692039781429f0be1e0949ea5884e011aa8e", size = 12676364 },
+ { url = "https://files.pythonhosted.org/packages/f5/c4/db52e2189983c70114ff2b7e3997e48c8318af44fe83e1ce9517570a50c6/ruff-0.9.6-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:4a091729086dffa4bd070aa5dab7e39cc6b9d62eb2bef8f3d91172d30d599666", size = 13335518 },
+ { url = "https://files.pythonhosted.org/packages/66/44/545f8a4d136830f08f4d24324e7db957c5374bf3a3f7a6c0bc7be4623a37/ruff-0.9.6-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d1bbc6808bf7b15796cef0815e1dfb796fbd383e7dbd4334709642649625e7c5", size = 12823287 },
+ { url = "https://files.pythonhosted.org/packages/c5/26/8208ef9ee7431032c143649a9967c3ae1aae4257d95e6f8519f07309aa66/ruff-0.9.6-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:589d1d9f25b5754ff230dce914a174a7c951a85a4e9270613a2b74231fdac2f5", size = 14592374 },
+ { url = "https://files.pythonhosted.org/packages/31/70/e917781e55ff39c5b5208bda384fd397ffd76605e68544d71a7e40944945/ruff-0.9.6-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc61dd5131742e21103fbbdcad683a8813be0e3c204472d520d9a5021ca8b217", size = 12500173 },
+ { url = "https://files.pythonhosted.org/packages/84/f5/e4ddee07660f5a9622a9c2b639afd8f3104988dc4f6ba0b73ffacffa9a8c/ruff-0.9.6-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:5e2d9126161d0357e5c8f30b0bd6168d2c3872372f14481136d13de9937f79b6", size = 11906555 },
+ { url = "https://files.pythonhosted.org/packages/f1/2b/6ff2fe383667075eef8656b9892e73dd9b119b5e3add51298628b87f6429/ruff-0.9.6-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:68660eab1a8e65babb5229a1f97b46e3120923757a68b5413d8561f8a85d4897", size = 11538958 },
+ { url = "https://files.pythonhosted.org/packages/3c/db/98e59e90de45d1eb46649151c10a062d5707b5b7f76f64eb1e29edf6ebb1/ruff-0.9.6-py3-none-musllinux_1_2_i686.whl", hash = "sha256:c4cae6c4cc7b9b4017c71114115db0445b00a16de3bcde0946273e8392856f08", size = 12117247 },
+ { url = "https://files.pythonhosted.org/packages/ec/bc/54e38f6d219013a9204a5a2015c09e7a8c36cedcd50a4b01ac69a550b9d9/ruff-0.9.6-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:19f505b643228b417c1111a2a536424ddde0db4ef9023b9e04a46ed8a1cb4656", size = 12554647 },
+ { url = "https://files.pythonhosted.org/packages/a5/7d/7b461ab0e2404293c0627125bb70ac642c2e8d55bf590f6fce85f508f1b2/ruff-0.9.6-py3-none-win32.whl", hash = "sha256:194d8402bceef1b31164909540a597e0d913c0e4952015a5b40e28c146121b5d", size = 9949214 },
+ { url = "https://files.pythonhosted.org/packages/ee/30/c3cee10f915ed75a5c29c1e57311282d1a15855551a64795c1b2bbe5cf37/ruff-0.9.6-py3-none-win_amd64.whl", hash = "sha256:03482d5c09d90d4ee3f40d97578423698ad895c87314c4de39ed2af945633caa", size = 10999914 },
+ { url = "https://files.pythonhosted.org/packages/e8/a8/d71f44b93e3aa86ae232af1f2126ca7b95c0f515ec135462b3e1f351441c/ruff-0.9.6-py3-none-win_arm64.whl", hash = "sha256:0e2bb706a2be7ddfea4a4af918562fdc1bcb16df255e5fa595bbd800ce322a5a", size = 10177499 },
+]
+
+[[package]]
+name = "rustworkx"
+version = "0.16.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "numpy" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/a5/c4/6d6ef39e57610d54c5f106dc3dece9eebce8b9d52d561ae092e3aede1b66/rustworkx-0.16.0.tar.gz", hash = "sha256:9f0dcb83f38d5ca2c3a683eb9b6951c8aec3262fbfe5141946a7ee5ba37e0bb6", size = 349524 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/f8/70/36f5916aee41ffe4f604ad75742eb1bb1b849fb568e010555f9d159cd93e/rustworkx-0.16.0-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:476a6c67b0142acd941691943750cc6737a48372304489969c2b62d30aaf4c27", size = 2141999 },
+ { url = "https://files.pythonhosted.org/packages/94/47/7e7c37fb73efcc87be6414b235534605c4008a4cdbd92a61db23b878eecd/rustworkx-0.16.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:bef2ef42870f806af93979b457e240f6dfa4f867ca33965c620f3a804409ed3a", size = 1940309 },
+ { url = "https://files.pythonhosted.org/packages/c6/42/a6d6b3137be55ef1d887becdf6b64b0917c7d437bd483065a88500a55603/rustworkx-0.16.0-cp39-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0db3a73bf68b3e66c08322a2fc95d3aa663d037d9b4e49c3509da4898d3529cc", size = 2195350 },
+ { url = "https://files.pythonhosted.org/packages/59/d2/1bc99df831c132c4b7420a85ce9150e065f4c993798f31b6a4229f238398/rustworkx-0.16.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f12a13d7486234fa2a84746d5e41f436bf9df43548043e7a232f48804ff8c61", size = 1971689 },
+ { url = "https://files.pythonhosted.org/packages/b5/3b/1125e7eb834f4408bcec3cee79947efd504c715fb7ab1876f8cd4bbca497/rustworkx-0.16.0-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:89efd5c3a4653ddacc55ca39f28b261d43deec7d678f8f8fc6b76b5087f1dfea", size = 3297342 },
+ { url = "https://files.pythonhosted.org/packages/4f/e2/e21187b255c6211d71db0d08a44fc16771038b2af41712d66c408d9bec16/rustworkx-0.16.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec0c12aac8c54910ace20ac6ada4b890cd39f95f69100514715f8ad7af9041e4", size = 2110107 },
+ { url = "https://files.pythonhosted.org/packages/3c/79/e3fcff21f31253ea85ef196bf2fcabad7802b11468f7d3a5d592cd0ac789/rustworkx-0.16.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:d650e39fc1a1534335f7517358ebfc3478bb235428463cfcd7c5750d50377b33", size = 2007544 },
+ { url = "https://files.pythonhosted.org/packages/67/04/741ed09c2b0dc0f360f85270c1179ed433785372ac9ab6ab26d3dd3ae02d/rustworkx-0.16.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:293180b83509ee9bff4c3af7ccc1024f6528d61b65d0cb7320bd31924f10cb71", size = 2172787 },
+ { url = "https://files.pythonhosted.org/packages/6d/fd/9c71e90f8cde76fed95dbc1e7d019977b89a29492f49ded232c6fad3055f/rustworkx-0.16.0-cp39-abi3-win32.whl", hash = "sha256:040c4368729cf502f756a3b0ff5f1c6915fc389f74dcc6afc6c3833688c97c01", size = 1840183 },
+ { url = "https://files.pythonhosted.org/packages/3e/79/9bdd52d2a33d468c81c1827de1b588080cb055d1d3561b194ab7bf2635b5/rustworkx-0.16.0-cp39-abi3-win_amd64.whl", hash = "sha256:905df608843c32fa45ac023687769fe13056edf7584474c801d5c50705d76e9b", size = 1953559 },
+]
+
+[[package]]
+name = "s3transfer"
+version = "0.11.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "botocore" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/62/45/2323b5928f86fd29f9afdcef4659f68fa73eaa5356912b774227f5cf46b5/s3transfer-0.11.2.tar.gz", hash = "sha256:3b39185cb72f5acc77db1a58b6e25b977f28d20496b6e58d6813d75f464d632f", size = 147885 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/1b/ac/e7dc469e49048dc57f62e0c555d2ee3117fa30813d2a1a2962cce3a2a82a/s3transfer-0.11.2-py3-none-any.whl", hash = "sha256:be6ecb39fadd986ef1701097771f87e4d2f821f27f6071c872143884d2950fbc", size = 84151 },
+]
+
+[[package]]
+name = "sentry-sdk"
+version = "2.20.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "certifi" },
+ { name = "urllib3" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/68/e8/6a366c0cd5e129dda6ecb20ff097f70b18182c248d4c27e813c21f98992a/sentry_sdk-2.20.0.tar.gz", hash = "sha256:afa82713a92facf847df3c6f63cec71eb488d826a50965def3d7722aa6f0fdab", size = 300125 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/e6/0f/6f7e6cd0f4a141752caef3f79300148422fdf2b8b68b531f30b2b0c0cbda/sentry_sdk-2.20.0-py2.py3-none-any.whl", hash = "sha256:c359a1edf950eb5e80cffd7d9111f3dbeef57994cb4415df37d39fda2cf22364", size = 322576 },
+]
+
+[[package]]
+name = "setuptools"
+version = "75.8.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/92/ec/089608b791d210aec4e7f97488e67ab0d33add3efccb83a056cbafe3a2a6/setuptools-75.8.0.tar.gz", hash = "sha256:c5afc8f407c626b8313a86e10311dd3f661c6cd9c09d4bf8c15c0e11f9f2b0e6", size = 1343222 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/69/8a/b9dc7678803429e4a3bc9ba462fa3dd9066824d3c607490235c6a796be5a/setuptools-75.8.0-py3-none-any.whl", hash = "sha256:e3982f444617239225d675215d51f6ba05f845d4eec313da4418fdbb56fb27e3", size = 1228782 },
+]
+
+[[package]]
+name = "setuptools-scm"
+version = "8.1.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "packaging" },
+ { name = "setuptools" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/4f/a4/00a9ac1b555294710d4a68d2ce8dfdf39d72aa4d769a7395d05218d88a42/setuptools_scm-8.1.0.tar.gz", hash = "sha256:42dea1b65771cba93b7a515d65a65d8246e560768a66b9106a592c8e7f26c8a7", size = 76465 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/a0/b9/1906bfeb30f2fc13bb39bf7ddb8749784c05faadbd18a21cf141ba37bff2/setuptools_scm-8.1.0-py3-none-any.whl", hash = "sha256:897a3226a6fd4a6eb2f068745e49733261a21f70b1bb28fce0339feb978d9af3", size = 43666 },
+]
+
+[[package]]
+name = "shellingham"
+version = "1.5.4"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755 },
+]
+
+[[package]]
+name = "sigtools"
+version = "4.0.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "attrs" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/5f/db/669ca14166814da187b3087b908ca924cf83f5b504fe23b3859a3ef67d4f/sigtools-4.0.1.tar.gz", hash = "sha256:4b8e135a9cd4d2ea00da670c093372d74e672ba3abb87f4c98d8e73dea54445c", size = 71910 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/1f/91/853dbf6ec096197dba9cd5fd0c836c5fc19142038b7db60ebe6332b1bab1/sigtools-4.0.1-py2.py3-none-any.whl", hash = "sha256:d216b4cf920bbab0fce636ddc429ed8463a5b533d9e1492acb45a2a1bc36ac6c", size = 76419 },
+]
+
+[[package]]
+name = "six"
+version = "1.17.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050 },
+]
+
+[[package]]
+name = "smmap"
+version = "5.0.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/44/cd/a040c4b3119bbe532e5b0732286f805445375489fceaec1f48306068ee3b/smmap-5.0.2.tar.gz", hash = "sha256:26ea65a03958fa0c8a1c7e8c7a58fdc77221b8910f6be2131affade476898ad5", size = 22329 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/04/be/d09147ad1ec7934636ad912901c5fd7667e1c858e19d355237db0d0cd5e4/smmap-5.0.2-py3-none-any.whl", hash = "sha256:b30115f0def7d7531d22a0fb6502488d879e75b260a9db4d0819cfb25403af5e", size = 24303 },
+]
+
+[[package]]
+name = "sniffio"
+version = "1.3.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235 },
+]
+
+[[package]]
+name = "sqlalchemy"
+version = "2.0.38"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "greenlet", marker = "(python_full_version < '3.14' and platform_machine == 'AMD64') or (python_full_version < '3.14' and platform_machine == 'WIN32') or (python_full_version < '3.14' and platform_machine == 'aarch64') or (python_full_version < '3.14' and platform_machine == 'amd64') or (python_full_version < '3.14' and platform_machine == 'ppc64le') or (python_full_version < '3.14' and platform_machine == 'win32') or (python_full_version < '3.14' and platform_machine == 'x86_64')" },
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/e4/08/9a90962ea72acd532bda71249a626344d855c4032603924b1b547694b837/sqlalchemy-2.0.38.tar.gz", hash = "sha256:e5a4d82bdb4bf1ac1285a68eab02d253ab73355d9f0fe725a97e1e0fa689decb", size = 9634782 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/5a/f8/6d0424af1442c989b655a7b5f608bc2ae5e4f94cdf6df9f6054f629dc587/SQLAlchemy-2.0.38-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:12d5b06a1f3aeccf295a5843c86835033797fea292c60e72b07bcb5d820e6dd3", size = 2104927 },
+ { url = "https://files.pythonhosted.org/packages/25/80/fc06e65fca0a19533e2bfab633a5633ed8b6ee0b9c8d580acf84609ce4da/SQLAlchemy-2.0.38-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e036549ad14f2b414c725349cce0772ea34a7ab008e9cd67f9084e4f371d1f32", size = 2095317 },
+ { url = "https://files.pythonhosted.org/packages/98/2d/5d66605f76b8e344813237dc160a01f03b987201e974b46056a7fb94a874/SQLAlchemy-2.0.38-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ee3bee874cb1fadee2ff2b79fc9fc808aa638670f28b2145074538d4a6a5028e", size = 3244735 },
+ { url = "https://files.pythonhosted.org/packages/73/8d/b0539e8dce90861efc38fea3eefb15a5d0cfeacf818614762e77a9f192f9/SQLAlchemy-2.0.38-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e185ea07a99ce8b8edfc788c586c538c4b1351007e614ceb708fd01b095ef33e", size = 3255581 },
+ { url = "https://files.pythonhosted.org/packages/ac/a5/94e1e44bf5bdffd1782807fcc072542b110b950f0be53f49e68b5f5eca1b/SQLAlchemy-2.0.38-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b79ee64d01d05a5476d5cceb3c27b5535e6bb84ee0f872ba60d9a8cd4d0e6579", size = 3190877 },
+ { url = "https://files.pythonhosted.org/packages/91/13/f08b09996dce945aec029c64f61c13b4788541ac588d9288e31e0d3d8850/SQLAlchemy-2.0.38-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:afd776cf1ebfc7f9aa42a09cf19feadb40a26366802d86c1fba080d8e5e74bdd", size = 3217485 },
+ { url = "https://files.pythonhosted.org/packages/13/8f/8cfe2ba5ba6d8090f4de0e658330c53be6b7bf430a8df1b141c2b180dcdf/SQLAlchemy-2.0.38-cp312-cp312-win32.whl", hash = "sha256:a5645cd45f56895cfe3ca3459aed9ff2d3f9aaa29ff7edf557fa7a23515a3725", size = 2075254 },
+ { url = "https://files.pythonhosted.org/packages/c2/5c/e3c77fae41862be1da966ca98eec7fbc07cdd0b00f8b3e1ef2a13eaa6cca/SQLAlchemy-2.0.38-cp312-cp312-win_amd64.whl", hash = "sha256:1052723e6cd95312f6a6eff9a279fd41bbae67633415373fdac3c430eca3425d", size = 2100865 },
+ { url = "https://files.pythonhosted.org/packages/21/77/caa875a1f5a8a8980b564cc0e6fee1bc992d62d29101252561d0a5e9719c/SQLAlchemy-2.0.38-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ecef029b69843b82048c5b347d8e6049356aa24ed644006c9a9d7098c3bd3bfd", size = 2100201 },
+ { url = "https://files.pythonhosted.org/packages/f4/ec/94bb036ec78bf9a20f8010c807105da9152dd84f72e8c51681ad2f30b3fd/SQLAlchemy-2.0.38-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9c8bcad7fc12f0cc5896d8e10fdf703c45bd487294a986903fe032c72201596b", size = 2090678 },
+ { url = "https://files.pythonhosted.org/packages/7b/61/63ff1893f146e34d3934c0860209fdd3925c25ee064330e6c2152bacc335/SQLAlchemy-2.0.38-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a0ef3f98175d77180ffdc623d38e9f1736e8d86b6ba70bff182a7e68bed7727", size = 3177107 },
+ { url = "https://files.pythonhosted.org/packages/a9/4f/b933bea41a602b5f274065cc824fae25780ed38664d735575192490a021b/SQLAlchemy-2.0.38-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8b0ac78898c50e2574e9f938d2e5caa8fe187d7a5b69b65faa1ea4648925b096", size = 3190435 },
+ { url = "https://files.pythonhosted.org/packages/f5/23/9e654b4059e385988de08c5d3b38a369ea042f4c4d7c8902376fd737096a/SQLAlchemy-2.0.38-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9eb4fa13c8c7a2404b6a8e3772c17a55b1ba18bc711e25e4d6c0c9f5f541b02a", size = 3123648 },
+ { url = "https://files.pythonhosted.org/packages/83/59/94c6d804e76ebc6412a08d2b086a8cb3e5a056cd61508e18ddaf3ec70100/SQLAlchemy-2.0.38-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5dba1cdb8f319084f5b00d41207b2079822aa8d6a4667c0f369fce85e34b0c86", size = 3151789 },
+ { url = "https://files.pythonhosted.org/packages/b2/27/17f143013aabbe1256dce19061eafdce0b0142465ce32168cdb9a18c04b1/SQLAlchemy-2.0.38-cp313-cp313-win32.whl", hash = "sha256:eae27ad7580529a427cfdd52c87abb2dfb15ce2b7a3e0fc29fbb63e2ed6f8120", size = 2073023 },
+ { url = "https://files.pythonhosted.org/packages/e2/3e/259404b03c3ed2e7eee4c179e001a07d9b61070334be91124cf4ad32eec7/SQLAlchemy-2.0.38-cp313-cp313-win_amd64.whl", hash = "sha256:b335a7c958bc945e10c522c069cd6e5804f4ff20f9a744dd38e748eb602cbbda", size = 2096908 },
+ { url = "https://files.pythonhosted.org/packages/aa/e4/592120713a314621c692211eba034d09becaf6bc8848fabc1dc2a54d8c16/SQLAlchemy-2.0.38-py3-none-any.whl", hash = "sha256:63178c675d4c80def39f1febd625a6333f44c0ba269edd8a468b156394b27753", size = 1896347 },
+]
+
+[[package]]
+name = "starlette"
+version = "0.45.3"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "anyio" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/ff/fb/2984a686808b89a6781526129a4b51266f678b2d2b97ab2d325e56116df8/starlette-0.45.3.tar.gz", hash = "sha256:2cbcba2a75806f8a41c722141486f37c28e30a0921c5f6fe4346cb0dcee1302f", size = 2574076 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/d9/61/f2b52e107b1fc8944b33ef56bf6ac4ebbe16d91b94d2b87ce013bf63fb84/starlette-0.45.3-py3-none-any.whl", hash = "sha256:dfb6d332576f136ec740296c7e8bb8c8a7125044e7c6da30744718880cdd059d", size = 71507 },
+]
+
+[[package]]
+name = "synchronicity"
+version = "0.9.11"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "sigtools" },
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/b5/52/f34a9ab6d514e0808d0f572affb360411d596b3439107318c00889277dd6/synchronicity-0.9.11.tar.gz", hash = "sha256:cb5dbbcb43d637e516ae50db05a776da51a705d1e1a9c0e301f6049afc3c2cae", size = 50323 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/f2/d5/7675cd9b8e18f05b9ea261acad5d197fcb8027d2a65b1a750427ec084593/synchronicity-0.9.11-py3-none-any.whl", hash = "sha256:231129654d2f56b1aa148e85ebd8545231be135771f6d2196d414175b1594ef6", size = 36827 },
+]
+
+[[package]]
+name = "tabulate"
+version = "0.9.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/ec/fe/802052aecb21e3797b8f7902564ab6ea0d60ff8ca23952079064155d1ae1/tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c", size = 81090 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/40/44/4a5f08c96eb108af5cb50b41f76142f0afa346dfa99d5296fe7202a11854/tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f", size = 35252 },
+]
+
+[[package]]
+name = "tenacity"
+version = "9.0.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/cd/94/91fccdb4b8110642462e653d5dcb27e7b674742ad68efd146367da7bdb10/tenacity-9.0.0.tar.gz", hash = "sha256:807f37ca97d62aa361264d497b0e31e92b8027044942bfa756160d908320d73b", size = 47421 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/b6/cb/b86984bed139586d01532a587464b5805f12e397594f19f931c4c2fbfa61/tenacity-9.0.0-py3-none-any.whl", hash = "sha256:93de0c98785b27fcf659856aa9f54bfbd399e29969b0621bc7f762bd441b4539", size = 28169 },
+]
+
+[[package]]
+name = "termcolor"
+version = "2.5.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/37/72/88311445fd44c455c7d553e61f95412cf89054308a1aa2434ab835075fc5/termcolor-2.5.0.tar.gz", hash = "sha256:998d8d27da6d48442e8e1f016119076b690d962507531df4890fcd2db2ef8a6f", size = 13057 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/7f/be/df630c387a0a054815d60be6a97eb4e8f17385d5d6fe660e1c02750062b4/termcolor-2.5.0-py3-none-any.whl", hash = "sha256:37b17b5fc1e604945c2642c872a3764b5d547a48009871aea3edd3afa180afb8", size = 7755 },
+]
+
+[[package]]
+name = "tiktoken"
+version = "0.8.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "regex" },
+ { name = "requests" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/37/02/576ff3a6639e755c4f70997b2d315f56d6d71e0d046f4fb64cb81a3fb099/tiktoken-0.8.0.tar.gz", hash = "sha256:9ccbb2740f24542534369c5635cfd9b2b3c2490754a78ac8831d99f89f94eeb2", size = 35107 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c1/22/34b2e136a6f4af186b6640cbfd6f93400783c9ef6cd550d9eab80628d9de/tiktoken-0.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:881839cfeae051b3628d9823b2e56b5cc93a9e2efb435f4cf15f17dc45f21586", size = 1039357 },
+ { url = "https://files.pythonhosted.org/packages/04/d2/c793cf49c20f5855fd6ce05d080c0537d7418f22c58e71f392d5e8c8dbf7/tiktoken-0.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fe9399bdc3f29d428f16a2f86c3c8ec20be3eac5f53693ce4980371c3245729b", size = 982616 },
+ { url = "https://files.pythonhosted.org/packages/b3/a1/79846e5ef911cd5d75c844de3fa496a10c91b4b5f550aad695c5df153d72/tiktoken-0.8.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9a58deb7075d5b69237a3ff4bb51a726670419db6ea62bdcd8bd80c78497d7ab", size = 1144011 },
+ { url = "https://files.pythonhosted.org/packages/26/32/e0e3a859136e95c85a572e4806dc58bf1ddf651108ae8b97d5f3ebe1a244/tiktoken-0.8.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2908c0d043a7d03ebd80347266b0e58440bdef5564f84f4d29fb235b5df3b04", size = 1175432 },
+ { url = "https://files.pythonhosted.org/packages/c7/89/926b66e9025b97e9fbabeaa59048a736fe3c3e4530a204109571104f921c/tiktoken-0.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:294440d21a2a51e12d4238e68a5972095534fe9878be57d905c476017bff99fc", size = 1236576 },
+ { url = "https://files.pythonhosted.org/packages/45/e2/39d4aa02a52bba73b2cd21ba4533c84425ff8786cc63c511d68c8897376e/tiktoken-0.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:d8f3192733ac4d77977432947d563d7e1b310b96497acd3c196c9bddb36ed9db", size = 883824 },
+ { url = "https://files.pythonhosted.org/packages/e3/38/802e79ba0ee5fcbf240cd624143f57744e5d411d2e9d9ad2db70d8395986/tiktoken-0.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:02be1666096aff7da6cbd7cdaa8e7917bfed3467cd64b38b1f112e96d3b06a24", size = 1039648 },
+ { url = "https://files.pythonhosted.org/packages/b1/da/24cdbfc302c98663fbea66f5866f7fa1048405c7564ab88483aea97c3b1a/tiktoken-0.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c94ff53c5c74b535b2cbf431d907fc13c678bbd009ee633a2aca269a04389f9a", size = 982763 },
+ { url = "https://files.pythonhosted.org/packages/e4/f0/0ecf79a279dfa41fc97d00adccf976ecc2556d3c08ef3e25e45eb31f665b/tiktoken-0.8.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b231f5e8982c245ee3065cd84a4712d64692348bc609d84467c57b4b72dcbc5", size = 1144417 },
+ { url = "https://files.pythonhosted.org/packages/ab/d3/155d2d4514f3471a25dc1d6d20549ef254e2aa9bb5b1060809b1d3b03d3a/tiktoken-0.8.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4177faa809bd55f699e88c96d9bb4635d22e3f59d635ba6fd9ffedf7150b9953", size = 1175108 },
+ { url = "https://files.pythonhosted.org/packages/19/eb/5989e16821ee8300ef8ee13c16effc20dfc26c777d05fbb6825e3c037b81/tiktoken-0.8.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5376b6f8dc4753cd81ead935c5f518fa0fbe7e133d9e25f648d8c4dabdd4bad7", size = 1236520 },
+ { url = "https://files.pythonhosted.org/packages/40/59/14b20465f1d1cb89cfbc96ec27e5617b2d41c79da12b5e04e96d689be2a7/tiktoken-0.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:18228d624807d66c87acd8f25fc135665617cab220671eb65b50f5d70fa51f69", size = 883849 },
+]
+
+[[package]]
+name = "tokenizers"
+version = "0.21.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "huggingface-hub" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/20/41/c2be10975ca37f6ec40d7abd7e98a5213bb04f284b869c1a24e6504fd94d/tokenizers-0.21.0.tar.gz", hash = "sha256:ee0894bf311b75b0c03079f33859ae4b2334d675d4e93f5a4132e1eae2834fe4", size = 343021 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/b0/5c/8b09607b37e996dc47e70d6a7b6f4bdd4e4d5ab22fe49d7374565c7fefaf/tokenizers-0.21.0-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:3c4c93eae637e7d2aaae3d376f06085164e1660f89304c0ab2b1d08a406636b2", size = 2647461 },
+ { url = "https://files.pythonhosted.org/packages/22/7a/88e58bb297c22633ed1c9d16029316e5b5ac5ee44012164c2edede599a5e/tokenizers-0.21.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:f53ea537c925422a2e0e92a24cce96f6bc5046bbef24a1652a5edc8ba975f62e", size = 2563639 },
+ { url = "https://files.pythonhosted.org/packages/f7/14/83429177c19364df27d22bc096d4c2e431e0ba43e56c525434f1f9b0fd00/tokenizers-0.21.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b177fb54c4702ef611de0c069d9169f0004233890e0c4c5bd5508ae05abf193", size = 2903304 },
+ { url = "https://files.pythonhosted.org/packages/7e/db/3433eab42347e0dc5452d8fcc8da03f638c9accffefe5a7c78146666964a/tokenizers-0.21.0-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6b43779a269f4629bebb114e19c3fca0223296ae9fea8bb9a7a6c6fb0657ff8e", size = 2804378 },
+ { url = "https://files.pythonhosted.org/packages/57/8b/7da5e6f89736c2ade02816b4733983fca1c226b0c42980b1ae9dc8fcf5cc/tokenizers-0.21.0-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9aeb255802be90acfd363626753fda0064a8df06031012fe7d52fd9a905eb00e", size = 3095488 },
+ { url = "https://files.pythonhosted.org/packages/4d/f6/5ed6711093dc2c04a4e03f6461798b12669bc5a17c8be7cce1240e0b5ce8/tokenizers-0.21.0-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d8b09dbeb7a8d73ee204a70f94fc06ea0f17dcf0844f16102b9f414f0b7463ba", size = 3121410 },
+ { url = "https://files.pythonhosted.org/packages/81/42/07600892d48950c5e80505b81411044a2d969368cdc0d929b1c847bf6697/tokenizers-0.21.0-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:400832c0904f77ce87c40f1a8a27493071282f785724ae62144324f171377273", size = 3388821 },
+ { url = "https://files.pythonhosted.org/packages/22/06/69d7ce374747edaf1695a4f61b83570d91cc8bbfc51ccfecf76f56ab4aac/tokenizers-0.21.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e84ca973b3a96894d1707e189c14a774b701596d579ffc7e69debfc036a61a04", size = 3008868 },
+ { url = "https://files.pythonhosted.org/packages/c8/69/54a0aee4d576045b49a0eb8bffdc495634309c823bf886042e6f46b80058/tokenizers-0.21.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:eb7202d231b273c34ec67767378cd04c767e967fda12d4a9e36208a34e2f137e", size = 8975831 },
+ { url = "https://files.pythonhosted.org/packages/f7/f3/b776061e4f3ebf2905ba1a25d90380aafd10c02d406437a8ba22d1724d76/tokenizers-0.21.0-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:089d56db6782a73a27fd8abf3ba21779f5b85d4a9f35e3b493c7bbcbbf0d539b", size = 8920746 },
+ { url = "https://files.pythonhosted.org/packages/d8/ee/ce83d5ec8b6844ad4c3ecfe3333d58ecc1adc61f0878b323a15355bcab24/tokenizers-0.21.0-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:c87ca3dc48b9b1222d984b6b7490355a6fdb411a2d810f6f05977258400ddb74", size = 9161814 },
+ { url = "https://files.pythonhosted.org/packages/18/07/3e88e65c0ed28fa93aa0c4d264988428eef3df2764c3126dc83e243cb36f/tokenizers-0.21.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:4145505a973116f91bc3ac45988a92e618a6f83eb458f49ea0790df94ee243ff", size = 9357138 },
+ { url = "https://files.pythonhosted.org/packages/15/b0/dc4572ca61555fc482ebc933f26cb407c6aceb3dc19c301c68184f8cad03/tokenizers-0.21.0-cp39-abi3-win32.whl", hash = "sha256:eb1702c2f27d25d9dd5b389cc1f2f51813e99f8ca30d9e25348db6585a97e24a", size = 2202266 },
+ { url = "https://files.pythonhosted.org/packages/44/69/d21eb253fa91622da25585d362a874fa4710be600f0ea9446d8d0217cec1/tokenizers-0.21.0-cp39-abi3-win_amd64.whl", hash = "sha256:87841da5a25a3a5f70c102de371db120f41873b854ba65e52bccd57df5a3780c", size = 2389192 },
+]
+
+[[package]]
+name = "toml"
+version = "0.10.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f", size = 22253 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", size = 16588 },
+]
+
+[[package]]
+name = "tomlkit"
+version = "0.13.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/b1/09/a439bec5888f00a54b8b9f05fa94d7f901d6735ef4e55dcec9bc37b5d8fa/tomlkit-0.13.2.tar.gz", hash = "sha256:fff5fe59a87295b278abd31bec92c15d9bc4a06885ab12bcea52c71119392e79", size = 192885 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/f9/b6/a447b5e4ec71e13871be01ba81f5dfc9d0af7e473da256ff46bc0e24026f/tomlkit-0.13.2-py3-none-any.whl", hash = "sha256:7a974427f6e119197f670fbbbeae7bef749a6c14e793db934baefc1b5f03efde", size = 37955 },
+]
+
+[[package]]
+name = "tox"
+version = "4.24.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "cachetools" },
+ { name = "chardet" },
+ { name = "colorama" },
+ { name = "filelock" },
+ { name = "packaging" },
+ { name = "platformdirs" },
+ { name = "pluggy" },
+ { name = "pyproject-api" },
+ { name = "virtualenv" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/cf/7b/97f757e159983737bdd8fb513f4c263cd411a846684814ed5433434a1fa9/tox-4.24.1.tar.gz", hash = "sha256:083a720adbc6166fff0b7d1df9d154f9d00bfccb9403b8abf6bc0ee435d6a62e", size = 194742 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/ab/04/b0d1c1b44c98583cab9eabb4acdba964fdf6b6c597c53cfb8870fd08cbbf/tox-4.24.1-py3-none-any.whl", hash = "sha256:57ba7df7d199002c6df8c2db9e6484f3de6ca8f42013c083ea2d4d1e5c6bdc75", size = 171829 },
+]
+
+[[package]]
+name = "tox-uv"
+version = "1.23.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "packaging" },
+ { name = "tox" },
+ { name = "uv" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/9b/bf/88041224a87804774d321e2b0caaf38b4705fcf62d7c272d1bb8c2d18e80/tox_uv-1.23.0.tar.gz", hash = "sha256:37b32014b5e0154f275f0868d05c666454accee1acb839da02901009dfbe2702", size = 19440 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/68/f6/f9cf2584e3b19b5b3523147b257aee54f039e48888e5f883147952d5570c/tox_uv-1.23.0-py3-none-any.whl", hash = "sha256:5ca40a3d2fe52c5c0ab4dd639309d8763d9ff5665a00fec6a1299f437b9b612f", size = 14941 },
+]
+
+[[package]]
+name = "tqdm"
+version = "4.67.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "colorama", marker = "sys_platform == 'win32'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/a8/4b/29b4ef32e036bb34e4ab51796dd745cdba7ed47ad142a9f4a1eb8e0c744d/tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2", size = 169737 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", size = 78540 },
+]
+
+[[package]]
+name = "tree-sitter"
+version = "0.24.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/a7/a2/698b9d31d08ad5558f8bfbfe3a0781bd4b1f284e89bde3ad18e05101a892/tree-sitter-0.24.0.tar.gz", hash = "sha256:abd95af65ca2f4f7eca356343391ed669e764f37748b5352946f00f7fc78e734", size = 168304 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/e9/57/3a590f287b5aa60c07d5545953912be3d252481bf5e178f750db75572bff/tree_sitter-0.24.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:14beeff5f11e223c37be7d5d119819880601a80d0399abe8c738ae2288804afc", size = 140788 },
+ { url = "https://files.pythonhosted.org/packages/61/0b/fc289e0cba7dbe77c6655a4dd949cd23c663fd62a8b4d8f02f97e28d7fe5/tree_sitter-0.24.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:26a5b130f70d5925d67b47db314da209063664585a2fd36fa69e0717738efaf4", size = 133945 },
+ { url = "https://files.pythonhosted.org/packages/86/d7/80767238308a137e0b5b5c947aa243e3c1e3e430e6d0d5ae94b9a9ffd1a2/tree_sitter-0.24.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5fc5c3c26d83c9d0ecb4fc4304fba35f034b7761d35286b936c1db1217558b4e", size = 564819 },
+ { url = "https://files.pythonhosted.org/packages/bf/b3/6c5574f4b937b836601f5fb556b24804b0a6341f2eb42f40c0e6464339f4/tree_sitter-0.24.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:772e1bd8c0931c866b848d0369b32218ac97c24b04790ec4b0e409901945dd8e", size = 579303 },
+ { url = "https://files.pythonhosted.org/packages/0a/f4/bd0ddf9abe242ea67cca18a64810f8af230fc1ea74b28bb702e838ccd874/tree_sitter-0.24.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:24a8dd03b0d6b8812425f3b84d2f4763322684e38baf74e5bb766128b5633dc7", size = 581054 },
+ { url = "https://files.pythonhosted.org/packages/8c/1c/ff23fa4931b6ef1bbeac461b904ca7e49eaec7e7e5398584e3eef836ec96/tree_sitter-0.24.0-cp312-cp312-win_amd64.whl", hash = "sha256:f9e8b1605ab60ed43803100f067eed71b0b0e6c1fb9860a262727dbfbbb74751", size = 120221 },
+ { url = "https://files.pythonhosted.org/packages/b2/2a/9979c626f303177b7612a802237d0533155bf1e425ff6f73cc40f25453e2/tree_sitter-0.24.0-cp312-cp312-win_arm64.whl", hash = "sha256:f733a83d8355fc95561582b66bbea92ffd365c5d7a665bc9ebd25e049c2b2abb", size = 108234 },
+ { url = "https://files.pythonhosted.org/packages/61/cd/2348339c85803330ce38cee1c6cbbfa78a656b34ff58606ebaf5c9e83bd0/tree_sitter-0.24.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0d4a6416ed421c4210f0ca405a4834d5ccfbb8ad6692d4d74f7773ef68f92071", size = 140781 },
+ { url = "https://files.pythonhosted.org/packages/8b/a3/1ea9d8b64e8dcfcc0051028a9c84a630301290995cd6e947bf88267ef7b1/tree_sitter-0.24.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e0992d483677e71d5c5d37f30dfb2e3afec2f932a9c53eec4fca13869b788c6c", size = 133928 },
+ { url = "https://files.pythonhosted.org/packages/fe/ae/55c1055609c9428a4aedf4b164400ab9adb0b1bf1538b51f4b3748a6c983/tree_sitter-0.24.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:57277a12fbcefb1c8b206186068d456c600dbfbc3fd6c76968ee22614c5cd5ad", size = 564497 },
+ { url = "https://files.pythonhosted.org/packages/ce/d0/f2ffcd04882c5aa28d205a787353130cbf84b2b8a977fd211bdc3b399ae3/tree_sitter-0.24.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d25fa22766d63f73716c6fec1a31ee5cf904aa429484256bd5fdf5259051ed74", size = 578917 },
+ { url = "https://files.pythonhosted.org/packages/af/82/aebe78ea23a2b3a79324993d4915f3093ad1af43d7c2208ee90be9273273/tree_sitter-0.24.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7d5d9537507e1c8c5fa9935b34f320bfec4114d675e028f3ad94f11cf9db37b9", size = 581148 },
+ { url = "https://files.pythonhosted.org/packages/a1/b4/6b0291a590c2b0417cfdb64ccb8ea242f270a46ed429c641fbc2bfab77e0/tree_sitter-0.24.0-cp313-cp313-win_amd64.whl", hash = "sha256:f58bb4956917715ec4d5a28681829a8dad5c342cafd4aea269f9132a83ca9b34", size = 120207 },
+ { url = "https://files.pythonhosted.org/packages/a8/18/542fd844b75272630229c9939b03f7db232c71a9d82aadc59c596319ea6a/tree_sitter-0.24.0-cp313-cp313-win_arm64.whl", hash = "sha256:23641bd25dcd4bb0b6fa91b8fb3f46cc9f1c9f475efe4d536d3f1f688d1b84c8", size = 108232 },
+]
+
+[[package]]
+name = "tree-sitter-javascript"
+version = "0.23.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/cd/dc/1c55c33cc6bbe754359b330534cf9f261c1b9b2c26ddf23aef3c5fa67759/tree_sitter_javascript-0.23.1.tar.gz", hash = "sha256:b2059ce8b150162cda05a457ca3920450adbf915119c04b8c67b5241cd7fcfed", size = 110058 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/20/d3/c67d7d49967344b51208ad19f105233be1afdf07d3dcb35b471900265227/tree_sitter_javascript-0.23.1-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:6ca583dad4bd79d3053c310b9f7208cd597fd85f9947e4ab2294658bb5c11e35", size = 59333 },
+ { url = "https://files.pythonhosted.org/packages/a5/db/ea0ee1547679d1750e80a0c4bc60b3520b166eeaf048764cfdd1ba3fd5e5/tree_sitter_javascript-0.23.1-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:94100e491a6a247aa4d14caf61230c171b6376c863039b6d9cd71255c2d815ec", size = 61071 },
+ { url = "https://files.pythonhosted.org/packages/67/6e/07c4857e08be37bfb55bfb269863df8ec908b2f6a3f1893cd852b893ecab/tree_sitter_javascript-0.23.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5a6bc1055b061c5055ec58f39ee9b2e9efb8e6e0ae970838af74da0afb811f0a", size = 96999 },
+ { url = "https://files.pythonhosted.org/packages/5f/f5/4de730afe8b9422845bc2064020a8a8f49ebd1695c04261c38d1b3e3edec/tree_sitter_javascript-0.23.1-cp39-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:056dc04fb6b24293f8c5fec43c14e7e16ba2075b3009c643abf8c85edc4c7c3c", size = 94020 },
+ { url = "https://files.pythonhosted.org/packages/77/0a/f980520da86c4eff8392867840a945578ef43372c9d4a37922baa6b121fe/tree_sitter_javascript-0.23.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a11ca1c0f736da42967586b568dff8a465ee148a986c15ebdc9382806e0ce871", size = 92927 },
+ { url = "https://files.pythonhosted.org/packages/ff/5c/36a98d512aa1d1082409d6b7eda5d26b820bd4477a54100ad9f62212bc55/tree_sitter_javascript-0.23.1-cp39-abi3-win_amd64.whl", hash = "sha256:041fa22b34250ea6eb313d33104d5303f79504cb259d374d691e38bbdc49145b", size = 58824 },
+ { url = "https://files.pythonhosted.org/packages/dc/79/ceb21988e6de615355a63eebcf806cd2a0fe875bec27b429d58b63e7fb5f/tree_sitter_javascript-0.23.1-cp39-abi3-win_arm64.whl", hash = "sha256:eb28130cd2fb30d702d614cbf61ef44d1c7f6869e7d864a9cc17111e370be8f7", size = 57027 },
+]
+
+[[package]]
+name = "tree-sitter-python"
+version = "0.23.6"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/1c/30/6766433b31be476fda6569a3a374c2220e45ffee0bff75460038a57bf23b/tree_sitter_python-0.23.6.tar.gz", hash = "sha256:354bfa0a2f9217431764a631516f85173e9711af2c13dbd796a8815acfe505d9", size = 155868 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/ab/67/577a02acae5f776007c924ca86ef14c19c12e71de0aa9d2a036f3c248e7b/tree_sitter_python-0.23.6-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:28fbec8f74eeb2b30292d97715e60fac9ccf8a8091ce19b9d93e9b580ed280fb", size = 74361 },
+ { url = "https://files.pythonhosted.org/packages/d2/a6/194b3625a7245c532ad418130d63077ce6cd241152524152f533e4d6edb0/tree_sitter_python-0.23.6-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:680b710051b144fedf61c95197db0094f2245e82551bf7f0c501356333571f7a", size = 76436 },
+ { url = "https://files.pythonhosted.org/packages/d0/62/1da112689d6d282920e62c40e67ab39ea56463b0e7167bfc5e81818a770e/tree_sitter_python-0.23.6-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8a9dcef55507b6567207e8ee0a6b053d0688019b47ff7f26edc1764b7f4dc0a4", size = 112060 },
+ { url = "https://files.pythonhosted.org/packages/5d/62/c9358584c96e38318d69b6704653684fd8467601f7b74e88aa44f4e6903f/tree_sitter_python-0.23.6-cp39-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:29dacdc0cd2f64e55e61d96c6906533ebb2791972bec988450c46cce60092f5d", size = 112338 },
+ { url = "https://files.pythonhosted.org/packages/1a/58/c5e61add45e34fb8ecbf057c500bae9d96ed7c9ca36edb7985da8ae45526/tree_sitter_python-0.23.6-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:7e048733c36f564b379831689006801feb267d8194f9e793fbb395ef1723335d", size = 109382 },
+ { url = "https://files.pythonhosted.org/packages/e9/f3/9b30893cae9b3811fe652dc6f90aaadfda12ae0b2757f5722fc7266f423c/tree_sitter_python-0.23.6-cp39-abi3-win_amd64.whl", hash = "sha256:a24027248399fb41594b696f929f9956828ae7cc85596d9f775e6c239cd0c2be", size = 75904 },
+ { url = "https://files.pythonhosted.org/packages/87/cb/ce35a65f83a47b510d8a2f1eddf3bdbb0d57aabc87351c8788caf3309f76/tree_sitter_python-0.23.6-cp39-abi3-win_arm64.whl", hash = "sha256:71334371bd73d5fe080aed39fbff49ed8efb9506edebe16795b0c7567ed6a272", size = 73649 },
+]
+
+[[package]]
+name = "tree-sitter-typescript"
+version = "0.23.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/1e/fc/bb52958f7e399250aee093751e9373a6311cadbe76b6e0d109b853757f35/tree_sitter_typescript-0.23.2.tar.gz", hash = "sha256:7b167b5827c882261cb7a50dfa0fb567975f9b315e87ed87ad0a0a3aedb3834d", size = 773053 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/28/95/4c00680866280e008e81dd621fd4d3f54aa3dad1b76b857a19da1b2cc426/tree_sitter_typescript-0.23.2-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:3cd752d70d8e5371fdac6a9a4df9d8924b63b6998d268586f7d374c9fba2a478", size = 286677 },
+ { url = "https://files.pythonhosted.org/packages/8f/2f/1f36fda564518d84593f2740d5905ac127d590baf5c5753cef2a88a89c15/tree_sitter_typescript-0.23.2-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:c7cc1b0ff5d91bac863b0e38b1578d5505e718156c9db577c8baea2557f66de8", size = 302008 },
+ { url = "https://files.pythonhosted.org/packages/96/2d/975c2dad292aa9994f982eb0b69cc6fda0223e4b6c4ea714550477d8ec3a/tree_sitter_typescript-0.23.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b1eed5b0b3a8134e86126b00b743d667ec27c63fc9de1b7bb23168803879e31", size = 351987 },
+ { url = "https://files.pythonhosted.org/packages/49/d1/a71c36da6e2b8a4ed5e2970819b86ef13ba77ac40d9e333cb17df6a2c5db/tree_sitter_typescript-0.23.2-cp39-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e96d36b85bcacdeb8ff5c2618d75593ef12ebaf1b4eace3477e2bdb2abb1752c", size = 344960 },
+ { url = "https://files.pythonhosted.org/packages/7f/cb/f57b149d7beed1a85b8266d0c60ebe4c46e79c9ba56bc17b898e17daf88e/tree_sitter_typescript-0.23.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:8d4f0f9bcb61ad7b7509d49a1565ff2cc363863644a234e1e0fe10960e55aea0", size = 340245 },
+ { url = "https://files.pythonhosted.org/packages/8b/ab/dd84f0e2337296a5f09749f7b5483215d75c8fa9e33738522e5ed81f7254/tree_sitter_typescript-0.23.2-cp39-abi3-win_amd64.whl", hash = "sha256:3f730b66396bc3e11811e4465c41ee45d9e9edd6de355a58bbbc49fa770da8f9", size = 278015 },
+ { url = "https://files.pythonhosted.org/packages/9f/e4/81f9a935789233cf412a0ed5fe04c883841d2c8fb0b7e075958a35c65032/tree_sitter_typescript-0.23.2-cp39-abi3-win_arm64.whl", hash = "sha256:05db58f70b95ef0ea126db5560f3775692f609589ed6f8dd0af84b7f19f1cbb7", size = 274052 },
+]
+
+[[package]]
+name = "trove-classifiers"
+version = "2025.1.15.22"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/f3/cb/8f6a91c74049180e395590901834d68bef5d6a2ce4c9ca9792cfadc1b9b4/trove_classifiers-2025.1.15.22.tar.gz", hash = "sha256:90af74358d3a01b3532bc7b3c88d8c6a094c2fd50a563d13d9576179326d7ed9", size = 16236 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/2b/c5/6422dbc59954389b20b2aba85b737ab4a552e357e7ea14b52f40312e7c84/trove_classifiers-2025.1.15.22-py3-none-any.whl", hash = "sha256:5f19c789d4f17f501d36c94dbbf969fb3e8c2784d008e6f5164dd2c3d6a2b07c", size = 13610 },
+]
+
+[[package]]
+name = "typer"
+version = "0.15.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "click" },
+ { name = "rich" },
+ { name = "shellingham" },
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/cb/ce/dca7b219718afd37a0068f4f2530a727c2b74a8b6e8e0c0080a4c0de4fcd/typer-0.15.1.tar.gz", hash = "sha256:a0588c0a7fa68a1978a069818657778f86abe6ff5ea6abf472f940a08bfe4f0a", size = 99789 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/d0/cc/0a838ba5ca64dc832aa43f727bd586309846b0ffb2ce52422543e6075e8a/typer-0.15.1-py3-none-any.whl", hash = "sha256:7994fb7b8155b64d3402518560648446072864beefd44aa2dc36972a5972e847", size = 44908 },
+]
+
+[[package]]
+name = "types-awscrt"
+version = "0.23.10"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/a3/53/7c69677327794fe91cc89a1362400b78f00b1a20364384da1e004c259d42/types_awscrt-0.23.10.tar.gz", hash = "sha256:965659260599b421564204b895467684104a2c0311bbacfd3c2423b8b0d3f3e9", size = 15455 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/89/ad/3d7c9a8b972048f3987355e3e48da56eb9f3ed8e151113c3c973b43ad91e/types_awscrt-0.23.10-py3-none-any.whl", hash = "sha256:7391bf502f6093221e68da8fb6a2af7ec67a98d376c58d5b76cc3938f449d121", size = 19426 },
+]
+
+[[package]]
+name = "types-boto3"
+version = "1.36.21"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "botocore-stubs" },
+ { name = "types-s3transfer" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/a4/42/8a589a425b8883bf7e4539abff02a69045a075fc8e3f0fef9ccd483f7772/types_boto3-1.36.21.tar.gz", hash = "sha256:18a4654942457421ca96b371fe67869ff8762c88eb5c638be482440a06155ef0", size = 99101 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c7/7d/cb8569ee5a33daf2ed77fc5684306452c142d437a597ff264f6834c1d37d/types_boto3-1.36.21-py3-none-any.whl", hash = "sha256:a430c3054d4280bf67b8ac76f5270473f2a1432039100c9c1a54f136366a3e0a", size = 68184 },
+]
+
+[package.optional-dependencies]
+s3 = [
+ { name = "types-boto3-s3" },
+]
+
+[[package]]
+name = "types-boto3-s3"
+version = "1.36.21"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/6c/8f/d20248ed80dee1665fe8024a506ee97750a3ca4aaf1b85978bc6fa7be9a5/types_boto3_s3-1.36.21.tar.gz", hash = "sha256:2c6795508c64470c661be18ce5422939126c615dd894b4b01fbc9710a130f173", size = 73258 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/88/b8/15e2901810846759b1b169d9ae35ba194c883886fc2068f9a2801c99a583/types_boto3_s3-1.36.21-py3-none-any.whl", hash = "sha256:d58fc4fac8acddf4d65cb084220f4d60af36d418bad2f07f6412d948572eba74", size = 80057 },
+]
+
+[[package]]
+name = "types-certifi"
+version = "2021.10.8.3"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/52/68/943c3aeaf14624712a0357c4a67814dba5cea36d194f5c764dad7959a00c/types-certifi-2021.10.8.3.tar.gz", hash = "sha256:72cf7798d165bc0b76e1c10dd1ea3097c7063c42c21d664523b928e88b554a4f", size = 2095 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/b5/63/2463d89481e811f007b0e1cd0a91e52e141b47f9de724d20db7b861dcfec/types_certifi-2021.10.8.3-py3-none-any.whl", hash = "sha256:b2d1e325e69f71f7c78e5943d410e650b4707bb0ef32e4ddf3da37f54176e88a", size = 2136 },
+]
+
+[[package]]
+name = "types-s3transfer"
+version = "0.11.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/56/a9/21967d4fc03bb7980b7af040642d67c4f1e5bf093dc7ff263d4f06020043/types_s3transfer-0.11.2.tar.gz", hash = "sha256:3ccb8b90b14434af2fb0d6c08500596d93f3a83fb804a2bb843d9bf4f7c2ca60", size = 14054 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/17/12/0256488171e2947b20364638779891db45bf25af14e9a6bde50b2df65cd6/types_s3transfer-0.11.2-py3-none-any.whl", hash = "sha256:09c31cff8c79a433fcf703b840b66d1f694a6c70c410ef52015dd4fe07ee0ae2", size = 19486 },
+]
+
+[[package]]
+name = "types-setuptools"
+version = "75.8.0.20250210"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/c3/20/794589df23b1e7d3c1a1f86285e749f2a83ef845d90f2461bc2912b8f989/types_setuptools-75.8.0.20250210.tar.gz", hash = "sha256:c1547361b2441f07c94e25dce8a068e18c611593ad4b6fdd727b1a8f5d1fda33", size = 48240 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/2d/b4/5978a63dac80d9a653fdb73f58e08b208486d303f9a3ee481f0c807630de/types_setuptools-75.8.0.20250210-py3-none-any.whl", hash = "sha256:a217d7b4d59be04c29e23d142c959a0f85e71292fd3fc4313f016ca11f0b56dc", size = 71535 },
+]
+
+[[package]]
+name = "types-toml"
+version = "0.10.8.20240310"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/86/47/3e4c75042792bff8e90d7991aa5c51812cc668828cc6cce711e97f63a607/types-toml-0.10.8.20240310.tar.gz", hash = "sha256:3d41501302972436a6b8b239c850b26689657e25281b48ff0ec06345b8830331", size = 4392 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/da/a2/d32ab58c0b216912638b140ab2170ee4b8644067c293b170e19fba340ccc/types_toml-0.10.8.20240310-py3-none-any.whl", hash = "sha256:627b47775d25fa29977d9c70dc0cbab3f314f32c8d8d0c012f2ef5de7aaec05d", size = 4777 },
+]
+
+[[package]]
+name = "typing-extensions"
+version = "4.12.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/df/db/f35a00659bc03fec321ba8bce9420de607a1d37f8342eee1863174c69557/typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8", size = 85321 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/26/9f/ad63fc0248c5379346306f8668cda6e2e2e9c95e01216d2b8ffd9ff037d0/typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", size = 37438 },
+]
+
+[[package]]
+name = "typing-inspect"
+version = "0.9.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "mypy-extensions" },
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/dc/74/1789779d91f1961fa9438e9a8710cdae6bd138c80d7303996933d117264a/typing_inspect-0.9.0.tar.gz", hash = "sha256:b23fc42ff6f6ef6954e4852c1fb512cdd18dbea03134f91f856a95ccc9461f78", size = 13825 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/65/f3/107a22063bf27bdccf2024833d3445f4eea42b2e598abfbd46f6a63b6cb0/typing_inspect-0.9.0-py3-none-any.whl", hash = "sha256:9ee6fc59062311ef8547596ab6b955e1b8aa46242d854bfc78f4f6b0eff35f9f", size = 8827 },
+]
+
+[[package]]
+name = "unidiff"
+version = "0.7.5"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/a3/48/81be0ac96e423a877754153699731ef439fd7b80b4c8b5425c94ed079ebd/unidiff-0.7.5.tar.gz", hash = "sha256:2e5f0162052248946b9f0970a40e9e124236bf86c82b70821143a6fc1dea2574", size = 20931 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/8a/54/57c411a6e8f7bd7848c8b66e4dcaffa586bf4c02e63f2280db0327a4e6eb/unidiff-0.7.5-py2.py3-none-any.whl", hash = "sha256:c93bf2265cc1ba2a520e415ab05da587370bc2a3ae9e0414329f54f0c2fc09e8", size = 14386 },
+]
+
+[[package]]
+name = "urllib3"
+version = "2.3.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/aa/63/e53da845320b757bf29ef6a9062f5c669fe997973f966045cb019c3f4b66/urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d", size = 307268 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c8/19/4ec628951a74043532ca2cf5d97b7b14863931476d117c471e8e2b1eb39f/urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df", size = 128369 },
+]
+
+[[package]]
+name = "uv"
+version = "0.5.30"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/e0/41/ba1c4ed43d59a2403ba653345cc43da09aecc203726a033d851b3b0798c0/uv-0.5.30.tar.gz", hash = "sha256:e40c77c012d087a51ae9a33189e7c59aa25da40f883c06e034a841b7a05c6639", size = 2860983 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/ee/42/1d5122a959be3f11992f3f25f60bdfceb8d3c2cd45a77de60123aeebc3fc/uv-0.5.30-py3-none-linux_armv6l.whl", hash = "sha256:b4ad4c4597f27d97f9273aa2b06654dab97380d1567582c7e719624220556eb2", size = 15435555 },
+ { url = "https://files.pythonhosted.org/packages/53/19/47ec2ea94895d383852922785cb573f6f0dfb32f105d46841870b3496861/uv-0.5.30-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:20a3fbe5662aa12d9196d1c842d267f375195818e53d0687761ae1571676cd40", size = 15617182 },
+ { url = "https://files.pythonhosted.org/packages/4b/90/40197a57f374ad3d9c9a86ddb43cfdac4459b0ea14f18553d7a2d90b72cc/uv-0.5.30-py3-none-macosx_11_0_arm64.whl", hash = "sha256:98aacbaa74393710e1125382688b74d1080fb3fdeb8659484b3a30120106524b", size = 14510030 },
+ { url = "https://files.pythonhosted.org/packages/f2/25/0dd9b0261e51e1702631be30c5d25a71f3a9bd5fdf453402e42ee114fd81/uv-0.5.30-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:c39834b2ba5ed4ce27641dcdd6b601fc091d0c45c8bc95d2f684148beb35d032", size = 14970225 },
+ { url = "https://files.pythonhosted.org/packages/92/56/5b41cab8292cf27ed510d6d9eb6adc595171cf8369eae2bde377829c7aab/uv-0.5.30-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7eaa0ea685b2962c995fa68c817740002379327767d25b6bfc4449afd9d28350", size = 15164087 },
+ { url = "https://files.pythonhosted.org/packages/19/d0/5aac4892d0d8c2a85de8adca905f87506d451ef1a60472e9cd2846e3f502/uv-0.5.30-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a35a297e8835ac686492228085c18799a4f9e4502b97830d9fa629ab33c628fc", size = 15938782 },
+ { url = "https://files.pythonhosted.org/packages/5f/c7/f772bea86b87d642100ba908a8cd6ebd6f3d171991b55a361ab6cae25fb2/uv-0.5.30-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:e3323a6aef65d7c35ce557a1dfe32c18b2c98b14361e6991e8903473cdc1c80a", size = 16884983 },
+ { url = "https://files.pythonhosted.org/packages/28/dc/93ec4bbe0df4edee1292673cc1edb13fa6b8cd90b4893d7d5bdf0b0760d0/uv-0.5.30-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:39d0daa24e41b0d7f69cced458eb69cd32f1259edb7f1c7018ed8906694c5af9", size = 16624144 },
+ { url = "https://files.pythonhosted.org/packages/dc/02/69cf46866ba9a7308c88d378bd42a0e096817af8e5a88451709c80994145/uv-0.5.30-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f442f1962c325921d5141f47a970aeb0454a1808f1901e27e25a958e0055244a", size = 20959582 },
+ { url = "https://files.pythonhosted.org/packages/16/f2/96c61ee44ea4c08645a96c1b18a53ffa2a19044ce60c9e3a0b3712ea1a11/uv-0.5.30-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a46b72bdb1855789b35277f894dac2b15fc0a084146ea8821b7cc7cae559a901", size = 16256029 },
+ { url = "https://files.pythonhosted.org/packages/ae/70/304e89f486c06bbd924b37833c2cec7c8f4bde607b467d7748e51460939f/uv-0.5.30-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:ee63749036afe168f477006e5d198cce618fcb6accf036fa33d4006f7e787e12", size = 15256649 },
+ { url = "https://files.pythonhosted.org/packages/51/eb/01ed61dbf91eb64916d0581c1646dba7710a63006eba0bf1e4306cf63a5c/uv-0.5.30-py3-none-musllinux_1_1_armv7l.whl", hash = "sha256:0a2624d586e71f4c8d27fb45fe7c27f8585b2669cfb85344be435bea5932a774", size = 15162449 },
+ { url = "https://files.pythonhosted.org/packages/86/fd/fb18df5324a8e67671a3dbb899746e1e93253a7d1ef5789816c82f9c031f/uv-0.5.30-py3-none-musllinux_1_1_i686.whl", hash = "sha256:194891c7473eb9cedfcd0ddd25fe7c1f208df639f67474068459c53f2f1ac034", size = 15560853 },
+ { url = "https://files.pythonhosted.org/packages/6f/93/89b390fd6bc941c341d4b6cae85a67473ba2cfc67334931796fb9432dfe3/uv-0.5.30-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:79dd27c2c0fdf887aadc9796345339786f27a07de7f80c9a892696e5740251c4", size = 16381075 },
+ { url = "https://files.pythonhosted.org/packages/45/1a/b42793b982dd6d3a94a489d408acd745d1a1a733e10cc2707985f79e93b6/uv-0.5.30-py3-none-win32.whl", hash = "sha256:5d42cd9051ab6d1bd18ca1cceb8099963a28315bcd8c9cd4104ffdb896af3075", size = 15607311 },
+ { url = "https://files.pythonhosted.org/packages/31/cc/9c9dadb39959bddf5b7884123b0230067de91cc975d99c5346df99cde8a8/uv-0.5.30-py3-none-win_amd64.whl", hash = "sha256:a8ebb553230ae811c16b2c4889095f7a8c39f657d75cf39f6f3fa5a38a5b9731", size = 16936894 },
+ { url = "https://files.pythonhosted.org/packages/bb/6f/d6ea64ffc7d1e0f0875cb75620ff70845c7a210a1c220629223e10d2a80a/uv-0.5.30-py3-none-win_arm64.whl", hash = "sha256:c6b359832c7caf58c43b37e156bfeabf3adc8f2a894a0f325d617cd41a57578e", size = 15752133 },
+]
+
+[[package]]
+name = "uvicorn"
+version = "0.34.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "click" },
+ { name = "h11" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/4b/4d/938bd85e5bf2edeec766267a5015ad969730bb91e31b44021dfe8b22df6c/uvicorn-0.34.0.tar.gz", hash = "sha256:404051050cd7e905de2c9a7e61790943440b3416f49cb409f965d9dcd0fa73e9", size = 76568 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/61/14/33a3a1352cfa71812a3a21e8c9bfb83f60b0011f5e36f2b1399d51928209/uvicorn-0.34.0-py3-none-any.whl", hash = "sha256:023dc038422502fa28a09c7a30bf2b6991512da7dcdb8fd35fe57cfc154126f4", size = 62315 },
+]
+
+[package.optional-dependencies]
+standard = [
+ { name = "colorama", marker = "sys_platform == 'win32'" },
+ { name = "httptools" },
+ { name = "python-dotenv" },
+ { name = "pyyaml" },
+ { name = "uvloop", marker = "platform_python_implementation != 'PyPy' and sys_platform != 'cygwin' and sys_platform != 'win32'" },
+ { name = "watchfiles" },
+ { name = "websockets" },
+]
+
+[[package]]
+name = "uvloop"
+version = "0.21.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/af/c0/854216d09d33c543f12a44b393c402e89a920b1a0a7dc634c42de91b9cf6/uvloop-0.21.0.tar.gz", hash = "sha256:3bf12b0fda68447806a7ad847bfa591613177275d35b6724b1ee573faa3704e3", size = 2492741 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/8c/4c/03f93178830dc7ce8b4cdee1d36770d2f5ebb6f3d37d354e061eefc73545/uvloop-0.21.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:359ec2c888397b9e592a889c4d72ba3d6befba8b2bb01743f72fffbde663b59c", size = 1471284 },
+ { url = "https://files.pythonhosted.org/packages/43/3e/92c03f4d05e50f09251bd8b2b2b584a2a7f8fe600008bcc4523337abe676/uvloop-0.21.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f7089d2dc73179ce5ac255bdf37c236a9f914b264825fdaacaded6990a7fb4c2", size = 821349 },
+ { url = "https://files.pythonhosted.org/packages/a6/ef/a02ec5da49909dbbfb1fd205a9a1ac4e88ea92dcae885e7c961847cd51e2/uvloop-0.21.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:baa4dcdbd9ae0a372f2167a207cd98c9f9a1ea1188a8a526431eef2f8116cc8d", size = 4580089 },
+ { url = "https://files.pythonhosted.org/packages/06/a7/b4e6a19925c900be9f98bec0a75e6e8f79bb53bdeb891916609ab3958967/uvloop-0.21.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86975dca1c773a2c9864f4c52c5a55631038e387b47eaf56210f873887b6c8dc", size = 4693770 },
+ { url = "https://files.pythonhosted.org/packages/ce/0c/f07435a18a4b94ce6bd0677d8319cd3de61f3a9eeb1e5f8ab4e8b5edfcb3/uvloop-0.21.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:461d9ae6660fbbafedd07559c6a2e57cd553b34b0065b6550685f6653a98c1cb", size = 4451321 },
+ { url = "https://files.pythonhosted.org/packages/8f/eb/f7032be105877bcf924709c97b1bf3b90255b4ec251f9340cef912559f28/uvloop-0.21.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:183aef7c8730e54c9a3ee3227464daed66e37ba13040bb3f350bc2ddc040f22f", size = 4659022 },
+ { url = "https://files.pythonhosted.org/packages/3f/8d/2cbef610ca21539f0f36e2b34da49302029e7c9f09acef0b1c3b5839412b/uvloop-0.21.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:bfd55dfcc2a512316e65f16e503e9e450cab148ef11df4e4e679b5e8253a5281", size = 1468123 },
+ { url = "https://files.pythonhosted.org/packages/93/0d/b0038d5a469f94ed8f2b2fce2434a18396d8fbfb5da85a0a9781ebbdec14/uvloop-0.21.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:787ae31ad8a2856fc4e7c095341cccc7209bd657d0e71ad0dc2ea83c4a6fa8af", size = 819325 },
+ { url = "https://files.pythonhosted.org/packages/50/94/0a687f39e78c4c1e02e3272c6b2ccdb4e0085fda3b8352fecd0410ccf915/uvloop-0.21.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ee4d4ef48036ff6e5cfffb09dd192c7a5027153948d85b8da7ff705065bacc6", size = 4582806 },
+ { url = "https://files.pythonhosted.org/packages/d2/19/f5b78616566ea68edd42aacaf645adbf71fbd83fc52281fba555dc27e3f1/uvloop-0.21.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3df876acd7ec037a3d005b3ab85a7e4110422e4d9c1571d4fc89b0fc41b6816", size = 4701068 },
+ { url = "https://files.pythonhosted.org/packages/47/57/66f061ee118f413cd22a656de622925097170b9380b30091b78ea0c6ea75/uvloop-0.21.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bd53ecc9a0f3d87ab847503c2e1552b690362e005ab54e8a48ba97da3924c0dc", size = 4454428 },
+ { url = "https://files.pythonhosted.org/packages/63/9a/0962b05b308494e3202d3f794a6e85abe471fe3cafdbcf95c2e8c713aabd/uvloop-0.21.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a5c39f217ab3c663dc699c04cbd50c13813e31d917642d459fdcec07555cc553", size = 4660018 },
+]
+
+[[package]]
+name = "virtualenv"
+version = "20.29.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "distlib" },
+ { name = "filelock" },
+ { name = "platformdirs" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/f1/88/dacc875dd54a8acadb4bcbfd4e3e86df8be75527116c91d8f9784f5e9cab/virtualenv-20.29.2.tar.gz", hash = "sha256:fdaabebf6d03b5ba83ae0a02cfe96f48a716f4fae556461d180825866f75b728", size = 4320272 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/93/fa/849483d56773ae29740ae70043ad88e068f98a6401aa819b5d6bee604683/virtualenv-20.29.2-py3-none-any.whl", hash = "sha256:febddfc3d1ea571bdb1dc0f98d7b45d24def7428214d4fb73cc486c9568cce6a", size = 4301478 },
+]
+
+[[package]]
+name = "watchfiles"
+version = "1.0.4"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "anyio" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/f5/26/c705fc77d0a9ecdb9b66f1e2976d95b81df3cae518967431e7dbf9b5e219/watchfiles-1.0.4.tar.gz", hash = "sha256:6ba473efd11062d73e4f00c2b730255f9c1bdd73cd5f9fe5b5da8dbd4a717205", size = 94625 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/5b/1a/8f4d9a1461709756ace48c98f07772bc6d4519b1e48b5fa24a4061216256/watchfiles-1.0.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:229e6ec880eca20e0ba2f7e2249c85bae1999d330161f45c78d160832e026ee2", size = 391345 },
+ { url = "https://files.pythonhosted.org/packages/bc/d2/6750b7b3527b1cdaa33731438432e7238a6c6c40a9924049e4cebfa40805/watchfiles-1.0.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5717021b199e8353782dce03bd8a8f64438832b84e2885c4a645f9723bf656d9", size = 381515 },
+ { url = "https://files.pythonhosted.org/packages/4e/17/80500e42363deef1e4b4818729ed939aaddc56f82f4e72b2508729dd3c6b/watchfiles-1.0.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0799ae68dfa95136dde7c472525700bd48777875a4abb2ee454e3ab18e9fc712", size = 449767 },
+ { url = "https://files.pythonhosted.org/packages/10/37/1427fa4cfa09adbe04b1e97bced19a29a3462cc64c78630787b613a23f18/watchfiles-1.0.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:43b168bba889886b62edb0397cab5b6490ffb656ee2fcb22dec8bfeb371a9e12", size = 455677 },
+ { url = "https://files.pythonhosted.org/packages/c5/7a/39e9397f3a19cb549a7d380412fd9e507d4854eddc0700bfad10ef6d4dba/watchfiles-1.0.4-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fb2c46e275fbb9f0c92e7654b231543c7bbfa1df07cdc4b99fa73bedfde5c844", size = 482219 },
+ { url = "https://files.pythonhosted.org/packages/45/2d/7113931a77e2ea4436cad0c1690c09a40a7f31d366f79c6f0a5bc7a4f6d5/watchfiles-1.0.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:857f5fc3aa027ff5e57047da93f96e908a35fe602d24f5e5d8ce64bf1f2fc733", size = 518830 },
+ { url = "https://files.pythonhosted.org/packages/f9/1b/50733b1980fa81ef3c70388a546481ae5fa4c2080040100cd7bf3bf7b321/watchfiles-1.0.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55ccfd27c497b228581e2838d4386301227fc0cb47f5a12923ec2fe4f97b95af", size = 497997 },
+ { url = "https://files.pythonhosted.org/packages/2b/b4/9396cc61b948ef18943e7c85ecfa64cf940c88977d882da57147f62b34b1/watchfiles-1.0.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c11ea22304d17d4385067588123658e9f23159225a27b983f343fcffc3e796a", size = 452249 },
+ { url = "https://files.pythonhosted.org/packages/fb/69/0c65a5a29e057ad0dc691c2fa6c23b2983c7dabaa190ba553b29ac84c3cc/watchfiles-1.0.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:74cb3ca19a740be4caa18f238298b9d472c850f7b2ed89f396c00a4c97e2d9ff", size = 614412 },
+ { url = "https://files.pythonhosted.org/packages/7f/b9/319fcba6eba5fad34327d7ce16a6b163b39741016b1996f4a3c96b8dd0e1/watchfiles-1.0.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c7cce76c138a91e720d1df54014a047e680b652336e1b73b8e3ff3158e05061e", size = 611982 },
+ { url = "https://files.pythonhosted.org/packages/f1/47/143c92418e30cb9348a4387bfa149c8e0e404a7c5b0585d46d2f7031b4b9/watchfiles-1.0.4-cp312-cp312-win32.whl", hash = "sha256:b045c800d55bc7e2cadd47f45a97c7b29f70f08a7c2fa13241905010a5493f94", size = 271822 },
+ { url = "https://files.pythonhosted.org/packages/ea/94/b0165481bff99a64b29e46e07ac2e0df9f7a957ef13bec4ceab8515f44e3/watchfiles-1.0.4-cp312-cp312-win_amd64.whl", hash = "sha256:c2acfa49dd0ad0bf2a9c0bb9a985af02e89345a7189be1efc6baa085e0f72d7c", size = 285441 },
+ { url = "https://files.pythonhosted.org/packages/11/de/09fe56317d582742d7ca8c2ca7b52a85927ebb50678d9b0fa8194658f536/watchfiles-1.0.4-cp312-cp312-win_arm64.whl", hash = "sha256:22bb55a7c9e564e763ea06c7acea24fc5d2ee5dfc5dafc5cfbedfe58505e9f90", size = 277141 },
+ { url = "https://files.pythonhosted.org/packages/08/98/f03efabec64b5b1fa58c0daab25c68ef815b0f320e54adcacd0d6847c339/watchfiles-1.0.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:8012bd820c380c3d3db8435e8cf7592260257b378b649154a7948a663b5f84e9", size = 390954 },
+ { url = "https://files.pythonhosted.org/packages/16/09/4dd49ba0a32a45813debe5fb3897955541351ee8142f586303b271a02b40/watchfiles-1.0.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:aa216f87594f951c17511efe5912808dfcc4befa464ab17c98d387830ce07b60", size = 381133 },
+ { url = "https://files.pythonhosted.org/packages/76/59/5aa6fc93553cd8d8ee75c6247763d77c02631aed21551a97d94998bf1dae/watchfiles-1.0.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62c9953cf85529c05b24705639ffa390f78c26449e15ec34d5339e8108c7c407", size = 449516 },
+ { url = "https://files.pythonhosted.org/packages/4c/aa/df4b6fe14b6317290b91335b23c96b488d365d65549587434817e06895ea/watchfiles-1.0.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7cf684aa9bba4cd95ecb62c822a56de54e3ae0598c1a7f2065d51e24637a3c5d", size = 454820 },
+ { url = "https://files.pythonhosted.org/packages/5e/71/185f8672f1094ce48af33252c73e39b48be93b761273872d9312087245f6/watchfiles-1.0.4-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f44a39aee3cbb9b825285ff979ab887a25c5d336e5ec3574f1506a4671556a8d", size = 481550 },
+ { url = "https://files.pythonhosted.org/packages/85/d7/50ebba2c426ef1a5cb17f02158222911a2e005d401caf5d911bfca58f4c4/watchfiles-1.0.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a38320582736922be8c865d46520c043bff350956dfc9fbaee3b2df4e1740a4b", size = 518647 },
+ { url = "https://files.pythonhosted.org/packages/f0/7a/4c009342e393c545d68987e8010b937f72f47937731225b2b29b7231428f/watchfiles-1.0.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:39f4914548b818540ef21fd22447a63e7be6e24b43a70f7642d21f1e73371590", size = 497547 },
+ { url = "https://files.pythonhosted.org/packages/0f/7c/1cf50b35412d5c72d63b2bf9a4fffee2e1549a245924960dd087eb6a6de4/watchfiles-1.0.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f12969a3765909cf5dc1e50b2436eb2c0e676a3c75773ab8cc3aa6175c16e902", size = 452179 },
+ { url = "https://files.pythonhosted.org/packages/d6/a9/3db1410e1c1413735a9a472380e4f431ad9a9e81711cda2aaf02b7f62693/watchfiles-1.0.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:0986902677a1a5e6212d0c49b319aad9cc48da4bd967f86a11bde96ad9676ca1", size = 614125 },
+ { url = "https://files.pythonhosted.org/packages/f2/e1/0025d365cf6248c4d1ee4c3d2e3d373bdd3f6aff78ba4298f97b4fad2740/watchfiles-1.0.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:308ac265c56f936636e3b0e3f59e059a40003c655228c131e1ad439957592303", size = 611911 },
+ { url = "https://files.pythonhosted.org/packages/55/55/035838277d8c98fc8c917ac9beeb0cd6c59d675dc2421df5f9fcf44a0070/watchfiles-1.0.4-cp313-cp313-win32.whl", hash = "sha256:aee397456a29b492c20fda2d8961e1ffb266223625346ace14e4b6d861ba9c80", size = 271152 },
+ { url = "https://files.pythonhosted.org/packages/f0/e5/96b8e55271685ddbadc50ce8bc53aa2dff278fb7ac4c2e473df890def2dc/watchfiles-1.0.4-cp313-cp313-win_amd64.whl", hash = "sha256:d6097538b0ae5c1b88c3b55afa245a66793a8fec7ada6755322e465fb1a0e8cc", size = 285216 },
+]
+
+[[package]]
+name = "websockets"
+version = "14.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/94/54/8359678c726243d19fae38ca14a334e740782336c9f19700858c4eb64a1e/websockets-14.2.tar.gz", hash = "sha256:5059ed9c54945efb321f097084b4c7e52c246f2c869815876a69d1efc4ad6eb5", size = 164394 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c1/81/04f7a397653dc8bec94ddc071f34833e8b99b13ef1a3804c149d59f92c18/websockets-14.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1f20522e624d7ffbdbe259c6b6a65d73c895045f76a93719aa10cd93b3de100c", size = 163096 },
+ { url = "https://files.pythonhosted.org/packages/ec/c5/de30e88557e4d70988ed4d2eabd73fd3e1e52456b9f3a4e9564d86353b6d/websockets-14.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:647b573f7d3ada919fd60e64d533409a79dcf1ea21daeb4542d1d996519ca967", size = 160758 },
+ { url = "https://files.pythonhosted.org/packages/e5/8c/d130d668781f2c77d106c007b6c6c1d9db68239107c41ba109f09e6c218a/websockets-14.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6af99a38e49f66be5a64b1e890208ad026cda49355661549c507152113049990", size = 160995 },
+ { url = "https://files.pythonhosted.org/packages/a6/bc/f6678a0ff17246df4f06765e22fc9d98d1b11a258cc50c5968b33d6742a1/websockets-14.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:091ab63dfc8cea748cc22c1db2814eadb77ccbf82829bac6b2fbe3401d548eda", size = 170815 },
+ { url = "https://files.pythonhosted.org/packages/d8/b2/8070cb970c2e4122a6ef38bc5b203415fd46460e025652e1ee3f2f43a9a3/websockets-14.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b374e8953ad477d17e4851cdc66d83fdc2db88d9e73abf755c94510ebddceb95", size = 169759 },
+ { url = "https://files.pythonhosted.org/packages/81/da/72f7caabd94652e6eb7e92ed2d3da818626e70b4f2b15a854ef60bf501ec/websockets-14.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a39d7eceeea35db85b85e1169011bb4321c32e673920ae9c1b6e0978590012a3", size = 170178 },
+ { url = "https://files.pythonhosted.org/packages/31/e0/812725b6deca8afd3a08a2e81b3c4c120c17f68c9b84522a520b816cda58/websockets-14.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0a6f3efd47ffd0d12080594f434faf1cd2549b31e54870b8470b28cc1d3817d9", size = 170453 },
+ { url = "https://files.pythonhosted.org/packages/66/d3/8275dbc231e5ba9bb0c4f93144394b4194402a7a0c8ffaca5307a58ab5e3/websockets-14.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:065ce275e7c4ffb42cb738dd6b20726ac26ac9ad0a2a48e33ca632351a737267", size = 169830 },
+ { url = "https://files.pythonhosted.org/packages/a3/ae/e7d1a56755ae15ad5a94e80dd490ad09e345365199600b2629b18ee37bc7/websockets-14.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e9d0e53530ba7b8b5e389c02282f9d2aa47581514bd6049d3a7cffe1385cf5fe", size = 169824 },
+ { url = "https://files.pythonhosted.org/packages/b6/32/88ccdd63cb261e77b882e706108d072e4f1c839ed723bf91a3e1f216bf60/websockets-14.2-cp312-cp312-win32.whl", hash = "sha256:20e6dd0984d7ca3037afcb4494e48c74ffb51e8013cac71cf607fffe11df7205", size = 163981 },
+ { url = "https://files.pythonhosted.org/packages/b3/7d/32cdb77990b3bdc34a306e0a0f73a1275221e9a66d869f6ff833c95b56ef/websockets-14.2-cp312-cp312-win_amd64.whl", hash = "sha256:44bba1a956c2c9d268bdcdf234d5e5ff4c9b6dc3e300545cbe99af59dda9dcce", size = 164421 },
+ { url = "https://files.pythonhosted.org/packages/82/94/4f9b55099a4603ac53c2912e1f043d6c49d23e94dd82a9ce1eb554a90215/websockets-14.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6f1372e511c7409a542291bce92d6c83320e02c9cf392223272287ce55bc224e", size = 163102 },
+ { url = "https://files.pythonhosted.org/packages/8e/b7/7484905215627909d9a79ae07070057afe477433fdacb59bf608ce86365a/websockets-14.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4da98b72009836179bb596a92297b1a61bb5a830c0e483a7d0766d45070a08ad", size = 160766 },
+ { url = "https://files.pythonhosted.org/packages/a3/a4/edb62efc84adb61883c7d2c6ad65181cb087c64252138e12d655989eec05/websockets-14.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8a86a269759026d2bde227652b87be79f8a734e582debf64c9d302faa1e9f03", size = 160998 },
+ { url = "https://files.pythonhosted.org/packages/f5/79/036d320dc894b96af14eac2529967a6fc8b74f03b83c487e7a0e9043d842/websockets-14.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:86cf1aaeca909bf6815ea714d5c5736c8d6dd3a13770e885aafe062ecbd04f1f", size = 170780 },
+ { url = "https://files.pythonhosted.org/packages/63/75/5737d21ee4dd7e4b9d487ee044af24a935e36a9ff1e1419d684feedcba71/websockets-14.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9b0f6c3ba3b1240f602ebb3971d45b02cc12bd1845466dd783496b3b05783a5", size = 169717 },
+ { url = "https://files.pythonhosted.org/packages/2c/3c/bf9b2c396ed86a0b4a92ff4cdaee09753d3ee389be738e92b9bbd0330b64/websockets-14.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:669c3e101c246aa85bc8534e495952e2ca208bd87994650b90a23d745902db9a", size = 170155 },
+ { url = "https://files.pythonhosted.org/packages/75/2d/83a5aca7247a655b1da5eb0ee73413abd5c3a57fc8b92915805e6033359d/websockets-14.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:eabdb28b972f3729348e632ab08f2a7b616c7e53d5414c12108c29972e655b20", size = 170495 },
+ { url = "https://files.pythonhosted.org/packages/79/dd/699238a92761e2f943885e091486378813ac8f43e3c84990bc394c2be93e/websockets-14.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2066dc4cbcc19f32c12a5a0e8cc1b7ac734e5b64ac0a325ff8353451c4b15ef2", size = 169880 },
+ { url = "https://files.pythonhosted.org/packages/c8/c9/67a8f08923cf55ce61aadda72089e3ed4353a95a3a4bc8bf42082810e580/websockets-14.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ab95d357cd471df61873dadf66dd05dd4709cae001dd6342edafc8dc6382f307", size = 169856 },
+ { url = "https://files.pythonhosted.org/packages/17/b1/1ffdb2680c64e9c3921d99db460546194c40d4acbef999a18c37aa4d58a3/websockets-14.2-cp313-cp313-win32.whl", hash = "sha256:a9e72fb63e5f3feacdcf5b4ff53199ec8c18d66e325c34ee4c551ca748623bbc", size = 163974 },
+ { url = "https://files.pythonhosted.org/packages/14/13/8b7fc4cb551b9cfd9890f0fd66e53c18a06240319915533b033a56a3d520/websockets-14.2-cp313-cp313-win_amd64.whl", hash = "sha256:b439ea828c4ba99bb3176dc8d9b933392a2413c0f6b149fdcba48393f573377f", size = 164420 },
+ { url = "https://files.pythonhosted.org/packages/7b/c8/d529f8a32ce40d98309f4470780631e971a5a842b60aec864833b3615786/websockets-14.2-py3-none-any.whl", hash = "sha256:7a6ceec4ea84469f15cf15807a747e9efe57e369c384fa86e022b3bea679b79b", size = 157416 },
+]
+
+[[package]]
+name = "win32-setctime"
+version = "1.2.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/b3/8f/705086c9d734d3b663af0e9bb3d4de6578d08f46b1b101c2442fd9aecaa2/win32_setctime-1.2.0.tar.gz", hash = "sha256:ae1fdf948f5640aae05c511ade119313fb6a30d7eabe25fef9764dca5873c4c0", size = 4867 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/e1/07/c6fe3ad3e685340704d314d765b7912993bcb8dc198f0e7a89382d37974b/win32_setctime-1.2.0-py3-none-any.whl", hash = "sha256:95d644c4e708aba81dc3704a116d8cbc974d70b3bdb8be1d150e36be6e9d1390", size = 4083 },
+]
+
+[[package]]
+name = "wrapt"
+version = "1.17.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/c3/fc/e91cc220803d7bc4db93fb02facd8461c37364151b8494762cc88b0fbcef/wrapt-1.17.2.tar.gz", hash = "sha256:41388e9d4d1522446fe79d3213196bd9e3b301a336965b9e27ca2788ebd122f3", size = 55531 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/a1/bd/ab55f849fd1f9a58ed7ea47f5559ff09741b25f00c191231f9f059c83949/wrapt-1.17.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d5e2439eecc762cd85e7bd37161d4714aa03a33c5ba884e26c81559817ca0925", size = 53799 },
+ { url = "https://files.pythonhosted.org/packages/53/18/75ddc64c3f63988f5a1d7e10fb204ffe5762bc663f8023f18ecaf31a332e/wrapt-1.17.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3fc7cb4c1c744f8c05cd5f9438a3caa6ab94ce8344e952d7c45a8ed59dd88392", size = 38821 },
+ { url = "https://files.pythonhosted.org/packages/48/2a/97928387d6ed1c1ebbfd4efc4133a0633546bec8481a2dd5ec961313a1c7/wrapt-1.17.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8fdbdb757d5390f7c675e558fd3186d590973244fab0c5fe63d373ade3e99d40", size = 38919 },
+ { url = "https://files.pythonhosted.org/packages/73/54/3bfe5a1febbbccb7a2f77de47b989c0b85ed3a6a41614b104204a788c20e/wrapt-1.17.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5bb1d0dbf99411f3d871deb6faa9aabb9d4e744d67dcaaa05399af89d847a91d", size = 88721 },
+ { url = "https://files.pythonhosted.org/packages/25/cb/7262bc1b0300b4b64af50c2720ef958c2c1917525238d661c3e9a2b71b7b/wrapt-1.17.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d18a4865f46b8579d44e4fe1e2bcbc6472ad83d98e22a26c963d46e4c125ef0b", size = 80899 },
+ { url = "https://files.pythonhosted.org/packages/2a/5a/04cde32b07a7431d4ed0553a76fdb7a61270e78c5fd5a603e190ac389f14/wrapt-1.17.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc570b5f14a79734437cb7b0500376b6b791153314986074486e0b0fa8d71d98", size = 89222 },
+ { url = "https://files.pythonhosted.org/packages/09/28/2e45a4f4771fcfb109e244d5dbe54259e970362a311b67a965555ba65026/wrapt-1.17.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6d9187b01bebc3875bac9b087948a2bccefe464a7d8f627cf6e48b1bbae30f82", size = 86707 },
+ { url = "https://files.pythonhosted.org/packages/c6/d2/dcb56bf5f32fcd4bd9aacc77b50a539abdd5b6536872413fd3f428b21bed/wrapt-1.17.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:9e8659775f1adf02eb1e6f109751268e493c73716ca5761f8acb695e52a756ae", size = 79685 },
+ { url = "https://files.pythonhosted.org/packages/80/4e/eb8b353e36711347893f502ce91c770b0b0929f8f0bed2670a6856e667a9/wrapt-1.17.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e8b2816ebef96d83657b56306152a93909a83f23994f4b30ad4573b00bd11bb9", size = 87567 },
+ { url = "https://files.pythonhosted.org/packages/17/27/4fe749a54e7fae6e7146f1c7d914d28ef599dacd4416566c055564080fe2/wrapt-1.17.2-cp312-cp312-win32.whl", hash = "sha256:468090021f391fe0056ad3e807e3d9034e0fd01adcd3bdfba977b6fdf4213ea9", size = 36672 },
+ { url = "https://files.pythonhosted.org/packages/15/06/1dbf478ea45c03e78a6a8c4be4fdc3c3bddea5c8de8a93bc971415e47f0f/wrapt-1.17.2-cp312-cp312-win_amd64.whl", hash = "sha256:ec89ed91f2fa8e3f52ae53cd3cf640d6feff92ba90d62236a81e4e563ac0e991", size = 38865 },
+ { url = "https://files.pythonhosted.org/packages/ce/b9/0ffd557a92f3b11d4c5d5e0c5e4ad057bd9eb8586615cdaf901409920b14/wrapt-1.17.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6ed6ffac43aecfe6d86ec5b74b06a5be33d5bb9243d055141e8cabb12aa08125", size = 53800 },
+ { url = "https://files.pythonhosted.org/packages/c0/ef/8be90a0b7e73c32e550c73cfb2fa09db62234227ece47b0e80a05073b375/wrapt-1.17.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:35621ae4c00e056adb0009f8e86e28eb4a41a4bfa8f9bfa9fca7d343fe94f998", size = 38824 },
+ { url = "https://files.pythonhosted.org/packages/36/89/0aae34c10fe524cce30fe5fc433210376bce94cf74d05b0d68344c8ba46e/wrapt-1.17.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a604bf7a053f8362d27eb9fefd2097f82600b856d5abe996d623babd067b1ab5", size = 38920 },
+ { url = "https://files.pythonhosted.org/packages/3b/24/11c4510de906d77e0cfb5197f1b1445d4fec42c9a39ea853d482698ac681/wrapt-1.17.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5cbabee4f083b6b4cd282f5b817a867cf0b1028c54d445b7ec7cfe6505057cf8", size = 88690 },
+ { url = "https://files.pythonhosted.org/packages/71/d7/cfcf842291267bf455b3e266c0c29dcb675b5540ee8b50ba1699abf3af45/wrapt-1.17.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49703ce2ddc220df165bd2962f8e03b84c89fee2d65e1c24a7defff6f988f4d6", size = 80861 },
+ { url = "https://files.pythonhosted.org/packages/d5/66/5d973e9f3e7370fd686fb47a9af3319418ed925c27d72ce16b791231576d/wrapt-1.17.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8112e52c5822fc4253f3901b676c55ddf288614dc7011634e2719718eaa187dc", size = 89174 },
+ { url = "https://files.pythonhosted.org/packages/a7/d3/8e17bb70f6ae25dabc1aaf990f86824e4fd98ee9cadf197054e068500d27/wrapt-1.17.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9fee687dce376205d9a494e9c121e27183b2a3df18037f89d69bd7b35bcf59e2", size = 86721 },
+ { url = "https://files.pythonhosted.org/packages/6f/54/f170dfb278fe1c30d0ff864513cff526d624ab8de3254b20abb9cffedc24/wrapt-1.17.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:18983c537e04d11cf027fbb60a1e8dfd5190e2b60cc27bc0808e653e7b218d1b", size = 79763 },
+ { url = "https://files.pythonhosted.org/packages/4a/98/de07243751f1c4a9b15c76019250210dd3486ce098c3d80d5f729cba029c/wrapt-1.17.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:703919b1633412ab54bcf920ab388735832fdcb9f9a00ae49387f0fe67dad504", size = 87585 },
+ { url = "https://files.pythonhosted.org/packages/f9/f0/13925f4bd6548013038cdeb11ee2cbd4e37c30f8bfd5db9e5a2a370d6e20/wrapt-1.17.2-cp313-cp313-win32.whl", hash = "sha256:abbb9e76177c35d4e8568e58650aa6926040d6a9f6f03435b7a522bf1c487f9a", size = 36676 },
+ { url = "https://files.pythonhosted.org/packages/bf/ae/743f16ef8c2e3628df3ddfd652b7d4c555d12c84b53f3d8218498f4ade9b/wrapt-1.17.2-cp313-cp313-win_amd64.whl", hash = "sha256:69606d7bb691b50a4240ce6b22ebb319c1cfb164e5f6569835058196e0f3a845", size = 38871 },
+ { url = "https://files.pythonhosted.org/packages/3d/bc/30f903f891a82d402ffb5fda27ec1d621cc97cb74c16fea0b6141f1d4e87/wrapt-1.17.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:4a721d3c943dae44f8e243b380cb645a709ba5bd35d3ad27bc2ed947e9c68192", size = 56312 },
+ { url = "https://files.pythonhosted.org/packages/8a/04/c97273eb491b5f1c918857cd26f314b74fc9b29224521f5b83f872253725/wrapt-1.17.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:766d8bbefcb9e00c3ac3b000d9acc51f1b399513f44d77dfe0eb026ad7c9a19b", size = 40062 },
+ { url = "https://files.pythonhosted.org/packages/4e/ca/3b7afa1eae3a9e7fefe499db9b96813f41828b9fdb016ee836c4c379dadb/wrapt-1.17.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e496a8ce2c256da1eb98bd15803a79bee00fc351f5dfb9ea82594a3f058309e0", size = 40155 },
+ { url = "https://files.pythonhosted.org/packages/89/be/7c1baed43290775cb9030c774bc53c860db140397047cc49aedaf0a15477/wrapt-1.17.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40d615e4fe22f4ad3528448c193b218e077656ca9ccb22ce2cb20db730f8d306", size = 113471 },
+ { url = "https://files.pythonhosted.org/packages/32/98/4ed894cf012b6d6aae5f5cc974006bdeb92f0241775addad3f8cd6ab71c8/wrapt-1.17.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a5aaeff38654462bc4b09023918b7f21790efb807f54c000a39d41d69cf552cb", size = 101208 },
+ { url = "https://files.pythonhosted.org/packages/ea/fd/0c30f2301ca94e655e5e057012e83284ce8c545df7661a78d8bfca2fac7a/wrapt-1.17.2-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a7d15bbd2bc99e92e39f49a04653062ee6085c0e18b3b7512a4f2fe91f2d681", size = 109339 },
+ { url = "https://files.pythonhosted.org/packages/75/56/05d000de894c4cfcb84bcd6b1df6214297b8089a7bd324c21a4765e49b14/wrapt-1.17.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:e3890b508a23299083e065f435a492b5435eba6e304a7114d2f919d400888cc6", size = 110232 },
+ { url = "https://files.pythonhosted.org/packages/53/f8/c3f6b2cf9b9277fb0813418e1503e68414cd036b3b099c823379c9575e6d/wrapt-1.17.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:8c8b293cd65ad716d13d8dd3624e42e5a19cc2a2f1acc74b30c2c13f15cb61a6", size = 100476 },
+ { url = "https://files.pythonhosted.org/packages/a7/b1/0bb11e29aa5139d90b770ebbfa167267b1fc548d2302c30c8f7572851738/wrapt-1.17.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4c82b8785d98cdd9fed4cac84d765d234ed3251bd6afe34cb7ac523cb93e8b4f", size = 106377 },
+ { url = "https://files.pythonhosted.org/packages/6a/e1/0122853035b40b3f333bbb25f1939fc1045e21dd518f7f0922b60c156f7c/wrapt-1.17.2-cp313-cp313t-win32.whl", hash = "sha256:13e6afb7fe71fe7485a4550a8844cc9ffbe263c0f1a1eea569bc7091d4898555", size = 37986 },
+ { url = "https://files.pythonhosted.org/packages/09/5e/1655cf481e079c1f22d0cabdd4e51733679932718dc23bf2db175f329b76/wrapt-1.17.2-cp313-cp313t-win_amd64.whl", hash = "sha256:eaf675418ed6b3b31c7a989fd007fa7c3be66ce14e5c3b27336383604c9da85c", size = 40750 },
+ { url = "https://files.pythonhosted.org/packages/2d/82/f56956041adef78f849db6b289b282e72b55ab8045a75abad81898c28d19/wrapt-1.17.2-py3-none-any.whl", hash = "sha256:b18f2d1533a71f069c7f82d524a52599053d4c7166e9dd374ae2136b7f40f7c8", size = 23594 },
+]
+
+[[package]]
+name = "xmltodict"
+version = "0.14.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/50/05/51dcca9a9bf5e1bce52582683ce50980bcadbc4fa5143b9f2b19ab99958f/xmltodict-0.14.2.tar.gz", hash = "sha256:201e7c28bb210e374999d1dde6382923ab0ed1a8a5faeece48ab525b7810a553", size = 51942 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/d6/45/fc303eb433e8a2a271739c98e953728422fa61a3c1f36077a49e395c972e/xmltodict-0.14.2-py2.py3-none-any.whl", hash = "sha256:20cc7d723ed729276e808f26fb6b3599f786cbc37e06c65e192ba77c40f20aac", size = 9981 },
+]
+
+[[package]]
+name = "yarl"
+version = "1.18.3"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "idna" },
+ { name = "multidict" },
+ { name = "propcache" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/b7/9d/4b94a8e6d2b51b599516a5cb88e5bc99b4d8d4583e468057eaa29d5f0918/yarl-1.18.3.tar.gz", hash = "sha256:ac1801c45cbf77b6c99242eeff4fffb5e4e73a800b5c4ad4fc0be5def634d2e1", size = 181062 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/33/85/bd2e2729752ff4c77338e0102914897512e92496375e079ce0150a6dc306/yarl-1.18.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1dd4bdd05407ced96fed3d7f25dbbf88d2ffb045a0db60dbc247f5b3c5c25d50", size = 142644 },
+ { url = "https://files.pythonhosted.org/packages/ff/74/1178322cc0f10288d7eefa6e4a85d8d2e28187ccab13d5b844e8b5d7c88d/yarl-1.18.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7c33dd1931a95e5d9a772d0ac5e44cac8957eaf58e3c8da8c1414de7dd27c576", size = 94962 },
+ { url = "https://files.pythonhosted.org/packages/be/75/79c6acc0261e2c2ae8a1c41cf12265e91628c8c58ae91f5ff59e29c0787f/yarl-1.18.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:25b411eddcfd56a2f0cd6a384e9f4f7aa3efee14b188de13048c25b5e91f1640", size = 92795 },
+ { url = "https://files.pythonhosted.org/packages/6b/32/927b2d67a412c31199e83fefdce6e645247b4fb164aa1ecb35a0f9eb2058/yarl-1.18.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:436c4fc0a4d66b2badc6c5fc5ef4e47bb10e4fd9bf0c79524ac719a01f3607c2", size = 332368 },
+ { url = "https://files.pythonhosted.org/packages/19/e5/859fca07169d6eceeaa4fde1997c91d8abde4e9a7c018e371640c2da2b71/yarl-1.18.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e35ef8683211db69ffe129a25d5634319a677570ab6b2eba4afa860f54eeaf75", size = 342314 },
+ { url = "https://files.pythonhosted.org/packages/08/75/76b63ccd91c9e03ab213ef27ae6add2e3400e77e5cdddf8ed2dbc36e3f21/yarl-1.18.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:84b2deecba4a3f1a398df819151eb72d29bfeb3b69abb145a00ddc8d30094512", size = 341987 },
+ { url = "https://files.pythonhosted.org/packages/1a/e1/a097d5755d3ea8479a42856f51d97eeff7a3a7160593332d98f2709b3580/yarl-1.18.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00e5a1fea0fd4f5bfa7440a47eff01d9822a65b4488f7cff83155a0f31a2ecba", size = 336914 },
+ { url = "https://files.pythonhosted.org/packages/0b/42/e1b4d0e396b7987feceebe565286c27bc085bf07d61a59508cdaf2d45e63/yarl-1.18.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d0e883008013c0e4aef84dcfe2a0b172c4d23c2669412cf5b3371003941f72bb", size = 325765 },
+ { url = "https://files.pythonhosted.org/packages/7e/18/03a5834ccc9177f97ca1bbb245b93c13e58e8225276f01eedc4cc98ab820/yarl-1.18.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5a3f356548e34a70b0172d8890006c37be92995f62d95a07b4a42e90fba54272", size = 344444 },
+ { url = "https://files.pythonhosted.org/packages/c8/03/a713633bdde0640b0472aa197b5b86e90fbc4c5bc05b727b714cd8a40e6d/yarl-1.18.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:ccd17349166b1bee6e529b4add61727d3f55edb7babbe4069b5764c9587a8cc6", size = 340760 },
+ { url = "https://files.pythonhosted.org/packages/eb/99/f6567e3f3bbad8fd101886ea0276c68ecb86a2b58be0f64077396cd4b95e/yarl-1.18.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b958ddd075ddba5b09bb0be8a6d9906d2ce933aee81100db289badbeb966f54e", size = 346484 },
+ { url = "https://files.pythonhosted.org/packages/8e/a9/84717c896b2fc6cb15bd4eecd64e34a2f0a9fd6669e69170c73a8b46795a/yarl-1.18.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c7d79f7d9aabd6011004e33b22bc13056a3e3fb54794d138af57f5ee9d9032cb", size = 359864 },
+ { url = "https://files.pythonhosted.org/packages/1e/2e/d0f5f1bef7ee93ed17e739ec8dbcb47794af891f7d165fa6014517b48169/yarl-1.18.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:4891ed92157e5430874dad17b15eb1fda57627710756c27422200c52d8a4e393", size = 364537 },
+ { url = "https://files.pythonhosted.org/packages/97/8a/568d07c5d4964da5b02621a517532adb8ec5ba181ad1687191fffeda0ab6/yarl-1.18.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ce1af883b94304f493698b00d0f006d56aea98aeb49d75ec7d98cd4a777e9285", size = 357861 },
+ { url = "https://files.pythonhosted.org/packages/7d/e3/924c3f64b6b3077889df9a1ece1ed8947e7b61b0a933f2ec93041990a677/yarl-1.18.3-cp312-cp312-win32.whl", hash = "sha256:f91c4803173928a25e1a55b943c81f55b8872f0018be83e3ad4938adffb77dd2", size = 84097 },
+ { url = "https://files.pythonhosted.org/packages/34/45/0e055320daaabfc169b21ff6174567b2c910c45617b0d79c68d7ab349b02/yarl-1.18.3-cp312-cp312-win_amd64.whl", hash = "sha256:7e2ee16578af3b52ac2f334c3b1f92262f47e02cc6193c598502bd46f5cd1477", size = 90399 },
+ { url = "https://files.pythonhosted.org/packages/30/c7/c790513d5328a8390be8f47be5d52e141f78b66c6c48f48d241ca6bd5265/yarl-1.18.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:90adb47ad432332d4f0bc28f83a5963f426ce9a1a8809f5e584e704b82685dcb", size = 140789 },
+ { url = "https://files.pythonhosted.org/packages/30/aa/a2f84e93554a578463e2edaaf2300faa61c8701f0898725842c704ba5444/yarl-1.18.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:913829534200eb0f789d45349e55203a091f45c37a2674678744ae52fae23efa", size = 94144 },
+ { url = "https://files.pythonhosted.org/packages/c6/fc/d68d8f83714b221a85ce7866832cba36d7c04a68fa6a960b908c2c84f325/yarl-1.18.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ef9f7768395923c3039055c14334ba4d926f3baf7b776c923c93d80195624782", size = 91974 },
+ { url = "https://files.pythonhosted.org/packages/56/4e/d2563d8323a7e9a414b5b25341b3942af5902a2263d36d20fb17c40411e2/yarl-1.18.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88a19f62ff30117e706ebc9090b8ecc79aeb77d0b1f5ec10d2d27a12bc9f66d0", size = 333587 },
+ { url = "https://files.pythonhosted.org/packages/25/c9/cfec0bc0cac8d054be223e9f2c7909d3e8442a856af9dbce7e3442a8ec8d/yarl-1.18.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e17c9361d46a4d5addf777c6dd5eab0715a7684c2f11b88c67ac37edfba6c482", size = 344386 },
+ { url = "https://files.pythonhosted.org/packages/ab/5d/4c532190113b25f1364d25f4c319322e86232d69175b91f27e3ebc2caf9a/yarl-1.18.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1a74a13a4c857a84a845505fd2d68e54826a2cd01935a96efb1e9d86c728e186", size = 345421 },
+ { url = "https://files.pythonhosted.org/packages/23/d1/6cdd1632da013aa6ba18cee4d750d953104a5e7aac44e249d9410a972bf5/yarl-1.18.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:41f7ce59d6ee7741af71d82020346af364949314ed3d87553763a2df1829cc58", size = 339384 },
+ { url = "https://files.pythonhosted.org/packages/9a/c4/6b3c39bec352e441bd30f432cda6ba51681ab19bb8abe023f0d19777aad1/yarl-1.18.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f52a265001d830bc425f82ca9eabda94a64a4d753b07d623a9f2863fde532b53", size = 326689 },
+ { url = "https://files.pythonhosted.org/packages/23/30/07fb088f2eefdc0aa4fc1af4e3ca4eb1a3aadd1ce7d866d74c0f124e6a85/yarl-1.18.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:82123d0c954dc58db301f5021a01854a85bf1f3bb7d12ae0c01afc414a882ca2", size = 345453 },
+ { url = "https://files.pythonhosted.org/packages/63/09/d54befb48f9cd8eec43797f624ec37783a0266855f4930a91e3d5c7717f8/yarl-1.18.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:2ec9bbba33b2d00999af4631a3397d1fd78290c48e2a3e52d8dd72db3a067ac8", size = 341872 },
+ { url = "https://files.pythonhosted.org/packages/91/26/fd0ef9bf29dd906a84b59f0cd1281e65b0c3e08c6aa94b57f7d11f593518/yarl-1.18.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:fbd6748e8ab9b41171bb95c6142faf068f5ef1511935a0aa07025438dd9a9bc1", size = 347497 },
+ { url = "https://files.pythonhosted.org/packages/d9/b5/14ac7a256d0511b2ac168d50d4b7d744aea1c1aa20c79f620d1059aab8b2/yarl-1.18.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:877d209b6aebeb5b16c42cbb377f5f94d9e556626b1bfff66d7b0d115be88d0a", size = 359981 },
+ { url = "https://files.pythonhosted.org/packages/ca/b3/d493221ad5cbd18bc07e642894030437e405e1413c4236dd5db6e46bcec9/yarl-1.18.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:b464c4ab4bfcb41e3bfd3f1c26600d038376c2de3297760dfe064d2cb7ea8e10", size = 366229 },
+ { url = "https://files.pythonhosted.org/packages/04/56/6a3e2a5d9152c56c346df9b8fb8edd2c8888b1e03f96324d457e5cf06d34/yarl-1.18.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8d39d351e7faf01483cc7ff7c0213c412e38e5a340238826be7e0e4da450fdc8", size = 360383 },
+ { url = "https://files.pythonhosted.org/packages/fd/b7/4b3c7c7913a278d445cc6284e59b2e62fa25e72758f888b7a7a39eb8423f/yarl-1.18.3-cp313-cp313-win32.whl", hash = "sha256:61ee62ead9b68b9123ec24bc866cbef297dd266175d53296e2db5e7f797f902d", size = 310152 },
+ { url = "https://files.pythonhosted.org/packages/f5/d5/688db678e987c3e0fb17867970700b92603cadf36c56e5fb08f23e822a0c/yarl-1.18.3-cp313-cp313-win_amd64.whl", hash = "sha256:578e281c393af575879990861823ef19d66e2b1d0098414855dd367e234f5b3c", size = 315723 },
+ { url = "https://files.pythonhosted.org/packages/f5/4b/a06e0ec3d155924f77835ed2d167ebd3b211a7b0853da1cf8d8414d784ef/yarl-1.18.3-py3-none-any.whl", hash = "sha256:b57f4f58099328dfb26c6a771d09fb20dbbae81d20cfb66141251ea063bd101b", size = 45109 },
+]
+
+[[package]]
+name = "zstandard"
+version = "0.23.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "cffi", marker = "platform_python_implementation == 'PyPy'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/ed/f6/2ac0287b442160a89d726b17a9184a4c615bb5237db763791a7fd16d9df1/zstandard-0.23.0.tar.gz", hash = "sha256:b2d8c62d08e7255f68f7a740bae85b3c9b8e5466baa9cbf7f57f1cde0ac6bc09", size = 681701 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/7b/83/f23338c963bd9de687d47bf32efe9fd30164e722ba27fb59df33e6b1719b/zstandard-0.23.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b4567955a6bc1b20e9c31612e615af6b53733491aeaa19a6b3b37f3b65477094", size = 788713 },
+ { url = "https://files.pythonhosted.org/packages/5b/b3/1a028f6750fd9227ee0b937a278a434ab7f7fdc3066c3173f64366fe2466/zstandard-0.23.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1e172f57cd78c20f13a3415cc8dfe24bf388614324d25539146594c16d78fcc8", size = 633459 },
+ { url = "https://files.pythonhosted.org/packages/26/af/36d89aae0c1f95a0a98e50711bc5d92c144939efc1f81a2fcd3e78d7f4c1/zstandard-0.23.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b0e166f698c5a3e914947388c162be2583e0c638a4703fc6a543e23a88dea3c1", size = 4945707 },
+ { url = "https://files.pythonhosted.org/packages/cd/2e/2051f5c772f4dfc0aae3741d5fc72c3dcfe3aaeb461cc231668a4db1ce14/zstandard-0.23.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:12a289832e520c6bd4dcaad68e944b86da3bad0d339ef7989fb7e88f92e96072", size = 5306545 },
+ { url = "https://files.pythonhosted.org/packages/0a/9e/a11c97b087f89cab030fa71206963090d2fecd8eb83e67bb8f3ffb84c024/zstandard-0.23.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d50d31bfedd53a928fed6707b15a8dbeef011bb6366297cc435accc888b27c20", size = 5337533 },
+ { url = "https://files.pythonhosted.org/packages/fc/79/edeb217c57fe1bf16d890aa91a1c2c96b28c07b46afed54a5dcf310c3f6f/zstandard-0.23.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72c68dda124a1a138340fb62fa21b9bf4848437d9ca60bd35db36f2d3345f373", size = 5436510 },
+ { url = "https://files.pythonhosted.org/packages/81/4f/c21383d97cb7a422ddf1ae824b53ce4b51063d0eeb2afa757eb40804a8ef/zstandard-0.23.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:53dd9d5e3d29f95acd5de6802e909ada8d8d8cfa37a3ac64836f3bc4bc5512db", size = 4859973 },
+ { url = "https://files.pythonhosted.org/packages/ab/15/08d22e87753304405ccac8be2493a495f529edd81d39a0870621462276ef/zstandard-0.23.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:6a41c120c3dbc0d81a8e8adc73312d668cd34acd7725f036992b1b72d22c1772", size = 4936968 },
+ { url = "https://files.pythonhosted.org/packages/eb/fa/f3670a597949fe7dcf38119a39f7da49a8a84a6f0b1a2e46b2f71a0ab83f/zstandard-0.23.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:40b33d93c6eddf02d2c19f5773196068d875c41ca25730e8288e9b672897c105", size = 5467179 },
+ { url = "https://files.pythonhosted.org/packages/4e/a9/dad2ab22020211e380adc477a1dbf9f109b1f8d94c614944843e20dc2a99/zstandard-0.23.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9206649ec587e6b02bd124fb7799b86cddec350f6f6c14bc82a2b70183e708ba", size = 4848577 },
+ { url = "https://files.pythonhosted.org/packages/08/03/dd28b4484b0770f1e23478413e01bee476ae8227bbc81561f9c329e12564/zstandard-0.23.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:76e79bc28a65f467e0409098fa2c4376931fd3207fbeb6b956c7c476d53746dd", size = 4693899 },
+ { url = "https://files.pythonhosted.org/packages/2b/64/3da7497eb635d025841e958bcd66a86117ae320c3b14b0ae86e9e8627518/zstandard-0.23.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:66b689c107857eceabf2cf3d3fc699c3c0fe8ccd18df2219d978c0283e4c508a", size = 5199964 },
+ { url = "https://files.pythonhosted.org/packages/43/a4/d82decbab158a0e8a6ebb7fc98bc4d903266bce85b6e9aaedea1d288338c/zstandard-0.23.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9c236e635582742fee16603042553d276cca506e824fa2e6489db04039521e90", size = 5655398 },
+ { url = "https://files.pythonhosted.org/packages/f2/61/ac78a1263bc83a5cf29e7458b77a568eda5a8f81980691bbc6eb6a0d45cc/zstandard-0.23.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a8fffdbd9d1408006baaf02f1068d7dd1f016c6bcb7538682622c556e7b68e35", size = 5191313 },
+ { url = "https://files.pythonhosted.org/packages/e7/54/967c478314e16af5baf849b6ee9d6ea724ae5b100eb506011f045d3d4e16/zstandard-0.23.0-cp312-cp312-win32.whl", hash = "sha256:dc1d33abb8a0d754ea4763bad944fd965d3d95b5baef6b121c0c9013eaf1907d", size = 430877 },
+ { url = "https://files.pythonhosted.org/packages/75/37/872d74bd7739639c4553bf94c84af7d54d8211b626b352bc57f0fd8d1e3f/zstandard-0.23.0-cp312-cp312-win_amd64.whl", hash = "sha256:64585e1dba664dc67c7cdabd56c1e5685233fbb1fc1966cfba2a340ec0dfff7b", size = 495595 },
+ { url = "https://files.pythonhosted.org/packages/80/f1/8386f3f7c10261fe85fbc2c012fdb3d4db793b921c9abcc995d8da1b7a80/zstandard-0.23.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:576856e8594e6649aee06ddbfc738fec6a834f7c85bf7cadd1c53d4a58186ef9", size = 788975 },
+ { url = "https://files.pythonhosted.org/packages/16/e8/cbf01077550b3e5dc86089035ff8f6fbbb312bc0983757c2d1117ebba242/zstandard-0.23.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:38302b78a850ff82656beaddeb0bb989a0322a8bbb1bf1ab10c17506681d772a", size = 633448 },
+ { url = "https://files.pythonhosted.org/packages/06/27/4a1b4c267c29a464a161aeb2589aff212b4db653a1d96bffe3598f3f0d22/zstandard-0.23.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d2240ddc86b74966c34554c49d00eaafa8200a18d3a5b6ffbf7da63b11d74ee2", size = 4945269 },
+ { url = "https://files.pythonhosted.org/packages/7c/64/d99261cc57afd9ae65b707e38045ed8269fbdae73544fd2e4a4d50d0ed83/zstandard-0.23.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2ef230a8fd217a2015bc91b74f6b3b7d6522ba48be29ad4ea0ca3a3775bf7dd5", size = 5306228 },
+ { url = "https://files.pythonhosted.org/packages/7a/cf/27b74c6f22541f0263016a0fd6369b1b7818941de639215c84e4e94b2a1c/zstandard-0.23.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:774d45b1fac1461f48698a9d4b5fa19a69d47ece02fa469825b442263f04021f", size = 5336891 },
+ { url = "https://files.pythonhosted.org/packages/fa/18/89ac62eac46b69948bf35fcd90d37103f38722968e2981f752d69081ec4d/zstandard-0.23.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f77fa49079891a4aab203d0b1744acc85577ed16d767b52fc089d83faf8d8ed", size = 5436310 },
+ { url = "https://files.pythonhosted.org/packages/a8/a8/5ca5328ee568a873f5118d5b5f70d1f36c6387716efe2e369010289a5738/zstandard-0.23.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ac184f87ff521f4840e6ea0b10c0ec90c6b1dcd0bad2f1e4a9a1b4fa177982ea", size = 4859912 },
+ { url = "https://files.pythonhosted.org/packages/ea/ca/3781059c95fd0868658b1cf0440edd832b942f84ae60685d0cfdb808bca1/zstandard-0.23.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c363b53e257246a954ebc7c488304b5592b9c53fbe74d03bc1c64dda153fb847", size = 4936946 },
+ { url = "https://files.pythonhosted.org/packages/ce/11/41a58986f809532742c2b832c53b74ba0e0a5dae7e8ab4642bf5876f35de/zstandard-0.23.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:e7792606d606c8df5277c32ccb58f29b9b8603bf83b48639b7aedf6df4fe8171", size = 5466994 },
+ { url = "https://files.pythonhosted.org/packages/83/e3/97d84fe95edd38d7053af05159465d298c8b20cebe9ccb3d26783faa9094/zstandard-0.23.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a0817825b900fcd43ac5d05b8b3079937073d2b1ff9cf89427590718b70dd840", size = 4848681 },
+ { url = "https://files.pythonhosted.org/packages/6e/99/cb1e63e931de15c88af26085e3f2d9af9ce53ccafac73b6e48418fd5a6e6/zstandard-0.23.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:9da6bc32faac9a293ddfdcb9108d4b20416219461e4ec64dfea8383cac186690", size = 4694239 },
+ { url = "https://files.pythonhosted.org/packages/ab/50/b1e703016eebbc6501fc92f34db7b1c68e54e567ef39e6e59cf5fb6f2ec0/zstandard-0.23.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:fd7699e8fd9969f455ef2926221e0233f81a2542921471382e77a9e2f2b57f4b", size = 5200149 },
+ { url = "https://files.pythonhosted.org/packages/aa/e0/932388630aaba70197c78bdb10cce2c91fae01a7e553b76ce85471aec690/zstandard-0.23.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:d477ed829077cd945b01fc3115edd132c47e6540ddcd96ca169facff28173057", size = 5655392 },
+ { url = "https://files.pythonhosted.org/packages/02/90/2633473864f67a15526324b007a9f96c96f56d5f32ef2a56cc12f9548723/zstandard-0.23.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa6ce8b52c5987b3e34d5674b0ab529a4602b632ebab0a93b07bfb4dfc8f8a33", size = 5191299 },
+ { url = "https://files.pythonhosted.org/packages/b0/4c/315ca5c32da7e2dc3455f3b2caee5c8c2246074a61aac6ec3378a97b7136/zstandard-0.23.0-cp313-cp313-win32.whl", hash = "sha256:a9b07268d0c3ca5c170a385a0ab9fb7fdd9f5fd866be004c4ea39e44edce47dd", size = 430862 },
+ { url = "https://files.pythonhosted.org/packages/a2/bf/c6aaba098e2d04781e8f4f7c0ba3c7aa73d00e4c436bcc0cf059a66691d1/zstandard-0.23.0-cp313-cp313-win_amd64.whl", hash = "sha256:f3513916e8c645d0610815c257cbfd3242adfd5c4cfa78be514e5a3ebb42a41b", size = 495578 },
+]
diff --git a/organize_codebase.py b/organize_codebase.py
new file mode 100644
index 000000000..d12d4f660
--- /dev/null
+++ b/organize_codebase.py
@@ -0,0 +1,234 @@
+#!/usr/bin/env python3
+"""
+Codebase Organizer Script
+
+This script helps organize a codebase by analyzing file contents and moving
+related files into appropriate directories based on their functionality.
+"""
+
+import os
+import re
+import shutil
+from pathlib import Path
+from typing import Dict, List, Set, Tuple
+
+# Define categories and their related patterns
+CATEGORIES = {
+ "analyzers": [
+ r"analyzer", r"analysis", r"analyze"
+ ],
+ "code_quality": [
+ r"code_quality", r"quality", r"lint"
+ ],
+ "context": [
+ r"context", r"codebase_context"
+ ],
+ "dependencies": [
+ r"dependenc", r"import"
+ ],
+ "issues": [
+ r"issue", r"error"
+ ],
+ "visualization": [
+ r"visual", r"display", r"render"
+ ],
+}
+
+def read_file_content(file_path: str) -> str:
+ """Read the content of a file."""
+ try:
+ with open(file_path, 'r', encoding='utf-8') as f:
+ return f.read()
+ except Exception as e:
+ print(f"Error reading {file_path}: {e}")
+ return ""
+
+def categorize_file(file_path: str, categories: Dict[str, List[str]]) -> List[str]:
+ """Categorize a file based on its content and name."""
+ file_categories = []
+ content = read_file_content(file_path)
+ filename = os.path.basename(file_path)
+
+ # Check filename and content against category patterns
+ for category, patterns in categories.items():
+ for pattern in patterns:
+ if re.search(pattern, filename, re.IGNORECASE) or re.search(pattern, content, re.IGNORECASE):
+ file_categories.append(category)
+ break
+
+ return file_categories
+
+def analyze_imports(file_path: str) -> Set[str]:
+ """Analyze imports in a Python file."""
+ imports = set()
+ content = read_file_content(file_path)
+
+ # Find import statements
+ import_patterns = [
+ r'import\s+([a-zA-Z0-9_\.]+)',
+ r'from\s+([a-zA-Z0-9_\.]+)\s+import'
+ ]
+
+ for pattern in import_patterns:
+ for match in re.finditer(pattern, content):
+ imports.add(match.group(1))
+
+ return imports
+
+def build_dependency_graph(files: List[str]) -> Dict[str, Set[str]]:
+ """Build a dependency graph for the files."""
+ graph = {}
+ module_to_file = {}
+
+ # Map module names to files
+ for file_path in files:
+ if not file_path.endswith('.py'):
+ continue
+
+ module_name = os.path.splitext(os.path.basename(file_path))[0]
+ module_to_file[module_name] = file_path
+
+ # Build the graph
+ for file_path in files:
+ if not file_path.endswith('.py'):
+ continue
+
+ imports = analyze_imports(file_path)
+ graph[file_path] = set()
+
+ for imp in imports:
+ # Check if this is a local import
+ parts = imp.split('.')
+ if parts[0] in module_to_file:
+ graph[file_path].add(module_to_file[parts[0]])
+
+ return graph
+
+def find_related_files(graph: Dict[str, Set[str]], file_path: str) -> Set[str]:
+ """Find files related to the given file based on the dependency graph."""
+ related = set()
+
+ # Files that this file imports
+ if file_path in graph:
+ related.update(graph[file_path])
+
+ # Files that import this file
+ for other_file, deps in graph.items():
+ if file_path in deps:
+ related.add(other_file)
+
+ return related
+
+def organize_files(directory: str, dry_run: bool = True) -> Dict[str, List[str]]:
+ """
+ Organize files in the directory into categories.
+
+ Args:
+ directory: The directory containing the files to organize
+ dry_run: If True, only print the planned changes without making them
+
+ Returns:
+ A dictionary mapping categories to lists of files
+ """
+ # Get all Python files
+ py_files = [os.path.join(directory, f) for f in os.listdir(directory)
+ if f.endswith('.py') and os.path.isfile(os.path.join(directory, f))]
+
+ # Build dependency graph
+ graph = build_dependency_graph(py_files)
+
+ # Categorize files
+ categorized_files = {}
+ for category in CATEGORIES:
+ categorized_files[category] = []
+
+ # Special case for README and init files
+ categorized_files["root"] = []
+
+ for file_path in py_files:
+ filename = os.path.basename(file_path)
+
+ # Keep some files in the root directory
+ if filename in ['__init__.py', 'README.md']:
+ categorized_files["root"].append(file_path)
+ continue
+
+ # Categorize the file
+ categories = categorize_file(file_path, CATEGORIES)
+
+ if not categories:
+ # If no category found, use related files to determine category
+ related = find_related_files(graph, file_path)
+ for related_file in related:
+ related_categories = categorize_file(related_file, CATEGORIES)
+ categories.extend(related_categories)
+
+ # Remove duplicates
+ categories = list(set(categories))
+
+ if not categories:
+ # If still no category, put in a default category based on filename
+ if "analyzer" in filename:
+ categories = ["analyzers"]
+ elif "context" in filename:
+ categories = ["context"]
+ elif "issue" in filename or "error" in filename:
+ categories = ["issues"]
+ elif "visual" in filename:
+ categories = ["visualization"]
+ elif "depend" in filename:
+ categories = ["dependencies"]
+ elif "quality" in filename:
+ categories = ["code_quality"]
+ else:
+ # Default to analyzers if nothing else matches
+ categories = ["analyzers"]
+
+ # Use the first category (most relevant)
+ primary_category = categories[0]
+ categorized_files[primary_category].append(file_path)
+
+ # Print and execute the organization plan
+ for category, files in categorized_files.items():
+ if not files:
+ continue
+
+ print(f"\nCategory: {category}")
+ for file_path in files:
+ print(f" - {os.path.basename(file_path)}")
+
+ if not dry_run and category != "root":
+ # Create the category directory if it doesn't exist
+ category_dir = os.path.join(directory, category)
+ os.makedirs(category_dir, exist_ok=True)
+
+ # Move files to the category directory
+ for file_path in files:
+ if category != "root":
+ dest_path = os.path.join(category_dir, os.path.basename(file_path))
+ shutil.move(file_path, dest_path)
+ print(f" Moved to {dest_path}")
+
+ return categorized_files
+
+def main():
+ """Main function to organize the codebase."""
+ import argparse
+
+ parser = argparse.ArgumentParser(description='Organize a codebase by categorizing files.')
+ parser.add_argument('directory', help='The directory containing the files to organize')
+ parser.add_argument('--execute', action='store_true', help='Execute the organization plan (default is dry run)')
+
+ args = parser.parse_args()
+
+ print(f"Analyzing files in {args.directory}...")
+ organize_files(args.directory, dry_run=not args.execute)
+
+ if not args.execute:
+ print("\nThis was a dry run. Use --execute to actually move the files.")
+ else:
+ print("\nFiles have been organized.")
+
+if __name__ == "__main__":
+ main()
+
diff --git a/organize_specific_codebase.py b/organize_specific_codebase.py
new file mode 100644
index 000000000..cfe8f534d
--- /dev/null
+++ b/organize_specific_codebase.py
@@ -0,0 +1,156 @@
+#!/usr/bin/env python3
+"""
+Specific Codebase Organizer
+
+This script organizes the specific codebase structure shown in the screenshot,
+with 5 folders and 21 Python files in the root directory.
+"""
+
+import os
+import re
+import shutil
+from pathlib import Path
+from typing import Dict, List, Set
+
+# Define the organization structure based on the files in the screenshot
+ORGANIZATION_PLAN = {
+ "analyzers": [
+ "analyzer.py",
+ "analyzer_manager.py",
+ "base_analyzer.py",
+ "code_quality_analyzer.py",
+ "codebase_analyzer.py",
+ "dependency_analyzer.py",
+ "error_analyzer.py",
+ "unified_analyzer.py"
+ ],
+ "code_quality": [
+ "code_quality.py"
+ ],
+ "context": [
+ "codebase_context.py",
+ "context_codebase.py",
+ "current_code_codebase.py"
+ ],
+ "issues": [
+ "issue_analyzer.py",
+ "issue_types.py",
+ "issues.py"
+ ],
+ "dependencies": [
+ "dependencies.py"
+ ],
+ # Files to keep in root
+ "root": [
+ "__init__.py",
+ "api.py",
+ "README.md"
+ ]
+}
+
+def organize_specific_codebase(directory: str, dry_run: bool = True) -> None:
+ """
+ Organize the specific codebase structure.
+
+ Args:
+ directory: The directory containing the files to organize
+ dry_run: If True, only print the planned changes without making them
+ """
+ print(f"Organizing codebase in {directory}...")
+
+ # Create directories if they don't exist (unless dry run)
+ if not dry_run:
+ for category in ORGANIZATION_PLAN:
+ if category != "root":
+ os.makedirs(os.path.join(directory, category), exist_ok=True)
+
+ # Process each file according to the plan
+ for category, files in ORGANIZATION_PLAN.items():
+ print(f"\nCategory: {category}")
+
+ for filename in files:
+ source_path = os.path.join(directory, filename)
+
+ # Skip if file doesn't exist
+ if not os.path.exists(source_path):
+ print(f" - {filename} (not found, skipping)")
+ continue
+
+ print(f" - {filename}")
+
+ # Move the file if not a dry run and not in root category
+ if not dry_run and category != "root":
+ dest_path = os.path.join(directory, category, filename)
+ shutil.move(source_path, dest_path)
+ print(f" Moved to {dest_path}")
+
+ # Handle any remaining Python files not explicitly categorized
+ all_planned_files = [f for files in ORGANIZATION_PLAN.values() for f in files]
+ remaining_files = [f for f in os.listdir(directory)
+ if f.endswith('.py') and os.path.isfile(os.path.join(directory, f))
+ and f not in all_planned_files]
+
+ if remaining_files:
+ print("\nRemaining Python files (not categorized):")
+ for filename in remaining_files:
+ print(f" - {filename}")
+
+ # Try to categorize based on filename
+ if "analyzer" in filename.lower():
+ category = "analyzers"
+ elif "context" in filename.lower() or "codebase" in filename.lower():
+ category = "context"
+ elif "visual" in filename.lower():
+ category = "visualization"
+ elif "issue" in filename.lower() or "error" in filename.lower():
+ category = "issues"
+ elif "depend" in filename.lower():
+ category = "dependencies"
+ elif "quality" in filename.lower():
+ category = "code_quality"
+ else:
+ # Default to analyzers
+ category = "analyzers"
+
+ print(f" Suggested category: {category}")
+
+ # Move the file if not a dry run
+ if not dry_run:
+ os.makedirs(os.path.join(directory, category), exist_ok=True)
+ dest_path = os.path.join(directory, category, filename)
+ shutil.move(os.path.join(directory, filename), dest_path)
+ print(f" Moved to {dest_path}")
+
+def main():
+ """Main function to organize the specific codebase."""
+ import argparse
+
+ parser = argparse.ArgumentParser(description='Organize the specific codebase structure.')
+ parser.add_argument('directory', help='The directory containing the files to organize')
+ parser.add_argument('--execute', action='store_true', help='Execute the organization plan (default is dry run)')
+
+ args = parser.parse_args()
+
+ organize_specific_codebase(args.directory, dry_run=not args.execute)
+
+ if not args.execute:
+ print("\nThis was a dry run. Use --execute to actually move the files.")
+ else:
+ print("\nFiles have been organized according to the plan.")
+
+ print("\nAfter organizing, you may need to update imports in your code.")
+ print("You can use the Codegen SDK to automatically update imports:")
+ print("""
+ # Example code to update imports after moving files
+ from codegen.sdk import Codebase
+
+ # Initialize the codebase
+ codebase = Codebase("path/to/your/codebase")
+
+ # Commit the changes to ensure the codebase is up-to-date
+ codebase.commit()
+ """)
+
+if __name__ == "__main__":
+ main()
+
diff --git a/organize_with_codegen_sdk.py b/organize_with_codegen_sdk.py
new file mode 100644
index 000000000..263947c1b
--- /dev/null
+++ b/organize_with_codegen_sdk.py
@@ -0,0 +1,203 @@
+#!/usr/bin/env python3
+"""
+Codebase Organizer using Codegen SDK
+
+This script uses the Codegen SDK to programmatically organize a codebase by
+moving symbols between files and updating imports automatically.
+"""
+
+import os
+import sys
+from typing import Dict, List, Set, Optional
+
+try:
+ from codegen.sdk import Codebase
+except ImportError:
+ print("Error: Codegen SDK not found. Please install it with:")
+ print("pip install codegen-sdk")
+ sys.exit(1)
+
+# Define the organization structure based on the files in the screenshot
+ORGANIZATION_PLAN = {
+ "analyzers": [
+ "analyzer.py",
+ "analyzer_manager.py",
+ "base_analyzer.py",
+ "code_quality_analyzer.py",
+ "codebase_analyzer.py",
+ "dependency_analyzer.py",
+ "error_analyzer.py",
+ "unified_analyzer.py"
+ ],
+ "code_quality": [
+ "code_quality.py"
+ ],
+ "context": [
+ "codebase_context.py",
+ "context_codebase.py",
+ "current_code_codebase.py"
+ ],
+ "issues": [
+ "issue_analyzer.py",
+ "issue_types.py",
+ "issues.py"
+ ],
+ "dependencies": [
+ "dependencies.py"
+ ],
+ # Files to keep in root
+ "root": [
+ "__init__.py",
+ "api.py",
+ "README.md"
+ ]
+}
+
+def organize_with_codegen_sdk(directory: str, dry_run: bool = True) -> None:
+ """
+ Organize the codebase using Codegen SDK.
+
+ Args:
+ directory: The directory containing the files to organize
+ dry_run: If True, only print the planned changes without making them
+ """
+ print(f"Organizing codebase in {directory} using Codegen SDK...")
+
+ # Initialize the codebase
+ codebase = Codebase(directory)
+
+ # Create directories if they don't exist (unless dry run)
+ if not dry_run:
+ for category in ORGANIZATION_PLAN:
+ if category != "root":
+ os.makedirs(os.path.join(directory, category), exist_ok=True)
+
+ # Process each file according to the plan
+ for category, files in ORGANIZATION_PLAN.items():
+ if category == "root":
+ continue # Skip files that should stay in root
+
+ print(f"\nCategory: {category}")
+
+ for filename in files:
+ source_path = os.path.join(directory, filename)
+
+ # Skip if file doesn't exist
+ if not os.path.exists(source_path):
+ print(f" - {filename} (not found, skipping)")
+ continue
+
+ print(f" - {filename}")
+
+ # Move the file if not a dry run
+ if not dry_run:
+ try:
+ # Get the source file
+ source_file = codebase.get_file(filename)
+
+ # Create the destination file path
+ dest_path = os.path.join(category, filename)
+
+ # Create the destination file if it doesn't exist
+ if not os.path.exists(os.path.join(directory, dest_path)):
+ dest_file = codebase.create_file(dest_path)
+ else:
+ dest_file = codebase.get_file(dest_path)
+
+ # Move all symbols from source to destination
+ for symbol in source_file.symbols:
+ print(f" Moving symbol: {symbol.name}")
+ symbol.move_to_file(
+ dest_file,
+ include_dependencies=True,
+ strategy="update_all_imports"
+ )
+
+ # Commit changes to ensure the codebase is up-to-date
+ codebase.commit()
+
+ print(f" Moved to {dest_path} with imports updated")
+ except Exception as e:
+ print(f" Error moving {filename}: {e}")
+
+ # Handle any remaining Python files not explicitly categorized
+ all_planned_files = [f for files in ORGANIZATION_PLAN.values() for f in files]
+ remaining_files = [f for f in os.listdir(directory)
+ if f.endswith('.py') and os.path.isfile(os.path.join(directory, f))
+ and f not in all_planned_files]
+
+ if remaining_files:
+ print("\nRemaining Python files (not categorized):")
+ for filename in remaining_files:
+ print(f" - {filename}")
+
+ # Try to categorize based on filename
+ if "analyzer" in filename.lower():
+ category = "analyzers"
+ elif "context" in filename.lower() or "codebase" in filename.lower():
+ category = "context"
+ elif "visual" in filename.lower():
+ category = "visualization"
+ elif "issue" in filename.lower() or "error" in filename.lower():
+ category = "issues"
+ elif "depend" in filename.lower():
+ category = "dependencies"
+ elif "quality" in filename.lower():
+ category = "code_quality"
+ else:
+ # Default to analyzers
+ category = "analyzers"
+
+ print(f" Suggested category: {category}")
+
+ # Move the file if not a dry run
+ if not dry_run:
+ try:
+ # Get the source file
+ source_file = codebase.get_file(filename)
+
+ # Create the destination file path
+ dest_path = os.path.join(category, filename)
+
+ # Create the destination file if it doesn't exist
+ if not os.path.exists(os.path.join(directory, dest_path)):
+ dest_file = codebase.create_file(dest_path)
+ else:
+ dest_file = codebase.get_file(dest_path)
+
+ # Move all symbols from source to destination
+ for symbol in source_file.symbols:
+ print(f" Moving symbol: {symbol.name}")
+ symbol.move_to_file(
+ dest_file,
+ include_dependencies=True,
+ strategy="update_all_imports"
+ )
+
+ # Commit changes to ensure the codebase is up-to-date
+ codebase.commit()
+
+ print(f" Moved to {dest_path} with imports updated")
+ except Exception as e:
+ print(f" Error moving {filename}: {e}")
+
+def main():
+ """Main function to organize the codebase using Codegen SDK."""
+ import argparse
+
+ parser = argparse.ArgumentParser(description='Organize the codebase using Codegen SDK.')
+ parser.add_argument('directory', help='The directory containing the files to organize')
+ parser.add_argument('--execute', action='store_true', help='Execute the organization plan (default is dry run)')
+
+ args = parser.parse_args()
+
+ organize_with_codegen_sdk(args.directory, dry_run=not args.execute)
+
+ if not args.execute:
+ print("\nThis was a dry run. Use --execute to actually move the files.")
+ else:
+ print("\nFiles have been organized according to the plan.")
+
+if __name__ == "__main__":
+ main()
+
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 000000000..6a778d879
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,5 @@
+codegen-sdk>=0.1.0
+networkx>=2.6.3
+matplotlib>=3.5.1
+rich>=12.0.0
+
diff --git a/src/codegen/__init__.py b/src/codegen/__init__.py
index 1b9b91d17..d3244eb5d 100644
--- a/src/codegen/__init__.py
+++ b/src/codegen/__init__.py
@@ -1,11 +1,8 @@
-from codegen.agents.code_agent import CodeAgent
+from codegen.agents.agent import Agent
from codegen.cli.sdk.decorator import function
from codegen.cli.sdk.functions import Function
from codegen.extensions.events.codegen_app import CodegenApp
-
-# from codegen.extensions.index.file_index import FileIndex
-# from codegen.extensions.langchain.agent import create_agent_with_tools, create_codebase_agent
from codegen.sdk.core.codebase import Codebase
from codegen.shared.enums.programming_language import ProgrammingLanguage
-__all__ = ["CodeAgent", "Codebase", "CodegenApp", "Function", "ProgrammingLanguage", "function"]
+__all__ = ["Agent", "Codebase", "CodegenApp", "Function", "ProgrammingLanguage", "function"]
diff --git a/src/codegen/agents/README.md b/src/codegen/agents/README.md
new file mode 100644
index 000000000..254ed4bc9
--- /dev/null
+++ b/src/codegen/agents/README.md
@@ -0,0 +1,124 @@
+# Codegen Agents - Python SDK
+
+This module provides a Python client for interacting with the Codegen AI agents API.
+
+## Installation
+
+The Codegen Agent SDK is included as part of the Codegen package. Ensure you have the latest version installed:
+
+```bash
+pip install codegen
+```
+
+## Usage
+
+### Basic Example
+
+```python
+from codegen.agents.agent import Agent
+
+# Initialize the Agent with your organization ID and API token
+agent = Agent(
+ org_id="11", # Your organization ID
+ token="your_api_token_here", # Your API authentication token
+ base_url="https://codegen-sh-rest-api.modal.run", # Optional - defaults to this URL
+)
+
+# Run an agent with a prompt
+task = agent.run(prompt="Which github repos can you currently access?")
+
+# Check the initial status
+print(task.status) # Returns the current status of the task (e.g., "queued", "in_progress", etc.)
+
+# Refresh the task to get updated status
+task.refresh()
+
+# Check the updated status
+print(task.status)
+
+# Once task is complete, you can access the result
+if task.status == "completed":
+ print(task.result)
+```
+
+### Agent Class
+
+The `Agent` class is the main entry point for interacting with Codegen AI agents:
+
+```python
+Agent(token: str, org_id: Optional[int] = None, base_url: Optional[str] = CODEGEN_BASE_API_URL)
+```
+
+Parameters:
+
+- `token` (required): Your API authentication token
+- `org_id` (optional): Your organization ID. If not provided, defaults to environment variable `CODEGEN_ORG_ID` or "1"
+- `base_url` (optional): API base URL. Defaults to "https://codegen-sh-rest-api.modal.run"
+
+### Methods
+
+#### run()
+
+```python
+run(prompt: str) -> AgentTask
+```
+
+Runs an agent with the given prompt.
+
+Parameters:
+
+- `prompt` (required): The instruction for the agent to execute
+
+Returns:
+
+- An `AgentTask` object representing the running task
+
+#### get_status()
+
+```python
+get_status() -> Optional[Dict[str, Any]]
+```
+
+Gets the status of the current task.
+
+Returns:
+
+- A dictionary containing task status information (`id`, `status`, `result`), or `None` if no task has been run
+
+### AgentTask Class
+
+The `AgentTask` class represents a running or completed agent task:
+
+#### Attributes
+
+- `id`: The unique identifier for the task
+- `org_id`: The organization ID
+- `status`: Current status of the task (e.g., "queued", "in_progress", "completed", "failed")
+- `result`: The task result (available when status is "completed")
+
+#### Methods
+
+##### refresh()
+
+```python
+refresh() -> None
+```
+
+Refreshes the task status from the API.
+
+## Environment Variables
+
+- `CODEGEN_ORG_ID`: Default organization ID (used if `org_id` is not provided)
+
+## Error Handling
+
+Handle potential API errors using standard try/except blocks:
+
+```python
+try:
+ task = agent.run(prompt="Your prompt here")
+ task.refresh()
+ print(task.status)
+except Exception as e:
+ print(f"Error: {e}")
+```
diff --git a/src/codegen/agents/__init__.py b/src/codegen/agents/__init__.py
index e69de29bb..d428226e3 100644
--- a/src/codegen/agents/__init__.py
+++ b/src/codegen/agents/__init__.py
@@ -0,0 +1,5 @@
+"""Codegen Agent API module."""
+
+from codegen.agents.agent import Agent
+
+__all__ = ["Agent"]
diff --git a/src/codegen/agents/agent.py b/src/codegen/agents/agent.py
new file mode 100644
index 000000000..3f50bb87f
--- /dev/null
+++ b/src/codegen/agents/agent.py
@@ -0,0 +1,94 @@
+import os
+from typing import Any, Optional
+
+from codegen.agents.client.openapi_client.api.agents_api import AgentsApi
+from codegen.agents.client.openapi_client.api_client import ApiClient
+from codegen.agents.client.openapi_client.configuration import Configuration
+from codegen.agents.client.openapi_client.models.agent_run_response import AgentRunResponse
+from codegen.agents.client.openapi_client.models.create_agent_run_input import CreateAgentRunInput
+from codegen.agents.constants import CODEGEN_BASE_API_URL
+
+
+class AgentTask:
+ """Represents an agent run job."""
+
+ def __init__(self, task_data: AgentRunResponse, api_client: ApiClient, org_id: int):
+ self.id = task_data.id
+ self.org_id = org_id
+ self.status = task_data.status
+ self.result = task_data.result
+ self.web_url = task_data.web_url
+ self._api_client = api_client
+ self._agents_api = AgentsApi(api_client)
+
+ def refresh(self) -> None:
+ """Refresh the job status from the API."""
+ if self.id is None:
+ return
+
+ job_data = self._agents_api.get_agent_run_v1_organizations_org_id_agent_run_agent_run_id_get(
+ agent_run_id=int(self.id), org_id=int(self.org_id), authorization=f"Bearer {self._api_client.configuration.access_token}"
+ )
+
+ # Convert API response to dict for attribute access
+ job_dict = {}
+ if hasattr(job_data, "__dict__"):
+ job_dict = job_data.__dict__
+ elif isinstance(job_data, dict):
+ job_dict = job_data
+
+ self.status = job_dict.get("status")
+ self.result = job_dict.get("result")
+
+
+class Agent:
+ """API client for interacting with Codegen AI agents."""
+
+ def __init__(self, token: str, org_id: Optional[int] = None, base_url: Optional[str] = CODEGEN_BASE_API_URL):
+ """Initialize a new Agent client.
+
+ Args:
+ token: API authentication token
+ org_id: Optional organization ID. If not provided, default org will be used.
+ """
+ self.token = token
+ self.org_id = org_id or int(os.environ.get("CODEGEN_ORG_ID", "1")) # Default to org ID 1 if not specified
+
+ # Configure API client
+ config = Configuration(host=base_url, access_token=token)
+ self.api_client = ApiClient(configuration=config)
+ self.agents_api = AgentsApi(self.api_client)
+
+ # Current job
+ self.current_job = None
+
+ def run(self, prompt: str) -> AgentTask:
+ """Run an agent with the given prompt.
+
+ Args:
+ prompt: The instruction for the agent to execute
+
+ Returns:
+ Job: A job object representing the agent run
+ """
+ run_input = CreateAgentRunInput(prompt=prompt)
+ agent_run_response = self.agents_api.create_agent_run_v1_organizations_org_id_agent_run_post(
+ org_id=int(self.org_id), create_agent_run_input=run_input, authorization=f"Bearer {self.token}", _headers={"Content-Type": "application/json"}
+ )
+ # Convert API response to dict for Job initialization
+
+ job = AgentTask(agent_run_response, self.api_client, self.org_id)
+ self.current_job = job
+ return job
+
+ def get_status(self) -> Optional[dict[str, Any]]:
+ """Get the status of the current job.
+
+ Returns:
+ dict: A dictionary containing job status information,
+ or None if no job has been run.
+ """
+ if self.current_job:
+ self.current_job.refresh()
+ return {"id": self.current_job.id, "status": self.current_job.status, "result": self.current_job.result, "web_url": self.current_job.web_url}
+ return None
diff --git a/src/codegen/agents/client/.openapi-generator/FILES b/src/codegen/agents/client/.openapi-generator/FILES
new file mode 100644
index 000000000..5aa764c59
--- /dev/null
+++ b/src/codegen/agents/client/.openapi-generator/FILES
@@ -0,0 +1,62 @@
+.github/workflows/python.yml
+.gitignore
+.gitlab-ci.yml
+.openapi-generator-ignore
+.travis.yml
+README.md
+docs/AgentRunResponse.md
+docs/AgentsApi.md
+docs/CreateAgentRunInput.md
+docs/HTTPValidationError.md
+docs/OrganizationResponse.md
+docs/OrganizationSettings.md
+docs/OrganizationsApi.md
+docs/PageOrganizationResponse.md
+docs/PageUserResponse.md
+docs/UserResponse.md
+docs/UsersApi.md
+docs/ValidationError.md
+docs/ValidationErrorLocInner.md
+git_push.sh
+openapi_client/__init__.py
+openapi_client/api/__init__.py
+openapi_client/api/agents_api.py
+openapi_client/api/organizations_api.py
+openapi_client/api/users_api.py
+openapi_client/api_client.py
+openapi_client/api_response.py
+openapi_client/configuration.py
+openapi_client/exceptions.py
+openapi_client/models/__init__.py
+openapi_client/models/agent_run_response.py
+openapi_client/models/create_agent_run_input.py
+openapi_client/models/http_validation_error.py
+openapi_client/models/organization_response.py
+openapi_client/models/organization_settings.py
+openapi_client/models/page_organization_response.py
+openapi_client/models/page_user_response.py
+openapi_client/models/user_response.py
+openapi_client/models/validation_error.py
+openapi_client/models/validation_error_loc_inner.py
+openapi_client/py.typed
+openapi_client/rest.py
+pyproject.toml
+requirements.txt
+setup.cfg
+setup.py
+test-requirements.txt
+test/__init__.py
+test/test_agent_run_response.py
+test/test_agents_api.py
+test/test_create_agent_run_input.py
+test/test_http_validation_error.py
+test/test_organization_response.py
+test/test_organization_settings.py
+test/test_organizations_api.py
+test/test_page_organization_response.py
+test/test_page_user_response.py
+test/test_user_response.py
+test/test_users_api.py
+test/test_validation_error.py
+test/test_validation_error_loc_inner.py
+tox.ini
diff --git a/src/codegen/agents/client/.openapi-generator/VERSION b/src/codegen/agents/client/.openapi-generator/VERSION
new file mode 100644
index 000000000..5f84a81db
--- /dev/null
+++ b/src/codegen/agents/client/.openapi-generator/VERSION
@@ -0,0 +1 @@
+7.12.0
diff --git a/src/codegen/agents/client/README.md b/src/codegen/agents/client/README.md
new file mode 100644
index 000000000..3dbaa3a7d
--- /dev/null
+++ b/src/codegen/agents/client/README.md
@@ -0,0 +1,22 @@
+# openapi-client
+
+API for application developers
+
+This Python directory was automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project. However it the generated code was altered to make compatible with the rest of the project.
+
+- API version: 1.0.0
+
+### Steps to update client directory
+
+1. Fetch the api schema from the API endpoint \[https://codegen-sh--rest-api.modal.run/api/openapi.json\](schema file)
+1. generate the client code with the following command:
+
+```bash
+openapi-generator generate -i openapi.yaml -g python -o ./client
+```
+
+3. This command will generate a lot of unused files we just need to include the files in the `openapi_client` directory to the project.
+
+1. May need to fix the imports for `openapi_client` to be fully qualified import paths.
+
+1. TODO: make updates more streamlined. Ideally setup this api client as it's own package so all it takes is to generate the new code, no addtional manual steps are needed.
diff --git a/src/codegen/agents/client/openapi_client/__init__.py b/src/codegen/agents/client/openapi_client/__init__.py
new file mode 100644
index 000000000..83f920a03
--- /dev/null
+++ b/src/codegen/agents/client/openapi_client/__init__.py
@@ -0,0 +1,44 @@
+# coding: utf-8
+
+# flake8: noqa
+
+"""
+Developer API
+
+API for application developers
+
+The version of the OpenAPI document: 1.0.0
+Generated by OpenAPI Generator (https://openapi-generator.tech)
+
+Do not edit the class manually.
+""" # noqa: E501
+
+__version__ = "1.0.0"
+
+# import apis into sdk package
+from codegen.agents.client.openapi_client.api.agents_api import AgentsApi
+from codegen.agents.client.openapi_client.api.organizations_api import OrganizationsApi
+from codegen.agents.client.openapi_client.api.users_api import UsersApi
+
+# import ApiClient
+from codegen.agents.client.openapi_client.api_response import ApiResponse
+from codegen.agents.client.openapi_client.api_client import ApiClient
+from codegen.agents.client.openapi_client.configuration import Configuration
+from codegen.agents.client.openapi_client.exceptions import OpenApiException
+from codegen.agents.client.openapi_client.exceptions import ApiTypeError
+from codegen.agents.client.openapi_client.exceptions import ApiValueError
+from codegen.agents.client.openapi_client.exceptions import ApiKeyError
+from codegen.agents.client.openapi_client.exceptions import ApiAttributeError
+from codegen.agents.client.openapi_client.exceptions import ApiException
+
+# import models into sdk package
+from codegen.agents.client.openapi_client.models.agent_run_response import AgentRunResponse
+from codegen.agents.client.openapi_client.models.create_agent_run_input import CreateAgentRunInput
+from codegen.agents.client.openapi_client.models.http_validation_error import HTTPValidationError
+from codegen.agents.client.openapi_client.models.organization_response import OrganizationResponse
+from codegen.agents.client.openapi_client.models.organization_settings import OrganizationSettings
+from codegen.agents.client.openapi_client.models.page_organization_response import PageOrganizationResponse
+from codegen.agents.client.openapi_client.models.page_user_response import PageUserResponse
+from codegen.agents.client.openapi_client.models.user_response import UserResponse
+from codegen.agents.client.openapi_client.models.validation_error import ValidationError
+from codegen.agents.client.openapi_client.models.validation_error_loc_inner import ValidationErrorLocInner
diff --git a/src/codegen/agents/client/openapi_client/api/__init__.py b/src/codegen/agents/client/openapi_client/api/__init__.py
new file mode 100644
index 000000000..952fb6199
--- /dev/null
+++ b/src/codegen/agents/client/openapi_client/api/__init__.py
@@ -0,0 +1,6 @@
+# flake8: noqa
+
+# import apis into api package
+from codegen.agents.client.openapi_client.api.agents_api import AgentsApi
+from codegen.agents.client.openapi_client.api.organizations_api import OrganizationsApi
+from codegen.agents.client.openapi_client.api.users_api import UsersApi
diff --git a/src/codegen/agents/client/openapi_client/api/agents_api.py b/src/codegen/agents/client/openapi_client/api/agents_api.py
new file mode 100644
index 000000000..73c570738
--- /dev/null
+++ b/src/codegen/agents/client/openapi_client/api/agents_api.py
@@ -0,0 +1,1460 @@
+"""Developer API
+
+API for application developers
+
+The version of the OpenAPI document: 1.0.0
+Generated by OpenAPI Generator (https://openapi-generator.tech)
+
+Do not edit the class manually.
+"""
+
+from typing import Annotated, Any, Optional, Union
+
+from pydantic import Field, StrictFloat, StrictInt, StrictStr, validate_call
+
+from codegen.agents.client.openapi_client.api_client import ApiClient, RequestSerialized
+from codegen.agents.client.openapi_client.api_response import ApiResponse
+from codegen.agents.client.openapi_client.models.agent_run_response import AgentRunResponse
+from codegen.agents.client.openapi_client.models.create_agent_run_input import CreateAgentRunInput
+from codegen.agents.client.openapi_client.rest import RESTResponseType
+
+
+class AgentsApi:
+ """NOTE: This class is auto generated by OpenAPI Generator
+ Ref: https://openapi-generator.tech
+
+ Do not edit the class manually.
+ """
+
+ def __init__(self, api_client=None) -> None:
+ if api_client is None:
+ api_client = ApiClient.get_default()
+ self.api_client = api_client
+
+ @validate_call
+ def create_agent_run_v1_organizations_org_id_agent_run_post(
+ self,
+ org_id: StrictInt,
+ create_agent_run_input: CreateAgentRunInput,
+ authorization: Optional[Any] = None,
+ _request_timeout: Union[None, Annotated[StrictFloat, Field(gt=0)], tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]]] = None,
+ _request_auth: Optional[dict[StrictStr, Any]] = None,
+ _content_type: Optional[StrictStr] = None,
+ _headers: Optional[dict[StrictStr, Any]] = None,
+ _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
+ ) -> AgentRunResponse:
+ """Create Agent Run
+
+ Create a new agent run. Creates and initiates a long-running agent process based on the provided prompt. The process will complete asynchronously, and the response contains the agent run ID which can be used to check the status later. The requesting user must be a member of the specified organization.
+
+ :param org_id: (required)
+ :type org_id: int
+ :param create_agent_run_input: (required)
+ :type create_agent_run_input: CreateAgentRunInput
+ :param authorization:
+ :type authorization: object
+ :param _request_timeout: timeout setting for this request. If one
+ number provided, it will be total request
+ timeout. It can also be a pair (tuple) of
+ (connection, read) timeouts.
+ :type _request_timeout: int, tuple(int, int), optional
+ :param _request_auth: set to override the auth_settings for an a single
+ request; this effectively ignores the
+ authentication in the spec for a single request.
+ :type _request_auth: dict, optional
+ :param _content_type: force content-type for the request.
+ :type _content_type: str, Optional
+ :param _headers: set to override the headers for a single
+ request; this effectively ignores the headers
+ in the spec for a single request.
+ :type _headers: dict, optional
+ :param _host_index: set to override the host_index for a single
+ request; this effectively ignores the host_index
+ in the spec for a single request.
+ :type _host_index: int, optional
+ :return: Returns the result object.
+ """ # noqa: E501
+ _param = self._create_agent_run_v1_organizations_org_id_agent_run_post_serialize(
+ org_id=org_id,
+ create_agent_run_input=create_agent_run_input,
+ authorization=authorization,
+ _request_auth=_request_auth,
+ _content_type=_content_type,
+ _headers=_headers,
+ _host_index=_host_index,
+ )
+
+ _response_types_map: dict[str, Optional[str]] = {
+ "200": "AgentRunResponse",
+ "422": "HTTPValidationError",
+ }
+ response_data = self.api_client.call_api(*_param, _request_timeout=_request_timeout)
+ response_data.read()
+ return self.api_client.response_deserialize(
+ response_data=response_data,
+ response_types_map=_response_types_map,
+ ).data
+
+ @validate_call
+ def create_agent_run_v1_organizations_org_id_agent_run_post_with_http_info(
+ self,
+ org_id: StrictInt,
+ create_agent_run_input: CreateAgentRunInput,
+ authorization: Optional[Any] = None,
+ _request_timeout: Union[None, Annotated[StrictFloat, Field(gt=0)], tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]]] = None,
+ _request_auth: Optional[dict[StrictStr, Any]] = None,
+ _content_type: Optional[StrictStr] = None,
+ _headers: Optional[dict[StrictStr, Any]] = None,
+ _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
+ ) -> ApiResponse[AgentRunResponse]:
+ """Create Agent Run
+
+ Create a new agent run. Creates and initiates a long-running agent process based on the provided prompt. The process will complete asynchronously, and the response contains the agent run ID which can be used to check the status later. The requesting user must be a member of the specified organization.
+
+ :param org_id: (required)
+ :type org_id: int
+ :param create_agent_run_input: (required)
+ :type create_agent_run_input: CreateAgentRunInput
+ :param authorization:
+ :type authorization: object
+ :param _request_timeout: timeout setting for this request. If one
+ number provided, it will be total request
+ timeout. It can also be a pair (tuple) of
+ (connection, read) timeouts.
+ :type _request_timeout: int, tuple(int, int), optional
+ :param _request_auth: set to override the auth_settings for an a single
+ request; this effectively ignores the
+ authentication in the spec for a single request.
+ :type _request_auth: dict, optional
+ :param _content_type: force content-type for the request.
+ :type _content_type: str, Optional
+ :param _headers: set to override the headers for a single
+ request; this effectively ignores the headers
+ in the spec for a single request.
+ :type _headers: dict, optional
+ :param _host_index: set to override the host_index for a single
+ request; this effectively ignores the host_index
+ in the spec for a single request.
+ :type _host_index: int, optional
+ :return: Returns the result object.
+ """ # noqa: E501
+ _param = self._create_agent_run_v1_organizations_org_id_agent_run_post_serialize(
+ org_id=org_id,
+ create_agent_run_input=create_agent_run_input,
+ authorization=authorization,
+ _request_auth=_request_auth,
+ _content_type=_content_type,
+ _headers=_headers,
+ _host_index=_host_index,
+ )
+
+ _response_types_map: dict[str, Optional[str]] = {
+ "200": "AgentRunResponse",
+ "422": "HTTPValidationError",
+ }
+ response_data = self.api_client.call_api(*_param, _request_timeout=_request_timeout)
+ response_data.read()
+ return self.api_client.response_deserialize(
+ response_data=response_data,
+ response_types_map=_response_types_map,
+ )
+
+ @validate_call
+ def create_agent_run_v1_organizations_org_id_agent_run_post_without_preload_content(
+ self,
+ org_id: StrictInt,
+ create_agent_run_input: CreateAgentRunInput,
+ authorization: Optional[Any] = None,
+ _request_timeout: Union[None, Annotated[StrictFloat, Field(gt=0)], tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]]] = None,
+ _request_auth: Optional[dict[StrictStr, Any]] = None,
+ _content_type: Optional[StrictStr] = None,
+ _headers: Optional[dict[StrictStr, Any]] = None,
+ _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
+ ) -> RESTResponseType:
+ """Create Agent Run
+
+ Create a new agent run. Creates and initiates a long-running agent process based on the provided prompt. The process will complete asynchronously, and the response contains the agent run ID which can be used to check the status later. The requesting user must be a member of the specified organization.
+
+ :param org_id: (required)
+ :type org_id: int
+ :param create_agent_run_input: (required)
+ :type create_agent_run_input: CreateAgentRunInput
+ :param authorization:
+ :type authorization: object
+ :param _request_timeout: timeout setting for this request. If one
+ number provided, it will be total request
+ timeout. It can also be a pair (tuple) of
+ (connection, read) timeouts.
+ :type _request_timeout: int, tuple(int, int), optional
+ :param _request_auth: set to override the auth_settings for an a single
+ request; this effectively ignores the
+ authentication in the spec for a single request.
+ :type _request_auth: dict, optional
+ :param _content_type: force content-type for the request.
+ :type _content_type: str, Optional
+ :param _headers: set to override the headers for a single
+ request; this effectively ignores the headers
+ in the spec for a single request.
+ :type _headers: dict, optional
+ :param _host_index: set to override the host_index for a single
+ request; this effectively ignores the host_index
+ in the spec for a single request.
+ :type _host_index: int, optional
+ :return: Returns the result object.
+ """ # noqa: E501
+ _param = self._create_agent_run_v1_organizations_org_id_agent_run_post_serialize(
+ org_id=org_id,
+ create_agent_run_input=create_agent_run_input,
+ authorization=authorization,
+ _request_auth=_request_auth,
+ _content_type=_content_type,
+ _headers=_headers,
+ _host_index=_host_index,
+ )
+
+ _response_types_map: dict[str, Optional[str]] = {
+ "200": "AgentRunResponse",
+ "422": "HTTPValidationError",
+ }
+ response_data = self.api_client.call_api(*_param, _request_timeout=_request_timeout)
+ return response_data.response
+
+ def _create_agent_run_v1_organizations_org_id_agent_run_post_serialize(
+ self,
+ org_id,
+ create_agent_run_input,
+ authorization,
+ _request_auth,
+ _content_type,
+ _headers,
+ _host_index,
+ ) -> RequestSerialized:
+ _host = None
+
+ _collection_formats: dict[str, str] = {}
+
+ _path_params: dict[str, str] = {}
+ _query_params: list[tuple[str, str]] = []
+ _header_params: dict[str, Optional[str]] = _headers or {}
+ _form_params: list[tuple[str, str]] = []
+ _files: dict[str, Union[str, bytes, list[str], list[bytes], list[tuple[str, bytes]]]] = {}
+ _body_params: Optional[bytes] = None
+
+ # process the path parameters
+ if org_id is not None:
+ _path_params["org_id"] = org_id
+ # process the query parameters
+ # process the header parameters
+ if authorization is not None:
+ _header_params["authorization"] = authorization
+ # process the form parameters
+ # process the body parameter
+ if create_agent_run_input is not None:
+ _body_params = create_agent_run_input
+
+ # set the HTTP header `Accept`
+ if "Accept" not in _header_params:
+ _header_params["Accept"] = self.api_client.select_header_accept(["application/json"])
+
+ # set the HTTP header `Content-Type`
+ if _content_type:
+ _header_params["Content-Type"] = _content_type
+ else:
+ _default_content_type = self.api_client.select_header_content_type(["application/json"])
+ if _default_content_type is not None:
+ _header_params["Content-Type"] = _default_content_type
+
+ # authentication setting
+ _auth_settings: list[str] = []
+
+ return self.api_client.param_serialize(
+ method="POST",
+ resource_path="/v1/organizations/{org_id}/agent/run",
+ path_params=_path_params,
+ query_params=_query_params,
+ header_params=_header_params,
+ body=_body_params,
+ post_params=_form_params,
+ files=_files,
+ auth_settings=_auth_settings,
+ collection_formats=_collection_formats,
+ _host=_host,
+ _request_auth=_request_auth,
+ )
+
+ @validate_call
+ def create_agent_run_v1_organizations_org_id_agent_run_post_0(
+ self,
+ org_id: StrictInt,
+ create_agent_run_input: CreateAgentRunInput,
+ authorization: Optional[Any] = None,
+ _request_timeout: Union[None, Annotated[StrictFloat, Field(gt=0)], tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]]] = None,
+ _request_auth: Optional[dict[StrictStr, Any]] = None,
+ _content_type: Optional[StrictStr] = None,
+ _headers: Optional[dict[StrictStr, Any]] = None,
+ _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
+ ) -> AgentRunResponse:
+ """Create Agent Run
+
+ Create a new agent run. Creates and initiates a long-running agent process based on the provided prompt. The process will complete asynchronously, and the response contains the agent run ID which can be used to check the status later. The requesting user must be a member of the specified organization.
+
+ :param org_id: (required)
+ :type org_id: int
+ :param create_agent_run_input: (required)
+ :type create_agent_run_input: CreateAgentRunInput
+ :param authorization:
+ :type authorization: object
+ :param _request_timeout: timeout setting for this request. If one
+ number provided, it will be total request
+ timeout. It can also be a pair (tuple) of
+ (connection, read) timeouts.
+ :type _request_timeout: int, tuple(int, int), optional
+ :param _request_auth: set to override the auth_settings for an a single
+ request; this effectively ignores the
+ authentication in the spec for a single request.
+ :type _request_auth: dict, optional
+ :param _content_type: force content-type for the request.
+ :type _content_type: str, Optional
+ :param _headers: set to override the headers for a single
+ request; this effectively ignores the headers
+ in the spec for a single request.
+ :type _headers: dict, optional
+ :param _host_index: set to override the host_index for a single
+ request; this effectively ignores the host_index
+ in the spec for a single request.
+ :type _host_index: int, optional
+ :return: Returns the result object.
+ """ # noqa: E501
+ _param = self._create_agent_run_v1_organizations_org_id_agent_run_post_0_serialize(
+ org_id=org_id,
+ create_agent_run_input=create_agent_run_input,
+ authorization=authorization,
+ _request_auth=_request_auth,
+ _content_type=_content_type,
+ _headers=_headers,
+ _host_index=_host_index,
+ )
+
+ _response_types_map: dict[str, Optional[str]] = {
+ "200": "AgentRunResponse",
+ "422": "HTTPValidationError",
+ }
+ response_data = self.api_client.call_api(*_param, _request_timeout=_request_timeout)
+ response_data.read()
+ return self.api_client.response_deserialize(
+ response_data=response_data,
+ response_types_map=_response_types_map,
+ ).data
+
+ @validate_call
+ def create_agent_run_v1_organizations_org_id_agent_run_post_0_with_http_info(
+ self,
+ org_id: StrictInt,
+ create_agent_run_input: CreateAgentRunInput,
+ authorization: Optional[Any] = None,
+ _request_timeout: Union[None, Annotated[StrictFloat, Field(gt=0)], tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]]] = None,
+ _request_auth: Optional[dict[StrictStr, Any]] = None,
+ _content_type: Optional[StrictStr] = None,
+ _headers: Optional[dict[StrictStr, Any]] = None,
+ _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
+ ) -> ApiResponse[AgentRunResponse]:
+ """Create Agent Run
+
+ Create a new agent run. Creates and initiates a long-running agent process based on the provided prompt. The process will complete asynchronously, and the response contains the agent run ID which can be used to check the status later. The requesting user must be a member of the specified organization.
+
+ :param org_id: (required)
+ :type org_id: int
+ :param create_agent_run_input: (required)
+ :type create_agent_run_input: CreateAgentRunInput
+ :param authorization:
+ :type authorization: object
+ :param _request_timeout: timeout setting for this request. If one
+ number provided, it will be total request
+ timeout. It can also be a pair (tuple) of
+ (connection, read) timeouts.
+ :type _request_timeout: int, tuple(int, int), optional
+ :param _request_auth: set to override the auth_settings for an a single
+ request; this effectively ignores the
+ authentication in the spec for a single request.
+ :type _request_auth: dict, optional
+ :param _content_type: force content-type for the request.
+ :type _content_type: str, Optional
+ :param _headers: set to override the headers for a single
+ request; this effectively ignores the headers
+ in the spec for a single request.
+ :type _headers: dict, optional
+ :param _host_index: set to override the host_index for a single
+ request; this effectively ignores the host_index
+ in the spec for a single request.
+ :type _host_index: int, optional
+ :return: Returns the result object.
+ """ # noqa: E501
+ _param = self._create_agent_run_v1_organizations_org_id_agent_run_post_0_serialize(
+ org_id=org_id,
+ create_agent_run_input=create_agent_run_input,
+ authorization=authorization,
+ _request_auth=_request_auth,
+ _content_type=_content_type,
+ _headers=_headers,
+ _host_index=_host_index,
+ )
+
+ _response_types_map: dict[str, Optional[str]] = {
+ "200": "AgentRunResponse",
+ "422": "HTTPValidationError",
+ }
+ response_data = self.api_client.call_api(*_param, _request_timeout=_request_timeout)
+ response_data.read()
+ return self.api_client.response_deserialize(
+ response_data=response_data,
+ response_types_map=_response_types_map,
+ )
+
+ @validate_call
+ def create_agent_run_v1_organizations_org_id_agent_run_post_0_without_preload_content(
+ self,
+ org_id: StrictInt,
+ create_agent_run_input: CreateAgentRunInput,
+ authorization: Optional[Any] = None,
+ _request_timeout: Union[None, Annotated[StrictFloat, Field(gt=0)], tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]]] = None,
+ _request_auth: Optional[dict[StrictStr, Any]] = None,
+ _content_type: Optional[StrictStr] = None,
+ _headers: Optional[dict[StrictStr, Any]] = None,
+ _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
+ ) -> RESTResponseType:
+ """Create Agent Run
+
+ Create a new agent run. Creates and initiates a long-running agent process based on the provided prompt. The process will complete asynchronously, and the response contains the agent run ID which can be used to check the status later. The requesting user must be a member of the specified organization.
+
+ :param org_id: (required)
+ :type org_id: int
+ :param create_agent_run_input: (required)
+ :type create_agent_run_input: CreateAgentRunInput
+ :param authorization:
+ :type authorization: object
+ :param _request_timeout: timeout setting for this request. If one
+ number provided, it will be total request
+ timeout. It can also be a pair (tuple) of
+ (connection, read) timeouts.
+ :type _request_timeout: int, tuple(int, int), optional
+ :param _request_auth: set to override the auth_settings for an a single
+ request; this effectively ignores the
+ authentication in the spec for a single request.
+ :type _request_auth: dict, optional
+ :param _content_type: force content-type for the request.
+ :type _content_type: str, Optional
+ :param _headers: set to override the headers for a single
+ request; this effectively ignores the headers
+ in the spec for a single request.
+ :type _headers: dict, optional
+ :param _host_index: set to override the host_index for a single
+ request; this effectively ignores the host_index
+ in the spec for a single request.
+ :type _host_index: int, optional
+ :return: Returns the result object.
+ """ # noqa: E501
+ _param = self._create_agent_run_v1_organizations_org_id_agent_run_post_0_serialize(
+ org_id=org_id,
+ create_agent_run_input=create_agent_run_input,
+ authorization=authorization,
+ _request_auth=_request_auth,
+ _content_type=_content_type,
+ _headers=_headers,
+ _host_index=_host_index,
+ )
+
+ _response_types_map: dict[str, Optional[str]] = {
+ "200": "AgentRunResponse",
+ "422": "HTTPValidationError",
+ }
+ response_data = self.api_client.call_api(*_param, _request_timeout=_request_timeout)
+ return response_data.response
+
+ def _create_agent_run_v1_organizations_org_id_agent_run_post_0_serialize(
+ self,
+ org_id,
+ create_agent_run_input,
+ authorization,
+ _request_auth,
+ _content_type,
+ _headers,
+ _host_index,
+ ) -> RequestSerialized:
+ _host = None
+
+ _collection_formats: dict[str, str] = {}
+
+ _path_params: dict[str, str] = {}
+ _query_params: list[tuple[str, str]] = []
+ _header_params: dict[str, Optional[str]] = _headers or {}
+ _form_params: list[tuple[str, str]] = []
+ _files: dict[str, Union[str, bytes, list[str], list[bytes], list[tuple[str, bytes]]]] = {}
+ _body_params: Optional[bytes] = None
+
+ # process the path parameters
+ if org_id is not None:
+ _path_params["org_id"] = org_id
+ # process the query parameters
+ # process the header parameters
+ if authorization is not None:
+ _header_params["authorization"] = authorization
+ # process the form parameters
+ # process the body parameter
+ if create_agent_run_input is not None:
+ _body_params = create_agent_run_input
+
+ # set the HTTP header `Accept`
+ if "Accept" not in _header_params:
+ _header_params["Accept"] = self.api_client.select_header_accept(["application/json"])
+
+ # set the HTTP header `Content-Type`
+ if _content_type:
+ _header_params["Content-Type"] = _content_type
+ else:
+ _default_content_type = self.api_client.select_header_content_type(["application/json"])
+ if _default_content_type is not None:
+ _header_params["Content-Type"] = _default_content_type
+
+ # authentication setting
+ _auth_settings: list[str] = []
+
+ return self.api_client.param_serialize(
+ method="POST",
+ resource_path="/v1/organizations/{org_id}/agent/run",
+ path_params=_path_params,
+ query_params=_query_params,
+ header_params=_header_params,
+ body=_body_params,
+ post_params=_form_params,
+ files=_files,
+ auth_settings=_auth_settings,
+ collection_formats=_collection_formats,
+ _host=_host,
+ _request_auth=_request_auth,
+ )
+
+ @validate_call
+ def create_agent_run_v1_organizations_org_id_agent_run_post_1(
+ self,
+ org_id: StrictInt,
+ create_agent_run_input: CreateAgentRunInput,
+ authorization: Optional[Any] = None,
+ _request_timeout: Union[None, Annotated[StrictFloat, Field(gt=0)], tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]]] = None,
+ _request_auth: Optional[dict[StrictStr, Any]] = None,
+ _content_type: Optional[StrictStr] = None,
+ _headers: Optional[dict[StrictStr, Any]] = None,
+ _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
+ ) -> AgentRunResponse:
+ """Create Agent Run
+
+ Create a new agent run. Creates and initiates a long-running agent process based on the provided prompt. The process will complete asynchronously, and the response contains the agent run ID which can be used to check the status later. The requesting user must be a member of the specified organization.
+
+ :param org_id: (required)
+ :type org_id: int
+ :param create_agent_run_input: (required)
+ :type create_agent_run_input: CreateAgentRunInput
+ :param authorization:
+ :type authorization: object
+ :param _request_timeout: timeout setting for this request. If one
+ number provided, it will be total request
+ timeout. It can also be a pair (tuple) of
+ (connection, read) timeouts.
+ :type _request_timeout: int, tuple(int, int), optional
+ :param _request_auth: set to override the auth_settings for an a single
+ request; this effectively ignores the
+ authentication in the spec for a single request.
+ :type _request_auth: dict, optional
+ :param _content_type: force content-type for the request.
+ :type _content_type: str, Optional
+ :param _headers: set to override the headers for a single
+ request; this effectively ignores the headers
+ in the spec for a single request.
+ :type _headers: dict, optional
+ :param _host_index: set to override the host_index for a single
+ request; this effectively ignores the host_index
+ in the spec for a single request.
+ :type _host_index: int, optional
+ :return: Returns the result object.
+ """ # noqa: E501
+ _param = self._create_agent_run_v1_organizations_org_id_agent_run_post_1_serialize(
+ org_id=org_id,
+ create_agent_run_input=create_agent_run_input,
+ authorization=authorization,
+ _request_auth=_request_auth,
+ _content_type=_content_type,
+ _headers=_headers,
+ _host_index=_host_index,
+ )
+
+ _response_types_map: dict[str, Optional[str]] = {
+ "200": "AgentRunResponse",
+ "422": "HTTPValidationError",
+ }
+ response_data = self.api_client.call_api(*_param, _request_timeout=_request_timeout)
+ response_data.read()
+ return self.api_client.response_deserialize(
+ response_data=response_data,
+ response_types_map=_response_types_map,
+ ).data
+
+ @validate_call
+ def create_agent_run_v1_organizations_org_id_agent_run_post_1_with_http_info(
+ self,
+ org_id: StrictInt,
+ create_agent_run_input: CreateAgentRunInput,
+ authorization: Optional[Any] = None,
+ _request_timeout: Union[None, Annotated[StrictFloat, Field(gt=0)], tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]]] = None,
+ _request_auth: Optional[dict[StrictStr, Any]] = None,
+ _content_type: Optional[StrictStr] = None,
+ _headers: Optional[dict[StrictStr, Any]] = None,
+ _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
+ ) -> ApiResponse[AgentRunResponse]:
+ """Create Agent Run
+
+ Create a new agent run. Creates and initiates a long-running agent process based on the provided prompt. The process will complete asynchronously, and the response contains the agent run ID which can be used to check the status later. The requesting user must be a member of the specified organization.
+
+ :param org_id: (required)
+ :type org_id: int
+ :param create_agent_run_input: (required)
+ :type create_agent_run_input: CreateAgentRunInput
+ :param authorization:
+ :type authorization: object
+ :param _request_timeout: timeout setting for this request. If one
+ number provided, it will be total request
+ timeout. It can also be a pair (tuple) of
+ (connection, read) timeouts.
+ :type _request_timeout: int, tuple(int, int), optional
+ :param _request_auth: set to override the auth_settings for an a single
+ request; this effectively ignores the
+ authentication in the spec for a single request.
+ :type _request_auth: dict, optional
+ :param _content_type: force content-type for the request.
+ :type _content_type: str, Optional
+ :param _headers: set to override the headers for a single
+ request; this effectively ignores the headers
+ in the spec for a single request.
+ :type _headers: dict, optional
+ :param _host_index: set to override the host_index for a single
+ request; this effectively ignores the host_index
+ in the spec for a single request.
+ :type _host_index: int, optional
+ :return: Returns the result object.
+ """ # noqa: E501
+ _param = self._create_agent_run_v1_organizations_org_id_agent_run_post_1_serialize(
+ org_id=org_id,
+ create_agent_run_input=create_agent_run_input,
+ authorization=authorization,
+ _request_auth=_request_auth,
+ _content_type=_content_type,
+ _headers=_headers,
+ _host_index=_host_index,
+ )
+
+ _response_types_map: dict[str, Optional[str]] = {
+ "200": "AgentRunResponse",
+ "422": "HTTPValidationError",
+ }
+ response_data = self.api_client.call_api(*_param, _request_timeout=_request_timeout)
+ response_data.read()
+ return self.api_client.response_deserialize(
+ response_data=response_data,
+ response_types_map=_response_types_map,
+ )
+
+ @validate_call
+ def create_agent_run_v1_organizations_org_id_agent_run_post_1_without_preload_content(
+ self,
+ org_id: StrictInt,
+ create_agent_run_input: CreateAgentRunInput,
+ authorization: Optional[Any] = None,
+ _request_timeout: Union[None, Annotated[StrictFloat, Field(gt=0)], tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]]] = None,
+ _request_auth: Optional[dict[StrictStr, Any]] = None,
+ _content_type: Optional[StrictStr] = None,
+ _headers: Optional[dict[StrictStr, Any]] = None,
+ _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
+ ) -> RESTResponseType:
+ """Create Agent Run
+
+ Create a new agent run. Creates and initiates a long-running agent process based on the provided prompt. The process will complete asynchronously, and the response contains the agent run ID which can be used to check the status later. The requesting user must be a member of the specified organization.
+
+ :param org_id: (required)
+ :type org_id: int
+ :param create_agent_run_input: (required)
+ :type create_agent_run_input: CreateAgentRunInput
+ :param authorization:
+ :type authorization: object
+ :param _request_timeout: timeout setting for this request. If one
+ number provided, it will be total request
+ timeout. It can also be a pair (tuple) of
+ (connection, read) timeouts.
+ :type _request_timeout: int, tuple(int, int), optional
+ :param _request_auth: set to override the auth_settings for an a single
+ request; this effectively ignores the
+ authentication in the spec for a single request.
+ :type _request_auth: dict, optional
+ :param _content_type: force content-type for the request.
+ :type _content_type: str, Optional
+ :param _headers: set to override the headers for a single
+ request; this effectively ignores the headers
+ in the spec for a single request.
+ :type _headers: dict, optional
+ :param _host_index: set to override the host_index for a single
+ request; this effectively ignores the host_index
+ in the spec for a single request.
+ :type _host_index: int, optional
+ :return: Returns the result object.
+ """ # noqa: E501
+ _param = self._create_agent_run_v1_organizations_org_id_agent_run_post_1_serialize(
+ org_id=org_id,
+ create_agent_run_input=create_agent_run_input,
+ authorization=authorization,
+ _request_auth=_request_auth,
+ _content_type=_content_type,
+ _headers=_headers,
+ _host_index=_host_index,
+ )
+
+ _response_types_map: dict[str, Optional[str]] = {
+ "200": "AgentRunResponse",
+ "422": "HTTPValidationError",
+ }
+ response_data = self.api_client.call_api(*_param, _request_timeout=_request_timeout)
+ return response_data.response
+
+ def _create_agent_run_v1_organizations_org_id_agent_run_post_1_serialize(
+ self,
+ org_id,
+ create_agent_run_input,
+ authorization,
+ _request_auth,
+ _content_type,
+ _headers,
+ _host_index,
+ ) -> RequestSerialized:
+ _host = None
+
+ _collection_formats: dict[str, str] = {}
+
+ _path_params: dict[str, str] = {}
+ _query_params: list[tuple[str, str]] = []
+ _header_params: dict[str, Optional[str]] = _headers or {}
+ _form_params: list[tuple[str, str]] = []
+ _files: dict[str, Union[str, bytes, list[str], list[bytes], list[tuple[str, bytes]]]] = {}
+ _body_params: Optional[bytes] = None
+
+ # process the path parameters
+ if org_id is not None:
+ _path_params["org_id"] = org_id
+ # process the query parameters
+ # process the header parameters
+ if authorization is not None:
+ _header_params["authorization"] = authorization
+ # process the form parameters
+ # process the body parameter
+ if create_agent_run_input is not None:
+ _body_params = create_agent_run_input
+
+ # set the HTTP header `Accept`
+ if "Accept" not in _header_params:
+ _header_params["Accept"] = self.api_client.select_header_accept(["application/json"])
+
+ # set the HTTP header `Content-Type`
+ if _content_type:
+ _header_params["Content-Type"] = _content_type
+ else:
+ _default_content_type = self.api_client.select_header_content_type(["application/json"])
+ if _default_content_type is not None:
+ _header_params["Content-Type"] = _default_content_type
+
+ # authentication setting
+ _auth_settings: list[str] = []
+
+ return self.api_client.param_serialize(
+ method="POST",
+ resource_path="/v1/organizations/{org_id}/agent/run",
+ path_params=_path_params,
+ query_params=_query_params,
+ header_params=_header_params,
+ body=_body_params,
+ post_params=_form_params,
+ files=_files,
+ auth_settings=_auth_settings,
+ collection_formats=_collection_formats,
+ _host=_host,
+ _request_auth=_request_auth,
+ )
+
+ @validate_call
+ def get_agent_run_v1_organizations_org_id_agent_run_agent_run_id_get(
+ self,
+ agent_run_id: StrictInt,
+ org_id: StrictInt,
+ authorization: Optional[Any] = None,
+ _request_timeout: Union[None, Annotated[StrictFloat, Field(gt=0)], tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]]] = None,
+ _request_auth: Optional[dict[StrictStr, Any]] = None,
+ _content_type: Optional[StrictStr] = None,
+ _headers: Optional[dict[StrictStr, Any]] = None,
+ _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
+ ) -> AgentRunResponse:
+ """Get Agent Run
+
+ Retrieve the status and result of an agent run. Returns the current status, progress, and any available results for the specified agent run. The agent run must belong to the specified organization. If the agent run is still in progress, this endpoint can be polled to check for completion.
+
+ :param agent_run_id: (required)
+ :type agent_run_id: int
+ :param org_id: (required)
+ :type org_id: int
+ :param authorization:
+ :type authorization: object
+ :param _request_timeout: timeout setting for this request. If one
+ number provided, it will be total request
+ timeout. It can also be a pair (tuple) of
+ (connection, read) timeouts.
+ :type _request_timeout: int, tuple(int, int), optional
+ :param _request_auth: set to override the auth_settings for an a single
+ request; this effectively ignores the
+ authentication in the spec for a single request.
+ :type _request_auth: dict, optional
+ :param _content_type: force content-type for the request.
+ :type _content_type: str, Optional
+ :param _headers: set to override the headers for a single
+ request; this effectively ignores the headers
+ in the spec for a single request.
+ :type _headers: dict, optional
+ :param _host_index: set to override the host_index for a single
+ request; this effectively ignores the host_index
+ in the spec for a single request.
+ :type _host_index: int, optional
+ :return: Returns the result object.
+ """ # noqa: E501
+ _param = self._get_agent_run_v1_organizations_org_id_agent_run_agent_run_id_get_serialize(
+ agent_run_id=agent_run_id, org_id=org_id, authorization=authorization, _request_auth=_request_auth, _content_type=_content_type, _headers=_headers, _host_index=_host_index
+ )
+
+ _response_types_map: dict[str, Optional[str]] = {
+ "200": "AgentRunResponse",
+ "422": "HTTPValidationError",
+ }
+ response_data = self.api_client.call_api(*_param, _request_timeout=_request_timeout)
+ response_data.read()
+ return self.api_client.response_deserialize(
+ response_data=response_data,
+ response_types_map=_response_types_map,
+ ).data
+
+ @validate_call
+ def get_agent_run_v1_organizations_org_id_agent_run_agent_run_id_get_with_http_info(
+ self,
+ agent_run_id: StrictInt,
+ org_id: StrictInt,
+ authorization: Optional[Any] = None,
+ _request_timeout: Union[None, Annotated[StrictFloat, Field(gt=0)], tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]]] = None,
+ _request_auth: Optional[dict[StrictStr, Any]] = None,
+ _content_type: Optional[StrictStr] = None,
+ _headers: Optional[dict[StrictStr, Any]] = None,
+ _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
+ ) -> ApiResponse[AgentRunResponse]:
+ """Get Agent Run
+
+ Retrieve the status and result of an agent run. Returns the current status, progress, and any available results for the specified agent run. The agent run must belong to the specified organization. If the agent run is still in progress, this endpoint can be polled to check for completion.
+
+ :param agent_run_id: (required)
+ :type agent_run_id: int
+ :param org_id: (required)
+ :type org_id: int
+ :param authorization:
+ :type authorization: object
+ :param _request_timeout: timeout setting for this request. If one
+ number provided, it will be total request
+ timeout. It can also be a pair (tuple) of
+ (connection, read) timeouts.
+ :type _request_timeout: int, tuple(int, int), optional
+ :param _request_auth: set to override the auth_settings for an a single
+ request; this effectively ignores the
+ authentication in the spec for a single request.
+ :type _request_auth: dict, optional
+ :param _content_type: force content-type for the request.
+ :type _content_type: str, Optional
+ :param _headers: set to override the headers for a single
+ request; this effectively ignores the headers
+ in the spec for a single request.
+ :type _headers: dict, optional
+ :param _host_index: set to override the host_index for a single
+ request; this effectively ignores the host_index
+ in the spec for a single request.
+ :type _host_index: int, optional
+ :return: Returns the result object.
+ """ # noqa: E501
+ _param = self._get_agent_run_v1_organizations_org_id_agent_run_agent_run_id_get_serialize(
+ agent_run_id=agent_run_id, org_id=org_id, authorization=authorization, _request_auth=_request_auth, _content_type=_content_type, _headers=_headers, _host_index=_host_index
+ )
+
+ _response_types_map: dict[str, Optional[str]] = {
+ "200": "AgentRunResponse",
+ "422": "HTTPValidationError",
+ }
+ response_data = self.api_client.call_api(*_param, _request_timeout=_request_timeout)
+ response_data.read()
+ return self.api_client.response_deserialize(
+ response_data=response_data,
+ response_types_map=_response_types_map,
+ )
+
+ @validate_call
+ def get_agent_run_v1_organizations_org_id_agent_run_agent_run_id_get_without_preload_content(
+ self,
+ agent_run_id: StrictInt,
+ org_id: StrictInt,
+ authorization: Optional[Any] = None,
+ _request_timeout: Union[None, Annotated[StrictFloat, Field(gt=0)], tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]]] = None,
+ _request_auth: Optional[dict[StrictStr, Any]] = None,
+ _content_type: Optional[StrictStr] = None,
+ _headers: Optional[dict[StrictStr, Any]] = None,
+ _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
+ ) -> RESTResponseType:
+ """Get Agent Run
+
+ Retrieve the status and result of an agent run. Returns the current status, progress, and any available results for the specified agent run. The agent run must belong to the specified organization. If the agent run is still in progress, this endpoint can be polled to check for completion.
+
+ :param agent_run_id: (required)
+ :type agent_run_id: int
+ :param org_id: (required)
+ :type org_id: int
+ :param authorization:
+ :type authorization: object
+ :param _request_timeout: timeout setting for this request. If one
+ number provided, it will be total request
+ timeout. It can also be a pair (tuple) of
+ (connection, read) timeouts.
+ :type _request_timeout: int, tuple(int, int), optional
+ :param _request_auth: set to override the auth_settings for an a single
+ request; this effectively ignores the
+ authentication in the spec for a single request.
+ :type _request_auth: dict, optional
+ :param _content_type: force content-type for the request.
+ :type _content_type: str, Optional
+ :param _headers: set to override the headers for a single
+ request; this effectively ignores the headers
+ in the spec for a single request.
+ :type _headers: dict, optional
+ :param _host_index: set to override the host_index for a single
+ request; this effectively ignores the host_index
+ in the spec for a single request.
+ :type _host_index: int, optional
+ :return: Returns the result object.
+ """ # noqa: E501
+ _param = self._get_agent_run_v1_organizations_org_id_agent_run_agent_run_id_get_serialize(
+ agent_run_id=agent_run_id, org_id=org_id, authorization=authorization, _request_auth=_request_auth, _content_type=_content_type, _headers=_headers, _host_index=_host_index
+ )
+
+ _response_types_map: dict[str, Optional[str]] = {
+ "200": "AgentRunResponse",
+ "422": "HTTPValidationError",
+ }
+ response_data = self.api_client.call_api(*_param, _request_timeout=_request_timeout)
+ return response_data.response
+
+ def _get_agent_run_v1_organizations_org_id_agent_run_agent_run_id_get_serialize(
+ self,
+ agent_run_id,
+ org_id,
+ authorization,
+ _request_auth,
+ _content_type,
+ _headers,
+ _host_index,
+ ) -> RequestSerialized:
+ _host = None
+
+ _collection_formats: dict[str, str] = {}
+
+ _path_params: dict[str, str] = {}
+ _query_params: list[tuple[str, str]] = []
+ _header_params: dict[str, Optional[str]] = _headers or {}
+ _form_params: list[tuple[str, str]] = []
+ _files: dict[str, Union[str, bytes, list[str], list[bytes], list[tuple[str, bytes]]]] = {}
+ _body_params: Optional[bytes] = None
+
+ # process the path parameters
+ if agent_run_id is not None:
+ _path_params["agent_run_id"] = agent_run_id
+ if org_id is not None:
+ _path_params["org_id"] = org_id
+ # process the query parameters
+ # process the header parameters
+ if authorization is not None:
+ _header_params["authorization"] = authorization
+ # process the form parameters
+ # process the body parameter
+
+ # set the HTTP header `Accept`
+ if "Accept" not in _header_params:
+ _header_params["Accept"] = self.api_client.select_header_accept(["application/json"])
+
+ # authentication setting
+ _auth_settings: list[str] = []
+
+ return self.api_client.param_serialize(
+ method="GET",
+ resource_path="/v1/organizations/{org_id}/agent/run/{agent_run_id}",
+ path_params=_path_params,
+ query_params=_query_params,
+ header_params=_header_params,
+ body=_body_params,
+ post_params=_form_params,
+ files=_files,
+ auth_settings=_auth_settings,
+ collection_formats=_collection_formats,
+ _host=_host,
+ _request_auth=_request_auth,
+ )
+
+ @validate_call
+ def get_agent_run_v1_organizations_org_id_agent_run_agent_run_id_get_0(
+ self,
+ agent_run_id: StrictInt,
+ org_id: StrictInt,
+ authorization: Optional[Any] = None,
+ _request_timeout: Union[None, Annotated[StrictFloat, Field(gt=0)], tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]]] = None,
+ _request_auth: Optional[dict[StrictStr, Any]] = None,
+ _content_type: Optional[StrictStr] = None,
+ _headers: Optional[dict[StrictStr, Any]] = None,
+ _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
+ ) -> AgentRunResponse:
+ """Get Agent Run
+
+ Retrieve the status and result of an agent run. Returns the current status, progress, and any available results for the specified agent run. The agent run must belong to the specified organization. If the agent run is still in progress, this endpoint can be polled to check for completion.
+
+ :param agent_run_id: (required)
+ :type agent_run_id: int
+ :param org_id: (required)
+ :type org_id: int
+ :param authorization:
+ :type authorization: object
+ :param _request_timeout: timeout setting for this request. If one
+ number provided, it will be total request
+ timeout. It can also be a pair (tuple) of
+ (connection, read) timeouts.
+ :type _request_timeout: int, tuple(int, int), optional
+ :param _request_auth: set to override the auth_settings for an a single
+ request; this effectively ignores the
+ authentication in the spec for a single request.
+ :type _request_auth: dict, optional
+ :param _content_type: force content-type for the request.
+ :type _content_type: str, Optional
+ :param _headers: set to override the headers for a single
+ request; this effectively ignores the headers
+ in the spec for a single request.
+ :type _headers: dict, optional
+ :param _host_index: set to override the host_index for a single
+ request; this effectively ignores the host_index
+ in the spec for a single request.
+ :type _host_index: int, optional
+ :return: Returns the result object.
+ """ # noqa: E501
+ _param = self._get_agent_run_v1_organizations_org_id_agent_run_agent_run_id_get_0_serialize(
+ agent_run_id=agent_run_id, org_id=org_id, authorization=authorization, _request_auth=_request_auth, _content_type=_content_type, _headers=_headers, _host_index=_host_index
+ )
+
+ _response_types_map: dict[str, Optional[str]] = {
+ "200": "AgentRunResponse",
+ "422": "HTTPValidationError",
+ }
+ response_data = self.api_client.call_api(*_param, _request_timeout=_request_timeout)
+ response_data.read()
+ return self.api_client.response_deserialize(
+ response_data=response_data,
+ response_types_map=_response_types_map,
+ ).data
+
+ @validate_call
+ def get_agent_run_v1_organizations_org_id_agent_run_agent_run_id_get_0_with_http_info(
+ self,
+ agent_run_id: StrictInt,
+ org_id: StrictInt,
+ authorization: Optional[Any] = None,
+ _request_timeout: Union[None, Annotated[StrictFloat, Field(gt=0)], tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]]] = None,
+ _request_auth: Optional[dict[StrictStr, Any]] = None,
+ _content_type: Optional[StrictStr] = None,
+ _headers: Optional[dict[StrictStr, Any]] = None,
+ _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
+ ) -> ApiResponse[AgentRunResponse]:
+ """Get Agent Run
+
+ Retrieve the status and result of an agent run. Returns the current status, progress, and any available results for the specified agent run. The agent run must belong to the specified organization. If the agent run is still in progress, this endpoint can be polled to check for completion.
+
+ :param agent_run_id: (required)
+ :type agent_run_id: int
+ :param org_id: (required)
+ :type org_id: int
+ :param authorization:
+ :type authorization: object
+ :param _request_timeout: timeout setting for this request. If one
+ number provided, it will be total request
+ timeout. It can also be a pair (tuple) of
+ (connection, read) timeouts.
+ :type _request_timeout: int, tuple(int, int), optional
+ :param _request_auth: set to override the auth_settings for an a single
+ request; this effectively ignores the
+ authentication in the spec for a single request.
+ :type _request_auth: dict, optional
+ :param _content_type: force content-type for the request.
+ :type _content_type: str, Optional
+ :param _headers: set to override the headers for a single
+ request; this effectively ignores the headers
+ in the spec for a single request.
+ :type _headers: dict, optional
+ :param _host_index: set to override the host_index for a single
+ request; this effectively ignores the host_index
+ in the spec for a single request.
+ :type _host_index: int, optional
+ :return: Returns the result object.
+ """ # noqa: E501
+ _param = self._get_agent_run_v1_organizations_org_id_agent_run_agent_run_id_get_0_serialize(
+ agent_run_id=agent_run_id, org_id=org_id, authorization=authorization, _request_auth=_request_auth, _content_type=_content_type, _headers=_headers, _host_index=_host_index
+ )
+
+ _response_types_map: dict[str, Optional[str]] = {
+ "200": "AgentRunResponse",
+ "422": "HTTPValidationError",
+ }
+ response_data = self.api_client.call_api(*_param, _request_timeout=_request_timeout)
+ response_data.read()
+ return self.api_client.response_deserialize(
+ response_data=response_data,
+ response_types_map=_response_types_map,
+ )
+
+ @validate_call
+ def get_agent_run_v1_organizations_org_id_agent_run_agent_run_id_get_0_without_preload_content(
+ self,
+ agent_run_id: StrictInt,
+ org_id: StrictInt,
+ authorization: Optional[Any] = None,
+ _request_timeout: Union[None, Annotated[StrictFloat, Field(gt=0)], tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]]] = None,
+ _request_auth: Optional[dict[StrictStr, Any]] = None,
+ _content_type: Optional[StrictStr] = None,
+ _headers: Optional[dict[StrictStr, Any]] = None,
+ _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
+ ) -> RESTResponseType:
+ """Get Agent Run
+
+ Retrieve the status and result of an agent run. Returns the current status, progress, and any available results for the specified agent run. The agent run must belong to the specified organization. If the agent run is still in progress, this endpoint can be polled to check for completion.
+
+ :param agent_run_id: (required)
+ :type agent_run_id: int
+ :param org_id: (required)
+ :type org_id: int
+ :param authorization:
+ :type authorization: object
+ :param _request_timeout: timeout setting for this request. If one
+ number provided, it will be total request
+ timeout. It can also be a pair (tuple) of
+ (connection, read) timeouts.
+ :type _request_timeout: int, tuple(int, int), optional
+ :param _request_auth: set to override the auth_settings for an a single
+ request; this effectively ignores the
+ authentication in the spec for a single request.
+ :type _request_auth: dict, optional
+ :param _content_type: force content-type for the request.
+ :type _content_type: str, Optional
+ :param _headers: set to override the headers for a single
+ request; this effectively ignores the headers
+ in the spec for a single request.
+ :type _headers: dict, optional
+ :param _host_index: set to override the host_index for a single
+ request; this effectively ignores the host_index
+ in the spec for a single request.
+ :type _host_index: int, optional
+ :return: Returns the result object.
+ """ # noqa: E501
+ _param = self._get_agent_run_v1_organizations_org_id_agent_run_agent_run_id_get_0_serialize(
+ agent_run_id=agent_run_id, org_id=org_id, authorization=authorization, _request_auth=_request_auth, _content_type=_content_type, _headers=_headers, _host_index=_host_index
+ )
+
+ _response_types_map: dict[str, Optional[str]] = {
+ "200": "AgentRunResponse",
+ "422": "HTTPValidationError",
+ }
+ response_data = self.api_client.call_api(*_param, _request_timeout=_request_timeout)
+ return response_data.response
+
+ def _get_agent_run_v1_organizations_org_id_agent_run_agent_run_id_get_0_serialize(
+ self,
+ agent_run_id,
+ org_id,
+ authorization,
+ _request_auth,
+ _content_type,
+ _headers,
+ _host_index,
+ ) -> RequestSerialized:
+ _host = None
+
+ _collection_formats: dict[str, str] = {}
+
+ _path_params: dict[str, str] = {}
+ _query_params: list[tuple[str, str]] = []
+ _header_params: dict[str, Optional[str]] = _headers or {}
+ _form_params: list[tuple[str, str]] = []
+ _files: dict[str, Union[str, bytes, list[str], list[bytes], list[tuple[str, bytes]]]] = {}
+ _body_params: Optional[bytes] = None
+
+ # process the path parameters
+ if agent_run_id is not None:
+ _path_params["agent_run_id"] = agent_run_id
+ if org_id is not None:
+ _path_params["org_id"] = org_id
+ # process the query parameters
+ # process the header parameters
+ if authorization is not None:
+ _header_params["authorization"] = authorization
+ # process the form parameters
+ # process the body parameter
+
+ # set the HTTP header `Accept`
+ if "Accept" not in _header_params:
+ _header_params["Accept"] = self.api_client.select_header_accept(["application/json"])
+
+ # authentication setting
+ _auth_settings: list[str] = []
+
+ return self.api_client.param_serialize(
+ method="GET",
+ resource_path="/v1/organizations/{org_id}/agent/run/{agent_run_id}",
+ path_params=_path_params,
+ query_params=_query_params,
+ header_params=_header_params,
+ body=_body_params,
+ post_params=_form_params,
+ files=_files,
+ auth_settings=_auth_settings,
+ collection_formats=_collection_formats,
+ _host=_host,
+ _request_auth=_request_auth,
+ )
+
+ @validate_call
+ def get_agent_run_v1_organizations_org_id_agent_run_agent_run_id_get_1(
+ self,
+ agent_run_id: StrictInt,
+ org_id: StrictInt,
+ authorization: Optional[Any] = None,
+ _request_timeout: Union[None, Annotated[StrictFloat, Field(gt=0)], tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]]] = None,
+ _request_auth: Optional[dict[StrictStr, Any]] = None,
+ _content_type: Optional[StrictStr] = None,
+ _headers: Optional[dict[StrictStr, Any]] = None,
+ _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
+ ) -> AgentRunResponse:
+ """Get Agent Run
+
+ Retrieve the status and result of an agent run. Returns the current status, progress, and any available results for the specified agent run. The agent run must belong to the specified organization. If the agent run is still in progress, this endpoint can be polled to check for completion.
+
+ :param agent_run_id: (required)
+ :type agent_run_id: int
+ :param org_id: (required)
+ :type org_id: int
+ :param authorization:
+ :type authorization: object
+ :param _request_timeout: timeout setting for this request. If one
+ number provided, it will be total request
+ timeout. It can also be a pair (tuple) of
+ (connection, read) timeouts.
+ :type _request_timeout: int, tuple(int, int), optional
+ :param _request_auth: set to override the auth_settings for an a single
+ request; this effectively ignores the
+ authentication in the spec for a single request.
+ :type _request_auth: dict, optional
+ :param _content_type: force content-type for the request.
+ :type _content_type: str, Optional
+ :param _headers: set to override the headers for a single
+ request; this effectively ignores the headers
+ in the spec for a single request.
+ :type _headers: dict, optional
+ :param _host_index: set to override the host_index for a single
+ request; this effectively ignores the host_index
+ in the spec for a single request.
+ :type _host_index: int, optional
+ :return: Returns the result object.
+ """ # noqa: E501
+ _param = self._get_agent_run_v1_organizations_org_id_agent_run_agent_run_id_get_1_serialize(
+ agent_run_id=agent_run_id, org_id=org_id, authorization=authorization, _request_auth=_request_auth, _content_type=_content_type, _headers=_headers, _host_index=_host_index
+ )
+
+ _response_types_map: dict[str, Optional[str]] = {
+ "200": "AgentRunResponse",
+ "422": "HTTPValidationError",
+ }
+ response_data = self.api_client.call_api(*_param, _request_timeout=_request_timeout)
+ response_data.read()
+ return self.api_client.response_deserialize(
+ response_data=response_data,
+ response_types_map=_response_types_map,
+ ).data
+
+ @validate_call
+ def get_agent_run_v1_organizations_org_id_agent_run_agent_run_id_get_1_with_http_info(
+ self,
+ agent_run_id: StrictInt,
+ org_id: StrictInt,
+ authorization: Optional[Any] = None,
+ _request_timeout: Union[None, Annotated[StrictFloat, Field(gt=0)], tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]]] = None,
+ _request_auth: Optional[dict[StrictStr, Any]] = None,
+ _content_type: Optional[StrictStr] = None,
+ _headers: Optional[dict[StrictStr, Any]] = None,
+ _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
+ ) -> ApiResponse[AgentRunResponse]:
+ """Get Agent Run
+
+ Retrieve the status and result of an agent run. Returns the current status, progress, and any available results for the specified agent run. The agent run must belong to the specified organization. If the agent run is still in progress, this endpoint can be polled to check for completion.
+
+ :param agent_run_id: (required)
+ :type agent_run_id: int
+ :param org_id: (required)
+ :type org_id: int
+ :param authorization:
+ :type authorization: object
+ :param _request_timeout: timeout setting for this request. If one
+ number provided, it will be total request
+ timeout. It can also be a pair (tuple) of
+ (connection, read) timeouts.
+ :type _request_timeout: int, tuple(int, int), optional
+ :param _request_auth: set to override the auth_settings for an a single
+ request; this effectively ignores the
+ authentication in the spec for a single request.
+ :type _request_auth: dict, optional
+ :param _content_type: force content-type for the request.
+ :type _content_type: str, Optional
+ :param _headers: set to override the headers for a single
+ request; this effectively ignores the headers
+ in the spec for a single request.
+ :type _headers: dict, optional
+ :param _host_index: set to override the host_index for a single
+ request; this effectively ignores the host_index
+ in the spec for a single request.
+ :type _host_index: int, optional
+ :return: Returns the result object.
+ """ # noqa: E501
+ _param = self._get_agent_run_v1_organizations_org_id_agent_run_agent_run_id_get_1_serialize(
+ agent_run_id=agent_run_id, org_id=org_id, authorization=authorization, _request_auth=_request_auth, _content_type=_content_type, _headers=_headers, _host_index=_host_index
+ )
+
+ _response_types_map: dict[str, Optional[str]] = {
+ "200": "AgentRunResponse",
+ "422": "HTTPValidationError",
+ }
+ response_data = self.api_client.call_api(*_param, _request_timeout=_request_timeout)
+ response_data.read()
+ return self.api_client.response_deserialize(
+ response_data=response_data,
+ response_types_map=_response_types_map,
+ )
+
+ @validate_call
+ def get_agent_run_v1_organizations_org_id_agent_run_agent_run_id_get_1_without_preload_content(
+ self,
+ agent_run_id: StrictInt,
+ org_id: StrictInt,
+ authorization: Optional[Any] = None,
+ _request_timeout: Union[None, Annotated[StrictFloat, Field(gt=0)], tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]]] = None,
+ _request_auth: Optional[dict[StrictStr, Any]] = None,
+ _content_type: Optional[StrictStr] = None,
+ _headers: Optional[dict[StrictStr, Any]] = None,
+ _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
+ ) -> RESTResponseType:
+ """Get Agent Run
+
+ Retrieve the status and result of an agent run. Returns the current status, progress, and any available results for the specified agent run. The agent run must belong to the specified organization. If the agent run is still in progress, this endpoint can be polled to check for completion.
+
+ :param agent_run_id: (required)
+ :type agent_run_id: int
+ :param org_id: (required)
+ :type org_id: int
+ :param authorization:
+ :type authorization: object
+ :param _request_timeout: timeout setting for this request. If one
+ number provided, it will be total request
+ timeout. It can also be a pair (tuple) of
+ (connection, read) timeouts.
+ :type _request_timeout: int, tuple(int, int), optional
+ :param _request_auth: set to override the auth_settings for an a single
+ request; this effectively ignores the
+ authentication in the spec for a single request.
+ :type _request_auth: dict, optional
+ :param _content_type: force content-type for the request.
+ :type _content_type: str, Optional
+ :param _headers: set to override the headers for a single
+ request; this effectively ignores the headers
+ in the spec for a single request.
+ :type _headers: dict, optional
+ :param _host_index: set to override the host_index for a single
+ request; this effectively ignores the host_index
+ in the spec for a single request.
+ :type _host_index: int, optional
+ :return: Returns the result object.
+ """ # noqa: E501
+ _param = self._get_agent_run_v1_organizations_org_id_agent_run_agent_run_id_get_1_serialize(
+ agent_run_id=agent_run_id, org_id=org_id, authorization=authorization, _request_auth=_request_auth, _content_type=_content_type, _headers=_headers, _host_index=_host_index
+ )
+
+ _response_types_map: dict[str, Optional[str]] = {
+ "200": "AgentRunResponse",
+ "422": "HTTPValidationError",
+ }
+ response_data = self.api_client.call_api(*_param, _request_timeout=_request_timeout)
+ return response_data.response
+
+ def _get_agent_run_v1_organizations_org_id_agent_run_agent_run_id_get_1_serialize(
+ self,
+ agent_run_id,
+ org_id,
+ authorization,
+ _request_auth,
+ _content_type,
+ _headers,
+ _host_index,
+ ) -> RequestSerialized:
+ _host = None
+
+ _collection_formats: dict[str, str] = {}
+
+ _path_params: dict[str, str] = {}
+ _query_params: list[tuple[str, str]] = []
+ _header_params: dict[str, Optional[str]] = _headers or {}
+ _form_params: list[tuple[str, str]] = []
+ _files: dict[str, Union[str, bytes, list[str], list[bytes], list[tuple[str, bytes]]]] = {}
+ _body_params: Optional[bytes] = None
+
+ # process the path parameters
+ if agent_run_id is not None:
+ _path_params["agent_run_id"] = agent_run_id
+ if org_id is not None:
+ _path_params["org_id"] = org_id
+ # process the query parameters
+ # process the header parameters
+ if authorization is not None:
+ _header_params["authorization"] = authorization
+ # process the form parameters
+ # process the body parameter
+
+ # set the HTTP header `Accept`
+ if "Accept" not in _header_params:
+ _header_params["Accept"] = self.api_client.select_header_accept(["application/json"])
+
+ # authentication setting
+ _auth_settings: list[str] = []
+
+ return self.api_client.param_serialize(
+ method="GET",
+ resource_path="/v1/organizations/{org_id}/agent/run/{agent_run_id}",
+ path_params=_path_params,
+ query_params=_query_params,
+ header_params=_header_params,
+ body=_body_params,
+ post_params=_form_params,
+ files=_files,
+ auth_settings=_auth_settings,
+ collection_formats=_collection_formats,
+ _host=_host,
+ _request_auth=_request_auth,
+ )
diff --git a/src/codegen/agents/client/openapi_client/api/organizations_api.py b/src/codegen/agents/client/openapi_client/api/organizations_api.py
new file mode 100644
index 000000000..091c830a1
--- /dev/null
+++ b/src/codegen/agents/client/openapi_client/api/organizations_api.py
@@ -0,0 +1,712 @@
+"""Developer API
+
+API for application developers
+
+The version of the OpenAPI document: 1.0.0
+Generated by OpenAPI Generator (https://openapi-generator.tech)
+
+Do not edit the class manually.
+"""
+
+from typing import Annotated, Any, Optional, Union
+
+from pydantic import Field, StrictFloat, StrictInt, StrictStr, validate_call
+
+from codegen.agents.client.openapi_client.api_client import ApiClient, RequestSerialized
+from codegen.agents.client.openapi_client.api_response import ApiResponse
+from codegen.agents.client.openapi_client.models.page_organization_response import PageOrganizationResponse
+from codegen.agents.client.openapi_client.rest import RESTResponseType
+
+
+class OrganizationsApi:
+ """NOTE: This class is auto generated by OpenAPI Generator
+ Ref: https://openapi-generator.tech
+
+ Do not edit the class manually.
+ """
+
+ def __init__(self, api_client=None) -> None:
+ if api_client is None:
+ api_client = ApiClient.get_default()
+ self.api_client = api_client
+
+ @validate_call
+ def get_organizations_v1_organizations_get(
+ self,
+ skip: Optional[Annotated[int, Field(strict=True, ge=0)]] = None,
+ limit: Optional[Annotated[int, Field(le=100, strict=True, ge=1)]] = None,
+ authorization: Optional[Any] = None,
+ _request_timeout: Union[None, Annotated[StrictFloat, Field(gt=0)], tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]]] = None,
+ _request_auth: Optional[dict[StrictStr, Any]] = None,
+ _content_type: Optional[StrictStr] = None,
+ _headers: Optional[dict[StrictStr, Any]] = None,
+ _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
+ ) -> PageOrganizationResponse:
+ """Get Organizations
+
+ Get organizations for the authenticated user. Returns a paginated list of all organizations that the authenticated user is a member of. Results include basic organization details such as name, ID, and membership information. Use pagination parameters to control the number of results returned.
+
+ :param skip:
+ :type skip: int
+ :param limit:
+ :type limit: int
+ :param authorization:
+ :type authorization: object
+ :param _request_timeout: timeout setting for this request. If one
+ number provided, it will be total request
+ timeout. It can also be a pair (tuple) of
+ (connection, read) timeouts.
+ :type _request_timeout: int, tuple(int, int), optional
+ :param _request_auth: set to override the auth_settings for an a single
+ request; this effectively ignores the
+ authentication in the spec for a single request.
+ :type _request_auth: dict, optional
+ :param _content_type: force content-type for the request.
+ :type _content_type: str, Optional
+ :param _headers: set to override the headers for a single
+ request; this effectively ignores the headers
+ in the spec for a single request.
+ :type _headers: dict, optional
+ :param _host_index: set to override the host_index for a single
+ request; this effectively ignores the host_index
+ in the spec for a single request.
+ :type _host_index: int, optional
+ :return: Returns the result object.
+ """ # noqa: E501
+ _param = self._get_organizations_v1_organizations_get_serialize(
+ skip=skip, limit=limit, authorization=authorization, _request_auth=_request_auth, _content_type=_content_type, _headers=_headers, _host_index=_host_index
+ )
+
+ _response_types_map: dict[str, Optional[str]] = {
+ "200": "PageOrganizationResponse",
+ "422": "HTTPValidationError",
+ }
+ response_data = self.api_client.call_api(*_param, _request_timeout=_request_timeout)
+ response_data.read()
+ return self.api_client.response_deserialize(
+ response_data=response_data,
+ response_types_map=_response_types_map,
+ ).data
+
+ @validate_call
+ def get_organizations_v1_organizations_get_with_http_info(
+ self,
+ skip: Optional[Annotated[int, Field(strict=True, ge=0)]] = None,
+ limit: Optional[Annotated[int, Field(le=100, strict=True, ge=1)]] = None,
+ authorization: Optional[Any] = None,
+ _request_timeout: Union[None, Annotated[StrictFloat, Field(gt=0)], tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]]] = None,
+ _request_auth: Optional[dict[StrictStr, Any]] = None,
+ _content_type: Optional[StrictStr] = None,
+ _headers: Optional[dict[StrictStr, Any]] = None,
+ _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
+ ) -> ApiResponse[PageOrganizationResponse]:
+ """Get Organizations
+
+ Get organizations for the authenticated user. Returns a paginated list of all organizations that the authenticated user is a member of. Results include basic organization details such as name, ID, and membership information. Use pagination parameters to control the number of results returned.
+
+ :param skip:
+ :type skip: int
+ :param limit:
+ :type limit: int
+ :param authorization:
+ :type authorization: object
+ :param _request_timeout: timeout setting for this request. If one
+ number provided, it will be total request
+ timeout. It can also be a pair (tuple) of
+ (connection, read) timeouts.
+ :type _request_timeout: int, tuple(int, int), optional
+ :param _request_auth: set to override the auth_settings for an a single
+ request; this effectively ignores the
+ authentication in the spec for a single request.
+ :type _request_auth: dict, optional
+ :param _content_type: force content-type for the request.
+ :type _content_type: str, Optional
+ :param _headers: set to override the headers for a single
+ request; this effectively ignores the headers
+ in the spec for a single request.
+ :type _headers: dict, optional
+ :param _host_index: set to override the host_index for a single
+ request; this effectively ignores the host_index
+ in the spec for a single request.
+ :type _host_index: int, optional
+ :return: Returns the result object.
+ """ # noqa: E501
+ _param = self._get_organizations_v1_organizations_get_serialize(
+ skip=skip, limit=limit, authorization=authorization, _request_auth=_request_auth, _content_type=_content_type, _headers=_headers, _host_index=_host_index
+ )
+
+ _response_types_map: dict[str, Optional[str]] = {
+ "200": "PageOrganizationResponse",
+ "422": "HTTPValidationError",
+ }
+ response_data = self.api_client.call_api(*_param, _request_timeout=_request_timeout)
+ response_data.read()
+ return self.api_client.response_deserialize(
+ response_data=response_data,
+ response_types_map=_response_types_map,
+ )
+
+ @validate_call
+ def get_organizations_v1_organizations_get_without_preload_content(
+ self,
+ skip: Optional[Annotated[int, Field(strict=True, ge=0)]] = None,
+ limit: Optional[Annotated[int, Field(le=100, strict=True, ge=1)]] = None,
+ authorization: Optional[Any] = None,
+ _request_timeout: Union[None, Annotated[StrictFloat, Field(gt=0)], tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]]] = None,
+ _request_auth: Optional[dict[StrictStr, Any]] = None,
+ _content_type: Optional[StrictStr] = None,
+ _headers: Optional[dict[StrictStr, Any]] = None,
+ _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
+ ) -> RESTResponseType:
+ """Get Organizations
+
+ Get organizations for the authenticated user. Returns a paginated list of all organizations that the authenticated user is a member of. Results include basic organization details such as name, ID, and membership information. Use pagination parameters to control the number of results returned.
+
+ :param skip:
+ :type skip: int
+ :param limit:
+ :type limit: int
+ :param authorization:
+ :type authorization: object
+ :param _request_timeout: timeout setting for this request. If one
+ number provided, it will be total request
+ timeout. It can also be a pair (tuple) of
+ (connection, read) timeouts.
+ :type _request_timeout: int, tuple(int, int), optional
+ :param _request_auth: set to override the auth_settings for an a single
+ request; this effectively ignores the
+ authentication in the spec for a single request.
+ :type _request_auth: dict, optional
+ :param _content_type: force content-type for the request.
+ :type _content_type: str, Optional
+ :param _headers: set to override the headers for a single
+ request; this effectively ignores the headers
+ in the spec for a single request.
+ :type _headers: dict, optional
+ :param _host_index: set to override the host_index for a single
+ request; this effectively ignores the host_index
+ in the spec for a single request.
+ :type _host_index: int, optional
+ :return: Returns the result object.
+ """ # noqa: E501
+ _param = self._get_organizations_v1_organizations_get_serialize(
+ skip=skip, limit=limit, authorization=authorization, _request_auth=_request_auth, _content_type=_content_type, _headers=_headers, _host_index=_host_index
+ )
+
+ _response_types_map: dict[str, Optional[str]] = {
+ "200": "PageOrganizationResponse",
+ "422": "HTTPValidationError",
+ }
+ response_data = self.api_client.call_api(*_param, _request_timeout=_request_timeout)
+ return response_data.response
+
+ def _get_organizations_v1_organizations_get_serialize(
+ self,
+ skip,
+ limit,
+ authorization,
+ _request_auth,
+ _content_type,
+ _headers,
+ _host_index,
+ ) -> RequestSerialized:
+ _host = None
+
+ _collection_formats: dict[str, str] = {}
+
+ _path_params: dict[str, str] = {}
+ _query_params: list[tuple[str, str]] = []
+ _header_params: dict[str, Optional[str]] = _headers or {}
+ _form_params: list[tuple[str, str]] = []
+ _files: dict[str, Union[str, bytes, list[str], list[bytes], list[tuple[str, bytes]]]] = {}
+ _body_params: Optional[bytes] = None
+
+ # process the path parameters
+ # process the query parameters
+ if skip is not None:
+ _query_params.append(("skip", skip))
+
+ if limit is not None:
+ _query_params.append(("limit", limit))
+
+ # process the header parameters
+ if authorization is not None:
+ _header_params["authorization"] = authorization
+ # process the form parameters
+ # process the body parameter
+
+ # set the HTTP header `Accept`
+ if "Accept" not in _header_params:
+ _header_params["Accept"] = self.api_client.select_header_accept(["application/json"])
+
+ # authentication setting
+ _auth_settings: list[str] = []
+
+ return self.api_client.param_serialize(
+ method="GET",
+ resource_path="/v1/organizations",
+ path_params=_path_params,
+ query_params=_query_params,
+ header_params=_header_params,
+ body=_body_params,
+ post_params=_form_params,
+ files=_files,
+ auth_settings=_auth_settings,
+ collection_formats=_collection_formats,
+ _host=_host,
+ _request_auth=_request_auth,
+ )
+
+ @validate_call
+ def get_organizations_v1_organizations_get_0(
+ self,
+ skip: Optional[Annotated[int, Field(strict=True, ge=0)]] = None,
+ limit: Optional[Annotated[int, Field(le=100, strict=True, ge=1)]] = None,
+ authorization: Optional[Any] = None,
+ _request_timeout: Union[None, Annotated[StrictFloat, Field(gt=0)], tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]]] = None,
+ _request_auth: Optional[dict[StrictStr, Any]] = None,
+ _content_type: Optional[StrictStr] = None,
+ _headers: Optional[dict[StrictStr, Any]] = None,
+ _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
+ ) -> PageOrganizationResponse:
+ """Get Organizations
+
+ Get organizations for the authenticated user. Returns a paginated list of all organizations that the authenticated user is a member of. Results include basic organization details such as name, ID, and membership information. Use pagination parameters to control the number of results returned.
+
+ :param skip:
+ :type skip: int
+ :param limit:
+ :type limit: int
+ :param authorization:
+ :type authorization: object
+ :param _request_timeout: timeout setting for this request. If one
+ number provided, it will be total request
+ timeout. It can also be a pair (tuple) of
+ (connection, read) timeouts.
+ :type _request_timeout: int, tuple(int, int), optional
+ :param _request_auth: set to override the auth_settings for an a single
+ request; this effectively ignores the
+ authentication in the spec for a single request.
+ :type _request_auth: dict, optional
+ :param _content_type: force content-type for the request.
+ :type _content_type: str, Optional
+ :param _headers: set to override the headers for a single
+ request; this effectively ignores the headers
+ in the spec for a single request.
+ :type _headers: dict, optional
+ :param _host_index: set to override the host_index for a single
+ request; this effectively ignores the host_index
+ in the spec for a single request.
+ :type _host_index: int, optional
+ :return: Returns the result object.
+ """ # noqa: E501
+ _param = self._get_organizations_v1_organizations_get_0_serialize(
+ skip=skip, limit=limit, authorization=authorization, _request_auth=_request_auth, _content_type=_content_type, _headers=_headers, _host_index=_host_index
+ )
+
+ _response_types_map: dict[str, Optional[str]] = {
+ "200": "PageOrganizationResponse",
+ "422": "HTTPValidationError",
+ }
+ response_data = self.api_client.call_api(*_param, _request_timeout=_request_timeout)
+ response_data.read()
+ return self.api_client.response_deserialize(
+ response_data=response_data,
+ response_types_map=_response_types_map,
+ ).data
+
+ @validate_call
+ def get_organizations_v1_organizations_get_0_with_http_info(
+ self,
+ skip: Optional[Annotated[int, Field(strict=True, ge=0)]] = None,
+ limit: Optional[Annotated[int, Field(le=100, strict=True, ge=1)]] = None,
+ authorization: Optional[Any] = None,
+ _request_timeout: Union[None, Annotated[StrictFloat, Field(gt=0)], tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]]] = None,
+ _request_auth: Optional[dict[StrictStr, Any]] = None,
+ _content_type: Optional[StrictStr] = None,
+ _headers: Optional[dict[StrictStr, Any]] = None,
+ _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
+ ) -> ApiResponse[PageOrganizationResponse]:
+ """Get Organizations
+
+ Get organizations for the authenticated user. Returns a paginated list of all organizations that the authenticated user is a member of. Results include basic organization details such as name, ID, and membership information. Use pagination parameters to control the number of results returned.
+
+ :param skip:
+ :type skip: int
+ :param limit:
+ :type limit: int
+ :param authorization:
+ :type authorization: object
+ :param _request_timeout: timeout setting for this request. If one
+ number provided, it will be total request
+ timeout. It can also be a pair (tuple) of
+ (connection, read) timeouts.
+ :type _request_timeout: int, tuple(int, int), optional
+ :param _request_auth: set to override the auth_settings for an a single
+ request; this effectively ignores the
+ authentication in the spec for a single request.
+ :type _request_auth: dict, optional
+ :param _content_type: force content-type for the request.
+ :type _content_type: str, Optional
+ :param _headers: set to override the headers for a single
+ request; this effectively ignores the headers
+ in the spec for a single request.
+ :type _headers: dict, optional
+ :param _host_index: set to override the host_index for a single
+ request; this effectively ignores the host_index
+ in the spec for a single request.
+ :type _host_index: int, optional
+ :return: Returns the result object.
+ """ # noqa: E501
+ _param = self._get_organizations_v1_organizations_get_0_serialize(
+ skip=skip, limit=limit, authorization=authorization, _request_auth=_request_auth, _content_type=_content_type, _headers=_headers, _host_index=_host_index
+ )
+
+ _response_types_map: dict[str, Optional[str]] = {
+ "200": "PageOrganizationResponse",
+ "422": "HTTPValidationError",
+ }
+ response_data = self.api_client.call_api(*_param, _request_timeout=_request_timeout)
+ response_data.read()
+ return self.api_client.response_deserialize(
+ response_data=response_data,
+ response_types_map=_response_types_map,
+ )
+
+ @validate_call
+ def get_organizations_v1_organizations_get_0_without_preload_content(
+ self,
+ skip: Optional[Annotated[int, Field(strict=True, ge=0)]] = None,
+ limit: Optional[Annotated[int, Field(le=100, strict=True, ge=1)]] = None,
+ authorization: Optional[Any] = None,
+ _request_timeout: Union[None, Annotated[StrictFloat, Field(gt=0)], tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]]] = None,
+ _request_auth: Optional[dict[StrictStr, Any]] = None,
+ _content_type: Optional[StrictStr] = None,
+ _headers: Optional[dict[StrictStr, Any]] = None,
+ _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
+ ) -> RESTResponseType:
+ """Get Organizations
+
+ Get organizations for the authenticated user. Returns a paginated list of all organizations that the authenticated user is a member of. Results include basic organization details such as name, ID, and membership information. Use pagination parameters to control the number of results returned.
+
+ :param skip:
+ :type skip: int
+ :param limit:
+ :type limit: int
+ :param authorization:
+ :type authorization: object
+ :param _request_timeout: timeout setting for this request. If one
+ number provided, it will be total request
+ timeout. It can also be a pair (tuple) of
+ (connection, read) timeouts.
+ :type _request_timeout: int, tuple(int, int), optional
+ :param _request_auth: set to override the auth_settings for an a single
+ request; this effectively ignores the
+ authentication in the spec for a single request.
+ :type _request_auth: dict, optional
+ :param _content_type: force content-type for the request.
+ :type _content_type: str, Optional
+ :param _headers: set to override the headers for a single
+ request; this effectively ignores the headers
+ in the spec for a single request.
+ :type _headers: dict, optional
+ :param _host_index: set to override the host_index for a single
+ request; this effectively ignores the host_index
+ in the spec for a single request.
+ :type _host_index: int, optional
+ :return: Returns the result object.
+ """ # noqa: E501
+ _param = self._get_organizations_v1_organizations_get_0_serialize(
+ skip=skip, limit=limit, authorization=authorization, _request_auth=_request_auth, _content_type=_content_type, _headers=_headers, _host_index=_host_index
+ )
+
+ _response_types_map: dict[str, Optional[str]] = {
+ "200": "PageOrganizationResponse",
+ "422": "HTTPValidationError",
+ }
+ response_data = self.api_client.call_api(*_param, _request_timeout=_request_timeout)
+ return response_data.response
+
+ def _get_organizations_v1_organizations_get_0_serialize(
+ self,
+ skip,
+ limit,
+ authorization,
+ _request_auth,
+ _content_type,
+ _headers,
+ _host_index,
+ ) -> RequestSerialized:
+ _host = None
+
+ _collection_formats: dict[str, str] = {}
+
+ _path_params: dict[str, str] = {}
+ _query_params: list[tuple[str, str]] = []
+ _header_params: dict[str, Optional[str]] = _headers or {}
+ _form_params: list[tuple[str, str]] = []
+ _files: dict[str, Union[str, bytes, list[str], list[bytes], list[tuple[str, bytes]]]] = {}
+ _body_params: Optional[bytes] = None
+
+ # process the path parameters
+ # process the query parameters
+ if skip is not None:
+ _query_params.append(("skip", skip))
+
+ if limit is not None:
+ _query_params.append(("limit", limit))
+
+ # process the header parameters
+ if authorization is not None:
+ _header_params["authorization"] = authorization
+ # process the form parameters
+ # process the body parameter
+
+ # set the HTTP header `Accept`
+ if "Accept" not in _header_params:
+ _header_params["Accept"] = self.api_client.select_header_accept(["application/json"])
+
+ # authentication setting
+ _auth_settings: list[str] = []
+
+ return self.api_client.param_serialize(
+ method="GET",
+ resource_path="/v1/organizations",
+ path_params=_path_params,
+ query_params=_query_params,
+ header_params=_header_params,
+ body=_body_params,
+ post_params=_form_params,
+ files=_files,
+ auth_settings=_auth_settings,
+ collection_formats=_collection_formats,
+ _host=_host,
+ _request_auth=_request_auth,
+ )
+
+ @validate_call
+ def get_organizations_v1_organizations_get_1(
+ self,
+ skip: Optional[Annotated[int, Field(strict=True, ge=0)]] = None,
+ limit: Optional[Annotated[int, Field(le=100, strict=True, ge=1)]] = None,
+ authorization: Optional[Any] = None,
+ _request_timeout: Union[None, Annotated[StrictFloat, Field(gt=0)], tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]]] = None,
+ _request_auth: Optional[dict[StrictStr, Any]] = None,
+ _content_type: Optional[StrictStr] = None,
+ _headers: Optional[dict[StrictStr, Any]] = None,
+ _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
+ ) -> PageOrganizationResponse:
+ """Get Organizations
+
+ Get organizations for the authenticated user. Returns a paginated list of all organizations that the authenticated user is a member of. Results include basic organization details such as name, ID, and membership information. Use pagination parameters to control the number of results returned.
+
+ :param skip:
+ :type skip: int
+ :param limit:
+ :type limit: int
+ :param authorization:
+ :type authorization: object
+ :param _request_timeout: timeout setting for this request. If one
+ number provided, it will be total request
+ timeout. It can also be a pair (tuple) of
+ (connection, read) timeouts.
+ :type _request_timeout: int, tuple(int, int), optional
+ :param _request_auth: set to override the auth_settings for an a single
+ request; this effectively ignores the
+ authentication in the spec for a single request.
+ :type _request_auth: dict, optional
+ :param _content_type: force content-type for the request.
+ :type _content_type: str, Optional
+ :param _headers: set to override the headers for a single
+ request; this effectively ignores the headers
+ in the spec for a single request.
+ :type _headers: dict, optional
+ :param _host_index: set to override the host_index for a single
+ request; this effectively ignores the host_index
+ in the spec for a single request.
+ :type _host_index: int, optional
+ :return: Returns the result object.
+ """ # noqa: E501
+ _param = self._get_organizations_v1_organizations_get_1_serialize(
+ skip=skip, limit=limit, authorization=authorization, _request_auth=_request_auth, _content_type=_content_type, _headers=_headers, _host_index=_host_index
+ )
+
+ _response_types_map: dict[str, Optional[str]] = {
+ "200": "PageOrganizationResponse",
+ "422": "HTTPValidationError",
+ }
+ response_data = self.api_client.call_api(*_param, _request_timeout=_request_timeout)
+ response_data.read()
+ return self.api_client.response_deserialize(
+ response_data=response_data,
+ response_types_map=_response_types_map,
+ ).data
+
+ @validate_call
+ def get_organizations_v1_organizations_get_1_with_http_info(
+ self,
+ skip: Optional[Annotated[int, Field(strict=True, ge=0)]] = None,
+ limit: Optional[Annotated[int, Field(le=100, strict=True, ge=1)]] = None,
+ authorization: Optional[Any] = None,
+ _request_timeout: Union[None, Annotated[StrictFloat, Field(gt=0)], tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]]] = None,
+ _request_auth: Optional[dict[StrictStr, Any]] = None,
+ _content_type: Optional[StrictStr] = None,
+ _headers: Optional[dict[StrictStr, Any]] = None,
+ _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
+ ) -> ApiResponse[PageOrganizationResponse]:
+ """Get Organizations
+
+ Get organizations for the authenticated user. Returns a paginated list of all organizations that the authenticated user is a member of. Results include basic organization details such as name, ID, and membership information. Use pagination parameters to control the number of results returned.
+
+ :param skip:
+ :type skip: int
+ :param limit:
+ :type limit: int
+ :param authorization:
+ :type authorization: object
+ :param _request_timeout: timeout setting for this request. If one
+ number provided, it will be total request
+ timeout. It can also be a pair (tuple) of
+ (connection, read) timeouts.
+ :type _request_timeout: int, tuple(int, int), optional
+ :param _request_auth: set to override the auth_settings for an a single
+ request; this effectively ignores the
+ authentication in the spec for a single request.
+ :type _request_auth: dict, optional
+ :param _content_type: force content-type for the request.
+ :type _content_type: str, Optional
+ :param _headers: set to override the headers for a single
+ request; this effectively ignores the headers
+ in the spec for a single request.
+ :type _headers: dict, optional
+ :param _host_index: set to override the host_index for a single
+ request; this effectively ignores the host_index
+ in the spec for a single request.
+ :type _host_index: int, optional
+ :return: Returns the result object.
+ """ # noqa: E501
+ _param = self._get_organizations_v1_organizations_get_1_serialize(
+ skip=skip, limit=limit, authorization=authorization, _request_auth=_request_auth, _content_type=_content_type, _headers=_headers, _host_index=_host_index
+ )
+
+ _response_types_map: dict[str, Optional[str]] = {
+ "200": "PageOrganizationResponse",
+ "422": "HTTPValidationError",
+ }
+ response_data = self.api_client.call_api(*_param, _request_timeout=_request_timeout)
+ response_data.read()
+ return self.api_client.response_deserialize(
+ response_data=response_data,
+ response_types_map=_response_types_map,
+ )
+
+ @validate_call
+ def get_organizations_v1_organizations_get_1_without_preload_content(
+ self,
+ skip: Optional[Annotated[int, Field(strict=True, ge=0)]] = None,
+ limit: Optional[Annotated[int, Field(le=100, strict=True, ge=1)]] = None,
+ authorization: Optional[Any] = None,
+ _request_timeout: Union[None, Annotated[StrictFloat, Field(gt=0)], tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]]] = None,
+ _request_auth: Optional[dict[StrictStr, Any]] = None,
+ _content_type: Optional[StrictStr] = None,
+ _headers: Optional[dict[StrictStr, Any]] = None,
+ _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
+ ) -> RESTResponseType:
+ """Get Organizations
+
+ Get organizations for the authenticated user. Returns a paginated list of all organizations that the authenticated user is a member of. Results include basic organization details such as name, ID, and membership information. Use pagination parameters to control the number of results returned.
+
+ :param skip:
+ :type skip: int
+ :param limit:
+ :type limit: int
+ :param authorization:
+ :type authorization: object
+ :param _request_timeout: timeout setting for this request. If one
+ number provided, it will be total request
+ timeout. It can also be a pair (tuple) of
+ (connection, read) timeouts.
+ :type _request_timeout: int, tuple(int, int), optional
+ :param _request_auth: set to override the auth_settings for an a single
+ request; this effectively ignores the
+ authentication in the spec for a single request.
+ :type _request_auth: dict, optional
+ :param _content_type: force content-type for the request.
+ :type _content_type: str, Optional
+ :param _headers: set to override the headers for a single
+ request; this effectively ignores the headers
+ in the spec for a single request.
+ :type _headers: dict, optional
+ :param _host_index: set to override the host_index for a single
+ request; this effectively ignores the host_index
+ in the spec for a single request.
+ :type _host_index: int, optional
+ :return: Returns the result object.
+ """ # noqa: E501
+ _param = self._get_organizations_v1_organizations_get_1_serialize(
+ skip=skip, limit=limit, authorization=authorization, _request_auth=_request_auth, _content_type=_content_type, _headers=_headers, _host_index=_host_index
+ )
+
+ _response_types_map: dict[str, Optional[str]] = {
+ "200": "PageOrganizationResponse",
+ "422": "HTTPValidationError",
+ }
+ response_data = self.api_client.call_api(*_param, _request_timeout=_request_timeout)
+ return response_data.response
+
+ def _get_organizations_v1_organizations_get_1_serialize(
+ self,
+ skip,
+ limit,
+ authorization,
+ _request_auth,
+ _content_type,
+ _headers,
+ _host_index,
+ ) -> RequestSerialized:
+ _host = None
+
+ _collection_formats: dict[str, str] = {}
+
+ _path_params: dict[str, str] = {}
+ _query_params: list[tuple[str, str]] = []
+ _header_params: dict[str, Optional[str]] = _headers or {}
+ _form_params: list[tuple[str, str]] = []
+ _files: dict[str, Union[str, bytes, list[str], list[bytes], list[tuple[str, bytes]]]] = {}
+ _body_params: Optional[bytes] = None
+
+ # process the path parameters
+ # process the query parameters
+ if skip is not None:
+ _query_params.append(("skip", skip))
+
+ if limit is not None:
+ _query_params.append(("limit", limit))
+
+ # process the header parameters
+ if authorization is not None:
+ _header_params["authorization"] = authorization
+ # process the form parameters
+ # process the body parameter
+
+ # set the HTTP header `Accept`
+ if "Accept" not in _header_params:
+ _header_params["Accept"] = self.api_client.select_header_accept(["application/json"])
+
+ # authentication setting
+ _auth_settings: list[str] = []
+
+ return self.api_client.param_serialize(
+ method="GET",
+ resource_path="/v1/organizations",
+ path_params=_path_params,
+ query_params=_query_params,
+ header_params=_header_params,
+ body=_body_params,
+ post_params=_form_params,
+ files=_files,
+ auth_settings=_auth_settings,
+ collection_formats=_collection_formats,
+ _host=_host,
+ _request_auth=_request_auth,
+ )
diff --git a/src/codegen/agents/client/openapi_client/api/users_api.py b/src/codegen/agents/client/openapi_client/api/users_api.py
new file mode 100644
index 000000000..2787cd5a4
--- /dev/null
+++ b/src/codegen/agents/client/openapi_client/api/users_api.py
@@ -0,0 +1,1424 @@
+"""Developer API
+
+API for application developers
+
+The version of the OpenAPI document: 1.0.0
+Generated by OpenAPI Generator (https://openapi-generator.tech)
+
+Do not edit the class manually.
+"""
+
+from typing import Annotated, Any, Optional, Union
+
+from pydantic import Field, StrictFloat, StrictInt, StrictStr, validate_call
+
+from codegen.agents.client.openapi_client.api_client import ApiClient, RequestSerialized
+from codegen.agents.client.openapi_client.api_response import ApiResponse
+from codegen.agents.client.openapi_client.models.page_user_response import PageUserResponse
+from codegen.agents.client.openapi_client.models.user_response import UserResponse
+from codegen.agents.client.openapi_client.rest import RESTResponseType
+
+
+class UsersApi:
+ """NOTE: This class is auto generated by OpenAPI Generator
+ Ref: https://openapi-generator.tech
+
+ Do not edit the class manually.
+ """
+
+ def __init__(self, api_client=None) -> None:
+ if api_client is None:
+ api_client = ApiClient.get_default()
+ self.api_client = api_client
+
+ @validate_call
+ def get_user_v1_organizations_org_id_users_user_id_get(
+ self,
+ org_id: StrictStr,
+ user_id: StrictStr,
+ authorization: Optional[Any] = None,
+ _request_timeout: Union[None, Annotated[StrictFloat, Field(gt=0)], tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]]] = None,
+ _request_auth: Optional[dict[StrictStr, Any]] = None,
+ _content_type: Optional[StrictStr] = None,
+ _headers: Optional[dict[StrictStr, Any]] = None,
+ _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
+ ) -> UserResponse:
+ """Get User
+
+ Get details for a specific user in an organization. Returns detailed information about a user within the specified organization. The requesting user must be a member of the organization to access this endpoint.
+
+ :param org_id: (required)
+ :type org_id: str
+ :param user_id: (required)
+ :type user_id: str
+ :param authorization:
+ :type authorization: object
+ :param _request_timeout: timeout setting for this request. If one
+ number provided, it will be total request
+ timeout. It can also be a pair (tuple) of
+ (connection, read) timeouts.
+ :type _request_timeout: int, tuple(int, int), optional
+ :param _request_auth: set to override the auth_settings for an a single
+ request; this effectively ignores the
+ authentication in the spec for a single request.
+ :type _request_auth: dict, optional
+ :param _content_type: force content-type for the request.
+ :type _content_type: str, Optional
+ :param _headers: set to override the headers for a single
+ request; this effectively ignores the headers
+ in the spec for a single request.
+ :type _headers: dict, optional
+ :param _host_index: set to override the host_index for a single
+ request; this effectively ignores the host_index
+ in the spec for a single request.
+ :type _host_index: int, optional
+ :return: Returns the result object.
+ """ # noqa: E501
+ _param = self._get_user_v1_organizations_org_id_users_user_id_get_serialize(
+ org_id=org_id, user_id=user_id, authorization=authorization, _request_auth=_request_auth, _content_type=_content_type, _headers=_headers, _host_index=_host_index
+ )
+
+ _response_types_map: dict[str, Optional[str]] = {
+ "200": "UserResponse",
+ "422": "HTTPValidationError",
+ }
+ response_data = self.api_client.call_api(*_param, _request_timeout=_request_timeout)
+ response_data.read()
+ return self.api_client.response_deserialize(
+ response_data=response_data,
+ response_types_map=_response_types_map,
+ ).data
+
+ @validate_call
+ def get_user_v1_organizations_org_id_users_user_id_get_with_http_info(
+ self,
+ org_id: StrictStr,
+ user_id: StrictStr,
+ authorization: Optional[Any] = None,
+ _request_timeout: Union[None, Annotated[StrictFloat, Field(gt=0)], tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]]] = None,
+ _request_auth: Optional[dict[StrictStr, Any]] = None,
+ _content_type: Optional[StrictStr] = None,
+ _headers: Optional[dict[StrictStr, Any]] = None,
+ _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
+ ) -> ApiResponse[UserResponse]:
+ """Get User
+
+ Get details for a specific user in an organization. Returns detailed information about a user within the specified organization. The requesting user must be a member of the organization to access this endpoint.
+
+ :param org_id: (required)
+ :type org_id: str
+ :param user_id: (required)
+ :type user_id: str
+ :param authorization:
+ :type authorization: object
+ :param _request_timeout: timeout setting for this request. If one
+ number provided, it will be total request
+ timeout. It can also be a pair (tuple) of
+ (connection, read) timeouts.
+ :type _request_timeout: int, tuple(int, int), optional
+ :param _request_auth: set to override the auth_settings for an a single
+ request; this effectively ignores the
+ authentication in the spec for a single request.
+ :type _request_auth: dict, optional
+ :param _content_type: force content-type for the request.
+ :type _content_type: str, Optional
+ :param _headers: set to override the headers for a single
+ request; this effectively ignores the headers
+ in the spec for a single request.
+ :type _headers: dict, optional
+ :param _host_index: set to override the host_index for a single
+ request; this effectively ignores the host_index
+ in the spec for a single request.
+ :type _host_index: int, optional
+ :return: Returns the result object.
+ """ # noqa: E501
+ _param = self._get_user_v1_organizations_org_id_users_user_id_get_serialize(
+ org_id=org_id, user_id=user_id, authorization=authorization, _request_auth=_request_auth, _content_type=_content_type, _headers=_headers, _host_index=_host_index
+ )
+
+ _response_types_map: dict[str, Optional[str]] = {
+ "200": "UserResponse",
+ "422": "HTTPValidationError",
+ }
+ response_data = self.api_client.call_api(*_param, _request_timeout=_request_timeout)
+ response_data.read()
+ return self.api_client.response_deserialize(
+ response_data=response_data,
+ response_types_map=_response_types_map,
+ )
+
+ @validate_call
+ def get_user_v1_organizations_org_id_users_user_id_get_without_preload_content(
+ self,
+ org_id: StrictStr,
+ user_id: StrictStr,
+ authorization: Optional[Any] = None,
+ _request_timeout: Union[None, Annotated[StrictFloat, Field(gt=0)], tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]]] = None,
+ _request_auth: Optional[dict[StrictStr, Any]] = None,
+ _content_type: Optional[StrictStr] = None,
+ _headers: Optional[dict[StrictStr, Any]] = None,
+ _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
+ ) -> RESTResponseType:
+ """Get User
+
+ Get details for a specific user in an organization. Returns detailed information about a user within the specified organization. The requesting user must be a member of the organization to access this endpoint.
+
+ :param org_id: (required)
+ :type org_id: str
+ :param user_id: (required)
+ :type user_id: str
+ :param authorization:
+ :type authorization: object
+ :param _request_timeout: timeout setting for this request. If one
+ number provided, it will be total request
+ timeout. It can also be a pair (tuple) of
+ (connection, read) timeouts.
+ :type _request_timeout: int, tuple(int, int), optional
+ :param _request_auth: set to override the auth_settings for an a single
+ request; this effectively ignores the
+ authentication in the spec for a single request.
+ :type _request_auth: dict, optional
+ :param _content_type: force content-type for the request.
+ :type _content_type: str, Optional
+ :param _headers: set to override the headers for a single
+ request; this effectively ignores the headers
+ in the spec for a single request.
+ :type _headers: dict, optional
+ :param _host_index: set to override the host_index for a single
+ request; this effectively ignores the host_index
+ in the spec for a single request.
+ :type _host_index: int, optional
+ :return: Returns the result object.
+ """ # noqa: E501
+ _param = self._get_user_v1_organizations_org_id_users_user_id_get_serialize(
+ org_id=org_id, user_id=user_id, authorization=authorization, _request_auth=_request_auth, _content_type=_content_type, _headers=_headers, _host_index=_host_index
+ )
+
+ _response_types_map: dict[str, Optional[str]] = {
+ "200": "UserResponse",
+ "422": "HTTPValidationError",
+ }
+ response_data = self.api_client.call_api(*_param, _request_timeout=_request_timeout)
+ return response_data.response
+
+ def _get_user_v1_organizations_org_id_users_user_id_get_serialize(
+ self,
+ org_id,
+ user_id,
+ authorization,
+ _request_auth,
+ _content_type,
+ _headers,
+ _host_index,
+ ) -> RequestSerialized:
+ _host = None
+
+ _collection_formats: dict[str, str] = {}
+
+ _path_params: dict[str, str] = {}
+ _query_params: list[tuple[str, str]] = []
+ _header_params: dict[str, Optional[str]] = _headers or {}
+ _form_params: list[tuple[str, str]] = []
+ _files: dict[str, Union[str, bytes, list[str], list[bytes], list[tuple[str, bytes]]]] = {}
+ _body_params: Optional[bytes] = None
+
+ # process the path parameters
+ if org_id is not None:
+ _path_params["org_id"] = org_id
+ if user_id is not None:
+ _path_params["user_id"] = user_id
+ # process the query parameters
+ # process the header parameters
+ if authorization is not None:
+ _header_params["authorization"] = authorization
+ # process the form parameters
+ # process the body parameter
+
+ # set the HTTP header `Accept`
+ if "Accept" not in _header_params:
+ _header_params["Accept"] = self.api_client.select_header_accept(["application/json"])
+
+ # authentication setting
+ _auth_settings: list[str] = []
+
+ return self.api_client.param_serialize(
+ method="GET",
+ resource_path="/v1/organizations/{org_id}/users/{user_id}",
+ path_params=_path_params,
+ query_params=_query_params,
+ header_params=_header_params,
+ body=_body_params,
+ post_params=_form_params,
+ files=_files,
+ auth_settings=_auth_settings,
+ collection_formats=_collection_formats,
+ _host=_host,
+ _request_auth=_request_auth,
+ )
+
+ @validate_call
+ def get_user_v1_organizations_org_id_users_user_id_get_0(
+ self,
+ org_id: StrictStr,
+ user_id: StrictStr,
+ authorization: Optional[Any] = None,
+ _request_timeout: Union[None, Annotated[StrictFloat, Field(gt=0)], tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]]] = None,
+ _request_auth: Optional[dict[StrictStr, Any]] = None,
+ _content_type: Optional[StrictStr] = None,
+ _headers: Optional[dict[StrictStr, Any]] = None,
+ _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
+ ) -> UserResponse:
+ """Get User
+
+ Get details for a specific user in an organization. Returns detailed information about a user within the specified organization. The requesting user must be a member of the organization to access this endpoint.
+
+ :param org_id: (required)
+ :type org_id: str
+ :param user_id: (required)
+ :type user_id: str
+ :param authorization:
+ :type authorization: object
+ :param _request_timeout: timeout setting for this request. If one
+ number provided, it will be total request
+ timeout. It can also be a pair (tuple) of
+ (connection, read) timeouts.
+ :type _request_timeout: int, tuple(int, int), optional
+ :param _request_auth: set to override the auth_settings for an a single
+ request; this effectively ignores the
+ authentication in the spec for a single request.
+ :type _request_auth: dict, optional
+ :param _content_type: force content-type for the request.
+ :type _content_type: str, Optional
+ :param _headers: set to override the headers for a single
+ request; this effectively ignores the headers
+ in the spec for a single request.
+ :type _headers: dict, optional
+ :param _host_index: set to override the host_index for a single
+ request; this effectively ignores the host_index
+ in the spec for a single request.
+ :type _host_index: int, optional
+ :return: Returns the result object.
+ """ # noqa: E501
+ _param = self._get_user_v1_organizations_org_id_users_user_id_get_0_serialize(
+ org_id=org_id, user_id=user_id, authorization=authorization, _request_auth=_request_auth, _content_type=_content_type, _headers=_headers, _host_index=_host_index
+ )
+
+ _response_types_map: dict[str, Optional[str]] = {
+ "200": "UserResponse",
+ "422": "HTTPValidationError",
+ }
+ response_data = self.api_client.call_api(*_param, _request_timeout=_request_timeout)
+ response_data.read()
+ return self.api_client.response_deserialize(
+ response_data=response_data,
+ response_types_map=_response_types_map,
+ ).data
+
+ @validate_call
+ def get_user_v1_organizations_org_id_users_user_id_get_0_with_http_info(
+ self,
+ org_id: StrictStr,
+ user_id: StrictStr,
+ authorization: Optional[Any] = None,
+ _request_timeout: Union[None, Annotated[StrictFloat, Field(gt=0)], tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]]] = None,
+ _request_auth: Optional[dict[StrictStr, Any]] = None,
+ _content_type: Optional[StrictStr] = None,
+ _headers: Optional[dict[StrictStr, Any]] = None,
+ _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
+ ) -> ApiResponse[UserResponse]:
+ """Get User
+
+ Get details for a specific user in an organization. Returns detailed information about a user within the specified organization. The requesting user must be a member of the organization to access this endpoint.
+
+ :param org_id: (required)
+ :type org_id: str
+ :param user_id: (required)
+ :type user_id: str
+ :param authorization:
+ :type authorization: object
+ :param _request_timeout: timeout setting for this request. If one
+ number provided, it will be total request
+ timeout. It can also be a pair (tuple) of
+ (connection, read) timeouts.
+ :type _request_timeout: int, tuple(int, int), optional
+ :param _request_auth: set to override the auth_settings for an a single
+ request; this effectively ignores the
+ authentication in the spec for a single request.
+ :type _request_auth: dict, optional
+ :param _content_type: force content-type for the request.
+ :type _content_type: str, Optional
+ :param _headers: set to override the headers for a single
+ request; this effectively ignores the headers
+ in the spec for a single request.
+ :type _headers: dict, optional
+ :param _host_index: set to override the host_index for a single
+ request; this effectively ignores the host_index
+ in the spec for a single request.
+ :type _host_index: int, optional
+ :return: Returns the result object.
+ """ # noqa: E501
+ _param = self._get_user_v1_organizations_org_id_users_user_id_get_0_serialize(
+ org_id=org_id, user_id=user_id, authorization=authorization, _request_auth=_request_auth, _content_type=_content_type, _headers=_headers, _host_index=_host_index
+ )
+
+ _response_types_map: dict[str, Optional[str]] = {
+ "200": "UserResponse",
+ "422": "HTTPValidationError",
+ }
+ response_data = self.api_client.call_api(*_param, _request_timeout=_request_timeout)
+ response_data.read()
+ return self.api_client.response_deserialize(
+ response_data=response_data,
+ response_types_map=_response_types_map,
+ )
+
+ @validate_call
+ def get_user_v1_organizations_org_id_users_user_id_get_0_without_preload_content(
+ self,
+ org_id: StrictStr,
+ user_id: StrictStr,
+ authorization: Optional[Any] = None,
+ _request_timeout: Union[None, Annotated[StrictFloat, Field(gt=0)], tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]]] = None,
+ _request_auth: Optional[dict[StrictStr, Any]] = None,
+ _content_type: Optional[StrictStr] = None,
+ _headers: Optional[dict[StrictStr, Any]] = None,
+ _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
+ ) -> RESTResponseType:
+ """Get User
+
+ Get details for a specific user in an organization. Returns detailed information about a user within the specified organization. The requesting user must be a member of the organization to access this endpoint.
+
+ :param org_id: (required)
+ :type org_id: str
+ :param user_id: (required)
+ :type user_id: str
+ :param authorization:
+ :type authorization: object
+ :param _request_timeout: timeout setting for this request. If one
+ number provided, it will be total request
+ timeout. It can also be a pair (tuple) of
+ (connection, read) timeouts.
+ :type _request_timeout: int, tuple(int, int), optional
+ :param _request_auth: set to override the auth_settings for an a single
+ request; this effectively ignores the
+ authentication in the spec for a single request.
+ :type _request_auth: dict, optional
+ :param _content_type: force content-type for the request.
+ :type _content_type: str, Optional
+ :param _headers: set to override the headers for a single
+ request; this effectively ignores the headers
+ in the spec for a single request.
+ :type _headers: dict, optional
+ :param _host_index: set to override the host_index for a single
+ request; this effectively ignores the host_index
+ in the spec for a single request.
+ :type _host_index: int, optional
+ :return: Returns the result object.
+ """ # noqa: E501
+ _param = self._get_user_v1_organizations_org_id_users_user_id_get_0_serialize(
+ org_id=org_id, user_id=user_id, authorization=authorization, _request_auth=_request_auth, _content_type=_content_type, _headers=_headers, _host_index=_host_index
+ )
+
+ _response_types_map: dict[str, Optional[str]] = {
+ "200": "UserResponse",
+ "422": "HTTPValidationError",
+ }
+ response_data = self.api_client.call_api(*_param, _request_timeout=_request_timeout)
+ return response_data.response
+
+ def _get_user_v1_organizations_org_id_users_user_id_get_0_serialize(
+ self,
+ org_id,
+ user_id,
+ authorization,
+ _request_auth,
+ _content_type,
+ _headers,
+ _host_index,
+ ) -> RequestSerialized:
+ _host = None
+
+ _collection_formats: dict[str, str] = {}
+
+ _path_params: dict[str, str] = {}
+ _query_params: list[tuple[str, str]] = []
+ _header_params: dict[str, Optional[str]] = _headers or {}
+ _form_params: list[tuple[str, str]] = []
+ _files: dict[str, Union[str, bytes, list[str], list[bytes], list[tuple[str, bytes]]]] = {}
+ _body_params: Optional[bytes] = None
+
+ # process the path parameters
+ if org_id is not None:
+ _path_params["org_id"] = org_id
+ if user_id is not None:
+ _path_params["user_id"] = user_id
+ # process the query parameters
+ # process the header parameters
+ if authorization is not None:
+ _header_params["authorization"] = authorization
+ # process the form parameters
+ # process the body parameter
+
+ # set the HTTP header `Accept`
+ if "Accept" not in _header_params:
+ _header_params["Accept"] = self.api_client.select_header_accept(["application/json"])
+
+ # authentication setting
+ _auth_settings: list[str] = []
+
+ return self.api_client.param_serialize(
+ method="GET",
+ resource_path="/v1/organizations/{org_id}/users/{user_id}",
+ path_params=_path_params,
+ query_params=_query_params,
+ header_params=_header_params,
+ body=_body_params,
+ post_params=_form_params,
+ files=_files,
+ auth_settings=_auth_settings,
+ collection_formats=_collection_formats,
+ _host=_host,
+ _request_auth=_request_auth,
+ )
+
+ @validate_call
+ def get_user_v1_organizations_org_id_users_user_id_get_1(
+ self,
+ org_id: StrictStr,
+ user_id: StrictStr,
+ authorization: Optional[Any] = None,
+ _request_timeout: Union[None, Annotated[StrictFloat, Field(gt=0)], tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]]] = None,
+ _request_auth: Optional[dict[StrictStr, Any]] = None,
+ _content_type: Optional[StrictStr] = None,
+ _headers: Optional[dict[StrictStr, Any]] = None,
+ _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
+ ) -> UserResponse:
+ """Get User
+
+ Get details for a specific user in an organization. Returns detailed information about a user within the specified organization. The requesting user must be a member of the organization to access this endpoint.
+
+ :param org_id: (required)
+ :type org_id: str
+ :param user_id: (required)
+ :type user_id: str
+ :param authorization:
+ :type authorization: object
+ :param _request_timeout: timeout setting for this request. If one
+ number provided, it will be total request
+ timeout. It can also be a pair (tuple) of
+ (connection, read) timeouts.
+ :type _request_timeout: int, tuple(int, int), optional
+ :param _request_auth: set to override the auth_settings for an a single
+ request; this effectively ignores the
+ authentication in the spec for a single request.
+ :type _request_auth: dict, optional
+ :param _content_type: force content-type for the request.
+ :type _content_type: str, Optional
+ :param _headers: set to override the headers for a single
+ request; this effectively ignores the headers
+ in the spec for a single request.
+ :type _headers: dict, optional
+ :param _host_index: set to override the host_index for a single
+ request; this effectively ignores the host_index
+ in the spec for a single request.
+ :type _host_index: int, optional
+ :return: Returns the result object.
+ """ # noqa: E501
+ _param = self._get_user_v1_organizations_org_id_users_user_id_get_1_serialize(
+ org_id=org_id, user_id=user_id, authorization=authorization, _request_auth=_request_auth, _content_type=_content_type, _headers=_headers, _host_index=_host_index
+ )
+
+ _response_types_map: dict[str, Optional[str]] = {
+ "200": "UserResponse",
+ "422": "HTTPValidationError",
+ }
+ response_data = self.api_client.call_api(*_param, _request_timeout=_request_timeout)
+ response_data.read()
+ return self.api_client.response_deserialize(
+ response_data=response_data,
+ response_types_map=_response_types_map,
+ ).data
+
+ @validate_call
+ def get_user_v1_organizations_org_id_users_user_id_get_1_with_http_info(
+ self,
+ org_id: StrictStr,
+ user_id: StrictStr,
+ authorization: Optional[Any] = None,
+ _request_timeout: Union[None, Annotated[StrictFloat, Field(gt=0)], tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]]] = None,
+ _request_auth: Optional[dict[StrictStr, Any]] = None,
+ _content_type: Optional[StrictStr] = None,
+ _headers: Optional[dict[StrictStr, Any]] = None,
+ _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
+ ) -> ApiResponse[UserResponse]:
+ """Get User
+
+ Get details for a specific user in an organization. Returns detailed information about a user within the specified organization. The requesting user must be a member of the organization to access this endpoint.
+
+ :param org_id: (required)
+ :type org_id: str
+ :param user_id: (required)
+ :type user_id: str
+ :param authorization:
+ :type authorization: object
+ :param _request_timeout: timeout setting for this request. If one
+ number provided, it will be total request
+ timeout. It can also be a pair (tuple) of
+ (connection, read) timeouts.
+ :type _request_timeout: int, tuple(int, int), optional
+ :param _request_auth: set to override the auth_settings for an a single
+ request; this effectively ignores the
+ authentication in the spec for a single request.
+ :type _request_auth: dict, optional
+ :param _content_type: force content-type for the request.
+ :type _content_type: str, Optional
+ :param _headers: set to override the headers for a single
+ request; this effectively ignores the headers
+ in the spec for a single request.
+ :type _headers: dict, optional
+ :param _host_index: set to override the host_index for a single
+ request; this effectively ignores the host_index
+ in the spec for a single request.
+ :type _host_index: int, optional
+ :return: Returns the result object.
+ """ # noqa: E501
+ _param = self._get_user_v1_organizations_org_id_users_user_id_get_1_serialize(
+ org_id=org_id, user_id=user_id, authorization=authorization, _request_auth=_request_auth, _content_type=_content_type, _headers=_headers, _host_index=_host_index
+ )
+
+ _response_types_map: dict[str, Optional[str]] = {
+ "200": "UserResponse",
+ "422": "HTTPValidationError",
+ }
+ response_data = self.api_client.call_api(*_param, _request_timeout=_request_timeout)
+ response_data.read()
+ return self.api_client.response_deserialize(
+ response_data=response_data,
+ response_types_map=_response_types_map,
+ )
+
+ @validate_call
+ def get_user_v1_organizations_org_id_users_user_id_get_1_without_preload_content(
+ self,
+ org_id: StrictStr,
+ user_id: StrictStr,
+ authorization: Optional[Any] = None,
+ _request_timeout: Union[None, Annotated[StrictFloat, Field(gt=0)], tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]]] = None,
+ _request_auth: Optional[dict[StrictStr, Any]] = None,
+ _content_type: Optional[StrictStr] = None,
+ _headers: Optional[dict[StrictStr, Any]] = None,
+ _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
+ ) -> RESTResponseType:
+ """Get User
+
+ Get details for a specific user in an organization. Returns detailed information about a user within the specified organization. The requesting user must be a member of the organization to access this endpoint.
+
+ :param org_id: (required)
+ :type org_id: str
+ :param user_id: (required)
+ :type user_id: str
+ :param authorization:
+ :type authorization: object
+ :param _request_timeout: timeout setting for this request. If one
+ number provided, it will be total request
+ timeout. It can also be a pair (tuple) of
+ (connection, read) timeouts.
+ :type _request_timeout: int, tuple(int, int), optional
+ :param _request_auth: set to override the auth_settings for an a single
+ request; this effectively ignores the
+ authentication in the spec for a single request.
+ :type _request_auth: dict, optional
+ :param _content_type: force content-type for the request.
+ :type _content_type: str, Optional
+ :param _headers: set to override the headers for a single
+ request; this effectively ignores the headers
+ in the spec for a single request.
+ :type _headers: dict, optional
+ :param _host_index: set to override the host_index for a single
+ request; this effectively ignores the host_index
+ in the spec for a single request.
+ :type _host_index: int, optional
+ :return: Returns the result object.
+ """ # noqa: E501
+ _param = self._get_user_v1_organizations_org_id_users_user_id_get_1_serialize(
+ org_id=org_id, user_id=user_id, authorization=authorization, _request_auth=_request_auth, _content_type=_content_type, _headers=_headers, _host_index=_host_index
+ )
+
+ _response_types_map: dict[str, Optional[str]] = {
+ "200": "UserResponse",
+ "422": "HTTPValidationError",
+ }
+ response_data = self.api_client.call_api(*_param, _request_timeout=_request_timeout)
+ return response_data.response
+
+ def _get_user_v1_organizations_org_id_users_user_id_get_1_serialize(
+ self,
+ org_id,
+ user_id,
+ authorization,
+ _request_auth,
+ _content_type,
+ _headers,
+ _host_index,
+ ) -> RequestSerialized:
+ _host = None
+
+ _collection_formats: dict[str, str] = {}
+
+ _path_params: dict[str, str] = {}
+ _query_params: list[tuple[str, str]] = []
+ _header_params: dict[str, Optional[str]] = _headers or {}
+ _form_params: list[tuple[str, str]] = []
+ _files: dict[str, Union[str, bytes, list[str], list[bytes], list[tuple[str, bytes]]]] = {}
+ _body_params: Optional[bytes] = None
+
+ # process the path parameters
+ if org_id is not None:
+ _path_params["org_id"] = org_id
+ if user_id is not None:
+ _path_params["user_id"] = user_id
+ # process the query parameters
+ # process the header parameters
+ if authorization is not None:
+ _header_params["authorization"] = authorization
+ # process the form parameters
+ # process the body parameter
+
+ # set the HTTP header `Accept`
+ if "Accept" not in _header_params:
+ _header_params["Accept"] = self.api_client.select_header_accept(["application/json"])
+
+ # authentication setting
+ _auth_settings: list[str] = []
+
+ return self.api_client.param_serialize(
+ method="GET",
+ resource_path="/v1/organizations/{org_id}/users/{user_id}",
+ path_params=_path_params,
+ query_params=_query_params,
+ header_params=_header_params,
+ body=_body_params,
+ post_params=_form_params,
+ files=_files,
+ auth_settings=_auth_settings,
+ collection_formats=_collection_formats,
+ _host=_host,
+ _request_auth=_request_auth,
+ )
+
+ @validate_call
+ def get_users_v1_organizations_org_id_users_get(
+ self,
+ org_id: StrictStr,
+ skip: Optional[Annotated[int, Field(strict=True, ge=0)]] = None,
+ limit: Optional[Annotated[int, Field(le=100, strict=True, ge=1)]] = None,
+ authorization: Optional[Any] = None,
+ _request_timeout: Union[None, Annotated[StrictFloat, Field(gt=0)], tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]]] = None,
+ _request_auth: Optional[dict[StrictStr, Any]] = None,
+ _content_type: Optional[StrictStr] = None,
+ _headers: Optional[dict[StrictStr, Any]] = None,
+ _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
+ ) -> PageUserResponse:
+ """Get Users
+
+ Get paginated list of users for a specific organization. Returns a paginated list of all users associated with the specified organization. The requesting user must be a member of the organization to access this endpoint.
+
+ :param org_id: (required)
+ :type org_id: str
+ :param skip:
+ :type skip: int
+ :param limit:
+ :type limit: int
+ :param authorization:
+ :type authorization: object
+ :param _request_timeout: timeout setting for this request. If one
+ number provided, it will be total request
+ timeout. It can also be a pair (tuple) of
+ (connection, read) timeouts.
+ :type _request_timeout: int, tuple(int, int), optional
+ :param _request_auth: set to override the auth_settings for an a single
+ request; this effectively ignores the
+ authentication in the spec for a single request.
+ :type _request_auth: dict, optional
+ :param _content_type: force content-type for the request.
+ :type _content_type: str, Optional
+ :param _headers: set to override the headers for a single
+ request; this effectively ignores the headers
+ in the spec for a single request.
+ :type _headers: dict, optional
+ :param _host_index: set to override the host_index for a single
+ request; this effectively ignores the host_index
+ in the spec for a single request.
+ :type _host_index: int, optional
+ :return: Returns the result object.
+ """ # noqa: E501
+ _param = self._get_users_v1_organizations_org_id_users_get_serialize(
+ org_id=org_id, skip=skip, limit=limit, authorization=authorization, _request_auth=_request_auth, _content_type=_content_type, _headers=_headers, _host_index=_host_index
+ )
+
+ _response_types_map: dict[str, Optional[str]] = {
+ "200": "PageUserResponse",
+ "422": "HTTPValidationError",
+ }
+ response_data = self.api_client.call_api(*_param, _request_timeout=_request_timeout)
+ response_data.read()
+ return self.api_client.response_deserialize(
+ response_data=response_data,
+ response_types_map=_response_types_map,
+ ).data
+
+ @validate_call
+ def get_users_v1_organizations_org_id_users_get_with_http_info(
+ self,
+ org_id: StrictStr,
+ skip: Optional[Annotated[int, Field(strict=True, ge=0)]] = None,
+ limit: Optional[Annotated[int, Field(le=100, strict=True, ge=1)]] = None,
+ authorization: Optional[Any] = None,
+ _request_timeout: Union[None, Annotated[StrictFloat, Field(gt=0)], tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]]] = None,
+ _request_auth: Optional[dict[StrictStr, Any]] = None,
+ _content_type: Optional[StrictStr] = None,
+ _headers: Optional[dict[StrictStr, Any]] = None,
+ _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
+ ) -> ApiResponse[PageUserResponse]:
+ """Get Users
+
+ Get paginated list of users for a specific organization. Returns a paginated list of all users associated with the specified organization. The requesting user must be a member of the organization to access this endpoint.
+
+ :param org_id: (required)
+ :type org_id: str
+ :param skip:
+ :type skip: int
+ :param limit:
+ :type limit: int
+ :param authorization:
+ :type authorization: object
+ :param _request_timeout: timeout setting for this request. If one
+ number provided, it will be total request
+ timeout. It can also be a pair (tuple) of
+ (connection, read) timeouts.
+ :type _request_timeout: int, tuple(int, int), optional
+ :param _request_auth: set to override the auth_settings for an a single
+ request; this effectively ignores the
+ authentication in the spec for a single request.
+ :type _request_auth: dict, optional
+ :param _content_type: force content-type for the request.
+ :type _content_type: str, Optional
+ :param _headers: set to override the headers for a single
+ request; this effectively ignores the headers
+ in the spec for a single request.
+ :type _headers: dict, optional
+ :param _host_index: set to override the host_index for a single
+ request; this effectively ignores the host_index
+ in the spec for a single request.
+ :type _host_index: int, optional
+ :return: Returns the result object.
+ """ # noqa: E501
+ _param = self._get_users_v1_organizations_org_id_users_get_serialize(
+ org_id=org_id, skip=skip, limit=limit, authorization=authorization, _request_auth=_request_auth, _content_type=_content_type, _headers=_headers, _host_index=_host_index
+ )
+
+ _response_types_map: dict[str, Optional[str]] = {
+ "200": "PageUserResponse",
+ "422": "HTTPValidationError",
+ }
+ response_data = self.api_client.call_api(*_param, _request_timeout=_request_timeout)
+ response_data.read()
+ return self.api_client.response_deserialize(
+ response_data=response_data,
+ response_types_map=_response_types_map,
+ )
+
+ @validate_call
+ def get_users_v1_organizations_org_id_users_get_without_preload_content(
+ self,
+ org_id: StrictStr,
+ skip: Optional[Annotated[int, Field(strict=True, ge=0)]] = None,
+ limit: Optional[Annotated[int, Field(le=100, strict=True, ge=1)]] = None,
+ authorization: Optional[Any] = None,
+ _request_timeout: Union[None, Annotated[StrictFloat, Field(gt=0)], tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]]] = None,
+ _request_auth: Optional[dict[StrictStr, Any]] = None,
+ _content_type: Optional[StrictStr] = None,
+ _headers: Optional[dict[StrictStr, Any]] = None,
+ _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
+ ) -> RESTResponseType:
+ """Get Users
+
+ Get paginated list of users for a specific organization. Returns a paginated list of all users associated with the specified organization. The requesting user must be a member of the organization to access this endpoint.
+
+ :param org_id: (required)
+ :type org_id: str
+ :param skip:
+ :type skip: int
+ :param limit:
+ :type limit: int
+ :param authorization:
+ :type authorization: object
+ :param _request_timeout: timeout setting for this request. If one
+ number provided, it will be total request
+ timeout. It can also be a pair (tuple) of
+ (connection, read) timeouts.
+ :type _request_timeout: int, tuple(int, int), optional
+ :param _request_auth: set to override the auth_settings for an a single
+ request; this effectively ignores the
+ authentication in the spec for a single request.
+ :type _request_auth: dict, optional
+ :param _content_type: force content-type for the request.
+ :type _content_type: str, Optional
+ :param _headers: set to override the headers for a single
+ request; this effectively ignores the headers
+ in the spec for a single request.
+ :type _headers: dict, optional
+ :param _host_index: set to override the host_index for a single
+ request; this effectively ignores the host_index
+ in the spec for a single request.
+ :type _host_index: int, optional
+ :return: Returns the result object.
+ """ # noqa: E501
+ _param = self._get_users_v1_organizations_org_id_users_get_serialize(
+ org_id=org_id, skip=skip, limit=limit, authorization=authorization, _request_auth=_request_auth, _content_type=_content_type, _headers=_headers, _host_index=_host_index
+ )
+
+ _response_types_map: dict[str, Optional[str]] = {
+ "200": "PageUserResponse",
+ "422": "HTTPValidationError",
+ }
+ response_data = self.api_client.call_api(*_param, _request_timeout=_request_timeout)
+ return response_data.response
+
+ def _get_users_v1_organizations_org_id_users_get_serialize(
+ self,
+ org_id,
+ skip,
+ limit,
+ authorization,
+ _request_auth,
+ _content_type,
+ _headers,
+ _host_index,
+ ) -> RequestSerialized:
+ _host = None
+
+ _collection_formats: dict[str, str] = {}
+
+ _path_params: dict[str, str] = {}
+ _query_params: list[tuple[str, str]] = []
+ _header_params: dict[str, Optional[str]] = _headers or {}
+ _form_params: list[tuple[str, str]] = []
+ _files: dict[str, Union[str, bytes, list[str], list[bytes], list[tuple[str, bytes]]]] = {}
+ _body_params: Optional[bytes] = None
+
+ # process the path parameters
+ if org_id is not None:
+ _path_params["org_id"] = org_id
+ # process the query parameters
+ if skip is not None:
+ _query_params.append(("skip", skip))
+
+ if limit is not None:
+ _query_params.append(("limit", limit))
+
+ # process the header parameters
+ if authorization is not None:
+ _header_params["authorization"] = authorization
+ # process the form parameters
+ # process the body parameter
+
+ # set the HTTP header `Accept`
+ if "Accept" not in _header_params:
+ _header_params["Accept"] = self.api_client.select_header_accept(["application/json"])
+
+ # authentication setting
+ _auth_settings: list[str] = []
+
+ return self.api_client.param_serialize(
+ method="GET",
+ resource_path="/v1/organizations/{org_id}/users",
+ path_params=_path_params,
+ query_params=_query_params,
+ header_params=_header_params,
+ body=_body_params,
+ post_params=_form_params,
+ files=_files,
+ auth_settings=_auth_settings,
+ collection_formats=_collection_formats,
+ _host=_host,
+ _request_auth=_request_auth,
+ )
+
+ @validate_call
+ def get_users_v1_organizations_org_id_users_get_0(
+ self,
+ org_id: StrictStr,
+ skip: Optional[Annotated[int, Field(strict=True, ge=0)]] = None,
+ limit: Optional[Annotated[int, Field(le=100, strict=True, ge=1)]] = None,
+ authorization: Optional[Any] = None,
+ _request_timeout: Union[None, Annotated[StrictFloat, Field(gt=0)], tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]]] = None,
+ _request_auth: Optional[dict[StrictStr, Any]] = None,
+ _content_type: Optional[StrictStr] = None,
+ _headers: Optional[dict[StrictStr, Any]] = None,
+ _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
+ ) -> PageUserResponse:
+ """Get Users
+
+ Get paginated list of users for a specific organization. Returns a paginated list of all users associated with the specified organization. The requesting user must be a member of the organization to access this endpoint.
+
+ :param org_id: (required)
+ :type org_id: str
+ :param skip:
+ :type skip: int
+ :param limit:
+ :type limit: int
+ :param authorization:
+ :type authorization: object
+ :param _request_timeout: timeout setting for this request. If one
+ number provided, it will be total request
+ timeout. It can also be a pair (tuple) of
+ (connection, read) timeouts.
+ :type _request_timeout: int, tuple(int, int), optional
+ :param _request_auth: set to override the auth_settings for an a single
+ request; this effectively ignores the
+ authentication in the spec for a single request.
+ :type _request_auth: dict, optional
+ :param _content_type: force content-type for the request.
+ :type _content_type: str, Optional
+ :param _headers: set to override the headers for a single
+ request; this effectively ignores the headers
+ in the spec for a single request.
+ :type _headers: dict, optional
+ :param _host_index: set to override the host_index for a single
+ request; this effectively ignores the host_index
+ in the spec for a single request.
+ :type _host_index: int, optional
+ :return: Returns the result object.
+ """ # noqa: E501
+ _param = self._get_users_v1_organizations_org_id_users_get_0_serialize(
+ org_id=org_id, skip=skip, limit=limit, authorization=authorization, _request_auth=_request_auth, _content_type=_content_type, _headers=_headers, _host_index=_host_index
+ )
+
+ _response_types_map: dict[str, Optional[str]] = {
+ "200": "PageUserResponse",
+ "422": "HTTPValidationError",
+ }
+ response_data = self.api_client.call_api(*_param, _request_timeout=_request_timeout)
+ response_data.read()
+ return self.api_client.response_deserialize(
+ response_data=response_data,
+ response_types_map=_response_types_map,
+ ).data
+
+ @validate_call
+ def get_users_v1_organizations_org_id_users_get_0_with_http_info(
+ self,
+ org_id: StrictStr,
+ skip: Optional[Annotated[int, Field(strict=True, ge=0)]] = None,
+ limit: Optional[Annotated[int, Field(le=100, strict=True, ge=1)]] = None,
+ authorization: Optional[Any] = None,
+ _request_timeout: Union[None, Annotated[StrictFloat, Field(gt=0)], tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]]] = None,
+ _request_auth: Optional[dict[StrictStr, Any]] = None,
+ _content_type: Optional[StrictStr] = None,
+ _headers: Optional[dict[StrictStr, Any]] = None,
+ _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
+ ) -> ApiResponse[PageUserResponse]:
+ """Get Users
+
+ Get paginated list of users for a specific organization. Returns a paginated list of all users associated with the specified organization. The requesting user must be a member of the organization to access this endpoint.
+
+ :param org_id: (required)
+ :type org_id: str
+ :param skip:
+ :type skip: int
+ :param limit:
+ :type limit: int
+ :param authorization:
+ :type authorization: object
+ :param _request_timeout: timeout setting for this request. If one
+ number provided, it will be total request
+ timeout. It can also be a pair (tuple) of
+ (connection, read) timeouts.
+ :type _request_timeout: int, tuple(int, int), optional
+ :param _request_auth: set to override the auth_settings for an a single
+ request; this effectively ignores the
+ authentication in the spec for a single request.
+ :type _request_auth: dict, optional
+ :param _content_type: force content-type for the request.
+ :type _content_type: str, Optional
+ :param _headers: set to override the headers for a single
+ request; this effectively ignores the headers
+ in the spec for a single request.
+ :type _headers: dict, optional
+ :param _host_index: set to override the host_index for a single
+ request; this effectively ignores the host_index
+ in the spec for a single request.
+ :type _host_index: int, optional
+ :return: Returns the result object.
+ """ # noqa: E501
+ _param = self._get_users_v1_organizations_org_id_users_get_0_serialize(
+ org_id=org_id, skip=skip, limit=limit, authorization=authorization, _request_auth=_request_auth, _content_type=_content_type, _headers=_headers, _host_index=_host_index
+ )
+
+ _response_types_map: dict[str, Optional[str]] = {
+ "200": "PageUserResponse",
+ "422": "HTTPValidationError",
+ }
+ response_data = self.api_client.call_api(*_param, _request_timeout=_request_timeout)
+ response_data.read()
+ return self.api_client.response_deserialize(
+ response_data=response_data,
+ response_types_map=_response_types_map,
+ )
+
+ @validate_call
+ def get_users_v1_organizations_org_id_users_get_0_without_preload_content(
+ self,
+ org_id: StrictStr,
+ skip: Optional[Annotated[int, Field(strict=True, ge=0)]] = None,
+ limit: Optional[Annotated[int, Field(le=100, strict=True, ge=1)]] = None,
+ authorization: Optional[Any] = None,
+ _request_timeout: Union[None, Annotated[StrictFloat, Field(gt=0)], tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]]] = None,
+ _request_auth: Optional[dict[StrictStr, Any]] = None,
+ _content_type: Optional[StrictStr] = None,
+ _headers: Optional[dict[StrictStr, Any]] = None,
+ _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
+ ) -> RESTResponseType:
+ """Get Users
+
+ Get paginated list of users for a specific organization. Returns a paginated list of all users associated with the specified organization. The requesting user must be a member of the organization to access this endpoint.
+
+ :param org_id: (required)
+ :type org_id: str
+ :param skip:
+ :type skip: int
+ :param limit:
+ :type limit: int
+ :param authorization:
+ :type authorization: object
+ :param _request_timeout: timeout setting for this request. If one
+ number provided, it will be total request
+ timeout. It can also be a pair (tuple) of
+ (connection, read) timeouts.
+ :type _request_timeout: int, tuple(int, int), optional
+ :param _request_auth: set to override the auth_settings for an a single
+ request; this effectively ignores the
+ authentication in the spec for a single request.
+ :type _request_auth: dict, optional
+ :param _content_type: force content-type for the request.
+ :type _content_type: str, Optional
+ :param _headers: set to override the headers for a single
+ request; this effectively ignores the headers
+ in the spec for a single request.
+ :type _headers: dict, optional
+ :param _host_index: set to override the host_index for a single
+ request; this effectively ignores the host_index
+ in the spec for a single request.
+ :type _host_index: int, optional
+ :return: Returns the result object.
+ """ # noqa: E501
+ _param = self._get_users_v1_organizations_org_id_users_get_0_serialize(
+ org_id=org_id, skip=skip, limit=limit, authorization=authorization, _request_auth=_request_auth, _content_type=_content_type, _headers=_headers, _host_index=_host_index
+ )
+
+ _response_types_map: dict[str, Optional[str]] = {
+ "200": "PageUserResponse",
+ "422": "HTTPValidationError",
+ }
+ response_data = self.api_client.call_api(*_param, _request_timeout=_request_timeout)
+ return response_data.response
+
+ def _get_users_v1_organizations_org_id_users_get_0_serialize(
+ self,
+ org_id,
+ skip,
+ limit,
+ authorization,
+ _request_auth,
+ _content_type,
+ _headers,
+ _host_index,
+ ) -> RequestSerialized:
+ _host = None
+
+ _collection_formats: dict[str, str] = {}
+
+ _path_params: dict[str, str] = {}
+ _query_params: list[tuple[str, str]] = []
+ _header_params: dict[str, Optional[str]] = _headers or {}
+ _form_params: list[tuple[str, str]] = []
+ _files: dict[str, Union[str, bytes, list[str], list[bytes], list[tuple[str, bytes]]]] = {}
+ _body_params: Optional[bytes] = None
+
+ # process the path parameters
+ if org_id is not None:
+ _path_params["org_id"] = org_id
+ # process the query parameters
+ if skip is not None:
+ _query_params.append(("skip", skip))
+
+ if limit is not None:
+ _query_params.append(("limit", limit))
+
+ # process the header parameters
+ if authorization is not None:
+ _header_params["authorization"] = authorization
+ # process the form parameters
+ # process the body parameter
+
+ # set the HTTP header `Accept`
+ if "Accept" not in _header_params:
+ _header_params["Accept"] = self.api_client.select_header_accept(["application/json"])
+
+ # authentication setting
+ _auth_settings: list[str] = []
+
+ return self.api_client.param_serialize(
+ method="GET",
+ resource_path="/v1/organizations/{org_id}/users",
+ path_params=_path_params,
+ query_params=_query_params,
+ header_params=_header_params,
+ body=_body_params,
+ post_params=_form_params,
+ files=_files,
+ auth_settings=_auth_settings,
+ collection_formats=_collection_formats,
+ _host=_host,
+ _request_auth=_request_auth,
+ )
+
+ @validate_call
+ def get_users_v1_organizations_org_id_users_get_1(
+ self,
+ org_id: StrictStr,
+ skip: Optional[Annotated[int, Field(strict=True, ge=0)]] = None,
+ limit: Optional[Annotated[int, Field(le=100, strict=True, ge=1)]] = None,
+ authorization: Optional[Any] = None,
+ _request_timeout: Union[None, Annotated[StrictFloat, Field(gt=0)], tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]]] = None,
+ _request_auth: Optional[dict[StrictStr, Any]] = None,
+ _content_type: Optional[StrictStr] = None,
+ _headers: Optional[dict[StrictStr, Any]] = None,
+ _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
+ ) -> PageUserResponse:
+ """Get Users
+
+ Get paginated list of users for a specific organization. Returns a paginated list of all users associated with the specified organization. The requesting user must be a member of the organization to access this endpoint.
+
+ :param org_id: (required)
+ :type org_id: str
+ :param skip:
+ :type skip: int
+ :param limit:
+ :type limit: int
+ :param authorization:
+ :type authorization: object
+ :param _request_timeout: timeout setting for this request. If one
+ number provided, it will be total request
+ timeout. It can also be a pair (tuple) of
+ (connection, read) timeouts.
+ :type _request_timeout: int, tuple(int, int), optional
+ :param _request_auth: set to override the auth_settings for an a single
+ request; this effectively ignores the
+ authentication in the spec for a single request.
+ :type _request_auth: dict, optional
+ :param _content_type: force content-type for the request.
+ :type _content_type: str, Optional
+ :param _headers: set to override the headers for a single
+ request; this effectively ignores the headers
+ in the spec for a single request.
+ :type _headers: dict, optional
+ :param _host_index: set to override the host_index for a single
+ request; this effectively ignores the host_index
+ in the spec for a single request.
+ :type _host_index: int, optional
+ :return: Returns the result object.
+ """ # noqa: E501
+ _param = self._get_users_v1_organizations_org_id_users_get_1_serialize(
+ org_id=org_id, skip=skip, limit=limit, authorization=authorization, _request_auth=_request_auth, _content_type=_content_type, _headers=_headers, _host_index=_host_index
+ )
+
+ _response_types_map: dict[str, Optional[str]] = {
+ "200": "PageUserResponse",
+ "422": "HTTPValidationError",
+ }
+ response_data = self.api_client.call_api(*_param, _request_timeout=_request_timeout)
+ response_data.read()
+ return self.api_client.response_deserialize(
+ response_data=response_data,
+ response_types_map=_response_types_map,
+ ).data
+
+ @validate_call
+ def get_users_v1_organizations_org_id_users_get_1_with_http_info(
+ self,
+ org_id: StrictStr,
+ skip: Optional[Annotated[int, Field(strict=True, ge=0)]] = None,
+ limit: Optional[Annotated[int, Field(le=100, strict=True, ge=1)]] = None,
+ authorization: Optional[Any] = None,
+ _request_timeout: Union[None, Annotated[StrictFloat, Field(gt=0)], tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]]] = None,
+ _request_auth: Optional[dict[StrictStr, Any]] = None,
+ _content_type: Optional[StrictStr] = None,
+ _headers: Optional[dict[StrictStr, Any]] = None,
+ _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
+ ) -> ApiResponse[PageUserResponse]:
+ """Get Users
+
+ Get paginated list of users for a specific organization. Returns a paginated list of all users associated with the specified organization. The requesting user must be a member of the organization to access this endpoint.
+
+ :param org_id: (required)
+ :type org_id: str
+ :param skip:
+ :type skip: int
+ :param limit:
+ :type limit: int
+ :param authorization:
+ :type authorization: object
+ :param _request_timeout: timeout setting for this request. If one
+ number provided, it will be total request
+ timeout. It can also be a pair (tuple) of
+ (connection, read) timeouts.
+ :type _request_timeout: int, tuple(int, int), optional
+ :param _request_auth: set to override the auth_settings for an a single
+ request; this effectively ignores the
+ authentication in the spec for a single request.
+ :type _request_auth: dict, optional
+ :param _content_type: force content-type for the request.
+ :type _content_type: str, Optional
+ :param _headers: set to override the headers for a single
+ request; this effectively ignores the headers
+ in the spec for a single request.
+ :type _headers: dict, optional
+ :param _host_index: set to override the host_index for a single
+ request; this effectively ignores the host_index
+ in the spec for a single request.
+ :type _host_index: int, optional
+ :return: Returns the result object.
+ """ # noqa: E501
+ _param = self._get_users_v1_organizations_org_id_users_get_1_serialize(
+ org_id=org_id, skip=skip, limit=limit, authorization=authorization, _request_auth=_request_auth, _content_type=_content_type, _headers=_headers, _host_index=_host_index
+ )
+
+ _response_types_map: dict[str, Optional[str]] = {
+ "200": "PageUserResponse",
+ "422": "HTTPValidationError",
+ }
+ response_data = self.api_client.call_api(*_param, _request_timeout=_request_timeout)
+ response_data.read()
+ return self.api_client.response_deserialize(
+ response_data=response_data,
+ response_types_map=_response_types_map,
+ )
+
+ @validate_call
+ def get_users_v1_organizations_org_id_users_get_1_without_preload_content(
+ self,
+ org_id: StrictStr,
+ skip: Optional[Annotated[int, Field(strict=True, ge=0)]] = None,
+ limit: Optional[Annotated[int, Field(le=100, strict=True, ge=1)]] = None,
+ authorization: Optional[Any] = None,
+ _request_timeout: Union[None, Annotated[StrictFloat, Field(gt=0)], tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]]] = None,
+ _request_auth: Optional[dict[StrictStr, Any]] = None,
+ _content_type: Optional[StrictStr] = None,
+ _headers: Optional[dict[StrictStr, Any]] = None,
+ _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
+ ) -> RESTResponseType:
+ """Get Users
+
+ Get paginated list of users for a specific organization. Returns a paginated list of all users associated with the specified organization. The requesting user must be a member of the organization to access this endpoint.
+
+ :param org_id: (required)
+ :type org_id: str
+ :param skip:
+ :type skip: int
+ :param limit:
+ :type limit: int
+ :param authorization:
+ :type authorization: object
+ :param _request_timeout: timeout setting for this request. If one
+ number provided, it will be total request
+ timeout. It can also be a pair (tuple) of
+ (connection, read) timeouts.
+ :type _request_timeout: int, tuple(int, int), optional
+ :param _request_auth: set to override the auth_settings for an a single
+ request; this effectively ignores the
+ authentication in the spec for a single request.
+ :type _request_auth: dict, optional
+ :param _content_type: force content-type for the request.
+ :type _content_type: str, Optional
+ :param _headers: set to override the headers for a single
+ request; this effectively ignores the headers
+ in the spec for a single request.
+ :type _headers: dict, optional
+ :param _host_index: set to override the host_index for a single
+ request; this effectively ignores the host_index
+ in the spec for a single request.
+ :type _host_index: int, optional
+ :return: Returns the result object.
+ """ # noqa: E501
+ _param = self._get_users_v1_organizations_org_id_users_get_1_serialize(
+ org_id=org_id, skip=skip, limit=limit, authorization=authorization, _request_auth=_request_auth, _content_type=_content_type, _headers=_headers, _host_index=_host_index
+ )
+
+ _response_types_map: dict[str, Optional[str]] = {
+ "200": "PageUserResponse",
+ "422": "HTTPValidationError",
+ }
+ response_data = self.api_client.call_api(*_param, _request_timeout=_request_timeout)
+ return response_data.response
+
+ def _get_users_v1_organizations_org_id_users_get_1_serialize(
+ self,
+ org_id,
+ skip,
+ limit,
+ authorization,
+ _request_auth,
+ _content_type,
+ _headers,
+ _host_index,
+ ) -> RequestSerialized:
+ _host = None
+
+ _collection_formats: dict[str, str] = {}
+
+ _path_params: dict[str, str] = {}
+ _query_params: list[tuple[str, str]] = []
+ _header_params: dict[str, Optional[str]] = _headers or {}
+ _form_params: list[tuple[str, str]] = []
+ _files: dict[str, Union[str, bytes, list[str], list[bytes], list[tuple[str, bytes]]]] = {}
+ _body_params: Optional[bytes] = None
+
+ # process the path parameters
+ if org_id is not None:
+ _path_params["org_id"] = org_id
+ # process the query parameters
+ if skip is not None:
+ _query_params.append(("skip", skip))
+
+ if limit is not None:
+ _query_params.append(("limit", limit))
+
+ # process the header parameters
+ if authorization is not None:
+ _header_params["authorization"] = authorization
+ # process the form parameters
+ # process the body parameter
+
+ # set the HTTP header `Accept`
+ if "Accept" not in _header_params:
+ _header_params["Accept"] = self.api_client.select_header_accept(["application/json"])
+
+ # authentication setting
+ _auth_settings: list[str] = []
+
+ return self.api_client.param_serialize(
+ method="GET",
+ resource_path="/v1/organizations/{org_id}/users",
+ path_params=_path_params,
+ query_params=_query_params,
+ header_params=_header_params,
+ body=_body_params,
+ post_params=_form_params,
+ files=_files,
+ auth_settings=_auth_settings,
+ collection_formats=_collection_formats,
+ _host=_host,
+ _request_auth=_request_auth,
+ )
diff --git a/src/codegen/agents/client/openapi_client/api_client.py b/src/codegen/agents/client/openapi_client/api_client.py
new file mode 100644
index 000000000..02e3fdb6c
--- /dev/null
+++ b/src/codegen/agents/client/openapi_client/api_client.py
@@ -0,0 +1,661 @@
+"""Developer API
+
+API for application developers
+
+The version of the OpenAPI document: 1.0.0
+Generated by OpenAPI Generator (https://openapi-generator.tech)
+
+Do not edit the class manually.
+"""
+
+import datetime
+import decimal
+import json
+import mimetypes
+import os
+import re
+import tempfile
+from enum import Enum
+from typing import Optional, Union
+from urllib.parse import quote
+
+from dateutil.parser import parse
+from pydantic import SecretStr
+
+import codegen.agents.client.openapi_client as openapi_client
+from codegen.agents.client.openapi_client import rest
+from codegen.agents.client.openapi_client.api_response import ApiResponse
+from codegen.agents.client.openapi_client.api_response import T as ApiResponseT
+from codegen.agents.client.openapi_client.configuration import Configuration
+from codegen.agents.client.openapi_client.exceptions import ApiException, ApiValueError
+
+RequestSerialized = tuple[str, str, dict[str, str], Optional[str], list[str]]
+
+
+class ApiClient:
+ """Generic API client for OpenAPI client library builds.
+
+ OpenAPI generic API client. This client handles the client-
+ server communication, and is invariant across implementations. Specifics of
+ the methods and models for each application are generated from the OpenAPI
+ templates.
+
+ :param configuration: .Configuration object for this client
+ :param header_name: a header to pass when making calls to the API.
+ :param header_value: a header value to pass when making calls to
+ the API.
+ :param cookie: a cookie to include in the header when making calls
+ to the API
+ """
+
+ PRIMITIVE_TYPES = (float, bool, bytes, str, int)
+ NATIVE_TYPES_MAPPING = {
+ "int": int,
+ "long": int, # TODO remove as only py3 is supported?
+ "float": float,
+ "str": str,
+ "bool": bool,
+ "date": datetime.date,
+ "datetime": datetime.datetime,
+ "decimal": decimal.Decimal,
+ "object": object,
+ }
+ _pool = None
+
+ def __init__(self, configuration=None, header_name=None, header_value=None, cookie=None) -> None:
+ # use default configuration if none is provided
+ if configuration is None:
+ configuration = Configuration.get_default()
+ self.configuration = configuration
+
+ self.rest_client = rest.RESTClientObject(configuration)
+ self.default_headers = {}
+ if header_name is not None:
+ self.default_headers[header_name] = header_value
+ self.cookie = cookie
+ # Set default User-Agent.
+ self.user_agent = "OpenAPI-Generator/1.0.0/python"
+ self.client_side_validation = configuration.client_side_validation
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, exc_type, exc_value, traceback):
+ pass
+
+ @property
+ def user_agent(self):
+ """User agent for this API client"""
+ return self.default_headers["User-Agent"]
+
+ @user_agent.setter
+ def user_agent(self, value):
+ self.default_headers["User-Agent"] = value
+
+ def set_default_header(self, header_name, header_value):
+ self.default_headers[header_name] = header_value
+
+ _default = None
+
+ @classmethod
+ def get_default(cls):
+ """Return new instance of ApiClient.
+
+ This method returns newly created, based on default constructor,
+ object of ApiClient class or returns a copy of default
+ ApiClient.
+
+ :return: The ApiClient object.
+ """
+ if cls._default is None:
+ cls._default = ApiClient()
+ return cls._default
+
+ @classmethod
+ def set_default(cls, default):
+ """Set default instance of ApiClient.
+
+ It stores default ApiClient.
+
+ :param default: object of ApiClient.
+ """
+ cls._default = default
+
+ def param_serialize(
+ self,
+ method,
+ resource_path,
+ path_params=None,
+ query_params=None,
+ header_params=None,
+ body=None,
+ post_params=None,
+ files=None,
+ auth_settings=None,
+ collection_formats=None,
+ _host=None,
+ _request_auth=None,
+ ) -> RequestSerialized:
+ """Builds the HTTP request params needed by the request.
+ :param method: Method to call.
+ :param resource_path: Path to method endpoint.
+ :param path_params: Path parameters in the url.
+ :param query_params: Query parameters in the url.
+ :param header_params: Header parameters to be
+ placed in the request header.
+ :param body: Request body.
+ :param post_params dict: Request post form parameters,
+ for `application/x-www-form-urlencoded`, `multipart/form-data`.
+ :param auth_settings list: Auth Settings names for the request.
+ :param files dict: key -> filename, value -> filepath,
+ for `multipart/form-data`.
+ :param collection_formats: dict of collection formats for path, query,
+ header, and post parameters.
+ :param _request_auth: set to override the auth_settings for an a single
+ request; this effectively ignores the authentication
+ in the spec for a single request.
+ :return: tuple of form (path, http_method, query_params, header_params,
+ body, post_params, files)
+ """
+ config = self.configuration
+
+ # header parameters
+ header_params = header_params or {}
+ header_params.update(self.default_headers)
+ if self.cookie:
+ header_params["Cookie"] = self.cookie
+ if header_params:
+ header_params = self.sanitize_for_serialization(header_params)
+ header_params = dict(self.parameters_to_tuples(header_params, collection_formats))
+
+ # path parameters
+ if path_params:
+ path_params = self.sanitize_for_serialization(path_params)
+ path_params = self.parameters_to_tuples(path_params, collection_formats)
+ for k, v in path_params:
+ # specified safe chars, encode everything
+ resource_path = resource_path.replace(f"{{{k}}}", quote(str(v), safe=config.safe_chars_for_path_param))
+
+ # post parameters
+ if post_params or files:
+ post_params = post_params if post_params else []
+ post_params = self.sanitize_for_serialization(post_params)
+ post_params = self.parameters_to_tuples(post_params, collection_formats)
+ if files:
+ post_params.extend(self.files_parameters(files))
+
+ # auth setting
+ self.update_params_for_auth(header_params, query_params, auth_settings, resource_path, method, body, request_auth=_request_auth)
+
+ # body
+ if body:
+ body = self.sanitize_for_serialization(body)
+
+ # request url
+ if _host is None or self.configuration.ignore_operation_servers:
+ url = self.configuration.host + resource_path
+ else:
+ # use server/host defined in path or operation instead
+ url = _host + resource_path
+
+ # query parameters
+ if query_params:
+ query_params = self.sanitize_for_serialization(query_params)
+ url_query = self.parameters_to_url_query(query_params, collection_formats)
+ url += "?" + url_query
+
+ return method, url, header_params, body, post_params
+
+ def call_api(self, method, url, header_params=None, body=None, post_params=None, _request_timeout=None) -> rest.RESTResponse:
+ """Makes the HTTP request (synchronous)
+ :param method: Method to call.
+ :param url: Path to method endpoint.
+ :param header_params: Header parameters to be
+ placed in the request header.
+ :param body: Request body.
+ :param post_params dict: Request post form parameters,
+ for `application/x-www-form-urlencoded`, `multipart/form-data`.
+ :param _request_timeout: timeout setting for this request.
+ :return: RESTResponse
+ """
+ try:
+ # perform request and return response
+ response_data = self.rest_client.request(method, url, headers=header_params, body=body, post_params=post_params, _request_timeout=_request_timeout)
+
+ except ApiException as e:
+ raise e
+
+ return response_data
+
+ def response_deserialize(self, response_data: rest.RESTResponse, response_types_map: Optional[dict[str, ApiResponseT]] = None) -> ApiResponse[ApiResponseT]:
+ """Deserializes response into an object.
+ :param response_data: RESTResponse object to be deserialized.
+ :param response_types_map: dict of response types.
+ :return: ApiResponse
+ """
+ msg = "RESTResponse.read() must be called before passing it to response_deserialize()"
+ assert response_data.data is not None, msg
+
+ response_type = response_types_map.get(str(response_data.status), None)
+ if not response_type and isinstance(response_data.status, int) and 100 <= response_data.status <= 599:
+ # if not found, look for '1XX', '2XX', etc.
+ response_type = response_types_map.get(str(response_data.status)[0] + "XX", None)
+
+ # deserialize response data
+ response_text = None
+ return_data = None
+ try:
+ if response_type == "bytearray":
+ return_data = response_data.data
+ elif response_type == "file":
+ return_data = self.__deserialize_file(response_data)
+ elif response_type is not None:
+ match = None
+ content_type = response_data.getheader("content-type")
+ if content_type is not None:
+ match = re.search(r"charset=([a-zA-Z\-\d]+)[\s;]?", content_type)
+ encoding = match.group(1) if match else "utf-8"
+ response_text = response_data.data.decode(encoding)
+ return_data = self.deserialize(response_text, response_type, content_type)
+ finally:
+ if not 200 <= response_data.status <= 299:
+ raise ApiException.from_response(
+ http_resp=response_data,
+ body=response_text,
+ data=return_data,
+ )
+
+ return ApiResponse(status_code=response_data.status, data=return_data, headers=response_data.getheaders(), raw_data=response_data.data)
+
+ def sanitize_for_serialization(self, obj):
+ """Builds a JSON POST object.
+
+ If obj is None, return None.
+ If obj is SecretStr, return obj.get_secret_value()
+ If obj is str, int, long, float, bool, return directly.
+ If obj is datetime.datetime, datetime.date
+ convert to string in iso8601 format.
+ If obj is decimal.Decimal return string representation.
+ If obj is list, sanitize each element in the list.
+ If obj is dict, return the dict.
+ If obj is OpenAPI model, return the properties dict.
+
+ :param obj: The data to serialize.
+ :return: The serialized form of data.
+ """
+ if obj is None:
+ return None
+ elif isinstance(obj, Enum):
+ return obj.value
+ elif isinstance(obj, SecretStr):
+ return obj.get_secret_value()
+ elif isinstance(obj, self.PRIMITIVE_TYPES):
+ return obj
+ elif isinstance(obj, list):
+ return [self.sanitize_for_serialization(sub_obj) for sub_obj in obj]
+ elif isinstance(obj, tuple):
+ return tuple(self.sanitize_for_serialization(sub_obj) for sub_obj in obj)
+ elif isinstance(obj, (datetime.datetime, datetime.date)):
+ return obj.isoformat()
+ elif isinstance(obj, decimal.Decimal):
+ return str(obj)
+
+ elif isinstance(obj, dict):
+ obj_dict = obj
+ else:
+ # Convert model obj to dict except
+ # attributes `openapi_types`, `attribute_map`
+ # and attributes which value is not None.
+ # Convert attribute name to json key in
+ # model definition for request.
+ if hasattr(obj, "to_dict") and callable(getattr(obj, "to_dict")):
+ obj_dict = obj.to_dict()
+ else:
+ obj_dict = obj.__dict__
+
+ return {key: self.sanitize_for_serialization(val) for key, val in obj_dict.items()}
+
+ def deserialize(self, response_text: str, response_type: str, content_type: Optional[str]):
+ """Deserializes response into an object.
+
+ :param response: RESTResponse object to be deserialized.
+ :param response_type: class literal for
+ deserialized object, or string of class name.
+ :param content_type: content type of response.
+
+ :return: deserialized object.
+ """
+ # fetch data from response object
+ if content_type is None:
+ try:
+ data = json.loads(response_text)
+ except ValueError:
+ data = response_text
+ elif re.match(r"^application/(json|[\w!#$&.+-^_]+\+json)\s*(;|$)", content_type, re.IGNORECASE):
+ if response_text == "":
+ data = ""
+ else:
+ data = json.loads(response_text)
+ elif re.match(r"^text\/[a-z.+-]+\s*(;|$)", content_type, re.IGNORECASE):
+ data = response_text
+ else:
+ raise ApiException(status=0, reason=f"Unsupported content type: {content_type}")
+
+ return self.__deserialize(data, response_type)
+
+ def __deserialize(self, data, klass):
+ """Deserializes dict, list, str into an object.
+
+ :param data: dict, list or str.
+ :param klass: class literal, or string of class name.
+
+ :return: object.
+ """
+ if data is None:
+ return None
+
+ if isinstance(klass, str):
+ if klass.startswith("List["):
+ m = re.match(r"List\[(.*)]", klass)
+ assert m is not None, "Malformed List type definition"
+ sub_kls = m.group(1)
+ return [self.__deserialize(sub_data, sub_kls) for sub_data in data]
+
+ if klass.startswith("Dict["):
+ m = re.match(r"Dict\[([^,]*), (.*)]", klass)
+ assert m is not None, "Malformed Dict type definition"
+ sub_kls = m.group(2)
+ return {k: self.__deserialize(v, sub_kls) for k, v in data.items()}
+
+ # convert str to class
+ if klass in self.NATIVE_TYPES_MAPPING:
+ klass = self.NATIVE_TYPES_MAPPING[klass]
+ else:
+ klass = getattr(openapi_client.models, klass)
+
+ if klass in self.PRIMITIVE_TYPES:
+ return self.__deserialize_primitive(data, klass)
+ elif klass == object:
+ return self.__deserialize_object(data)
+ elif klass == datetime.date:
+ return self.__deserialize_date(data)
+ elif klass == datetime.datetime:
+ return self.__deserialize_datetime(data)
+ elif klass == decimal.Decimal:
+ return decimal.Decimal(data)
+ elif issubclass(klass, Enum):
+ return self.__deserialize_enum(data, klass)
+ else:
+ return self.__deserialize_model(data, klass)
+
+ def parameters_to_tuples(self, params, collection_formats):
+ """Get parameters as list of tuples, formatting collections.
+
+ :param params: Parameters as dict or list of two-tuples
+ :param dict collection_formats: Parameter collection formats
+ :return: Parameters as list of tuples, collections formatted
+ """
+ new_params: list[tuple[str, str]] = []
+ if collection_formats is None:
+ collection_formats = {}
+ for k, v in params.items() if isinstance(params, dict) else params:
+ if k in collection_formats:
+ collection_format = collection_formats[k]
+ if collection_format == "multi":
+ new_params.extend((k, value) for value in v)
+ else:
+ if collection_format == "ssv":
+ delimiter = " "
+ elif collection_format == "tsv":
+ delimiter = "\t"
+ elif collection_format == "pipes":
+ delimiter = "|"
+ else: # csv is the default
+ delimiter = ","
+ new_params.append((k, delimiter.join(str(value) for value in v)))
+ else:
+ new_params.append((k, v))
+ return new_params
+
+ def parameters_to_url_query(self, params, collection_formats):
+ """Get parameters as list of tuples, formatting collections.
+
+ :param params: Parameters as dict or list of two-tuples
+ :param dict collection_formats: Parameter collection formats
+ :return: URL query string (e.g. a=Hello%20World&b=123)
+ """
+ new_params: list[tuple[str, str]] = []
+ if collection_formats is None:
+ collection_formats = {}
+ for k, v in params.items() if isinstance(params, dict) else params:
+ if isinstance(v, bool):
+ v = str(v).lower()
+ if isinstance(v, (int, float)):
+ v = str(v)
+ if isinstance(v, dict):
+ v = json.dumps(v)
+
+ if k in collection_formats:
+ collection_format = collection_formats[k]
+ if collection_format == "multi":
+ new_params.extend((k, quote(str(value))) for value in v)
+ else:
+ if collection_format == "ssv":
+ delimiter = " "
+ elif collection_format == "tsv":
+ delimiter = "\t"
+ elif collection_format == "pipes":
+ delimiter = "|"
+ else: # csv is the default
+ delimiter = ","
+ new_params.append((k, delimiter.join(quote(str(value)) for value in v)))
+ else:
+ new_params.append((k, quote(str(v))))
+
+ return "&".join(["=".join(map(str, item)) for item in new_params])
+
+ def files_parameters(
+ self,
+ files: dict[str, Union[str, bytes, list[str], list[bytes], tuple[str, bytes]]],
+ ):
+ """Builds form parameters.
+
+ :param files: File parameters.
+ :return: Form parameters with files.
+ """
+ params = []
+ for k, v in files.items():
+ if isinstance(v, str):
+ with open(v, "rb") as f:
+ filename = os.path.basename(f.name)
+ filedata = f.read()
+ elif isinstance(v, bytes):
+ filename = k
+ filedata = v
+ elif isinstance(v, tuple):
+ filename, filedata = v
+ elif isinstance(v, list):
+ for file_param in v:
+ params.extend(self.files_parameters({k: file_param}))
+ continue
+ else:
+ msg = "Unsupported file value"
+ raise ValueError(msg)
+ mimetype = mimetypes.guess_type(filename)[0] or "application/octet-stream"
+ params.append(tuple([k, tuple([filename, filedata, mimetype])]))
+ return params
+
+ def select_header_accept(self, accepts: list[str]) -> Optional[str]:
+ """Returns `Accept` based on an array of accepts provided.
+
+ :param accepts: List of headers.
+ :return: Accept (e.g. application/json).
+ """
+ if not accepts:
+ return None
+
+ for accept in accepts:
+ if re.search("json", accept, re.IGNORECASE):
+ return accept
+
+ return accepts[0]
+
+ def select_header_content_type(self, content_types):
+ """Returns `Content-Type` based on an array of content_types provided.
+
+ :param content_types: List of content-types.
+ :return: Content-Type (e.g. application/json).
+ """
+ if not content_types:
+ return None
+
+ for content_type in content_types:
+ if re.search("json", content_type, re.IGNORECASE):
+ return content_type
+
+ return content_types[0]
+
+ def update_params_for_auth(self, headers, queries, auth_settings, resource_path, method, body, request_auth=None) -> None:
+ """Updates header and query params based on authentication setting.
+
+ :param headers: Header parameters dict to be updated.
+ :param queries: Query parameters tuple list to be updated.
+ :param auth_settings: Authentication setting identifiers list.
+ :resource_path: A string representation of the HTTP request resource path.
+ :method: A string representation of the HTTP request method.
+ :body: A object representing the body of the HTTP request.
+ The object type is the return value of sanitize_for_serialization().
+ :param request_auth: if set, the provided settings will
+ override the token in the configuration.
+ """
+ if not auth_settings:
+ return
+
+ if request_auth:
+ self._apply_auth_params(headers, queries, resource_path, method, body, request_auth)
+ else:
+ for auth in auth_settings:
+ auth_setting = self.configuration.auth_settings().get(auth)
+ if auth_setting:
+ self._apply_auth_params(headers, queries, resource_path, method, body, auth_setting)
+
+ def _apply_auth_params(self, headers, queries, resource_path, method, body, auth_setting) -> None:
+ """Updates the request parameters based on a single auth_setting
+
+ :param headers: Header parameters dict to be updated.
+ :param queries: Query parameters tuple list to be updated.
+ :resource_path: A string representation of the HTTP request resource path.
+ :method: A string representation of the HTTP request method.
+ :body: A object representing the body of the HTTP request.
+ The object type is the return value of sanitize_for_serialization().
+ :param auth_setting: auth settings for the endpoint
+ """
+ if auth_setting["in"] == "cookie":
+ headers["Cookie"] = auth_setting["value"]
+ elif auth_setting["in"] == "header":
+ if auth_setting["type"] != "http-signature":
+ headers[auth_setting["key"]] = auth_setting["value"]
+ elif auth_setting["in"] == "query":
+ queries.append((auth_setting["key"], auth_setting["value"]))
+ else:
+ msg = "Authentication token must be in `query` or `header`"
+ raise ApiValueError(msg)
+
+ def __deserialize_file(self, response):
+ """Deserializes body to file
+
+ Saves response body into a file in a temporary folder,
+ using the filename from the `Content-Disposition` header if provided.
+
+ handle file downloading
+ save response body into a tmp file and return the instance
+
+ :param response: RESTResponse.
+ :return: file path.
+ """
+ fd, path = tempfile.mkstemp(dir=self.configuration.temp_folder_path)
+ os.close(fd)
+ os.remove(path)
+
+ content_disposition = response.getheader("Content-Disposition")
+ if content_disposition:
+ m = re.search(r'filename=[\'"]?([^\'"\s]+)[\'"]?', content_disposition)
+ assert m is not None, "Unexpected 'content-disposition' header value"
+ filename = m.group(1)
+ path = os.path.join(os.path.dirname(path), filename)
+
+ with open(path, "wb") as f:
+ f.write(response.data)
+
+ return path
+
+ def __deserialize_primitive(self, data, klass):
+ """Deserializes string to primitive type.
+
+ :param data: str.
+ :param klass: class literal.
+
+ :return: int, long, float, str, bool.
+ """
+ try:
+ return klass(data)
+ except UnicodeEncodeError:
+ return str(data)
+ except TypeError:
+ return data
+
+ def __deserialize_object(self, value):
+ """Return an original value.
+
+ :return: object.
+ """
+ return value
+
+ def __deserialize_date(self, string):
+ """Deserializes string to date.
+
+ :param string: str.
+ :return: date.
+ """
+ try:
+ return parse(string).date()
+ except ImportError:
+ return string
+ except ValueError:
+ raise rest.ApiException(status=0, reason=f"Failed to parse `{string}` as date object")
+
+ def __deserialize_datetime(self, string):
+ """Deserializes string to datetime.
+
+ The string should be in iso8601 datetime format.
+
+ :param string: str.
+ :return: datetime.
+ """
+ try:
+ return parse(string)
+ except ImportError:
+ return string
+ except ValueError:
+ raise rest.ApiException(status=0, reason=(f"Failed to parse `{string}` as datetime object"))
+
+ def __deserialize_enum(self, data, klass):
+ """Deserializes primitive type to enum.
+
+ :param data: primitive type.
+ :param klass: class literal.
+ :return: enum value.
+ """
+ try:
+ return klass(data)
+ except ValueError:
+ raise rest.ApiException(status=0, reason=(f"Failed to parse `{data}` as `{klass}`"))
+
+ def __deserialize_model(self, data, klass):
+ """Deserializes list or dict to model.
+
+ :param data: dict, list.
+ :param klass: class literal.
+ :return: model object.
+ """
+ return klass.from_dict(data)
diff --git a/src/codegen/agents/client/openapi_client/api_response.py b/src/codegen/agents/client/openapi_client/api_response.py
new file mode 100644
index 000000000..3842a95da
--- /dev/null
+++ b/src/codegen/agents/client/openapi_client/api_response.py
@@ -0,0 +1,20 @@
+"""API response object."""
+
+from __future__ import annotations
+
+from typing import Generic, TypeVar
+
+from pydantic import BaseModel, Field, StrictBytes, StrictInt
+
+T = TypeVar("T")
+
+
+class ApiResponse(BaseModel, Generic[T]):
+ """API response object"""
+
+ status_code: StrictInt = Field(description="HTTP status code")
+ headers: dict[str, str] | None = Field(None, description="HTTP headers")
+ data: T = Field(description="Deserialized data given the data type")
+ raw_data: StrictBytes = Field(description="Raw data (HTTP response body)")
+
+ model_config = {"arbitrary_types_allowed": True}
diff --git a/src/codegen/agents/client/openapi_client/configuration.py b/src/codegen/agents/client/openapi_client/configuration.py
new file mode 100644
index 000000000..8c1b3f2e6
--- /dev/null
+++ b/src/codegen/agents/client/openapi_client/configuration.py
@@ -0,0 +1,545 @@
+"""Developer API
+
+API for application developers
+
+The version of the OpenAPI document: 1.0.0
+Generated by OpenAPI Generator (https://openapi-generator.tech)
+
+Do not edit the class manually.
+"""
+
+import copy
+import http.client as httplib
+import logging
+import multiprocessing
+import sys
+from logging import FileHandler
+from typing import Any, ClassVar, Literal, Optional, TypedDict, Union
+
+import urllib3
+from typing_extensions import NotRequired, Self
+
+JSON_SCHEMA_VALIDATION_KEYWORDS = {"multipleOf", "maximum", "exclusiveMaximum", "minimum", "exclusiveMinimum", "maxLength", "minLength", "pattern", "maxItems", "minItems"}
+
+ServerVariablesT = dict[str, str]
+
+GenericAuthSetting = TypedDict(
+ "GenericAuthSetting",
+ {
+ "type": str,
+ "in": str,
+ "key": str,
+ "value": str,
+ },
+)
+
+
+OAuth2AuthSetting = TypedDict(
+ "OAuth2AuthSetting",
+ {
+ "type": Literal["oauth2"],
+ "in": Literal["header"],
+ "key": Literal["Authorization"],
+ "value": str,
+ },
+)
+
+
+APIKeyAuthSetting = TypedDict(
+ "APIKeyAuthSetting",
+ {
+ "type": Literal["api_key"],
+ "in": str,
+ "key": str,
+ "value": Optional[str],
+ },
+)
+
+
+BasicAuthSetting = TypedDict(
+ "BasicAuthSetting",
+ {
+ "type": Literal["basic"],
+ "in": Literal["header"],
+ "key": Literal["Authorization"],
+ "value": Optional[str],
+ },
+)
+
+
+BearerFormatAuthSetting = TypedDict(
+ "BearerFormatAuthSetting",
+ {
+ "type": Literal["bearer"],
+ "in": Literal["header"],
+ "format": Literal["JWT"],
+ "key": Literal["Authorization"],
+ "value": str,
+ },
+)
+
+
+BearerAuthSetting = TypedDict(
+ "BearerAuthSetting",
+ {
+ "type": Literal["bearer"],
+ "in": Literal["header"],
+ "key": Literal["Authorization"],
+ "value": str,
+ },
+)
+
+
+HTTPSignatureAuthSetting = TypedDict(
+ "HTTPSignatureAuthSetting",
+ {
+ "type": Literal["http-signature"],
+ "in": Literal["header"],
+ "key": Literal["Authorization"],
+ "value": None,
+ },
+)
+
+
+class AuthSettings(TypedDict, total=False):
+ pass
+
+
+class HostSettingVariable(TypedDict):
+ description: str
+ default_value: str
+ enum_values: list[str]
+
+
+class HostSetting(TypedDict):
+ url: str
+ description: str
+ variables: NotRequired[dict[str, HostSettingVariable]]
+
+
+class Configuration:
+ """This class contains various settings of the API client.
+
+ :param host: Base url.
+ :param ignore_operation_servers
+ Boolean to ignore operation servers for the API client.
+ Config will use `host` as the base url regardless of the operation servers.
+ :param api_key: Dict to store API key(s).
+ Each entry in the dict specifies an API key.
+ The dict key is the name of the security scheme in the OAS specification.
+ The dict value is the API key secret.
+ :param api_key_prefix: Dict to store API prefix (e.g. Bearer).
+ The dict key is the name of the security scheme in the OAS specification.
+ The dict value is an API key prefix when generating the auth data.
+ :param username: Username for HTTP basic authentication.
+ :param password: Password for HTTP basic authentication.
+ :param access_token: Access token.
+ :param server_index: Index to servers configuration.
+ :param server_variables: Mapping with string values to replace variables in
+ templated server configuration. The validation of enums is performed for
+ variables with defined enum values before.
+ :param server_operation_index: Mapping from operation ID to an index to server
+ configuration.
+ :param server_operation_variables: Mapping from operation ID to a mapping with
+ string values to replace variables in templated server configuration.
+ The validation of enums is performed for variables with defined enum
+ values before.
+ :param ssl_ca_cert: str - the path to a file of concatenated CA certificates
+ in PEM format.
+ :param retries: Number of retries for API requests.
+ :param ca_cert_data: verify the peer using concatenated CA certificate data
+ in PEM (str) or DER (bytes) format.
+
+ """
+
+ _default: ClassVar[Optional[Self]] = None
+
+ def __init__(
+ self,
+ host: Optional[str] = None,
+ api_key: Optional[dict[str, str]] = None,
+ api_key_prefix: Optional[dict[str, str]] = None,
+ username: Optional[str] = None,
+ password: Optional[str] = None,
+ access_token: Optional[str] = None,
+ server_index: Optional[int] = None,
+ server_variables: Optional[ServerVariablesT] = None,
+ server_operation_index: Optional[dict[int, int]] = None,
+ server_operation_variables: Optional[dict[int, ServerVariablesT]] = None,
+ ignore_operation_servers: bool = False,
+ ssl_ca_cert: Optional[str] = None,
+ retries: Optional[int] = None,
+ ca_cert_data: Optional[Union[str, bytes]] = None,
+ *,
+ debug: Optional[bool] = None,
+ ) -> None:
+ """Constructor"""
+ self._base_path = "http://localhost" if host is None else host
+ """Default Base url
+ """
+ self.server_index = 0 if server_index is None and host is None else server_index
+ self.server_operation_index = server_operation_index or {}
+ """Default server index
+ """
+ self.server_variables = server_variables or {}
+ self.server_operation_variables = server_operation_variables or {}
+ """Default server variables
+ """
+ self.ignore_operation_servers = ignore_operation_servers
+ """Ignore operation servers
+ """
+ self.temp_folder_path = None
+ """Temp file folder for downloading files
+ """
+ # Authentication Settings
+ self.api_key = {}
+ if api_key:
+ self.api_key = api_key
+ """dict to store API key(s)
+ """
+ self.api_key_prefix = {}
+ if api_key_prefix:
+ self.api_key_prefix = api_key_prefix
+ """dict to store API prefix (e.g. Bearer)
+ """
+ self.refresh_api_key_hook = None
+ """function hook to refresh API key if expired
+ """
+ self.username = username
+ """Username for HTTP basic authentication
+ """
+ self.password = password
+ """Password for HTTP basic authentication
+ """
+ self.access_token = access_token
+ """Access token
+ """
+ self.logger = {}
+ """Logging Settings
+ """
+ self.logger["package_logger"] = logging.getLogger("openapi_client")
+ self.logger["urllib3_logger"] = logging.getLogger("urllib3")
+ self.logger_format = "%(asctime)s %(levelname)s %(message)s"
+ """Log format
+ """
+ self.logger_stream_handler = None
+ """Log stream handler
+ """
+ self.logger_file_handler: Optional[FileHandler] = None
+ """Log file handler
+ """
+ self.logger_file = None
+ """Debug file location
+ """
+ if debug is not None:
+ self.debug = debug
+ else:
+ self.__debug = False
+ """Debug switch
+ """
+
+ self.verify_ssl = True
+ """SSL/TLS verification
+ Set this to false to skip verifying SSL certificate when calling API
+ from https server.
+ """
+ self.ssl_ca_cert = ssl_ca_cert
+ """Set this to customize the certificate file to verify the peer.
+ """
+ self.ca_cert_data = ca_cert_data
+ """Set this to verify the peer using PEM (str) or DER (bytes)
+ certificate data.
+ """
+ self.cert_file = None
+ """client certificate file
+ """
+ self.key_file = None
+ """client key file
+ """
+ self.assert_hostname = None
+ """Set this to True/False to enable/disable SSL hostname verification.
+ """
+ self.tls_server_name = None
+ """SSL/TLS Server Name Indication (SNI)
+ Set this to the SNI value expected by the server.
+ """
+
+ self.connection_pool_maxsize = multiprocessing.cpu_count() * 5
+ """urllib3 connection pool's maximum number of connections saved
+ per pool. urllib3 uses 1 connection as default value, but this is
+ not the best value when you are making a lot of possibly parallel
+ requests to the same host, which is often the case here.
+ cpu_count * 5 is used as default value to increase performance.
+ """
+
+ self.proxy: Optional[str] = None
+ """Proxy URL
+ """
+ self.proxy_headers = None
+ """Proxy headers
+ """
+ self.safe_chars_for_path_param = ""
+ """Safe chars for path_param
+ """
+ self.retries = retries
+ """Adding retries to override urllib3 default value 3
+ """
+ # Enable client side validation
+ self.client_side_validation = True
+
+ self.socket_options = None
+ """Options to pass down to the underlying urllib3 socket
+ """
+
+ self.datetime_format = "%Y-%m-%dT%H:%M:%S.%f%z"
+ """datetime format
+ """
+
+ self.date_format = "%Y-%m-%d"
+ """date format
+ """
+
+ def __deepcopy__(self, memo: dict[int, Any]) -> Self:
+ cls = self.__class__
+ result = cls.__new__(cls)
+ memo[id(self)] = result
+ for k, v in self.__dict__.items():
+ if k not in ("logger", "logger_file_handler"):
+ setattr(result, k, copy.deepcopy(v, memo))
+ # shallow copy of loggers
+ result.logger = copy.copy(self.logger)
+ # use setters to configure loggers
+ result.logger_file = self.logger_file
+ result.debug = self.debug
+ return result
+
+ def __setattr__(self, name: str, value: Any) -> None:
+ object.__setattr__(self, name, value)
+
+ @classmethod
+ def set_default(cls, default: Optional[Self]) -> None:
+ """Set default instance of configuration.
+
+ It stores default configuration, which can be
+ returned by get_default_copy method.
+
+ :param default: object of Configuration
+ """
+ cls._default = default
+
+ @classmethod
+ def get_default_copy(cls) -> Self:
+ """Deprecated. Please use `get_default` instead.
+
+ Deprecated. Please use `get_default` instead.
+
+ :return: The configuration object.
+ """
+ return cls.get_default()
+
+ @classmethod
+ def get_default(cls) -> Self:
+ """Return the default configuration.
+
+ This method returns newly created, based on default constructor,
+ object of Configuration class or returns a copy of default
+ configuration.
+
+ :return: The configuration object.
+ """
+ if cls._default is None:
+ cls._default = cls()
+ return cls._default
+
+ @property
+ def logger_file(self) -> Optional[str]:
+ """The logger file.
+
+ If the logger_file is None, then add stream handler and remove file
+ handler. Otherwise, add file handler and remove stream handler.
+
+ :param value: The logger_file path.
+ :type: str
+ """
+ return self.__logger_file
+
+ @logger_file.setter
+ def logger_file(self, value: Optional[str]) -> None:
+ """The logger file.
+
+ If the logger_file is None, then add stream handler and remove file
+ handler. Otherwise, add file handler and remove stream handler.
+
+ :param value: The logger_file path.
+ :type: str
+ """
+ self.__logger_file = value
+ if self.__logger_file:
+ # If set logging file,
+ # then add file handler and remove stream handler.
+ self.logger_file_handler = logging.FileHandler(self.__logger_file)
+ self.logger_file_handler.setFormatter(self.logger_formatter)
+ for _, logger in self.logger.items():
+ logger.addHandler(self.logger_file_handler)
+
+ @property
+ def debug(self) -> bool:
+ """Debug status
+
+ :param value: The debug status, True or False.
+ :type: bool
+ """
+ return self.__debug
+
+ @debug.setter
+ def debug(self, value: bool) -> None:
+ """Debug status
+
+ :param value: The debug status, True or False.
+ :type: bool
+ """
+ self.__debug = value
+ if self.__debug:
+ # if debug status is True, turn on debug logging
+ for _, logger in self.logger.items():
+ logger.setLevel(logging.DEBUG)
+ # turn on httplib debug
+ httplib.HTTPConnection.debuglevel = 1
+ else:
+ # if debug status is False, turn off debug logging,
+ # setting log level to default `logging.WARNING`
+ for _, logger in self.logger.items():
+ logger.setLevel(logging.WARNING)
+ # turn off httplib debug
+ httplib.HTTPConnection.debuglevel = 0
+
+ @property
+ def logger_format(self) -> str:
+ """The logger format.
+
+ The logger_formatter will be updated when sets logger_format.
+
+ :param value: The format string.
+ :type: str
+ """
+ return self.__logger_format
+
+ @logger_format.setter
+ def logger_format(self, value: str) -> None:
+ """The logger format.
+
+ The logger_formatter will be updated when sets logger_format.
+
+ :param value: The format string.
+ :type: str
+ """
+ self.__logger_format = value
+ self.logger_formatter = logging.Formatter(self.__logger_format)
+
+ def get_api_key_with_prefix(self, identifier: str, alias: Optional[str] = None) -> Optional[str]:
+ """Gets API key (with prefix if set).
+
+ :param identifier: The identifier of apiKey.
+ :param alias: The alternative identifier of apiKey.
+ :return: The token for api key authentication.
+ """
+ if self.refresh_api_key_hook is not None:
+ self.refresh_api_key_hook(self)
+ key = self.api_key.get(identifier, self.api_key.get(alias) if alias is not None else None)
+ if key:
+ prefix = self.api_key_prefix.get(identifier)
+ if prefix:
+ return f"{prefix} {key}"
+ else:
+ return key
+
+ return None
+
+ def get_basic_auth_token(self) -> Optional[str]:
+ """Gets HTTP basic authentication header (string).
+
+ :return: The token for basic HTTP authentication.
+ """
+ username = ""
+ if self.username is not None:
+ username = self.username
+ password = ""
+ if self.password is not None:
+ password = self.password
+ return urllib3.util.make_headers(basic_auth=username + ":" + password).get("authorization")
+
+ def auth_settings(self) -> AuthSettings:
+ """Gets Auth Settings dict for api client.
+
+ :return: The Auth Settings information dict.
+ """
+ auth: AuthSettings = {}
+ return auth
+
+ def to_debug_report(self) -> str:
+ """Gets the essential information for debugging.
+
+ :return: The report for debugging.
+ """
+ return f"Python SDK Debug Report:\nOS: {sys.platform}\nPython Version: {sys.version}\nVersion of the API: 1.0.0\nSDK Package Version: 1.0.0"
+
+ def get_host_settings(self) -> list[HostSetting]:
+ """Gets an array of host settings
+
+ :return: An array of host settings
+ """
+ return [
+ {
+ "url": "",
+ "description": "No description provided",
+ }
+ ]
+
+ def get_host_from_settings(
+ self,
+ index: Optional[int],
+ variables: Optional[ServerVariablesT] = None,
+ servers: Optional[list[HostSetting]] = None,
+ ) -> str:
+ """Gets host URL based on the index and variables
+ :param index: array index of the host settings
+ :param variables: hash of variable and the corresponding value
+ :param servers: an array of host settings or None
+ :return: URL based on host settings
+ """
+ if index is None:
+ return self._base_path
+
+ variables = {} if variables is None else variables
+ servers = self.get_host_settings() if servers is None else servers
+
+ try:
+ server = servers[index]
+ except IndexError:
+ msg = f"Invalid index {index} when selecting the host settings. Must be less than {len(servers)}"
+ raise ValueError(msg)
+
+ url = server["url"]
+
+ # go through variables and replace placeholders
+ for variable_name, variable in server.get("variables", {}).items():
+ used_value = variables.get(variable_name, variable["default_value"])
+
+ if "enum_values" in variable and used_value not in variable["enum_values"]:
+ msg = "The variable `{}` in the host URL has invalid value {}. Must be {}.".format(variable_name, variables[variable_name], variable["enum_values"])
+ raise ValueError(msg)
+
+ url = url.replace("{" + variable_name + "}", used_value)
+
+ return url
+
+ @property
+ def host(self) -> str:
+ """Return generated host."""
+ return self.get_host_from_settings(self.server_index, variables=self.server_variables)
+
+ @host.setter
+ def host(self, value: str) -> None:
+ """Fix base path."""
+ self._base_path = value
+ self.server_index = None
diff --git a/src/codegen/agents/client/openapi_client/exceptions.py b/src/codegen/agents/client/openapi_client/exceptions.py
new file mode 100644
index 000000000..46e8604fa
--- /dev/null
+++ b/src/codegen/agents/client/openapi_client/exceptions.py
@@ -0,0 +1,209 @@
+"""Developer API
+
+API for application developers
+
+The version of the OpenAPI document: 1.0.0
+Generated by OpenAPI Generator (https://openapi-generator.tech)
+
+Do not edit the class manually.
+"""
+
+from typing import Any, Optional
+
+from typing_extensions import Self
+
+
+class OpenApiException(Exception):
+ """The base exception class for all OpenAPIExceptions"""
+
+
+class ApiTypeError(OpenApiException, TypeError):
+ def __init__(self, msg, path_to_item=None, valid_classes=None, key_type=None) -> None:
+ """Raises an exception for TypeErrors
+
+ Args:
+ msg (str): the exception message
+
+ Keyword Args:
+ path_to_item (list): a list of keys an indices to get to the
+ current_item
+ None if unset
+ valid_classes (tuple): the primitive classes that current item
+ should be an instance of
+ None if unset
+ key_type (bool): False if our value is a value in a dict
+ True if it is a key in a dict
+ False if our item is an item in a list
+ None if unset
+ """
+ self.path_to_item = path_to_item
+ self.valid_classes = valid_classes
+ self.key_type = key_type
+ full_msg = msg
+ if path_to_item:
+ full_msg = f"{msg} at {render_path(path_to_item)}"
+ super().__init__(full_msg)
+
+
+class ApiValueError(OpenApiException, ValueError):
+ def __init__(self, msg, path_to_item=None) -> None:
+ """Args:
+ msg (str): the exception message
+
+ Keyword Args:
+ path_to_item (list) the path to the exception in the
+ received_data dict. None if unset
+ """
+ self.path_to_item = path_to_item
+ full_msg = msg
+ if path_to_item:
+ full_msg = f"{msg} at {render_path(path_to_item)}"
+ super().__init__(full_msg)
+
+
+class ApiAttributeError(OpenApiException, AttributeError):
+ def __init__(self, msg, path_to_item=None) -> None:
+ """Raised when an attribute reference or assignment fails.
+
+ Args:
+ msg (str): the exception message
+
+ Keyword Args:
+ path_to_item (None/list) the path to the exception in the
+ received_data dict
+ """
+ self.path_to_item = path_to_item
+ full_msg = msg
+ if path_to_item:
+ full_msg = f"{msg} at {render_path(path_to_item)}"
+ super().__init__(full_msg)
+
+
+class ApiKeyError(OpenApiException, KeyError):
+ def __init__(self, msg, path_to_item=None) -> None:
+ """Args:
+ msg (str): the exception message
+
+ Keyword Args:
+ path_to_item (None/list) the path to the exception in the
+ received_data dict
+ """
+ self.path_to_item = path_to_item
+ full_msg = msg
+ if path_to_item:
+ full_msg = f"{msg} at {render_path(path_to_item)}"
+ super().__init__(full_msg)
+
+
+class ApiException(OpenApiException):
+ def __init__(
+ self,
+ status=None,
+ reason=None,
+ http_resp=None,
+ *,
+ body: Optional[str] = None,
+ data: Optional[Any] = None,
+ ) -> None:
+ self.status = status
+ self.reason = reason
+ self.body = body
+ self.data = data
+ self.headers = None
+
+ if http_resp:
+ if self.status is None:
+ self.status = http_resp.status
+ if self.reason is None:
+ self.reason = http_resp.reason
+ if self.body is None:
+ try:
+ self.body = http_resp.data.decode("utf-8")
+ except Exception:
+ pass
+ self.headers = http_resp.getheaders()
+
+ @classmethod
+ def from_response(
+ cls,
+ *,
+ http_resp,
+ body: Optional[str],
+ data: Optional[Any],
+ ) -> Self:
+ if http_resp.status == 400:
+ raise BadRequestException(http_resp=http_resp, body=body, data=data)
+
+ if http_resp.status == 401:
+ raise UnauthorizedException(http_resp=http_resp, body=body, data=data)
+
+ if http_resp.status == 403:
+ raise ForbiddenException(http_resp=http_resp, body=body, data=data)
+
+ if http_resp.status == 404:
+ raise NotFoundException(http_resp=http_resp, body=body, data=data)
+
+ # Added new conditions for 409 and 422
+ if http_resp.status == 409:
+ raise ConflictException(http_resp=http_resp, body=body, data=data)
+
+ if http_resp.status == 422:
+ raise UnprocessableEntityException(http_resp=http_resp, body=body, data=data)
+
+ if 500 <= http_resp.status <= 599:
+ raise ServiceException(http_resp=http_resp, body=body, data=data)
+ raise ApiException(http_resp=http_resp, body=body, data=data)
+
+ def __str__(self):
+ """Custom error messages for exception"""
+ error_message = f"({self.status})\nReason: {self.reason}\n"
+ if self.headers:
+ error_message += f"HTTP response headers: {self.headers}\n"
+
+ if self.data or self.body:
+ error_message += f"HTTP response body: {self.data or self.body}\n"
+
+ return error_message
+
+
+class BadRequestException(ApiException):
+ pass
+
+
+class NotFoundException(ApiException):
+ pass
+
+
+class UnauthorizedException(ApiException):
+ pass
+
+
+class ForbiddenException(ApiException):
+ pass
+
+
+class ServiceException(ApiException):
+ pass
+
+
+class ConflictException(ApiException):
+ """Exception for HTTP 409 Conflict."""
+
+ pass
+
+
+class UnprocessableEntityException(ApiException):
+ """Exception for HTTP 422 Unprocessable Entity."""
+
+ pass
+
+
+def render_path(path_to_item):
+ """Returns a string representation of a path"""
+ result = ""
+ for pth in path_to_item:
+ if isinstance(pth, int):
+ result += f"[{pth}]"
+ else:
+ result += f"['{pth}']"
+ return result
diff --git a/src/codegen/agents/client/openapi_client/models/__init__.py b/src/codegen/agents/client/openapi_client/models/__init__.py
new file mode 100644
index 000000000..6f60d132d
--- /dev/null
+++ b/src/codegen/agents/client/openapi_client/models/__init__.py
@@ -0,0 +1,25 @@
+# coding: utf-8
+
+# flake8: noqa
+"""
+Developer API
+
+API for application developers
+
+The version of the OpenAPI document: 1.0.0
+Generated by OpenAPI Generator (https://openapi-generator.tech)
+
+Do not edit the class manually.
+""" # noqa: E501
+
+# import models into model package
+from codegen.agents.client.openapi_client.models.agent_run_response import AgentRunResponse
+from codegen.agents.client.openapi_client.models.create_agent_run_input import CreateAgentRunInput
+from codegen.agents.client.openapi_client.models.http_validation_error import HTTPValidationError
+from codegen.agents.client.openapi_client.models.organization_response import OrganizationResponse
+from codegen.agents.client.openapi_client.models.organization_settings import OrganizationSettings
+from codegen.agents.client.openapi_client.models.page_organization_response import PageOrganizationResponse
+from codegen.agents.client.openapi_client.models.page_user_response import PageUserResponse
+from codegen.agents.client.openapi_client.models.user_response import UserResponse
+from codegen.agents.client.openapi_client.models.validation_error import ValidationError
+from codegen.agents.client.openapi_client.models.validation_error_loc_inner import ValidationErrorLocInner
diff --git a/src/codegen/agents/client/openapi_client/models/agent_run_response.py b/src/codegen/agents/client/openapi_client/models/agent_run_response.py
new file mode 100644
index 000000000..10190e84f
--- /dev/null
+++ b/src/codegen/agents/client/openapi_client/models/agent_run_response.py
@@ -0,0 +1,106 @@
+"""Developer API
+
+API for application developers
+
+The version of the OpenAPI document: 1.0.0
+Generated by OpenAPI Generator (https://openapi-generator.tech)
+
+Do not edit the class manually.
+"""
+
+from __future__ import annotations
+
+import json
+import pprint
+import re # noqa: F401
+from typing import Any, ClassVar
+
+from pydantic import BaseModel, ConfigDict, StrictInt, StrictStr
+from typing_extensions import Self
+
+
+class AgentRunResponse(BaseModel):
+ """Represents an agent run in API responses"""
+
+ id: StrictInt
+ organization_id: StrictInt
+ status: StrictStr | None = None
+ created_at: StrictStr | None = None
+ result: StrictStr | None = None
+ web_url: StrictStr | None = None
+ __properties: ClassVar[list[str]] = ["id", "organization_id", "status", "created_at", "result", "web_url"]
+
+ model_config = ConfigDict(
+ populate_by_name=True,
+ validate_assignment=True,
+ protected_namespaces=(),
+ )
+
+ def to_str(self) -> str:
+ """Returns the string representation of the model using alias"""
+ return pprint.pformat(self.model_dump(by_alias=True))
+
+ def to_json(self) -> str:
+ """Returns the JSON representation of the model using alias"""
+ # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead
+ return json.dumps(self.to_dict())
+
+ @classmethod
+ def from_json(cls, json_str: str) -> Self | None:
+ """Create an instance of AgentRunResponse from a JSON string"""
+ return cls.from_dict(json.loads(json_str))
+
+ def to_dict(self) -> dict[str, Any]:
+ """Return the dictionary representation of the model using alias.
+
+ This has the following differences from calling pydantic's
+ `self.model_dump(by_alias=True)`:
+
+ * `None` is only added to the output dict for nullable fields that
+ were set at model initialization. Other fields with value `None`
+ are ignored.
+ """
+ excluded_fields: set[str] = set([])
+
+ _dict = self.model_dump(
+ by_alias=True,
+ exclude=excluded_fields,
+ exclude_none=True,
+ )
+ # set to None if status (nullable) is None
+ # and model_fields_set contains the field
+ if self.status is None and "status" in self.model_fields_set:
+ _dict["status"] = None
+
+ # set to None if created_at (nullable) is None
+ # and model_fields_set contains the field
+ if self.created_at is None and "created_at" in self.model_fields_set:
+ _dict["created_at"] = None
+
+ # set to None if result (nullable) is None
+ # and model_fields_set contains the field
+ if self.result is None and "result" in self.model_fields_set:
+ _dict["result"] = None
+
+ return _dict
+
+ @classmethod
+ def from_dict(cls, obj: dict[str, Any] | None) -> Self | None:
+ """Create an instance of AgentRunResponse from a dict"""
+ if obj is None:
+ return None
+
+ if not isinstance(obj, dict):
+ return cls.model_validate(obj)
+
+ _obj = cls.model_validate(
+ {
+ "id": obj.get("id"),
+ "organization_id": obj.get("organization_id"),
+ "status": obj.get("status"),
+ "created_at": obj.get("created_at"),
+ "result": obj.get("result"),
+ "web_url": obj.get("web_url"),
+ }
+ )
+ return _obj
diff --git a/src/codegen/agents/client/openapi_client/models/create_agent_run_input.py b/src/codegen/agents/client/openapi_client/models/create_agent_run_input.py
new file mode 100644
index 000000000..74469d48f
--- /dev/null
+++ b/src/codegen/agents/client/openapi_client/models/create_agent_run_input.py
@@ -0,0 +1,77 @@
+"""Developer API
+
+API for application developers
+
+The version of the OpenAPI document: 1.0.0
+Generated by OpenAPI Generator (https://openapi-generator.tech)
+
+Do not edit the class manually.
+"""
+
+from __future__ import annotations
+
+import json
+import pprint
+import re # noqa: F401
+from typing import Any, ClassVar
+
+from pydantic import BaseModel, ConfigDict, StrictStr
+from typing_extensions import Self
+
+
+class CreateAgentRunInput(BaseModel):
+ """CreateAgentRunInput"""
+
+ prompt: StrictStr
+ __properties: ClassVar[list[str]] = ["prompt"]
+
+ model_config = ConfigDict(
+ populate_by_name=True,
+ validate_assignment=True,
+ protected_namespaces=(),
+ )
+
+ def to_str(self) -> str:
+ """Returns the string representation of the model using alias"""
+ return pprint.pformat(self.model_dump(by_alias=True))
+
+ def to_json(self) -> str:
+ """Returns the JSON representation of the model using alias"""
+ # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead
+ return json.dumps(self.to_dict())
+
+ @classmethod
+ def from_json(cls, json_str: str) -> Self | None:
+ """Create an instance of CreateAgentRunInput from a JSON string"""
+ return cls.from_dict(json.loads(json_str))
+
+ def to_dict(self) -> dict[str, Any]:
+ """Return the dictionary representation of the model using alias.
+
+ This has the following differences from calling pydantic's
+ `self.model_dump(by_alias=True)`:
+
+ * `None` is only added to the output dict for nullable fields that
+ were set at model initialization. Other fields with value `None`
+ are ignored.
+ """
+ excluded_fields: set[str] = set([])
+
+ _dict = self.model_dump(
+ by_alias=True,
+ exclude=excluded_fields,
+ exclude_none=True,
+ )
+ return _dict
+
+ @classmethod
+ def from_dict(cls, obj: dict[str, Any] | None) -> Self | None:
+ """Create an instance of CreateAgentRunInput from a dict"""
+ if obj is None:
+ return None
+
+ if not isinstance(obj, dict):
+ return cls.model_validate(obj)
+
+ _obj = cls.model_validate({"prompt": obj.get("prompt")})
+ return _obj
diff --git a/src/codegen/agents/client/openapi_client/models/http_validation_error.py b/src/codegen/agents/client/openapi_client/models/http_validation_error.py
new file mode 100644
index 000000000..1f9125486
--- /dev/null
+++ b/src/codegen/agents/client/openapi_client/models/http_validation_error.py
@@ -0,0 +1,86 @@
+"""Developer API
+
+API for application developers
+
+The version of the OpenAPI document: 1.0.0
+Generated by OpenAPI Generator (https://openapi-generator.tech)
+
+Do not edit the class manually.
+"""
+
+from __future__ import annotations
+
+import json
+import pprint
+import re # noqa: F401
+from typing import Any, ClassVar
+
+from pydantic import BaseModel, ConfigDict
+from typing_extensions import Self
+
+from codegen.agents.client.openapi_client.models.validation_error import ValidationError
+
+
+class HTTPValidationError(BaseModel):
+ """HTTPValidationError"""
+
+ detail: list[ValidationError] | None = None
+ __properties: ClassVar[list[str]] = ["detail"]
+
+ model_config = ConfigDict(
+ populate_by_name=True,
+ validate_assignment=True,
+ protected_namespaces=(),
+ )
+
+ def to_str(self) -> str:
+ """Returns the string representation of the model using alias"""
+ return pprint.pformat(self.model_dump(by_alias=True))
+
+ def to_json(self) -> str:
+ """Returns the JSON representation of the model using alias"""
+ # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead
+ return json.dumps(self.to_dict())
+
+ @classmethod
+ def from_json(cls, json_str: str) -> Self | None:
+ """Create an instance of HTTPValidationError from a JSON string"""
+ return cls.from_dict(json.loads(json_str))
+
+ def to_dict(self) -> dict[str, Any]:
+ """Return the dictionary representation of the model using alias.
+
+ This has the following differences from calling pydantic's
+ `self.model_dump(by_alias=True)`:
+
+ * `None` is only added to the output dict for nullable fields that
+ were set at model initialization. Other fields with value `None`
+ are ignored.
+ """
+ excluded_fields: set[str] = set([])
+
+ _dict = self.model_dump(
+ by_alias=True,
+ exclude=excluded_fields,
+ exclude_none=True,
+ )
+ # override the default output from pydantic by calling `to_dict()` of each item in detail (list)
+ _items = []
+ if self.detail:
+ for _item_detail in self.detail:
+ if _item_detail:
+ _items.append(_item_detail.to_dict())
+ _dict["detail"] = _items
+ return _dict
+
+ @classmethod
+ def from_dict(cls, obj: dict[str, Any] | None) -> Self | None:
+ """Create an instance of HTTPValidationError from a dict"""
+ if obj is None:
+ return None
+
+ if not isinstance(obj, dict):
+ return cls.model_validate(obj)
+
+ _obj = cls.model_validate({"detail": [ValidationError.from_dict(_item) for _item in obj["detail"]] if obj.get("detail") is not None else None})
+ return _obj
diff --git a/src/codegen/agents/client/openapi_client/models/organization_response.py b/src/codegen/agents/client/openapi_client/models/organization_response.py
new file mode 100644
index 000000000..5912da5f1
--- /dev/null
+++ b/src/codegen/agents/client/openapi_client/models/organization_response.py
@@ -0,0 +1,84 @@
+"""Developer API
+
+API for application developers
+
+The version of the OpenAPI document: 1.0.0
+Generated by OpenAPI Generator (https://openapi-generator.tech)
+
+Do not edit the class manually.
+"""
+
+from __future__ import annotations
+
+import json
+import pprint
+import re # noqa: F401
+from typing import Any, ClassVar
+
+from pydantic import BaseModel, ConfigDict, StrictInt, StrictStr
+from typing_extensions import Self
+
+from codegen.agents.client.openapi_client.models.organization_settings import OrganizationSettings
+
+
+class OrganizationResponse(BaseModel):
+ """Represents an organization in API responses"""
+
+ id: StrictInt
+ name: StrictStr
+ settings: OrganizationSettings
+ __properties: ClassVar[list[str]] = ["id", "name", "settings"]
+
+ model_config = ConfigDict(
+ populate_by_name=True,
+ validate_assignment=True,
+ protected_namespaces=(),
+ )
+
+ def to_str(self) -> str:
+ """Returns the string representation of the model using alias"""
+ return pprint.pformat(self.model_dump(by_alias=True))
+
+ def to_json(self) -> str:
+ """Returns the JSON representation of the model using alias"""
+ # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead
+ return json.dumps(self.to_dict())
+
+ @classmethod
+ def from_json(cls, json_str: str) -> Self | None:
+ """Create an instance of OrganizationResponse from a JSON string"""
+ return cls.from_dict(json.loads(json_str))
+
+ def to_dict(self) -> dict[str, Any]:
+ """Return the dictionary representation of the model using alias.
+
+ This has the following differences from calling pydantic's
+ `self.model_dump(by_alias=True)`:
+
+ * `None` is only added to the output dict for nullable fields that
+ were set at model initialization. Other fields with value `None`
+ are ignored.
+ """
+ excluded_fields: set[str] = set([])
+
+ _dict = self.model_dump(
+ by_alias=True,
+ exclude=excluded_fields,
+ exclude_none=True,
+ )
+ # override the default output from pydantic by calling `to_dict()` of settings
+ if self.settings:
+ _dict["settings"] = self.settings.to_dict()
+ return _dict
+
+ @classmethod
+ def from_dict(cls, obj: dict[str, Any] | None) -> Self | None:
+ """Create an instance of OrganizationResponse from a dict"""
+ if obj is None:
+ return None
+
+ if not isinstance(obj, dict):
+ return cls.model_validate(obj)
+
+ _obj = cls.model_validate({"id": obj.get("id"), "name": obj.get("name"), "settings": OrganizationSettings.from_dict(obj["settings"]) if obj.get("settings") is not None else None})
+ return _obj
diff --git a/src/codegen/agents/client/openapi_client/models/organization_settings.py b/src/codegen/agents/client/openapi_client/models/organization_settings.py
new file mode 100644
index 000000000..d7538126d
--- /dev/null
+++ b/src/codegen/agents/client/openapi_client/models/organization_settings.py
@@ -0,0 +1,83 @@
+"""Developer API
+
+API for application developers
+
+The version of the OpenAPI document: 1.0.0
+Generated by OpenAPI Generator (https://openapi-generator.tech)
+
+Do not edit the class manually.
+"""
+
+from __future__ import annotations
+
+import json
+import pprint
+import re # noqa: F401
+from typing import Any, ClassVar
+
+from pydantic import BaseModel, ConfigDict, StrictBool
+from typing_extensions import Self
+
+
+class OrganizationSettings(BaseModel):
+ """OrganizationSettings"""
+
+ enable_pr_creation: StrictBool | None = True
+ enable_rules_detection: StrictBool | None = True
+ __properties: ClassVar[list[str]] = ["enable_pr_creation", "enable_rules_detection"]
+
+ model_config = ConfigDict(
+ populate_by_name=True,
+ validate_assignment=True,
+ protected_namespaces=(),
+ )
+
+ def to_str(self) -> str:
+ """Returns the string representation of the model using alias"""
+ return pprint.pformat(self.model_dump(by_alias=True))
+
+ def to_json(self) -> str:
+ """Returns the JSON representation of the model using alias"""
+ # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead
+ return json.dumps(self.to_dict())
+
+ @classmethod
+ def from_json(cls, json_str: str) -> Self | None:
+ """Create an instance of OrganizationSettings from a JSON string"""
+ return cls.from_dict(json.loads(json_str))
+
+ def to_dict(self) -> dict[str, Any]:
+ """Return the dictionary representation of the model using alias.
+
+ This has the following differences from calling pydantic's
+ `self.model_dump(by_alias=True)`:
+
+ * `None` is only added to the output dict for nullable fields that
+ were set at model initialization. Other fields with value `None`
+ are ignored.
+ """
+ excluded_fields: set[str] = set([])
+
+ _dict = self.model_dump(
+ by_alias=True,
+ exclude=excluded_fields,
+ exclude_none=True,
+ )
+ return _dict
+
+ @classmethod
+ def from_dict(cls, obj: dict[str, Any] | None) -> Self | None:
+ """Create an instance of OrganizationSettings from a dict"""
+ if obj is None:
+ return None
+
+ if not isinstance(obj, dict):
+ return cls.model_validate(obj)
+
+ _obj = cls.model_validate(
+ {
+ "enable_pr_creation": obj.get("enable_pr_creation") if obj.get("enable_pr_creation") is not None else True,
+ "enable_rules_detection": obj.get("enable_rules_detection") if obj.get("enable_rules_detection") is not None else True,
+ }
+ )
+ return _obj
diff --git a/src/codegen/agents/client/openapi_client/models/page_organization_response.py b/src/codegen/agents/client/openapi_client/models/page_organization_response.py
new file mode 100644
index 000000000..1390ac802
--- /dev/null
+++ b/src/codegen/agents/client/openapi_client/models/page_organization_response.py
@@ -0,0 +1,98 @@
+"""Developer API
+
+API for application developers
+
+The version of the OpenAPI document: 1.0.0
+Generated by OpenAPI Generator (https://openapi-generator.tech)
+
+Do not edit the class manually.
+"""
+
+from __future__ import annotations
+
+import json
+import pprint
+import re # noqa: F401
+from typing import Any, ClassVar
+
+from pydantic import BaseModel, ConfigDict, StrictInt
+from typing_extensions import Self
+
+from codegen.agents.client.openapi_client.models.organization_response import OrganizationResponse
+
+
+class PageOrganizationResponse(BaseModel):
+ """PageOrganizationResponse"""
+
+ items: list[OrganizationResponse]
+ total: StrictInt
+ page: StrictInt
+ size: StrictInt
+ pages: StrictInt
+ __properties: ClassVar[list[str]] = ["items", "total", "page", "size", "pages"]
+
+ model_config = ConfigDict(
+ populate_by_name=True,
+ validate_assignment=True,
+ protected_namespaces=(),
+ )
+
+ def to_str(self) -> str:
+ """Returns the string representation of the model using alias"""
+ return pprint.pformat(self.model_dump(by_alias=True))
+
+ def to_json(self) -> str:
+ """Returns the JSON representation of the model using alias"""
+ # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead
+ return json.dumps(self.to_dict())
+
+ @classmethod
+ def from_json(cls, json_str: str) -> Self | None:
+ """Create an instance of PageOrganizationResponse from a JSON string"""
+ return cls.from_dict(json.loads(json_str))
+
+ def to_dict(self) -> dict[str, Any]:
+ """Return the dictionary representation of the model using alias.
+
+ This has the following differences from calling pydantic's
+ `self.model_dump(by_alias=True)`:
+
+ * `None` is only added to the output dict for nullable fields that
+ were set at model initialization. Other fields with value `None`
+ are ignored.
+ """
+ excluded_fields: set[str] = set([])
+
+ _dict = self.model_dump(
+ by_alias=True,
+ exclude=excluded_fields,
+ exclude_none=True,
+ )
+ # override the default output from pydantic by calling `to_dict()` of each item in items (list)
+ _items = []
+ if self.items:
+ for _item_items in self.items:
+ if _item_items:
+ _items.append(_item_items.to_dict())
+ _dict["items"] = _items
+ return _dict
+
+ @classmethod
+ def from_dict(cls, obj: dict[str, Any] | None) -> Self | None:
+ """Create an instance of PageOrganizationResponse from a dict"""
+ if obj is None:
+ return None
+
+ if not isinstance(obj, dict):
+ return cls.model_validate(obj)
+
+ _obj = cls.model_validate(
+ {
+ "items": [OrganizationResponse.from_dict(_item) for _item in obj["items"]] if obj.get("items") is not None else None,
+ "total": obj.get("total"),
+ "page": obj.get("page"),
+ "size": obj.get("size"),
+ "pages": obj.get("pages"),
+ }
+ )
+ return _obj
diff --git a/src/codegen/agents/client/openapi_client/models/page_user_response.py b/src/codegen/agents/client/openapi_client/models/page_user_response.py
new file mode 100644
index 000000000..b79c8b7e3
--- /dev/null
+++ b/src/codegen/agents/client/openapi_client/models/page_user_response.py
@@ -0,0 +1,98 @@
+"""Developer API
+
+API for application developers
+
+The version of the OpenAPI document: 1.0.0
+Generated by OpenAPI Generator (https://openapi-generator.tech)
+
+Do not edit the class manually.
+"""
+
+from __future__ import annotations
+
+import json
+import pprint
+import re # noqa: F401
+from typing import Any, ClassVar
+
+from pydantic import BaseModel, ConfigDict, StrictInt
+from typing_extensions import Self
+
+from codegen.agents.client.openapi_client.models.user_response import UserResponse
+
+
+class PageUserResponse(BaseModel):
+ """PageUserResponse"""
+
+ items: list[UserResponse]
+ total: StrictInt
+ page: StrictInt
+ size: StrictInt
+ pages: StrictInt
+ __properties: ClassVar[list[str]] = ["items", "total", "page", "size", "pages"]
+
+ model_config = ConfigDict(
+ populate_by_name=True,
+ validate_assignment=True,
+ protected_namespaces=(),
+ )
+
+ def to_str(self) -> str:
+ """Returns the string representation of the model using alias"""
+ return pprint.pformat(self.model_dump(by_alias=True))
+
+ def to_json(self) -> str:
+ """Returns the JSON representation of the model using alias"""
+ # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead
+ return json.dumps(self.to_dict())
+
+ @classmethod
+ def from_json(cls, json_str: str) -> Self | None:
+ """Create an instance of PageUserResponse from a JSON string"""
+ return cls.from_dict(json.loads(json_str))
+
+ def to_dict(self) -> dict[str, Any]:
+ """Return the dictionary representation of the model using alias.
+
+ This has the following differences from calling pydantic's
+ `self.model_dump(by_alias=True)`:
+
+ * `None` is only added to the output dict for nullable fields that
+ were set at model initialization. Other fields with value `None`
+ are ignored.
+ """
+ excluded_fields: set[str] = set([])
+
+ _dict = self.model_dump(
+ by_alias=True,
+ exclude=excluded_fields,
+ exclude_none=True,
+ )
+ # override the default output from pydantic by calling `to_dict()` of each item in items (list)
+ _items = []
+ if self.items:
+ for _item_items in self.items:
+ if _item_items:
+ _items.append(_item_items.to_dict())
+ _dict["items"] = _items
+ return _dict
+
+ @classmethod
+ def from_dict(cls, obj: dict[str, Any] | None) -> Self | None:
+ """Create an instance of PageUserResponse from a dict"""
+ if obj is None:
+ return None
+
+ if not isinstance(obj, dict):
+ return cls.model_validate(obj)
+
+ _obj = cls.model_validate(
+ {
+ "items": [UserResponse.from_dict(_item) for _item in obj["items"]] if obj.get("items") is not None else None,
+ "total": obj.get("total"),
+ "page": obj.get("page"),
+ "size": obj.get("size"),
+ "pages": obj.get("pages"),
+ }
+ )
+ return _obj
diff --git a/src/codegen/agents/client/openapi_client/models/user_response.py b/src/codegen/agents/client/openapi_client/models/user_response.py
new file mode 100644
index 000000000..71aa4cdae
--- /dev/null
+++ b/src/codegen/agents/client/openapi_client/models/user_response.py
@@ -0,0 +1,106 @@
+"""Developer API
+
+API for application developers
+
+The version of the OpenAPI document: 1.0.0
+Generated by OpenAPI Generator (https://openapi-generator.tech)
+
+Do not edit the class manually.
+"""
+
+from __future__ import annotations
+
+import json
+import pprint
+import re # noqa: F401
+from typing import Any, ClassVar
+
+from pydantic import BaseModel, ConfigDict, StrictInt, StrictStr
+from typing_extensions import Self
+
+
+class UserResponse(BaseModel):
+ """Represents a user in API responses"""
+
+ id: StrictInt
+ email: StrictStr | None
+ github_user_id: StrictStr
+ github_username: StrictStr
+ avatar_url: StrictStr | None
+ full_name: StrictStr | None
+ __properties: ClassVar[list[str]] = ["id", "email", "github_user_id", "github_username", "avatar_url", "full_name"]
+
+ model_config = ConfigDict(
+ populate_by_name=True,
+ validate_assignment=True,
+ protected_namespaces=(),
+ )
+
+ def to_str(self) -> str:
+ """Returns the string representation of the model using alias"""
+ return pprint.pformat(self.model_dump(by_alias=True))
+
+ def to_json(self) -> str:
+ """Returns the JSON representation of the model using alias"""
+ # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead
+ return json.dumps(self.to_dict())
+
+ @classmethod
+ def from_json(cls, json_str: str) -> Self | None:
+ """Create an instance of UserResponse from a JSON string"""
+ return cls.from_dict(json.loads(json_str))
+
+ def to_dict(self) -> dict[str, Any]:
+ """Return the dictionary representation of the model using alias.
+
+ This has the following differences from calling pydantic's
+ `self.model_dump(by_alias=True)`:
+
+ * `None` is only added to the output dict for nullable fields that
+ were set at model initialization. Other fields with value `None`
+ are ignored.
+ """
+ excluded_fields: set[str] = set([])
+
+ _dict = self.model_dump(
+ by_alias=True,
+ exclude=excluded_fields,
+ exclude_none=True,
+ )
+ # set to None if email (nullable) is None
+ # and model_fields_set contains the field
+ if self.email is None and "email" in self.model_fields_set:
+ _dict["email"] = None
+
+ # set to None if avatar_url (nullable) is None
+ # and model_fields_set contains the field
+ if self.avatar_url is None and "avatar_url" in self.model_fields_set:
+ _dict["avatar_url"] = None
+
+ # set to None if full_name (nullable) is None
+ # and model_fields_set contains the field
+ if self.full_name is None and "full_name" in self.model_fields_set:
+ _dict["full_name"] = None
+
+ return _dict
+
+ @classmethod
+ def from_dict(cls, obj: dict[str, Any] | None) -> Self | None:
+ """Create an instance of UserResponse from a dict"""
+ if obj is None:
+ return None
+
+ if not isinstance(obj, dict):
+ return cls.model_validate(obj)
+
+ _obj = cls.model_validate(
+ {
+ "id": obj.get("id"),
+ "email": obj.get("email"),
+ "github_user_id": obj.get("github_user_id"),
+ "github_username": obj.get("github_username"),
+ "avatar_url": obj.get("avatar_url"),
+ "full_name": obj.get("full_name"),
+ }
+ )
+ return _obj
diff --git a/src/codegen/agents/client/openapi_client/models/validation_error.py b/src/codegen/agents/client/openapi_client/models/validation_error.py
new file mode 100644
index 000000000..fab1db703
--- /dev/null
+++ b/src/codegen/agents/client/openapi_client/models/validation_error.py
@@ -0,0 +1,88 @@
+"""Developer API
+
+API for application developers
+
+The version of the OpenAPI document: 1.0.0
+Generated by OpenAPI Generator (https://openapi-generator.tech)
+
+Do not edit the class manually.
+"""
+
+from __future__ import annotations
+
+import json
+import pprint
+import re # noqa: F401
+from typing import Any, ClassVar
+
+from pydantic import BaseModel, ConfigDict, StrictStr
+from typing_extensions import Self
+
+from codegen.agents.client.openapi_client.models.validation_error_loc_inner import ValidationErrorLocInner
+
+
+class ValidationError(BaseModel):
+ """ValidationError"""
+
+ loc: list[ValidationErrorLocInner]
+ msg: StrictStr
+ type: StrictStr
+ __properties: ClassVar[list[str]] = ["loc", "msg", "type"]
+
+ model_config = ConfigDict(
+ populate_by_name=True,
+ validate_assignment=True,
+ protected_namespaces=(),
+ )
+
+ def to_str(self) -> str:
+ """Returns the string representation of the model using alias"""
+ return pprint.pformat(self.model_dump(by_alias=True))
+
+ def to_json(self) -> str:
+ """Returns the JSON representation of the model using alias"""
+ # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead
+ return json.dumps(self.to_dict())
+
+ @classmethod
+ def from_json(cls, json_str: str) -> Self | None:
+ """Create an instance of ValidationError from a JSON string"""
+ return cls.from_dict(json.loads(json_str))
+
+ def to_dict(self) -> dict[str, Any]:
+ """Return the dictionary representation of the model using alias.
+
+ This has the following differences from calling pydantic's
+ `self.model_dump(by_alias=True)`:
+
+ * `None` is only added to the output dict for nullable fields that
+ were set at model initialization. Other fields with value `None`
+ are ignored.
+ """
+ excluded_fields: set[str] = set([])
+
+ _dict = self.model_dump(
+ by_alias=True,
+ exclude=excluded_fields,
+ exclude_none=True,
+ )
+ # override the default output from pydantic by calling `to_dict()` of each item in loc (list)
+ _items = []
+ if self.loc:
+ for _item_loc in self.loc:
+ if _item_loc:
+ _items.append(_item_loc.to_dict())
+ _dict["loc"] = _items
+ return _dict
+
+ @classmethod
+ def from_dict(cls, obj: dict[str, Any] | None) -> Self | None:
+ """Create an instance of ValidationError from a dict"""
+ if obj is None:
+ return None
+
+ if not isinstance(obj, dict):
+ return cls.model_validate(obj)
+
+ _obj = cls.model_validate({"loc": [ValidationErrorLocInner.from_dict(_item) for _item in obj["loc"]] if obj.get("loc") is not None else None, "msg": obj.get("msg"), "type": obj.get("type")})
+ return _obj
diff --git a/src/codegen/agents/client/openapi_client/models/validation_error_loc_inner.py b/src/codegen/agents/client/openapi_client/models/validation_error_loc_inner.py
new file mode 100644
index 000000000..04a44443e
--- /dev/null
+++ b/src/codegen/agents/client/openapi_client/models/validation_error_loc_inner.py
@@ -0,0 +1,132 @@
+"""Developer API
+
+API for application developers
+
+The version of the OpenAPI document: 1.0.0
+Generated by OpenAPI Generator (https://openapi-generator.tech)
+
+Do not edit the class manually.
+"""
+
+from __future__ import annotations
+
+import json
+import pprint
+import re # noqa: F401
+from typing import TYPE_CHECKING, Any
+
+from pydantic import BaseModel, StrictInt, StrictStr, ValidationError, field_validator
+from typing_extensions import Self
+
+VALIDATIONERRORLOCINNER_ANY_OF_SCHEMAS = ["int", "str"]
+
+
+class ValidationErrorLocInner(BaseModel):
+ """ValidationErrorLocInner"""
+
+ # data type: str
+ anyof_schema_1_validator: StrictStr | None = None
+ # data type: int
+ anyof_schema_2_validator: StrictInt | None = None
+ if TYPE_CHECKING:
+ actual_instance: int | str | None = None
+ else:
+ actual_instance: Any = None
+ any_of_schemas: set[str] = {"int", "str"}
+
+ model_config = {
+ "validate_assignment": True,
+ "protected_namespaces": (),
+ }
+
+ def __init__(self, *args, **kwargs) -> None:
+ if args:
+ if len(args) > 1:
+ msg = "If a position argument is used, only 1 is allowed to set `actual_instance`"
+ raise ValueError(msg)
+ if kwargs:
+ msg = "If a position argument is used, keyword arguments cannot be used."
+ raise ValueError(msg)
+ super().__init__(actual_instance=args[0])
+ else:
+ super().__init__(**kwargs)
+
+ @field_validator("actual_instance")
+ def actual_instance_must_validate_anyof(cls, v):
+ instance = ValidationErrorLocInner.model_construct()
+ error_messages = []
+ # validate data type: str
+ try:
+ instance.anyof_schema_1_validator = v
+ return v
+ except (ValidationError, ValueError) as e:
+ error_messages.append(str(e))
+ # validate data type: int
+ try:
+ instance.anyof_schema_2_validator = v
+ return v
+ except (ValidationError, ValueError) as e:
+ error_messages.append(str(e))
+ if error_messages:
+ # no match
+ raise ValueError("No match found when setting the actual_instance in ValidationErrorLocInner with anyOf schemas: int, str. Details: " + ", ".join(error_messages))
+ else:
+ return v
+
+ @classmethod
+ def from_dict(cls, obj: dict[str, Any]) -> Self:
+ return cls.from_json(json.dumps(obj))
+
+ @classmethod
+ def from_json(cls, json_str: str) -> Self:
+ """Returns the object represented by the json string"""
+ instance = cls.model_construct()
+ error_messages = []
+ # deserialize data into str
+ try:
+ # validation
+ instance.anyof_schema_1_validator = json.loads(json_str)
+ # assign value to actual_instance
+ instance.actual_instance = instance.anyof_schema_1_validator
+ return instance
+ except (ValidationError, ValueError) as e:
+ error_messages.append(str(e))
+ # deserialize data into int
+ try:
+ # validation
+ instance.anyof_schema_2_validator = json.loads(json_str)
+ # assign value to actual_instance
+ instance.actual_instance = instance.anyof_schema_2_validator
+ return instance
+ except (ValidationError, ValueError) as e:
+ error_messages.append(str(e))
+
+ if error_messages:
+ # no match
+ raise ValueError("No match found when deserializing the JSON string into ValidationErrorLocInner with anyOf schemas: int, str. Details: " + ", ".join(error_messages))
+ else:
+ return instance
+
+ def to_json(self) -> str:
+ """Returns the JSON representation of the actual instance"""
+ if self.actual_instance is None:
+ return "null"
+
+ if hasattr(self.actual_instance, "to_json") and callable(self.actual_instance.to_json):
+ return self.actual_instance.to_json()
+ else:
+ return json.dumps(self.actual_instance)
+
+ def to_dict(self) -> dict[str, Any] | int | str | None:
+ """Returns the dict representation of the actual instance"""
+ if self.actual_instance is None:
+ return None
+
+ if hasattr(self.actual_instance, "to_dict") and callable(self.actual_instance.to_dict):
+ return self.actual_instance.to_dict()
+ else:
+ return self.actual_instance
+
+ def to_str(self) -> str:
+ """Returns the string representation of the actual instance"""
+ return pprint.pformat(self.model_dump())
diff --git a/src/codegen/agents/client/openapi_client/rest.py b/src/codegen/agents/client/openapi_client/rest.py
new file mode 100644
index 000000000..445c0144c
--- /dev/null
+++ b/src/codegen/agents/client/openapi_client/rest.py
@@ -0,0 +1,180 @@
+"""Developer API
+
+API for application developers
+
+The version of the OpenAPI document: 1.0.0
+Generated by OpenAPI Generator (https://openapi-generator.tech)
+
+Do not edit the class manually.
+"""
+
+import io
+import json
+import re
+import ssl
+
+import urllib3
+
+from codegen.agents.client.openapi_client.exceptions import ApiException, ApiValueError
+
+SUPPORTED_SOCKS_PROXIES = {"socks5", "socks5h", "socks4", "socks4a"}
+RESTResponseType = urllib3.HTTPResponse
+
+
+def is_socks_proxy_url(url):
+ if url is None:
+ return False
+ split_section = url.split("://")
+ if len(split_section) < 2:
+ return False
+ else:
+ return split_section[0].lower() in SUPPORTED_SOCKS_PROXIES
+
+
+class RESTResponse(io.IOBase):
+ def __init__(self, resp) -> None:
+ self.response = resp
+ self.status = resp.status
+ self.reason = resp.reason
+ self.data = None
+
+ def read(self):
+ if self.data is None:
+ self.data = self.response.data
+ return self.data
+
+ def getheaders(self):
+ """Returns a dictionary of the response headers."""
+ return self.response.headers
+
+ def getheader(self, name, default=None):
+ """Returns a given response header."""
+ return self.response.headers.get(name, default)
+
+
+class RESTClientObject:
+ def __init__(self, configuration) -> None:
+ # urllib3.PoolManager will pass all kw parameters to connectionpool
+ # https://github.com/shazow/urllib3/blob/f9409436f83aeb79fbaf090181cd81b784f1b8ce/urllib3/poolmanager.py#L75
+ # https://github.com/shazow/urllib3/blob/f9409436f83aeb79fbaf090181cd81b784f1b8ce/urllib3/connectionpool.py#L680
+ # Custom SSL certificates and client certificates: http://urllib3.readthedocs.io/en/latest/advanced-usage.html
+
+ # cert_reqs
+ if configuration.verify_ssl:
+ cert_reqs = ssl.CERT_REQUIRED
+ else:
+ cert_reqs = ssl.CERT_NONE
+
+ pool_args = {
+ "cert_reqs": cert_reqs,
+ "ca_certs": configuration.ssl_ca_cert,
+ "cert_file": configuration.cert_file,
+ "key_file": configuration.key_file,
+ "ca_cert_data": configuration.ca_cert_data,
+ }
+ if configuration.assert_hostname is not None:
+ pool_args["assert_hostname"] = configuration.assert_hostname
+
+ if configuration.retries is not None:
+ pool_args["retries"] = configuration.retries
+
+ if configuration.tls_server_name:
+ pool_args["server_hostname"] = configuration.tls_server_name
+
+ if configuration.socket_options is not None:
+ pool_args["socket_options"] = configuration.socket_options
+
+ if configuration.connection_pool_maxsize is not None:
+ pool_args["maxsize"] = configuration.connection_pool_maxsize
+
+ # https pool manager
+ self.pool_manager: urllib3.PoolManager
+
+ if configuration.proxy:
+ if is_socks_proxy_url(configuration.proxy):
+ from urllib3.contrib.socks import SOCKSProxyManager
+
+ pool_args["proxy_url"] = configuration.proxy
+ pool_args["headers"] = configuration.proxy_headers
+ self.pool_manager = SOCKSProxyManager(**pool_args)
+ else:
+ pool_args["proxy_url"] = configuration.proxy
+ pool_args["proxy_headers"] = configuration.proxy_headers
+ self.pool_manager = urllib3.ProxyManager(**pool_args)
+ else:
+ self.pool_manager = urllib3.PoolManager(**pool_args)
+
+ def request(self, method, url, headers=None, body=None, post_params=None, _request_timeout=None):
+ """Perform requests.
+
+ :param method: http request method
+ :param url: http request url
+ :param headers: http request headers
+ :param body: request json body, for `application/json`
+ :param post_params: request post parameters,
+ `application/x-www-form-urlencoded`
+ and `multipart/form-data`
+ :param _request_timeout: timeout setting for this request. If one
+ number provided, it will be total request
+ timeout. It can also be a pair (tuple) of
+ (connection, read) timeouts.
+ """
+ method = method.upper()
+ assert method in ["GET", "HEAD", "DELETE", "POST", "PUT", "PATCH", "OPTIONS"]
+
+ if post_params and body:
+ msg = "body parameter cannot be used with post_params parameter."
+ raise ApiValueError(msg)
+
+ post_params = post_params or {}
+ headers = headers or {}
+
+ timeout = None
+ if _request_timeout:
+ if isinstance(_request_timeout, (int, float)):
+ timeout = urllib3.Timeout(total=_request_timeout)
+ elif isinstance(_request_timeout, tuple) and len(_request_timeout) == 2:
+ timeout = urllib3.Timeout(connect=_request_timeout[0], read=_request_timeout[1])
+
+ try:
+ # For `POST`, `PUT`, `PATCH`, `OPTIONS`, `DELETE`
+ if method in ["POST", "PUT", "PATCH", "OPTIONS", "DELETE"]:
+ # no content type provided or payload is json
+ content_type = headers.get("Content-Type")
+ if not content_type or re.search("json", content_type, re.IGNORECASE):
+ request_body = None
+ if body is not None:
+ request_body = json.dumps(body)
+ r = self.pool_manager.request(method, url, body=request_body, timeout=timeout, headers=headers, preload_content=False)
+ elif content_type == "application/x-www-form-urlencoded":
+ r = self.pool_manager.request(method, url, fields=post_params, encode_multipart=False, timeout=timeout, headers=headers, preload_content=False)
+ elif content_type == "multipart/form-data":
+ # must del headers['Content-Type'], or the correct
+ # Content-Type which generated by urllib3 will be
+ # overwritten.
+ del headers["Content-Type"]
+ # Ensures that dict objects are serialized
+ post_params = [(a, json.dumps(b)) if isinstance(b, dict) else (a, b) for a, b in post_params]
+ r = self.pool_manager.request(method, url, fields=post_params, encode_multipart=True, timeout=timeout, headers=headers, preload_content=False)
+ # Pass a `string` parameter directly in the body to support
+ # other content types than JSON when `body` argument is
+ # provided in serialized form.
+ elif isinstance(body, str) or isinstance(body, bytes):
+ r = self.pool_manager.request(method, url, body=body, timeout=timeout, headers=headers, preload_content=False)
+ elif headers["Content-Type"].startswith("text/") and isinstance(body, bool):
+ request_body = "true" if body else "false"
+ r = self.pool_manager.request(method, url, body=request_body, preload_content=False, timeout=timeout, headers=headers)
+ else:
+ # Cannot generate the request from given parameters
+ msg = """Cannot prepare a request message for provided
+ arguments. Please check that your arguments match
+ declared content type."""
+ raise ApiException(status=0, reason=msg)
+ # For `GET`, `HEAD`
+ else:
+ r = self.pool_manager.request(method, url, fields={}, timeout=timeout, headers=headers, preload_content=False)
+ except urllib3.exceptions.SSLError as e:
+ msg = "\n".join([type(e).__name__, str(e)])
+ raise ApiException(status=0, reason=msg)
+
+ return RESTResponse(r)
diff --git a/src/codegen/agents/constants.py b/src/codegen/agents/constants.py
new file mode 100644
index 000000000..ec94f38e5
--- /dev/null
+++ b/src/codegen/agents/constants.py
@@ -0,0 +1 @@
+CODEGEN_BASE_API_URL = "https://codegen-sh--rest-api.modal.run"
diff --git a/src/codegen/agents/data.py b/src/codegen/agents/data.py
index 6ac9b1d81..fab2283da 100644
--- a/src/codegen/agents/data.py
+++ b/src/codegen/agents/data.py
@@ -52,6 +52,7 @@ class ToolMessageData(BaseMessage):
tool_name: Optional[str] = None
tool_response: Optional[str] = None
tool_id: Optional[str] = None
+ status: Optional[str] = None
@dataclass
diff --git a/src/codegen/agents/tracer.py b/src/codegen/agents/tracer.py
index 816835c41..ef711b9e9 100644
--- a/src/codegen/agents/tracer.py
+++ b/src/codegen/agents/tracer.py
@@ -71,7 +71,14 @@ def extract_structured_data(self, chunk: dict[str, Any]) -> Optional[BaseMessage
tool_calls = [ToolCall(name=tc.get("name"), arguments=tc.get("arguments"), id=tc.get("id")) for tc in tool_calls_data]
return AssistantMessage(type=message_type, content=content, tool_calls=tool_calls)
elif message_type == "tool":
- return ToolMessageData(type=message_type, content=content, tool_name=getattr(latest_message, "name", None), tool_response=content, tool_id=getattr(latest_message, "tool_call_id", None))
+ return ToolMessageData(
+ type=message_type,
+ content=content,
+ tool_name=getattr(latest_message, "name", None),
+ tool_response=getattr(latest_message, "artifact", content),
+ tool_id=getattr(latest_message, "tool_call_id", None),
+ status=getattr(latest_message, "status", None),
+ )
elif message_type == "function":
return FunctionMessageData(type=message_type, content=content)
else:
diff --git a/src/codegen/cli/commands/agent/main.py b/src/codegen/cli/commands/agent/main.py
index 9862c1e5a..617c170aa 100644
--- a/src/codegen/cli/commands/agent/main.py
+++ b/src/codegen/cli/commands/agent/main.py
@@ -16,7 +16,7 @@
MoveSymbolTool,
RenameFileTool,
RevealSymbolTool,
- SearchTool,
+ RipGrepTool,
ViewFileTool,
)
from codegen.sdk.core.codebase import Codebase
@@ -62,7 +62,7 @@ def say(message: str):
tools = [
ViewFileTool(codebase),
ListDirectoryTool(codebase),
- SearchTool(codebase),
+ RipGrepTool(codebase),
CreateFileTool(codebase),
DeleteFileTool(codebase),
RenameFileTool(codebase),
diff --git a/src/codegen/extensions/langchain/__init__.py b/src/codegen/extensions/langchain/__init__.py
index 0df13e62b..301756a01 100644
--- a/src/codegen/extensions/langchain/__init__.py
+++ b/src/codegen/extensions/langchain/__init__.py
@@ -11,7 +11,7 @@
EditFileTool,
ListDirectoryTool,
RevealSymbolTool,
- SearchTool,
+ RipGrepTool,
SemanticEditTool,
ViewFileTool,
)
@@ -24,7 +24,7 @@
"EditFileTool",
"ListDirectoryTool",
"RevealSymbolTool",
- "SearchTool",
+ "RipGrepTool",
"SemanticEditTool",
"ViewFileTool",
# Helper functions
@@ -44,7 +44,7 @@ def get_workspace_tools(codebase: Codebase) -> list[BaseTool]:
return [
ViewFileTool(codebase),
ListDirectoryTool(codebase),
- SearchTool(codebase),
+ RipGrepTool(codebase),
EditFileTool(codebase),
CreateFileTool(codebase),
DeleteFileTool(codebase),
diff --git a/src/codegen/extensions/langchain/agent.py b/src/codegen/extensions/langchain/agent.py
index 167aa3128..551db5b46 100644
--- a/src/codegen/extensions/langchain/agent.py
+++ b/src/codegen/extensions/langchain/agent.py
@@ -21,8 +21,8 @@
RenameFileTool,
ReplacementEditTool,
RevealSymbolTool,
+ RipGrepTool,
SearchFilesByNameTool,
- SearchTool,
# SemanticEditTool,
ViewFileTool,
)
@@ -63,11 +63,11 @@ def create_codebase_agent(
"""
llm = LLM(model_provider=model_provider, model_name=model_name, **kwargs)
- # Get all codebase tools
+ # Initialize default tools
tools = [
ViewFileTool(codebase),
ListDirectoryTool(codebase),
- SearchTool(codebase),
+ RipGrepTool(codebase),
# EditFileTool(codebase),
CreateFileTool(codebase),
DeleteFileTool(codebase),
@@ -80,17 +80,13 @@ def create_codebase_agent(
ReflectionTool(codebase),
SearchFilesByNameTool(codebase),
GlobalReplacementEditTool(codebase),
- # SemanticSearchTool(codebase),
- # =====[ Github Integration ]=====
- # Enable Github integration
- # GithubCreatePRTool(codebase),
- # GithubViewPRTool(codebase),
- # GithubCreatePRCommentTool(codebase),
- # GithubCreatePRReviewCommentTool(codebase),
]
- # Add additional tools if provided
if additional_tools:
+ # Get names of additional tools
+ additional_names = {t.get_name() for t in additional_tools}
+ # Keep only tools that don't have matching names in additional_tools
+ tools = [t for t in tools if t.get_name() not in additional_names]
tools.extend(additional_tools)
memory = MemorySaver() if memory else None
@@ -131,7 +127,7 @@ def create_chat_agent(
tools = [
ViewFileTool(codebase),
ListDirectoryTool(codebase),
- SearchTool(codebase),
+ RipGrepTool(codebase),
CreateFileTool(codebase),
DeleteFileTool(codebase),
RenameFileTool(codebase),
@@ -177,7 +173,7 @@ def create_codebase_inspector_agent(
tools = [
ViewFileTool(codebase),
ListDirectoryTool(codebase),
- SearchTool(codebase),
+ RipGrepTool(codebase),
DeleteFileTool(codebase),
RevealSymbolTool(codebase),
]
diff --git a/src/codegen/extensions/langchain/graph.py b/src/codegen/extensions/langchain/graph.py
index 2987f6863..436cc7d30 100644
--- a/src/codegen/extensions/langchain/graph.py
+++ b/src/codegen/extensions/langchain/graph.py
@@ -6,17 +6,24 @@
import anthropic
import openai
from langchain.tools import BaseTool
-from langchain_core.messages import AIMessage, AnyMessage, HumanMessage, SystemMessage, ToolMessage
+from langchain_core.messages import (
+ AIMessage,
+ AnyMessage,
+ HumanMessage,
+ SystemMessage,
+ ToolMessage,
+)
from langchain_core.prompts import ChatPromptTemplate
+from langchain_core.stores import InMemoryBaseStore
from langgraph.checkpoint.memory import MemorySaver
from langgraph.graph import END, START
from langgraph.graph.state import CompiledGraph, StateGraph
-from langgraph.prebuilt import ToolNode
from langgraph.pregel import RetryPolicy
from codegen.agents.utils import AgentConfig
from codegen.extensions.langchain.llm import LLM
from codegen.extensions.langchain.prompts import SUMMARIZE_CONVERSATION_PROMPT
+from codegen.extensions.langchain.utils.custom_tool_node import CustomToolNode
from codegen.extensions.langchain.utils.utils import get_max_model_input_tokens
@@ -87,6 +94,7 @@ def __init__(self, model: "LLM", tools: list[BaseTool], system_message: SystemMe
self.config = config
self.max_messages = config.get("max_messages", 100) if config else 100
self.keep_first_messages = config.get("keep_first_messages", 1) if config else 1
+ self.store = InMemoryBaseStore()
# =================================== NODES ====================================
@@ -100,7 +108,7 @@ def reasoner(self, state: GraphState) -> dict[str, Any]:
messages.append(HumanMessage(content=query))
result = self.model.invoke([self.system_message, *messages])
- if isinstance(result, AIMessage):
+ if isinstance(result, AIMessage) and not result.tool_calls:
updated_messages = [*messages, result]
return {"messages": updated_messages, "final_answer": result.content}
@@ -147,15 +155,27 @@ def format_header(header_type: str) -> str:
# Format messages with appropriate headers
formatted_messages = []
- for msg in to_summarize: # No need for slice when iterating full list
+ image_urls = [] # Track image URLs for the summary prompt
+
+ for msg in to_summarize:
if isinstance(msg, HumanMessage):
- formatted_messages.append(format_header("human") + msg.content)
+ # Now we know content is always a list
+ for item in msg.content:
+ if item.get("type") == "text":
+ text_content = item.get("text", "")
+ if text_content:
+ formatted_messages.append(format_header("human") + text_content)
+ elif item.get("type") == "image_url":
+ image_url = item.get("image_url", {}).get("url")
+ if image_url:
+ # We are not including any string data in the summary for image. The image will be present itself!
+ image_urls.append({"type": "image_url", "image_url": {"url": image_url}})
elif isinstance(msg, AIMessage):
# Check for summary message using additional_kwargs
if msg.additional_kwargs.get("is_summary"):
formatted_messages.append(format_header("summary") + msg.content)
elif isinstance(msg.content, list) and len(msg.content) > 0 and isinstance(msg.content[0], dict):
- for item in msg.content: # No need for slice when iterating full list
+ for item in msg.content:
if item.get("type") == "text":
formatted_messages.append(format_header("ai") + item["text"])
elif item.get("type") == "tool_use":
@@ -165,7 +185,7 @@ def format_header(header_type: str) -> str:
elif isinstance(msg, ToolMessage):
formatted_messages.append(format_header("tool_response") + msg.content)
- conversation = "\n".join(formatted_messages) # No need for slice when joining full list
+ conversation = "\n".join(formatted_messages)
summary_llm = LLM(
model_provider="anthropic",
@@ -173,8 +193,17 @@ def format_header(header_type: str) -> str:
temperature=0.3,
)
- chain = ChatPromptTemplate.from_template(SUMMARIZE_CONVERSATION_PROMPT) | summary_llm
- new_summary = chain.invoke({"conversation": conversation}).content
+ # Choose template based on whether we have images
+ summarizer_content = [{"type": "text", "text": SUMMARIZE_CONVERSATION_PROMPT}]
+ for image_url in image_urls:
+ summarizer_content.append(image_url)
+
+ chain = ChatPromptTemplate([("human", summarizer_content)]) | summary_llm
+ new_summary = chain.invoke(
+ {
+ "conversation": conversation,
+ }
+ ).content
return {"messages": {"type": "summarize", "summary": new_summary, "tail": tail, "head": head}}
@@ -191,7 +220,7 @@ def should_continue(self, state: GraphState) -> Literal["tools", "summarize_conv
return "summarize_conversation"
# Summarize if the last message exceeds the max input tokens of the model - 10000 tokens
- elif isinstance(last_message, AIMessage) and not just_summarized and curr_input_tokens > (max_input_tokens - 10000):
+ elif isinstance(last_message, AIMessage) and not just_summarized and curr_input_tokens > (max_input_tokens - 30000):
return "summarize_conversation"
elif hasattr(last_message, "tool_calls") and last_message.tool_calls:
@@ -455,11 +484,11 @@ def get_field_descriptions(tool_obj):
return f"Error: Could not identify the tool you're trying to use.\n\nAvailable tools:\n{available_tools}\n\nPlease use one of the available tools with the correct parameters."
# For other types of errors
- return f"Error executing tool: {error_msg}\n\nPlease check your tool usage and try again with the correct parameters."
+ return f"Error executing tool: {exception!s}\n\nPlease check your tool usage and try again with the correct parameters."
# Add nodes
builder.add_node("reasoner", self.reasoner, retry=retry_policy)
- builder.add_node("tools", ToolNode(self.tools, handle_tool_errors=handle_tool_errors), retry=retry_policy)
+ builder.add_node("tools", CustomToolNode(self.tools, handle_tool_errors=handle_tool_errors), retry=retry_policy)
builder.add_node("summarize_conversation", self.summarize_conversation, retry=retry_policy)
# Add edges
@@ -471,7 +500,7 @@ def get_field_descriptions(tool_obj):
)
builder.add_conditional_edges("summarize_conversation", self.should_continue)
- return builder.compile(checkpointer=checkpointer, debug=debug)
+ return builder.compile(checkpointer=checkpointer, store=self.store, debug=debug)
def create_react_agent(
diff --git a/src/codegen/extensions/langchain/llm.py b/src/codegen/extensions/langchain/llm.py
index 4c457e46d..dadcf6314 100644
--- a/src/codegen/extensions/langchain/llm.py
+++ b/src/codegen/extensions/langchain/llm.py
@@ -89,7 +89,7 @@ def _get_model(self) -> BaseChatModel:
if not os.getenv("ANTHROPIC_API_KEY"):
msg = "ANTHROPIC_API_KEY not found in environment. Please set it in your .env file or environment variables."
raise ValueError(msg)
- max_tokens = 16384 if "claude-3-7" in self.model_name else 8192
+ max_tokens = 8192
return ChatAnthropic(**self._get_model_kwargs(), max_tokens=max_tokens, max_retries=10, timeout=1000)
elif self.model_provider == "openai":
diff --git a/src/codegen/extensions/langchain/tools.py b/src/codegen/extensions/langchain/tools.py
index 1ef9df85f..0749384a4 100644
--- a/src/codegen/extensions/langchain/tools.py
+++ b/src/codegen/extensions/langchain/tools.py
@@ -1,9 +1,13 @@
"""Langchain tools for workspace operations."""
from collections.abc import Callable
-from typing import ClassVar, Literal
+from typing import Annotated, ClassVar, Literal, Optional
+from langchain_core.messages import ToolMessage
+from langchain_core.stores import InMemoryBaseStore
+from langchain_core.tools import InjectedToolCallId
from langchain_core.tools.base import BaseTool
+from langgraph.prebuilt import InjectedStore
from pydantic import BaseModel, Field
from codegen.extensions.linear.linear_client import LinearClient
@@ -52,10 +56,11 @@ class ViewFileInput(BaseModel):
"""Input for viewing a file."""
filepath: str = Field(..., description="Path to the file relative to workspace root")
- start_line: int | None = Field(None, description="Starting line number to view (1-indexed, inclusive)")
- end_line: int | None = Field(None, description="Ending line number to view (1-indexed, inclusive)")
- max_lines: int | None = Field(None, description="Maximum number of lines to view at once, defaults to 500")
- line_numbers: bool | None = Field(True, description="If True, add line numbers to the content (1-indexed)")
+ start_line: Optional[int] = Field(None, description="Starting line number to view (1-indexed, inclusive)")
+ end_line: Optional[int] = Field(None, description="Ending line number to view (1-indexed, inclusive)")
+ max_lines: Optional[int] = Field(None, description="Maximum number of lines to view at once, defaults to 500")
+ line_numbers: Optional[bool] = Field(True, description="If True, add line numbers to the content (1-indexed)")
+ tool_call_id: Annotated[str, InjectedToolCallId]
class ViewFileTool(BaseTool):
@@ -73,12 +78,13 @@ def __init__(self, codebase: Codebase) -> None:
def _run(
self,
+ tool_call_id: str,
filepath: str,
- start_line: int | None = None,
- end_line: int | None = None,
- max_lines: int | None = None,
- line_numbers: bool | None = True,
- ) -> str:
+ start_line: Optional[int] = None,
+ end_line: Optional[int] = None,
+ max_lines: Optional[int] = None,
+ line_numbers: Optional[bool] = True,
+ ) -> ToolMessage:
result = view_file(
self.codebase,
filepath,
@@ -88,7 +94,7 @@ def _run(
max_lines=max_lines if max_lines is not None else 500,
)
- return result.render()
+ return result.render(tool_call_id)
class ListDirectoryInput(BaseModel):
@@ -96,6 +102,7 @@ class ListDirectoryInput(BaseModel):
dirpath: str = Field(default="./", description="Path to directory relative to workspace root")
depth: int = Field(default=1, description="How deep to traverse. Use -1 for unlimited depth.")
+ tool_call_id: Annotated[str, InjectedToolCallId]
class ListDirectoryTool(BaseTool):
@@ -109,9 +116,9 @@ class ListDirectoryTool(BaseTool):
def __init__(self, codebase: Codebase) -> None:
super().__init__(codebase=codebase)
- def _run(self, dirpath: str = "./", depth: int = 1) -> str:
+ def _run(self, tool_call_id: str, dirpath: str = "./", depth: int = 1) -> ToolMessage:
result = list_directory(self.codebase, dirpath, depth)
- return result.render()
+ return result.render(tool_call_id)
class SearchInput(BaseModel):
@@ -119,29 +126,29 @@ class SearchInput(BaseModel):
query: str = Field(
...,
- description="""The search query to find in the codebase. When ripgrep is available, this will be passed as a ripgrep pattern. For regex searches, set use_regex=True.
- Ripgrep is the preferred method.""",
+ description="""ripgrep query (or regex pattern) to run. For regex searches, set use_regex=True. Ripgrep is the preferred method.""",
)
file_extensions: list[str] | None = Field(default=None, description="Optional list of file extensions to search (e.g. ['.py', '.ts'])")
page: int = Field(default=1, description="Page number to return (1-based, default: 1)")
files_per_page: int = Field(default=10, description="Number of files to return per page (default: 10)")
use_regex: bool = Field(default=False, description="Whether to treat query as a regex pattern (default: False)")
+ tool_call_id: Annotated[str, InjectedToolCallId]
-class SearchTool(BaseTool):
- """Tool for searching the codebase."""
+class RipGrepTool(BaseTool):
+ """Tool for searching the codebase via RipGrep."""
name: ClassVar[str] = "search"
- description: ClassVar[str] = "Search the codebase using text search or regex pattern matching"
+ description: ClassVar[str] = "Search the codebase using `ripgrep` or regex pattern matching"
args_schema: ClassVar[type[BaseModel]] = SearchInput
codebase: Codebase = Field(exclude=True)
def __init__(self, codebase: Codebase) -> None:
super().__init__(codebase=codebase)
- def _run(self, query: str, file_extensions: list[str] | None = None, page: int = 1, files_per_page: int = 10, use_regex: bool = False) -> str:
+ def _run(self, tool_call_id: str, query: str, file_extensions: Optional[list[str]] = None, page: int = 1, files_per_page: int = 10, use_regex: bool = False) -> ToolMessage:
result = search(self.codebase, query, file_extensions=file_extensions, page=page, files_per_page=files_per_page, use_regex=use_regex)
- return result.render()
+ return result.render(tool_call_id)
class EditFileInput(BaseModel):
@@ -149,6 +156,7 @@ class EditFileInput(BaseModel):
filepath: str = Field(..., description="Path to the file to edit")
content: str = Field(..., description="New content for the file")
+ tool_call_id: Annotated[str, InjectedToolCallId]
class EditFileTool(BaseTool):
@@ -181,19 +189,21 @@ class EditFileTool(BaseTool):
def __init__(self, codebase: Codebase) -> None:
super().__init__(codebase=codebase)
- def _run(self, filepath: str, content: str) -> str:
+ def _run(self, filepath: str, content: str, tool_call_id: str) -> str:
result = edit_file(self.codebase, filepath, content)
- return result.render()
+ return result.render(tool_call_id)
class CreateFileInput(BaseModel):
"""Input for creating a file."""
+ model_config = {"arbitrary_types_allowed": True}
filepath: str = Field(..., description="Path where to create the file")
+ store: Annotated[InMemoryBaseStore, InjectedStore()]
content: str = Field(
- ...,
+ default="",
description="""
-Content for the new file (REQUIRED).
+Content for the new file.
⚠️ IMPORTANT: This parameter MUST be a STRING, not a dictionary, JSON object, or any other data type.
Example: content="print('Hello world')"
@@ -207,19 +217,14 @@ class CreateFileTool(BaseTool):
name: ClassVar[str] = "create_file"
description: ClassVar[str] = """
-Create a new file in the codebase. Always provide content for the new file, even if minimal.
-
-⚠️ CRITICAL WARNING ⚠️
-Both parameters MUST be provided as STRINGS:
-The content for the new file always needs to be provided.
+Create a new file in the codebase.
1. filepath: The path where to create the file (as a string)
2. content: The content for the new file (as a STRING, NOT as a dictionary or JSON object)
✅ CORRECT usage:
create_file(filepath="path/to/file.py", content="print('Hello world')")
-
-The content parameter is REQUIRED and MUST be a STRING. If you receive a validation error about
+If you receive a validation error about
missing content, you are likely trying to pass a dictionary instead of a string.
"""
args_schema: ClassVar[type[BaseModel]] = CreateFileInput
@@ -228,8 +233,15 @@ class CreateFileTool(BaseTool):
def __init__(self, codebase: Codebase) -> None:
super().__init__(codebase=codebase)
- def _run(self, filepath: str, content: str) -> str:
- result = create_file(self.codebase, filepath, content)
+ def _run(self, filepath: str, store: InMemoryBaseStore, content: str = "") -> str:
+ create_file_tool_status = store.mget([self.name])[0]
+ if create_file_tool_status and create_file_tool_status.get("max_tokens_reached", False):
+ max_tokens = create_file_tool_status.get("max_tokens", None)
+ store.mset([(self.name, {"max_tokens": max_tokens, "max_tokens_reached": False})])
+ result = create_file(self.codebase, filepath, content, max_tokens=max_tokens)
+ else:
+ result = create_file(self.codebase, filepath, content)
+
return result.render()
@@ -340,6 +352,7 @@ class SemanticEditInput(BaseModel):
edit_content: str = Field(..., description=FILE_EDIT_PROMPT)
start: int = Field(default=1, description="Starting line number (1-indexed, inclusive). Default is 1.")
end: int = Field(default=-1, description="Ending line number (1-indexed, inclusive). Default is -1 (end of file).")
+ tool_call_id: Annotated[str, InjectedToolCallId]
class SemanticEditTool(BaseTool):
@@ -353,10 +366,10 @@ class SemanticEditTool(BaseTool):
def __init__(self, codebase: Codebase) -> None:
super().__init__(codebase=codebase)
- def _run(self, filepath: str, edit_content: str, start: int = 1, end: int = -1) -> str:
+ def _run(self, filepath: str, tool_call_id: str, edit_content: str, start: int = 1, end: int = -1) -> ToolMessage:
# Create the the draft editor mini llm
result = semantic_edit(self.codebase, filepath, edit_content, start=start, end=end)
- return result.render()
+ return result.render(tool_call_id)
class RenameFileInput(BaseModel):
@@ -853,7 +866,7 @@ def get_workspace_tools(codebase: Codebase) -> list["BaseTool"]:
RevealSymbolTool(codebase),
GlobalReplacementEditTool(codebase),
RunBashCommandTool(), # Note: This tool doesn't need the codebase
- SearchTool(codebase),
+ RipGrepTool(codebase),
SearchFilesByNameTool(codebase),
# SemanticEditTool(codebase),
# SemanticSearchTool(codebase),
@@ -1033,6 +1046,7 @@ class RelaceEditInput(BaseModel):
filepath: str = Field(..., description="Path of the file relative to workspace root")
edit_snippet: str = Field(..., description=RELACE_EDIT_PROMPT)
+ tool_call_id: Annotated[str, InjectedToolCallId]
class RelaceEditTool(BaseTool):
@@ -1046,9 +1060,9 @@ class RelaceEditTool(BaseTool):
def __init__(self, codebase: Codebase) -> None:
super().__init__(codebase=codebase)
- def _run(self, filepath: str, edit_snippet: str) -> str:
+ def _run(self, filepath: str, edit_snippet: str, tool_call_id: str) -> ToolMessage:
result = relace_edit(self.codebase, filepath, edit_snippet)
- return result.render()
+ return result.render(tool_call_id=tool_call_id)
class ReflectionInput(BaseModel):
diff --git a/src/codegen/extensions/langchain/utils/custom_tool_node.py b/src/codegen/extensions/langchain/utils/custom_tool_node.py
new file mode 100644
index 000000000..bdbe4ab0e
--- /dev/null
+++ b/src/codegen/extensions/langchain/utils/custom_tool_node.py
@@ -0,0 +1,43 @@
+from typing import Any, Literal, Optional, Union
+
+from langchain_core.messages import (
+ AIMessage,
+ AnyMessage,
+ ToolCall,
+)
+from langchain_core.stores import InMemoryBaseStore
+from langgraph.prebuilt import ToolNode
+from pydantic import BaseModel
+
+
+class CustomToolNode(ToolNode):
+ """Extended ToolNode that detects truncated tool calls."""
+
+ def _parse_input(
+ self,
+ input: Union[
+ list[AnyMessage],
+ dict[str, Any],
+ BaseModel,
+ ],
+ store: Optional[InMemoryBaseStore],
+ ) -> tuple[list[ToolCall], Literal["list", "dict", "tool_calls"]]:
+ """Parse the input and check for truncated tool calls."""
+ messages = input.get("messages", [])
+ if isinstance(messages, list):
+ if isinstance(messages[-1], AIMessage):
+ response_metadata = messages[-1].response_metadata
+ # Check if the stop reason is due to max tokens
+ if response_metadata.get("stop_reason") == "max_tokens":
+ # Check if the response metadata contains usage information
+ if "usage" not in response_metadata or "output_tokens" not in response_metadata["usage"]:
+ msg = "Response metadata is missing usage information."
+ raise ValueError(msg)
+
+ output_tokens = response_metadata["usage"]["output_tokens"]
+ for tool_call in messages[-1].tool_calls:
+ if tool_call.get("name") == "create_file":
+ # Set the max tokens and max tokens reached flag in the store
+ store.mset([(tool_call["name"], {"max_tokens": output_tokens, "max_tokens_reached": True})])
+
+ return super()._parse_input(input, store)
diff --git a/src/codegen/extensions/tools/create_file.py b/src/codegen/extensions/tools/create_file.py
index 3a54303ff..cc22d3ede 100644
--- a/src/codegen/extensions/tools/create_file.py
+++ b/src/codegen/extensions/tools/create_file.py
@@ -1,6 +1,6 @@
"""Tool for creating new files."""
-from typing import ClassVar
+from typing import ClassVar, Optional
from pydantic import Field
@@ -23,7 +23,7 @@ class CreateFileObservation(Observation):
str_template: ClassVar[str] = "Created file {filepath}"
-def create_file(codebase: Codebase, filepath: str, content: str) -> CreateFileObservation:
+def create_file(codebase: Codebase, filepath: str, content: str, max_tokens: Optional[int] = None) -> CreateFileObservation:
"""Create a new file.
Args:
@@ -34,6 +34,16 @@ def create_file(codebase: Codebase, filepath: str, content: str) -> CreateFileOb
Returns:
CreateFileObservation containing new file state, or error if file exists
"""
+ if max_tokens:
+ error = f"""Your response reached the max output tokens limit of {max_tokens} tokens (~ {max_tokens / 10} lines).
+Create the file in chunks or break up the content into smaller files.
+ """
+ return CreateFileObservation(
+ status="error",
+ error=error,
+ filepath=filepath,
+ file_info=ViewFileObservation(status="error", error=error, filepath=filepath, content="", raw_content="", line_count=0),
+ )
if codebase.has_file(filepath):
return CreateFileObservation(
status="error",
@@ -45,6 +55,7 @@ def create_file(codebase: Codebase, filepath: str, content: str) -> CreateFileOb
filepath=filepath,
content="",
line_count=0,
+ raw_content="",
),
)
@@ -72,5 +83,6 @@ def create_file(codebase: Codebase, filepath: str, content: str) -> CreateFileOb
filepath=filepath,
content="",
line_count=0,
+ raw_content="",
),
)
diff --git a/src/codegen/extensions/tools/edit_file.py b/src/codegen/extensions/tools/edit_file.py
index 13ba35951..b4831b968 100644
--- a/src/codegen/extensions/tools/edit_file.py
+++ b/src/codegen/extensions/tools/edit_file.py
@@ -1,7 +1,8 @@
"""Tool for editing file contents."""
-from typing import ClassVar
+from typing import TYPE_CHECKING, ClassVar, Optional
+from langchain_core.messages import ToolMessage
from pydantic import Field
from codegen.sdk.core.codebase import Codebase
@@ -9,6 +10,9 @@
from .observation import Observation
from .replacement_edit import generate_diff
+if TYPE_CHECKING:
+ from .tool_output_types import EditFileArtifacts
+
class EditFileObservation(Observation):
"""Response from editing a file."""
@@ -16,17 +20,34 @@ class EditFileObservation(Observation):
filepath: str = Field(
description="Path to the edited file",
)
- diff: str = Field(
+ diff: Optional[str] = Field(
+ default=None,
description="Unified diff showing the changes made",
)
str_template: ClassVar[str] = "Edited file {filepath}"
- def render(self) -> str:
+ def render(self, tool_call_id: str) -> ToolMessage:
"""Render edit results in a clean format."""
- return f"""[EDIT FILE]: {self.filepath}
-
-{self.diff}"""
+ if self.status == "error":
+ artifacts_error: EditFileArtifacts = {"filepath": self.filepath, "error": self.error}
+ return ToolMessage(
+ content=f"[ERROR EDITING FILE]: {self.filepath}: {self.error}",
+ status=self.status,
+ name="edit_file",
+ artifact=artifacts_error,
+ tool_call_id=tool_call_id,
+ )
+
+ artifacts_success: EditFileArtifacts = {"filepath": self.filepath, "diff": self.diff}
+
+ return ToolMessage(
+ content=f"""[EDIT FILE]: {self.filepath}\n\n{self.diff}""",
+ status=self.status,
+ name="edit_file",
+ artifact=artifacts_success,
+ tool_call_id=tool_call_id,
+ )
def edit_file(codebase: Codebase, filepath: str, new_content: str) -> EditFileObservation:
diff --git a/src/codegen/extensions/tools/list_directory.py b/src/codegen/extensions/tools/list_directory.py
index 357f303ca..398dc9cc8 100644
--- a/src/codegen/extensions/tools/list_directory.py
+++ b/src/codegen/extensions/tools/list_directory.py
@@ -2,13 +2,14 @@
from typing import ClassVar
+from langchain_core.messages import ToolMessage
from pydantic import Field
+from codegen.extensions.tools.observation import Observation
+from codegen.extensions.tools.tool_output_types import ListDirectoryArtifacts
from codegen.sdk.core.codebase import Codebase
from codegen.sdk.core.directory import Directory
-from .observation import Observation
-
class DirectoryInfo(Observation):
"""Information about a directory."""
@@ -31,6 +32,14 @@ class DirectoryInfo(Observation):
default=False,
description="Whether this is a leaf node (at max depth)",
)
+ depth: int = Field(
+ default=0,
+ description="Current depth in the tree",
+ )
+ max_depth: int = Field(
+ default=1,
+ description="Maximum depth allowed",
+ )
str_template: ClassVar[str] = "Directory {path} ({file_count} files, {dir_count} subdirs)"
@@ -41,7 +50,7 @@ def _get_details(self) -> dict[str, int]:
"dir_count": len(self.subdirectories),
}
- def render(self) -> str:
+ def render_as_string(self) -> str:
"""Render directory listing as a file tree."""
lines = [
f"[LIST DIRECTORY]: {self.path}",
@@ -97,6 +106,26 @@ def build_tree(items: list[tuple[str, bool, "DirectoryInfo | None"]], prefix: st
return "\n".join(lines)
+ def to_artifacts(self) -> ListDirectoryArtifacts:
+ """Convert directory info to artifacts for UI."""
+ artifacts: ListDirectoryArtifacts = {
+ "dirpath": self.path,
+ "name": self.name,
+ "is_leaf": self.is_leaf,
+ "depth": self.depth,
+ "max_depth": self.max_depth,
+ }
+
+ if self.files is not None:
+ artifacts["files"] = self.files
+ artifacts["file_paths"] = [f"{self.path}/{f}" for f in self.files]
+
+ if self.subdirectories:
+ artifacts["subdirs"] = [d.name for d in self.subdirectories]
+ artifacts["subdir_paths"] = [d.path for d in self.subdirectories]
+
+ return artifacts
+
class ListDirectoryObservation(Observation):
"""Response from listing directory contents."""
@@ -107,9 +136,29 @@ class ListDirectoryObservation(Observation):
str_template: ClassVar[str] = "{directory_info}"
- def render(self) -> str:
- """Render directory listing."""
- return self.directory_info.render()
+ def render(self, tool_call_id: str) -> ToolMessage:
+ """Render directory listing with artifacts for UI."""
+ if self.status == "error":
+ error_artifacts: ListDirectoryArtifacts = {
+ "dirpath": self.directory_info.path,
+ "name": self.directory_info.name,
+ "error": self.error,
+ }
+ return ToolMessage(
+ content=f"[ERROR LISTING DIRECTORY]: {self.directory_info.path}: {self.error}",
+ status=self.status,
+ name="list_directory",
+ artifact=error_artifacts,
+ tool_call_id=tool_call_id,
+ )
+
+ return ToolMessage(
+ content=self.directory_info.render_as_string(),
+ status=self.status,
+ name="list_directory",
+ artifact=self.directory_info.to_artifacts(),
+ tool_call_id=tool_call_id,
+ )
def list_directory(codebase: Codebase, path: str = "./", depth: int = 2) -> ListDirectoryObservation:
@@ -136,7 +185,7 @@ def list_directory(codebase: Codebase, path: str = "./", depth: int = 2) -> List
),
)
- def get_directory_info(dir_obj: Directory, current_depth: int) -> DirectoryInfo:
+ def get_directory_info(dir_obj: Directory, current_depth: int, max_depth: int) -> DirectoryInfo:
"""Helper function to get directory info recursively."""
# Get direct files (always include files unless at max depth)
all_files = []
@@ -151,7 +200,7 @@ def get_directory_info(dir_obj: Directory, current_depth: int) -> DirectoryInfo:
if current_depth > 1 or current_depth == -1:
# For deeper traversal, get full directory info
new_depth = current_depth - 1 if current_depth > 1 else -1
- subdirs.append(get_directory_info(subdir, new_depth))
+ subdirs.append(get_directory_info(subdir, new_depth, max_depth))
else:
# At max depth, return a leaf node
subdirs.append(
@@ -161,6 +210,8 @@ def get_directory_info(dir_obj: Directory, current_depth: int) -> DirectoryInfo:
path=subdir.dirpath,
files=None, # Don't include files at max depth
is_leaf=True,
+ depth=current_depth,
+ max_depth=max_depth,
)
)
@@ -170,9 +221,11 @@ def get_directory_info(dir_obj: Directory, current_depth: int) -> DirectoryInfo:
path=dir_obj.dirpath,
files=sorted(all_files),
subdirectories=subdirs,
+ depth=current_depth,
+ max_depth=max_depth,
)
- dir_info = get_directory_info(directory, depth)
+ dir_info = get_directory_info(directory, depth, depth)
return ListDirectoryObservation(
status="success",
directory_info=dir_info,
diff --git a/src/codegen/extensions/tools/observation.py b/src/codegen/extensions/tools/observation.py
index 512b10117..6cde37317 100644
--- a/src/codegen/extensions/tools/observation.py
+++ b/src/codegen/extensions/tools/observation.py
@@ -3,8 +3,13 @@
import json
from typing import Any, ClassVar, Optional
+from langchain_core.messages import ToolMessage
from pydantic import BaseModel, Field
+from codegen.shared.logging.get_logger import get_logger
+
+logger = get_logger(__name__)
+
class Observation(BaseModel):
"""Base class for all tool observations.
@@ -37,13 +42,51 @@ def __str__(self) -> str:
"""Get string representation of the observation."""
if self.status == "error":
return f"Error: {self.error}"
- details = self._get_details()
- return self.render()
+ return self.render_as_string()
def __repr__(self) -> str:
"""Get detailed string representation of the observation."""
return f"{self.__class__.__name__}({self.model_dump_json()})"
- def render(self) -> str:
- """Render the observation as a string."""
- return json.dumps(self.model_dump(), indent=2)
+ def render_as_string(self, max_tokens: int = 8000) -> str:
+ """Render the observation as a string.
+
+ This is used for string representation and as the content field
+ in the ToolMessage. Subclasses can override this to customize
+ their string output format.
+ """
+ rendered = json.dumps(self.model_dump(), indent=2)
+ if len(rendered) > (max_tokens * 3):
+ logger.error(f"Observation is too long to render: {len(rendered) * 3} tokens")
+ return rendered[:max_tokens] + "\n\n...truncated...\n\n"
+ return rendered
+
+ def render(self, tool_call_id: Optional[str] = None) -> ToolMessage | str:
+ """Render the observation as a ToolMessage or string.
+
+ Args:
+ tool_call_id: Optional[str] = None - If provided, return a ToolMessage.
+ If None, return a string representation.
+
+ Returns:
+ ToolMessage or str containing the observation content and metadata.
+ For error cases, includes error information in artifacts.
+ """
+ if tool_call_id is None:
+ return self.render_as_string()
+
+ # Get content first in case render_as_string has side effects
+ content = self.render_as_string()
+
+ if self.status == "error":
+ return ToolMessage(
+ content=content,
+ status=self.status,
+ tool_call_id=tool_call_id,
+ )
+
+ return ToolMessage(
+ content=content,
+ status=self.status,
+ tool_call_id=tool_call_id,
+ )
diff --git a/src/codegen/extensions/tools/relace_edit.py b/src/codegen/extensions/tools/relace_edit.py
index 276a4c946..0f7637bb8 100644
--- a/src/codegen/extensions/tools/relace_edit.py
+++ b/src/codegen/extensions/tools/relace_edit.py
@@ -2,9 +2,10 @@
import difflib
import os
-from typing import ClassVar, Optional
+from typing import TYPE_CHECKING, ClassVar
import requests
+from langchain_core.messages import ToolMessage
from pydantic import Field
from codegen.sdk.core.codebase import Codebase
@@ -12,6 +13,9 @@
from .observation import Observation
from .view_file import add_line_numbers
+if TYPE_CHECKING:
+ from codegen.extensions.tools.tool_output_types import RelaceEditArtifacts
+
class RelaceEditObservation(Observation):
"""Response from making edits to a file using Relace Instant Apply API."""
@@ -19,21 +23,48 @@ class RelaceEditObservation(Observation):
filepath: str = Field(
description="Path to the edited file",
)
- diff: Optional[str] = Field(
+ diff: str | None = Field(
default=None,
description="Unified diff showing the changes made",
)
- new_content: Optional[str] = Field(
+ new_content: str | None = Field(
default=None,
description="New content with line numbers",
)
- line_count: Optional[int] = Field(
+ line_count: int | None = Field(
default=None,
description="Total number of lines in file",
)
str_template: ClassVar[str] = "Edited file {filepath} using Relace Instant Apply"
+ def render(self, tool_call_id: str) -> ToolMessage:
+ """Render the relace edit observation as a ToolMessage."""
+ artifacts: RelaceEditArtifacts = {
+ "filepath": self.filepath,
+ "diff": self.diff,
+ "new_content": self.new_content,
+ "line_count": self.line_count,
+ "error": self.error,
+ }
+
+ if self.status == "error":
+ return ToolMessage(
+ content=f"[ERROR EDITING FILE]: {self.filepath}: {self.error}",
+ status=self.status,
+ name="relace_edit",
+ artifact=artifacts,
+ tool_call_id=tool_call_id,
+ )
+
+ return ToolMessage(
+ content=self.render_as_string(),
+ status=self.status,
+ name="relace_edit",
+ tool_call_id=tool_call_id,
+ artifact=artifacts,
+ )
+
def generate_diff(original: str, modified: str) -> str:
"""Generate a unified diff between two strings.
@@ -104,7 +135,7 @@ def apply_relace_edit(api_key: str, initial_code: str, edit_snippet: str, stream
raise Exception(msg)
-def relace_edit(codebase: Codebase, filepath: str, edit_snippet: str, api_key: Optional[str] = None) -> RelaceEditObservation:
+def relace_edit(codebase: Codebase, filepath: str, edit_snippet: str, api_key: str | None = None) -> RelaceEditObservation:
"""Edit a file using the Relace Instant Apply API.
Args:
@@ -145,6 +176,8 @@ def relace_edit(codebase: Codebase, filepath: str, edit_snippet: str, api_key: O
# Apply the edit using Relace API
try:
merged_code = apply_relace_edit(api_key, original_content, edit_snippet)
+ if original_content.endswith("\n") and not merged_code.endswith("\n"):
+ merged_code += "\n"
except Exception as e:
return RelaceEditObservation(
status="error",
diff --git a/src/codegen/extensions/tools/search.py b/src/codegen/extensions/tools/search.py
index 2a347c133..3f69be59c 100644
--- a/src/codegen/extensions/tools/search.py
+++ b/src/codegen/extensions/tools/search.py
@@ -11,8 +11,11 @@
import subprocess
from typing import ClassVar
+from langchain_core.messages import ToolMessage
from pydantic import Field
+from codegen.extensions.tools.tool_output_types import SearchArtifacts
+from codegen.extensions.tools.tool_output_types import SearchMatch as SearchMatchDict
from codegen.sdk.core.codebase import Codebase
from .observation import Observation
@@ -34,10 +37,18 @@ class SearchMatch(Observation):
)
str_template: ClassVar[str] = "Line {line_number}: {match}"
- def render(self) -> str:
+ def render_as_string(self) -> str:
"""Render match in a VSCode-like format."""
return f"{self.line_number:>4}: {self.line}"
+ def to_dict(self) -> SearchMatchDict:
+ """Convert to SearchMatch TypedDict format."""
+ return {
+ "line_number": self.line_number,
+ "line": self.line,
+ "match": self.match,
+ }
+
class SearchFileResult(Observation):
"""Search results for a single file."""
@@ -51,13 +62,13 @@ class SearchFileResult(Observation):
str_template: ClassVar[str] = "{filepath}: {match_count} matches"
- def render(self) -> str:
+ def render_as_string(self) -> str:
"""Render file results in a VSCode-like format."""
lines = [
f"📄 {self.filepath}",
]
for match in self.matches:
- lines.append(match.render())
+ lines.append(match.render_as_string())
return "\n".join(lines)
def _get_details(self) -> dict[str, str | int]:
@@ -89,11 +100,47 @@ class SearchObservation(Observation):
str_template: ClassVar[str] = "Found {total_files} files with matches for '{query}' (page {page}/{total_pages})"
- def render(self) -> str:
- """Render search results in a VSCode-like format."""
+ def render(self, tool_call_id: str) -> ToolMessage:
+ """Render search results in a VSCode-like format.
+
+ Args:
+ tool_call_id: ID of the tool call that triggered this search
+
+ Returns:
+ ToolMessage containing search results or error
+ """
+ # Prepare artifacts dictionary with default values
+ artifacts: SearchArtifacts = {
+ "query": self.query,
+ "error": self.error if self.status == "error" else None,
+ "matches": [], # List[SearchMatchDict] - match data as TypedDict
+ "file_paths": [], # List[str] - file paths with matches
+ "page": self.page,
+ "total_pages": self.total_pages if self.status == "success" else 0,
+ "total_files": self.total_files if self.status == "success" else 0,
+ "files_per_page": self.files_per_page,
+ }
+
+ # Handle error case early
if self.status == "error":
- return f"[SEARCH ERROR]: {self.error}"
+ return ToolMessage(
+ content=f"[SEARCH ERROR]: {self.error}",
+ status=self.status,
+ name="search",
+ tool_call_id=tool_call_id,
+ artifact=artifacts,
+ )
+ # Build matches and file paths for success case
+ for result in self.results:
+ artifacts["file_paths"].append(result.filepath)
+ for match in result.matches:
+ # Convert match to SearchMatchDict format
+ match_dict = match.to_dict()
+ match_dict["filepath"] = result.filepath
+ artifacts["matches"].append(match_dict)
+
+ # Build content lines
lines = [
f"[SEARCH RESULTS]: {self.query}",
f"Found {self.total_files} files with matches (showing page {self.page} of {self.total_pages})",
@@ -102,16 +149,23 @@ def render(self) -> str:
if not self.results:
lines.append("No matches found")
- return "\n".join(lines)
-
- for result in self.results:
- lines.append(result.render())
- lines.append("") # Add blank line between files
-
- if self.total_pages > 1:
- lines.append(f"Page {self.page}/{self.total_pages} (use page parameter to see more results)")
-
- return "\n".join(lines)
+ else:
+ # Add results with blank lines between files
+ for result in self.results:
+ lines.append(result.render_as_string())
+ lines.append("") # Add blank line between files
+
+ # Add pagination info if there are multiple pages
+ if self.total_pages > 1:
+ lines.append(f"Page {self.page}/{self.total_pages} (use page parameter to see more results)")
+
+ return ToolMessage(
+ content="\n".join(lines),
+ status=self.status,
+ name="search",
+ tool_call_id=tool_call_id,
+ artifact=artifacts,
+ )
def _search_with_ripgrep(
diff --git a/src/codegen/extensions/tools/semantic_edit.py b/src/codegen/extensions/tools/semantic_edit.py
index 91f35083d..97ba927c5 100644
--- a/src/codegen/extensions/tools/semantic_edit.py
+++ b/src/codegen/extensions/tools/semantic_edit.py
@@ -2,8 +2,9 @@
import difflib
import re
-from typing import ClassVar, Optional
+from typing import TYPE_CHECKING, ClassVar, Optional
+from langchain_core.messages import ToolMessage
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from pydantic import Field
@@ -15,6 +16,9 @@
from .semantic_edit_prompts import _HUMAN_PROMPT_DRAFT_EDITOR, COMMANDER_SYSTEM_PROMPT
from .view_file import add_line_numbers
+if TYPE_CHECKING:
+ from .tool_output_types import SemanticEditArtifacts
+
class SemanticEditObservation(Observation):
"""Response from making semantic edits to a file."""
@@ -24,19 +28,55 @@ class SemanticEditObservation(Observation):
)
diff: Optional[str] = Field(
default=None,
- description="Unified diff showing the changes made",
+ description="Unified diff of changes made to the file",
)
new_content: Optional[str] = Field(
default=None,
- description="New content with line numbers",
+ description="New content of the file with line numbers after edits",
)
line_count: Optional[int] = Field(
default=None,
- description="Total number of lines in file",
+ description="Total number of lines in the edited file",
)
str_template: ClassVar[str] = "Edited file {filepath}"
+ def render(self, tool_call_id: str) -> ToolMessage:
+ """Render the observation as a ToolMessage.
+
+ Args:
+ tool_call_id: ID of the tool call that triggered this edit
+
+ Returns:
+ ToolMessage containing edit results or error
+ """
+ # Prepare artifacts dictionary with default values
+ artifacts: SemanticEditArtifacts = {
+ "filepath": self.filepath,
+ "diff": self.diff,
+ "new_content": self.new_content,
+ "line_count": self.line_count,
+ "error": self.error if self.status == "error" else None,
+ }
+
+ # Handle error case early
+ if self.status == "error":
+ return ToolMessage(
+ content=f"[EDIT ERROR]: {self.error}",
+ status=self.status,
+ name="semantic_edit",
+ tool_call_id=tool_call_id,
+ artifact=artifacts,
+ )
+
+ return ToolMessage(
+ content=self.render_as_string(),
+ status=self.status,
+ name="semantic_edit",
+ tool_call_id=tool_call_id,
+ artifact=artifacts,
+ )
+
def generate_diff(original: str, modified: str) -> str:
"""Generate a unified diff between two strings.
diff --git a/src/codegen/extensions/tools/tool_output_types.py b/src/codegen/extensions/tools/tool_output_types.py
new file mode 100644
index 000000000..1678e0c7e
--- /dev/null
+++ b/src/codegen/extensions/tools/tool_output_types.py
@@ -0,0 +1,105 @@
+"""Type definitions for tool outputs."""
+
+from typing import Optional, TypedDict
+
+
+class EditFileArtifacts(TypedDict, total=False):
+ """Artifacts for edit file operations.
+
+ All fields are optional to support both success and error cases.
+ """
+
+ filepath: str # Path to the edited file
+ diff: Optional[str] # Diff of changes made to the file
+ error: Optional[str] # Error message (only present on error)
+
+
+class ViewFileArtifacts(TypedDict, total=False):
+ """Artifacts for view file operations.
+
+ All fields are optional to support both success and error cases.
+ Includes metadata useful for UI logging and pagination.
+ """
+
+ filepath: str # Path to the viewed file
+ start_line: Optional[int] # Starting line number viewed
+ end_line: Optional[int] # Ending line number viewed
+ content: Optional[str] # Content of the file
+ total_lines: Optional[int] # Total number of lines in file
+ has_more: Optional[bool] # Whether there are more lines to view
+ max_lines_per_page: Optional[int] # Maximum lines that can be viewed at once
+ file_size: Optional[int] # Size of file in bytes
+ error: Optional[str] # Error message (only present on error)
+
+
+class ListDirectoryArtifacts(TypedDict, total=False):
+ """Artifacts for directory listing operations.
+
+ All fields are optional to support both success and error cases.
+ Includes metadata useful for UI tree view and navigation.
+ """
+
+ dirpath: str # Full path to the directory
+ name: str # Name of the directory
+ files: Optional[list[str]] # List of files in this directory
+ file_paths: Optional[list[str]] # Full paths to files in this directory
+ subdirs: Optional[list[str]] # List of subdirectory names
+ subdir_paths: Optional[list[str]] # Full paths to subdirectories
+ is_leaf: Optional[bool] # Whether this is a leaf node (at max depth)
+ depth: Optional[int] # Current depth in the tree
+ max_depth: Optional[int] # Maximum depth allowed
+ error: Optional[str] # Error message (only present on error)
+
+
+class SearchMatch(TypedDict, total=False):
+ """Information about a single search match."""
+
+ filepath: str # Path to the file containing the match
+ line_number: int # 1-based line number of the match
+ line: str # The full line containing the match
+ match: str # The specific text that matched
+
+
+class SearchArtifacts(TypedDict, total=False):
+ """Artifacts for search operations.
+
+ All fields are optional to support both success and error cases.
+ Includes metadata useful for UI search results and navigation.
+ """
+
+ query: str # Search query that was used
+ page: int # Current page number (1-based)
+ total_pages: int # Total number of pages available
+ total_files: int # Total number of files with matches
+ files_per_page: int # Number of files shown per page
+ matches: list[SearchMatch] # List of matches with file paths and line numbers
+ file_paths: list[str] # List of files containing matches
+ error: Optional[str] # Error message (only present on error)
+
+
+class SemanticEditArtifacts(TypedDict, total=False):
+ """Artifacts for semantic edit operations.
+
+ All fields are optional to support both success and error cases.
+ Includes metadata useful for UI diff view and file content.
+ """
+
+ filepath: str # Path to the edited file
+ diff: Optional[str] # Unified diff of changes made to the file
+ new_content: Optional[str] # New content of the file after edits
+ line_count: Optional[int] # Total number of lines in the edited file
+ error: Optional[str] # Error message (only present on error)
+
+
+class RelaceEditArtifacts(TypedDict, total=False):
+ """Artifacts for relace edit operations.
+
+ All fields are optional to support both success and error cases.
+ Includes metadata useful for UI diff view and file content.
+ """
+
+ filepath: str # Path to the edited file
+ diff: Optional[str] # Unified diff of changes made to the file
+ new_content: Optional[str] # New content of the file after edits
+ line_count: Optional[int] # Total number of lines in the edited file
+ error: Optional[str] # Error message (only present on error)
diff --git a/src/codegen/extensions/tools/view_file.py b/src/codegen/extensions/tools/view_file.py
index a53106491..fbfcd8b02 100644
--- a/src/codegen/extensions/tools/view_file.py
+++ b/src/codegen/extensions/tools/view_file.py
@@ -1,13 +1,17 @@
"""Tool for viewing file contents and metadata."""
-from typing import ClassVar, Optional
+from typing import TYPE_CHECKING, ClassVar, Optional
+from langchain_core.messages import ToolMessage
from pydantic import Field
from codegen.sdk.core.codebase import Codebase
from .observation import Observation
+if TYPE_CHECKING:
+ from .tool_output_types import ViewFileArtifacts
+
class ViewFileObservation(Observation):
"""Response from viewing a file."""
@@ -18,6 +22,9 @@ class ViewFileObservation(Observation):
content: str = Field(
description="Content of the file",
)
+ raw_content: str = Field(
+ description="Raw content of the file",
+ )
line_count: Optional[int] = Field(
default=None,
description="Number of lines in the file",
@@ -41,8 +48,31 @@ class ViewFileObservation(Observation):
str_template: ClassVar[str] = "File {filepath} (showing lines {start_line}-{end_line} of {line_count})"
- def render(self) -> str:
+ def render(self, tool_call_id: str) -> ToolMessage:
"""Render the file view with pagination information if applicable."""
+ if self.status == "error":
+ error_artifacts: ViewFileArtifacts = {"filepath": self.filepath}
+ return ToolMessage(
+ content=f"[ERROR VIEWING FILE]: {self.filepath}: {self.error}",
+ status=self.status,
+ tool_call_id=tool_call_id,
+ name="view_file",
+ artifact=error_artifacts,
+ additional_kwargs={
+ "error": self.error,
+ },
+ )
+
+ success_artifacts: ViewFileArtifacts = {
+ "filepath": self.filepath,
+ "start_line": self.start_line,
+ "end_line": self.end_line,
+ "content": self.raw_content,
+ "total_lines": self.line_count,
+ "has_more": self.has_more,
+ "max_lines_per_page": self.max_lines_per_page,
+ }
+
header = f"[VIEW FILE]: {self.filepath}"
if self.line_count is not None:
header += f" ({self.line_count} lines total)"
@@ -52,10 +82,13 @@ def render(self) -> str:
if self.has_more:
header += f" (more lines available, max {self.max_lines_per_page} lines per page)"
- if not self.content:
- return f"{header}\n"
-
- return f"{header}\n\n{self.content}"
+ return ToolMessage(
+ content=f"{header}\n\n{self.content}" if self.content else f"{header}\n",
+ status=self.status,
+ name="view_file",
+ tool_call_id=tool_call_id,
+ artifact=success_artifacts,
+ )
def add_line_numbers(content: str) -> str:
@@ -92,12 +125,15 @@ def view_file(
"""
try:
file = codebase.get_file(filepath)
+
except ValueError:
return ViewFileObservation(
status="error",
- error=f"File not found: {filepath}. Please use full filepath relative to workspace root.",
+ error=f"""File not found: {filepath}. Please use full filepath relative to workspace root.
+Ensure that this is indeed the correct filepath, else keep searching to find the correct fullpath.""",
filepath=filepath,
content="",
+ raw_content="",
line_count=0,
start_line=start_line,
end_line=end_line,
@@ -141,6 +177,7 @@ def view_file(
status="success",
filepath=file.filepath,
content=content,
+ raw_content=file.content,
line_count=total_lines,
)
diff --git a/src/codegen/git/clients/git_repo_client.py b/src/codegen/git/clients/git_repo_client.py
index 72fc1e048..0ac741077 100644
--- a/src/codegen/git/clients/git_repo_client.py
+++ b/src/codegen/git/clients/git_repo_client.py
@@ -173,6 +173,19 @@ def get_pull_safe(self, number: int) -> PullRequest | None:
logger.warning(f"Error getting PR by number: {number}\n\t{e}")
return None
+ def get_issue_safe(self, number: int) -> Issue | None:
+ """Returns an issue by its number
+ TODO: catching UnknownObjectException is common enough to create a decorator
+ """
+ try:
+ pr = self.repo.get_issue(number)
+ return pr
+ except UnknownObjectException as e:
+ return None
+ except Exception as e:
+ logger.warning(f"Error getting issue by number: {number}\n\t{e}")
+ return None
+
def get_or_create_pull(
self,
head_branch_name: str,
diff --git a/src/codegen/git/repo_operator/repo_operator.py b/src/codegen/git/repo_operator/repo_operator.py
index edee45a18..f3bf2776f 100644
--- a/src/codegen/git/repo_operator/repo_operator.py
+++ b/src/codegen/git/repo_operator/repo_operator.py
@@ -897,8 +897,7 @@ def create_from_repo(cls, repo_path: str, url: str, access_token: str | None = N
if local_head.hexsha == remote_head.hexsha:
return cls(repo_config=RepoConfig.from_repo_path(repo_path), bot_commit=False, access_token=access_token)
except Exception:
- # If any git operations fail, fallback to fresh clone
- pass
+ logger.exception("Failed to initialize Git repository. Falling back to fresh clone.")
# If we get here, repo exists but is not up to date or valid
# Remove the existing directory to do a fresh clone
@@ -916,7 +915,6 @@ def create_from_repo(cls, repo_path: str, url: str, access_token: str | None = N
# Initialize with the cloned repo
git_cli = GitCLI(repo_path)
except (GitCommandError, ValueError) as e:
- logger.exception("Failed to initialize Git repository:")
- logger.exception("Please authenticate with a valid token and ensure the repository is properly initialized.")
+ logger.exception("Failed to initialize Git repository")
return None
return cls(repo_config=RepoConfig.from_repo_path(repo_path), bot_commit=False, access_token=access_token)
diff --git a/src/codegen/git/schemas/repo_config.py b/src/codegen/git/schemas/repo_config.py
index 0e54b8362..f94e85592 100644
--- a/src/codegen/git/schemas/repo_config.py
+++ b/src/codegen/git/schemas/repo_config.py
@@ -25,6 +25,9 @@ class RepoConfig(BaseModel):
base_path: str | None = None # root directory of the codebase within the repo
subdirectories: list[str] | None = None
+ # Additional sandbox settings
+ setup_commands: list[str] | None = None
+
@classmethod
def from_envs(cls) -> "RepoConfig":
default_repo_config = RepositoryConfig()
diff --git a/src/codegen/sdk/codebase/transaction_manager.py b/src/codegen/sdk/codebase/transaction_manager.py
index a59b6eb4e..87e938a1c 100644
--- a/src/codegen/sdk/codebase/transaction_manager.py
+++ b/src/codegen/sdk/codebase/transaction_manager.py
@@ -1,3 +1,4 @@
+import math
import time
from collections.abc import Callable
from pathlib import Path
@@ -289,6 +290,22 @@ def get_transactions_at_range(self, file_path: Path, start_byte: int, end_byte:
return matching_transactions
+ def get_transaction_containing_range(self, file_path: Path, start_byte: int, end_byte: int, transaction_order: TransactionPriority | None = None) -> Transaction | None:
+ """Returns the nearest transaction that includes the range specified given the filtering criteria."""
+ if file_path not in self.queued_transactions:
+ return None
+
+ smallest_difference = math.inf
+ best_fit_transaction = None
+ for t in self.queued_transactions[file_path]:
+ if t.start_byte <= start_byte and t.end_byte >= end_byte:
+ if transaction_order is None or t.transaction_order == transaction_order:
+ smallest_difference = min(smallest_difference, abs(t.start_byte - start_byte) + abs(t.end_byte - end_byte))
+ if smallest_difference == 0:
+ return t
+ best_fit_transaction = t
+ return best_fit_transaction
+
def _get_conflicts(self, transaction: Transaction) -> list[Transaction]:
"""Returns all transactions that overlap with the given transaction"""
overlapping_transactions = []
diff --git a/src/codegen/sdk/core/codebase.py b/src/codegen/sdk/core/codebase.py
index 6b4c103aa..fc3e0557e 100644
--- a/src/codegen/sdk/core/codebase.py
+++ b/src/codegen/sdk/core/codebase.py
@@ -25,7 +25,7 @@
from codegen.configs.models.codebase import CodebaseConfig, PinkMode
from codegen.configs.models.secrets import SecretsConfig
from codegen.git.repo_operator.repo_operator import RepoOperator
-from codegen.git.schemas.enums import CheckoutResult, SetupOption
+from codegen.git.schemas.enums import CheckoutResult
from codegen.git.schemas.repo_config import RepoConfig
from codegen.git.utils.pr_review import CodegenPR
from codegen.sdk._proxy import proxy_property
@@ -1337,7 +1337,6 @@ def from_repo(
language: Literal["python", "typescript"] | ProgrammingLanguage | None = None,
config: CodebaseConfig | None = None,
secrets: SecretsConfig | None = None,
- setup_option: SetupOption | None = None,
full_history: bool = False,
) -> "Codebase":
"""Fetches a codebase from GitHub and returns a Codebase instance.
@@ -1382,6 +1381,11 @@ def from_repo(
else:
# Ensure the operator can handle remote operations
repo_operator = RepoOperator.create_from_commit(repo_path=repo_path, commit=commit, url=repo_url, full_name=repo_full_name, access_token=access_token)
+
+ if repo_operator is None:
+ logger.error("Failed to clone repository")
+ return None
+
logger.info("Clone completed successfully")
# Initialize and return codebase with proper context
@@ -1527,13 +1531,13 @@ def from_files(
logger.info("Codebase initialization complete")
return codebase
- def get_modified_symbols_in_pr(self, pr_id: int) -> tuple[str, dict[str, str], list[str]]:
+ def get_modified_symbols_in_pr(self, pr_id: int) -> tuple[str, dict[str, str], list[str], str]:
"""Get all modified symbols in a pull request"""
pr = self._op.get_pull_request(pr_id)
cg_pr = CodegenPR(self._op, self, pr)
patch = cg_pr.get_pr_diff()
commit_sha = cg_pr.get_file_commit_shas()
- return patch, commit_sha, cg_pr.modified_symbols
+ return patch, commit_sha, cg_pr.modified_symbols, pr.head.ref
def create_pr_comment(self, pr_number: int, body: str) -> None:
"""Create a comment on a pull request"""
diff --git a/src/codegen/sdk/core/expressions/chained_attribute.py b/src/codegen/sdk/core/expressions/chained_attribute.py
index 04704fbbc..ccd5a788f 100644
--- a/src/codegen/sdk/core/expressions/chained_attribute.py
+++ b/src/codegen/sdk/core/expressions/chained_attribute.py
@@ -134,19 +134,16 @@ def object(self) -> Object:
@noapidoc
@override
def _resolved_types(self) -> Generator[ResolutionStack[Self], None, None]:
- from codegen.sdk.typescript.namespace import TSNamespace
-
if not self.ctx.config.method_usages:
return
if res := self.file.valid_import_names.get(self.full_name, None):
# Module imports
yield from self.with_resolution_frame(res)
return
- # HACK: This is a hack to skip the resolved types for namespaces
- if isinstance(self.object, TSNamespace):
- return
+
for resolved_type in self.object.resolved_type_frames:
top = resolved_type.top
+
if not isinstance(top.node, HasAttribute):
generics: dict = resolved_type.generics.copy()
if top.node.source.lower() == "dict" and self.attribute.source in ("values", "get", "pop"):
diff --git a/src/codegen/sdk/core/file.py b/src/codegen/sdk/core/file.py
index 12bcab303..e5af34ef9 100644
--- a/src/codegen/sdk/core/file.py
+++ b/src/codegen/sdk/core/file.py
@@ -943,6 +943,13 @@ def remove_unused_exports(self) -> None:
None
"""
+ def remove_unused_imports(self) -> None:
+ # Process each import statement
+ for import_stmt in self.imports:
+ # Don't remove imports we can't be sure about
+ if import_stmt.usage_is_ascertainable():
+ import_stmt.remove_if_unused()
+
####################################################################################################################
# MANIPULATIONS
####################################################################################################################
diff --git a/src/codegen/sdk/core/import_resolution.py b/src/codegen/sdk/core/import_resolution.py
index c6d11af9d..1fb17df50 100644
--- a/src/codegen/sdk/core/import_resolution.py
+++ b/src/codegen/sdk/core/import_resolution.py
@@ -5,7 +5,6 @@
from typing import TYPE_CHECKING, ClassVar, Generic, Literal, Self, TypeVar, override
from codegen.sdk.codebase.resolution_stack import ResolutionStack
-from codegen.sdk.codebase.transactions import TransactionPriority
from codegen.sdk.core.autocommit import commiter, reader, remover, writer
from codegen.sdk.core.dataclasses.usage import UsageKind
from codegen.sdk.core.expressions.name import Name
@@ -221,6 +220,17 @@ def is_symbol_import(self) -> bool:
"""
return not self.is_module_import()
+ @reader
+ def usage_is_ascertainable(self) -> bool:
+ """Returns True if we can determine for sure whether the import is unused or not.
+
+ Returns:
+ bool: True if the usage can be ascertained for the import, False otherwise.
+ """
+ if self.is_wildcard_import() or self.is_sideffect_import():
+ return False
+ return True
+
@reader
def is_wildcard_import(self) -> bool:
"""Returns True if the import symbol is a wildcard import.
@@ -234,6 +244,16 @@ def is_wildcard_import(self) -> bool:
"""
return self.import_type == ImportType.WILDCARD
+ @reader
+ def is_sideffect_import(self) -> bool:
+ # Maybe better name for this
+ """Determines if this is a sideffect.
+
+ Returns:
+ bool: True if this is a sideffect import, False otherwise
+ """
+ return self.import_type == ImportType.SIDE_EFFECT
+
@property
@abstractmethod
def namespace(self) -> str | None:
@@ -661,12 +681,21 @@ def __eq__(self, other: object):
@noapidoc
@reader
- def remove_if_unused(self) -> None:
- if all(
- self.transaction_manager.get_transactions_at_range(self.filepath, start_byte=usage.match.start_byte, end_byte=usage.match.end_byte, transaction_order=TransactionPriority.Remove)
- for usage in self.usages
- ):
+ def remove_if_unused(self, force: bool = False) -> bool:
+ """Removes import if it is not being used. Considers current transaction removals.
+
+ Args:
+ force (bool, optional): If true removes the import even if we cannot ascertain the usage for sure. Defaults to False.
+
+ Returns:
+ bool: True if removed, False if not
+ """
+ if all(usage.match.get_transaction_if_pending_removal() for usage in self.usages):
+ if not force and not self.usage_is_ascertainable():
+ return False
self.remove()
+ return True
+ return False
@noapidoc
@reader
diff --git a/src/codegen/sdk/core/interfaces/editable.py b/src/codegen/sdk/core/interfaces/editable.py
index 86e08c844..0a8c6dd67 100644
--- a/src/codegen/sdk/core/interfaces/editable.py
+++ b/src/codegen/sdk/core/interfaces/editable.py
@@ -10,7 +10,7 @@
from rich.pretty import Pretty
from codegen.sdk.codebase.span import Span
-from codegen.sdk.codebase.transactions import EditTransaction, InsertTransaction, RemoveTransaction, TransactionPriority
+from codegen.sdk.codebase.transactions import EditTransaction, InsertTransaction, RemoveTransaction, Transaction, TransactionPriority
from codegen.sdk.core.autocommit import commiter, reader, remover, repr_func, writer
from codegen.sdk.core.placeholder.placeholder import Placeholder
from codegen.sdk.extensions.utils import get_all_identifiers
@@ -1156,6 +1156,15 @@ def parent_class(self) -> Class | None:
return self.parent_of_type(Class)
+ @noapidoc
+ def get_transaction_if_pending_removal(self) -> Transaction | None:
+ """Checks if this editable is being removed by some transaction and if so returns it.
+
+ Returns:
+ Transaction|None: The transaction removing the editable
+ """
+ return self.transaction_manager.get_transaction_containing_range(self.file.path, self.start_byte, self.end_byte, TransactionPriority.Remove)
+
def _get_ast_children(self) -> list[tuple[str | None, AST]]:
children = []
names = {}
diff --git a/src/codegen/sdk/core/symbol.py b/src/codegen/sdk/core/symbol.py
index cc0238b45..bce4a91e1 100644
--- a/src/codegen/sdk/core/symbol.py
+++ b/src/codegen/sdk/core/symbol.py
@@ -5,6 +5,7 @@
from rich.markup import escape
+from codegen.sdk.codebase.transactions import TransactionPriority
from codegen.sdk.core.autocommit import commiter, reader, writer
from codegen.sdk.core.dataclasses.usage import UsageKind, UsageType
from codegen.sdk.core.detached_symbols.argument import Argument
@@ -266,11 +267,38 @@ def insert_before(self, new_src: str, fix_indentation: bool = False, newline: bo
return first_node.insert_before(new_src, fix_indentation, newline, priority, dedupe)
return super().insert_before(new_src, fix_indentation, newline, priority, dedupe)
+ def _post_move_import_cleanup(self, encountered_symbols, strategy):
+ # =====[ Remove any imports that are no longer used ]=====
+ from codegen.sdk.core.import_resolution import Import
+
+ for dep in self.dependencies:
+ if strategy != "duplicate_dependencies":
+ other_usages = [usage.usage_symbol for usage in dep.usages if usage.usage_symbol not in encountered_symbols]
+ else:
+ other_usages = [usage.usage_symbol for usage in dep.usages]
+ if isinstance(dep, Import):
+ dep.remove_if_unused()
+
+ elif isinstance(dep, Symbol):
+ usages_in_file = [symb for symb in other_usages if symb.file == self.file and not symb.get_transaction_if_pending_removal()]
+ if dep.get_transaction_if_pending_removal():
+ if not usages_in_file and strategy != "add_back_edge":
+ # We are going to assume there is only one such import
+ if imp_list := [import_str for import_str in self.file._pending_imports if dep.name and dep.name in import_str]:
+ if insert_import_list := [
+ transaction
+ for transaction in self.transaction_manager.queued_transactions[self.file.path]
+ if imp_list[0] and transaction.new_content and imp_list[0] in transaction.new_content and transaction.transaction_order == TransactionPriority.Insert
+ ]:
+ self.transaction_manager.queued_transactions[self.file.path].remove(insert_import_list[0])
+ self.file._pending_imports.remove(imp_list[0])
+
def move_to_file(
self,
file: SourceFile,
include_dependencies: bool = True,
strategy: Literal["add_back_edge", "update_all_imports", "duplicate_dependencies"] = "update_all_imports",
+ cleanup_unused_imports: bool = True,
) -> None:
"""Moves the given symbol to a new file and updates its imports and references.
@@ -290,7 +318,7 @@ def move_to_file(
AssertionError: If an invalid strategy is provided.
"""
encountered_symbols = {self}
- self._move_to_file(file, encountered_symbols, include_dependencies, strategy)
+ self._move_to_file(file, encountered_symbols, include_dependencies, strategy, cleanup_unused_imports)
@noapidoc
def _move_to_file(
@@ -299,6 +327,7 @@ def _move_to_file(
encountered_symbols: set[Symbol | Import],
include_dependencies: bool = True,
strategy: Literal["add_back_edge", "update_all_imports", "duplicate_dependencies"] = "update_all_imports",
+ cleanup_unused_imports: bool = True,
) -> tuple[NodeId, NodeId]:
"""Helper recursive function for `move_to_file`"""
from codegen.sdk.core.import_resolution import Import
@@ -391,6 +420,9 @@ def _move_to_file(
# Delete the original symbol
self.remove()
+ if cleanup_unused_imports:
+ self._post_move_import_cleanup(encountered_symbols, strategy)
+
@property
@reader
@noapidoc
diff --git a/src/codegen/sdk/python/import_resolution.py b/src/codegen/sdk/python/import_resolution.py
index 5c2a1f640..bf8e1cf49 100644
--- a/src/codegen/sdk/python/import_resolution.py
+++ b/src/codegen/sdk/python/import_resolution.py
@@ -15,12 +15,12 @@
from tree_sitter import Node as TSNode
from codegen.sdk.codebase.codebase_context import CodebaseContext
+ from codegen.sdk.core.file import SourceFile
from codegen.sdk.core.interfaces.editable import Editable
from codegen.sdk.core.interfaces.exportable import Exportable
from codegen.sdk.core.node_id_factory import NodeId
from codegen.sdk.core.statements.import_statement import ImportStatement
from codegen.sdk.python.file import PyFile
- from src.codegen.sdk.core.file import SourceFile
logger = get_logger(__name__)
diff --git a/src/codegen/sdk/typescript/import_resolution.py b/src/codegen/sdk/typescript/import_resolution.py
index 387ff2b14..82b770a79 100644
--- a/src/codegen/sdk/typescript/import_resolution.py
+++ b/src/codegen/sdk/typescript/import_resolution.py
@@ -8,7 +8,7 @@
from codegen.sdk.core.expressions import Name
from codegen.sdk.core.import_resolution import Import, ImportResolution, WildcardImport
from codegen.sdk.core.interfaces.exportable import Exportable
-from codegen.sdk.enums import ImportType, NodeType
+from codegen.sdk.enums import ImportType, NodeType, SymbolType
from codegen.sdk.utils import find_all_descendants, find_first_ancestor, find_first_descendant
from codegen.shared.decorators.docs import noapidoc, ts_apidoc
@@ -24,6 +24,7 @@
from codegen.sdk.core.statements.import_statement import ImportStatement
from codegen.sdk.core.symbol import Symbol
from codegen.sdk.typescript.file import TSFile
+ from codegen.sdk.typescript.namespace import TSNamespace
from codegen.sdk.typescript.statements.import_statement import TSImportStatement
@@ -578,6 +579,48 @@ def names(self) -> Generator[tuple[str, Self | WildcardImport[Self]], None, None
return
yield from super().names
+ @property
+ def namespace_imports(self) -> list[TSNamespace]:
+ """Returns any namespace objects imported by this import statement.
+
+ For example:
+ import * as MyNS from './mymodule';
+
+ Returns:
+ List of namespace objects imported
+ """
+ if not self.is_namespace_import():
+ return []
+
+ from codegen.sdk.typescript.namespace import TSNamespace
+
+ resolved = self.resolved_symbol
+ if resolved is None or not isinstance(resolved, TSNamespace):
+ return []
+
+ return [resolved]
+
+ @property
+ def is_namespace_import(self) -> bool:
+ """Returns True if this import is importing a namespace.
+
+ Examples:
+ import { MathUtils } from './file1'; # True if MathUtils is a namespace
+ import * as AllUtils from './utils'; # True
+ """
+ # For wildcard imports with namespace alias
+ if self.import_type == ImportType.WILDCARD and self.namespace:
+ return True
+
+ # For named imports, check if any imported symbol is a namespace
+ if self.import_type == ImportType.NAMED_EXPORT:
+ for name, _ in self.names:
+ symbol = self.resolved_symbol
+ if symbol and symbol.symbol_type == SymbolType.Namespace:
+ return True
+
+ return False
+
@override
def set_import_module(self, new_module: str) -> None:
"""Sets the module of an import.
diff --git a/src/codegen/sdk/typescript/namespace.py b/src/codegen/sdk/typescript/namespace.py
index 4d1e3f7db..2442ce6da 100644
--- a/src/codegen/sdk/typescript/namespace.py
+++ b/src/codegen/sdk/typescript/namespace.py
@@ -1,11 +1,15 @@
from __future__ import annotations
-from typing import TYPE_CHECKING
+from typing import TYPE_CHECKING, override
from codegen.sdk.core.autocommit import commiter
+from codegen.sdk.core.autocommit.decorators import writer
+from codegen.sdk.core.export import Export
+from codegen.sdk.core.interfaces.has_attribute import HasAttribute
from codegen.sdk.core.interfaces.has_name import HasName
-from codegen.sdk.core.statements.symbol_statement import SymbolStatement
from codegen.sdk.enums import SymbolType
+from codegen.sdk.extensions.autocommit import reader
+from codegen.sdk.extensions.sort import sort_editables
from codegen.sdk.extensions.utils import cached_property
from codegen.sdk.typescript.class_definition import TSClass
from codegen.sdk.typescript.enum_definition import TSEnum
@@ -15,20 +19,29 @@
from codegen.sdk.typescript.symbol import TSSymbol
from codegen.sdk.typescript.type_alias import TSTypeAlias
from codegen.shared.decorators.docs import noapidoc, ts_apidoc
+from codegen.shared.logging.get_logger import get_logger
if TYPE_CHECKING:
+ from collections.abc import Sequence
+
from tree_sitter import Node as TSNode
from codegen.sdk.codebase.codebase_context import CodebaseContext
from codegen.sdk.core.dataclasses.usage import UsageKind
+ from codegen.sdk.core.interfaces.importable import Importable
from codegen.sdk.core.node_id_factory import NodeId
from codegen.sdk.core.statements.statement import Statement
from codegen.sdk.core.symbol import Symbol
from codegen.sdk.typescript.detached_symbols.code_block import TSCodeBlock
+ from codegen.sdk.typescript.export import TSExport
+ from codegen.sdk.typescript.import_resolution import TSImport
+
+
+logger = get_logger(__name__)
@ts_apidoc
-class TSNamespace(TSSymbol, TSHasBlock, HasName):
+class TSNamespace(TSSymbol, TSHasBlock, HasName, HasAttribute):
"""Representation of a namespace module in TypeScript.
Attributes:
@@ -55,8 +68,7 @@ def _compute_dependencies(self, usage_type: UsageKind | None = None, dest: HasNa
"""
# Use self as destination if none provided
dest = dest or self.self_dest
-
- # Compute dependencies from the namespace's code block
+ # Compute dependencies from namespace's code block
self.code_block._compute_dependencies(usage_type, dest)
@cached_property
@@ -64,37 +76,81 @@ def symbols(self) -> list[Symbol]:
"""Returns all symbols defined within this namespace, including nested ones."""
all_symbols = []
for stmt in self.code_block.statements:
- # Handle export statements
if stmt.ts_node_type == "export_statement":
for export in stmt.exports:
all_symbols.append(export.declared_symbol)
- # Handle direct symbols
- elif isinstance(stmt, SymbolStatement):
+ elif hasattr(stmt, "assignments"):
+ all_symbols.extend(stmt.assignments)
+ else:
all_symbols.append(stmt)
return all_symbols
- def get_symbol(self, name: str, recursive: bool = True) -> Symbol | None:
- """Get a symbol by name from this namespace.
+ def get_symbol(self, name: str, recursive: bool = True, get_private: bool = False) -> Symbol | None:
+ """Get an exported or private symbol by name from this namespace. Returns only exported symbols by default.
Args:
name: Name of the symbol to find
recursive: If True, also search in nested namespaces
+ get_private: If True, also search in private symbols
Returns:
Symbol | None: The found symbol, or None if not found
"""
# First check direct symbols in this namespace
for symbol in self.symbols:
+ # Handle TSAssignmentStatement case
+ if hasattr(symbol, "assignments"):
+ for assignment in symbol.assignments:
+ if assignment.name == name:
+ # If we are looking for private symbols then return it, else only return exported symbols
+ if get_private:
+ return assignment
+ elif assignment.is_exported:
+ return assignment
+
+ # Handle regular symbol case
if hasattr(symbol, "name") and symbol.name == name:
- return symbol
+ if get_private:
+ return symbol
+ elif symbol.is_exported:
+ return symbol
# If recursive and this is a namespace, check its symbols
if recursive and isinstance(symbol, TSNamespace):
- nested_symbol = symbol.get_symbol(name, recursive=True)
+ nested_symbol = symbol.get_symbol(name, recursive=True, get_private=get_private)
return nested_symbol
return None
+ @reader(cache=False)
+ @noapidoc
+ def get_nodes(self, *, sort_by_id: bool = False, sort: bool = True) -> Sequence[Importable]:
+ """Returns all nodes in the namespace, sorted by position in the namespace."""
+ file_nodes = self.file.get_nodes(sort_by_id=sort_by_id, sort=sort)
+ start_limit = self.start_byte
+ end_limit = self.end_byte
+ namespace_nodes = []
+ for file_node in file_nodes:
+ if file_node.start_byte > start_limit:
+ if file_node.end_byte < end_limit:
+ namespace_nodes.append(file_node)
+ else:
+ break
+ return namespace_nodes
+
+ @cached_property
+ @reader(cache=False)
+ def exports(self) -> list[TSExport]:
+ """Returns all Export symbols in the namespace.
+
+ Retrieves a list of all top-level export declarations in the current TypeScript namespace.
+
+ Returns:
+ list[TSExport]: A list of TSExport objects representing all top-level export declarations in the namespace.
+ """
+ # Filter to only get exports that are direct children of the namespace's code block
+ return sort_editables(filter(lambda node: isinstance(node, Export), self.get_nodes(sort=False)), by_id=True)
+
@cached_property
def functions(self) -> list[TSFunction]:
"""Get all functions defined in this namespace.
@@ -104,22 +160,13 @@ def functions(self) -> list[TSFunction]:
"""
return [symbol for symbol in self.symbols if isinstance(symbol, TSFunction)]
- def get_function(self, name: str, recursive: bool = True, use_full_name: bool = False) -> TSFunction | None:
+ def get_function(self, name: str, recursive: bool = True) -> TSFunction | None:
"""Get a function by name from this namespace.
Args:
- name: Name of the function to find (can be fully qualified like 'Outer.Inner.func')
+ name: Name of the function to find
recursive: If True, also search in nested namespaces
- use_full_name: If True, match against the full qualified name
-
- Returns:
- TSFunction | None: The found function, or None if not found
"""
- if use_full_name and "." in name:
- namespace_path, func_name = name.rsplit(".", 1)
- target_ns = self.get_namespace(namespace_path)
- return target_ns.get_function(func_name, recursive=False) if target_ns else None
-
symbol = self.get_symbol(name, recursive=recursive)
return symbol if isinstance(symbol, TSFunction) else None
@@ -206,3 +253,148 @@ def get_nested_namespaces(self) -> list[TSNamespace]:
nested.append(symbol)
nested.extend(symbol.get_nested_namespaces())
return nested
+
+ @writer
+ def add_symbol_from_source(self, source: str) -> None:
+ """Adds a symbol to a namespace from a string representation.
+
+ This method adds a new symbol definition to the namespace by appending its source code string. The symbol will be added
+ after existing symbols if present, otherwise at the beginning of the namespace.
+
+ Args:
+ source (str): String representation of the symbol to be added. This should be valid source code for
+ the file's programming language.
+
+ Returns:
+ None: The symbol is added directly to the namespace's content.
+ """
+ symbols = self.symbols
+ if len(symbols) > 0:
+ symbols[-1].insert_after("\n" + source, fix_indentation=True)
+ else:
+ self.insert_after("\n" + source)
+
+ @commiter
+ def add_symbol(self, symbol: TSSymbol, should_export: bool = True) -> TSSymbol | None:
+ """Adds a new symbol to the namespace, optionally exporting it if applicable. If the symbol already exists in the namespace, returns the existing symbol.
+
+ Args:
+ symbol: The symbol to add to the namespace (either a TSSymbol instance or source code string)
+ export: Whether to export the symbol. Defaults to True.
+
+ Returns:
+ TSSymbol | None: The existing symbol if it already exists in the file or None if it was added.
+ """
+ existing_symbol = self.get_symbol(symbol.name)
+ if existing_symbol is not None:
+ return existing_symbol
+
+ if not self.file.symbol_can_be_added(symbol):
+ msg = f"Symbol {symbol.name} cannot be added to this file type."
+ raise ValueError(msg)
+
+ source = symbol.source
+ if isinstance(symbol, TSFunction) and symbol.is_arrow:
+ raw_source = symbol._named_arrow_function.text.decode("utf-8")
+ else:
+ raw_source = symbol.ts_node.text.decode("utf-8")
+ if should_export and hasattr(symbol, "export") and (not symbol.is_exported or raw_source not in symbol.export.source):
+ source = source.replace(source, f"export {source}")
+ self.add_symbol_from_source(source)
+
+ @commiter
+ def remove_symbol(self, symbol_name: str) -> TSSymbol | None:
+ """Removes a symbol from the namespace by name.
+
+ Args:
+ symbol_name: Name of the symbol to remove
+
+ Returns:
+ The removed symbol if found, None otherwise
+ """
+ symbol = self.get_symbol(symbol_name)
+ if symbol:
+ # Remove from code block statements
+ for i, stmt in enumerate(self.code_block.statements):
+ if symbol.source == stmt.source:
+ logger.debug(f"stmt to be removed: {stmt}")
+ self.code_block.statements.pop(i)
+ return symbol
+ return None
+
+ @commiter
+ def rename_symbol(self, old_name: str, new_name: str) -> None:
+ """Renames a symbol within the namespace.
+
+ Args:
+ old_name: Current symbol name
+ new_name: New symbol name
+ """
+ symbol = self.get_symbol(old_name)
+ if symbol:
+ symbol.rename(new_name)
+
+ @commiter
+ @noapidoc
+ def export_symbol(self, name: str) -> None:
+ """Marks a symbol as exported in the namespace.
+
+ Args:
+ name: Name of symbol to export
+ """
+ symbol = self.get_symbol(name, get_private=True)
+ if not symbol or symbol.is_exported:
+ return
+
+ export_source = f"export {symbol.source}"
+ symbol.parent.edit(export_source)
+
+ @cached_property
+ @noapidoc
+ @reader(cache=True)
+ def valid_import_names(self) -> dict[str, TSSymbol | TSImport]:
+ """Returns set of valid import names for this namespace.
+
+ This includes all exported symbols plus the namespace name itself
+ for namespace imports.
+ """
+ valid_export_names = {}
+ valid_export_names[self.name] = self
+ for export in self.exports:
+ for name, dest in export.names:
+ valid_export_names[name] = dest
+ return valid_export_names
+
+ def resolve_import(self, import_name: str) -> Symbol | None:
+ """Resolves an import name to a symbol within this namespace.
+
+ Args:
+ import_name: Name to resolve
+
+ Returns:
+ Resolved symbol or None if not found
+ """
+ # First check direct symbols
+ for symbol in self.symbols:
+ if symbol.is_exported and symbol.name == import_name:
+ return symbol
+
+ # Then check nested namespaces
+ for nested in self.get_nested_namespaces():
+ resolved = nested.resolve_import(import_name)
+ if resolved is not None:
+ return resolved
+
+ return None
+
+ @override
+ def resolve_attribute(self, name: str) -> Symbol | None:
+ """Resolves an attribute access on the namespace.
+
+ Args:
+ name: Name of the attribute to resolve
+
+ Returns:
+ The resolved symbol or None if not found
+ """
+ return self.valid_import_names.get(name, None)
diff --git a/src/codegen/sdk/typescript/symbol.py b/src/codegen/sdk/typescript/symbol.py
index e3cc89828..903fd8806 100644
--- a/src/codegen/sdk/typescript/symbol.py
+++ b/src/codegen/sdk/typescript/symbol.py
@@ -261,12 +261,17 @@ def _move_to_file(
encountered_symbols: set[Symbol | Import],
include_dependencies: bool = True,
strategy: Literal["add_back_edge", "update_all_imports", "duplicate_dependencies"] = "update_all_imports",
+ cleanup_unused_imports: bool = True,
) -> tuple[NodeId, NodeId]:
# TODO: Prevent creation of import loops (!) - raise a ValueError and make the agent fix it
# =====[ Arg checking ]=====
if file == self.file:
return file.file_node_id, self.node_id
+ if imp := file.get_import(self.name):
+ encountered_symbols.add(imp)
+ imp.remove()
+
# =====[ Move over dependencies recursively ]=====
if include_dependencies:
try:
@@ -319,7 +324,12 @@ def _move_to_file(
# =====[ Make a new symbol in the new file ]=====
# This will update all edges etc.
- file.add_symbol(self)
+ should_export = False
+
+ if self.is_exported or [usage for usage in self.usages if usage.usage_symbol not in encountered_symbols and not usage.usage_symbol.get_transaction_if_pending_removal()]:
+ should_export = True
+
+ file.add_symbol(self, should_export=should_export)
import_line = self.get_import_string(module=file.import_module_name)
# =====[ Checks if symbol is used in original file ]=====
@@ -329,6 +339,7 @@ def _move_to_file(
# ======[ Strategy: Duplicate Dependencies ]=====
if strategy == "duplicate_dependencies":
# If not used in the original file. or if not imported from elsewhere, we can just remove the original symbol
+ is_used_in_file = any(usage.file == self.file and usage.node_type == NodeType.SYMBOL for usage in self.symbol_usages)
if not is_used_in_file and not any(usage.kind is UsageKind.IMPORTED and usage.usage_symbol not in encountered_symbols for usage in self.usages):
self.remove()
@@ -336,9 +347,10 @@ def _move_to_file(
# Here, we will add a "back edge" to the old file importing the self
elif strategy == "add_back_edge":
if is_used_in_file:
- self.file.add_import(import_line)
+ back_edge_line = import_line
if self.is_exported:
- self.file.add_import(f"export {{ {self.name} }}")
+ back_edge_line = back_edge_line.replace("import", "export")
+ self.file.add_import(back_edge_line)
elif self.is_exported:
module_name = file.name
self.file.add_import(f"export {{ {self.name} }} from '{module_name}'")
@@ -349,23 +361,26 @@ def _move_to_file(
# Update the imports in all the files which use this symbol to get it from the new file now
elif strategy == "update_all_imports":
for usage in self.usages:
- if isinstance(usage.usage_symbol, TSImport):
+ if isinstance(usage.usage_symbol, TSImport) and usage.usage_symbol.file != file:
# Add updated import
- if usage.usage_symbol.resolved_symbol is not None and usage.usage_symbol.resolved_symbol.node_type == NodeType.SYMBOL and usage.usage_symbol.resolved_symbol == self:
- usage.usage_symbol.file.add_import(import_line)
- usage.usage_symbol.remove()
+ usage.usage_symbol.file.add_import(import_line)
+ usage.usage_symbol.remove()
elif usage.usage_type == UsageType.CHAINED:
# Update all previous usages of import * to the new import name
if usage.match and "." + self.name in usage.match:
- if isinstance(usage.match, FunctionCall):
+ if isinstance(usage.match, FunctionCall) and self.name in usage.match.get_name():
usage.match.get_name().edit(self.name)
if isinstance(usage.match, ChainedAttribute):
usage.match.edit(self.name)
- usage.usage_symbol.file.add_import(import_line)
+ usage.usage_symbol.file.add_import(imp=import_line)
+
+ # Add the import to the original file
if is_used_in_file:
- self.file.add_import(import_line)
+ self.file.add_import(imp=import_line)
# Delete the original symbol
self.remove()
+ if cleanup_unused_imports:
+ self._post_move_import_cleanup(encountered_symbols, strategy)
def _convert_proptype_to_typescript(self, prop_type: Editable, param: Parameter | None, level: int) -> str:
"""Converts a PropType definition to its TypeScript equivalent."""
diff --git a/src/codegen/shared/logging/get_logger.py b/src/codegen/shared/logging/get_logger.py
index 823e5161e..57b5129b3 100644
--- a/src/codegen/shared/logging/get_logger.py
+++ b/src/codegen/shared/logging/get_logger.py
@@ -1,41 +1,76 @@
import logging
+import sys
import colorlog
-
-def get_logger(name: str, level: int = logging.INFO) -> logging.Logger:
- # Force configure the root logger with a NullHandler to prevent duplicate logs
- logging.basicConfig(handlers=[logging.NullHandler()], force=True)
-
- formatter = colorlog.ColoredFormatter(
- "%(white)s%(asctime)s - %(name)s - %(log_color)s%(levelname)s%(reset)s%(white)s - %(message_log_color)s%(message)s",
- log_colors={
+formatter = colorlog.ColoredFormatter(
+ "%(white)s%(asctime)s - %(name)s - %(log_color)s%(levelname)s%(reset)s%(white)s - %(message_log_color)s%(message)s",
+ log_colors={
+ "DEBUG": "white",
+ "INFO": "green",
+ "WARNING": "yellow",
+ "ERROR": "red",
+ "CRITICAL": "red,bg_white",
+ },
+ secondary_log_colors={
+ "message": {
"DEBUG": "cyan",
- "INFO": "green",
+ "INFO": "white",
"WARNING": "yellow",
"ERROR": "red",
"CRITICAL": "red,bg_white",
- },
- secondary_log_colors={
- "message": {
- "DEBUG": "cyan",
- "INFO": "blue",
- "WARNING": "yellow",
- "ERROR": "red",
- "CRITICAL": "red,bg_white",
- }
- },
- )
+ }
+ },
+)
+
+
+class StdOutFilter(logging.Filter):
+ def filter(self, record):
+ return record.levelno < logging.ERROR
+
+
+class StdErrFilter(logging.Filter):
+ def filter(self, record):
+ return record.levelno >= logging.ERROR
+
+
+# Create handlers
+stdout_handler = logging.StreamHandler(sys.stdout) # Logs to stdout
+stdout_handler.setFormatter(formatter)
+stdout_handler.addFilter(StdOutFilter())
+
+stderr_handler = logging.StreamHandler(sys.stderr) # Logs to stderr
+stderr_handler.setFormatter(formatter)
+stderr_handler.addFilter(StdErrFilter())
+
+
+def get_logger(name: str, level: int = logging.INFO) -> logging.Logger:
+ logger = _setup_logger(name, level)
+ _setup_exception_logging(logger)
+ return logger
+
+
+def _setup_logger(name: str, level: int = logging.INFO) -> logging.Logger:
+ # Force configure the root logger with a NullHandler to prevent duplicate logs
+ logging.basicConfig(handlers=[logging.NullHandler()], force=True)
logger = logging.getLogger(name)
if logger.hasHandlers():
for h in logger.handlers:
logger.removeHandler(h)
- handler = colorlog.StreamHandler()
- handler.setFormatter(formatter)
- logger.addHandler(handler)
+ logger.addHandler(stdout_handler)
+ logger.addHandler(stderr_handler)
+
# Ensure the logger propagates to the root logger
logger.propagate = True
# Set the level on the logger itself
logger.setLevel(level)
return logger
+
+
+def _setup_exception_logging(logger: logging.Logger) -> None:
+ def log_exception(exc_type, exc_value, exc_traceback):
+ logger.exception("Uncaught exception", exc_info=(exc_type, exc_value, exc_traceback))
+
+ # Set the log_exception function as the exception hook
+ sys.excepthook = log_exception
diff --git a/tests/integration/codegen/git/codebase/conftest.py b/tests/integration/codegen/git/codebase/conftest.py
index 4e163c87d..a8ab00e4f 100644
--- a/tests/integration/codegen/git/codebase/conftest.py
+++ b/tests/integration/codegen/git/codebase/conftest.py
@@ -2,12 +2,11 @@
import pytest
-from codegen.git.schemas.enums import SetupOption
from codegen.sdk.core.codebase import Codebase
@pytest.fixture
def codebase(tmpdir):
os.chdir(tmpdir)
- codebase = Codebase.from_repo(repo_full_name="codegen-sh/Kevin-s-Adventure-Game", tmp_dir=tmpdir, language="python", setup_option=SetupOption.PULL_OR_CLONE)
+ codebase = Codebase.from_repo(repo_full_name="codegen-sh/Kevin-s-Adventure-Game", tmp_dir=tmpdir, language="python")
yield codebase
diff --git a/tests/unit/codegen/agents/__init__.py b/tests/unit/codegen/agents/__init__.py
new file mode 100644
index 000000000..e84cdd741
--- /dev/null
+++ b/tests/unit/codegen/agents/__init__.py
@@ -0,0 +1 @@
+# Unit tests for codegen.agents package
diff --git a/tests/unit/codegen/agents/test_agent.py b/tests/unit/codegen/agents/test_agent.py
new file mode 100644
index 000000000..a41a9fea5
--- /dev/null
+++ b/tests/unit/codegen/agents/test_agent.py
@@ -0,0 +1,281 @@
+from unittest.mock import MagicMock, patch
+
+import pytest
+
+from codegen.agents.agent import Agent, AgentTask
+from codegen.agents.client.openapi_client.api.agents_api import AgentsApi
+from codegen.agents.client.openapi_client.configuration import Configuration
+from codegen.agents.client.openapi_client.models.agent_run_response import AgentRunResponse
+from codegen.agents.constants import CODEGEN_BASE_API_URL
+
+
+class TestAgentTask:
+ @pytest.fixture
+ def agent_run_response(self):
+ """Create a mock AgentRunResponse"""
+ mock_response = MagicMock(spec=AgentRunResponse)
+ mock_response.id = "123" # Keep as string as this is likely the format from API
+ mock_response.status = "running"
+ mock_response.result = None
+ mock_response.web_url = "https://example.com/run/123"
+ return mock_response
+
+ @pytest.fixture
+ def api_client(self):
+ """Create a mock ApiClient"""
+ mock_client = MagicMock() # Remove spec to allow dynamic attributes
+ mock_client.configuration = MagicMock() # Create configuration attribute
+ mock_client.configuration.access_token = "test-token"
+ return mock_client
+
+ @pytest.fixture
+ def mock_agents_api(self):
+ """Create a proper mock for the AgentsApi"""
+ # Create a proper mock with a get method
+ mock_api = MagicMock(spec=AgentsApi)
+ return mock_api
+
+ @pytest.fixture
+ def agent_task(self, agent_run_response, api_client, mock_agents_api):
+ """Create an AgentTask instance with mock dependencies"""
+ # Patch the AgentsApi constructor to return our mock
+ with patch("codegen.agents.agent.AgentsApi", return_value=mock_agents_api):
+ task = AgentTask(agent_run_response, api_client, org_id=42)
+ return task
+
+ def test_init(self, agent_task, agent_run_response, api_client, mock_agents_api):
+ """Test initialization of AgentTask"""
+ assert agent_task.id == "123"
+ assert agent_task.org_id == 42
+ assert agent_task.status == "running"
+ assert agent_task.result is None
+ assert agent_task.web_url == "https://example.com/run/123"
+ assert agent_task._api_client == api_client
+ assert agent_task._agents_api == mock_agents_api
+
+ def test_refresh_without_id(self, agent_task, mock_agents_api):
+ """Test refresh method when job ID is None"""
+ agent_task.id = None
+ # Should return early without making API call
+ agent_task.refresh()
+ mock_agents_api.get_agent_run_v1_organizations_org_id_agent_run_agent_run_id_get.assert_not_called()
+
+ def test_refresh_with_id(self, agent_task, mock_agents_api):
+ """Test refresh method updates job status"""
+ # Setup mock API response
+ mock_updated_response = {"status": "completed", "result": {"output": "Success!"}}
+ mock_agents_api.get_agent_run_v1_organizations_org_id_agent_run_agent_run_id_get.return_value = mock_updated_response
+
+ # Call refresh
+ agent_task.refresh()
+
+ # Verify API was called with correct params
+ mock_agents_api.get_agent_run_v1_organizations_org_id_agent_run_agent_run_id_get.assert_called_once_with(
+ agent_run_id=123, # Use string ID as stored in agent_task.id
+ org_id=42,
+ authorization="Bearer test-token",
+ )
+
+ # Verify status was updated
+ assert agent_task.status == "completed"
+ assert agent_task.result == {"output": "Success!"}
+
+ def test_refresh_with_dict_response(self, agent_task, mock_agents_api):
+ """Test refresh method when API returns dict instead of object"""
+ # Setup mock API response as dict
+ mock_updated_response = {"status": "failed", "result": {"error": "Something went wrong"}}
+ mock_agents_api.get_agent_run_v1_organizations_org_id_agent_run_agent_run_id_get.return_value = mock_updated_response
+
+ # Call refresh
+ agent_task.refresh()
+
+ # Verify status was updated
+ assert agent_task.status == "failed"
+ assert agent_task.result == {"error": "Something went wrong"}
+
+
+class TestAgent:
+ @pytest.fixture
+ def mock_api_client(self):
+ """Create a mock ApiClient"""
+ with patch("codegen.agents.agent.ApiClient") as mock_client_class:
+ mock_client = MagicMock() # Remove spec to allow dynamic attributes
+ mock_client.configuration = MagicMock() # Create configuration attribute
+ mock_client.configuration.access_token = "test-token"
+ mock_client_class.return_value = mock_client
+ yield mock_client
+
+ @pytest.fixture
+ def mock_agents_api(self):
+ """Create a mock AgentsApi"""
+ with patch("codegen.agents.agent.AgentsApi") as mock_api_class:
+ mock_api = MagicMock(spec=AgentsApi)
+ mock_api_class.return_value = mock_api
+ yield mock_api
+
+ @pytest.fixture
+ def agent(self, mock_api_client, mock_agents_api):
+ """Create an Agent instance with mock dependencies"""
+ with patch.object(Configuration, "__init__", return_value=None) as mock_config:
+ agent = Agent(token="test-token", org_id=42)
+ # Verify config initialization
+ mock_config.assert_called_once_with(host=CODEGEN_BASE_API_URL, access_token="test-token")
+ return agent
+
+ def test_init_with_explicit_org_id(self, mock_api_client, mock_agents_api):
+ """Test initialization with explicitly provided org_id"""
+ with patch.object(Configuration, "__init__", return_value=None):
+ agent = Agent(token="test-token", org_id=42)
+ assert agent.token == "test-token"
+ assert agent.org_id == 42
+ assert agent.api_client == mock_api_client
+ assert agent.agents_api == mock_agents_api
+ assert agent.current_job is None
+
+ def test_init_with_default_org_id(self, mock_api_client, mock_agents_api):
+ """Test initialization with default org_id"""
+ with patch.object(Configuration, "__init__", return_value=None):
+ with patch.dict("os.environ", {"CODEGEN_ORG_ID": "99"}):
+ agent = Agent(token="test-token")
+ assert agent.org_id == 99
+
+ def test_init_with_custom_base_url(self, mock_api_client):
+ """Test initialization with custom base URL"""
+ with patch.object(Configuration, "__init__", return_value=None) as mock_config:
+ custom_url = "https://custom-api.example.com"
+ agent = Agent(token="test-token", org_id=42, base_url=custom_url)
+ mock_config.assert_called_once_with(host=custom_url, access_token="test-token")
+
+ def test_run(self, agent, mock_agents_api):
+ """Test run method creates and returns job"""
+ # Setup mock API response
+ mock_run_response = MagicMock(spec=AgentRunResponse)
+ mock_run_response.id = "123"
+ mock_run_response.status = "running"
+ mock_run_response.result = None
+ mock_run_response.web_url = "https://example.com/run/123"
+ mock_agents_api.create_agent_run_v1_organizations_org_id_agent_run_post.return_value = mock_run_response
+
+ # Call run
+ job = agent.run("Test prompt")
+
+ # Verify API call
+ mock_agents_api.create_agent_run_v1_organizations_org_id_agent_run_post.assert_called_once()
+ call_args = mock_agents_api.create_agent_run_v1_organizations_org_id_agent_run_post.call_args
+ assert call_args[1]["org_id"] == 42
+ assert call_args[1]["authorization"] == "Bearer test-token"
+ assert call_args[1]["_headers"] == {"Content-Type": "application/json"}
+ assert call_args[1]["create_agent_run_input"].prompt == "Test prompt"
+
+ # Verify job
+ assert isinstance(job, AgentTask)
+ assert job.id == "123"
+ assert job.status == "running"
+ assert agent.current_job == job
+
+ def test_get_status_with_no_job(self, agent):
+ """Test get_status when no job has been run"""
+ assert agent.get_status() is None
+
+ def test_get_status_with_job(self, agent):
+ """Test get_status returns current job status"""
+ # Setup mock job
+ mock_job = MagicMock(spec=AgentTask)
+ mock_job.id = "123"
+ mock_job.status = "completed"
+ mock_job.result = {"output": "Success!"}
+ mock_job.web_url = "https://example.com/run/123"
+
+ agent.current_job = mock_job
+
+ # Call get_status
+ status = agent.get_status()
+
+ # Verify job was refreshed
+ mock_job.refresh.assert_called_once()
+
+ # Verify status
+ assert status == {"id": "123", "status": "completed", "result": {"output": "Success!"}, "web_url": "https://example.com/run/123"}
+
+
+# Integration-like tests
+class TestAgentIntegration:
+ @pytest.fixture
+ def mock_response(self):
+ """Create a mock response for API calls"""
+ mock_response = MagicMock() # Remove spec=AgentRunResponse
+ mock_response.id = 987
+ mock_response.status = "running"
+ mock_response.result = None
+ mock_response.web_url = "https://example.com/run/987"
+ return mock_response
+
+ @pytest.fixture
+ def mock_updated_response(self):
+ """Create a mock updated response for API calls"""
+ mock_updated = {"id": 987, "status": "completed", "result": {"output": "Task completed successfully"}, "web_url": "https://example.com/run/987"}
+
+ return mock_updated
+
+ def test_full_workflow(self, mock_response, mock_updated_response):
+ """Test a complete agent workflow from initialization to status check"""
+ with (
+ patch("codegen.agents.agent.ApiClient") as mock_api_client_class,
+ patch("codegen.agents.agent.AgentsApi") as mock_agents_api_class,
+ patch.object(Configuration, "__init__", return_value=None),
+ ):
+ # Setup mocks
+ mock_api_client = MagicMock() # Remove spec to allow dynamic attributes
+ mock_api_client.configuration = MagicMock() # Create configuration attribute
+ mock_api_client.configuration.access_token = "test-token"
+ mock_api_client_class.return_value = mock_api_client
+
+ # Setup agents API mock
+ mock_agents_api = MagicMock(spec=AgentsApi)
+ mock_agents_api.create_agent_run_v1_organizations_org_id_agent_run_post.return_value = mock_response
+ mock_agents_api_class.return_value = mock_agents_api
+
+ # We're patching the same class for both the Agent and AgentTask
+ mock_inner_agents_api = mock_agents_api
+ mock_inner_agents_api.get_agent_run_v1_organizations_org_id_agent_run_agent_run_id_get.return_value = mock_updated_response
+
+ # Initialize agent
+ agent = Agent(token="test-token", org_id=123)
+
+ # Run agent
+ job = agent.run("Execute this instruction")
+
+ # Verify job properties
+ assert job.id == 987
+ assert job.status == "running"
+ assert job.result is None
+
+ # Check status
+ status = agent.get_status()
+
+ # Verify API calls
+ mock_agents_api.get_agent_run_v1_organizations_org_id_agent_run_agent_run_id_get.assert_called_once_with(agent_run_id=987, org_id=123, authorization="Bearer test-token")
+
+ # Verify status
+ assert isinstance(status, dict)
+ assert status["id"] == 987
+ assert status["status"] == "completed"
+ assert status["result"] == {"output": "Task completed successfully"}
+ assert status["web_url"] == "https://example.com/run/987"
+
+ def test_exception_handling(self):
+ """Test handling of API exceptions during agent run"""
+ with patch("codegen.agents.agent.ApiClient"), patch("codegen.agents.agent.AgentsApi") as mock_agents_api_class, patch.object(Configuration, "__init__", return_value=None):
+ # Setup API to raise exception
+ mock_agents_api = MagicMock(spec=AgentsApi)
+ mock_agents_api.create_agent_run_v1_organizations_org_id_agent_run_post.side_effect = Exception("API Error")
+ mock_agents_api_class.return_value = mock_agents_api
+
+ # Initialize agent
+ agent = Agent(token="test-token", org_id=123)
+
+ # Run agent and expect exception
+ with pytest.raises(Exception) as excinfo:
+ agent.run("Execute this instruction")
+
+ assert "API Error" in str(excinfo.value)
diff --git a/tests/unit/codegen/agents/test_api_client.py b/tests/unit/codegen/agents/test_api_client.py
new file mode 100644
index 000000000..ff75e1261
--- /dev/null
+++ b/tests/unit/codegen/agents/test_api_client.py
@@ -0,0 +1,361 @@
+import datetime
+import decimal
+from enum import Enum
+from unittest.mock import MagicMock, patch
+
+import pytest
+from pydantic import SecretStr
+
+from codegen.agents.client.openapi_client.api_client import ApiClient
+from codegen.agents.client.openapi_client.api_response import ApiResponse
+from codegen.agents.client.openapi_client.configuration import Configuration
+from codegen.agents.client.openapi_client.exceptions import ApiException, ApiValueError
+
+
+class TestEnum(Enum):
+ VALUE1 = "value1"
+ VALUE2 = "value2"
+
+
+class TestModel:
+ def __init__(self, name: str, value: int):
+ self.name = name
+ self.value = value
+
+ def to_dict(self):
+ return {"name": self.name, "value": self.value}
+
+
+class TestApiClient:
+ @pytest.fixture
+ def api_client(self):
+ config = Configuration()
+ # Mock the RESTClientObject to avoid making actual HTTP requests
+ with patch("codegen.agents.client.openapi_client.rest.RESTClientObject") as mock_rest:
+ client = ApiClient(configuration=config)
+ # Return the client with mocked rest_client
+ yield client
+
+ def test_init_default_configuration(self):
+ """Test initialization with default configuration"""
+ with patch("codegen.agents.client.openapi_client.configuration.Configuration.get_default") as mock_get_default:
+ mock_config = MagicMock()
+ mock_get_default.return_value = mock_config
+ with patch("codegen.agents.client.openapi_client.rest.RESTClientObject"):
+ client = ApiClient()
+ assert client.configuration == mock_config
+ assert client.user_agent == "OpenAPI-Generator/1.0.0/python"
+
+ def test_user_agent(self, api_client):
+ """Test user agent getter and setter"""
+ api_client.user_agent = "TestAgent/1.0"
+ assert api_client.user_agent == "TestAgent/1.0"
+ assert api_client.default_headers["User-Agent"] == "TestAgent/1.0"
+
+ def test_set_default_header(self, api_client):
+ """Test setting default header"""
+ api_client.set_default_header("Custom-Header", "Custom-Value")
+ assert api_client.default_headers["Custom-Header"] == "Custom-Value"
+
+ def test_sanitize_for_serialization_none(self, api_client):
+ """Test sanitization of None value"""
+ assert api_client.sanitize_for_serialization(None) is None
+
+ def test_sanitize_for_serialization_enum(self, api_client):
+ """Test sanitization of Enum value"""
+ assert api_client.sanitize_for_serialization(TestEnum.VALUE1) == "value1"
+
+ def test_sanitize_for_serialization_secret_str(self, api_client):
+ """Test sanitization of SecretStr value"""
+ secret = SecretStr("secret_value")
+ assert api_client.sanitize_for_serialization(secret) == "secret_value"
+
+ def test_sanitize_for_serialization_primitive(self, api_client):
+ """Test sanitization of primitive values"""
+ assert api_client.sanitize_for_serialization("string") == "string"
+ assert api_client.sanitize_for_serialization(123) == 123
+ assert api_client.sanitize_for_serialization(True) == True
+ assert api_client.sanitize_for_serialization(b"bytes") == b"bytes"
+
+ def test_sanitize_for_serialization_list(self, api_client):
+ """Test sanitization of list values"""
+ data = [1, "string", None]
+ assert api_client.sanitize_for_serialization(data) == [1, "string", None]
+
+ def test_sanitize_for_serialization_tuple(self, api_client):
+ """Test sanitization of tuple values"""
+ data = (1, "string", None)
+ assert api_client.sanitize_for_serialization(data) == (1, "string", None)
+
+ def test_sanitize_for_serialization_datetime(self, api_client):
+ """Test sanitization of datetime values"""
+ dt = datetime.datetime(2022, 1, 1, 12, 0, 0, tzinfo=datetime.timezone.utc)
+ assert api_client.sanitize_for_serialization(dt) == "2022-01-01T12:00:00+00:00"
+
+ date = datetime.date(2022, 1, 1)
+ assert api_client.sanitize_for_serialization(date) == "2022-01-01"
+
+ def test_sanitize_for_serialization_decimal(self, api_client):
+ """Test sanitization of Decimal values"""
+ dec = decimal.Decimal("123.45")
+ assert api_client.sanitize_for_serialization(dec) == "123.45"
+
+ def test_sanitize_for_serialization_dict(self, api_client):
+ """Test sanitization of dict values"""
+ data = {"key1": "value1", "key2": 123, "key3": None}
+ assert api_client.sanitize_for_serialization(data) == data
+
+ def test_sanitize_for_serialization_model(self, api_client):
+ """Test sanitization of OpenAPI model"""
+ model = TestModel("test", 123)
+ assert api_client.sanitize_for_serialization(model) == {"name": "test", "value": 123}
+
+ def test_deserialize_primitive(self, api_client):
+ """Test deserialization of primitive values"""
+ # Testing through __deserialize method
+ assert api_client._ApiClient__deserialize_primitive("123", int) == 123
+ assert api_client._ApiClient__deserialize_primitive("true", bool) == True
+ assert api_client._ApiClient__deserialize_primitive("12.34", float) == 12.34
+
+ def test_deserialize_date(self, api_client):
+ """Test deserialization of date values"""
+ date_str = "2022-01-01"
+ result = api_client._ApiClient__deserialize_date(date_str)
+ assert isinstance(result, datetime.date)
+ assert result.year == 2022
+ assert result.month == 1
+ assert result.day == 1
+
+ def test_deserialize_datetime(self, api_client):
+ """Test deserialization of datetime values"""
+ dt_str = "2022-01-01T12:00:00Z"
+ result = api_client._ApiClient__deserialize_datetime(dt_str)
+ assert isinstance(result, datetime.datetime)
+ assert result.year == 2022
+ assert result.month == 1
+ assert result.day == 1
+ assert result.hour == 12
+ assert result.minute == 0
+ assert result.second == 0
+
+ def test_deserialize_enum(self, api_client):
+ """Test deserialization of enum values"""
+ assert api_client._ApiClient__deserialize_enum("value1", TestEnum) == TestEnum.VALUE1
+
+ # Test exception case
+ with pytest.raises(ApiException):
+ api_client._ApiClient__deserialize_enum("invalid", TestEnum)
+
+ def test_parameters_to_tuples(self, api_client):
+ """Test parameters_to_tuples method"""
+ # Test with dictionary
+ params = {"param1": "value1", "param2": "value2"}
+ result = api_client.parameters_to_tuples(params, None)
+ assert result == [("param1", "value1"), ("param2", "value2")]
+
+ # Test with list of tuples
+ params = [("param1", "value1"), ("param2", "value2")]
+ result = api_client.parameters_to_tuples(params, None)
+ assert result == params
+
+ # Test with collection format
+ params = {"param1": ["value1", "value2", "value3"]}
+ collection_formats = {"param1": "csv"}
+ result = api_client.parameters_to_tuples(params, collection_formats)
+ assert result == [("param1", "value1,value2,value3")]
+
+ # Test with 'multi' collection format
+ params = {"param1": ["value1", "value2", "value3"]}
+ collection_formats = {"param1": "multi"}
+ result = api_client.parameters_to_tuples(params, collection_formats)
+ assert result == [("param1", "value1"), ("param1", "value2"), ("param1", "value3")]
+
+ def test_parameters_to_url_query(self, api_client):
+ """Test parameters_to_url_query method"""
+ # Test basic parameters
+ params = {"param1": "value1", "param2": "value2"}
+ result = api_client.parameters_to_url_query(params, None)
+ assert result == "param1=value1¶m2=value2"
+
+ # Test with boolean values
+ params = {"param1": True, "param2": False}
+ result = api_client.parameters_to_url_query(params, None)
+ assert result == "param1=true¶m2=false"
+
+ # Test with numeric values
+ params = {"param1": 123, "param2": 45.67}
+ result = api_client.parameters_to_url_query(params, None)
+ assert result == "param1=123¶m2=45.67"
+
+ # Test with dict values (should be JSON serialized)
+ params = {"param1": {"key": "value"}}
+ result = api_client.parameters_to_url_query(params, None)
+ assert result == "param1=%7B%22key%22%3A%20%22value%22%7D"
+
+ # Test with 'multi' collection format
+ params = {"param1": ["value1", "value2", "value3"]}
+ collection_formats = {"param1": "multi"}
+ result = api_client.parameters_to_url_query(params, collection_formats)
+ assert result == "param1=value1¶m1=value2¶m1=value3"
+
+ def test_select_header_accept(self, api_client):
+ """Test select_header_accept method"""
+ # Test empty accepts
+ assert api_client.select_header_accept([]) is None
+
+ # Test with JSON in accepts
+ accepts = ["application/xml", "application/json", "text/plain"]
+ assert api_client.select_header_accept(accepts) == "application/json"
+
+ # Test without JSON in accepts
+ accepts = ["application/xml", "text/plain"]
+ assert api_client.select_header_accept(accepts) == "application/xml"
+
+ def test_select_header_content_type(self, api_client):
+ """Test select_header_content_type method"""
+ # Test empty content types
+ assert api_client.select_header_content_type([]) is None
+
+ # Test with JSON in content types
+ content_types = ["application/xml", "application/json", "text/plain"]
+ assert api_client.select_header_content_type(content_types) == "application/json"
+
+ # Test without JSON in content types
+ content_types = ["application/xml", "text/plain"]
+ assert api_client.select_header_content_type(content_types) == "application/xml"
+
+ def test_update_params_for_auth(self, api_client):
+ """Test update_params_for_auth method"""
+ # Setup mock configuration
+ api_client.configuration = MagicMock()
+ api_client.configuration.auth_settings.return_value = {
+ "api_key": {"in": "header", "key": "X-API-KEY", "value": "test-api-key", "type": "apiKey"},
+ "query_param": {"in": "query", "key": "api_key", "value": "test-query-key", "type": "apiKey"},
+ "cookie_auth": {"in": "cookie", "key": "session", "value": "test-cookie", "type": "apiKey"},
+ }
+
+ # Test authentication in header
+ headers = {}
+ queries = []
+ api_client.update_params_for_auth(headers, queries, ["api_key"], "", "", None)
+ assert headers == {"X-API-KEY": "test-api-key"}
+
+ # Test authentication in query
+ headers = {}
+ queries = []
+ api_client.update_params_for_auth(headers, queries, ["query_param"], "", "", None)
+ assert queries == [("api_key", "test-query-key")]
+
+ # Test authentication in cookie
+ headers = {}
+ queries = []
+ api_client.update_params_for_auth(headers, queries, ["cookie_auth"], "", "", None)
+ assert headers == {"Cookie": "test-cookie"}
+
+ # Test with request_auth override
+ headers = {}
+ queries = []
+ request_auth = {"in": "header", "key": "X-CUSTOM-KEY", "value": "custom-value", "type": "apiKey"}
+ api_client.update_params_for_auth(headers, queries, ["api_key"], "", "", None, request_auth)
+ assert headers == {"X-CUSTOM-KEY": "custom-value"}
+
+ # Test with invalid auth location
+ invalid_auth = {"in": "invalid", "key": "x-key", "value": "value", "type": "apiKey"}
+ with pytest.raises(ApiValueError):
+ api_client._apply_auth_params({}, [], "", "", None, invalid_auth)
+
+ def test_param_serialize(self, api_client):
+ """Test param_serialize method"""
+ with patch.object(api_client, "sanitize_for_serialization") as mock_sanitize, patch.object(api_client, "default_headers", {}): # Empty the default headers
+ # Set return values for sanitize_for_serialization
+ mock_sanitize.side_effect = lambda x: x
+
+ # Test with basic parameters
+ method = "GET"
+ resource_path = "/test/{id}"
+ path_params = {"id": "123"}
+ query_params = {"query": "value"}
+ header_params = {"header": "value"}
+ body = {"body": "content"}
+
+ result = api_client.param_serialize(method, resource_path, path_params, query_params, header_params, body, None, None, None, None, None)
+
+ # Verify result
+ assert isinstance(result, tuple)
+ assert result[0] == "GET" # method
+ assert "/test/123" in result[1] # url
+ assert "query=value" in result[1] # query params in url
+ assert "header" in result[2] # header_params contains 'header' key
+ assert result[2]["header"] == "value" # header_params has correct value
+ assert result[3] == {"body": "content"} # body
+
+ def test_call_api(self, api_client):
+ """Test call_api method"""
+ # Mock the rest_client.request method
+ api_client.rest_client.request = MagicMock()
+ mock_response = MagicMock()
+ api_client.rest_client.request.return_value = mock_response
+
+ # Call the method
+ response = api_client.call_api("GET", "https://api.example.com/test", {"header": "value"}, {"body": "content"}, [("param", "value")], 30)
+
+ # Verify the call to rest_client.request
+ api_client.rest_client.request.assert_called_once_with(
+ "GET", "https://api.example.com/test", headers={"header": "value"}, body={"body": "content"}, post_params=[("param", "value")], _request_timeout=30
+ )
+
+ # Verify the result
+ assert response == mock_response
+
+ # Test exception case
+ api_client.rest_client.request.side_effect = ApiException(400)
+ with pytest.raises(ApiException):
+ api_client.call_api("GET", "https://api.example.com/test")
+
+ def test_response_deserialize(self, api_client):
+ """Test response_deserialize method"""
+ # Mock RESTResponse
+ response_data = MagicMock()
+ response_data.status = 200
+ response_data.data = b'{"name": "test", "value": 123}'
+ response_data.getheader.return_value = "application/json"
+ response_data.getheaders.return_value = {"Content-Type": "application/json"}
+
+ # Create a mock response to return
+ mock_api_response = MagicMock(spec=ApiResponse)
+
+ # Mock deserialize method and ApiResponse constructor
+ with (
+ patch.object(api_client, "deserialize") as mock_deserialize,
+ patch("codegen.agents.client.openapi_client.api_client.ApiResponse", return_value=mock_api_response) as mock_api_response_class,
+ ):
+ mock_deserialize.return_value = {"name": "test", "value": 123}
+
+ # Test successful response deserialization
+ response_types_map = {"200": "TestModel"}
+ result = api_client.response_deserialize(response_data, response_types_map)
+
+ # Verify ApiResponse was called with correct params
+ mock_api_response_class.assert_called_once_with(status_code=200, data={"name": "test", "value": 123}, headers={"Content-Type": "application/json"}, raw_data=response_data.data)
+
+ # Verify the result
+ assert result == mock_api_response
+
+ def test_response_deserialize_error(self, api_client):
+ """Test response_deserialize method with error response"""
+ # Mock RESTResponse for error
+ response_data = MagicMock()
+ response_data.status = 400
+ response_data.data = b'{"error": "Bad Request"}'
+ response_data.getheader.return_value = "application/json"
+ response_data.getheaders.return_value = {"Content-Type": "application/json"}
+
+ # Mock methods
+ with patch.object(api_client, "deserialize") as mock_deserialize, patch("codegen.agents.client.openapi_client.exceptions.ApiException.from_response") as mock_exception:
+ mock_deserialize.return_value = {"error": "Bad Request"}
+ mock_exception.side_effect = ApiException(400)
+
+ # Test error response
+ response_types_map = {"400": "ErrorModel"}
+ with pytest.raises(ApiException):
+ api_client.response_deserialize(response_data, response_types_map)
diff --git a/tests/unit/codegen/agents/test_simple_agent.py b/tests/unit/codegen/agents/test_simple_agent.py
new file mode 100644
index 000000000..522093b87
--- /dev/null
+++ b/tests/unit/codegen/agents/test_simple_agent.py
@@ -0,0 +1,106 @@
+"""Simplified test for the Agent class focusing on public interfaces.
+This approach avoids the complexity of mocking internal implementations.
+"""
+
+from unittest.mock import MagicMock, patch
+
+import pytest
+
+from codegen.agents.agent import Agent
+from codegen.agents.constants import CODEGEN_BASE_API_URL
+
+
+class TestAgent:
+ """Test the public interface of the Agent class."""
+
+ @pytest.fixture
+ def mock_agents_api(self):
+ """Create a mock for the AgentsApi."""
+ mock_api = MagicMock()
+ # Set up response for create_agent_run
+ mock_create_response = MagicMock()
+ mock_create_response.id = 123
+ mock_create_response.status = "running"
+ mock_create_response.result = None
+ mock_create_response.web_url = "https://example.com/agent/123"
+
+ # Set up response for get_agent_run
+ mock_get_response = MagicMock()
+ mock_get_response.status = "completed"
+ mock_get_response.result = {"output": "Task completed successfully"}
+
+ # Configure the mock methods
+ mock_api.create_agent_run_v1_organizations_org_id_agent_run_post.return_value = mock_create_response
+ mock_api.get_agent_run_v1_organizations_org_id_agent_run_agent_run_id_get.return_value = mock_get_response
+
+ return mock_api
+
+ @pytest.fixture
+ def agent(self, mock_agents_api):
+ """Create an Agent with mocked dependencies."""
+ with patch("codegen.agents.agent.ApiClient"), patch("codegen.agents.agent.AgentsApi", return_value=mock_agents_api), patch("codegen.agents.agent.Configuration"):
+ agent = Agent(token="test-token", org_id=42)
+ return agent
+
+ def test_initialization(self):
+ """Test Agent initialization with different parameters."""
+ # Test with explicit org_id
+ with patch("codegen.agents.agent.ApiClient"), patch("codegen.agents.agent.AgentsApi"), patch("codegen.agents.agent.Configuration") as mock_config:
+ agent = Agent(token="test-token", org_id=42)
+ assert agent.token == "test-token"
+ assert agent.org_id == 42
+ assert agent.current_job is None
+
+ # Verify Configuration was initialized correctly
+ mock_config.assert_called_once_with(host=CODEGEN_BASE_API_URL, access_token="test-token")
+
+ # Test with env var for org_id
+ with patch.dict("os.environ", {"CODEGEN_ORG_ID": "99"}):
+ agent = Agent(token="test-token")
+ assert agent.org_id == 99
+
+ # Test with custom base URL
+ custom_url = "https://custom-api.example.com"
+ agent = Agent(token="test-token", org_id=42, base_url=custom_url)
+ mock_config.assert_called_with(host=custom_url, access_token="test-token")
+
+ def test_run_agent(self, agent, mock_agents_api):
+ """Test running an agent with a prompt."""
+ # Run the agent
+ job = agent.run("Test prompt")
+
+ # Verify the API was called correctly
+ mock_agents_api.create_agent_run_v1_organizations_org_id_agent_run_post.assert_called_once()
+ call_args = mock_agents_api.create_agent_run_v1_organizations_org_id_agent_run_post.call_args[1]
+ assert call_args["org_id"] == 42
+ assert call_args["authorization"] == "Bearer test-token"
+ assert call_args["_headers"] == {"Content-Type": "application/json"}
+ assert call_args["create_agent_run_input"].prompt == "Test prompt"
+
+ # Verify the job properties
+ assert job.id == 123
+ assert job.status == "running"
+ assert job.result is None
+ assert job.web_url == "https://example.com/agent/123"
+ assert agent.current_job == job
+
+ def test_get_status_no_job(self, agent):
+ """Test get_status when no job has been run."""
+ assert agent.get_status() is None
+
+ def test_exception_handling(self):
+ """Test handling of API exceptions during agent run."""
+ with patch("codegen.agents.agent.ApiClient"), patch("codegen.agents.agent.AgentsApi") as mock_agents_api_class, patch("codegen.agents.agent.Configuration"):
+ # Setup API to raise exception
+ mock_agents_api = MagicMock()
+ mock_agents_api.create_agent_run_v1_organizations_org_id_agent_run_post.side_effect = Exception("API Error")
+ mock_agents_api_class.return_value = mock_agents_api
+
+ # Initialize agent
+ agent = Agent(token="test-token", org_id=123)
+
+ # Run agent and expect exception
+ with pytest.raises(Exception) as excinfo:
+ agent.run("Execute this instruction")
+
+ assert "API Error" in str(excinfo.value)
diff --git a/tests/unit/codegen/extensions/test_tools.py b/tests/unit/codegen/extensions/test_tools.py
index 0af57b89d..046acaf9e 100644
--- a/tests/unit/codegen/extensions/test_tools.py
+++ b/tests/unit/codegen/extensions/test_tools.py
@@ -225,14 +225,14 @@ def test_list_directory(codebase):
core_dir = next(d for d in src_dir.subdirectories if d.name == "core")
# Verify rendered output has proper tree structure
- rendered = result.render()
+ rendered = result.render(tool_call_id="test")
print(rendered)
expected_tree = """
└── src/
├── main.py
├── utils.py
└── core/"""
- assert expected_tree in rendered.strip()
+ assert expected_tree in rendered.content.strip()
def test_edit_file(codebase):
diff --git a/tests/unit/codegen/sdk/core/test_codebase.py b/tests/unit/codegen/sdk/core/test_codebase.py
index d8369f4c5..ee0cabcae 100644
--- a/tests/unit/codegen/sdk/core/test_codebase.py
+++ b/tests/unit/codegen/sdk/core/test_codebase.py
@@ -2,8 +2,10 @@
import pytest
+from codegen.configs.models.secrets import SecretsConfig
from codegen.sdk.codebase.codebase_context import CodebaseContext
from codegen.sdk.codebase.factory.get_session import get_codebase_session
+from codegen.sdk.core.codebase import Codebase
@pytest.fixture(autouse=True)
@@ -39,3 +41,9 @@ def test_codeowners_property(context_mock, codebase):
assert len(codebase.codeowners) == 1
assert callable(codebase.codeowners[0].files_source)
assert codebase.codeowners[0].files_source() == codebase.files.return_value
+
+
+def test_from_codebase_non_existent_repo(context_mock, tmpdir):
+ with get_codebase_session(tmpdir=tmpdir, files={"src/main.py": "print('Hello, world!')"}, verify_output=False) as codebase:
+ codebase = Codebase.from_repo("some-org/non-existent-repo", tmp_dir=tmpdir, secrets=SecretsConfig(github_token="some-token"))
+ assert codebase is None
diff --git a/tests/unit/codegen/sdk/python/file/test_file_remove_unused_import.py b/tests/unit/codegen/sdk/python/file/test_file_remove_unused_import.py
new file mode 100644
index 000000000..c36e8e52a
--- /dev/null
+++ b/tests/unit/codegen/sdk/python/file/test_file_remove_unused_import.py
@@ -0,0 +1,284 @@
+from codegen.sdk.codebase.factory.get_session import get_codebase_session
+
+
+def test_remove_unused_imports_basic(tmpdir) -> None:
+ """Test basic unused import removal"""
+ # language=python
+ content = """
+import os
+import sys
+from math import pi, sin
+import json as jsonlib
+
+print(os.getcwd())
+sin(pi)
+"""
+ with get_codebase_session(tmpdir=tmpdir, files={"test.py": content}) as codebase:
+ file = codebase.get_file("test.py")
+ file.remove_unused_imports()
+
+ assert "import sys" not in file.content
+ assert "import jsonlib" not in file.content
+ assert "import os" in file.content
+ assert "from math import pi, sin" in file.content
+
+
+def test_remove_unused_imports_multiline(tmpdir) -> None:
+ """Test removal of unused imports in multiline import statements"""
+ # language=python
+ content = """
+from my_module import (
+ used_func,
+ unused_func,
+ another_unused,
+ used_class,
+ unused_class
+)
+
+result = used_func()
+obj = used_class()
+"""
+ with get_codebase_session(tmpdir=tmpdir, files={"test.py": content}) as codebase:
+ file = codebase.get_file("test.py")
+ file.remove_unused_imports()
+
+ assert "unused_func" not in file.content
+ assert "another_unused" not in file.content
+ assert "unused_class" not in file.content
+ assert "used_func" in file.content
+ assert "used_class" in file.content
+
+
+def test_remove_unused_imports_with_aliases(tmpdir) -> None:
+ """Test removal of unused imports with aliases"""
+ # language=python
+ content = """
+from module import (
+ long_name as short,
+ unused as alias,
+ used_thing as ut
+)
+import pandas as pd
+import numpy as np
+
+print(short)
+result = ut.process()
+data = pd.DataFrame()
+"""
+ with get_codebase_session(tmpdir=tmpdir, files={"test.py": content}) as codebase:
+ file = codebase.get_file("test.py")
+ file.remove_unused_imports()
+
+ assert "unused as alias" not in file.content
+ assert "numpy as np" not in file.content
+ assert "long_name as short" in file.content
+ assert "used_thing as ut" in file.content
+ assert "pandas as pd" in file.content
+
+
+def test_remove_unused_imports_preserves_comments(tmpdir) -> None:
+ """Test that removing unused imports preserves relevant comments"""
+ # language=python
+ content = """
+# Important imports below
+import os # Used for OS operations
+import sys # Unused but commented
+from math import ( # Math utilities
+ pi, # Circle constant
+ e, # Unused constant
+ sin # Trig function
+)
+
+print(os.getcwd())
+print(sin(pi))
+"""
+ with get_codebase_session(tmpdir=tmpdir, files={"test.py": content}) as codebase:
+ file = codebase.get_file("test.py")
+ file.remove_unused_imports()
+
+ assert "# Important imports below" in file.content
+ assert "import os # Used for OS operations" in file.content
+ assert "import sys # Unused but commented" not in file.content
+ assert "e, # Unused constant" not in file.content
+ assert "pi, # Circle constant" in file.content
+ assert "sin # Trig function" in file.content
+
+
+def test_remove_unused_imports_relative_imports(tmpdir) -> None:
+ """Test handling of relative imports"""
+ # language=python
+ content = """
+from . import used_module
+from .. import unused_module
+from .subpackage import used_thing, unused_thing
+from ..utils import helper
+
+used_module.func()
+used_thing.process()
+"""
+ with get_codebase_session(tmpdir=tmpdir, files={"test.py": content}) as codebase:
+ file = codebase.get_file("test.py")
+ file.remove_unused_imports()
+
+ assert "from . import used_module" in file.content
+ assert "from .. import unused_module" not in file.content
+ assert "unused_thing" not in file.content
+ assert "from ..utils import helper" not in file.content
+ assert "used_thing" in file.content
+
+
+def test_remove_unused_imports_star_imports(tmpdir) -> None:
+ """Test handling of star imports (should not be removed as we can't track usage)"""
+ # language=python
+ content = """
+from os import *
+from sys import *
+from math import pi
+from math import sqrt
+
+getcwd() # from os
+print(pi)
+"""
+ with get_codebase_session(tmpdir=tmpdir, files={"test.py": content}) as codebase:
+ file = codebase.get_file("test.py")
+ file.remove_unused_imports()
+
+ assert "from os import *" in file.content
+ assert "from sys import *" in file.content
+ assert "from math import pi" in file.content
+
+
+def test_remove_unused_imports_type_hints(tmpdir) -> None:
+ """Test handling of imports used in type hints"""
+ # language=python
+ content = """
+from typing import List, Dict, Optional, Any
+from custom_types import CustomType, UnusedType
+
+def func(arg: List[int], opt: Optional[CustomType]) -> Dict[str, Any]:
+ return {"result": arg}
+"""
+ with get_codebase_session(tmpdir=tmpdir, files={"test.py": content}) as codebase:
+ file = codebase.get_file("test.py")
+ file.remove_unused_imports()
+
+ assert "List, Dict, Optional, Any" in file.content
+ assert "CustomType" in file.content
+ assert "UnusedType" not in file.content
+
+
+def test_remove_unused_imports_empty_file(tmpdir) -> None:
+ """Test handling of empty files"""
+ # language=python
+ content = """
+# Empty file with imports
+import os
+import sys
+"""
+ with get_codebase_session(tmpdir=tmpdir, files={"test.py": content}) as codebase:
+ file = codebase.get_file("test.py")
+ file.remove_unused_imports()
+
+ assert file.content.strip() == "# Empty file with imports"
+
+
+def test_remove_unused_imports_multiple_removals(tmpdir) -> None:
+ """Test multiple rounds of import removal"""
+ # language=python
+ content = """
+import os
+import sys
+import json
+
+def func():
+ print(os.getcwd())
+"""
+ with get_codebase_session(tmpdir=tmpdir, files={"test.py": content}) as codebase:
+ file = codebase.get_file("test.py")
+
+ # First removal
+ file.remove_unused_imports()
+ codebase.commit()
+ file = codebase.get_file("test.py")
+
+ assert "import sys" not in file.content
+ assert "import json" not in file.content
+ assert "import os" in file.content
+
+ # Second removal (should not change anything)
+ file.remove_unused_imports()
+ codebase.commit()
+ file = codebase.get_file("test.py")
+
+ assert "import sys" not in file.content
+ assert "import json" not in file.content
+ assert "import os" in file.content
+
+
+def test_file_complex_example_test_spliter(tmpdir) -> None:
+ """Test splitting a test file into multiple files, removing unused imports"""
+ # language=python
+ content = """
+from math import pi
+from math import sqrt
+
+def test_set_comparison():
+ set1 = set("1308")
+ set2 = set("8035")
+ assert set1 == set2
+
+def test_math_sqrt():
+ assert sqrt(4) == 2
+"""
+ with get_codebase_session(tmpdir=tmpdir, files={"test.py": content}) as codebase:
+ file = codebase.get_file("test.py")
+ base_name = "test_utils"
+
+ # Group tests by subpath
+ test_groups = {}
+ for test_function in file.functions:
+ if test_function.name.startswith("test_"):
+ test_subpath = "_".join(test_function.name.split("_")[:3])
+ if test_subpath not in test_groups:
+ test_groups[test_subpath] = []
+ test_groups[test_subpath].append(test_function)
+
+ # Print and process each group
+ for subpath, tests in test_groups.items():
+ new_filename = f"{base_name}/{subpath}.py"
+
+ # Create file if it doesn't exist
+ if not codebase.has_file(new_filename):
+ new_file = codebase.create_file(new_filename)
+ file = codebase.get_file(new_filename)
+
+ # Move each test in the group
+ for test_function in tests:
+ print(f"Moving function {test_function.name} to {new_filename}")
+ test_function.move_to_file(new_file, strategy="update_all_imports", include_dependencies=True)
+ original_file = codebase.get_file("test.py")
+
+ # Force a commit to ensure all changes are applied
+ codebase.commit()
+
+ # Verify the results
+ # Check that original test.py is empty of test functions
+ original_file = codebase.get_file("test.py", optional=True)
+ assert original_file is not None
+ assert len([f for f in original_file.functions if f.name.startswith("test_")]) == 0
+
+ # Verify test_set_comparison was moved correctly
+ set_comparison_file = codebase.get_file("test_utils/test_set_comparison.py", optional=True)
+ assert set_comparison_file is not None
+ assert "test_set_comparison" in set_comparison_file.content
+ assert 'set1 = set("1308")' in set_comparison_file.content
+
+ # Verify test_math_sqrt was moved correctly
+ math_file = codebase.get_file("test_utils/test_math_sqrt.py", optional=True)
+ assert math_file is not None
+ assert "test_math_sqrt" in math_file.content
+ assert "assert sqrt(4) == 2" in math_file.content
+
+ # Verify imports were preserved
+ assert "from math import sqrt" in math_file.content
+ assert "from math import pi" not in math_file.content # Unused import should be removed
diff --git a/tests/unit/codegen/sdk/python/file/test_file_unicode.py b/tests/unit/codegen/sdk/python/file/test_file_unicode.py
index af1c0e73a..0792c266e 100644
--- a/tests/unit/codegen/sdk/python/file/test_file_unicode.py
+++ b/tests/unit/codegen/sdk/python/file/test_file_unicode.py
@@ -39,7 +39,7 @@ def baz():
file3 = codebase.get_file("file3.py")
bar = file2.get_function("bar")
- bar.move_to_file(file3, include_dependencies=True, strategy="add_back_edge")
+ bar.move_to_file(file3, include_dependencies=True, strategy="add_back_edge", cleanup_unused_imports=False)
assert file1.content == content1
# language=python
diff --git a/tests/unit/codegen/sdk/python/function/test_function_move_to_file.py b/tests/unit/codegen/sdk/python/function/test_function_move_to_file.py
index 31dc17fa9..a4c29dcdc 100644
--- a/tests/unit/codegen/sdk/python/function/test_function_move_to_file.py
+++ b/tests/unit/codegen/sdk/python/function/test_function_move_to_file.py
@@ -46,8 +46,6 @@ def external_dep():
# language=python
EXPECTED_FILE_2_CONTENT = """
-from file1 import external_dep
-
def foo():
return foo_dep() + 1
@@ -68,7 +66,6 @@ def bar():
return external_dep() + bar_dep()
"""
# ===============================
- # TODO: [low] Should maybe remove unused external_dep?
# TODO: [low] Missing newline after import
with get_codebase_session(
@@ -91,6 +88,100 @@ def bar():
assert file3.content.strip() == EXPECTED_FILE_3_CONTENT.strip()
+def test_move_to_file_update_all_imports_multi_layer_usage(tmpdir) -> None:
+ # ========== [ BEFORE ] ==========
+ # language=python
+ FILE_1_CONTENT = """
+def external_dep():
+ return 42
+"""
+
+ # language=python
+ FILE_2_CONTENT = """
+from file1 import external_dep
+
+def foo():
+ return foo_dep_wrapped() + foo_dep()
+
+def foo_dep_wrapped():
+ return foo_dep()+2
+
+def foo_dep():
+ return 24
+
+def bar():
+ return external_dep() + bar_dep()
+
+def bar_dep():
+ return 2
+"""
+
+ # language=python
+ FILE_3_CONTENT = """
+from file2 import bar
+
+def baz():
+ return bar() + 1
+"""
+
+ # ========== [ AFTER ] ==========
+ # language=python
+ EXPECTED_FILE_1_CONTENT = """
+def external_dep():
+ return 42
+"""
+
+ # language=python
+ EXPECTED_FILE_2_CONTENT = """
+from file1 import external_dep
+
+def bar():
+ return external_dep() + bar_dep()
+
+def bar_dep():
+ return 2
+"""
+
+ # language=python
+ EXPECTED_FILE_3_CONTENT = """
+from file2 import bar
+
+def baz():
+ return bar() + 1
+
+def foo_dep():
+ return 24
+
+def foo_dep_wrapped():
+ return foo_dep()+2
+
+def foo():
+ return foo_dep_wrapped() + foo_dep()
+
+"""
+ # ===============================
+ # TODO: [low] Missing newline after import
+
+ with get_codebase_session(
+ tmpdir=tmpdir,
+ files={
+ "file1.py": FILE_1_CONTENT,
+ "file2.py": FILE_2_CONTENT,
+ "file3.py": FILE_3_CONTENT,
+ },
+ ) as codebase:
+ file1 = codebase.get_file("file1.py")
+ file2 = codebase.get_file("file2.py")
+ file3 = codebase.get_file("file3.py")
+
+ foo = file2.get_function("foo")
+ foo.move_to_file(file3, include_dependencies=True, strategy="update_all_imports")
+
+ assert file1.content.strip() == EXPECTED_FILE_1_CONTENT.strip()
+ assert file2.content.strip() == EXPECTED_FILE_2_CONTENT.strip()
+ assert file3.content.strip() == EXPECTED_FILE_3_CONTENT.strip()
+
+
def test_move_to_file_update_all_imports_include_dependencies(tmpdir) -> None:
# ========== [ BEFORE ] ==========
# language=python
@@ -279,7 +370,7 @@ def baz():
assert isinstance(new_symbol, Function)
-def test_move_to_file_add_back_edge(tmpdir) -> None:
+def test_move_to_file_add_back_edge_internal_use(tmpdir) -> None:
# ========== [ BEFORE ] ==========
# language=python
FILE_1_CONTENT = """
@@ -297,6 +388,9 @@ def foo():
def foo_dep():
return 24
+def use_bar():
+ return 1 + bar()
+
def bar():
return external_dep() + bar_dep()
@@ -321,8 +415,103 @@ def external_dep():
# language=python
EXPECTED_FILE_2_CONTENT = """
+from file3 import bar
+def foo():
+ return foo_dep() + 1
+
+def foo_dep():
+ return 24
+
+def use_bar():
+ return 1 + bar()
+
+"""
+
+ # language=python
+ EXPECTED_FILE_3_CONTENT = """
from file1 import external_dep
+def baz():
+ return bar() + 1
+
+def bar_dep():
+ return 2
+
+def bar():
+ return external_dep() + bar_dep()
+"""
+
+ # ===============================
+ # TODO: [low] Missing newline after import
+
+ with get_codebase_session(
+ tmpdir=tmpdir,
+ files={
+ "file1.py": FILE_1_CONTENT,
+ "file2.py": FILE_2_CONTENT,
+ "file3.py": FILE_3_CONTENT,
+ },
+ ) as codebase:
+ file1 = codebase.get_file("file1.py")
+ file2 = codebase.get_file("file2.py")
+ file3 = codebase.get_file("file3.py")
+
+ bar = file2.get_function("bar")
+ bar.move_to_file(file3, include_dependencies=True, strategy="add_back_edge")
+
+ assert file1.content.strip() == EXPECTED_FILE_1_CONTENT.strip()
+ assert file2.content.strip() == EXPECTED_FILE_2_CONTENT.strip()
+ assert file3.content.strip() == EXPECTED_FILE_3_CONTENT.strip()
+
+
+def test_move_to_file_add_back_edge_external_use(tmpdir) -> None:
+ # ========== [ BEFORE ] ==========
+ # language=python
+ FILE_1_CONTENT = """
+def external_dep():
+ return 42
+"""
+
+ # language=python
+ FILE_2_CONTENT = """
+from file1 import external_dep
+
+def foo():
+ return foo_dep() + 1
+
+def foo_dep():
+ return 24
+
+def bar():
+ return external_dep() + bar_dep()
+
+def bar_dep():
+ return 2
+"""
+
+ # language=python
+ FILE_3_CONTENT = """
+from file2 import bar
+
+def baz():
+ return bar() + 1
+"""
+ FILE_4_CONTENT = """
+from file2 import bar
+def bla():
+ return bar() + 1
+"""
+
+ # ========== [ AFTER ] ==========
+ # language=python
+ EXPECTED_FILE_1_CONTENT = """
+def external_dep():
+ return 42
+"""
+
+ # language=python
+ EXPECTED_FILE_2_CONTENT = """
+from file3 import bar
def foo():
return foo_dep() + 1
@@ -343,8 +532,14 @@ def bar():
return external_dep() + bar_dep()
"""
+ EXPECTED_FILE_4_CONTENT = """
+from file2 import bar
+
+def bla():
+ return bar() + 1
+ """
+
# ===============================
- # TODO: [low] Should maybe remove unused external_dep?
# TODO: [low] Missing newline after import
with get_codebase_session(
@@ -353,11 +548,13 @@ def bar():
"file1.py": FILE_1_CONTENT,
"file2.py": FILE_2_CONTENT,
"file3.py": FILE_3_CONTENT,
+ "file4.py": FILE_4_CONTENT,
},
) as codebase:
file1 = codebase.get_file("file1.py")
file2 = codebase.get_file("file2.py")
file3 = codebase.get_file("file3.py")
+ file4 = codebase.get_file("file4.py")
bar = file2.get_function("bar")
bar.move_to_file(file3, include_dependencies=True, strategy="add_back_edge")
@@ -365,6 +562,7 @@ def bar():
assert file1.content.strip() == EXPECTED_FILE_1_CONTENT.strip()
assert file2.content.strip() == EXPECTED_FILE_2_CONTENT.strip()
assert file3.content.strip() == EXPECTED_FILE_3_CONTENT.strip()
+ assert file4.content.strip() == EXPECTED_FILE_4_CONTENT.strip()
def test_move_to_file_add_back_edge_including_dependencies(tmpdir) -> None:
@@ -601,8 +799,6 @@ def external_dep():
# language=python
EXPECTED_FILE_2_CONTENT = """
-from file1 import external_dep
-
def foo():
return foo_dep() + 1
@@ -872,10 +1068,7 @@ def test_move_global_var(tmpdir) -> None:
"""
# language=python
- EXPECTED_FILE_2_CONTENT = """
-from import1 import thing1
-from import2 import thing2, thing3
-"""
+ EXPECTED_FILE_2_CONTENT = """"""
# ===============================
# TODO: [medium] Space messed up in file1
@@ -1311,8 +1504,6 @@ def bar(config: ExtendedConfig):
# ========== [ AFTER ] ==========
# language=python
EXPECTED_FILE_1_CONTENT = """
-from dataclasses import dataclass
-
def foo():
return 1
"""
@@ -1332,8 +1523,7 @@ class Config:
# language=python
EXPECTED_FILE_2_CONTENT = """
from file2.types import ExtendedConfig
-from file1.types import Config
-from dataclasses import dataclass
+
def bar(config: ExtendedConfig):
'''Function that uses the dataclass'''
@@ -1381,3 +1571,205 @@ class ExtendedConfig(Config):
assert file1_types.content.strip() == EXPECTED_FILE_1_TYPES_CONTENT.strip()
assert file2.content.strip() == EXPECTED_FILE_2_CONTENT.strip()
assert file2_types.content.strip() == EXPECTED_FILE_2_TYPES_CONTENT.strip()
+
+
+def test_move_to_file_decorators(tmpdir) -> None:
+ # ========== [ BEFORE ] ==========
+ # language=python
+ FILE_1_CONTENT = """
+from test.foo import TEST
+
+test_decorator = TEST()
+
+@test_decorator.foo()
+def test_func():
+ pass
+ """
+
+ FILE_2_CONTENT = ""
+ EXPECTED_FILE_1_CONTENT = ""
+
+ EXPECTED_FILE_2_CONTENT = """from test.foo import TEST
+
+
+test_decorator = TEST()
+
+@test_decorator.foo()
+def test_func():
+ pass"""
+
+ with get_codebase_session(
+ tmpdir=tmpdir,
+ files={
+ "file1.py": FILE_1_CONTENT,
+ "file2.py": FILE_2_CONTENT,
+ },
+ ) as codebase:
+ file1 = codebase.get_file("file1.py")
+ file2 = codebase.get_file("file2.py")
+
+ test_func = file1.get_function("test_func")
+ test_func.move_to_file(file2)
+
+ codebase.commit()
+ file1 = codebase.get_file("file1.py")
+ file2 = codebase.get_file("file2.py")
+
+ assert file1.source == EXPECTED_FILE_1_CONTENT
+ assert file2.source == EXPECTED_FILE_2_CONTENT
+
+
+def test_move_to_file_multiple_same_transaction(tmpdir) -> None:
+ # language=python
+ FILE_1_CONTENT = """
+from test.foo import TEST
+
+NO_MOVE=2
+def useful():
+ pass
+
+def test_func():
+ print(TEST)
+
+def foo():
+ test_func()
+ useful()
+
+def bar():
+ print(5)
+ useful()
+
+def boo():
+ print(6)
+ useful()
+"""
+
+ # language=python
+ FILE_2_CONTENT = "NO_MOVE_FILE_2 = 6"
+
+ FILE_1_EXPECTED = """
+NO_MOVE=2
+"""
+ FILE_2_EXPECTED = """
+from test.foo import TEST
+NO_MOVE_FILE_2 = 6
+
+def useful():
+ pass
+
+def test_func():
+ print(TEST)
+
+def foo():
+ test_func()
+ useful()
+
+def bar():
+ print(5)
+ useful()
+
+def boo():
+ print(6)
+ useful()
+"""
+
+ with get_codebase_session(
+ tmpdir=tmpdir,
+ files={
+ "file1.py": FILE_1_CONTENT,
+ "file2.py": FILE_2_CONTENT,
+ },
+ ) as codebase:
+ file1 = codebase.get_file("file1.py")
+ file2 = codebase.get_file("file2.py")
+
+ foo = file1.get_function("foo")
+ bar = file1.get_function("bar")
+ boo = file1.get_function("boo")
+ foo.move_to_file(file2)
+ bar.move_to_file(file2)
+ boo.move_to_file(file2)
+
+ codebase.commit()
+ file1 = codebase.get_file("file1.py")
+ file2 = codebase.get_file("file2.py")
+ assert file1.source.strip() == FILE_1_EXPECTED.strip()
+ assert file2.source.strip() == FILE_2_EXPECTED.strip()
+
+
+def test_move_to_file_multiple_same_transaction_partial(tmpdir) -> None:
+ # language=python
+ FILE_1_CONTENT = """
+from test.foo import TEST
+
+NO_MOVE=2
+def useful():
+ pass
+
+def test_func():
+ print(TEST)
+
+def foo():
+ test_func()
+ useful()
+
+def bar():
+ print(5)
+ useful()
+
+def boo():
+ print(6)
+ useful()
+"""
+
+ # language=python
+ FILE_2_CONTENT = "NO_MOVE_FILE_2 = 6"
+
+ FILE_1_EXPECTED = """
+from file2 import useful
+NO_MOVE=2
+
+def boo():
+ print(6)
+ useful()
+"""
+ FILE_2_EXPECTED = """
+from test.foo import TEST
+NO_MOVE_FILE_2 = 6
+
+def useful():
+ pass
+
+def test_func():
+ print(TEST)
+
+def foo():
+ test_func()
+ useful()
+
+def bar():
+ print(5)
+ useful()
+"""
+
+ with get_codebase_session(
+ tmpdir=tmpdir,
+ files={
+ "file1.py": FILE_1_CONTENT,
+ "file2.py": FILE_2_CONTENT,
+ },
+ ) as codebase:
+ file1 = codebase.get_file("file1.py")
+ file2 = codebase.get_file("file2.py")
+
+ foo = file1.get_function("foo")
+ bar = file1.get_function("bar")
+ boo = file1.get_function("boo")
+ foo.move_to_file(file2)
+ bar.move_to_file(file2)
+
+ codebase.commit()
+ file1 = codebase.get_file("file1.py")
+ file2 = codebase.get_file("file2.py")
+ assert file1.source.strip() == FILE_1_EXPECTED.strip()
+ assert file2.source.strip() == FILE_2_EXPECTED.strip()
diff --git a/tests/unit/codegen/sdk/typescript/file/test_file_remove.py b/tests/unit/codegen/sdk/typescript/file/test_file_remove.py
index cca4fabcd..39b8932ee 100644
--- a/tests/unit/codegen/sdk/typescript/file/test_file_remove.py
+++ b/tests/unit/codegen/sdk/typescript/file/test_file_remove.py
@@ -1,5 +1,7 @@
import os
+import pytest
+
from codegen.sdk.codebase.factory.get_session import get_codebase_session
from codegen.shared.enums.programming_language import ProgrammingLanguage
@@ -16,3 +18,197 @@ def tets_remove_existing_file(tmpdir) -> None:
file.remove()
assert not os.path.exists(file.filepath)
+
+
+def test_remove_unused_imports_complete_removal(tmpdir):
+ content = """
+ import { unused1, unused2 } from './module1';
+ import type { UnusedType } from './types';
+
+ const x = 5;
+ """
+ expected = """
+ const x = 5;
+ """
+
+ with get_codebase_session(tmpdir=tmpdir, programming_language=ProgrammingLanguage.TYPESCRIPT, files={"test.ts": content}) as codebase:
+ file = codebase.get_file("test.ts")
+ file.remove_unused_imports()
+ assert file.content.strip() == expected.strip()
+
+
+def test_remove_unused_imports_partial_removal(tmpdir):
+ content = """
+ import { used, unused } from './module1';
+
+ console.log(used);
+ """
+ expected = """
+ import { used } from './module1';
+
+ console.log(used);
+ """
+
+ with get_codebase_session(tmpdir=tmpdir, programming_language=ProgrammingLanguage.TYPESCRIPT, files={"test.ts": content}) as codebase:
+ file = codebase.get_file("test.ts")
+ file.remove_unused_imports()
+ assert file.content.strip() == expected.strip()
+
+
+def test_remove_unused_imports_with_side_effects(tmpdir):
+ content = """
+ import './styles.css';
+ import { unused } from './module1';
+
+ const x = 5;
+ """
+ expected = """
+ import './styles.css';
+
+ const x = 5;
+ """
+
+ with get_codebase_session(tmpdir=tmpdir, programming_language=ProgrammingLanguage.TYPESCRIPT, files={"test.ts": content}) as codebase:
+ file = codebase.get_file("test.ts")
+ file.remove_unused_imports()
+ assert file.content.strip() == expected.strip()
+
+
+def test_remove_unused_imports_with_moved_symbols(tmpdir):
+ content1 = """
+ import { helper } from './utils';
+
+ export function foo() {
+ return helper();
+ }
+ """
+ # The original file should be empty after move since foo was the only content
+ expected1 = ""
+
+ content2 = """
+ export function helper() {
+ return true;
+ }
+ """
+
+ with get_codebase_session(tmpdir=tmpdir, programming_language=ProgrammingLanguage.TYPESCRIPT, files={"main.ts": content1, "utils.ts": content2}) as codebase:
+ main_file = codebase.get_file("main.ts")
+ foo = main_file.get_function("foo")
+
+ # Move foo to a new file
+ new_file = codebase.create_file("new.ts")
+ foo.move_to_file(new_file, cleanup_unused_imports=False)
+ codebase.commit()
+ # Confirm cleanup false is respected
+ assert main_file.content.strip() == "import { helper } from './utils';"
+
+ # Now explicitly remove unused imports after the move
+ main_file.remove_unused_imports()
+ assert main_file.content.strip() == ""
+
+
+@pytest.mark.skip(reason="This test is not implemented properly yet")
+def test_remove_unused_exports_with_side_effects(tmpdir):
+ content = """
+import './styles.css';
+export const unused = 5;
+export function usedFunction() { return true; }
+
+const x = usedFunction();
+ """
+ expected = """
+import './styles.css';
+export function usedFunction() { return true; }
+
+const x = usedFunction();
+ """
+
+ with get_codebase_session(tmpdir=tmpdir, programming_language=ProgrammingLanguage.TYPESCRIPT, files={"test.ts": content}) as codebase:
+ file = codebase.get_file("test.ts")
+ file.remove_unused_exports()
+ assert file.content.strip() == expected.strip()
+
+
+@pytest.mark.skip(reason="This test is not implemented properly yet")
+def test_remove_unused_exports_with_multiple_types(tmpdir):
+ content = """
+export const UNUSED_CONSTANT = 42;
+export type UnusedType = string;
+export interface UnusedInterface {}
+export default function main() { return true; }
+export function usedFunction() { return true; }
+const x = usedFunction();
+ """
+ # Only value exports that are unused should be removed
+ expected = """
+export type UnusedType = string;
+export interface UnusedInterface {}
+export default function main() { return true; }
+export function usedFunction() { return true; }
+const x = usedFunction();
+ """
+
+ with get_codebase_session(tmpdir=tmpdir, programming_language=ProgrammingLanguage.TYPESCRIPT, files={"test.ts": content}) as codebase:
+ file = codebase.get_file("test.ts")
+ file.remove_unused_exports()
+ assert file.content.strip() == expected.strip()
+
+
+@pytest.mark.skip(reason="This test is not implemented properly yet")
+def test_remove_unused_exports_with_reexports(tmpdir):
+ content1 = """
+export { helper } from './utils';
+export { unused } from './other';
+export function localFunction() { return true; }
+ """
+ content2 = """
+import { helper } from './main';
+const x = helper();
+ """
+ expected1 = """
+export { helper } from './utils';
+export function localFunction() { return true; }
+ """
+
+ with get_codebase_session(tmpdir=tmpdir, programming_language=ProgrammingLanguage.TYPESCRIPT, files={"main.ts": content1, "other.ts": content2}) as codebase:
+ main_file = codebase.get_file("main.ts")
+ main_file.remove_unused_exports()
+ assert main_file.content.strip() == expected1.strip()
+
+
+def test_remove_unused_exports_with_moved_and_reexported_symbol(tmpdir):
+ content1 = """
+export function helper() {
+ return true;
+}
+ """
+ content2 = """
+import { helper } from './utils';
+export { helper }; // This re-export should be preserved as it's used
+
+const x = helper();
+ """
+ content3 = """
+import { helper } from './main';
+
+function useHelper() {
+ return helper();
+}
+ """
+
+ with get_codebase_session(tmpdir=tmpdir, programming_language=ProgrammingLanguage.TYPESCRIPT, files={"utils.ts": content1, "main.ts": content2, "consumer.ts": content3}) as codebase:
+ utils_file = codebase.get_file("utils.ts")
+ main_file = codebase.get_file("main.ts")
+ consumer_file = codebase.get_file("consumer.ts")
+ # Move helper to main.ts
+ helper = utils_file.get_function("helper")
+ helper.move_to_file(main_file)
+
+ # Remove unused exports
+ utils_file.remove_unused_exports()
+ main_file.remove_unused_exports()
+
+ # The re-export in main.ts should be preserved since it's used by consumer.ts
+ assert "export { helper }" in main_file.content
+ # The original export in utils.ts should be gone
+ assert "export function helper" not in utils_file.content
diff --git a/tests/unit/codegen/sdk/typescript/file/test_file_unicode.py b/tests/unit/codegen/sdk/typescript/file/test_file_unicode.py
index 8beab6133..9042477cf 100644
--- a/tests/unit/codegen/sdk/typescript/file/test_file_unicode.py
+++ b/tests/unit/codegen/sdk/typescript/file/test_file_unicode.py
@@ -47,7 +47,7 @@ def test_unicode_move_symbol(tmpdir) -> None:
file3 = codebase.get_file("file3.ts")
bar = file2.get_function("bar")
- bar.move_to_file(file3, include_dependencies=True, strategy="add_back_edge")
+ bar.move_to_file(file3, include_dependencies=True, strategy="add_back_edge", cleanup_unused_imports=False)
assert file1.content == content1
# language=typescript
@@ -72,13 +72,11 @@ def test_unicode_move_symbol(tmpdir) -> None:
file3.content
== """
import { externalDep } from 'file1';
-import { bar } from "./file2";
-
function baz(): string {
return bar() + "🤯" + 1;
}
-export function barDep(): string {
+function barDep(): string {
return "😀";
}
diff --git a/tests/unit/codegen/sdk/typescript/function/test_function_move_to_file.py b/tests/unit/codegen/sdk/typescript/function/test_function_move_to_file.py
index db1b87275..61d37530e 100644
--- a/tests/unit/codegen/sdk/typescript/function/test_function_move_to_file.py
+++ b/tests/unit/codegen/sdk/typescript/function/test_function_move_to_file.py
@@ -83,8 +83,6 @@ def test_move_to_file_update_all_imports(tmpdir) -> None:
# language=typescript
EXPECTED_FILE_2_CONTENT = """
-import { externalDep } from 'file1';
-
function foo() {
return fooDep() + 1;
}
@@ -97,12 +95,11 @@ def test_move_to_file_update_all_imports(tmpdir) -> None:
# language=typescript
EXPECTED_FILE_3_CONTENT = """
import { externalDep } from 'file1';
-import { bar } from 'file3';
export function baz() {
return bar() + 1;
}
-export function barDep() {
+function barDep() {
return 2;
}
@@ -112,8 +109,6 @@ def test_move_to_file_update_all_imports(tmpdir) -> None:
"""
# ===============================
- # TODO: [!HIGH!] Self import of bar in file3
- # TODO: [medium] Why is barDep exported?
# TODO: [low] Missing newline after import
with get_codebase_session(
@@ -181,7 +176,7 @@ def test_move_to_file_update_all_imports_include_dependencies(tmpdir) -> None:
return 1;
}
-export function abc(): string {
+function abc(): string {
// dependency, gets moved
return 'abc';
}
@@ -210,7 +205,6 @@ def test_move_to_file_update_all_imports_include_dependencies(tmpdir) -> None:
"""
# ===============================
- # TODO: [medium] Why is abc exported?
# TODO: [low] Missing newline after import
with get_codebase_session(
@@ -394,8 +388,6 @@ def test_move_to_file_add_back_edge(tmpdir) -> None:
# language=typescript
EXPECTED_FILE_2_CONTENT = """
export { bar } from 'file3'
-import { externalDep } from 'file1';
-
function foo() {
return fooDep() + 1;
}
@@ -408,13 +400,11 @@ def test_move_to_file_add_back_edge(tmpdir) -> None:
# language=typescript
EXPECTED_FILE_3_CONTENT = """
import { externalDep } from 'file1';
-import { bar } from 'file2';
-
export function baz() {
return bar() + 1;
}
-export function barDep() {
+function barDep() {
return 2;
}
@@ -424,9 +414,7 @@ def test_move_to_file_add_back_edge(tmpdir) -> None:
"""
# ===============================
- # TODO: [!HIGH!] Creates circular import for bar between file2 and file3
# TODO: [medium] Missing semicolon in import on file3
- # TODO: [medium] Why did barDep get changed to export?
with get_codebase_session(
tmpdir=tmpdir,
@@ -493,7 +481,7 @@ def test_move_to_file_add_back_edge_including_dependencies(tmpdir) -> None:
return 1;
}
-export function abc(): string {
+function abc(): string {
// dependency, gets moved
return 'abc';
}
@@ -526,7 +514,6 @@ def test_move_to_file_add_back_edge_including_dependencies(tmpdir) -> None:
# ===============================
# TODO: [medium] Missing semicolon in import on file2
- # TODO: [medium] Why is abc exported?
with get_codebase_session(
tmpdir=tmpdir,
@@ -711,8 +698,6 @@ def test_move_to_file_duplicate_dependencies(tmpdir) -> None:
# language=typescript
EXPECTED_FILE_2_CONTENT = """
-import { externalDep } from 'file1';
-
function foo() {
return fooDep() + 1;
}
@@ -721,21 +706,19 @@ def test_move_to_file_duplicate_dependencies(tmpdir) -> None:
return 24;
}
-export function bar() {
- return externalDep() + barDep();
+function barDep() {
+ return 2;
}
"""
# language=typescript
EXPECTED_FILE_3_CONTENT = """
import { externalDep } from 'file1';
-import { bar } from 'file2';
-
export function baz() {
return bar() + 1;
}
-export function barDep() {
+function barDep() {
return 2;
}
@@ -746,7 +729,6 @@ def test_move_to_file_duplicate_dependencies(tmpdir) -> None:
# ===============================
# TODO: [!HIGH!] Incorrect deletion of bar's import and dependency
- # TODO: [medium] Why is barDep exported?
with get_codebase_session(
tmpdir=tmpdir,
@@ -813,7 +795,7 @@ def test_move_to_file_duplicate_dependencies_include_dependencies(tmpdir) -> Non
return 1;
}
-export function abc(): string {
+function abc(): string {
// dependency, gets duplicated
return 'abc';
}
@@ -826,6 +808,11 @@ def test_move_to_file_duplicate_dependencies_include_dependencies(tmpdir) -> Non
# language=typescript
EXPECTED_FILE_2_CONTENT = """
+function abc(): string {
+ // dependency, gets duplicated
+ return 'abc';
+}
+
export function bar(): string {
// gets duplicated
return abc();
@@ -848,8 +835,6 @@ def test_move_to_file_duplicate_dependencies_include_dependencies(tmpdir) -> Non
"""
# ===============================
- # TODO: [!HIGH!] Incorrect deletion of bar's import and dependency
- # TODO: [medium] Why is abc exported?
# TODO: [low] Missing newline after import
with get_codebase_session(
@@ -1390,8 +1375,7 @@ def test_function_move_to_file_no_deps(tmpdir) -> None:
# ========== [ AFTER ] ==========
# language=typescript
EXPECTED_FILE_1_CONTENT = """
-import { foo } from 'File2';
-export { foo }
+export { foo } from 'File2';
export function bar(): number {
return foo() + 1;
@@ -1410,7 +1394,6 @@ def test_function_move_to_file_no_deps(tmpdir) -> None:
# ===============================
# TODO: [medium] Is the extra new lines here expected behavior?
# TODO: [low] Missing semicolons
- # TOOD: [low] Import and export should be changed to a re-export
with get_codebase_session(
tmpdir=tmpdir,
@@ -1447,8 +1430,7 @@ def test_function_move_to_file_lower_upper_no_deps(tmpdir) -> None:
# ========== [ AFTER ] ==========
# language=typescript
EXPECTED_FILE_1_CONTENT = """
-import { foo } from 'File1';
-export { foo }
+export { foo } from 'File1';
export function bar(): number {
return foo() + 1;
diff --git a/tests/unit/codegen/sdk/typescript/import_resolution/test_import_resolution_resolve_import.py b/tests/unit/codegen/sdk/typescript/import_resolution/test_import_resolution_resolve_import.py
index 5cbfcc7f6..e1ee905ab 100644
--- a/tests/unit/codegen/sdk/typescript/import_resolution/test_import_resolution_resolve_import.py
+++ b/tests/unit/codegen/sdk/typescript/import_resolution/test_import_resolution_resolve_import.py
@@ -834,3 +834,32 @@ def test_resolve_double_dynamic_import(tmpdir) -> None:
assert len(bar.call_sites) == 1
assert foo.call_sites[0].source == "myFile2.foo()"
assert bar.call_sites[0].source == "myFile3.bar()"
+
+
+def test_resolve_namespace_import(tmpdir) -> None:
+ # language=typescript
+ content = """
+import { CONSTS } from './file2'
+
+let use_a = CONSTS.a
+let use_b = CONSTS.b
+let use_c = CONSTS.c
+
+ """
+ # language=typescript
+ content2 = """
+export namespace CONSTS {
+ export const a = 2;
+ export const b = 3;
+ export const c = 4;
+}
+ """
+ with get_codebase_session(tmpdir=tmpdir, files={"file.ts": content, "file2.ts": content2}, programming_language=ProgrammingLanguage.TYPESCRIPT) as codebase:
+ file = codebase.get_file("file.ts")
+ file2 = codebase.get_file("file2.ts")
+ assert len(file.imports) == 1
+
+ consts = file2.get_namespace("CONSTS")
+
+ assert file.imports[0].resolved_symbol == consts
+ assert file.get_symbol("use_a").resolved_value == consts.get_symbol("a").resolved_value
diff --git a/tests/unit/codegen/sdk/typescript/move_symbol_to_file/test_move.py b/tests/unit/codegen/sdk/typescript/move_symbol_to_file/test_move.py
new file mode 100644
index 000000000..ee823144f
--- /dev/null
+++ b/tests/unit/codegen/sdk/typescript/move_symbol_to_file/test_move.py
@@ -0,0 +1,1750 @@
+import platform
+
+import pytest
+
+from codegen.sdk.codebase.factory.get_session import get_codebase_session
+from codegen.shared.enums.programming_language import ProgrammingLanguage
+
+
+class TestBasicMoveToFile:
+ """Test basic function move functionality without imports, using multiple strategies."""
+
+ def test_basic_move(self, tmpdir) -> None:
+ """Test basic function move without imports."""
+ # language=typescript
+ source_content = """
+ export function targetFunction() {
+ return "Hello World";
+ }
+ """
+
+ dest_filename = "destination.ts"
+ # language=typescript
+ dest_content = """
+ """
+
+ files = {
+ "source.ts": source_content,
+ dest_filename: dest_content,
+ }
+
+ with get_codebase_session(tmpdir=tmpdir, programming_language=ProgrammingLanguage.TYPESCRIPT, files=files) as codebase:
+ source_file = codebase.get_file("source.ts")
+ dest_file = codebase.get_file(dest_filename)
+ target_function = source_file.get_function("targetFunction")
+ target_function.move_to_file(dest_file, include_dependencies=False)
+
+ assert "targetFunction" not in source_file.content
+ assert "export function targetFunction" in dest_file.content
+
+ def test_update_all_imports_basic(self, tmpdir) -> None:
+ """Test update_all_imports strategy updates imports in all dependent files."""
+ # language=typescript
+ source_content = """
+ export function targetFunction() {
+ return "Hello World";
+ }
+ """
+
+ usage_content = """
+ import { targetFunction } from './source';
+ const value = targetFunction();
+ """
+
+ files = {
+ "source.ts": source_content,
+ "destination.ts": "",
+ "usage.ts": usage_content,
+ }
+
+ with get_codebase_session(tmpdir=tmpdir, programming_language=ProgrammingLanguage.TYPESCRIPT, files=files) as codebase:
+ source_file = codebase.get_file("source.ts")
+ dest_file = codebase.get_file("destination.ts")
+ usage_file = codebase.get_file("usage.ts")
+
+ target_function = source_file.get_function("targetFunction")
+ target_function.move_to_file(dest_file, include_dependencies=False, strategy="update_all_imports")
+
+ assert "targetFunction" not in source_file.content
+ assert "export function targetFunction" in dest_file.content
+ assert "import { targetFunction } from 'destination'" in usage_file.content
+
+ @pytest.mark.skip(reason="This test or related implementation needs work.")
+ def test_add_back_edge_basic(self, tmpdir) -> None:
+ """Test add_back_edge strategy - adds import in source file and re-exports the moved symbol."""
+ # language=typescript
+ source_content = """
+ export function targetFunction() {
+ return "Hello World";
+ }
+ """
+
+ files = {
+ "source.ts": source_content,
+ "destination.ts": "",
+ }
+
+ with get_codebase_session(tmpdir=tmpdir, programming_language=ProgrammingLanguage.TYPESCRIPT, files=files) as codebase:
+ source_file = codebase.get_file("source.ts")
+ dest_file = codebase.get_file("destination.ts")
+
+ target_function = source_file.get_function("targetFunction")
+ target_function.move_to_file(dest_file, include_dependencies=False, strategy="add_back_edge")
+
+ assert "import { targetFunction } from 'destination'" in source_file.content
+ assert "export { targetFunction }" in source_file.content
+ assert "export function targetFunction" in dest_file.content
+
+ def test_update_all_imports_with_dependencies(self, tmpdir) -> None:
+ """Test update_all_imports strategy with dependencies."""
+ # language=typescript
+ source_content = """
+ import { helperUtil } from './utils';
+
+ export function targetFunction() {
+ return helperUtil("test");
+ }
+ """
+
+ files = {
+ "source.ts": source_content,
+ "destination.ts": "",
+ }
+
+ with get_codebase_session(tmpdir=tmpdir, programming_language=ProgrammingLanguage.TYPESCRIPT, files=files) as codebase:
+ source_file = codebase.get_file("source.ts")
+ dest_file = codebase.get_file("destination.ts")
+
+ target_function = source_file.get_function("targetFunction")
+ target_function.move_to_file(dest_file, include_dependencies=True, strategy="update_all_imports")
+
+ assert "import { helperUtil } from './utils'" not in source_file.content
+ assert "import { helperUtil } from './utils'" in dest_file.content
+
+ @pytest.mark.skip(reason="This test or related implementation needs work.")
+ def test_add_back_edge_with_dependencies(self, tmpdir) -> None:
+ """Test add_back_edge strategy with dependencies."""
+ # language=typescript
+ source_content = """
+ import { helperUtil } from './utils';
+
+ export function targetFunction() {
+ return helperUtil("test");
+ }
+ """
+
+ files = {
+ "source.ts": source_content,
+ "destination.ts": "",
+ }
+
+ with get_codebase_session(tmpdir=tmpdir, programming_language=ProgrammingLanguage.TYPESCRIPT, files=files) as codebase:
+ source_file = codebase.get_file("source.ts")
+ dest_file = codebase.get_file("destination.ts")
+
+ target_function = source_file.get_function("targetFunction")
+ target_function.move_to_file(dest_file, include_dependencies=True, strategy="add_back_edge")
+
+ assert "import { targetFunction } from 'destination'" in source_file.content
+ assert "import { helperUtil } from './utils'" not in source_file.content
+ assert "import { helperUtil } from './utils'" in dest_file.content
+
+
+class TestMoveToFileImports:
+ """Test moving functions with various import scenarios."""
+
+ @pytest.mark.skip(reason="This test or related implementation needs work.")
+ def test_remove_unused_imports(self, tmpdir) -> None:
+ """Test that unused imports are removed when cleanup_unused_imports=True."""
+ # language=typescript
+ source_content = """
+ import { helperUtil } from './utils';
+ import { otherUtil } from './other';
+
+ export function targetFunction() {
+ return helperUtil("test");
+ }
+ """
+
+ files = {
+ "source.ts": source_content,
+ "destination.ts": "",
+ }
+
+ with get_codebase_session(tmpdir=tmpdir, programming_language=ProgrammingLanguage.TYPESCRIPT, files=files) as codebase:
+ source_file = codebase.get_file("source.ts")
+ dest_file = codebase.get_file("destination.ts")
+
+ target_function = source_file.get_function("targetFunction")
+ target_function.move_to_file(dest_file, include_dependencies=True, strategy="update_all_imports", cleanup_unused_imports=True)
+
+ # Unused import should be removed
+ assert "import { otherUtil } from './other'" not in source_file.content
+ # Used import should move to destination
+ assert "import { helperUtil } from './utils'" in dest_file.content
+
+ def test_keep_unused_imports(self, tmpdir) -> None:
+ """Test that unused imports are kept when cleanup_unused_imports=False."""
+ # language=typescript
+ source_content = """
+ import { helperUtil } from './utils';
+ import { otherUtil } from './other';
+
+ export function targetFunction() {
+ return helperUtil("test");
+ }
+ """
+
+ files = {
+ "source.ts": source_content,
+ "destination.ts": "",
+ }
+
+ with get_codebase_session(tmpdir=tmpdir, programming_language=ProgrammingLanguage.TYPESCRIPT, files=files) as codebase:
+ source_file = codebase.get_file("source.ts")
+ dest_file = codebase.get_file("destination.ts")
+
+ target_function = source_file.get_function("targetFunction")
+ target_function.move_to_file(dest_file, include_dependencies=True, strategy="update_all_imports", cleanup_unused_imports=False)
+
+ # All imports should be kept in source
+ assert "import { helperUtil } from './utils'" in source_file.content
+ assert "import { otherUtil } from './other'" in source_file.content
+ # Used import should also be in destination
+ assert "import { helperUtil } from './utils'" in dest_file.content
+
+ def test_used_imports_always_move(self, tmpdir) -> None:
+ """Test that used imports always move to destination regardless of remove_unused_imports flag."""
+ # language=typescript
+ source_content = """
+ import { helperUtil } from './utils';
+ import { otherUtil } from './other';
+
+ export function targetFunction() {
+ return helperUtil("test");
+ }
+ """
+
+ files = {
+ "source.ts": source_content,
+ "destination.ts": "",
+ }
+
+ for remove_unused in [True, False]:
+ with get_codebase_session(tmpdir=tmpdir, programming_language=ProgrammingLanguage.TYPESCRIPT, files=files) as codebase:
+ source_file = codebase.get_file("source.ts")
+ dest_file = codebase.get_file("destination.ts")
+
+ target_function = source_file.get_function("targetFunction")
+ target_function.move_to_file(dest_file, include_dependencies=True, strategy="update_all_imports", cleanup_unused_imports=remove_unused)
+
+ # Used import should always move to destination
+ assert "import { helperUtil } from './utils'" in dest_file.content
+
+
+class TestMoveToFileImportVariations:
+ """Test moving functions with various import scenarios."""
+
+ @pytest.mark.skip(reason="This test or related implementation needs work.")
+ def test_move_with_module_imports(self, tmpdir) -> None:
+ """Test moving a symbol that uses module imports (import * as)"""
+ # language=typescript
+ source_content = """
+ import * as utils from './utils';
+ import * as unused from './unused';
+
+ export function targetFunction() {
+ return utils.helperUtil("test");
+ }
+ """
+
+ dest_filename = "destination.ts"
+ # language=typescript
+ dest_content = """
+ """
+
+ files = {
+ "source.ts": source_content,
+ dest_filename: dest_content,
+ }
+
+ with get_codebase_session(tmpdir=tmpdir, programming_language=ProgrammingLanguage.TYPESCRIPT, files=files) as codebase:
+ source_file = codebase.get_file("source.ts")
+ dest_file = codebase.get_file(dest_filename)
+
+ target_function = source_file.get_function("targetFunction")
+ target_function.move_to_file(dest_file, include_dependencies=True, strategy="update_all_imports")
+
+ assert "import * as utils from './utils'" not in source_file.content
+ assert "import * as unused from './unused'" not in source_file.content
+ assert "import * as utils from './utils'" in dest_file.content
+
+ def test_move_with_side_effect_imports(self, tmpdir) -> None:
+ """Test moving a symbol that has side effect imports"""
+ # language=typescript
+ source_content = """
+ import './styles.css';
+ import './polyfills';
+ import { helperUtil } from './utils';
+
+ export function targetFunction() {
+ return helperUtil("test");
+ }
+ """
+
+ dest_filename = "destination.ts"
+ # language=typescript
+ dest_content = """
+ """
+
+ files = {
+ "source.ts": source_content,
+ dest_filename: dest_content,
+ }
+
+ with get_codebase_session(tmpdir=tmpdir, programming_language=ProgrammingLanguage.TYPESCRIPT, files=files) as codebase:
+ source_file = codebase.get_file("source.ts")
+ dest_file = codebase.get_file(dest_filename)
+
+ target_function = source_file.get_function("targetFunction")
+ target_function.move_to_file(dest_file, include_dependencies=True, strategy="update_all_imports")
+
+ # Side effect imports should remain in source
+ assert "import './styles.css';" in source_file.content
+ assert "import './polyfills';" in source_file.content
+ # Used import should move
+ assert "import { helperUtil } from './utils'" not in source_file.content
+ assert "import { helperUtil } from './utils'" in dest_file.content
+
+ def test_move_with_circular_dependencies(self, tmpdir) -> None:
+ """Test moving a symbol that has circular dependencies"""
+ # language=typescript
+ source_content = """
+ import { helperB } from './helper-b';
+
+ export function targetFunction() {
+ return helperB(innerHelper());
+ }
+
+ function innerHelper() {
+ return "inner";
+ }
+ """
+
+ # language=typescript
+ helper_b_content = """
+ import { targetFunction } from './source';
+
+ export function helperB(value: string) {
+ return targetFunction();
+ }
+ """
+
+ dest_filename = "destination.ts"
+ # language=typescript
+ dest_content = """
+ """
+
+ files = {
+ "source.ts": source_content,
+ dest_filename: dest_content,
+ "helper-b.ts": helper_b_content,
+ }
+
+ with get_codebase_session(tmpdir=tmpdir, programming_language=ProgrammingLanguage.TYPESCRIPT, files=files) as codebase:
+ source_file = codebase.get_file("source.ts")
+ dest_file = codebase.get_file(dest_filename)
+ helper_b_file = codebase.get_file("helper-b.ts")
+
+ target_function = source_file.get_function("targetFunction")
+ target_function.move_to_file(dest_file, include_dependencies=True, strategy="update_all_imports")
+
+ # Check circular dependency handling
+ assert "import { helperB } from './helper-b'" not in source_file.content
+ assert "import { helperB } from 'helper-b'" in dest_file.content
+ assert "import { targetFunction } from 'destination'" in helper_b_file.content
+
+ @pytest.mark.skip(reason="This test or related implementation needs work.")
+ def test_move_with_reexports(self, tmpdir) -> None:
+ """Test moving a symbol that is re-exported from multiple files"""
+ # language=typescript
+ source_content = """
+ export function targetFunction() {
+ return "test";
+ }
+ """
+
+ # language=typescript
+ reexport_a_content = """
+ export { targetFunction } from './source';
+ """
+
+ # language=typescript
+ reexport_b_content = """
+ export { targetFunction as renamedFunction } from './source';
+ """
+
+ dest_filename = "destination.ts"
+ # language=typescript
+ dest_content = """
+ """
+
+ files = {
+ "source.ts": source_content,
+ dest_filename: dest_content,
+ "reexport-a.ts": reexport_a_content,
+ "reexport-b.ts": reexport_b_content,
+ }
+
+ with get_codebase_session(tmpdir=tmpdir, programming_language=ProgrammingLanguage.TYPESCRIPT, files=files) as codebase:
+ source_file = codebase.get_file("source.ts")
+ dest_file = codebase.get_file(dest_filename)
+ reexport_a_file = codebase.get_file("reexport-a.ts")
+ reexport_b_file = codebase.get_file("reexport-b.ts")
+
+ target_function = source_file.get_function("targetFunction")
+ target_function.move_to_file(dest_file, include_dependencies=True, strategy="update_all_imports")
+
+ # Check re-export updates
+ assert "export { targetFunction } from './destination'" in reexport_a_file.content
+ assert "export { targetFunction as renamedFunction } from './destination'" in reexport_b_file.content
+
+
+class TestMoveToFileDecoratorsAndComments:
+ @pytest.mark.skip(reason="This test or related implementation needs work.")
+ def test_move_with_decorators(self, tmpdir) -> None:
+ """Test moving a symbol that has decorators"""
+ # language=typescript
+ source_content = """
+ import { injectable } from 'inversify';
+ import { validate } from './validators';
+
+ @injectable()
+ @validate()
+ export function targetFunction() {
+ return "test";
+ }
+ """
+
+ dest_filename = "destination.ts"
+ # language=typescript
+ dest_content = """
+ """
+
+ files = {
+ "source.ts": source_content,
+ dest_filename: dest_content,
+ }
+
+ with get_codebase_session(tmpdir=tmpdir, programming_language=ProgrammingLanguage.TYPESCRIPT, files=files) as codebase:
+ source_file = codebase.get_file("source.ts")
+ dest_file = codebase.get_file(dest_filename)
+
+ target_function = source_file.get_function("targetFunction")
+ target_function.move_to_file(dest_file, include_dependencies=True, strategy="update_all_imports")
+
+ assert "@injectable()" not in source_file.content
+ assert "@validate()" not in source_file.content
+ assert "@injectable()" in dest_file.content
+ assert "@validate()" in dest_file.content
+ assert "import { injectable } from 'inversify'" in dest_file.content
+ assert "import { validate } from './validators'" in dest_file.content
+
+ def test_move_with_jsdoc(self, tmpdir) -> None:
+ """Test moving a symbol with JSDoc comments"""
+ # language=typescript
+ source_content = """
+ import { SomeType } from './types';
+
+ /**
+ * @param {string} value - Input value
+ * @returns {SomeType} Processed result
+ */
+ export function targetFunction(value: string): SomeType {
+ return { value };
+ }
+ """
+
+ dest_filename = "destination.ts"
+ # language=typescript
+ dest_content = """
+ """
+
+ files = {
+ "source.ts": source_content,
+ dest_filename: dest_content,
+ }
+
+ with get_codebase_session(tmpdir=tmpdir, programming_language=ProgrammingLanguage.TYPESCRIPT, files=files) as codebase:
+ source_file = codebase.get_file("source.ts")
+ dest_file = codebase.get_file(dest_filename)
+
+ target_function = source_file.get_function("targetFunction")
+ target_function.move_to_file(dest_file, include_dependencies=True, strategy="update_all_imports")
+
+ assert "@param {string}" not in source_file.content
+ assert "@returns {SomeType}" not in source_file.content
+ assert "@param {string}" in dest_file.content
+ assert "@returns {SomeType}" in dest_file.content
+ assert "import { SomeType } from './types'" in dest_file.content
+
+
+class TestMoveToFileDynamicImports:
+ def test_move_with_dynamic_imports(self, tmpdir) -> None:
+ """Test moving a symbol that uses dynamic imports"""
+ # language=typescript
+ source_content = """
+ export async function targetFunction() {
+ const { helper } = await import('./helper');
+ const utils = await import('./utils');
+ return helper(utils.format("test"));
+ }
+ """
+
+ dest_filename = "destination.ts"
+ # language=typescript
+ dest_content = """
+ """
+
+ files = {
+ "source.ts": source_content,
+ dest_filename: dest_content,
+ }
+
+ with get_codebase_session(tmpdir=tmpdir, programming_language=ProgrammingLanguage.TYPESCRIPT, files=files) as codebase:
+ source_file = codebase.get_file("source.ts")
+ dest_file = codebase.get_file(dest_filename)
+
+ target_function = source_file.get_function("targetFunction")
+ target_function.move_to_file(dest_file, include_dependencies=True, strategy="update_all_imports")
+
+ assert "import('./helper')" not in source_file.content
+ assert "import('./utils')" not in source_file.content
+ assert "import('./helper')" in dest_file.content
+ assert "import('./utils')" in dest_file.content
+
+ def test_move_with_mixed_dynamic_static_imports(self, tmpdir) -> None:
+ """Test moving a symbol that uses both dynamic and static imports"""
+ # language=typescript
+ source_content = """
+ import { baseHelper } from './base';
+
+ export async function targetFunction() {
+ const { dynamicHelper } = await import('./dynamic');
+ return baseHelper(await dynamicHelper());
+ }
+ """
+
+ dest_filename = "destination.ts"
+ # language=typescript
+ dest_content = """
+ """
+
+ files = {
+ "source.ts": source_content,
+ dest_filename: dest_content,
+ }
+
+ with get_codebase_session(tmpdir=tmpdir, programming_language=ProgrammingLanguage.TYPESCRIPT, files=files) as codebase:
+ source_file = codebase.get_file("source.ts")
+ dest_file = codebase.get_file(dest_filename)
+
+ target_function = source_file.get_function("targetFunction")
+ target_function.move_to_file(dest_file, include_dependencies=True, strategy="update_all_imports")
+
+ assert "import { baseHelper }" not in source_file.content
+ assert "import('./dynamic')" not in source_file.content
+ assert "import { baseHelper }" in dest_file.content
+ assert "import('./dynamic')" in dest_file.content
+
+
+class TestMoveToFileNamedImports:
+ """Test moving functions with named imports."""
+
+ @pytest.mark.skip(reason="This test or related implementation needs work.")
+ def test_move_with_named_imports(self, tmpdir) -> None:
+ """Test moving a symbol that uses named imports."""
+ # language=typescript
+ source_content = """
+ import { foo, bar as alias, unused } from './module';
+
+ export function targetFunction() {
+ return foo(alias("test"));
+ }
+ """
+
+ dest_filename = "destination.ts"
+ # language=typescript
+ dest_content = """
+ """
+
+ files = {
+ "source.ts": source_content,
+ dest_filename: dest_content,
+ }
+
+ with get_codebase_session(tmpdir=tmpdir, programming_language=ProgrammingLanguage.TYPESCRIPT, files=files) as codebase:
+ source_file = codebase.get_file("source.ts")
+ dest_file = codebase.get_file(dest_filename)
+
+ target_function = source_file.get_function("targetFunction")
+ target_function.move_to_file(dest_file, include_dependencies=True, strategy="update_all_imports")
+
+ assert "import { foo, bar as alias" in dest_file.content
+ assert "unused" not in dest_file.content
+ assert "import { foo" not in source_file.content
+
+ @pytest.mark.skip(reason="This test or related implementation needs work.")
+ def test_move_with_default_and_named_imports(self, tmpdir) -> None:
+ """Test moving a symbol that uses both default and named imports."""
+ # language=typescript
+ source_content = """
+ import defaultHelper, { namedHelper, unusedHelper } from './helper';
+
+ export function targetFunction() {
+ return defaultHelper(namedHelper("test"));
+ }
+ """
+
+ dest_filename = "destination.ts"
+ # language=typescript
+ dest_content = """
+ """
+
+ files = {
+ "source.ts": source_content,
+ dest_filename: dest_content,
+ }
+
+ with get_codebase_session(tmpdir=tmpdir, programming_language=ProgrammingLanguage.TYPESCRIPT, files=files) as codebase:
+ source_file = codebase.get_file("source.ts")
+ dest_file = codebase.get_file(dest_filename)
+
+ target_function = source_file.get_function("targetFunction")
+ target_function.move_to_file(dest_file, include_dependencies=True, strategy="update_all_imports")
+
+ assert "import defaultHelper, { namedHelper }" in dest_file.content
+ assert "unusedHelper" not in dest_file.content
+ assert "defaultHelper" not in source_file.content
+
+
+class TestMoveToFileTypeImports:
+ """Test moving functions with type imports."""
+
+ @pytest.mark.skip(reason="This test or related implementation needs work.")
+ def test_move_with_type_imports(self, tmpdir) -> None:
+ """Test moving a symbol that uses type imports."""
+ # language=typescript
+ source_content = """
+ import type { Config } from './config';
+ import type DefaultType from './types';
+ import type { Used as Alias, Unused } from './utils';
+
+ export function targetFunction(config: Config, type: DefaultType): Alias {
+ return { value: config.value };
+ }
+ """
+
+ dest_filename = "destination.ts"
+ # language=typescript
+ dest_content = """
+ """
+
+ files = {
+ "source.ts": source_content,
+ dest_filename: dest_content,
+ }
+
+ with get_codebase_session(tmpdir=tmpdir, programming_language=ProgrammingLanguage.TYPESCRIPT, files=files) as codebase:
+ source_file = codebase.get_file("source.ts")
+ dest_file = codebase.get_file(dest_filename)
+
+ target_function = source_file.get_function("targetFunction")
+ target_function.move_to_file(dest_file, include_dependencies=True, strategy="update_all_imports")
+
+ # Check type imports are moved correctly
+ assert "import type { Config }" in dest_file.content
+ assert "import type DefaultType" in dest_file.content
+ assert "import type { Used as Alias }" in dest_file.content
+ assert "Unused" not in dest_file.content
+ # Check original file cleanup
+ assert "import type" not in source_file.content
+
+ @pytest.mark.skip(reason="This test or related implementation needs work.")
+ def test_move_with_mixed_type_value_imports(self, tmpdir) -> None:
+ """Test moving a symbol that uses both type and value imports."""
+ # language=typescript
+ source_content = """
+ import type { Type1, Type2 } from './types';
+ import { value1, value2 } from './values';
+
+ export function targetFunction(t1: Type1): value1 {
+ return value1(t1);
+ }
+ """
+
+ dest_filename = "destination.ts"
+ # language=typescript
+ dest_content = """
+ """
+
+ files = {
+ "source.ts": source_content,
+ dest_filename: dest_content,
+ }
+
+ with get_codebase_session(tmpdir=tmpdir, programming_language=ProgrammingLanguage.TYPESCRIPT, files=files) as codebase:
+ source_file = codebase.get_file("source.ts")
+ dest_file = codebase.get_file(dest_filename)
+
+ target_function = source_file.get_function("targetFunction")
+ target_function.move_to_file(dest_file, include_dependencies=True, strategy="update_all_imports")
+
+ # Check both type and value imports are handled
+ assert "import type { Type1 }" in dest_file.content
+ assert "Type2" not in dest_file.content
+ assert "import { value1 }" in dest_file.content
+ assert "value2" not in dest_file.content
+
+
+class TestMoveToFileUsageUpdates:
+ """Test updating import statements in files that use the moved symbol."""
+
+ @pytest.mark.skip(reason="This test or related implementation needs work.")
+ def test_usage_file_updates(self, tmpdir) -> None:
+ """Test that usage files are updated correctly."""
+ # language=typescript
+ source_content = """
+ export function targetFunction() {
+ return "test";
+ }
+ """
+
+ # language=typescript
+ usage_content = """
+ import { targetFunction } from './source';
+ import { otherFunction } from './source';
+
+ export function consumer() {
+ return targetFunction();
+ }
+ """
+
+ dest_filename = "destination.ts"
+ # language=typescript
+ dest_content = """
+ """
+
+ files = {
+ "source.ts": source_content,
+ dest_filename: dest_content,
+ "usage.ts": usage_content,
+ }
+
+ with get_codebase_session(tmpdir=tmpdir, programming_language=ProgrammingLanguage.TYPESCRIPT, files=files) as codebase:
+ source_file = codebase.get_file("source.ts")
+ dest_file = codebase.get_file(dest_filename)
+ usage_file = codebase.get_file("usage.ts")
+
+ target_function = source_file.get_function("targetFunction")
+ target_function.move_to_file(dest_file, include_dependencies=True, strategy="update_all_imports")
+
+ # Check usage file updates
+ assert "import { targetFunction } from './destination'" in usage_file.content
+ assert "import { otherFunction } from './source'" in usage_file.content
+
+
+class TestMoveToFileComplexScenarios:
+ """Test complex scenarios with multiple files and dependencies."""
+
+ @pytest.mark.skip(reason="This test or related implementation needs work.")
+ def test_complex_dependency_chain(self, tmpdir) -> None:
+ """Test moving a symbol with a complex chain of dependencies."""
+ # language=typescript
+ source_content = """
+ import { helperA } from './helper-a';
+ import { helperB } from './helper-b';
+ import type { ConfigType } from './types';
+
+ export function targetFunction(config: ConfigType) {
+ return helperA(helperB(config));
+ }
+ """
+
+ # language=typescript
+ helper_a_content = """
+ import { helperB } from './helper-b';
+ export function helperA(value: string) {
+ return helperB(value);
+ }
+ """
+
+ # language=typescript
+ helper_b_content = """
+ import type { ConfigType } from './types';
+ export function helperB(config: ConfigType) {
+ return config.value;
+ }
+ """
+
+ # language=typescript
+ types_content = """
+ export interface ConfigType {
+ value: string;
+ }
+ """
+
+ dest_filename = "destination.ts"
+ # language=typescript
+ dest_content = """
+ """
+
+ files = {
+ "source.ts": source_content,
+ dest_filename: dest_content,
+ "helper-a.ts": helper_a_content,
+ "helper-b.ts": helper_b_content,
+ "types.ts": types_content,
+ }
+
+ with get_codebase_session(tmpdir=tmpdir, programming_language=ProgrammingLanguage.TYPESCRIPT, files=files) as codebase:
+ source_file = codebase.get_file("source.ts")
+ dest_file = codebase.get_file(dest_filename)
+
+ target_function = source_file.get_function("targetFunction")
+ target_function.move_to_file(dest_file, include_dependencies=True, strategy="update_all_imports")
+
+ # Check imports in destination file
+ assert "import { helperA } from './helper-a'" in dest_file.content
+ assert "import { helperB } from './helper-b'" in dest_file.content
+ assert "import type { ConfigType } from './types'" in dest_file.content
+
+ # Check source file is cleaned up
+ assert "helperA" not in source_file.content
+ assert "helperB" not in source_file.content
+ assert "ConfigType" not in source_file.content
+
+
+class TestMoveToFileEdgeCases:
+ """Test edge cases and error conditions."""
+
+ def test_move_with_self_reference(self, tmpdir) -> None:
+ """Test moving a function that references itself."""
+ # language=typescript
+ source_content = """
+ export function targetFunction(n: number): number {
+ if (n <= 1) return n;
+ return targetFunction(n - 1) + targetFunction(n - 2);
+ }
+ """
+
+ dest_filename = "destination.ts"
+ # language=typescript
+ dest_content = """
+ """
+
+ files = {
+ "source.ts": source_content,
+ dest_filename: dest_content,
+ }
+
+ with get_codebase_session(tmpdir=tmpdir, programming_language=ProgrammingLanguage.TYPESCRIPT, files=files) as codebase:
+ source_file = codebase.get_file("source.ts")
+ dest_file = codebase.get_file(dest_filename)
+
+ target_function = source_file.get_function("targetFunction")
+ target_function.move_to_file(dest_file, include_dependencies=True, strategy="update_all_imports")
+
+ # Check self-reference is preserved
+ assert "targetFunction(n - 1)" in dest_file.content
+ assert "targetFunction(n - 2)" in dest_file.content
+
+ @pytest.mark.skip(reason="This test or related implementation needs work.")
+ def test_move_with_namespace_imports(self, tmpdir) -> None:
+ """Test moving a symbol that uses namespace imports."""
+ # language=typescript
+ source_content = """
+ import * as ns1 from './namespace1';
+ import * as ns2 from './namespace2';
+
+ export function targetFunction() {
+ return ns1.helper(ns2.config);
+ }
+ """
+
+ # language=typescript
+ namespace1_content = """
+ export function helper(config: any) {
+ return config.value;
+ }
+ """
+
+ # language=typescript
+ namespace2_content = """
+ export const config = {
+ value: "test"
+ };
+ """
+
+ dest_filename = "destination.ts"
+ # language=typescript
+ dest_content = """
+ """
+
+ files = {
+ "source.ts": source_content,
+ dest_filename: dest_content,
+ "namespace1.ts": namespace1_content,
+ "namespace2.ts": namespace2_content,
+ }
+
+ with get_codebase_session(tmpdir=tmpdir, programming_language=ProgrammingLanguage.TYPESCRIPT, files=files) as codebase:
+ source_file = codebase.get_file("source.ts")
+ dest_file = codebase.get_file(dest_filename)
+
+ target_function = source_file.get_function("targetFunction")
+ target_function.move_to_file(dest_file, include_dependencies=True, strategy="update_all_imports")
+
+ # Check namespace imports are handled correctly
+ assert "import * as ns1 from './namespace1'" in dest_file.content
+ assert "import * as ns2 from './namespace2'" in dest_file.content
+ assert "ns1.helper" in dest_file.content
+ assert "ns2.config" in dest_file.content
+
+
+class TestMoveToFileErrorConditions:
+ """Test error conditions and invalid moves."""
+
+ @pytest.mark.skip(reason="This test or related implementation needs work.")
+ def test_move_with_circular_dependencies(self, tmpdir) -> None:
+ """Test moving a symbol involved in circular dependencies."""
+ # language=typescript
+ source_content = """
+ import { helperB } from './helper-b';
+
+ export function targetFunction() {
+ return helperB();
+ }
+ """
+
+ # language=typescript
+ helper_b_content = """
+ import { targetFunction } from './source';
+
+ export function helperB() {
+ return targetFunction();
+ }
+ """
+
+ source_filename = "source.ts"
+ dest_filename = "destination.ts"
+ # language=typescript
+ dest_content = """
+ """
+
+ files = {source_filename: source_content, dest_filename: dest_content, "helper-b.ts": helper_b_content}
+
+ with get_codebase_session(tmpdir=tmpdir, programming_language=ProgrammingLanguage.TYPESCRIPT, files=files) as codebase:
+ source_file = codebase.get_file(source_filename)
+ dest_file = codebase.get_file(dest_filename)
+ helper_b_file = codebase.get_file("helper-b.ts")
+
+ target_function = source_file.get_function("targetFunction")
+ target_function.move_to_file(dest_file, include_dependencies=True, strategy="update_all_imports")
+
+ # Check circular dependency is resolved
+ assert "import { targetFunction } from './destination'" in helper_b_file.content
+ assert "import { helperB } from './helper-b'" in dest_file.content
+
+
+class TestMoveToFileJSXScenarios:
+ """Test moving JSX/TSX components and related scenarios."""
+
+ @pytest.mark.skip(reason="This test or related implementation needs work.")
+ def test_move_component_with_props(self, tmpdir) -> None:
+ """Test moving a React component with props interface."""
+ # language=typescript
+ source_content = """
+ import React from 'react';
+ import type { ButtonProps } from './types';
+ import { styled } from '@emotion/styled';
+
+ const StyledButton = styled.button`
+ color: blue;
+ `;
+
+ export function TargetComponent({ onClick, children }: ButtonProps) {
+ return (
+
+ {children}
+
+ );
+ }
+ """
+
+ source_filename = "source.tsx"
+ dest_filename = "destination.tsx"
+ # language=typescript
+ dest_content = """
+ """
+
+ files = {source_filename: source_content, dest_filename: dest_content}
+
+ with get_codebase_session(tmpdir=tmpdir, programming_language=ProgrammingLanguage.TYPESCRIPT, files=files) as codebase:
+ source_file = codebase.get_file(source_filename)
+ dest_file = codebase.get_file(dest_filename)
+
+ target_component = source_file.get_function("TargetComponent")
+ target_component.move_to_file(dest_file, include_dependencies=True, strategy="update_all_imports")
+
+ # Check JSX-specific imports and dependencies
+ assert "import React from 'react'" in dest_file.content
+ assert "import type { ButtonProps } from './types'" in dest_file.content
+ assert "import { styled } from '@emotion/styled'" in dest_file.content
+ assert "const StyledButton = styled.button" in dest_file.content
+
+
+class TestMoveToFileModuleAugmentation:
+ """Test moving symbols with module augmentation."""
+
+ @pytest.mark.skip(reason="This test or related implementation needs work.")
+ def test_move_with_module_augmentation(self, tmpdir) -> None:
+ """Test moving a symbol that involves module augmentation."""
+ # language=typescript
+ source_content = """
+ declare module 'external-module' {
+ export interface ExternalType {
+ newProperty: string;
+ }
+ }
+
+ import type { ExternalType } from 'external-module';
+
+ export function targetFunction(param: ExternalType) {
+ return param.newProperty;
+ }
+ """
+
+ source_filename = "source.ts"
+ dest_filename = "destination.ts"
+ # language=typescript
+ dest_content = """
+ """
+
+ files = {
+ source_filename: source_content,
+ dest_filename: dest_content,
+ }
+
+ with get_codebase_session(tmpdir=tmpdir, programming_language=ProgrammingLanguage.TYPESCRIPT, files=files) as codebase:
+ source_file = codebase.get_file(source_filename)
+ dest_file = codebase.get_file(dest_filename)
+
+ target_function = source_file.get_function("targetFunction")
+ target_function.move_to_file(dest_file, include_dependencies=True, strategy="update_all_imports")
+
+ # Check module augmentation is handled
+ assert "declare module 'external-module'" in dest_file.content
+ assert "interface ExternalType" in dest_file.content
+ assert "import type { ExternalType }" in dest_file.content
+
+
+class TestMoveToFileReExportChains:
+ """Test moving symbols involved in re-export chains."""
+
+ @pytest.mark.skip(reason="This test or related implementation needs work.")
+ def test_move_with_reexport_chain(self, tmpdir) -> None:
+ """Test moving a symbol that's re-exported through multiple files."""
+ # language=typescript
+ source_content = """
+ export function targetFunction() {
+ return "test";
+ }
+ """
+
+ # language=typescript
+ barrel_a_content = """
+ export { targetFunction } from './source';
+ """
+
+ # language=typescript
+ barrel_b_content = """
+ export * from './barrel-a';
+ """
+
+ # language=typescript
+ usage_content = """
+ import { targetFunction } from './barrel-b';
+
+ export function consumer() {
+ return targetFunction();
+ }
+ """
+
+ source_filename = "source.ts"
+ dest_filename = "destination.ts"
+ # language=typescript
+ dest_content = """
+ """
+
+ files = {source_filename: source_content, dest_filename: dest_content, "barrel-a.ts": barrel_a_content, "barrel-b.ts": barrel_b_content, "usage.ts": usage_content}
+
+ with get_codebase_session(tmpdir=tmpdir, programming_language=ProgrammingLanguage.TYPESCRIPT, files=files) as codebase:
+ source_file = codebase.get_file(source_filename)
+ dest_file = codebase.get_file(dest_filename)
+ barrel_a_file = codebase.get_file("barrel-a.ts")
+ barrel_b_file = codebase.get_file("barrel-b.ts")
+ usage_file = codebase.get_file("usage.ts")
+
+ target_function = source_file.get_function("targetFunction")
+ target_function.move_to_file(dest_file, include_dependencies=True, strategy="update_all_imports")
+
+ # Check re-export chain updates
+ assert "export { targetFunction } from './destination'" in barrel_a_file.content
+ assert "export * from './barrel-a'" in barrel_b_file.content
+ assert "import { targetFunction } from './barrel-b'" in usage_file.content
+
+
+class TestMoveToFileAmbientDeclarations:
+ """Test moving symbols with ambient declarations."""
+
+ @pytest.mark.skip(reason="This test or related implementation needs work.")
+ def test_move_with_ambient_module(self, tmpdir) -> None:
+ """Test moving a symbol that uses ambient module declarations."""
+ # language=typescript
+ source_content = """
+ declare module 'config' {
+ interface Config {
+ apiKey: string;
+ endpoint: string;
+ }
+ }
+
+ import type { Config } from 'config';
+
+ export function targetFunction(config: Config) {
+ return fetch(config.endpoint, {
+ headers: { 'Authorization': config.apiKey }
+ });
+ }
+ """
+
+ source_filename = "source.ts"
+ dest_filename = "destination.ts"
+ # language=typescript
+ dest_content = """
+ """
+
+ files = {
+ source_filename: source_content,
+ dest_filename: dest_content,
+ }
+
+ with get_codebase_session(tmpdir=tmpdir, programming_language=ProgrammingLanguage.TYPESCRIPT, files=files) as codebase:
+ source_file = codebase.get_file(source_filename)
+ dest_file = codebase.get_file(dest_filename)
+
+ target_function = source_file.get_function("targetFunction")
+ target_function.move_to_file(dest_file, include_dependencies=True, strategy="update_all_imports")
+
+ # Check ambient declarations are moved
+ assert "declare module 'config'" in dest_file.content
+ assert "interface Config" in dest_file.content
+ assert "import type { Config } from 'config'" in dest_file.content
+
+
+class TestMoveToFileGenerics:
+ """Test moving symbols with generic type parameters."""
+
+ @pytest.mark.skip(reason="This test or related implementation needs work.")
+ def test_move_with_generic_constraints(self, tmpdir) -> None:
+ """Test moving a function with generic type constraints."""
+ # language=typescript
+ source_content = """
+ import { Validator, Serializable } from './types';
+
+ export function targetFunction>(
+ value: T,
+ validator: U
+ ): T {
+ return validator.validate(value);
+ }
+ """
+
+ source_filename = "source.ts"
+ dest_filename = "destination.ts"
+ # language=typescript
+ dest_content = """
+ """
+
+ files = {
+ source_filename: source_content,
+ dest_filename: dest_content,
+ }
+
+ with get_codebase_session(tmpdir=tmpdir, programming_language=ProgrammingLanguage.TYPESCRIPT, files=files) as codebase:
+ source_file = codebase.get_file(source_filename)
+ dest_file = codebase.get_file(dest_filename)
+
+ target_function = source_file.get_function("targetFunction")
+ target_function.move_to_file(dest_file, include_dependencies=True, strategy="update_all_imports")
+
+ assert "import { Validator, Serializable }" not in source_file.content
+ assert "import { Validator, Serializable } from './types'" in dest_file.content
+
+
+class TestMoveToFileDecoratorFactories:
+ """Test moving symbols with decorator factories."""
+
+ @pytest.mark.skip(reason="This test or related implementation needs work.")
+ def test_move_with_decorator_factories(self, tmpdir) -> None:
+ """Test moving a function that uses decorator factories."""
+ # language=typescript
+ source_content = """
+ import { createDecorator } from './decorator-factory';
+ import type { Options } from './types';
+
+ const customDecorator = createDecorator({ timeout: 1000 });
+
+ @customDecorator
+ export function targetFunction() {
+ return new Promise(resolve => setTimeout(resolve, 1000));
+ }
+ """
+
+ source_filename = "source.ts"
+ dest_filename = "destination.ts"
+ # language=typescript
+ dest_content = """
+ """
+
+ files = {
+ source_filename: source_content,
+ dest_filename: dest_content,
+ }
+
+ with get_codebase_session(tmpdir=tmpdir, programming_language=ProgrammingLanguage.TYPESCRIPT, files=files) as codebase:
+ source_file = codebase.get_file(source_filename)
+ dest_file = codebase.get_file(dest_filename)
+
+ target_function = source_file.get_function("targetFunction")
+ target_function.move_to_file(dest_file, include_dependencies=True, strategy="update_all_imports")
+
+ # Check decorator factory and its dependencies are moved
+ assert "import { createDecorator }" in dest_file.content
+ assert "import type { Options }" in dest_file.content
+ assert "const customDecorator = createDecorator" in dest_file.content
+
+
+class TestMoveToFileDefaultExports:
+ """Test moving symbols with default exports and re-exports."""
+
+ @pytest.mark.skip(reason="This test or related implementation needs work.")
+ def test_move_with_default_export(self, tmpdir) -> None:
+ """Test moving a default exported function."""
+ # language=typescript
+ source_content = """
+ import { helper } from './helper';
+
+ export default function targetFunction() {
+ return helper();
+ }
+ """
+
+ # language=typescript
+ usage_content = """
+ import targetFunction from './source';
+ import { default as renamed } from './source';
+
+ export const result = targetFunction();
+ export const aliased = renamed();
+ """
+
+ source_filename = "source.ts"
+ dest_filename = "destination.ts"
+ # language=typescript
+ dest_content = """
+ """
+
+ files = {source_filename: source_content, dest_filename: dest_content, "usage.ts": usage_content}
+
+ with get_codebase_session(tmpdir=tmpdir, programming_language=ProgrammingLanguage.TYPESCRIPT, files=files) as codebase:
+ source_file = codebase.get_file(source_filename)
+ dest_file = codebase.get_file(dest_filename)
+ usage_file = codebase.get_file("usage.ts")
+
+ target_function = source_file.get_function("targetFunction")
+ target_function.move_to_file(dest_file, include_dependencies=True, strategy="update_all_imports")
+
+ # Check default export handling
+ assert "import targetFunction from './destination'" in usage_file.content
+ assert "import { default as renamed } from './destination'" in usage_file.content
+
+ @pytest.mark.skip(reason="This test or related implementation needs work.")
+ def test_move_with_multiline_imports(self, tmpdir) -> None:
+ """Test removing unused imports from multiline import statements"""
+ # language=typescript
+ source_content = """
+ import {
+ helperUtil,
+ formatUtil,
+ parseUtil,
+ unusedUtil
+ } from './utils';
+ import { otherUtil } from './other';
+
+ export function targetFunction() {
+ const formatted = formatUtil(helperUtil("test"));
+ return parseUtil(formatted);
+ }
+ """
+
+ source_filename = "source.ts"
+ dest_filename = "destination.ts"
+ # language=typescript
+ dest_content = """
+ """
+
+ files = {
+ source_filename: source_content,
+ dest_filename: dest_content,
+ }
+
+ with get_codebase_session(tmpdir=tmpdir, programming_language=ProgrammingLanguage.TYPESCRIPT, files=files) as codebase:
+ source_file = codebase.get_file(source_filename)
+ dest_file = codebase.get_file(dest_filename)
+
+ target_function = source_file.get_function("targetFunction")
+ target_function.move_to_file(dest_file, include_dependencies=True, strategy="update_all_imports")
+
+ # Verify only used imports were moved
+ assert "unusedUtil" not in source_file.content
+ assert "otherUtil" not in source_file.content
+ assert "helperUtil" in dest_file.content
+ assert "formatUtil" in dest_file.content
+ assert "parseUtil" in dest_file.content
+ assert "unusedUtil" not in dest_file.content
+
+ @pytest.mark.skip(reason="This test or related implementation needs work.")
+ def test_move_with_aliased_imports(self, tmpdir) -> None:
+ """Test removing unused imports with aliases"""
+ # language=typescript
+ source_content = """
+ import { helperUtil as helper } from './utils';
+ import { formatUtil as fmt, parseUtil as parse } from './formatters';
+ import { validateUtil as validate } from './validators';
+
+ export function targetFunction() {
+ return helper(fmt("test"));
+ }
+ """
+
+ source_filename = "source.ts"
+ dest_filename = "destination.ts"
+ # language=typescript
+ dest_content = """
+ """
+
+ files = {
+ source_filename: source_content,
+ dest_filename: dest_content,
+ }
+
+ with get_codebase_session(tmpdir=tmpdir, programming_language=ProgrammingLanguage.TYPESCRIPT, files=files) as codebase:
+ source_file = codebase.get_file(source_filename)
+ dest_file = codebase.get_file(dest_filename)
+
+ target_function = source_file.get_function("targetFunction")
+ target_function.move_to_file(dest_file, include_dependencies=True, strategy="update_all_imports")
+
+ # Verify only used aliased imports were moved
+ assert "helper" not in source_file.content
+ assert "fmt" not in source_file.content
+ assert "parse" not in source_file.content
+ assert "validate" in source_file.content
+ assert "helper" in dest_file.content
+ assert "fmt" in dest_file.content
+ assert "parse" not in dest_file.content
+
+ @pytest.mark.skip(reason="This test or related implementation needs work.")
+ def test_back_edge_with_import_retention(self, tmpdir) -> None:
+ """Test back edge strategy retains necessary imports"""
+ # language=typescript
+ source_content = """
+ import { helperUtil } from './utils';
+ import { otherUtil } from './other';
+
+ export function targetFunction() {
+ return helperUtil("test");
+ }
+ """
+
+ source_filename = "source.ts"
+ dest_filename = "destination.ts"
+ # language=typescript
+ dest_content = """
+ """
+
+ files = {
+ source_filename: source_content,
+ dest_filename: dest_content,
+ }
+
+ with get_codebase_session(tmpdir=tmpdir, programming_language=ProgrammingLanguage.TYPESCRIPT, files=files) as codebase:
+ source_file = codebase.get_file(source_filename)
+ dest_file = codebase.get_file(dest_filename)
+
+ target_function = source_file.get_function("targetFunction")
+ target_function.move_to_file(dest_file, include_dependencies=True, strategy="add_back_edge", cleanup_unused_imports=True)
+
+ # Source file should have import from new location but keep originals
+ assert "import { targetFunction } from './destination'" in source_file.content
+ assert "import { helperUtil } from './utils'" in source_file.content
+ assert "import { otherUtil } from './other'" in source_file.content
+ # Destination should have required imports
+ assert "import { helperUtil } from './utils'" in dest_file.content
+
+
+class TestMoveToFileStrategies:
+ """Test different move strategies and their behaviors."""
+
+ @pytest.mark.skip(reason="This test or related implementation needs work.")
+ def test_update_all_imports_strategy(self, tmpdir) -> None:
+ """Test update_all_imports strategy behavior"""
+ # language=typescript
+ source_content = """
+ import { helperUtil } from './utils';
+ import { otherUtil } from './other';
+
+ export function targetFunction() {
+ return helperUtil("test");
+ }
+ """
+
+ source_filename = "source.ts"
+ dest_filename = "destination.ts"
+ # language=typescript
+ dest_content = """
+ """
+
+ files = {
+ source_filename: source_content,
+ dest_filename: dest_content,
+ }
+
+ with get_codebase_session(tmpdir=tmpdir, programming_language=ProgrammingLanguage.TYPESCRIPT, files=files) as codebase:
+ source_file = codebase.get_file(source_filename)
+ dest_file = codebase.get_file(dest_filename)
+
+ target_function = source_file.get_function("targetFunction")
+ target_function.move_to_file(dest_file, include_dependencies=True, strategy="update_all_imports", cleanup_unused_imports=True)
+
+ assert "import { helperUtil } from './utils'" not in source_file.content
+ assert "import { otherUtil } from './other'" not in source_file.content
+ assert "import { helperUtil } from './utils'" in dest_file.content
+
+ @pytest.mark.skip(reason="This test or related implementation needs work.")
+ def test_back_edge_strategy(self, tmpdir) -> None:
+ """Test back edge strategy behavior"""
+ # language=typescript
+ source_content = """
+ import { helperUtil } from './utils';
+ import { otherUtil } from './other';
+
+ export function targetFunction() {
+ return helperUtil("test");
+ }
+ """
+
+ source_filename = "source.ts"
+ dest_filename = "destination.ts"
+ # language=typescript
+ dest_content = """
+ """
+
+ files = {
+ source_filename: source_content,
+ dest_filename: dest_content,
+ }
+
+ with get_codebase_session(tmpdir=tmpdir, programming_language=ProgrammingLanguage.TYPESCRIPT, files=files) as codebase:
+ source_file = codebase.get_file(source_filename)
+ dest_file = codebase.get_file(dest_filename)
+
+ target_function = source_file.get_function("targetFunction")
+ target_function.move_to_file(dest_file, include_dependencies=True, strategy="add_back_edge", cleanup_unused_imports=True)
+
+ # Source file should have import from new location
+ assert "import { targetFunction } from './destination'" in source_file.content
+ assert "import { helperUtil } from './utils'" in source_file.content
+ assert "import { otherUtil } from './other'" in source_file.content
+ # Destination should have required imports
+ assert "import { helperUtil } from './utils'" in dest_file.content
+
+ def test_move_with_absolute_imports(self, tmpdir) -> None:
+ """Test moving a symbol that uses absolute imports"""
+ # language=typescript
+ source_content = """
+ import { helperUtil } from '@/utils/helpers';
+ import { formatUtil } from '/src/utils/format';
+ import { configUtil } from '~/config';
+
+ export function targetFunction() {
+ return helperUtil(formatUtil(configUtil.getValue()));
+ }
+ """
+
+ dest_filename = "destination.ts"
+ dest_content = ""
+
+ files = {
+ "source.ts": source_content,
+ dest_filename: dest_content,
+ }
+
+ with get_codebase_session(tmpdir=tmpdir, programming_language=ProgrammingLanguage.TYPESCRIPT, files=files) as codebase:
+ source_file = codebase.get_file("source.ts")
+ dest_file = codebase.get_file(dest_filename)
+
+ target_function = source_file.get_function("targetFunction")
+ target_function.move_to_file(dest_file, include_dependencies=True, strategy="update_all_imports")
+
+ # Verify absolute imports are preserved
+ assert "import { helperUtil } from '@/utils/helpers'" in dest_file.content
+ assert "import { formatUtil } from '/src/utils/format'" in dest_file.content
+ assert "import { configUtil } from '~/config'" in dest_file.content
+
+ @pytest.mark.skip(reason="This test or related implementation needs work.")
+ def test_move_with_complex_relative_paths(self, tmpdir) -> None:
+ """Test moving a symbol that uses complex relative paths"""
+ # language=typescript
+ source_content = """
+ import { helperA } from '../../../utils/helpers';
+ import { helperB } from '../../../../shared/utils';
+ import { helperC } from './local/helper';
+
+ export function targetFunction() {
+ return helperA(helperB(helperC()));
+ }
+ """
+
+ files = {
+ "src/features/auth/components/source.ts": source_content,
+ "src/features/user/services/destination.ts": "",
+ "src/utils/helpers.ts": "export const helperA = (x) => x;",
+ "shared/utils.ts": "export const helperB = (x) => x;",
+ "src/features/auth/components/local/helper.ts": "export const helperC = () => 'test';",
+ }
+
+ with get_codebase_session(tmpdir=tmpdir, programming_language=ProgrammingLanguage.TYPESCRIPT, files=files) as codebase:
+ source_file = codebase.get_file("src/features/auth/components/source.ts")
+ dest_file = codebase.get_file("src/features/user/services/destination.ts")
+
+ target_function = source_file.get_function("targetFunction")
+ target_function.move_to_file(dest_file, include_dependencies=True, strategy="update_all_imports")
+
+ # Verify relative paths are correctly updated based on new file location
+ assert "import { helperA } from '../../utils/helpers'" in dest_file.content
+ assert "import { helperB } from '../../../../shared/utils'" in dest_file.content
+ assert "import { helperC } from '../../auth/components/local/helper'" in dest_file.content
+
+ @pytest.mark.skip(reason="This test or related implementation needs work.")
+ def test_move_with_mixed_import_styles(self, tmpdir) -> None:
+ """Test moving a symbol that uses mixed import styles"""
+ # language=typescript
+ source_content = """
+ import defaultHelper from '@/helpers/default';
+ import * as utils from '~/utils';
+ import { namedHelper as aliasedHelper } from '../shared/helpers';
+ import type { HelperType } from './types';
+ const dynamicHelper = await import('./dynamic-helper');
+
+ export function targetFunction(): HelperType {
+ return defaultHelper(
+ utils.helper(
+ aliasedHelper(
+ dynamicHelper.default()
+ )
+ )
+ );
+ }
+ """
+
+ files = {
+ "src/features/source.ts": source_content,
+ "src/services/destination.ts": "",
+ "src/helpers/default.ts": "export default (x) => x;",
+ "lib/utils.ts": "export const helper = (x) => x;",
+ "src/shared/helpers.ts": "export const namedHelper = (x) => x;",
+ "src/features/types.ts": "export type HelperType = string;",
+ "src/features/dynamic-helper.ts": "export default () => 'test';",
+ }
+
+ with get_codebase_session(tmpdir=tmpdir, programming_language=ProgrammingLanguage.TYPESCRIPT, files=files) as codebase:
+ source_file = codebase.get_file("src/features/source.ts")
+ dest_file = codebase.get_file("src/services/destination.ts")
+
+ target_function = source_file.get_function("targetFunction")
+ target_function.move_to_file(dest_file, include_dependencies=True, strategy="update_all_imports")
+
+ # Verify different import styles are handled correctly
+ assert "import defaultHelper from '@/helpers/default'" in dest_file.content
+ assert "import * as utils from '~/utils'" in dest_file.content
+ assert "import { namedHelper as aliasedHelper } from '../shared/helpers'" in dest_file.content
+ assert "import type { HelperType } from '../features/types'" in dest_file.content
+ assert "const dynamicHelper = await import('../features/dynamic-helper')" in dest_file.content
+
+ @pytest.mark.skip(reason="This test or related implementation needs work.")
+ def test_move_between_monorepo_packages(self, tmpdir) -> None:
+ """Test moving a symbol between different packages in a monorepo"""
+ # language=typescript
+ source_content = """
+ import { sharedUtil } from '@myorg/shared';
+ import { helperUtil } from '@myorg/utils';
+ import { localUtil } from './utils';
+
+ export function targetFunction() {
+ return sharedUtil(helperUtil(localUtil()));
+ }
+ """
+
+ files = {
+ "packages/package-a/src/source.ts": source_content,
+ "packages/package-b/src/destination.ts": "",
+ "packages/shared/src/index.ts": "export const sharedUtil = (x) => x;",
+ "packages/utils/src/index.ts": "export const helperUtil = (x) => x;",
+ "packages/package-a/src/utils.ts": "export const localUtil = () => 'test';",
+ "packages/package-a/package.json": '{"name": "@myorg/package-a"}',
+ "packages/package-b/package.json": '{"name": "@myorg/package-b"}',
+ }
+
+ with get_codebase_session(tmpdir=tmpdir, programming_language=ProgrammingLanguage.TYPESCRIPT, files=files) as codebase:
+ source_file = codebase.get_file("packages/package-a/src/source.ts")
+ dest_file = codebase.get_file("packages/package-b/src/destination.ts")
+
+ target_function = source_file.get_function("targetFunction")
+ target_function.move_to_file(dest_file, include_dependencies=True, strategy="update_all_imports")
+
+ # Verify package imports are handled correctly
+ assert "import { sharedUtil } from '@myorg/shared'" in dest_file.content
+ assert "import { helperUtil } from '@myorg/utils'" in dest_file.content
+ assert "import { localUtil } from '@myorg/package-a/src/utils'" in dest_file.content
+
+ @pytest.mark.skip(reason="This test or related implementation needs work.")
+ def test_move_between_different_depths(self, tmpdir) -> None:
+ """Test moving a symbol between files at different directory depths"""
+ # language=typescript
+ source_content = """
+ import { helperA } from './helper';
+ import { helperB } from '../utils/helper';
+ import { helperC } from '../../shared/helper';
+
+ export function targetFunction() {
+ return helperA(helperB(helperC()));
+ }
+ """
+
+ files = {
+ "src/features/auth/source.ts": source_content,
+ "src/features/auth/helper.ts": "export const helperA = (x) => x;",
+ "src/features/utils/helper.ts": "export const helperB = (x) => x;",
+ "src/shared/helper.ts": "export const helperC = () => 'test';",
+ "lib/services/destination.ts": "",
+ }
+
+ with get_codebase_session(tmpdir=tmpdir, programming_language=ProgrammingLanguage.TYPESCRIPT, files=files) as codebase:
+ source_file = codebase.get_file("src/features/auth/source.ts")
+ dest_file = codebase.get_file("lib/services/destination.ts")
+
+ target_function = source_file.get_function("targetFunction")
+ target_function.move_to_file(dest_file, include_dependencies=True, strategy="update_all_imports")
+
+ # Verify imports are updated for new directory depth
+ assert "import { helperA } from '../../src/features/auth/helper'" in dest_file.content
+ assert "import { helperB } from '../../src/features/utils/helper'" in dest_file.content
+ assert "import { helperC } from '../../src/shared/helper'" in dest_file.content
+
+
+class TestMoveToFileFileSystem:
+ """Test moving functions with different file system considerations."""
+
+ @pytest.mark.skipif(condition=platform.system() != "Linux", reason="Only works on case-sensitive file systems")
+ def test_function_move_to_file_lower_upper(self, tmpdir) -> None:
+ # language=typescript
+ content1 = """
+export function foo(): number {
+ return bar() + 1;
+}
+
+export function bar(): number {
+ return foo() + 1;
+}
+ """
+ with get_codebase_session(tmpdir, files={"file1.ts": content1}, programming_language=ProgrammingLanguage.TYPESCRIPT) as codebase:
+ file1 = codebase.get_file("file1.ts")
+ foo = file1.get_function("foo")
+ bar = file1.get_function("bar")
+ assert bar in foo.dependencies
+ assert foo in bar.dependencies
+
+ file2 = codebase.create_file("File1.ts", "")
+ foo.move_to_file(file2, include_dependencies=True, strategy="add_back_edge")
+
+ # language=typescript
+ assert (
+ file2.content.strip()
+ == """
+export function bar(): number {
+ return foo() + 1;
+}
+
+export function foo(): number {
+ return bar() + 1;
+}
+ """.strip()
+ )
+ assert file1.content.strip() == "export { bar } from 'File1'\nexport { foo } from 'File1'"
+
+ @pytest.mark.skipif(condition=platform.system() != "Linux", reason="Only works on case-sensitive file systems")
+ def test_function_move_to_file_lower_upper_no_deps(self, tmpdir) -> None:
+ # language=typescript
+ content1 = """
+export function foo(): number {
+ return bar() + 1;
+}
+
+export function bar(): number {
+ return foo() + 1;
+}
+ """
+ with get_codebase_session(tmpdir, files={"file1.ts": content1}, programming_language=ProgrammingLanguage.TYPESCRIPT) as codebase:
+ file1 = codebase.get_file("file1.ts")
+ foo = file1.get_function("foo")
+ bar = file1.get_function("bar")
+ assert bar in foo.dependencies
+ assert foo in bar.dependencies
+
+ file2 = codebase.create_file("File1.ts", "")
+ foo.move_to_file(file2, include_dependencies=False, strategy="add_back_edge")
+
+ # language=typescript
+ assert (
+ file1.content.strip()
+ == """export { foo } from 'File1';
+
+export function bar(): number {
+ return foo() + 1;
+}"""
+ )
+ # language=typescript
+ assert (
+ file2.content.strip()
+ == """
+import { bar } from 'file1';
+
+
+export function foo(): number {
+ return bar() + 1;
+}
+ """.strip()
+ )
diff --git a/tests/unit/codegen/sdk/typescript/move_symbol_to_file/test_move_tsx_to_file.py b/tests/unit/codegen/sdk/typescript/move_symbol_to_file/test_move_tsx_to_file.py
index d2c3e8484..ec3524e42 100644
--- a/tests/unit/codegen/sdk/typescript/move_symbol_to_file/test_move_tsx_to_file.py
+++ b/tests/unit/codegen/sdk/typescript/move_symbol_to_file/test_move_tsx_to_file.py
@@ -1,3 +1,5 @@
+import pytest
+
from codegen.sdk.codebase.factory.get_session import get_codebase_session
from codegen.shared.enums.programming_language import ProgrammingLanguage
@@ -63,7 +65,7 @@ def test_move_component_with_dependencies(tmpdir) -> None:
# Verify ComponentB move
assert "const ComponentB" not in src_file.content
- assert "import { ComponentB } from 'dst'" in src_file.content
+ assert "export { ComponentB } from 'dst'" in src_file.content
assert "const ComponentB = () => {" in dst_file.content
assert "export { ComponentB }" in src_file.content
@@ -72,11 +74,12 @@ def test_move_component_with_dependencies(tmpdir) -> None:
assert "export { ComponentD } from 'dst'" in src_file.content
+@pytest.mark.skip(reason="This test is failing because of the way we handle re-exports. Address in CG-10686")
def test_remove_unused_exports(tmpdir) -> None:
"""Tests removing unused exports when moving components between files"""
- src_filename = "Component.tsx"
+ # ========== [ BEFORE ] ==========
# language=typescript jsx
- src_content = """
+ SRC_CONTENT = """
export default function MainComponent() {
const [state, setState] = useState()
return (
@@ -116,9 +119,8 @@ def test_remove_unused_exports(tmpdir) -> None:
)
}
"""
- adj_filename = "adjacent.tsx"
# language=typescript jsx
- adj_content = """
+ ADJ_CONTENT = """
import MainComponent from 'Component'
import { SharedComponent } from 'Component'
import { StateComponent } from 'utils'
@@ -127,26 +129,79 @@ def test_remove_unused_exports(tmpdir) -> None:
return (
)
}
"""
- misc_filename = "misc.tsx"
# language=typescript jsx
- misc_content = """
+ MISC_CONTENT = """
export { UnusedComponent } from 'Component'
function Helper({ props }: HelperProps) {}
export { Helper }
"""
- import_filename = "import.tsx"
# language=typescript jsx
- import_content = """
+ IMPORT_CONTENT = """
import { UnusedComponent } from 'misc'
"""
- files = {src_filename: src_content, adj_filename: adj_content, misc_filename: misc_content, import_filename: import_content}
+ # ========== [ AFTER ] ==========
+ # language=typescript jsx
+ EXPECTED_SRC_CONTENT = """
+import { SubComponent } from 'new';
+
+export default function MainComponent() {
+ const [state, setState] = useState
()
+ return ()
+}
+
+export function UnusedComponent({ props }: UnusedProps) {
+ return (
+ Unused
+ )
+}
+"""
+ # language=typescript jsx
+ EXPECTED_NEW_CONTENT = """
+export function SubComponent({ props }: SubComponentProps) {
+ return (
+
+ )
+}
+
+function HelperComponent({ props }: HelperComponentProps) {
+ return (
+
+ )
+}
+
+export function SharedComponent({ props }: SharedComponentProps) {
+ return (
+
+ )
+}
+"""
+ # language=typescript jsx
+ EXPECTED_ADJ_CONTENT = """
+import MainComponent from 'Component'
+import { SharedComponent } from 'new'
+import { StateComponent } from 'utils'
+
+function Container(props: ContainerProps) {
+ return ()
+}
+"""
+ # language=typescript jsx
+ EXPECTED_MISC_CONTENT = """
+function Helper({ props }: HelperProps) {}
+"""
+
+ files = {"Component.tsx": SRC_CONTENT, "adjacent.tsx": ADJ_CONTENT, "misc.tsx": MISC_CONTENT, "import.tsx": IMPORT_CONTENT}
with get_codebase_session(tmpdir=tmpdir, programming_language=ProgrammingLanguage.TYPESCRIPT, files=files) as codebase:
- src_file = codebase.get_file(src_filename)
- adj_file = codebase.get_file(adj_filename)
- misc_file = codebase.get_file(misc_filename)
+ src_file = codebase.get_file("Component.tsx")
+ adj_file = codebase.get_file("adjacent.tsx")
+ misc_file = codebase.get_file("misc.tsx")
new_file = codebase.create_file("new.tsx")
sub_component = src_file.get_symbol("SubComponent")
@@ -159,20 +214,7 @@ def test_remove_unused_exports(tmpdir) -> None:
src_file.remove_unused_exports()
misc_file.remove_unused_exports()
- # Verify exports in new file
- assert "export function SubComponent" in new_file.content
- assert "function HelperComponent" in new_file.content
- assert "export function HelperComponent" not in new_file.content
- assert "export function SharedComponent" in new_file.content
-
- # Verify imports updated
- assert "import { SharedComponent } from 'new'" in adj_file.content
-
- # Verify original file exports
- assert "export default function MainComponent()" in src_file.content
- assert "function UnusedComponent" in src_file.content
- assert "export function UnusedComponent" not in src_file.content
-
- # Verify misc file exports cleaned up
- assert "export { Helper }" not in misc_file.content
- assert "export { UnusedComponent } from 'Component'" not in misc_file.content
+ assert src_file.content.strip() == EXPECTED_SRC_CONTENT.strip()
+ assert new_file.content.strip() == EXPECTED_NEW_CONTENT.strip()
+ assert adj_file.content.strip() == EXPECTED_ADJ_CONTENT.strip()
+ assert misc_file.content.strip() == EXPECTED_MISC_CONTENT.strip()
diff --git a/tests/unit/codegen/sdk/typescript/namespace/test_namespace.py b/tests/unit/codegen/sdk/typescript/namespace/test_namespace.py
index aed6271b9..ab0764f76 100644
--- a/tests/unit/codegen/sdk/typescript/namespace/test_namespace.py
+++ b/tests/unit/codegen/sdk/typescript/namespace/test_namespace.py
@@ -65,8 +65,7 @@ def test_namespace_basic_symbols(tmpdir) -> None:
assert namespace.get_symbol("privateVar") is None # private not accessible
# Test symbols collection
- assert len(namespace.symbols) == 2 # only exported symbols
- assert all(symbol.is_exported for symbol in namespace.symbols)
+ assert len(namespace.symbols) == 3
def test_namespace_recursive_symbol_lookup(tmpdir) -> None:
@@ -124,44 +123,6 @@ def test_namespace_functions(tmpdir) -> None:
assert all(func.is_exported for func in namespace.functions)
-def test_namespace_function_full_name(tmpdir) -> None:
- """Test getting functions using full names."""
- FILE_NAME = "test.ts"
- # language=typescript
- FILE_CONTENT = """
- namespace Outer {
- export function shared() { return 1; }
- export namespace Inner {
- export function shared() { return 2; }
- export function unique() { return 3; }
- }
- }
- """
- with get_codebase_session(tmpdir=tmpdir, programming_language=ProgrammingLanguage.TYPESCRIPT, files={FILE_NAME: FILE_CONTENT}) as codebase:
- namespace: TSNamespace = codebase.get_symbol("Outer")
- assert namespace is not None
-
- # Test getting functions by local name
- outer_shared = namespace.get_function("shared", recursive=False)
- assert outer_shared is not None
- inner_shared = namespace.get_function("shared", recursive=True)
- assert inner_shared is not None
- # Without full names, we might get either shared function
- assert outer_shared == inner_shared
-
- # Test getting functions by full name
- outer_shared = namespace.get_function("shared", use_full_name=True)
- assert outer_shared is not None
- inner_shared = namespace.get_function("Inner.shared", use_full_name=True)
- assert inner_shared is not None
- inner_unique = namespace.get_function("Inner.unique", use_full_name=True)
- assert inner_unique is not None
-
- # Test non-existent paths
- assert namespace.get_function("NonExistent.shared", use_full_name=True) is None
- assert namespace.get_function("Inner.NonExistent", use_full_name=True) is None
-
-
def test_namespace_function_overloading(tmpdir) -> None:
"""Test function overloading within namespace."""
FILE_NAME = "test.ts"
@@ -333,3 +294,46 @@ def test_namespace_nested_deep(tmpdir) -> None:
assert len(nested) == 2 # Should find B and C
assert all(isinstance(ns, TSNamespace) for ns in nested)
assert {ns.name for ns in nested} == {"B", "C"}
+
+
+def test_namespace_imports(tmpdir) -> None:
+ """Test importing and using namespaces."""
+ FILE_NAME_1 = "math.ts"
+ # language=typescript
+ FILE_CONTENT_1 = """
+ export namespace Math {
+ export const PI = 3.14159;
+ export function square(x: number) { return x * x; }
+
+ export namespace Advanced {
+ export function cube(x: number) { return x * x * x; }
+ }
+ }
+ """
+
+ FILE_NAME_2 = "app.ts"
+ # language=typescript
+ FILE_CONTENT_2 = """
+ import { Math } from './math';
+
+ console.log(Math.PI);
+ console.log(Math.square(5));
+ console.log(Math.Advanced.cube(3));
+ """
+
+ with get_codebase_session(tmpdir=tmpdir, programming_language=ProgrammingLanguage.TYPESCRIPT, files={FILE_NAME_1: FILE_CONTENT_1, FILE_NAME_2: FILE_CONTENT_2}) as codebase:
+ math_ns = codebase.get_symbol("Math")
+ assert math_ns is not None
+ assert math_ns.name == "Math"
+
+ # Test namespace import resolution
+ file2 = codebase.get_file(FILE_NAME_2)
+ math_import = file2.get_import("Math")
+ assert math_import is not None
+ assert math_import.is_namespace_import
+
+ # Test nested namespace access
+ advanced = math_ns.get_namespace("Advanced")
+ assert advanced is not None
+ assert advanced.name == "Advanced"
+ assert advanced.get_function("cube") is not None
diff --git a/tests/unit/codegen/sdk/typescript/namespace/test_namespace_complex_examples.py b/tests/unit/codegen/sdk/typescript/namespace/test_namespace_complex_examples.py
index 3dfa77e28..9af4baf6f 100644
--- a/tests/unit/codegen/sdk/typescript/namespace/test_namespace_complex_examples.py
+++ b/tests/unit/codegen/sdk/typescript/namespace/test_namespace_complex_examples.py
@@ -133,3 +133,42 @@ def test_namespace_validators(tmpdir) -> None:
# Verify non-exported items are not accessible
assert namespace.get_symbol("lettersRegexp") is None
assert namespace.get_symbol("numberRegexp") is None
+
+
+def test_namespace_wildcard_import(tmpdir) -> None:
+ """Test wildcard imports with namespaces."""
+ FILE_NAME_1 = "utils.ts"
+ # language=typescript
+ FILE_CONTENT_1 = """
+ export namespace Utils {
+ export const helper1 = () => "help1";
+ export const helper2 = () => "help2";
+ const internal = () => "internal";
+ }
+ """
+
+ FILE_NAME_2 = "app.ts"
+ # language=typescript
+ FILE_CONTENT_2 = """
+ import * as AllUtils from './utils';
+
+ function test() {
+ console.log(AllUtils.Utils.helper1());
+ console.log(AllUtils.Utils.helper2());
+ }
+ """
+
+ with get_codebase_session(tmpdir=tmpdir, programming_language=ProgrammingLanguage.TYPESCRIPT, files={FILE_NAME_1: FILE_CONTENT_1, FILE_NAME_2: FILE_CONTENT_2}) as codebase:
+ utils_file = codebase.get_file(FILE_NAME_1)
+ app_file = codebase.get_file(FILE_NAME_2)
+
+ # Verify namespace import
+ utils_import = app_file.get_import("AllUtils")
+ assert utils_import is not None
+ assert utils_import.namespace == "AllUtils"
+
+ # Verify access to exported symbols
+ utils_ns = utils_file.get_symbol("Utils")
+ assert "helper1" in utils_ns.valid_import_names
+ assert "helper2" in utils_ns.valid_import_names
+ assert "internal" not in utils_ns.valid_import_names
diff --git a/tests/unit/codegen/sdk/typescript/namespace/test_namespace_modifications.py b/tests/unit/codegen/sdk/typescript/namespace/test_namespace_modifications.py
new file mode 100644
index 000000000..fc592beac
--- /dev/null
+++ b/tests/unit/codegen/sdk/typescript/namespace/test_namespace_modifications.py
@@ -0,0 +1,183 @@
+from typing import TYPE_CHECKING
+
+import pytest
+
+from codegen.sdk.codebase.factory.get_session import get_codebase_session
+from codegen.shared.enums.programming_language import ProgrammingLanguage
+
+if TYPE_CHECKING:
+ from codegen.sdk.typescript.namespace import TSNamespace
+
+
+def test_namespace_add_symbol(tmpdir) -> None:
+ """Test adding symbols to namespace."""
+ FILE_NAME = "test.ts"
+ # language=typescript
+ FILE_CONTENT = """
+ namespace MyNamespace {
+ export const x = 1;
+ }
+ """
+ with get_codebase_session(tmpdir=tmpdir, programming_language=ProgrammingLanguage.TYPESCRIPT, files={FILE_NAME: FILE_CONTENT}) as codebase:
+ file = codebase.get_file("test.ts")
+ namespace: TSNamespace = codebase.get_symbol("MyNamespace")
+
+ # 1. a) Add new symbol from object, then manually remove the original symbol from the file
+ # 1. b) Add new symbol by moving operation
+ file.add_symbol_from_source(source="const ya = 2")
+ codebase.ctx.commit_transactions()
+ new_const = file.get_symbol("ya")
+
+ # Store original location
+
+ # Add to namespace and remove from original location
+ namespace.add_symbol(new_const, should_export=True)
+
+ codebase.ctx.commit_transactions()
+
+ # Get fresh reference to namespace
+ namespace: TSNamespace = codebase.get_symbol("MyNamespace")
+
+ # Verify symbols were moved correctly
+ assert namespace.get_symbol("ya") is not None
+ assert namespace.get_symbol("ya").export is not None
+
+ # 2. Add new symbol from string
+ code = "const z = 3"
+ namespace.add_symbol_from_source(code)
+ codebase.ctx.commit_transactions()
+ namespace: TSNamespace = codebase.get_symbol("MyNamespace")
+
+ code_symbol = namespace.get_symbol("z", get_private=True)
+ # Verify exported symbol
+ assert code_symbol is not None
+ assert code_symbol.name == "z"
+
+ assert len(namespace.symbols) == 3
+ assert {s.name for s in namespace.symbols} == {"x", "ya", "z"}
+
+
+def test_namespace_remove_symbol(tmpdir) -> None:
+ """Test removing symbols from namespace."""
+ FILE_NAME = "test.ts"
+ # language=typescript
+ FILE_CONTENT = """
+ namespace MyNamespace {
+ export const x = 1;
+ export const y = 2;
+ }
+ """
+ with get_codebase_session(tmpdir=tmpdir, programming_language=ProgrammingLanguage.TYPESCRIPT, files={FILE_NAME: FILE_CONTENT}) as codebase:
+ namespace: TSNamespace = codebase.get_symbol("MyNamespace")
+
+ # Remove existing symbol
+ removed = namespace.remove_symbol("x")
+ codebase.ctx.commit_transactions()
+ assert removed is not None
+ assert removed.name == "x"
+
+ # Verify symbol was removed
+ assert namespace.get_symbol("x") is None
+ assert len(namespace.symbols) == 1
+ assert namespace.symbols[0].name == "y"
+
+ # Try removing non-existent symbol
+ assert namespace.remove_symbol("z") is None
+
+
+def test_namespace_rename(tmpdir) -> None:
+ """Test renaming namespace."""
+ FILE_NAME = "test.ts"
+ # language=typescript
+ FILE_CONTENT = """
+ namespace OldName {
+ export const x = 1;
+ }
+ """
+ with get_codebase_session(tmpdir=tmpdir, programming_language=ProgrammingLanguage.TYPESCRIPT, files={FILE_NAME: FILE_CONTENT}) as codebase:
+ namespace: TSNamespace = codebase.get_symbol("OldName")
+
+ # Rename namespace
+ namespace.rename("NewName")
+ codebase.ctx.commit_transactions()
+
+ # Verify rename
+ namespace: TSNamespace = codebase.get_symbol("NewName")
+ assert namespace.name == "NewName"
+ assert codebase.get_symbol("NewName") is namespace
+ assert codebase.get_symbol("OldName", optional=True) is None
+
+
+def test_namespace_export_symbol(tmpdir) -> None:
+ """Test exporting symbols in namespace."""
+ FILE_NAME = "test.ts"
+ # language=typescript
+ FILE_CONTENT = """
+ namespace ExportTest {
+ export const external = 123;
+ const internal = 123;
+ }
+ """
+ with get_codebase_session(tmpdir=tmpdir, programming_language=ProgrammingLanguage.TYPESCRIPT, files={FILE_NAME: FILE_CONTENT}) as codebase:
+ namespace: TSNamespace = codebase.get_symbol("ExportTest")
+
+ # Export internal symbol
+ namespace.export_symbol("internal")
+ codebase.ctx.commit_transactions()
+
+ # Verify export
+ namespace: TSNamespace = codebase.get_symbol("ExportTest")
+ internal = namespace.get_symbol("internal")
+ assert internal is not None
+ assert all(symbol.is_exported for symbol in namespace.symbols)
+
+ # Export already exported symbol (no change)
+ namespace.export_symbol("external")
+ codebase.ctx.commit_transactions()
+
+ namespace: TSNamespace = codebase.get_symbol("ExportTest")
+ external = namespace.get_symbol("external")
+ assert external is not None
+ assert external.is_exported
+
+
+@pytest.mark.skip("TODO: Symbol Animals is ambiguous in codebase - more than one instance")
+def test_namespace_merging(tmpdir) -> None:
+ """Test TypeScript namespace merging functionality."""
+ FILE_NAME = "test.ts"
+ # language=typescript
+ FILE_CONTENT = """
+ namespace Animals {
+ export class Dog { bark() {} }
+ }
+
+ namespace Animals { // Merge with previous namespace
+ export class Cat { meow() {} }
+ }
+
+ namespace Plants { // Different namespace, should not merge
+ export class Tree {}
+ }
+ """
+ with get_codebase_session(tmpdir=tmpdir, programming_language=ProgrammingLanguage.TYPESCRIPT, files={FILE_NAME: FILE_CONTENT}) as codebase:
+ animals = codebase.get_symbol("Animals")
+ assert animals is not None
+
+ # Test merged namespace access
+ assert animals.get_class("Dog") is not None
+ assert animals.get_class("Cat") is not None
+
+ # Verify merged namespaces
+ assert len(animals.merged_namespaces) == 1
+ merged = animals.merged_namespaces[0]
+ assert merged.name == "Animals"
+ assert merged != animals
+
+ # Verify all symbols accessible
+ all_symbols = animals.symbols
+ assert len(all_symbols) == 2
+ assert {s.name for s in all_symbols} == {"Dog", "Cat"}
+
+ # Verify non-merged namespace
+ plants = codebase.get_symbol("Plants")
+ assert len(plants.merged_namespaces) == 0
diff --git a/tests/unit/codegen/sdk/typescript/namespace/test_namespace_usage.py b/tests/unit/codegen/sdk/typescript/namespace/test_namespace_usage.py
new file mode 100644
index 000000000..9f72250b0
--- /dev/null
+++ b/tests/unit/codegen/sdk/typescript/namespace/test_namespace_usage.py
@@ -0,0 +1,103 @@
+from codegen.sdk.codebase.factory.get_session import get_codebase_session
+from codegen.sdk.core.dataclasses.usage import UsageType
+from codegen.shared.enums.programming_language import ProgrammingLanguage
+
+
+def test_namespace_same_file_usage(tmpdir) -> None:
+ """Test namespace usage within the same file."""
+ # language=typescript
+ content = """
+ namespace MathUtils {
+ export const PI = 3.14159;
+ export function square(x: number) { return x * x; }
+ }
+
+ function calculateArea(radius: number) {
+ return MathUtils.PI * MathUtils.square(radius);
+ }
+ """
+ with get_codebase_session(tmpdir=tmpdir, files={"test.ts": content}, programming_language=ProgrammingLanguage.TYPESCRIPT) as codebase:
+ file = codebase.get_file("test.ts")
+
+ namespace = file.get_symbol("MathUtils")
+ pi = namespace.get_symbol("PI")
+ square = namespace.get_symbol("square")
+ calc_area = file.get_function("calculateArea")
+
+ # Check if namespace is in valid_import_names
+ assert "MathUtils" in file.valid_symbol_names
+ assert "MathUtils" in namespace.valid_import_names
+ assert len(namespace.valid_import_names) == 3 # MathUtils, PI, and square
+
+ # Check usages
+ assert {calc_area}.issubset(namespace.symbol_usages)
+
+ # PI has direct usage (export) and chained usage (in calculateArea)
+ assert set(pi.symbol_usages(UsageType.DIRECT)) == {pi.export}
+ assert set(pi.symbol_usages(UsageType.CHAINED)) == {calc_area}
+ assert set(pi.symbol_usages) == {pi.export, calc_area}
+
+ # square has direct usage (export) and chained usage (in calculateArea)
+ assert set(square.symbol_usages(UsageType.DIRECT)) == {square.export}
+ assert set(square.symbol_usages(UsageType.CHAINED)) == {calc_area}
+ assert set(square.symbol_usages) == {square.export, calc_area}
+
+ # Verify attribute resolution
+ assert namespace.resolve_attribute("PI") == pi.export
+ assert namespace.resolve_attribute("square") == square.export
+
+
+def test_namespace_cross_file_usage(tmpdir) -> None:
+ """Test namespace usage across files with imports."""
+ # language=typescript
+ content1 = """
+ export namespace MathUtils {
+ export const PI = 3.14159;
+ export function square(x: number) { return x * x; }
+ const internal = 123; // not exported
+ }
+ """
+ # language=typescript
+ content2 = """
+ import { MathUtils } from './file1';
+
+ function calculateArea(radius: number) {
+ return MathUtils.PI * MathUtils.square(radius);
+ }
+
+ function calculateVolume(radius: number) {
+ const area = calculateArea(radius);
+ return area * radius;
+ }
+ """
+ with get_codebase_session(tmpdir=tmpdir, files={"file1.ts": content1, "file2.ts": content2}, programming_language=ProgrammingLanguage.TYPESCRIPT) as codebase:
+ file1 = codebase.get_file("file1.ts")
+ file2 = codebase.get_file("file2.ts")
+
+ # Get symbols
+ namespace = file1.get_symbol("MathUtils")
+ pi = namespace.get_symbol("PI")
+ square = namespace.get_symbol("square")
+ internal = namespace.get_symbol("internal")
+ calc_area = file2.get_function("calculateArea")
+ calc_volume = file2.get_function("calculateVolume")
+ namespace_import = file2.get_import("MathUtils")
+
+ # Check namespace visibility
+ assert "MathUtils" in namespace.valid_import_names
+ assert "PI" in namespace.valid_import_names
+ assert "square" in namespace.valid_import_names
+ assert "internal" not in namespace.valid_import_names
+ assert internal is None # private symbol not accessible
+
+ # Check direct vs chained usages
+ assert {namespace.export}.issubset(namespace.symbol_usages(UsageType.DIRECT))
+ assert {namespace.export, calc_area}.issubset(namespace.symbol_usages)
+ assert {pi.export}.issubset(pi.symbol_usages(UsageType.DIRECT))
+ assert {pi.export, calc_area}.issubset(pi.symbol_usages)
+ assert {calc_area}.issubset(square.symbol_usages(UsageType.CHAINED))
+
+ # Verify attribute resolution
+ assert namespace.resolve_attribute("PI") == pi.export
+ assert namespace.resolve_attribute("square") == square.export
+ assert namespace.resolve_attribute("internal") is None
diff --git a/tests/unit/codegen/sdk/typescript/tsx/test_tsx_edit.py b/tests/unit/codegen/sdk/typescript/tsx/test_tsx_edit.py
index 6f21af839..a7147bf3a 100644
--- a/tests/unit/codegen/sdk/typescript/tsx/test_tsx_edit.py
+++ b/tests/unit/codegen/sdk/typescript/tsx/test_tsx_edit.py
@@ -333,7 +333,7 @@ def test_tsx_move_component(tmpdir) -> None:
ctx.commit_transactions()
assert "export function FooBar" in new_file.content
- assert "export function MyFooBar" in new_file.content
+ assert "function MyFooBar" in new_file.content
assert "import { FooBar } from 'new'" in original_file.content
assert "import { MyFooBar } from 'new'" not in original_file.content
diff --git a/tests/unit/codegen/sdk/typescript/tsx/test_tsx_parsing.py b/tests/unit/codegen/sdk/typescript/tsx/test_tsx_parsing.py
index af2f32446..813102927 100644
--- a/tests/unit/codegen/sdk/typescript/tsx/test_tsx_parsing.py
+++ b/tests/unit/codegen/sdk/typescript/tsx/test_tsx_parsing.py
@@ -105,7 +105,7 @@ def test_tsx_file_type_validation(tmpdir) -> None:
test_component.move_to_file(tsx_file)
- assert "export function TestComponent" in tsx_file.content
+ assert "function TestComponent" in tsx_file.content
def test_jsx_element_attributes(tmpdir) -> None:
diff --git a/tests/unit/skills/implementations/guides/organize-your-codebase.py b/tests/unit/skills/implementations/guides/organize-your-codebase.py
index 5827d2ca5..d2e914bd2 100644
--- a/tests/unit/skills/implementations/guides/organize-your-codebase.py
+++ b/tests/unit/skills/implementations/guides/organize-your-codebase.py
@@ -416,7 +416,7 @@ def my_symbol():
SkillTestCaseTSFile(
input="",
output="""
-export function dependencyFunction() {
+function dependencyFunction() {
console.log("I'm a dependency");
}